Message ID | 20181108143252.14842-2-Denis.Osterland@diehl.com |
---|---|
State | Accepted |
Headers | show |
Series | signature: additional checks on signer certificate | expand |
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 --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 {
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(+)