krb5_error_code _kdc_pk_rd_padata(krb5_context context, krb5_kdc_configuration *config, const KDC_REQ *req, const PA_DATA *pa, hdb_entry_ex *client, pk_client_params **ret_params) { pk_client_params *cp; krb5_error_code ret; heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL }; krb5_data eContent = { 0, NULL }; krb5_data signed_content = { 0, NULL }; const char *type = "unknown type"; hx509_certs trust_anchors; int have_data = 0; const HDB_Ext_PKINIT_cert *pc; *ret_params = NULL; if (!config->enable_pkinit) { kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled"); krb5_clear_error_message(context); return 0; } cp = calloc(1, sizeof(*cp)); if (cp == NULL) { krb5_clear_error_message(context); ret = ENOMEM; goto out; } ret = hx509_certs_init(context->hx509ctx, "MEMORY:trust-anchors", 0, NULL, &trust_anchors); if (ret) { krb5_set_error_message(context, ret, "failed to create trust anchors"); goto out; } ret = hx509_certs_merge(context->hx509ctx, trust_anchors, kdc_identity->anchors); if (ret) { hx509_certs_free(&trust_anchors); krb5_set_error_message(context, ret, "failed to create verify context"); goto out; } /* Add any registered certificates for this client as trust anchors */ ret = hdb_entry_get_pkinit_cert(&client->entry, &pc); if (ret == 0 && pc != NULL) { hx509_cert cert; unsigned int i; for (i = 0; i < pc->len; i++) { cert = hx509_cert_init_data(context->hx509ctx, pc->val[i].cert.data, pc->val[i].cert.length, NULL); if (cert == NULL) continue; hx509_certs_add(context->hx509ctx, trust_anchors, cert); hx509_cert_free(cert); } } ret = hx509_verify_init_ctx(context->hx509ctx, &cp->verify_ctx); if (ret) { hx509_certs_free(&trust_anchors); krb5_set_error_message(context, ret, "failed to create verify context"); goto out; } hx509_verify_set_time(cp->verify_ctx, kdc_time); hx509_verify_attach_anchors(cp->verify_ctx, trust_anchors); hx509_certs_free(&trust_anchors); if (config->pkinit_allow_proxy_certs) hx509_verify_set_proxy_certificate(cp->verify_ctx, 1); if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) { PA_PK_AS_REQ_Win2k r; type = "PK-INIT-Win2k"; if (_kdc_is_anon_request(&req->req_body)) { ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED; krb5_set_error_message(context, ret, "Anon not supported in RSA mode"); goto out; } ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data, pa->padata_value.length, &r, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode " "PK-AS-REQ-Win2k: %d", ret); goto out; } ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack, &contentInfoOid, &signed_content, &have_data); free_PA_PK_AS_REQ_Win2k(&r); if (ret) { krb5_set_error_message(context, ret, "Can't unwrap ContentInfo(win): %d", ret); goto out; } } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) { PA_PK_AS_REQ r; type = "PK-INIT-IETF"; ret = decode_PA_PK_AS_REQ(pa->padata_value.data, pa->padata_value.length, &r, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode PK-AS-REQ: %d", ret); goto out; } /* XXX look at r.kdcPkId */ if (r.trustedCertifiers) { ExternalPrincipalIdentifiers *edi = r.trustedCertifiers; unsigned int i, maxedi; ret = hx509_certs_init(context->hx509ctx, "MEMORY:client-anchors", 0, NULL, &cp->client_anchors); if (ret) { krb5_set_error_message(context, ret, "Can't allocate client anchors: %d", ret); goto out; } /* * If the client sent more then 10 EDI, don't bother * looking more then 10 of performance reasons. */ maxedi = edi->len; if (maxedi > 10) maxedi = 10; for (i = 0; i < maxedi; i++) { IssuerAndSerialNumber iasn; hx509_query *q; hx509_cert cert; size_t size; if (edi->val[i].issuerAndSerialNumber == NULL) continue; ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) { krb5_set_error_message(context, ret, "Failed to allocate hx509_query"); goto out; } ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data, edi->val[i].issuerAndSerialNumber->length, &iasn, &size); if (ret) { hx509_query_free(context->hx509ctx, q); continue; } ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber); free_IssuerAndSerialNumber(&iasn); if (ret) { hx509_query_free(context->hx509ctx, q); continue; } ret = hx509_certs_find(context->hx509ctx, kdc_identity->certs, q, &cert); hx509_query_free(context->hx509ctx, q); if (ret) continue; hx509_certs_add(context->hx509ctx, cp->client_anchors, cert); hx509_cert_free(cert); } } ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack, &contentInfoOid, &signed_content, &have_data); free_PA_PK_AS_REQ(&r); if (ret) { krb5_set_error_message(context, ret, "Can't unwrap ContentInfo: %d", ret); goto out; } } else { krb5_clear_error_message(context); ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP; goto out; } ret = der_heim_oid_cmp(&contentInfoOid, &asn1_oid_id_pkcs7_signedData); if (ret != 0) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "PK-AS-REQ-Win2k invalid content type oid"); goto out; } if (!have_data) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "PK-AS-REQ-Win2k no signed auth pack"); goto out; } { hx509_certs signer_certs; int flags = HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; /* BTMM */ if (_kdc_is_anon_request(&req->req_body)) flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER; ret = hx509_cms_verify_signed(context->hx509ctx, cp->verify_ctx, flags, signed_content.data, signed_content.length, NULL, kdc_identity->certpool, &eContentType, &eContent, &signer_certs); if (ret) { char *s = hx509_get_error_string(context->hx509ctx, ret); krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d", s, ret); free(s); goto out; } if (signer_certs) { ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &cp->cert); hx509_certs_free(&signer_certs); } if (ret) goto out; } /* Signature is correct, now verify the signed message */ if (der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkcs7_data) != 0 && der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkauthdata) != 0) { ret = KRB5_BADMSGTYPE; krb5_set_error_message(context, ret, "got wrong oid for pkauthdata"); goto out; } if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) { AuthPack_Win2k ap; ret = decode_AuthPack_Win2k(eContent.data, eContent.length, &ap, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode AuthPack: %d", ret); goto out; } ret = pk_check_pkauthenticator_win2k(context, &ap.pkAuthenticator, req); if (ret) { free_AuthPack_Win2k(&ap); goto out; } cp->type = PKINIT_WIN2K; cp->nonce = ap.pkAuthenticator.nonce; if (ap.clientPublicValue) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "DH not supported for windows"); goto out; } free_AuthPack_Win2k(&ap); } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) { AuthPack ap; ret = decode_AuthPack(eContent.data, eContent.length, &ap, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode AuthPack: %d", ret); free_AuthPack(&ap); goto out; } if (_kdc_is_anon_request(&req->req_body) && ap.clientPublicValue == NULL) { free_AuthPack(&ap); ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED; krb5_set_error_message(context, ret, "Anon not supported in RSA mode"); goto out; } ret = pk_check_pkauthenticator(context, &ap.pkAuthenticator, req); if (ret) { free_AuthPack(&ap); goto out; } cp->type = PKINIT_27; cp->nonce = ap.pkAuthenticator.nonce; if (ap.clientPublicValue) { if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_dhpublicnumber) == 0) { cp->keyex = USE_DH; ret = get_dh_param(context, config, ap.clientPublicValue, cp); } else if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_ecPublicKey) == 0) { cp->keyex = USE_ECDH; ret = _kdc_get_ecdh_param(context, config, ap.clientPublicValue, &cp->u.ecdh.public_key); } else { ret = KRB5_BADMSGTYPE; krb5_set_error_message(context, ret, "PKINIT unknown DH mechanism"); } if (ret) { free_AuthPack(&ap); goto out; } } else cp->keyex = USE_RSA; ret = hx509_peer_info_alloc(context->hx509ctx, &cp->peer); if (ret) { free_AuthPack(&ap); goto out; } if (ap.supportedCMSTypes) { ret = hx509_peer_info_set_cms_algs(context->hx509ctx, cp->peer, ap.supportedCMSTypes->val, ap.supportedCMSTypes->len); if (ret) { free_AuthPack(&ap); goto out; } } else { /* assume old client */ hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer, hx509_crypto_des_rsdi_ede3_cbc()); hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer, hx509_signature_rsa_with_sha1()); hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer, hx509_signature_sha1()); } free_AuthPack(&ap); } else krb5_abortx(context, "internal pkinit error"); kdc_log(context, config, 0, "PK-INIT request of type %s", type); out: if (ret) krb5_warn(context, ret, "PKINIT"); if (signed_content.data) free(signed_content.data); krb5_data_free(&eContent); der_free_oid(&eContentType); der_free_oid(&contentInfoOid); if (ret) { _kdc_pk_free_client_param(context, cp); } else *ret_params = cp; return ret; }
krb5_error_code _kdc_pk_rd_padata(krb5_context context, krb5_kdc_configuration *config, const KDC_REQ *req, const PA_DATA *pa, pk_client_params **ret_params) { pk_client_params *client_params; krb5_error_code ret; heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL }; krb5_data eContent = { 0, NULL }; krb5_data signed_content = { 0, NULL }; const char *type = "unknown type"; int have_data = 0; *ret_params = NULL; if (!config->enable_pkinit) { kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled"); krb5_clear_error_message(context); return 0; } hx509_verify_set_time(kdc_identity->verify_ctx, kdc_time); client_params = calloc(1, sizeof(*client_params)); if (client_params == NULL) { krb5_clear_error_message(context); ret = ENOMEM; goto out; } if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) { PA_PK_AS_REQ_Win2k r; type = "PK-INIT-Win2k"; ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data, pa->padata_value.length, &r, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode " "PK-AS-REQ-Win2k: %d", ret); goto out; } ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack, &contentInfoOid, &signed_content, &have_data); free_PA_PK_AS_REQ_Win2k(&r); if (ret) { krb5_set_error_message(context, ret, "Can't decode PK-AS-REQ: %d", ret); goto out; } } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) { PA_PK_AS_REQ r; type = "PK-INIT-IETF"; ret = decode_PA_PK_AS_REQ(pa->padata_value.data, pa->padata_value.length, &r, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode PK-AS-REQ: %d", ret); goto out; } /* XXX look at r.kdcPkId */ if (r.trustedCertifiers) { ExternalPrincipalIdentifiers *edi = r.trustedCertifiers; unsigned int i; ret = hx509_certs_init(kdc_identity->hx509ctx, "MEMORY:client-anchors", 0, NULL, &client_params->client_anchors); if (ret) { krb5_set_error_message(context, ret, "Can't allocate client anchors: %d", ret); goto out; } for (i = 0; i < edi->len; i++) { IssuerAndSerialNumber iasn; hx509_query *q; hx509_cert cert; size_t size; if (edi->val[i].issuerAndSerialNumber == NULL) continue; ret = hx509_query_alloc(kdc_identity->hx509ctx, &q); if (ret) { krb5_set_error_message(context, ret, "Failed to allocate hx509_query"); goto out; } ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data, edi->val[i].issuerAndSerialNumber->length, &iasn, &size); if (ret) { hx509_query_free(kdc_identity->hx509ctx, q); continue; } ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber); free_IssuerAndSerialNumber(&iasn); if (ret) continue; ret = hx509_certs_find(kdc_identity->hx509ctx, kdc_identity->certs, q, &cert); hx509_query_free(kdc_identity->hx509ctx, q); if (ret) continue; hx509_certs_add(kdc_identity->hx509ctx, client_params->client_anchors, cert); hx509_cert_free(cert); } } ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack, &contentInfoOid, &signed_content, &have_data); free_PA_PK_AS_REQ(&r); if (ret) { krb5_set_error_message(context, ret, "Can't unwrap ContentInfo: %d", ret); goto out; } } else { krb5_clear_error_message(context); ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP; goto out; } ret = der_heim_oid_cmp(&contentInfoOid, oid_id_pkcs7_signedData()); if (ret != 0) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "PK-AS-REQ-Win2k invalid content type oid"); goto out; } if (!have_data) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "PK-AS-REQ-Win2k no signed auth pack"); goto out; } { hx509_certs signer_certs; ret = hx509_cms_verify_signed(kdc_identity->hx509ctx, kdc_identity->verify_ctx, signed_content.data, signed_content.length, NULL, kdc_identity->certpool, &eContentType, &eContent, &signer_certs); if (ret) { char *s = hx509_get_error_string(kdc_identity->hx509ctx, ret); krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d", s, ret); free(s); goto out; } ret = hx509_get_one_cert(kdc_identity->hx509ctx, signer_certs, &client_params->cert); hx509_certs_free(&signer_certs); if (ret) goto out; } /* Signature is correct, now verify the signed message */ if (der_heim_oid_cmp(&eContentType, oid_id_pkcs7_data()) != 0 && der_heim_oid_cmp(&eContentType, oid_id_pkauthdata()) != 0) { ret = KRB5_BADMSGTYPE; krb5_set_error_message(context, ret, "got wrong oid for pkauthdata"); goto out; } if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) { AuthPack_Win2k ap; ret = decode_AuthPack_Win2k(eContent.data, eContent.length, &ap, NULL); if (ret) { krb5_set_error_message(context, ret, "can't decode AuthPack: %d", ret); goto out; } ret = pk_check_pkauthenticator_win2k(context, &ap.pkAuthenticator, req); if (ret) { free_AuthPack_Win2k(&ap); goto out; } client_params->type = PKINIT_WIN2K; client_params->nonce = ap.pkAuthenticator.nonce; if (ap.clientPublicValue) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "DH not supported for windows"); goto out; } free_AuthPack_Win2k(&ap); } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) { AuthPack ap; ret = decode_AuthPack(eContent.data, eContent.length, &ap, NULL); if (ret) { krb5_set_error_message(context, ret, "can't decode AuthPack: %d", ret); free_AuthPack(&ap); goto out; } ret = pk_check_pkauthenticator(context, &ap.pkAuthenticator, req); if (ret) { free_AuthPack(&ap); goto out; } client_params->type = PKINIT_27; client_params->nonce = ap.pkAuthenticator.nonce; if (ap.clientPublicValue) { ret = get_dh_param(context, config, ap.clientPublicValue, client_params); if (ret) { free_AuthPack(&ap); goto out; } } if (ap.supportedCMSTypes) { ret = hx509_peer_info_alloc(kdc_identity->hx509ctx, &client_params->peer); if (ret) { free_AuthPack(&ap); goto out; } ret = hx509_peer_info_set_cms_algs(kdc_identity->hx509ctx, client_params->peer, ap.supportedCMSTypes->val, ap.supportedCMSTypes->len); if (ret) { free_AuthPack(&ap); goto out; } } free_AuthPack(&ap); } else krb5_abortx(context, "internal pkinit error"); kdc_log(context, config, 0, "PK-INIT request of type %s", type); out: if (ret) krb5_warn(context, ret, "PKINIT"); if (signed_content.data) free(signed_content.data); krb5_data_free(&eContent); der_free_oid(&eContentType); der_free_oid(&contentInfoOid); if (ret) _kdc_pk_free_client_param(context, client_params); else *ret_params = client_params; return ret; }