diff --git a/kdc/kerberos5.c b/kdc/kerberos5.c index f2057015c..8994b9cc1 100644 --- a/kdc/kerberos5.c +++ b/kdc/kerberos5.c @@ -7,37 +7,62 @@ RCSID("$Id$"); krb5_error_code as_rep(krb5_context context, KDC_REQ *req, - krb5_data *reply) + krb5_data *reply, + const char *from) { KDC_REQ_BODY *b = &req->req_body; AS_REP rep; KDCOptions f = b->kdc_options; - hdb_entry *client, *server; + hdb_entry *client = NULL, *server = NULL; int etype; EncTicketPart *et = calloc(1, sizeof(*et)); EncKDCRepPart *ek = calloc(1, sizeof(*ek)); - krb5_principal client_princ; - krb5_error_code ret; + krb5_principal client_princ, server_princ; + char *client_name, *server_name; + krb5_error_code ret = 0; + const char *e_text = NULL; int i; krb5_keyblock *ckey, *skey; - client = db_fetch(context, b->cname, b->realm); - if(client == NULL) - return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; + if(b->sname == NULL){ + server_name = ""; + ret = KRB5KRB_ERR_GENERIC; + e_text = "No server in request"; + } else{ + principalname2krb5_principal (&server_princ, *(b->sname), b->realm); + krb5_unparse_name(context, server_princ, &server_name); + } + + if(b->cname == NULL){ + client_name = ""; + ret = KRB5KRB_ERR_GENERIC; + e_text = "No client in request"; + } else { + principalname2krb5_principal (&client_princ, *(b->cname), b->realm); + krb5_unparse_name(context, client_princ, &client_name); + } + kdc_log(0, "AS-REQ %s from %s for %s", client_name, from, server_name); - server = db_fetch(context, b->sname, b->realm); + if(ret) + goto out; - if(server == NULL){ - hdb_free_entry(context, client); - free(client); - return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; + client = db_fetch(context, client_princ); + if(client == NULL){ + kdc_log(0, "UNKNOWN -- %s", client_name); + ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; + goto out; + } + + server = db_fetch(context, server_princ); + + if(server == NULL){ + kdc_log(0, "UNKNOWN -- %s", server_name); + ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; + goto out; } - principalname2krb5_principal (&client_princ, *(b->cname), b->realm); - /* XXX Check for pa_enc_timestamp */ - if(req->padata == NULL || req->padata->len < 1 || req->padata->val->padata_type != pa_enc_timestamp) { PA_DATA foo; @@ -56,14 +81,18 @@ as_rep(krb5_context context, foo_data.length = len; foo_data.data = buf + sizeof(buf) - len; - krb5_mk_error (client_princ, - KRB5KDC_ERR_PREAUTH_REQUIRED, - "Need to use PA-ENC-TIMESTAMP", - &foo_data, - reply); + ret = KRB5KDC_ERR_PREAUTH_REQUIRED; + krb5_mk_error(context, + ret, + "Need to use PA-ENC-TIMESTAMP", + &foo_data, + client_princ, + server_princ, + 0, + reply); - ret = 0; - goto out; + kdc_log(0, "No PA-ENC-TIMESTAMP -- %s", client_name); + goto out2; } else { krb5_data ts_data; PA_ENC_TS_ENC p; @@ -76,11 +105,8 @@ as_rep(krb5_context context, &enc_data, &len); if (ret) { - krb5_mk_error (client_princ, - KRB5KRB_AP_ERR_BAD_INTEGRITY, - "Couldn't decode", - NULL, - reply); + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + kdc_log(0, "Failed to decode PA-DATA -- %s", client_name); goto out; } @@ -92,12 +118,8 @@ as_rep(krb5_context context, &ts_data); free_EncryptedData(&enc_data); if (ret) { - krb5_mk_error (client_princ, - KRB5KRB_AP_ERR_BAD_INTEGRITY, - "Couldn't decode", - NULL, - reply); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + kdc_log(0, "Failed to decrypt PA-DATA -- %s", client_name); goto out; } ret = decode_PA_ENC_TS_ENC(ts_data.data, @@ -106,26 +128,27 @@ as_rep(krb5_context context, &len); krb5_data_free(&ts_data); if (ret) { - krb5_mk_error (client_princ, - KRB5KRB_AP_ERR_BAD_INTEGRITY, - "Couldn't decode", - NULL, - reply); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + kdc_log(0, "Failed to decode PA-ENC-TS-ENC -- %s", client_name); goto out; } patime = p.patimestamp; free_PA_ENC_TS_ENC(&p); if (abs(kdc_time - p.patimestamp) > context->max_skew) { - krb5_mk_error (client_princ, + krb5_mk_error (context, KRB5KDC_ERR_PREAUTH_FAILED, "Too large time skew", NULL, + client_princ, + server_princ, + 0, reply); ret = KRB5KDC_ERR_PREAUTH_FAILED; - goto out; + kdc_log(0, "Too large time skew -- %s", client_name); + goto out2; } et->flags.pre_authent = 1; + kdc_log(2, "Pre-authentication succeded -- %s", client_name); } /* Find appropriate key */ @@ -141,10 +164,13 @@ as_rep(krb5_context context, if(ret){ ret = KRB5KDC_ERR_ETYPE_NOSUPP; + kdc_log(0, "No support for etypes -- %s", client_name); goto out; } etype = b->etype.val[i]; + + kdc_log(2, "Using etype %d -- %s", etype, client_name); memset(&rep, 0, sizeof(rep)); rep.pvno = 5; @@ -157,6 +183,7 @@ as_rep(krb5_context context, if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey){ ret = KRB5KDC_ERR_BADOPTION; + kdc_log(0, "Bad KDC options -- %s", client_name); goto out; } @@ -180,6 +207,7 @@ as_rep(krb5_context context, start = *et->starttime = *req->req_body.from; et->flags.invalid = 1; et->flags.postdated = 1; /* XXX ??? */ + kdc_log(2, "Postdated ticket requested -- %s", client_name); } if(b->till == 0) b->till = MAX_TIME; @@ -216,6 +244,7 @@ as_rep(krb5_context context, *et->renew_till = t; } } + if(b->addresses){ et->caddr = malloc(sizeof(*et->caddr)); copy_HostAddresses(b->addresses, et->caddr); @@ -248,8 +277,10 @@ as_rep(krb5_context context, ret = encode_EncTicketPart(buf + sizeof(buf) - 1, sizeof(buf),et, &len); free_EncTicketPart(et); free(et); - if(ret) + if(ret) { + kdc_log(0, "Failed to encode ticket -- %s", client); goto out; + } krb5_encrypt_EncryptedData(context, buf + sizeof(buf) - len, @@ -265,8 +296,10 @@ as_rep(krb5_context context, ret = encode_EncASRepPart(buf + sizeof(buf) - 1, sizeof(buf), ek, &len); free_EncKDCRepPart(ek); free(ek); - if(ret) + if(ret) { + kdc_log(0, "Failed to encode KDC-REP -- %s", client_name); goto out; + } krb5_encrypt_EncryptedData(context, buf + sizeof(buf) - len, len, @@ -280,338 +313,586 @@ as_rep(krb5_context context, ret = encode_AS_REP(buf + sizeof(buf) - 1, sizeof(buf), &rep, &len); free_AS_REP(&rep); - if(ret) + if(ret) { + kdc_log(0, "Failed to encode AS-REP -- %s", client_name); goto out; + } krb5_data_copy(reply, buf + sizeof(buf) - len, len); } out: + if(ret){ + krb5_mk_error(context, + ret, + e_text, + NULL, + client_princ, + server_princ, + 0, + reply); + } +out2: krb5_free_principal(context, client_princ); - hdb_free_entry(context, client); - free(client); - hdb_free_entry(context, server); - free(server); + free(client_name); + krb5_free_principal(context, server_princ); + free(server_name); + if(client){ + hdb_free_entry(context, client); + free(client); + } + if(server){ + hdb_free_entry(context, server); + free(server); + } return ret; } -krb5_error_code -tgs_rep(krb5_context context, - KDC_REQ *req, - krb5_data *data) + +static krb5_error_code +check_tgs_flags(KDC_REQ_BODY *b, + EncTicketPart *tgt, EncTicketPart *et) { - KDC_REQ_BODY *b = &req->req_body; - KDCOptions f = req->req_body.kdc_options; - EncTicketPart *tgt; - hdb_entry *server, *krbtgt, *client; - TGS_REP rep; - EncTicketPart *et = calloc(1, sizeof(*et)); - EncKDCRepPart *ek = calloc(1, sizeof(*ek)); + KDCOptions f = b->kdc_options; + if(f.forwardable){ + if(!tgt->flags.forwardable){ + kdc_log(0, "Bad request for forwardable ticket"); + return KRB5KDC_ERR_BADOPTION; + } + et->flags.forwardable = 1; + } + if(f.forwarded){ + if(!tgt->flags.forwardable){ + kdc_log(0, "Request to forward non-forwardable ticket"); + return KRB5KDC_ERR_BADOPTION; + } + et->flags.forwarded = 1; + et->caddr = b->addresses; + /* resp.caddr := req.addresses */ + } + if(tgt->flags.forwarded) + et->flags.forwarded = 1; + + if(f.proxiable){ + if(!tgt->flags.proxiable){ + kdc_log(0, "Bad request for proxiable ticket"); + return KRB5KDC_ERR_BADOPTION; + } + et->flags.proxiable = 1; + } + if(f.proxy){ + if(!tgt->flags.proxiable){ + kdc_log(0, "Request to proxy non-proxiable ticket"); + return KRB5KDC_ERR_BADOPTION; + } + et->flags.proxy = 1; + et->caddr = b->addresses; + /* resp.caddr := req.addresses */ + } + if(f.allow_postdate){ + if(!tgt->flags.may_postdate){ + kdc_log(0, "Bad request for post-datable ticket"); + return KRB5KDC_ERR_BADOPTION; + } + et->flags.may_postdate = 1; + } + if(f.postdated){ + if(!tgt->flags.may_postdate){ + kdc_log(0, "Bad request for postdated ticket"); + return KRB5KDC_ERR_BADOPTION; + } + et->flags.postdated = 1; + et->flags.invalid = 1; + et->starttime = malloc(sizeof(*et->starttime)); + *et->starttime = *b->from; + } + if(f.validate){ + if(!tgt->flags.invalid || tgt->starttime == NULL){ + kdc_log(0, "Bad request to validate ticket"); + return KRB5KDC_ERR_BADOPTION; + } + if(*tgt->starttime < kdc_time){ + kdc_log(0, "Early request to validate ticket"); + return KRB5KRB_AP_ERR_TKT_NYV; + } + /* XXX tkt = tgt */ + et->flags.invalid = 0; + } + + /* check for excess flags */ + return 0; +} + +static krb5_error_code +tgs_make_reply(krb5_context context, KDC_REQ_BODY *b, EncTicketPart *tgt, + hdb_entry *server, hdb_entry *client, krb5_data *reply) +{ + KDC_REP rep; + EncKDCRepPart ek; + EncTicketPart et; + KDCOptions f = b->kdc_options; + krb5_error_code ret; int i; - krb5_keyblock *skey; krb5_enctype etype; + krb5_keyblock *skey; - if(req->padata == NULL || req->padata->len < 1) - return KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */ - if(req->padata->val->padata_type != pa_tgs_req) - return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; + /* Find appropriate key */ + for(i = 0; i < b->etype.len; i++){ + ret = hdb_etype2key(context, server, b->etype.val[i], &skey); + if(ret == 0) + break; + } + + if(ret){ + kdc_log(0, "Failed to find requested etype"); + return KRB5KDC_ERR_ETYPE_NOSUPP; + } + + etype = b->etype.val[i]; + ret = check_tgs_flags(b, tgt, &et); + if(ret) + return ret; + + memset(&rep, 0, sizeof(rep)); + memset(&et, 0, sizeof(et)); + memset(&ek, 0, sizeof(ek)); + + rep.pvno = 5; + rep.msg_type = krb_tgs_rep; + copy_Realm(&tgt->crealm, &rep.crealm); + copy_PrincipalName(&tgt->cname, &rep.cname); + rep.ticket.tkt_vno = 5; + copy_Realm(krb5_princ_realm(context, server->principal), + &rep.ticket.realm); + krb5_principal2principalname(&rep.ticket.sname, server->principal); + + et.caddr = tgt->caddr; + et.authtime = tgt->authtime; + + if(f.renew){ + time_t old_life; + if(!tgt->flags.renewable) + return KRB5KDC_ERR_BADOPTION; + if(*tgt->renew_till >= kdc_time) + return KRB5KRB_AP_ERR_TKT_EXPIRED; + /* XXX tkt = tgt */ + et.starttime = malloc(sizeof(*et.starttime)); + *et.starttime = kdc_time; + old_life = tgt->endtime - *tgt->starttime; + et.endtime = min(*tgt->renew_till, + *et.starttime + old_life); + }else{ + time_t till; + et.starttime = malloc(sizeof(*et.starttime)); + *et.starttime = kdc_time; + till = b->till; + if(till == 0) + till = MAX_TIME; + if(client->max_life) + till = min(till, *et.starttime + client->max_life); + if(server->max_life) + till = min(till, *et.starttime + server->max_life); + till = min(till, tgt->endtime); +#if 0 + till = min(till, et.starttime + realm->max_life); +#endif + et.endtime = till; + if(f.renewable_ok && + et.endtime < b->till && + tgt->flags.renewable){ + f.renewable = 1; + b->rtime = malloc(sizeof(*b->rtime)); + *b->rtime = min(b->till, *tgt->renew_till); + } + } + if(f.renewable && tgt->flags.renewable && b->rtime){ + time_t rtime; + rtime = *b->rtime; + if(rtime == 0) + rtime = MAX_TIME; + et.flags.renewable = 1; + if(client->max_renew) + rtime = min(rtime, *et.starttime + client->max_renew); + if(server->max_renew) + rtime = min(rtime, *et.starttime + server->max_renew); + rtime = min(rtime, *tgt->renew_till); +#if 0 + rtime = min(rtime, *et.starttime + realm->max_renew); +#endif + et.renew_till = malloc(sizeof(*et.renew_till)); + *et.renew_till = rtime; + } + + et.flags.pre_authent = tgt->flags.pre_authent; + et.flags.hw_authent = tgt->flags.hw_authent; + + /* XXX Check enc-authorization-data */ + + krb5_generate_random_keyblock(context, + skey->keytype, + &et.key); + et.crealm = tgt->crealm; + et.cname = tgt->cname; + /* do cross realm stuff */ + et.transited = tgt->transited; + + + ek.key = et.key; + /* MIT must have at least one last_req */ + ek.last_req.len = 1; + ek.last_req.val = calloc(1, sizeof(*ek.last_req.val)); + ek.nonce = b->nonce; + ek.flags = et.flags; + ek.authtime = et.authtime; + ek.starttime = et.starttime; + ek.endtime = et.endtime; + ek.renew_till = et.renew_till; + ek.srealm = rep.ticket.realm; + ek.sname = rep.ticket.sname; + ek.caddr = et.caddr; + { - krb5_auth_context ac = NULL; - krb5_principal princ; - krb5_flags ap_req_options; - krb5_ticket *ticket; - krb5_error_code ret; - hdb_entry *ent; - - ret = krb5_build_principal(context, - &princ, - strlen(req->req_body.realm), - req->req_body.realm, - "krbtgt", - req->req_body.realm, - NULL); - if(ret) return ret; - - { - PrincipalName p; - p.name_type = 0; - p.name_string.val = calloc(2, sizeof(*p.name_string.val)); - p.name_string.len = 2; - p.name_string.val[0] = "krbtgt"; - p.name_string.val[1] = req->req_body.realm; - krbtgt = db_fetch(context, &p, req->req_body.realm); - free(p.name_string.val); + unsigned char buf[1024]; /* XXX The data could be indefinite */ + size_t len; + ret = encode_EncTicketPart(buf + sizeof(buf) - 1, + sizeof(buf), &et, &len); + if(ret){ + kdc_log(0, "Failed to encode EncTicketPart: %s", + krb5_get_err_text(context, ret)); + goto out; } - if(krbtgt == NULL) - return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; - - ret = krb5_rd_req_with_keyblock(context, &ac, - &req->padata->val->padata_value, - princ, - &krbtgt->keyblock, - &ap_req_options, - &ticket); - - krb5_free_principal(context, princ); - if(ret) - return ret; - - tgt = &ticket->ticket; - - { - krb5_authenticator auth; - size_t len; - unsigned char buf[1024]; - krb5_auth_getauthenticator(context, ac, &auth); - if(auth->cksum == NULL) - return KRB5KRB_AP_ERR_INAPP_CKSUM; - /* XXX */ - if (auth->cksum->cksumtype != CKSUMTYPE_RSA_MD4 && - auth->cksum->cksumtype != CKSUMTYPE_RSA_MD5 && - auth->cksum->cksumtype != CKSUMTYPE_RSA_MD5_DES) - return KRB5KRB_AP_ERR_INAPP_CKSUM; + krb5_encrypt_EncryptedData(context, buf + sizeof(buf) - len, len, + etype, + skey, + &rep.ticket.enc_part); - /* XXX */ - encode_KDC_REQ_BODY(buf + sizeof(buf) - 1, sizeof(buf), - b, &len); - ret = krb5_verify_checksum(context, buf + sizeof(buf) - len, len, - &tgt->key, - auth->cksum); - if(ret) - return ret; - krb5_auth_con_free(context, ac); - free_Authenticator(auth); - free(auth); + ret = encode_EncTGSRepPart(buf + sizeof(buf) - 1, + sizeof(buf), &ek, &len); + if(ret){ + kdc_log(0, "Failed to encode EncTicketPart: %s", + krb5_get_err_text(context, ret)); + goto out; } - - server = db_fetch(context, b->sname, b->realm); - if(server == NULL){ - /* do foreign realm stuff */ - return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; + /* It is somewhat unclear where the etype in the following + encryption should come from. What we have is a session + key in the passed tgt, and a list of preferred etypes + *for the new ticket*. Should we pick the best possible + etype, given the keytype in the tgt, or should we look + at the etype list here as well? What if the tgt + session key is DES3 and we want a ticket with a (say) + CAST session key. Should the DES3 etype be added to the + etype list, even if we don't want a session key with + DES3? */ + + + krb5_encrypt_EncryptedData(context, + buf + sizeof(buf) - len, len, + etype, /* XXX */ + &tgt->key, + &rep.enc_part); + + ret = encode_TGS_REP(buf + sizeof(buf) - 1, sizeof(buf), &rep, &len); + if(ret){ + kdc_log(0, "Failed to encode TGS-REP: %s", + krb5_get_err_text(context, ret)); + goto out; } + krb5_data_copy(reply, buf + sizeof(buf) - len, len); + out: + free_TGS_REP(&rep); + if(et.starttime) + free(et.starttime); + if(et.renew_till) + free(et.renew_till); + free_LastReq(&ek.last_req); + memset(et.key.keyvalue.data, 0, et.key.keyvalue.length); + free_EncryptionKey(&et.key); + } + return ret; +} - client = db_fetch(context, &tgt->cname, tgt->crealm); - if(client == NULL) - return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; - - /* Find appropriate key */ - for(i = 0; i < b->etype.len; i++){ - ret = hdb_etype2key(context, server, b->etype.val[i], &skey); - if(ret == 0) - break; - } - - if(ret) - return KRB5KDC_ERR_ETYPE_NOSUPP; - - etype = b->etype.val[i]; +static krb5_error_code +tgs_check_authenticator(krb5_context context, krb5_auth_context ac, + KDC_REQ_BODY *b, krb5_keyblock *key) +{ + krb5_authenticator auth; + size_t len; + unsigned char buf[1024]; + krb5_error_code ret; - memset(&rep, 0, sizeof(rep)); - rep.pvno = 5; - rep.msg_type = krb_tgs_rep; - copy_Realm(&tgt->crealm, &rep.crealm); - copy_PrincipalName(&tgt->cname, &rep.cname); - rep.ticket.tkt_vno = 5; - copy_Realm(&b->realm, &rep.ticket.realm); - copy_PrincipalName (b->sname, &rep.ticket.sname); + krb5_auth_getauthenticator(context, ac, &auth); + if(auth->cksum == NULL){ + kdc_log(0, "No authenticator in request"); + ret = KRB5KRB_AP_ERR_INAPP_CKSUM; + goto out; + } + /* XXX */ + if (auth->cksum->cksumtype != CKSUMTYPE_RSA_MD4 && + auth->cksum->cksumtype != CKSUMTYPE_RSA_MD5 && + auth->cksum->cksumtype != CKSUMTYPE_RSA_MD5_DES){ + kdc_log(0, "Bad checksum type in authenticator: %d", + auth->cksum->cksumtype); + ret = KRB5KRB_AP_ERR_INAPP_CKSUM; + goto out; + } + + /* XXX */ + ret = encode_KDC_REQ_BODY(buf + sizeof(buf) - 1, sizeof(buf), + b, &len); + if(ret){ + kdc_log(0, "Failed to encode KDC-REQ-BODY: %s", + krb5_get_err_text(context, ret)); + goto out; + } + ret = krb5_verify_checksum(context, buf + sizeof(buf) - len, len, + key, + auth->cksum); + if(ret){ + kdc_log(0, "Failed to verify checksum: %s", + krb5_get_err_text(context, ret)); + } +out: + free_Authenticator(auth); + free(auth); + return 0; +} + - et->caddr = tgt->caddr; - - if(f.forwardable){ - if(!tgt->flags.forwardable) - return KRB5KDC_ERR_BADOPTION; - et->flags.forwardable = 1; - } - if(f.forwarded){ - if(!tgt->flags.forwardable) - return KRB5KDC_ERR_BADOPTION; - et->flags.forwarded = 1; - et->caddr = req->req_body.addresses; - /* resp.caddr := req.addresses */ - } - if(tgt->flags.forwarded) - et->flags.forwarded = 1; - if(f.proxiable){ - if(!tgt->flags.proxiable) - return KRB5KDC_ERR_BADOPTION; - et->flags.proxiable = 1; - } - if(f.proxy){ - if(!tgt->flags.proxiable) - return KRB5KDC_ERR_BADOPTION; - et->flags.proxy = 1; - et->caddr = req->req_body.addresses; - /* resp.caddr := req.addresses */ - } - if(f.allow_postdate){ - if(!tgt->flags.may_postdate) - return KRB5KDC_ERR_BADOPTION; - et->flags.may_postdate = 1; - } - if(f.postdated){ - if(!tgt->flags.may_postdate) - return KRB5KDC_ERR_BADOPTION; - et->flags.postdated = 1; - et->flags.invalid = 1; - et->starttime = malloc(sizeof(*et->starttime)); - *et->starttime = *req->req_body.from; - } - if(f.validate){ - if(!tgt->flags.invalid) - return KRB5KDC_ERR_BADOPTION; - if(*tgt->starttime > kdc_time) - return KRB5KRB_AP_ERR_TKT_NYV; - /* XXX tkt = tgt */ - et->flags.invalid = 0; - } - - /* check for excess flags */ - - et->authtime = tgt->authtime; - - if(f.renew){ - time_t old_life; - if(!tgt->flags.renewable) - return KRB5KDC_ERR_BADOPTION; - if(*tgt->renew_till >= kdc_time) - return KRB5KRB_AP_ERR_TKT_EXPIRED; - /* XXX tkt = tgt */ - et->starttime = malloc(sizeof(*et->starttime)); - *et->starttime = kdc_time; - old_life = tgt->endtime - *tgt->starttime; - et->endtime = min(*tgt->renew_till, - *et->starttime + old_life); - }else{ - time_t till; - et->starttime = malloc(sizeof(*et->starttime)); - *et->starttime = kdc_time; - till = b->till; - if(till == 0) - till = MAX_TIME; - if(client->max_life) - till = min(till, *et->starttime + client->max_life); - if(server->max_life) - till = min(till, *et->starttime + server->max_life); - till = min(till, tgt->endtime); -#if 0 - till = min(till, et->starttime + realm->max_life); -#endif - et->endtime = till; - if(f.renewable_ok && - et->endtime < b->till && - tgt->flags.renewable){ - f.renewable = 1; - b->rtime = malloc(sizeof(*b->rtime)); - *b->rtime = min(b->till, *tgt->renew_till); +static krb5_error_code +tgs_rep2(krb5_context context, + KDC_REQ_BODY *b, + krb5_principal sp, + PA_DATA *pa_data, + krb5_data *reply, + const char *from) +{ + krb5_ap_req ap_req; + size_t len; + krb5_error_code ret; + krb5_principal princ; + krb5_auth_context ac = NULL; + krb5_ticket *ticket; + krb5_flags ap_req_options; + const char *e_text = NULL; + + hdb_entry *krbtgt; + EncTicketPart *tgt; + + ret = krb5_decode_ap_req(context, &pa_data->padata_value, &ap_req); + if(ret){ + kdc_log(0, "Failed to decode AP-REQ: %s", + krb5_get_err_text(context, ret)); + goto out; + } + + if(ap_req.ticket.sname.name_string.len != 2 || + strcmp(ap_req.ticket.sname.name_string.val[0], "krbtgt")){ + kdc_log(0, "PA-DATA is not a ticket-granting ticket"); + ret = KRB5KDC_ERR_POLICY; /* ? */ + goto out; + } + + principalname2krb5_principal(&princ, + ap_req.ticket.sname, + ap_req.ticket.realm); + + krbtgt = db_fetch(context, princ); + + if(krbtgt == NULL) { + char *p; + krb5_unparse_name(context, princ, &p); + kdc_log(0, "Ticket-granting ticket not found in database: %s", p); + free(p); + ret = KRB5KRB_AP_ERR_NOT_US; + goto out; + } + + ret = krb5_verify_ap_req(context, + &ac, + &ap_req, + princ, + &krbtgt->keyblock, + &ap_req_options, + &ticket); + + krb5_free_principal(context, princ); + if(ret) { + kdc_log(0, "Failed to verify AP-REQ: %s", + krb5_get_err_text(context, ret)); + goto out; + } + + tgt = &ticket->ticket; + + ret = tgs_check_authenticator(context, ac, b, &tgt->key); + + krb5_auth_con_free(context, ac); + + if(ret){ + kdc_log(0, "Failed to verify authenticator: %s", + krb5_get_err_text(context, ret)); + goto out; + } + + { + PrincipalName *s; + Realm r; + krb5_principal cp; + char *spn, *cpn; + hdb_entry *server, *client; + + s = b->sname; + r = b->realm; + if(s == NULL) + if(b->kdc_options.enc_tkt_in_skey && + b->additional_tickets && + b->additional_tickets->len >= 1){ + krb5_principal p; + hdb_entry *uu; + principalname2krb5_principal(&p, + b->additional_tickets->val[0].sname, + b->additional_tickets->val[0].realm); + uu = db_fetch(context, p); + krb5_free_principal(context, p); + if(uu == NULL){ + ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; + goto out; + } + /* XXX */ + }else{ + ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; + goto out; } - } - if(f.renewable && tgt->flags.renewable && b->rtime){ - time_t rtime; - rtime = *b->rtime; - if(rtime == 0) - rtime = MAX_TIME; - et->flags.renewable = 1; - if(client->max_renew) - rtime = min(rtime, *et->starttime + client->max_renew); - if(server->max_renew) - rtime = min(rtime, *et->starttime + server->max_renew); - rtime = min(rtime, *tgt->renew_till); + #if 0 - rtime = min(rtime, *et->starttime + realm->max_renew); + principalname2krb5_principal(&sp, *s, r); #endif - et->renew_till = malloc(sizeof(*et->renew_till)); - *et->renew_till = rtime; + krb5_unparse_name(context, sp, &spn); + server = db_fetch(context, sp); + + principalname2krb5_principal(&cp, tgt->cname, tgt->crealm); + krb5_unparse_name(context, cp, &cpn); + client = db_fetch(context, cp); + + kdc_log(0, "TGS-REQ %s from %s for %s", cpn, from, spn); + + if(server == NULL){ + kdc_log(0, "Server not found in database: %s", spn); + /* do foreign realm stuff */ + ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; + goto out; } - /* XXX Check enc-authorization-data */ - - - krb5_generate_random_keyblock(context, - skey->keytype, - &et->key); - et->crealm = tgt->crealm; - et->cname = tgt->cname; - /* do cross realm stuff */ - et->transited = tgt->transited; - - - memset(ek, 0, sizeof(*ek)); - ek->key = et->key; - /* MIT must have at least one last_req */ - ek->last_req.len = 1; - ek->last_req.val = calloc(1, sizeof(*ek->last_req.val)); - ek->nonce = b->nonce; - ek->flags = et->flags; - ek->authtime = et->authtime; - ek->starttime = et->starttime; - ek->endtime = et->endtime; - ek->renew_till = et->renew_till; - ek->srealm = rep.ticket.realm; - ek->sname = rep.ticket.sname; - ek->caddr = et->caddr; - - { - unsigned char buf[1024]; /* XXX The data could be indefinite */ - size_t len; - int e; - e = encode_EncTicketPart(buf + sizeof(buf) - 1, - sizeof(buf), et, &len); - if(e) - return e; - krb5_encrypt_EncryptedData(context, buf + sizeof(buf) - len, len, - etype, - skey, - &rep.ticket.enc_part); - - e = encode_EncTGSRepPart(buf + sizeof(buf) - 1, - sizeof(buf), ek, &len); - if(e) - return e; - - /* It is somewhat unclear where the etype in the following - encryption should come from. What we have is a session - key in the passed tgt, and a list of preferred etypes - *for the new ticket*. Should we pick the best possible - etype, given the keytype in the tgt, or should we look - at the etype list here as well? What if the tgt - session key is DES3 and we want a ticket with a (say) - CAST session key. Should the DES3 etype be added to the - etype list, even if we don't want a session key with - DES3? */ - - - krb5_encrypt_EncryptedData(context, - buf + sizeof(buf) - len, len, - etype, /* XXX */ - &tgt->key, - &rep.enc_part); - - e = encode_TGS_REP(buf + sizeof(buf) - 1, sizeof(buf), &rep, &len); - if(e) - return e; - free_TGS_REP(&rep); - krb5_data_copy(data, buf + sizeof(buf) - len, len); + if(client == NULL){ + kdc_log(0, "Client not found in database: %s", cpn); + ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; + goto out; } + + ret = tgs_make_reply(context, b, tgt, server, client, reply); + + out: + if(ret) + krb5_mk_error(context, + ret, + e_text, + NULL, + cp, + sp, + 0, + reply); + free_EncTicketPart(tgt); krb5_free_principal(context, ticket->client); free(ticket); - hdb_free_entry(context, krbtgt); - free(krbtgt); - hdb_free_entry(context, server); - free(server); - hdb_free_entry(context, client); - free(client); - free_EncryptionKey(&et->key); - if(et->starttime) - free(et->starttime); - free(et); - free(ek->last_req.val); - free(ek); - return 0; - } + krb5_free_principal(context, sp); + free(spn); + krb5_free_principal(context, cp); + free(cpn); + if(krbtgt){ + hdb_free_entry(context, krbtgt); + free(krbtgt); + } + if(server){ + hdb_free_entry(context, server); + free(server); + } + if(client){ + hdb_free_entry(context, client); + free(client); + } + + return ret; + } +} + +static krb5_error_code +request_server(krb5_context context, 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 +tgs_rep(krb5_context context, + KDC_REQ *req, + krb5_data *data, + const char *from) +{ + krb5_error_code ret; + int i; + PA_DATA *pa_data = NULL; + krb5_principal server; + + request_server(context, req, &server); + + if(req->padata == NULL){ + ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */ + kdc_log(0, "TGS-REQ from %s without PA-DATA", from); + goto out; + } + + for(i = 0; i < req->padata->len; i++) + if(req->padata->val[i].padata_type == pa_tgs_req){ + pa_data = &req->padata->val[i]; + break; + } + if(pa_data == NULL){ + ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP; + + kdc_log(0, "TGS-REQ from %s without PA-TGS-REQ", from); + goto out; + } + ret = tgs_rep2(context, &req->req_body, server, pa_data, data, from); +out: + if(ret && data->data == NULL) + krb5_mk_error(context, + ret, + NULL, + NULL, + NULL, + server, + 0, + data); + krb5_free_principal(context, server); + return ret; }