20 Commits

Author SHA1 Message Date
510061c2b8 Added RPM and TAR building rules
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-04-05 15:24:01 +03:00
7b0cbf5e53 evm-utils renamed to ima-evm-utils
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-04-05 14:54:28 +03:00
776183a642 added command options description
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-04-05 14:32:28 +03:00
c3d090abba removed unused parameter
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-04-05 13:48:39 +03:00
bb79f7aaf2 import functions combined
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-04-05 13:48:08 +03:00
a3c7609b80 updated error handling
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-04-05 12:23:45 +03:00
d70816cbf1 read list of existing extended attributes
getxattr() might return runtime value which does not really exist
on file system. It happens for SMACK LSM. Reading the list of existing
attributes allows to prevent such to happen.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-04-04 16:48:38 +03:00
c6c8cccb83 added HMAC API error handling
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-04-04 16:48:16 +03:00
94de24e5ad version 0.1.0 2012-04-02 15:52:30 +03:00
3f2f98aef8 remove unused parameter
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-03-15 11:52:36 +02:00
ae47101134 Changed time_t timestamp type to uint32_t
time_t is actually long and is different on 32 and 64 bit architectures.
Format of the signatures should not depend on the architecture and should
be the same. Changed timestamp to uint32_t like in GPG.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-02-02 10:12:34 +02:00
d98e4a9bed Added missing CFLAGS
Added missing CFLAGS

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-02-01 15:24:07 +02:00
179664d7e9 Added signature write to .sig file
To enable module signature verification working on file systems
without extended attributes, or to be able to copy modules by methods,
which does not support extended attribute copying, it is necessary
to store signature in the file. This patch provides command line parameter
for storing signature in .sig file.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-02-01 15:24:02 +02:00
c440d2d95f Change set_xattr to xattr.
set_xattr changed to xattr.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-02-01 11:10:15 +02:00
fed7fb6933 Changed to conform Linux kernel coding style
Changed to conform Linux kernel coding style, except 80 characters
line length limit.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-02-01 11:04:36 +02:00
78494ab370 added password parameter for using encrypted keys
Added password parameter for using encrypted keys.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-02-01 10:35:43 +02:00
192f897b8e added openssl initialization and error reporting
Added openssl initialization and error reporting.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-02-01 10:35:38 +02:00
0799e24820 minor fixes
- error message
- command info

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-01-30 13:23:28 +02:00
c94a0b9262 Merge branch 'master' of ssh://linux-ima.git.sourceforge.net/gitroot/linux-ima/evm-utils 2011-12-02 14:39:56 +02:00
e2da6956c4 evmctl - IMA/EVM control tool
evmctl provides signing support for IMA/EVM.
Functionality includes signing of file content (IMA), file metadata (EVM),
importing public keys into kernel keyring.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2011-10-14 16:53:34 +03:00
4 changed files with 266 additions and 192 deletions

View File

@ -4,3 +4,20 @@ SUBDIRS = src tests
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
SRCS = $(HOME)/rpmbuild/SOURCES
SPEC = $(PACKAGE_NAME).spec
pkgname = $(PACKAGE_NAME)-$(PACKAGE_VERSION)
tarname = $(pkgname).tar.gz
$(tarname):
git tag -f v$(PACKAGE_VERSION)
git archive --format=tar --prefix=$(pkgname)/ v$(PACKAGE_VERSION) $(FILES) | gzip >$@;
tar: $(tarname)
rpm: $(tarname)
cp $(tarname) $(SRCS)/
rpmbuild -ba --nodeps $(SPEC)
.PHONY: $(tarname)

View File

@ -1,7 +1,7 @@
# autoconf script # autoconf script
AC_PREREQ([2.65]) AC_PREREQ([2.65])
AC_INIT(evm-utils, 0.1, dmitry.kasatkin@intel.com) AC_INIT(ima-evm-utils, 0.1.1, dmitry.kasatkin@intel.com)
AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
@ -47,7 +47,7 @@ fi
AC_CONFIG_FILES([Makefile AC_CONFIG_FILES([Makefile
src/Makefile src/Makefile
tests/Makefile tests/Makefile
evm-utils.spec ima-evm-utils.spec
]) ])
AC_OUTPUT AC_OUTPUT

View File

