From patchwork Fri Nov 16 18:32:28 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yauheni Kaliuta X-Patchwork-Id: 10686773 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7540013B5 for ; Fri, 16 Nov 2018 18:32:34 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 670982D728 for ; Fri, 16 Nov 2018 18:32:34 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5B2F92D77F; Fri, 16 Nov 2018 18:32:34 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2151F2D728 for ; Fri, 16 Nov 2018 18:32:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727710AbeKQEqA (ORCPT ); Fri, 16 Nov 2018 23:46:00 -0500 Received: from mx1.redhat.com ([209.132.183.28]:44004 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727462AbeKQEqA (ORCPT ); Fri, 16 Nov 2018 23:46:00 -0500 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0C41537F43; Fri, 16 Nov 2018 18:32:31 +0000 (UTC) Received: from astarta.redhat.com (ovpn-117-20.ams2.redhat.com [10.36.117.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D05735C6A6; Fri, 16 Nov 2018 18:32:29 +0000 (UTC) From: Yauheni Kaliuta To: linux-modules@vger.kernel.org Cc: ykaliuta@redhat.com, Lucas De Marchi Subject: [PATCH] libkmod-signature: implement pkcs7 parsing with openssl Date: Fri, 16 Nov 2018 20:32:28 +0200 Message-Id: <20181116183228.32080-1-yauheni.kaliuta@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.29]); Fri, 16 Nov 2018 18:32:31 +0000 (UTC) Sender: owner-linux-modules@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP 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, 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 --- Makefile.am | 4 +- configure.ac | 11 ++ libkmod/libkmod-internal.h | 3 + libkmod/libkmod-module.c | 3 + libkmod/libkmod-signature.c | 195 +++++++++++++++++++++++++++++++++++- 5 files changed, 211 insertions(+), 5 deletions(-) diff --git a/Makefile.am b/Makefile.am index 194e1115ae70..5ab7fc0b4750 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 fbc7391b2d1b..2e33380a0cc2 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 346579c71aab..a65ddd156f18 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 889f26479a98..bffe715cdef4 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 429ffbd8a957..4b44c3701d28 100644 --- a/libkmod/libkmod-signature.c +++ b/libkmod/libkmod-signature.c @@ -19,6 +19,10 @@ #include #include +#ifdef ENABLE_OPENSSL +#include +#include +#endif #include #include #include @@ -115,15 +119,192 @@ 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); + 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); + } + + sig_info->algo = NULL; + + 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 err; + + pvt->cms = cms; + pvt->key_id = key_id_str; + pvt->sno = sno_bn; + sig_info->private = pvt; + + sig_info->free = pkcs7_free; + + return true; + +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 +348,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); +}