summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jasinski <z.jasinski@samsung.com>2014-05-13 12:00:37 +0200
committerZbigniew Jasinski <z.jasinski@samsung.com>2014-05-13 12:00:37 +0200
commit944cf4990dcb61804d0c9a311985e532b5af2610 (patch)
treec6beebb13f66b3fd65a090950b43b71ea63bbbb5
parent1f16f6e6756f6685863aa8f1b0621f2b172fde1d (diff)
parent7ece92b4e1ff58a8a805548ef60b4edd4b6296be (diff)
downloadima-evm-utils-944cf4990dcb61804d0c9a311985e532b5af2610.tar.gz
ima-evm-utils-944cf4990dcb61804d0c9a311985e532b5af2610.tar.bz2
ima-evm-utils-944cf4990dcb61804d0c9a311985e532b5af2610.zip
Merge branch 'upstream' into tizen
Conflicts: packaging/ima-evm-utils.spec Change-Id: I33c22533d7a4d1ff00cd75ef2df4bae0ded1c3a4
-rw-r--r--AUTHORS1
-rw-r--r--COPYING41
-rw-r--r--ChangeLog19
-rw-r--r--INSTALL8
-rw-r--r--README43
-rw-r--r--configure.ac6
-rwxr-xr-xexamples/ima-gen-local-ca.sh29
-rwxr-xr-xexamples/ima-genkey-self.sh29
-rwxr-xr-xexamples/ima-genkey.sh33
-rw-r--r--packaging/ima-evm-utils.spec3
-rw-r--r--packaging/ima-evm-utils.spec.in (renamed from ima-evm-utils.spec.in)3
-rw-r--r--src/Makefile.am12
-rw-r--r--src/evmctl.c1423
-rw-r--r--src/imaevm.h195
-rw-r--r--src/libimaevm.c535
15 files changed, 1640 insertions, 740 deletions
diff --git a/AUTHORS b/AUTHORS
index 3a0e428..9ffa1a1 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -2,4 +2,5 @@ Dmitry Kasatkin <d.kasatkin@samsung.com>
CONTRIBUTORS:
Vivek Goyal <vgoyal@redhat.com>
+Mimi Zohar <zohar@linux.vnet.ibm.com>
diff --git a/COPYING b/COPYING
index d60c31a..d159169 100644
--- a/COPYING
+++ b/COPYING
@@ -1,12 +1,12 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
- Preamble
+ Preamble
The licenses for most software are designed to take away your
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
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
+the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
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
modification follow.
-
- GNU GENERAL PUBLIC LICENSE
+
+ GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
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
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
-
+
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
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
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
-
+
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
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
be a consequence of the rest of this License.
-
+
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
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 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
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
POSSIBILITY OF SUCH DAMAGES.
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
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
@@ -303,17 +303,16 @@ the "copyright" line and a pointer to where the full notice is found.
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, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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
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'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
@@ -336,5 +335,5 @@ necessary. Here is a sample; alter the names:
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
+library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
index 79b1e3d..3ead819 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+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
diff --git a/INSTALL b/INSTALL
index a1e89e1..007e939 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,7 +1,7 @@
Installation Instructions
*************************
-Copyright (C) 1994-1996, 1999-2002, 2004-2011 Free Software Foundation,
+Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
Inc.
Copying and distribution of this file, with or without modification,
@@ -309,9 +309,10 @@ causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
-an Autoconf bug. Until the bug is fixed you can use this workaround:
+an Autoconf limitation. Until the limitation is lifted, you can use
+this workaround:
- CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+ CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
@@ -367,4 +368,3 @@ operates.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.
-
diff --git a/README b/README
index 3e8d1df..f460ec7 100644
--- a/README
+++ b/README
@@ -14,14 +14,13 @@ Key and signature formats
EVM support (v2) in latest version of the kernel adds the file system UUID to
the HMAC calculation. It is controlled by the CONFIG_EVM_HMAC_VERSION and
-version 2 is enabled by default. To include the UUID to the signature calculation,
-it is necessary to provide '--uuid' or '-u' parameter to the 'sign' command.
-UUID can be provided on command line in form of '-uUUID' or '--uuid=UUID'.
+version 2 is enabled by default. In this version default UUID is included by
+default. Custom value can be supplied via '--uuid=UUID' or '-uUUID' parameter
+to the 'sign' command. To use old format HMAC format use '-' as a parameter.
Latest kernel got IMA/EVM support for using X509 certificates and asymmetric key
-support for verifying digital signatures. The new command line parameter
-'-x' or '--x509' was added to the evmctl to enable using of X509 certificates
-and new signature format.
+support for verifying digital signatures. This version uses x509 format by default.
+Use '--rsa' or '-1' parameter to use old signature format and API.
Key generation
@@ -83,6 +82,16 @@ or
$ scp x509_evm.pem target:/etc/keys
+Generation of EVM keys
+
+ $ # create and save the kernel master key (user type)
+ $ keyctl add user kmk "`dd if=/dev/urandom bs=1 count=32 2>/dev/null`" @u
+ $ keyctl pipe `keyctl search @u user kmk` > /etc/keys/kmk
+ $ # create the EVM encrypted key
+ $ keyctl add encrypted evm-key "new user:kmk 32" @u
+ $ keyctl pipe `keyctl search @u encrypted evm-key` >/etc/keys/evm-key
+
+
Initialization
--------------
@@ -93,16 +102,16 @@ Here is an example script /etc/initramfs-tools/scripts/local-top/ima.sh
# import EVM HMAC key
keyctl clear @u
- keyctl add user kmk "testing123" @u
+ cat /etc/keys/kmk | keyctl padd user kmk @u
keyctl add encrypted evm-key "load `cat /etc/keys/evm-key`" @u
# import IMA public key
ima_id=`keyctl newring _ima @u`
- evmctl import /etc/keys/pubkey_evm.pem $ima_id
+ evmctl --rsa import /etc/keys/pubkey_evm.pem $ima_id
# import EVM public key
evm_id=`keyctl newring _evm @u`
- evmctl import /etc/keys/pubkey_evm.pem $evm_id
+ evmctl --rsa import /etc/keys/pubkey_evm.pem $evm_id
# enable EVM
echo "1" > /sys/kernel/security/evm
@@ -110,8 +119,8 @@ Here is an example script /etc/initramfs-tools/scripts/local-top/ima.sh
Import X509 certificate into the kernel keyring (since kernel 3.9?)
- $ evmctl -x import /etc/keys/x509_evm.der `keyctl search @u keyring _ima`
- $ evmctl -x import /etc/keys/x509_evm.der `keyctl search @u keyring _evm`
+ $ evmctl import /etc/keys/x509_evm.der `keyctl search @u keyring _ima`
+ $ evmctl import /etc/keys/x509_evm.der `keyctl search @u keyring _evm`
Signing
@@ -121,24 +130,24 @@ Default public key: /etc/keys/pubkey_evm.pem
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.
+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] [-x] --imahash test.txt
+ $ evmctl sign [-u] [-1] --imahash test.txt
Sign file with both IMA and EVM signatures - for immutable files
- $ evmctl sign [-u] [-x] --imasig test.txt
+ $ evmctl sign [-u] [-1] --imasig test.txt
Sign file with IMA signature - for immutable files
- $ evmctl ima_sign [-x] test.txt
+ $ evmctl ima_sign [-1] test.txt
Label whole filesystem with EVM signatures
- $ find / \( -fstype rootfs -o -fstype ext4 \) -exec evmctl sign [-u] [-x] --imahash '{}' \;
+ $ find / \( -fstype rootfs -o -fstype ext4 \) -exec evmctl sign [-u] [-1] --imahash '{}' \;
Label filesystem in fix mode - kernel sets correct values to IMA and EVM xattrs
diff --git a/configure.ac b/configure.ac
index 5decc4f..78821c5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,8 +1,8 @@
# autoconf script
AC_PREREQ([2.65])
-AC_INIT(ima-evm-utils, 0.6, d.kasatkin@samsung.com)
-AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
+AC_INIT(ima-evm-utils, 0.8, d.kasatkin@samsung.com)
+AM_INIT_AUTOMAKE
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
@@ -49,7 +49,7 @@ fi
AC_CONFIG_FILES([Makefile
src/Makefile
- ima-evm-utils.spec
+ packaging/ima-evm-utils.spec
])
AC_OUTPUT
diff --git a/examples/ima-gen-local-ca.sh b/examples/ima-gen-local-ca.sh
new file mode 100755
index 0000000..1f24949
--- /dev/null
+++ b/examples/ima-gen-local-ca.sh
@@ -0,0 +1,29 @@
+#!/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
+
diff --git a/examples/ima-genkey-self.sh b/examples/ima-genkey-self.sh
new file mode 100755
index 0000000..e293b94
--- /dev/null
+++ b/examples/ima-genkey-self.sh
@@ -0,0 +1,29 @@
+#!/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
+
diff --git a/examples/ima-genkey.sh b/examples/ima-genkey.sh
new file mode 100755
index 0000000..b08778f
--- /dev/null
+++ b/examples/ima-genkey.sh
@@ -0,0 +1,33 @@
+#!/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
+
diff --git a/packaging/ima-evm-utils.spec b/packaging/ima-evm-utils.spec
index 2b147de..42c56ef 100644
--- a/packaging/ima-evm-utils.spec
+++ b/packaging/ima-evm-utils.spec
@@ -1,5 +1,5 @@
Name: ima-evm-utils
-Version: 0.6
+Version: 0.8
Release: 1
Summary: IMA/EVM control utility
Group: System/Libraries
@@ -45,6 +45,7 @@ exit 0
%defattr(-,root,root,-)
%{_bindir}/*
%{_libdir}/*
+%{_includedir}/*
%changelog
* Thu Apr 05 2012 Dmitry Kasatkin <dmitry.kasatkin@intel.com>
diff --git a/ima-evm-utils.spec.in b/packaging/ima-evm-utils.spec.in
index ad08f73..7ca6c6f 100644
--- a/ima-evm-utils.spec.in
+++ b/packaging/ima-evm-utils.spec.in
@@ -44,7 +44,8 @@ exit 0
%files
%defattr(-,root,root,-)
%{_bindir}/*
-%{_libdir}/*
+%{_libdir}/libimaevm.*
+%{_includedir}/*
%changelog
* Thu Apr 05 2012 Dmitry Kasatkin <dmitry.kasatkin@intel.com>
diff --git a/src/Makefile.am b/src/Makefile.am
index 6779baf..deb18fb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,10 +1,20 @@
+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
evmctl_SOURCES = evmctl.c
evmctl_CPPFLAGS = $(OPENSSL_CFLAGS)
evmctl_LDFLAGS = $(LDFLAGS_READLINE)
-evmctl_LDADD = $(OPENSSL_LIBS) -lkeyutils
+evmctl_LDADD = $(OPENSSL_LIBS) -lkeyutils libimaevm.la
INCLUDES = -I$(top_srcdir) -include config.h
diff --git a/src/evmctl.c b/src/evmctl.c
index aa61338..c5529f8 100644
--- a/src/evmctl.c
+++ b/src/evmctl.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2011 Nokia Corporation
* Copyright (C) 2011,2012,2013 Intel Corporation
- * Copyright (C) 2013 Samsung Electronics
+ * Copyright (C) 2013,2014 Samsung Electronics
*
* Authors:
* Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
@@ -22,209 +22,48 @@
* 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: evmctl.c
* IMA/EVM control program
*/
#include <sys/types.h>
+#include <sys/param.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
-#include <sys/param.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
+#include <dirent.h>
#include <attr/xattr.h>
#include <getopt.h>
-#include <signal.h>
#include <keyutils.h>
#include <asm/byteorder.h>
-#include <syslog.h>
-#include <attr/xattr.h>
-#include <dirent.h>
#include <ctype.h>
#include <openssl/sha.h>
-#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/hmac.h>
-#include <openssl/engine.h>
-#include <openssl/evp.h>
#include <openssl/err.h>
-#include <openssl/x509.h>
+#include <openssl/rsa.h>
#define USE_FPRINTF
-#ifdef USE_FPRINTF
-#define do_log(level, fmt, args...) ({ if (level <= verbose) fprintf(stderr, fmt, ##args); })
-#define do_log_dump(level, p, len) ({ if (level <= verbose) do_dump(stderr, p, len); })
-#else
-#define do_log(level, fmt, args...) syslog(level, fmt, ##args)
-#define do_log_dump(p, len)
-#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)
-#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)
-#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 EXT2_IOC_GETVERSION _IOR('v', 1, long)
-#define EXT34_IOC_GETVERSION _IOR('f', 3, long)
-
-#define FS_IOC_GETFLAGS _IOR('f', 1, long)
-#define FS_IOC_SETFLAGS _IOW('f', 2, long)
-#define FS_IOC32_GETFLAGS _IOR('f', 1, int)
-#define FS_IOC32_SETFLAGS _IOW('f', 2, int)
-
-struct h_misc {
- unsigned long ino;
- uint32_t generation;
- uid_t uid;
- gid_t gid;
- unsigned short mode;
-} hmac_misc;
-
-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];
-} __attribute__ ((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];
-} __attribute__ ((packed));
-
-enum pkey_hash_algo {
- PKEY_HASH_MD4,
- PKEY_HASH_MD5,
- PKEY_HASH_SHA1,
- PKEY_HASH_RIPE_MD_160,
- PKEY_HASH_SHA256,
- PKEY_HASH_SHA384,
- PKEY_HASH_SHA512,
- PKEY_HASH_SHA224,
- PKEY_HASH__LAST
-};
-
-const char *const pkey_hash_algo[PKEY_HASH__LAST] = {
- [PKEY_HASH_MD4] = "md4",
- [PKEY_HASH_MD5] = "md5",
- [PKEY_HASH_SHA1] = "sha1",
- [PKEY_HASH_RIPE_MD_160] = "rmd160",
- [PKEY_HASH_SHA256] = "sha256",
- [PKEY_HASH_SHA384] = "sha384",
- [PKEY_HASH_SHA512] = "sha512",
- [PKEY_HASH_SHA224] = "sha224",
-};
-
-/*
- * signature format v2 - for using with asymmetric keys
- */
-struct signature_v2_hdr {
- uint8_t version; /* signature format version */
- uint8_t hash_algo; /* Digest algorithm [enum pkey_hash_algo] */
- uint32_t keyid; /* IMA key identifier - not X509/PGP specific*/
- uint16_t sig_size; /* signature size */
- uint8_t sig[0]; /* signature payload */
-} __attribute__ ((packed));
-
-
-/*
- * Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2].
- */
-static const uint8_t RSA_digest_info_MD5[] = {
- 0x30, 0x20, 0x30, 0x0C, 0x06, 0x08,
- 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, /* OID */
- 0x05, 0x00, 0x04, 0x10
-};
-
-static const uint8_t RSA_digest_info_SHA1[] = {
- 0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
- 0x2B, 0x0E, 0x03, 0x02, 0x1A,
- 0x05, 0x00, 0x04, 0x14
-};
-
-static const uint8_t RSA_digest_info_RIPE_MD_160[] = {
- 0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
- 0x2B, 0x24, 0x03, 0x02, 0x01,
- 0x05, 0x00, 0x04, 0x14
-};
-
-static const uint8_t RSA_digest_info_SHA224[] = {
- 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09,
- 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04,
- 0x05, 0x00, 0x04, 0x1C
-};
-
-static const uint8_t RSA_digest_info_SHA256[] = {
- 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
- 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
- 0x05, 0x00, 0x04, 0x20
-};
-
-static const uint8_t RSA_digest_info_SHA384[] = {
- 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09,
- 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
- 0x05, 0x00, 0x04, 0x30
-};
-
-static const uint8_t RSA_digest_info_SHA512[] = {
- 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09,
- 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
- 0x05, 0x00, 0x04, 0x40
-};
-
-static const struct RSA_ASN1_template {
- const uint8_t *data;
- size_t size;
-} RSA_ASN1_templates[PKEY_HASH__LAST] = {
-#define _(X) { RSA_digest_info_##X, sizeof(RSA_digest_info_##X) }
- [PKEY_HASH_MD5] = _(MD5),
- [PKEY_HASH_SHA1] = _(SHA1),
- [PKEY_HASH_RIPE_MD_160] = _(RIPE_MD_160),
- [PKEY_HASH_SHA256] = _(SHA256),
- [PKEY_HASH_SHA384] = _(SHA384),
- [PKEY_HASH_SHA512] = _(SHA512),
- [PKEY_HASH_SHA224] = _(SHA224),
-#undef _
-};
+#include "imaevm.h"
static char *evm_config_xattrnames[] = {
"security.selinux",
@@ -242,65 +81,37 @@ struct command {
char *msg; /* extra info message */
};
-static int verbose = LOG_INFO - 1;
static int g_argc;
static char **g_argv;
static int xattr = 1;
static int sigdump;
static int digest;
static int digsig;
-static const char *hash_algo = "sha1";
-static int user_hash_algo;
static char *keypass;
static int sigfile;
-static int modsig;
-static char *uuid_str;
-static int x509;
-static int user_sig_type;
-static char *keyfile;
+static int x509 = 1;
+static char *uuid_str = "+";
+static char *search_type;
+static int recursive;
+static int msize;
+static dev_t fs_dev;
+
+typedef int (*find_cb_t)(const char *path);
+static int find(const char *path, int dts, find_cb_t func);
+
+#define REG_MASK (1 << DT_REG)
+#define DIR_MASK (1 << DT_DIR)
+#define LNK_MASK (1 << DT_LNK)
+#define CHR_MASK (1 << DT_CHR)
+#define BLK_MASK (1 << DT_BLK)
typedef int (*sign_hash_fn_t)(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig);
static sign_hash_fn_t sign_hash;
-typedef int (*verify_hash_fn_t)(const unsigned char *hash, int size, unsigned char *sig, int siglen, const char *keyfile);
-
-static verify_hash_fn_t verify_hash;
-
struct command cmds[];
static void print_usage(struct command *cmd);
-static void do_dump(FILE *fp, const void *ptr, int len)
-{
- int i;
- uint8_t *data = (uint8_t *) ptr;
-
- for (i = 0; i < len; i++)
- fprintf(fp, "%02x", data[i]);
- fprintf(fp, "\n");
-}
-
-static void dump(const void *ptr, int len)
-{
- do_dump(stdout, ptr, len);
-}
-
-static inline 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 int get_fdsize(int fd)
-{
- struct stat stats;
- /* Need to know the file length */
- fstat(fd, &stats);
- return (int)stats.st_size;
-}
-
static int bin2file(const char *file, const char *ext, const unsigned char *data, int len)
{
FILE *fp;
@@ -386,47 +197,6 @@ static int key2bin(RSA *key, unsigned char *pub)
return offset;
}
-static RSA *read_pub_key(const char *keyfile)
-{
- FILE *fp;
- RSA *key = NULL;
- X509 *crt = NULL;
- EVP_PKEY *pkey = NULL;
-
- fp = fopen(keyfile, "r");
- if (!fp) {
- log_err("Unable 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;
-}
-
static void calc_keyid_v1(uint8_t *keyid, char *str, const unsigned char *pkey, int len)
{
uint8_t sha1[SHA_DIGEST_LENGTH];
@@ -483,7 +253,7 @@ static RSA *read_priv_key(const char *keyfile)
return key;
}
-int get_hash_algo_v1(const char *algo)
+static int get_hash_algo_v1(const char *algo)
{
if (!strcmp(algo, "sha1"))
@@ -547,24 +317,13 @@ static int sign_hash_v1(const char *hashalgo, const unsigned char *hash, int siz
*blen = __cpu_to_be16(len << 3);
len += sizeof(*hdr) + 2;
log_info("evm/ima signature: %d bytes\n", len);
- if (sigdump || verbose >= LOG_INFO)
+ if (sigdump || params.verbose >= LOG_INFO)
dump(sig, len);
out:
RSA_free(key);
return len;
}
-uint8_t get_hash_algo(const char *algo)
-{
- int i;
-
- for (i = 0; i < PKEY_HASH__LAST; i++)
- if (!strcmp(algo, pkey_hash_algo[i]))
- return i;
-
- return PKEY_HASH_SHA1;
-}
-
static int sign_hash_v2(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig)
{
struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
@@ -605,7 +364,7 @@ static int sign_hash_v2(const char *algo, const unsigned char *hash, int size, c
hdr->sig_size = __cpu_to_be16(len);
len += sizeof(*hdr);
log_info("evm/ima signature: %d bytes\n", len);
- if (sigdump || verbose >= LOG_INFO)
+ if (sigdump || params.verbose >= LOG_INFO)
dump(sig, len);
out:
if (buf)
@@ -636,6 +395,42 @@ static int hex_to_bin(char ch)
return -1;
}
+static int hex2bin(uint8_t *dst, const char *src, size_t count)
+{
+ int hi, lo;
+
+ while (count--) {
+ if (*src == ' ')
+ src++;
+
+ hi = hex_to_bin(*src++);
+ lo = hex_to_bin(*src++);
+
+ if ((hi < 0) || (lo < 0))
+ return -1;
+
+ *dst++ = (hi << 4) | lo;
+ }
+ return 0;
+}
+
+#define hex_asc_lo(x) hex_asc[((x) & 0x0f)]
+#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4]
+
+const char hex_asc[] = "0123456789abcdef";
+
+/* this is faster than fprintf - makes sense? */
+static void bin2hex(uint8_t *buf, size_t buflen, FILE *stream)
+{
+ char asciihex[2];
+
+ for (; buflen--; buf++) {
+ asciihex[0] = hex_asc_hi(*buf);
+ asciihex[1] = hex_asc_lo(*buf);
+ fwrite(asciihex, 2, 1, stream);
+ }
+}
+
static int pack_uuid(const char *uuid_str, char *uuid)
{
int i;
@@ -675,7 +470,7 @@ static int get_uuid(struct stat *st, char *uuid)
FILE *fp;
size_t len;
- if (uuid_str[0] != '-')
+ if (uuid_str[0] != '+')
return pack_uuid(uuid_str, uuid);
dev = st->st_dev;
@@ -704,8 +499,8 @@ static int get_uuid(struct stat *st, char *uuid)
static int calc_evm_hash(const char *file, unsigned char *hash)
{
struct stat st;
- int fd, err;
- uint32_t generation;
+ int err;
+ uint32_t generation = 0;
EVP_MD_CTX ctx;
unsigned int mdlen;
char **xattrname;
@@ -713,25 +508,29 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
char list[1024];
ssize_t list_size;
char uuid[16];
+ struct h_misc_64 hmac_misc;
+ int hmac_size;
- fd = open(file, 0);
- if (fd < 0) {
- log_err("Unable to open %s\n", file);
+ if (lstat(file, &st)) {
+ log_err("lstat() failed\n");
return -1;
}
- if (fstat(fd, &st)) {
- log_err("fstat() failed\n");
- return -1;
- }
-
- if (ioctl(fd, EXT34_IOC_GETVERSION, &generation)) {
- log_err("ioctl() failed\n");
- return -1;
+ if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) {
+ /* we cannot at the momement to get generation of special files..
+ * kernel API does not support it */
+ int fd = open(file, 0);
+ if (fd < 0) {
+ log_err("Unable to open %s\n", file);
+ return -1;
+ }
+ if (ioctl(fd, EXT34_IOC_GETVERSION, &generation)) {
+ log_err("ioctl() failed\n");
+ return -1;
+ }
+ close(fd);
}
- close(fd);
-
log_info("generation: %u\n", generation);
list_size = llistxattr(file, list, sizeof(list));
@@ -747,7 +546,7 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
}
for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
- err = getxattr(file, *xattrname, xattr_value, sizeof(xattr_value));
+ err = lgetxattr(file, *xattrname, xattr_value, sizeof(xattr_value));
if (err < 0) {
log_info("no xattr: %s\n", *xattrname);
continue;
@@ -767,19 +566,43 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
}
memset(&hmac_misc, 0, sizeof(hmac_misc));
- hmac_misc.ino = st.st_ino;
- hmac_misc.generation = generation;
- hmac_misc.uid = st.st_uid;
- hmac_misc.gid = st.st_gid;
- hmac_misc.mode = st.st_mode;
- err = EVP_DigestUpdate(&ctx, (const unsigned char *)&hmac_misc, sizeof(hmac_misc));
+ if (msize == 0) {
+ struct h_misc *hmac = (struct h_misc *)&hmac_misc;
+ hmac_size = sizeof(*hmac);
+ hmac->ino = st.st_ino;
+ hmac->generation = generation;
+ hmac->uid = st.st_uid;
+ hmac->gid = st.st_gid;
+ hmac->mode = st.st_mode;
+ } else if (msize == 64) {
+ struct h_misc_64 *hmac = (struct h_misc_64 *)&hmac_misc;
+ hmac_size = sizeof(*hmac);
+ hmac->ino = st.st_ino;
+ hmac->generation = generation;
+ hmac->uid = st.st_uid;
+ hmac->gid = st.st_gid;
+ hmac->mode = st.st_mode;
+ } else {
+ struct h_misc_32 *hmac = (struct h_misc_32 *)&hmac_misc;
+ hmac_size = sizeof(*hmac);
+ hmac->ino = st.st_ino;
+ hmac->generation = generation;
+ hmac->uid = st.st_uid;
+ hmac->gid = st.st_gid;
+ hmac->mode = st.st_mode;
+ }
+
+ log_debug("hmac_misc (%d): ", hmac_size);
+ log_debug_dump(&hmac_misc, hmac_size);
+
+ err = EVP_DigestUpdate(&ctx, &hmac_misc, hmac_size);
if (!err) {
log_err("EVP_DigestUpdate() failed\n");
return 1;
}
- if (uuid_str) {
+ if (*uuid_str != '-') {
err = get_uuid(&st, uuid);
if (err)
return -1;
@@ -815,7 +638,7 @@ static int sign_evm(const char *file, const char *key)
return len;
if (xattr) {
- err = setxattr(file, "security.evm", sig, len + 1, 0);
+ err = lsetxattr(file, "security.evm", sig, len + 1, 0);
if (err < 0) {
log_err("setxattr failed: %s\n", file);
return err;
@@ -825,180 +648,23 @@ static int sign_evm(const char *file, const char *key)
return 0;
}
-static int add_file_hash(const char *file, EVP_MD_CTX *ctx)
-{
- uint8_t *data;
- int err, size, bs = DATA_SIZE;
- size_t len;
- FILE *fp;
-
- data = malloc(bs);
- if (!data) {
- log_err("malloc failed\n");
- return -1;
- }
-
- fp = fopen(file, "r");
- if (!fp) {
- log_err("Unable to open %s\n", file);
- return -1;
- }
-
- for (size = get_fdsize(fileno(fp)); size; size -= len) {
- len = MIN(size, bs);
- err = fread(data, len, 1, fp);
- if (!err) {
- if (ferror(fp)) {
- log_err("fread() error\n\n");
- return -1;
- }
- break;
- }
- err = EVP_DigestUpdate(ctx, data, len);
- if (!err) {
- log_err("EVP_DigestUpdate() failed\n");
- return 1;
- }
- }
-
- fclose(fp);
- free(data);
-
- return 0;
-}
-
-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;
-
- dir = opendir(file);
- if (!dir) {
- log_err("Unable 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");
- return 1;
- }
- }
-
- closedir(dir);
-
- return 0;
-}
-
-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));
-}
-
-static int calc_hash(const char *file, uint8_t *hash)
-{
- struct stat st;
- EVP_MD_CTX ctx;
- const EVP_MD *md;
- unsigned int mdlen;
- int err;
-
- /* Need to know the file length */
- err = lstat(file, &st);
- if (err < 0) {
- log_err("stat() failed\n");
- return err;
- }
-
- md = EVP_get_digestbyname(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;
-}
-
static int hash_ima(const char *file)
{
unsigned char hash[65] = "\x01"; /* MAX hash size + 1 */
int len, err;
- len = calc_hash(file, hash + 1);
+ len = ima_calc_hash(file, hash + 1);
if (len <= 1)
return len;
- if (verbose >= LOG_INFO)
+ if (params.verbose >= LOG_INFO)
log_info("hash: ");
- if (sigdump || verbose >= LOG_INFO)
+ if (sigdump || params.verbose >= LOG_INFO)
dump(hash, len + 1);
if (xattr) {
- err = setxattr(file, "security.ima", hash, len + 1, 0);
+ err = lsetxattr(file, "security.ima", hash, len + 1, 0);
if (err < 0) {
log_err("setxattr failed: %s\n", file);
return err;
@@ -1025,35 +691,24 @@ static int sign_ima(const char *file, const char *key)
{
unsigned char hash[64];
unsigned char sig[1024] = "\x03";
- char magic[] = "This Is A Crypto Signed Module";
int len, err;
- len = calc_hash(file, hash);
+ len = ima_calc_hash(file, hash);
if (len <= 1)
return len;
- len = sign_hash(hash_algo, hash, len, key, sig + 1);
+ len = sign_hash(params.hash_algo, hash, len, key, sig + 1);
if (len <= 1)
return len;
/* add header */
len++;
- if (modsig) {
- /* add signature length */
- *(uint16_t *)(sig + len) = __cpu_to_be16(len - 1);
- len += sizeof(uint16_t);
- memcpy(sig + len, magic, sizeof(magic) - 1);
- len += sizeof(magic) - 1;
- bin2file(file, "sig", sig + 1, len - 1);
- return 0;
- }
-
if (sigfile)
bin2file(file, "sig", sig, len);
if (xattr) {
- err = setxattr(file, "security.ima", sig, len, 0);
+ err = lsetxattr(file, "security.ima", sig, len, 0);
if (err < 0) {
log_err("setxattr failed: %s\n", file);
return err;
@@ -1063,26 +718,47 @@ static int sign_ima(const char *file, const char *key)
return 0;
}
-static int cmd_sign_ima(struct command *cmd)
+static int get_file_type(const char *path, const char *search_type)
{
- char *key, *file = g_argv[optind++];
+ int err, dts = 0, i;
+ struct stat st;
- if (!file) {
- log_err("Parameters missing\n");
- print_usage(cmd);
- return -1;
+ for (i = 0; search_type[i]; i++) {
+ switch (search_type[i]) {
+ case 'f':
+ dts |= REG_MASK; break;
+ case 'd':
+ dts |= DIR_MASK; break;
+ case 's':
+ dts |= BLK_MASK | CHR_MASK | LNK_MASK; break;
+ case 'm':
+ /* stay within the same filesystem*/
+ err = lstat(path, &st);
+ if (err < 0) {
+ log_err("stat() failed\n");
+ return err;
+ }
+ fs_dev = st.st_dev; /* filesystem to start from */
+ break;
+ }
}
- key = keyfile ? : "/etc/keys/privkey_evm.pem";
+ return dts;
+}
- return sign_ima(file, key);
+static int sign_ima_file(const char *file)
+{
+ char *key;
+
+ key = params.keyfile ? : "/etc/keys/privkey_evm.pem";
+ return sign_ima(file, key);
}
-static int cmd_sign_evm(struct command *cmd)
+static int cmd_sign_ima(struct command *cmd)
{
- char *key, *file = g_argv[optind++];
- int err;
+ char *file = g_argv[optind++];
+ int err, dts = REG_MASK; /* only regular files by default */
if (!file) {
log_err("Parameters missing\n");
@@ -1090,111 +766,111 @@ static int cmd_sign_evm(struct command *cmd)
return -1;
}
- key = keyfile ? : "/etc/keys/privkey_evm.pem";
-
- if (digsig) {
- err = sign_ima(file, key);
- if (err)
- return err;
- }
-
- if (digest) {
- err = hash_ima(file);
- if (err)
- return err;
+ if (recursive) {
+ if (search_type) {
+ dts = get_file_type(file, search_type);
+ if (dts < 0)
+ return dts;
+ }
+ err = find(file, dts, sign_ima_file);
+ } else {
+ err = sign_ima_file(file);
}
- return sign_evm(file, key);
+ return err;
}
-static int verify_hash_v1(const unsigned char *hash, int size, unsigned char *sig, int siglen, const char *keyfile)
+static int cmd_sign_hash(struct command *cmd)
{
- int err, len;
- SHA_CTX ctx;
- unsigned char out[1024];
- RSA *key;
- unsigned char sighash[20];
- struct signature_hdr *hdr = (struct signature_hdr *)sig;
+ char *key, *token, *line = NULL;
+ int hashlen = 0;
+ size_t line_len;
+ ssize_t len;
+ unsigned char hash[64];
+ unsigned char sig[1024] = "\x03";
+ int siglen;
- log_info("hash: ");
- log_dump(hash, size);
+ key = params.keyfile ? : "/etc/keys/privkey_evm.pem";
- key = read_pub_key(keyfile);
- if (!key)
- return 1;
+ /* support reading hash (eg. output of shasum) */
+ while ((len = getline(&line, &line_len, stdin)) > 0) {
+ /* remove end of line */
+ if (line[len - 1] == '\n')
+ line[--len] = '\0';
- 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));
+ /* find the end of the hash */
+ token = strpbrk(line, ", \t");
+ hashlen = token ? token - line : strlen(line);
- 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;
- }
+ hex2bin(hash, line, hashlen);
+ siglen = sign_hash(params.hash_algo, hash, hashlen/2,
+ key, sig + 1);
+ if (siglen <= 1)
+ return siglen;
- len = err;
+ fwrite(line, len, 1, stdout);
+ fprintf(stdout, " ");
+ bin2hex(sig, siglen + 1, stdout);
+ fprintf(stdout, "\n");
+ }
- if (len != sizeof(sighash) || memcmp(out, sighash, len) != 0) {
- log_err("Verification failed: %d\n", err);
+ if (!hashlen) {
+ log_err("Parameters missing\n");
+ print_usage(cmd);
return -1;
- } else {
- /*log_info("Verification is OK\n");*/
- printf("Verification is OK\n");
}
return 0;
}
-static int verify_hash_v2(const unsigned char *hash, int size, unsigned char *sig, int siglen, const char *keyfile)
+static int sign_evm_path(const char *file)
{
- 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;
+ char *key;
+ int err;
- log_info("hash: ");
- log_dump(hash, size);
+ key = params.keyfile ? : "/etc/keys/privkey_evm.pem";
- key = read_pub_key(keyfile);
- if (!key)
- return 1;
+ if (digsig) {
+ err = sign_ima(file, key);
+ if (err)
+ return err;
+ }
- 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;
+ if (digest) {
+ err = hash_ima(file);
+ if (err)
+ return err;
}
- len = err;
+ return sign_evm(file, key);
+}
- asn1 = &RSA_ASN1_templates[hdr->hash_algo];
+static int cmd_sign_evm(struct command *cmd)
+{
+ char *path = g_argv[optind++];
+ int err, dts = REG_MASK; /* only regular files by default */
- if (len < asn1->size || memcmp(out, asn1->data, asn1->size)) {
- log_err("Verification failed: %d\n", err);
+ if (!path) {
+ log_err("Parameters missing\n");
+ print_usage(cmd);
return -1;
}
- len -= asn1->size;
-
- if (len != size || memcmp(out + asn1->size, hash, len)) {
- log_err("Verification failed: %d\n", err);
- return -1;
+ if (recursive) {
+ if (search_type) {
+ dts = get_file_type(path, search_type);
+ if (dts < 0)
+ return dts;
+ }
+ err = find(path, dts, sign_evm_path);
+ } else {
+ err = sign_evm_path(path);
}
- /*log_info("Verification is OK\n");*/
- printf("Verification is OK\n");
-
- return 0;
+ return err;
}
-static int verify_evm(const char *file, const char *key)
+static int verify_evm(const char *file)
{
unsigned char hash[20];
unsigned char sig[1024];
@@ -1204,7 +880,7 @@ static int verify_evm(const char *file, const char *key)
if (len <= 1)
return len;
- len = getxattr(file, "security.evm", sig, sizeof(sig));
+ len = lgetxattr(file, "security.evm", sig, sizeof(sig));
if (len < 0) {
log_err("getxattr failed\n");
return len;
@@ -1215,12 +891,12 @@ static int verify_evm(const char *file, const char *key)
return -1;
}
- return verify_hash(hash, sizeof(hash), sig + 1, len - 1, key);
+ return verify_hash(hash, sizeof(hash), sig + 1, len - 1);
}
static int cmd_verify_evm(struct command *cmd)
{
- char *key, *file = g_argv[optind++];
+ char *file = g_argv[optind++];
if (!file) {
log_err("Parameters missing\n");
@@ -1228,50 +904,16 @@ static int cmd_verify_evm(struct command *cmd)
return -1;
}
- key = keyfile ? : x509 ?
- "/etc/keys/x509_evm.der" :
- "/etc/keys/pubkey_evm.pem";
-
- return verify_evm(file, key);
-}
-
-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;
+ return verify_evm(file);
}
static int verify_ima(const char *file)
{
- unsigned char hash[64];
unsigned char sig[1024];
- int len, hashlen;
- int sig_hash_algo;
- char *key;
+ int len;
if (xattr) {
- len = getxattr(file, "security.ima", sig, sizeof(sig));
+ len = lgetxattr(file, "security.ima", sig, sizeof(sig));
if (len < 0) {
log_err("getxattr failed\n");
return len;
@@ -1285,44 +927,7 @@ static int verify_ima(const char *file)
free(tmp);
}
- if (sig[0] != 0x03) {
- log_err("security.ima has no signature\n");
- return -1;
- }
-
- /* If user specified an hash algo on command line, let it override */
- if (!user_hash_algo) {
- 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 */
- hash_algo = pkey_hash_algo[sig_hash_algo];
- }
-
- hashlen = calc_hash(file, hash);
- if (hashlen <= 1)
- return hashlen;
-
- /* Get signature type from sig header if user did not enforce it */
- if (!user_sig_type) {
- if (sig[1] == DIGSIG_VERSION_1)
- verify_hash = verify_hash_v1;
- else if (sig[1] == DIGSIG_VERSION_2) {
- verify_hash = verify_hash_v2;
- /* Read pubkey from x509 cert */
- x509 = 1;
- }
- }
-
- /* Determine what key to use for verification*/
- key = keyfile ? : x509 ?
- "/etc/keys/x509_evm.der" :
- "/etc/keys/pubkey_evm.pem";
-
- return verify_hash(hash, hashlen, sig + 1, len - 1, key);
+ return ima_verify_signature(file, sig, len);
}
static int cmd_verify_ima(struct command *cmd)
@@ -1354,12 +959,28 @@ static int cmd_import(struct command *cmd)
} else
ring = g_argv[optind++];
- if (!ring)
- id = KEY_SPEC_USER_KEYRING;
- else
- id = atoi(ring);
+ id = KEY_SPEC_USER_KEYRING; /* default keyring */
+
+ if (ring) {
+ if (ring[0] != '@') {
+ id = atoi(ring);
+ } else {
+ if (strcmp(ring, "@t") == 0)
+ id = -1;
+ else if (strcmp(ring, "@p") == 0)
+ id = -2;
+ else if (strcmp(ring, "@s") == 0)
+ id = -3;
+ else if (strcmp(ring, "@u") == 0)
+ id = -4;
+ else if (strcmp(ring, "@us") == 0)
+ id = -5;
+ else if (strcmp(ring, "@g") == 0)
+ id = -6;
+ }
+ }
- key = read_pub_key(inkey);
+ key = read_pub_key(inkey, x509);
if (!key)
return 1;
@@ -1395,8 +1016,8 @@ out:
static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *hash)
{
struct stat st;
- int fd, err = -1;
- uint32_t generation;
+ int err = -1;
+ uint32_t generation = 0;
HMAC_CTX ctx;
unsigned int mdlen;
char **xattrname;
@@ -1406,6 +1027,8 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
unsigned char evmkey[MAX_KEY_SIZE];
char list[1024];
ssize_t list_size;
+ struct h_misc_64 hmac_misc;
+ int hmac_size;
key = file2bin(keyfile, NULL, &keylen);
if (!key) {
@@ -1422,24 +1045,26 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
memcpy(evmkey, key, keylen);
memset(evmkey + keylen, 0, sizeof(evmkey) - keylen);
- fd = open(file, 0);
- if (fd < 0) {
- log_err("Unable to open %s\n", file);
- goto out;
- }
-
- if (fstat(fd, &st)) {
- log_err("fstat() failed\n");
+ if (lstat(file, &st)) {
+ log_err("lstat() failed\n");
goto out;
}
- if (ioctl(fd, EXT34_IOC_GETVERSION, &generation)) {
- log_err("ioctl() failed\n");
- goto out;
+ if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) {
+ /* we cannot at the momement to get generation of special files..
+ * kernel API does not support it */
+ int fd = open(file, 0);
+ if (fd < 0) {
+ log_err("Unable to open %s\n", file);
+ goto out;
+ }
+ if (ioctl(fd, EXT34_IOC_GETVERSION, &generation)) {
+ log_err("ioctl() failed\n");
+ goto out;
+ }
+ close(fd);
}
- close(fd);
-
log_info("generation: %u\n", generation);
list_size = llistxattr(file, list, sizeof(list));
@@ -1455,7 +1080,7 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
}
for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
- err = getxattr(file, *xattrname, xattr_value, sizeof(xattr_value));
+ err = lgetxattr(file, *xattrname, xattr_value, sizeof(xattr_value));
if (err < 0) {
log_info("no xattr: %s\n", *xattrname);
continue;
@@ -1475,13 +1100,37 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
}
memset(&hmac_misc, 0, sizeof(hmac_misc));
- hmac_misc.ino = st.st_ino;
- hmac_misc.generation = generation;
- hmac_misc.uid = st.st_uid;
- hmac_misc.gid = st.st_gid;
- hmac_misc.mode = st.st_mode;
- err = !HMAC_Update(&ctx, (const unsigned char *)&hmac_misc, sizeof(hmac_misc));
+ if (msize == 0) {
+ struct h_misc *hmac = (struct h_misc *)&hmac_misc;
+ hmac_size = sizeof(*hmac);
+ hmac->ino = st.st_ino;
+ hmac->generation = generation;
+ hmac->uid = st.st_uid;
+ hmac->gid = st.st_gid;
+ hmac->mode = st.st_mode;
+ } else if (msize == 64) {
+ struct h_misc_64 *hmac = (struct h_misc_64 *)&hmac_misc;
+ hmac_size = sizeof(*hmac);
+ hmac->ino = st.st_ino;
+ hmac->generation = generation;
+ hmac->uid = st.st_uid;
+ hmac->gid = st.st_gid;
+ hmac->mode = st.st_mode;
+ } else {
+ struct h_misc_32 *hmac = (struct h_misc_32 *)&hmac_misc;
+ hmac_size = sizeof(*hmac);
+ hmac->ino = st.st_ino;
+ hmac->generation = generation;
+ hmac->uid = st.st_uid;
+ hmac->gid = st.st_gid;
+ hmac->mode = st.st_mode;
+ }
+
+ log_debug("hmac_misc (%d): ", hmac_size);
+ log_debug_dump(&hmac_misc, hmac_size);
+
+ err = !HMAC_Update(&ctx, (const unsigned char *)&hmac_misc, hmac_size);
if (err) {
log_err("HMAC_Update() failed\n");
goto out_ctx_cleanup;
@@ -1511,7 +1160,7 @@ static int hmac_evm(const char *file, const char *key)
memcpy(sig + 1, hash, len);
if (xattr) {
- err = setxattr(file, "security.evm", sig, len + 1, 0);
+ err = lsetxattr(file, "security.evm", sig, len + 1, 0);
if (err < 0) {
log_err("setxattr failed: %s\n", file);
return err;
@@ -1532,7 +1181,7 @@ static int cmd_hmac_evm(struct command *cmd)
return -1;
}
- key = keyfile ? : "/etc/keys/privkey_evm.pem";
+ key = params.keyfile ? : "/etc/keys/privkey_evm.pem";
if (digsig) {
err = sign_ima(file, key);
@@ -1549,6 +1198,379 @@ static int cmd_hmac_evm(struct command *cmd)
return hmac_evm(file, "/etc/keys/evm-key-plain");
}
+static int ima_fix(const char *path)
+{
+ int fd, size, len, ima = 0, evm = 0;
+ char buf[1024], *list = buf;
+
+ log_info("%s\n", path);
+
+ if (xattr) {
+ /* re-measuring takes a time
+ * in some cases we can skip labeling if xattrs exists
+ */
+ size = llistxattr(path, list, sizeof(buf));
+ if (size < 0) {
+ log_errno("llistxattr() failed: %s\n", path);
+ return -1;
+ }
+ for (; size > 0; len++, size -= len, list += len) {
+ len = strlen(list);
+ if (!strcmp(list, "security.ima"))
+ ima = 1;
+ else if (!strcmp(list, "security.evm"))
+ evm = 1;
+ }
+ if (ima && evm)
+ return 0;
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ log_errno("%s open failed", path);
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+static int find(const char *path, int dts, find_cb_t func)
+{
+ struct dirent *de;
+ DIR *dir;
+
+ if (fs_dev) {
+ struct stat st;
+ int err = lstat(path, &st);
+ if (err < 0) {
+ log_err("stat() failed\n");
+ return err;
+ }
+ if (st.st_dev != fs_dev)
+ return 0;
+ }
+
+ dir = opendir(path);
+ if (!dir) {
+ log_err("Unable to open %s\n", path);
+ return -1;
+ }
+
+ if (fchdir(dirfd(dir))) {
+ log_err("Unable to chdir %s\n", path);
+ return -1;
+ }
+
+ while ((de = readdir(dir))) {
+ if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, "."))
+ continue;
+ log_debug("path: %s, type: %u\n", de->d_name, de->d_type);
+ if (de->d_type == DT_DIR)
+ find(de->d_name, dts, func);
+ else if (dts & (1 << de->d_type))
+ func(de->d_name);
+ }
+
+ if (chdir("..")) {
+ log_err("Unable to chdir %s\n", path);
+ return -1;
+ }
+
+ if (dts & DIR_MASK)
+ func(path);
+
+ closedir(dir);
+
+ return 0;
+}
+
+static int cmd_ima_fix(struct command *cmd)
+{
+ char *path = g_argv[optind++];
+ int err, dts = REG_MASK; /* only regular files by default */
+ struct stat st;
+
+ if (!path) {
+ log_err("Parameters missing\n");
+ print_usage(cmd);
+ return -1;
+ }
+
+ xattr = 0; /* do not check xattrs, fix everything */
+
+ if (search_type) {
+ int i;
+
+ dts = 0;
+ for (i = 0; search_type[i]; i++) {
+ switch (search_type[i]) {
+ case 'f':
+ dts |= REG_MASK; break;
+ case 'd':
+ dts |= DIR_MASK; break;
+ case 's':
+ dts |= BLK_MASK | CHR_MASK | LNK_MASK; break;
+ case 'x':
+ /* check xattrs */
+ xattr = 1; break;
+ case 'm':
+ /* stay within the same filesystem*/
+ err = lstat(path, &st);
+ if (err < 0) {
+ log_err("stat() failed\n");
+ return err;
+ }
+ fs_dev = st.st_dev; /* filesystem to start from */
+ break;
+ }
+ }
+ }
+
+ err = find(path, dts, ima_fix);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+
+static char *pcrs = "/sys/class/misc/tpm0/device/pcrs";
+
+static int tpm_pcr_read(int idx, uint8_t *pcr, int len)
+{
+ FILE *fp;
+ char *p, pcr_str[7], buf[70]; /* length of the TPM string */
+
+ sprintf(pcr_str, "PCR-%d", idx);
+
+ fp = fopen(pcrs, "r");
+ if (!fp) {
+ log_err("Unable to open %s\n", pcrs);
+ return -1;
+ }
+
+ for (;;) {
+ p = fgets(buf, sizeof(buf), fp);
+ if (!p)
+ break;
+ if (!strncmp(p, pcr_str, 6)) {
+ hex2bin(pcr, p + 7, len);
+ return 0;
+ }
+ }
+ fclose(fp);
+ return -1;
+}
+
+#define TCG_EVENT_NAME_LEN_MAX 255
+
+struct template_entry {
+ struct {
+ uint32_t pcr;
+ uint8_t digest[SHA_DIGEST_LENGTH];
+ uint32_t name_len;
+ } header __packed;
+ char name[TCG_EVENT_NAME_LEN_MAX + 1];
+ int template_len;
+ uint8_t *template;
+ int template_buf_len;
+};
+
+static uint8_t zero[SHA_DIGEST_LENGTH];
+static uint8_t fox[SHA_DIGEST_LENGTH];
+
+int validate = 1;
+
+void ima_extend_pcr(uint8_t *pcr, uint8_t *digest, int length)
+{
+ SHA_CTX ctx;
+
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, pcr, length);
+ if (validate && !memcmp(digest, zero, length))
+ SHA1_Update(&ctx, fox, length);
+ else
+ SHA1_Update(&ctx, digest, length);
+ SHA1_Final(pcr, &ctx);
+}
+
+static int ima_verify_tamplate_hash(struct template_entry *entry)
+{
+ uint8_t digest[SHA_DIGEST_LENGTH];
+
+ if (!memcmp(zero, entry->header.digest, sizeof(zero)))
+ return 0;
+
+ SHA1(entry->template, entry->template_len, digest);
+
+ if (memcmp(digest, entry->header.digest, sizeof(digest))) {
+ log_err("template hash error\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+void ima_show(struct template_entry *entry)
+{
+ log_debug("ima, digest: ");
+ log_debug_dump(entry->header.digest, sizeof(entry->header.digest));
+}
+
+void ima_ng_show(struct template_entry *entry)
+{
+ uint8_t *fieldp = entry->template;
+ uint32_t field_len;
+ int total_len = entry->template_len, digest_len, len, sig_len;
+ uint8_t *digest, *sig = NULL;
+ char *algo, *path;
+
+ /* get binary digest */
+ field_len = *(uint8_t *)fieldp;
+ fieldp += sizeof(field_len);
+ total_len -= sizeof(field_len);
+
+ algo = (char *)fieldp;
+ len = strlen(algo) + 1;
+ digest_len = field_len - len;
+ digest = fieldp + len;
+
+ /* move to next field */
+ fieldp += field_len;
+ total_len -= field_len;
+
+ /* get path */
+ field_len = *(uint8_t *)fieldp;
+ fieldp += sizeof(field_len);
+ total_len -= sizeof(field_len);
+
+ path = (char *)fieldp;
+
+ /* move to next field */
+ fieldp += field_len;
+ total_len -= field_len;
+
+ if (!strcmp(entry->name, "ima-sig")) {
+ /* get signature */
+ field_len = *(uint8_t *)fieldp;
+ fieldp += sizeof(field_len);
+ total_len -= sizeof(field_len);
+
+ if (field_len) {
+ sig = fieldp;
+ sig_len = field_len;
+
+ /* move to next field */
+ fieldp += field_len;
+ total_len -= field_len;
+ }
+ }
+
+ /* ascii_runtime_measurements */
+ log_info("%d ", entry->header.pcr);
+ log_dump_n(entry->header.digest, sizeof(entry->header.digest));
+ log_info(" %s %s", entry->name, algo);
+ log_dump_n(digest, digest_len);
+ log_info(" %s", path);
+
+ if (sig) {
+ log_info(" ");
+ log_dump(sig, sig_len);
+ ima_verify_signature(path, sig, sig_len);
+ } else
+ log_info("\n");
+
+ if (total_len)
+ log_err("Remain unprocessed data: %d\n", total_len);
+}
+
+static int ima_measurement(const char *file)
+{
+ uint8_t pcr[SHA_DIGEST_LENGTH] = {0,};
+ uint8_t pcr10[SHA_DIGEST_LENGTH];
+ struct template_entry entry = { .template = 0 };
+ FILE *fp;
+ int err;
+
+ memset(fox, 0xff, SHA_DIGEST_LENGTH);
+
+ log_debug("Initial PCR value: ");
+ log_debug_dump(pcr, sizeof(pcr));
+
+ fp = fopen(file, "rb");
+ if (!fp) {
+ log_err("Unable to open measurement file\n");
+ return -1;
+ }
+
+ while ((err = fread(&entry.header, sizeof(entry.header), 1, fp))) {
+ ima_extend_pcr(pcr, entry.header.digest, SHA_DIGEST_LENGTH);
+
+ if (!fread(entry.name, entry.header.name_len, 1, fp)) {
+ log_err("Unable to read template name\n");
+ return -1;
+ }
+
+ entry.name[entry.header.name_len] = '\0';
+
+ if (!fread(&entry.template_len, sizeof(entry.template_len), 1, fp)) {
+ log_err("Unable to read template length\n");
+ return -1;
+ }
+
+ if (entry.template_buf_len < entry.template_len) {
+ free(entry.template);
+ entry.template_buf_len = entry.template_len;
+ entry.template = malloc(entry.template_len);
+ }
+
+ if (!fread(entry.template, entry.template_len, 1, fp)) {
+ log_err("Unable to read template\n");
+ return -1;
+ }
+
+ if (validate)
+ ima_verify_tamplate_hash(&entry);
+
+ if (!strcmp(entry.name, "ima"))
+ ima_show(&entry);
+ else
+ ima_ng_show(&entry);
+ }
+
+ fclose(fp);
+
+ tpm_pcr_read(10, pcr10, sizeof(pcr10));
+
+ log_info("PCRAgg: ");
+ log_dump(pcr, sizeof(pcr));
+
+ log_info("PCR-10: ");
+ log_dump(pcr10, sizeof(pcr10));
+
+ if (memcmp(pcr, pcr10, sizeof(pcr))) {
+ log_err("PCRAgg does not match PCR-10\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int cmd_ima_measurement(struct command *cmd)
+{
+ char *file = g_argv[optind++];
+
+ if (!file) {
+ log_err("Parameters missing\n");
+ print_usage(cmd);
+ return -1;
+ }
+
+ return ima_measurement(file);
+}
+
static void print_usage(struct command *cmd)
{
printf("usage: %s %s\n", cmd->name, cmd->arg ? cmd->arg : "");
@@ -1623,12 +1645,17 @@ static void usage(void)
" -s, --imasig also make IMA signature\n"
" -d, --imahash also make IMA hash\n"
" -f, --sigfile store IMA signature in .sig file instead of xattr\n"
- " -m, --modsig store module signature in .sig file instead of xattr\n"
- " -x, --x509 signing key is in x509 DER format (signing v2 for using asymmetric keys)\n"
+ " -1, --rsa signing key is in RSA DER format (signing v1)\n"
" -k, --key path to signing key (default keys are /etc/keys/{privkey,pubkey}_evm.pem)\n"
" -p, --pass password for encrypted signing key\n"
" -u, --uuid use file system UUID in HMAC calculation (EVM v2)\n"
+ " -t, --type file types to fix 'fdsxm' (f - file, d - directory, s - block/char/symlink)\n"
+ " x - skip fixing if both ima and evm xattrs exist (caution: they may be wrong)\n"
+ " m - stay on the same filesystem (like 'find -xdev')\n"
" -n print result to stdout instead of setting xattr\n"
+ " -r, --recursive recurse into directories (sign)\n"
+ " --m32 force signature for 32 bit target system\n"
+ " --m64 force signature for 32 bit target system\n"
" -v increase verbosity level\n"
" -h, --help display this help and exit\n"
"\n");
@@ -1636,12 +1663,15 @@ static void usage(void)
struct command cmds[] = {
{"help", cmd_help, 0, "<command>"},
- {"import", cmd_import, 0, "[--x509] pubkey keyring", "Import public key into the keyring.\n"},
- {"sign", cmd_sign_evm, 0, "[--imahash | --imasig ] [--key key] [--pass password] file", "Sign file metadata.\n"},
+ {"import", cmd_import, 0, "[--rsa] pubkey keyring", "Import public key into the keyring.\n"},
+ {"sign", cmd_sign_evm, 0, "[-r] [--imahash | --imasig ] [--key key] [--pass password] file", "Sign file metadata.\n"},
{"verify", cmd_verify_evm, 0, "file", "Verify EVM signature (for debugging).\n"},
- {"ima_sign", cmd_sign_ima, 0, "[--sigfile | --modsig] [--key key] [--pass password] file", "Make file content signature.\n"},
+ {"ima_sign", cmd_sign_ima, 0, "[--sigfile] [--key key] [--pass password] file", "Make file content signature.\n"},
{"ima_verify", cmd_verify_ima, 0, "file", "Verify IMA signature (for debugging).\n"},
{"ima_hash", cmd_hash_ima, 0, "file", "Make file content hash.\n"},
+ {"ima_measurement", cmd_ima_measurement, 0, "file", "Verify measurement list (experimental).\n"},
+ {"ima_fix", cmd_ima_fix, 0, "[-t fdsxm] path", "Recursively fix IMA/EVM xattrs in fix mode.\n"},
+ {"sign_hash", cmd_sign_hash, 0, "[--key key] [--pass password]", "Sign hashes from shaXsum output.\n"},
#ifdef DEBUG
{"hmac", cmd_hmac_evm, 0, "[--imahash | --imasig ] file", "Sign file metadata with HMAC using symmetric key (for testing purpose).\n"},
#endif
@@ -1655,10 +1685,13 @@ static struct option opts[] = {
{"hashalgo", 1, 0, 'a'},
{"pass", 1, 0, 'p'},
{"sigfile", 0, 0, 'f'},
- {"modsig", 0, 0, 'm'},
{"uuid", 2, 0, 'u'},
- {"x509", 0, 0, 'x'},
+ {"rsa", 0, 0, '1'},
{"key", 1, 0, 'k'},
+ {"type", 1, 0, 't'},
+ {"recursive", 0, 0, 'r'},
+ {"m32", 0, 0, '3'},
+ {"m64", 0, 0, '6'},
{}
};
@@ -1670,11 +1703,8 @@ int main(int argc, char *argv[])
g_argv = argv;
g_argc = argc;
- sign_hash = sign_hash_v1;
- verify_hash = verify_hash_v1;
-
while (1) {
- c = getopt_long(argc, argv, "hvnsda:p:fu::xk:", opts, &lind);
+ c = getopt_long(argc, argv, "hvnsda:p:fu::k:t:r", opts, &lind);
if (c == -1)
break;
@@ -1684,7 +1714,7 @@ int main(int argc, char *argv[])
exit(0);
break;
case 'v':
- verbose++;
+ params.verbose++;
break;
case 'd':
digest = 1;
@@ -1698,8 +1728,7 @@ int main(int argc, char *argv[])
sigdump = 1;
break;
case 'a':
- hash_algo = optarg;
- user_hash_algo = 1;
+ params.hash_algo = optarg;
break;
case 'p':
keypass = optarg;
@@ -1708,21 +1737,26 @@ int main(int argc, char *argv[])
sigfile = 1;
xattr = 0;
break;
- case 'm':
- modsig = 1;
- xattr = 0;
- break;
case 'u':
- uuid_str = optarg ?: "-";
+ uuid_str = optarg ?: "+";
break;
- case 'x':
- x509 = 1;
- sign_hash = sign_hash_v2;
- verify_hash = verify_hash_v2;
- user_sig_type = 1;
+ case '1':
+ x509 = 0;
break;
case 'k':
- keyfile = optarg;
+ params.keyfile = optarg;
+ break;
+ case 't':
+ search_type = optarg;
+ break;
+ case 'r':
+ recursive = 1;
+ break;
+ case '3':
+ msize = 32;
+ break;
+ case '6':
+ msize = 64;
break;
case '?':
exit(1);
@@ -1732,6 +1766,11 @@ int main(int argc, char *argv[])
}
}
+ if (x509)
+ sign_hash = sign_hash_v2;
+ else
+ sign_hash = sign_hash_v1;
+
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
diff --git a/src/imaevm.h b/src/imaevm.h
new file mode 100644
index 0000000..7dc2651
--- /dev/null
+++ b/src/imaevm.h
@@ -0,0 +1,195 @@
+/*
+ * 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 <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 EXT2_IOC_GETVERSION _IOR('v', 1, long)
+#define EXT34_IOC_GETVERSION _IOR('f', 3, long)
+
+#define FS_IOC_GETFLAGS _IOR('f', 1, long)
+#define FS_IOC_SETFLAGS _IOW('f', 2, long)
+#define FS_IOC32_GETFLAGS _IOR('f', 1, int)
+#define FS_IOC32_SETFLAGS _IOW('f', 2, int)
+
+#define __packed __attribute__((packed))
+
+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;
+};
+
+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;
+ const char *hash_algo;
+ char *keyfile;
+};
+
+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);
+
+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
diff --git a/src/libimaevm.c b/src/libimaevm.c
new file mode 100644
index 0000000..40b5229
--- /dev/null
+++ b/src/libimaevm.c
@@ -0,0 +1,535 @@
+/*
+ * 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 <unistd.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/x509.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,
+ .hash_algo = "sha1",
+};
+
+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 int get_fdsize(int fd)
+{
+ struct stat stats;
+ /* Need to know the file length */
+ fstat(fd, &stats);
+ return (int)stats.st_size;
+}
+
+static int add_file_hash(const char *file, EVP_MD_CTX *ctx)
+{
+ uint8_t *data;
+ int err, size, bs = DATA_SIZE;
+ size_t len;
+ FILE *fp;
+
+ data = malloc(bs);
+ if (!data) {
+ log_err("malloc failed\n");
+ return -1;
+ }
+
+ fp = fopen(file, "r");
+ if (!fp) {
+ log_err("Unable to open %s\n", file);
+ return -1;
+ }
+
+ for (size = get_fdsize(fileno(fp)); size; size -= len) {
+ len = MIN(size, bs);
+ err = fread(data, len, 1, fp);
+ if (!err) {
+ if (ferror(fp)) {
+ log_err("fread() error\n\n");
+ return -1;
+ }
+ break;
+ }
+ err = EVP_DigestUpdate(ctx, data, len);
+ if (!err) {
+ log_err("EVP_DigestUpdate() failed\n");
+ return 1;
+ }
+ }
+
+ fclose(fp);
+ free(data);
+
+ return 0;
+}
+
+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;
+
+ dir = opendir(file);
+ if (!dir) {
+ log_err("Unable 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");
+ return 1;
+ }
+ }
+
+ closedir(dir);
+
+ return 0;
+}
+
+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)
+{
+ struct stat st;
+ EVP_MD_CTX ctx;
+ const EVP_MD *md;
+ unsigned int mdlen;
+ int err;
+
+ /* Need to know the file length */
+ err = lstat(file, &st);
+ if (err < 0) {
+ log_err("stat() failed\n");
+ 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("Unable 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: ");
+ 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)
+{
+ 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);
+}