On Mon, Nov 19, 2018 at 6:00 AM Yauheni Kaliuta <yauheni.kaliuta@xxxxxxxxxx> wrote: > > The patch adds data fetching from the PKCS#7 certificate using > gnutls library. > > 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. > > gnutls has LGPL2.1 license, same as libkmod. but... why are you adding both openssl and gnutls? Lucas De Marchi > > Signed-off-by: Yauheni Kaliuta <yauheni.kaliuta@xxxxxxxxxx> > --- > Makefile.am | 4 +- > configure.ac | 11 ++ > libkmod/libkmod-internal.h | 3 + > libkmod/libkmod-module.c | 3 + > libkmod/libkmod-signature.c | 208 +++++++++++++++++++++++++++++++++++- > 5 files changed, 224 insertions(+), 5 deletions(-) > > diff --git a/Makefile.am b/Makefile.am > index 194e1115ae70..2fd44fca69f2 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,@gnutls_CFLAGS\@,${gnutls_CFLAGS},g' \ > + -e 's,@gnutls_LIBS\@,${gnutls_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} ${gnutls_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..0a6787fa9a36 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([gnutls], > + AS_HELP_STRING([--with-gnutls], [handle PKCS7 signatures @<:@default=disabled@:>@]), > + [], [with_gnutls=no]) > +AS_IF([test "x$with_gnutls" != "xno"], [ > + PKG_CHECK_MODULES([gnutls], [gnutls]) > + AC_DEFINE([ENABLE_GNUTLS], [1], [Enable gnutls for modinfo.]) > +], [ > + AC_MSG_NOTICE([gnutls support not requested]) > +]) > +CC_FEATURE_APPEND([with_features], [with_gnutls], [GNUTLS]) > + > 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..326fa2ea776a 100644 > --- a/libkmod/libkmod-signature.c > +++ b/libkmod/libkmod-signature.c > @@ -19,6 +19,9 @@ > > #include <endian.h> > #include <inttypes.h> > +#ifdef ENABLE_GNUTLS > +#include <gnutls/pkcs7.h> > +#endif > #include <stdio.h> > #include <stdlib.h> > #include <string.h> > @@ -92,6 +95,13 @@ struct module_signature { > uint32_t sig_len; /* Length of signature data (big endian) */ > }; > > +static const char *pkey_hash_algo_to_str(enum pkey_hash_algo algo) > +{ > + if (algo < 0 || algo >= PKEY_HASH__LAST) > + return "unknown"; > + return pkey_hash_algo[algo]; > +} > + > static bool fill_default(const char *mem, off_t size, > const struct module_signature *modsig, size_t sig_len, > struct kmod_signature_info *sig_info) > @@ -115,15 +125,199 @@ 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_GNUTLS > + > +struct pkcs7_private { > + gnutls_pkcs7_t pkcs7; > + gnutls_pkcs7_signature_info_st si; > + gnutls_x509_dn_t dn; > + gnutls_datum_t dn_data; > + char *issuer; > +}; > + > +static void pkcs7_free(void *s) > +{ > + struct kmod_signature_info *si = s; > + struct pkcs7_private *pvt = si->private; > + > + free(pvt->issuer); > + gnutls_free(pvt->dn_data.data); > + gnutls_x509_dn_deinit(pvt->dn); > + gnutls_pkcs7_signature_info_deinit(&pvt->si); > + gnutls_pkcs7_deinit(pvt->pkcs7); > + > + free(pvt); > + si->private = NULL; > +} > + > +static int gnutls_algo_translate(gnutls_sign_algorithm_t algo) > +{ > + switch (algo) { > + case GNUTLS_SIGN_RSA_SHA1: > + case GNUTLS_SIGN_DSA_SHA1: > + case GNUTLS_SIGN_ECDSA_SHA1: > + return PKEY_HASH_SHA1; > + case GNUTLS_SIGN_RSA_MD5: > + return PKEY_HASH_MD5; > + case GNUTLS_SIGN_RSA_RMD160: > + return PKEY_HASH_RIPE_MD_160; > + case GNUTLS_SIGN_RSA_SHA256: > + case GNUTLS_SIGN_DSA_SHA256: > + case GNUTLS_SIGN_ECDSA_SHA256: > + return PKEY_HASH_SHA256; > + case GNUTLS_SIGN_RSA_SHA384: > + case GNUTLS_SIGN_ECDSA_SHA384: > + case GNUTLS_SIGN_DSA_SHA384: > + return PKEY_HASH_SHA384; > + case GNUTLS_SIGN_RSA_SHA512: > + case GNUTLS_SIGN_ECDSA_SHA512: > + case GNUTLS_SIGN_DSA_SHA512: > + return PKEY_HASH_SHA512; > + case GNUTLS_SIGN_RSA_SHA224: > + case GNUTLS_SIGN_DSA_SHA224: > + case GNUTLS_SIGN_ECDSA_SHA224: > + return PKEY_HASH_SHA224; > + default: > + return -1; > + } > + return -1; > +} > + > +/* > + * Extracts CN from O=Org,CN=CommonName,EMAIL=email > + */ > +static char *dn_str_to_cn(unsigned char *dn) > +{ > + char *s; > + char *e; > + char *r; > + size_t len; > + > + s = strstr((char *)dn, "CN="); > + if (s == NULL) > + return NULL; > + > + len = strlen(s); > + if (len < strlen("CN=") + 1) /* at least one symbol */ > + return NULL; > + s += strlen("CN="); > + > + e = strchr(s, ','); > + if (e == NULL) > + e = s + len; > + len = e - s; > + > + r = malloc(len + 1); > + if (r == NULL) > + return NULL; > + > + memcpy(r, s, len); > + r[len] = '\0'; > + return r; > +} > +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) > +{ > + int rc; > + const char *pkcs7_raw; > + gnutls_pkcs7_t pkcs7; > + gnutls_datum_t data; > + gnutls_pkcs7_signature_info_st si; > + gnutls_x509_dn_t dn; > + struct pkcs7_private *pvt; > + char *issuer; > + > + size -= sig_len; > + pkcs7_raw = mem + size; > + > + rc = gnutls_pkcs7_init(&pkcs7); > + if (rc < 0) > + return false; > + > + data.data = (unsigned char *)pkcs7_raw; > + data.size = sig_len; > + rc = gnutls_pkcs7_import(pkcs7, &data, GNUTLS_X509_FMT_DER); > + if (rc < 0) > + goto err1; > + > + rc = gnutls_pkcs7_get_signature_info(pkcs7, 0, &si); > + if (rc < 0) > + goto err1; > + > + rc = gnutls_x509_dn_init(&dn); > + if (rc < 0) > + goto err2; > + > + rc = gnutls_x509_dn_import(dn, &si.issuer_dn); > + if (rc < 0) > + goto err3; > + > + /* > + * I could not find simple wrapper to extract the data > + * directly from ASN1, so get the string and parse it. > + * > + * Returns null-terminated string in data.data > + */ > + rc = gnutls_x509_dn_get_str(dn, &data); > + if (rc < 0) > + goto err3; > + > + sig_info->sig = (const char *)si.sig.data; > + sig_info->sig_len = si.sig.size; > + > + sig_info->key_id = (const char *)si.signer_serial.data; > + sig_info->key_id_len = si.signer_serial.size; > + > + issuer = dn_str_to_cn(data.data); > + if (issuer != NULL) { > + sig_info->signer = issuer; > + sig_info->signer_len = strlen(issuer); > + } > + > + sig_info->hash_algo = pkey_hash_algo_to_str(gnutls_algo_translate(si.algo)); > + sig_info->id_type = pkey_id_type[modsig->id_type]; > + > + pvt = malloc(sizeof(*pvt)); > + if (pvt == NULL) > + goto err4; > + > + pvt->pkcs7 = pkcs7; > + pvt->si = si; > + pvt->dn = dn; > + pvt->dn_data = data; > + pvt->issuer = issuer; > + > + sig_info->private = pvt; > + sig_info->free = pkcs7_free; > + > + return true; > + > +err4: > + gnutls_free(data.data); > +err3: > + gnutls_x509_dn_deinit(dn); > +err2: > + gnutls_pkcs7_signature_info_deinit(&si); > +err1: > + gnutls_pkcs7_deinit(pkcs7); > + > + return false; > +} > + > +#else /* ENABLE GNUTLS */ > + > +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 GNUTLS */ > + > #define SIG_MAGIC "~Module signature appended~\n" > > /* > @@ -167,8 +361,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 > -- Lucas De Marchi