static krb5_error_code pkinit_server_get_edata(krb5_context context, krb5_kdc_req * request, struct _krb5_db_entry_new * client, struct _krb5_db_entry_new * server, preauth_get_entry_data_proc server_get_entry_data, void *pa_plugin_context, krb5_pa_data * data) { krb5_error_code retval = 0; pkinit_kdc_context plgctx = NULL; krb5_keyblock *armor_key = NULL; pkiDebug("pkinit_server_get_edata: entered!\n"); /* Remove (along with armor_key) when FAST PKINIT is settled. */ retval = fast_kdc_get_armor_key(context, server_get_entry_data, request, client, &armor_key); if (retval == 0 && armor_key != NULL) { /* Don't advertise PKINIT if the client used FAST. */ krb5_free_keyblock(context, armor_key); return EINVAL; } /* * If we don't have a realm context for the given realm, * don't tell the client that we support pkinit! */ plgctx = pkinit_find_realm_context(context, pa_plugin_context, request->server); if (plgctx == NULL) retval = EINVAL; return retval; }
static krb5_error_code kdc_include_padata(krb5_context context, krb5_kdc_req *request, struct _krb5_db_entry_new *client, struct _krb5_db_entry_new *server, preauth_get_entry_data_proc get_entry_proc, void *pa_module_context, krb5_pa_data *data) { krb5_error_code retval = 0; krb5_keyblock *armor_key = NULL; retval = fast_kdc_get_armor_key(context, get_entry_proc, request, client, &armor_key); if (retval) return retval; if (armor_key == 0) return ENOENT; krb5_free_keyblock(context, armor_key); return 0; }
static krb5_error_code pkinit_server_verify_padata(krb5_context context, struct _krb5_db_entry_new * client, krb5_data *req_pkt, krb5_kdc_req * request, krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data * data, preauth_get_entry_data_proc server_get_entry_data, void *pa_plugin_context, void **pa_request_context, krb5_data **e_data, krb5_authdata ***authz_data) { krb5_error_code retval = 0; krb5_octet_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL}; krb5_pa_pk_as_req *reqp = NULL; krb5_pa_pk_as_req_draft9 *reqp9 = NULL; krb5_auth_pack *auth_pack = NULL; krb5_auth_pack_draft9 *auth_pack9 = NULL; pkinit_kdc_context plgctx = NULL; pkinit_kdc_req_context reqctx; krb5_preauthtype pa_type; krb5_checksum cksum = {0, 0, 0, NULL}; krb5_data *der_req = NULL; int valid_eku = 0, valid_san = 0; krb5_kdc_req *tmp_as_req = NULL; krb5_data k5data; int is_signed = 1; krb5_keyblock *armor_key; pkiDebug("pkinit_verify_padata: entered!\n"); if (data == NULL || data->length <= 0 || data->contents == NULL) return 0; /* Remove (along with armor_key) when FAST PKINIT is settled. */ retval = fast_kdc_get_armor_key(context, server_get_entry_data, request, client, &armor_key); if (retval == 0 && armor_key != NULL) { /* Don't allow PKINIT if the client used FAST. */ krb5_free_keyblock(context, armor_key); return EINVAL; } if (pa_plugin_context == NULL || e_data == NULL) return EINVAL; plgctx = pkinit_find_realm_context(context, pa_plugin_context, request->server); if (plgctx == NULL) return 0; #ifdef DEBUG_ASN1 print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req"); #endif /* create a per-request context */ retval = pkinit_init_kdc_req_context(context, (void **)&reqctx); if (retval) goto cleanup; reqctx->pa_type = data->pa_type; PADATA_TO_KRB5DATA(data, &k5data); switch ((int)data->pa_type) { case KRB5_PADATA_PK_AS_REQ: pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n"); pa_type = (int)data->pa_type; retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp); if (retval) { pkiDebug("decode_krb5_pa_pk_as_req failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin(reqp->signedAuthPack.data, reqp->signedAuthPack.length, "/tmp/kdc_signed_data"); #endif retval = cms_signeddata_verify(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT, plgctx->opts->require_crl_checking, reqp->signedAuthPack.data, reqp->signedAuthPack.length, &authp_data.data, &authp_data.length, &krb5_authz.data, &krb5_authz.length, &is_signed); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n"); pa_type = KRB5_PADATA_PK_AS_REQ_OLD; retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9); if (retval) { pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin(reqp9->signedAuthPack.data, reqp9->signedAuthPack.length, "/tmp/kdc_signed_data_draft9"); #endif retval = cms_signeddata_verify(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, plgctx->opts->require_crl_checking, reqp9->signedAuthPack.data, reqp9->signedAuthPack.length, &authp_data.data, &authp_data.length, &krb5_authz.data, &krb5_authz.length, NULL); break; default: pkiDebug("unrecognized pa_type = %d\n", data->pa_type); retval = EINVAL; goto cleanup; } if (retval) { 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; 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 OCTETDATA_TO_KRB5DATA(&authp_data, &k5data); switch ((int)data->pa_type) { case KRB5_PADATA_PK_AS_REQ: retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack); if (retval) { pkiDebug("failed to decode krb5_auth_pack\n"); goto cleanup; } /* check dh parameters */ if (auth_pack->clientPublicValue != NULL) { retval = server_check_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, &auth_pack->clientPublicValue->algorithm.parameters, plgctx->opts->dh_min_bits); if (retval) { 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. * We need to compute the checksum on the data that * came from the client. Therefore, we use the original * packet contents. */ retval = k5int_decode_krb5_as_req(req_pkt, &tmp_as_req); if (retval) { pkiDebug("decode_krb5_as_req returned %d\n", (int)retval); goto cleanup; } retval = k5int_encode_krb5_kdc_req_body(tmp_as_req, &der_req); if (retval) { pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval); goto cleanup; } retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0, der_req, &cksum); if (retval) { pkiDebug("unable to calculate AS REQ checksum\n"); goto cleanup; } if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length || memcmp(cksum.contents, auth_pack->pkAuthenticator.paChecksum.contents, cksum.length)) { pkiDebug("failed to match the checksum\n"); #ifdef DEBUG_CKSUM pkiDebug("calculating checksum on buf size (%d)\n", req_pkt->length); print_buffer(req_pkt->data, req_pkt->length); pkiDebug("received checksum type=%d size=%d ", auth_pack->pkAuthenticator.paChecksum.checksum_type, auth_pack->pkAuthenticator.paChecksum.length); print_buffer(auth_pack->pkAuthenticator.paChecksum.contents, auth_pack->pkAuthenticator.paChecksum.length); pkiDebug("expected checksum type=%d size=%d ", cksum.checksum_type, cksum.length); print_buffer(cksum.contents, cksum.length); #endif retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED; goto cleanup; } /* check if kdcPkId present and match KDC's subjectIdentifier */ if (reqp->kdcPkId.data != NULL) { int valid_kdcPkId = 0; retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, reqp->kdcPkId.data, reqp->kdcPkId.length, &valid_kdcPkId); if (retval) goto cleanup; if (!valid_kdcPkId) pkiDebug("kdcPkId in AS_REQ does not match KDC's cert" "RFC says to ignore and proceed\n"); } /* remember the decoded auth_pack for verify_padata routine */ reqctx->rcv_auth_pack = auth_pack; auth_pack = NULL; break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: retval = k5int_decode_krb5_auth_pack_draft9(&k5data, &auth_pack9); if (retval) { pkiDebug("failed to decode krb5_auth_pack_draft9\n"); goto cleanup; } if (auth_pack9->clientPublicValue != NULL) { retval = server_check_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, &auth_pack9->clientPublicValue->algorithm.parameters, plgctx->opts->dh_min_bits); if (retval) { pkiDebug("bad dh parameters\n"); goto cleanup; } } /* remember the decoded auth_pack for verify_padata routine */ reqctx->rcv_auth_pack9 = auth_pack9; auth_pack9 = NULL; break; } /* * This code used to generate ad-initial-verified-cas authorization data. * However that has been removed until the ad-kdc-issued discussion can * happen in the working group. Dec 2009 */ /* return authorization data to be included in the ticket */ switch ((int)data->pa_type) { default: *authz_data = NULL; } /* remember to set the PREAUTH flag in the reply */ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; *pa_request_context = reqctx; reqctx = NULL; cleanup: if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) { pkiDebug("pkinit_verify_padata failed: creating e-data\n"); if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, plgctx->opts, retval, e_data)) pkiDebug("pkinit_create_edata failed\n"); } switch ((int)data->pa_type) { case KRB5_PADATA_PK_AS_REQ: free_krb5_pa_pk_as_req(&reqp); free(cksum.contents); if (der_req != NULL) krb5_free_data(context, der_req); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: free_krb5_pa_pk_as_req_draft9(&reqp9); } if (tmp_as_req != NULL) k5int_krb5_free_kdc_req(context, tmp_as_req); free(authp_data.data); free(krb5_authz.data); if (reqctx != NULL) pkinit_fini_kdc_req_context(context, reqctx); if (auth_pack != NULL) free_krb5_auth_pack(&auth_pack); if (auth_pack9 != NULL) free_krb5_auth_pack_draft9(context, &auth_pack9); return retval; }
static krb5_error_code kdc_verify_preauth(krb5_context context, struct _krb5_db_entry_new *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data, preauth_get_entry_data_proc get_entry_proc, void *pa_module_context, void **pa_request_context, krb5_data **e_data, krb5_authdata ***authz_data) { krb5_error_code retval = 0; krb5_timestamp now; krb5_enc_data *enc = NULL; krb5_data scratch, plain; krb5_keyblock *armor_key = NULL; krb5_pa_enc_ts *ts = NULL; krb5int_access kaccess; krb5_keyblock *client_keys = NULL; krb5_data *client_data = NULL; krb5_keyblock *challenge_key = NULL; int i = 0; plain.data = NULL; if (krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION) != 0) return 0; retval = fast_kdc_get_armor_key(context, get_entry_proc, request, client, &armor_key); if (retval == 0 &&armor_key == NULL) { retval = ENOENT; krb5_set_error_message(context, ENOENT, "Encrypted Challenge used outside of FAST tunnel"); } scratch.data = (char *) data->contents; scratch.length = data->length; if (retval == 0) retval = kaccess.decode_enc_data(&scratch, &enc); if (retval == 0) { plain.data = malloc(enc->ciphertext.length); plain.length = enc->ciphertext.length; if (plain.data == NULL) retval = ENOMEM; } if (retval == 0) retval = get_entry_proc(context, request, client, krb5plugin_preauth_keys, &client_data); if (retval == 0) { client_keys = (krb5_keyblock *) client_data->data; for (i = 0; client_keys[i].enctype&& (retval == 0); i++ ) { retval = krb5_c_fx_cf2_simple(context, armor_key, "clientchallengearmor", &client_keys[i], "challengelongterm", &challenge_key); if (retval == 0) retval = krb5_c_decrypt(context, challenge_key, KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT, NULL, enc, &plain); if (challenge_key) krb5_free_keyblock(context, challenge_key); challenge_key = NULL; if (retval == 0) break; /*We failed to decrypt. Try next key*/ retval = 0; krb5_free_keyblock_contents(context, &client_keys[i]); } if (client_keys[i].enctype == 0) { retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, retval, "Incorrect password in encrypted challenge"); } else { /*not run out of keys*/ int j; assert (retval == 0); for (j = i+1; client_keys[j].enctype; j++) krb5_free_keyblock_contents(context, &client_keys[j]); } } if (retval == 0) retval = kaccess.decode_enc_ts(&plain, &ts); if (retval == 0) retval = krb5_timeofday(context, &now); if (retval == 0) { if (labs(now-ts->patimestamp) < context->clockskew) { enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; /* * If this fails, we won't generate a reply to the client. That * may cause the client to fail, but at this point the KDC has * considered this a success, so the return value is ignored. */ fast_kdc_replace_reply_key(context, get_entry_proc, request); krb5_c_fx_cf2_simple(context, armor_key, "kdcchallengearmor", &client_keys[i], "challengelongterm", (krb5_keyblock **) pa_request_context); } else { /*skew*/ retval = KRB5KRB_AP_ERR_SKEW; } } if (client_keys) { if (client_keys[i].enctype) krb5_free_keyblock_contents(context, &client_keys[i]); krb5_free_data(context, client_data); } if (armor_key) krb5_free_keyblock(context, armor_key); if (plain.data) free(plain.data); if (enc) kaccess.free_enc_data(context, enc); if (ts) kaccess.free_enc_ts(context, ts); return retval; }