1
0
mirror of https://git.code.sf.net/p/linux-ima/ima-evm-utils synced 2025-07-03 22:23:16 +02:00

8 Commits
v0.1.0 ... v0.2

Author SHA1 Message Date
76f3496455 evm-utils renamed to ima-evm-utils.
Version set to 0.2.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-09-06 14:08:53 +03:00
dc36ed86d3 Added RPM and TAR building rules
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-09-06 14:08:53 +03:00
de89119dbf added command options description
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-09-06 14:08:53 +03:00
04e3ff3ef5 removed unused parameter
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-09-06 14:08:53 +03:00
5769fb1833 import functions combined
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-09-06 14:08:53 +03:00
52960f8b93 updated error handling
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-09-06 14:08:53 +03:00
e576ac9771 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-09-06 14:08:53 +03:00
d61b9c0be7 added HMAC API error handling
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-09-06 14:08:53 +03:00
5 changed files with 275 additions and 192 deletions

View File

@ -1,3 +1,12 @@
2012-04-05 Dmitry Kasatkin <dmitry.kasatkin@intel.com>
version 0.2
* added RPM & TAR building makefile rules
* renamed evm-utils to ima-evm-utils
* added command options description
* updated error handling
* refactored redundant code
2012-04-02 Dmitry Kasatkin <dmitry.kasatkin@intel.com> 2012-04-02 Dmitry Kasatkin <dmitry.kasatkin@intel.com>
version 0.1.0 version 0.1.0

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.2, 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)
else inkey = "/etc/keys/pubkey_evm.bin";
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);
len = read_key(inkey, key); if (binkey) {
if (len < 0) key = file2bin(inkey, &len);
return -1; if (!key)
return -1;
} else {
len = read_key(inkey, key);
if (len < 0)
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,18 +1141,20 @@ 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);
else if (cmd->msg) else if (cmd->msg)
printf("%s", cmd->msg); printf(" %s", cmd->msg);
} }
} }
@ -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();