1
0
mirror of https://git.code.sf.net/p/linux-ima/ima-evm-utils synced 2025-07-03 22:23:16 +02:00

57 Commits

Author SHA1 Message Date
3eab1f93b6 ima-evm-utils: Release version 1.2.1
This release contains a few bug fixes:
autoconf, keys for v1 signature verification, return code error, and
openssl version.

Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-30 15:19:00 -04:00
90176e835a ima-evm-utils: Do not load keys from x509 certs if user pass --rsa
If user wants to verify v1 signature and specify RSA public key in `-k'
option, this key will be attempted to be loaded as x509 certificate and
this process will output errors.

Do not load a key as a x509 cert if user pass `--rsa'.

This is not perfect solution. As now it's possible to specify `-k' and
`--rsa' and v2 signatures will not verify, because of no keys.

This improvement is not added into ima_measurement().

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-30 13:32:28 -04:00
2b491be5e2 ima-evm-utils: Fix ima_verify return value on multiple files
If any tested file results in failure produce failure exit code.
Previously exit code affected only by the last file tested.

Fixes: "Allow multiple files in ima_verify"
Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-30 07:33:56 -04:00
bd8b503206 ima-evm-utils: limit OPENSSL_init_crypto calls
OPENSSL_init_crypto() was introduced in version 1.1.  When using an
older version of openssl, don't call OPENSSL_init_crypto.  Partially
revert commit 782224f33c ("ima-evm-utils: Rework openssl init").

Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-30 07:15:18 -04:00
d8e7d63feb ima-evm-utils: Fix ima_verify for v1 signatures
Use user supplied key in verify_hash for DIGSIG_VERSION_1.
Otherwise v1 signatures don't pass verification.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-30 07:15:10 -04:00
e1bd9c9887 ima-evm-utils: fix trailing chars from configure script
Two chars were left in a AC_DEFINE() in configure.ac, leading to an error
message during ./configure call:

checking for tsspcrread... yes
./configure: line 9894: ],: command not found

Signed-off-by: Bruno E. O. Meneguele <bmeneg@redhat.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-30 07:14:18 -04:00
515c99856e ima-evm-utils: Release version 1.2
Updated both the release and library (ABI change) versions.  See the
"Changelog" for a short list of the new features, bug fixes, and code
cleanup.

Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-26 07:42:17 -04:00
28cbfa1769 ima-evm-utils: functions/arrays define as static
Make sign_hash_v1(), sign_hash_v2(), get_hash_algo_by_id, and
 pkey_hash_algo[] and pkey_hash_algo_kern[] static.

Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-25 12:14:21 -04:00
c317d4618f ima-evm-utils: Namespace some too generic object names
Prefix `dump', `do_dump', and `params' with `imaevm_' to avoid colliding
with other global symbols.
Also, rename `libevm_' to `libimaevm_`, only used with `params'.
Additionally, rename `dump' into `hexdump'.
Finally, rename `get_hash_algo' to `imaevm_get_hash_algo' as suggested by
Mimi Zohar.

Lines that became too long are split, indent corrected. No code changes.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Reviewed-by: Bruno E. O. Meneguele <bmeneg@redhat.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-25 12:14:21 -04:00
a1b149bda4 ima-evm-utils: Show used hash algo in verbose mode
This could be useful for users.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-25 12:14:10 -04:00
25fce6e76a ima-evm-utils: Do not allow fallback and unknown hash algos
Falling back and permissiveness could have security implications.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-25 12:14:01 -04:00
31ceff7eb6 ima-evm-utils: use tsspcrread to read the TPM 2.0 PCRs
The kernel does not expose the crypto agile TPM 2.0 PCR banks to
userspace like it exposes PCRs for TPM 1.2.  As a result, a userspace
application is required to read PCRs.

This patch adds tsspcrread support for reading the TPM 2.0 PCRs.
tsspcrread is one application included in the ibmtss package.

Sample error messages:
Failed to read PCRs: (tsspcrread failed: No such file or directory)
Failed to read PCRs: (TSS_Dev_Open: Error opening /dev/tpmrm0)

Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
Reviewed-by: Petr Vorel <pvorel@suse.cz>
Reviewed-by: Bruno E. O. Meneguele <bmeneg@redhat.com>
2019-07-24 06:09:17 -04:00
340f7eb7dd ima-evm-utils: Remove ERR_load_crypto_strings from read_priv_pkey
ERR_load_crypto_strings() is already called in other place.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-23 20:27:41 -04:00
3eac3710a9 ima-evm-utils: log unknown keyid's as errors
Each tima a new unknown key is encountered, emit a message of the format
"key #: <keyid> (unknown keyid)".  The individual files using unknown
keys are then only logged in verbose mode.  Also update the message
emitted to be consistent with other "verification failed" messages.

Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>

Changlog:
- Incorporated Vitaly's fix to prevent a null dereference in `tail->next`
2019-07-23 20:24:58 -04:00
15afdbeae7 ima-evm-utils: Improve OpenSSL error reporting
Previously OpenSSL errors was delayed until evmctl exit (sometimes not).
Since we try to make libimaevm more robust, there could be many errors
accumulated, so it's useful to output OpenSSL errors as they happen.
This will also make output more understandable as you can see which
openssl error correspond to which libimaevm error.

Additionally, change spelling of read_pub_pkey and read_priv_pkey to
include key file name.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-18 21:09:32 -04:00
42d1636f52 ima-evm-utils: Remove not needed argument from verify_hash_v2
Since we now always call verify_hash_v2() with NULL keyfile (assuming
all keys are already loaded into public_keys list), remove keyfile
argument and its handling from verify_hash_v2().

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
[zohar@linux.ibm.com: make verify_hash_v1() and verify_hash_v2() static.]
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-18 21:09:32 -04:00
3359563dbe ima-evm-utils: Remove indirect call to subfunctions in verify_hash
This is more human understandable and also will improve handling of
the sources by cscope.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-18 21:09:32 -04:00
388f807a0f ima_evm_utils: erroneous "verification failed: 0 (invalid padding)" message
When public keys are specified on the boot command line (--key "<public
key file>,[<public key file>,...]"), the appropriate public key is used
to verify EVM or file signatures.  If no keys are specified, the default
x509_evm.der or pubkey_evm.pem file is used to verify the DIGSIG_VERSION_2
or DIGSIG_VERSION_1 signatures respectively, without first checking the
keyids.  Instead of emitting a "verification failed: 0 (invalid
padding)" message, an "unknown keyid" message would be clearer.

To address this problem, when no public keys are specified, this patch
loads the x509_evm.der default public key onto the "public_keys" list,
while the pubkey_evm.pem continues to be passed to verify_hash_v1()

Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-18 21:07:48 -04:00
a225728550 ima-evm-utils: Add more error checking in add_file_hash
Check return value of fstat(2) in add_file_hash() and remove
now unused get_fdsize().

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-16 14:14:10 -04:00
08a51e7460 ima-evm-utils: Fix file2bin stat and fopen relations
Check stat(2) return value, use fstat(2) to avoid race between
stat() and fopen(), remove now unused get_filesize().

Fixes: CID 229889.
Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-16 14:14:03 -04:00
9d52489bd3 ima-evm-utils: Fix memory leak in get_password
Free allocated password buffer when returning NULL.

Fixes: CID 229894 (partially).
Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-16 14:13:50 -04:00
4b7a74cc41 ima-evm-utils: Fix possible xattr_value overflows in calc_evm_hash
`selinux_str',`caps_str', and `ima_str' are passed from the command line
but copied into the fixed-size buffer.

Yes, length of `selinux_str' is calculated differently than of `caps_str'.

Fixes: CID 229895.
Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-16 14:13:39 -04:00
d47951c6e1 ima-evm-utils: Fix null dereference from file2bin to memcpy
file2bin() may return NULL, which is set to tmp, which is passed to
memcpy. Add explicit check for it.

Fixes: CID 229904.
Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-16 14:12:47 -04:00
164c51ff2b ima-evm-utils: support template "buf" field
Other than the "boot-aggregate" measurement entry in the IMA
measuremeent list, all other measurements are of file data.  Kernel
support was recently added to support measuring the kexec boot command
line buffer, which is stored in a new template field named 'buf'.

This patch adds support for a new template named "ima-buf", defined as
"d-ng|n-ng|buf".

Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 10:00:13 -04:00
4ec7c1d028 ima-evm-utils: limit "remain unprocessed data" messages
New, unknown template formats containing unknown fields are not
processed, resulting in "remain unprocessed data" messages.  Processing
these unknown fields is unnecessary for walking the measurement list to
re-calculate the PCRs.

The "remain unproccessed data" may also be emitted for malformed, known
template records.

This patch limits the number of messages emitted to once per template
format and includes the template name in the message.

Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 10:00:13 -04:00
40c842ace3 ima-evm-utils: Fix clang warning about possible unaligned pointer for hdr->keyid
Struct signature_v2_hdr is packed so clang complains that taking address
of packed member may result in an unaligned pointer value:

libimaevm.c:481:21: warning: taking address of packed member 'keyid' of class or structure 'signature_v2_hdr' may result in an unaligned pointer value
      [-Waddress-of-packed-member]
                                __be32_to_cpup(&hdr->keyid));
                                                ^~~~~~~~~~

libimaevm.c:905:17: warning: taking address of packed member 'keyid' of class or structure 'signature_v2_hdr' may result in an unaligned pointer value
      [-Waddress-of-packed-member]
        calc_keyid_v2(&hdr->keyid, name, pkey);
                       ^~~~~~~~~~

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 10:00:13 -04:00
ae57a3e1e4 ima-evm-utils: Allow multiple files in ima_verify
This allows testing multiple verify in a row, similar to ima_measurement.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 10:00:13 -04:00
556f2fc66d ima-evm-utils: Preload public keys for ima_verify
This allows testing verify_hash_v2() with multiple public keys.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 10:00:13 -04:00
bb35e696df ima-evm-utils: Fix memory leak in init_public_keys
strdup'ed string should be freed. Found with ASan.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 10:00:13 -04:00
faea7ca2b1 ima-evm-utils: Fix EVP_MD_CTX leak in ima_calc_hash
When pctx is allocated using EVP_MD_CTX_new() it should be freed.
Found with ASan.

Fixes: 81010f0 ("ima-evm-utils: Add backward compatible support for openssl 1.1")
Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 10:00:13 -04:00
30add9c2eb ima-evm-utils: Log hash_algo with hash value in verbose mode
It's useful to know not just a hash value but also which algorithm is
used.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 10:00:13 -04:00
5f126d1d25 ima-evm-utils: Pass status codes from sign and hash functions to the callers
Move sign_hash()/ima_calc_hash()/calc_evm_hmac()/calc_evm_hash() status
checking before assert()'ing of their return values, so it can be passed
to the upper level callers. Especially useful for showing errors.

Fixes: 1d9c279279 ("Define hash and sig buffer sizes and add asserts")
Fixes: 9643544701 ("Fix hash buffer overflow in verify_evm and hmac_evm")
Signed-off-by: Vitaly Chikunov <vt@altlinux.org>

