Fixes for cross-realm, including (but not limited to):
* allow client to be non-existant (should probably check for "local realm") * if server isn't found and it is a request for a krbtgt, try to find a realm on the way to the requested realm * update the transited encoding iff client-realm != server-realm != tgt-realm git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@3463 ec53bebd-3082-4978-b11e-865c3cabbd6b
This commit is contained in:
167
kdc/kerberos5.c
167
kdc/kerberos5.c
@@ -530,7 +530,7 @@ as_rep(KDC_REQ *req,
|
|||||||
&ekey->key,
|
&ekey->key,
|
||||||
&rep.enc_part);
|
&rep.enc_part);
|
||||||
hdb_free_key(ekey);
|
hdb_free_key(ekey);
|
||||||
set_salt_padata (&rep.padata, ckey->salt);
|
set_salt_padata ((METHOD_DATA**)&rep.padata, ckey->salt);
|
||||||
|
|
||||||
ret = encode_AS_REP(buf + sizeof(buf) - 1, sizeof(buf), &rep, &len);
|
ret = encode_AS_REP(buf + sizeof(buf) - 1, sizeof(buf), &rep, &len);
|
||||||
free_AS_REP(&rep);
|
free_AS_REP(&rep);
|
||||||
@@ -675,9 +675,65 @@ check_tgs_flags(KDC_REQ_BODY *b, EncTicketPart *tgt, EncTicketPart *et)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static krb5_error_code
|
||||||
|
fix_transited_encoding(TransitedEncoding *tr,
|
||||||
|
const char *client_realm,
|
||||||
|
const char *server_realm,
|
||||||
|
const char *tgt_realm)
|
||||||
|
{
|
||||||
|
krb5_error_code ret = 0;
|
||||||
|
if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)){
|
||||||
|
char **realms = NULL, **tmp;
|
||||||
|
int num_realms = 0;
|
||||||
|
int i;
|
||||||
|
if(tr->tr_type){
|
||||||
|
if(tr->tr_type != DOMAIN_X500_COMPRESS){
|
||||||
|
kdc_log(0, "Unknown transited type: %u",
|
||||||
|
tr->tr_type);
|
||||||
|
return KRB5KDC_ERR_TRTYPE_NOSUPP;
|
||||||
|
}
|
||||||
|
ret = krb5_domain_x500_decode(&tr->contents,
|
||||||
|
&realms,
|
||||||
|
&num_realms,
|
||||||
|
client_realm,
|
||||||
|
server_realm);
|
||||||
|
if(ret){
|
||||||
|
krb5_warn(context, ret, "Decoding transited encoding");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
|
||||||
|
if(tmp == NULL){
|
||||||
|
ret = ENOMEM;
|
||||||
|
goto free_realms;
|
||||||
|
}
|
||||||
|
realms = tmp;
|
||||||
|
realms[num_realms] = strdup(tgt_realm);
|
||||||
|
if(realms[num_realms] == NULL){
|
||||||
|
ret = ENOMEM;
|
||||||
|
goto free_realms;
|
||||||
|
}
|
||||||
|
num_realms++;
|
||||||
|
free_TransitedEncoding(tr);
|
||||||
|
tr->tr_type = DOMAIN_X500_COMPRESS;
|
||||||
|
ret = krb5_domain_x500_encode(realms, num_realms, &tr->contents);
|
||||||
|
if(ret)
|
||||||
|
krb5_warn(context, ret, "Encoding transited encoding");
|
||||||
|
free_realms:
|
||||||
|
for(i = 0; i < num_realms; i++)
|
||||||
|
free(realms[i]);
|
||||||
|
free(realms);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static krb5_error_code
|
static krb5_error_code
|
||||||
tgs_make_reply(KDC_REQ_BODY *b, EncTicketPart *tgt,
|
tgs_make_reply(KDC_REQ_BODY *b, EncTicketPart *tgt,
|
||||||
hdb_entry *server, hdb_entry *client, krb5_data *reply)
|
hdb_entry *server, hdb_entry *client,
|
||||||
|
krb5_principal client_principal,
|
||||||
|
hdb_entry *krbtgt,
|
||||||
|
krb5_data *reply)
|
||||||
{
|
{
|
||||||
KDC_REP rep;
|
KDC_REP rep;
|
||||||
EncKDCRepPart ek;
|
EncKDCRepPart ek;
|
||||||
@@ -719,6 +775,17 @@ tgs_make_reply(KDC_REQ_BODY *b, EncTicketPart *tgt,
|
|||||||
if(ret)
|
if(ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
copy_TransitedEncoding(&tgt->transited, &et.transited);
|
||||||
|
ret = fix_transited_encoding(&et.transited,
|
||||||
|
*krb5_princ_realm(context, client_principal),
|
||||||
|
*krb5_princ_realm(context, server->principal),
|
||||||
|
*krb5_princ_realm(context, krbtgt->principal));
|
||||||
|
if(ret){
|
||||||
|
free_TransitedEncoding(&et.transited);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
copy_Realm(krb5_princ_realm(context, server->principal),
|
copy_Realm(krb5_princ_realm(context, server->principal),
|
||||||
&rep.ticket.realm);
|
&rep.ticket.realm);
|
||||||
krb5_principal2principalname(&rep.ticket.sname, server->principal);
|
krb5_principal2principalname(&rep.ticket.sname, server->principal);
|
||||||
@@ -733,7 +800,7 @@ tgs_make_reply(KDC_REQ_BODY *b, EncTicketPart *tgt,
|
|||||||
{
|
{
|
||||||
time_t life;
|
time_t life;
|
||||||
life = et.endtime - *et.starttime;
|
life = et.endtime - *et.starttime;
|
||||||
if(client->max_life)
|
if(client && client->max_life)
|
||||||
life = min(life, *client->max_life);
|
life = min(life, *client->max_life);
|
||||||
if(server->max_life)
|
if(server->max_life)
|
||||||
life = min(life, *server->max_life);
|
life = min(life, *server->max_life);
|
||||||
@@ -748,7 +815,7 @@ tgs_make_reply(KDC_REQ_BODY *b, EncTicketPart *tgt,
|
|||||||
if(et.renew_till){
|
if(et.renew_till){
|
||||||
time_t renew;
|
time_t renew;
|
||||||
renew = *et.renew_till - et.authtime;
|
renew = *et.renew_till - et.authtime;
|
||||||
if(client->max_renew)
|
if(client && client->max_renew)
|
||||||
renew = min(renew, *client->max_renew);
|
renew = min(renew, *client->max_renew);
|
||||||
if(server->max_renew)
|
if(server->max_renew)
|
||||||
renew = min(renew, *server->max_renew);
|
renew = min(renew, *server->max_renew);
|
||||||
@@ -800,6 +867,7 @@ tgs_make_reply(KDC_REQ_BODY *b, EncTicketPart *tgt,
|
|||||||
ek.srealm = rep.ticket.realm;
|
ek.srealm = rep.ticket.realm;
|
||||||
ek.sname = rep.ticket.sname;
|
ek.sname = rep.ticket.sname;
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
unsigned char buf[8192]; /* XXX The data could be indefinite */
|
unsigned char buf[8192]; /* XXX The data could be indefinite */
|
||||||
size_t len;
|
size_t len;
|
||||||
@@ -854,6 +922,7 @@ tgs_make_reply(KDC_REQ_BODY *b, EncTicketPart *tgt,
|
|||||||
krb5_data_copy(reply, buf + sizeof(buf) - len, len);
|
krb5_data_copy(reply, buf + sizeof(buf) - len, len);
|
||||||
out:
|
out:
|
||||||
free_TGS_REP(&rep);
|
free_TGS_REP(&rep);
|
||||||
|
free_TransitedEncoding(&et.transited);
|
||||||
if(et.starttime)
|
if(et.starttime)
|
||||||
free(et.starttime);
|
free(et.starttime);
|
||||||
if(et.renew_till)
|
if(et.renew_till)
|
||||||
@@ -911,11 +980,29 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Realm
|
||||||
|
is_krbtgt(PrincipalName *p)
|
||||||
|
{
|
||||||
|
if(p->name_string.len == 2 && strcmp(p->name_string.val[0], "krbtgt") == 0)
|
||||||
|
return p->name_string.val[1];
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Realm
|
||||||
|
find_rpath(Realm r)
|
||||||
|
{
|
||||||
|
const char *new_realm = krb5_config_get_string(context->cf,
|
||||||
|
"libdefaults",
|
||||||
|
"capath",
|
||||||
|
r,
|
||||||
|
NULL);
|
||||||
|
return (Realm)new_realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static krb5_error_code
|
static krb5_error_code
|
||||||
tgs_rep2(KDC_REQ_BODY *b,
|
tgs_rep2(KDC_REQ_BODY *b,
|
||||||
krb5_principal sp,
|
|
||||||
PA_DATA *pa_data,
|
PA_DATA *pa_data,
|
||||||
krb5_data *reply,
|
krb5_data *reply,
|
||||||
const char *from)
|
const char *from)
|
||||||
@@ -933,6 +1020,7 @@ tgs_rep2(KDC_REQ_BODY *b,
|
|||||||
EncTicketPart *tgt;
|
EncTicketPart *tgt;
|
||||||
Key *ekey;
|
Key *ekey;
|
||||||
krb5_principal cp = NULL;
|
krb5_principal cp = NULL;
|
||||||
|
krb5_principal sp = NULL;
|
||||||
|
|
||||||
memset(&ap_req, 0, sizeof(ap_req));
|
memset(&ap_req, 0, sizeof(ap_req));
|
||||||
ret = krb5_decode_ap_req(context, &pa_data->padata_value, &ap_req);
|
ret = krb5_decode_ap_req(context, &pa_data->padata_value, &ap_req);
|
||||||
@@ -942,8 +1030,7 @@ tgs_rep2(KDC_REQ_BODY *b,
|
|||||||
goto out2;
|
goto out2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ap_req.ticket.sname.name_string.len != 2 ||
|
if(!is_krbtgt(&ap_req.ticket.sname)){
|
||||||
strcmp(ap_req.ticket.sname.name_string.val[0], "krbtgt")){
|
|
||||||
kdc_log(0, "PA-DATA is not a ticket-granting ticket");
|
kdc_log(0, "PA-DATA is not a ticket-granting ticket");
|
||||||
ret = KRB5KDC_ERR_POLICY; /* ? */
|
ret = KRB5KDC_ERR_POLICY; /* ? */
|
||||||
goto out2;
|
goto out2;
|
||||||
@@ -998,9 +1085,12 @@ tgs_rep2(KDC_REQ_BODY *b,
|
|||||||
Realm r;
|
Realm r;
|
||||||
char *spn = NULL, *cpn = NULL;
|
char *spn = NULL, *cpn = NULL;
|
||||||
hdb_entry *server = NULL, *client = NULL;
|
hdb_entry *server = NULL, *client = NULL;
|
||||||
|
TransitedEncoding tr;
|
||||||
|
int loop = 0;
|
||||||
|
|
||||||
s = b->sname;
|
s = b->sname;
|
||||||
r = b->realm;
|
r = b->realm;
|
||||||
|
|
||||||
if(s == NULL)
|
if(s == NULL)
|
||||||
if(b->kdc_options.enc_tkt_in_skey &&
|
if(b->kdc_options.enc_tkt_in_skey &&
|
||||||
b->additional_tickets &&
|
b->additional_tickets &&
|
||||||
@@ -1022,30 +1112,47 @@ tgs_rep2(KDC_REQ_BODY *b,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
principalname2krb5_principal(&sp, *s, r);
|
principalname2krb5_principal(&sp, *s, r);
|
||||||
#endif
|
|
||||||
krb5_unparse_name(context, sp, &spn);
|
krb5_unparse_name(context, sp, &spn);
|
||||||
server = db_fetch(sp);
|
|
||||||
|
|
||||||
principalname2krb5_principal(&cp, tgt->cname, tgt->crealm);
|
principalname2krb5_principal(&cp, tgt->cname, tgt->crealm);
|
||||||
krb5_unparse_name(context, cp, &cpn);
|
krb5_unparse_name(context, cp, &cpn);
|
||||||
client = db_fetch(cp);
|
|
||||||
|
|
||||||
kdc_log(0, "TGS-REQ %s from %s for %s", cpn, from, spn);
|
kdc_log(0, "TGS-REQ %s from %s for %s", cpn, from, spn);
|
||||||
|
server_lookup:
|
||||||
|
server = db_fetch(sp);
|
||||||
|
|
||||||
|
|
||||||
if(server == NULL){
|
if(server == NULL){
|
||||||
|
Realm req_rlm, new_rlm;
|
||||||
|
if(loop++ < 2 && (req_rlm = is_krbtgt(&sp->name))){
|
||||||
|
new_rlm = find_rpath(req_rlm);
|
||||||
|
if(new_rlm){
|
||||||
|
kdc_log(5, "krbtgt for realm %s not found, trying %s",
|
||||||
|
req_rlm, new_rlm);
|
||||||
|
krb5_free_principal(context, sp);
|
||||||
|
free(spn);
|
||||||
|
krb5_make_principal(context, &sp, r,
|
||||||
|
"krbtgt", new_rlm, NULL);
|
||||||
|
krb5_unparse_name(context, sp, &spn);
|
||||||
|
goto server_lookup;
|
||||||
|
}
|
||||||
|
}
|
||||||
kdc_log(0, "Server not found in database: %s", spn);
|
kdc_log(0, "Server not found in database: %s", spn);
|
||||||
/* do foreign realm stuff */
|
|
||||||
ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
|
ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client = db_fetch(cp);
|
||||||
|
if(client == NULL)
|
||||||
|
kdc_log(1, "Client not found in database: %s", cpn);
|
||||||
|
#if 0
|
||||||
|
/* XXX check client only if same realm as krbtgt-instance */
|
||||||
if(client == NULL){
|
if(client == NULL){
|
||||||
kdc_log(0, "Client not found in database: %s", cpn);
|
kdc_log(0, "Client not found in database: %s", cpn);
|
||||||
ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
|
ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
if((b->kdc_options.validate || b->kdc_options.renew) &&
|
if((b->kdc_options.validate || b->kdc_options.renew) &&
|
||||||
!krb5_principal_compare(context,
|
!krb5_principal_compare(context,
|
||||||
@@ -1056,7 +1163,7 @@ tgs_rep2(KDC_REQ_BODY *b,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = tgs_make_reply(b, tgt, server, client, reply);
|
ret = tgs_make_reply(b, tgt, server, client, cp, krbtgt, reply);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
free(spn);
|
free(spn);
|
||||||
@@ -1083,6 +1190,7 @@ out2:
|
|||||||
0,
|
0,
|
||||||
reply);
|
reply);
|
||||||
krb5_free_principal(context, cp);
|
krb5_free_principal(context, cp);
|
||||||
|
krb5_free_principal(context, sp);
|
||||||
if (ticket) {
|
if (ticket) {
|
||||||
krb5_free_ticket(context, ticket);
|
krb5_free_ticket(context, ticket);
|
||||||
free(ticket);
|
free(ticket);
|
||||||
@@ -1096,27 +1204,6 @@ out2:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static krb5_error_code
|
|
||||||
request_server(KDC_REQ *req, krb5_principal *server)
|
|
||||||
{
|
|
||||||
PrincipalName *s = NULL;
|
|
||||||
Realm r;
|
|
||||||
s = req->req_body.sname;
|
|
||||||
r = req->req_body.realm;
|
|
||||||
if(s == NULL &&
|
|
||||||
req->req_body.additional_tickets &&
|
|
||||||
req->req_body.additional_tickets->len){
|
|
||||||
s = &req->req_body.additional_tickets->val[0].sname;
|
|
||||||
r = req->req_body.additional_tickets->val[0].realm;
|
|
||||||
}
|
|
||||||
if(s)
|
|
||||||
principalname2krb5_principal(server, *s, r);
|
|
||||||
else
|
|
||||||
krb5_build_principal(context, server, strlen(r), r, "anonymous", NULL);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
krb5_error_code
|
krb5_error_code
|
||||||
tgs_rep(KDC_REQ *req,
|
tgs_rep(KDC_REQ *req,
|
||||||
krb5_data *data,
|
krb5_data *data,
|
||||||
@@ -1125,9 +1212,6 @@ tgs_rep(KDC_REQ *req,
|
|||||||
krb5_error_code ret;
|
krb5_error_code ret;
|
||||||
int i;
|
int i;
|
||||||
PA_DATA *pa_data = NULL;
|
PA_DATA *pa_data = NULL;
|
||||||
krb5_principal server;
|
|
||||||
|
|
||||||
request_server(req, &server);
|
|
||||||
|
|
||||||
if(req->padata == NULL){
|
if(req->padata == NULL){
|
||||||
ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
|
ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
|
||||||
@@ -1146,7 +1230,7 @@ tgs_rep(KDC_REQ *req,
|
|||||||
kdc_log(0, "TGS-REQ from %s without PA-TGS-REQ", from);
|
kdc_log(0, "TGS-REQ from %s without PA-TGS-REQ", from);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
ret = tgs_rep2(&req->req_body, server, pa_data, data, from);
|
ret = tgs_rep2(&req->req_body, pa_data, data, from);
|
||||||
out:
|
out:
|
||||||
if(ret && data->data == NULL)
|
if(ret && data->data == NULL)
|
||||||
krb5_mk_error(context,
|
krb5_mk_error(context,
|
||||||
@@ -1154,9 +1238,8 @@ out:
|
|||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
server,
|
NULL,
|
||||||
0,
|
0,
|
||||||
data);
|
data);
|
||||||
krb5_free_principal(context, server);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user