From 3a1ef5f5c743130b237a550cc08b3d666ae5260b Mon Sep 17 00:00:00 2001 From: Zbigniew Jasinski Date: Fri, 30 May 2014 17:44:12 +0300 Subject: Use proper loff_t type for file size Signed-off-byL Zbigniew Jasinski Signed-off-by: Dmitry Kasatkin --- src/libimaevm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libimaevm.c b/src/libimaevm.c index 40b5229..abc86a6 100644 --- a/src/libimaevm.c +++ b/src/libimaevm.c @@ -152,19 +152,19 @@ int get_filesize(const char *filename) return (int)stats.st_size; } -static inline int get_fdsize(int fd) +static inline off_t get_fdsize(int fd) { struct stat stats; /* Need to know the file length */ fstat(fd, &stats); - return (int)stats.st_size; + return stats.st_size; } static int add_file_hash(const char *file, EVP_MD_CTX *ctx) { uint8_t *data; - int err, size, bs = DATA_SIZE; - size_t len; + int err, bs = DATA_SIZE; + off_t size, len; FILE *fp; data = malloc(bs); -- cgit v1.2.3 From 1a4c9ed2d6280c4667781139b4c5eb9d82561948 Mon Sep 17 00:00:00 2001 From: Zbigniew Jasinski Date: Fri, 30 May 2014 17:45:34 +0300 Subject: Fix memory leak Signed-off-by: Zbigniew Jasinski Signed-off-by: Dmitry Kasatkin --- src/libimaevm.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/libimaevm.c b/src/libimaevm.c index abc86a6..7f14cc5 100644 --- a/src/libimaevm.c +++ b/src/libimaevm.c @@ -163,43 +163,43 @@ static inline off_t get_fdsize(int fd) static int add_file_hash(const char *file, EVP_MD_CTX *ctx) { uint8_t *data; - int err, bs = DATA_SIZE; + int err = -1, bs = DATA_SIZE; off_t size, len; FILE *fp; - data = malloc(bs); - if (!data) { - log_err("malloc failed\n"); - return -1; - } - fp = fopen(file, "r"); if (!fp) { log_err("Unable to open %s\n", file); return -1; } + data = malloc(bs); + if (!data) { + log_err("malloc failed\n"); + goto out; + } + for (size = get_fdsize(fileno(fp)); size; size -= len) { len = MIN(size, bs); - err = fread(data, len, 1, fp); - if (!err) { + if (!fread(data, len, 1, fp)) { if (ferror(fp)) { log_err("fread() error\n\n"); - return -1; + goto out; } break; } - err = EVP_DigestUpdate(ctx, data, len); - if (!err) { + if (!EVP_DigestUpdate(ctx, data, len)) { log_err("EVP_DigestUpdate() failed\n"); - return 1; + err = 1; + goto out; } } - + err = 0; +out: fclose(fp); free(data); - return 0; + return err; } static int add_dir_hash(const char *file, EVP_MD_CTX *ctx) -- cgit v1.2.3 From 24c9e6adc7878270cb2e6601497e6f16c8ad9048 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 13 Jun 2014 12:58:16 +0300 Subject: add extra auto built files to .gitignore Signed-off-by: Dmitry Kasatkin --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index ab8a13c..ca7a06e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ *~ # Generated by autotools +.libs +m4 .deps aclocal.m4 autom4te.cache @@ -20,9 +22,12 @@ compile libtool ltmain.sh + # Compiled executables *.o *.a +*.lo +*.la src/evmctl tests/openclose config.h -- cgit v1.2.3 From dddef9172e6d9fc69fa4394c74ec374952e39bd5 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 13 Jun 2014 12:59:14 +0300 Subject: make it possible to provide keyring id in hexadecimal format Signed-off-by: Dmitry Kasatkin --- src/evmctl.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/evmctl.c b/src/evmctl.c index c5529f8..0d7c286 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -963,7 +963,10 @@ static int cmd_import(struct command *cmd) if (ring) { if (ring[0] != '@') { - id = atoi(ring); + int base = 10; + if (ring[0] == '0' && ring[1] == 'x') + base = 16; + id = strtoul(ring, NULL, base); } else { if (strcmp(ring, "@t") == 0) id = -1; -- cgit v1.2.3 From c5a2992bc868cf54ffc0f8d344b99ddb951e6123 Mon Sep 17 00:00:00 2001 From: Zbigniew Jasinski Date: Wed, 4 Jun 2014 17:49:58 +0200 Subject: Fix resource leak Signed-off-by: Zbigniew Jasinski --- src/evmctl.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/evmctl.c b/src/evmctl.c index 0d7c286..4d542d3 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -1495,7 +1495,7 @@ static int ima_measurement(const char *file) uint8_t pcr10[SHA_DIGEST_LENGTH]; struct template_entry entry = { .template = 0 }; FILE *fp; - int err; + int err = -1; memset(fox, 0xff, SHA_DIGEST_LENGTH); @@ -1508,19 +1508,19 @@ static int ima_measurement(const char *file) return -1; } - while ((err = fread(&entry.header, sizeof(entry.header), 1, fp))) { + while (fread(&entry.header, sizeof(entry.header), 1, fp)) { ima_extend_pcr(pcr, entry.header.digest, SHA_DIGEST_LENGTH); if (!fread(entry.name, entry.header.name_len, 1, fp)) { log_err("Unable to read template name\n"); - return -1; + goto out; } entry.name[entry.header.name_len] = '\0'; if (!fread(&entry.template_len, sizeof(entry.template_len), 1, fp)) { log_err("Unable to read template length\n"); - return -1; + goto out; } if (entry.template_buf_len < entry.template_len) { @@ -1531,7 +1531,7 @@ static int ima_measurement(const char *file) if (!fread(entry.template, entry.template_len, 1, fp)) { log_err("Unable to read template\n"); - return -1; + goto out; } if (validate) @@ -1543,8 +1543,6 @@ static int ima_measurement(const char *file) ima_ng_show(&entry); } - fclose(fp); - tpm_pcr_read(10, pcr10, sizeof(pcr10)); log_info("PCRAgg: "); @@ -1555,10 +1553,14 @@ static int ima_measurement(const char *file) if (memcmp(pcr, pcr10, sizeof(pcr))) { log_err("PCRAgg does not match PCR-10\n"); - return -1; + goto out; } - return 0; + err = 0; +out: + fclose(fp); + + return err; } static int cmd_ima_measurement(struct command *cmd) -- cgit v1.2.3 From 9bc24ee556e554cf75a07ffc1ff26c6773992cda Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 24 Jun 2014 15:40:58 +0300 Subject: Use defined xattr types Signed-off-by: Dmitry Kasatkin --- src/evmctl.c | 12 ++++++++---- src/imaevm.h | 7 +++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/evmctl.c b/src/evmctl.c index 4d542d3..3644b41 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -626,7 +626,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash) static int sign_evm(const char *file, const char *key) { unsigned char hash[20]; - unsigned char sig[1024] = "\x03"; + unsigned char sig[1024]; int len, err; len = calc_evm_hash(file, hash); @@ -638,6 +638,7 @@ static int sign_evm(const char *file, const char *key) return len; if (xattr) { + sig[0] = EVM_IMA_XATTR_DIGSIG; err = lsetxattr(file, "security.evm", sig, len + 1, 0); if (err < 0) { log_err("setxattr failed: %s\n", file); @@ -650,9 +651,10 @@ static int sign_evm(const char *file, const char *key) static int hash_ima(const char *file) { - unsigned char hash[65] = "\x01"; /* MAX hash size + 1 */ + unsigned char hash[65]; /* MAX hash size + 1 */ int len, err; + hash[0] = IMA_XATTR_DIGEST; len = ima_calc_hash(file, hash + 1); if (len <= 1) return len; @@ -690,7 +692,7 @@ static int cmd_hash_ima(struct command *cmd) static int sign_ima(const char *file, const char *key) { unsigned char hash[64]; - unsigned char sig[1024] = "\x03"; + unsigned char sig[1024]; int len, err; len = ima_calc_hash(file, hash); @@ -703,6 +705,7 @@ static int sign_ima(const char *file, const char *key) /* add header */ len++; + sig[0] = EVM_IMA_XATTR_DIGSIG; if (sigfile) bin2file(file, "sig", sig, len); @@ -1151,7 +1154,7 @@ out: static int hmac_evm(const char *file, const char *key) { unsigned char hash[20]; - unsigned char sig[1024] = "\x02"; + unsigned char sig[1024]; int len, err; len = calc_evm_hmac(file, key, hash); @@ -1163,6 +1166,7 @@ static int hmac_evm(const char *file, const char *key) memcpy(sig + 1, hash, len); if (xattr) { + sig[0] = EVM_XATTR_HMAC; err = lsetxattr(file, "security.evm", sig, len + 1, 0); if (err < 0) { log_err("setxattr failed: %s\n", file); diff --git a/src/imaevm.h b/src/imaevm.h index 7dc2651..7e784ec 100644 --- a/src/imaevm.h +++ b/src/imaevm.h @@ -84,6 +84,13 @@ #define __packed __attribute__((packed)) +enum evm_ima_xattr_type { + IMA_XATTR_DIGEST = 0x01, + EVM_XATTR_HMAC, + EVM_IMA_XATTR_DIGSIG, + IMA_XATTR_DIGEST_NG, +}; + struct h_misc { unsigned long ino; uint32_t generation; -- cgit v1.2.3 From c95806202313cebacd4403480f8bc6da1cabd2c5 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 24 Jun 2014 15:52:16 +0300 Subject: Fix setting correct hash header 'ima_hash -a sha256' and 'sign -a sha256 --imahash' commands did set incorrect xattr header for hash algos other than sha1. Fix it. Signed-off-by: Dmitry Kasatkin --- src/evmctl.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/evmctl.c b/src/evmctl.c index 3644b41..1a7cda1 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -651,22 +651,33 @@ static int sign_evm(const char *file, const char *key) static int hash_ima(const char *file) { - unsigned char hash[65]; /* MAX hash size + 1 */ - int len, err; + unsigned char hash[66]; /* MAX hash size + 2 */ + int len, err, offset; + int algo = get_hash_algo(params.hash_algo); + + if (algo > PKEY_HASH_SHA1) { + hash[0] = IMA_XATTR_DIGEST_NG; + hash[1] = algo; + offset = 2; + } else { + hash[0] = IMA_XATTR_DIGEST; + offset = 1; + } - hash[0] = IMA_XATTR_DIGEST; - len = ima_calc_hash(file, hash + 1); + len = ima_calc_hash(file, hash + offset); if (len <= 1) return len; + len += offset; + if (params.verbose >= LOG_INFO) log_info("hash: "); if (sigdump || params.verbose >= LOG_INFO) - dump(hash, len + 1); + dump(hash, len); if (xattr) { - err = lsetxattr(file, "security.ima", hash, len + 1, 0); + err = lsetxattr(file, "security.ima", hash, len, 0); if (err < 0) { log_err("setxattr failed: %s\n", file); return err; -- cgit v1.2.3 From 02594d3003dc4b2dad45581a2edc577d810e4b99 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 13 Jun 2014 15:17:21 +0300 Subject: Switch to HMAC attribute mask Signed-off-by: Dmitry Kasatkin --- src/evmctl.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/evmctl.c b/src/evmctl.c index 1a7cda1..d4f9cdb 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -90,12 +90,16 @@ static int digsig; static char *keypass; static int sigfile; static int x509 = 1; -static char *uuid_str = "+"; +static char *uuid_str; static char *search_type; static int recursive; static int msize; static dev_t fs_dev; +#define HMAC_FLAG_UUID 0x0001 +#define HMAC_FLAG_UUID_SET 0x0002 +static unsigned long hmac_flags = HMAC_FLAG_UUID; + typedef int (*find_cb_t)(const char *path); static int find(const char *path, int dts, find_cb_t func); @@ -470,7 +474,7 @@ static int get_uuid(struct stat *st, char *uuid) FILE *fp; size_t len; - if (uuid_str[0] != '+') + if (hmac_flags & HMAC_FLAG_UUID_SET) return pack_uuid(uuid_str, uuid); dev = st->st_dev; @@ -602,7 +606,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash) return 1; } - if (*uuid_str != '-') { + if (hmac_flags & HMAC_FLAG_UUID) { err = get_uuid(&st, uuid); if (err) return -1; @@ -1758,7 +1762,11 @@ int main(int argc, char *argv[]) xattr = 0; break; case 'u': - uuid_str = optarg ?: "+"; + uuid_str = optarg; + if (uuid_str) + hmac_flags |= HMAC_FLAG_UUID_SET; + else + hmac_flags &= ~HMAC_FLAG_UUID; break; case '1': x509 = 0; -- cgit v1.2.3 From 0636083acdf7fd7cafa269e6149c20f40d5868a1 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 25 Jun 2014 18:12:19 +0300 Subject: Remove code duplication Signed-off-by: Dmitry Kasatkin --- src/evmctl.c | 47 +++++++++++++---------------------------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/src/evmctl.c b/src/evmctl.c index d4f9cdb..4601eab 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -84,6 +84,7 @@ struct command { static int g_argc; static char **g_argv; static int xattr = 1; +static bool check_xattr; static int sigdump; static int digest; static int digsig; @@ -749,6 +750,8 @@ static int get_file_type(const char *path, const char *search_type) dts |= DIR_MASK; break; case 's': dts |= BLK_MASK | CHR_MASK | LNK_MASK; break; + case 'x': + check_xattr = true; break; case 'm': /* stay within the same filesystem*/ err = lstat(path, &st); @@ -1227,7 +1230,7 @@ static int ima_fix(const char *path) log_info("%s\n", path); - if (xattr) { + if (check_xattr) { /* re-measuring takes a time * in some cases we can skip labeling if xattrs exists */ @@ -1312,7 +1315,6 @@ static int cmd_ima_fix(struct command *cmd) { char *path = g_argv[optind++]; int err, dts = REG_MASK; /* only regular files by default */ - struct stat st; if (!path) { log_err("Parameters missing\n"); @@ -1320,41 +1322,18 @@ static int cmd_ima_fix(struct command *cmd) return -1; } - xattr = 0; /* do not check xattrs, fix everything */ - - if (search_type) { - int i; - - dts = 0; - for (i = 0; search_type[i]; i++) { - switch (search_type[i]) { - case 'f': - dts |= REG_MASK; break; - case 'd': - dts |= DIR_MASK; break; - case 's': - dts |= BLK_MASK | CHR_MASK | LNK_MASK; break; - case 'x': - /* check xattrs */ - xattr = 1; break; - case 'm': - /* stay within the same filesystem*/ - err = lstat(path, &st); - if (err < 0) { - log_err("stat() failed\n"); - return err; - } - fs_dev = st.st_dev; /* filesystem to start from */ - break; - } + if (recursive) { + if (search_type) { + dts = get_file_type(path, search_type); + if (dts < 0) + return dts; } + err = find(path, dts, ima_fix); + } else { + err = ima_fix(path); } - err = find(path, dts, ima_fix); - if (err) - return err; - - return 0; + return err; } -- cgit v1.2.3 From 29adc34d356dab6a5022c6ec6c443b0b9e4d641c Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 13 Jun 2014 14:39:48 +0300 Subject: Remove local ioctl definitions and use Use standard flags, supported by ext2/3/4 Signed-off-by: Dmitry Kasatkin --- src/evmctl.c | 4 ++-- src/imaevm.h | 9 +-------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/evmctl.c b/src/evmctl.c index 4601eab..1e37119 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -529,7 +529,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash) log_err("Unable to open %s\n", file); return -1; } - if (ioctl(fd, EXT34_IOC_GETVERSION, &generation)) { + if (ioctl(fd, FS_IOC_GETVERSION, &generation)) { log_err("ioctl() failed\n"); return -1; } @@ -1082,7 +1082,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h log_err("Unable to open %s\n", file); goto out; } - if (ioctl(fd, EXT34_IOC_GETVERSION, &generation)) { + if (ioctl(fd, FS_IOC_GETVERSION, &generation)) { log_err("ioctl() failed\n"); goto out; } diff --git a/src/imaevm.h b/src/imaevm.h index 7e784ec..02fe850 100644 --- a/src/imaevm.h +++ b/src/imaevm.h @@ -41,6 +41,7 @@ #ifndef _LIBIMAEVM_H #define _LIBIMAEVM_H +#include #include #include #include @@ -74,14 +75,6 @@ #define DATA_SIZE 4096 #define SHA1_HASH_LEN 20 -#define EXT2_IOC_GETVERSION _IOR('v', 1, long) -#define EXT34_IOC_GETVERSION _IOR('f', 3, long) - -#define FS_IOC_GETFLAGS _IOR('f', 1, long) -#define FS_IOC_SETFLAGS _IOW('f', 2, long) -#define FS_IOC32_GETFLAGS _IOR('f', 1, int) -#define FS_IOC32_SETFLAGS _IOW('f', 2, int) - #define __packed __attribute__((packed)) enum evm_ima_xattr_type { -- cgit v1.2.3 From 8f04d131aceb0774a47739b21295915252c652e3 Mon Sep 17 00:00:00 2001 From: Fionnuala Gunter Date: Thu, 14 Aug 2014 13:29:06 -0500 Subject: Move sign hash functions to library This patch enables package managers, such as rpm, to include IMA signatures in packages. To do this, sign_hash and some helper functions were moved from evmctl to libimaevm. These functions used global variables that belong to evmctl, sigdump and keypass. The variable sigdump is a flag that file signatures should be printed to stdout, so the signature dump is now handled by functions that call sign_hash. The variable keypass is a passphrase for an encrypted key, so it was added to 'struct libevm_params'. v2: Uses 'struct libevm_params' to minimize sign_hash parameters v3: Export single sign_hash function that selects _v1 or _v2 internally based on params.x509. Moved parameter checks and explicitly return -1 for failures. Signed-off-by: Fionnuala Gunter Signed-off-by: Dmitry Kasatkin --- src/evmctl.c | 250 +++++------------------------------------------------- src/imaevm.h | 7 ++ src/libimaevm.c | 258 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 284 insertions(+), 231 deletions(-) diff --git a/src/evmctl.c b/src/evmctl.c index 1e37119..49329ac 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -52,7 +52,6 @@ #include #include #include -#include #include #include @@ -88,9 +87,7 @@ static bool check_xattr; static int sigdump; static int digest; static int digsig; -static char *keypass; static int sigfile; -static int x509 = 1; static char *uuid_str; static char *search_type; static int recursive; @@ -110,10 +107,6 @@ static int find(const char *path, int dts, find_cb_t func); #define CHR_MASK (1 << DT_CHR) #define BLK_MASK (1 << DT_BLK) -typedef int (*sign_hash_fn_t)(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig); - -static sign_hash_fn_t sign_hash; - struct command cmds[]; static void print_usage(struct command *cmd); @@ -169,215 +162,6 @@ static unsigned char *file2bin(const char *file, const char *ext, int *size) return data; } -/* - * Create binary key representation suitable for kernel - */ -static int key2bin(RSA *key, unsigned char *pub) -{ - int len, b, offset = 0; - struct pubkey_hdr *pkh = (struct pubkey_hdr *)pub; - - /* add key header */ - pkh->version = 1; - pkh->timestamp = 0; /* PEM has no timestamp?? */ - pkh->algo = PUBKEY_ALGO_RSA; - pkh->nmpi = 2; - - offset += sizeof(*pkh); - - len = BN_num_bytes(key->n); - b = BN_num_bits(key->n); - pub[offset++] = b >> 8; - pub[offset++] = b & 0xff; - BN_bn2bin(key->n, &pub[offset]); - offset += len; - - len = BN_num_bytes(key->e); - b = BN_num_bits(key->e); - pub[offset++] = b >> 8; - pub[offset++] = b & 0xff; - BN_bn2bin(key->e, &pub[offset]); - offset += len; - - return offset; -} - -static void calc_keyid_v1(uint8_t *keyid, char *str, const unsigned char *pkey, int len) -{ - uint8_t sha1[SHA_DIGEST_LENGTH]; - uint64_t id; - - SHA1(pkey, len, sha1); - - /* sha1[12 - 19] is exactly keyid from gpg file */ - memcpy(keyid, sha1 + 12, 8); - log_debug("keyid: "); - log_debug_dump(keyid, 8); - - id = __be64_to_cpup((__be64 *) keyid); - sprintf(str, "%llX", (unsigned long long)id); - log_info("keyid: %s\n", str); -} - -static void calc_keyid_v2(uint32_t *keyid, char *str, RSA *key) -{ - uint8_t sha1[SHA_DIGEST_LENGTH]; - unsigned char *pkey = NULL; - int len; - - len = i2d_RSAPublicKey(key, &pkey); - - SHA1(pkey, len, sha1); - - /* sha1[12 - 19] is exactly keyid from gpg file */ - memcpy(keyid, sha1 + 16, 4); - log_debug("keyid: "); - log_debug_dump(keyid, 4); - - sprintf(str, "%x", __be32_to_cpup(keyid)); - log_info("keyid: %s\n", str); - - free(pkey); -} - -static RSA *read_priv_key(const char *keyfile) -{ - FILE *fp; - RSA *key; - - fp = fopen(keyfile, "r"); - if (!fp) { - log_err("Unable to open keyfile %s\n", keyfile); - return NULL; - } - key = PEM_read_RSAPrivateKey(fp, NULL, NULL, keypass); - if (!key) - log_err("PEM_read_RSAPrivateKey() failed\n"); - - fclose(fp); - return key; -} - -static int get_hash_algo_v1(const char *algo) -{ - - if (!strcmp(algo, "sha1")) - return DIGEST_ALGO_SHA1; - else if (!strcmp(algo, "sha256")) - return DIGEST_ALGO_SHA256; - - return -1; -} - -static int sign_hash_v1(const char *hashalgo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig) -{ - int len = -1, hashalgo_idx; - SHA_CTX ctx; - unsigned char pub[1024]; - RSA *key; - char name[20]; - unsigned char sighash[20]; - struct signature_hdr *hdr = (struct signature_hdr *)sig; - uint16_t *blen; - - log_info("hash: "); - log_dump(hash, size); - - key = read_priv_key(keyfile); - if (!key) - return -1; - - /* now create a new hash */ - hdr->version = (uint8_t) DIGSIG_VERSION_1; - hdr->timestamp = time(NULL); - hdr->algo = PUBKEY_ALGO_RSA; - hashalgo_idx = get_hash_algo_v1(hashalgo); - if (hashalgo_idx < 0) { - log_err("Signature version 1 does not support hash algo %s\n", - hashalgo); - goto out; - } - hdr->hash = (uint8_t) hashalgo_idx; - - len = key2bin(key, pub); - calc_keyid_v1(hdr->keyid, name, pub, len); - - hdr->nmpi = 1; - - SHA1_Init(&ctx); - SHA1_Update(&ctx, hash, size); - SHA1_Update(&ctx, hdr, sizeof(*hdr)); - SHA1_Final(sighash, &ctx); - log_info("sighash: "); - log_dump(sighash, sizeof(sighash)); - - len = RSA_private_encrypt(sizeof(sighash), sighash, sig + sizeof(*hdr) + 2, key, RSA_PKCS1_PADDING); - if (len < 0) { - log_err("RSA_private_encrypt() failed: %d\n", len); - goto out; - } - - /* we add bit length of the signature to make it gnupg compatible */ - blen = (uint16_t *) (sig + sizeof(*hdr)); - *blen = __cpu_to_be16(len << 3); - len += sizeof(*hdr) + 2; - log_info("evm/ima signature: %d bytes\n", len); - if (sigdump || params.verbose >= LOG_INFO) - dump(sig, len); -out: - RSA_free(key); - return len; -} - -static int sign_hash_v2(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig) -{ - struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig; - int len = -1; - RSA *key; - char name[20]; - unsigned char *buf; - const struct RSA_ASN1_template *asn1; - - log_info("hash: "); - log_dump(hash, size); - - key = read_priv_key(keyfile); - if (!key) - return -1; - - hdr->version = (uint8_t) DIGSIG_VERSION_2; - hdr->hash_algo = get_hash_algo(algo); - - calc_keyid_v2(&hdr->keyid, name, key); - - asn1 = &RSA_ASN1_templates[hdr->hash_algo]; - - buf = malloc(size + asn1->size); - if (!buf) - goto out; - - memcpy(buf, asn1->data, asn1->size); - memcpy(buf + asn1->size, hash, size); - len = RSA_private_encrypt(size + asn1->size, buf, hdr->sig, - key, RSA_PKCS1_PADDING); - if (len < 0) { - log_err("RSA_private_encrypt() failed: %d\n", len); - goto out; - } - - /* we add bit length of the signature to make it gnupg compatible */ - hdr->sig_size = __cpu_to_be16(len); - len += sizeof(*hdr); - log_info("evm/ima signature: %d bytes\n", len); - if (sigdump || params.verbose >= LOG_INFO) - dump(sig, len); -out: - if (buf) - free(buf); - RSA_free(key); - return len; -} - static int find_xattr(const char *list, int list_size, const char *xattr) { int len; @@ -642,9 +426,15 @@ static int sign_evm(const char *file, const char *key) if (len <= 1) return len; + /* add header */ + len++; + sig[0] = EVM_IMA_XATTR_DIGSIG; + + if (sigdump || params.verbose >= LOG_INFO) + dump(sig, len); + if (xattr) { - sig[0] = EVM_IMA_XATTR_DIGSIG; - err = lsetxattr(file, "security.evm", sig, len + 1, 0); + err = lsetxattr(file, "security.evm", sig, len, 0); if (err < 0) { log_err("setxattr failed: %s\n", file); return err; @@ -723,6 +513,9 @@ static int sign_ima(const char *file, const char *key) len++; sig[0] = EVM_IMA_XATTR_DIGSIG; + if (sigdump || params.verbose >= LOG_INFO) + dump(sig, len); + if (sigfile) bin2file(file, "sig", sig, len); @@ -975,8 +768,8 @@ static int cmd_import(struct command *cmd) inkey = g_argv[optind++]; if (!inkey) { - inkey = x509 ? "/etc/keys/x509_evm.der" : - "/etc/keys/pubkey_evm.pem"; + inkey = params.x509 ? "/etc/keys/x509_evm.der" : + "/etc/keys/pubkey_evm.pem"; } else ring = g_argv[optind++]; @@ -1004,11 +797,11 @@ static int cmd_import(struct command *cmd) } } - key = read_pub_key(inkey, x509); + key = read_pub_key(inkey, params.x509); if (!key) return 1; - if (x509) { + if (params.x509) { pub = file2bin(inkey, NULL, &len); if (!pub) goto out; @@ -1020,7 +813,7 @@ static int cmd_import(struct command *cmd) log_info("Importing public key %s from file %s into keyring %d\n", name, inkey, id); - id = add_key(x509 ? "asymmetric" : "user", x509 ? NULL : name, pub, len, id); + id = add_key(params.x509 ? "asymmetric" : "user", params.x509 ? NULL : name, pub, len, id); if (id < 0) { log_err("add_key failed\n"); err = id; @@ -1028,7 +821,7 @@ static int cmd_import(struct command *cmd) log_info("keyid: %d\n", id); printf("%d\n", id); } - if (x509) + if (params.x509) free(pub); out: RSA_free(key); @@ -1734,7 +1527,7 @@ int main(int argc, char *argv[]) params.hash_algo = optarg; break; case 'p': - keypass = optarg; + params.keypass = optarg; break; case 'f': sigfile = 1; @@ -1748,7 +1541,7 @@ int main(int argc, char *argv[]) hmac_flags &= ~HMAC_FLAG_UUID; break; case '1': - x509 = 0; + params.x509 = 0; break; case 'k': params.keyfile = optarg; @@ -1773,11 +1566,6 @@ int main(int argc, char *argv[]) } } - if (x509) - sign_hash = sign_hash_v2; - else - sign_hash = sign_hash_v1; - OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); diff --git a/src/imaevm.h b/src/imaevm.h index 02fe850..72de47a 100644 --- a/src/imaevm.h +++ b/src/imaevm.h @@ -170,8 +170,10 @@ typedef int (*verify_hash_fn_t)(const unsigned char *hash, int size, unsigned ch struct libevm_params { int verbose; + int x509; const char *hash_algo; char *keyfile; + char *keypass; }; struct RSA_ASN1_template { @@ -189,6 +191,11 @@ int ima_calc_hash(const char *file, uint8_t *hash); int get_hash_algo(const char *algo); RSA *read_pub_key(const char *keyfile, int x509); +void calc_keyid_v1(uint8_t *keyid, char *str, const unsigned char *pkey, int len); +void calc_keyid_v2(uint32_t *keyid, char *str, RSA *key); +int key2bin(RSA *key, unsigned char *pub); + +int sign_hash(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig); int verify_hash(const unsigned char *hash, int size, unsigned char *sig, int siglen); int ima_verify_signature(const char *file, unsigned char *sig, int siglen); diff --git a/src/libimaevm.c b/src/libimaevm.c index 7f14cc5..4d32e85 100644 --- a/src/libimaevm.c +++ b/src/libimaevm.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -125,6 +126,7 @@ const struct RSA_ASN1_template RSA_ASN1_templates[PKEY_HASH__LAST] = { struct libevm_params params = { .verbose = LOG_INFO - 1, + .x509 = 1, .hash_algo = "sha1", }; @@ -533,3 +535,259 @@ int ima_verify_signature(const char *file, unsigned char *sig, int siglen) return verify_hash(hash, hashlen, sig + 1, siglen - 1); } + +/* + * Create binary key representation suitable for kernel + */ +int key2bin(RSA *key, unsigned char *pub) +{ + int len, b, offset = 0; + struct pubkey_hdr *pkh = (struct pubkey_hdr *)pub; + + /* add key header */ + pkh->version = 1; + pkh->timestamp = 0; /* PEM has no timestamp?? */ + pkh->algo = PUBKEY_ALGO_RSA; + pkh->nmpi = 2; + + offset += sizeof(*pkh); + + len = BN_num_bytes(key->n); + b = BN_num_bits(key->n); + pub[offset++] = b >> 8; + pub[offset++] = b & 0xff; + BN_bn2bin(key->n, &pub[offset]); + offset += len; + + len = BN_num_bytes(key->e); + b = BN_num_bits(key->e); + pub[offset++] = b >> 8; + pub[offset++] = b & 0xff; + BN_bn2bin(key->e, &pub[offset]); + offset += len; + + return offset; +} + +void calc_keyid_v1(uint8_t *keyid, char *str, const unsigned char *pkey, int len) +{ + uint8_t sha1[SHA_DIGEST_LENGTH]; + uint64_t id; + + SHA1(pkey, len, sha1); + + /* sha1[12 - 19] is exactly keyid from gpg file */ + memcpy(keyid, sha1 + 12, 8); + log_debug("keyid: "); + log_debug_dump(keyid, 8); + + id = __be64_to_cpup((__be64 *) keyid); + sprintf(str, "%llX", (unsigned long long)id); + log_info("keyid: %s\n", str); +} + +void calc_keyid_v2(uint32_t *keyid, char *str, RSA *key) +{ + uint8_t sha1[SHA_DIGEST_LENGTH]; + unsigned char *pkey = NULL; + int len; + + len = i2d_RSAPublicKey(key, &pkey); + + SHA1(pkey, len, sha1); + + /* sha1[12 - 19] is exactly keyid from gpg file */ + memcpy(keyid, sha1 + 16, 4); + log_debug("keyid: "); + log_debug_dump(keyid, 4); + + sprintf(str, "%x", __be32_to_cpup(keyid)); + log_info("keyid: %s\n", str); + + free(pkey); +} + +static RSA *read_priv_key(const char *keyfile, char *keypass) +{ + FILE *fp; + RSA *key; + + fp = fopen(keyfile, "r"); + if (!fp) { + log_err("Unable to open keyfile %s\n", keyfile); + return NULL; + } + key = PEM_read_RSAPrivateKey(fp, NULL, NULL, keypass); + if (!key) + log_err("PEM_read_RSAPrivateKey() failed\n"); + + fclose(fp); + return key; +} + +static int get_hash_algo_v1(const char *algo) +{ + + if (!strcmp(algo, "sha1")) + return DIGEST_ALGO_SHA1; + else if (!strcmp(algo, "sha256")) + return DIGEST_ALGO_SHA256; + + return -1; +} + +int sign_hash_v1(const char *hashalgo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig) +{ + int len = -1, hashalgo_idx; + SHA_CTX ctx; + unsigned char pub[1024]; + RSA *key; + char name[20]; + unsigned char sighash[20]; + struct signature_hdr *hdr; + uint16_t *blen; + + if (!hash) { + log_err("sign_hash_v1: hash is null\n"); + return -1; + } + + if (size < 0) { + log_err("sign_hash_v1: size is negative: %d\n", size); + return -1; + } + + if (!hashalgo) { + log_err("sign_hash_v1: hashalgo is null\n"); + return -1; + } + + if (!sig) { + log_err("sign_hash_v1: sig is null\n"); + return -1; + } + + log_info("hash: "); + log_dump(hash, size); + + key = read_priv_key(keyfile, params.keypass); + if (!key) { + return -1; + } + + hdr = (struct signature_hdr *)sig; + + /* now create a new hash */ + hdr->version = (uint8_t) DIGSIG_VERSION_1; + hdr->timestamp = time(NULL); + hdr->algo = PUBKEY_ALGO_RSA; + hashalgo_idx = get_hash_algo_v1(hashalgo); + if (hashalgo_idx < 0) { + log_err("Signature version 1 does not support hash algo %s\n", + hashalgo); + goto out; + } + hdr->hash = (uint8_t) hashalgo_idx; + + len = key2bin(key, pub); + calc_keyid_v1(hdr->keyid, name, pub, len); + + hdr->nmpi = 1; + + SHA1_Init(&ctx); + SHA1_Update(&ctx, hash, size); + SHA1_Update(&ctx, hdr, sizeof(*hdr)); + SHA1_Final(sighash, &ctx); + log_info("sighash: "); + log_dump(sighash, sizeof(sighash)); + + len = RSA_private_encrypt(sizeof(sighash), sighash, sig + sizeof(*hdr) + 2, key, RSA_PKCS1_PADDING); + if (len < 0) { + log_err("RSA_private_encrypt() failed: %d\n", len); + goto out; + } + + /* we add bit length of the signature to make it gnupg compatible */ + blen = (uint16_t *) (sig + sizeof(*hdr)); + *blen = __cpu_to_be16(len << 3); + len += sizeof(*hdr) + 2; + log_info("evm/ima signature: %d bytes\n", len); +out: + RSA_free(key); + return len; +} + +int sign_hash_v2(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig) +{ + struct signature_v2_hdr *hdr; + int len = -1; + RSA *key; + char name[20]; + unsigned char *buf; + const struct RSA_ASN1_template *asn1; + + if (!hash) { + log_err("sign_hash_v2: hash is null\n"); + return -1; + } + + if (size < 0) { + log_err("sign_hash_v2: size is negative: %d\n", size); + return -1; + } + + if (!sig) { + log_err("sign_hash_v2: sig is null\n"); + return -1; + } + + if (!algo) { + log_err("sign_hash_v2: algo is null\n"); + return -1; + } + + log_info("hash: "); + log_dump(hash, size); + + key = read_priv_key(keyfile, params.keypass); + if (!key) + return -1; + + hdr = (struct signature_v2_hdr *)sig; + hdr->version = (uint8_t) DIGSIG_VERSION_2; + + hdr->hash_algo = get_hash_algo(algo); + + calc_keyid_v2(&hdr->keyid, name, key); + + asn1 = &RSA_ASN1_templates[hdr->hash_algo]; + + buf = malloc(size + asn1->size); + if (!buf) + goto out; + + memcpy(buf, asn1->data, asn1->size); + memcpy(buf + asn1->size, hash, size); + len = RSA_private_encrypt(size + asn1->size, buf, hdr->sig, + key, RSA_PKCS1_PADDING); + if (len < 0) { + log_err("RSA_private_encrypt() failed: %d\n", len); + goto out; + } + + /* we add bit length of the signature to make it gnupg compatible */ + hdr->sig_size = __cpu_to_be16(len); + len += sizeof(*hdr); + log_info("evm/ima signature: %d bytes\n", len); +out: + if (buf) + free(buf); + RSA_free(key); + return len; +} + +int sign_hash(const char *hashalgo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig) +{ + return params.x509 ? sign_hash_v2(hashalgo, hash, size, keyfile, sig) : + sign_hash_v1(hashalgo, hash, size, keyfile, sig); +} -- cgit v1.2.3 From dede7cf850b23b4bb36cb370647d3f03c36db310 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 15 Aug 2014 10:30:06 +0300 Subject: Fix checkpatch errors Signed-off-by: Dmitry Kasatkin --- src/evmctl.c | 15 +++++++++++++-- src/libimaevm.c | 6 +++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/evmctl.c b/src/evmctl.c index 49329ac..da852fe 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -309,6 +309,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash) /* we cannot at the momement to get generation of special files.. * kernel API does not support it */ int fd = open(file, 0); + if (fd < 0) { log_err("Unable to open %s\n", file); return -1; @@ -358,6 +359,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash) if (msize == 0) { struct h_misc *hmac = (struct h_misc *)&hmac_misc; + hmac_size = sizeof(*hmac); hmac->ino = st.st_ino; hmac->generation = generation; @@ -366,6 +368,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash) hmac->mode = st.st_mode; } else if (msize == 64) { struct h_misc_64 *hmac = (struct h_misc_64 *)&hmac_misc; + hmac_size = sizeof(*hmac); hmac->ino = st.st_ino; hmac->generation = generation; @@ -374,6 +377,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash) hmac->mode = st.st_mode; } else { struct h_misc_32 *hmac = (struct h_misc_32 *)&hmac_misc; + hmac_size = sizeof(*hmac); hmac->ino = st.st_ino; hmac->generation = generation; @@ -735,8 +739,8 @@ static int verify_ima(const char *file) } if (sigfile) { - void *tmp; - tmp = file2bin(file, "sig", &len); + void *tmp = file2bin(file, "sig", &len); + memcpy(sig, tmp, len); free(tmp); } @@ -778,6 +782,7 @@ static int cmd_import(struct command *cmd) if (ring) { if (ring[0] != '@') { int base = 10; + if (ring[0] == '0' && ring[1] == 'x') base = 16; id = strtoul(ring, NULL, base); @@ -871,6 +876,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h /* we cannot at the momement to get generation of special files.. * kernel API does not support it */ int fd = open(file, 0); + if (fd < 0) { log_err("Unable to open %s\n", file); goto out; @@ -920,6 +926,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h if (msize == 0) { struct h_misc *hmac = (struct h_misc *)&hmac_misc; + hmac_size = sizeof(*hmac); hmac->ino = st.st_ino; hmac->generation = generation; @@ -928,6 +935,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h hmac->mode = st.st_mode; } else if (msize == 64) { struct h_misc_64 *hmac = (struct h_misc_64 *)&hmac_misc; + hmac_size = sizeof(*hmac); hmac->ino = st.st_ino; hmac->generation = generation; @@ -936,6 +944,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h hmac->mode = st.st_mode; } else { struct h_misc_32 *hmac = (struct h_misc_32 *)&hmac_misc; + hmac_size = sizeof(*hmac); hmac->ino = st.st_ino; hmac->generation = generation; @@ -1062,6 +1071,7 @@ static int find(const char *path, int dts, find_cb_t func) if (fs_dev) { struct stat st; int err = lstat(path, &st); + if (err < 0) { log_err("stat() failed\n"); return err; @@ -1576,6 +1586,7 @@ int main(int argc, char *argv[]) if (err) { unsigned long error; + if (errno) log_err("errno: %s (%d)\n", strerror(errno), errno); for (;;) { diff --git a/src/libimaevm.c b/src/libimaevm.c index 4d32e85..e24a589 100644 --- a/src/libimaevm.c +++ b/src/libimaevm.c @@ -257,15 +257,16 @@ static int add_dev_hash(struct stat *st, EVP_MD_CTX *ctx) uint32_t dev = st->st_rdev; unsigned major = (dev & 0xfff00) >> 8; unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00); + log_info("device: %u:%u\n", major, minor); return !EVP_DigestUpdate(ctx, &dev, sizeof(dev)); } int ima_calc_hash(const char *file, uint8_t *hash) { + const EVP_MD *md; struct stat st; EVP_MD_CTX ctx; - const EVP_MD *md; unsigned int mdlen; int err; @@ -671,9 +672,8 @@ int sign_hash_v1(const char *hashalgo, const unsigned char *hash, int size, cons log_dump(hash, size); key = read_priv_key(keyfile, params.keypass); - if (!key) { + if (!key) return -1; - } hdr = (struct signature_hdr *)sig; -- cgit v1.2.3 From d5891ad96d4c1af1c7b04be634c3cde26713443c Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 10 Sep 2014 16:27:38 +0300 Subject: Include example scripts to distribution and installation Signed-off-by: Dmitry Kasatkin --- Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 73d3a4d..36c6b1d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,7 @@ SUBDIRS = src -EXTRA_DIST = autogen.sh +doc_DATA = examples/ima-genkey-self.sh examples/ima-genkey.sh examples/ima-gen-local-ca.sh +EXTRA_DIST = autogen.sh $(doc_DATA) ACLOCAL_AMFLAGS = -I m4 -- cgit v1.2.3 From 635288f70f81dbbaea791c72f6dac19322d4dc1c Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 10 Sep 2014 13:08:50 +0300 Subject: Update README to produce initial evmctl.1 man page Update README with additional information to produce initial evmctl.1 man page. Sligtly reformat it for that purpose as well. Requires asciidoc, xslproc, docbook-xsl packages to build man page. Signed-off-by: Dmitry Kasatkin --- Makefile.am | 18 +++++++ README | 160 +++++++++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 133 insertions(+), 45 deletions(-) diff --git a/Makefile.am b/Makefile.am index 36c6b1d..9dde06e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,8 +1,12 @@ SUBDIRS = src +# does work on Ubuntu, but on Fedora different path to XSL stylesheets +#man_MANS = evmctl.1 doc_DATA = examples/ima-genkey-self.sh examples/ima-genkey.sh examples/ima-gen-local-ca.sh EXTRA_DIST = autogen.sh $(doc_DATA) +CLEANFILES = *.html *.1 *.xsl + ACLOCAL_AMFLAGS = -I m4 SRCS = $(HOME)/rpmbuild/SOURCES @@ -20,4 +24,18 @@ rpm: $(tarname) cp $(tarname) $(SRCS)/ rpmbuild -ba --nodeps $(SPEC) +# requires asciidoc, xslproc, docbook-xsl +MANPAGE_DOCBOOK_XSL = /usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl + +evmctl.1.html: README + @asciidoc -o $@ $< + +evmctl.1.xsl: README + @asciidoc -d manpage -b docbook -o $@ $< + +evmctl.1: evmctl.1.xsl + @xsltproc --nonet -o $@ $(MANPAGE_DOCBOOK_XSL) $< + +doc: evmctl.1.html evmctl.1 + .PHONY: $(tarname) diff --git a/README b/README index f460ec7..1a22c12 100644 --- a/README +++ b/README @@ -1,12 +1,61 @@ -ima-evm-utils - IMA/EVM signing utility -========================================= +EVMCTL(1) +========= -Contents: +NAME +---- - 1. Key and signature formats - 2. Key generation - 3. Initialization - 4. Signing +evmctl - IMA/EVM signing utility + + +SYNOPSIS +-------- + +evmctl [options] [OPTIONS] + + +DESCRIPTION +----------- + +The evmctl utility can be used for producing and verifying digital signatures, +which are used by Linux kernel integrity subsystem (IMA/EVM). It can be also +used to import keys into the kernel keyring. + +COMMANDS +-------- + + help + import [--rsa] pubkey keyring + sign [-r] [--imahash | --imasig ] [--key key] [--pass password] file + verify file + ima_sign [--sigfile] [--key key] [--pass password] file + ima_verify file + ima_hash file + ima_measurement file + ima_fix [-t fdsxm] path + sign_hash [--key key] [--pass password] + hmac [--imahash | --imasig ] file + + +OPTIONS +------- + + -a, --hashalgo sha1 (default), sha224, sha256, sha384, sha512 + -s, --imasig also make IMA signature + -d, --imahash also make IMA hash + -f, --sigfile store IMA signature in .sig file instead of xattr + -1, --rsa signing key is in RSA DER format (signing v1) + -k, --key path to signing key (default: /etc/keys/{privkey,pubkey}_evm.pem) + -p, --pass password for encrypted signing key + -u, --uuid use file system UUID in HMAC calculation (EVM v2) + -t, --type file types to fix 'fdsxm' (f: file, d: directory, s: block/char/symlink) + x - skip fixing if both ima and evm xattrs exist (use with caution) + m - stay on the same filesystem (like 'find -xdev') + -n print result to stdout instead of setting xattr + -r, --recursive recurse into directories (sign) + --m32 force signature for 32 bit target system + --m64 force signature for 32 bit target system + -v increase verbosity level + -h, --help display this help and exit Key and signature formats @@ -26,24 +75,24 @@ Use '--rsa' or '-1' parameter to use old signature format and API. Key generation -------------- -Generate private key in plain text format +Generate private key in plain text format: - $ openssl genrsa -out privkey_evm.pem 1024 + openssl genrsa -out privkey_evm.pem 1024 -Generate encrypted private key +Generate encrypted private key: - $ openssl genrsa -des3 -out privkey_evm.pem 1024 + openssl genrsa -des3 -out privkey_evm.pem 1024 -Make encrypted private key from unencrypted +Make encrypted private key from unencrypted: - $ openssl rsa -in /etc/keys/privkey_evm.pem -out privkey_evm_enc.pem -des3 + openssl rsa -in /etc/keys/privkey_evm.pem -out privkey_evm_enc.pem -des3 -Generate self-signed X509 certificate and private key for using kernel asymmetric -keys support +Generate self-signed X509 certificate and private key for using kernel +asymmetric keys support: - $ openssl req -new -nodes -utf8 -sha1 -days 36500 -batch \ - -x509 -config x509_evm.genkey \ - -outform DER -out x509_evm.der -keyout privkey_evm.pem + openssl req -new -nodes -utf8 -sha1 -days 36500 -batch \ + -x509 -config x509_evm.genkey \ + -outform DER -out x509_evm.der -keyout privkey_evm.pem Configuration file x509_evm.genkey: @@ -68,28 +117,28 @@ Configuration file x509_evm.genkey: # EOF -Get public key +Get public key: - $ openssl rsa -pubout -in privkey_evm.pem -out pubkey_evm.pem + openssl rsa -pubout -in privkey_evm.pem -out pubkey_evm.pem -Copy keys to /etc/keys +Copy keys to /etc/keys: - $ cp pubkey_evm.pem /etc/keys - $ scp pubkey_evm.pem target:/etc/keys + cp pubkey_evm.pem /etc/keys + scp pubkey_evm.pem target:/etc/keys or - $ cp x509_evm.pem /etc/keys - $ scp x509_evm.pem target:/etc/keys + cp x509_evm.pem /etc/keys + scp x509_evm.pem target:/etc/keys -Generation of EVM keys +Generate EVM keys: - $ # create and save the kernel master key (user type) - $ keyctl add user kmk "`dd if=/dev/urandom bs=1 count=32 2>/dev/null`" @u - $ keyctl pipe `keyctl search @u user kmk` > /etc/keys/kmk - $ # create the EVM encrypted key - $ keyctl add encrypted evm-key "new user:kmk 32" @u - $ keyctl pipe `keyctl search @u encrypted evm-key` >/etc/keys/evm-key + # create and save the kernel master key (user type) + keyctl add user kmk "`dd if=/dev/urandom bs=1 count=32 2>/dev/null`" @u + keyctl pipe `keyctl search @u user kmk` > /etc/keys/kmk + # create the EVM encrypted key + keyctl add encrypted evm-key "new user:kmk 32" @u + keyctl pipe `keyctl search @u encrypted evm-key` >/etc/keys/evm-key Initialization @@ -117,10 +166,10 @@ Here is an example script /etc/initramfs-tools/scripts/local-top/ima.sh echo "1" > /sys/kernel/security/evm -Import X509 certificate into the kernel keyring (since kernel 3.9?) +Import X509 certificate into the kernel keyring (since kernel 3.9?): - $ evmctl import /etc/keys/x509_evm.der `keyctl search @u keyring _ima` - $ evmctl import /etc/keys/x509_evm.der `keyctl search @u keyring _evm` + evmctl import /etc/keys/x509_evm.der `keyctl search @u keyring _ima` + evmctl import /etc/keys/x509_evm.der `keyctl search @u keyring _evm` Signing @@ -133,23 +182,44 @@ Default X509 certificate: /etc/keys/x509_evm.der Signing for using old RSA format is done using '-1' or '--rsa' parameter. Signing for using old EVM HMAC format is done using '-u-' or '--uuid=-' parameter. -Sign file with EVM signature and use hash value for IMA - common case +Sign file with EVM signature and use hash value for IMA - common case: - $ evmctl sign [-u] [-1] --imahash test.txt + evmctl sign [-u] [-1] --imahash test.txt -Sign file with both IMA and EVM signatures - for immutable files +Sign file with both IMA and EVM signatures - for immutable files: - $ evmctl sign [-u] [-1] --imasig test.txt + evmctl sign [-u] [-1] --imasig test.txt: -Sign file with IMA signature - for immutable files +Sign file with IMA signature - for immutable files: - $ evmctl ima_sign [-1] test.txt + evmctl ima_sign [-1] test.txt -Label whole filesystem with EVM signatures +Label whole filesystem with EVM signatures: - $ find / \( -fstype rootfs -o -fstype ext4 \) -exec evmctl sign [-u] [-1] --imahash '{}' \; + find / \( -fstype rootfs -o -fstype ext4 \) -exec evmctl sign [-u] [-1] --imahash '{}' \; -Label filesystem in fix mode - kernel sets correct values to IMA and EVM xattrs +Label filesystem in fix mode - kernel sets correct values to IMA and EVM xattrs: + + find / \( -fstype rootfs -o -fstype ext4 \) -exec sh -c "< '{}'" \; + + +AUTHOR +------ + +Written by Dmitry Kasatkin, + + +RESOURCES +--------- + +http://sourceforge.net/p/linux-ima/wiki/Home + +http://sourceforge.net/p/linux-ima/ima-evm-utils + + +COPYING +------- - $ find / \( -fstype rootfs -o -fstype ext4 \) -exec sh -c "< '{}'" \; +Copyright \(C) 2012 - 2014 Dmitry Kasatkin. Free use of this software is granted under +the terms of the GNU Public License (GPL). -- cgit v1.2.3 From 14a90aa87cd65d748eaee4b726d7d6df0ef02c7f Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 10 Sep 2014 18:09:05 +0300 Subject: Make evmctl.1 as part of distribution and release Do not require to re-build man file at the build process. It will require less build dependencies. Signed-off-by: Dmitry Kasatkin --- Makefile.am | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Makefile.am b/Makefile.am index 9dde06e..06ebf59 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,11 +1,10 @@ SUBDIRS = src -# does work on Ubuntu, but on Fedora different path to XSL stylesheets -#man_MANS = evmctl.1 +dist_man_MANS = evmctl.1 doc_DATA = examples/ima-genkey-self.sh examples/ima-genkey.sh examples/ima-gen-local-ca.sh EXTRA_DIST = autogen.sh $(doc_DATA) -CLEANFILES = *.html *.1 *.xsl +CLEANFILES = *.html *.xsl ACLOCAL_AMFLAGS = -I m4 @@ -30,12 +29,14 @@ MANPAGE_DOCBOOK_XSL = /usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/doc evmctl.1.html: README @asciidoc -o $@ $< -evmctl.1.xsl: README - @asciidoc -d manpage -b docbook -o $@ $< +evmctl.1: + asciidoc -d manpage -b docbook -o evmctl.1.xsl README + xsltproc --nonet -o $@ $(MANPAGE_DOCBOOK_XSL) evmctl.1.xsl + rm -f evmctl.1.xsl -evmctl.1: evmctl.1.xsl - @xsltproc --nonet -o $@ $(MANPAGE_DOCBOOK_XSL) $< +rmman: + rm -f evmctl.1 -doc: evmctl.1.html evmctl.1 +doc: evmctl.1.html rmman evmctl.1 .PHONY: $(tarname) -- cgit v1.2.3 From 47d3048ce18a8b5cfa2633d91c6b7c089eaac73a Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 11 Sep 2014 16:05:55 +0300 Subject: Make error and help messages more understandable Signed-off-by: Dmitry Kasatkin --- src/evmctl.c | 75 ++++++++++++++++++++++++++++----------------------------- src/libimaevm.c | 12 ++++----- 2 files changed, 43 insertions(+), 44 deletions(-) diff --git a/src/evmctl.c b/src/evmctl.c index da852fe..1af04a4 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -125,7 +125,7 @@ static int bin2file(const char *file, const char *ext, const unsigned char *data fp = fopen(name, "w"); if (!fp) { - log_err("Unable to open %s for writing\n", name); + log_err("Failed to open: %s\n", name); return -1; } err = fwrite(data, len, 1, fp); @@ -150,7 +150,7 @@ static unsigned char *file2bin(const char *file, const char *ext, int *size) len = get_filesize(name); fp = fopen(name, "r"); if (!fp) { - log_err("Unable to open %s\n", name); + log_err("Failed to open: %s\n", name); return NULL; } data = malloc(len); @@ -270,19 +270,18 @@ static int get_uuid(struct stat *st, char *uuid) sprintf(path, "blkid -s UUID -o value /dev/block/%u:%u", major, minor); fp = popen(path, "r"); - if (!fp) { - log_err("popen() failed\n"); - return -1; - } + if (!fp) + goto err; len = fread(_uuid, 1, sizeof(_uuid), fp); pclose(fp); - if (len != sizeof(_uuid)) { - log_err("fread() failed\n"); - return -1; - } + if (len != sizeof(_uuid)) + goto err; return pack_uuid(_uuid, uuid); +err: + log_err("Failed to read UUID. Root access might require.\n"); + return -1; } static int calc_evm_hash(const char *file, unsigned char *hash) @@ -301,7 +300,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash) int hmac_size; if (lstat(file, &st)) { - log_err("lstat() failed\n"); + log_err("Failed to stat: %s\n", file); return -1; } @@ -311,7 +310,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash) int fd = open(file, 0); if (fd < 0) { - log_err("Unable to open %s\n", file); + log_err("Failed to open: %s\n", file); return -1; } if (ioctl(fd, FS_IOC_GETVERSION, &generation)) { @@ -553,7 +552,7 @@ static int get_file_type(const char *path, const char *search_type) /* stay within the same filesystem*/ err = lstat(path, &st); if (err < 0) { - log_err("stat() failed\n"); + log_err("Failed to stat: %s\n", path); return err; } fs_dev = st.st_dev; /* filesystem to start from */ @@ -700,12 +699,12 @@ static int verify_evm(const char *file) len = lgetxattr(file, "security.evm", sig, sizeof(sig)); if (len < 0) { - log_err("getxattr failed\n"); + log_err("getxattr failed: %s\n", file); return len; } if (sig[0] != 0x03) { - log_err("security.evm has not signature\n"); + log_err("security.evm has no signature\n"); return -1; } @@ -733,7 +732,7 @@ static int verify_ima(const char *file) if (xattr) { len = lgetxattr(file, "security.ima", sig, sizeof(sig)); if (len < 0) { - log_err("getxattr failed\n"); + log_err("getxattr failed: %s\n", file); return len; } } @@ -854,12 +853,12 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h key = file2bin(keyfile, NULL, &keylen); if (!key) { - log_err("Unable to read a key: %s\n\n", keyfile); + log_err("Failed to read a key: %s\n", keyfile); return -1; } if (keylen > sizeof(evmkey)) { - log_err("key is too long\n"); + log_err("key is too long: %d\n", keylen); goto out; } @@ -868,7 +867,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h memset(evmkey + keylen, 0, sizeof(evmkey) - keylen); if (lstat(file, &st)) { - log_err("lstat() failed\n"); + log_err("Failed to stat: %s\n", file); goto out; } @@ -878,7 +877,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h int fd = open(file, 0); if (fd < 0) { - log_err("Unable to open %s\n", file); + log_err("Failed to open %s\n", file); goto out; } if (ioctl(fd, FS_IOC_GETVERSION, &generation)) { @@ -892,7 +891,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h list_size = llistxattr(file, list, sizeof(list)); if (list_size <= 0) { - log_err("llistxattr() failed\n"); + log_err("llistxattr() failed: %s\n", file); goto out; } @@ -1038,7 +1037,7 @@ static int ima_fix(const char *path) */ size = llistxattr(path, list, sizeof(buf)); if (size < 0) { - log_errno("llistxattr() failed: %s\n", path); + log_errno("Failed to read xattrs (llistxattr): %s\n", path); return -1; } for (; size > 0; len++, size -= len, list += len) { @@ -1054,7 +1053,7 @@ static int ima_fix(const char *path) fd = open(path, O_RDONLY); if (fd < 0) { - log_errno("%s open failed", path); + log_errno("Failed to open file: %s", path); return -1; } @@ -1073,7 +1072,7 @@ static int find(const char *path, int dts, find_cb_t func) int err = lstat(path, &st); if (err < 0) { - log_err("stat() failed\n"); + log_err("Failed to stat: %s\n", path); return err; } if (st.st_dev != fs_dev) @@ -1082,12 +1081,12 @@ static int find(const char *path, int dts, find_cb_t func) dir = opendir(path); if (!dir) { - log_err("Unable to open %s\n", path); + log_err("Failed to open directory %s\n", path); return -1; } if (fchdir(dirfd(dir))) { - log_err("Unable to chdir %s\n", path); + log_err("Failed to chdir %s\n", path); return -1; } @@ -1102,7 +1101,7 @@ static int find(const char *path, int dts, find_cb_t func) } if (chdir("..")) { - log_err("Unable to chdir %s\n", path); + log_err("Failed to chdir: %s\n", path); return -1; } @@ -1305,7 +1304,7 @@ static int ima_measurement(const char *file) fp = fopen(file, "rb"); if (!fp) { - log_err("Unable to open measurement file\n"); + log_err("Failed to open measurement file: %s\n", file); return -1; } @@ -1448,20 +1447,20 @@ static void usage(void) 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" + " -s, --imasig make IMA signature\n" + " -d, --imahash make IMA hash\n" " -f, --sigfile store IMA signature in .sig file instead of xattr\n" - " -1, --rsa signing key is in RSA DER format (signing v1)\n" - " -k, --key path to signing key (default keys are /etc/keys/{privkey,pubkey}_evm.pem)\n" + " --rsa use RSA key type and signing scheme v1\n" + " -k, --key path to signing key (default: /etc/keys/{privkey,pubkey}_evm.pem)\n" " -p, --pass password for encrypted signing key\n" - " -u, --uuid use file system UUID in HMAC calculation (EVM v2)\n" - " -t, --type file types to fix 'fdsxm' (f - file, d - directory, s - block/char/symlink)\n" - " x - skip fixing if both ima and evm xattrs exist (caution: they may be wrong)\n" + " -r, --recursive recurse into directories (sign)\n" + " -t, --type file types to fix 'fdsxm' (f: file, d: directory, s: block/char/symlink)\n" + " x - skip fixing if both ima and evm xattrs exist (use with caution)\n" " m - stay on the same filesystem (like 'find -xdev')\n" " -n print result to stdout instead of setting xattr\n" - " -r, --recursive recurse into directories (sign)\n" - " --m32 force signature for 32 bit target system\n" - " --m64 force signature for 32 bit target system\n" + " -u, --uuid use custom FS UUID for EVM (unspecified: from FS, empty: do not use)\n" + " --m32 force EVM hmac/signature for 32 bit target system\n" + " --m64 force EVM hmac/signature for 64 bit target system\n" " -v increase verbosity level\n" " -h, --help display this help and exit\n" "\n"); diff --git a/src/libimaevm.c b/src/libimaevm.c index e24a589..2ce819f 100644 --- a/src/libimaevm.c +++ b/src/libimaevm.c @@ -171,7 +171,7 @@ static int add_file_hash(const char *file, EVP_MD_CTX *ctx) fp = fopen(file, "r"); if (!fp) { - log_err("Unable to open %s\n", file); + log_err("Failed to open: %s\n", file); return -1; } @@ -185,7 +185,7 @@ static int add_file_hash(const char *file, EVP_MD_CTX *ctx) len = MIN(size, bs); if (!fread(data, len, 1, fp)) { if (ferror(fp)) { - log_err("fread() error\n\n"); + log_err("fread() failed\n\n"); goto out; } break; @@ -214,7 +214,7 @@ static int add_dir_hash(const char *file, EVP_MD_CTX *ctx) dir = opendir(file); if (!dir) { - log_err("Unable to open %s\n", file); + log_err("Failed to open: %s\n", file); return -1; } @@ -273,7 +273,7 @@ int ima_calc_hash(const char *file, uint8_t *hash) /* Need to know the file length */ err = lstat(file, &st); if (err < 0) { - log_err("stat() failed\n"); + log_err("Failed to stat: %s\n", file); return err; } @@ -329,7 +329,7 @@ RSA *read_pub_key(const char *keyfile, int x509) fp = fopen(keyfile, "r"); if (!fp) { - log_err("Unable to open keyfile %s\n", keyfile); + log_err("Failed to open keyfile: %s\n", keyfile); return NULL; } @@ -615,7 +615,7 @@ static RSA *read_priv_key(const char *keyfile, char *keypass) fp = fopen(keyfile, "r"); if (!fp) { - log_err("Unable to open keyfile %s\n", keyfile); + log_err("Failed to open keyfile: %s\n", keyfile); return NULL; } key = PEM_read_RSAPrivateKey(fp, NULL, NULL, keypass); -- cgit v1.2.3 From 6261753e1ec4a9c01e22f084c68e7e4c37685724 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 17 Sep 2014 16:01:12 +0300 Subject: Use for security xattrs Signed-off-by: Dmitry Kasatkin --- src/evmctl.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/evmctl.c b/src/evmctl.c index 1af04a4..95759df 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -65,10 +66,10 @@ #include "imaevm.h" static char *evm_config_xattrnames[] = { - "security.selinux", - "security.SMACK64", - "security.ima", - "security.capability", + XATTR_NAME_SELINUX, + XATTR_NAME_SMACK, + XATTR_NAME_IMA, + XATTR_NAME_CAPS, NULL }; -- cgit v1.2.3 From a99a8a517c2af015cf8082ab91c1347fc8d15220 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 17 Sep 2014 13:57:04 +0300 Subject: Provide command parameter to include extra SMACK xattrs for EVM signature Latest versions of smack uses additional xattrs. This patch adds them to EVM protection. Linux kernel configuration option CONFIG_EVM_EXTRA_SMACK_XATTRS has to be enabled. Signed-off-by: Dmitry Kasatkin --- src/evmctl.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/evmctl.c b/src/evmctl.c index 95759df..f2d9c3a 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -65,7 +65,7 @@ #include "imaevm.h" -static char *evm_config_xattrnames[] = { +static char *evm_default_xattrs[] = { XATTR_NAME_SELINUX, XATTR_NAME_SMACK, XATTR_NAME_IMA, @@ -73,6 +73,19 @@ static char *evm_config_xattrnames[] = { NULL }; +static char *evm_extra_smack_xattrs[] = { + XATTR_NAME_SELINUX, + XATTR_NAME_SMACK, + XATTR_NAME_SMACKEXEC, + XATTR_NAME_SMACKTRANSMUTE, + XATTR_NAME_SMACKMMAP, + XATTR_NAME_IMA, + XATTR_NAME_CAPS, + NULL +}; + +static char **evm_config_xattrnames = evm_default_xattrs; + struct command { char *name; int (*func)(struct command *cmd); @@ -1460,6 +1473,7 @@ static void usage(void) " m - stay on the same filesystem (like 'find -xdev')\n" " -n print result to stdout instead of setting xattr\n" " -u, --uuid use custom FS UUID for EVM (unspecified: from FS, empty: do not use)\n" + " --smack use extra SMACK xattrs for EVM\n" " --m32 force EVM hmac/signature for 32 bit target system\n" " --m64 force EVM hmac/signature for 64 bit target system\n" " -v increase verbosity level\n" @@ -1498,6 +1512,7 @@ static struct option opts[] = { {"recursive", 0, 0, 'r'}, {"m32", 0, 0, '3'}, {"m64", 0, 0, '6'}, + {"smack", 0, 0, 256}, {} }; @@ -1568,6 +1583,9 @@ int main(int argc, char *argv[]) case '6': msize = 64; break; + case 256: + evm_config_xattrnames = evm_extra_smack_xattrs; + break; case '?': exit(1); break; -- cgit v1.2.3 From 05d1f74559147fa8cf934802f1762ec862bf16cb Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 17 Sep 2014 14:41:42 +0300 Subject: Update README/man page documentation Add more info on: * introduction * EVM formats * Signature and keys formats * IMA trusted keys and keyrings * EVM trusted keys * Updated scripts and examples Signed-off-by: Dmitry Kasatkin --- README | 369 +++++++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 290 insertions(+), 79 deletions(-) diff --git a/README b/README index 1a22c12..6bc2368 100644 --- a/README +++ b/README @@ -39,41 +39,129 @@ COMMANDS OPTIONS ------- - -a, --hashalgo sha1 (default), sha224, sha256, sha384, sha512 - -s, --imasig also make IMA signature - -d, --imahash also make IMA hash - -f, --sigfile store IMA signature in .sig file instead of xattr - -1, --rsa signing key is in RSA DER format (signing v1) - -k, --key path to signing key (default: /etc/keys/{privkey,pubkey}_evm.pem) - -p, --pass password for encrypted signing key - -u, --uuid use file system UUID in HMAC calculation (EVM v2) - -t, --type file types to fix 'fdsxm' (f: file, d: directory, s: block/char/symlink) - x - skip fixing if both ima and evm xattrs exist (use with caution) - m - stay on the same filesystem (like 'find -xdev') - -n print result to stdout instead of setting xattr - -r, --recursive recurse into directories (sign) - --m32 force signature for 32 bit target system - --m64 force signature for 32 bit target system - -v increase verbosity level - -h, --help display this help and exit + -a, --hashalgo sha1 (default), sha224, sha256, sha384, sha512 + -s, --imasig make IMA signature + -d, --imahash make IMA hash + -f, --sigfile store IMA signature in .sig file instead of xattr + --rsa use RSA key type and signing scheme v1 + -k, --key path to signing key (default: /etc/keys/{privkey,pubkey}_evm.pem) + -p, --pass password for encrypted signing key + -r, --recursive recurse into directories (sign) + -t, --type file types to fix 'fdsxm' (f: file, d: directory, s: block/char/symlink) + x - skip fixing if both ima and evm xattrs exist (use with caution) + m - stay on the same filesystem (like 'find -xdev') + -n print result to stdout instead of setting xattr + -u, --uuid use custom FS UUID for EVM (unspecified: from FS, empty: do not use) + --smack use extra SMACK xattrs for EVM + --m32 force EVM hmac/signature for 32 bit target system + --m64 force EVM hmac/signature for 64 bit target system + -v increase verbosity level + -h, --help display this help and exit + + +INTRODUCTION +------------ + +Linux kernel integrity subsystem is comprised of a number of different components +including the Integrity Measurement Architecture (IMA), Extended Verification Module +(EVM), IMA-appraisal extension, digital signature verification extension and audit +measurement log support. + +The evmctl utility is used for producing and verifying digital signatures, which +are used by the Linux kernel integrity subsystem. It is also used for importing keys +into the kernel keyring. + +Linux integrity subsystem allows to use IMA and EVM signatures. EVM signature +protects file metadata, such as file attributes and extended attributes. IMA +signature protects file content. + +For more detailed information about integrity subsystem it is recommended to follow +resources in RESOURCES section. + + +EVM HMAC and signature metadata +------------------------------- + +EVM protects file metadata by including following attributes into HMAC and signature +calculation: inode number, inode generation, UID, GID, file mode, security.selinux, +security.SMACK64, security.ima, security.capability. + +EVM HMAC and signature in may also include additional file and file system attributes. +Currently supported additional attributes are filesystem UUID and extra SMACK +extended attributes. + +Kernel configuration option CONFIG_EVM_ATTR_FSUUID controls whether to include +filesystem UUID into HMAC and enabled by default. Therefore evmctl also includes +fsuuid by default. Providing '--uuid' option without parameter allows to disable +usage of fs uuid. Providing '--uuid=UUID' option with parameter allows to use +custom UUID. + +Kernel configuration option CONFIG_EVM_EXTRA_SMACK_XATTRS controls whether to +include additional SMACK extended attributes into HMAC. They are following: +security.SMACK64EXEC, security.SMACK64TRANSMUTE and security.SMACK64MMAP. +evmctl '--smack' options enables that. Key and signature formats ------------------------- -EVM support (v2) in latest version of the kernel adds the file system UUID to -the HMAC calculation. It is controlled by the CONFIG_EVM_HMAC_VERSION and -version 2 is enabled by default. In this version default UUID is included by -default. Custom value can be supplied via '--uuid=UUID' or '-uUUID' parameter -to the 'sign' command. To use old format HMAC format use '-' as a parameter. +Linux integrity subsystem supports two type of signature and respectively two +key formats. -Latest kernel got IMA/EVM support for using X509 certificates and asymmetric key -support for verifying digital signatures. This version uses x509 format by default. -Use '--rsa' or '-1' parameter to use old signature format and API. +First key format (v1) is pure RSA key encoded in PEM a format and uses own signature +format. It is now non-default format and requires to provide evmctl '--rsa' option +for signing and importing the key. +Second key format uses X509 DER encoded public key certificates and uses asymmetric key support +in the kernel (since kernel 3.9). CONFIG_INTEGRITY_ASYMMETRIC_KEYS must be enabled (default). -Key generation --------------- + +Integrity keyrings +---------------- + +Integrity subsystem uses dedicated IMA/EVM keyrings to search for signature verification +keys - '_ima' and '_evm' respectively. + +Since 3.13 IMA allows to declare IMA keyring as trusted. It allows only to load keys, +signed by a key from the system keyring (.system). It means self-signed keys are not +allowed. This is a default behavior unless CONFIG_IMA_TRUSTED_KEYRING is undefined. +IMA trusted keyring is has different name '.ima'. Trusted keyring requires X509 +public key certificates. Old version RSA public keys are not compatible with trusted +keyring. + + +Generate EVM encrypted keys +--------------------------- + +EVM encrypted key is used for EVM HMAC calculation: + + # create and save the key kernel master key (user type) + # LMK is used to encrypt encrypted keys + keyctl add user kmk "`dd if=/dev/urandom bs=1 count=32 2>/dev/null`" @u + keyctl pipe `keyctl search @u user kmk` > /etc/keys/kmk + + # create the EVM encrypted key + keyctl add encrypted evm-key "new user:kmk 32" @u + keyctl pipe `keyctl search @u encrypted evm-key` >/etc/keys/evm-key + + +Generate EVM trusted keys (TPM based) +------------------------------------- + +Trusted EVM keys are keys which a generate with the help of TPM. +They are not related to integrity trusted keys. + + # create and save the key kernel master key (user type) + keyctl add trusted kmk "new 32" @u + keyctl pipe `keyctl search @u trusted kmk` >kmk + + # create the EVM trusted key + keyctl add encrypted evm-key "new trusted:kmk 32" @u + keyctl pipe `keyctl search @u encrypted evm-key` >evm-key + + +Generate signing and verification keys +-------------------------------------- Generate private key in plain text format: @@ -87,7 +175,7 @@ Make encrypted private key from unencrypted: openssl rsa -in /etc/keys/privkey_evm.pem -out privkey_evm_enc.pem -des3 -Generate self-signed X509 certificate and private key for using kernel +Generate self-signed X509 public key certificate and private key for using kernel asymmetric keys support: openssl req -new -nodes -utf8 -sha1 -days 36500 -batch \ @@ -117,109 +205,232 @@ Configuration file x509_evm.genkey: # EOF -Get public key: +Generate public key for using RSA key format: openssl rsa -pubout -in privkey_evm.pem -out pubkey_evm.pem + Copy keys to /etc/keys: cp pubkey_evm.pem /etc/keys scp pubkey_evm.pem target:/etc/keys - -or + or cp x509_evm.pem /etc/keys scp x509_evm.pem target:/etc/keys -Generate EVM keys: +Generate trusted keys +--------------------- - # create and save the kernel master key (user type) - keyctl add user kmk "`dd if=/dev/urandom bs=1 count=32 2>/dev/null`" @u - keyctl pipe `keyctl search @u user kmk` > /etc/keys/kmk - # create the EVM encrypted key - keyctl add encrypted evm-key "new user:kmk 32" @u - keyctl pipe `keyctl search @u encrypted evm-key` >/etc/keys/evm-key +Generation of trusted keys is a bit more complicated process and involves +following steps: +* Creation of local IMA certification authority (CA). + It consist of private and public key certificate which are used + to sign and verify other keys. +* Build Linux kernel with embedded local IMA CA X509 certificate. + It is used to verify other keys added to the '.ima' trusted keyring +* Generate IMA private signing key and verification public key certificate, + which is signed using local IMA CA private key. -Initialization --------------- +Configuration file ima-local-ca.genkey: -IMA/EVM initialization should be normally done from initial RAM file system -before mounting root filesystem. + # Begining of the file + [ req ] + default_bits = 2048 + distinguished_name = req_distinguished_name + prompt = no + string_mask = utf8only + x509_extensions = v3_ca -Here is an example script /etc/initramfs-tools/scripts/local-top/ima.sh + [ req_distinguished_name ] + O = IMA-CA + CN = IMA/EVM certificate signing key + emailAddress = ca@ima-ca - # import EVM HMAC key - keyctl clear @u - cat /etc/keys/kmk | keyctl padd user kmk @u - keyctl add encrypted evm-key "load `cat /etc/keys/evm-key`" @u + [ v3_ca ] + basicConstraints=CA:TRUE + subjectKeyIdentifier=hash + authorityKeyIdentifier=keyid:always,issuer + # keyUsage = cRLSign, keyCertSign + # EOF - # import IMA public key - ima_id=`keyctl newring _ima @u` - evmctl --rsa import /etc/keys/pubkey_evm.pem $ima_id +Generate private key and X509 public key certificate: - # import EVM public key - evm_id=`keyctl newring _evm @u` - evmctl --rsa import /etc/keys/pubkey_evm.pem $evm_id + openssl req -new -x509 -utf8 -sha1 -days 3650 -batch -config $GENKEY \ + -outform DER -out ima-local-ca.x509 -keyout ima-local-ca.priv - # enable EVM - echo "1" > /sys/kernel/security/evm +Produce X509 in DER format for using while building the kernel: + + openssl x509 -inform DER -in ima-local-ca.x509 -out ima-local-ca.pem + +Configuration file ima.genkey: + + # Begining of the file + [ req ] + default_bits = 1024 + distinguished_name = req_distinguished_name + prompt = no + string_mask = utf8only + x509_extensions = v3_usr + [ req_distinguished_name ] + O = `hostname` + CN = `whoami` signing key + emailAddress = `whoami`@`hostname` -Import X509 certificate into the kernel keyring (since kernel 3.9?): + [ v3_usr ] + basicConstraints=critical,CA:FALSE + #basicConstraints=CA:FALSE + keyUsage=digitalSignature + #keyUsage = nonRepudiation, digitalSignature, keyEncipherment + subjectKeyIdentifier=hash + authorityKeyIdentifier=keyid + #authorityKeyIdentifier=keyid,issuer + # EOF - evmctl import /etc/keys/x509_evm.der `keyctl search @u keyring _ima` - evmctl import /etc/keys/x509_evm.der `keyctl search @u keyring _evm` +Generate private key and X509 public key certificate signing request: -Signing -------- + openssl req -new -nodes -utf8 -sha1 -days 365 -batch -config $GENKEY \ + -out csr_ima.pem -keyout privkey_ima.pem -Default public key: /etc/keys/pubkey_evm.pem -Default private key: /etc/keys/privkey_evm.pem -Default X509 certificate: /etc/keys/x509_evm.der +Sign X509 public key certificate signing request with local IMA CA private key: -Signing for using old RSA format is done using '-1' or '--rsa' parameter. -Signing for using old EVM HMAC format is done using '-u-' or '--uuid=-' parameter. + openssl x509 -req -in csr_ima.pem -days 365 -extfile $GENKEY -extensions v3_usr \ + -CA ima-local-ca.pem -CAkey ima-local-ca.priv -CAcreateserial \ + -outform DER -out x509_ima.der -Sign file with EVM signature and use hash value for IMA - common case: - evmctl sign [-u] [-1] --imahash test.txt +Sign file data and metadata +--------------------------- -Sign file with both IMA and EVM signatures - for immutable files: +Default key locations: - evmctl sign [-u] [-1] --imasig test.txt: + Private RSA key: /etc/keys/privkey_evm.pem + Public RSA key: /etc/keys/pubkey_evm.pem + X509 certificate: /etc/keys/x509_evm.der -Sign file with IMA signature - for immutable files: +Options to remember: '-k', '-r', '--rsa', '--uuid', '--smack'. - evmctl ima_sign [-1] test.txt +Sign file with EVM signature and calculate hash value for IMA: -Label whole filesystem with EVM signatures: + evmctl sign --imahash test.txt - find / \( -fstype rootfs -o -fstype ext4 \) -exec evmctl sign [-u] [-1] --imahash '{}' \; +Sign file with both IMA and EVM signatures: -Label filesystem in fix mode - kernel sets correct values to IMA and EVM xattrs: + evmctl sign --imasig test.txt: + +Sign file with IMA signature: + + evmctl ima_sign test.txt + +Sign recursively whole filesystem: + + evmctl -r sign --imahash / + +Fix recursively whole filesystem: + + evmctl -r ima_fix / + +Sign filesystem selectively using 'find' command: + + find / \( -fstype rootfs -o -fstype ext4 \) -exec evmctl sign --imahash '{}' \; + +Fix filesystem selectively using 'find' command: find / \( -fstype rootfs -o -fstype ext4 \) -exec sh -c "< '{}'" \; +Initialize IMA/EVM at early boot +-------------------------------- + +IMA/EVM initialization should be normally done from initial RAM file system +before mounting root filesystem. + +Here is Ubuntu initramfs example script (/etc/initramfs-tools/scripts/local-top/ima.sh) + + # mount securityfs if not mounted + SECFS=/sys/kernel/security + grep -q $SECFS /proc/mounts || mount -n -t securityfs securityfs $SECFS + + # search for IMA trusted keyring, then for untrusted + ima_id="`awk '/\.ima/ { printf "%d", "0x"$1; }' /proc/keys`" + if [ -z "$ima_id" ]; then + ima_id=`keyctl search @u keyring _ima 2>/dev/null` + if [ -z "$ima_id" ]; then + ima_id=`keyctl newring _ima @u` + fi + fi + # import IMA X509 certificate + evmctl import /etc/keys/x509_ima.der $ima_id + + # search for EVM keyring + evm_id=`keyctl search @u keyring _evm 2>/dev/null` + if [ -z "$evm_id" ]; then + evm_id=`keyctl newring _evm @u` + fi + # import EVM X509 certificate + evmctl import /etc/keys/x509_evm.der $evm_id + + # a) import EVM encrypted key + cat /etc/keys/kmk | keyctl padd user kmk @u + keyctl add encrypted evm-key "load `cat /etc/keys/evm-key`" @u + # OR + # b) import EVM trusted key + keyctl add trusted kmk "load `cat /etc/keys/kmk`" @u + keyctl add encrypted evm-key "load `cat /etc/keys/evm-key`" @u + + # enable EVM + echo "1" > /sys/kernel/security/evm + +Optionally it is possible also to forbid adding, removing of new public keys +and certificates into keyrings and revoking keys using 'keyctl setperm' command: + + # protect EVM keyring + keyctl setperm $evm_id 0x0b0b0000 + # protect IMA keyring + keyctl setperm $ima_id 0x0b0b0000 + # protecting IMA key from revoking (against DoS) + ima_key=`evmctl import /etc/keys/x509_ima.der $ima_id` + keyctl setperm $ima_key 0x0b0b0000 + + +When using plain RSA public keys in PEM format, use 'evmctl import --rsa' for importing keys: + + evmctl import --rsa /etc/keys/pubkey_evm.pem $evm_id + +Latest version of keyctl allows to import X509 public key certificates: + + cat /etc/keys/x509_ima.der | keyctl padd asymmetric '' @ima_id + + +FILES +----- + +Examples of scripts to generate X509 public key certificates: + + /usr/share/doc/ima-evm-utils/ima-genkey-self.sh + /usr/share/doc/ima-evm-utils/ima-genkey.sh + /usr/share/doc/ima-evm-utils/ima-gen-local-ca.sh + + AUTHOR ------ -Written by Dmitry Kasatkin, +Written by Dmitry Kasatkin, and others. RESOURCES --------- -http://sourceforge.net/p/linux-ima/wiki/Home - -http://sourceforge.net/p/linux-ima/ima-evm-utils + http://sourceforge.net/p/linux-ima/wiki/Home + http://sourceforge.net/p/linux-ima/ima-evm-utils COPYING ------- -Copyright \(C) 2012 - 2014 Dmitry Kasatkin. Free use of this software is granted under +Copyright \(C) 2012 - 2014 Linux Integrity Project. Free use of this software is granted under the terms of the GNU Public License (GPL). -- cgit v1.2.3 From f4fe504f98b809a6dd6d91e58e801b4a94063a3f Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 23 Sep 2014 14:12:19 +0300 Subject: Add 'evmctl --version' version reporting Signed-off-by: Dmitry Kasatkin --- README | 1 + src/evmctl.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/README b/README index 6bc2368..c14a3ed 100644 --- a/README +++ b/README @@ -23,6 +23,7 @@ used to import keys into the kernel keyring. COMMANDS -------- + --version help import [--rsa] pubkey keyring sign [-r] [--imahash | --imasig ] [--key key] [--pass password] file diff --git a/src/evmctl.c b/src/evmctl.c index f2d9c3a..109b82a 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -1482,6 +1482,7 @@ static void usage(void) } struct command cmds[] = { + {"--version", NULL, 0, ""}, {"help", cmd_help, 0, ""}, {"import", cmd_import, 0, "[--rsa] pubkey keyring", "Import public key into the keyring.\n"}, {"sign", cmd_sign_evm, 0, "[-r] [--imahash | --imasig ] [--key key] [--pass password] file", "Sign file metadata.\n"}, @@ -1513,6 +1514,7 @@ static struct option opts[] = { {"m32", 0, 0, '3'}, {"m64", 0, 0, '6'}, {"smack", 0, 0, 256}, + {"version", 0, 0, 257}, {} }; @@ -1586,6 +1588,10 @@ int main(int argc, char *argv[]) case 256: evm_config_xattrnames = evm_extra_smack_xattrs; break; + case 257: + printf("evmctl %s\n", VERSION); + exit(0); + break; case '?': exit(1); break; -- cgit v1.2.3 From 3d9bdc1de282846de3523fd7a698d473304650b0 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 23 Sep 2014 15:09:05 +0300 Subject: Release version 0.9 Signed-off-by: Dmitry Kasatkin --- ChangeLog | 9 +++++++++ configure.ac | 2 +- packaging/ima-evm-utils.spec | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3ead819..1129d42 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2014-09-23 Dmitry Kasatkin + + version 0.9 + * Updated README + * man page generated and added to the package + * Use additional SMACK xattrs for EVM signature generation + * Signing functions moved to libimaevm for external use (RPM) + * Fixed setting of correct hash header + 2014-05-05 Dmitry Kasatkin version 0.8 diff --git a/configure.ac b/configure.ac index 78821c5..5e5ea21 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # autoconf script AC_PREREQ([2.65]) -AC_INIT(ima-evm-utils, 0.8, d.kasatkin@samsung.com) +AC_INIT(ima-evm-utils, 0.9, d.kasatkin@samsung.com) AM_INIT_AUTOMAKE AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/packaging/ima-evm-utils.spec b/packaging/ima-evm-utils.spec index cc90465..7ae326f 100644 --- a/packaging/ima-evm-utils.spec +++ b/packaging/ima-evm-utils.spec @@ -1,5 +1,5 @@ Name: ima-evm-utils -Version: 0.8 +Version: 0.9 Release: 1%{?dist} Summary: ima-evm-utils - IMA/EVM control utility Group: System/Libraries -- cgit v1.2.3