From fcusack@ratbert.iconnet.net Wed Jan 6 17:27:07 1999 Received: from MIT.EDU (PACIFIC-CARRIER-ANNEX.MIT.EDU [18.69.0.28]) by rt-11.MIT.EDU (8.7.5/8.7.3) with SMTP id RAA18130 for ; Wed, 6 Jan 1999 17:27:06 -0500 Received: from ratbert.iconnet.net by MIT.EDU with SMTP id AA18407; Wed, 6 Jan 99 17:27:07 EST Received: (from fcusack@localhost) by ratbert.iconnet.net (8.9.1/8.9.1) id RAA21759; Wed, 6 Jan 1999 17:28:33 -0500 (EST) Message-Id: <199901062228.RAA21759@ratbert.iconnet.net> Date: Wed, 6 Jan 1999 17:28:33 -0500 (EST) From: fcusack@iconnet.net Reply-To: fcusack@iconnet.net To: krb5-bugs@MIT.EDU Cc: fcusack@iconnet.net Subject: Principal impersonation when using SAM X-Send-Pr-Version: 3.99 >Number: 681 >Category: krb5-kdc >Synopsis: It's possible to replay another principal's SAM data. >Confidential: no >Severity: serious >Priority: high >Responsible: krb5-unassigned >State: open >Class: sw-bug >Submitter-Id: unknown >Arrival-Date: Wed Jan 06 17:28:01 EST 1999 >Last-Modified: >Originator: Frank Cusack >Organization: Icon CMT Corp. >Release: krb5-current-19981119 >Environment: Unix System: SunOS ratbert 5.6 Generic_105181-09 sun4u sparc SUNW,Ultra-5_10 Architecture: sun4 >Description: If authentication of 2 principal's requires the same SAM mechanism, it's possible to use a known SAM reply from principal A to authenticate as principal B. This is b/c the current SAM code doesn't do enough checking on the returned SAM data. The fix below is based on previous patches I submitted. Without these patches (and this one), using SAM is significantly (critically) weaker than not using it. >How-To-Repeat: >Fix: Index: include/k5-int.h =================================================================== RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/include/k5-int.h,v retrieving revision 1.8 diff -u -r1.8 k5-int.h --- k5-int.h 1998/12/02 23:16:14 1.8 +++ k5-int.h 1999/01/06 22:10:50 @@ -349,6 +349,7 @@ krb5_keyblock sam_key; krb5_timestamp stime; /* for replay detection */ krb5_int32 susec; + krb5_principal client; krb5_data msd; /* mechanism specific data */ } krb5_predicted_sam_response; Index: kdc/preauth/pa_sam.c =================================================================== RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/kdc/preauth/pa_sam.c,v retrieving revision 1.2 diff -u -r1.2 pa_sam.c --- pa_sam.c 1998/12/01 02:06:21 1.2 +++ pa_sam.c 1999/01/06 22:10:51 @@ -36,11 +36,6 @@ */ /* - * XXX Most (all?) of the sam types use the master database key to - * encrypt the track-data. This is probably bad. - */ - -/* * To define a new SAM type, place it in the sam_inst_map below, * and take a look at the code for the other SAM types. * Index: kdc/preauth/pa_sam_cryptocard.c =================================================================== RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/kdc/preauth/pa_sam_cryptocard.c,v retrieving revision 1.8 diff -u -r1.8 pa_sam_cryptocard.c --- pa_sam_cryptocard.c 1998/12/02 23:16:14 1.8 +++ pa_sam_cryptocard.c 1999/01/06 22:10:51 @@ -242,14 +242,17 @@ } /* switch (mode) */ #endif /* KRBCONF_KDC_MODIFIES_KDB */ - /* Convert the SAD into a key. */ psr.magic = KV5M_PREDICTED_SAM_RESPONSE; + if (retval = krb5_copy_principal(context, request->client, &psr.client)) + goto cleanup; + #ifdef USE_RCACHE /* XXX Watch these types when time_t or krb5_timestamp changes. */ if (retval = krb5_us_timeofday(context, &psr.stime, &psr.susec)) goto cleanup; #endif /* USE_RCACHE */ + /* Convert the SAD into a key. */ if (retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5, &predict_response, 0 /* salt */, &psr.sam_key)) { @@ -382,6 +385,7 @@ krb5_predicted_sam_response * psr = 0; krb5_enc_sam_response_enc * esre = 0; krb5_timestamp timenow; + char * princ_req = 0, *princ_psr = 0; /* Sanity check */ if (sr->sam_type != PA_SAM_TYPE_CRYPTOCARD) { @@ -419,6 +423,16 @@ goto cleanup; } + if (retval = krb5_unparse_name(context, request->client, &princ_req)) + goto cleanup; + if (retval = krb5_unparse_name(context, psr->client, &princ_psr)) + goto cleanup; + if (strcmp(princ_req, princ_psr) != 0) { + com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED, + "Principal mismatch in rb1 track-data!"); + goto cleanup; + } + if (retval = krb5_timeofday(context, &timenow)) goto cleanup; @@ -438,8 +452,8 @@ } /* Now check the replay cache. */ - rep.client = "sam/rc"; /* Any fixed value will do, although this */ - rep.server = "sam/rc"; /* should not match any principal name. */ + rep.client = princ_psr; + rep.server = "sam/rc"; /* Should not match any principal name. */ rep.ctime = psr->stime; rep.cusec = psr->susec; if (retval = krb5_rc_store(kdc_context, kdc_rcache, &rep)) { @@ -508,6 +522,11 @@ krb5_free_predicted_sam_response(context, psr); if (esre) krb5_free_enc_sam_response_enc(context, esre); + + if (princ_req) + krb5_xfree(princ_req); + if (princ_psr) + krb5_xfree(princ_psr); if (!retval) { setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH); Index: kdc/preauth/pa_sam_digi_path.c =================================================================== RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/kdc/preauth/pa_sam_digi_path.c,v retrieving revision 1.9 diff -u -r1.9 pa_sam_digi_path.c --- pa_sam_digi_path.c 1998/12/02 23:16:15 1.9 +++ pa_sam_digi_path.c 1999/01/06 22:10:51 @@ -150,14 +150,18 @@ predict_response.data = response; predict_response.length = 8; - /* Convert the SAD into a key. */ psr.magic = KV5M_PREDICTED_SAM_RESPONSE; + if (retval = krb5_copy_principal(context, request->client, &psr.client)) + goto cleanup; + + #ifdef USE_RCACHE /* XXX Watch these types when time_t or krb5_timestamp changes. */ if (retval = krb5_us_timeofday(context, &psr.stime, &psr.susec)) goto cleanup; #endif /* USE_RCACHE */ + /* Convert the SAD into a key. */ if (retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5, &predict_response, 0 /* salt */, &psr.sam_key)) { @@ -281,6 +285,7 @@ krb5_predicted_sam_response * psr = 0; krb5_enc_sam_response_enc * esre = 0; krb5_timestamp timenow; + char * princ_req = 0, *princ_psr = 0; /* Sanity check */ if (sr->sam_type != PA_SAM_TYPE_DIGI_PATH) { @@ -318,6 +323,16 @@ goto cleanup; } + if (retval = krb5_unparse_name(context, request->client, &princ_req)) + goto cleanup; + if (retval = krb5_unparse_name(context, psr->client, &princ_psr)) + goto cleanup; + if (strcmp(princ_req, princ_psr) != 0) { + com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED, + "Principal mismatch in snk4 track-data!"); + goto cleanup; + } + if (retval = krb5_timeofday(context, &timenow)) goto cleanup; @@ -337,8 +352,8 @@ } /* Now check the replay cache. */ - rep.client = "sam/rc"; /* Any fixed value will do, although this */ - rep.server = "sam/rc"; /* should not match any principal name. */ + rep.client = princ_psr; + rep.server = "sam/rc"; /* Should not match any principal name. */ rep.ctime = psr->stime; rep.cusec = psr->susec; if (retval = krb5_rc_store(kdc_context, kdc_rcache, &rep)) { @@ -392,6 +407,11 @@ krb5_free_predicted_sam_response(context, psr); if (esre) krb5_free_enc_sam_response_enc(context, esre); + + if (princ_req) + krb5_xfree(princ_req); + if (princ_psr) + krb5_xfree(princ_psr); if (!retval) { setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH); Index: kdc/preauth/pa_sam_grail.c =================================================================== RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/kdc/preauth/pa_sam_grail.c,v retrieving revision 1.6 diff -u -r1.6 pa_sam_grail.c --- pa_sam_grail.c 1998/12/02 23:16:15 1.6 +++ pa_sam_grail.c 1999/01/06 22:10:51 @@ -69,6 +69,9 @@ sc.sam_challenge.length = strlen(sc.sam_challenge.data); psr.magic = KV5M_PREDICTED_SAM_RESPONSE; + if (retval = krb5_copy_principal(context, request->client, &psr.client)) + goto cleanup; + #ifdef USE_RCACHE /* XXX Watch these types when time_t or krb5_timestamp changes. */ if (retval = krb5_us_timeofday(context, &psr.stime, &psr.susec)) @@ -183,6 +186,7 @@ krb5_predicted_sam_response * psr = 0; krb5_enc_sam_response_enc * esre = 0; krb5_timestamp timenow; + char * princ_req = 0, *princ_psr = 0; /* Sanity check */ if (sr->sam_type != PA_SAM_TYPE_GRAIL) { @@ -220,6 +224,16 @@ goto cleanup; } + if (retval = krb5_unparse_name(context, request->client, &princ_req)) + goto cleanup; + if (retval = krb5_unparse_name(context, psr->client, &princ_psr)) + goto cleanup; + if (strcmp(princ_req, princ_psr) != 0) { + com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED, + "Principal mismatch in Grail track-data!"); + goto cleanup; + } + if (retval = krb5_timeofday(context, &timenow)) goto cleanup; @@ -239,8 +253,8 @@ } /* Now check the replay cache. */ - rep.client = "sam/rc"; /* Any fixed value will do, although this */ - rep.server = "sam/rc"; /* should not match any principal name. */ + rep.client = princ_psr; + rep.server = "sam/rc"; /* Should not match any principal name. */ rep.ctime = psr->stime; rep.cusec = psr->susec; if (retval = krb5_rc_store(kdc_context, kdc_rcache, &rep)) { @@ -294,6 +308,11 @@ krb5_free_predicted_sam_response(context, psr); if (esre) krb5_free_enc_sam_response_enc(context, esre); + + if (princ_req) + krb5_xfree(princ_req); + if (princ_psr) + krb5_xfree(princ_psr); if (!retval) { setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH); Index: kdc/preauth/pa_sam_securid.c =================================================================== RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/kdc/preauth/pa_sam_securid.c,v retrieving revision 1.7 diff -u -r1.7 pa_sam_securid.c --- pa_sam_securid.c 1998/12/02 23:16:15 1.7 +++ pa_sam_securid.c 1999/01/06 22:10:53 @@ -142,6 +142,9 @@ g_sc.sam_response_prompt.length = strlen(g_sc.sam_response_prompt.data); psr.magic = KV5M_PREDICTED_SAM_RESPONSE; + if (retval = krb5_copy_principal(context, request->client, &psr.client)) + goto cleanup; + psr.msd.data = &g_sid_track_data; psr.msd.length = sizeof(g_sid_track_data); #ifdef USE_RCACHE @@ -269,6 +272,7 @@ krb5_predicted_sam_response * psr = 0; krb5_enc_sam_response_enc * esre = 0; krb5_timestamp timenow; + char * princ_req = 0, *princ_psr = 0; char * user = 0, cp; @@ -378,6 +382,16 @@ goto cleanup; } + if (retval = krb5_unparse_name(context, request->client, &princ_req)) + goto cleanup; + if (retval = krb5_unparse_name(context, psr->client, &princ_psr)) + goto cleanup; + if (strcmp(princ_req, princ_psr) != 0) { + com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED, + "Principal mismatch in SecurID track-data!"); + goto cleanup; + } + #ifdef USE_RCACHE /* Probably not useful for SecurID, but what the hell. */ { @@ -395,8 +409,8 @@ } /* Now check the replay cache. */ - rep.client = "sam/rc"; /* Any fixed value will do, although this */ - rep.server = "sam/rc"; /* should not match any principal name. */ + rep.client = princ_psr; + rep.server = "sam/rc"; /* Should not match any principal name. */ rep.ctime = psr->stime; rep.cusec = psr->susec; if (retval = krb5_rc_store(kdc_context, kdc_rcache, &rep)) { @@ -551,6 +565,11 @@ krb5_free_enc_sam_response_enc(context, esre); if (client_key.contents) krb5_free_keyblock_contents(context, &client_key); + + if (princ_req) + krb5_xfree(princ_req); + if (princ_psr) + krb5_xfree(princ_psr); if (user) krb5_xfree(user); Index: lib/krb5/asn.1/asn1_k_decode.c =================================================================== RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/asn.1/asn1_k_decode.c,v retrieving revision 1.8 diff -u -r1.8 asn1_k_decode.c --- asn1_k_decode.c 1998/12/02 23:16:15 1.8 +++ asn1_k_decode.c 1999/01/06 22:10:55 @@ -815,7 +815,10 @@ get_field(val->sam_key,0,asn1_decode_encryption_key); get_field(val->stime,1,asn1_decode_kerberos_time); get_field(val->susec,2,asn1_decode_int32); - opt_string(val->msd,3,asn1_decode_octetstring); + alloc_field(val->client,krb5_principal_data); + get_field(val->client,3,asn1_decode_realm); + get_field(val->client,4,asn1_decode_principal_name); + opt_string(val->msd,5,asn1_decode_octetstring); end_structure(); val->magic = KV5M_PREDICTED_SAM_RESPONSE; } Index: lib/krb5/asn.1/asn1_k_encode.c =================================================================== RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/asn.1/asn1_k_encode.c,v retrieving revision 1.8 diff -u -r1.8 asn1_k_encode.c --- asn1_k_encode.c 1998/12/02 23:16:15 1.8 +++ asn1_k_encode.c 1999/01/06 22:10:55 @@ -949,7 +949,9 @@ { asn1_setup(); - add_optstring(val->msd,3,asn1_encode_octetstring); + add_optstring(val->msd,5,asn1_encode_octetstring); + asn1_addfield(val->client,4,asn1_encode_principal_name); + asn1_addfield(val->client,3,asn1_encode_realm); asn1_addfield(val->susec,2,asn1_encode_integer); asn1_addfield(val->stime,1,asn1_encode_kerberos_time); asn1_addfield(&(val->sam_key),0,asn1_encode_encryption_key); Index: lib/krb5/error_tables/kv5m_err.et =================================================================== RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/error_tables/kv5m_err.et,v retrieving revision 1.2 diff -u -r1.2 kv5m_err.et --- kv5m_err.et 1998/11/25 06:50:50 1.2 +++ kv5m_err.et 1999/01/06 22:10:56 @@ -80,6 +80,5 @@ error_code KV5M_PASSWD_PHRASE_ELEMENT, "Bad magic number for passwd_phrase_element" error_code KV5M_GSS_OID, "Bad magic number for GSSAPI OID" error_code KV5M_GSS_QUEUE, "Bad magic number for GSSAPI QUEUE" -error_code KV5M_RB1_TRACK_DATA, "Bad magic number for RB1_TRACK_DATA" end Index: lib/krb5/krb/kfree.c =================================================================== RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/krb/kfree.c,v retrieving revision 1.4 diff -u -r1.4 kfree.c --- kfree.c 1998/12/01 02:06:23 1.4 +++ kfree.c 1999/01/06 22:10:57 @@ -653,6 +653,8 @@ return; if (psr->sam_key.contents) krb5_free_keyblock_contents(ctx, &psr->sam_key); + if (psr->client) + krb5_free_principal(ctx, psr->client); if (psr->msd.data) krb5_free_data_contents(ctx, &psr->msd); } >Audit-Trail: >Unformatted: