diff --git a/kdc/kerberos5.c b/kdc/kerberos5.c index 6b6ea7d49..f93a0108b 100644 --- a/kdc/kerberos5.c +++ b/kdc/kerberos5.c @@ -432,7 +432,7 @@ pa_enc_chal_validate(kdc_request_t r, const PA_DATA *pa) heim_assert(r->armor_crypto != NULL, "ENC-CHAL called for non FAST"); - if (b->kdc_options.request_anonymous) { + if (_kdc_is_anon_request(b)) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; kdc_log(r->context, r->config, 0, "ENC-CHALL doesn't support anon"); return ret; @@ -555,7 +555,7 @@ pa_enc_ts_validate(kdc_request_t r, const PA_DATA *pa) Key *pa_key; char *str; - if (r->req.req_body.kdc_options.request_anonymous) { + if (_kdc_is_anon_request(&r->req.req_body)) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; _kdc_set_e_text(r, "ENC-TS doesn't support anon"); goto out; @@ -717,9 +717,14 @@ static const struct kdc_patypes pat[] = { KRB5_PADATA_PK_AS_REQ_WIN, "PK-INIT(win2k)", PA_ANNOUNCE, pa_pkinit_validate }, + { + KRB5_PADATA_PKINIT_KX, "Anonymous PK-INIT", PA_ANNOUNCE, + NULL + }, #else { KRB5_PADATA_PK_AS_REQ, "PK-INIT(ietf)", 0, NULL }, { KRB5_PADATA_PK_AS_REQ_WIN, "PK-INIT(win2k)", 0, NULL }, + { KRB5_PADATA_PKINIT_KX, "Anonymous PK-INIT", 0, NULL }, #endif { KRB5_PADATA_PA_PK_OCSP_RESPONSE , "OCSP", 0, NULL }, { @@ -1656,12 +1661,12 @@ _kdc_as_rep(kdc_request_t r, */ if (_kdc_is_anonymous(context, r->client_princ)) { - if (!b->kdc_options.request_anonymous) { + if (!_kdc_is_anon_request(b)) { kdc_log(context, config, 0, "Anonymous ticket w/o anonymous flag"); ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto out; } - } else if (b->kdc_options.request_anonymous) { + } else if (_kdc_is_anon_request(b)) { kdc_log(context, config, 0, "Request for a anonymous ticket with non " "anonymous client name: %s", r->client_name); @@ -1810,7 +1815,7 @@ _kdc_as_rep(kdc_request_t r, * send requre preauth is its required or anon is requested, * anon is today only allowed via preauth mechanisms. */ - if (require_preauth_p(r) || b->kdc_options.request_anonymous) { + if (require_preauth_p(r) || _kdc_is_anon_request(b)) { ret = KRB5KDC_ERR_PREAUTH_REQUIRED; _kdc_set_e_text(r, "Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ"); goto out; @@ -1854,7 +1859,7 @@ _kdc_as_rep(kdc_request_t r, goto out; if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey - || (f.request_anonymous && !config->allow_anonymous)) { + || (_kdc_is_anon_request(b) && !config->allow_anonymous)) { ret = KRB5KDC_ERR_BADOPTION; _kdc_set_e_text(r, "Bad KDC options"); goto out; @@ -1867,7 +1872,11 @@ _kdc_as_rep(kdc_request_t r, rep.pvno = 5; rep.msg_type = krb_as_rep; - ret = copy_Realm(&r->client->entry.principal->realm, &rep.crealm); + if (_kdc_is_anonymous(context, r->client_princ)) { + Realm anon_realm=KRB5_ANON_REALM; + ret = copy_Realm(&anon_realm, &rep.crealm); + } else + ret = copy_Realm(&r->client->entry.principal->realm, &rep.crealm); if (ret) goto out; ret = _krb5_principal2principalname(&rep.cname, r->client->entry.principal); @@ -1973,7 +1982,7 @@ _kdc_as_rep(kdc_request_t r, } } - if (f.request_anonymous) + if (_kdc_is_anon_request(b)) r->et.flags.anonymous = 1; if(b->addresses){ @@ -2320,3 +2329,14 @@ _kdc_tkt_add_if_relevant_ad(krb5_context context, return 0; } + +krb5_boolean +_kdc_is_anon_request(const KDC_REQ_BODY *b) +{ + /* some versions of heimdal use bit 14 instead of 16 for + request_anonymous, as indicated in the anonymous draft prior to + version 11. Bit 14 is assigned to S4U2Proxy, but all S4U2Proxy + requests will have a second ticket; don't consider those anonymous */ + return (b->kdc_options.request_anonymous || + (b->kdc_options.constrained_delegation && !b->additional_tickets)); +} diff --git a/kdc/pkinit.c b/kdc/pkinit.c index cd2067532..66c27504a 100644 --- a/kdc/pkinit.c +++ b/kdc/pkinit.c @@ -573,7 +573,7 @@ _kdc_pk_rd_padata(krb5_context context, type = "PK-INIT-Win2k"; - if (req->req_body.kdc_options.request_anonymous) { + 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"); @@ -719,7 +719,7 @@ _kdc_pk_rd_padata(krb5_context context, hx509_certs signer_certs; int flags = HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; /* BTMM */ - if (req->req_body.kdc_options.request_anonymous) + if (_kdc_is_anon_request(&req->req_body)) flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER; ret = hx509_cms_verify_signed(context->hx509ctx, @@ -804,7 +804,7 @@ _kdc_pk_rd_padata(krb5_context context, goto out; } - if (req->req_body.kdc_options.request_anonymous && + if (_kdc_is_anon_request(&req->req_body) && ap.clientPublicValue == NULL) { free_AuthPack(&ap); ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED; @@ -1370,16 +1370,86 @@ _kdc_pk_mk_pa_reply(krb5_context context, if (rep.u.encKeyPack.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); - /* XXX KRB-FX-CF2 */ - ret = krb5_generate_random_keyblock(context, sessionetype, - sessionkey); - if (ret) { - free_PA_PK_AS_REP(&rep); - goto out; + /* generate the session key using the method from RFC6112 */ + { + krb5_keyblock kdc_contribution_key; + krb5_crypto reply_crypto; + krb5_crypto kdccont_crypto; + krb5_data p1 = { strlen("PKINIT"), "PKINIT"}; + krb5_data p2 = { strlen("KEYEXCHANGE"), "KEYEXCHANGE"}; + void *kckdata; + size_t kcklen; + EncryptedData kx; + void *kxdata; + size_t kxlen; + + ret = krb5_generate_random_keyblock(context, sessionetype, + &kdc_contribution_key); + if (ret) { + free_PA_PK_AS_REP(&rep); + goto out; + } + ret = krb5_crypto_init(context, &cp->reply_key, enctype, &reply_crypto); + if (ret) { + krb5_free_keyblock_contents(context, &kdc_contribution_key); + free_PA_PK_AS_REP(&rep); + goto out; + } + ret = krb5_crypto_init(context, &kdc_contribution_key, sessionetype, &kdccont_crypto); + if (ret) { + krb5_crypto_destroy(context, reply_crypto); + krb5_free_keyblock_contents(context, &kdc_contribution_key); + free_PA_PK_AS_REP(&rep); + goto out; + } + /* KRB-FX-CF2 */ + ret = krb5_crypto_fx_cf2(context, kdccont_crypto, reply_crypto, + &p1, &p2, sessionetype, sessionkey); + krb5_crypto_destroy(context, kdccont_crypto); + if (ret) { + krb5_crypto_destroy(context, reply_crypto); + krb5_free_keyblock_contents(context, &kdc_contribution_key); + free_PA_PK_AS_REP(&rep); + goto out; + } + ASN1_MALLOC_ENCODE(EncryptionKey, kckdata, kcklen, + &kdc_contribution_key, &size, ret); + krb5_free_keyblock_contents(context, &kdc_contribution_key); + if (ret) { + krb5_set_error_message(context, ret, "encoding of PKINIT-KX Key failed %d", ret); + krb5_crypto_destroy(context, reply_crypto); + free_PA_PK_AS_REP(&rep); + goto out; + } + if (kcklen != size) + krb5_abortx(context, "Internal ASN.1 encoder error"); + ret = krb5_encrypt_EncryptedData(context, reply_crypto, KRB5_KU_PA_PKINIT_KX, + kckdata, kcklen, 0, &kx); + krb5_crypto_destroy(context, reply_crypto); + free(kckdata); + if (ret) { + free_PA_PK_AS_REP(&rep); + goto out; + } + ASN1_MALLOC_ENCODE(EncryptedData, kxdata, kxlen, + &kx, &size, ret); + free_EncryptedData(&kx); + if (ret) { + krb5_set_error_message(context, ret, "encoding of PKINIT-KX failed %d", ret); + free_PA_PK_AS_REP(&rep); + goto out; + } + if (kxlen != size) + krb5_abortx(context, "Internal ASN.1 encoder error"); + /* Add PA-PKINIT-KX */ + ret = krb5_padata_add(context, md, KRB5_PADATA_PKINIT_KX, kxdata, kxlen); + if (ret) { + krb5_set_error_message(context, ret, + "Failed adding PKINIT-KX %d", ret); + free(buf); + goto out; + } } - - /* XXX Add PA-PKINIT-KX */ - } #define use_btmm_with_enckey 0 diff --git a/lib/asn1/krb5.asn1 b/lib/asn1/krb5.asn1 index c51cbbf6a..88c4f687a 100644 --- a/lib/asn1/krb5.asn1 +++ b/lib/asn1/krb5.asn1 @@ -338,8 +338,8 @@ TicketFlags ::= BIT STRING { hw-authent(11), transited-policy-checked(12), ok-as-delegate(13), - anonymous(14), - enc-pa-rep(15) + enc-pa-rep(15), + anonymous(16) } KDCOptions ::= BIT STRING { @@ -351,9 +351,9 @@ KDCOptions ::= BIT STRING { allow-postdate(5), postdated(6), renewable(8), - request-anonymous(14), + constrained-delegation(14), -- ms extension (aka cname-in-addl-tkt) canonicalize(15), - constrained-delegation(16), -- ms extension + request-anonymous(16), disable-transited-check(26), renewable-ok(27), enc-tkt-in-skey(28),