ima-evm-utils: Fix assert after ima_calc_hash
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 10:00:13 -04:00
28d3a1b293 ima-evm-utils: Remove RSA_ASN1_templates
RSA_ASN1_templates[] are not needed anymore, because we switched to the
generic EVP_PKEY OpenSSL API to generate v2 signatures instead of
constructing PKCS1 ourselves.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Acked-by: Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 10:00:06 -04:00
13d4521dbf ima-evm-utils: Replace calc_keyid_v2 with calc_pkeyid_v2
Finish conversion of calc keyid v2 to EVP_PKEY API. After sign_hash_v2()
is switched to EVP_PKEY API (in previous commit), older RSA-specific
calc_keyid_v2() does not needed anymore and can be replaced with
calc_pkeyid_v2().

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Acked-by: Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 09:59:59 -04:00
07e623b608 ima-evm-utils: Convert sign_hash_v2 to EVP_PKEY API
Convert sign_hash_v2() to use more generic EVP_PKEY API instead of RSA
API. This enables generation of more signatures out of the box, such as
EC-RDSA (GOST) and any other that OpenSSL supports. This conversion also
fixes generation of MD4 signatures, because it didn't have proper
RSA_ASN1_template.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Acked-by: Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 09:59:53 -04:00
e0d778c608 ima-evm-utils: Convert verify_hash_v2 and find_keyid to EVP_PKEY API
Rely on OpenSSL API to verify v2 signatures instead of manual PKCS1
decoding. Also, convert find_keyid() to return EVP_PKEY because
verify_hash_v2() is sole user of it.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Acked-by: Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 09:59:45 -04:00
af4e3c06bb ima-evm-utils: Convert cmd_import and calc keyid v2 to EVP_PKEY API
Introduce calc_pkeyid_v2() (which accepts EVP_PKEY) to replace
calc_keyid_v2() (which accepts RSA) in the future and use it in
cmd_import().

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Acked-by: Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 09:59:14 -04:00
3df7b5d779 ima-evm-utils: Convert read_priv_key to EVP_PKEY API
Introduce read_priv_pkey() to read keys using EVP_PKEY, and change
read_priv_key() to be wrapper for it.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Acked-by: Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 09:59:07 -04:00
71c1be47e7 ima-evm-utils: Convert read_pub_key to EVP_PKEY API
Introduce read_pub_pkey() to read keys using EVP_PKEY, and change
read_pub_key() to be wrapper for it.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Acked-by: Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-07-08 09:58:49 -04:00
2308132957 ima-evm-utils: update .gitignore files
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-05-23 10:07:27 -04:00
103b90d27c ima-evm-utils: include hash-info.gen into distribution
Include hash-info.gen into tarball and call it from the sourcedir to fix
out-of-tree build (and thus 'make distcheck').

Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-05-23 10:07:27 -04:00
8acbae598b ima-evm-utils: replace INCLUDES with AM_CPPFLAGS
Replace INCLUDES variable with AM_CPPFLAGS to stop Automake from warning
about deprecated variable usage.

Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-05-23 10:07:27 -04:00
b09a25690f ima-evm-utils: link to libcrypto instead of OpenSSL
There is no need to link to full libssl. evmctl uses functions from
libcrypto, so let's link only against that library.

Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-05-23 10:07:27 -04:00
782224f33c ima-evm-utils: Rework openssl init
Remove deprecated call to OpenSSL_add_all_algorithms().
Allow to disable openssl config loading by evmctl via configure
`--disable-openssl-conf' option. Show status of that in configure.
Move config loading from libimaevm to evmctl.
Finish engine initialization properly by calling ENGINE_set_default(),
as suggested by James Bottomley.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-05-21 10:41:29 -04:00
ebbfc41ad6 ima-evm-utils: try to load digest by its alias
Primary names of the algorithms are different for OpenSSL and Kernel.
"Streebog" is a name of the hash algorithm in the Kernel Crypto API.
"md_gost12_X" is the name used by most versions of OpenSSL, it's
placed in pkey_hash_algo[] so that algo IDs are resolved to them.
Allow to use both names.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
[zohar@linux.ibm.com: updated patch description based input from Vitaly]
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-05-21 10:41:02 -04:00
b628d8bfdb ima-evm-utils: Extract digest algorithms from hash_info.h
If configured with "--with-kernel-headers=PATH" try to extract hash
algorithms from "hash_info.h" from the kernel source tree or
kernel-headers package located in the specified path. (Otherwise, it
will be tried to get from the installed kernel.)

This also introduces two algorithm lists, one is built-in and another is
from the kernel source. (They should never contain conflicting algorithm
IDs by their append-only nature.) If the digest is not found in the
built-in list it will be searched in the list from kernel's
"hash_info.h".

This patch will allow evmctl to be just recompiled to work with digest
algorithms introduced in the newer kernels.

Suggested-by: Mimi Zohar <zohar@linux.ibm.com>
Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2019-04-03 16:47:37 -04:00
07d799cb6c ima-evm-utils: Preload OpenSSL engine via '--engine' option
Another method of using GOST algorithms (and cryptographic accelerators)
is via direct preloading of appropriate engine using '--engine' option.
For the gost-engine it should be '--engine gost'.

Usage example:

1. Install gost-engine appropriately. (No need to edit openssl.cnf).

2. Then GOST algorithms should work:

  # cp /dev/null a
  # evmctl -v ima_hash --engine gost -a streebog256 a
  hash: 04123f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2018-12-03 06:08:36 -05:00
7e2a7840a7 ima-evm-utils: Allow using Streebog hash function
This patch will allow using GOST algorithms from OpenSSL's
gost-engine[1] via config extension (which is the usual way).

[1] https://github.com/gost-engine/engine

Full usage example:

1. Install the gost-engine package for your distro, this could be
libengine-gost-openssl1.1, openssl-gost-engine, or openssl-engines.

2. Edit openssl.cnf appropriately. Reference INSTALL.md of gost-engine
for the detailed instructions.

3. Then GOST algorithms should work:

  $ cp /dev/null a
  $ openssl dgst -streebog256 a
  md_gost12_256(a)= 3f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb
  $ evmctl -v ima_hash -a streebog256 --xattr-user a
  hash: 04123f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb
  $ getfattr -d -m. -ehex a
  # file: a
  user.ima=0x04123f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2018-12-03 06:08:36 -05:00
b853b7ba93 ima-evm-utils: Define the '--xattr-user' option for testing
The IMA/EVM attributes are currently stored in the "security" namespace,
which requires root privileges. Storing the ima/evm attributes in the
"user" namespace, instead of the "security" namespace, would be useful
for debugging and testing purposes, and because "--sigfile" does not
work for evm signatures.

Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2018-12-03 06:08:36 -05:00
1d9c279279 ima-evm-utils: Define hash and sig buffer sizes and add asserts
To prevent hash and sig buffers size mismatch, define their maximum
sizes and add sanity checking asserts.

Suggested-by: Mimi Zohar <zohar@linux.ibm.com>
Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2018-12-03 06:08:36 -05:00
9643544701 ima-evm-utils: Fix hash buffer overflow in verify_evm and hmac_evm
Commit ae1319eeab ("Remove hardcoding of SHA1 in EVM signatures")
introduces overflow of 20 byte buffer on the stack while calculating
hash. Also, invalid hash length is passed to the underlying verification
function in verify_evm. This prevents any non-SHA1 hashes from being
properly validated using evmctl.

Fixes: ae1319eeab ("Remove hardcoding of SHA1 in EVM signatures")
Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2018-12-03 06:08:36 -05:00
15410695c7 ima-evm-utils: libimaevm: get key description out of verbose condition
Key description in keyring is being filled with memory garbage during
import process if the LOG_LEVEL is not satisfied (using '-vv').

Testing in kernels without trusted keyring support, and importing a v1
(RSA) key pair, the kernel fails to find the key since it looks for the
key description, which is not found due to this issue:

    "digsig: key not found, id: DD0558FEB7DDBD26"

Looking at:
    # keyctl show
    Session Keyring
     635748007 --alswrv      0     0  keyring: _ses
     673181018 --alswrv      0 65534   \_ keyring: _uid.0
     360651479 --alswrv      0     0       \_ keyring: _ima
     499360916 --alswrv      0     0       |   \_ user: .N=
     266933436 --alswrv      0     0       |   \_ user: B641632DA94DEE26

Key id 499360916 and 266933436 are both the same key, but the first was added
without '-vv' in the command line, while the second one was using it.

Signed-off-by: Bruno E. O. Meneguele <bmeneg@redhat.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2018-12-02 11:16:23 -05:00
8c8f29e870 ima-evm-utils: check the return code from tpm_pcr_read() in ima_measurement()
Don't log garbage when neither /sys/class/tpm/tpm0/device/pcrs nor
/sys/class/misc/tpm0/device/pcrs can be read and exit nicely with a
value of 1.

Signed-off-by: George Wilson <gcwilson@linux.ibm.com>
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
2018-07-01 14:55:19 -04:00
9119f771a2 Add security.apparmor to the set of extended attributes used by EVM
The kernel is taking security.apparmor into account when validating EVM,
so evmctl should be doing the same.

Signed-off-by: Matthew Garrett <mjg59@google.com>
Signed-off-by: Mimi Zohar <zoahr@linux.vnet.ibm.com>

Changelog:
- Prevent compilation from failing due to XATTR_NAME_APPARMOR not being
included in the kernel-headers package.
2018-07-01 14:48:51 -04:00
ae1319eeab Remove hardcoding of SHA1 in EVM signatures
EVM signatures are always being generated with SHA1 even if the -a
argument has been provided to evmctl. Fix this so the provided hash
algorithm is used instead.

Signed-off-by: Matthew Garrett <mjg59@google.com>
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>

Changelog:
- Like for cmd_sign_hash() and sign_ima(), the hash size for sign_evm()
should now be 64.
2018-07-01 14:48:51 -04:00
6aea54d2ad evmctl: use correct include for xattr.h
The xattr API/ABI is provided by both the c-library, as well as by the
libattr package. The c-library's header file is sys/xattr.h, whereas
libattr's header file can be found in attr/xattr.h.

Given none of the code here *links* against the libattr.so shared library, it
is wrong to *compile* against libattr's API (header file).

Doing so avoids confusion as to which xattr.h is used as the least problem,
and potential ABI differences as the worst problem due the mismatching header
file used.

So make sure we compile and link against the same thing, the c-library in
both cases.

Signed-off-by: André Draszik <git@andred.net>
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
2018-07-01 14:47:17 -04:00
5fa7d35de5 autotools: Try to find correct manpage stylesheet path
xslt docbook styles differ across distributions, which requires some
workarounds [1]. Try to check the manpage stylesheet path with
xmlcatalog and fallback to the original one.

Add option --with-xml-catalog to use non-default catalog.

+ remove trailing whitespace

[1] https://github.com/CESNET/ipfixcol/blob/master/base/m4/lbr_check_xsltproc.m4

Signed-off-by: Petr Vorel <pvorel@suse.cz>
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
2018-05-07 07:51:32 -04:00
14 changed files with 846 additions and 362 deletions

1
.gitignore vendored
View File

@ -45,6 +45,7 @@ cscope.*
ncscope.*
# Generated documentation
*.1
*.8
*.5
manpage.links

View File

@ -1,3 +1,38 @@
2019-07-24 Mimi Zohar <zohar@linux.ibm.com>
version 1.2 new features:
* Generate EVM signatures based on the specified hash algorithm
* include "security.apparmor" in EVM signature
* Add support for writing & verifying "user.xxxx" xattrs for testing
* Support Strebog/Gost hash functions
* Add OpenSSL engine support
* Use of EVP_PKEY OpenSSL API to generate/verify v2 signatures
* Support verifying multiple signatures at once
* Support new template "buf" field and warn about other unknown fields
* Improve OpenSSL error reporting
* Support reading TPM 2.0 PCRs using tsspcrread
Bug fixes and code cleanup:
* Update manpage stylesheet detection
* Fix xattr.h include file
* On error when reading TPM PCRs, don't log gargabe
* Properly return keyid string to calc_keyid_v1/v2 callers, caused by
limiting keyid output to verbose mode
* Fix hash buffer overflow caused by EVM support for larger hashes,
defined MAX_DIGEST_SIZE and MAX_SIGNATURE_SIZE, and added "asserts".
* Linked with libcrypto instead of OpenSSL
* Updated Autotools, replacing INCLUDES with AM_CPPFLAGS
* Include new "hash-info.gen" in tar
* Log the hash algorithm, not just the hash value
* Fixed memory leaks in: EV_MD_CTX, init_public_keys
* Fixed other warnings/bugs discovered by clang, coverity
* Remove indirect calls in verify_hash() to improve code readability
* Don't fallback to using sha1
* Namespace some too generic object names
* Make functions/arrays static if possible
2018-01-28 Mimi Zohar <zohar@us.ibm.com>
version 1.1

View File

@ -23,9 +23,6 @@ rpm: $(tarname)
cp $(tarname) $(SRCS)/
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 $@ $<

2
README
View File

@ -44,6 +44,7 @@ OPTIONS
-s, --imasig make IMA signature
-d, --imahash make IMA hash
-f, --sigfile store IMA signature in .sig file instead of xattr
--xattr-user store xattrs in user namespace (for testing purposes)
--rsa use RSA key type and signing scheme v1
-k, --key path to signing key (default: /etc/keys/{privkey,pubkey}_evm.pem)
-o, --portable generate portable EVM signatures
@ -57,6 +58,7 @@ OPTIONS
--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
--engine e preload OpenSSL engine e (such as: gost)
-v increase verbosity level
-h, --help display this help and exit

View File

@ -1,12 +1,13 @@
# autoconf script
AC_PREREQ([2.65])
AC_INIT(ima-evm-utils, 1.1, zohar@linux.vnet.ibm.com)
AC_INIT(ima-evm-utils, 1.2.1, zohar@linux.ibm.com)
AM_INIT_AUTOMAKE
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
AC_CANONICAL_HOST
AC_USE_SYSTEM_EXTENSIONS
# Checks for programs.
AC_PROG_CC
@ -24,15 +25,30 @@ LT_INIT
# Checks for header files.
AC_HEADER_STDC
PKG_CHECK_MODULES(OPENSSL, [ openssl >= 0.9.8 ])
AC_SUBST(OPENSSL_CFLAGS)
AC_SUBST(OPENSSL_LIBS)
PKG_CHECK_MODULES(LIBCRYPTO, [libcrypto >= 0.9.8 ])
AC_SUBST(KERNEL_HEADERS)
AC_CHECK_HEADER(unistd.h)
AC_CHECK_HEADERS(openssl/conf.h)
AC_CHECK_HEADERS(attr/xattr.h, , [AC_MSG_ERROR([attr/xattr.h header not found. You need the libattr development package.])])
AC_CHECK_PROG(TSSPCRREAD, [tsspcrread], yes, no)
if test "x$TSSPCRREAD" = "xyes"; then
AC_DEFINE(HAVE_TSSPCRREAD, 1, [Define to 1 if you have tsspcrread binary installed])
fi
AC_CHECK_HEADERS(sys/xattr.h, , [AC_MSG_ERROR([sys/xattr.h header not found. You need the c-library development package.])])
AC_CHECK_HEADERS(keyutils.h, , [AC_MSG_ERROR([keyutils.h header not found. You need the libkeyutils development package.])])
AC_ARG_WITH(kernel_headers, [AS_HELP_STRING([--with-kernel-headers=PATH],
[specifies the Linux kernel-headers package location or kernel root directory you want to use])],
[KERNEL_HEADERS="$withval"],
[KERNEL_HEADERS=/lib/modules/$(uname -r)/source])
AC_ARG_ENABLE([openssl_conf],
[AS_HELP_STRING([--disable-openssl-conf], [disable loading of openssl config by evmctl])],
[if test "$enable_openssl_conf" = "no"; then
AC_DEFINE(DISABLE_OPENSSL_CONF, 1, [Define to disable loading of openssl config by evmctl.])
fi], [enable_openssl_conf=yes])
#debug support - yes for a while
PKG_ARG_ENABLE(debug, "yes", DEBUG, [Enable Debug support])
if test $pkg_cv_enable_debug = yes; then
@ -41,6 +57,8 @@ else
CFLAGS="$CFLAGS -Wall -Wstrict-prototypes -pipe -fomit-frame-pointer"
fi
EVMCTL_MANPAGE_DOCBOOK_XSL
# for gcov
#CFLAGS="$CFLAGS -Wall -fprofile-arcs -ftest-coverage"
#CXXFLAGS="$CXXFLAGS -Wall -fprofile-arcs -ftest-coverage"
@ -57,6 +75,7 @@ AC_OUTPUT
echo
echo
echo "Configuration:"
echo " debug: $pkg_cv_enable_debug"
echo " debug: $pkg_cv_enable_debug"
echo " openssl-conf: $enable_openssl_conf"
echo " tsspcrread: $TSSPCRREAD"
echo

28
m4/manpage-docbook-xsl.m4 Normal file
View File

@ -0,0 +1,28 @@
dnl Copyright (c) 2018 Petr Vorel <pvorel@suse.cz>
dnl Find docbook manpage stylesheet
AC_DEFUN([EVMCTL_MANPAGE_DOCBOOK_XSL], [
AC_PATH_PROGS(XMLCATALOG, xmlcatalog)
AC_ARG_WITH([xml-catalog],
AC_HELP_STRING([--with-xml-catalog=CATALOG],
[path to xml catalog to use]),,
[with_xml_catalog=/etc/xml/catalog])
XML_CATALOG_FILE="$with_xml_catalog"
AC_SUBST([XML_CATALOG_FILE])
AC_MSG_CHECKING([for XML catalog ($XML_CATALOG_FILE)])
if test -f "$XML_CATALOG_FILE"; then
have_xmlcatalog_file=yes
AC_MSG_RESULT([found])
else
AC_MSG_RESULT([not found])
fi
if test "x${XMLCATALOG}" != "x" -a "x$have_xmlcatalog_file" = "xyes"; then
DOCBOOK_XSL_URI="http://docbook.sourceforge.net/release/xsl/current"
DOCBOOK_XSL_PATH="manpages/docbook.xsl"
MANPAGE_DOCBOOK_XSL=$(${XMLCATALOG} ${XML_CATALOG_FILE} ${DOCBOOK_XSL_URI}/${DOCBOOK_XSL_PATH} | sed -n 's|^file:/\+|/|p;q')
fi
if test "x${MANPAGE_DOCBOOK_XSL}" = "x"; then
MANPAGE_DOCBOOK_XSL="/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl"
fi
AC_SUBST(MANPAGE_DOCBOOK_XSL)
])

View File

@ -1,5 +1,5 @@
Name: ima-evm-utils
Version: 1.1
Version: 1.2.1
Release: 1%{?dist}
Summary: ima-evm-utils - IMA/EVM control utility
Group: System/Libraries
@ -11,7 +11,6 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
BuildRequires: autoconf
BuildRequires: automake
BuildRequires: openssl-devel
BuildRequires: libattr-devel
BuildRequires: keyutils-libs-devel
%description

View File

@ -11,7 +11,6 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
BuildRequires: autoconf
BuildRequires: automake
BuildRequires: openssl-devel
BuildRequires: libattr-devel
BuildRequires: keyutils-libs-devel
%description

1
src/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
hash_info.h

View File

@ -1,22 +1,28 @@
lib_LTLIBRARIES = libimaevm.la
libimaevm_la_SOURCES = libimaevm.c
libimaevm_la_CPPFLAGS = $(OPENSSL_CFLAGS)
libimaevm_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCRYPTO_CFLAGS)
# current[:revision[:age]]
# result: [current-age].age.revision
libimaevm_la_LDFLAGS = -version-info 0:0:0
libimaevm_la_LIBADD = $(OPENSSL_LIBS)
libimaevm_la_LDFLAGS = -version-info 1:0:0
libimaevm_la_LIBADD = $(LIBCRYPTO_LIBS)
include_HEADERS = imaevm.h
nodist_libimaevm_la_SOURCES = hash_info.h
BUILT_SOURCES = hash_info.h
EXTRA_DIST = hash_info.gen
hash_info.h: Makefile
$(srcdir)/hash_info.gen $(KERNEL_HEADERS) >$@
bin_PROGRAMS = evmctl
evmctl_SOURCES = evmctl.c
evmctl_CPPFLAGS = $(OPENSSL_CFLAGS)
evmctl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCRYPTO_CFLAGS)
evmctl_LDFLAGS = $(LDFLAGS_READLINE)
evmctl_LDADD = $(OPENSSL_LIBS) -lkeyutils libimaevm.la
evmctl_LDADD = $(LIBCRYPTO_LIBS) -lkeyutils libimaevm.la
INCLUDES = -I$(top_srcdir) -include config.h
AM_CPPFLAGS = -I$(top_srcdir) -include config.h
CLEANFILES = hash_info.h
DISTCLEANFILES = @DISTCLEANFILES@

View File

@ -49,18 +49,25 @@
#include <stdint.h>
#include <string.h>
#include <dirent.h>
#include <attr/xattr.h>
#include <sys/xattr.h>
#include <linux/xattr.h>
#include <getopt.h>
#include <keyutils.h>
#include <ctype.h>
#include <termios.h>
#include <assert.h>
#include <openssl/sha.h>
#include <openssl/pem.h>
#include <openssl/hmac.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/engine.h>
#ifndef XATTR_APPAARMOR_SUFFIX
#define XATTR_APPARMOR_SUFFIX "apparmor"
#define XATTR_NAME_APPARMOR XATTR_SECURITY_PREFIX XATTR_APPARMOR_SUFFIX
#endif
#define USE_FPRINTF
@ -69,6 +76,7 @@
static char *evm_default_xattrs[] = {
XATTR_NAME_SELINUX,
XATTR_NAME_SMACK,
XATTR_NAME_APPARMOR,
XATTR_NAME_IMA,
XATTR_NAME_CAPS,
NULL
@ -80,6 +88,7 @@ static char *evm_extra_smack_xattrs[] = {
XATTR_NAME_SMACKEXEC,
XATTR_NAME_SMACKTRANSMUTE,
XATTR_NAME_SMACKMMAP,
XATTR_NAME_APPARMOR,
XATTR_NAME_IMA,
XATTR_NAME_CAPS,
NULL
@ -137,6 +146,9 @@ static int find(const char *path, int dts, find_cb_t func);
struct command cmds[];
static void print_usage(struct command *cmd);
static const char *xattr_ima = "security.ima";
static const char *xattr_evm = "security.evm";
static int bin2file(const char *file, const char *ext, const unsigned char *data, int len)
{
FILE *fp;
@ -163,9 +175,10 @@ static int bin2file(const char *file, const char *ext, const unsigned char *data
static unsigned char *file2bin(const char *file, const char *ext, int *size)
{
FILE *fp;
int len;
size_t len;
unsigned char *data;
char name[strlen(file) + (ext ? strlen(ext) : 0) + 2];
struct stat stats;
if (ext)
sprintf(name, "%s.%s", file, ext);
@ -174,18 +187,33 @@ static unsigned char *file2bin(const char *file, const char *ext, int *size)
log_info("Reading to %s\n", name);
len = get_filesize(name);
fp = fopen(name, "r");
if (!fp) {
log_err("Failed to open: %s\n", name);
return NULL;
}
if (fstat(fileno(fp), &stats) == -1) {
log_err("Failed to fstat: %s (%s)\n", name, strerror(errno));
fclose(fp);
return NULL;
}
len = stats.st_size;
data = malloc(len);
if (!fread(data, len, 1, fp))
len = 0;
if (!data) {
log_err("Failed to malloc %zu bytes: %s\n", len, name);
fclose(fp);
return NULL;
}
if (fread(data, len, 1, fp) != len) {
log_err("Failed to fread %zu bytes: %s\n", len, name);
fclose(fp);
free(data);
return NULL;
}
fclose(fp);
*size = len;
*size = (int)len;
return data;
}
@ -313,6 +341,7 @@ err:
static int calc_evm_hash(const char *file, unsigned char *hash)
{
const EVP_MD *md;
struct stat st;
int err;
uint32_t generation = 0;
@ -374,7 +403,14 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
return -1;
}
err = EVP_DigestInit(pctx, EVP_sha1());
md = EVP_get_digestbyname(imaevm_params.hash_algo);
if (!md) {
log_err("EVP_get_digestbyname(%s) failed\n",
imaevm_params.hash_algo);
return 1;
}
err = EVP_DigestInit(pctx, md);
if (!err) {
log_err("EVP_DigestInit() failed\n");
return 1;
@ -382,16 +418,31 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
if (!strcmp(*xattrname, XATTR_NAME_SELINUX) && selinux_str) {
strcpy(xattr_value, selinux_str);
err = strlen(selinux_str) + 1;
if (err > sizeof(xattr_value)) {
log_err("selinux[%u] value is too long to fit into xattr[%zu]\n",
err, sizeof(xattr_value));
return -1;
}
strcpy(xattr_value, selinux_str);
} else if (!strcmp(*xattrname, XATTR_NAME_IMA) && ima_str) {
hex2bin(xattr_value, ima_str, strlen(ima_str) / 2);
err = strlen(ima_str) / 2;
if (err > sizeof(xattr_value)) {
log_err("ima[%u] value is too long to fit into xattr[%zu]\n",
err, sizeof(xattr_value));
return -1;
}
hex2bin(xattr_value, ima_str, err);
} else if (!strcmp(*xattrname, XATTR_NAME_CAPS) && (hmac_flags & HMAC_FLAG_CAPS_SET)) {
if (!caps_str)
continue;
strcpy(xattr_value, caps_str);
err = strlen(caps_str);
if (err >= sizeof(xattr_value)) {
log_err("caps[%u] value is too long to fit into xattr[%zu]\n",
err + 1, sizeof(xattr_value));
return -1;
}
strcpy(xattr_value, caps_str);
} else {
err = lgetxattr(file, *xattrname, xattr_value, sizeof(xattr_value));
if (err < 0) {
@ -490,17 +541,19 @@ static int calc_evm_hash(const char *file, unsigned char *hash)
static int sign_evm(const char *file, const char *key)
{
unsigned char hash[20];
unsigned char sig[1024];
unsigned char hash[MAX_DIGEST_SIZE];
unsigned char sig[MAX_SIGNATURE_SIZE];
int len, err;
len = calc_evm_hash(file, hash);
if (len <= 1)
return len;
assert(len <= sizeof(hash));
len = sign_hash("sha1", hash, len, key, NULL, sig + 1);
len = sign_hash(imaevm_params.hash_algo, hash, len, key, NULL, sig + 1);
if (len <= 1)
return len;
assert(len < sizeof(sig));
/* add header */
len++;
@ -512,11 +565,11 @@ static int sign_evm(const char *file, const char *key)
if (evm_immutable)
sig[1] = 3; /* immutable signature version */
if (sigdump || params.verbose >= LOG_INFO)
dump(sig, len);
if (sigdump || imaevm_params.verbose >= LOG_INFO)
imaevm_hexdump(sig, len);
if (xattr) {
err = lsetxattr(file, "security.evm", sig, len, 0);
err = lsetxattr(file, xattr_evm, sig, len, 0);
if (err < 0) {
log_err("setxattr failed: %s\n", file);
return err;
@ -528,10 +581,14 @@ static int sign_evm(const char *file, const char *key)
static int hash_ima(const char *file)
{
unsigned char hash[66]; /* MAX hash size + 2 */
unsigned char hash[MAX_DIGEST_SIZE + 2]; /* +2 byte xattr header */
int len, err, offset;
int algo = get_hash_algo(params.hash_algo);
int algo = imaevm_get_hash_algo(imaevm_params.hash_algo);
if (algo < 0) {
log_err("Unknown hash algo: %s\n", imaevm_params.hash_algo);
return -1;
}
if (algo > PKEY_HASH_SHA1) {
hash[0] = IMA_XATTR_DIGEST_NG;
hash[1] = algo;
@ -544,17 +601,18 @@ static int hash_ima(const char *file)
len = ima_calc_hash(file, hash + offset);
if (len <= 1)
return len;
assert(len + offset <= sizeof(hash));
len += offset;
if (params.verbose >= LOG_INFO)
log_info("hash: ");
if (imaevm_params.verbose >= LOG_INFO)
log_info("hash(%s): ", imaevm_params.hash_algo);
if (sigdump || params.verbose >= LOG_INFO)
dump(hash, len);
if (sigdump || imaevm_params.verbose >= LOG_INFO)
imaevm_hexdump(hash, len);
if (xattr) {
err = lsetxattr(file, "security.ima", hash, len, 0);
err = lsetxattr(file, xattr_ima, hash, len, 0);
if (err < 0) {
log_err("setxattr failed: %s\n", file);
return err;
@ -566,30 +624,32 @@ static int hash_ima(const char *file)
static int sign_ima(const char *file, const char *key)
{
unsigned char hash[64];
unsigned char sig[1024];
unsigned char hash[MAX_DIGEST_SIZE];
unsigned char sig[MAX_SIGNATURE_SIZE];
int len, err;
len = ima_calc_hash(file, hash);
if (len <= 1)
return len;
assert(len <= sizeof(hash));
len = sign_hash(params.hash_algo, hash, len, key, NULL, sig + 1);
len = sign_hash(imaevm_params.hash_algo, hash, len, key, NULL, sig + 1);
if (len <= 1)
return len;
assert(len < sizeof(sig));
/* add header */
len++;
sig[0] = EVM_IMA_XATTR_DIGSIG;
if (sigdump || params.verbose >= LOG_INFO)
dump(sig, len);
if (sigdump || imaevm_params.verbose >= LOG_INFO)
imaevm_hexdump(sig, len);
if (sigfile)
bin2file(file, "sig", sig, len);
if (xattr) {
err = lsetxattr(file, "security.ima", sig, len, 0);
err = lsetxattr(file, xattr_ima, sig, len, 0);
if (err < 0) {
log_err("setxattr failed: %s\n", file);
return err;
@ -663,7 +723,7 @@ static int sign_ima_file(const char *file)
{
const char *key;
key = params.keyfile ? : "/etc/keys/privkey_evm.pem";
key = imaevm_params.keyfile ? : "/etc/keys/privkey_evm.pem";
return sign_ima(file, key);
}
@ -680,11 +740,11 @@ static int cmd_sign_hash(struct command *cmd)
int hashlen = 0;
size_t line_len;
ssize_t len;
unsigned char hash[64];
unsigned char sig[1024] = "\x03";
unsigned char hash[MAX_DIGEST_SIZE];
unsigned char sig[MAX_SIGNATURE_SIZE] = "\x03";
int siglen;
key = params.keyfile ? : "/etc/keys/privkey_evm.pem";
key = imaevm_params.keyfile ? : "/etc/keys/privkey_evm.pem";
/* support reading hash (eg. output of shasum) */
while ((len = getline(&line, &line_len, stdin)) > 0) {
@ -696,11 +756,13 @@ static int cmd_sign_hash(struct command *cmd)
token = strpbrk(line, ", \t");
hashlen = token ? token - line : strlen(line);
hex2bin(hash, line, hashlen);
siglen = sign_hash(params.hash_algo, hash, hashlen/2,
assert(hashlen / 2 <= sizeof(hash));
hex2bin(hash, line, hashlen / 2);
siglen = sign_hash(imaevm_params.hash_algo, hash, hashlen / 2,
key, NULL, sig + 1);
if (siglen <= 1)
return siglen;
assert(siglen < sizeof(sig));
fwrite(line, len, 1, stdout);
fprintf(stdout, " ");
@ -722,7 +784,7 @@ static int sign_evm_path(const char *file)
const char *key;
int err;
key = params.keyfile ? : "/etc/keys/privkey_evm.pem";
key = imaevm_params.keyfile ? : "/etc/keys/privkey_evm.pem";
if (digsig) {
err = sign_ima(file, key);
@ -746,26 +808,28 @@ static int cmd_sign_evm(struct command *cmd)
static int verify_evm(const char *file)
{
unsigned char hash[20];
unsigned char sig[1024];
unsigned char hash[MAX_DIGEST_SIZE];
unsigned char sig[MAX_SIGNATURE_SIZE];
int mdlen;
int len;
len = calc_evm_hash(file, hash);
if (len <= 1)
return len;
mdlen = calc_evm_hash(file, hash);
if (mdlen <= 1)
return mdlen;
assert(mdlen <= sizeof(hash));
len = lgetxattr(file, "security.evm", sig, sizeof(sig));
len = lgetxattr(file, xattr_evm, sig, sizeof(sig));
if (len < 0) {
log_err("getxattr failed: %s\n", file);
return len;
}
if (sig[0] != 0x03) {
log_err("security.evm has no signature\n");
log_err("%s has no signature\n", xattr_evm);
return -1;
}
return verify_hash(file, hash, sizeof(hash), sig + 1, len - 1);
return verify_hash(file, hash, mdlen, sig + 1, len - 1);
}
static int cmd_verify_evm(struct command *cmd)
@ -779,24 +843,40 @@ static int cmd_verify_evm(struct command *cmd)
return -1;
}
if (imaevm_params.x509) {
if (imaevm_params.keyfile) /* Support multiple public keys */
init_public_keys(imaevm_params.keyfile);
else /* assume read pubkey from x509 cert */
init_public_keys("/etc/keys/x509_evm.der");
}
err = verify_evm(file);
if (!err && params.verbose >= LOG_INFO)
if (!err && imaevm_params.verbose >= LOG_INFO)
log_info("%s: verification is OK\n", file);
return err;
}
static int verify_ima(const char *file)
{
unsigned char sig[1024];
unsigned char sig[MAX_SIGNATURE_SIZE];
int len;
if (sigfile) {
void *tmp = file2bin(file, "sig", &len);
if (!tmp) {
log_err("Failed reading: %s\n", file);
return -1;
}
if (len > sizeof(sig)) {
log_err("Signature file is too big: %s\n", file);
free(tmp);
return -1;
}
memcpy(sig, tmp, len);
free(tmp);
} else {
len = lgetxattr(file, "security.ima", sig, sizeof(sig));
len = lgetxattr(file, xattr_ima, sig, sizeof(sig));
if (len < 0) {
log_err("getxattr failed: %s\n", file);
return len;
@ -809,7 +889,14 @@ static int verify_ima(const char *file)
static int cmd_verify_ima(struct command *cmd)
{
char *file = g_argv[optind++];
int err;
int err, fails = 0;
if (imaevm_params.x509) {
if (imaevm_params.keyfile) /* Support multiple public keys */
init_public_keys(imaevm_params.keyfile);
else /* assume read pubkey from x509 cert */
init_public_keys("/etc/keys/x509_evm.der");
}
errno = 0;
if (!file) {
@ -818,10 +905,14 @@ static int cmd_verify_ima(struct command *cmd)
return -1;
}
err = verify_ima(file);
if (!err && params.verbose >= LOG_INFO)
log_info("%s: verification is OK\n", file);
return err;
do {
err = verify_ima(file);
if (err)
fails++;
if (!err && imaevm_params.verbose >= LOG_INFO)
log_info("%s: verification is OK\n", file);
} while ((file = g_argv[optind++]));
return fails > 0;
}
static int cmd_convert(struct command *cmd)
@ -833,15 +924,15 @@ static int cmd_convert(struct command *cmd)
uint8_t keyid[8];
RSA *key;
params.x509 = 0;
imaevm_params.x509 = 0;
inkey = g_argv[optind++];
if (!inkey) {
inkey = params.x509 ? "/etc/keys/x509_evm.der" :
"/etc/keys/pubkey_evm.pem";
inkey = imaevm_params.x509 ? "/etc/keys/x509_evm.der" :
"/etc/keys/pubkey_evm.pem";
}
key = read_pub_key(inkey, params.x509);
key = read_pub_key(inkey, imaevm_params.x509);
if (!key)
return 1;
@ -862,12 +953,11 @@ static int cmd_import(struct command *cmd)
int id, len, err = 0;
char name[20];
uint8_t keyid[8];
RSA *key;
inkey = g_argv[optind++];
if (!inkey) {
inkey = params.x509 ? "/etc/keys/x509_evm.der" :
"/etc/keys/pubkey_evm.pem";
inkey = imaevm_params.x509 ? "/etc/keys/x509_evm.der" :
"/etc/keys/pubkey_evm.pem";
} else
ring = g_argv[optind++];
@ -896,23 +986,32 @@ static int cmd_import(struct command *cmd)
}
}
key = read_pub_key(inkey, params.x509);
if (!key)
return 1;
if (imaevm_params.x509) {
EVP_PKEY *pkey = read_pub_pkey(inkey, imaevm_params.x509);
if (params.x509) {
if (!pkey)
return 1;
pub = file2bin(inkey, NULL, &len);
if (!pub)
goto out;
calc_keyid_v2((uint32_t *)keyid, name, key);
if (!pub) {
EVP_PKEY_free(pkey);
return 1;
}
calc_keyid_v2((uint32_t *)keyid, name, pkey);
EVP_PKEY_free(pkey);
} else {
RSA *key = read_pub_key(inkey, imaevm_params.x509);
if (!key)
return 1;
len = key2bin(key, pub);
calc_keyid_v1(keyid, name, pub, len);
RSA_free(key);
}
log_info("Importing public key %s from file %s into keyring %d\n", name, inkey, id);
id = add_key(params.x509 ? "asymmetric" : "user", params.x509 ? NULL : name, pub, len, id);
id = add_key(imaevm_params.x509 ? "asymmetric" : "user",
imaevm_params.x509 ? NULL : name, pub, len, id);
if (id < 0) {
log_err("add_key failed\n");
err = id;
@ -920,10 +1019,8 @@ static int cmd_import(struct command *cmd)
log_info("keyid: %d\n", id);
printf("%d\n", id);
}
if (params.x509)
if (imaevm_params.x509)
free(pub);
out:
RSA_free(key);
return err;
}
@ -939,7 +1036,7 @@ static int setxattr_ima(const char *file, char *sig_file)
if (!sig)
return 0;
err = lsetxattr(file, "security.ima", sig, len, 0);
err = lsetxattr(file, xattr_ima, sig, len, 0);
if (err < 0)
log_err("setxattr failed: %s\n", file);
free(sig);
@ -967,6 +1064,7 @@ static int cmd_setxattr_ima(struct command *cmd)
static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *hash)
{
const EVP_MD *md;
struct stat st;
int err = -1;
uint32_t generation = 0;
@ -1033,7 +1131,14 @@ static int calc_evm_hmac(const char *file, const char *keyfile, unsigned char *h
goto out;
}
err = !HMAC_Init_ex(pctx, evmkey, sizeof(evmkey), EVP_sha1(), NULL);
md = EVP_get_digestbyname(imaevm_params.hash_algo);
if (!md) {
log_err("EVP_get_digestbyname(%s) failed\n",
imaevm_params.hash_algo);
goto out;
}
err = !HMAC_Init_ex(pctx, evmkey, sizeof(evmkey), md, NULL);
if (err) {
log_err("HMAC_Init() failed\n");
goto out;
@ -1114,21 +1219,23 @@ out:
static int hmac_evm(const char *file, const char *key)
{
unsigned char hash[20];
unsigned char sig[1024];
unsigned char hash[MAX_DIGEST_SIZE];
unsigned char sig[MAX_SIGNATURE_SIZE];
int len, err;
len = calc_evm_hmac(file, key, hash);
if (len <= 1)
return len;
assert(len <= sizeof(hash));
log_info("hmac: ");
log_dump(hash, len);
assert(len < sizeof(sig));
memcpy(sig + 1, hash, len);
if (xattr) {
sig[0] = EVM_XATTR_HMAC;
err = lsetxattr(file, "security.evm", sig, len + 1, 0);
err = lsetxattr(file, xattr_evm, sig, len + 1, 0);
if (err < 0) {
log_err("setxattr failed: %s\n", file);
return err;
@ -1149,7 +1256,7 @@ static int cmd_hmac_evm(struct command *cmd)
return -1;
}
key = params.keyfile ? : "/etc/keys/privkey_evm.pem";
key = imaevm_params.keyfile ? : "/etc/keys/privkey_evm.pem";
if (digsig) {
err = sign_ima(file, key);
@ -1184,9 +1291,9 @@ static int ima_fix(const char *path)
}
for (; size > 0; len++, size -= len, list += len) {
len = strlen(list);
if (!strcmp(list, "security.ima"))
if (!strcmp(list, xattr_ima))
ima = 1;
else if (!strcmp(list, "security.evm"))
else if (!strcmp(list, xattr_evm))
evm = 1;
}
if (ima && evm)
@ -1263,8 +1370,8 @@ static int cmd_ima_fix(struct command *cmd)
static int ima_clear(const char *path)
{
log_info("%s\n", path);
lremovexattr(path, "security.ima");
lremovexattr(path, "security.evm");
lremovexattr(path, xattr_ima);
lremovexattr(path, xattr_evm);
return 0;
}
@ -1289,10 +1396,8 @@ static int tpm_pcr_read(int idx, uint8_t *pcr, int len)
if (!fp)
fp = fopen(misc_pcrs, "r");
if (!fp) {
log_err("Unable to open %s or %s\n", pcrs, misc_pcrs);
if (!fp)
return -1;
}
for (;;) {
p = fgets(buf, sizeof(buf), fp);
@ -1308,6 +1413,43 @@ static int tpm_pcr_read(int idx, uint8_t *pcr, int len)
return result;
}
#ifdef HAVE_TSSPCRREAD
static int tpm2_pcr_read(int idx, uint8_t *hwpcr, int len, char **errmsg)
{
FILE *fp;
char pcr[100]; /* may contain an error */
char cmd[50];
int ret;
sprintf(cmd, "tsspcrread -halg sha1 -ha %d -ns 2> /dev/null", idx);
fp = popen(cmd, "r");
if (!fp) {
ret = asprintf(errmsg, "popen failed: %s", strerror(errno));
if (ret == -1) /* the contents of errmsg is undefined */
*errmsg = NULL;
return -1;
}
if (fgets(pcr, sizeof(pcr), fp) == NULL) {
ret = asprintf(errmsg, "tsspcrread failed: %s",
strerror(errno));
if (ret == -1) /* the contents of errmsg is undefined */
*errmsg = NULL;
ret = pclose(fp);
return -1;
}
/* get the popen "cmd" return code */
ret = pclose(fp);
if (!ret)
hex2bin(hwpcr, pcr, SHA_DIGEST_LENGTH);
else
*errmsg = strndup(pcr, strlen(pcr) - 1); /* remove newline */
return ret;
}
#endif
#define TCG_EVENT_NAME_LEN_MAX 255
struct template_entry {
@ -1363,13 +1505,42 @@ void ima_show(struct template_entry *entry)
log_debug_dump(entry->header.digest, sizeof(entry->header.digest));
}
/*
* Keep track of unknown or malformed template names.
*
* Return 1 for found, return 0 for not found.
*/
static int lookup_template_name_entry(char *template_name)
{
struct template_name_entry {
struct template_name_entry *next;
char name[];
} *entry;
static struct template_name_entry *template_names = NULL;
for (entry = template_names; entry != NULL; entry = entry->next) {
if (strcmp(entry->name, template_name) == 0)
return 1;
}
entry = malloc(sizeof(struct template_name_entry) +
strlen(template_name) + 1);
if (entry) {
strcpy(entry->name, template_name);
entry->next = template_names;
template_names = entry;
}
return 0;
}
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;
int total_len = entry->template_len, digest_len, len, sig_len, fbuf_len;
uint8_t *digest, *sig = NULL, *fbuf = NULL;
char *algo, *path;
int found;
int err;
/* get binary digest */
@ -1407,6 +1578,18 @@ void ima_ng_show(struct template_entry *entry)
sig = fieldp;
sig_len = field_len;
/* move to next field */
fieldp += field_len;
total_len -= field_len;
}
} else if (!strcmp(entry->name, "ima-buf")) {
field_len = *(uint32_t *)fieldp;
fieldp += sizeof(field_len);
total_len -= sizeof(field_len);
if (field_len) {
fbuf = fieldp;
fbuf_len = field_len;
/* move to next field */
fieldp += field_len;
total_len -= field_len;
@ -1414,16 +1597,20 @@ void ima_ng_show(struct template_entry *entry)
}
/* ascii_runtime_measurements */
if (params.verbose > LOG_INFO) {
if (imaevm_params.verbose > LOG_INFO) {
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 (fbuf) {
log_info(" ");
log_dump_n(fbuf, fbuf_len);
}
}
if (sig) {
if (params.verbose > LOG_INFO) {
if (imaevm_params.verbose > LOG_INFO) {
log_info(" ");
log_dump(sig, sig_len);
}
@ -1432,15 +1619,19 @@ void ima_ng_show(struct template_entry *entry)
digest, digest_len);
else
err = ima_verify_signature(path, sig, sig_len, NULL, 0);
if (!err && params.verbose > LOG_INFO)
if (!err && imaevm_params.verbose > LOG_INFO)
log_info("%s: verification is OK\n", path);
} else {
if (params.verbose > LOG_INFO)
if (imaevm_params.verbose > LOG_INFO)
log_info("\n");
}
if (total_len)
log_err("Remain unprocessed data: %d\n", total_len);
if (total_len) {
found = lookup_template_name_entry(entry->name);
if (!found)
log_err("Template \"%s\" contains unprocessed data: "
"%d bytes\n", entry->name, total_len);
}
}
static int ima_measurement(const char *file)
@ -1466,9 +1657,10 @@ static int ima_measurement(const char *file)
return -1;
}
/* Support multiple public keys */
if (params.keyfile)
init_public_keys(params.keyfile);
if (imaevm_params.keyfile) /* Support multiple public keys */
init_public_keys(imaevm_params.keyfile);
else /* assume read pubkey from x509 cert */
init_public_keys("/etc/keys/x509_evm.der");
while (fread(&entry.header, sizeof(entry.header), 1, fp)) {
ima_extend_pcr(pcr[entry.header.pcr], entry.header.digest,
@ -1514,7 +1706,22 @@ static int ima_measurement(const char *file)
log_info("PCRAgg %.2d: ", i);
log_dump(pcr[i], SHA_DIGEST_LENGTH);
tpm_pcr_read(i, hwpcr, sizeof(hwpcr));
if (tpm_pcr_read(i, hwpcr, sizeof(hwpcr))) {
#ifdef HAVE_TSSPCRREAD
char *errmsg = NULL;
err = tpm2_pcr_read(i, hwpcr, sizeof(hwpcr), &errmsg);
if (err) {
log_info("Failed to read PCRs: (%s)\n", errmsg);
free(errmsg);
exit(1);
}
#else
log_info("Failed to read TPM 1.2 PCRs.\n");
exit(1);
#endif
}
log_info("HW PCR-%d: ", i);
log_dump(hwpcr, sizeof(hwpcr));
@ -1615,10 +1822,11 @@ static void usage(void)
printf(
"\n"
" -a, --hashalgo sha1 (default), sha224, sha256, sha384, sha512\n"
" -a, --hashalgo sha1 (default), sha224, sha256, sha384, sha512, streebog256, streebog512\n"
" -s, --imasig make IMA signature\n"
" -d, --imahash make IMA hash\n"
" -f, --sigfile store IMA signature in .sig file instead of xattr\n"
" --xattr-user store xattrs in user namespace (for testing purposes)\n"
" --rsa use RSA key type and signing scheme v1\n"
" -k, --key path to signing key (default: /etc/keys/{privkey,pubkey}_evm.pem)\n"
" -o, --portable generate portable EVM signatures\n"
@ -1641,6 +1849,7 @@ static void usage(void)
" --selinux use custom Selinux label for EVM\n"
" --caps use custom Capabilities for EVM(unspecified: from FS, empty: do not use)\n"
" --list measurement list verification\n"
" --engine e preload OpenSSL engine e (such as: gost)\n"
" -v increase verbosity level\n"
" -h, --help display this help and exit\n"
"\n");
@ -1693,6 +1902,8 @@ static struct option opts[] = {
{"selinux", 1, 0, 136},
{"caps", 2, 0, 137},
{"list", 0, 0, 138},
{"engine", 1, 0, 139},
{"xattr-user", 0, 0, 140},
{}
};
@ -1716,6 +1927,7 @@ static char *get_password(void)
if (tcsetattr(fileno(stdin), TCSANOW, &tmp_flags) != 0) {
perror("tcsetattr");
free(password);
return NULL;
}
@ -1725,6 +1937,7 @@ static char *get_password(void)
/* restore terminal */
if (tcsetattr(fileno(stdin), TCSANOW, &flags) != 0) {
perror("tcsetattr");
free(password);
return NULL;
}
@ -1734,7 +1947,15 @@ static char *get_password(void)
int main(int argc, char *argv[])
{
int err = 0, c, lind;
ENGINE *eng = NULL;
#if !(OPENSSL_VERSION_NUMBER < 0x10100000)
OPENSSL_init_crypto(
#ifndef DISABLE_OPENSSL_CONF
OPENSSL_INIT_LOAD_CONFIG |
#endif
OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL);
#endif
g_argv = argv;
g_argc = argc;
@ -1749,7 +1970,7 @@ int main(int argc, char *argv[])
exit(0);
break;
case 'v':
params.verbose++;
imaevm_params.verbose++;
break;
case 'd':
digest = 1;
@ -1763,13 +1984,13 @@ int main(int argc, char *argv[])
sigdump = 1;
break;
case 'a':
params.hash_algo = optarg;
imaevm_params.hash_algo = optarg;
break;
case 'p':
if (optarg)
params.keypass = optarg;
imaevm_params.keypass = optarg;
else
params.keypass = get_password();
imaevm_params.keypass = get_password();
break;
case 'f':
sigfile = 1;
@ -1780,10 +2001,10 @@ int main(int argc, char *argv[])
hmac_flags |= HMAC_FLAG_NO_UUID;
break;
case '1':
params.x509 = 0;
imaevm_params.x509 = 0;
break;
case 'k':
params.keyfile = optarg;
imaevm_params.keyfile = optarg;
break;
case 'i':
if (evm_portable)
@ -1844,6 +2065,23 @@ int main(int argc, char *argv[])
case 138:
measurement_list = 1;
break;
case 139: /* --engine e */
eng = ENGINE_by_id(optarg);
if (!eng) {
log_err("engine %s isn't available\n", optarg);
ERR_print_errors_fp(stderr);
} else if (!ENGINE_init(eng)) {
log_err("engine %s init failed\n", optarg);
ERR_print_errors_fp(stderr);
ENGINE_free(eng);
eng = NULL;
}
ENGINE_set_default(eng, ENGINE_METHOD_ALL);
break;
case 140: /* --xattr-user */
xattr_ima = "user.ima";
xattr_evm = "user.evm";
break;
case '?':
exit(1);
break;
@ -1870,6 +2108,13 @@ int main(int argc, char *argv[])
}
}
if (eng) {
ENGINE_finish(eng);
ENGINE_free(eng);
#if OPENSSL_API_COMPAT < 0x10100000L
ENGINE_cleanup();
#endif
}
ERR_free_strings();
EVP_cleanup();
BIO_free(NULL);

49
src/hash_info.gen Executable file
View File

@ -0,0 +1,49 @@
#!/bin/sh
#
# Generate hash_info.h from kernel headers
#
# Copyright (C) 2018 <vt@altlinux.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# 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.
KERNEL_HEADERS=$1
HASH_INFO_H=uapi/linux/hash_info.h
HASH_INFO=$KERNEL_HEADERS/include/$HASH_INFO_H
# Allow to specify kernel-headers past include/
if [ ! -e $HASH_INFO ]; then
HASH_INFO2=$KERNEL_HEADERS/$HASH_INFO_H
if [ -e $HASH_INFO2 ]; then
HASH_INFO=$HASH_INFO2
fi
fi
if [ ! -e $HASH_INFO ]; then
echo "/* $HASH_INFO is not found */"
HASH_INFO=/dev/null
else
echo "/* $HASH_INFO is found */"
fi
echo "enum hash_algo {"
grep HASH_ALGO_.*, $HASH_INFO
printf "\tHASH_ALGO__LAST\n"
echo "};"
echo "const char *const hash_algo_name[HASH_ALGO__LAST] = {"
sed -n 's/HASH_ALGO_\(.*\),/\1 \L\1\E/p' $HASH_INFO | \
while read a b; do
# Normalize text hash name: if it contains underscore between
# digits replace it with a dash, other underscores are removed.
b=$(echo "$b" | sed "s/\([0-9]\)_\([0-9]\)/\1-\2/g;s/_//g")
printf '\t%-26s = "%s",\n' "[HASH_ALGO_$a]" "$b"
done
echo "};"

View File

@ -50,8 +50,10 @@
#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); })
#define do_log(level, fmt, args...) \
({ if (level <= imaevm_params.verbose) fprintf(stderr, fmt, ##args); })
#define do_log_dump(level, p, len, cr) \
({ if (level <= imaevm_params.verbose) imaevm_do_hexdump(stderr, p, len, cr); })
#else
#define do_log(level, fmt, args...) syslog(level, fmt, ##args)
#define do_log_dump(level, p, len, cr)
@ -75,6 +77,9 @@
#define DATA_SIZE 4096
#define SHA1_HASH_LEN 20
#define MAX_DIGEST_SIZE 64
#define MAX_SIGNATURE_SIZE 1024
#define __packed __attribute__((packed))
enum evm_ima_xattr_type {
@ -149,6 +154,7 @@ struct signature_hdr {
char mpi[0];
} __packed;
/* reflect enum hash_algo from include/uapi/linux/hash_info.h */
enum pkey_hash_algo {
PKEY_HASH_MD4,
PKEY_HASH_MD5,
@ -158,6 +164,18 @@ enum pkey_hash_algo {
PKEY_HASH_SHA384,
PKEY_HASH_SHA512,
PKEY_HASH_SHA224,
PKEY_HASH_RIPE_MD_128,
PKEY_HASH_RIPE_MD_256,
PKEY_HASH_RIPE_MD_320,
PKEY_HASH_WP_256,
PKEY_HASH_WP_384,
PKEY_HASH_WP_512,
PKEY_HASH_TGR_128,
PKEY_HASH_TGR_160,
PKEY_HASH_TGR_192,
PKEY_HASH_SM3_256,
PKEY_HASH_STREEBOG_256,
PKEY_HASH_STREEBOG_512,
PKEY_HASH__LAST
};
@ -172,10 +190,7 @@ struct signature_v2_hdr {
uint8_t sig[0]; /* signature payload */
} __packed;
typedef int (*verify_hash_fn_t)(const char *file, const unsigned char *hash, int size, unsigned char *sig, int siglen, const char *keyfile);
struct libevm_params {
struct libimaevm_params {
int verbose;
int x509;
const char *hash_algo;
@ -191,18 +206,17 @@ struct RSA_ASN1_template {
#define NUM_PCRS 20
#define DEFAULT_PCR 10
extern const struct RSA_ASN1_template RSA_ASN1_templates[PKEY_HASH__LAST];
extern struct libevm_params params;
extern struct libimaevm_params imaevm_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);
void imaevm_do_hexdump(FILE *fp, const void *ptr, int len, bool cr);
void imaevm_hexdump(const void *ptr, int len);
int ima_calc_hash(const char *file, uint8_t *hash);
int get_hash_algo(const char *algo);
int imaevm_get_hash_algo(const char *algo);
RSA *read_pub_key(const char *keyfile, int x509);
EVP_PKEY *read_pub_pkey(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);
void calc_keyid_v2(uint32_t *keyid, char *str, EVP_PKEY *pkey);
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);

View File

@ -49,15 +49,20 @@
#include <dirent.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <ctype.h>
#include <openssl/crypto.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#include "imaevm.h"
#include "hash_info.h"
const char *const pkey_hash_algo[PKEY_HASH__LAST] = {
/* Names that are primary for OpenSSL. */
static const char *const pkey_hash_algo[PKEY_HASH__LAST] = {
[PKEY_HASH_MD4] = "md4",
[PKEY_HASH_MD5] = "md5",
[PKEY_HASH_SHA1] = "sha1",
@ -66,66 +71,17 @@ const char *const pkey_hash_algo[PKEY_HASH__LAST] = {
[PKEY_HASH_SHA384] = "sha384",
[PKEY_HASH_SHA512] = "sha512",
[PKEY_HASH_SHA224] = "sha224",
[PKEY_HASH_STREEBOG_256] = "md_gost12_256",
[PKEY_HASH_STREEBOG_512] = "md_gost12_512",
};
/*
* 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
/* Names that are primary for the kernel. */
static const char *const pkey_hash_algo_kern[PKEY_HASH__LAST] = {
[PKEY_HASH_STREEBOG_256] = "streebog256",
[PKEY_HASH_STREEBOG_512] = "streebog512",
};
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 = {
struct libimaevm_params imaevm_params = {
.verbose = LOG_INFO - 1,
.x509 = 1,
.hash_algo = "sha1",
@ -133,7 +89,7 @@ struct libevm_params params = {
static void __attribute__ ((constructor)) libinit(void);
void do_dump(FILE *fp, const void *ptr, int len, bool cr)
void imaevm_do_hexdump(FILE *fp, const void *ptr, int len, bool cr)
{
int i;
uint8_t *data = (uint8_t *) ptr;
@ -144,25 +100,32 @@ void do_dump(FILE *fp, const void *ptr, int len, bool cr)
fprintf(fp, "\n");
}
void dump(const void *ptr, int len)
void imaevm_hexdump(const void *ptr, int len)
{
do_dump(stdout, ptr, len, true);
imaevm_do_hexdump(stdout, ptr, len, true);
}
int get_filesize(const char *filename)
static const char *get_hash_algo_by_id(int algo)
{
struct stat stats;
/* Need to know the file length */
stat(filename, &stats);
return (int)stats.st_size;
if (algo < PKEY_HASH__LAST)
return pkey_hash_algo[algo];
if (algo < HASH_ALGO__LAST)
return hash_algo_name[algo];
log_err("digest %d not found\n", algo);
return "unknown";
}
static inline off_t get_fdsize(int fd)
/* Output all remaining openssl error messages. */
static void output_openssl_errors(void)
{
struct stat stats;
/* Need to know the file length */
fstat(fd, &stats);
return stats.st_size;
while (ERR_peek_error()) {
char buf[256];
/* buf must be at least 256 bytes long according to man */
ERR_error_string(ERR_get_error(), buf);
log_err("openssl: %s\n", buf);
}
}
static int add_file_hash(const char *file, EVP_MD_CTX *ctx)
@ -171,6 +134,7 @@ static int add_file_hash(const char *file, EVP_MD_CTX *ctx)
int err = -1, bs = DATA_SIZE;
off_t size, len;
FILE *fp;
struct stat stats;
fp = fopen(file, "r");
if (!fp) {
@ -184,7 +148,12 @@ static int add_file_hash(const char *file, EVP_MD_CTX *ctx)
goto out;
}
for (size = get_fdsize(fileno(fp)); size; size -= len) {
if (fstat(fileno(fp), &stats) == -1) {
log_err("Failed to fstat: %s (%s)\n", file, strerror(errno));
goto out;
}
for (size = stats.st_size; size; size -= len) {
len = MIN(size, bs);
if (!fread(data, len, 1, fp)) {
if (ferror(fp)) {
@ -234,6 +203,7 @@ static int add_dir_hash(const char *file, EVP_MD_CTX *ctx)
err |= EVP_DigestUpdate(ctx, &type, sizeof(type));
if (!err) {
log_err("EVP_DigestUpdate() failed\n");
output_openssl_errors();
result = 1;
break;
}
@ -285,19 +255,22 @@ int ima_calc_hash(const char *file, uint8_t *hash)
err = lstat(file, &st);
if (err < 0) {
log_err("Failed to stat: %s\n", file);
return err;
goto err;
}
md = EVP_get_digestbyname(params.hash_algo);
md = EVP_get_digestbyname(imaevm_params.hash_algo);
if (!md) {
log_err("EVP_get_digestbyname() failed\n");
return 1;
log_err("EVP_get_digestbyname(%s) failed\n",
imaevm_params.hash_algo);
err = 1;
goto err;
}
err = EVP_DigestInit(pctx, md);
if (!err) {
log_err("EVP_DigestInit() failed\n");
return 1;
err = 1;
goto err;
}
switch (st.st_mode & S_IFMT) {
@ -316,28 +289,37 @@ int ima_calc_hash(const char *file, uint8_t *hash)
break;
default:
log_errno("Unsupported file type");
return -1;
err = -1;
goto err;
}
if (err)
return err;
goto err;
err = EVP_DigestFinal(pctx, hash, &mdlen);
if (!err) {
log_err("EVP_DigestFinal() failed\n");
return 1;
err = 1;
goto err;
}
return mdlen;
err = mdlen;
err:
if (err == 1)
output_openssl_errors();
#if OPENSSL_VERSION_NUMBER >= 0x10100000
EVP_MD_CTX_free(pctx);
#endif
return err;
}
RSA *read_pub_key(const char *keyfile, int x509)
EVP_PKEY *read_pub_pkey(const char *keyfile, int x509)
{
FILE *fp;
RSA *key = NULL;
X509 *crt = NULL;
EVP_PKEY *pkey = NULL;
if (!keyfile)
return NULL;
fp = fopen(keyfile, "r");
if (!fp) {
log_err("Failed to open keyfile: %s\n", keyfile);
@ -345,35 +327,54 @@ RSA *read_pub_key(const char *keyfile, int x509)
}
if (x509) {
crt = d2i_X509_fp(fp, NULL);
X509 *crt = d2i_X509_fp(fp, NULL);
if (!crt) {
log_err("d2i_X509_fp() failed\n");
log_err("Failed to d2i_X509_fp key file: %s\n",
keyfile);
goto out;
}
pkey = X509_extract_key(crt);
X509_free(crt);
if (!pkey) {
log_err("X509_extract_key() failed\n");
log_err("Failed to X509_extract_key key file: %s\n",
keyfile);
goto out;
}
key = EVP_PKEY_get1_RSA(pkey);
} else {
key = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
if (!pkey)
log_err("Failed to PEM_read_PUBKEY key file: %s\n",
keyfile);
}
if (!key)
log_err("PEM_read_RSA_PUBKEY() failed\n");
out:
if (pkey)
EVP_PKEY_free(pkey);
if (crt)
X509_free(crt);
if (!pkey)
output_openssl_errors();
fclose(fp);
return pkey;
}
RSA *read_pub_key(const char *keyfile, int x509)
{
EVP_PKEY *pkey;
RSA *key;
pkey = read_pub_pkey(keyfile, x509);
if (!pkey)
return NULL;
key = EVP_PKEY_get1_RSA(pkey);
EVP_PKEY_free(pkey);
if (!key) {
log_err("read_pub_key: unsupported key type\n");
output_openssl_errors();
return NULL;
}
return key;
}
int verify_hash_v1(const char *file, const unsigned char *hash, int size,
unsigned char *sig, int siglen, const char *keyfile)
static int verify_hash_v1(const char *file, const unsigned char *hash, int size,
unsigned char *sig, int siglen, const char *keyfile)
{
int err, len;
SHA_CTX ctx;
@ -400,6 +401,7 @@ int verify_hash_v1(const char *file, const unsigned char *hash, int size,
RSA_free(key);
if (err < 0) {
log_err("%s: RSA_public_decrypt() failed: %d\n", file, err);
output_openssl_errors();
return 1;
}
@ -417,29 +419,46 @@ struct public_key_entry {
struct public_key_entry *next;
uint32_t keyid;
char name[9];
RSA *key;
EVP_PKEY *key;
};
static struct public_key_entry *public_keys = NULL;
static RSA *find_keyid(uint32_t keyid)
static EVP_PKEY *find_keyid(uint32_t keyid)
{
struct public_key_entry *entry;
struct public_key_entry *entry, *tail = public_keys;
int i = 1;
for (entry = public_keys; entry != NULL; entry = entry->next) {
if (entry->keyid == keyid)
return entry->key;
i++;
tail = entry;
}
return NULL;
/* add unknown keys to list */
entry = calloc(1, sizeof(struct public_key_entry));
if (!entry) {
perror("calloc");
return 0;
}
entry->keyid = keyid;
if (tail)
tail->next = entry;
else
public_keys = entry;
log_err("key %d: %x (unknown keyid)\n", i, __be32_to_cpup(&keyid));
return 0;
}
void init_public_keys(const char *keyfiles)
{
struct public_key_entry *entry;
char *tmp_keyfiles;
char *tmp_keyfiles, *keyfiles_free;
char *keyfile;
int i = 1;
tmp_keyfiles = strdup(keyfiles);
keyfiles_free = tmp_keyfiles;
while ((keyfile = strsep(&tmp_keyfiles, ", \t")) != NULL) {
if (!keyfile)
@ -454,7 +473,7 @@ void init_public_keys(const char *keyfiles)
break;
}
entry->key = read_pub_key(keyfile, 1);
entry->key = read_pub_pkey(keyfile, 1);
if (!entry->key) {
free(entry);
continue;
@ -466,71 +485,94 @@ void init_public_keys(const char *keyfiles)
entry->next = public_keys;
public_keys = entry;
}
free(keyfiles_free);
}
int verify_hash_v2(const char *file, const unsigned char *hash, int size,
unsigned char *sig, int siglen, const char *keyfile)
/*
* Return: 0 verification good, 1 verification bad, -1 error.
*/
static int verify_hash_v2(const char *file, const unsigned char *hash, int size,
unsigned char *sig, int siglen)
{
int err, len;
unsigned char out[1024];
RSA *key;
int ret = -1;
EVP_PKEY *pkey, *pkey_free = NULL;
struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
const struct RSA_ASN1_template *asn1;
EVP_PKEY_CTX *ctx;
const EVP_MD *md;
const char *st;
if (params.verbose > LOG_INFO) {
log_info("hash: ");
if (imaevm_params.verbose > LOG_INFO) {
log_info("hash(%s): ", imaevm_params.hash_algo);
log_dump(hash, size);
}
if (public_keys) {
key = find_keyid(hdr->keyid);
if (!key) {
log_err("%s: unknown keyid: %x\n", file,
__be32_to_cpup(&hdr->keyid));
return -1;
}
} else {
key = read_pub_key(keyfile, 1);
if (!key)
return 1;
}
pkey = find_keyid(hdr->keyid);
if (!pkey) {
uint32_t keyid = hdr->keyid;
err = RSA_public_decrypt(siglen - sizeof(*hdr), sig + sizeof(*hdr),
out, key, RSA_PKCS1_PADDING);
if (err < 0) {
log_err("%s: RSA_public_decrypt() failed: %d\n", file, err);
return 1;
}
len = err;
asn1 = &RSA_ASN1_templates[hdr->hash_algo];
if (len < asn1->size || memcmp(out, asn1->data, asn1->size)) {
log_err("%s: verification failed: %d\n", file, err);
log_info("%s: verification failed: unknown keyid %x\n",
file, __be32_to_cpup(&keyid));
return -1;
}
len -= asn1->size;
if (len != size || memcmp(out + asn1->size, hash, len)) {
log_err("%s: verification failed: %d\n", file, err);
return -1;
st = "EVP_PKEY_CTX_new";
if (!(ctx = EVP_PKEY_CTX_new(pkey, NULL)))
goto err;
st = "EVP_PKEY_verify_init";
if (!EVP_PKEY_verify_init(ctx))
goto err;
st = "EVP_get_digestbyname";
if (!(md = EVP_get_digestbyname(imaevm_params.hash_algo)))
goto err;
st = "EVP_PKEY_CTX_set_signature_md";
if (!EVP_PKEY_CTX_set_signature_md(ctx, md))
goto err;
st = "EVP_PKEY_verify";
ret = EVP_PKEY_verify(ctx, sig + sizeof(*hdr),
siglen - sizeof(*hdr), hash, size);
if (ret == 1)
ret = 0;
else if (ret == 0) {
log_err("%s: verification failed: %d (%s)\n",
file, ret, ERR_reason_error_string(ERR_get_error()));
output_openssl_errors();
ret = 1;
}
return 0;
err:
if (ret < 0 || ret > 1) {
log_err("%s: verification failed: %d (%s) in %s\n",
file, ret, ERR_reason_error_string(ERR_peek_error()),
st);
output_openssl_errors();
ret = -1;
}
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(pkey_free);
return ret;
}
int get_hash_algo(const char *algo)
int imaevm_get_hash_algo(const char *algo)
{
int i;
/* first iterate over builtin algorithms */
for (i = 0; i < PKEY_HASH__LAST; i++)
if (!strcmp(algo, pkey_hash_algo[i]))
if (pkey_hash_algo[i] &&
!strcmp(algo, pkey_hash_algo[i]))
return i;
return PKEY_HASH_SHA1;
for (i = 0; i < PKEY_HASH__LAST; i++)
if (pkey_hash_algo_kern[i] &&
!strcmp(algo, pkey_hash_algo_kern[i]))
return i;
/* iterate over algorithms provided by kernel-headers */
for (i = 0; i < HASH_ALGO__LAST; i++)
if (hash_algo_name[i] &&
!strcmp(algo, hash_algo_name[i]))
return i;
return -1;
}
static int get_hash_algo_from_sig(unsigned char *sig)
@ -563,38 +605,30 @@ static int get_hash_algo_from_sig(unsigned char *sig)
int verify_hash(const char *file, const unsigned char *hash, int size, unsigned char *sig,
int siglen)
{
const char *key;
int x509;
verify_hash_fn_t verify_hash;
/* Get signature type from sig header */
if (sig[0] == DIGSIG_VERSION_1) {
verify_hash = verify_hash_v1;
const char *key = NULL;
/* Read pubkey from RSA key */
x509 = 0;
if (!imaevm_params.keyfile)
key = "/etc/keys/pubkey_evm.pem";
else
key = imaevm_params.keyfile;
return verify_hash_v1(file, hash, size, sig, siglen, key);
} else if (sig[0] == DIGSIG_VERSION_2) {
verify_hash = verify_hash_v2;
/* Read pubkey from x509 cert */
x509 = 1;
return verify_hash_v2(file, hash, size, sig, siglen);
} 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(file, hash, size, sig, siglen, key);
}
int ima_verify_signature(const char *file, unsigned char *sig, int siglen,
unsigned char *digest, int digestlen)
{
unsigned char hash[64];
unsigned char hash[MAX_DIGEST_SIZE];
int hashlen, sig_hash_algo;
if (sig[0] != 0x03) {
log_err("security.ima has no signature\n");
log_err("xattr ima has no signature\n");
return -1;
}
@ -604,7 +638,7 @@ int ima_verify_signature(const char *file, unsigned char *sig, int siglen,
return -1;
}
/* Use hash algorithm as retrieved from signature */
params.hash_algo = pkey_hash_algo[sig_hash_algo];
imaevm_params.hash_algo = get_hash_algo_by_id(sig_hash_algo);
/*
* Validate the signature based on the digest included in the
@ -616,6 +650,7 @@ int ima_verify_signature(const char *file, unsigned char *sig, int siglen,
hashlen = ima_calc_hash(file, hash);
if (hashlen <= 1)
return hashlen;
assert(hashlen <= sizeof(hash));
return verify_hash(file, hash, hashlen, sig + 1, siglen - 1);
}
@ -672,57 +707,79 @@ void calc_keyid_v1(uint8_t *keyid, char *str, const unsigned char *pkey, int len
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);
if (params.verbose > LOG_INFO) {
id = __be64_to_cpup((__be64 *) keyid);
sprintf(str, "%llX", (unsigned long long)id);
if (imaevm_params.verbose > LOG_INFO)
log_info("keyid-v1: %s\n", str);
}
}
void calc_keyid_v2(uint32_t *keyid, char *str, RSA *key)
/*
* Calculate keyid of the public_key part of EVP_PKEY
*/
void calc_keyid_v2(uint32_t *keyid, char *str, EVP_PKEY *pkey)
{
uint8_t sha1[SHA_DIGEST_LENGTH];
unsigned char *pkey = NULL;
X509_PUBKEY *pk = NULL;
const unsigned char *public_key = NULL;
int len;
len = i2d_RSAPublicKey(key, &pkey);
/* This is more generic than i2d_PublicKey() */
if (X509_PUBKEY_set(&pk, pkey) &&
X509_PUBKEY_get0_param(NULL, &public_key, &len, NULL, pk)) {
uint8_t sha1[SHA_DIGEST_LENGTH];
SHA1(pkey, len, sha1);
SHA1(public_key, len, sha1);
/* sha1[12 - 19] is exactly keyid from gpg file */
memcpy(keyid, sha1 + 16, 4);
} else
*keyid = 0;
/* 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));
if (params.verbose > LOG_INFO) {
sprintf(str, "%x", __be32_to_cpup(keyid));
if (imaevm_params.verbose > LOG_INFO)
log_info("keyid: %s\n", str);
}
free(pkey);
X509_PUBKEY_free(pk);
}
static RSA *read_priv_key(const char *keyfile, const char *keypass)
static EVP_PKEY *read_priv_pkey(const char *keyfile, const char *keypass)
{
FILE *fp;
RSA *key;
EVP_PKEY *pkey;
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);
pkey = PEM_read_PrivateKey(fp, NULL, NULL, (void *)keypass);
if (!pkey) {
log_err("Failed to PEM_read_PrivateKey key file: %s\n",
keyfile);
output_openssl_errors();
}
fclose(fp);
return pkey;
}
static RSA *read_priv_key(const char *keyfile, const char *keypass)
{
EVP_PKEY *pkey;
RSA *key;
pkey = read_priv_pkey(keyfile, keypass);
if (!pkey)
return NULL;
key = EVP_PKEY_get1_RSA(pkey);
EVP_PKEY_free(pkey);
if (!key) {
log_err("read_priv_key: unsupported key type\n");
output_openssl_errors();
return NULL;
}
return key;
}
@ -737,7 +794,8 @@ static int get_hash_algo_v1(const char *algo)
return -1;
}
int sign_hash_v1(const char *hashalgo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig)
static int sign_hash_v1(const char *hashalgo, const unsigned char *hash,
int size, const char *keyfile, unsigned char *sig)
{
int len = -1, hashalgo_idx;
SHA_CTX ctx;
@ -768,10 +826,10 @@ int sign_hash_v1(const char *hashalgo, const unsigned char *hash, int size, cons
return -1;
}
log_info("hash: ");
log_info("hash(%s): ", hashalgo);
log_dump(hash, size);
key = read_priv_key(keyfile, params.keypass);
key = read_priv_key(keyfile, imaevm_params.keypass);
if (!key)
return -1;
@ -804,6 +862,7 @@ int sign_hash_v1(const char *hashalgo, const unsigned char *hash, int size, cons
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);
output_openssl_errors();
goto out;
}
@ -817,14 +876,22 @@ out:
return len;
}
int sign_hash_v2(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig)
/*
* @sig is assumed to be of (MAX_SIGNATURE_SIZE - 1) size
* Return: -1 signing error, >0 length of signature
*/
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;
int len = -1;
RSA *key;
EVP_PKEY *pkey;
char name[20];
unsigned char *buf;
const struct RSA_ASN1_template *asn1;
EVP_PKEY_CTX *ctx = NULL;
const EVP_MD *md;
size_t sigsize;
const char *st;
uint32_t keyid;
if (!hash) {
log_err("sign_hash_v2: hash is null\n");
@ -846,43 +913,56 @@ int sign_hash_v2(const char *algo, const unsigned char *hash, int size, const ch
return -1;
}
log_info("hash: ");
log_info("hash(%s): ", imaevm_params.hash_algo);
log_dump(hash, size);
key = read_priv_key(keyfile, params.keypass);
if (!key)
pkey = read_priv_pkey(keyfile, imaevm_params.keypass);
if (!pkey)
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;
hdr->hash_algo = imaevm_get_hash_algo(algo);
if (hdr->hash_algo == -1) {
log_err("sign_hash_v2: hash algo is unknown: %s\n", algo);
return -1;
}
calc_keyid_v2(&keyid, name, pkey);
hdr->keyid = keyid;
st = "EVP_PKEY_CTX_new";
if (!(ctx = EVP_PKEY_CTX_new(pkey, NULL)))
goto err;
st = "EVP_PKEY_sign_init";
if (!EVP_PKEY_sign_init(ctx))
goto err;
st = "EVP_get_digestbyname";
if (!(md = EVP_get_digestbyname(imaevm_params.hash_algo)))
goto err;
st = "EVP_PKEY_CTX_set_signature_md";
if (!EVP_PKEY_CTX_set_signature_md(ctx, md))
goto err;
st = "EVP_PKEY_sign";
sigsize = MAX_SIGNATURE_SIZE - sizeof(struct signature_v2_hdr) - 1;
if (!EVP_PKEY_sign(ctx, hdr->sig, &sigsize, hash, size))
goto err;
len = (int)sigsize;
/* 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);
err:
if (len == -1) {
log_err("sign_hash_v2: signing failed: (%s) in %s\n",
ERR_reason_error_string(ERR_peek_error()), st);
output_openssl_errors();
}
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(pkey);
return len;
}
@ -890,14 +970,23 @@ out:
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;
imaevm_params.keypass = keypass;
return params.x509 ? sign_hash_v2(hashalgo, hash, size, keyfile, sig) :
sign_hash_v1(hashalgo, hash, size, keyfile, sig);
return imaevm_params.x509 ?
sign_hash_v2(hashalgo, hash, size, keyfile, sig) :
sign_hash_v1(hashalgo, hash, size, keyfile, sig);
}
static void libinit()
{
#if OPENSSL_VERSION_NUMBER < 0x10100000
OpenSSL_add_all_algorithms();
OPENSSL_add_all_algorithms_conf();
#else
OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS |
OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
ERR_load_crypto_strings();
#endif
}