10 Commits
v0.3 ... v0.5

Author SHA1 Message Date
076fd302bb support for asymmetric crypto keys and new signature format
Asymmetric keys were introduced in linux-3.7 to verify the signature on
signed kernel modules. The asymmetric keys infrastructure abstracts the
signature verification from the crypto details. Asymmetric crypto keys
support allows to import X509 public key certificate in a DER format
into the kernel keyring. Asymmetric keys require a new signature format.
'evmctl -x' or 'evmctl --x509' option can be used to utilize new
signature format.

Using of key filename after the file name for signing and verification commands
is a bit odd. This patch add '--key' parameter to specify non-default key file.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2013-02-11 16:12:08 +02:00
1d24a94bb5 added uuid support for EVM
Latest version of EVM uses file system UUID as part of an HMAC
calculation to prevent pasting of inode metadata from other file
systems. This patch adds support for adding file system UUID
to HMAC calculation. It is necessary to specify '-u -' or '--uuid -'
on evmctl command line.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2013-02-11 16:07:30 +02:00
5be54eaca4 Update README
README updated.
Module signing info has been removed. Module signing is done now in kernel
source tree and uses appended signatures. No need to create sig files or
set extended attributes. Information about test scripts has been removed.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-09-10 15:37:40 +03:00
a58cd9f4af Remove test scripts
Test scripts are not used at all.
All needed information is in README.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-09-10 15:17:42 +03:00
c8b4f34fd4 remove directory entry list sorting
Directory entries list sorting is not needed.
Entries are read always in the same order.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-09-06 14:08:54 +03:00
c171931236 added ima signature verification support
For debugging puporse it is usefull to have signature verification
functionality. It supports use of xattrs and .sig files.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-09-06 14:08:54 +03:00
ba07c9d4b1 do not output type prefix for sig files
sig files do not need type prefix as they are contain only signatures.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-09-06 14:08:54 +03:00
203f058903 added support for kernel module signature
Kernel module signature is appended to the kernel module.
Kernel signature also contains signature length and magic.
Added --modsig parameter to generate kernel module signature.

Signature can be added to the module like: cat module.sig >> module.ko

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-09-06 14:08:54 +03:00
72ad26c3be disable printing signature when using sigfiles
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-09-06 14:08:54 +03:00
f41d43026b Remove tag creation
Better to create tag manually when release is done.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
2012-09-06 14:08:54 +03:00
13 changed files with 648 additions and 316 deletions

View File

@ -1,4 +1,4 @@
SUBDIRS = src tests
SUBDIRS = src
#EXTRA_DIST = LEGAL acinclude.m4 include
@ -11,7 +11,6 @@ pkgname = $(PACKAGE_NAME)-$(PACKAGE_VERSION)
tarname = $(pkgname).tar.gz
$(tarname):
git tag v$(PACKAGE_VERSION)
git archive --format=tar --prefix=$(pkgname)/ v$(PACKAGE_VERSION) $(FILES) | gzip >$@
tar: $(tarname)

153
README
View File

