Compare commits

..

No commits in common. "master" and "v0.5" have entirely different histories.
master ... v0.5

18 changed files with 1089 additions and 2704 deletions

5
.gitignore vendored
View File

@ -2,8 +2,6 @@
*~ *~
# Generated by autotools # Generated by autotools
.libs
m4
.deps .deps
aclocal.m4 aclocal.m4
autom4te.cache autom4te.cache
@ -22,12 +20,9 @@ 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,6 +1,2 @@
Dmitry Kasatkin <d.kasatkin@samsung.com> Dmitry Kasatkin <dmitry.kasatkin@intel.com>
CONTRIBUTORS:
Vivek Goyal <vgoyal@redhat.com>
Mimi Zohar <zohar@linux.vnet.ibm.com>

25
COPYING
View File

@ -1,8 +1,8 @@
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
Version 2, June 1991 Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc., Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. of this license document, but changing it is not allowed.
@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to the GNU Library General Public License instead.) You can apply it to
your programs, too. your programs, too.
When we speak of free software, we are referring to freedom, not When we speak of free software, we are referring to freedom, not
@ -55,7 +55,7 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and The precise terms and conditions for copying, distribution and
modification follow. modification follow.
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
License. (Exception: if the Program itself is interactive but License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on does not normally print such an announcement, your work based on
the Program is not required to print an announcement.) the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program, identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in and can be reasonably considered independent and separate works in
@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not distribution of the source code, even though third parties are not
compelled to copy the source along with the object code. compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program 4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is otherwise to copy, modify, sublicense or distribute the Program is
@ -225,7 +225,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License. be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in 8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License original copyright holder who places the Program under this License
@ -278,7 +278,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES. POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest If you develop a new program, and you want it to be of the greatest
@ -303,9 +303,10 @@ the "copyright" line and a pointer to where the full notice is found.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License along You should have received a copy of the GNU General Public License
with this program; if not, write to the Free Software Foundation, Inc., along with this program; if not, write to the Free Software
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper mail.
@ -335,5 +336,5 @@ necessary. Here is a sample; alter the names:
This General Public License does not permit incorporating your program into This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General library. If this is what you want to do, use the GNU Library General
Public License instead of this License. Public License instead of this License.

View File

@ -1,50 +1,3 @@
2014-07-30 Dmitry Kasatkin <dmitry.kasatkin@huawei.com>
version 1.0
* Recursive hashing
* Immutable EVM signatures (experimental)
* Command 'ima_clear' to remove xattrs
* Support for passing password to the library
* Support for asking password safely from the user
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>
version 0.8
* Symbilic names for keyrings
* Hash list signing
* License text fix for using OpenSSL
* Help output fix
2014-02-17 Dmitry Kasatkin <d.kasatkin@samsung.com>
version 0.7
* Fix symbolic links related bugs
* Provide recursive fixing
* Provide recursive signing
* Move IMA verification to the library (first for LTP use)
* Support for target architecture data size
* Remove obsolete module signing code
* Code cleanup
2013-08-28 Dmitry Kasatkin <d.kasatkin@samsung.com>
version 0.6
* support for asymmetric crypto keys and new signature format (v2)
* fixes to set correct hash algo for digital signature v1
* uuid support for EVM
* signature verification support
* test scripts removed
* README updates
2012-05-18 Dmitry Kasatkin <dmitry.kasatkin@intel.com> 2012-05-18 Dmitry Kasatkin <dmitry.kasatkin@intel.com>
version 0.3 version 0.3

View File

@ -1,7 +1,7 @@
Installation Instructions Installation Instructions
************************* *************************
Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, Copyright (C) 1994-1996, 1999-2002, 2004-2011 Free Software Foundation,
Inc. Inc.
Copying and distribution of this file, with or without modification, Copying and distribution of this file, with or without modification,
@ -309,10 +309,9 @@ causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script). overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf limitation. Until the limitation is lifted, you can use an Autoconf bug. Until the bug is fixed you can use this workaround:
this workaround:
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation `configure' Invocation
====================== ======================
@ -368,3 +367,4 @@ operates.
`configure' also accepts some other, not widely useful, options. Run `configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details. `configure --help' for more details.

