Implement heuristic for matching broken Heimdal sequence number encodings
authorTom Yu <tlyu@mit.edu>
Fri, 23 May 2003 03:37:59 +0000 (03:37 +0000)
committerTom Yu <tlyu@mit.edu>
Fri, 23 May 2003 03:37:59 +0000 (03:37 +0000)
ticket: 1263
target_version: 1.3
tags: pullup
status: open

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@15479 dc483132-0cff-0310-8789-dd5450dbe970

src/include/ChangeLog
src/include/k5-int.h
src/lib/krb5/krb/ChangeLog
src/lib/krb5/krb/auth_con.c
src/lib/krb5/krb/auth_con.h
src/lib/krb5/krb/rd_priv.c
src/lib/krb5/krb/rd_safe.c

index 552d4c34d2c73818fc276bc306eac5fb39b55dd7..2d88cf3ca78aecec91d749995d644fa614eb9bb0 100644 (file)
@@ -1,5 +1,7 @@
 2003-05-22  Tom Yu  <tlyu@mit.edu>
 
+       * k5-int.h: Add prototype for krb5int_auth_con_chkseqnum.
+
        * krb5.hin: Default KRB5_DEPRECATED to 0.  Default KRB5_PRIVATE to
        0 on all platforms.
 
index 1f49b236b491f4461b00d181961af832d5b88cd2..ca6bbf69a7408d4b635a3bd2709aeb3d3678a21c 100644 (file)
@@ -1488,6 +1488,8 @@ krb5_error_code krb5_encode_kdc_rep
 krb5_error_code krb5_validate_times
        (krb5_context, 
                krb5_ticket_times *);
+krb5_boolean krb5int_auth_con_chkseqnum
+       (krb5_context ctx, krb5_auth_context ac, krb5_ui_4 in_seq);
 /*
  * [De]Serialization Handle and operations.
  */
index 42010819a1624d2a9d82bdf67513877630c1e50d..0b7e9d94fef863c492a325e4f4b4f3bf30ca8e73 100644 (file)
@@ -1,3 +1,15 @@
+2003-05-22  Tom Yu  <tlyu@mit.edu>
+
+       * auth_con.c (krb5int_auth_con_chkseqnum): New function; implement
+       heuristic for broken Heimdal sequence number encoding.
+       (chk_heimdal_seqnum): Auxiliary function for above.
+
+       * auth_con.h: Add flags for sequence number heuristic.
+
+       * rd_priv.c: Use krb5int_auth_con_chkseqnum.
+
+       * rd_safe.c: Use krb5int_auth_con_chkseqnum.
+
 2003-05-22  Sam Hartman  <hartmans@mit.edu>
 
        * gic_pwd.c (krb5int_populate_gic_opt): returns void
index bc26774a6c054789210bffd6b6b3c7ba5355023b..cd3acf176c09d2900d314ebca3993a94b08f581c 100644 (file)
@@ -1,6 +1,8 @@
 #include "k5-int.h"
 #include "auth_con.h"
 