@ -1,52 +1,145 @@
ima-evm-utils - IMA/EVM signing utility
=========================================
1. Generate private key
Contents:
# plain key
openssl genrsa -out privkey_evm.pem 1024
1. Key and signature formats
2. Key generation
3. Initialization
4. Signing
# encrypted key
openssl genrsa -des3 -out privkey_evm.pem 1024
# set password for the key
openssl rsa -in /etc/keys/privkey_evm.pem -out privkey_evm_enc.pem -des3
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. To include the UUID to the signature calculation,
it is necessary to provide '--uuid -' or '-u -' parameter to the 'sign' command.
Latest kernel got IMA/EVM support for using X509 certificates and asymmetric key
support for verifying digital signatures. The new command line parameter
'-x' or '--x509' was added to the evmctl to enable using of X509 certificates
and new signature format.
Key generation
--------------
Generate private key in plain text format
$ openssl genrsa -out privkey_evm.pem 1024
Generate encrypted private key
$ openssl genrsa -des3 -out privkey_evm.pem 1024
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 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
Configuration file x509_evm.genkey:
# Begining of the file
[ req ]
default_bits = 1024
distinguished_name = req_distinguished_name
prompt = no
string_mask = utf8only
x509_extensions = myexts
[ req_distinguished_name ]
O = Magrathea
CN = Glacier signing key
emailAddress = slartibartfast@magrathea.h2g2
[ myexts ]
basicConstraints=critical,CA:FALSE
keyUsage=digitalSignature
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid
# EOF
Get public key
$ 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
openssl pkcs8 -topk8 -in /etc/keys/privkey_evm.pem -out privkey_evm_enc.pem
$ cp x509_evm.pem /etc/keys
$ scp x509_evm.pem target:/etc/keys
2. Generate public key
openssl rsa -pubout -in privkey_evm.pem -out pubkey_evm.pem
Initialization
--------------
3. Copy public (+private if to sign on device) key to the device/qemu /etc/keys
IMA/EVM initialization should be normally done from initial RAM file system
before mounting root filesystem.
scp pubkey_evm.pem mad:/etc/keys
Here is an example script /etc/initramfs-tools/scripts/local-top/ima.sh
4. Load keys and enable EVM
# import EVM HMAC key
keyctl clear @u
keyctl add user kmk "testing123" @u
keyctl add encrypted evm-key "load `cat /etc/keys/evm-key`" @u
evm_enable.sh
# import IMA public key
ima_id=`keyctl newring _ima @u`
evmctl import /etc/keys/pubkey_evm.pem $ima_id
This should be done at early phase, before mounting root filesystem.
# import EVM public key
evm_id=`keyctl newring _evm @u`
evmctl import /etc/keys/pubkey_evm.pem $evm_id
5. Sign EVM and use hash value for IMA - common case
# enable EVM
echo "1" > /sys/kernel/security/evm
evmctl sign --imahash test.txt
6. Sign IMA and EVM - for immutable files and modules
Import X509 certificate into the kernel keyring (since kernel 3.9?)
evmctl sign --imasig test.txt
$ evmctl -x import /etc/keys/x509_evm.der `keyctl search @u keyring _ima`
$ evmctl -x import /etc/keys/x509_evm.der `keyctl search @u keyring _evm`
7. Sign whole filesystem
evm_sign_all.sh
or
find / \( -fstype rootfs -o -fstype ext3 -o -fstype ext4 \) ! -path "/lib/modules/*" -type f -uid 0 -exec evmctl sign --imahash '{}' \;
find /lib/modules ! -name "*.ko" -type f -uid 0 -exec evmctl sign --imahash '{}' \;
# security.ima needs to have signature for modules
find /lib/modules -name "*.ko" -type f -uid 0 -exec evmctl sign --imasig '{}' \;
Signing
-------
# generate signatures in .sig files
find /lib/modules -name "*.ko" -type f -uid 0 -exec evmctl -n --sigfile ima_sign '{}' \;
Default public key: /etc/keys/pubkey_evm.pem
Default private key: /etc/keys/privkey_evm.pem
Default X509 certificate: /etc/keys/x509_evm.der
8. Label filesystem in fix mode...
Signing for using X509 certificates is done using '-x' or '--x509' parameter.
Signing for using new the EVM HMAC format is done using '-u -' or '--uuid -' parameter.
ima_fix_dir.sh <dir>
Sign file with EVM signature and use hash value for IMA - common case
$ evmctl sign [-u -] [-x] --imahash test.txt
Sign file with both IMA and EVM signatures - for immutable files
$ evmctl sign [-u -] [-x] --imasig test.txt
Sign file with IMA signature - for immutable files
$ evmctl ima_sign [-x] test.txt
Label whole filesystem with EVM signatures
$ find / \( -fstype rootfs -o -fstype ext4 \) -exec evmctl sign [-u -] [-x] --imahash '{}' \;
Label filesystem in fix mode - kernel sets correct values to IMA and EVM xattrs
$ find / \( -fstype rootfs -o -fstype ext4 \) -exec sh -c "< '{}'" \;

View File

@ -46,7 +46,6 @@ fi
AC_CONFIG_FILES([Makefile
src/Makefile
tests/Makefile
ima-evm-utils.spec
])
AC_OUTPUT

View File

