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

81 Commits
v0.9 ... v1.2

Author SHA1 Message Date
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
c860e0d9bb ima-evm-utils: Release version 1.1
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
2018-02-15 08:02:19 -05:00
360655f059 Support different levels of output for "ima_measurement"
Instead of always displaying the entire measurement list, the default
behavior is just to return an error.  Verbose (-v) displays the key ids
used in validating the measurement list, the PCR aggregate and TPM PCR
values.  Verbose+ (-v -v) also displays the measurement list.

Signed-of-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
2018-02-06 07:50:31 -05:00
057efc397d Include the file name in "ima_measurement" verification result
When displaying the measurement list, include the filename in the result.

Signed-of-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
2018-02-06 07:50:01 -05:00
c2ef2aabe2 ima-evm-utils: sysfs pathname change
Commit 313d21e "tpm: device class for tpm" moved the TPM sysfs location
from /sys/class/misc/tpmX/device/ to /sys/class/tpm/tpmX/device/.

Mimi Zohar <zohar@linux.vnet.ibm.com>
2018-01-29 14:24:03 -05:00
81010f0d87 ima-evm-utils: Add backward compatible support for openssl 1.1
Openssl 1.1 is really annoying in that it made certain objects opaque
and added accessors for the necessary componenets, but these accessors
often don't exist in 1.0 and before, so there's no way to create clean
code that will compile with both 1.0 and 1.1; instead you have to
compiled with both code bases to make sure everything is working).

The other problem is that since the structures are opaque, their size
isn't known, so having a structure declared as a variable is no longer
possible.

This change switches all uses of EVP_MD_CTX to be pointers initialised
with the correct EVP_MD_CTX_new() (not available in 1.0), does the
same for HMAC_CTX, and uses the 1.1 only primitve RSA_get0_key() to
extract the public modulus and exponent from an RSA key.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Tested-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
2018-01-28 13:47:54 -05:00
6921833477 ima-evm-utils: add support for validating multiple pcrs
The IMA measurement list may contain records for different PCRs.  This
patch walks the measurement list, calculating a PCR aggregate value for
each PCR.

Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
2018-01-28 10:02:18 -05:00
1a69e42ac1 ima-evm-utils: verify the measurement list signature based on the list digest
Instead of verifying file signatures included in the measurement list,
by calculating the local file hash, verify the file signature based on the
digest contained in the measurement list.

This patch defines a new option named "--list".

Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
2018-01-28 09:57:34 -05:00
9c79b7de72 ima-evm-utils: support verifying the measurement list using multiple keys
On a running system, different software packages might be signed by
different parties.  Support verifying signatures in the measurement
list using multiple public keys(eg.  -k "key1, key2, ...").

Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
2018-01-28 09:57:34 -05:00
838b08b449 ima-evm-utils: fix spelling error
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
2018-01-28 09:57:34 -05:00
ba92e44719 ima-evm-utils: fix "ima_measurement" template fields length
The template data field length is uint32_t, not uint8_t.

Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
2018-01-28 08:37:08 -05:00
4928548d9d Add support for portable EVM format
Add a --portable argument that generates EVM signatures without using
the inode number and generation or fs UUID.

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

Changelog:
- immutable and portable flags are exclusive, prevent enabling both.
2017-11-16 15:02:40 -05:00
233dedffe9 Write out .sig file as security.ima xattr
To write the .sig file as security.ima xattr using setfattr first
requires converting the .sig file from binary to ascii-hex.  Although
this conversion can be done using hexdump, it is unnecessary when
calling setxattr.  This patch defines a new command called
"ima_setxattr", which calls lsetxattr() to write the .sig file as
the security.ima xattr.

Changelog:
- remove unnecessary copy
- fixed --sigfile option

Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
2016-03-06 07:55:04 -05:00
3e2a67bdb0 script to build static evmctl version
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@huawei.com>
2015-10-01 22:42:45 +03:00
839a674580 Supply file attributes values on command line
Can be used by Android filesystem image creation tool.

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@huawei.com>
2015-09-20 23:28:20 +03:00
e55d286ad6 Use single flag to indicate 'no'flag
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@huawei.com>
2015-09-20 23:15:32 +03:00
28d99354de Use byte range values
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@huawei.com>
2015-09-20 22:22:06 +03:00
ea5ccdf38f Newer kernels requires at least 64 byte keys
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@huawei.com>
2015-09-20 22:22:00 +03:00
b0d13ba557 calc_evm_hmac/hash: avoid fd leak when ioctl fails
When opening the file succeeds but ioctl() then fails, the file must
be closed before returning.

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2015-09-09 23:02:01 +03:00
199311e6a5 ima_verify: ignore -n flag
"evmutil ima_verify -n <some file>" disabled using xattrs without enabling
using a signature file, resulting in the use of uninitialized memory in
ima_verify_signature() and thus unpredictable results.

Such a mode of operation makes no sense, so interpret -n as
documented ("print result to stdout instead of setting xattr") and ignore it
during ima_verify. Instead, switch between the two verification modes only
via the global sigfile variable.

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2015-09-09 22:50:45 +03:00
453d3db8a5 tpm_pcr_read: close file when returning early
When return from inside the for() loop, the open file was not
closed.

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2015-09-09 22:50:35 +03:00
fa0b30b15e add_dir_hash: fix DIR leak in case of failure
When bailing out of the function due to EVP_DigestUpdate()
failing, the DIR resources allocated with opendir() were
not freed.

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2015-09-09 22:50:18 +03:00
4b56112c09 Release version 1.0
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@huawei.com>
2015-07-30 21:28:53 +03:00
9c8a67a209 Prompt for the password
Supplying the password on the command line is not safe.  This patch
adds support for prompting the user to enter the password.  At some
point, supplying the pasword on the command line should be deprecated.

