[RFC] libkmod-signature: implement pkcs7 parsing with gnutls
diff mbox series

Message ID 20181119135958.18318-1-yauheni.kaliuta@redhat.com
State New
Headers show
Series
  • [RFC] libkmod-signature: implement pkcs7 parsing with gnutls
Related show

Commit Message

Yauheni Kaliuta Nov. 19, 2018, 1:59 p.m. UTC
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.

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 | 208 +++++++++++++++++++++++++++++++++++-
 5 files changed, 224 insertions(+), 5 deletions(-)

Comments

Lucas De Marchi Nov. 30, 2018, 8:41 p.m. UTC | #1
On Mon, Nov 19, 2018 at 6:00 AM Yauheni Kaliuta
<yauheni.kaliuta@redhat.com> 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@redhat.com>
> ---
>  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
>
Yauheni Kaliuta Dec. 1, 2018, 4:18 p.m. UTC | #2
Hi, Lucas!

Sending again because of accidental HTML previous time.

>>>>> On Fri, 30 Nov 2018 12:41:48 -0800, Lucas De Marchi  wrote:

 > On Mon, Nov 19, 2018 at 6:00 AM Yauheni Kaliuta
 > <yauheni.kaliuta@redhat.com> 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?

They are mutually exclusive options since I do not get any
feedback and do not know which direction to go :)

 > Lucas De Marchi

 >> 
 >> 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 | 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

Patch
diff mbox series

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);
+}