@ -2,7 +2,7 @@
* evm-utils - IMA/EVM support utilities
*
* Copyright (C) 2011 Nokia Corporation
* Copyright (C) 2011, 2012 Intel Corporation
* Copyright (C) 2011,2012,2013 Intel Corporation
*
* Authors:
* Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
@ -44,6 +44,7 @@
#include <syslog.h>
#include <attr/xattr.h>
#include <dirent.h>
#include <ctype.h>
#include <openssl/sha.h>
#include <openssl/rsa.h>
@ -52,6 +53,7 @@
#include <openssl/engine.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/x509.h>
#define USE_FPRINTF
@ -124,6 +126,101 @@ struct signature_hdr {
char mpi[0];
} __attribute__ ((packed));
enum pkey_hash_algo {
PKEY_HASH_MD4,
PKEY_HASH_MD5,
PKEY_HASH_SHA1,
PKEY_HASH_RIPE_MD_160,
PKEY_HASH_SHA256,
PKEY_HASH_SHA384,
PKEY_HASH_SHA512,
PKEY_HASH_SHA224,
PKEY_HASH__LAST
};
const char *const pkey_hash_algo[PKEY_HASH__LAST] = {
[PKEY_HASH_MD4] = "md4",
[PKEY_HASH_MD5] = "md5",
[PKEY_HASH_SHA1] = "sha1",
[PKEY_HASH_RIPE_MD_160] = "rmd160",
[PKEY_HASH_SHA256] = "sha256",
[PKEY_HASH_SHA384] = "sha384",
[PKEY_HASH_SHA512] = "sha512",
[PKEY_HASH_SHA224] = "sha224",
};
/*
* signature format v2 - for using with asymmetric keys
*/
struct signature_v2_hdr {
uint8_t version; /* signature format version */
uint8_t hash_algo; /* Digest algorithm [enum pkey_hash_algo] */
uint32_t keyid; /* IMA key identifier - not X509/PGP specific*/
uint16_t sig_size; /* signature size */
uint8_t sig[0]; /* signature payload */
} __attribute__ ((packed));
/*
* Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2].
*/
static const uint8_t RSA_digest_info_MD5[] = {
0x30, 0x20, 0x30, 0x0C, 0x06, 0x08,
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, /* OID */
0x05, 0x00, 0x04, 0x10
};
static const uint8_t RSA_digest_info_SHA1[] = {
0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
0x2B, 0x0E, 0x03, 0x02, 0x1A,
0x05, 0x00, 0x04, 0x14
};
static const uint8_t RSA_digest_info_RIPE_MD_160[] = {
0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
0x2B, 0x24, 0x03, 0x02, 0x01,
0x05, 0x00, 0x04, 0x14
};
static const uint8_t RSA_digest_info_SHA224[] = {
0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04,
0x05, 0x00, 0x04, 0x1C
};
static const uint8_t RSA_digest_info_SHA256[] = {
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
0x05, 0x00, 0x04, 0x20
};
static const uint8_t RSA_digest_info_SHA384[] = {
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
0x05, 0x00, 0x04, 0x30
};
static const uint8_t RSA_digest_info_SHA512[] = {
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
0x05, 0x00, 0x04, 0x40
};
static const struct RSA_ASN1_template {
const uint8_t *data;
size_t size;
} RSA_ASN1_templates[PKEY_HASH__LAST] = {
#define _(X) { RSA_digest_info_##X, sizeof(RSA_digest_info_##X) }
[PKEY_HASH_MD5] = _(MD5),
[PKEY_HASH_SHA1] = _(SHA1),
[PKEY_HASH_RIPE_MD_160] = _(RIPE_MD_160),
[PKEY_HASH_SHA256] = _(SHA256),
[PKEY_HASH_SHA384] = _(SHA384),
[PKEY_HASH_SHA512] = _(SHA512),
[PKEY_HASH_SHA224] = _(SHA224),
#undef _
};
static char *evm_config_xattrnames[] = {
"security.selinux",
"security.SMACK64",
@ -144,12 +241,24 @@ static int verbose = LOG_INFO - 1;
static int g_argc;
static char **g_argv;
static int xattr = 1;
static int sigdump;
static int digest;
static int digsig;
static char *hash_algo = "sha1";
static int binkey;
static char *keypass;
static int sigfile;
static int modsig;
static char *uuid_str;
static int x509;
static char *keyfile;
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;
typedef int (*verify_hash_fn_t)(const unsigned char *hash, int size, unsigned char *sig, int siglen, const char *keyfile);
static verify_hash_fn_t verify_hash;
struct command cmds[];
static void print_usage(struct command *cmd);
@ -208,16 +317,24 @@ static int bin2file(const char *file, const char *ext, const unsigned char *data
return err;
}
static unsigned char *file2bin(const char *file, int *size)
static unsigned char *file2bin(const char *file, const char *ext, int *size)
{
FILE *fp;
int len;
unsigned char *data;
char name[strlen(file) + (ext ? strlen(ext) : 0) + 2];
len = get_filesize(file);
fp = fopen(file, "r");
if (ext)
sprintf(name, "%s.%s", file, ext);
else
sprintf(name, "%s", file);
log_info("Reading to %s\n", name);
len = get_filesize(name);
fp = fopen(name, "r");
if (!fp) {
log_err("Unable to open %s\n", file);
log_err("Unable to open %s\n", name);
return NULL;
}
data = malloc(len);
@ -262,44 +379,57 @@ static int key2bin(RSA *key, unsigned char *pub)
return offset;
}
static int read_key(const char *inkey, unsigned char *pub)
static RSA *read_pub_key(const char *keyfile)
{
FILE *fp;
RSA *key = NULL, *key1;
int len;
RSA *key = NULL;
X509 *crt = NULL;
EVP_PKEY *pkey = NULL;
fp = fopen(inkey, "r");
fp = fopen(keyfile, "r");
if (!fp) {
log_err("read key failed from file %s\n", inkey);
return -1;
log_err("Unable to open keyfile %s\n", keyfile);
return NULL;
}
key1 = PEM_read_RSA_PUBKEY(fp, &key, NULL, NULL);
fclose(fp);
if (!key1) {
if (x509) {
crt = d2i_X509_fp(fp, NULL);
if (!crt) {
log_err("d2i_X509_fp() failed\n");
goto out;
}
pkey = X509_extract_key(crt);
if (!pkey) {
log_err("X509_extract_key() failed\n");
goto out;
}
key = EVP_PKEY_get1_RSA(pkey);
} else {
key = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
}
if (!key)
log_err("PEM_read_RSA_PUBKEY() failed\n");
return -1;
}
len = key2bin(key, pub);
RSA_free(key);
return len;
out:
if (pkey)
EVP_PKEY_free(pkey);
if (crt)
X509_free(crt);
fclose(fp);
return key;
}
static void calc_keyid(uint8_t *keyid, char *str, const unsigned char *pkey, int len)
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;
log_debug("pkey:\n");
log_debug_dump(pkey, len);
SHA1(pkey, len, sha1);
/* sha1[12 - 19] is exactly keyid from gpg file */
memcpy(keyid, sha1 + 12, 8);
log_debug("keyid:\n");
log_debug("keyid: ");
log_debug_dump(keyid, 8);
id = __be64_to_cpup((__be64 *) keyid);
@ -307,13 +437,51 @@ static void calc_keyid(uint8_t *keyid, char *str, const unsigned char *pkey, int
log_info("keyid: %s\n", str);
}
static int sign_hash(const unsigned char *hash, int size, const char *keyfile, unsigned char *sig)
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 sign_hash_v1(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig)
{
int err, len;
SHA_CTX ctx;
unsigned char pub[1024];
RSA *key = NULL, *key1;
FILE *fp;
RSA *key;
char name[20];
unsigned char sighash[20];
struct signature_hdr *hdr = (struct signature_hdr *)sig;
@ -322,17 +490,9 @@ static int sign_hash(const unsigned char *hash, int size, const char *keyfile, u
log_info("hash: ");
log_dump(hash, size);
fp = fopen(keyfile, "r");
if (!fp) {
log_err("Unable to open keyfile %s\n", keyfile);
key = read_priv_key(keyfile);
if (!key)
return -1;
}
key1 = PEM_read_RSAPrivateKey(fp, &key, NULL, keypass);
fclose(fp);
if (!key1) {
log_err("PEM_read_RSAPrivateKey() failed\n");
return 1;
}
/* now create a new hash */
hdr->version = 1;
@ -341,7 +501,7 @@ static int sign_hash(const unsigned char *hash, int size, const char *keyfile, u
hdr->hash = DIGEST_ALGO_SHA1;
len = key2bin(key, pub);
calc_keyid(hdr->keyid, name, pub, len);
calc_keyid_v1(hdr->keyid, name, pub, len);
hdr->nmpi = 1;
@ -366,7 +526,65 @@ static int sign_hash(const unsigned char *hash, int size, const char *keyfile, u
*blen = __cpu_to_be16(len << 3);
len += sizeof(*hdr) + 2;
log_info("evm/ima signature: %d bytes\n", len);
if (!xattr || verbose >= LOG_INFO)
if (sigdump || verbose >= LOG_INFO)
dump(sig, len);
return len;
}
uint8_t get_hash_algo(const char *algo)
{
int i;
for (i = 0; i < PKEY_HASH__LAST; i++)
if (!strcmp(algo, pkey_hash_algo[i]))
return i;
return PKEY_HASH_SHA1;
}
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;
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 = 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)
return -1;
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);
RSA_free(key);
if (len < 0) {
log_err("RSA_private_encrypt() failed: %d\n", len);
return -1;
}
/* 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 || verbose >= LOG_INFO)
dump(sig, len);
return len;
@ -384,6 +602,75 @@ static int find_xattr(const char *list, int list_size, const char *xattr)
return 0;
}
static int hex_to_bin(char ch)
{
if ((ch >= '0') && (ch <= '9'))
return ch - '0';
ch = tolower(ch);
if ((ch >= 'a') && (ch <= 'f'))
return ch - 'a' + 10;
return -1;
}
static void pack_uuid(const char *uuid_str, char *to)
{
int i;
for (i = 0; i < 16; ++i) {
*to++ = (hex_to_bin(*uuid_str) << 4) |
(hex_to_bin(*(uuid_str + 1)));
uuid_str += 2;
switch (i) {
case 3:
case 5:
case 7:
case 9:
uuid_str++;
continue;
}
}
}
static int get_uuid(struct stat *st, char *uuid)
{
uint32_t dev;
unsigned minor, major;
char path[PATH_MAX], _uuid[37];
FILE *fp;
size_t len;
if (uuid_str[0] != '-') {
pack_uuid(uuid_str, uuid);
return 0;
}
dev = st->st_dev;
major = (dev & 0xfff00) >> 8;
minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
log_debug("dev: %u:%u\n", major, minor);
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;
}
len = fread(_uuid, 1, sizeof(_uuid), fp);
pclose(fp);
if (len != sizeof(_uuid)) {
log_err("fread() failed\n");
return -1;
}
pack_uuid(_uuid, uuid);
log_info("uuid: ");
log_dump(uuid, 16);
return 0;
}
static int calc_evm_hash(const char *file, unsigned char *hash)
{
struct stat st;
@ -395,6 +682,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
char xattr_value[1024];
char list[1024];
ssize_t list_size;
char uuid[16];
fd = open(file, 0);
if (fd < 0) {
@ -460,6 +748,19 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
log_err("EVP_DigestUpdate() failed\n");
return 1;
}
if (uuid_str) {
err = get_uuid(&st, uuid);
if (err)
return -1;
err = EVP_DigestUpdate(&ctx, (const unsigned char *)uuid, sizeof(uuid));
if (!err) {
log_err("EVP_DigestUpdate() failed\n");
return 1;
}
}
err = EVP_DigestFinal(&ctx, hash, &mdlen);
if (!err) {
log_err("EVP_DigestFinal() failed\n");
@ -479,7 +780,7 @@ static int sign_evm(const char *file, const char *key)
if (len <= 1)
return len;
len = sign_hash(hash, len, key, sig + 1);
len = sign_hash("sha1", hash, len, key, sig + 1);
if (len <= 1)
return len;
@ -536,18 +837,13 @@ static int add_file_hash(const char *file, EVP_MD_CTX *ctx)
return 0;
}
struct dirent_list {
struct dirent_list *next;
struct dirent de;
};
static int add_dir_hash(const char *file, EVP_MD_CTX *ctx)
{
int err;
struct dirent *de;
DIR *dir;
struct dirent_list *head = NULL, *pos, *prev, *cur;
uint64_t ino;
unsigned long long ino, off;
unsigned int type;
dir = opendir(file);
if (!dir) {
@ -556,40 +852,19 @@ static int add_dir_hash(const char *file, EVP_MD_CTX *ctx)
}
while ((de = readdir(dir))) {
/*log_debug("entry: ino: %lu, %s\n", de->d_ino, de->d_name);*/
for (prev = NULL, pos = head; pos; prev = pos, pos = pos->next) {
if (de->d_ino < pos->de.d_ino)
break;
}
cur = malloc(sizeof(*cur));
cur->de = *de;
cur->next = pos;
if (!head || !prev)
head = cur;
else
prev->next = cur;
}
for (cur = head; cur; cur = pos) {
pos = cur->next;
ino = cur->de.d_ino;
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));
ino = de->d_ino;
off = de->d_off;
type = de->d_type;
log_debug("entry: %s, ino: %llu, type: %u, off: %llu, reclen: %hu\n",
de->d_name, ino, type, off, de->d_reclen);
err = EVP_DigestUpdate(ctx, de->d_name, strlen(de->d_name));
/*err |= EVP_DigestUpdate(ctx, &off, sizeof(off));*/
err |= EVP_DigestUpdate(ctx, &ino, sizeof(ino));
err |= EVP_DigestUpdate(ctx, &type, sizeof(type));
if (!err) {
log_err("EVP_DigestUpdate() failed\n");
return 1;
}
err = EVP_DigestUpdate(ctx, &ino, sizeof(ino));
if (!err) {
log_err("EVP_DigestUpdate() failed\n");
return 1;
}
err = EVP_DigestUpdate(ctx, &cur->de.d_type, sizeof(cur->de.d_type));
if (!err) {
log_err("EVP_DigestUpdate() failed\n");
return 1;
}
free(cur);
}
closedir(dir);
@ -689,7 +964,7 @@ static int hash_ima(const char *file)
if (verbose >= LOG_INFO)
log_info("hash: ");
if (!xattr || verbose >= LOG_INFO)
if (sigdump || verbose >= LOG_INFO)
dump(hash, len + 1);
if (xattr) {
@ -720,21 +995,35 @@ static int sign_ima(const char *file, const char *key)
{
unsigned char hash[64];
unsigned char sig[1024] = "\x03";
char magic[] = "This Is A Crypto Signed Module";
int len, err;
len = calc_hash(file, hash);
if (len <= 1)
return len;
len = sign_hash(hash, len, key, sig + 1);
len = sign_hash(hash_algo, hash, len, key, sig + 1);
if (len <= 1)
return len;
/* add header */
len++;
if (modsig) {
/* add signature length */
*(uint16_t *)(sig + len) = __cpu_to_be16(len - 1);
len += sizeof(uint16_t);
memcpy(sig + len, magic, sizeof(magic) - 1);
len += sizeof(magic) - 1;
bin2file(file, "sig", sig + 1, len - 1);
return 0;
}
if (sigfile)
bin2file(file, "sig", sig, len + 1);
bin2file(file, "sig", sig + 1, len - 1);
if (xattr) {
err = setxattr(file, "security.ima", sig, len + 1, 0);
err = setxattr(file, "security.ima", sig, len, 0);
if (err < 0) {
log_err("setxattr failed: %s\n", file);
return err;
@ -754,9 +1043,7 @@ static int cmd_sign_ima(struct command *cmd)
return -1;
}
key = g_argv[optind++];
if (!key)
key = "/etc/keys/privkey_evm.pem";
key = keyfile ? : "/etc/keys/privkey_evm.pem";
return sign_ima(file, key);
@ -779,9 +1066,7 @@ static int cmd_sign_evm(struct command *cmd)
return -1;
}
key = g_argv[optind++];
if (!key)
key = "/etc/keys/privkey_evm.pem";
key = keyfile ? : "/etc/keys/privkey_evm.pem";
if (digsig) {
err = sign_ima(file, key);
@ -798,30 +1083,21 @@ static int cmd_sign_evm(struct command *cmd)
return sign_evm(file, key);
}
static int verify_hash(const unsigned char *hash, int size, unsigned char *sig, int siglen, const char *keyfile)
static int verify_hash_v1(const unsigned char *hash, int size, unsigned char *sig, int siglen, const char *keyfile)
{
int err, len;
SHA_CTX ctx;
unsigned char out[1024];
RSA *key = NULL, *key1;
FILE *fp;
RSA *key;
unsigned char sighash[20];
struct signature_hdr *hdr = (struct signature_hdr *)sig;
log_info("hash: ");
log_dump(hash, size);
fp = fopen(keyfile, "r");
if (!fp) {
log_err("Unable to open keyfile %s\n", keyfile);
return -1;
}
key1 = PEM_read_RSA_PUBKEY(fp, &key, NULL, NULL);
fclose(fp);
if (!key1) {
log_err("PEM_read_RSA_PUBKEY() failed\n");
key = read_pub_key(keyfile);
if (!key)
return 1;
}
SHA1_Init(&ctx);
SHA1_Update(&ctx, hash, size);
@ -850,6 +1126,50 @@ static int verify_hash(const unsigned char *hash, int size, unsigned char *sig,
return 0;
}
static int verify_hash_v2(const unsigned char *hash, int size, unsigned char *sig, int siglen, const char *keyfile)
{
int err, len;
unsigned char out[1024];
RSA *key;
struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
const struct RSA_ASN1_template *asn1;
log_info("hash: ");
log_dump(hash, size);
key = read_pub_key(keyfile);
if (!key)
return 1;
err = RSA_public_decrypt(siglen - sizeof(*hdr) - 2, sig + sizeof(*hdr) + 2, out, key, RSA_PKCS1_PADDING);
RSA_free(key);
if (err < 0) {
log_err("RSA_public_decrypt() failed: %d\n", err);
return 1;
}
len = err;
asn1 = &RSA_ASN1_templates[hdr->hash_algo];
if (len < asn1->size || memcmp(out, asn1->data, asn1->size)) {
log_err("Verification failed: %d\n", err);
return -1;
}
len -= asn1->size;
if (len != size || memcmp(out + asn1->size, hash, len)) {
log_err("Verification failed: %d\n", err);
return -1;
}
/*log_info("Verification is OK\n");*/
printf("Verification is OK\n");
return 0;
}
static int verify_evm(const char *file, const char *key)
{
unsigned char hash[20];
@ -884,57 +1204,76 @@ static int cmd_verify_evm(struct command *cmd)
return -1;
}
key = g_argv[optind++];
if (!key)
key = "/etc/keys/pubkey_evm.pem";
key = keyfile ? : x509 ?
"/etc/keys/x509_evm.der" :
"/etc/keys/pubkey_evm.pem";
return verify_evm(file, key);
}
static int cmd_convert(struct command *cmd)
static int verify_ima(const char *file, const char *key)
{
char *inkey, *outkey = NULL;
unsigned char pub[1024];
char name[20];
unsigned char hash[20];
unsigned char sig[1024];
int len;
uint8_t keyid[8];
inkey = g_argv[optind++];
if (!inkey)
inkey = "/etc/keys/pubkey_evm.pem";
else
outkey = g_argv[optind++];
len = calc_hash(file, hash);
if (len <= 1)
return len;
if (!outkey)
outkey = "pubkey_evm.bin";
if (xattr) {
len = getxattr(file, "security.ima", sig, sizeof(sig));
if (len < 0) {
log_err("getxattr failed\n");
return len;
}
}
log_info("Convert public key %s to %s\n", inkey, outkey);
if (sigfile) {
void *tmp;
tmp = file2bin(file, "sig", &len);
memcpy(sig, tmp, len);
free(tmp);
}
len = read_key(inkey, pub);
if (len < 0)
if (sig[0] != 0x03) {
log_err("security.ima has no signature\n");
return -1;
}
calc_keyid(keyid, name, pub, len);
return verify_hash(hash, sizeof(hash), sig + 1, len - 1, key);
}
bin2file(outkey, name, pub, len);
static int cmd_verify_ima(struct command *cmd)
{
char *key, *file = g_argv[optind++];
return 0;
if (!file) {
log_err("Parameters missing\n");
print_usage(cmd);
return -1;
}
key = keyfile ? : x509 ?
"/etc/keys/x509_evm.der" :
"/etc/keys/pubkey_evm.pem";
return verify_ima(file, key);
}
static int cmd_import(struct command *cmd)
{
char *inkey, *ring = NULL;
unsigned char _key[1024], *key = _key;
int id, len;
unsigned char _pub[1024], *pub = _pub;
int id, len, err = -1;
char name[20];
uint8_t keyid[8];
RSA *key = NULL;
inkey = g_argv[optind++];
if (!inkey) {
if (binkey)
inkey = "/etc/keys/pubkey_evm.bin";
else
inkey = "/etc/keys/pubkey_evm.pem";
inkey = x509 ? "/etc/keys/x509_evm.der" :
"/etc/keys/pubkey_evm.pem";
} else
ring = g_argv[optind++];
@ -943,33 +1282,39 @@ static int cmd_import(struct command *cmd)
else
id = atoi(ring);
if (binkey) {
key = file2bin(inkey, &len);
if (!key)
return -1;
} else {
len = read_key(inkey, key);
if (len < 0)
return -1;
}
key = read_pub_key(inkey);
if (!key)
goto out;
calc_keyid(keyid, name, key, len);
if (x509) {
pub = file2bin(inkey, NULL, &len);
if (!pub)
goto out;
calc_keyid_v2((uint32_t *)keyid, name, key);
} else {
len = key2bin(key, pub);
calc_keyid_v1(keyid, name, pub, 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);
id = add_key(x509 ? "asymmetric" : "user", x509 ? NULL : name, pub, len, id);
if (id < 0) {
log_err("add_key failed\n");
return -1;
goto out;
}
log_info("keyid: %d\n", id);
printf("%d\n", id);
if (binkey)
free(key);
err = 0;
out:
if (key)
RSA_free(key);
if (x509)
free(pub);
return 0;
return err;
}
#define MAX_KEY_SIZE 128
@ -989,7 +1334,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
char list[1024];
ssize_t list_size;
key = file2bin(keyfile, &keylen);
key = file2bin(keyfile, NULL, &keylen);
if (!key) {
log_err("Unable to read a key: %s\n\n", keyfile);
return -1;
@ -1122,9 +1467,7 @@ static int cmd_hmac_evm(struct command *cmd)
return -1;
}
key = g_argv[optind++];
if (!key)
key = "/etc/keys/privkey_evm.pem";
key = keyfile ? : "/etc/keys/privkey_evm.pem";
if (digsig) {
err = sign_ima(file, key);
@ -1215,7 +1558,9 @@ static void usage(void)
" -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"
" -m, --modsig store module signature in .sig file instead of xattr\n"
" -x, --x509 signing key is in x509 DER format (signing v2 for using asymmetric keys)\n"
" -k, --key path to signing key (default keys are /etc/keys/{privkey,pubkey}_evm.pem)\n"
" -p, --pass password for encrypted signing key\n"
" -n print result to stdout instead of setting xattr\n"
" -v increase verbosity level\n"
@ -1225,14 +1570,14 @@ static void usage(void)
struct command cmds[] = {
{"help", cmd_help, 0, "<command>"},
{"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"},
{"sign", cmd_sign_evm, 0, "[--imahash | --imasig ] [--pass password] file [key]", "Sign file metadata.\n"},
{"import", cmd_import, 0, "[--x509] pubkey keyring", "Import public key into the keyring.\n"},
{"sign", cmd_sign_evm, 0, "[--imahash | --imasig ] [--key key] [--pass password] file", "Sign file metadata.\n"},
{"verify", cmd_verify_evm, 0, "file", "Verify EVM signature (for debugging).\n"},
{"ima_sign", cmd_sign_ima, 0, "[--sigfile] [--pass password] file [key]", "Make file content signature.\n"},
{"ima_sign", cmd_sign_ima, 0, "[--sigfile | --modsig] [--key key] [--pass password] file", "Make file content signature.\n"},
{"ima_verify", cmd_verify_ima, 0, "file", "Verify IMA signature (for debugging).\n"},
{"ima_hash", cmd_hash_ima, 0, "file", "Make file content hash.\n"},
#ifdef DEBUG
{"hmac", cmd_hmac_evm, 0, "[--imahash | --imasig ] file [key]", "Sign file metadata with HMAC using symmetric key (for testing purpose).\n"},
{"hmac", cmd_hmac_evm, 0, "[--imahash | --imasig ] file", "Sign file metadata with HMAC using symmetric key (for testing purpose).\n"},
#endif
{0, 0, 0, NULL}
};
@ -1242,9 +1587,12 @@ static struct option opts[] = {
{"imasig", 0, 0, 's'},
{"imahash", 0, 0, 'd'},
{"hashalgo", 1, 0, 'a'},
{"bin", 0, 0, 'b'},
{"pass", 1, 0, 'p'},
{"sigfile", 0, 0, 'f'},
{"modsig", 0, 0, 'm'},
{"uuid", 1, 0, 'u'},
{"x509", 0, 0, 'x'},
{"key", 1, 0, 'k'},
{}
};
@ -1256,8 +1604,11 @@ int main(int argc, char *argv[])
g_argv = argv;
g_argc = argc;
sign_hash = sign_hash_v1;
verify_hash = verify_hash_v1;
while (1) {
c = getopt_long(argc, argv, "hvnsda:bp:f", opts, &lind);
c = getopt_long(argc, argv, "hvnsda:p:fu:xk:", opts, &lind);
if (c == -1)
break;
@ -1278,18 +1629,32 @@ int main(int argc, char *argv[])
case 'n':
/* do not set Extended Attributes... just print signature */
xattr = 0;
sigdump = 1;
break;
case 'a':
hash_algo = optarg;
break;
case 'b':
binkey = 1;
break;
case 'p':
keypass = optarg;
break;
case 'f':
sigfile = 1;
xattr = 0;
break;
case 'm':
modsig = 1;
xattr = 0;
break;
case 'u':
uuid_str = optarg;
break;
case 'x':
x509 = 1;
sign_hash = sign_hash_v2;
verify_hash = verify_hash_v2;
break;
case 'k':
keyfile = optarg;
break;
case '?':
exit(1);

View File

@ -1,7 +0,0 @@
pkglibexec_PROGRAMS = openclose
openclose_SOURCES = openclose.c
dist_pkglibexec_SCRIPTS = evm_enable.sh evm_genkey.sh evm_sign_all.sh evm_sign_modules.sh \
ima_fix_dir.sh evm_hmac_all.sh evm_hmac_modules.sh

View File

@ -1,25 +0,0 @@
#!/bin/sh
# import EVM HMAC key
keyctl clear @u
keyctl add user kmk "testing123" @u
keyctl add encrypted evm-key "load `cat /etc/keys/evm-key`" @u
# import Moule public key
mod_id=`keyctl newring _module @u`
evmctl import /etc/keys/pubkey_evm.pem $mod_id
# import IMA public key
ima_id=`keyctl newring _ima @u`
evmctl import /etc/keys/pubkey_evm.pem $ima_id
# import EVM public key
evm_id=`keyctl newring _evm @u`
evmctl import /etc/keys/pubkey_evm.pem $evm_id
# enable EVM
echo "1" > /sys/kernel/security/evm
# enable module checking
echo "1" > /sys/kernel/security/ima/module_check

View File

@ -1,8 +0,0 @@
#!/bin/sh
keyctl add user kmk "testing123" @u
key=`keyctl add encrypted evm-key "new user:kmk 32" @u`
keyctl print $key >/etc/keys/evm-key
keyctl list @u

View File

@ -1,14 +0,0 @@
#!/bin/sh
verbose=""
if [ "$1" = "-v" ] ; then
verbose="-v"
shift 1
fi
dir=${1:-/}
echo "Label: $dir"
find $dir \( -fstype rootfs -o -fstype ext3 -o -fstype ext4 \) \( -type f -o -type d \) -exec evmctl hmac --imahash $verbose '{}' \;

View File

@ -1,14 +0,0 @@
#!/bin/sh
verbose=""
if [ "$1" = "-v" ] ; then
verbose="-v"
shift 1
fi
dir=${1:-/lib/modules}
echo "HMAC modules: $dir"
find $dir -name "*.ko" -type f -exec evmctl hmac --imasig $verbose '{}' \;

View File

@ -1,14 +0,0 @@
#!/bin/sh
verbose=""
if [ "$1" = "-v" ] ; then
verbose="-v"
shift 1
fi
dir=${1:-/}
echo "Label: $dir"
find $dir \( -fstype rootfs -o -fstype ext3 -o -fstype ext4 \) -type f -exec evmctl sign --imahash $verbose '{}' \;

View File

@ -1,14 +0,0 @@
#!/bin/sh
verbose=""
if [ "$1" = "-v" ] ; then
verbose="-v"
shift 1
fi
dir=${1:-/lib/modules}
echo "Signing modules: $dir"
find $dir -name "*.ko" -type f -exec evmctl sign --imasig $verbose '{}' \;

View File

@ -1,8 +0,0 @@
#!/bin/sh
dir=${1:-/}
echo "Fixing dir: $dir"
find $dir \( -fstype rootfs -o -fstype ext3 -o -fstype ext4 \) -type f -exec openclose '{}' \;

View File

@ -1,20 +0,0 @@
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int fd;
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror("open()");
exit(1);
}
close(fd);
return 0;
}