diff mbox series

[v4,1/3] signature: allow codeSigning as key usage

Message ID 20181108143252.14842-2-Denis.Osterland@diehl.com
State Accepted
Headers show
Series signature: additional checks on signer certificate | expand

Commit Message

Denis Osterland-Heim Nov. 8, 2018, 3:08 p.m. UTC
OpenSSL CMS checks only for emailProtection key usage,
but codeSigning makes more sense here.
This patch adds command line parameter and configuration variable
to set expected certificate purpose.

Signed-off-by: Denis Osterland <Denis.Osterland@diehl.com>
---
 core/swupdate.c            | 27 +++++++++++++++++++++++
 corelib/verify_signature.c | 45 ++++++++++++++++++++++++++++++++++++++
 include/sslapi.h           | 31 ++++++++++++++++++++++++++
 include/swupdate.h         |  1 +
 4 files changed, 104 insertions(+)

Comments

Stefano Babic Nov. 15, 2018, 5:50 p.m. UTC | #1
On 08/11/18 16:08, Denis OSTERLAND wrote:
> OpenSSL CMS checks only for emailProtection key usage,
> but codeSigning makes more sense here.
> This patch adds command line parameter and configuration variable
> to set expected certificate purpose.
> 
> Signed-off-by: Denis Osterland <Denis.Osterland@diehl.com>
> ---
>  core/swupdate.c            | 27 +++++++++++++++++++++++
>  corelib/verify_signature.c | 45 ++++++++++++++++++++++++++++++++++++++
>  include/sslapi.h           | 31 ++++++++++++++++++++++++++
>  include/swupdate.h         |  1 +
>  4 files changed, 104 insertions(+)
> 
> diff --git a/core/swupdate.c b/core/swupdate.c
> index 161b03f..1f60ebf 100644
> --- a/core/swupdate.c
> +++ b/core/swupdate.c
> @@ -81,6 +81,7 @@ static struct option long_options[] = {
>  	{"no-downgrading", required_argument, NULL, 'N'},
>  #ifdef CONFIG_SIGNED_IMAGES
>  	{"key", required_argument, NULL, 'k'},
> +	{"cert-purpose", required_argument, NULL, '1'},
>  #endif
>  #ifdef CONFIG_ENCRYPTED_IMAGES
>  	{"key-aes", required_argument, NULL, 'K'},
> @@ -126,6 +127,8 @@ static void usage(char *programname)
>  		" -L, --syslog                   : enable syslog logger\n"
>  #ifdef CONFIG_SIGNED_IMAGES
>  		" -k, --key <public key file>    : file with public key to verify images\n"
> +		"     --cert-purpose <purpose>   : set expected certificate purpose\n"
> +		"                                  [emailProtection|codeSigning] (default: emailProtection)\n"
>  #endif
>  #ifdef CONFIG_ENCRYPTED_IMAGES
>  		" -K, --key-aes <key file>       : the file contains the symmetric key to be used\n"
> @@ -446,6 +449,7 @@ static void swupdate_init(struct swupdate_cfg *sw)
>  	LIST_INIT(&sw->bootscripts);
>  	LIST_INIT(&sw->bootloader);
>  	LIST_INIT(&sw->extprocs);
> +	sw->globals.cert_purpose = SSL_PURPOSE_DEFAULT;
>  
>  
>  	/* Create directories for scripts */
> @@ -462,8 +466,24 @@ static void swupdate_init(struct swupdate_cfg *sw)
>  #endif
>  }
>  
> +static int parse_cert_purpose(const char *text)
> +{
> +	static const char CODE_SIGN[] = "codeSigning";
> +	static const char EMAIL_PROT[] = "emailProtection";
> +
> +	if (strncmp(CODE_SIGN, text, sizeof(CODE_SIGN)) == 0)
> +		return SSL_PURPOSE_CODE_SIGN;
> +
> +	if (strncmp(EMAIL_PROT, text, sizeof(EMAIL_PROT)) == 0)
> +		return SSL_PURPOSE_EMAIL_PROT;
> +
> +	ERROR("unknown certificate purpose '%s'\n", text);
> +	exit(EXIT_FAILURE);
> +}
> +
>  static int read_globals_settings(void *elem, void *data)
>  {
> +	char tmp[SWUPDATE_GENERAL_STRING_SIZE] = "";
>  	struct swupdate_cfg *sw = (struct swupdate_cfg *)data;
>  
>  	GET_FIELD_STRING(LIBCFG_PARSER, elem,
> @@ -481,6 +501,10 @@ static int read_globals_settings(void *elem, void *data)
>  				"no-downgrading", sw->globals.current_version);
>  	if (strlen(sw->globals.current_version))
>  		sw->globals.no_downgrading = 1;
> +	GET_FIELD_STRING(LIBCFG_PARSER, elem,
> +				"cert-purpose", tmp);
> +	if (tmp[0] != '\0')
> +		sw->globals.cert_purpose = parse_cert_purpose(tmp);
>  
>  	return 0;
>  }
> @@ -694,6 +718,9 @@ int main(int argc, char **argv)
>  				optarg,
>  			       	sizeof(swcfg.globals.publickeyfname));
>  			break;
> +		case '1':
> +			swcfg.globals.cert_purpose = parse_cert_purpose(optarg);
> +			break;
>  #ifdef CONFIG_ENCRYPTED_IMAGES
>  		case 'K':
>  			strncpy(swcfg.globals.aeskeyfname,
> diff --git a/corelib/verify_signature.c b/corelib/verify_signature.c
> index 6ddfa42..d624339 100644
> --- a/corelib/verify_signature.c
> +++ b/corelib/verify_signature.c
> @@ -195,6 +195,32 @@ out:
>  }
>  
>  #elif defined(CONFIG_SIGALG_CMS)
> +static int check_code_sign(const X509_PURPOSE *xp, const X509 *crt, int ca)
> +{
> +	X509 *x = (X509 *)crt;
> +	uint32_t ex_flags = SSL_X509_get_extension_flags(x);
> +	uint32_t ex_xkusage = SSL_X509_get_extended_key_usage(x);
> +
> +	(void)xp;
> +
> +	if (ca) {
> +		int idx;
> +		const X509_PURPOSE *pt;
> +
> +		if ((ex_flags & EXFLAG_XKUSAGE) && !(ex_xkusage & XKU_CODE_SIGN))
> +			return 0;
> +
> +		idx = X509_PURPOSE_get_by_id(X509_PURPOSE_OCSP_HELPER);
> +		if (idx == -1)
> +			return 0;
> +
> +		pt = X509_PURPOSE_get0(idx);
> +		return pt->check_purpose(pt, x, ca);
> +	}
> +
> +	return (ex_flags & EXFLAG_XKUSAGE) && (ex_xkusage & XKU_CODE_SIGN);
> +}
> +
>  static int cms_verify_callback(int ok, X509_STORE_CTX *ctx) {
>  	int cert_error = X509_STORE_CTX_get_error(ctx);
>  
> @@ -439,6 +465,25 @@ int swupdate_dgst_init(struct swupdate_cfg *sw, const char *keyfile)
>  		ret = -EINVAL;
>  		goto dgst_init_error;
>  	}
> +
> +	{
> +		static char code_sign_name[] = "Code signing";
> +		static char code_sign_sname[] = "codesign";
> +
> +		if (!X509_PURPOSE_add(X509_PURPOSE_CODE_SIGN, X509_TRUST_EMAIL,
> +				0, check_code_sign, code_sign_name,
> +				code_sign_sname, NULL)) {
> +			ERROR("failed to add code sign purpose");
> +			ret = -EINVAL;
> +			goto dgst_init_error;
> +		}
> +	}
> +
> +	if (!X509_STORE_set_purpose(dgst->certs, sw->globals.cert_purpose)) {
> +		ERROR("failed to set purpose");
> +		ret = -EINVAL;
> +		goto dgst_init_error;
> +	}
>  #else
>  	TRACE("public key / cert %s ignored, you need to set SIGALG", keyfile);
>  #endif
> diff --git a/include/sslapi.h b/include/sslapi.h
> index 1d24dfb..9af74af 100644
> --- a/include/sslapi.h
> +++ b/include/sslapi.h
> @@ -8,6 +8,8 @@
>  #ifndef _SWUPDATE_SSL_H
>  #define _SWUPDATE_SSL_H
>  
> +#include <stdint.h>
> +
>  #define SHA_DEFAULT	"sha256"
>  
>  /*
> @@ -36,6 +38,11 @@
>  
>  #include <openssl/opensslv.h>
>  
> +#define X509_PURPOSE_CODE_SIGN (X509_PURPOSE_MAX + 1)
> +#define SSL_PURPOSE_EMAIL_PROT X509_PURPOSE_SMIME_SIGN
> +#define SSL_PURPOSE_CODE_SIGN  X509_PURPOSE_CODE_SIGN
> +#define SSL_PURPOSE_DEFAULT SSL_PURPOSE_EMAIL_PROT
> +
>  struct swupdate_digest {
>  	EVP_PKEY *pkey;		/* this is used for RSA key */
>  	X509_STORE *certs;	/* this is used if CMS is set */
> @@ -70,6 +77,24 @@ struct swupdate_digest {
>  #define swupdate_crypto_init()
>  #endif
>  
> +static inline uint32_t SSL_X509_get_extension_flags(X509 *x)
> +{
> +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
> +	return x->ex_flags;
> +#else
> +	return X509_get_extension_flags(x);
> +#endif
> +}
> +
> +static inline uint32_t SSL_X509_get_extended_key_usage(X509 *x)
> +{
> +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
> +	return x->ex_xkusage;
> +#else
> +	return X509_get_extended_key_usage(x);
> +#endif
> +}
> +
>  #else
>  #define swupdate_crypto_init()
>  #define AES_BLOCK_SIZE	16
> @@ -123,5 +148,11 @@ void swupdate_DECRYPT_cleanup(struct swupdate_digest *dgst);
>  #define SHA_DIGEST_LENGTH 20
>  #endif
>  
> +#ifndef SSL_PURPOSE_DEFAULT
> +#define SSL_PURPOSE_EMAIL_PROT -1
> +#define SSL_PURPOSE_CODE_SIGN  -1
> +#define SSL_PURPOSE_DEFAULT -1
> +#endif
> +
>  #endif
>  
> diff --git a/include/swupdate.h b/include/swupdate.h
> index 05604e8..a8c243e 100644
> --- a/include/swupdate.h
> +++ b/include/swupdate.h
> @@ -113,6 +113,7 @@ struct swupdate_global_cfg {
>  	char aeskeyfname[SWUPDATE_GENERAL_STRING_SIZE];
>  	char postupdatecmd[SWUPDATE_GENERAL_STRING_SIZE];
>  	char current_version[SWUPDATE_GENERAL_STRING_SIZE];
> +	int cert_purpose;
>  };
>  
>  struct swupdate_cfg {
> 

Tested both emailProtection and codeSigning options.

Tested-by: Stefano Babic <sbabic@denx.de>

Best regards,
Stefano Babic
diff mbox series

Patch

diff --git a/core/swupdate.c b/core/swupdate.c
index 161b03f..1f60ebf 100644
--- a/core/swupdate.c
+++ b/core/swupdate.c
@@ -81,6 +81,7 @@  static struct option long_options[] = {
 	{"no-downgrading", required_argument, NULL, 'N'},
 #ifdef CONFIG_SIGNED_IMAGES
 	{"key", required_argument, NULL, 'k'},
+	{"cert-purpose", required_argument, NULL, '1'},
 #endif
 #ifdef CONFIG_ENCRYPTED_IMAGES
 	{"key-aes", required_argument, NULL, 'K'},
@@ -126,6 +127,8 @@  static void usage(char *programname)
 		" -L, --syslog                   : enable syslog logger\n"
 #ifdef CONFIG_SIGNED_IMAGES
 		" -k, --key <public key file>    : file with public key to verify images\n"
+		"     --cert-purpose <purpose>   : set expected certificate purpose\n"
+		"                                  [emailProtection|codeSigning] (default: emailProtection)\n"
 #endif
 #ifdef CONFIG_ENCRYPTED_IMAGES
 		" -K, --key-aes <key file>       : the file contains the symmetric key to be used\n"
@@ -446,6 +449,7 @@  static void swupdate_init(struct swupdate_cfg *sw)
 	LIST_INIT(&sw->bootscripts);
 	LIST_INIT(&sw->bootloader);
 	LIST_INIT(&sw->extprocs);
+	sw->globals.cert_purpose = SSL_PURPOSE_DEFAULT;
 
 
 	/* Create directories for scripts */
@@ -462,8 +466,24 @@  static void swupdate_init(struct swupdate_cfg *sw)
 #endif
 }
 
+static int parse_cert_purpose(const char *text)
+{
+	static const char CODE_SIGN[] = "codeSigning";
+	static const char EMAIL_PROT[] = "emailProtection";
+
+	if (strncmp(CODE_SIGN, text, sizeof(CODE_SIGN)) == 0)
+		return SSL_PURPOSE_CODE_SIGN;
+
+	if (strncmp(EMAIL_PROT, text, sizeof(EMAIL_PROT)) == 0)
+		return SSL_PURPOSE_EMAIL_PROT;
+
+	ERROR("unknown certificate purpose '%s'\n", text);
+	exit(EXIT_FAILURE);
+}
+
 static int read_globals_settings(void *elem, void *data)
 {
+	char tmp[SWUPDATE_GENERAL_STRING_SIZE] = "";
 	struct swupdate_cfg *sw = (struct swupdate_cfg *)data;
 
 	GET_FIELD_STRING(LIBCFG_PARSER, elem,
@@ -481,6 +501,10 @@  static int read_globals_settings(void *elem, void *data)
 				"no-downgrading", sw->globals.current_version);
 	if (strlen(sw->globals.current_version))
 		sw->globals.no_downgrading = 1;
+	GET_FIELD_STRING(LIBCFG_PARSER, elem,
+				"cert-purpose", tmp);
+	if (tmp[0] != '\0')
+		sw->globals.cert_purpose = parse_cert_purpose(tmp);
 
 	return 0;
 }
@@ -694,6 +718,9 @@  int main(int argc, char **argv)
 				optarg,
 			       	sizeof(swcfg.globals.publickeyfname));
 			break;
+		case '1':
+			swcfg.globals.cert_purpose = parse_cert_purpose(optarg);
+			break;
 #ifdef CONFIG_ENCRYPTED_IMAGES
 		case 'K':
 			strncpy(swcfg.globals.aeskeyfname,
diff --git a/corelib/verify_signature.c b/corelib/verify_signature.c
index 6ddfa42..d624339 100644
--- a/corelib/verify_signature.c
+++ b/corelib/verify_signature.c
@@ -195,6 +195,32 @@  out:
 }
 
 #elif defined(CONFIG_SIGALG_CMS)
+static int check_code_sign(const X509_PURPOSE *xp, const X509 *crt, int ca)
+{
+	X509 *x = (X509 *)crt;
+	uint32_t ex_flags = SSL_X509_get_extension_flags(x);
+	uint32_t ex_xkusage = SSL_X509_get_extended_key_usage(x);
+
+	(void)xp;
+
+	if (ca) {
+		int idx;
+		const X509_PURPOSE *pt;
+
+		if ((ex_flags & EXFLAG_XKUSAGE) && !(ex_xkusage & XKU_CODE_SIGN))
+			return 0;
+
+		idx = X509_PURPOSE_get_by_id(X509_PURPOSE_OCSP_HELPER);
+		if (idx == -1)
+			return 0;
+
+		pt = X509_PURPOSE_get0(idx);
+		return pt->check_purpose(pt, x, ca);
+	}
+
+	return (ex_flags & EXFLAG_XKUSAGE) && (ex_xkusage & XKU_CODE_SIGN);
+}
+
 static int cms_verify_callback(int ok, X509_STORE_CTX *ctx) {
 	int cert_error = X509_STORE_CTX_get_error(ctx);
 
@@ -439,6 +465,25 @@  int swupdate_dgst_init(struct swupdate_cfg *sw, const char *keyfile)
 		ret = -EINVAL;
 		goto dgst_init_error;
 	}
+
+	{
+		static char code_sign_name[] = "Code signing";
+		static char code_sign_sname[] = "codesign";
+
+		if (!X509_PURPOSE_add(X509_PURPOSE_CODE_SIGN, X509_TRUST_EMAIL,
+				0, check_code_sign, code_sign_name,
+				code_sign_sname, NULL)) {
+			ERROR("failed to add code sign purpose");
+			ret = -EINVAL;
+			goto dgst_init_error;
+		}
+	}
+
+	if (!X509_STORE_set_purpose(dgst->certs, sw->globals.cert_purpose)) {
+		ERROR("failed to set purpose");
+		ret = -EINVAL;
+		goto dgst_init_error;
+	}
 #else
 	TRACE("public key / cert %s ignored, you need to set SIGALG", keyfile);
 #endif
diff --git a/include/sslapi.h b/include/sslapi.h
index 1d24dfb..9af74af 100644
--- a/include/sslapi.h
+++ b/include/sslapi.h
@@ -8,6 +8,8 @@ 
 #ifndef _SWUPDATE_SSL_H
 #define _SWUPDATE_SSL_H
 
+#include <stdint.h>
+
 #define SHA_DEFAULT	"sha256"
 
 /*
@@ -36,6 +38,11 @@ 
 
 #include <openssl/opensslv.h>
 
+#define X509_PURPOSE_CODE_SIGN (X509_PURPOSE_MAX + 1)
+#define SSL_PURPOSE_EMAIL_PROT X509_PURPOSE_SMIME_SIGN
+#define SSL_PURPOSE_CODE_SIGN  X509_PURPOSE_CODE_SIGN
+#define SSL_PURPOSE_DEFAULT SSL_PURPOSE_EMAIL_PROT
+
 struct swupdate_digest {
 	EVP_PKEY *pkey;		/* this is used for RSA key */
 	X509_STORE *certs;	/* this is used if CMS is set */
@@ -70,6 +77,24 @@  struct swupdate_digest {
 #define swupdate_crypto_init()
 #endif
 
+static inline uint32_t SSL_X509_get_extension_flags(X509 *x)
+{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+	return x->ex_flags;
+#else
+	return X509_get_extension_flags(x);
+#endif
+}
+
+static inline uint32_t SSL_X509_get_extended_key_usage(X509 *x)
+{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+	return x->ex_xkusage;
+#else
+	return X509_get_extended_key_usage(x);
+#endif
+}
+
 #else
 #define swupdate_crypto_init()
 #define AES_BLOCK_SIZE	16
@@ -123,5 +148,11 @@  void swupdate_DECRYPT_cleanup(struct swupdate_digest *dgst);
 #define SHA_DIGEST_LENGTH 20
 #endif
 
+#ifndef SSL_PURPOSE_DEFAULT
+#define SSL_PURPOSE_EMAIL_PROT -1
+#define SSL_PURPOSE_CODE_SIGN  -1
+#define SSL_PURPOSE_DEFAULT -1
+#endif
+
 #endif
 
diff --git a/include/swupdate.h b/include/swupdate.h
index 05604e8..a8c243e 100644
--- a/include/swupdate.h
+++ b/include/swupdate.h
@@ -113,6 +113,7 @@  struct swupdate_global_cfg {
 	char aeskeyfname[SWUPDATE_GENERAL_STRING_SIZE];
 	char postupdatecmd[SWUPDATE_GENERAL_STRING_SIZE];
 	char current_version[SWUPDATE_GENERAL_STRING_SIZE];
+	int cert_purpose;
 };
 
 struct swupdate_cfg {