Prior to this patch, the password could be specified with a blank in
between the '-p' option and the password.  With this patch, the
password now must be adjacent

Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
2015-07-28 21:42:35 +03:00
96e55082c2 Must use 'const char*'
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@huawei.com>
2015-07-24 22:51:39 +03:00
6a712b3b38 Add support for passing the private key password to sign_hash()
evmctl defines the "--pass | -p" command line option for providing
the private key's password.  The password is then stored in a global
variable accessible by the sign_hash_XXXX() functions.

This patch modifies the arguments to the library sign_hash()
function to include the password, allowing callers to specify the
private key password.

Changelog:
- add library init to call OpenSSL_add_all_algorithms

Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
2015-07-24 22:51:27 +03:00
17f49a1881 Add "ima_clear" command to remove IMA/EVM xattrs
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-10-29 21:38:03 +02:00
4d7d2c71a5 Define common function for recursive scanning
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-10-29 21:31:58 +02:00
92033dc404 Produce immutable EVM signature
'evmctl sign -i <file>' generates immutable EVM signature.

Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-10-29 13:00:43 +02:00
f805d4d0fe Fix typo
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-10-29 12:33:58 +02:00
8558dc5250 Add recursive hashing
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
2014-10-02 19:22:19 +03:00
15 changed files with 1398 additions and 533 deletions

1
.gitignore vendored
View File

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

View File

@ -1,3 +1,60 @@
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
* Support the new openssl 1.1 api
* Support for validating multiple pcrs
* Verify the measurement list signature based on the list digest
* Verify the "ima-sig" measurement list using multiple keys
* Fixed parsing the measurement template data field length
* Portable & immutable EVM signatures (new format)
* Multiple fixes that have been lingering in the next branch. Some
are for experimental features that are not yet supported in the
kernel.
2014-07-30 Dmitry Kasatkin <dmitry.kasatkin@huawei.com>
version 1.0
* Recursive hashing
* Immutable EVM signatures (experimental)
* Command 'ima_clear' to remove xattrs
* Support for passing password to the library
* Support for asking password safely from the user
2014-09-23 Dmitry Kasatkin <d.kasatkin@samsung.com> 2014-09-23 Dmitry Kasatkin <d.kasatkin@samsung.com>
version 0.9 version 0.9

View File

@ -23,9 +23,6 @@ rpm: $(tarname)
cp $(tarname) $(SRCS)/ cp $(tarname) $(SRCS)/
rpmbuild -ba --nodeps $(SPEC) rpmbuild -ba --nodeps $(SPEC)
# requires asciidoc, xslproc, docbook-xsl
MANPAGE_DOCBOOK_XSL = /usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl
evmctl.1.html: README evmctl.1.html: README
@asciidoc -o $@ $< @asciidoc -o $@ $<

14
README
View File

@ -26,12 +26,12 @@ COMMANDS
--version --version
help <command> help <command>
import [--rsa] pubkey keyring import [--rsa] pubkey keyring
sign [-r] [--imahash | --imasig ] [--key key] [--pass password] file sign [-r] [--imahash | --imasig ] [--portable] [--key key] [--pass password] file
verify file verify file
ima_sign [--sigfile] [--key key] [--pass password] file ima_sign [--sigfile] [--key key] [--pass password] file
ima_verify file ima_verify file
ima_hash file ima_hash file
ima_measurement file ima_measurement [--key "key1, key2, ..."] [--list] file
ima_fix [-t fdsxm] path ima_fix [-t fdsxm] path
sign_hash [--key key] [--pass password] sign_hash [--key key] [--pass password]
hmac [--imahash | --imasig ] file hmac [--imahash | --imasig ] file
@ -44,8 +44,10 @@ OPTIONS
-s, --imasig make IMA signature -s, --imasig make IMA signature
-d, --imahash make IMA hash -d, --imahash make IMA hash
-f, --sigfile store IMA signature in .sig file instead of xattr -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 --rsa use RSA key type and signing scheme v1
-k, --key path to signing key (default: /etc/keys/{privkey,pubkey}_evm.pem) -k, --key path to signing key (default: /etc/keys/{privkey,pubkey}_evm.pem)
-o, --portable generate portable EVM signatures
-p, --pass password for encrypted signing key -p, --pass password for encrypted signing key
-r, --recursive recurse into directories (sign) -r, --recursive recurse into directories (sign)
-t, --type file types to fix 'fdsxm' (f: file, d: directory, s: block/char/symlink) -t, --type file types to fix 'fdsxm' (f: file, d: directory, s: block/char/symlink)
@ -56,6 +58,7 @@ OPTIONS
--smack use extra SMACK xattrs for EVM --smack use extra SMACK xattrs for EVM
--m32 force EVM hmac/signature for 32 bit target system --m32 force EVM hmac/signature for 32 bit target system
--m64 force EVM hmac/signature for 64 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 -v increase verbosity level
-h, --help display this help and exit -h, --help display this help and exit
@ -95,7 +98,8 @@ Kernel configuration option CONFIG_EVM_ATTR_FSUUID controls whether to include
filesystem UUID into HMAC and enabled by default. Therefore evmctl also includes filesystem UUID into HMAC and enabled by default. Therefore evmctl also includes
fsuuid by default. Providing '--uuid' option without parameter allows to disable fsuuid by default. Providing '--uuid' option without parameter allows to disable
usage of fs uuid. Providing '--uuid=UUID' option with parameter allows to use usage of fs uuid. Providing '--uuid=UUID' option with parameter allows to use
custom UUID. custom UUID. Providing the '--portable' option will disable usage of the fs uuid
and also the inode number and generation.
Kernel configuration option CONFIG_EVM_EXTRA_SMACK_XATTRS controls whether to Kernel configuration option CONFIG_EVM_EXTRA_SMACK_XATTRS controls whether to
include additional SMACK extended attributes into HMAC. They are following: include additional SMACK extended attributes into HMAC. They are following:
@ -142,7 +146,7 @@ EVM encrypted key is used for EVM HMAC calculation:
keyctl pipe `keyctl search @u user kmk` > /etc/keys/kmk keyctl pipe `keyctl search @u user kmk` > /etc/keys/kmk
# create the EVM encrypted key # create the EVM encrypted key
keyctl add encrypted evm-key "new user:kmk 32" @u keyctl add encrypted evm-key "new user:kmk 64" @u
keyctl pipe `keyctl search @u encrypted evm-key` >/etc/keys/evm-key keyctl pipe `keyctl search @u encrypted evm-key` >/etc/keys/evm-key
@ -403,7 +407,7 @@ When using plain RSA public keys in PEM format, use 'evmctl import --rsa' for im
Latest version of keyctl allows to import X509 public key certificates: Latest version of keyctl allows to import X509 public key certificates:
cat /etc/keys/x509_ima.der | keyctl padd asymmetric '' @ima_id cat /etc/keys/x509_ima.der | keyctl padd asymmetric '' $ima_id
FILES FILES

