Linux-Modules Archive on lore.kernel.org
 help / Atom feed
* [PATCH RFC] libkmod-signature: implement pkcs7 parsing with gnutls
@ 2018-11-19 13:59 Yauheni Kaliuta
  2018-11-30 20:41 ` Lucas De Marchi
  0 siblings, 1 reply; 3+ messages in thread
From: Yauheni Kaliuta @ 2018-11-19 13:59 UTC (permalink / raw)
  To: linux-modules; +Cc: ykaliuta, Lucas De Marchi

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

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


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH RFC] libkmod-signature: implement pkcs7 parsing with gnutls
  2018-11-19 13:59 [PATCH RFC] libkmod-signature: implement pkcs7 parsing with gnutls Yauheni Kaliuta
@ 2018-11-30 20:41 ` Lucas De Marchi
  2018-12-01 16:18   ` Yauheni Kaliuta
  0 siblings, 1 reply; 3+ messages in thread
From: Lucas De Marchi @ 2018-11-30 20:41 UTC (permalink / raw)
  To: Yauheni Kaliuta; +Cc: linux-modules, ykaliuta, Lucas De Marchi

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
>


-- 
Lucas De Marchi

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH RFC] libkmod-signature: implement pkcs7 parsing with gnutls
  2018-11-30 20:41 ` Lucas De Marchi
@ 2018-12-01 16:18   ` Yauheni Kaliuta
  0 siblings, 0 replies; 3+ messages in thread
From: Yauheni Kaliuta @ 2018-12-01 16:18 UTC (permalink / raw)
  To: Lucas De Marchi; +Cc: linux-modules, ykaliuta, Lucas De Marchi

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

-- 
WBR,
Yauheni Kaliuta

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, back to index

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-19 13:59 [PATCH RFC] libkmod-signature: implement pkcs7 parsing with gnutls Yauheni Kaliuta
2018-11-30 20:41 ` Lucas De Marchi
2018-12-01 16:18   ` Yauheni Kaliuta

Linux-Modules Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-modules/0 linux-modules/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-modules linux-modules/ https://lore.kernel.org/linux-modules \
		linux-modules@vger.kernel.org linux-modules@archiver.kernel.org
	public-inbox-index linux-modules


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-modules


AGPL code for this site: git clone https://public-inbox.org/ public-inbox