Compare commits

..

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

12 changed files with 591 additions and 1321 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

@ -2,5 +2,4 @@ Dmitry Kasatkin <d.kasatkin@samsung.com>
CONTRIBUTORS: CONTRIBUTORS:
Vivek Goyal <vgoyal@redhat.com> Vivek Goyal <vgoyal@redhat.com>
Mimi Zohar <zohar@linux.vnet.ibm.com>

41
COPYING
View File

@ -1,12 +1,12 @@
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.
Preamble Preamble
The licenses for most software are designed to take away your The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public freedom to share and change it. By contrast, the GNU General Public
@ -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,8 +55,8 @@ 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
0. This License applies to any program or other work which contains 0. This License applies to any program or other work which contains
@ -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
@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally. of promoting the sharing and reuse of software generally.
NO WARRANTY NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 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
possible use to the public, the best way to achieve this is to make it possible use to the public, the best way to achieve this is to make it
@ -303,16 +303,17 @@ 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.
If the program is interactive, make it output a short notice like this If the program is interactive, make it output a short notice like this
when it starts in an interactive mode: when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details. under certain conditions; type `show c' for details.
@ -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,29 +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> 2014-02-17 Dmitry Kasatkin <d.kasatkin@samsung.com>
version 0.7 version 0.7

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 = autogen.sh
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)

462
README
View File

@ -1,187 +1,49 @@
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. 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.
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. This version uses x509 format by default.
for signing and importing the key. Use '--rsa' or '-1' parameter to use old signature format and API.
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) -x509 -config x509_evm.genkey \
------------------------------------- -outform DER -out x509_evm.der -keyout privkey_evm.pem
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 \
-outform DER -out x509_evm.der -keyout privkey_evm.pem
Configuration file x509_evm.genkey: Configuration file x509_evm.genkey:
@ -206,232 +68,88 @@ 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
$ 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
Copy keys to /etc/keys: Generation of EVM keys
cp pubkey_evm.pem /etc/keys $ # create and save the kernel master key (user type)
scp pubkey_evm.pem target:/etc/keys $ keyctl add user kmk "`dd if=/dev/urandom bs=1 count=32 2>/dev/null`" @u
or $ keyctl pipe `keyctl search @u user kmk` > /etc/keys/kmk
cp x509_evm.pem /etc/keys $ # create the EVM encrypted key
scp x509_evm.pem target:/etc/keys $ keyctl add encrypted evm-key "new user:kmk 32" @u
$ keyctl pipe `keyctl search @u encrypted evm-key` >/etc/keys/evm-key
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
# 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
# b) import EVM trusted key # import IMA public key
keyctl add trusted kmk "load `cat /etc/keys/kmk`" @u ima_id=`keyctl newring _ima @u`
keyctl add encrypted evm-key "load `cat /etc/keys/evm-key`" @u evmctl --rsa import /etc/keys/pubkey_evm.pem $ima_id
# 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:
# protect EVM keyring Import X509 certificate into the kernel keyring (since kernel 3.9?)
keyctl setperm $evm_id 0x0b0b0000
# protect IMA keyring $ evmctl import /etc/keys/x509_evm.der `keyctl search @u keyring _ima`
keyctl setperm $ima_id 0x0b0b0000 $ evmctl 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 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,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,7 +1,7 @@
# 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.7, 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: 1.0 Version: 0.7
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

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +1,6 @@
/*
* 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 #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>
@ -75,14 +34,15 @@
#define DATA_SIZE 4096 #define DATA_SIZE 4096
#define SHA1_HASH_LEN 20 #define SHA1_HASH_LEN 20
#define __packed __attribute__((packed)) #define EXT2_IOC_GETVERSION _IOR('v', 1, long)
#define EXT34_IOC_GETVERSION _IOR('f', 3, long)
enum evm_ima_xattr_type { #define FS_IOC_GETFLAGS _IOR('f', 1, long)
IMA_XATTR_DIGEST = 0x01, #define FS_IOC_SETFLAGS _IOW('f', 2, long)
EVM_XATTR_HMAC, #define FS_IOC32_GETFLAGS _IOR('f', 1, int)
EVM_IMA_XATTR_DIGSIG, #define FS_IOC32_SETFLAGS _IOW('f', 2, int)
IMA_XATTR_DIGEST_NG,
}; #define __packed __attribute__((packed))
struct h_misc { struct h_misc {
unsigned long ino; unsigned long ino;
@ -108,12 +68,6 @@ struct h_misc_64 {
unsigned short mode; unsigned short mode;
}; };
struct h_misc_digsig {
uid_t uid;
gid_t gid;
unsigned short mode;
};
enum pubkey_algo { enum pubkey_algo {
PUBKEY_ALGO_RSA, PUBKEY_ALGO_RSA,
PUBKEY_ALGO_MAX, PUBKEY_ALGO_MAX,
@ -176,10 +130,8 @@ 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;
const char *keyfile; char *keyfile;
const char *keypass;
}; };
struct RSA_ASN1_template { struct RSA_ASN1_template {
@ -197,11 +149,6 @@ 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, const char *keypass, 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

@ -19,22 +19,7 @@
* 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 * File: libevm.c
* 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 * IMA/EVM library
*/ */
@ -44,7 +29,6 @@
#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>
@ -53,7 +37,6 @@
#include <openssl/pem.h> #include <openssl/pem.h>
#include <openssl/evp.h> #include <openssl/evp.h>
#include <openssl/x509.h> #include <openssl/x509.h>
#include <openssl/err.h>
#include "imaevm.h" #include "imaevm.h"
@ -127,12 +110,9 @@ 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",
}; };
static void __attribute__ ((constructor)) libinit(void);
void do_dump(FILE *fp, const void *ptr, int len, bool cr) void do_dump(FILE *fp, const void *ptr, int len, bool cr)
{ {
int i; int i;
@ -157,54 +137,54 @@ int get_filesize(const char *filename)
return (int)stats.st_size; return (int)stats.st_size;
} }
static inline off_t get_fdsize(int fd) static inline int 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 stats.st_size; return (int)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 = -1, bs = DATA_SIZE; int err, size, bs = DATA_SIZE;
off_t size, len; size_t 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");
goto out; return -1;
}
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);
if (!fread(data, len, 1, fp)) { err = fread(data, len, 1, fp);
if (!err) {
if (ferror(fp)) { if (ferror(fp)) {
log_err("fread() failed\n\n"); log_err("fread() error\n\n");
goto out; return -1;
} }
break; break;
} }
if (!EVP_DigestUpdate(ctx, data, len)) { err = EVP_DigestUpdate(ctx, data, len);
if (!err) {
log_err("EVP_DigestUpdate() failed\n"); log_err("EVP_DigestUpdate() failed\n");
err = 1; return 1;
goto out;
} }
} }
err = 0;
out:
fclose(fp); fclose(fp);
free(data); free(data);
return err; return 0;
} }
static int add_dir_hash(const char *file, EVP_MD_CTX *ctx) static int add_dir_hash(const char *file, EVP_MD_CTX *ctx)
@ -214,11 +194,10 @@ static int add_dir_hash(const char *file, EVP_MD_CTX *ctx)
DIR *dir; DIR *dir;
unsigned long long ino, off; unsigned long long ino, off;
unsigned int type; unsigned int type;
int result = 0;
dir = opendir(file); dir = opendir(file);
if (!dir) { if (!dir) {
log_err("Failed to open: %s\n", file); log_err("Unable to open %s\n", file);
return -1; return -1;
} }
@ -234,14 +213,13 @@ static int add_dir_hash(const char *file, EVP_MD_CTX *ctx)
err |= EVP_DigestUpdate(ctx, &type, sizeof(type)); err |= EVP_DigestUpdate(ctx, &type, sizeof(type));
if (!err) { if (!err) {
log_err("EVP_DigestUpdate() failed\n"); log_err("EVP_DigestUpdate() failed\n");
result = 1; return 1;
break;
} }
} }
closedir(dir); closedir(dir);
return result; return 0;
} }
static int add_link_hash(const char *path, EVP_MD_CTX *ctx) static int add_link_hash(const char *path, EVP_MD_CTX *ctx)
@ -262,23 +240,22 @@ 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("Failed to stat: %s\n", file); log_err("stat() failed\n");
return err; return err;
} }
@ -334,7 +311,7 @@ RSA *read_pub_key(const char *keyfile, int x509)
fp = fopen(keyfile, "r"); fp = fopen(keyfile, "r");
if (!fp) { if (!fp) {
log_err("Failed to open keyfile: %s\n", keyfile); log_err("Unable to open keyfile %s\n", keyfile);
return NULL; return NULL;
} }
@ -375,7 +352,7 @@ int verify_hash_v1(const unsigned char *hash, int size, unsigned char *sig, int
unsigned char sighash[20]; unsigned char sighash[20];
struct signature_hdr *hdr = (struct signature_hdr *)sig; struct signature_hdr *hdr = (struct signature_hdr *)sig;
log_info("hash-v1: "); log_info("hash: ");
log_dump(hash, size); log_dump(hash, size);
key = read_pub_key(keyfile, 0); key = read_pub_key(keyfile, 0);
@ -493,7 +470,7 @@ static int get_hash_algo_from_sig(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)
{ {
const char *key; char *key;
int x509; int x509;
verify_hash_fn_t verify_hash; verify_hash_fn_t verify_hash;
@ -541,273 +518,3 @@ 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-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();
}