4
build-static.sh Executable file
View File

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

View File

@ -1,12 +1,13 @@
# autoconf script # autoconf script
AC_PREREQ([2.65]) AC_PREREQ([2.65])
AC_INIT(ima-evm-utils, 0.9, d.kasatkin@samsung.com) AC_INIT(ima-evm-utils, 1.2, zohar@linux.ibm.com)
AM_INIT_AUTOMAKE AM_INIT_AUTOMAKE
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AC_CANONICAL_HOST AC_CANONICAL_HOST
AC_USE_SYSTEM_EXTENSIONS
# Checks for programs. # Checks for programs.
AC_PROG_CC AC_PROG_CC
@ -24,15 +25,30 @@ LT_INIT
# Checks for header files. # Checks for header files.
AC_HEADER_STDC AC_HEADER_STDC
PKG_CHECK_MODULES(OPENSSL, [ openssl >= 0.9.8 ]) PKG_CHECK_MODULES(LIBCRYPTO, [libcrypto >= 0.9.8 ])
AC_SUBST(OPENSSL_CFLAGS) AC_SUBST(KERNEL_HEADERS)
AC_SUBST(OPENSSL_LIBS)
AC_CHECK_HEADER(unistd.h) AC_CHECK_HEADER(unistd.h)
AC_CHECK_HEADERS(openssl/conf.h) AC_CHECK_HEADERS(openssl/conf.h)
AC_CHECK_HEADERS(attr/xattr.h, , [AC_MSG_ERROR([attr/xattr.h header not found. You need the libattr development package.])]) AC_CHECK_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_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 #debug support - yes for a while
PKG_ARG_ENABLE(debug, "yes", DEBUG, [Enable Debug support]) PKG_ARG_ENABLE(debug, "yes", DEBUG, [Enable Debug support])
if test $pkg_cv_enable_debug = yes; then if test $pkg_cv_enable_debug = yes; then
@ -41,6 +57,8 @@ else
CFLAGS="$CFLAGS -Wall -Wstrict-prototypes -pipe -fomit-frame-pointer" CFLAGS="$CFLAGS -Wall -Wstrict-prototypes -pipe -fomit-frame-pointer"
fi fi
EVMCTL_MANPAGE_DOCBOOK_XSL
# for gcov # for gcov
#CFLAGS="$CFLAGS -Wall -fprofile-arcs -ftest-coverage" #CFLAGS="$CFLAGS -Wall -fprofile-arcs -ftest-coverage"
#CXXFLAGS="$CXXFLAGS -Wall -fprofile-arcs -ftest-coverage" #CXXFLAGS="$CXXFLAGS -Wall -fprofile-arcs -ftest-coverage"
@ -56,7 +74,8 @@ AC_OUTPUT
# Give some feedback # Give some feedback
echo echo
echo echo
echo "Configuration:" echo "Configuration:"
echo " debug: $pkg_cv_enable_debug" echo " debug: $pkg_cv_enable_debug"
echo " openssl-conf: $enable_openssl_conf"
echo " tsspcrread: $TSSPCRREAD"
echo 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 Name: ima-evm-utils
Version: 0.9 Version: 1.2
Release: 1%{?dist} Release: 1%{?dist}
Summary: ima-evm-utils - IMA/EVM control utility Summary: ima-evm-utils - IMA/EVM control utility
Group: System/Libraries Group: System/Libraries
@ -11,7 +11,6 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
BuildRequires: autoconf BuildRequires: autoconf
BuildRequires: automake BuildRequires: automake
BuildRequires: openssl-devel BuildRequires: openssl-devel
BuildRequires: libattr-devel
BuildRequires: keyutils-libs-devel BuildRequires: keyutils-libs-devel
%description %description

View File

@ -11,7 +11,6 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
BuildRequires: autoconf BuildRequires: autoconf
BuildRequires: automake BuildRequires: automake
BuildRequires: openssl-devel BuildRequires: openssl-devel
BuildRequires: libattr-devel
BuildRequires: keyutils-libs-devel BuildRequires: keyutils-libs-devel
%description %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 lib_LTLIBRARIES = libimaevm.la
libimaevm_la_SOURCES = libimaevm.c libimaevm_la_SOURCES = libimaevm.c
libimaevm_la_CPPFLAGS = $(OPENSSL_CFLAGS) libimaevm_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCRYPTO_CFLAGS)
# current[:revision[:age]] # current[:revision[:age]]
# result: [current-age].age.revision # result: [current-age].age.revision
libimaevm_la_LDFLAGS = -version-info 0:0:0 libimaevm_la_LDFLAGS = -version-info 1:0:0
libimaevm_la_LIBADD = $(OPENSSL_LIBS) libimaevm_la_LIBADD = $(LIBCRYPTO_LIBS)
include_HEADERS = imaevm.h 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 bin_PROGRAMS = evmctl
evmctl_SOURCES = evmctl.c evmctl_SOURCES = evmctl.c
evmctl_CPPFLAGS = $(OPENSSL_CFLAGS) evmctl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCRYPTO_CFLAGS)
evmctl_LDFLAGS = $(LDFLAGS_READLINE) 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@ DISTCLEANFILES = @DISTCLEANFILES@