@ -1,7 +1,7 @@
Name: @PACKAGE_NAME@ Name: @PACKAGE_NAME@
Version: @PACKAGE_VERSION@ Version: @PACKAGE_VERSION@
Release: 1%{?dist} Release: 1%{?dist}
Summary: evm-utils - IMA/EVM support utilities Summary: @PACKAGE_NAME@ - IMA/EVM control utility
Group: System/Libraries Group: System/Libraries
License: LGPLv2 License: LGPLv2
#URL: #URL:
@ -12,11 +12,10 @@ BuildRequires: autoconf
BuildRequires: automake BuildRequires: automake
BuildRequires: openssl-devel BuildRequires: openssl-devel
BuildRequires: libattr-devel BuildRequires: libattr-devel
BuildRequires: readline-devel
BuildRequires: keyutils-libs-devel BuildRequires: keyutils-libs-devel
%description %description
This library provides EVM support utilities. This package provide IMA/EVM control utility
%prep %prep
%setup -q %setup -q
@ -48,6 +47,6 @@ exit 0
%{_libdir}/* %{_libdir}/*
%changelog %changelog
* Wed Jul 20 2011 Dmitry Kasatkin <dmitry.kasatkin@intel.com> * Thu Apr 05 2012 Dmitry Kasatkin <dmitry.kasatkin@intel.com>
- Initial package for MeeGo - Initial RPM spec file

View File

@ -2,7 +2,7 @@
* evm-utils - IMA/EVM support utilities * evm-utils - IMA/EVM support utilities
* *
* Copyright (C) 2011 Nokia Corporation * Copyright (C) 2011 Nokia Corporation
* Copyright (C) 2011 Intel Corporation * Copyright (C) 2011, 2012 Intel Corporation
* *
* Authors: * Authors:
* Dmitry Kasatkin <dmitry.kasatkin@nokia.com> * Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
@ -74,7 +74,7 @@
#define log_dump(p, len) do_log_dump(LOG_INFO, p, len) #define log_dump(p, len) do_log_dump(LOG_INFO, p, len)
#define log_info(fmt, args...) do_log(LOG_INFO, fmt, ##args) #define log_info(fmt, args...) do_log(LOG_INFO, fmt, ##args)
#define log_err(fmt, args...) do_log(LOG_ERR, fmt, ##args) #define log_err(fmt, args...) do_log(LOG_ERR, fmt, ##args)
#define log_errno(fmt, args...) do_log(LOG_ERR, fmt ": %s (%d)\n", ##args, strerror(errno), errno) #define log_errno(fmt, args...) do_log(LOG_ERR, fmt ": errno: %s (%d)\n", ##args, strerror(errno), errno)
#define DATA_SIZE 4096 #define DATA_SIZE 4096
#define SHA1_HASH_LEN 20 #define SHA1_HASH_LEN 20
@ -200,7 +200,7 @@ static int bin2file(const char *file, const char *ext, const unsigned char *data
fp = fopen(name, "w"); fp = fopen(name, "w");
if (!fp) { if (!fp) {
log_errno("Unable to open %s for writing", name); log_err("Unable to open %s for writing\n", name);
return -1; return -1;
} }
err = fwrite(data, len, 1, fp); err = fwrite(data, len, 1, fp);
@ -208,16 +208,16 @@ static int bin2file(const char *file, const char *ext, const unsigned char *data
return err; return err;
} }
static char *file2bin(const char *file, int *size) static unsigned char *file2bin(const char *file, int *size)
{ {
FILE *fp; FILE *fp;
int len; int len;
char *data; unsigned char *data;
len = get_filesize(file); len = get_filesize(file);
fp = fopen(file, "r"); fp = fopen(file, "r");
if (!fp) { if (!fp) {
log_errno("Unable to open %s", file); log_err("Unable to open %s\n", file);
return NULL; return NULL;
} }
data = malloc(len); data = malloc(len);
@ -270,14 +270,14 @@ static int read_key(const char *inkey, unsigned char *pub)
fp = fopen(inkey, "r"); fp = fopen(inkey, "r");
if (!fp) { if (!fp) {
log_errno("read key failed from file %s", inkey); log_err("read key failed from file %s\n", inkey);
return -1; return -1;
} }
key1 = PEM_read_RSA_PUBKEY(fp, &key, NULL, NULL); key1 = PEM_read_RSA_PUBKEY(fp, &key, NULL, NULL);
fclose(fp); fclose(fp);
if (!key1) { if (!key1) {
log_errno("PEM_read_RSA_PUBKEY() failed"); log_err("PEM_read_RSA_PUBKEY() failed\n");
return -1; return -1;
} }
@ -324,14 +324,14 @@ static int sign_hash(const unsigned char *hash, int size, const char *keyfile, u
fp = fopen(keyfile, "r"); fp = fopen(keyfile, "r");
if (!fp) { if (!fp) {
log_errno("Unable to open keyfile %s", keyfile); log_err("Unable to open keyfile %s\n", keyfile);
return -1; return -1;
} }
key1 = PEM_read_RSAPrivateKey(fp, &key, NULL, keypass); key1 = PEM_read_RSAPrivateKey(fp, &key, NULL, keypass);
fclose(fp); fclose(fp);
if (!key1) { if (!key1) {
log_errno("RSAPrivateKey() failed"); log_err("PEM_read_RSAPrivateKey() failed\n");
return -1; return 1;
} }
/* now create a new hash */ /* now create a new hash */
@ -355,8 +355,8 @@ static int sign_hash(const unsigned char *hash, int size, const char *keyfile, u
err = RSA_private_encrypt(sizeof(sighash), sighash, sig + sizeof(*hdr) + 2, key, RSA_PKCS1_PADDING); err = RSA_private_encrypt(sizeof(sighash), sighash, sig + sizeof(*hdr) + 2, key, RSA_PKCS1_PADDING);
RSA_free(key); RSA_free(key);
if (err < 0) { if (err < 0) {
log_errno("RSA_private_encrypt() failed: %d", err); log_err("RSA_private_encrypt() failed: %d\n", err);
return -1; return 1;
} }
len = err; len = err;
@ -372,6 +372,18 @@ static int sign_hash(const unsigned char *hash, int size, const char *keyfile, u
return len; return len;
} }
static int find_xattr(const char *list, int list_size, const char *xattr)
{
int len;
for (; list_size > 0; len++, list_size -= len, list += len) {
len = strlen(list);
if (!strcmp(list, xattr))
return 1;
}
return 0;
}
static int calc_evm_hash(const char *file, unsigned char *hash) static int calc_evm_hash(const char *file, unsigned char *hash)
{ {
struct stat st; struct stat st;
@ -382,20 +394,22 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
unsigned int mdlen; unsigned int mdlen;
char **xattrname; char **xattrname;
char xattr_value[1024]; char xattr_value[1024];
char list[1024];
ssize_t list_size;
fd = open(file, 0); fd = open(file, 0);
if (fd < 0) { if (fd < 0) {
log_errno("Unable to open %s", file); log_err("Unable to open %s\n", file);
return -1; return -1;
} }
if (fstat(fd, &st)) { if (fstat(fd, &st)) {
log_errno("fstat() failed"); log_err("fstat() failed\n");
return -1; return -1;
} }
if (ioctl(fd, EXT34_IOC_GETVERSION, &generation)) { if (ioctl(fd, EXT34_IOC_GETVERSION, &generation)) {
log_errno("ioctl() failed"); log_err("ioctl() failed\n");
return -1; return -1;
} }
@ -403,22 +417,32 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
log_info("generation: %u\n", generation); log_info("generation: %u\n", generation);
list_size = llistxattr(file, list, sizeof(list));
if (list_size <= 0) {
log_err("llistxattr() failed\n");
return -1;
}
md = EVP_get_digestbyname("sha1"); md = EVP_get_digestbyname("sha1");
if (!md) { if (!md) {
log_errno("EVP_get_digestbyname() failed"); log_err("EVP_get_digestbyname() failed\n");
return -1; return 1;
} }
err = EVP_DigestInit(&ctx, md); err = EVP_DigestInit(&ctx, md);
if (!err) { if (!err) {
log_errno("EVP_DigestInit() failed"); log_err("EVP_DigestInit() failed\n");
return -1; return 1;
} }
for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) { for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
err = getxattr(file, *xattrname, xattr_value, sizeof(xattr_value)); err = getxattr(file, *xattrname, xattr_value, sizeof(xattr_value));
if (err < 0) { if (err < 0) {
log_info("no attr: %s\n", *xattrname); log_info("no xattr: %s\n", *xattrname);
continue;
}
if (!find_xattr(list, list_size, *xattrname)) {
log_info("skipping xattr: %s\n", *xattrname);
continue; continue;
} }
/*log_debug("name: %s, value: %s, size: %d\n", *xattrname, xattr_value, err);*/ /*log_debug("name: %s, value: %s, size: %d\n", *xattrname, xattr_value, err);*/
@ -426,8 +450,8 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
log_debug_dump(xattr_value, err); log_debug_dump(xattr_value, err);
err = EVP_DigestUpdate(&ctx, xattr_value, err); err = EVP_DigestUpdate(&ctx, xattr_value, err);
if (!err) { if (!err) {
log_errno("EVP_DigestUpdate() failed"); log_err("EVP_DigestUpdate() failed\n");
return -1; return 1;
} }
} }
@ -440,34 +464,36 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
err = EVP_DigestUpdate(&ctx, (const unsigned char *)&hmac_misc, sizeof(hmac_misc)); err = EVP_DigestUpdate(&ctx, (const unsigned char *)&hmac_misc, sizeof(hmac_misc));
if (!err) { if (!err) {
log_errno("EVP_DigestUpdate() failed"); log_err("EVP_DigestUpdate() failed\n");
return -1; return 1;
} }
err = EVP_DigestFinal(&ctx, hash, &mdlen); err = EVP_DigestFinal(&ctx, hash, &mdlen);
if (!err) { if (!err) {
log_errno("EVP_DigestFinal() failed"); log_err("EVP_DigestFinal() failed\n");
return -1; return 1;
} }
return 0; return mdlen;
} }
static int sign_evm(const char *file, const char *key) static int sign_evm(const char *file, const char *key)
{ {
unsigned char hash[20]; unsigned char hash[20];
unsigned char sig[1024] = "\x03"; unsigned char sig[1024] = "\x03";
int err; int len, err;
calc_evm_hash(file, hash); len = calc_evm_hash(file, hash);
if (len <= 1)
return len;
err = sign_hash(hash, sizeof(hash), key, sig + 1); len = sign_hash(hash, len, key, sig + 1);
if (err < 0) if (len <= 1)
return err; return len;
if (xattr) { if (xattr) {
err = setxattr(file, "security.evm", sig, err + 1, 0); err = setxattr(file, "security.evm", sig, len + 1, 0);
if (err < 0) { if (err < 0) {
log_errno("setxattr failed: %s", file); log_err("setxattr failed: %s\n", file);
return err; return err;
} }
} }
@ -487,26 +513,26 @@ static int calc_file_hash(const char *file, uint8_t *hash)
data = malloc(bs); data = malloc(bs);
if (!data) { if (!data) {
log_errno("malloc failed"); log_err("malloc failed\n");
return -1; return -1;
} }
fp = fopen(file, "r"); fp = fopen(file, "r");
if (!fp) { if (!fp) {
log_errno("Unable to open %s", file); log_err("Unable to open %s\n", file);
return -1; return -1;
} }
md = EVP_get_digestbyname(hash_algo); md = EVP_get_digestbyname(hash_algo);
if (!md) { if (!md) {
log_errno("EVP_get_digestbyname() failed"); log_err("EVP_get_digestbyname() failed\n");
return -1; return 1;
} }
err = EVP_DigestInit(&ctx, md); err = EVP_DigestInit(&ctx, md);
if (!err) { if (!err) {
log_errno("EVP_DigestInit() failed"); log_err("EVP_DigestInit() failed\n");
return -1; return 1;
} }
for (size = get_fdsize(fileno(fp)); size; size -= len) { for (size = get_fdsize(fileno(fp)); size; size -= len) {
@ -514,22 +540,22 @@ static int calc_file_hash(const char *file, uint8_t *hash)
err = fread(data, len, 1, fp); err = fread(data, len, 1, fp);
if (!err) { if (!err) {
if (ferror(fp)) { if (ferror(fp)) {
log_errno("fread() error\n"); log_err("fread() error\n\n");
return -1; return -1;
} }
break; break;
} }
err = EVP_DigestUpdate(&ctx, data, len); err = EVP_DigestUpdate(&ctx, data, len);
if (!err) { if (!err) {
log_errno("EVP_DigestUpdate() failed"); log_err("EVP_DigestUpdate() failed\n");
return -1; return 1;
} }
} }
err = EVP_DigestFinal(&ctx, hash, &mdlen); err = EVP_DigestFinal(&ctx, hash, &mdlen);
if (!err) { if (!err) {
log_errno("EVP_DigestFinal() failed"); log_err("EVP_DigestFinal() failed\n");
return -1; return 1;
} }
fclose(fp); fclose(fp);
@ -557,20 +583,20 @@ static int calc_dir_hash(const char *file, uint8_t *hash)
dir = opendir(file); dir = opendir(file);
if (!dir) { if (!dir) {
log_errno("Unable to open %s", file); log_err("Unable to open %s\n", file);
return -1; return -1;
} }
md = EVP_get_digestbyname(hash_algo); md = EVP_get_digestbyname(hash_algo);
if (!md) { if (!md) {
log_errno("EVP_get_digestbyname() failed"); log_err("EVP_get_digestbyname() failed\n");
return -1; return 1;
} }
err = EVP_DigestInit(&ctx, md); err = EVP_DigestInit(&ctx, md);
if (!err) { if (!err) {
log_errno("EVP_DigestInit() failed"); log_err("EVP_DigestInit() failed\n");
return -1; return 1;
} }
while ((de = readdir(dir))) { while ((de = readdir(dir))) {
@ -594,21 +620,21 @@ static int calc_dir_hash(const char *file, uint8_t *hash)
log_debug("entry: ino: %llu, %s\n", (unsigned long long)ino, cur->de.d_name); log_debug("entry: ino: %llu, %s\n", (unsigned long long)ino, cur->de.d_name);
err = EVP_DigestUpdate(&ctx, cur->de.d_name, strlen(cur->de.d_name)); err = EVP_DigestUpdate(&ctx, cur->de.d_name, strlen(cur->de.d_name));
if (!err) { if (!err) {
log_errno("EVP_DigestUpdate() failed"); log_err("EVP_DigestUpdate() failed\n");
return -1; return 1;
} }
err = EVP_DigestUpdate(&ctx, &ino, sizeof(ino)); err = EVP_DigestUpdate(&ctx, &ino, sizeof(ino));
if (!err) { if (!err) {
log_errno("EVP_DigestUpdate() failed"); log_err("EVP_DigestUpdate() failed\n");
return -1; return 1;
} }
free(cur); free(cur);
} }
err = EVP_DigestFinal(&ctx, hash, &mdlen); err = EVP_DigestFinal(&ctx, hash, &mdlen);
if (!err) { if (!err) {
log_errno("EVP_DigestFinal() failed"); log_err("EVP_DigestFinal() failed\n");
return -1; return 1;
} }
closedir(dir); closedir(dir);
@ -619,33 +645,33 @@ static int calc_dir_hash(const char *file, uint8_t *hash)
static int hash_ima(const char *file) static int hash_ima(const char *file)
{ {
unsigned char hash[65] = "\x01"; /* MAX hash size + 1 */ unsigned char hash[65] = "\x01"; /* MAX hash size + 1 */
int err; int len, err;
struct stat st; struct stat st;
/* Need to know the file length */ /* Need to know the file length */
err = stat(file, &st); err = stat(file, &st);
if (err < 0) { if (err < 0) {
log_errno("stat() failed"); log_err("stat() failed\n");
return err; return err;
} }
if (S_ISDIR(st.st_mode)) if (S_ISDIR(st.st_mode))
err = calc_dir_hash(file, hash + 1); len = calc_dir_hash(file, hash + 1);
else else
err = calc_file_hash(file, hash + 1); len = calc_file_hash(file, hash + 1);
if (err < 0) if (len <= 1)
return err; return len;
if (verbose >= LOG_INFO) if (verbose >= LOG_INFO)
log_info("hash: "); log_info("hash: ");
if (!xattr || verbose >= LOG_INFO) if (!xattr || verbose >= LOG_INFO)
dump(hash, err + 1); dump(hash, len + 1);
if (xattr) { if (xattr) {
err = setxattr(file, "security.ima", hash, err + 1, 0); err = setxattr(file, "security.ima", hash, len + 1, 0);
if (err < 0) { if (err < 0) {
log_errno("setxattr failed: %s", file); log_err("setxattr failed: %s\n", file);
return err; return err;
} }
} }
@ -660,7 +686,7 @@ static int cmd_hash_ima(struct command *cmd)
if (!file) { if (!file) {
log_err("Parameters missing\n"); log_err("Parameters missing\n");
print_usage(cmd); print_usage(cmd);
return 1; return -1;
} }
return hash_ima(file); return hash_ima(file);
@ -670,23 +696,23 @@ static int sign_ima(const char *file, const char *key)
{ {
unsigned char hash[64]; unsigned char hash[64];
unsigned char sig[1024] = "\x03"; unsigned char sig[1024] = "\x03";
int err; int len, err;
err = calc_file_hash(file, hash); len = calc_file_hash(file, hash);
if (err < 0) if (len <= 1)
return err; return len;
err = sign_hash(hash, err, key, sig + 1); len = sign_hash(hash, len, key, sig + 1);
if (err < 0) if (len <= 1)
return err; return len;
if (sigfile) if (sigfile)
bin2file(file, "sig", sig, err + 1); bin2file(file, "sig", sig, len + 1);
if (xattr) { if (xattr) {
err = setxattr(file, "security.ima", sig, err + 1, 0); err = setxattr(file, "security.ima", sig, len + 1, 0);
if (err < 0) { if (err < 0) {
log_errno("setxattr failed: %s", file); log_err("setxattr failed: %s\n", file);
return err; return err;
} }
} }
@ -701,7 +727,7 @@ static int cmd_sign_ima(struct command *cmd)
if (!file) { if (!file) {
log_err("Parameters missing\n"); log_err("Parameters missing\n");
print_usage(cmd); print_usage(cmd);
return 1; return -1;
} }
key = g_argv[optind++]; key = g_argv[optind++];
@ -720,7 +746,13 @@ static int cmd_sign_evm(struct command *cmd)
if (!file) { if (!file) {
log_err("Parameters missing\n"); log_err("Parameters missing\n");
print_usage(cmd); print_usage(cmd);
return 1; return -1;
}
if (!digsig && !digest) {
log_err("Parameters missing\n");
print_usage(cmd);
return -1;
} }
key = g_argv[optind++]; key = g_argv[optind++];
@ -757,14 +789,14 @@ static int verify_hash(const unsigned char *hash, int size, unsigned char *sig,
fp = fopen(keyfile, "r"); fp = fopen(keyfile, "r");
if (!fp) { if (!fp) {
log_errno("Unable to open keyfile %s", keyfile); log_err("Unable to open keyfile %s\n", keyfile);
return -1; return -1;
} }
key1 = PEM_read_RSA_PUBKEY(fp, &key, NULL, NULL); key1 = PEM_read_RSA_PUBKEY(fp, &key, NULL, NULL);
fclose(fp); fclose(fp);
if (!key1) { if (!key1) {
log_errno("PEM_read_RSA_PUBKEY() failed"); log_err("PEM_read_RSA_PUBKEY() failed\n");
return -1; return 1;
} }
SHA1_Init(&ctx); SHA1_Init(&ctx);
@ -777,14 +809,14 @@ static int verify_hash(const unsigned char *hash, int size, unsigned char *sig,
err = RSA_public_decrypt(siglen - sizeof(*hdr) - 2, sig + sizeof(*hdr) + 2, out, key, RSA_PKCS1_PADDING); err = RSA_public_decrypt(siglen - sizeof(*hdr) - 2, sig + sizeof(*hdr) + 2, out, key, RSA_PKCS1_PADDING);
RSA_free(key); RSA_free(key);
if (err < 0) { if (err < 0) {
log_errno("RSA_public_decrypt() failed: %d", err); log_err("RSA_public_decrypt() failed: %d\n", err);
return -1; return 1;
} }
len = err; len = err;
if (len != sizeof(sighash) || memcmp(out, sighash, len) != 0) { if (len != sizeof(sighash) || memcmp(out, sighash, len) != 0) {
log_errno("Verification failed: %d", err); log_err("Verification failed: %d\n", err);
return -1; return -1;
} else { } else {
/*log_info("Verification is OK\n");*/ /*log_info("Verification is OK\n");*/
@ -798,22 +830,24 @@ static int verify_evm(const char *file, const char *key)
{ {
unsigned char hash[20]; unsigned char hash[20];
unsigned char sig[1024]; unsigned char sig[1024];
int err; int len;
calc_evm_hash(file, hash); len = calc_evm_hash(file, hash);
if (len <= 1)
return len;
err = getxattr(file, "security.evm", sig, sizeof(sig)); len = getxattr(file, "security.evm", sig, sizeof(sig));
if (err < 0) { if (len < 0) {
log_errno("getxattr failed"); log_err("getxattr failed\n");
return err; return len;
} }
if (sig[0] != 0x03) { if (sig[0] != 0x03) {
log_errno("security.evm has not signature"); log_err("security.evm has not signature\n");
return err; return -1;
} }
return verify_hash(hash, sizeof(hash), sig + 1, err - 1, key); return verify_hash(hash, sizeof(hash), sig + 1, len - 1, key);
} }
static int cmd_verify_evm(struct command *cmd) static int cmd_verify_evm(struct command *cmd)
@ -823,7 +857,7 @@ static int cmd_verify_evm(struct command *cmd)
if (!file) { if (!file) {
log_err("Parameters missing\n"); log_err("Parameters missing\n");
print_usage(cmd); print_usage(cmd);
return 1; return -1;
} }
key = g_argv[optind++]; key = g_argv[optind++];
@ -863,62 +897,21 @@ static int cmd_convert(struct command *cmd)
return 0; return 0;
} }
static int cmd_import_bin(struct command *cmd)
{
int len;
char *inkey, *ring = NULL;
char *key, name[20];
key_serial_t id;
uint8_t keyid[8];
inkey = g_argv[optind++];
if (!inkey)
inkey = "/etc/keys/pubkey_evm.bin";
else
ring = g_argv[optind++];
if (!ring)
id = KEY_SPEC_USER_KEYRING;
else
id = atoi(ring);
key = file2bin(inkey, &len);
if (!key)
return -1;
calc_keyid(keyid, name, (unsigned char *)key, len);
log_info("Importing public key %s from file %s into keyring %d\n", name, inkey, id);
id = add_key("user", name, key, len, id);
if (id < 0) {
log_errno("add_key failed");
return -1;
}
log_info("keyid: %d\n", id);
printf("%d\n", id);
free(key);
return 0;
}
static int cmd_import(struct command *cmd) static int cmd_import(struct command *cmd)
{ {
char *inkey, *ring = NULL; char *inkey, *ring = NULL;
unsigned char key[1024]; unsigned char _key[1024], *key = _key;
int id, len; int id, len;
char name[20]; char name[20];
uint8_t keyid[8]; uint8_t keyid[8];
if (binkey)
return cmd_import_bin(cmd);
inkey = g_argv[optind++]; inkey = g_argv[optind++];
if (!inkey) if (!inkey) {
inkey = "/etc/keys/pubkey_evm.pem"; if (binkey)
inkey = "/etc/keys/pubkey_evm.bin";
else else
inkey = "/etc/keys/pubkey_evm.pem";
} else
ring = g_argv[optind++]; ring = g_argv[optind++];
if (!ring) if (!ring)
@ -926,9 +919,15 @@ static int cmd_import(struct command *cmd)
else else
id = atoi(ring); id = atoi(ring);
if (binkey) {
key = file2bin(inkey, &len);
if (!key)
return -1;
} else {
len = read_key(inkey, key); len = read_key(inkey, key);
if (len < 0) if (len < 0)
return -1; return -1;
}
calc_keyid(keyid, name, key, len); calc_keyid(keyid, name, key, len);
@ -936,13 +935,16 @@ static int cmd_import(struct command *cmd)
id = add_key("user", name, key, len, id); id = add_key("user", name, key, len, id);
if (id < 0) { if (id < 0) {
log_errno("add_key failed"); log_err("add_key failed\n");
return -1; return -1;
} }
log_info("keyid: %d\n", id); log_info("keyid: %d\n", id);
printf("%d\n", id); printf("%d\n", id);
if (binkey)
free(key);
return 0; return 0;
} }
@ -957,18 +959,20 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
unsigned int mdlen; unsigned int mdlen;
char **xattrname; char **xattrname;
unsigned char xattr_value[1024]; unsigned char xattr_value[1024];
char *key; unsigned char *key;
int keylen; int keylen;
unsigned char evmkey[MAX_KEY_SIZE]; unsigned char evmkey[MAX_KEY_SIZE];
char list[1024];
ssize_t list_size;
key = file2bin(keyfile, &keylen); key = file2bin(keyfile, &keylen);
if (!key) { if (!key) {
log_errno("Unable to read a key: %s\n", keyfile); log_err("Unable to read a key: %s\n\n", keyfile);
return -1; return -1;
} }
if (keylen > sizeof(evmkey)) { if (keylen > sizeof(evmkey)) {
log_errno("key is too long\n"); log_err("key is too long\n");
return -1; return -1;
} }
@ -978,17 +982,17 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
fd = open(file, 0); fd = open(file, 0);
if (fd < 0) { if (fd < 0) {
log_errno("Unable to open %s", file); log_err("Unable to open %s\n", file);
return -1; return -1;
} }
if (fstat(fd, &st)) { if (fstat(fd, &st)) {
log_errno("fstat() failed"); log_err("fstat() failed\n");
return -1; return -1;
} }
if (ioctl(fd, EXT34_IOC_GETVERSION, &generation)) { if (ioctl(fd, EXT34_IOC_GETVERSION, &generation)) {
log_errno("ioctl() failed"); log_err("ioctl() failed\n");
return -1; return -1;
} }
@ -996,18 +1000,36 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
log_info("generation: %u\n", generation); log_info("generation: %u\n", generation);
HMAC_Init(&ctx, evmkey, sizeof(evmkey), EVP_sha1()); list_size = llistxattr(file, list, sizeof(list));
if (list_size <= 0) {
log_err("llistxattr() failed\n");
return -1;
}
err = HMAC_Init(&ctx, evmkey, sizeof(evmkey), EVP_sha1());
if (!err) {
log_err("HMAC_Init() failed\n");
return 1;
}
for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) { for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
err = getxattr(file, *xattrname, xattr_value, sizeof(xattr_value)); err = getxattr(file, *xattrname, xattr_value, sizeof(xattr_value));
if (err < 0) { if (err < 0) {
log_info("no attr: %s\n", *xattrname); log_info("no xattr: %s\n", *xattrname);
continue;
}
if (!find_xattr(list, list_size, *xattrname)) {
log_info("skipping xattr: %s\n", *xattrname);
continue; continue;
} }
/*log_debug("name: %s, value: %s, size: %d\n", *xattrname, xattr_value, err);*/ /*log_debug("name: %s, value: %s, size: %d\n", *xattrname, xattr_value, err);*/
log_info("name: %s, size: %d\n", *xattrname, err); log_info("name: %s, size: %d\n", *xattrname, err);
log_debug_dump(xattr_value, err); log_debug_dump(xattr_value, err);
HMAC_Update(&ctx, xattr_value, err); err = HMAC_Update(&ctx, xattr_value, err);
if (!err) {
log_err("HMAC_Update() failed\n");
return 1;
}
} }
memset(&hmac_misc, 0, sizeof(hmac_misc)); memset(&hmac_misc, 0, sizeof(hmac_misc));
@ -1017,32 +1039,41 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
hmac_misc.gid = st.st_gid; hmac_misc.gid = st.st_gid;
hmac_misc.mode = st.st_mode; hmac_misc.mode = st.st_mode;
HMAC_Update(&ctx, (const unsigned char *)&hmac_misc, sizeof(hmac_misc)); err = HMAC_Update(&ctx, (const unsigned char *)&hmac_misc, sizeof(hmac_misc));
HMAC_Final(&ctx, hash, &mdlen); if (!err) {
log_err("HMAC_Update() failed\n");
return 1;
}
err = HMAC_Final(&ctx, hash, &mdlen);
if (!err) {
log_err("HMAC_Final() failed\n");
return 1;
}
HMAC_CTX_cleanup(&ctx); HMAC_CTX_cleanup(&ctx);
free(key); free(key);
return 0; return mdlen;
} }
static int hmac_evm(const char *file, const char *key) static int hmac_evm(const char *file, const char *key)
{ {
unsigned char hash[20]; unsigned char hash[20];
unsigned char sig[1024] = "\x02"; unsigned char sig[1024] = "\x02";
int err; int len, err;
calc_evm_hmac(file, key, hash); len = calc_evm_hmac(file, key, hash);
if (len <= 1)
return len;
log_info("hmac: "); log_info("hmac: ");
log_dump(hash, sizeof(hash)); log_dump(hash, len);
memcpy(sig + 1, hash, sizeof(hash)); memcpy(sig + 1, hash, len);
err = sizeof(hash);
if (xattr) { if (xattr) {
err = setxattr(file, "security.evm", sig, err + 1, 0); err = setxattr(file, "security.evm", sig, len + 1, 0);
if (err < 0) { if (err < 0) {
log_errno("setxattr failed: %s", file); log_err("setxattr failed: %s\n", file);
return err; return err;
} }
} }
@ -1058,7 +1089,13 @@ static int cmd_hmac_evm(struct command *cmd)
if (!file) { if (!file) {
log_err("Parameters missing\n"); log_err("Parameters missing\n");
print_usage(cmd); print_usage(cmd);
return 1; return -1;
}
if (!digsig && !digest) {
log_err("Parameters missing\n");
print_usage(cmd);
return -1;
} }
key = g_argv[optind++]; key = g_argv[optind++];
@ -1090,8 +1127,7 @@ static void print_full_usage(struct command *cmd)
if (cmd->name) if (cmd->name)
printf("usage: %s %s\n", cmd->name, cmd->arg ? cmd->arg : ""); printf("usage: %s %s\n", cmd->name, cmd->arg ? cmd->arg : "");
if (cmd->msg) if (cmd->msg)
printf("description:\n%s", cmd->msg); printf("%s", cmd->msg);
} }
static int print_command_usage(struct command *cmds, char *command) static int print_command_usage(struct command *cmds, char *command)
@ -1105,13 +1141,15 @@ static int print_command_usage(struct command *cmds, char *command)
} }
} }
printf("invalid command: %s\n", command); printf("invalid command: %s\n", command);
return 1; return -1;
} }
static void print_all_usage(struct command *cmds) static void print_all_usage(struct command *cmds)
{ {
struct command *cmd; struct command *cmd;
printf("commands:\n");
for (cmd = cmds; cmd->name; cmd++) { for (cmd = cmds; cmd->name; cmd++) {
if (cmd->arg) if (cmd->arg)
printf(" %s %s\n", cmd->name, cmd->arg); printf(" %s %s\n", cmd->name, cmd->arg);
@ -1143,26 +1181,40 @@ static int cmd_help(struct command *cmd)
static void usage(void) static void usage(void)
{ {
printf("Usage: evmctl <command> [parameters..]\n"); printf("Usage: evmctl [-v] <command> [OPTIONS]\n");
print_all_usage(cmds); print_all_usage(cmds);
printf(
"\n"
" -a, --hashalgo sha1 (default), sha224, sha256, sha384, sha512\n"
" -s, --imasig also make IMA signature\n"
" -d, --imahash also make IMA hash\n"
" -f, --sigfile store IMA signature in .sig file instead of xattr\n"
" -b, --bin signing key is in binary format\n"
" -p, --pass password for encrypted signing key\n"
" -n print result to stdout instead of setting xattr\n"
" -v increase verbosity level\n"
" -h, --help display this help and exit\n"
"\n");
} }
struct command cmds[] = { struct command cmds[] = {
{"help", cmd_help, 0, "<command>"}, {"help", cmd_help, 0, "<command>"},
{"import", cmd_import, 0, "[--bin] inkey keyring", "Import public key (PEM/bin) into the keyring.\n"}, {"import", cmd_import, 0, "[--bin] pubkey keyring", "Import public key (PEM/bin) into the keyring.\n"},
{"convert", cmd_convert, 0, "inkey outkey", "Convert PEM public key into IMA/EVM kernel friendly format.\n"}, {"convert", cmd_convert, 0, "inkey outkey", "Convert PEM public key into IMA/EVM kernel friendly format.\n"},
{"sign", cmd_sign_evm, 0, "[--imahash | --imasig ] file [key]", "Sign file metadata.\n"}, {"sign", cmd_sign_evm, 0, "[--imahash | --imasig ] [--pass password] file [key]", "Sign file metadata.\n"},
{"verify", cmd_verify_evm, 0, "file", "Verify EVM signature (for debugging).\n"}, {"verify", cmd_verify_evm, 0, "file", "Verify EVM signature (for debugging).\n"},
{"ima_sign", cmd_sign_ima, 0, "[--sigfile] file [key]", "Sign file content.\n"}, {"ima_sign", cmd_sign_ima, 0, "[--sigfile] [--pass password] file [key]", "Make file content signature.\n"},
{"ima_hash", cmd_hash_ima, 0, "file", "Hash file content.\n"}, {"ima_hash", cmd_hash_ima, 0, "file", "Make file content hash.\n"},
{"hmac", cmd_hmac_evm, 0, "[--imahash | --imasig ] file [key]", "Sign file metadata with HMAC (for debugging).\n"}, #ifdef DEBUG
{"hmac", cmd_hmac_evm, 0, "[--imahash | --imasig ] file [key]", "Sign file metadata with HMAC using symmetric key (for testing purpose).\n"},
#endif
{0, 0, 0, NULL} {0, 0, 0, NULL}
}; };
static struct option opts[] = { static struct option opts[] = {
{"help", 0, 0, 'h'}, {"help", 0, 0, 'h'},
{"inkey", 1, 0, 'k'},
{"imasig", 0, 0, 's'}, {"imasig", 0, 0, 's'},
{"imahash", 0, 0, 'd'}, {"imahash", 0, 0, 'd'},
{"hashalgo", 1, 0, 'a'}, {"hashalgo", 1, 0, 'a'},
@ -1181,7 +1233,7 @@ int main(int argc, char *argv[])
g_argc = argc; g_argc = argc;
while (1) { while (1) {
c = getopt_long(argc, argv, "hk:vnsda:bp:f", opts, &lind); c = getopt_long(argc, argv, "hvnsda:bp:f", opts, &lind);
if (c == -1) if (c == -1)
break; break;
@ -1190,9 +1242,6 @@ int main(int argc, char *argv[])
usage(); usage();
exit(0); exit(0);
break; break;
case 'k':
printf("inkey: %s\n", optarg);
break;
case 'v': case 'v':
verbose++; verbose++;
break; break;
@ -1234,8 +1283,17 @@ int main(int argc, char *argv[])
else else
err = call_command(cmds, argv[optind++]); err = call_command(cmds, argv[optind++]);
if (err) if (err) {
log_err("error: %s\n", ERR_error_string(ERR_get_error(), NULL)); unsigned long err;
if (errno)
log_err("errno: %s (%d)\n", strerror(errno), errno);
for (;;) {
err = ERR_get_error();
if (!err)
break;
log_err("%s\n", ERR_error_string(err, NULL));
}
}
ERR_free_strings(); ERR_free_strings();
EVP_cleanup(); EVP_cleanup();