Message ID | 20181116183228.32080-1-yauheni.kaliuta@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | libkmod-signature: implement pkcs7 parsing with openssl | expand |
>>>>> On Fri, 16 Nov 2018 20:32:28 +0200, Yauheni Kaliuta wrote: > 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, Did not finish the paragraph: 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> > --- > 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 <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,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); > +} > -- > 2.19.1
Hi! >>>>> On Fri, 16 Nov 2018 20:32:28 +0200, Yauheni Kaliuta wrote: [...] Yeah, cleanup on error for the values allocated below. But the main question is in general if the way of linking is possible. > + > + > + 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; > +} > +
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 <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,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); +}
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 <yauheni.kaliuta@redhat.com> --- 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(-)