+static krb5_boolean chk_heimdal_seqnum(krb5_ui_4, krb5_ui_4);
+
 static krb5_error_code
 actx_copy_addr(krb5_context context, const krb5_address *inad, krb5_address **outad)
 {
@@ -395,3 +397,167 @@ krb5_auth_con_get_checksum_func( krb5_context context,
   *data = auth_context->checksum_func_data;
   return 0;
 }
+
+/*
+ * krb5int_auth_con_chkseqnum
+ *
+ * We use a somewhat complex heuristic for validating received
+ * sequence numbers.  We must accommodate both our older
+ * implementation, which sends negative sequence numbers, and the
+ * broken Heimdal implementation (at least as of 0.5.2), which
+ * violates X.690 BER for integer encodings.  The requirement of
+ * handling negative sequence numbers removes one of easier means of
+ * detecting a Heimdal implementation, so we resort to this mess
+ * here.
+ *
+ * X.690 BER (and consequently DER, which are the required encoding
+ * rules in RFC1510) encode all integer types as signed integers.
+ * This means that the MSB being set on the first octet of the
+ * contents of the encoding indicates a negative value.  Heimdal does
+ * not prepend the required zero octet to unsigned integer encodings
+ * which would otherwise have the MSB of the first octet of their
+ * encodings set.
+ *
+ * Our ASN.1 library implements a special decoder for sequence
+ * numbers, accepting both negative and positive 32-bit numbers but
+ * mapping them both into the space of positive unsigned 32-bit
+ * numbers in the obvious bit-pattern-preserving way.  This maintains
+ * compatibility with our older implementations.  This also means that
+ * encodings emitted by Heimdal are ambiguous.
+ *
+ * Heimdal counter value       received uint32 value
+ *
+ * 0x00000080                  0xFFFFFF80
+ * 0x000000FF                  0xFFFFFFFF
+ * 0x00008000                  0xFFFF8000
+ * 0x0000FFFF                  0xFFFFFFFF
+ * 0x00800000                  0xFF800000
+ * 0x00FFFFFF                  0xFFFFFFFF
+ * 0xFF800000                  0xFF800000
+ * 0xFFFFFFFF                  0xFFFFFFFF
+ *
+ * We use two auth_context flags, SANE_SEQ and HEIMDAL_SEQ, which are
+ * only set after we can unambiguously determine the sanity of the
+ * sending implementation.  Once one of these flags is set, we accept
+ * only the sequence numbers appropriate to the remote implementation
+ * type.  We can make the determination in two different ways.  The
+ * first is to note the receipt of a "negative" sequence number when a
+ * "positive" one was expected.  The second is to note the receipt of
+ * a sequence number that wraps through "zero" in a weird way.  The
+ * latter corresponds to the receipt of an initial sequence number in
+ * the ambiguous range.
+ *
+ * There are 2^7 + 2^15 + 2^23 + 2^23 = 16810112 total ambiguous
+ * initial Heimdal counter values, but we receive them as one of 2^23
+ * possible values.  There is a ~1/256 chance of a Heimdal
+ * implementation sending an intial sequence number in the ambiguous
+ * range.
+ *
+ * We have to do special treatment when receiving sequence numbers
+ * between 0xFF800000..0xFFFFFFFF, or when wrapping through zero
+ * weirdly (due to ambiguous initial sequence number).  If we are
+ * expecting a value corresponding to an ambiguous Heimdal counter
+ * value, and we receive an exact match, we can mark the remote end as
+ * sane.
+ */
+krb5_boolean
+krb5int_auth_con_chkseqnum(
+    krb5_context ctx,
+    krb5_auth_context ac,
+    krb5_ui_4 in_seq)
+{
+    krb5_ui_4 exp_seq;
+
+    exp_seq = ac->remote_seq_number;
+
+    /*
+     * If sender is known to be sane, accept _only_ exact matches.
+     */
+    if (ac->auth_context_flags & KRB5_AUTH_CONN_SANE_SEQ)
+       return in_seq == exp_seq;
+
+    /*
+     * If sender is not known to be sane, first check the ambiguous
+     * range of received values, 0xFF800000..0xFFFFFFFF.
+     */
+    if ((in_seq & 0xFF800000) == 0xFF800000) {
+       /*
+        * If expected sequence number is in the range
+        * 0xFF800000..0xFFFFFFFF, then we can't make any
+        * determinations about the sanity of the sending
+        * implementation.
+        */
+       if ((exp_seq & 0xFF800000) == 0xFF800000 && in_seq == exp_seq)
+           return 1;
+       /*
+        * If sender is not known for certain to be a broken Heimdal
+        * implementation, check for exact match.
+        */
+       if (!(ac->auth_context_flags & KRB5_AUTH_CONN_HEIMDAL_SEQ)
+           && in_seq == exp_seq)
+           return 1;
+       /*
+        * Now apply hairy algorithm for matching sequence numbers
+        * sent by broken Heimdal implementations.  If it matches, we
+        * know for certain it's a broken Heimdal sender.
+        */
+       if (chk_heimdal_seqnum(exp_seq, in_seq)) {
+           ac->auth_context_flags |= KRB5_AUTH_CONN_HEIMDAL_SEQ;
+           return 1;
+       }
+       return 0;
+    }
+
+    /*
+     * Received value not in the ambiguous range?  If the _expected_
+     * value is in the range of ambiguous Hemidal counter values, and
+     * it matches the received value, sender is known to be sane.
+     */
+    if (in_seq == exp_seq) {
+       if ((   exp_seq & 0xFFFFFF80) == 0x00000080
+           || (exp_seq & 0xFFFF8000) == 0x00008000
+           || (exp_seq & 0xFF800000) == 0x00800000)
+           ac->auth_context_flags |= KRB5_AUTH_CONN_SANE_SEQ;
+       return 1;
+    }
+
+    /*
+     * Magic wraparound for the case where the intial sequence number
+     * is in the ambiguous range.  This means that the sender's
+     * counter is at a different count than ours, so we correct ours,
+     * and mark the sender as being a broken Heimdal implementation.
+     */
+    if (exp_seq == 0
+       && !(ac->auth_context_flags & KRB5_AUTH_CONN_HEIMDAL_SEQ)) {
+       switch (in_seq) {
+       case 0x100:
+       case 0x10000:
+       case 0x1000000:
+           ac->auth_context_flags |= KRB5_AUTH_CONN_HEIMDAL_SEQ;
+           exp_seq = in_seq;
+           return 1;
+       default:
+           return 0;
+       }
+    }
+    return 0;
+}
+
+static krb5_boolean
+chk_heimdal_seqnum(krb5_ui_4 exp_seq, krb5_ui_4 in_seq)
+{
+    if (( exp_seq & 0xFF800000) == 0x00800000
+       && (in_seq & 0xFF800000) == 0xFF800000
+       && (in_seq & 0x00FFFFFF) == exp_seq)
+       return 1;
+    else if ((  exp_seq & 0xFFFF8000) == 0x00008000
+            && (in_seq & 0xFFFF8000) == 0xFFFF8000
+            && (in_seq & 0x0000FFFF) == exp_seq)
+       return 1;
+    else if ((  exp_seq & 0xFFFFFF80) == 0x00000080
+            && (in_seq & 0xFFFFFF80) == 0xFFFFFF80
+            && (in_seq & 0x000000FF) == exp_seq)
+       return 1;
+    else
+       return 0;
+}
index 362909ce775894ac995c43f8fb2fcd032b1aa319..9543de355e35dedc601930e99aaf5e5361e15674 100644 (file)
@@ -30,5 +30,7 @@ struct _krb5_auth_context {
 #define KRB5_AUTH_CONN_INITIALIZED     0x00010000
 #define KRB5_AUTH_CONN_USED_W_MK_REQ   0x00020000
 #define KRB5_AUTH_CONN_USED_W_RD_REQ   0x00040000
+#define KRB5_AUTH_CONN_SANE_SEQ                0x00080000
+#define KRB5_AUTH_CONN_HEIMDAL_SEQ     0x00100000
 
 #endif
index 180559cc230f8b4f0fa5703ee30070a9393e17f6..cf74807793944ab0b09fa472c563d751ea24bcda 100644 (file)
@@ -246,7 +246,8 @@ krb5_rd_priv(krb5_context context, krb5_auth_context auth_context, const krb5_da
     }
 
     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
-       if (auth_context->remote_seq_number != replaydata.seq) {
+       if (!krb5int_auth_con_chkseqnum(context, auth_context,
+                                       replaydata.seq)) {
            retval =  KRB5KRB_AP_ERR_BADORDER;
            goto error;
        }
index 3194229a39995d519993b3380bdf1f537dadf40b..41c2596b7aba5f86182aaac67498e8324a6c9f98 100644 (file)
@@ -239,7 +239,8 @@ krb5_rd_safe(krb5_context context, krb5_auth_context auth_context, const krb5_da
     }
 
     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
-       if (auth_context->remote_seq_number != replaydata.seq) {
+       if (!krb5int_auth_con_chkseqnum(context, auth_context,
+                                       replaydata.seq)) {
            retval =  KRB5KRB_AP_ERR_BADORDER;
            goto error;
        }