[3014] in Kerberos-V5-bugs

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

krb5-kdc/672: SAM Replay detection + K/M integrity

daemon@ATHENA.MIT.EDU (fcusack@iconnet.net)
Wed Dec 2 18:27:16 1998

Resent-From: gnats@rt-11.MIT.EDU (GNATS Management)
Resent-To: krb5-unassigned@RT-11.MIT.EDU
Resent-Reply-To: krb5-bugs@MIT.EDU, fcusack@iconnet.net
Date: Wed, 2 Dec 1998 18:26:54 -0500 (EST)
From: fcusack@iconnet.net
Reply-To: fcusack@iconnet.net
To: krb5-bugs@MIT.EDU
Cc: fcusack@iconnet.net


>Number:         672
>Category:       krb5-kdc
>Synopsis:       gethostid() not portable; shouldn't encrypt sam-track-id w/ K/M
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    krb5-unassigned
>State:          open
>Class:          sw-bug
>Submitter-Id:   unknown
>Arrival-Date:   Wed Dec 02 18:27:00 EST 1998
>Last-Modified:
>Originator:     Frank Cusack
>Organization:
Icon CMT Corp.
>Release:        krb5-current-19981119
>Environment:
N/A
System: SunOS ratbert 5.6 Generic_105181-09 sun4u sparc SUNW,Ultra-5_10
Architecture: sun4

>Description:
	A previous patch submitted by me adds a var kdc_id which is
	the return value from gethostid(). This is used to prevent
	replay of a sam-response to a KDC different than the one
	that issued the sam-challenge.

	But gethostid() isn't portable (unix only? and maybe not
	all unices), and anyway, the sam-track-id had been
	encrypted with the master database key. Probably not
	too much of a risk doing this but it can provide a lot
	of ciphertext for analysis. I don't believe any other code
	uses K/M to encrypt anything that travels over the network.

	This patch removes the kdc_id patch; and adds a var psr_key,
	which is a random key generated at KDC startup. The psr_key
	is used to encrypt the track-id instead of K/M. Since
	each KDC will get a different key to encrypt the track-id with,
	the "different KDC replay" is protected as a bonus.
	The chance of key collision is extremely small, assuming
	good randomness from krb5_c_make_random_key().
	
>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.7
diff -u -r1.7 k5-int.h
--- k5-int.h	1998/12/02 03:10:56	1.7
+++ k5-int.h	1998/12/02 23:13:24
@@ -347,9 +347,8 @@
 typedef struct _krb5_predicted_sam_response {
 	krb5_magic	magic;
 	krb5_keyblock	sam_key;
-	krb5_int32	kdc_id; /* some magic to avoid esre replays */
-	krb5_timestamp	stime;
-	krb5_int32	susec;	/* more replay detection */
+	krb5_timestamp	stime;	/* for replay detection */
+	krb5_int32	susec;
 	krb5_data	msd;	/* mechanism specific data */
 } krb5_predicted_sam_response;
 
Index: kdc/main.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/kdc/main.c,v
retrieving revision 1.3
diff -u -r1.3 main.c
--- main.c	1998/12/02 03:10:56	1.3
+++ main.c	1998/12/02 23:13:26
@@ -51,6 +51,9 @@
 
 void setup_signal_handlers PROTOTYPE((void));
 
+krb5_keyblock psr_key;
+krb5_error_code setup_sam PROTOTYPE((void));
+
 void initialize_realms PROTOTYPE((krb5_context, int, char **));
 
 void finish_realms PROTOTYPE((char *));
@@ -59,7 +62,6 @@
 static char *kdc_current_rcname = (char *) NULL;
 static int rkey_init_done = 0;
 
-krb5_int32 kdc_id;	/* hostid for SAM replay detection */
 #ifdef USE_RCACHE
 krb5_deltat rc_lifetime;
 #endif /* USE_RCACHE */