File diff suppressed because it is too large Load Diff

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> #include <openssl/rsa.h>
#ifdef USE_FPRINTF #ifdef USE_FPRINTF
#define do_log(level, fmt, args...) ({ if (level <= params.verbose) fprintf(stderr, fmt, ##args); }) #define do_log(level, fmt, args...) \
#define do_log_dump(level, p, len, cr) ({ if (level <= params.verbose) do_dump(stderr, p, len, cr); }) ({ 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 #else
#define do_log(level, fmt, args...) syslog(level, fmt, ##args) #define do_log(level, fmt, args...) syslog(level, fmt, ##args)
#define do_log_dump(level, p, len, cr) #define do_log_dump(level, p, len, cr)
@ -75,6 +77,9 @@
#define DATA_SIZE 4096 #define DATA_SIZE 4096
#define SHA1_HASH_LEN 20 #define SHA1_HASH_LEN 20
#define MAX_DIGEST_SIZE 64
#define MAX_SIGNATURE_SIZE 1024
#define __packed __attribute__((packed)) #define __packed __attribute__((packed))
enum evm_ima_xattr_type { enum evm_ima_xattr_type {
@ -82,6 +87,7 @@ enum evm_ima_xattr_type {
EVM_XATTR_HMAC, EVM_XATTR_HMAC,
EVM_IMA_XATTR_DIGSIG, EVM_IMA_XATTR_DIGSIG,
IMA_XATTR_DIGEST_NG, IMA_XATTR_DIGEST_NG,
EVM_XATTR_PORTABLE_DIGSIG,
}; };
struct h_misc { struct h_misc {
@ -108,6 +114,12 @@ struct h_misc_64 {
unsigned short mode; unsigned short mode;
}; };
struct h_misc_digsig {
uid_t uid;
gid_t gid;
unsigned short mode;
};
enum pubkey_algo { enum pubkey_algo {
PUBKEY_ALGO_RSA, PUBKEY_ALGO_RSA,
PUBKEY_ALGO_MAX, PUBKEY_ALGO_MAX,
@ -142,6 +154,7 @@ struct signature_hdr {
char mpi[0]; char mpi[0];
} __packed; } __packed;
/* reflect enum hash_algo from include/uapi/linux/hash_info.h */
enum pkey_hash_algo { enum pkey_hash_algo {
PKEY_HASH_MD4, PKEY_HASH_MD4,
PKEY_HASH_MD5, PKEY_HASH_MD5,
@ -151,6 +164,18 @@ enum pkey_hash_algo {
PKEY_HASH_SHA384, PKEY_HASH_SHA384,
PKEY_HASH_SHA512, PKEY_HASH_SHA512,
PKEY_HASH_SHA224, 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 PKEY_HASH__LAST
}; };
@ -165,15 +190,12 @@ struct signature_v2_hdr {
uint8_t sig[0]; /* signature payload */ uint8_t sig[0]; /* signature payload */
} __packed; } __packed;
struct libimaevm_params {
typedef int (*verify_hash_fn_t)(const unsigned char *hash, int size, unsigned char *sig, int siglen, const char *keyfile);
struct libevm_params {
int verbose; int verbose;
int x509; int x509;
const char *hash_algo; const char *hash_algo;
char *keyfile; const char *keyfile;
char *keypass; const char *keypass;
}; };
struct RSA_ASN1_template { struct RSA_ASN1_template {
@ -181,22 +203,25 @@ struct RSA_ASN1_template {
size_t size; size_t size;
}; };
extern const struct RSA_ASN1_template RSA_ASN1_templates[PKEY_HASH__LAST]; #define NUM_PCRS 20
extern struct libevm_params params; #define DEFAULT_PCR 10
void do_dump(FILE *fp, const void *ptr, int len, bool cr); extern struct libimaevm_params imaevm_params;
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 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); 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_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 key2bin(RSA *key, unsigned char *pub);
int sign_hash(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig); int sign_hash(const char *algo, const unsigned char *hash, int size, const char *keyfile, const char *keypass, unsigned char *sig);
int verify_hash(const unsigned char *hash, int size, unsigned char *sig, int siglen); int verify_hash(const char *file, const unsigned char *hash, int size, unsigned char *sig, int siglen);
int ima_verify_signature(const char *file, unsigned char *sig, int siglen); int ima_verify_signature(const char *file, unsigned char *sig, int siglen, unsigned char *digest, int digestlen);
void init_public_keys(const char *keyfiles);
#endif #endif

View File

@ -49,14 +49,20 @@
#include <dirent.h> #include <dirent.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <assert.h>
#include <ctype.h>
#include <openssl/crypto.h>
#include <openssl/pem.h> #include <openssl/pem.h>
#include <openssl/evp.h> #include <openssl/evp.h>
#include <openssl/x509.h> #include <openssl/x509.h>
#include <openssl/err.h>
#include "imaevm.h" #include "imaevm.h"
#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_MD4] = "md4",
[PKEY_HASH_MD5] = "md5", [PKEY_HASH_MD5] = "md5",
[PKEY_HASH_SHA1] = "sha1", [PKEY_HASH_SHA1] = "sha1",
@ -65,72 +71,25 @@ const char *const pkey_hash_algo[PKEY_HASH__LAST] = {
[PKEY_HASH_SHA384] = "sha384", [PKEY_HASH_SHA384] = "sha384",
[PKEY_HASH_SHA512] = "sha512", [PKEY_HASH_SHA512] = "sha512",
[PKEY_HASH_SHA224] = "sha224", [PKEY_HASH_SHA224] = "sha224",
[PKEY_HASH_STREEBOG_256] = "md_gost12_256",
[PKEY_HASH_STREEBOG_512] = "md_gost12_512",
}; };
/* /* Names that are primary for the kernel. */
* Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2]. static const char *const pkey_hash_algo_kern[PKEY_HASH__LAST] = {
*/ [PKEY_HASH_STREEBOG_256] = "streebog256",
static const uint8_t RSA_digest_info_MD5[] = { [PKEY_HASH_STREEBOG_512] = "streebog512",
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[] = { struct libimaevm_params imaevm_params = {
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, .verbose = LOG_INFO - 1,
.x509 = 1, .x509 = 1,
.hash_algo = "sha1", .hash_algo = "sha1",
}; };
void do_dump(FILE *fp, const void *ptr, int len, bool cr) static void __attribute__ ((constructor)) libinit(void);
void imaevm_do_hexdump(FILE *fp, const void *ptr, int len, bool cr)
{ {
int i; int i;
uint8_t *data = (uint8_t *) ptr; uint8_t *data = (uint8_t *) ptr;
@ -141,25 +100,32 @@ void do_dump(FILE *fp, const void *ptr, int len, bool cr)
fprintf(fp, "\n"); 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; if (algo < PKEY_HASH__LAST)
/* Need to know the file length */ return pkey_hash_algo[algo];
stat(filename, &stats); if (algo < HASH_ALGO__LAST)
return (int)stats.st_size; 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; while (ERR_peek_error()) {
/* Need to know the file length */ char buf[256];
fstat(fd, &stats); /* buf must be at least 256 bytes long according to man */
return stats.st_size;
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) static int add_file_hash(const char *file, EVP_MD_CTX *ctx)
@ -168,6 +134,7 @@ static int add_file_hash(const char *file, EVP_MD_CTX *ctx)
int err = -1, bs = DATA_SIZE; int err = -1, bs = DATA_SIZE;
off_t size, len; off_t size, len;
FILE *fp; FILE *fp;
struct stat stats;
fp = fopen(file, "r"); fp = fopen(file, "r");
if (!fp) { if (!fp) {
@ -181,7 +148,12 @@ static int add_file_hash(const char *file, EVP_MD_CTX *ctx)
goto out; 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); len = MIN(size, bs);
if (!fread(data, len, 1, fp)) { if (!fread(data, len, 1, fp)) {
if (ferror(fp)) { if (ferror(fp)) {
@ -211,6 +183,7 @@ static int add_dir_hash(const char *file, EVP_MD_CTX *ctx)
DIR *dir; DIR *dir;
unsigned long long ino, off; unsigned long long ino, off;
unsigned int type; unsigned int type;
int result = 0;
dir = opendir(file); dir = opendir(file);
if (!dir) { if (!dir) {
@ -230,13 +203,15 @@ static int add_dir_hash(const char *file, EVP_MD_CTX *ctx)
err |= EVP_DigestUpdate(ctx, &type, sizeof(type)); err |= EVP_DigestUpdate(ctx, &type, sizeof(type));
if (!err) { if (!err) {
log_err("EVP_DigestUpdate() failed\n"); log_err("EVP_DigestUpdate() failed\n");
return 1; output_openssl_errors();
result = 1;
break;
} }
} }
closedir(dir); closedir(dir);
return 0; return result;
} }
static int add_link_hash(const char *path, EVP_MD_CTX *ctx) static int add_link_hash(const char *path, EVP_MD_CTX *ctx)
@ -266,67 +241,85 @@ int ima_calc_hash(const char *file, uint8_t *hash)
{ {
const EVP_MD *md; const EVP_MD *md;
struct stat st; struct stat st;
EVP_MD_CTX ctx; EVP_MD_CTX *pctx;
unsigned int mdlen; unsigned int mdlen;
int err; int err;
#if OPENSSL_VERSION_NUMBER < 0x10100000
EVP_MD_CTX ctx;
pctx = &ctx;
#else
pctx = EVP_MD_CTX_new();
#endif
/* Need to know the file length */ /* Need to know the file length */
err = lstat(file, &st); err = lstat(file, &st);
if (err < 0) { if (err < 0) {
log_err("Failed to stat: %s\n", file); log_err("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) { if (!md) {
log_err("EVP_get_digestbyname() failed\n"); log_err("EVP_get_digestbyname(%s) failed\n",
return 1; imaevm_params.hash_algo);
err = 1;
goto err;
} }
err = EVP_DigestInit(&ctx, md); err = EVP_DigestInit(pctx, md);
if (!err) { if (!err) {
log_err("EVP_DigestInit() failed\n"); log_err("EVP_DigestInit() failed\n");
return 1; err = 1;
goto err;
} }
switch (st.st_mode & S_IFMT) { switch (st.st_mode & S_IFMT) {
case S_IFREG: case S_IFREG:
err = add_file_hash(file, &ctx); err = add_file_hash(file, pctx);
break; break;
case S_IFDIR: case S_IFDIR:
err = add_dir_hash(file, &ctx); err = add_dir_hash(file, pctx);
break; break;
case S_IFLNK: case S_IFLNK:
err = add_link_hash(file, &ctx); err = add_link_hash(file, pctx);
break; break;
case S_IFIFO: case S_IFSOCK: case S_IFIFO: case S_IFSOCK:
case S_IFCHR: case S_IFBLK: case S_IFCHR: case S_IFBLK:
err = add_dev_hash(&st, &ctx); err = add_dev_hash(&st, pctx);
break; break;
default: default:
log_errno("Unsupported file type"); log_errno("Unsupported file type");
return -1; err = -1;
goto err;
} }
if (err) if (err)
return err; goto err;
err = EVP_DigestFinal(&ctx, hash, &mdlen); err = EVP_DigestFinal(pctx, hash, &mdlen);
if (!err) { if (!err) {
log_err("EVP_DigestFinal() failed\n"); log_err("EVP_DigestFinal() failed\n");
return 1; err = 1;
goto err;
} }
err = mdlen;
return 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; FILE *fp;
RSA *key = NULL;
X509 *crt = NULL;
EVP_PKEY *pkey = NULL; EVP_PKEY *pkey = NULL;
if (!keyfile)
return NULL;
fp = fopen(keyfile, "r"); fp = fopen(keyfile, "r");
if (!fp) { if (!fp) {
log_err("Failed to open keyfile: %s\n", keyfile); log_err("Failed to open keyfile: %s\n", keyfile);
@ -334,34 +327,54 @@ RSA *read_pub_key(const char *keyfile, int x509)
} }
if (x509) { if (x509) {
crt = d2i_X509_fp(fp, NULL); X509 *crt = d2i_X509_fp(fp, NULL);
if (!crt) { if (!crt) {
log_err("d2i_X509_fp() failed\n"); log_err("Failed to d2i_X509_fp key file: %s\n",
keyfile);
goto out; goto out;
} }
pkey = X509_extract_key(crt); pkey = X509_extract_key(crt);
X509_free(crt);
if (!pkey) { if (!pkey) {
log_err("X509_extract_key() failed\n"); log_err("Failed to X509_extract_key key file: %s\n",
keyfile);
goto out; goto out;
} }
key = EVP_PKEY_get1_RSA(pkey);
} else { } 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: out:
if (pkey) if (!pkey)
EVP_PKEY_free(pkey); output_openssl_errors();
if (crt)
X509_free(crt);
fclose(fp); 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; return key;
} }
int verify_hash_v1(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; int err, len;
SHA_CTX ctx; SHA_CTX ctx;
@ -370,7 +383,7 @@ int verify_hash_v1(const unsigned char *hash, int size, unsigned char *sig, int
unsigned char sighash[20]; unsigned char sighash[20];
struct signature_hdr *hdr = (struct signature_hdr *)sig; struct signature_hdr *hdr = (struct signature_hdr *)sig;
log_info("hash: "); log_info("hash-v1: ");
log_dump(hash, size); log_dump(hash, size);
key = read_pub_key(keyfile, 0); key = read_pub_key(keyfile, 0);
@ -387,76 +400,179 @@ int verify_hash_v1(const unsigned char *hash, int size, unsigned char *sig, int
err = RSA_public_decrypt(siglen - sizeof(*hdr) - 2, sig + sizeof(*hdr) + 2, out, key, RSA_PKCS1_PADDING); err = RSA_public_decrypt(siglen - sizeof(*hdr) - 2, sig + sizeof(*hdr) + 2, out, key, RSA_PKCS1_PADDING);
RSA_free(key); RSA_free(key);
if (err < 0) { if (err < 0) {
log_err("RSA_public_decrypt() failed: %d\n", err); log_err("%s: RSA_public_decrypt() failed: %d\n", file, err);
output_openssl_errors();
return 1; return 1;
} }
len = err; len = err;
if (len != sizeof(sighash) || memcmp(out, sighash, len) != 0) { if (len != sizeof(sighash) || memcmp(out, sighash, len) != 0) {
log_err("Verification failed: %d\n", err); log_err("%s: verification failed: %d\n", file, err);
return -1; return -1;
} else {
/*log_info("Verification is OK\n");*/
printf("Verification is OK\n");
} }
return 0; return 0;
} }
int verify_hash_v2(const unsigned char *hash, int size, unsigned char *sig, int siglen, const char *keyfile) struct public_key_entry {
struct public_key_entry *next;
uint32_t keyid;
char name[9];
EVP_PKEY *key;
};
static struct public_key_entry *public_keys = NULL;
static EVP_PKEY *find_keyid(uint32_t keyid)
{ {
int err, len; struct public_key_entry *entry, *tail = public_keys;
unsigned char out[1024]; int i = 1;
RSA *key;
struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
const struct RSA_ASN1_template *asn1;
log_info("hash: "); for (entry = public_keys; entry != NULL; entry = entry->next) {
log_dump(hash, size); if (entry->keyid == keyid)
return entry->key;
key = read_pub_key(keyfile, 1); i++;
if (!key) tail = entry;
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; /* add unknown keys to list */
entry = calloc(1, sizeof(struct public_key_entry));
asn1 = &RSA_ASN1_templates[hdr->hash_algo]; if (!entry) {
perror("calloc");
if (len < asn1->size || memcmp(out, asn1->data, asn1->size)) { return 0;
log_err("Verification failed: %d\n", err);
return -1;
} }
entry->keyid = keyid;
len -= asn1->size; if (tail)
tail->next = entry;
if (len != size || memcmp(out + asn1->size, hash, len)) { else
log_err("Verification failed: %d\n", err); public_keys = entry;
return -1; log_err("key %d: %x (unknown keyid)\n", i, __be32_to_cpup(&keyid));
}
/*log_info("Verification is OK\n");*/
printf("Verification is OK\n");
return 0; return 0;
} }
int get_hash_algo(const char *algo) void init_public_keys(const char *keyfiles)
{
struct public_key_entry *entry;
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)
break;
if ((*keyfile == '\0') || (*keyfile == ' ') ||
(*keyfile == '\t'))
continue;
entry = malloc(sizeof(struct public_key_entry));
if (!entry) {
perror("malloc");
break;
}
entry->key = read_pub_pkey(keyfile, 1);
if (!entry->key) {
free(entry);
continue;
}
calc_keyid_v2(&entry->keyid, entry->name, entry->key);
sprintf(entry->name, "%x", __be32_to_cpup(&entry->keyid));
log_info("key %d: %s %s\n", i++, entry->name, keyfile);
entry->next = public_keys;
public_keys = entry;
}
free(keyfiles_free);
}
/*
* 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 ret = -1;
EVP_PKEY *pkey, *pkey_free = NULL;
struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
EVP_PKEY_CTX *ctx;
const EVP_MD *md;
const char *st;
if (imaevm_params.verbose > LOG_INFO) {
log_info("hash(%s): ", imaevm_params.hash_algo);
log_dump(hash, size);
}
pkey = find_keyid(hdr->keyid);
if (!pkey) {
uint32_t keyid = hdr->keyid;
log_info("%s: verification failed: unknown keyid %x\n",
file, __be32_to_cpup(&keyid));
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;
}
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 imaevm_get_hash_algo(const char *algo)
{ {
int i; int i;
/* first iterate over builtin algorithms */
for (i = 0; i < PKEY_HASH__LAST; i++) 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 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) static int get_hash_algo_from_sig(unsigned char *sig)
@ -486,39 +602,31 @@ static int get_hash_algo_from_sig(unsigned char *sig)
return -1; return -1;
} }
int verify_hash(const unsigned char *hash, int size, unsigned char *sig, int siglen) int verify_hash(const char *file, 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 */ /* Get signature type from sig header */
if (sig[0] == DIGSIG_VERSION_1) { if (sig[0] == DIGSIG_VERSION_1) {
verify_hash = verify_hash_v1; const char *key = NULL;
/* Read pubkey from RSA key */ /* Read pubkey from RSA key */
x509 = 0; if (!imaevm_params.keyfile)
key = "/etc/keys/pubkey_evm.pem";
return verify_hash_v1(file, hash, size, sig, siglen, key);
} else if (sig[0] == DIGSIG_VERSION_2) { } else if (sig[0] == DIGSIG_VERSION_2) {
verify_hash = verify_hash_v2; return verify_hash_v2(file, hash, size, sig, siglen);
/* Read pubkey from x509 cert */
x509 = 1;
} else } else
return -1; 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) 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; int hashlen, sig_hash_algo;
if (sig[0] != 0x03) { if (sig[0] != 0x03) {
log_err("security.ima has no signature\n"); log_err("xattr ima has no signature\n");
return -1; return -1;
} }
@ -528,13 +636,21 @@ int ima_verify_signature(const char *file, unsigned char *sig, int siglen)
return -1; return -1;
} }
/* Use hash algorithm as retrieved from signature */ /* 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
* measurement list, not by calculating the local file digest.
*/
if (digestlen > 0)
return verify_hash(file, digest, digestlen, sig + 1, siglen - 1);
hashlen = ima_calc_hash(file, hash); hashlen = ima_calc_hash(file, hash);
if (hashlen <= 1) if (hashlen <= 1)
return hashlen; return hashlen;
assert(hashlen <= sizeof(hash));
return verify_hash(hash, hashlen, sig + 1, siglen - 1); return verify_hash(file, hash, hashlen, sig + 1, siglen - 1);
} }
/* /*
@ -544,6 +660,14 @@ int key2bin(RSA *key, unsigned char *pub)
{ {
int len, b, offset = 0; int len, b, offset = 0;
struct pubkey_hdr *pkh = (struct pubkey_hdr *)pub; struct pubkey_hdr *pkh = (struct pubkey_hdr *)pub;
const BIGNUM *n, *e;
#if OPENSSL_VERSION_NUMBER < 0x10100000
n = key->n;
e = key->e;
#else
RSA_get0_key(key, &n, &e, NULL);
#endif
/* add key header */ /* add key header */
pkh->version = 1; pkh->version = 1;
@ -553,18 +677,18 @@ int key2bin(RSA *key, unsigned char *pub)
offset += sizeof(*pkh); offset += sizeof(*pkh);
len = BN_num_bytes(key->n); len = BN_num_bytes(n);
b = BN_num_bits(key->n); b = BN_num_bits(n);
pub[offset++] = b >> 8; pub[offset++] = b >> 8;
pub[offset++] = b & 0xff; pub[offset++] = b & 0xff;
BN_bn2bin(key->n, &pub[offset]); BN_bn2bin(n, &pub[offset]);
offset += len; offset += len;
len = BN_num_bytes(key->e); len = BN_num_bytes(e);
b = BN_num_bits(key->e); b = BN_num_bits(e);
pub[offset++] = b >> 8; pub[offset++] = b >> 8;
pub[offset++] = b & 0xff; pub[offset++] = b & 0xff;
BN_bn2bin(key->e, &pub[offset]); BN_bn2bin(e, &pub[offset]);
offset += len; offset += len;
return offset; return offset;
@ -581,48 +705,79 @@ void calc_keyid_v1(uint8_t *keyid, char *str, const unsigned char *pkey, int len
memcpy(keyid, sha1 + 12, 8); memcpy(keyid, sha1 + 12, 8);
log_debug("keyid: "); log_debug("keyid: ");
log_debug_dump(keyid, 8); log_debug_dump(keyid, 8);
id = __be64_to_cpup((__be64 *) keyid); id = __be64_to_cpup((__be64 *) keyid);
sprintf(str, "%llX", (unsigned long long)id); sprintf(str, "%llX", (unsigned long long)id);
log_info("keyid: %s\n", str);
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]; X509_PUBKEY *pk = NULL;
unsigned char *pkey = NULL; const unsigned char *public_key = NULL;
int len; 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("keyid: ");
log_debug_dump(keyid, 4); log_debug_dump(keyid, 4);
sprintf(str, "%x", __be32_to_cpup(keyid)); sprintf(str, "%x", __be32_to_cpup(keyid));
log_info("keyid: %s\n", str);
free(pkey); if (imaevm_params.verbose > LOG_INFO)
log_info("keyid: %s\n", str);
X509_PUBKEY_free(pk);
} }
static RSA *read_priv_key(const char *keyfile, char *keypass) static EVP_PKEY *read_priv_pkey(const char *keyfile, const char *keypass)
{ {
FILE *fp; FILE *fp;
RSA *key; EVP_PKEY *pkey;
fp = fopen(keyfile, "r"); fp = fopen(keyfile, "r");
if (!fp) { if (!fp) {
log_err("Failed to open keyfile: %s\n", keyfile); log_err("Failed to open keyfile: %s\n", keyfile);
return NULL; return NULL;
} }
key = PEM_read_RSAPrivateKey(fp, NULL, NULL, keypass); pkey = PEM_read_PrivateKey(fp, NULL, NULL, (void *)keypass);
if (!key) if (!pkey) {
log_err("PEM_read_RSAPrivateKey() failed\n"); log_err("Failed to PEM_read_PrivateKey key file: %s\n",
keyfile);
output_openssl_errors();
}
fclose(fp); 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; return key;
} }
@ -637,7 +792,8 @@ static int get_hash_algo_v1(const char *algo)
return -1; 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; int len = -1, hashalgo_idx;
SHA_CTX ctx; SHA_CTX ctx;
@ -668,10 +824,10 @@ int sign_hash_v1(const char *hashalgo, const unsigned char *hash, int size, cons
return -1; return -1;
} }
log_info("hash: "); log_info("hash(%s): ", hashalgo);
log_dump(hash, size); log_dump(hash, size);
key = read_priv_key(keyfile, params.keypass); key = read_priv_key(keyfile, imaevm_params.keypass);
if (!key) if (!key)
return -1; return -1;
@ -704,6 +860,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); len = RSA_private_encrypt(sizeof(sighash), sighash, sig + sizeof(*hdr) + 2, key, RSA_PKCS1_PADDING);
if (len < 0) { if (len < 0) {
log_err("RSA_private_encrypt() failed: %d\n", len); log_err("RSA_private_encrypt() failed: %d\n", len);
output_openssl_errors();
goto out; goto out;
} }
@ -711,20 +868,28 @@ int sign_hash_v1(const char *hashalgo, const unsigned char *hash, int size, cons
blen = (uint16_t *) (sig + sizeof(*hdr)); blen = (uint16_t *) (sig + sizeof(*hdr));
*blen = __cpu_to_be16(len << 3); *blen = __cpu_to_be16(len << 3);
len += sizeof(*hdr) + 2; len += sizeof(*hdr) + 2;
log_info("evm/ima signature: %d bytes\n", len); log_info("evm/ima signature-v1: %d bytes\n", len);
out: out:
RSA_free(key); RSA_free(key);
return len; 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; struct signature_v2_hdr *hdr;
int len = -1; int len = -1;
RSA *key; EVP_PKEY *pkey;
char name[20]; char name[20];
unsigned char *buf; EVP_PKEY_CTX *ctx = NULL;
const struct RSA_ASN1_template *asn1; const EVP_MD *md;
size_t sigsize;
const char *st;
uint32_t keyid;
if (!hash) { if (!hash) {
log_err("sign_hash_v2: hash is null\n"); log_err("sign_hash_v2: hash is null\n");
@ -746,48 +911,73 @@ int sign_hash_v2(const char *algo, const unsigned char *hash, int size, const ch
return -1; return -1;
} }
log_info("hash: "); log_info("hash(%s): ", imaevm_params.hash_algo);
log_dump(hash, size); log_dump(hash, size);
key = read_priv_key(keyfile, params.keypass); pkey = read_priv_pkey(keyfile, imaevm_params.keypass);
if (!key) if (!pkey)
return -1; return -1;
hdr = (struct signature_v2_hdr *)sig; hdr = (struct signature_v2_hdr *)sig;
hdr->version = (uint8_t) DIGSIG_VERSION_2; hdr->version = (uint8_t) DIGSIG_VERSION_2;
hdr->hash_algo = get_hash_algo(algo); hdr->hash_algo = imaevm_get_hash_algo(algo);
if (hdr->hash_algo == -1) {
calc_keyid_v2(&hdr->keyid, name, key); log_err("sign_hash_v2: hash algo is unknown: %s\n", algo);
return -1;
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;
} }
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 */ /* we add bit length of the signature to make it gnupg compatible */
hdr->sig_size = __cpu_to_be16(len); hdr->sig_size = __cpu_to_be16(len);
len += sizeof(*hdr); len += sizeof(*hdr);
log_info("evm/ima signature: %d bytes\n", len); log_info("evm/ima signature: %d bytes\n", len);
out:
if (buf) err:
free(buf); if (len == -1) {
RSA_free(key); 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; return len;
} }
int sign_hash(const char *hashalgo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig)
int sign_hash(const char *hashalgo, const unsigned char *hash, int size, const char *keyfile, const char *keypass, unsigned char *sig)
{ {
return params.x509 ? sign_hash_v2(hashalgo, hash, size, keyfile, sig) : if (keypass)
sign_hash_v1(hashalgo, hash, size, keyfile, sig); imaevm_params.keypass = keypass;
return imaevm_params.x509 ?
sign_hash_v2(hashalgo, hash, size, keyfile, sig) :
sign_hash_v1(hashalgo, hash, size, keyfile, sig);
}
static void libinit()
{
OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS |
OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
ERR_load_crypto_strings();
} }