From be49dfd98e5161b5f4a88ed7a1405e0fa4dfb353 Mon Sep 17 00:00:00 2001 From: Theodore Tso Date: Mon, 15 Mar 1993 15:46:51 +0000 Subject: [PATCH] Added changes from HP to checksum actual asn.1 KDC-REQ-BODY structure, instead of encoding and decoding it. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@2527 dc483132-0cff-0310-8789-dd5450dbe970 --- src/kdc/kdc_util.c | 211 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 185 insertions(+), 26 deletions(-) diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c index c7b8c639c..9c88b1788 100644 --- a/src/kdc/kdc_util.c +++ b/src/kdc/kdc_util.c @@ -136,10 +136,43 @@ OLDDECLARG(krb5_keyblock **, key) } +/* + * given authentication data (provides seed for checksum), calculate checksum + * for source data and compare to authdata checksum. Storage for checksum + * is provided. + */ +static krb5_error_code +comp_cksum(type, source, authdat, dest) +krb5_cksumtype type; +krb5_data *source; +krb5_tkt_authent *authdat; +krb5_checksum *dest; +{ + krb5_error_code retval; + + /* first compute checksum */ + if (retval = krb5_calculate_checksum(type, + source->data, + source->length, + authdat->ticket->enc_part2->session->contents, /* seed */ + authdat->ticket->enc_part2->session->length, /* seed length */ + dest)) { + return retval; + } + if (dest->length != authdat->authenticator->checksum->length || + memcmp((char *)dest->contents, + (char *)authdat->authenticator->checksum->contents, + dest->length)) { + return KRB5KRB_AP_ERR_BAD_INTEGRITY; + } + return 0; +} + krb5_error_code -kdc_process_tgs_req(request, from, ret_authdat) +kdc_process_tgs_req(request, from, pkt, ret_authdat) krb5_kdc_req *request; const krb5_fulladdr *from; +krb5_data *pkt; krb5_tkt_authent **ret_authdat; { krb5_ap_req *apreq; @@ -147,7 +180,7 @@ krb5_tkt_authent **ret_authdat; struct kparg who; krb5_error_code retval; krb5_checksum our_cksum; - krb5_data *scratch, scratch2; + krb5_data *scratch, scratch1, scratch2; krb5_pa_data **tmppa; krb5_boolean local_client = TRUE; krb5_enc_tkt_part *ticket_enc; @@ -260,37 +293,26 @@ krb5_tkt_authent **ret_authdat; return ENOMEM; /* XXX cktype nosupp */ } - /* encode the body, verify the checksum */ + /* we try checksumming the req-body two different ways: first + try encoding using our local asn.1 library. If that fails, + then reach into the raw asn.1 stream (if available), and checksum + that directly. */ if (retval = encode_krb5_kdc_req_body(request, &scratch)) { krb5_free_ap_req(apreq); + xfree(our_cksum.contents); return retval; /* XXX should be in kdc range */ } - - if (retval = krb5_calculate_checksum(our_cksum.checksum_type, - scratch->data, - scratch->length, - ticket_enc->session->contents, /* seed */ - ticket_enc->session->length, /* seed length */ - &our_cksum)) { - xfree(our_cksum.contents); - krb5_free_data(scratch); - krb5_free_ap_req(apreq); - return retval; + if ((retval = comp_cksum(our_cksum.checksum_type, scratch, authdat, &our_cksum)) && (pkt)) { + if (fetch_asn1_field(pkt->data, 1, 4, &scratch1) < 0) { + retval = KRB5KRB_AP_ERR_BAD_INTEGRITY; + } else { + retval = comp_cksum(our_cksum.checksum_type, &scratch1, authdat, &our_cksum); } - if (our_cksum.length != authdat->authenticator->checksum->length || - memcmp((char *)our_cksum.contents, - (char *)authdat->authenticator->checksum->contents, - our_cksum.length)) { - xfree(our_cksum.contents); - krb5_free_data(scratch); - krb5_free_ap_req(apreq); - return KRB5KRB_AP_ERR_BAD_INTEGRITY; } + krb5_free_ap_req(apreq); krb5_free_data(scratch); xfree(our_cksum.contents); - - krb5_free_ap_req(apreq); - return 0; + return retval; } krb5_error_code @@ -749,6 +771,143 @@ char **status; return 0; } +#define ASN1_ID_CLASS (0xc0) +#define ASN1_ID_TYPE (0x20) +#define ASN1_ID_TAG (0x1f) +#define ASN1_CLASS_UNIV (0) +#define ASN1_CLASS_APP (1) +#define ASN1_CLASS_CTX (2) +#define ASN1_CLASS_PRIV (3) +#define asn1_id_constructed(x) (x & ASN1_ID_TYPE) +#define asn1_id_primitive(x) (!asn1_id_constructed(x)) +#define asn1_id_class(x) ((x & ASN1_ID_CLASS) >> 6) +#define asn1_id_tag(x) (x & ASN1_ID_TAG) + +/* + * asn1length - return encoded length of value. + * + * passed a pointer into the asn.1 stream, which is updated + * to point right after the length bits. + * + * returns -1 on failure. + */ +static int +asn1length(astream) +unsigned char **astream; +{ + int length; /* resulting length */ + int sublen; /* sublengths */ + int blen; /* bytes of length */ + unsigned char *p; /* substring searching */ + + if (**astream & 0x80) { + blen = **astream & 0x7f; + if (blen > 3) { + return(-1); + } + for (++*astream, length = 0; blen; ++*astream, blen--) { + length = (length << 8) | **astream; + } + if (length == 0) { + /* indefinite length, figure out by hand */ + p = *astream; + p++; + while (1) { + /* compute value length. */ + if ((sublen = asn1length(&p)) < 0) { + return(-1); + } + p += sublen; + /* check for termination */ + if ((!*p++) && (!*p)) { + p++; + break; + } + } + length = p - *astream; + } + } else { + length = **astream; + ++*astream; + } + return(length); +} + +/* + * fetch_asn1_field - return raw asn.1 stream of subfield. + * + * this routine is passed a context-dependent tag number and "level" and returns + * the size and length of the corresponding level subfield. + * + * levels and are numbered starting from 1. + * + * returns 0 on success, -1 otherwise. + */ +int +fetch_asn1_field(astream, level, field, data) +unsigned char *astream; +unsigned int level; +unsigned int field; +krb5_data *data; +{ + unsigned char *estream; /* end of stream */ + int classes; /* # classes seen so far this level */ + int levels = 0; /* levels seen so far */ + int lastlevel = 1000; /* last level seen */ + int length; /* various lengths */ + int tag; /* tag number */ + + /* we assume that the first identifier/length will tell us + how long the entire stream is. */ + astream++; + estream = astream; + if ((length = asn1length(&astream)) < 0) { + return(-1); + } + estream += length; + /* search down the stream, checking identifiers. we process identifiers + until we hit the "level" we want, and then process that level for our + subfield, always making sure we don't go off the end of the stream. */ + while (astream < estream) { + if (!asn1_id_constructed(*astream)) { + return(-1); + } + if (asn1_id_class(*astream) == ASN1_CLASS_CTX) { + if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) { + levels++; + classes = -1; + } + lastlevel = tag; + if (levels == level) { + /* in our context-dependent class, is this the one we're looking for ? */ + if (tag == field) { + /* return length and data */ + astream++; + if ((data->length = asn1length(&astream)) < 0) { + return(-1); + } + data->data = (char *)astream; + return(0); + } else if (tag <= classes) { + /* we've seen this class before, something must be wrong */ + return(-1); + } else { + classes = tag; + } + } + } + /* if we're not on our level yet, process this value. otherwise skip over it */ + astream++; + if ((length = asn1length(&astream)) < 0) { + return(-1); + } + if (levels == level) { + astream += length; + } + } + return(-1); +} + /* * Routines that validate a TGS request; checks a lot of things. :-) * @@ -886,7 +1045,7 @@ char **status; /* Server must allow DUP SKEY requests */ if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) && isflagset(server.attributes, KRB5_KDB_DISALLOW_DUP_SKEY)) { - *status = "DUP_SKEY DISALLOED"; + *status = "DUP_SKEY DISALLOWED"; return(KDC_ERR_POLICY); } -- 2.26.2