Skip Menu |
 

Download (untitled) / with headers
text/plain 18.2KiB
From fcusack@ratbert.iconnet.net Wed Nov 25 18:52:40 1998
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 SAA02176 for <bugs@RT-11.MIT.EDU>; Wed, 25 Nov 1998 18:52:39 -0500
Received: from ratbert.iconnet.net by MIT.EDU with SMTP
id AA19329; Wed, 25 Nov 98 18:52:21 EST
Received: (from fcusack@localhost)
by ratbert.iconnet.net (8.9.1/8.9.1) id SAA24884;
Wed, 25 Nov 1998 18:53:01 -0500 (EST)
Message-Id: <199811252353.SAA24884@ratbert.iconnet.net>
Date: Wed, 25 Nov 1998 18:53:01 -0500 (EST)
From: fcusack@iconnet.net
Reply-To: fcusack@iconnet.net
To: krb5-bugs@MIT.EDU
Cc: fcusack@iconnet.net
Subject: SAM preauth replay detection (I)
X-Send-Pr-Version: 3.99

Show quoted text
>Number: 665
>Category: krb5-kdc
>Synopsis: sam-response must go to the same KDC that sent sam-challenge
>Confidential: no
>Severity: serious
>Priority: high
>Responsible: krb5-unassigned
>State: open
>Class: sw-bug
>Submitter-Id: unknown
>Arrival-Date: Wed Nov 25 18:53:00 EST 1998
>Last-Modified:
>Originator: Frank Cusack
>Organization:
Icon CMT Corp.
Show quoted text
>Release: krb5-current-19981119
>Environment:
Unix
System: SunOS ratbert 5.6 Generic_105181-09 sun4u sparc SUNW,Ultra-5_10
Architecture: sun4

Show quoted text
>Description:
All state information for a sam-response is encoded in the
krb5_predicted_sam_response struct. This makes it easy for
an attacker to replay a sam-response to a different KDC.
(Actually there is no replay detection as of yet so it's
easy to replay to the same KDC as well; I'll submit a patch
for that later.)

This patch adds a kdc_id field to the predicted_sam_response,
which is the hostid of the KDC. This will only work on systems
that have gethostid() defined. Maybe you will want to write
a macro to define it some other way to systems missing it.

Also, some SAM types use multiple transactions, and those
transactions all need to go to the same KDC. This ensures that.

This patch depends on krb5-kdc/662, 663 and 664. This also
fixes /663 which was incomplete.
Show quoted text
>How-To-Repeat:
>Fix:
Index: lib/krb5/krb/kfree.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/krb/kfree.c,v
retrieving revision 1.2
diff -u -r1.2 kfree.c
--- kfree.c 1998/11/25 06:50:50 1.2
+++ kfree.c 1998/11/25 23:47:21
@@ -671,8 +671,8 @@
{
if (!esre)
return;
- if (esre->sam_passcode.data)
- krb5_free_data_contents(ctx, &esre->sam_passcode);
+ if (esre->sam_sad.data)
+ krb5_free_data_contents(ctx, &esre->sam_sad);
}

KRB5_DLLIMP void KRB5_CALLCONV
Index: include/k5-int.h
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/include/k5-int.h,v
retrieving revision 1.3
diff -u -r1.3 k5-int.h
--- k5-int.h 1998/11/25 18:34:20 1.3
+++ k5-int.h 1998/11/25 21:38:55
@@ -353,6 +353,7 @@
typedef struct _krb5_predicted_sam_response {
krb5_magic magic;
krb5_keyblock sam_key;
+ krb5_int32 kdc_id; /* some magic to avoid esre replays */
} krb5_predicted_sam_response;

