From 31f1f01ae50e49c1fe2c14bc1b91b2ebc62fb6ab Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Feb 23 2015 22:54:37 +0000 Subject: Correctly select the SCEP request digest Instead of assuming the server supports the default digest algorithm, select the preferred one if the server claims to support it, otherwise fall back to MD5, per the spec. --- diff --git a/src/pkcs7.c b/src/pkcs7.c index b96cb8e..0a848b7 100644 --- a/src/pkcs7.c +++ b/src/pkcs7.c @@ -913,7 +913,7 @@ int cm_pkcs7_verify_signed(unsigned char *data, size_t length, const char **roots, const char **othercerts, int expected_content_type, - void *parent, + void *parent, char **digest, char **tx, char **msgtype, char **pkistatus, char **failinfo, unsigned char **sender_nonce, @@ -927,6 +927,7 @@ cm_pkcs7_verify_signed(unsigned char *data, size_t length, STACK_OF(X509) *certs = NULL; STACK_OF(X509_ATTRIBUTE) *attrs; X509_STORE *store = NULL; + X509_ALGOR *algor = NULL; PKCS7_SIGNED *p7s; PKCS7_SIGNER_INFO *si; BIO *in, *out = NULL; @@ -935,6 +936,9 @@ cm_pkcs7_verify_signed(unsigned char *data, size_t length, int ret = -1, i; long error; + if (digest != NULL) { + *digest = NULL; + } if (tx != NULL) { *tx = NULL; } @@ -1067,6 +1071,26 @@ cm_pkcs7_verify_signed(unsigned char *data, size_t length, goto done; } ret = 0; + if (digest != NULL) { + algor = si->digest_alg; + switch (OBJ_obj2nid(algor->algorithm)) { + case NID_md5: + *digest = talloc_strdup(parent, "md5"); + break; + case NID_sha512: + *digest = talloc_strdup(parent, "sha512"); + break; + case NID_sha384: + *digest = talloc_strdup(parent, "sha384"); + break; + case NID_sha256: + *digest = talloc_strdup(parent, "sha256"); + break; + case NID_sha1: + *digest = talloc_strdup(parent, "sha1"); + break; + } + } if (tx != NULL) { *tx = get_pstring_attribute(parent, attrs, cm_scep_o_get_tx_nid()); diff --git a/src/pkcs7.h b/src/pkcs7.h index 43683e5..097f7ca 100644 --- a/src/pkcs7.h +++ b/src/pkcs7.h @@ -54,7 +54,7 @@ int cm_pkcs7_envelope_ias(char *encryption_cert, enum cm_prefs_cipher cipher, int cm_pkcs7_verify_signed(unsigned char *data, size_t length, const char **roots, const char **othercerts, int expected_content_type, - void *parent, + void *parent, char **digest, char **tx, char **msgtype, char **pkistatus, char **failinfo, unsigned char **sender_nonce, diff --git a/src/scep.c b/src/scep.c index ddfd711..305604e 100644 --- a/src/scep.c +++ b/src/scep.c @@ -287,7 +287,7 @@ main(int argc, char **argv) tmp1 = cm_submit_u_base64_from_text(message); tmp2 = cm_store_base64_as_bin(ctx, tmp1, -1, &c); cm_pkcs7_verify_signed((unsigned char *) tmp2, c, - NULL, NULL, NID_pkcs7_data, ctx, + NULL, NULL, NID_pkcs7_data, ctx, NULL, &sent_tx, &msgtype, NULL, NULL, &sent_nonce, &sent_nonce_length, NULL, NULL, NULL, NULL); @@ -500,7 +500,7 @@ main(int argc, char **argv) ERR_clear_error(); i = cm_pkcs7_verify_signed((unsigned char *) results, results_length, cacerts, racerts, - NID_pkcs7_data, ctx, + NID_pkcs7_data, ctx, NULL, &tx, &msgtype, &pkistatus, &failinfo, &sender_nonce, &sender_nonce_length, &recipient_nonce, &recipient_nonce_length, diff --git a/src/scepgen-o.c b/src/scepgen-o.c index ee40a42..f913e75 100644 --- a/src/scepgen-o.c +++ b/src/scepgen-o.c @@ -285,6 +285,7 @@ set_pkimessage_attrs(PKCS7 *p7, static PKCS7 * build_pkimessage(EVP_PKEY *key, X509 *signer, STACK_OF(X509) *certs, + enum cm_prefs_digest pref_digest, unsigned char *data, size_t data_length, const char *tx, const char *msgtype, const char *pkistatus, const char *failinfo, @@ -295,6 +296,9 @@ build_pkimessage(EVP_PKEY *key, X509 *signer, STACK_OF(X509) *certs, { BIO *in, *out; PKCS7 *ret; + PKCS7_SIGNER_INFO *p7i; + X509_ALGOR *digests; + ASN1_OBJECT *digest; long error; char buf[LINE_MAX]; int flags = PKCS7_BINARY | PKCS7_NOSMIMECAP | PKCS7_NOVERIFY; @@ -310,9 +314,48 @@ build_pkimessage(EVP_PKEY *key, X509 *signer, STACK_OF(X509) *certs, goto errors; } BIO_free(in); + + /* Set the digest to use for signing. */ + if (sk_PKCS7_SIGNER_INFO_num(ret->d.sign->signer_info) != 1) { + cm_log(1, "Error signing data: %d signers.\n", + sk_PKCS7_SIGNER_INFO_num(ret->d.sign->signer_info)); + goto errors; + } + p7i = sk_PKCS7_SIGNER_INFO_value(ret->d.sign->signer_info, 0); + digest = NULL; + switch (pref_digest) { + case cm_prefs_sha256: + digest = OBJ_nid2obj(NID_sha256); + break; + case cm_prefs_sha384: + digest = OBJ_nid2obj(NID_sha384); + break; + case cm_prefs_sha512: + digest = OBJ_nid2obj(NID_sha512); + break; + case cm_prefs_sha1: + digest = OBJ_nid2obj(NID_sha1); + break; + case cm_prefs_md5: + digest = OBJ_nid2obj(NID_md5); + break; + } + if ((digest != NULL) && (p7i->digest_alg != NULL)) { + ASN1_OBJECT_free(p7i->digest_alg->algorithm); + p7i->digest_alg->algorithm = OBJ_dup(digest); + digests = sk_X509_ALGOR_pop(ret->d.sign->md_algs); + if (digests != NULL) { + X509_ALGOR_free(digests); + } + sk_X509_ALGOR_push(ret->d.sign->md_algs, + X509_ALGOR_dup(p7i->digest_alg)); + } + + /* Set the SCEP parameters. */ set_pkimessage_attrs(ret, tx, msgtype, pkistatus, failinfo, sender_nonce, sender_nonce_length, recipient_nonce, recipient_nonce_length); + /* We'd use PKCS7_SIGNER_INFO_sign() here, but it's relatively new, and * we want to build on versions of OpenSSL that didn't have it. */ PKCS7_content_new(ret, NID_pkcs7_data); @@ -350,6 +393,7 @@ cm_scepgen_o_cooked(struct cm_store_ca *ca, struct cm_store_entry *entry, int i; long error; enum cm_prefs_cipher cipher; + enum cm_prefs_digest digest, pref_digest; util_o_init(); ERR_load_crypto_strings(); @@ -393,6 +437,29 @@ cm_scepgen_o_cooked(struct cm_store_ca *ca, struct cm_store_entry *entry, break; } } + pref_digest = cm_prefs_preferred_digest(); + digest = cm_prefs_md5; + for (i = 0; + (ca->cm_ca_capabilities != NULL) && + (ca->cm_ca_capabilities[i] != NULL); + i++) { + capability = ca->cm_ca_capabilities[i]; + if ((pref_digest == cm_prefs_sha1) && + (strcmp(capability, "SHA-1") == 0)) { + digest = cm_prefs_sha1; + break; + } + if ((pref_digest == cm_prefs_sha256) && + (strcmp(capability, "SHA-256") == 0)) { + digest = cm_prefs_sha256; + break; + } + if ((pref_digest == cm_prefs_sha512) && + (strcmp(capability, "SHA-512") == 0)) { + digest = cm_prefs_sha512; + break; + } + } if (old_cert != NULL) { if (cm_pkcs7_envelope_ias(ca->cm_ca_encryption_cert, cipher, ca->cm_ca_encryption_issuer_cert, @@ -428,14 +495,14 @@ cm_scepgen_o_cooked(struct cm_store_ca *ca, struct cm_store_entry *entry, * the matching key. */ pubkey = X509_PUBKEY_get(old_cert->cert_info->key); X509_PUBKEY_set(&old_cert->cert_info->key, old_pkey); - *csr_old = build_pkimessage(old_pkey, old_cert, chain, + *csr_old = build_pkimessage(old_pkey, old_cert, chain, digest, csr, csr_length, entry->cm_scep_tx, SCEP_MSGTYPE_PKCSREQ, NULL, NULL, nonce, nonce_length, NULL, 0); - *ias_old = build_pkimessage(old_pkey, old_cert, chain, + *ias_old = build_pkimessage(old_pkey, old_cert, chain, digest, old_ias, old_ias_length, entry->cm_scep_tx, SCEP_MSGTYPE_GETCERTINITIAL, @@ -453,14 +520,14 @@ cm_scepgen_o_cooked(struct cm_store_ca *ca, struct cm_store_entry *entry, * any previously-issued certificate won't match. */ pubkey = X509_PUBKEY_get(new_cert->cert_info->key); X509_PUBKEY_set(&new_cert->cert_info->key, new_pkey); - *csr_new = build_pkimessage(new_pkey, new_cert, chain, + *csr_new = build_pkimessage(new_pkey, new_cert, chain, digest, csr, csr_length, entry->cm_scep_tx, SCEP_MSGTYPE_PKCSREQ, NULL, NULL, nonce, nonce_length, NULL, 0); - *ias_new = build_pkimessage(new_pkey, new_cert, chain, + *ias_new = build_pkimessage(new_pkey, new_cert, chain, digest, new_ias, new_ias_length, entry->cm_scep_tx, SCEP_MSGTYPE_GETCERTINITIAL, @@ -474,14 +541,14 @@ cm_scepgen_o_cooked(struct cm_store_ca *ca, struct cm_store_entry *entry, * if we do, we just did that). */ pubkey = X509_PUBKEY_get(new_cert->cert_info->key); X509_PUBKEY_set(&new_cert->cert_info->key, old_pkey); - *csr_new = build_pkimessage(old_pkey, new_cert, chain, + *csr_new = build_pkimessage(old_pkey, new_cert, chain, digest, csr, csr_length, entry->cm_scep_tx, SCEP_MSGTYPE_PKCSREQ, NULL, NULL, nonce, nonce_length, NULL, 0); - *ias_new = build_pkimessage(old_pkey, new_cert, chain, + *ias_new = build_pkimessage(old_pkey, new_cert, chain, digest, new_ias, new_ias_length, entry->cm_scep_tx, SCEP_MSGTYPE_GETCERTINITIAL, diff --git a/tests/033-scep/run.sh b/tests/033-scep/run.sh index 49aa3d1..14bb58d 100755 --- a/tests/033-scep/run.sh +++ b/tests/033-scep/run.sh @@ -7,11 +7,15 @@ SCEP_MSGTYPE_GETCERTINITIAL="20" SCEP_MSGTYPE_GETCERT="21" SCEP_MSGTYPE_GETCRL="22" +CERTMONGER_CONFIG_DIR="$tmpdir" +export CERTMONGER_CONFIG_DIR + $toolsdir/cachain.sh 0 2> /dev/null cat > ca << EOF id=SCEP ca_type=EXTERNAL +ca_capabilities=Renewal,SHA-512,SHA-256,SHA-1,DES3 EOF var="ca_encryption_cert=" cat ca0.crt | while read line ; do @@ -51,31 +55,49 @@ check_failed() { } check_verified() { if ! grep -q "^verify passed$" results ; then - echo expected signature verification to fail, but it did not + echo expected signature verification to succeed, but it did not exit 1 fi } +set_digest() { + cat > $CERTMONGER_CONFIG_DIR/certmonger.conf <<- EOF + [defaults] + digest = $1 + notification_method = stdout + [selfsign] + validity_period = 1d + EOF +} +check_digest() { + digest=`grep ^digest: results | cut -f2 -d:` + if test $digest != $1 ; then + echo expected digest $1, got $digest + fi +} check_msgtype() { msgtype=`grep ^msgtype: results | cut -f2 -d:` if test $msgtype -ne $1 ; then - expected message type $1, got $msgtype + echo expected message type $1, got $msgtype fi } check_txid() { original=`grep ^tx: scepdata | cut -f2 -d:` parsed=`grep ^tx: results | cut -f2 -d:` if test "$original" != "$parsed" ; then - expected tx id "$original", got "$parsed" + echo expected tx id "$original", got "$parsed" fi } check_nonce() { original=`grep ^nonce: scepdata | cut -f2 -d:` parsed=`grep ^snonce: results | cut -f2 -d:` if test "$original" != "$parsed" ; then - expected nonce "$original", got "$parsed" + echo expected nonce "$original", got "$parsed" fi } +set_digest md5 +$toolsdir/scepgen ca entry > scepdata + echo "[req, no trust root]" grep ^req: scepdata | cut -f2- -d: | base64 -i -d | $toolsdir/pk7verify ee.crt 2>&1 > results 2>&1 check_failed @@ -93,18 +115,24 @@ grep ^gic: scepdata | cut -f2- -d: | base64 -i -d | $toolsdir/pk7verify -r mini. check_failed echo OK echo "[req, old root]" +set_digest md5 +$toolsdir/scepgen ca entry > scepdata grep ^req: scepdata | cut -f2- -d: | base64 -i -d | $toolsdir/pk7verify -r ca0.crt ee.crt 2>&1 > results 2>&1 check_verified check_msgtype $SCEP_MSGTYPE_PKCSREQ check_txid check_nonce +check_digest md5 echo OK echo "[gic, old trust root]" +set_digest sha1 +$toolsdir/scepgen ca entry > scepdata grep ^gic: scepdata | cut -f2- -d: | base64 -i -d | $toolsdir/pk7verify -r ca0.crt ee.crt 2>&1 > results 2>&1 check_verified check_msgtype $SCEP_MSGTYPE_GETCERTINITIAL check_txid check_nonce +check_digest sha1 echo OK echo "[req next, no trust root]" grep ^req.next.: scepdata | cut -f2- -d: | base64 -i -d | $toolsdir/pk7verify ee.crt > results 2>&1 @@ -115,11 +143,14 @@ grep ^gic.next.: scepdata | cut -f2- -d: | base64 -i -d | $toolsdir/pk7verify ee check_failed echo OK echo "[req next, self root]" +set_digest sha256 +$toolsdir/scepgen ca entry > scepdata grep ^req.next.: scepdata | cut -f2- -d: | base64 -i -d | $toolsdir/pk7verify -r mini.crt ee.crt > results 2>&1 check_verified check_msgtype $SCEP_MSGTYPE_PKCSREQ check_txid check_nonce +check_digest sha256 echo OK echo "[gic next, self root]" grep ^gic.next.: scepdata | cut -f2- -d: | base64 -i -d | $toolsdir/pk7verify -r mini.crt ee.crt > results 2>&1 diff --git a/tests/tools/pk7verify.c b/tests/tools/pk7verify.c index 6dc1471..9f32f68 100644 --- a/tests/tools/pk7verify.c +++ b/tests/tools/pk7verify.c @@ -43,7 +43,7 @@ main(int argc, char **argv) int fd, i, j, root = 0, n_roots = 0, n_others = 0; ssize_t len; void *parent; - char **roots, **others, *p; + char **roots, **others, *p, *digest = NULL; char *tx = NULL, *msgtype = NULL, *pkistatus = NULL, *failinfo = NULL; unsigned char *snonce = NULL, *rnonce = NULL, *payload = NULL; size_t snonce_length = 0, rnonce_length = 0, payload_length = 0; @@ -115,7 +115,7 @@ main(int argc, char **argv) i = cm_pkcs7_verify_signed(data, len, (const char **) roots, (const char **) others, - NID_pkcs7_data, parent, + NID_pkcs7_data, parent, &digest, &tx, &msgtype, &pkistatus, &failinfo, &snonce, &snonce_length, &rnonce, &rnonce_length, @@ -125,6 +125,9 @@ main(int argc, char **argv) } else { printf("verify failed\n"); } + if (digest != NULL) { + printf("digest:%s\n", digest); + } if (tx != NULL) { printf("tx:%s\n", tx); }