From b9f88cce4c5dc07f9321a8281803806682cc9c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Love=20H=C3=B6rnquist=20=C3=85strand?= Date: Mon, 24 Mar 2008 12:05:02 +0000 Subject: [PATCH] first version of the tgs referrals pathcheck git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@22703 ec53bebd-3082-4978-b11e-865c3cabbd6b --- kdc/krb5tgs.c | 333 +++++++++++++++++++++++++++++++------------ lib/asn1/Makefile.am | 2 + lib/asn1/k5.asn1 | 9 ++ 3 files changed, 251 insertions(+), 93 deletions(-) diff --git a/kdc/krb5tgs.c b/kdc/krb5tgs.c index d2ea75387..99b9baf78 100644 --- a/kdc/krb5tgs.c +++ b/kdc/krb5tgs.c @@ -187,13 +187,14 @@ check_KRB5SignedPath(krb5_context context, hdb_entry_ex *krbtgt, EncTicketPart *tkt, KRB5SignedPathPrincipals **delegated, - int require_signedpath) + int *signedpath) { krb5_error_code ret; krb5_data data; krb5_crypto crypto = NULL; - *delegated = NULL; + if (delegated) + *delegated = NULL; ret = find_KRB5SignedPath(context, tkt->authorization_data, &data); if (ret == 0) { @@ -246,7 +247,7 @@ check_KRB5SignedPath(krb5_context context, return ret; } - if (sp.delegated) { + if (delegated && sp.delegated) { *delegated = malloc(sizeof(*sp.delegated)); if (*delegated == NULL) { @@ -263,10 +264,8 @@ check_KRB5SignedPath(krb5_context context, } } free_KRB5SignedPath(&sp); - - } else { - if (require_signedpath) - return KRB5KDC_ERR_BADOPTION; + + *signedpath = 1; } return 0; @@ -286,7 +285,7 @@ check_PAC(krb5_context context, const EncryptionKey *krbtgt_key, EncTicketPart *tkt, krb5_data *rspac, - int *require_signedpath) + int *signedpath) { AuthorizationData *ad = tkt->authorization_data; unsigned i, j; @@ -338,7 +337,7 @@ check_PAC(krb5_context context, krb5_pac_free(context, pac); return ret; } - *require_signedpath = 0; + *signedpath = 1; ret = _krb5_pac_sign(context, pac, tkt->authtime, client_principal, @@ -1000,8 +999,16 @@ find_rpath(krb5_context context, Realm crealm, Realm srealm) static krb5_boolean -need_referral(krb5_context context, krb5_principal server, krb5_realm **realms) +need_referral(krb5_context context, krb5_kdc_configuration *config, + krb5_principal server, krb5_realm **realms) { + kdc_log(context, config, 0, + "need referral ? %d %s/%s@%s", + server->name.name_type, + server->name.name_string.val[0], + server->name.name_string.len > 1 ? server->name.name_string.val[1] : "", + server->realm); + if(server->name.name_type != KRB5_NT_SRV_INST || server->name.name_string.len != 2) return FALSE; @@ -1242,6 +1249,84 @@ out: return ret; } +static krb5_error_code +tgs_build_referral(krb5_context context, + krb5_kdc_configuration *config, + krb5_crypto session, + krb5_const_realm referred_realm, + const PrincipalName *true_principal_name, + const PrincipalName *requested_principal, + krb5_data *outdata) +{ + PA_ServerReferralData ref; + krb5_error_code ret; + EncryptedData ed; + krb5_data data; + size_t size; + + memset(&ref, 0, sizeof(ref)); + + if (referred_realm) { + ref.referred_realm = malloc(sizeof(ref.referred_realm)); + if (ref.referred_realm == NULL) + goto eout; + *ref.referred_realm = strdup(referred_realm); + if (*ref.referred_realm == NULL) + goto eout; + } + if (true_principal_name) { + ref.true_principal_name = + malloc(sizeof(ref.true_principal_name)); + if (ref.true_principal_name == NULL) + goto eout; + ret = copy_PrincipalName(true_principal_name, ref.true_principal_name); + if (ret) + goto eout; + } + if (requested_principal) { + ref.requested_principal_name = + malloc(sizeof(ref.requested_principal_name)); + if (ref.requested_principal_name == NULL) + goto eout; + ret = copy_PrincipalName(requested_principal, + ref.requested_principal_name); + if (ret) + goto eout; + } + + ASN1_MALLOC_ENCODE(PA_ServerReferralData, + data.data, data.length, + &ref, &size, ret); + free_PA_ServerReferralData(&ref); + if (ret) + return ret; + if (data.length != size) + krb5_abortx(context, "internal asn.1 encoder error"); + + ret = krb5_encrypt_EncryptedData(context, session, + KRB5_KU_PA_SERVER_REFERRAL, + data.data, data.length, + 0 /* kvno */, &ed); + free(data.data); + if (ret) + return ret; + + ASN1_MALLOC_ENCODE(EncryptedData, + outdata->data, outdata->length, + &ed, &size, ret); + free_EncryptedData(&ed); + if (ret) + return ret; + if (outdata->length != size) + krb5_abortx(context, "internal asn.1 encoder error"); + + return 0; +eout: + free_PA_ServerReferralData(&ref); + krb5_set_error_string(context, "out of memory"); + return ENOMEM; +} + static krb5_error_code tgs_build_reply(krb5_context context, krb5_kdc_configuration *config, @@ -1253,7 +1338,7 @@ tgs_build_reply(krb5_context context, krb5_data *reply, const char *from, const char **e_text, - AuthorizationData *auth_data, + AuthorizationData **auth_data, const struct sockaddr *from_addr, int datagram_reply) { @@ -1262,6 +1347,7 @@ tgs_build_reply(krb5_context context, krb5_principal client_principal = NULL; char *spn = NULL, *cpn = NULL; hdb_entry_ex *server = NULL, *client = NULL; + krb5_realm ref_realm = NULL; EncTicketPart *tgt = &ticket->ticket; KRB5SignedPathPrincipals *spp = NULL; const EncryptionKey *ekey; @@ -1275,7 +1361,7 @@ tgs_build_reply(krb5_context context, int nloop = 0; EncTicketPart adtkt; char opt_str[128]; - int require_signedpath = 0; + int signedpath = 0; memset(&sessionkey, 0, sizeof(sessionkey)); memset(&adtkt, 0, sizeof(adtkt)); @@ -1379,11 +1465,14 @@ server_lookup: ret = krb5_unparse_name(context, sp, &spn); if (ret) goto out; - auth_data = NULL; /* ms don't handle AD in referals */ + + if (ref_realm) + free(ref_realm); + ref_realm = strdup(new_rlm); goto server_lookup; } } - } else if(need_referral(context, sp, &realms)) { + } else if(need_referral(context, config, sp, &realms)) { if (strcmp(realms[0], sp->realm) != 0) { kdc_log(context, config, 5, "Returning a referral to realm %s for " @@ -1396,8 +1485,12 @@ server_lookup: ret = krb5_unparse_name(context, sp, &spn); if (ret) goto out; + + if (ref_realm) + free(ref_realm); + ref_realm = strdup(realms[0]); + krb5_free_host_realm(context, realms); - auth_data = NULL; /* ms don't handle AD in referals */ goto server_lookup; } krb5_free_host_realm(context, realms); @@ -1438,6 +1531,50 @@ server_lookup: cross_realm = 1; } + /* + * Select enctype, return key and kvno. + */ + + { + krb5_enctype etype; + + if(b->kdc_options.enc_tkt_in_skey) { + int i; + ekey = &adtkt.key; + for(i = 0; i < b->etype.len; i++) + if (b->etype.val[i] == adtkt.key.keytype) + break; + if(i == b->etype.len) { + kdc_log(context, config, 0, + "Addition ticket have not matching etypes", spp); + krb5_clear_error_string(context); + return KRB5KDC_ERR_ETYPE_NOSUPP; + } + etype = b->etype.val[i]; + kvno = 0; + } else { + Key *skey; + + ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len, + &skey, &etype); + if(ret) { + kdc_log(context, config, 0, + "Server (%s) has no support for etypes", spp); + return ret; + } + ekey = &skey->key; + kvno = server->entry.kvno; + } + + ret = krb5_generate_random_keyblock(context, etype, &sessionkey); + if (ret) + goto out; + } + + /* + * Validate authoriation data + */ + /* * Check that service is in the same realm as the krbtgt. If it's * not the same, it's someone that is using a uni-directional trust @@ -1459,8 +1596,45 @@ server_lookup: goto out; } + /* check PAC if not cross realm and if there is one */ + if (!cross_realm) { + Key *tkey; + + ret = hdb_enctype2key(context, &krbtgt->entry, + krbtgt_etype, &tkey); + if(ret) { + kdc_log(context, config, 0, + "Failed to find key for krbtgt PAC check"); + goto out; + } + + ret = check_PAC(context, config, cp, + client, server, ekey, &tkey->key, + tgt, &rspac, &signedpath); + if (ret) { + kdc_log(context, config, 0, + "Verify PAC failed for %s (%s) from %s with %s", + spn, cpn, from, krb5_get_err_text(context, ret)); + goto out; + } + } + + /* also check the krbtgt for signature */ + ret = check_KRB5SignedPath(context, + config, + krbtgt, + tgt, + &spp, + &signedpath); + if (ret) { + kdc_log(context, config, 0, + "KRB5SignedPath check failed for %s (%s) from %s with %s", + spn, cpn, from, krb5_get_err_text(context, ret)); + goto out; + } + /* - * + * Process request */ client_principal = cp; @@ -1566,10 +1740,23 @@ server_lookup: && b->additional_tickets->len != 0 && b->kdc_options.enc_tkt_in_skey == 0) { + int ad_signedpath = 0; Key *clientkey; Ticket *t; char *str; + /* + * Require that the KDC have issued the service's krbtgt (not + * self-issued ticket with kimpersonate(1). + */ + if (!signedpath) { + ret = KRB5KDC_ERR_BADOPTION; + kdc_log(context, config, 0, + "Constrained delegation done on service ticket %s/%s", + cpn, spn); + goto out; + } + t = &b->additional_tickets->val[0]; ret = hdb_enctype2key(context, &client->entry, @@ -1588,12 +1775,11 @@ server_lookup: } /* check that ticket is valid */ - if (adtkt.flags.forwardable == 0) { kdc_log(context, config, 0, "Missing forwardable flag on ticket for " "constrained delegation from %s to %s ", spn, cpn); - ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ + ret = KRB5KDC_ERR_BADOPTION; goto out; } @@ -1623,16 +1809,16 @@ server_lookup: } /* - * Check KRB5SignedPath in authorization data and add new entry to - * make sure servers can't fake a ticket to us. + * Check that the KDC issued the user's ticket. */ - ret = check_KRB5SignedPath(context, config, krbtgt, &adtkt, - &spp, - 1); + NULL, + &ad_signedpath); + if (ret == 0 && !ad_signedpath) + ret = KRB5KDC_ERR_BADOPTION; if (ret) { kdc_log(context, config, 0, "KRB5SignedPath check from service %s failed " @@ -1646,12 +1832,6 @@ server_lookup: kdc_log(context, config, 0, "constrained delegation for %s " "from %s to %s", str, cpn, spn); free(str); - - /* - * Also require that the KDC have issue the service's krbtgt - * used to do the request. - */ - require_signedpath = 1; } /* @@ -1682,78 +1862,43 @@ server_lookup: } /* - * Select enctype, return key and kvno. + * If this is an referral, add server referral data to the + * auth_data reply . */ + if (ref_realm) { + AuthorizationDataElement ade; + krb5_crypto crypto; - { - krb5_enctype etype; + kdc_log(context, config, 0, + "Adding server referral to %s", ref_realm); - if(b->kdc_options.enc_tkt_in_skey) { - int i; - ekey = &adtkt.key; - for(i = 0; i < b->etype.len; i++) - if (b->etype.val[i] == adtkt.key.keytype) - break; - if(i == b->etype.len) { - krb5_clear_error_string(context); - return KRB5KDC_ERR_ETYPE_NOSUPP; - } - etype = b->etype.val[i]; - kvno = 0; - } else { - Key *skey; - - ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len, - &skey, &etype); - if(ret) { - kdc_log(context, config, 0, - "Server (%s) has no support for etypes", spp); - return ret; - } - ekey = &skey->key; - kvno = server->entry.kvno; - } - - ret = krb5_generate_random_keyblock(context, etype, &sessionkey); + ret = krb5_crypto_init(context, &sessionkey, 0, &crypto); if (ret) goto out; - } - /* check PAC if not cross realm and if there is one */ - if (!cross_realm) { - Key *tkey; - - ret = hdb_enctype2key(context, &krbtgt->entry, - krbtgt_etype, &tkey); - if(ret) { - kdc_log(context, config, 0, - "Failed to find key for krbtgt PAC check"); - goto out; - } - - ret = check_PAC(context, config, client_principal, - client, server, ekey, &tkey->key, - tgt, &rspac, &require_signedpath); + ret = tgs_build_referral(context, config, crypto, ref_realm, + NULL, s, &ade.ad_data); + krb5_crypto_destroy(context, crypto); if (ret) { kdc_log(context, config, 0, - "Verify PAC failed for %s (%s) from %s with %s", - spn, cpn, from, krb5_get_err_text(context, ret)); + "Failed building server referral"); + goto out; + } + ade.ad_type = KRB5_PADATA_SERVER_REFERRAL; + if (*auth_data == NULL) { + *auth_data = calloc(1, sizeof(**auth_data)); + if (*auth_data == NULL) { + ret = ENOMEM; + goto out; + } + } + ret = add_AuthorizationData(*auth_data, &ade); + krb5_data_free(&ade.ad_data); + if (ret) { + kdc_log(context, config, 0, + "Add server referral AuthorizationData failed"); goto out; } - } - - /* also check the krbtgt for signature */ - ret = check_KRB5SignedPath(context, - config, - krbtgt, - tgt, - &spp, - require_signedpath); - if (ret) { - kdc_log(context, config, 0, - "KRB5SignedPath check failed for %s (%s) from %s with %s", - spn, cpn, from, krb5_get_err_text(context, ret)); - goto out; } /* @@ -1768,7 +1913,7 @@ server_lookup: ekey, &sessionkey, kvno, - auth_data, + *auth_data, server, spn, client, @@ -1797,6 +1942,8 @@ out: krb5_free_principal(context, cp); if (sp) krb5_free_principal(context, sp); + if (ref_realm) + free(ref_realm); free_EncTicketPart(&adtkt); @@ -1870,7 +2017,7 @@ _kdc_tgs_rep(krb5_context context, data, from, &e_text, - auth_data, + &auth_data, from_addr, datagram_reply); if (ret) { diff --git a/lib/asn1/Makefile.am b/lib/asn1/Makefile.am index 9aad31496..dbcdc7e16 100644 --- a/lib/asn1/Makefile.am +++ b/lib/asn1/Makefile.am @@ -90,6 +90,8 @@ gen_files_k5 = \ asn1_PA_ClientCanonicalized.x \ asn1_PA_ClientCanonicalizedNames.x \ asn1_PA_SvrReferralData.x \ + asn1_PA_ServerReferralData.x \ + asn1_PA_SERVER_REFERRAL_DATA.x \ asn1_PROV_SRV_LOCATION.x \ asn1_Principal.x \ asn1_PrincipalName.x \ diff --git a/lib/asn1/k5.asn1 b/lib/asn1/k5.asn1 index 0701e9f76..e3db196c2 100644 --- a/lib/asn1/k5.asn1 +++ b/lib/asn1/k5.asn1 @@ -654,6 +654,15 @@ PA-SvrReferralData ::= SEQUENCE { referred-realm [0] Realm } +PA-SERVER-REFERRAL-DATA ::= EncryptedData + +PA-ServerReferralData ::= SEQUENCE { + referred-realm [0] Realm OPTIONAL, + true-principal-name [1] PrincipalName OPTIONAL, + requested-principal-name [2] PrincipalName OPTIONAL, + ... +} + END -- etags -r '/\([A-Za-z][-A-Za-z0-9]*\).*::=/\1/' k5.asn1