summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYauheni Kaliuta <yauheni.kaliuta@redhat.com>2019-02-01 22:20:02 +0200
committerLucas De Marchi <lucas.demarchi@intel.com>2019-02-04 13:51:27 -0800
commit391b4714b495183baefa9cb10ac8e1600c166a59 (patch)
treea674ab2034405859716de1e75b5d15f6f4be5de5
parentdec990483bcb5f36557ab34918256a2251a6cf25 (diff)
downloadkmod-391b4714b495183baefa9cb10ac8e1600c166a59.tar.gz
kmod-391b4714b495183baefa9cb10ac8e1600c166a59.tar.bz2
kmod-391b4714b495183baefa9cb10ac8e1600c166a59.zip
libkmod-signature: implement pkcs7 parsing with openssl
The patch adds data fetching from the PKCS#7 certificate using openssl library (which is used by scripts/sign-file.c in the linux kernel to sign modules). In general the certificate can contain many signatures, but since kmod (modinfo) supports only one signature at the moment, only first one is taken. With the current sign-file.c certificate doesn't contain signer key's fingerprint, so "serial number" is used for the key id. Signed-off-by: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
-rw-r--r--Makefile.am4
-rw-r--r--configure.ac11
-rw-r--r--libkmod/libkmod-internal.h3
-rw-r--r--libkmod/libkmod-module.c3
-rw-r--r--libkmod/libkmod-signature.c197
5 files changed, 213 insertions, 5 deletions
diff --git a/Makefile.am b/Makefile.am
index 1ab1db5..de1026f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -35,6 +35,8 @@ SED_PROCESS = \
-e 's,@liblzma_LIBS\@,${liblzma_LIBS},g' \
-e 's,@zlib_CFLAGS\@,${zlib_CFLAGS},g' \
-e 's,@zlib_LIBS\@,${zlib_LIBS},g' \
+ -e 's,@openssl_CFLAGS\@,${openssl_CFLAGS},g' \
+ -e 's,@openssl_LIBS\@,${openssl_LIBS},g' \
< $< > $@ || rm $@
%.pc: %.pc.in Makefile
@@ -87,7 +89,7 @@ libkmod_libkmod_la_DEPENDENCIES = \
${top_srcdir}/libkmod/libkmod.sym
libkmod_libkmod_la_LIBADD = \
shared/libshared.la \
- ${liblzma_LIBS} ${zlib_LIBS}
+ ${liblzma_LIBS} ${zlib_LIBS} ${openssl_LIBS}
noinst_LTLIBRARIES += libkmod/libkmod-internal.la
libkmod_libkmod_internal_la_SOURCES = $(libkmod_libkmod_la_SOURCES)
diff --git a/configure.ac b/configure.ac
index fbc7391..2e33380 100644
--- a/configure.ac
+++ b/configure.ac
@@ -106,6 +106,17 @@ AS_IF([test "x$with_zlib" != "xno"], [
])
CC_FEATURE_APPEND([with_features], [with_zlib], [ZLIB])
+AC_ARG_WITH([openssl],
+ AS_HELP_STRING([--with-openssl], [handle PKCS7 signatures @<:@default=disabled@:>@]),
+ [], [with_openssl=no])
+AS_IF([test "x$with_openssl" != "xno"], [
+ PKG_CHECK_MODULES([openssl], [openssl])
+ AC_DEFINE([ENABLE_OPENSSL], [1], [Enable openssl for modinfo.])
+], [
+ AC_MSG_NOTICE([openssl support not requested])
+])
+CC_FEATURE_APPEND([with_features], [with_openssl], [OPENSSL])
+
AC_ARG_WITH([bashcompletiondir],
AS_HELP_STRING([--with-bashcompletiondir=DIR], [Bash completions directory]),
[],
diff --git a/libkmod/libkmod-internal.h b/libkmod/libkmod-internal.h
index 346579c..a65ddd1 100644
--- a/libkmod/libkmod-internal.h
+++ b/libkmod/libkmod-internal.h
@@ -188,5 +188,8 @@ struct kmod_signature_info {
const char *algo, *hash_algo, *id_type;
const char *sig;
size_t sig_len;
+ void (*free)(void *);
+ void *private;
};
bool kmod_module_signature_info(const struct kmod_file *file, struct kmod_signature_info *sig_info) _must_check_ __attribute__((nonnull(1, 2)));
+void kmod_module_signature_info_free(struct kmod_signature_info *sig_info) __attribute__((nonnull));
diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c
index 889f264..bffe715 100644
--- a/libkmod/libkmod-module.c
+++ b/libkmod/libkmod-module.c
@@ -2357,6 +2357,9 @@ KMOD_EXPORT int kmod_module_get_info(const struct kmod_module *mod, struct kmod_
ret = count;
list_error:
+ /* aux structures freed in normal case also */
+ kmod_module_signature_info_free(&sig_info);
+
if (ret < 0) {
kmod_module_info_free_list(*list);
*list = NULL;
diff --git a/libkmod/libkmod-signature.c b/libkmod/libkmod-signature.c
index 429ffbd..48d0145 100644
--- a/libkmod/libkmod-signature.c
+++ b/libkmod/libkmod-signature.c
@@ -19,6 +19,10 @@
#include <endian.h>
#include <inttypes.h>
+#ifdef ENABLE_OPENSSL
+#include <openssl/cms.h>
+#include <openssl/ssl.h>
+#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -115,15 +119,194 @@ static bool fill_default(const char *mem, off_t size,
return true;
}
-static bool fill_unknown(const char *mem, off_t size,
- const struct module_signature *modsig, size_t sig_len,
- struct kmod_signature_info *sig_info)
+#ifdef ENABLE_OPENSSL
+
+struct pkcs7_private {
+ CMS_ContentInfo *cms;
+ unsigned char *key_id;
+ BIGNUM *sno;
+};
+
+static void pkcs7_free(void *s)
+{
+ struct kmod_signature_info *si = s;
+ struct pkcs7_private *pvt = si->private;
+
+ CMS_ContentInfo_free(pvt->cms);
+ BN_free(pvt->sno);
+ free(pvt->key_id);
+ free(pvt);
+ si->private = NULL;
+}
+
+static int obj_to_hash_algo(const ASN1_OBJECT *o)
+{
+ int nid;
+
+ nid = OBJ_obj2nid(o);
+ switch (nid) {
+ case NID_md4:
+ return PKEY_HASH_MD4;
+ case NID_md5:
+ return PKEY_HASH_MD5;
+ case NID_sha1:
+ return PKEY_HASH_SHA1;
+ case NID_ripemd160:
+ return PKEY_HASH_RIPE_MD_160;
+ case NID_sha256:
+ return PKEY_HASH_SHA256;
+ case NID_sha384:
+ return PKEY_HASH_SHA384;
+ case NID_sha512:
+ return PKEY_HASH_SHA512;
+ case NID_sha224:
+ return PKEY_HASH_SHA224;
+ default:
+ return -1;
+ }
+ return -1;
+}
+
+static const char *x509_name_to_str(X509_NAME *name)
+{
+ int i;
+ X509_NAME_ENTRY *e;
+ ASN1_STRING *d;
+ ASN1_OBJECT *o;
+ int nid = -1;
+ const char *str;
+
+ for (i = 0; i < X509_NAME_entry_count(name); i++) {
+ e = X509_NAME_get_entry(name, i);
+ o = X509_NAME_ENTRY_get_object(e);
+ nid = OBJ_obj2nid(o);
+ if (nid == NID_commonName)
+ break;
+ }
+ if (nid == -1)
+ return NULL;
+
+ d = X509_NAME_ENTRY_get_data(e);
+ str = (const char *)ASN1_STRING_get0_data(d);
+
+ return str;
+}
+
+static bool fill_pkcs7(const char *mem, off_t size,
+ const struct module_signature *modsig, size_t sig_len,
+ struct kmod_signature_info *sig_info)
+{
+ const char *pkcs7_raw;
+ CMS_ContentInfo *cms;
+ STACK_OF(CMS_SignerInfo) *sis;
+ CMS_SignerInfo *si;
+ int rc;
+ ASN1_OCTET_STRING *key_id;
+ X509_NAME *issuer;
+ ASN1_INTEGER *sno;
+ ASN1_OCTET_STRING *sig;
+ BIGNUM *sno_bn;
+ X509_ALGOR *dig_alg;
+ X509_ALGOR *sig_alg;
+ const ASN1_OBJECT *o;
+ BIO *in;
+ int len;
+ unsigned char *key_id_str;
+ struct pkcs7_private *pvt;
+ const char *issuer_str;
+
+ size -= sig_len;
+ pkcs7_raw = mem + size;
+
+ in = BIO_new_mem_buf(pkcs7_raw, sig_len);
+
+ cms = d2i_CMS_bio(in, NULL);
+ if (cms == NULL) {
+ BIO_free(in);
+ return false;
+ }
+
+ BIO_free(in);
+
+ sis = CMS_get0_SignerInfos(cms);
+ if (sis == NULL)
+ goto err;
+
+ si = sk_CMS_SignerInfo_value(sis, 0);
+ if (si == NULL)
+ goto err;
+
+ rc = CMS_SignerInfo_get0_signer_id(si, &key_id, &issuer, &sno);
+ if (rc == 0)
+ goto err;
+
+ sig = CMS_SignerInfo_get0_signature(si);
+ if (sig == NULL)
+ goto err;
+
+ CMS_SignerInfo_get0_algs(si, NULL, NULL, &dig_alg, &sig_alg);
+
+ sig_info->sig = (const char *)ASN1_STRING_get0_data(sig);
+ sig_info->sig_len = ASN1_STRING_length(sig);
+
+ sno_bn = ASN1_INTEGER_to_BN(sno, NULL);
+ if (sno_bn == NULL)
+ goto err;
+
+ len = BN_num_bytes(sno_bn);
+ key_id_str = malloc(len);
+ if (key_id_str == NULL)
+ goto err2;
+ BN_bn2bin(sno_bn, key_id_str);
+
+ sig_info->key_id = (const char *)key_id_str;
+ sig_info->key_id_len = len;
+
+ issuer_str = x509_name_to_str(issuer);
+ if (issuer_str != NULL) {
+ sig_info->signer = issuer_str;
+ sig_info->signer_len = strlen(issuer_str);
+ }
+
+ X509_ALGOR_get0(&o, NULL, NULL, dig_alg);
+
+ sig_info->hash_algo = pkey_hash_algo[obj_to_hash_algo(o)];
+ sig_info->id_type = pkey_id_type[modsig->id_type];
+
+ pvt = malloc(sizeof(*pvt));
+ if (pvt == NULL)
+ goto err3;
+
+ pvt->cms = cms;
+ pvt->key_id = key_id_str;
+ pvt->sno = sno_bn;
+ sig_info->private = pvt;
+
+ sig_info->free = pkcs7_free;
+
+ return true;
+err3:
+ free(key_id_str);
+err2:
+ BN_free(sno_bn);
+err:
+ CMS_ContentInfo_free(cms);
+ return false;
+}
+
+#else /* ENABLE OPENSSL */
+
+static bool fill_pkcs7(const char *mem, off_t size,
+ const struct module_signature *modsig, size_t sig_len,
+ struct kmod_signature_info *sig_info)
{
sig_info->hash_algo = "unknown";
sig_info->id_type = pkey_id_type[modsig->id_type];
return true;
}
+#endif /* ENABLE OPENSSL */
+
#define SIG_MAGIC "~Module signature appended~\n"
/*
@@ -167,8 +350,14 @@ bool kmod_module_signature_info(const struct kmod_file *file, struct kmod_signat
switch (modsig->id_type) {
case PKEY_ID_PKCS7:
- return fill_unknown(mem, size, modsig, sig_len, sig_info);
+ return fill_pkcs7(mem, size, modsig, sig_len, sig_info);
default:
return fill_default(mem, size, modsig, sig_len, sig_info);
}
}
+
+void kmod_module_signature_info_free(struct kmod_signature_info *sig_info)
+{
+ if (sig_info->free)
+ sig_info->free(sig_info);
+}