From 59fbeb07361e536536e9e6bf73fe01ce99f4587b Mon Sep 17 00:00:00 2001 From: Theodore Tso Date: Tue, 17 Aug 1993 02:43:46 +0000 Subject: [PATCH] Improved logging of TGS and AS error cases Fixed interrealm authentication; will now give out tickets for foreign interrealm requests. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@2630 dc483132-0cff-0310-8789-dd5450dbe970 --- src/kdc/do_tgs_req.c | 48 ++++++++++++----- src/kdc/extern.c | 2 +- src/kdc/kdc_util.c | 126 ++++++++++++++++++++++++++++++++++--------- src/kdc/kdc_util.h | 1 + 4 files changed, 138 insertions(+), 39 deletions(-) diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c index 6d3348807..2f77fa706 100644 --- a/src/kdc/do_tgs_req.c +++ b/src/kdc/do_tgs_req.c @@ -92,7 +92,7 @@ krb5_data **response; /* filled in with a response packet */ krb5_last_req_entry *nolrarray[2], nolrentry; /* krb5_address *noaddrarray[1]; */ krb5_enctype useetype; - int errcode; + int errcode, errcode2; register int i; int firstpass = 1; char *status = 0; @@ -115,11 +115,32 @@ krb5_data **response; /* filled in with a response packet */ if (!fromstring) fromstring = ""; + if (errcode = krb5_unparse_name(request->server, &sname)) { + status = "UNPARSING SERVER"; + goto cleanup; + } + errcode = kdc_process_tgs_req(request, from, pkt, &req_authdat); if (req_authdat) header_ticket = req_authdat->ticket; - if (errcode) + + if (header_ticket && header_ticket->enc_part2 && + (errcode2 = krb5_unparse_name(header_ticket->enc_part2->client, + &cname))) { + status = "UNPARSING CLIENT"; + errcode = errcode2; + goto cleanup; + } + + if (errcode) { + status = "PROCESS_TGS"; + goto cleanup; + } + + if (!header_ticket) { + status="UNEXPECTED NULL in header_ticket"; goto cleanup; + } /* * We've already dealt with the AP_REQ authentication, so we can @@ -127,12 +148,6 @@ krb5_data **response; /* filled in with a response packet */ * decrypted with the session key. */ - if (errcode = krb5_unparse_name(header_ticket->enc_part2->client, &cname)) - goto cleanup; - - if (errcode = krb5_unparse_name(request->server, &sname)) - goto cleanup; - authtime = header_ticket->enc_part2->times.authtime; /* XXX make sure server here has the proper realm...taken from AP_REQ @@ -554,8 +569,12 @@ tgt_again: cleanup: if (status) - syslog(LOG_INFO, "TGS_REQ%c %s: authtime %d, host %s, %s for %s", - secondary_ch, status, authtime, fromstring, cname, sname); + syslog(LOG_INFO, "TGS_REQ%c %s: authtime %d, host %s, %s for %s%s%s", + secondary_ch, status, authtime, fromstring, + cname ? cname : "", + sname ? sname : "", + errcode ? ", " : "", + errcode ? error_message(errcode) : ""); if (errcode) { errcode -= ERROR_TABLE_BASE_krb5; if (errcode < 0 || errcode > 128) @@ -564,6 +583,7 @@ cleanup: retval = prepare_error_tgs(request, header_ticket, errcode, fromstring, response); } + if (request) krb5_free_kdc_req(request); if (req_authdat) @@ -578,7 +598,7 @@ cleanup: krb5_free_keyblock(session_key); if (newtransited) free(enc_tkt_reply.transited.tr_contents.data); - + return retval; } @@ -604,8 +624,10 @@ krb5_data **response; } errpkt.error = error; errpkt.server = request->server; - errpkt.client = ticket ? ticket->enc_part2->client : 0; /* may not know - the name */ + if (ticket && ticket->enc_part2) + errpkt.client = ticket->enc_part2->client; + else + errpkt.client = 0; errpkt.text.length = strlen(error_message(error+KRB5KDC_ERR_NONE))+1; if (!(errpkt.text.data = malloc(errpkt.text.length))) { if (ticket) diff --git a/src/kdc/extern.c b/src/kdc/extern.c index a3de5d6bc..3ecd98d7b 100644 --- a/src/kdc/extern.c +++ b/src/kdc/extern.c @@ -58,5 +58,5 @@ char *dbm_db_name = DEFAULT_KDB_FILE; krb5_keyblock tgs_key; krb5_kvno tgs_kvno; -static krb5_data tgs_data[3] = { {sizeof(TGTNAME)-1, TGTNAME}, {0, 0}}; +static krb5_data tgs_data[3] = { {KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME}, {0, 0}}; krb5_principal_data tgs_server_struct = { { 0, 0}, tgs_data, 2, 0}; diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c index f044f243c..40592f3e1 100644 --- a/src/kdc/kdc_util.c +++ b/src/kdc/kdc_util.c @@ -129,12 +129,34 @@ OLDDECLARG(krb5_kvno, vno) OLDDECLARG(krb5_keyblock **, key) { register struct kparg *whoisit = (struct kparg *)keyprocarg; - - if (vno != whoisit->kvno) + char *sname; + + if (vno != whoisit->kvno) { + if (!krb5_unparse_name(principal, &sname)) { + syslog(LOG_ERR, + "TGS_REQ: BAD KEY VNO: server='%s', expecting %d, got %d", + sname, vno, whoisit->kvno); + free(sname); + } return KRB5KRB_AP_ERR_BADKEYVER; + } return(krb5_copy_keyblock(whoisit->key, key)); } +/* + * Returns TRUE if the kerberos principal is the name of a Kerberos ticket + * service. + */ +krb5_boolean krb5_is_tgs_principal(principal) + krb5_principal principal; +{ + if ((krb5_princ_component(principal, 0)->length == + KRB5_TGS_NAME_SIZE) && + (!memcmp(krb5_princ_component(principal, 0)->data, + KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE))) + return TRUE; + return FALSE; +} /* * given authentication data (provides seed for checksum), calculate checksum @@ -182,7 +204,7 @@ krb5_tkt_authent **ret_authdat; krb5_checksum our_cksum; krb5_data *scratch = 0, scratch1, scratch2; krb5_pa_data **tmppa; - krb5_boolean local_client = TRUE; + krb5_boolean foreign_server = FALSE; krb5_enc_tkt_part *ticket_enc; our_cksum.contents = 0; @@ -201,7 +223,7 @@ krb5_tkt_authent **ret_authdat; if (retval = decode_krb5_ap_req(&scratch2, &apreq)) return retval; - + if (!(authdat = (krb5_tkt_authent *)malloc(sizeof(*authdat)))) { retval = ENOMEM; goto cleanup; @@ -212,6 +234,7 @@ krb5_tkt_authent **ret_authdat; if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) || isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) { + syslog(LOG_INFO, "TGS_REQ: SESSION KEY or MUTUAL"); retval = KRB5KDC_ERR_POLICY; apreq->ticket = 0; /* Caller will free the ticket */ goto cleanup; @@ -238,7 +261,14 @@ krb5_tkt_authent **ret_authdat; memcmp(krb5_princ_realm(apreq->ticket->server)->data, krb5_princ_realm(tgs_server)->data, krb5_princ_realm(tgs_server)->length)) - local_client = FALSE; + foreign_server = TRUE; + { + char *tmp, *tmp1; + + krb5_unparse_name(apreq->ticket->server, &tmp); + krb5_unparse_name(tgs_server, &tmp1); + syslog(LOG_INFO, "server: %s, tgs_server: %s", tmp, tmp1); + } retval = krb5_rd_req_decoded(apreq, apreq->ticket->server, from->address, @@ -267,16 +297,21 @@ krb5_tkt_authent **ret_authdat; /* now rearrange output from rd_req_decoded */ /* make sure the client is of proper lineage (see above) */ - if (!local_client) { + if (foreign_server) { krb5_data *tkt_realm = krb5_princ_realm(ticket_enc->client); krb5_data *tgs_realm = krb5_princ_realm(tgs_server); - if (tkt_realm->length != tgs_realm->length || - memcmp(tkt_realm->data, tgs_realm->data, tgs_realm->length)) { + if (tkt_realm->length == tgs_realm->length || + !memcmp(tkt_realm->data, tgs_realm->data, tgs_realm->length)) { /* someone in a foreign realm claiming to be local */ + syslog(LOG_INFO, "PROCESS_TGS: failed lineage check"); retval = KRB5KDC_ERR_POLICY; goto cleanup; } } + if (!authdat->authenticator->checksum) { + retval = KRB5KRB_AP_ERR_INAPP_CKSUM; + goto cleanup; + } our_cksum.checksum_type = authdat->authenticator->checksum->checksum_type; if (!valid_cksumtype(our_cksum.checksum_type)) { retval = KRB5KDC_ERR_SUMTYPE_NOSUPP; @@ -354,7 +389,7 @@ krb5_kvno *kvno; krb5_db_free_principal(&server, nprincs); if (!krb5_unparse_name(ticket->server, &sname)) { - syslog(LOG_ERR, "TGS_REQ: can't find key for '%s'", + syslog(LOG_ERR, "TGS_REQ: UNKNOWN SERVER: server='%s'", sname); free(sname); } @@ -865,6 +900,7 @@ krb5_data *data; int lastlevel = 1000; /* last level seen */ int length; /* various lengths */ int tag; /* tag number */ + unsigned char savelen; /* saved length of our field */ /* we assume that the first identifier/length will tell us how long the entire stream is. */ @@ -892,9 +928,17 @@ krb5_data *data; if (tag == field) { /* return length and data */ astream++; + savelen = *astream; if ((data->length = asn1length(&astream)) < 0) { return(-1); } + /* if the field length is indefinite, we will have to subtract two + (terminating octets) from the length returned since we don't want + to pass any info from the "wrapper" back. asn1length will always return + the *total* length of the field, not just what's contained in it */ + if ((savelen & 0xff) == 0x80) { + data->length -=2 ; + } data->data = (char *)astream; return(0); } else if (tag <= classes) { @@ -915,7 +959,7 @@ krb5_data *data; } } return(-1); -} +} /* * Routines that validate a TGS request; checks a lot of things. :-) @@ -957,23 +1001,55 @@ char **status; /* * Verify that the server principal in authdat->ticket is correct - * (either the ticket granting service or the service we're - * looking for) + * (either the ticket granting service or the service that was + * originally requested) */ - if (krb5_principal_compare(ticket->server, tgs_server)) { - /* Server must allow TGS based issuances */ - if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) { - *status = "TGT BASED NOT ALLOWED"; - return(KDC_ERR_POLICY); - } - } else { - if (!krb5_principal_compare(ticket->server, - request->server)) { - *status = "BAD SERVER IN TKT"; - return KRB5KRB_AP_ERR_NOT_US; - } + if (!krb5_principal_compare(ticket->server, request->server)) { + /* + * OK, we need to validate the krbtgt service in the ticket. + * + * The krbtgt service is of the form: + * krbtgt/realm-A@realm-B + * + * Realm A is the "server realm"; the realm of the + * server of the requested ticket must match this realm. + * Of course, it should be a realm serviced by this KDC. + * + * Realm B is the "client realm"; this is what should be + * added to the transited field. (which is done elsewhere) + */ + char *destination_realm; + + /* Make sure there are two components... */ + if (krb5_princ_size(ticket->server) != 2) { + *status = "BAD TGS SERVER LENGTH"; + return KRB_AP_ERR_NOT_US; + } + /* ...that the first component is krbtgt... */ + if (!krb5_is_tgs_principal(ticket->server)) { + *status = "BAD TGS SERVER NAME"; + return KRB_AP_ERR_NOT_US; + } + /* ...and that the second component matches the server realm... */ + if ((krb5_princ_component(ticket->server, 1)->length != + krb5_princ_realm(request->server)->length) || + memcmp(krb5_princ_component(ticket->server, 1)->data, + krb5_princ_realm(request->server)->data, + krb5_princ_realm(request->server)->length)) { + *status = "BAD TGS SERVER INSTANCE"; + return KRB_AP_ERR_NOT_US; + } + /* XXX add check that second component must match locally + * supported realm? + */ + + /* Server must allow TGS based issuances */ + if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) { + *status = "TGT BASED NOT ALLOWED"; + return(KDC_ERR_POLICY); + } } - + /* TGS must be forwardable to get forwarded or forwardable ticket */ if ((isflagset(request->kdc_options, KDC_OPT_FORWARDED) || isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) && diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h index b88523ad0..bc667543e 100644 --- a/src/kdc/kdc_util.h +++ b/src/kdc/kdc_util.h @@ -32,6 +32,7 @@ krb5_error_code check_hot_list PROTOTYPE((krb5_ticket *)); krb5_boolean realm_compare PROTOTYPE((krb5_data *, krb5_principal)); +krb5_boolean krb5_is_tgs_principal PROTOTYPE((krb5_principal)); krb5_error_code add_to_transited PROTOTYPE((krb5_data *, krb5_data *, krb5_principal, -- 2.26.2