typedef struct _krb5_sam_challenge {
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.3
diff -u -r1.3 asn1_k_decode.c
--- asn1_k_decode.c 1998/11/25 18:34:20 1.3
+++ asn1_k_decode.c 1998/11/25 21:39:01
@@ -813,6 +813,7 @@
setup();
{ begin_structure();
get_field(val->sam_key,0,asn1_decode_encryption_key);
+ get_field(val->kdc_id,1,asn1_decode_int32);
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.3
diff -u -r1.3 asn1_k_encode.c
--- asn1_k_encode.c 1998/11/25 18:34:20 1.3
+++ asn1_k_encode.c 1998/11/25 21:39:01
@@ -949,6 +949,7 @@
{
asn1_setup();

+ asn1_addfield(val->kdc_id,1,asn1_encode_integer);
asn1_addfield(&(val->sam_key),0,asn1_encode_encryption_key);

asn1_makeseq();
Index: include/k5-int.h
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/include/k5-int.h,v
retrieving revision 1.4
diff -u -r1.4 k5-int.h
--- k5-int.h 1998/11/25 21:40:28 1.4
+++ k5-int.h 1998/11/25 22:24:18
@@ -348,6 +348,7 @@
krb5_magic magic;
krb5_keyblock sam_key;
krb5_data next_challenge;
+ krb5_int32 kdc_id; /* some magic to avoid esre replays */
} krb5_rb1_track_data;

typedef struct _krb5_predicted_sam_response {
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.4
diff -u -r1.4 asn1_k_decode.c
--- asn1_k_decode.c 1998/11/25 21:40:28 1.4
+++ asn1_k_decode.c 1998/11/25 22:24:23
@@ -828,6 +828,7 @@
{ begin_structure();
opt_string(val->next_challenge, 0, asn1_decode_charstring);
get_field(val->sam_key, 1, asn1_decode_encryption_key);
+ get_field(val->kdc_id,2,asn1_decode_int32);
end_structure();
val->magic = KV5M_RB1_TRACK_DATA;
}
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.4
diff -u -r1.4 asn1_k_encode.c
--- asn1_k_encode.c 1998/11/25 21:40:28 1.4
+++ asn1_k_encode.c 1998/11/25 22:24:24
@@ -963,6 +963,7 @@
int *retlen;
{
asn1_setup();
+ asn1_addfield(val->kdc_id,2,asn1_encode_integer);
asn1_addfield(&(val->sam_key), 1, asn1_encode_encryption_key);
add_optstring(val->next_challenge, 0, asn1_encode_charstring);
asn1_makeseq();
Index: kdc/main.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/kdc/main.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 main.c
--- main.c 1998/11/24 17:05:08 1.1.1.1
+++ main.c 1998/11/25 23:33:45
@@ -28,6 +28,7 @@
#include <signal.h>
#include <errno.h>
#include <netdb.h>
+#include <unistd.h> /* gethostid() */

#include "k5-int.h"
#include "com_err.h"
@@ -58,6 +59,8 @@
static char *kdc_current_rcname = (char *) NULL;
static int rkey_init_done = 0;

+krb5_int32 kdc_id; /* hostid for SAM replay detection */
+
#ifdef POSIX_SIGNALS
static struct sigaction s_action;
#endif /* POSIX_SIGNALS */
@@ -813,6 +816,8 @@
initialize_realms(kcontext, argc, argv);

setup_signal_handlers();
+
+ kdc_id = gethostid();

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.1
diff -u -r1.1 pa_sam.h
--- pa_sam.h 1998/11/25 04:06:17 1.1
+++ pa_sam.h 1998/11/25 23:33:45
@@ -25,6 +25,8 @@

#include "k5-int.h"

+extern krb5_int32 kdc_id;
+
/* XXX These probably need windows callconv defines? */
typedef krb5_error_code (*sam_generate_proc)
KRB5_PROTOTYPE((krb5_context context,
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.4
diff -u -r1.4 pa_sam_cryptocard.c
--- pa_sam_cryptocard.c 1998/11/25 21:38:40 1.4
+++ pa_sam_cryptocard.c 1998/11/25 23:33:45
@@ -248,6 +248,7 @@

/* Convert the SAD into a key. */
rb1_track_data.magic = KV5M_PREDICTED_SAM_RESPONSE;
+ rb1_track_data.kdc_id = kdc_id;
if (retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
&predict_response, 0 /* salt */,
&rb1_track_data.sam_key)) {
@@ -320,7 +321,7 @@
#endif /* 0 */

if (retval = encode_krb5_sam_challenge(&sc, &scratch))
- goto cleanup;
+ goto cleanup;

pa_data->magic = KV5M_PA_DATA;
pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
@@ -377,16 +378,16 @@

/* Sanity check */
if (sr->sam_type != PA_SAM_TYPE_CRYPTOCARD) {
- retval = KRB5KDC_ERR_PREAUTH_FAILED;
- com_err("krb5kdc", retval, "inconsistent sam_type (RB1 expected)");
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "inconsistent sam_type (RB1 expected)");
goto cleanup;
}

scratch.length = sr->sam_track_id.length;
if (!scratch.length) {
- retval = KRB5KDC_ERR_PREAUTH_FAILED;
- com_err("krb5kdc", retval, "rb1 track_id data not found");
- goto cleanup;
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "rb1 track_id data not found");
+ goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
@@ -411,11 +412,17 @@
goto cleanup;
}

+ if (rb1_track_data->kdc_id != kdc_id) {
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "Warning - possible SAM replay attack!");
+ goto cleanup;
+ }
+
scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
if (!scratch.length) {
- retval = KRB5KDC_ERR_PREAUTH_FAILED;
- com_err("krb5kdc", retval, "RB1 sam_enc_nonce_or_ts not found");
- goto cleanup;
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "RB1 sam_enc_nonce_or_ts not found");
+ goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
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.4
diff -u -r1.4 pa_sam_digi_path.c
--- pa_sam_digi_path.c 1998/11/25 21:38:40 1.4
+++ pa_sam_digi_path.c 1998/11/25 23:33:45
@@ -152,6 +152,8 @@

/* Convert the SAD into a key. */
psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
+ psr.kdc_id = kdc_id;
+
if (retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
&predict_response, 0 /* salt */,
&psr.sam_key)) {
@@ -224,7 +226,7 @@
#endif /* 0 */

if (retval = encode_krb5_sam_challenge(&sc, &scratch))
- goto cleanup;
+ goto cleanup;

pa_data->magic = KV5M_PA_DATA;
pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
@@ -277,16 +279,16 @@

/* Sanity check */
if (sr->sam_type != PA_SAM_TYPE_DIGI_PATH) {
- retval = KRB5KDC_ERR_PREAUTH_FAILED;
- com_err("krb5kdc", retval, "inconsistent sam_type (snk4 expected)");
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "inconsistent sam_type (snk4 expected)");
goto cleanup;
}

scratch.length = sr->sam_track_id.length;
if (!scratch.length) {
- retval = KRB5KDC_ERR_PREAUTH_FAILED;
- com_err("krb5kdc", retval, "snk4 track_id data not found");
- goto cleanup;
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "snk4 track_id data not found");
+ goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
@@ -311,11 +313,17 @@
goto cleanup;
}

+ if (psr->kdc_id != kdc_id) {
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "Warning - possible SAM replay attack!");
+ goto cleanup;
+ }
+
scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
if (!scratch.length) {
- retval = KRB5KDC_ERR_PREAUTH_FAILED;
- com_err("krb5kdc", retval, "Grail sam_enc_nonce_or_ts not found");
- goto cleanup;
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "snk4 sam_enc_nonce_or_ts not found");
+ goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
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.2
diff -u -r1.2 pa_sam_grail.c
--- pa_sam_grail.c 1998/11/25 21:38:40 1.2
+++ pa_sam_grail.c 1998/11/25 23:33:45
@@ -69,6 +69,7 @@
sc.sam_challenge.length = strlen(sc.sam_challenge.data);

psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
+ psr.kdc_id = kdc_id;

/* For GRAIL, we ignore the sam_key, and use our own fixed key. */
if (retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
@@ -179,16 +180,16 @@

/* Sanity check */
if (sr->sam_type != PA_SAM_TYPE_GRAIL) {
- retval = KRB5KDC_ERR_PREAUTH_FAILED;
- com_err("krb5kdc", retval, "inconsistent sam_type (Grail expected)");
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "inconsistent sam_type (Grail expected)");
goto cleanup;
}

scratch.length = sr->sam_track_id.length;
if (!scratch.length) {
- retval = KRB5KDC_ERR_PREAUTH_FAILED;
- com_err("krb5kdc", retval, "Grail track_id data not found");
- goto cleanup;
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "Grail track_id data not found");
+ goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
@@ -213,11 +214,17 @@
goto cleanup;
}

+ if (psr->kdc_id != kdc_id) {
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "Warning - possible SAM replay attack!");
+ goto cleanup;
+ }
+
scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
if (!scratch.length) {
- retval = KRB5KDC_ERR_PREAUTH_FAILED;
- com_err("krb5kdc", retval, "Grail sam_enc_nonce_or_ts not found");
- goto cleanup;
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "Grail sam_enc_nonce_or_ts not found");
+ goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
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.3
diff -u -r1.3 pa_sam_securid.c
--- pa_sam_securid.c 1998/11/25 21:38:40 1.3
+++ pa_sam_securid.c 1998/11/25 23:33:45
@@ -63,11 +63,7 @@
krb5_timestamp tl_time; /* to avoid stale data */
char newpin[LENPRNST + 1]; /* for PIN verification */
struct SD_CLIENT sd_info; /* SecurID state info */
- krb5_addrtype addrtype; /* For verifying state info */
- krb5_int32 addr1; /* IPv4 address for state */
- krb5_int32 addr2; /* Reserved for IPv6 */
- krb5_int32 addr3; /* Reserved for IPv6 */
- krb5_int32 addr4; /* Reserved for IPv6 */
+ krb5_int32 kdc_id; /* Keep sd_info local */
} krb5_securid_state;

