1
0
mirror of https://git.code.sf.net/p/linux-ima/ima-evm-utils synced 2025-07-04 06:25:15 +02:00

21 Commits
v0.8 ... v0.9

Author SHA1 Message Date
3d9bdc1de2 Release version 0.9
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-09-23 15:09:05 +03:00
f4fe504f98 Add 'evmctl --version' version reporting
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-09-23 14:40:18 +03:00
05d1f74559 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 <d.kasatkin@samsung.com>
2014-09-23 14:40:14 +03:00
a99a8a517c 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 <d.kasatkin@samsung.com>
2014-09-17 16:02:22 +03:00
6261753e1e Use <linux/xattr.h> for security xattrs
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-09-17 16:01:12 +03:00
47d3048ce1 Make error and help messages more understandable
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-09-17 15:16:59 +03:00
14a90aa87c 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 <d.kasatkin@samsung.com>
2014-09-11 16:50:30 +03:00
635288f70f 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 <d.kasatkin@samsung.com>
2014-09-11 16:50:30 +03:00
d5891ad96d Include example scripts to distribution and installation
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-09-11 16:50:30 +03:00
dede7cf850 Fix checkpatch errors
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-09-11 16:50:30 +03:00
8f04d131ac 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 <fin@linux.vnet.ibm.com>
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-09-11 16:50:20 +03:00
29adc34d35 Remove local ioctl definitions and use <linux/fs.h>
Use standard flags, supported by ext2/3/4

Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-06-25 18:54:55 +03:00
0636083acd Remove code duplication
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-06-25 18:54:55 +03:00
02594d3003 Switch to HMAC attribute mask
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-06-25 18:54:41 +03:00
c958062023 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 <d.kasatkin@samsung.com>
2014-06-24 15:54:37 +03:00
9bc24ee556 Use defined xattr types
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-06-24 15:54:37 +03:00
c5a2992bc8 Fix resource leak
Signed-off-by: Zbigniew Jasinski <z.jasinski@samsung.com>
2014-06-24 15:54:37 +03:00
dddef9172e make it possible to provide keyring id in hexadecimal format
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-06-13 12:59:14 +03:00
24c9e6adc7 add extra auto built files to .gitignore
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-06-13 12:58:16 +03:00
1a4c9ed2d6 Fix memory leak
Signed-off-by: Zbigniew Jasinski <z.jasinski@samsung.com>
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-05-30 17:58:46 +03:00
3a1ef5f5c7 Use proper loff_t type for file size
Signed-off-byL Zbigniew Jasinski <z.jasinski@samsung.com>
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-05-30 17:46:43 +03:00
9 changed files with 869 additions and 458 deletions

5
.gitignore vendored
View File

@ -2,6 +2,8 @@
*~ *~
# Generated by autotools # Generated by autotools
.libs
m4
.deps .deps
aclocal.m4 aclocal.m4
autom4te.cache autom4te.cache
@ -20,9 +22,12 @@ compile
libtool libtool
ltmain.sh ltmain.sh
# Compiled executables # Compiled executables
*.o *.o
*.a *.a
*.lo
*.la
src/evmctl src/evmctl
tests/openclose tests/openclose
config.h config.h

View File

@ -1,3 +1,12 @@
2014-09-23 Dmitry Kasatkin <d.kasatkin@samsung.com>
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 <d.kasatkin@samsung.com> 2014-05-05 Dmitry Kasatkin <d.kasatkin@samsung.com>
version 0.8 version 0.8

View File

@ -1,6 +1,10 @@
SUBDIRS = src SUBDIRS = src
dist_man_MANS = evmctl.1
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)
CLEANFILES = *.html *.xsl
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
@ -19,4 +23,20 @@ rpm: $(tarname)
cp $(tarname) $(SRCS)/ cp $(tarname) $(SRCS)/
rpmbuild -ba --nodeps $(SPEC) 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:
asciidoc -d manpage -b docbook -o evmctl.1.xsl README
xsltproc --nonet -o $@ $(MANPAGE_DOCBOOK_XSL) evmctl.1.xsl
rm -f evmctl.1.xsl
rmman:
rm -f evmctl.1
doc: evmctl.1.html rmman evmctl.1
.PHONY: $(tarname) .PHONY: $(tarname)

458
README
View File

