From f79ba7eca6846552fcd00d42f4b8f67e05af2e40 Mon Sep 17 00:00:00 2001 From: Sam Hartman Date: Mon, 19 Sep 2011 00:34:36 +0000 Subject: [PATCH] pkinit: client: Use SignedData for anonymous Per RFc 6112 use SignedData not ContentInfo for anonymous when the KDC offers support for PKINIT_KX padata. ticket: 6962 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25186 dc483132-0cff-0310-8789-dd5450dbe970 --- src/plugins/preauth/pkinit/pkinit.h | 3 +- src/plugins/preauth/pkinit/pkinit_clnt.c | 37 +- .../preauth/pkinit/pkinit_crypto_openssl.c | 401 +++++++++--------- 3 files changed, 234 insertions(+), 207 deletions(-) diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h index 6ba399de1..8928e9d80 100644 --- a/src/plugins/preauth/pkinit/pkinit.h +++ b/src/plugins/preauth/pkinit/pkinit.h @@ -219,12 +219,13 @@ typedef struct _pkinit_context *pkinit_context; * Client's per-request context */ struct _pkinit_req_context { - int magic; + unsigned int magic; pkinit_req_crypto_context cryptoctx; pkinit_req_opts *opts; pkinit_identity_crypto_context idctx; pkinit_identity_opts *idopts; krb5_preauthtype pa_type; + int rfc6112_kdc; }; typedef struct _pkinit_req_context *pkinit_req_context; diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c index cf95bd57b..85b016737 100644 --- a/src/plugins/preauth/pkinit/pkinit_clnt.c +++ b/src/plugins/preauth/pkinit/pkinit_clnt.c @@ -57,6 +57,23 @@ */ int longhorn = 0; /* Talking to a Longhorn server? */ +/** + * Return true if we should use ContentInfo rather than SignedData. This + * happens if we are talking to what might be an old (pre-6112) MIT KDC and + * we're using anonymous. + */ +static int +use_content_info(krb5_context context, pkinit_req_context req, + krb5_principal client) +{ + if (req->rfc6112_kdc) + return 0; + if (krb5_principal_compare_any_realm(context, client, + krb5_anonymous_principal())) + return 1; + return 0; + } + static krb5_error_code pkinit_as_req_create(krb5_context context, pkinit_context plgctx, pkinit_req_context reqctx, krb5_timestamp ctsec, @@ -347,9 +364,7 @@ pkinit_as_req_create(krb5_context context, retval = ENOMEM; goto cleanup; } - /* For the new protocol, we support anonymous. */ - if (krb5_principal_compare_any_realm(context, client, - krb5_anonymous_principal())) { + if (use_content_info(context, reqctx, client)) retval = cms_contentinfo_create(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_CLIENT, (unsigned char *) @@ -357,7 +372,7 @@ pkinit_as_req_create(krb5_context context, coded_auth_pack->length, &req->signedAuthPack.data, &req->signedAuthPack.length); - } else { + else { retval = cms_signeddata_create(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_CLIENT, 1, @@ -1012,7 +1027,10 @@ pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata, return EINVAL; switch ((int) in_padata->pa_type) { - case KRB5_PADATA_PK_AS_REQ: + case KRB5_PADATA_PKINIT_KX: + reqctx->rfc6112_kdc = 1; + return 0; + case KRB5_PADATA_PK_AS_REQ: pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n"); processing_request = 1; break; @@ -1176,14 +1194,23 @@ cleanup: static int pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype) { + if (patype == KRB5_PADATA_PKINIT_KX) + return PA_INFO|PA_PSEUDO; return PA_REAL; } +/* + * We want to be notified about KRB5_PADATA_PKINIT_KX in addition to the actual + * pkinit patypes because RFC 6112 requires anonymous KDCs to send it. We use + * that to determine whether to use the broken MIT 1.9 behavior of sending + * ContentInfo rather than SignedData or the RFC 6112 behavior + */ static krb5_preauthtype supported_client_pa_types[] = { KRB5_PADATA_PK_AS_REP, KRB5_PADATA_PK_AS_REQ, KRB5_PADATA_PK_AS_REP_OLD, KRB5_PADATA_PK_AS_REQ_OLD, + KRB5_PADATA_PKINIT_KX, 0 }; diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c index 56fc3fe8f..4247524ae 100644 --- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c +++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c @@ -858,11 +858,6 @@ cms_signeddata_create(krb5_context context, X509 *cert = NULL; ASN1_OBJECT *oid = NULL; - if (id_cryptoctx->my_certs == NULL) { - krb5_set_error_message(context, EINVAL, _("cms_signdata_create called " - "with no certificates")); - return EINVAL; - } /* Start creating PKCS7 data. */ if ((p7 = PKCS7_new()) == NULL) goto cleanup; @@ -874,206 +869,208 @@ cms_signeddata_create(krb5_context context, if (!ASN1_INTEGER_set(p7s->version, 3)) goto cleanup; - /* create a cert chain that has at least the signer's certificate */ - if ((cert_stack = sk_X509_new_null()) == NULL) + /* pick the correct oid for the eContentInfo */ + oid = pkinit_pkcs7type2oid(plg_cryptoctx, cms_msg_type); + if (oid == NULL) goto cleanup; - cert = sk_X509_value(id_cryptoctx->my_certs, id_cryptoctx->cert_index); - if (!include_certchain) { - pkiDebug("only including signer's certificate\n"); - sk_X509_push(cert_stack, X509_dup(cert)); - } else { - /* create a cert chain */ - X509_STORE *certstore = NULL; - X509_STORE_CTX certctx; - STACK_OF(X509) *certstack = NULL; - char buf[DN_BUF_LEN]; - unsigned int i = 0, size = 0; - - if ((certstore = X509_STORE_new()) == NULL) - goto cleanup; - pkiDebug("building certificate chain\n"); - X509_STORE_set_verify_cb_func(certstore, openssl_callback); - X509_STORE_CTX_init(&certctx, certstore, cert, - id_cryptoctx->intermediateCAs); - X509_STORE_CTX_trusted_stack(&certctx, id_cryptoctx->trustedCAs); - if (!X509_verify_cert(&certctx)) { - pkiDebug("failed to create a certificate chain: %s\n", - X509_verify_cert_error_string(X509_STORE_CTX_get_error(&certctx))); - if (!sk_X509_num(id_cryptoctx->trustedCAs)) - pkiDebug("No trusted CAs found. Check your X509_anchors\n"); + if (id_cryptoctx->my_certs != NULL) { + /* create a cert chain that has at least the signer's certificate */ + if ((cert_stack = sk_X509_new_null()) == NULL) goto cleanup; - } - certstack = X509_STORE_CTX_get1_chain(&certctx); - size = sk_X509_num(certstack); - pkiDebug("size of certificate chain = %d\n", size); - for(i = 0; i < size - 1; i++) { - X509 *x = sk_X509_value(certstack, i); - X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf)); - pkiDebug("cert #%d: %s\n", i, buf); - sk_X509_push(cert_stack, X509_dup(x)); - } - X509_STORE_CTX_cleanup(&certctx); - X509_STORE_free(certstore); - sk_X509_pop_free(certstack, X509_free); - } - p7s->cert = cert_stack; - - /* fill-in PKCS7_SIGNER_INFO */ - if ((p7si = PKCS7_SIGNER_INFO_new()) == NULL) - goto cleanup; - if (!ASN1_INTEGER_set(p7si->version, 1)) - goto cleanup; - if (!X509_NAME_set(&p7si->issuer_and_serial->issuer, - X509_get_issuer_name(cert))) - goto cleanup; - /* because ASN1_INTEGER_set is used to set a 'long' we will do - * things the ugly way. */ - M_ASN1_INTEGER_free(p7si->issuer_and_serial->serial); - if (!(p7si->issuer_and_serial->serial = - M_ASN1_INTEGER_dup(X509_get_serialNumber(cert)))) - goto cleanup; - /* will not fill-out EVP_PKEY because it's on the smartcard */ + cert = sk_X509_value(id_cryptoctx->my_certs, id_cryptoctx->cert_index); + if (!include_certchain) { + pkiDebug("only including signer's certificate\n"); + sk_X509_push(cert_stack, X509_dup(cert)); + } else { + /* create a cert chain */ + X509_STORE *certstore = NULL; + X509_STORE_CTX certctx; + STACK_OF(X509) *certstack = NULL; + char buf[DN_BUF_LEN]; + unsigned int i = 0, size = 0; + + if ((certstore = X509_STORE_new()) == NULL) + goto cleanup; + pkiDebug("building certificate chain\n"); + X509_STORE_set_verify_cb_func(certstore, openssl_callback); + X509_STORE_CTX_init(&certctx, certstore, cert, + id_cryptoctx->intermediateCAs); + X509_STORE_CTX_trusted_stack(&certctx, id_cryptoctx->trustedCAs); + if (!X509_verify_cert(&certctx)) { + pkiDebug("failed to create a certificate chain: %s\n", + X509_verify_cert_error_string(X509_STORE_CTX_get_error(&certctx))); + if (!sk_X509_num(id_cryptoctx->trustedCAs)) + pkiDebug("No trusted CAs found. Check your X509_anchors\n"); + goto cleanup; + } + certstack = X509_STORE_CTX_get1_chain(&certctx); + size = sk_X509_num(certstack); + pkiDebug("size of certificate chain = %d\n", size); + for(i = 0; i < size - 1; i++) { + X509 *x = sk_X509_value(certstack, i); + X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf)); + pkiDebug("cert #%d: %s\n", i, buf); + sk_X509_push(cert_stack, X509_dup(x)); + } + X509_STORE_CTX_cleanup(&certctx); + X509_STORE_free(certstore); + sk_X509_pop_free(certstack, X509_free); + } + p7s->cert = cert_stack; - /* Set digest algs */ - p7si->digest_alg->algorithm = OBJ_nid2obj(NID_sha1); + /* fill-in PKCS7_SIGNER_INFO */ + if ((p7si = PKCS7_SIGNER_INFO_new()) == NULL) + goto cleanup; + if (!ASN1_INTEGER_set(p7si->version, 1)) + goto cleanup; + if (!X509_NAME_set(&p7si->issuer_and_serial->issuer, + X509_get_issuer_name(cert))) + goto cleanup; + /* because ASN1_INTEGER_set is used to set a 'long' we will do + * things the ugly way. */ + M_ASN1_INTEGER_free(p7si->issuer_and_serial->serial); + if (!(p7si->issuer_and_serial->serial = + M_ASN1_INTEGER_dup(X509_get_serialNumber(cert)))) + goto cleanup; - if (p7si->digest_alg->parameter != NULL) - ASN1_TYPE_free(p7si->digest_alg->parameter); - if ((p7si->digest_alg->parameter = ASN1_TYPE_new()) == NULL) - goto cleanup; - p7si->digest_alg->parameter->type = V_ASN1_NULL; + /* will not fill-out EVP_PKEY because it's on the smartcard */ - /* Set sig algs */ - if (p7si->digest_enc_alg->parameter != NULL) - ASN1_TYPE_free(p7si->digest_enc_alg->parameter); - p7si->digest_enc_alg->algorithm = OBJ_nid2obj(NID_sha1WithRSAEncryption); - if (!(p7si->digest_enc_alg->parameter = ASN1_TYPE_new())) - goto cleanup; - p7si->digest_enc_alg->parameter->type = V_ASN1_NULL; + /* Set digest algs */ + p7si->digest_alg->algorithm = OBJ_nid2obj(NID_sha1); - /* pick the correct oid for the eContentInfo */ - oid = pkinit_pkcs7type2oid(plg_cryptoctx, cms_msg_type); - if (oid == NULL) - goto cleanup; + if (p7si->digest_alg->parameter != NULL) + ASN1_TYPE_free(p7si->digest_alg->parameter); + if ((p7si->digest_alg->parameter = ASN1_TYPE_new()) == NULL) + goto cleanup; + p7si->digest_alg->parameter->type = V_ASN1_NULL; - if (cms_msg_type == CMS_SIGN_DRAFT9) { - /* don't include signed attributes for pa-type 15 request */ - abuf = data; - alen = data_len; - } else { - /* add signed attributes */ - /* compute sha1 digest over the EncapsulatedContentInfo */ - EVP_MD_CTX_init(&ctx); - EVP_DigestInit_ex(&ctx, EVP_sha1(), NULL); - EVP_DigestUpdate(&ctx, data, data_len); - md_tmp = EVP_MD_CTX_md(&ctx); - EVP_DigestFinal_ex(&ctx, md_data, &md_len); - - /* create a message digest attr */ - digest_attr = ASN1_OCTET_STRING_new(); - ASN1_OCTET_STRING_set(digest_attr, md_data, (int)md_len); - PKCS7_add_signed_attribute(p7si, NID_pkcs9_messageDigest, - V_ASN1_OCTET_STRING, (char *) digest_attr); - - /* create a content-type attr */ - PKCS7_add_signed_attribute(p7si, NID_pkcs9_contentType, - V_ASN1_OBJECT, oid); - - /* create the signature over signed attributes. get DER encoded value */ - /* This is the place where smartcard signature needs to be calculated */ - sk = p7si->auth_attr; - alen = ASN1_item_i2d((ASN1_VALUE *) sk, &abuf, - ASN1_ITEM_rptr(PKCS7_ATTR_SIGN)); - if (abuf == NULL) - goto cleanup2; - } + /* Set sig algs */ + if (p7si->digest_enc_alg->parameter != NULL) + ASN1_TYPE_free(p7si->digest_enc_alg->parameter); + p7si->digest_enc_alg->algorithm = OBJ_nid2obj(NID_sha1WithRSAEncryption); + if (!(p7si->digest_enc_alg->parameter = ASN1_TYPE_new())) + goto cleanup; + p7si->digest_enc_alg->parameter->type = V_ASN1_NULL; + + if (cms_msg_type == CMS_SIGN_DRAFT9){ + /* don't include signed attributes for pa-type 15 request */ + abuf = data; + alen = data_len; + } else { + /* add signed attributes */ + /* compute sha1 digest over the EncapsulatedContentInfo */ + EVP_MD_CTX_init(&ctx); + EVP_DigestInit_ex(&ctx, EVP_sha1(), NULL); + EVP_DigestUpdate(&ctx, data, data_len); + md_tmp = EVP_MD_CTX_md(&ctx); + EVP_DigestFinal_ex(&ctx, md_data, &md_len); + + /* create a message digest attr */ + digest_attr = ASN1_OCTET_STRING_new(); + ASN1_OCTET_STRING_set(digest_attr, md_data, (int)md_len); + PKCS7_add_signed_attribute(p7si, NID_pkcs9_messageDigest, + V_ASN1_OCTET_STRING, (char *) digest_attr); + + /* create a content-type attr */ + PKCS7_add_signed_attribute(p7si, NID_pkcs9_contentType, + V_ASN1_OBJECT, oid); + + /* create the signature over signed attributes. get DER encoded value */ + /* This is the place where smartcard signature needs to be calculated */ + sk = p7si->auth_attr; + alen = ASN1_item_i2d((ASN1_VALUE *) sk, &abuf, + ASN1_ITEM_rptr(PKCS7_ATTR_SIGN)); + if (abuf == NULL) + goto cleanup2; + } /* signed attributes */ #ifndef WITHOUT_PKCS11 - /* Some tokens can only do RSAEncryption without sha1 hash */ - /* to compute sha1WithRSAEncryption, encode the algorithm ID for the hash - * function and the hash value into an ASN.1 value of type DigestInfo - * DigestInfo::=SEQUENCE { - * digestAlgorithm AlgorithmIdentifier, - * digest OCTET STRING } - */ - if (id_cryptoctx->pkcs11_method == 1 && - id_cryptoctx->mech == CKM_RSA_PKCS) { - pkiDebug("mech = CKM_RSA_PKCS\n"); - EVP_MD_CTX_init(&ctx2); - /* if this is not draft9 request, include digest signed attribute */ - if (cms_msg_type != CMS_SIGN_DRAFT9) - EVP_DigestInit_ex(&ctx2, md_tmp, NULL); - else - EVP_DigestInit_ex(&ctx2, EVP_sha1(), NULL); - EVP_DigestUpdate(&ctx2, abuf, alen); - EVP_DigestFinal_ex(&ctx2, md_data2, &md_len2); - - alg = X509_ALGOR_new(); - if (alg == NULL) - goto cleanup2; - alg->algorithm = OBJ_nid2obj(NID_sha1); - alg->parameter = NULL; - alg_len = i2d_X509_ALGOR(alg, NULL); - alg_buf = malloc(alg_len); - if (alg_buf == NULL) - goto cleanup2; - - digest = ASN1_OCTET_STRING_new(); - if (digest == NULL) - goto cleanup2; - ASN1_OCTET_STRING_set(digest, md_data2, (int)md_len2); - digest_len = i2d_ASN1_OCTET_STRING(digest, NULL); - digest_buf = malloc(digest_len); - if (digest_buf == NULL) - goto cleanup2; - - digestInfo_len = ASN1_object_size(1, (int)(alg_len + digest_len), - V_ASN1_SEQUENCE); - y = digestInfo_buf = malloc(digestInfo_len); - if (digestInfo_buf == NULL) - goto cleanup2; - ASN1_put_object(&y, 1, (int)(alg_len + digest_len), V_ASN1_SEQUENCE, - V_ASN1_UNIVERSAL); - i2d_X509_ALGOR(alg, &y); - i2d_ASN1_OCTET_STRING(digest, &y); + /* Some tokens can only do RSAEncryption without sha1 hash */ + /* to compute sha1WithRSAEncryption, encode the algorithm ID for the hash + * function and the hash value into an ASN.1 value of type DigestInfo + * DigestInfo::=SEQUENCE { + * digestAlgorithm AlgorithmIdentifier, + * digest OCTET STRING } + */ + if (id_cryptoctx->pkcs11_method == 1 && + id_cryptoctx->mech == CKM_RSA_PKCS) { + pkiDebug("mech = CKM_RSA_PKCS\n"); + EVP_MD_CTX_init(&ctx2); + /* if this is not draft9 request, include digest signed attribute */ + if (cms_msg_type != CMS_SIGN_DRAFT9) + EVP_DigestInit_ex(&ctx2, md_tmp, NULL); + else + EVP_DigestInit_ex(&ctx2, EVP_sha1(), NULL); + EVP_DigestUpdate(&ctx2, abuf, alen); + EVP_DigestFinal_ex(&ctx2, md_data2, &md_len2); + + alg = X509_ALGOR_new(); + if (alg == NULL) + goto cleanup2; + alg->algorithm = OBJ_nid2obj(NID_sha1); + alg->parameter = NULL; + alg_len = i2d_X509_ALGOR(alg, NULL); + alg_buf = malloc(alg_len); + if (alg_buf == NULL) + goto cleanup2; + + digest = ASN1_OCTET_STRING_new(); + if (digest == NULL) + goto cleanup2; + ASN1_OCTET_STRING_set(digest, md_data2, (int)md_len2); + digest_len = i2d_ASN1_OCTET_STRING(digest, NULL); + digest_buf = malloc(digest_len); + if (digest_buf == NULL) + goto cleanup2; + + digestInfo_len = ASN1_object_size(1, (int)(alg_len + digest_len), + V_ASN1_SEQUENCE); + y = digestInfo_buf = malloc(digestInfo_len); + if (digestInfo_buf == NULL) + goto cleanup2; + ASN1_put_object(&y, 1, (int)(alg_len + digest_len), V_ASN1_SEQUENCE, + V_ASN1_UNIVERSAL); + i2d_X509_ALGOR(alg, &y); + i2d_ASN1_OCTET_STRING(digest, &y); #ifdef DEBUG_SIG - pkiDebug("signing buffer\n"); - print_buffer(digestInfo_buf, digestInfo_len); - print_buffer_bin(digestInfo_buf, digestInfo_len, "/tmp/pkcs7_tosign"); + pkiDebug("signing buffer\n"); + print_buffer(digestInfo_buf, digestInfo_len); + print_buffer_bin(digestInfo_buf, digestInfo_len, "/tmp/pkcs7_tosign"); #endif - retval = pkinit_sign_data(context, id_cryptoctx, digestInfo_buf, - digestInfo_len, &sig, &sig_len); - } else + retval = pkinit_sign_data(context, id_cryptoctx, digestInfo_buf, + digestInfo_len, &sig, &sig_len); + } else #endif - { - pkiDebug("mech = %s\n", - id_cryptoctx->pkcs11_method == 1 ? "CKM_SHA1_RSA_PKCS" : "FS"); - retval = pkinit_sign_data(context, id_cryptoctx, abuf, alen, - &sig, &sig_len); - } + { + pkiDebug("mech = %s\n", + id_cryptoctx->pkcs11_method == 1 ? "CKM_SHA1_RSA_PKCS" : "FS"); + retval = pkinit_sign_data(context, id_cryptoctx, abuf, alen, + &sig, &sig_len); + } #ifdef DEBUG_SIG - print_buffer(sig, sig_len); + print_buffer(sig, sig_len); #endif - if (cms_msg_type != CMS_SIGN_DRAFT9) - free(abuf); - if (retval) - goto cleanup2; + if (cms_msg_type != CMS_SIGN_DRAFT9 ) + free(abuf); + if (retval) + goto cleanup2; - /* Add signature */ - if (!ASN1_STRING_set(p7si->enc_digest, (unsigned char *) sig, - (int)sig_len)) { - unsigned long err = ERR_peek_error(); - retval = KRB5KDC_ERR_PREAUTH_FAILED; - krb5_set_error_message(context, retval, "%s\n", - ERR_error_string(err, NULL)); - pkiDebug("failed to add a signed digest attribute\n"); - goto cleanup2; - } - /* adder signer_info to pkcs7 signed */ - if (!PKCS7_add_signer(p7, p7si)) - goto cleanup2; + /* Add signature */ + if (!ASN1_STRING_set(p7si->enc_digest, (unsigned char *) sig, + (int)sig_len)) { + unsigned long err = ERR_peek_error(); + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_set_error_message(context, retval, "%s\n", + ERR_error_string(err, NULL)); + pkiDebug("failed to add a signed digest attribute\n"); + goto cleanup2; + } + /* adder signer_info to pkcs7 signed */ + if (!PKCS7_add_signer(p7, p7si)) + goto cleanup2; + } /* we have a certificate */ /* start on adding data to the pkcs7 signed */ retval = create_contentinfo(context, plg_cryptoctx, oid, @@ -1123,21 +1120,23 @@ cms_signeddata_create(krb5_context context, #endif cleanup2: - if (cms_msg_type != CMS_SIGN_DRAFT9) - EVP_MD_CTX_cleanup(&ctx); + if (p7si) { + if (cms_msg_type != CMS_SIGN_DRAFT9) + EVP_MD_CTX_cleanup(&ctx); #ifndef WITHOUT_PKCS11 - if (id_cryptoctx->pkcs11_method == 1 && - id_cryptoctx->mech == CKM_RSA_PKCS) { - EVP_MD_CTX_cleanup(&ctx2); - free(digest_buf); - free(digestInfo_buf); - free(alg_buf); - if (digest != NULL) - ASN1_OCTET_STRING_free(digest); - } + if (id_cryptoctx->pkcs11_method == 1 && + id_cryptoctx->mech == CKM_RSA_PKCS) { + EVP_MD_CTX_cleanup(&ctx2); + free(digest_buf); + free(digestInfo_buf); + free(alg_buf); + if (digest != NULL) + ASN1_OCTET_STRING_free(digest); + } #endif - if (alg != NULL) - X509_ALGOR_free(alg); + if (alg != NULL) + X509_ALGOR_free(alg); + } cleanup: if (p7 != NULL) PKCS7_free(p7); -- 2.26.2