@@ -586,6 +588,12 @@
     return;
 }
 
+krb5_error_code
+setup_sam()
+{
+    return krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_MD5, &psr_key);
+}
+
 void
 usage(name)
 char *name;
@@ -821,7 +829,11 @@
 
     setup_signal_handlers();
 
-    kdc_id = gethostid();
+    if (retval = setup_sam()) {
+	com_err(argv[0], retval, "while initializing SAM");
+	finish_realms(argv[0]);
+	return 1;
+    }
 
     if ((retval = setup_network(argv[0]))) {
 	com_err(argv[0], retval, "while initializing network");
Index: kdc/preauth/pa_sam.h
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/kdc/preauth/pa_sam.h,v
retrieving revision 1.4
diff -u -r1.4 pa_sam.h
--- pa_sam.h	1998/12/02 03:10:56	1.4
+++ pa_sam.h	1998/12/02 23:13:26
@@ -25,7 +25,8 @@
 
 #include "k5-int.h"
 
-extern krb5_int32 kdc_id;
+extern krb5_keyblock psr_key;
+
 #ifdef USE_RCACHE
 extern krb5_deltat rc_lifetime;
 #endif /* USE_RCACHE */
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.7
diff -u -r1.7 pa_sam_cryptocard.c
--- pa_sam_cryptocard.c	1998/12/02 03:10:56	1.7
+++ pa_sam_cryptocard.c	1998/12/02 23:13:26
@@ -244,7 +244,6 @@
 
     /* Convert the SAD into a key. */
     psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
-    psr.kdc_id = kdc_id;
 #ifdef USE_RCACHE
     /* XXX Watch these types when time_t or krb5_timestamp changes. */
     if (retval = krb5_us_timeofday(context, &psr.stime, &psr.susec))
@@ -287,7 +286,7 @@
     psr.msd.data = NULL;
     psr.msd.length = 0;
 
-    if (retval = krb5_c_encrypt_length(context, master_keyblock.enctype,
+    if (retval = krb5_c_encrypt_length(context, psr_key.enctype,
 				       scratch->length, &enclen)) {
 	krb5_free_data(context, scratch);
 	goto cleanup;
@@ -301,7 +300,7 @@
     tmpdata.ciphertext.length = enclen;
     sc.sam_track_id = tmpdata.ciphertext; /* So we know to free it. */
 
-    retval = krb5_c_encrypt(context, &master_keyblock,
+    retval = krb5_c_encrypt(context, &psr_key,
 			    /* XXX */ 0, 0, scratch, &tmpdata);
     krb5_free_data(context, scratch);
     if (retval)
@@ -404,7 +403,7 @@
 
     tmpdata.enctype = ENCTYPE_UNKNOWN;
     tmpdata.ciphertext = sr->sam_track_id;
-    if (retval = krb5_c_decrypt(context, &master_keyblock, /* XXX */ 0, 0,
+    if (retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
 				&tmpdata, &scratch)) {
 	/* Record our retval, but let the client see a generic error. */
 	com_err("krb5kdc", retval, "decrypt track_id failed");
@@ -417,12 +416,6 @@
     krb5_xfree(scratch.data);
     if (retval) {
 	com_err("krb5kdc", retval, "decode_krb5_predicted_sam_response failed");
-	goto cleanup;
-    }
-
-    if (psr->kdc_id != kdc_id) {
-	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
-		"Warning - possible SAM replay attack!");
 	goto cleanup;
     }
 
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.8
diff -u -r1.8 pa_sam_digi_path.c
--- pa_sam_digi_path.c	1998/12/02 22:05:29	1.8
+++ pa_sam_digi_path.c	1998/12/02 23:13:26
@@ -152,7 +152,6 @@
 
     /* Convert the SAD into a key. */
     psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
-    psr.kdc_id = kdc_id;
 #ifdef USE_RCACHE
     /* XXX Watch these types when time_t or krb5_timestamp changes. */
     if (retval = krb5_us_timeofday(context, &psr.stime, &psr.susec))
@@ -187,7 +186,7 @@
     if (retval = encode_krb5_predicted_sam_response(&psr, &scratch))
 	goto cleanup;
 
-    if (retval = krb5_c_encrypt_length(context, master_keyblock.enctype,
+    if (retval = krb5_c_encrypt_length(context, psr_key.enctype,
 				       scratch->length, &enclen)) {
 	krb5_free_data(context, scratch);
 	goto cleanup;
@@ -201,7 +200,7 @@
     tmpdata.ciphertext.length = enclen;
     sc.sam_track_id = tmpdata.ciphertext; /* So we know to free it. */
 
-    retval = krb5_c_encrypt(context, &master_keyblock,
+    retval = krb5_c_encrypt(context, &psr_key,
 			    /* XXX */ 0, 0, scratch, &tmpdata);
     krb5_free_data(context, scratch);
     if (retval)
@@ -303,7 +302,7 @@
 
     tmpdata.enctype = ENCTYPE_UNKNOWN;
     tmpdata.ciphertext = sr->sam_track_id;
-    if (retval = krb5_c_decrypt(context, &master_keyblock, /* XXX */ 0, 0,
+    if (retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
 				&tmpdata, &scratch)) {
 	/* Record our retval, but let the client see a generic error. */
 	com_err("krb5kdc", retval, "decrypt track_id failed");
@@ -316,12 +315,6 @@
     krb5_xfree(scratch.data);
     if (retval) {
 	com_err("krb5kdc", retval, "decode_krb5_predicted_sam_response failed");
-	goto cleanup;
-    }
-
-    if (psr->kdc_id != kdc_id) {
-	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
-		"Warning - possible SAM replay attack!");
 	goto cleanup;
     }
 
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.5
diff -u -r1.5 pa_sam_grail.c
--- pa_sam_grail.c	1998/12/02 03:10:57	1.5
+++ pa_sam_grail.c	1998/12/02 23:13:26
@@ -69,7 +69,6 @@
     sc.sam_challenge.length = strlen(sc.sam_challenge.data);
 
     psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
-    psr.kdc_id = kdc_id;
 #ifdef USE_RCACHE
     /* XXX Watch these types when time_t or krb5_timestamp changes. */
     if (retval = krb5_us_timeofday(context, &psr.stime, &psr.susec))
@@ -86,7 +85,7 @@
     if (retval = encode_krb5_predicted_sam_response(&psr, &scratch))
 	goto cleanup;
 
-    if (retval = krb5_c_encrypt_length(context, master_keyblock.enctype,
+    if (retval = krb5_c_encrypt_length(context, psr_key.enctype,
 				       scratch->length, &enclen)) {
 	krb5_free_data(context, scratch);
 	goto cleanup;
@@ -100,7 +99,7 @@
     tmpdata.ciphertext.length = enclen;
     sc.sam_track_id = tmpdata.ciphertext; /* So we know to free it. */
 
-    retval = krb5_c_encrypt(context, &master_keyblock,
+    retval = krb5_c_encrypt(context, &psr_key,
 			    /* XXX */ 0, 0, scratch, &tmpdata);
     krb5_free_data(context, scratch);
     if (retval)
@@ -205,7 +204,7 @@
 
     tmpdata.enctype = ENCTYPE_UNKNOWN;
     tmpdata.ciphertext = sr->sam_track_id;
-    if (retval = krb5_c_decrypt(context, &master_keyblock, /* XXX */ 0, 0,
+    if (retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
 				&tmpdata, &scratch)) {
 	/* Record our retval, but let the client see a generic error. */
 	com_err("krb5kdc", retval, "decrypt track_id failed");
@@ -218,12 +217,6 @@
     krb5_xfree(scratch.data);
     if (retval) {
 	com_err("krb5kdc", retval, "decode_krb5_predicted_sam_response failed");
-	goto cleanup;
-    }
-
-    if (psr->kdc_id != kdc_id) {
-	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
-		"Warning - possible SAM replay attack!");
 	goto cleanup;
     }
 
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.6
diff -u -r1.6 pa_sam_securid.c
--- pa_sam_securid.c	1998/12/02 03:10:57	1.6
+++ pa_sam_securid.c	1998/12/02 23:13:27
@@ -142,7 +142,6 @@
     g_sc.sam_response_prompt.length = strlen(g_sc.sam_response_prompt.data);
 
     psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
-    psr.kdc_id = kdc_id;
     psr.msd.data = &g_sid_track_data;
     psr.msd.length = sizeof(g_sid_track_data);
 #ifdef USE_RCACHE
@@ -165,7 +164,7 @@
     psr.msd.data = NULL;
     psr.msd.length = 0;
 
-    if (retval = krb5_c_encrypt_length(context, master_keyblock.enctype,
+    if (retval = krb5_c_encrypt_length(context, psr_key.enctype,
 				       scratch->length, &enclen)) {
 	krb5_free_data(context, scratch);
 	goto cleanup;
@@ -179,7 +178,7 @@
     tmpdata.ciphertext.length = enclen;
     g_sc.sam_track_id = tmpdata.ciphertext; /* So we know to free it. */
 
-    retval = krb5_c_encrypt(context, &master_keyblock,
+    retval = krb5_c_encrypt(context, &psr_key,
 			    /* XXX */ 0, 0, scratch, &tmpdata);
     krb5_free_data(context, scratch);
     if (retval)
@@ -362,7 +361,7 @@
 
 	tmpdata.enctype = ENCTYPE_UNKNOWN;
 	tmpdata.ciphertext = sr->sam_track_id;
-	if (retval = krb5_c_decrypt(context, &master_keyblock, /* XXX */ 0, 0,
+	if (retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
 				    &tmpdata, &scratch)) {
 	    /* Record our retval, but let the client see a generic error. */
 	    com_err("krb5kdc", retval, "decrypt track_id failed");
@@ -376,12 +375,6 @@
 	if (retval) {
 	    com_err("krb5kdc", retval,
 		    "decode_krb5_predicted_sam_response failed");
-	    goto cleanup;
-	}
-
-	if (psr->kdc_id != kdc_id) {
-	    com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
-		    "Warning - possible SAM replay attack!");
 	    goto cleanup;
 	}
 
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.7
diff -u -r1.7 asn1_k_decode.c
--- asn1_k_decode.c	1998/12/02 03:10:57	1.7
+++ asn1_k_decode.c	1998/12/02 23:13:29
@@ -813,10 +813,9 @@
   setup();
   { begin_structure();
     get_field(val->sam_key,0,asn1_decode_encryption_key);
-    get_field(val->kdc_id,1,asn1_decode_int32);
-    opt_string(val->msd,2,asn1_decode_generalstring);
-    get_field(val->stime,3,asn1_decode_kerberos_time);
-    get_field(val->susec,4,asn1_decode_int32);
+    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);
     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.7
diff -u -r1.7 asn1_k_encode.c
--- asn1_k_encode.c	1998/12/02 03:10:57	1.7
+++ asn1_k_encode.c	1998/12/02 23:13:30
@@ -949,10 +949,9 @@
 {
   asn1_setup();
 
-  asn1_addfield(val->susec,4,asn1_encode_integer);
-  asn1_addfield(val->stime,3,asn1_encode_kerberos_time);
-  add_optstring(val->msd,2,asn1_encode_generalstring);
-  asn1_addfield(val->kdc_id,1,asn1_encode_integer);
+  add_optstring(val->msd,3,asn1_encode_octetstring);
+  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);
 
   asn1_makeseq();
>Audit-Trail:
>Unformatted:

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