@ -1,47 +1,185 @@
ima-evm-utils - IMA/EVM signing utility EVMCTL(1)
========================================= =========
Contents: NAME
----
1. Key and signature formats evmctl - IMA/EVM signing utility
2. Key generation
3. Initialization
4. Signing SYNOPSIS
--------
evmctl [options] <command> [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
--------
--version
help <command>
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 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 Key and signature formats
------------------------- -------------------------
EVM support (v2) in latest version of the kernel adds the file system UUID to Linux integrity subsystem supports two type of signature and respectively two
the HMAC calculation. It is controlled by the CONFIG_EVM_HMAC_VERSION and key formats.
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.
Latest kernel got IMA/EVM support for using X509 certificates and asymmetric key First key format (v1) is pure RSA key encoded in PEM a format and uses own signature
support for verifying digital signatures. This version uses x509 format by default. format. It is now non-default format and requires to provide evmctl '--rsa' option
Use '--rsa' or '-1' parameter to use old signature format and API. 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
-------------- ----------------
Generate private key in plain text format Integrity subsystem uses dedicated IMA/EVM keyrings to search for signature verification
keys - '_ima' and '_evm' respectively.
$ openssl genrsa -out privkey_evm.pem 1024 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 encrypted private key
$ openssl genrsa -des3 -out privkey_evm.pem 1024 Generate EVM encrypted keys
---------------------------
Make encrypted private key from unencrypted EVM encrypted key is used for EVM HMAC calculation:
$ openssl rsa -in /etc/keys/privkey_evm.pem -out privkey_evm_enc.pem -des3 # 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
Generate self-signed X509 certificate and private key for using kernel asymmetric # create the EVM encrypted key
keys support keyctl add encrypted evm-key "new user:kmk 32" @u
keyctl pipe `keyctl search @u encrypted evm-key` >/etc/keys/evm-key
$ openssl req -new -nodes -utf8 -sha1 -days 36500 -batch \
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:
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 public key certificate and private key for using kernel
asymmetric keys support:
openssl req -new -nodes -utf8 -sha1 -days 36500 -batch \
-x509 -config x509_evm.genkey \ -x509 -config x509_evm.genkey \
-outform DER -out x509_evm.der -keyout privkey_evm.pem -outform DER -out x509_evm.der -keyout privkey_evm.pem
@ -68,88 +206,232 @@ Configuration file x509_evm.genkey:
# EOF # EOF
Get public key Generate public key for using RSA key format:
$ 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
$ 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
Generation of EVM keys Copy keys to /etc/keys:
$ # create and save the kernel master key (user type) cp pubkey_evm.pem /etc/keys
$ keyctl add user kmk "`dd if=/dev/urandom bs=1 count=32 2>/dev/null`" @u scp pubkey_evm.pem target:/etc/keys
$ keyctl pipe `keyctl search @u user kmk` > /etc/keys/kmk or
$ # create the EVM encrypted key cp x509_evm.pem /etc/keys
$ keyctl add encrypted evm-key "new user:kmk 32" @u scp x509_evm.pem target:/etc/keys
$ keyctl pipe `keyctl search @u encrypted evm-key` >/etc/keys/evm-key
Initialization Generate trusted keys
-------------- ---------------------
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.
Configuration file ima-local-ca.genkey:
# Begining of the file
[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
prompt = no
string_mask = utf8only
x509_extensions = v3_ca
[ req_distinguished_name ]
O = IMA-CA
CN = IMA/EVM certificate signing key
emailAddress = ca@ima-ca
[ v3_ca ]
basicConstraints=CA:TRUE
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
# keyUsage = cRLSign, keyCertSign
# EOF
Generate private key and X509 public key certificate:
openssl req -new -x509 -utf8 -sha1 -days 3650 -batch -config $GENKEY \
-outform DER -out ima-local-ca.x509 -keyout ima-local-ca.priv
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`
[ v3_usr ]
basicConstraints=critical,CA:FALSE
#basicConstraints=CA:FALSE
keyUsage=digitalSignature
#keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid
#authorityKeyIdentifier=keyid,issuer
# EOF
Generate private key and X509 public key certificate signing request:
openssl req -new -nodes -utf8 -sha1 -days 365 -batch -config $GENKEY \
-out csr_ima.pem -keyout privkey_ima.pem
Sign X509 public key certificate signing request with local IMA CA private key:
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 data and metadata
---------------------------
Default key locations:
Private RSA key: /etc/keys/privkey_evm.pem
Public RSA key: /etc/keys/pubkey_evm.pem
X509 certificate: /etc/keys/x509_evm.der
Options to remember: '-k', '-r', '--rsa', '--uuid', '--smack'.
Sign file with EVM signature and calculate hash value for IMA:
evmctl sign --imahash test.txt
Sign file with both IMA and EVM signatures:
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 IMA/EVM initialization should be normally done from initial RAM file system
before mounting root filesystem. before mounting root filesystem.
Here is an example script /etc/initramfs-tools/scripts/local-top/ima.sh Here is Ubuntu initramfs example script (/etc/initramfs-tools/scripts/local-top/ima.sh)
# import EVM HMAC key # mount securityfs if not mounted
keyctl clear @u 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 cat /etc/keys/kmk | keyctl padd user kmk @u
keyctl add encrypted evm-key "load `cat /etc/keys/evm-key`" @u keyctl add encrypted evm-key "load `cat /etc/keys/evm-key`" @u
# OR
# import IMA public key # b) import EVM trusted key
ima_id=`keyctl newring _ima @u` keyctl add trusted kmk "load `cat /etc/keys/kmk`" @u
evmctl --rsa import /etc/keys/pubkey_evm.pem $ima_id keyctl add encrypted evm-key "load `cat /etc/keys/evm-key`" @u
# import EVM public key
evm_id=`keyctl newring _evm @u`
evmctl --rsa import /etc/keys/pubkey_evm.pem $evm_id
# enable EVM # enable EVM
echo "1" > /sys/kernel/security/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:
Import X509 certificate into the kernel keyring (since kernel 3.9?) # protect EVM keyring
keyctl setperm $evm_id 0x0b0b0000
$ evmctl import /etc/keys/x509_evm.der `keyctl search @u keyring _ima` # protect IMA keyring
$ evmctl import /etc/keys/x509_evm.der `keyctl search @u keyring _evm` 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
Signing 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, <dmitry.kasatkin at gmail.com> and others.
RESOURCES
---------
http://sourceforge.net/p/linux-ima/wiki/Home
http://sourceforge.net/p/linux-ima/ima-evm-utils
COPYING
------- -------
Default public key: /etc/keys/pubkey_evm.pem Copyright \(C) 2012 - 2014 Linux Integrity Project. Free use of this software is granted under
Default private key: /etc/keys/privkey_evm.pem the terms of the GNU Public License (GPL).
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
$ evmctl sign [-u] [-1] --imahash test.txt
Sign file with both IMA and EVM signatures - for immutable files
$ evmctl sign [-u] [-1] --imasig test.txt
Sign file with IMA signature - for immutable files
$ evmctl ima_sign [-1] test.txt
Label whole filesystem with EVM signatures
$ 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
$ find / \( -fstype rootfs -o -fstype ext4 \) -exec sh -c "< '{}'" \;

View File

@ -1,7 +1,7 @@
# autoconf script # autoconf script
AC_PREREQ([2.65]) 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 AM_INIT_AUTOMAKE
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])

View File

@ -1,5 +1,5 @@
Name: ima-evm-utils Name: ima-evm-utils
Version: 0.8 Version: 0.9
Release: 1%{?dist} Release: 1%{?dist}
Summary: ima-evm-utils - IMA/EVM control utility Summary: ima-evm-utils - IMA/EVM control utility
Group: System/Libraries Group: System/Libraries

View File

@ -50,9 +50,9 @@
#include <string.h> #include <string.h>
#include <dirent.h> #include <dirent.h>
#include <attr/xattr.h> #include <attr/xattr.h>
#include <linux/xattr.h>
#include <getopt.h> #include <getopt.h>
#include <keyutils.h> #include <keyutils.h>
#include <asm/byteorder.h>
#include <ctype.h> #include <ctype.h>
#include <openssl/sha.h> #include <openssl/sha.h>
@ -65,14 +65,27 @@
#include "imaevm.h" #include "imaevm.h"
static char *evm_config_xattrnames[] = { static char *evm_default_xattrs[] = {
"security.selinux", XATTR_NAME_SELINUX,
"security.SMACK64", XATTR_NAME_SMACK,
"security.ima", XATTR_NAME_IMA,
"security.capability", XATTR_NAME_CAPS,
NULL 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 { struct command {
char *name; char *name;
int (*func)(struct command *cmd); int (*func)(struct command *cmd);
@ -84,18 +97,21 @@ struct command {
static int g_argc; static int g_argc;
static char **g_argv; static char **g_argv;
static int xattr = 1; static int xattr = 1;
static bool check_xattr;
static int sigdump; static int sigdump;
static int digest; static int digest;
static int digsig; static int digsig;
static char *keypass;
static int sigfile; static int sigfile;
static int x509 = 1; static char *uuid_str;
static char *uuid_str = "+";
static char *search_type; static char *search_type;
static int recursive; static int recursive;
static int msize; static int msize;
static dev_t fs_dev; 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); typedef int (*find_cb_t)(const char *path);
static int find(const char *path, int dts, find_cb_t func); static int find(const char *path, int dts, find_cb_t func);
@ -105,10 +121,6 @@ static int find(const char *path, int dts, find_cb_t func);
#define CHR_MASK (1 << DT_CHR) #define CHR_MASK (1 << DT_CHR)
#define BLK_MASK (1 << DT_BLK) #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[]; struct command cmds[];
static void print_usage(struct command *cmd); static void print_usage(struct command *cmd);
@ -127,7 +139,7 @@ static int bin2file(const char *file, const char *ext, const unsigned char *data
fp = fopen(name, "w"); fp = fopen(name, "w");
if (!fp) { if (!fp) {
log_err("Unable to open %s for writing\n", name); log_err("Failed to open: %s\n", name);
return -1; return -1;
} }
err = fwrite(data, len, 1, fp); err = fwrite(data, len, 1, fp);
@ -152,7 +164,7 @@ static unsigned char *file2bin(const char *file, const char *ext, int *size)
len = get_filesize(name); len = get_filesize(name);
fp = fopen(name, "r"); fp = fopen(name, "r");
if (!fp) { if (!fp) {
log_err("Unable to open %s\n", name); log_err("Failed to open: %s\n", name);
return NULL; return NULL;
} }
data = malloc(len); data = malloc(len);
@ -164,215 +176,6 @@ static unsigned char *file2bin(const char *file, const char *ext, int *size)
return data; 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) static int find_xattr(const char *list, int list_size, const char *xattr)
{ {
int len; int len;
@ -470,7 +273,7 @@ static int get_uuid(struct stat *st, char *uuid)
FILE *fp; FILE *fp;
size_t len; size_t len;
if (uuid_str[0] != '+') if (hmac_flags & HMAC_FLAG_UUID_SET)
return pack_uuid(uuid_str, uuid); return pack_uuid(uuid_str, uuid);
dev = st->st_dev; dev = st->st_dev;
@ -481,19 +284,18 @@ static int get_uuid(struct stat *st, char *uuid)
sprintf(path, "blkid -s UUID -o value /dev/block/%u:%u", major, minor); sprintf(path, "blkid -s UUID -o value /dev/block/%u:%u", major, minor);
fp = popen(path, "r"); fp = popen(path, "r");
if (!fp) { if (!fp)
log_err("popen() failed\n"); goto err;
return -1;
}
len = fread(_uuid, 1, sizeof(_uuid), fp); len = fread(_uuid, 1, sizeof(_uuid), fp);
pclose(fp); pclose(fp);
if (len != sizeof(_uuid)) { if (len != sizeof(_uuid))
log_err("fread() failed\n"); goto err;
return -1;
}
return pack_uuid(_uuid, uuid); 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) static int calc_evm_hash(const char *file, unsigned char *hash)
@ -512,7 +314,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
int hmac_size; int hmac_size;
if (lstat(file, &st)) { if (lstat(file, &st)) {
log_err("lstat() failed\n"); log_err("Failed to stat: %s\n", file);
return -1; return -1;
} }
@ -520,11 +322,12 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
/* we cannot at the momement to get generation of special files.. /* we cannot at the momement to get generation of special files..
* kernel API does not support it */ * kernel API does not support it */
int fd = open(file, 0); int fd = open(file, 0);
if (fd < 0) { if (fd < 0) {
log_err("Unable to open %s\n", file); log_err("Failed to open: %s\n", file);
return -1; return -1;
} }
if (ioctl(fd, EXT34_IOC_GETVERSION, &generation)) { if (ioctl(fd, FS_IOC_GETVERSION, &generation)) {
log_err("ioctl() failed\n"); log_err("ioctl() failed\n");
return -1; return -1;
} }
@ -569,6 +372,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
if (msize == 0) { if (msize == 0) {
struct h_misc *hmac = (struct h_misc *)&hmac_misc; struct h_misc *hmac = (struct h_misc *)&hmac_misc;
hmac_size = sizeof(*hmac); hmac_size = sizeof(*hmac);
hmac->ino = st.st_ino; hmac->ino = st.st_ino;
hmac->generation = generation; hmac->generation = generation;
@ -577,6 +381,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
hmac->mode = st.st_mode; hmac->mode = st.st_mode;
} else if (msize == 64) { } else if (msize == 64) {
struct h_misc_64 *hmac = (struct h_misc_64 *)&hmac_misc; struct h_misc_64 *hmac = (struct h_misc_64 *)&hmac_misc;
hmac_size = sizeof(*hmac); hmac_size = sizeof(*hmac);
hmac->ino = st.st_ino; hmac->ino = st.st_ino;
hmac->generation = generation; hmac->generation = generation;
@ -585,6 +390,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
hmac->mode = st.st_mode; hmac->mode = st.st_mode;
} else { } else {
struct h_misc_32 *hmac = (struct h_misc_32 *)&hmac_misc; struct h_misc_32 *hmac = (struct h_misc_32 *)&hmac_misc;
hmac_size = sizeof(*hmac); hmac_size = sizeof(*hmac);
hmac->ino = st.st_ino; hmac->ino = st.st_ino;
hmac->generation = generation; hmac->generation = generation;
@ -602,7 +408,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
return 1; return 1;
} }
if (*uuid_str != '-') { if (hmac_flags & HMAC_FLAG_UUID) {
err = get_uuid(&st, uuid); err = get_uuid(&st, uuid);
if (err) if (err)
return -1; return -1;
@ -626,7 +432,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
static int sign_evm(const char *file, const char *key) static int sign_evm(const char *file, const char *key)
{ {
unsigned char hash[20]; unsigned char hash[20];
unsigned char sig[1024] = "\x03"; unsigned char sig[1024];
int len, err; int len, err;
len = calc_evm_hash(file, hash); len = calc_evm_hash(file, hash);
@ -637,8 +443,15 @@ static int sign_evm(const char *file, const char *key)
if (len <= 1) if (len <= 1)
return len; return len;
/* add header */
len++;
sig[0] = EVM_IMA_XATTR_DIGSIG;
if (sigdump || params.verbose >= LOG_INFO)
dump(sig, len);
if (xattr) { if (xattr) {
err = lsetxattr(file, "security.evm", sig, len + 1, 0); err = lsetxattr(file, "security.evm", sig, len, 0);
if (err < 0) { if (err < 0) {
log_err("setxattr failed: %s\n", file); log_err("setxattr failed: %s\n", file);
return err; return err;
@ -650,21 +463,33 @@ static int sign_evm(const char *file, const char *key)
static int hash_ima(const char *file) static int hash_ima(const char *file)
{ {
unsigned char hash[65] = "\x01"; /* MAX hash size + 1 */ unsigned char hash[66]; /* MAX hash size + 2 */
int len, err; int len, err, offset;
int algo = get_hash_algo(params.hash_algo);
len = ima_calc_hash(file, hash + 1); if (algo > PKEY_HASH_SHA1) {
hash[0] = IMA_XATTR_DIGEST_NG;
hash[1] = algo;
offset = 2;
} else {
hash[0] = IMA_XATTR_DIGEST;
offset = 1;
}
len = ima_calc_hash(file, hash + offset);
if (len <= 1) if (len <= 1)
return len; return len;
len += offset;
if (params.verbose >= LOG_INFO) if (params.verbose >= LOG_INFO)
log_info("hash: "); log_info("hash: ");
if (sigdump || params.verbose >= LOG_INFO) if (sigdump || params.verbose >= LOG_INFO)
dump(hash, len + 1); dump(hash, len);
if (xattr) { if (xattr) {
err = lsetxattr(file, "security.ima", hash, len + 1, 0); err = lsetxattr(file, "security.ima", hash, len, 0);
if (err < 0) { if (err < 0) {
log_err("setxattr failed: %s\n", file); log_err("setxattr failed: %s\n", file);
return err; return err;
@ -690,7 +515,7 @@ static int cmd_hash_ima(struct command *cmd)
static int sign_ima(const char *file, const char *key) static int sign_ima(const char *file, const char *key)
{ {
unsigned char hash[64]; unsigned char hash[64];
unsigned char sig[1024] = "\x03"; unsigned char sig[1024];
int len, err; int len, err;
len = ima_calc_hash(file, hash); len = ima_calc_hash(file, hash);
@ -703,6 +528,10 @@ static int sign_ima(const char *file, const char *key)
/* add header */ /* add header */
len++; len++;
sig[0] = EVM_IMA_XATTR_DIGSIG;
if (sigdump || params.verbose >= LOG_INFO)
dump(sig, len);
if (sigfile) if (sigfile)
bin2file(file, "sig", sig, len); bin2file(file, "sig", sig, len);
@ -731,11 +560,13 @@ static int get_file_type(const char *path, const char *search_type)
dts |= DIR_MASK; break; dts |= DIR_MASK; break;
case 's': case 's':
dts |= BLK_MASK | CHR_MASK | LNK_MASK; break; dts |= BLK_MASK | CHR_MASK | LNK_MASK; break;
case 'x':
check_xattr = true; break;
case 'm': case 'm':
/* stay within the same filesystem*/ /* stay within the same filesystem*/
err = lstat(path, &st); err = lstat(path, &st);
if (err < 0) { if (err < 0) {
log_err("stat() failed\n"); log_err("Failed to stat: %s\n", path);
return err; return err;
} }
fs_dev = st.st_dev; /* filesystem to start from */ fs_dev = st.st_dev; /* filesystem to start from */
@ -882,12 +713,12 @@ static int verify_evm(const char *file)
len = lgetxattr(file, "security.evm", sig, sizeof(sig)); len = lgetxattr(file, "security.evm", sig, sizeof(sig));
if (len < 0) { if (len < 0) {
log_err("getxattr failed\n"); log_err("getxattr failed: %s\n", file);
return len; return len;
} }
if (sig[0] != 0x03) { if (sig[0] != 0x03) {
log_err("security.evm has not signature\n"); log_err("security.evm has no signature\n");
return -1; return -1;
} }
@ -915,14 +746,14 @@ static int verify_ima(const char *file)
if (xattr) { if (xattr) {
len = lgetxattr(file, "security.ima", sig, sizeof(sig)); len = lgetxattr(file, "security.ima", sig, sizeof(sig));
if (len < 0) { if (len < 0) {
log_err("getxattr failed\n"); log_err("getxattr failed: %s\n", file);
return len; return len;
} }
} }
if (sigfile) { if (sigfile) {
void *tmp; void *tmp = file2bin(file, "sig", &len);
tmp = file2bin(file, "sig", &len);
memcpy(sig, tmp, len); memcpy(sig, tmp, len);
free(tmp); free(tmp);
} }
@ -954,7 +785,7 @@ static int cmd_import(struct command *cmd)
inkey = g_argv[optind++]; inkey = g_argv[optind++];
if (!inkey) { if (!inkey) {
inkey = x509 ? "/etc/keys/x509_evm.der" : inkey = params.x509 ? "/etc/keys/x509_evm.der" :
"/etc/keys/pubkey_evm.pem"; "/etc/keys/pubkey_evm.pem";
} else } else
ring = g_argv[optind++]; ring = g_argv[optind++];
@ -963,7 +794,11 @@ static int cmd_import(struct command *cmd)
if (ring) { if (ring) {
if (ring[0] != '@') { if (ring[0] != '@') {
id = atoi(ring); int base = 10;
if (ring[0] == '0' && ring[1] == 'x')
base = 16;
id = strtoul(ring, NULL, base);
} else { } else {
if (strcmp(ring, "@t") == 0) if (strcmp(ring, "@t") == 0)
id = -1; id = -1;
@ -980,11 +815,11 @@ static int cmd_import(struct command *cmd)
} }
} }
key = read_pub_key(inkey, x509); key = read_pub_key(inkey, params.x509);
if (!key) if (!key)
return 1; return 1;
if (x509) { if (params.x509) {
pub = file2bin(inkey, NULL, &len); pub = file2bin(inkey, NULL, &len);
if (!pub) if (!pub)
goto out; goto out;
@ -996,7 +831,7 @@ static int cmd_import(struct command *cmd)
log_info("Importing public key %s from file %s into keyring %d\n", name, inkey, id); 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) { if (id < 0) {
log_err("add_key failed\n"); log_err("add_key failed\n");
err = id; err = id;
@ -1004,7 +839,7 @@ static int cmd_import(struct command *cmd)
log_info("keyid: %d\n", id); log_info("keyid: %d\n", id);
printf("%d\n", id); printf("%d\n", id);
} }
if (x509) if (params.x509)
free(pub); free(pub);
out: out:
RSA_free(key); RSA_free(key);
@ -1032,12 +867,12 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
key = file2bin(keyfile, NULL, &keylen); key = file2bin(keyfile, NULL, &keylen);
if (!key) { 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; return -1;
} }
if (keylen > sizeof(evmkey)) { if (keylen > sizeof(evmkey)) {
log_err("key is too long\n"); log_err("key is too long: %d\n", keylen);
goto out; goto out;
} }
@ -1046,7 +881,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
memset(evmkey + keylen, 0, sizeof(evmkey) - keylen); memset(evmkey + keylen, 0, sizeof(evmkey) - keylen);
if (lstat(file, &st)) { if (lstat(file, &st)) {
log_err("lstat() failed\n"); log_err("Failed to stat: %s\n", file);
goto out; goto out;
} }
@ -1054,11 +889,12 @@ 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.. /* we cannot at the momement to get generation of special files..
* kernel API does not support it */ * kernel API does not support it */
int fd = open(file, 0); int fd = open(file, 0);
if (fd < 0) { if (fd < 0) {
log_err("Unable to open %s\n", file); log_err("Failed to open %s\n", file);
goto out; goto out;
} }
if (ioctl(fd, EXT34_IOC_GETVERSION, &generation)) { if (ioctl(fd, FS_IOC_GETVERSION, &generation)) {
log_err("ioctl() failed\n"); log_err("ioctl() failed\n");
goto out; goto out;
} }
@ -1069,7 +905,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
list_size = llistxattr(file, list, sizeof(list)); list_size = llistxattr(file, list, sizeof(list));
if (list_size <= 0) { if (list_size <= 0) {
log_err("llistxattr() failed\n"); log_err("llistxattr() failed: %s\n", file);
goto out; goto out;
} }
@ -1103,6 +939,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
if (msize == 0) { if (msize == 0) {
struct h_misc *hmac = (struct h_misc *)&hmac_misc; struct h_misc *hmac = (struct h_misc *)&hmac_misc;
hmac_size = sizeof(*hmac); hmac_size = sizeof(*hmac);
hmac->ino = st.st_ino; hmac->ino = st.st_ino;
hmac->generation = generation; hmac->generation = generation;
@ -1111,6 +948,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
hmac->mode = st.st_mode; hmac->mode = st.st_mode;
} else if (msize == 64) { } else if (msize == 64) {
struct h_misc_64 *hmac = (struct h_misc_64 *)&hmac_misc; struct h_misc_64 *hmac = (struct h_misc_64 *)&hmac_misc;
hmac_size = sizeof(*hmac); hmac_size = sizeof(*hmac);
hmac->ino = st.st_ino; hmac->ino = st.st_ino;
hmac->generation = generation; hmac->generation = generation;
@ -1119,6 +957,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
hmac->mode = st.st_mode; hmac->mode = st.st_mode;
} else { } else {
struct h_misc_32 *hmac = (struct h_misc_32 *)&hmac_misc; struct h_misc_32 *hmac = (struct h_misc_32 *)&hmac_misc;
hmac_size = sizeof(*hmac); hmac_size = sizeof(*hmac);
hmac->ino = st.st_ino; hmac->ino = st.st_ino;
hmac->generation = generation; hmac->generation = generation;
@ -1148,7 +987,7 @@ out:
static int hmac_evm(const char *file, const char *key) static int hmac_evm(const char *file, const char *key)
{ {
unsigned char hash[20]; unsigned char hash[20];
unsigned char sig[1024] = "\x02"; unsigned char sig[1024];
int len, err; int len, err;
len = calc_evm_hmac(file, key, hash); len = calc_evm_hmac(file, key, hash);
@ -1160,6 +999,7 @@ static int hmac_evm(const char *file, const char *key)
memcpy(sig + 1, hash, len); memcpy(sig + 1, hash, len);
if (xattr) { if (xattr) {
sig[0] = EVM_XATTR_HMAC;
err = lsetxattr(file, "security.evm", sig, len + 1, 0); err = lsetxattr(file, "security.evm", sig, len + 1, 0);
if (err < 0) { if (err < 0) {
log_err("setxattr failed: %s\n", file); log_err("setxattr failed: %s\n", file);
@ -1205,13 +1045,13 @@ static int ima_fix(const char *path)
log_info("%s\n", path); log_info("%s\n", path);
if (xattr) { if (check_xattr) {
/* re-measuring takes a time /* re-measuring takes a time
* in some cases we can skip labeling if xattrs exists * in some cases we can skip labeling if xattrs exists
*/ */
size = llistxattr(path, list, sizeof(buf)); size = llistxattr(path, list, sizeof(buf));
if (size < 0) { if (size < 0) {
log_errno("llistxattr() failed: %s\n", path); log_errno("Failed to read xattrs (llistxattr): %s\n", path);
return -1; return -1;
} }
for (; size > 0; len++, size -= len, list += len) { for (; size > 0; len++, size -= len, list += len) {
@ -1227,7 +1067,7 @@ static int ima_fix(const char *path)
fd = open(path, O_RDONLY); fd = open(path, O_RDONLY);
if (fd < 0) { if (fd < 0) {
log_errno("%s open failed", path); log_errno("Failed to open file: %s", path);
return -1; return -1;
} }
@ -1244,8 +1084,9 @@ static int find(const char *path, int dts, find_cb_t func)
if (fs_dev) { if (fs_dev) {
struct stat st; struct stat st;
int err = lstat(path, &st); int err = lstat(path, &st);
if (err < 0) { if (err < 0) {
log_err("stat() failed\n"); log_err("Failed to stat: %s\n", path);
return err; return err;
} }
if (st.st_dev != fs_dev) if (st.st_dev != fs_dev)
@ -1254,12 +1095,12 @@ static int find(const char *path, int dts, find_cb_t func)
dir = opendir(path); dir = opendir(path);
if (!dir) { if (!dir) {
log_err("Unable to open %s\n", path); log_err("Failed to open directory %s\n", path);
return -1; return -1;
} }
if (fchdir(dirfd(dir))) { if (fchdir(dirfd(dir))) {
log_err("Unable to chdir %s\n", path); log_err("Failed to chdir %s\n", path);
return -1; return -1;
} }
@ -1274,7 +1115,7 @@ static int find(const char *path, int dts, find_cb_t func)
} }
if (chdir("..")) { if (chdir("..")) {
log_err("Unable to chdir %s\n", path); log_err("Failed to chdir: %s\n", path);
return -1; return -1;
} }
@ -1290,7 +1131,6 @@ static int cmd_ima_fix(struct command *cmd)
{ {
char *path = g_argv[optind++]; char *path = g_argv[optind++];
int err, dts = REG_MASK; /* only regular files by default */ int err, dts = REG_MASK; /* only regular files by default */
struct stat st;
if (!path) { if (!path) {
log_err("Parameters missing\n"); log_err("Parameters missing\n");
@ -1298,41 +1138,18 @@ static int cmd_ima_fix(struct command *cmd)
return -1; return -1;
} }
xattr = 0; /* do not check xattrs, fix everything */ if (recursive) {
if (search_type) { if (search_type) {
int i; dts = get_file_type(path, search_type);
if (dts < 0)
dts = 0; return dts;
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;
}
}
}
err = find(path, dts, ima_fix); err = find(path, dts, ima_fix);
if (err) } else {
return err; err = ima_fix(path);
}
return 0; return err;
} }
@ -1492,7 +1309,7 @@ static int ima_measurement(const char *file)
uint8_t pcr10[SHA_DIGEST_LENGTH]; uint8_t pcr10[SHA_DIGEST_LENGTH];
struct template_entry entry = { .template = 0 }; struct template_entry entry = { .template = 0 };
FILE *fp; FILE *fp;
int err; int err = -1;
memset(fox, 0xff, SHA_DIGEST_LENGTH); memset(fox, 0xff, SHA_DIGEST_LENGTH);
@ -1501,23 +1318,23 @@ static int ima_measurement(const char *file)
fp = fopen(file, "rb"); fp = fopen(file, "rb");
if (!fp) { if (!fp) {
log_err("Unable to open measurement file\n"); log_err("Failed to open measurement file: %s\n", file);
return -1; 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); ima_extend_pcr(pcr, entry.header.digest, SHA_DIGEST_LENGTH);
if (!fread(entry.name, entry.header.name_len, 1, fp)) { if (!fread(entry.name, entry.header.name_len, 1, fp)) {
log_err("Unable to read template name\n"); log_err("Unable to read template name\n");
return -1; goto out;
} }
entry.name[entry.header.name_len] = '\0'; entry.name[entry.header.name_len] = '\0';
if (!fread(&entry.template_len, sizeof(entry.template_len), 1, fp)) { if (!fread(&entry.template_len, sizeof(entry.template_len), 1, fp)) {
log_err("Unable to read template length\n"); log_err("Unable to read template length\n");
return -1; goto out;
} }
if (entry.template_buf_len < entry.template_len) { if (entry.template_buf_len < entry.template_len) {
@ -1528,7 +1345,7 @@ static int ima_measurement(const char *file)
if (!fread(entry.template, entry.template_len, 1, fp)) { if (!fread(entry.template, entry.template_len, 1, fp)) {
log_err("Unable to read template\n"); log_err("Unable to read template\n");
return -1; goto out;
} }
if (validate) if (validate)
@ -1540,8 +1357,6 @@ static int ima_measurement(const char *file)
ima_ng_show(&entry); ima_ng_show(&entry);
} }
fclose(fp);
tpm_pcr_read(10, pcr10, sizeof(pcr10)); tpm_pcr_read(10, pcr10, sizeof(pcr10));
log_info("PCRAgg: "); log_info("PCRAgg: ");
@ -1552,10 +1367,14 @@ static int ima_measurement(const char *file)
if (memcmp(pcr, pcr10, sizeof(pcr))) { if (memcmp(pcr, pcr10, sizeof(pcr))) {
log_err("PCRAgg does not match PCR-10\n"); 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) static int cmd_ima_measurement(struct command *cmd)
@ -1642,26 +1461,28 @@ static void usage(void)
printf( printf(
"\n" "\n"
" -a, --hashalgo sha1 (default), sha224, sha256, sha384, sha512\n" " -a, --hashalgo sha1 (default), sha224, sha256, sha384, sha512\n"
" -s, --imasig also make IMA signature\n" " -s, --imasig make IMA signature\n"
" -d, --imahash also make IMA hash\n" " -d, --imahash make IMA hash\n"
" -f, --sigfile store IMA signature in .sig file instead of xattr\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" " --rsa use RSA key type and signing scheme v1\n"
" -k, --key path to signing key (default keys are /etc/keys/{privkey,pubkey}_evm.pem)\n" " -k, --key path to signing key (default: /etc/keys/{privkey,pubkey}_evm.pem)\n"
" -p, --pass password for encrypted signing key\n" " -p, --pass password for encrypted signing key\n"
" -u, --uuid use file system UUID in HMAC calculation (EVM v2)\n" " -r, --recursive recurse into directories (sign)\n"
" -t, --type file types to fix 'fdsxm' (f - file, d - directory, s - block/char/symlink)\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" " x - skip fixing if both ima and evm xattrs exist (use with caution)\n"
" m - stay on the same filesystem (like 'find -xdev')\n" " m - stay on the same filesystem (like 'find -xdev')\n"
" -n print result to stdout instead of setting xattr\n" " -n print result to stdout instead of setting xattr\n"
" -r, --recursive recurse into directories (sign)\n" " -u, --uuid use custom FS UUID for EVM (unspecified: from FS, empty: do not use)\n"
" --m32 force signature for 32 bit target system\n" " --smack use extra SMACK xattrs for EVM\n"
" --m64 force signature for 32 bit target system\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" " -v increase verbosity level\n"
" -h, --help display this help and exit\n" " -h, --help display this help and exit\n"
"\n"); "\n");
} }
struct command cmds[] = { struct command cmds[] = {
{"--version", NULL, 0, ""},
{"help", cmd_help, 0, "<command>"}, {"help", cmd_help, 0, "<command>"},
{"import", cmd_import, 0, "[--rsa] pubkey keyring", "Import public key into the keyring.\n"}, {"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"}, {"sign", cmd_sign_evm, 0, "[-r] [--imahash | --imasig ] [--key key] [--pass password] file", "Sign file metadata.\n"},
@ -1692,6 +1513,8 @@ static struct option opts[] = {
{"recursive", 0, 0, 'r'}, {"recursive", 0, 0, 'r'},
{"m32", 0, 0, '3'}, {"m32", 0, 0, '3'},
{"m64", 0, 0, '6'}, {"m64", 0, 0, '6'},
{"smack", 0, 0, 256},
{"version", 0, 0, 257},
{} {}
}; };
@ -1731,17 +1554,21 @@ int main(int argc, char *argv[])
params.hash_algo = optarg; params.hash_algo = optarg;
break; break;
case 'p': case 'p':
keypass = optarg; params.keypass = optarg;
break; break;
case 'f': case 'f':
sigfile = 1; sigfile = 1;
xattr = 0; xattr = 0;
break; break;
case 'u': case 'u':
uuid_str = optarg ?: "+"; uuid_str = optarg;
if (uuid_str)
hmac_flags |= HMAC_FLAG_UUID_SET;
else
hmac_flags &= ~HMAC_FLAG_UUID;
break; break;
case '1': case '1':
x509 = 0; params.x509 = 0;
break; break;
case 'k': case 'k':
params.keyfile = optarg; params.keyfile = optarg;
@ -1758,6 +1585,13 @@ int main(int argc, char *argv[])
case '6': case '6':
msize = 64; msize = 64;
break; break;
case 256:
evm_config_xattrnames = evm_extra_smack_xattrs;
break;
case 257:
printf("evmctl %s\n", VERSION);
exit(0);
break;
case '?': case '?':
exit(1); exit(1);
break; break;
@ -1766,11 +1600,6 @@ int main(int argc, char *argv[])
} }
} }
if (x509)
sign_hash = sign_hash_v2;
else
sign_hash = sign_hash_v1;
OpenSSL_add_all_algorithms(); OpenSSL_add_all_algorithms();
ERR_load_crypto_strings(); ERR_load_crypto_strings();
@ -1781,6 +1610,7 @@ int main(int argc, char *argv[])
if (err) { if (err) {
unsigned long error; unsigned long error;
if (errno) if (errno)
log_err("errno: %s (%d)\n", strerror(errno), errno); log_err("errno: %s (%d)\n", strerror(errno), errno);
for (;;) { for (;;) {

View File

@ -41,6 +41,7 @@
#ifndef _LIBIMAEVM_H #ifndef _LIBIMAEVM_H
#define _LIBIMAEVM_H #define _LIBIMAEVM_H
#include <linux/fs.h>
#include <stdint.h> #include <stdint.h>
#include <syslog.h> #include <syslog.h>
#include <stdbool.h> #include <stdbool.h>
@ -74,16 +75,15 @@
#define DATA_SIZE 4096 #define DATA_SIZE 4096
#define SHA1_HASH_LEN 20 #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)) #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 { struct h_misc {
unsigned long ino; unsigned long ino;
uint32_t generation; uint32_t generation;
@ -170,8 +170,10 @@ typedef int (*verify_hash_fn_t)(const unsigned char *hash, int size, unsigned ch
struct libevm_params { struct libevm_params {
int verbose; int verbose;
int x509;
const char *hash_algo; const char *hash_algo;
char *keyfile; char *keyfile;
char *keypass;
}; };
struct RSA_ASN1_template { 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); int get_hash_algo(const char *algo);
RSA *read_pub_key(const char *keyfile, int x509); 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 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); int ima_verify_signature(const char *file, unsigned char *sig, int siglen);

View File

@ -44,6 +44,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <asm/byteorder.h>
#include <unistd.h> #include <unistd.h>
#include <dirent.h> #include <dirent.h>
#include <string.h> #include <string.h>
@ -125,6 +126,7 @@ const struct RSA_ASN1_template RSA_ASN1_templates[PKEY_HASH__LAST] = {
struct libevm_params params = { struct libevm_params params = {
.verbose = LOG_INFO - 1, .verbose = LOG_INFO - 1,
.x509 = 1,
.hash_algo = "sha1", .hash_algo = "sha1",
}; };
@ -152,54 +154,54 @@ int get_filesize(const char *filename)
return (int)stats.st_size; return (int)stats.st_size;
} }
static inline int get_fdsize(int fd) static inline off_t get_fdsize(int fd)
{ {
struct stat stats; struct stat stats;
/* Need to know the file length */ /* Need to know the file length */
fstat(fd, &stats); fstat(fd, &stats);
return (int)stats.st_size; return stats.st_size;
} }
static int add_file_hash(const char *file, EVP_MD_CTX *ctx) static int add_file_hash(const char *file, EVP_MD_CTX *ctx)
{ {
uint8_t *data; uint8_t *data;
int err, size, bs = DATA_SIZE; int err = -1, bs = DATA_SIZE;
size_t len; off_t size, len;
FILE *fp; FILE *fp;
fp = fopen(file, "r");
if (!fp) {
log_err("Failed to open: %s\n", file);
return -1;
}
data = malloc(bs); data = malloc(bs);
if (!data) { if (!data) {
log_err("malloc failed\n"); log_err("malloc failed\n");
return -1; goto out;
}
fp = fopen(file, "r");
if (!fp) {
log_err("Unable to open %s\n", file);
return -1;
} }
for (size = get_fdsize(fileno(fp)); size; size -= len) { for (size = get_fdsize(fileno(fp)); size; size -= len) {
len = MIN(size, bs); len = MIN(size, bs);
err = fread(data, len, 1, fp); if (!fread(data, len, 1, fp)) {
if (!err) {
if (ferror(fp)) { if (ferror(fp)) {
log_err("fread() error\n\n"); log_err("fread() failed\n\n");
return -1; goto out;
} }
break; break;
} }
err = EVP_DigestUpdate(ctx, data, len); if (!EVP_DigestUpdate(ctx, data, len)) {
if (!err) {
log_err("EVP_DigestUpdate() failed\n"); log_err("EVP_DigestUpdate() failed\n");
return 1; err = 1;
goto out;
} }
} }
err = 0;
out:
fclose(fp); fclose(fp);
free(data); free(data);
return 0; return err;
} }
static int add_dir_hash(const char *file, EVP_MD_CTX *ctx) static int add_dir_hash(const char *file, EVP_MD_CTX *ctx)
@ -212,7 +214,7 @@ static int add_dir_hash(const char *file, EVP_MD_CTX *ctx)
dir = opendir(file); dir = opendir(file);
if (!dir) { if (!dir) {
log_err("Unable to open %s\n", file); log_err("Failed to open: %s\n", file);
return -1; return -1;
} }
@ -255,22 +257,23 @@ static int add_dev_hash(struct stat *st, EVP_MD_CTX *ctx)
uint32_t dev = st->st_rdev; uint32_t dev = st->st_rdev;
unsigned major = (dev & 0xfff00) >> 8; unsigned major = (dev & 0xfff00) >> 8;
unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00); unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
log_info("device: %u:%u\n", major, minor); log_info("device: %u:%u\n", major, minor);
return !EVP_DigestUpdate(ctx, &dev, sizeof(dev)); return !EVP_DigestUpdate(ctx, &dev, sizeof(dev));
} }
int ima_calc_hash(const char *file, uint8_t *hash) int ima_calc_hash(const char *file, uint8_t *hash)
{ {
const EVP_MD *md;
struct stat st; struct stat st;
EVP_MD_CTX ctx; EVP_MD_CTX ctx;
const EVP_MD *md;
unsigned int mdlen; unsigned int mdlen;
int err; int err;
/* Need to know the file length */ /* Need to know the file length */
err = lstat(file, &st); err = lstat(file, &st);
if (err < 0) { if (err < 0) {
log_err("stat() failed\n"); log_err("Failed to stat: %s\n", file);
return err; return err;
} }
@ -326,7 +329,7 @@ RSA *read_pub_key(const char *keyfile, int x509)
fp = fopen(keyfile, "r"); fp = fopen(keyfile, "r");
if (!fp) { if (!fp) {
log_err("Unable to open keyfile %s\n", keyfile); log_err("Failed to open keyfile: %s\n", keyfile);
return NULL; return NULL;
} }
@ -533,3 +536,258 @@ int ima_verify_signature(const char *file, unsigned char *sig, int siglen)
return verify_hash(hash, hashlen, sig + 1, siglen - 1); 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("Failed 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);
}