[382] in Kerberos-V5-bugs

home help back first fref pref prev next nref lref last post

bug in kdc_process_tgs_req (sort of)

daemon@ATHENA.MIT.EDU (Jim Miller)
Fri Oct 22 21:53:46 1993

From: jim@bilbo.suite.com (Jim Miller)
Date: Fri, 22 Oct 93 20:39:42 -0500
To: krb5-bugs@MIT.EDU
Cc: kerberos@MIT.EDU
Reply-To: Jim_Miller@suite.com



This is for Kerberos 5, pre-beta 3


This bug will take some explaining.

krb5kdc blows up inside of "prepare_error_tgs"(kdc/do_tgs_req.c) on the  
following line:

    errpkt.client = ticket ? ticket->enc_part2->client : 0; /* may not know
							       the name */

because "ticket->enc_part2" is NULL.

The reason it is null is what takes long to explain.

Let's back up a bit to see how we got into "prepare_error_tgs"...

In this particular case, "prepare_error_tgs" was called by "process_tgs_req".   
Now "process_tgs_req" called it because "kdc_process_tgs_req" (kdc/kdc_util.c)  
returned with an error after the point in which the parameter "req_authdat" was  
set.  Here's the "process_tgs_req" code I'm refering to:

    errcode = kdc_process_tgs_req(request, from, pkt, &req_authdat);
    if (req_authdat)
	header_ticket = req_authdat->ticket;
    if (errcode)
	goto cleanup;

In other words, "kdc_process_tgs_req" returned an error (thus we'll goto  
cleanup) AND "req_authdat" had been set (thus header_ticket gets set pointing  
to req_authdat->ticket)

Ok, let's descend into "kdc_process_tgs_req" to see why it returned an error...


First, note that if "kdc_process_tgs_req" successfully decodes the imbedded  
apreq, then "req_authdat" gets set:  (NOTE: req_authdat == *ret_authdat)

    if (retval = decode_krb5_ap_req(&scratch2, &apreq))
	return retval;

    if (!(authdat = (krb5_tkt_authent *)malloc(sizeof(*authdat)))) {
	retval = ENOMEM;
	goto cleanup;
    }
    memset((char *)authdat, 0, sizeof(*authdat));
    authdat->ticket = apreq->ticket;
    *ret_authdat = authdat;

Moving along, we eventually get to a call to "krb5_rd_req_decoded".  Assume  
that "krb5_rd_req_decoded" returns an error.  (In the case where I discovered  
this bus, "krb5_rd_req_decoded" returned with KRB5KRB_AP_ERR_BADADDR.)   When  
"krb5_rd_req_decoded" returns with an error, we goto cleanup:

    retval = krb5_rd_req_decoded(apreq, apreq->ticket->server,
				 from->address,
				 0,	/* no fetchfrom */
				 kdc_rdreq_keyproc,
				 (krb5_pointer)&who,
				 kdc_rcache,
				 &nauthdat);
    krb5_free_keyblock(who.key);

    if (retval) {
	apreq->ticket = 0;		/* Caller will free the ticket */
	goto cleanup;
    }
	.
	.
	.
cleanup:
    if (apreq)
	krb5_free_ap_req(apreq);
    if (scratch)
	krb5_free_data(scratch);
    return retval;
}

The code indicates that we don't want "cleanup" to free the ticket that was in  
"apreq".  The intent is to return to the caller with "*ret_authdat" still  
pointing to a decoded and decrypted ticket that can later be used to form an  
error message in "prepare_error_tgs".

However...

Looking inside of "krb5_rd_req_decoded" we see the following:

    if (sender_addr && !krb5_address_search(sender_addr, 

    					req->ticket->enc_part2->caddrs)) {
	retval = KRB5KRB_AP_ERR_BADADDR;
	goto cleanup;
    }
	.
	.
	.
cleanup:
    if (tktauthent) {
        if (retval) {
	    krb5_free_tkt_authent(tktauthent);
        } else {
	    tktauthent->ap_options = req->ap_options;
	    *authdat = tktauthent;
	}
    }
    if (retval && req->ticket->enc_part2) {
	/* only free if we're erroring out...otherwise some
	   applications will need the output. */
        krb5_free_enc_tkt_part(req->ticket->enc_part2);   <--- *********
	req->ticket->enc_part2 = NULL;   		  <--- *********
    }
    if (tkt_key)
	krb5_free_keyblock(tkt_key);
    return retval;
}


Uh Oh!  The ticket we thought we were saving for "prepare_error_tgs" was  
actually cleaned up a bit.  In particular, the "enc_part2" portion is freed and  
set to NULL!  And that's why the following line in "prepare_error_tgs" blows  
up:

    errpkt.client = ticket ? ticket->enc_part2->client : 0; /* may not know
							       the name */



The easy way to fix this is to do the following:

    errpkt.client = (ticket && ticket->enc_part2) ? ticket->enc_part2->client
    						  : 0; /* may not know
							  the name */


However, that doesn't really fix the real problem, which is the incorrect  
assumption about the contents of "req_authdat" upon return from  
"kdc_process_tgs_req"

I don't have a suggested fix for that at this time.  I'll have to look at it  
some more.

Jim_Miller@suite.com

home help back first fref pref prev next nref lref last post