From: Sam Hartman Date: Wed, 23 Dec 2009 21:09:53 +0000 (+0000) Subject: Implement support for unsigned pkinit messages X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f72bffe690f55f18ca6ed419e67f184a209b0ca5;p=thirdparty%2Fkrb5.git Implement support for unsigned pkinit messages * cms_signeddata_verify will take a ContentInfo as well as SignedData and returns an indication of whether a message is signed * Accept unsigned authpacks in the anonymous case. git-svn-id: svn://anonsvn.mit.edu/krb5/branches/anonymous@23493 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c index ba1e4344fe..46ca022b7e 100644 --- a/src/plugins/preauth/pkinit/pkinit_clnt.c +++ b/src/plugins/preauth/pkinit/pkinit_clnt.c @@ -61,7 +61,8 @@ static krb5_error_code pkinit_as_req_create(krb5_context context, pkinit_context plgctx, pkinit_req_context reqctx, krb5_timestamp ctsec, krb5_int32 cusec, krb5_ui_4 nonce, - const krb5_checksum *cksum, krb5_principal server, + const krb5_checksum *cksum, + krb5_principal client, krb5_principal server, krb5_data **as_req); static krb5_error_code @@ -139,7 +140,7 @@ pa_pkinit_gen_req(krb5_context context, nonce = request->nonce; retval = pkinit_as_req_create(context, plgctx, reqctx, ctsec, cusec, - nonce, &cksum, request->server, &out_data); + nonce, &cksum, request->client, request->server, &out_data); if (retval || !out_data->length) { pkiDebug("error %d on pkinit_as_req_create; aborting PKINIT\n", (int) retval); @@ -218,6 +219,7 @@ pkinit_as_req_create(krb5_context context, krb5_int32 cusec, krb5_ui_4 nonce, const krb5_checksum * cksum, + krb5_principal client, krb5_principal server, krb5_data ** as_req) { @@ -344,10 +346,17 @@ pkinit_as_req_create(krb5_context context, retval = ENOMEM; goto cleanup; } - retval = cms_signeddata_create(context, plgctx->cryptoctx, - reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_CLIENT, 1, - (unsigned char *)coded_auth_pack->data, coded_auth_pack->length, - &req->signedAuthPack.data, &req->signedAuthPack.length); + /*For the new protocol, we support anonymous*/ + if (krb5_principal_compare_any_realm(context, client, + krb5_anonymous_principal())) + retval = cms_contentinfo_create(context, plgctx->cryptoctx, + reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_CLIENT, + (unsigned char *)coded_auth_pack->data, coded_auth_pack->length, + &req->signedAuthPack.data, &req->signedAuthPack.length); + else retval = cms_signeddata_create(context, plgctx->cryptoctx, + reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_CLIENT, 1, + (unsigned char *)coded_auth_pack->data, coded_auth_pack->length, + &req->signedAuthPack.data, &req->signedAuthPack.length); #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)req->signedAuthPack.data, req->signedAuthPack.length, @@ -677,7 +686,7 @@ pkinit_as_rep_parse(krb5_context context, reqctx->opts->require_crl_checking, kdc_reply->u.dh_Info.dhSignedData.data, kdc_reply->u.dh_Info.dhSignedData.length, - &dh_data.data, &dh_data.length, NULL, NULL)) != 0) { + &dh_data.data, &dh_data.length, NULL, NULL, NULL)) != 0) { pkiDebug("failed to verify pkcs7 signed data\n"); goto cleanup; } diff --git a/src/plugins/preauth/pkinit/pkinit_crypto.h b/src/plugins/preauth/pkinit/pkinit_crypto.h index 718a238247..dedd8f9456 100644 --- a/src/plugins/preauth/pkinit/pkinit_crypto.h +++ b/src/plugins/preauth/pkinit/pkinit_crypto.h @@ -181,8 +181,9 @@ krb5_error_code cms_signeddata_verify receives required authorization data that contains the verified certificate chain (only used by the KDC) */ - unsigned int *authz_data_len); /* OUT - receives length of authz_data */ + unsigned int *authz_data_len, /* OUT + receives length of authz_data */ + int *is_signed /*out: is message signed*/); /* * this function creates a CMS message where eContentType is EnvelopedData diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c index bfb3bf1584..fc1b1bc386 100644 --- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c +++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c @@ -1120,8 +1120,9 @@ cms_signeddata_verify(krb5_context context, unsigned char **data, unsigned int *data_len, unsigned char **authz_data, - unsigned int *authz_data_len) -{ + unsigned int *authz_data_len, + int *is_signed) + { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; PKCS7 *p7 = NULL; BIO *out = NULL; @@ -1146,7 +1147,8 @@ cms_signeddata_verify(krb5_context context, print_buffer_bin(signed_data, signed_data_len, "/tmp/client_received_pkcs7_signeddata"); #endif - + if (is_signed) + *is_signed = 1; /* Do this early enough to create the shadow OID for pkcs7-data if needed */ oid = pkinit_pkcs7type2oid(plgctx, cms_msg_type); if (oid == NULL) @@ -1162,12 +1164,32 @@ cms_signeddata_verify(krb5_context context, goto cleanup; } - /* verify that the received message is PKCS7 SignedData message */ - if (OBJ_obj2nid(p7->type) != NID_pkcs7_signed) { - pkiDebug("Expected id-signedData PKCS7 msg (received type = %d)\n", - OBJ_obj2nid(p7->type)); - krb5_set_error_message(context, retval, "wrong oid\n"); - goto cleanup; +/*Handle the case in pkinit anonymous where we get unsigned data.*/ + if (is_signed && !OBJ_cmp( p7->type, oid)) { + unsigned char *d; + *is_signed = 0; + if (p7->d.other->type != V_ASN1_OCTET_STRING) { + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, "Invalid pkinit packet: octet string expected"); + goto cleanup; + } + *data_len = ASN1_STRING_length(p7->d.other->value.octet_string); + d = malloc(*data_len); + if (d == NULL) { + retval = ENOMEM; + goto cleanup; + } + memcpy(d, ASN1_STRING_data(p7->d.other->value.octet_string), + *data_len); + *data = d; + goto out; + } else /* verify that the received message is PKCS7 SignedData message */ + if (OBJ_obj2nid(p7->type) != NID_pkcs7_signed) { + + pkiDebug("Expected id-signedData PKCS7 msg (received type = %d)\n", + OBJ_obj2nid(p7->type)); + krb5_set_error_message(context, retval, "wrong oid\n"); + goto cleanup; } /* setup to verify X509 certificate used to sign PKCS7 message */ @@ -1370,7 +1392,6 @@ cms_signeddata_verify(krb5_context context, for (size = 0;;) { int remain; retval = ENOMEM; - if ((*data = realloc(*data, size + 1024 * 10)) == NULL) goto cleanup; remain = BIO_read(out, &((*data)[size]), 1024 * 10); @@ -1703,7 +1724,7 @@ cms_envelopeddata_verify(krb5_context context, id_cryptoctx, msg_type, require_crl_checking, vfy_buf, vfy_buf_len, - data, data_len, NULL, NULL); + data, data_len, NULL, NULL, NULL); if (!retval) pkiDebug("PKCS7 Verification Success\n"); diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c index fcd3e78699..db9b568aa3 100644 --- a/src/plugins/preauth/pkinit/pkinit_srv.c +++ b/src/plugins/preauth/pkinit/pkinit_srv.c @@ -314,6 +314,7 @@ pkinit_server_verify_padata(krb5_context context, krb5_authdata **my_authz_data = NULL, *pkinit_authz_data = NULL; krb5_kdc_req *tmp_as_req = NULL; krb5_data k5data; + int is_signed = 1; krb5_keyblock *armor_key; pkiDebug("pkinit_verify_padata: entered!\n"); @@ -367,7 +368,7 @@ pkinit_server_verify_padata(krb5_context context, plgctx->opts->require_crl_checking, reqp->signedAuthPack.data, reqp->signedAuthPack.length, &authp_data.data, &authp_data.length, &krb5_authz.data, - &krb5_authz.length); + &krb5_authz.length, &is_signed); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: @@ -389,7 +390,7 @@ pkinit_server_verify_padata(krb5_context context, plgctx->opts->require_crl_checking, reqp9->signedAuthPack.data, reqp9->signedAuthPack.length, &authp_data.data, &authp_data.length, &krb5_authz.data, - &krb5_authz.length); + &krb5_authz.length, NULL); break; default: pkiDebug("unrecognized pa_type = %d\n", data->pa_type); @@ -400,28 +401,35 @@ pkinit_server_verify_padata(krb5_context context, pkiDebug("pkcs7_signeddata_verify failed\n"); goto cleanup; } + if (is_signed) { - retval = verify_client_san(context, plgctx, reqctx, request->client, - &valid_san); - if (retval) - goto cleanup; - if (!valid_san) { - pkiDebug("%s: did not find an acceptable SAN in user certificate\n", - __FUNCTION__); - retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; - goto cleanup; - } - retval = verify_client_eku(context, plgctx, reqctx, &valid_eku); - if (retval) - goto cleanup; + retval = verify_client_san(context, plgctx, reqctx, request->client, + &valid_san); + if (retval) + goto cleanup; + if (!valid_san) { + pkiDebug("%s: did not find an acceptable SAN in user certificate\n", + __FUNCTION__); + retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; + goto cleanup; + } + retval = verify_client_eku(context, plgctx, reqctx, &valid_eku); + if (retval) + goto cleanup; - if (!valid_eku) { - pkiDebug("%s: did not find an acceptable EKU in user certificate\n", - __FUNCTION__); - retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE; - goto cleanup; + if (!valid_eku) { + pkiDebug("%s: did not find an acceptable EKU in user certificate\n", + __FUNCTION__); + retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE; + goto cleanup; + } + } else { /*!is_signed*/ + if (!krb5_principal_compare( context, request->client, krb5_anonymous_principal())) { + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_set_error_message(context, retval, "Pkinit request not signed, but client not anonymous."); + goto cleanup; + } } - #ifdef DEBUG_ASN1 print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack"); #endif @@ -446,6 +454,11 @@ pkinit_server_verify_padata(krb5_context context, pkiDebug("bad dh parameters\n"); goto cleanup; } + } else if (!is_signed) { + /*Anonymous pkinit requires DH*/ + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_set_error_message(context, retval, "Anonymous pkinit without DH public value not supported."); + goto cleanup; } /* * The KDC may have modified the request after decoding it.