View File

@ -1,10 +1,6 @@
SUBDIRS = src SUBDIRS = src
dist_man_MANS = evmctl.1
doc_DATA = examples/ima-genkey-self.sh examples/ima-genkey.sh examples/ima-gen-local-ca.sh #EXTRA_DIST = LEGAL acinclude.m4 include
EXTRA_DIST = autogen.sh $(doc_DATA)
CLEANFILES = *.html *.xsl
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
@ -23,20 +19,4 @@ 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)

442
README
View File

@ -1,185 +1,47 @@
EVMCTL(1) ima-evm-utils - IMA/EVM signing utility
========= =========================================
NAME Contents:
----
evmctl - IMA/EVM signing utility 1. Key and signature formats
2. Key generation
3. Initialization
SYNOPSIS 4. Signing
--------
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
------------------------- -------------------------
Linux integrity subsystem supports two type of signature and respectively two EVM support (v2) in latest version of the kernel adds the file system UUID to
key formats. 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.
First key format (v1) is pure RSA key encoded in PEM a format and uses own signature Latest kernel got IMA/EVM support for using X509 certificates and asymmetric key
format. It is now non-default format and requires to provide evmctl '--rsa' option support for verifying digital signatures. The new command line parameter
for signing and importing the key. '-x' or '--x509' was added to the evmctl to enable using of X509 certificates
and new signature format.
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).
Integrity keyrings Key generation
---------------- --------------
Integrity subsystem uses dedicated IMA/EVM keyrings to search for signature verification Generate private key in plain text format
keys - '_ima' and '_evm' respectively.
Since 3.13 IMA allows to declare IMA keyring as trusted. It allows only to load keys, $ openssl genrsa -out privkey_evm.pem 1024
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
Generate EVM encrypted keys $ openssl genrsa -des3 -out privkey_evm.pem 1024
---------------------------
EVM encrypted key is used for EVM HMAC calculation: Make encrypted private key from unencrypted
# create and save the key kernel master key (user type) $ openssl rsa -in /etc/keys/privkey_evm.pem -out privkey_evm_enc.pem -des3
# LMK is used to encrypt encrypted keys
keyctl add user kmk "`dd if=/dev/urandom bs=1 count=32 2>/dev/null`" @u
keyctl pipe `keyctl search @u user kmk` > /etc/keys/kmk
# create the EVM encrypted key Generate self-signed X509 certificate and private key for using kernel asymmetric
keyctl add encrypted evm-key "new user:kmk 64" @u keys support
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
@ -206,232 +68,78 @@ Configuration file x509_evm.genkey:
# EOF # EOF
Generate public key for using RSA key format: Get public key
openssl rsa -pubout -in privkey_evm.pem -out pubkey_evm.pem $ openssl rsa -pubout -in privkey_evm.pem -out pubkey_evm.pem
Copy keys to /etc/keys
Copy keys to /etc/keys: $ cp pubkey_evm.pem /etc/keys
$ scp pubkey_evm.pem target:/etc/keys
cp pubkey_evm.pem /etc/keys
scp pubkey_evm.pem target:/etc/keys
or or
cp x509_evm.pem /etc/keys $ cp x509_evm.pem /etc/keys
scp x509_evm.pem target:/etc/keys $ scp x509_evm.pem target:/etc/keys
Generate trusted keys Initialization
--------------------- --------------
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 Ubuntu initramfs example script (/etc/initramfs-tools/scripts/local-top/ima.sh) Here is an example script /etc/initramfs-tools/scripts/local-top/ima.sh
# mount securityfs if not mounted # import EVM HMAC key
SECFS=/sys/kernel/security keyctl clear @u
grep -q $SECFS /proc/mounts || mount -n -t securityfs securityfs $SECFS keyctl add user kmk "testing123" @u
keyctl add encrypted evm-key "load `cat /etc/keys/evm-key`" @u
# search for IMA trusted keyring, then for untrusted # import IMA public key
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` ima_id=`keyctl newring _ima @u`
fi evmctl import /etc/keys/pubkey_evm.pem $ima_id
fi
# import IMA X509 certificate
evmctl import /etc/keys/x509_ima.der $ima_id
# search for EVM keyring # import EVM public key
evm_id=`keyctl search @u keyring _evm 2>/dev/null`
if [ -z "$evm_id" ]; then
evm_id=`keyctl newring _evm @u` evm_id=`keyctl newring _evm @u`
fi evmctl import /etc/keys/pubkey_evm.pem $evm_id
# import EVM X509 certificate
evmctl import /etc/keys/x509_evm.der $evm_id
# a) import EVM encrypted key
cat /etc/keys/kmk | keyctl padd user kmk @u
keyctl add encrypted evm-key "load `cat /etc/keys/evm-key`" @u
# OR
# b) import EVM trusted key
keyctl add trusted kmk "load `cat /etc/keys/kmk`" @u
keyctl add encrypted evm-key "load `cat /etc/keys/evm-key`" @u
# enable EVM # 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:
# protect EVM keyring Import X509 certificate into the kernel keyring (since kernel 3.9?)
keyctl setperm $evm_id 0x0b0b0000
# protect IMA keyring $ evmctl -x import /etc/keys/x509_evm.der `keyctl search @u keyring _ima`
keyctl setperm $ima_id 0x0b0b0000 $ evmctl -x import /etc/keys/x509_evm.der `keyctl search @u keyring _evm`
# protecting IMA key from revoking (against DoS)
ima_key=`evmctl import /etc/keys/x509_ima.der $ima_id`
keyctl setperm $ima_key 0x0b0b0000
When using plain RSA public keys in PEM format, use 'evmctl import --rsa' for importing keys: Signing
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
------- -------
Copyright \(C) 2012 - 2014 Linux Integrity Project. Free use of this software is granted under Default public key: /etc/keys/pubkey_evm.pem
the terms of the GNU Public License (GPL). Default private key: /etc/keys/privkey_evm.pem
Default X509 certificate: /etc/keys/x509_evm.der
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.
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

@ -1,4 +0,0 @@
#!/bin/sh
gcc -static -o evmctl.static -include config.h src/evmctl.c src/libimaevm.c -lcrypto -lkeyutils -ldl

View File

@ -1,8 +1,8 @@
# autoconf script # autoconf script
AC_PREREQ([2.65]) AC_PREREQ([2.65])
AC_INIT(ima-evm-utils, 1.0, dmitry.kasatkin@huawei.com) AC_INIT(ima-evm-utils, 0.3, dmitry.kasatkin@intel.com)
AM_INIT_AUTOMAKE AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
@ -30,9 +30,6 @@ AC_SUBST(OPENSSL_LIBS)
AC_CHECK_HEADER(unistd.h) AC_CHECK_HEADER(unistd.h)
AC_CHECK_HEADERS(openssl/conf.h) AC_CHECK_HEADERS(openssl/conf.h)
AC_CHECK_HEADERS(attr/xattr.h, , [AC_MSG_ERROR([attr/xattr.h header not found. You need the libattr development package.])])
AC_CHECK_HEADERS(keyutils.h, , [AC_MSG_ERROR([keyutils.h header not found. You need the libkeyutils development package.])])
#debug support - yes for a while #debug support - yes for a while
PKG_ARG_ENABLE(debug, "yes", DEBUG, [Enable Debug support]) PKG_ARG_ENABLE(debug, "yes", DEBUG, [Enable Debug support])
if test $pkg_cv_enable_debug = yes; then if test $pkg_cv_enable_debug = yes; then
@ -49,7 +46,7 @@ fi
AC_CONFIG_FILES([Makefile AC_CONFIG_FILES([Makefile
src/Makefile src/Makefile
packaging/ima-evm-utils.spec ima-evm-utils.spec
]) ])
AC_OUTPUT AC_OUTPUT

View File

@ -1,29 +0,0 @@
#!/bin/sh
GENKEY=ima-local-ca.genkey
cat << __EOF__ >$GENKEY
[ 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__
openssl req -new -x509 -utf8 -sha1 -days 3650 -batch -config $GENKEY \
-outform DER -out ima-local-ca.x509 -keyout ima-local-ca.priv
openssl x509 -inform DER -in ima-local-ca.x509 -out ima-local-ca.pem

View File

@ -1,29 +0,0 @@
#!/bin/sh
GENKEY=x509_evm.genkey
cat << __EOF__ >$GENKEY
[ req ]
default_bits = 1024
distinguished_name = req_distinguished_name
prompt = no
string_mask = utf8only
x509_extensions = myexts
[ req_distinguished_name ]
O = `hostname`
CN = `whoami` signing key
emailAddress = `whoami`@`hostname`
[ myexts ]
basicConstraints=critical,CA:FALSE
keyUsage=digitalSignature
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid
__EOF__
openssl req -x509 -new -nodes -utf8 -sha1 -days 3650 -batch -config $GENKEY \
-outform DER -out x509_evm.der -keyout privkey_evm.pem
openssl rsa -pubout -in privkey_evm.pem -out pubkey_evm.pem

View File

@ -1,33 +0,0 @@
#!/bin/sh
GENKEY=ima.genkey
cat << __EOF__ >$GENKEY
[ 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__
openssl req -new -nodes -utf8 -sha1 -days 365 -batch -config $GENKEY \
-out csr_ima.pem -keyout privkey_ima.pem
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

View File

@ -3,7 +3,7 @@ Version: @PACKAGE_VERSION@
Release: 1%{?dist} Release: 1%{?dist}
Summary: @PACKAGE_NAME@ - IMA/EVM control utility Summary: @PACKAGE_NAME@ - IMA/EVM control utility
Group: System/Libraries Group: System/Libraries
License: GPLv2 License: LGPLv2
#URL: #URL:
Source0: %{name}-%{version}.tar.gz Source0: %{name}-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
@ -44,8 +44,7 @@ exit 0
%files %files
%defattr(-,root,root,-) %defattr(-,root,root,-)
%{_bindir}/* %{_bindir}/*
%{_libdir}/libimaevm.* %{_libdir}/*
%{_includedir}/*
%changelog %changelog
* Thu Apr 05 2012 Dmitry Kasatkin <dmitry.kasatkin@intel.com> * Thu Apr 05 2012 Dmitry Kasatkin <dmitry.kasatkin@intel.com>

View File

@ -1,53 +0,0 @@
Name: ima-evm-utils
Version: 1.0
Release: 1%{?dist}
Summary: ima-evm-utils - IMA/EVM control utility
Group: System/Libraries
License: GPLv2
#URL:
Source0: %{name}-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
BuildRequires: autoconf
BuildRequires: automake
BuildRequires: openssl-devel
BuildRequires: libattr-devel
BuildRequires: keyutils-libs-devel
%description
This package provide IMA/EVM control utility
%prep
%setup -q
%build
./autogen.sh
%configure --prefix=/usr
make
%install
rm -rf %{buildroot}
make DESTDIR=%{buildroot} install
%clean
rm -rf %{buildroot}
%post
/sbin/ldconfig
exit 0
%preun -p /sbin/ldconfig
%postun
/sbin/ldconfig
%files
%defattr(-,root,root,-)
%{_bindir}/*
%{_libdir}/libimaevm.*
%{_includedir}/*
%changelog
* Thu Apr 05 2012 Dmitry Kasatkin <dmitry.kasatkin@intel.com>
- Initial RPM spec file

View File

@ -1,20 +1,10 @@
lib_LTLIBRARIES = libimaevm.la
libimaevm_la_SOURCES = libimaevm.c
libimaevm_la_CPPFLAGS = $(OPENSSL_CFLAGS)
# current[:revision[:age]]
# result: [current-age].age.revision
libimaevm_la_LDFLAGS = -version-info 0:0:0
libimaevm_la_LIBADD = $(OPENSSL_LIBS)
include_HEADERS = imaevm.h
bin_PROGRAMS = evmctl bin_PROGRAMS = evmctl
evmctl_SOURCES = evmctl.c evmctl_SOURCES = evmctl.c
evmctl_CPPFLAGS = $(OPENSSL_CFLAGS) evmctl_CPPFLAGS = $(OPENSSL_CFLAGS)
evmctl_LDFLAGS = $(LDFLAGS_READLINE) evmctl_LDFLAGS = $(LDFLAGS_READLINE)
evmctl_LDADD = $(OPENSSL_LIBS) -lkeyutils libimaevm.la evmctl_LDADD = $(OPENSSL_LIBS) -lkeyutils
INCLUDES = -I$(top_srcdir) -include config.h INCLUDES = -I$(top_srcdir) -include config.h

File diff suppressed because it is too large Load Diff

View File

@ -1,208 +0,0 @@
/*
* ima-evm-utils - IMA/EVM support utilities
*
* Copyright (C) 2011 Nokia Corporation
* Copyright (C) 2011,2012,2013 Intel Corporation
* Copyright (C) 2013,2014 Samsung Electronics
*
* Authors:
* Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
* <dmitry.kasatkin@intel.com>
* <d.kasatkin@samsung.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*
* File: imaevm.h
* IMA/EVM header file
*/
#ifndef _LIBIMAEVM_H
#define _LIBIMAEVM_H
#include <linux/fs.h>
#include <stdint.h>
#include <syslog.h>
#include <stdbool.h>
#include <errno.h>
#include <openssl/rsa.h>
#ifdef USE_FPRINTF
#define do_log(level, fmt, args...) ({ if (level <= params.verbose) fprintf(stderr, fmt, ##args); })
#define do_log_dump(level, p, len, cr) ({ if (level <= params.verbose) do_dump(stderr, p, len, cr); })
#else
#define do_log(level, fmt, args...) syslog(level, fmt, ##args)
#define do_log_dump(level, p, len, cr)
#endif
#ifdef DEBUG
#define log_debug(fmt, args...) do_log(LOG_DEBUG, "%s:%d " fmt, __func__ , __LINE__ , ##args)
#define log_debug_dump(p, len) do_log_dump(LOG_DEBUG, p, len, true)
#define log_debug_dump_n(p, len) do_log_dump(LOG_DEBUG, p, len, false)
#else
#define log_debug(fmt, args...)
#define log_debug_dump(p, len)
#endif
#define log_dump(p, len) do_log_dump(LOG_INFO, p, len, true)
#define log_dump_n(p, len) do_log_dump(LOG_INFO, p, len, false)
#define log_info(fmt, args...) do_log(LOG_INFO, fmt, ##args)
#define log_err(fmt, args...) do_log(LOG_ERR, fmt, ##args)
#define log_errno(fmt, args...) do_log(LOG_ERR, fmt ": errno: %s (%d)\n", ##args, strerror(errno), errno)
#define DATA_SIZE 4096
#define SHA1_HASH_LEN 20
#define __packed __attribute__((packed))
enum evm_ima_xattr_type {
IMA_XATTR_DIGEST = 0x01,
EVM_XATTR_HMAC,
EVM_IMA_XATTR_DIGSIG,
IMA_XATTR_DIGEST_NG,
};
struct h_misc {
unsigned long ino;
uint32_t generation;
uid_t uid;
gid_t gid;
unsigned short mode;
};
struct h_misc_32 {
uint32_t ino;
uint32_t generation;
uid_t uid;
gid_t gid;
unsigned short mode;
};
struct h_misc_64 {
uint64_t ino;
uint32_t generation;
uid_t uid;
gid_t gid;
unsigned short mode;
};
struct h_misc_digsig {
uid_t uid;
gid_t gid;
unsigned short mode;
};
enum pubkey_algo {
PUBKEY_ALGO_RSA,
PUBKEY_ALGO_MAX,
};
enum digest_algo {
DIGEST_ALGO_SHA1,
DIGEST_ALGO_SHA256,
DIGEST_ALGO_MAX
};
enum digsig_version {
DIGSIG_VERSION_1 = 1,
DIGSIG_VERSION_2
};
struct pubkey_hdr {
uint8_t version; /* key format version */
uint32_t timestamp; /* key made, always 0 for now */
uint8_t algo;
uint8_t nmpi;
char mpi[0];
} __packed;
struct signature_hdr {
uint8_t version; /* signature format version */
uint32_t timestamp; /* signature made */
uint8_t algo;
uint8_t hash;
uint8_t keyid[8];
uint8_t nmpi;
char mpi[0];
} __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
};
/*
* 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 */
} __packed;
typedef int (*verify_hash_fn_t)(const unsigned char *hash, int size, unsigned char *sig, int siglen, const char *keyfile);
struct libevm_params {
int verbose;
int x509;
const char *hash_algo;
const char *keyfile;
const char *keypass;
};
struct RSA_ASN1_template {
const uint8_t *data;
size_t size;
};
extern const struct RSA_ASN1_template RSA_ASN1_templates[PKEY_HASH__LAST];
extern struct libevm_params params;
void do_dump(FILE *fp, const void *ptr, int len, bool cr);
void dump(const void *ptr, int len);
int get_filesize(const char *filename);
int ima_calc_hash(const char *file, uint8_t *hash);
int get_hash_algo(const char *algo);
RSA *read_pub_key(const char *keyfile, int x509);
void calc_keyid_v1(uint8_t *keyid, char *str, const unsigned char *pkey, int len);
void calc_keyid_v2(uint32_t *keyid, char *str, RSA *key);
int key2bin(RSA *key, unsigned char *pub);
int sign_hash(const char *algo, const unsigned char *hash, int size, const char *keyfile, const char *keypass, unsigned char *sig);
int verify_hash(const unsigned char *hash, int size, unsigned char *sig, int siglen);
int ima_verify_signature(const char *file, unsigned char *sig, int siglen);
#endif

View File

@ -1,813 +0,0 @@
/*
* ima-evm-utils - IMA/EVM support utilities
*
* Copyright (C) 2011 Nokia Corporation
* Copyright (C) 2011,2012,2013 Intel Corporation
* Copyright (C) 2013,2014 Samsung Electronics
*
* Authors:
* Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
* <dmitry.kasatkin@intel.com>
* <d.kasatkin@samsung.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*
* File: libimaevm.c
* IMA/EVM library
*/
/* should we use logger instead for library? */
#define USE_FPRINTF
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <asm/byteorder.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <stdio.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#include "imaevm.h"
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",
};
/*
* 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
};
const struct RSA_ASN1_template 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 _
};
struct libevm_params params = {
.verbose = LOG_INFO - 1,
.x509 = 1,
.hash_algo = "sha1",
};
static void __attribute__ ((constructor)) libinit(void);
void do_dump(FILE *fp, const void *ptr, int len, bool cr)
{
int i;
uint8_t *data = (uint8_t *) ptr;
for (i = 0; i < len; i++)
fprintf(fp, "%02x", data[i]);
if (cr)
fprintf(fp, "\n");
}
void dump(const void *ptr, int len)
{
do_dump(stdout, ptr, len, true);
}
int get_filesize(const char *filename)
{
struct stat stats;
/* Need to know the file length */
stat(filename, &stats);
return (int)stats.st_size;
}
static inline off_t get_fdsize(int fd)
{
struct stat stats;
/* Need to know the file length */
fstat(fd, &stats);
return stats.st_size;
}
static int add_file_hash(const char *file, EVP_MD_CTX *ctx)
{
uint8_t *data;
int err = -1, bs = DATA_SIZE;
off_t size, len;
FILE *fp;
fp = fopen(file, "r");
if (!fp) {
log_err("Failed to open: %s\n", file);
return -1;
}
data = malloc(bs);
if (!data) {
log_err("malloc failed\n");
goto out;
}
for (size = get_fdsize(fileno(fp)); size; size -= len) {
len = MIN(size, bs);
if (!fread(data, len, 1, fp)) {
if (ferror(fp)) {
log_err("fread() failed\n\n");
goto out;
}
break;
}
if (!EVP_DigestUpdate(ctx, data, len)) {
log_err("EVP_DigestUpdate() failed\n");
err = 1;
goto out;
}
}
err = 0;
out:
fclose(fp);
free(data);
return err;
}
static int add_dir_hash(const char *file, EVP_MD_CTX *ctx)
{
int err;
struct dirent *de;
DIR *dir;
unsigned long long ino, off;
unsigned int type;
int result = 0;
dir = opendir(file);
if (!dir) {
log_err("Failed to open: %s\n", file);
return -1;
}
while ((de = readdir(dir))) {
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");
result = 1;
break;
}
}
closedir(dir);
return result;
}
static int add_link_hash(const char *path, EVP_MD_CTX *ctx)
{
int err;
char buf[1024];
err = readlink(path, buf, sizeof(buf));
if (err <= 0)
return -1;
log_info("link: %s -> %.*s\n", path, err, buf);
return !EVP_DigestUpdate(ctx, buf, err);
}
static int add_dev_hash(struct stat *st, EVP_MD_CTX *ctx)
{
uint32_t dev = st->st_rdev;
unsigned major = (dev & 0xfff00) >> 8;
unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
log_info("device: %u:%u\n", major, minor);
return !EVP_DigestUpdate(ctx, &dev, sizeof(dev));
}
int ima_calc_hash(const char *file, uint8_t *hash)
{
const EVP_MD *md;
struct stat st;
EVP_MD_CTX ctx;
unsigned int mdlen;
int err;
/* Need to know the file length */
err = lstat(file, &st);
if (err < 0) {
log_err("Failed to stat: %s\n", file);
return err;
}
md = EVP_get_digestbyname(params.hash_algo);
if (!md) {
log_err("EVP_get_digestbyname() failed\n");
return 1;
}
err = EVP_DigestInit(&ctx, md);
if (!err) {
log_err("EVP_DigestInit() failed\n");
return 1;
}
switch (st.st_mode & S_IFMT) {
case S_IFREG:
err = add_file_hash(file, &ctx);
break;
case S_IFDIR:
err = add_dir_hash(file, &ctx);
break;
case S_IFLNK:
err = add_link_hash(file, &ctx);
break;
case S_IFIFO: case S_IFSOCK:
case S_IFCHR: case S_IFBLK:
err = add_dev_hash(&st, &ctx);
break;
default:
log_errno("Unsupported file type");
return -1;
}
if (err)
return err;
err = EVP_DigestFinal(&ctx, hash, &mdlen);
if (!err) {
log_err("EVP_DigestFinal() failed\n");
return 1;
}
return mdlen;
}
RSA *read_pub_key(const char *keyfile, int x509)
{
FILE *fp;
RSA *key = NULL;
X509 *crt = NULL;
EVP_PKEY *pkey = NULL;
fp = fopen(keyfile, "r");
if (!fp) {
log_err("Failed to open keyfile: %s\n", keyfile);
return NULL;
}
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");
out:
if (pkey)
EVP_PKEY_free(pkey);
if (crt)
X509_free(crt);
fclose(fp);
return key;
}
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;
unsigned char sighash[20];
struct signature_hdr *hdr = (struct signature_hdr *)sig;
log_info("hash-v1: ");
log_dump(hash, size);
key = read_pub_key(keyfile, 0);
if (!key)
return 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));
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;
if (len != sizeof(sighash) || memcmp(out, sighash, len) != 0) {
log_err("Verification failed: %d\n", err);
return -1;
} else {
/*log_info("Verification is OK\n");*/
printf("Verification is OK\n");
}
return 0;
}
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, 1);
if (!key)
return 1;
err = RSA_public_decrypt(siglen - sizeof(*hdr), sig + sizeof(*hdr), 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;
}
int 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 get_hash_algo_from_sig(unsigned char *sig)
{
uint8_t hashalgo;
if (sig[0] == 1) {
hashalgo = ((struct signature_hdr *)sig)->hash;
if (hashalgo >= DIGEST_ALGO_MAX)
return -1;
switch (hashalgo) {
case DIGEST_ALGO_SHA1:
return PKEY_HASH_SHA1;
case DIGEST_ALGO_SHA256:
return PKEY_HASH_SHA256;
default:
return -1;
}
} else if (sig[0] == 2) {
hashalgo = ((struct signature_v2_hdr *)sig)->hash_algo;
if (hashalgo >= PKEY_HASH__LAST)
return -1;
return hashalgo;
} else
return -1;
}
int verify_hash(const unsigned char *hash, int size, unsigned char *sig, int siglen)
{
const char *key;
int x509;
verify_hash_fn_t verify_hash;
/* Get signature type from sig header */
if (sig[0] == DIGSIG_VERSION_1) {
verify_hash = verify_hash_v1;
/* Read pubkey from RSA key */
x509 = 0;
} else if (sig[0] == DIGSIG_VERSION_2) {
verify_hash = verify_hash_v2;
/* Read pubkey from x509 cert */
x509 = 1;
} else
return -1;
/* Determine what key to use for verification*/
key = params.keyfile ? : x509 ?
"/etc/keys/x509_evm.der" :
"/etc/keys/pubkey_evm.pem";
return verify_hash(hash, size, sig, siglen, key);
}
int ima_verify_signature(const char *file, unsigned char *sig, int siglen)
{
unsigned char hash[64];
int hashlen, sig_hash_algo;
if (sig[0] != 0x03) {
log_err("security.ima has no signature\n");
return -1;
}
sig_hash_algo = get_hash_algo_from_sig(sig + 1);
if (sig_hash_algo < 0) {
log_err("Invalid signature\n");
return -1;
}
/* Use hash algorithm as retrieved from signature */
params.hash_algo = pkey_hash_algo[sig_hash_algo];
hashlen = ima_calc_hash(file, hash);
if (hashlen <= 1)
return hashlen;
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-v1: %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, const char *keypass)
{
FILE *fp;
RSA *key;
fp = fopen(keyfile, "r");
if (!fp) {
log_err("Failed to open keyfile: %s\n", keyfile);
return NULL;
}
ERR_load_crypto_strings();
key = PEM_read_RSAPrivateKey(fp, NULL, NULL, (void *)keypass);
if (!key) {
char str[256];
ERR_error_string(ERR_get_error(), str);
log_err("PEM_read_RSAPrivateKey() failed: %s\n", str);
}
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-v1: %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, const char *keypass, unsigned char *sig)
{
if (keypass)
params.keypass = keypass;
return params.x509 ? sign_hash_v2(hashalgo, hash, size, keyfile, sig) :
sign_hash_v1(hashalgo, hash, size, keyfile, sig);
}
static void libinit()
{
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
}