union config_record configure;
@@ -99,6 +95,7 @@
krb5_tl_data tl_data;
krb5_securid_state securid_state;
krb5_timestamp timenow;
+ int update_tl_data = 0;

memset(&sc, 0, sizeof(sc));
memset(&psr, 0, sizeof(psr));
@@ -133,14 +130,6 @@
securid_state.state = SECURID_STATE_INITIAL;
}

- /* XXX Get local address, compare against saved address. */
- if (securid_state.state != SECURID_STATE_INITIAL) {
- if (krb5_address_compare(context, addr1, addr2) == FALSE) {
- securid_state.state = SECURID_STATE_INITIAL;
- }
- }
- */
-
switch (securid_state.state) {
case SECURID_STATE_INITIAL:
sc.sam_reponse_prompt.data = "SecurID Passcode";
@@ -171,7 +160,15 @@
}
sc.sam_response_prompt.length = strlen(sc.sam_challenge_label.data);

+ /*
+ * XXX Note that for states that require multiple transactions,
+ * the KRB_ERROR will always come from the same KDC that the
+ * first AS_REQ goes to; this is b/c this code runs right after
+ * the verify code in those states. Thus, we only have to check
+ * the kdc_id in the verify code.
+ */
psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
+ psr.kdc_id = kdc_id;
/* Encrypt with longterm key (send-encrypted-sad). */
krb5_copy_keyblock_contents(context, &client_key, &psr.sam_key);

@@ -289,16 +286,16 @@

/* Sanity check */
if (sr->sam_type != PA_SAM_TYPE_SECURID) {
- retval = KRB5KDC_ERR_PREAUTH_FAILED;
- com_err("krb5kdc", retval, "inconsistent sam_type (SecurID expected)");
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "inconsistent sam_type (SecurID expected)");
goto cleanup;
}

scratch.length = sr->sam_track_id.length;
if (!scratch.length) {
- retval = KRB5KDC_ERR_PREAUTH_FAILED;
- com_err("krb5kdc", retval, "securid track_id data not found");
- goto cleanup;
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "securid track_id data not found");
+ goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
@@ -323,11 +320,19 @@
goto cleanup;
}

+ if (psr->kdc_id != kdc_id) {
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "Warning - possible SAM replay attack!");
+ /* Reset state so user can try again quickly. */
+ old_state = SECURID_STATE_NONE;
+ goto update_tl_data;
+ }
+
scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
if (!scratch.length) {
- retval = KRB5KDC_ERR_PREAUTH_FAILED;
- com_err("krb5kdc", retval, "Grail sam_enc_nonce_or_ts not found");
- goto cleanup;
+ com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
+ "SecurID sam_enc_nonce_or_ts not found");
+ goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
@@ -453,7 +458,8 @@
* like the new password does. Unfortunately, we can't
* "simply" send back a key expired message as that is
* handled by the client (we won't get the message back,
- * also, the prompt will be wrong.)
+ * also, the prompt will be wrong.) Also, the PIN will
+ * be visible when typed. :(
*/
if (sd_dat.user_selectable == CANNOT_CHOOSE_PIN) {
com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
@@ -522,6 +528,7 @@
break;
} /* switch (securid_state.state) */

+update_tl_data:
/*
* Update state information if it has changed.
* Unlike CRYPTOCard, we might need to fail if we can't update.
@@ -536,8 +543,6 @@
}
tl_data.tl_data_length = sizeof(securid_state);
tl_data.tl_data_contents = &securid_state;
-
- /* XXX add our IP address/unique info */

if (retval2 = krb5_dbe_update_tl_data(kdc_context, &assoc, &tl_data)) {
com_err("krb5kdc", retval2, "while updating SecurID tl_data");
Show quoted text
>Audit-Trail:
>Unformatted: