Add 64-bit sequence number support. Do sequence number ordering tests relative
authorKen Raeburn <raeburn@mit.edu>
Sat, 13 Dec 2003 07:07:23 +0000 (07:07 +0000)
committerKen Raeburn <raeburn@mit.edu>
Sat, 13 Dec 2003 07:07:23 +0000 (07:07 +0000)
to the initial value rather than absolute.  Support tokens without pseudo-ASN.1
wrappers.  Don't restrict enctype lists.  Implement CFX token support.

With CFX_EXERCISE defined, use random padding, random rotates, and bogus
initial tokens, to exercise the associated code paths.

ticket: 2040
status: open

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

15 files changed:
src/lib/gssapi/generic/ChangeLog
src/lib/gssapi/generic/gssapiP_generic.h
src/lib/gssapi/generic/util_ordering.c
src/lib/gssapi/generic/util_token.c
src/lib/gssapi/krb5/ChangeLog
src/lib/gssapi/krb5/Makefile.in
src/lib/gssapi/krb5/accept_sec_context.c
src/lib/gssapi/krb5/delete_sec_context.c
src/lib/gssapi/krb5/gssapiP_krb5.h
src/lib/gssapi/krb5/init_sec_context.c
src/lib/gssapi/krb5/k5seal.c
src/lib/gssapi/krb5/k5sealv3.c [new file with mode: 0644]
src/lib/gssapi/krb5/k5unseal.c
src/lib/gssapi/krb5/ser_sctx.c
src/lib/gssapi/krb5/wrap_size_limit.c

index 5444c99d653e5627dbe109776d941792a85be820..a6e77399e12a311301a53e56b9caf77f91a7b238 100644 (file)
@@ -1,3 +1,20 @@
+2003-12-13  Ken Raeburn  <raeburn@mit.edu>
+
+       * gssapiP_generic.h: Include k5-platform.h.
+       (gssint_uint64): New typedef.
+       (g_order_init, g_order_check): Update decls.
+       * util_ordering.c (struct _queue): Change sequence number fields
+       to gssint_uint64.  Add mask field.
+       (queue_insert): Change sequence number to gssint_uint64.
+       (g_order_init): Change sequence numbers to gssint_uint64.  Add
+       "wide_nums" argument; initialize the queue mask field based on
+       it; all callers changed.  Store a -1 as the first element.
+       (g_order_check): Store and check elements as offsets from
+       firstnum.  Mask to 32 bits if desired.
+       * util_token.c (g_verify_token_header): Add new argument
+       indicating whether the pseudo-ASN.1 wrapper is required; all
+       callers changed.
+
 2003-07-17  Ken Raeburn  <raeburn@mit.edu>
 
        * Makefile.in (LIBNAME) [##WIN16##]: Don't define.
index 102ba699e15959dfc0478c99c866e094447f2ced..24d51d0d59f6bdd9c3dc7a1d182ff9a6a6d6fe5c 100644 (file)
@@ -40,6 +40,9 @@
 #include "gssapi_err_generic.h"
 #include <errno.h>
 
+#include "k5-platform.h"
+typedef UINT64_TYPE gssint_uint64;
+
 /** helper macros **/
 
 #define g_OID_equal(o1,o2) \
@@ -159,8 +162,9 @@ void g_make_token_header (gss_OID mech, unsigned int body_size,
                          unsigned char **buf, int tok_type);
 
 gss_int32 g_verify_token_header (gss_OID mech, unsigned int *body_size,
-                         unsigned char **buf, int tok_type, 
-                                unsigned int toksize_in);
+                                unsigned char **buf, int tok_type, 
+                                unsigned int toksize_in,
+                                int wrapper_required);
 
 OM_uint32 g_display_major_status (OM_uint32 *minor_status,
                                 OM_uint32 status_value,
@@ -171,10 +175,10 @@ OM_uint32 g_display_com_err_status (OM_uint32 *minor_status,
                                   OM_uint32 status_value,
                                   gss_buffer_t status_string);
 
-gss_int32 g_order_init (void **queue, OM_uint32 seqnum,
-                                 int do_replay, int do_sequence);
+gss_int32 g_order_init (void **queue, gssint_uint64 seqnum,
+                                 int do_replay, int do_sequence, int wide);
 
-gss_int32 g_order_check (void **queue, OM_uint32 seqnum);
+gss_int32 g_order_check (void **queue, gssint_uint64 seqnum);
 
 void g_order_free (void **queue);
 
index 21a8b0641fee8f4cd0807400cfc270c000fa06cc..fe2eaafc2e43c872429bc105dae3d8c2bc347816 100644 (file)
@@ -38,8 +38,14 @@ typedef struct _queue {
    int do_sequence;
    int start;
    int length;
-   unsigned int firstnum;
-   unsigned int elem[QUEUE_LENGTH];
+   gssint_uint64 firstnum;
+   /* Stored as deltas from firstnum.  This way, the high bit won't
+      overflow unless we've actually gone through 2**n messages, or
+      gotten something *way* out of sequence.  */
+   gssint_uint64 elem[QUEUE_LENGTH];
+   /* All ones for 64-bit sequence numbers; 32 ones for 32-bit
+      sequence numbers.  */
+   gssint_uint64 mask;
 } queue;
 
 /* rep invariant:
@@ -51,7 +57,7 @@ typedef struct _queue {
 #define QELEM(q,i) ((q)->elem[(i)%QSIZE(q)])
 
 static void
-queue_insert(queue *q, int after, unsigned int seqnum)
+queue_insert(queue *q, int after, gssint_uint64 seqnum)
 {
    /* insert.  this is not the fastest way, but it's easy, and it's
       optimized for insert at end, which is the common case */
@@ -80,10 +86,10 @@ queue_insert(queue *q, int after, unsigned int seqnum)
       q->length++;
    }
 }
-   
+
 gss_int32
-g_order_init(void **vqueue, OM_uint32 seqnum,
-            int do_replay, int do_sequence)
+g_order_init(void **vqueue, gssint_uint64 seqnum,
+            int do_replay, int do_sequence, int wide_nums)
 {
    queue *q;
 
@@ -92,38 +98,49 @@ g_order_init(void **vqueue, OM_uint32 seqnum,
 
    q->do_replay = do_replay;
    q->do_sequence = do_sequence;
+   q->mask = wide_nums ? ~(gssint_uint64)0 : 0xffffffffUL;
 
    q->start = 0;
    q->length = 1;
    q->firstnum = seqnum;
-   q->elem[q->start] = seqnum-1;
+   q->elem[q->start] = ((gssint_uint64)0 - 1) & q->mask;
 
    *vqueue = (void *) q;
    return(0);
 }
 
 gss_int32
-g_order_check(void **vqueue, OM_uint32 seqnum)
+g_order_check(void **vqueue, gssint_uint64 seqnum)
 {
    queue *q;
    int i;
-   
+   gssint_uint64 expected;
+
    q = (queue *) (*vqueue);
 
    if (!q->do_replay && !q->do_sequence)
       return(GSS_S_COMPLETE);
 
+   /* All checks are done relative to the initial sequence number, to
+      avoid (or at least put off) the pain of wrapping.  */
+   seqnum -= q->firstnum;
+   /* If we're only doing 32-bit values, adjust for that again.
+
+      Note that this will probably be the wrong thing to if we get
+      2**32 messages sent with 32-bit sequence numbers.  */
+   seqnum &= q->mask;
+
    /* rule 1: expected sequence number */
 
-   if (seqnum == QELEM(q,q->start+q->length-1)+1) { 
+   expected = (QELEM(q,q->start+q->length-1)+1) & q->mask;
+   if (seqnum == expected) { 
       queue_insert(q, q->start+q->length-1, seqnum);
       return(GSS_S_COMPLETE);
    }
 
    /* rule 2: > expected sequence number */
 
-   if ((seqnum > QELEM(q,q->start+q->length-1)+1) ||
-       (seqnum < q->firstnum)) {
+   if ((seqnum > expected)) {
       queue_insert(q, q->start+q->length-1, seqnum);
       if (q->do_replay && !q->do_sequence)
         return(GSS_S_COMPLETE);
@@ -134,7 +151,20 @@ g_order_check(void **vqueue, OM_uint32 seqnum)
    /* rule 3: seqnum < seqnum(first) */
 
    if ((seqnum < QELEM(q,q->start)) &&
-       (seqnum >= q->firstnum)) {
+       /* Is top bit of whatever width we're using set?
+
+         We used to check for greater than or equal to firstnum, but
+         (1) we've since switched to compute values relative to
+         firstnum, so the lowest we can have is 0, and (2) the effect
+         of the original scheme was highly dependent on whether
+         firstnum was close to either side of 0.  (Consider
+         firstnum==0xFFFFFFFE and we miss three packets; the next
+         packet is *new* but would look old.)
+
+          This check should give us 2**31 or 2**63 messages "new", and
+          just as many "old".  That's not quite right either.  */
+       (seqnum & (1 + (q->mask >> 1)))
+       ) {
       if (q->do_replay && !q->do_sequence)
         return(GSS_S_OLD_TOKEN);
       else
index 30ae0698c2fcda499761cf89294688b768564cd2..97a788c09edcef740404a439293e344445979785 100644 (file)
@@ -168,12 +168,15 @@ void g_make_token_header(mech, body_size, buf, tok_type)
  * mechanism in the token does not match the mech argument.  buf and
  * *body_size are left unmodified on error.
  */
-gss_int32 g_verify_token_header(mech, body_size, buf_in, tok_type, toksize_in)
+
+gss_int32 g_verify_token_header(mech, body_size, buf_in, tok_type, toksize_in,
+                               wrapper_required)
      gss_OID mech;
      unsigned int *body_size;
      unsigned char **buf_in;
      int tok_type;
      unsigned int toksize_in;
+     int wrapper_required;
 {
    unsigned char *buf = *buf_in;
    int seqsize;
@@ -182,8 +185,13 @@ gss_int32 g_verify_token_header(mech, body_size, buf_in, tok_type, toksize_in)
 
    if ((toksize-=1) < 0)
       return(G_BAD_TOK_HEADER);
-   if (*buf++ != 0x60)
-      return(G_BAD_TOK_HEADER);
+   if (*buf++ != 0x60) {
+       if (wrapper_required)
+          return(G_BAD_TOK_HEADER);
+       buf--;
+       toksize++;
+       goto skip_wrapper;
+   }
 
    if ((seqsize = der_read_length(&buf, &toksize)) < 0)
       return(G_BAD_TOK_HEADER);
@@ -207,16 +215,17 @@ gss_int32 g_verify_token_header(mech, body_size, buf_in, tok_type, toksize_in)
 
    if (! g_OID_equal(&toid, mech)) 
        return  G_WRONG_MECH;
+skip_wrapper:
    if (tok_type != -1) {
        if ((toksize-=2) < 0)
           return(G_BAD_TOK_HEADER);
 
        if ((*buf++ != ((tok_type>>8)&0xff)) ||
-          (*buf++ != (tok_type&0xff))) 
+          (*buf++ != (tok_type&0xff)))
           return(G_WRONG_TOKID);
    }
-       *buf_in = buf;
-       *body_size = toksize;
+   *buf_in = buf;
+   *body_size = toksize;
 
-       return 0;
-       }
+   return 0;
+}
index d02374809387ef37771e5b56e7d40111371f9050..66dcc1efddc7db47d7e994d29fb49942239bc33f 100644 (file)
@@ -1,3 +1,40 @@
+2003-12-13  Ken Raeburn  <raeburn@mit.edu>
+           Sam Hartman  <hartmans@avalanche-breakdown.mit.edu>
+
+       * k5sealv3.c: New file, implements Wrap and MIC tokens for CFX
+       extensions.
+       * gssapiP_krb5.h (struct _krb5_gss_ctx_id_rec): Added acceptor
+       subkey, 64-bit sequence numbers, checksum type, and hooks for
+       sending a bogus initial token for CFX testing.  Changed some flags
+       into bitfields.
+       (gss_krb5int_make_seal_token_v3): Declare.
+       * Makefile.in (SRCS, OBJS, STLIBOBJS): Build it.
+       * accept_sec_context.c (krb5_gss_accept_sec_context): Add CFX
+       support.  For G_WRONG_TOKID, send back an error token with
+       AP_ERR_MSG_TYPE code and return a CONTINUE_NEEDED indication.
+       Initialize new fields in context.
+       * delete_sec_context.c (krb5_gss_delete_sec_context): Free
+       acceptor subkey field.
+       * init_sec_context.c (get_credentials): Drop enctypes argument;
+       callers changed.
+       (get_requested_enctypes): Deleted.
+       (setup_enc): Combine some common sections.  Do CFX initialization
+       for newer enctypes.
+       (new_connection) [CFX_EXERCISE]: If doing CFX, send a bogus
+       token.  Delete the enctype list manipulation.
+       (mutual_auth): If CFX, save acceptor's subkey.
+       * k5seal.c (make_seal_token_v1): Sequence number is now 64 bits.
+       (kg_seal): Call out to _v3 code for CFX.
+       * k5unseal.c (kg_unseal): For CFX, adjust token id numbers and
+       call out to _v3 code.
+       * wrap_size_limit.c (krb5_gss_wrap_size_limit): Implement CFX
+       support.
+
+       * gssapiP_krb5.h (struct _krb5_gss_ctx_id_rec): Deleted fields
+       ctypes and nctypes.
+       * delete_sec_context.c, init_sec_context.c, ser_sctx.c: Removed
+       references.
+
 2003-12-11  Alexandra Ellwood <lxs@mit.edu>
 
        * acquire_cred.c, gssapi_krb5.c, gssapiP_krb5.h, set_ccache.c:
index 9a18c50a8cf56b08fc0268c57317b81b4c1b0c41..759eedd9490f593193c735043301ebbbe0cf9a84 100644 (file)
@@ -42,6 +42,7 @@ SRCS = \
        $(srcdir)/inq_cred.c \
        $(srcdir)/inq_names.c \
        $(srcdir)/k5seal.c \
+       $(srcdir)/k5sealv3.c \
        $(srcdir)/k5unseal.c \
        $(srcdir)/krb5_gss_glue.c \
        $(srcdir)/process_context_token.c \
@@ -89,6 +90,7 @@ OBJS = \
        $(OUTPRE)inq_cred.$(OBJEXT) \
        $(OUTPRE)inq_names.$(OBJEXT) \
        $(OUTPRE)k5seal.$(OBJEXT) \
+       $(OUTPRE)k5sealv3.$(OBJEXT) \
        $(OUTPRE)k5unseal.$(OBJEXT) \
        $(OUTPRE)krb5_gss_glue.$(OBJEXT) \
        $(OUTPRE)process_context_token.$(OBJEXT) \
@@ -136,6 +138,7 @@ STLIBOBJS = \
        inq_cred.o \
        inq_names.o \
        k5seal.o \
+       k5sealv3.o \
        k5unseal.o \
        krb5_gss_glue.o \
        process_context_token.o \
index 3367c1da753b55fa83f2bacea2df5dd7df3e9b2f..d507909f3ade2778e1108cebca48014f33cc9a5b 100644 (file)
 #endif
 #include <assert.h>
 
+#ifdef CFX_EXERCISE
+#define CFX_ACCEPTOR_SUBKEY (time(0) & 1)
+#else
+#define CFX_ACCEPTOR_SUBKEY 1
+#endif
+
 /* Decode, decrypt and store the forwarded creds in the local ccache. */
 static krb5_error_code
 rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
@@ -122,7 +128,8 @@ rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
                if ((retval = krb5_auth_con_init(context, &new_auth_ctx)))
                        goto cleanup;
                krb5_auth_con_setflags(context, new_auth_ctx, 0);
-               if ((retval = krb5_rd_cred(context, new_auth_ctx, inbuf, &creds, NULL)))
+               if ((retval = krb5_rd_cred(context, new_auth_ctx, inbuf,
+                                          &creds, NULL)))
                        goto cleanup;
                }
 
@@ -312,13 +319,13 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
    if (!(code = g_verify_token_header((gss_OID) gss_mech_krb5,
                                      &(ap_req.length),
                                      &ptr, KG_TOK_CTX_AP_REQ,
-                                     input_token->length))) {
+                                     input_token->length, 1))) {
        mech_used = gss_mech_krb5;
    } else if ((code == G_WRONG_MECH) &&
              !(code = g_verify_token_header((gss_OID) gss_mech_krb5_old,
                                             &(ap_req.length), 
                                             &ptr, KG_TOK_CTX_AP_REQ,
-                                            input_token->length))) {
+                                            input_token->length, 1))) {
        /*
        * Previous versions of this library used the old mech_id
        * and some broken behavior (wrong IV on checksum
@@ -327,6 +334,11 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
        * old behavior.
        */
        mech_used = gss_mech_krb5_old;
+   } else if (code == G_WRONG_TOKID) {
+       major_status = GSS_S_CONTINUE_NEEDED;
+       code = KRB5KRB_AP_ERR_MSG_TYPE;
+       mech_used = gss_mech_krb5;
+       goto fail;
    } else {
        major_status = GSS_S_DEFECTIVE_TOKEN;
        goto fail;
@@ -616,6 +628,7 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
        goto fail;
    }
 
+   ctx->proto = 0;
    switch(ctx->subkey->enctype) {
    case ENCTYPE_DES_CBC_MD5:
    case ENCTYPE_DES_CBC_CRC:
@@ -635,12 +648,7 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
           /*SUPPRESS 113*/
           ctx->enc->contents[i] ^= 0xf0;
 
-       if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq))) {
-          major_status = GSS_S_FAILURE;
-          goto fail;
-       }
-
-       break;
+       goto copy_subkey_to_seq;
 
    case ENCTYPE_DES3_CBC_SHA1:
        ctx->subkey->enctype = ENCTYPE_DES3_CBC_RAW;
@@ -649,36 +657,38 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
        ctx->sealalg = SEAL_ALG_DES3KD;
 
        /* fill in the encryption descriptors */
-
+   copy_subkey:
        if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) {
           major_status = GSS_S_FAILURE;
           goto fail;
        }
-
+   copy_subkey_to_seq:
        if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq))) {
           major_status = GSS_S_FAILURE;
           goto fail;
        }
-
        break;
-         case ENCTYPE_ARCFOUR_HMAC:
-           ctx->signalg = SGN_ALG_HMAC_MD5 ;
-           ctx->cksum_size = 8;
-           ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ;
-
-             code = krb5_copy_keyblock (context, ctx->subkey, &ctx->enc);
-             if (code)
-                 goto fail;
-             code = krb5_copy_keyblock (context, ctx->subkey, &ctx->seq);
-             if (code) {
-                 krb5_free_keyblock (context, ctx->enc);
-                 goto fail;
-             }
-             break;        
+
+   case ENCTYPE_ARCFOUR_HMAC:
+       ctx->signalg = SGN_ALG_HMAC_MD5 ;
+       ctx->cksum_size = 8;
+       ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ;
+       goto copy_subkey;
 
    default:
-       code = KRB5_BAD_ENCTYPE;
-       goto fail;
+       ctx->signalg = -1;
+       ctx->sealalg = -1;
+       ctx->proto = 1;
+       code = krb5int_c_mandatory_cksumtype(context, ctx->subkey->enctype,
+                                           &ctx->cksumtype);
+       if (code)
+          goto fail;
+       code = krb5_c_checksum_length(context, ctx->cksumtype,
+                                    &ctx->cksum_size);
+       if (code)
+          goto fail;
+       ctx->have_acceptor_subkey = 0;
+       goto copy_subkey;
    }
 
    ctx->endtime = ticket->enc_part2->times.endtime;
@@ -686,7 +696,11 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
 
    krb5_free_ticket(context, ticket); /* Done with ticket */
 
-   krb5_auth_con_getremoteseqnumber(context, auth_context, &ctx->seq_recv);
+   {
+       krb5_ui_4 seq_temp;
+       krb5_auth_con_getremoteseqnumber(context, auth_context, &seq_temp);
+       ctx->seq_recv = seq_temp;
+   }
 
    if ((code = krb5_timeofday(context, &now))) {
        major_status = GSS_S_FAILURE;
@@ -701,7 +715,7 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
 
    g_order_init(&(ctx->seqstate), ctx->seq_recv,
                (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
-               (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0);
+               (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
 
    /* at this point, the entire context structure is filled in, 
       so it can be released.  */
@@ -710,13 +724,53 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
 
    if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
        unsigned char * ptr3;
+       krb5_ui_4 seq_temp;
+       int cfx_generate_subkey;
+
+       if (ctx->proto == 1)
+          cfx_generate_subkey = CFX_ACCEPTOR_SUBKEY;
+       else
+          cfx_generate_subkey = 0;
+
+       if (cfx_generate_subkey) {
+          krb5_int32 acflags;
+          code = krb5_auth_con_getflags(context, auth_context, &acflags);
+          if (code == 0) {
+              acflags |= KRB5_AUTH_CONTEXT_USE_SUBKEY;
+              code = krb5_auth_con_setflags(context, auth_context, acflags);
+          }
+          if (code) {
+              major_status = GSS_S_FAILURE;
+              goto fail;
+          }
+       }
+
        if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) {
           major_status = GSS_S_FAILURE;
           goto fail;
        }
 
-       krb5_auth_con_getlocalseqnumber(context, auth_context,
-                                      &ctx->seq_send);
+       krb5_auth_con_getlocalseqnumber(context, auth_context, &seq_temp);
+       ctx->seq_send = seq_temp & 0xffffffffL;
+
+       if (cfx_generate_subkey) {
+          /* Get the new acceptor subkey.  With the code above, there
+             should always be one if we make it to this point.  */
+          code = krb5_auth_con_getsendsubkey(context, auth_context,
+                                             &ctx->acceptor_subkey);
+          if (code != 0) {
+              major_status = GSS_S_FAILURE;
+              goto fail;
+          }
+          code = krb5int_c_mandatory_cksumtype(context,
+                                               ctx->acceptor_subkey->enctype,
+                                               &ctx->acceptor_subkey_cksumtype);
+          if (code) {
+              major_status = GSS_S_FAILURE;
+              goto fail;
+          }
+          ctx->have_acceptor_subkey = 1;
+       }
 
        /* the reply token hasn't been sent yet, but that's ok. */
        ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
@@ -804,7 +858,7 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
    if (ap_rep.data)
        krb5_free_data_contents(context, &ap_rep);
 
-   if (!GSS_ERROR(major_status))
+   if (!GSS_ERROR(major_status) && major_status != GSS_S_CONTINUE_NEEDED)
        return(major_status);
 
    /* from here on is the real "fail" code */
@@ -844,7 +898,9 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
        krb5_free_ap_req(context, request);
    }
 
-   if (cred && (gss_flags & GSS_C_MUTUAL_FLAG)) {
+   if (cred
+       && ((gss_flags & GSS_C_MUTUAL_FLAG)
+          || (major_status == GSS_S_CONTINUE_NEEDED))) {
        unsigned int tmsglen;
        int toktype;
 
@@ -854,7 +910,7 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
        */
        memset(&krb_error_data, 0, sizeof(krb_error_data));
 
-       code  -= ERROR_TABLE_BASE_krb5;
+       code -= ERROR_TABLE_BASE_krb5;
        if (code < 0 || code > 128)
           code = 60 /* KRB_ERR_GENERIC */;
 
@@ -862,7 +918,7 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
        (void) krb5_us_timeofday(context, &krb_error_data.stime,
                                &krb_error_data.susec);
        krb_error_data.server = cred->princ;
-          
+
        code = krb5_mk_error(context, &krb_error_data, &scratch);
        if (code)
           return (major_status);
index 28c23589062f6209454d89da0c619509ab3cd027..94702b862701f785f9bbc7621f8cef672fb6fe73 100644 (file)
@@ -92,6 +92,8 @@ krb5_gss_delete_sec_context(minor_status, context_handle, output_token)
       krb5_free_principal(context, ctx->there);
    if (ctx->subkey)
       krb5_free_keyblock(context, ctx->subkey);
+   if (ctx->acceptor_subkey)
+       krb5_free_keyblock(context, ctx->acceptor_subkey);
 
    if (ctx->auth_context) {
        (void)krb5_auth_con_setrcache(context, ctx->auth_context, NULL);
@@ -101,9 +103,6 @@ krb5_gss_delete_sec_context(minor_status, context_handle, output_token)
    if (ctx->mech_used)
        gss_release_oid(minor_status, &ctx->mech_used);
    
-   if (ctx->ctypes)
-       xfree(ctx->ctypes);
-
    /* Zero out context */
    memset(ctx, 0, sizeof(*ctx));
    xfree(ctx);
index fc4cf2fcd0d062b7981e843ea48bafab5b07bc74..cee702bcd46507d2273c69735658fec1836c707c 100644 (file)
@@ -69,6 +69,9 @@
 #include "gssapi_krb5.h"
 #include "gssapi_err_krb5.h"
 
+/* for debugging */
+#undef CFX_EXERCISE
+
 /** constants **/
 
 #define CKSUMTYPE_KG_CB                0x8003
@@ -116,10 +119,17 @@ enum seal_alg {
   SEAL_ALG_DES3KD          = 0x0002
 };
 
+/* for 3DES */
 #define KG_USAGE_SEAL 22
 #define KG_USAGE_SIGN 23
 #define KG_USAGE_SEQ  24
 
+/* for draft-ietf-krb-wg-gssapi-cfx-01 */
+#define KG_USAGE_ACCEPTOR_SEAL 22
+#define KG_USAGE_ACCEPTOR_SIGN 23
+#define KG_USAGE_INITIATOR_SEAL        24
+#define KG_USAGE_INITIATOR_SIGN        25
+
 enum qop {
   GSS_KRB5_INTEG_C_QOP_MD5       = 0x0001, /* *partial* MD5 = "MD2.5" */
   GSS_KRB5_INTEG_C_QOP_DES_MD5   = 0x0002,
@@ -152,15 +162,21 @@ typedef struct _krb5_gss_cred_id_rec {
 } krb5_gss_cred_id_rec, *krb5_gss_cred_id_t; 
 
 typedef struct _krb5_gss_ctx_id_rec {
-   int initiate;       /* nonzero if initiating, zero if accepting */
+   unsigned int initiate : 1;  /* nonzero if initiating, zero if accepting */
+   unsigned int established : 1;
+   unsigned int big_endian : 1;
+   unsigned int have_acceptor_subkey : 1;
+   unsigned int seed_init : 1; /* XXX tested but never actually set */
+#ifdef CFX_EXERCISE
+   unsigned int testing_unknown_tokid : 1; /* for testing only */
+#endif
    OM_uint32 gss_flags;
-   int seed_init;
    unsigned char seed[16];
    krb5_principal here;
    krb5_principal there;
    krb5_keyblock *subkey;
    int signalg;
-   int cksum_size;
+   size_t cksum_size;
    int sealalg;
    krb5_keyblock *enc;
    krb5_keyblock *seq;
@@ -169,15 +185,22 @@ typedef struct _krb5_gss_ctx_id_rec {
    /* XXX these used to be signed.  the old spec is inspecific, and
       the new spec specifies unsigned.  I don't believe that the change
       affects the wire encoding. */
-   krb5_ui_4 seq_send;
-   krb5_ui_4 seq_recv;
+   gssint_uint64 seq_send;
+   gssint_uint64 seq_recv;
    void *seqstate;
-   int established;
-   int big_endian;
    krb5_auth_context auth_context;
    gss_OID_desc *mech_used;
-   int nctypes;
-   krb5_cksumtype *ctypes;
+    /* Protocol spec revision
+       0 => RFC 1964 with 3DES and RC4 enhancements
+       1 => draft-ietf-krb-wg-gssapi-cfx-01
+       No others defined so far.  */
+   int proto;
+   krb5_cksumtype cksumtype;   /* for "main" subkey */
+   krb5_keyblock *acceptor_subkey; /* CFX only */
+   krb5_cksumtype acceptor_subkey_cksumtype;
+#ifdef CFX_EXERCISE
+    gss_buffer_desc init_token;
+#endif
 } krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
 
 extern void *kg_vdb;
@@ -595,4 +618,10 @@ gss_OID krb5_gss_convert_static_mech_oid
 (gss_OID oid
         );
        
+krb5_error_code gss_krb5int_make_seal_token_v3(krb5_context,
+                                              krb5_gss_ctx_id_rec *,
+                                              const gss_buffer_desc *,
+                                              gss_buffer_t,
+                                              int, int);
+
 #endif /* _GSSAPIP_KRB5_H_ */
index 98102ce8705aafa08ac88d87fcf81f4961f4b6e2..102ffdadd377ddcdf68549371f6c074bc4d827d9 100644 (file)
@@ -90,13 +90,12 @@ int krb5_gss_dbg_client_expcreds = 0;
  * ccache.
  */
 static krb5_error_code get_credentials(context, cred, server, now,
-                                      endtime, enctypes, out_creds)
+                                      endtime, out_creds)
     krb5_context context;
     krb5_gss_cred_id_t cred;
     krb5_principal server;
     krb5_timestamp now;
     krb5_timestamp endtime;
-    const krb5_enctype *enctypes;
     krb5_creds **out_creds;
 {
     krb5_error_code    code;
@@ -112,10 +111,7 @@ static krb5_error_code get_credentials(context, cred, server, now,
 
     in_creds.keyblock.enctype = 0;
 
-    code = krb5_set_default_tgs_enctypes (context, enctypes);
-    if (code)
-      goto cleanup;
-        code = krb5_get_credentials(context, 0, cred->ccache,
+    code = krb5_get_credentials(context, 0, cred->ccache,
                                &in_creds, out_creds);
     if (code)
        goto cleanup;
@@ -324,86 +320,6 @@ make_ap_req_v1(context, ctx, cred, k_cred, chan_bindings, mech_type, token)
    return (code);
 }
 
-/*
- * get_requested_enctypes
- *
- * Filter the krb5 library's default enctype list with the set of
- * enctypes we support for GSSAPI.
- */
-static krb5_error_code
-get_requested_enctypes(
-   krb5_context context,
-   krb5_enctype **ret_enctypes)
-{
-   krb5_error_code code;
-   int i, j, k;
-   int is_duplicate_enctype;
-   int is_wanted_enctype;
-   static const krb5_enctype wanted_enctypes[] = {
-#if 1
-     ENCTYPE_DES3_CBC_SHA1,
-#endif
-     ENCTYPE_ARCFOUR_HMAC,
-     ENCTYPE_DES_CBC_CRC,
-     ENCTYPE_DES_CBC_MD5, ENCTYPE_DES_CBC_MD4,
-   };
-#define N_WANTED_ENCTYPES (sizeof(wanted_enctypes)/sizeof(wanted_enctypes[0]))
-   krb5_enctype *default_enctypes = 0;
-   krb5_enctype *requested_enctypes;
-
-   *ret_enctypes = malloc((N_WANTED_ENCTYPES + 1) * sizeof(krb5_enctype));
-   if (*ret_enctypes == NULL)
-      return ENOMEM;
-
-   code = krb5_get_tgs_ktypes (context, 0, &default_enctypes);
-   if (code) {
-      free(*ret_enctypes);
-      *ret_enctypes = NULL;
-      return code;
-   }
-   requested_enctypes = *ret_enctypes;
-
-   /* "i" denotes *next* slot to fill.  Don't forget to save room
-      for a trailing zero.  */
-   i = 0;
-   for (j = 0;
-       (default_enctypes[j] != 0
-        /* This part should be redundant, but let's be paranoid.  */
-        && i < N_WANTED_ENCTYPES);
-       j++) {
-
-      krb5_enctype e = default_enctypes[j];
-
-      /* Is this enctype one of the ones we want for GSSAPI?  */
-      is_wanted_enctype = 0;
-      for (k = 0; k < N_WANTED_ENCTYPES; k++) {
-        if (wanted_enctypes[k] == e) {
-           is_wanted_enctype = 1;
-           break;
-        }
-      }
-      /* If unwanted, go to the next one. */
-      if (!is_wanted_enctype)
-        continue;
-
-      /* Is this enctype already in the list of enctypes to
-        request?  (Is it a duplicate?)  */
-      is_duplicate_enctype = 0;
-      for (k = 0; k < i; k++) {
-        if (requested_enctypes[k] == e) {
-           is_duplicate_enctype = 1;
-           break;
-        }
-      }
-      /* If it is not a duplicate, add it. */
-      if (!is_duplicate_enctype)
-        requested_enctypes[i++] = e;
-   }
-   krb5_free_ktypes(context, default_enctypes);
-   requested_enctypes[i++] = 0;
-   return GSS_S_COMPLETE;
-}
-
 /*
  * setup_enc
  *
@@ -418,6 +334,9 @@ setup_enc(
    krb5_error_code code;
    int i;
 
+   ctx->have_acceptor_subkey = 0;
+   ctx->proto = 0;
+   ctx->cksumtype = 0;
    switch(ctx->subkey->enctype) {
    case ENCTYPE_DES_CBC_MD5:
    case ENCTYPE_DES_CBC_MD4:
@@ -433,54 +352,53 @@ setup_enc(
         goto fail;
 
       for (i=0; i<ctx->enc->length; i++)
-        /*SUPPRESS 113*/
         ctx->enc->contents[i] ^= 0xf0;
 
-      if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq)))
-        goto fail;
-
-      break;
+      goto copy_subkey_to_seq;
 
    case ENCTYPE_DES3_CBC_SHA1:
+       /* MIT extension */
       ctx->subkey->enctype = ENCTYPE_DES3_CBC_RAW;
       ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD;
       ctx->cksum_size = 20;
       ctx->sealalg = SEAL_ALG_DES3KD;
 
+   copy_subkey:
       code = krb5_copy_keyblock (context, ctx->subkey, &ctx->enc);
       if (code)
         goto fail;
+   copy_subkey_to_seq:
       code = krb5_copy_keyblock (context, ctx->subkey, &ctx->seq);
       if (code) {
         krb5_free_keyblock (context, ctx->enc);
         goto fail;
       }
       break;
+
    case ENCTYPE_ARCFOUR_HMAC:
+       /* Microsoft extension */
       ctx->signalg = SGN_ALG_HMAC_MD5 ;
       ctx->cksum_size = 8;
       ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ;
 
-      code = krb5_copy_keyblock (context, ctx->subkey, &ctx->enc);
-      if (code)
-        goto fail;
-      code = krb5_copy_keyblock (context, ctx->subkey, &ctx->seq);
-      if (code) {
-        krb5_free_keyblock (context, ctx->enc);
-        goto fail;
-      }
-      break;       
-#if 0
-   case ENCTYPE_DES3_CBC_MD5:
-      enctype = ENCTYPE_DES3_CBC_RAW;
-      ctx->signalg = 3;
-      ctx->cksum_size = 16;
-      ctx->sealalg = 1;
-      break;
-#endif
+      goto copy_subkey;
+
    default:
-      *minor_status = KRB5_BAD_ENCTYPE;
-      return GSS_S_FAILURE;
+       /* Fill some fields we shouldn't be using on this path
+         with garbage.  */
+       ctx->signalg = -10;
+       ctx->sealalg = -10;
+
+       ctx->proto = 1;
+       code = krb5int_c_mandatory_cksumtype(context, ctx->subkey->enctype,
+                                           &ctx->cksumtype);
+       if (code)
+          goto fail;
+       code = krb5_c_checksum_length(context, ctx->cksumtype,
+                                    &ctx->cksum_size);
+       if (code)
+          goto fail;
+       goto copy_subkey;
    }
 fail:
    *minor_status = code;
@@ -533,8 +451,17 @@ new_connection(
    /* complain if the input token is non-null */
 
    if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
-      *minor_status = 0;
-      return(GSS_S_DEFECTIVE_TOKEN);
+#ifdef CFX_EXERCISE
+       if (*context_handle != GSS_C_NO_CONTEXT
+          && ((krb5_gss_ctx_id_t)*context_handle)->testing_unknown_tokid) {
+          /* XXX Should check for a KRB_ERROR message that we can
+             parse, and which contains the expected error code.  */
+          ctx = (krb5_gss_ctx_id_t)*context_handle;
+          goto resume_after_testing;
+       }
+#endif
+       *minor_status = 0;
+       return(GSS_S_DEFECTIVE_TOKEN);
    }
 
    /* create the ctx */
@@ -557,8 +484,6 @@ new_connection(
    ctx->seed_init = 0;
    ctx->big_endian = 0;  /* all initiators do little-endian, as per spec */
    ctx->seqstate = 0;
-   ctx->nctypes = 0;
-   ctx->ctypes = 0;
 
    if ((code = krb5_timeofday(context, &now)))
       goto fail;
@@ -576,13 +501,8 @@ new_connection(
                                   &ctx->there)))
       goto fail;
 
-   code = get_requested_enctypes(context, &requested_enctypes);
-   if (code)
-      goto fail;
-
    code = get_credentials(context, cred, ctx->there, now,
-                         ctx->endtime, requested_enctypes, &k_cred);
-   free(requested_enctypes);
+                         ctx->endtime, &k_cred);
    if (code)
       goto fail;
 
@@ -602,6 +522,7 @@ new_connection(
 
    {
       /* gsskrb5 v1 */
+      krb5_ui_4 seq_temp;
       if ((code = make_ap_req_v1(context, ctx,
                                 cred, k_cred, input_chan_bindings, 
                                 mech_type, &token))) {
@@ -613,8 +534,8 @@ new_connection(
         goto fail;
       }
 
-      krb5_auth_con_getlocalseqnumber(context, ctx->auth_context,
-                                     &ctx->seq_send);
+      krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &seq_temp);
+      ctx->seq_send = seq_temp;
       krb5_auth_con_getsendsubkey(context, ctx->auth_context,
                                  &ctx->subkey);
    }
@@ -638,6 +559,47 @@ new_connection(
    *context_handle = (gss_ctx_id_t) ctx;
    ctx_free = 0;
 
+#ifdef CFX_EXERCISE
+   if (ctx->proto == 1
+       /* I think the RPC code may be broken.  Don't mess around
+         if we're authenticating to "kadmin/whatever".  */
+       && ctx->there->data[0].data[0] != 'k'
+       /* I *know* the FTP server code is broken.  */
+       && ctx->there->data[0].data[0] != 'f'
+       ) {
+       /* Create a bogus token and return it, with status
+         GSS_S_CONTINUE_NEEDED.  Save enough data that we can resume
+         on the next call.  */
+       static const unsigned char hack_token[20] = {
+          0x60, 0x12, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+          0xf7, 0x12, 0x01, 0x02, 0x02, 0x12, 0x34, 0x68,
+          0x65, 0x6c, 0x6c, 0x6f
+       };
+       ctx->testing_unknown_tokid = 1;
+       ctx->init_token = token;
+       token.value = malloc(20);
+       token.length = 20;
+       if (token.value == NULL) {
+          /* Skip testing.  We'll probably die soon enough, but let's
+             not do it because we couldn't exercise this code
+             path.  */
+          goto resume_after_testing;
+       }
+       memcpy(token.value, hack_token, sizeof(hack_token));
+       /* Can just fall through into the normal return path, because
+         it'll always return GSS_S_CONTINUE_NEEDED because we're
+         doing mutual authentication.  */
+   }
+   if (0) {
+   resume_after_testing:
+       token = ctx->init_token;
+       ctx->init_token.value = 0;
+       ctx->init_token.length = 0;
+       ctx->testing_unknown_tokid = 0;
+       ctx_free = 0;
+   }
+#endif
+
    /* compute time_rec */
    if (time_rec) {
       if ((code = krb5_timeofday(context, &now)))
@@ -664,7 +626,7 @@ new_connection(
       ctx->seq_recv = ctx->seq_send;
       g_order_init(&(ctx->seqstate), ctx->seq_recv,
                   (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0, 
-                  (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0);
+                  (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
       ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
       ctx->established = 1;
       return(GSS_S_COMPLETE);
@@ -680,8 +642,6 @@ fail:
           krb5_free_principal(context, ctx_free->there);
        if (ctx_free->subkey)
           krb5_free_keyblock(context, ctx_free->subkey);
-       if (ctx_free->ctypes)
-          krb5_free_cksumtypes(context, ctx_free->ctypes);
        xfree(ctx_free);
    } else
        (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL);
@@ -766,11 +726,11 @@ mutual_auth(
    if (g_verify_token_header((gss_OID) ctx->mech_used,
                             &(ap_rep.length),
                             &ptr, KG_TOK_CTX_AP_REP,
-                            input_token->length)) {
+                            input_token->length, 1)) {
       if (g_verify_token_header((gss_OID) ctx->mech_used,
                                &(ap_rep.length),
                                &ptr, KG_TOK_CTX_ERROR,
-                               input_token->length) == 0) {
+                               input_token->length, 1) == 0) {
 
         /* Handle a KRB_ERROR message from the server */
 
@@ -813,7 +773,21 @@ mutual_auth(
    ctx->seq_recv = ap_rep_data->seq_number;
    g_order_init(&(ctx->seqstate), ctx->seq_recv,
                (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
-               (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) !=0);
+               (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) !=0, ctx->proto);
+
+   if (ctx->proto == 1 && ap_rep_data->subkey) {
+       /* Keep acceptor's subkey.  */
+       ctx->have_acceptor_subkey = 1;
+       code = krb5_copy_keyblock(context, ap_rep_data->subkey,
+                                &ctx->acceptor_subkey);
+       if (code)
+          goto fail;
+       code = krb5int_c_mandatory_cksumtype(context,
+                                           ctx->acceptor_subkey->enctype,
+                                           &ctx->acceptor_subkey_cksumtype);
+       if (code)
+          goto fail;
+   }
 
    /* free the ap_rep_data */
    krb5_free_ap_rep_enc_part(context, ap_rep_data);
@@ -938,7 +912,11 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
    /* is this a new connection or not? */
 
    /*SUPPRESS 29*/
-   if (*context_handle == GSS_C_NO_CONTEXT) {
+   if (*context_handle == GSS_C_NO_CONTEXT
+#ifdef CFX_EXERCISE
+       || ((krb5_gss_ctx_id_t)*context_handle)->testing_unknown_tokid
+#endif
+       ) {
       major_status = new_connection(minor_status, cred, context_handle,
                                    target_name, mech_type, req_flags,
                                    time_req, input_chan_bindings,
index 3c8702a555ccd7f6b7c2dbb7aadfc2f14833aa82..7999a3e1580af757fb959169683c35e881e630cf 100644 (file)
@@ -54,7 +54,7 @@ static krb5_error_code
 make_seal_token_v1 (krb5_context context,
                    krb5_keyblock *enc,
                    krb5_keyblock *seq,
-                   krb5_ui_4 *seqnum,
+                   gssint_uint64 *seqnum,
                    int direction,
                    gss_buffer_t text,
                    gss_buffer_t token,
@@ -304,6 +304,7 @@ make_seal_token_v1 (krb5_context context,
     /* that's it.  return the token */
 
     (*seqnum)++;
+    *seqnum &= 0xffffffffL;
 
     token->length = tlen;
     token->value = (void *) t;
@@ -334,50 +335,16 @@ kg_seal(context, minor_status, context_handle, conf_req_flag, qop_req,
     output_message_buffer->length = 0;
     output_message_buffer->value = NULL;
 
-    /* only default qop or matching established cryptosystem is allowed */
-    
-#if 0
-    switch (qop_req & GSS_KRB5_CONF_C_QOP_MASK) {
-    case GSS_C_QOP_DEFAULT:
-       break;
-    default:
-    unknown_qop:
-       *minor_status = (OM_uint32) G_UNKNOWN_QOP;
-       return GSS_S_FAILURE;
-    case GSS_KRB5_CONF_C_QOP_DES:
-       if (ctx->sealalg != SEAL_ALG_DES) {
-       bad_qop:
-           *minor_status = (OM_uint32) G_BAD_QOP;
-           return GSS_S_FAILURE;
-       }
-       break;
-    case GSS_KRB5_CONF_C_QOP_DES3:
-       if (ctx->sealalg != SEAL_ALG_DES3)
-           goto bad_qop;
-       break;
-    }
-    switch (qop_req & GSS_KRB5_INTEG_C_QOP_MASK) {
-    case GSS_C_QOP_DEFAULT:
-       break;
-    default:
-       goto unknown_qop;
-    case GSS_KRB5_INTEG_C_QOP_MD5:
-    case GSS_KRB5_INTEG_C_QOP_DES_MD5:
-    case GSS_KRB5_INTEG_C_QOP_DES_MAC:
-       if (ctx->sealalg != SEAL_ALG_DES)
-           goto bad_qop;
-       break;
-    case GSS_KRB5_INTEG_C_QOP_HMAC_SHA1:
-       if (ctx->sealalg != SEAL_ALG_DES3KD)
-           goto bad_qop;
-       break;
-    }
-#else
+    /* Only default qop or matching established cryptosystem is allowed.
+
+       There are NO EXTENSIONS to this set for AES and friends!  The
+       new spec says "just use 0".  The old spec plus extensions would
+       actually allow for certain non-zero values.  Fix this to handle
+       them later.  */
     if (qop_req != 0) {
        *minor_status = (OM_uint32) G_UNKNOWN_QOP;
        return GSS_S_FAILURE;
     }
-#endif
 
     /* validate the context handle */
     if (! kg_validate_ctx_id(context_handle)) {
@@ -397,12 +364,26 @@ kg_seal(context, minor_status, context_handle, conf_req_flag, qop_req,
        return(GSS_S_FAILURE);
     }
 
-    code = make_seal_token_v1(context, ctx->enc, ctx->seq,
-                             &ctx->seq_send, ctx->initiate,
-                             input_message_buffer, output_message_buffer,
-                             ctx->signalg, ctx->cksum_size, ctx->sealalg,
-                             conf_req_flag, toktype, ctx->big_endian,
-                             ctx->mech_used);
+    switch (ctx->proto)
+    {
+    case 0:
+       code = make_seal_token_v1(context, ctx->enc, ctx->seq,
+                                 &ctx->seq_send, ctx->initiate,
+                                 input_message_buffer, output_message_buffer,
+                                 ctx->signalg, ctx->cksum_size, ctx->sealalg,
+                                 conf_req_flag, toktype, ctx->big_endian,
+                                 ctx->mech_used);
+       break;
+    case 1:
+       code = gss_krb5int_make_seal_token_v3(context, ctx,
+                                             input_message_buffer,
+                                             output_message_buffer,
+                                             conf_req_flag, toktype);
+       break;
+    default:
+       code = G_UNKNOWN_QOP;   /* XXX */
+       break;
+    }
 
     if (code) {
        *minor_status = code;
diff --git a/src/lib/gssapi/krb5/k5sealv3.c b/src/lib/gssapi/krb5/k5sealv3.c
new file mode 100644 (file)
index 0000000..73b993b
--- /dev/null
@@ -0,0 +1,476 @@
+/* Copyright 2003 MIT.  All rights reserved. */
+/* draft-ietf-krb-wg-gssapi-cfx-02 plus discussed changes */
+
+#include <assert.h>
+#include "k5-platform.h"       /* for 64-bit support */
+#include "k5-int.h"            /* for zap() */
+#include "gssapiP_krb5.h"
+#include <stdarg.h>
+
+static int
+rotate_left (void *ptr, size_t bufsiz, size_t rc)
+{
+    /* Optimize for receiving.  After some debugging is done, the MIT
+       implementation won't do any rotates on sending, and while
+       debugging, they'll be randomly chosen.
+
+       Return 1 for success, 0 for failure (ENOMEM).  */
+    void *tbuf;
+
+    if (bufsiz == 0)
+       return 1;
+    rc = rc % bufsiz;
+    if (rc == 0)
+       return 1;
+
+    tbuf = malloc(rc);
+    if (tbuf == 0)
+       return 0;
+    memcpy(tbuf, ptr, rc);
+    memmove(ptr, (char *)ptr + rc, bufsiz - rc);
+    memcpy((char *)ptr + bufsiz - rc, tbuf, rc);
+    free(tbuf);
+    return 1;
+}
+
+static const gss_buffer_desc empty_message = { 0, 0 };
+
+#define FLAG_SENDER_IS_ACCEPTOR        0x01
+#define FLAG_WRAP_CONFIDENTIAL 0x02
+#define FLAG_ACCEPTOR_SUBKEY   0x04
+
+krb5_error_code
+gss_krb5int_make_seal_token_v3 (krb5_context context,
+                               krb5_gss_ctx_id_rec *ctx,
+                               const gss_buffer_desc * message,
+                               gss_buffer_t token,
+                               int conf_req_flag, int toktype)
+{
+    size_t bufsize = 16;
+    unsigned char *outbuf = 0;
+    krb5_error_code err;
+    int key_usage;
+    unsigned char acceptor_flag;
+    const gss_buffer_desc *message2 = message;
+    size_t rrc, ec;
+    unsigned short tok_id;
+    krb5_checksum sum;
+    krb5_keyblock *key;
+
+    assert(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
+    assert(ctx->big_endian == 0);
+
+    acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
+    key_usage = (toktype == KG_TOK_WRAP_MSG
+                ? (ctx->initiate
+                   ? KG_USAGE_INITIATOR_SEAL
+                   : KG_USAGE_ACCEPTOR_SEAL)
+                : (ctx->initiate
+                   ? KG_USAGE_INITIATOR_SIGN
+                   : KG_USAGE_ACCEPTOR_SIGN));
+    if (ctx->have_acceptor_subkey) {
+       key = ctx->acceptor_subkey;
+    } else {
+       key = ctx->enc;
+    }
+
+#ifdef CFX_EXERCISE
+    {
+       static int initialized = 0;
+       if (!initialized) {
+           srand(time(0));
+           initialized = 1;
+       }
+    }
+#endif
+
+    if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
+       krb5_data plain;
+       krb5_enc_data cipher;
+       size_t ec_max;
+
+       /* 300: Adds some slop.  */
+       if (SIZE_MAX - 300 < message->length)
+           return ENOMEM;
+       ec_max = SIZE_MAX - message->length - 300;
+       if (ec_max > 0xffff)
+           ec_max = 0xffff;
+#ifdef CFX_EXERCISE
+       /* For testing only.  For performance, always set ec = 0.  */
+       ec = ec_max & rand();
+#else
+       ec = 0;
+#endif
+       plain.length = message->length + 16 + ec;
+       plain.data = malloc(message->length + 16 + ec);
+       if (plain.data == NULL)
+           return ENOMEM;
+
+       /* Get size of ciphertext.  */
+       bufsize = 16 + krb5_encrypt_size (plain.length, ctx->enc->enctype);
+       /* Allocate space for header plus encrypted data.  */
+       outbuf = malloc(bufsize);
+       if (outbuf == NULL) {
+           free(plain.data);
+           return ENOMEM;
+       }
+
+       /* TOK_ID */
+       store_16_be(0x0504, outbuf);
+       /* flags */
+       outbuf[2] = (acceptor_flag
+                    | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0)
+                    | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
+       /* filler */
+       outbuf[3] = 0xff;
+       /* EC */
+       store_16_be(ec, outbuf+4);
+       /* RRC */
+       store_16_be(0, outbuf+6);
+       store_64_be(ctx->seq_send, outbuf+8);
+
+       memcpy(plain.data, message->value, message->length);
+       memset(plain.data + message->length, 'x', ec);
+       memcpy(plain.data + message->length + ec, outbuf, 16);
+
+       cipher.ciphertext.data = outbuf + 16;
+       cipher.ciphertext.length = bufsize - 16;
+       cipher.enctype = key->enctype;
+       err = krb5_c_encrypt(context, key, key_usage, 0, &plain, &cipher);
+       zap(plain.data, plain.length);
+       free(plain.data);
+       plain.data = 0;
+       if (err)
+           goto error;
+
+       /* Now that we know we're returning a valid token....  */
+       ctx->seq_send++;
+
+#ifdef CFX_EXERCISE
+       rrc = rand() & 0xffff;
+       if (rotate_left(outbuf+16, bufsize-16,
+                       (bufsize-16) - (rrc % (bufsize - 16))))
+           store_16_be(rrc, outbuf+6);
+       /* If the rotate fails, don't worry about it.  */
+#endif
+    } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
+       krb5_data plain;
+
+       /* Here, message is the application-supplied data; message2 is
+          what goes into the output token.  They may be the same, or
+          message2 may be empty (for MIC).  */
+
+       tok_id = 0x0504;
+
+    wrap_with_checksum:
+       plain.length = message->length + 16;
+       plain.data = malloc(message->length + 16);
+       if (plain.data == NULL)
+           return ENOMEM;
+
+       if (ctx->cksum_size > 0xffff)
+           abort();
+
+       bufsize = 16 + message2->length + ctx->cksum_size;
+       outbuf = malloc(bufsize);
+       if (outbuf == NULL) {
+           free(plain.data);
+           plain.data = 0;
+           err = ENOMEM;
+           goto error;
+       }
+
+       /* TOK_ID */
+       store_16_be(tok_id, outbuf);
+       /* flags */
+       outbuf[2] = (acceptor_flag
+                    | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
+       /* filler */
+       outbuf[3] = 0xff;
+       if (toktype == KG_TOK_WRAP_MSG) {
+           /* Use 0 for checksum calculation, substitute
+              checksum length later.  */
+           /* EC */
+           store_16_be(0, outbuf+4);
+           /* RRC */
+           store_16_be(0, outbuf+6);
+       } else {
+           /* MIC and DEL store 0xFF in EC and RRC.  */
+           store_16_be(0xffff, outbuf+4);
+           store_16_be(0xffff, outbuf+6);
+       }
+       store_64_be(ctx->seq_send, outbuf+8);
+
+       memcpy(plain.data, message->value, message->length);
+       memcpy(plain.data + message->length, outbuf, 16);
+
+       /* Fill in the output token -- data contents, if any, and
+          space for the checksum.  */
+       if (message2->length)
+           memcpy(outbuf + 16, message2->value, message2->length);
+
+       sum.contents = outbuf + 16 + message2->length;
+       sum.length = ctx->cksum_size;
+
+       err = krb5_c_make_checksum(context, ctx->cksumtype, key,
+                                  key_usage, &plain, &sum);
+       zap(plain.data, plain.length);
+       free(plain.data);
+       plain.data = 0;
+       if (err) {
+           zap(outbuf,bufsize);
+           free(outbuf);
+           goto error;
+       }
+       if (sum.length != ctx->cksum_size)
+           abort();
+       memcpy(outbuf + 16 + message2->length, sum.contents, ctx->cksum_size);
+       krb5_free_checksum_contents(context, &sum);
+       sum.contents = 0;
+       /* Now that we know we're actually generating the token...  */
+       ctx->seq_send++;
+
+       if (toktype == KG_TOK_WRAP_MSG) {
+#ifdef CFX_EXERCISE
+           rrc = rand() & 0xffff;
+           /* If the rotate fails, don't worry about it.  */
+           if (rotate_left(outbuf+16, bufsize-16,
+                           (bufsize-16) - (rrc % (bufsize - 16))))
+               store_16_be(rrc, outbuf+6);
+#endif
+           /* Fix up EC field.  */
+           store_16_be(ctx->cksum_size, outbuf+4);
+       } else {
+           store_16_be(0xffff, outbuf+6);
+       }
+    } else if (toktype == KG_TOK_MIC_MSG) {
+       tok_id = 0x0404;
+       message2 = &empty_message;
+       goto wrap_with_checksum;
+    } else if (toktype == KG_TOK_DEL_CTX) {
+       tok_id = 0x0405;
+       message = message2 = &empty_message;
+       goto wrap_with_checksum;
+    } else
+       abort();
+
+    token->value = outbuf;
+    token->length = bufsize;
+    return 0;
+
+error:
+    free(outbuf);
+    token->value = NULL;
+    token->length = 0;
+    return err;
+}
+
+/* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX
+   conf_state is only valid if SEAL. */
+
+OM_uint32
+gss_krb5int_unseal_token_v3(krb5_context *contextptr,
+                           OM_uint32 *minor_status,
+                           krb5_gss_ctx_id_rec *ctx,
+                           unsigned char *ptr, int bodysize,
+                           gss_buffer_t message_buffer,
+                           int *conf_state, int *qop_state, int toktype)
+{
+    krb5_context context = *contextptr;
+    krb5_data plain;
+    gssint_uint64 seqnum;
+    size_t ec, rrc;
+    int key_usage;
+    unsigned char acceptor_flag;
+    krb5_checksum sum;
+    krb5_error_code err;
+    krb5_boolean valid;
+    krb5_keyblock *key;
+
+    assert(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
+    assert(ctx->big_endian == 0);
+    assert(ctx->proto == 1);
+
+    if (qop_state)
+       *qop_state = GSS_C_QOP_DEFAULT;
+
+    acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
+    key_usage = (toktype == KG_TOK_WRAP_MSG
+                ? (!ctx->initiate
+                   ? KG_USAGE_INITIATOR_SEAL
+                   : KG_USAGE_ACCEPTOR_SEAL)
+                : (!ctx->initiate
+                   ? KG_USAGE_INITIATOR_SIGN
+                   : KG_USAGE_ACCEPTOR_SIGN));
+
+    /* Oops.  I wrote this code assuming ptr would be at the start of
+       the token header.  */
+    ptr -= 2;
+    bodysize += 2;
+
+    if (bodysize < 16) {
+    defective:
+       *minor_status = 0;
+       return GSS_S_DEFECTIVE_TOKEN;
+    }
+    if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
+       *minor_status = G_BAD_DIRECTION;
+       return GSS_S_BAD_SIG;
+    }
+
+    /* Two things to note here.
+
+       First, we can't really enforce the use of the acceptor's subkey,
+       if we're the acceptor; the initiator may have sent messages
+       before getting the subkey.  We could probably enforce it if
+       we're the initiator.
+
+       Second, if someone tweaks the code to not set the flag telling
+       the krb5 library to generate a new subkey in the AP-REP
+       message, the MIT library may include a subkey anyways --
+       namely, a copy of the AP-REQ subkey, if it was provided.  So
+       the initiator may think we wanted a subkey, and set the flag,
+       even though we weren't trying to set the subkey.  The "other"
+       key, the one not asserted by the acceptor, will have the same
+       value in that case, though, so we can just ignore the flag.  */
+    if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
+       key = ctx->acceptor_subkey;
+    } else {
+       key = ctx->enc;
+    }
+
+    if (toktype == KG_TOK_WRAP_MSG) {
+       if (load_16_be(ptr) != 0x0504)
+           goto defective;
+       if (ptr[3] != 0xff)
+           goto defective;
+       ec = load_16_be(ptr+4);
+       rrc = load_16_be(ptr+6);
+       seqnum = load_64_be(ptr+8);
+       if (!rotate_left(ptr+16, bodysize-16, rrc)) {
+       no_mem:
+           *minor_status = ENOMEM;
+           return GSS_S_FAILURE;
+       }
+       if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) {
+           /* confidentiality */
+           krb5_enc_data cipher;
+           unsigned char *althdr;
+
+           if (conf_state)
+               *conf_state = 1;
+           /* Do we have no decrypt_size function?
+
+              For all current cryptosystems, the ciphertext size will
+              be larger than the plaintext size.  */
+           cipher.enctype = key->enctype;
+           cipher.ciphertext.length = bodysize - 16;
+           cipher.ciphertext.data = ptr + 16;
+           plain.length = bodysize - 16;
+           plain.data = malloc(plain.length);
+           if (plain.data == NULL)
+               goto no_mem;
+           err = krb5_c_decrypt(context, key, key_usage, 0,
+                                &cipher, &plain);
+           if (err) {
+               free(plain.data);
+               goto error;
+           }
+           /* Don't use bodysize here!  Use the fact that
+              cipher.ciphertext.length has been adjusted to the
+              correct length.  */
+           althdr = plain.data + plain.length - 16;
+           if (load_16_be(althdr) != 0x0504
+               || althdr[2] != ptr[2]
+               || althdr[3] != ptr[3]
+               || memcmp(althdr+8, ptr+8, 8))
+               goto defective;
+           message_buffer->value = plain.data;
+           message_buffer->length = plain.length - ec - 16;
+       } else {
+           /* no confidentiality */
+           if (conf_state)
+               *conf_state = 0;
+           if (ec + 16 < ec)
+               /* overflow check */
+               goto defective;
+           if (ec + 16 > bodysize)
+               goto defective;
+           /* We have: header | msg | cksum.
+              We need cksum(msg | header).
+              Rotate the first two.  */
+           store_16_be(0, ptr+4);
+           store_16_be(0, ptr+6);
+           plain.length = bodysize-ec;
+           plain.data = ptr;
+           if (!rotate_left(ptr, bodysize-ec, 16))
+               goto no_mem;
+           sum.length = ec;
+           if (sum.length != ctx->cksum_size) {
+               *minor_status = 0;
+               return GSS_S_BAD_SIG;
+           }
+           sum.contents = ptr+bodysize-ec;
+           sum.checksum_type = ctx->cksumtype;
+           err = krb5_c_verify_checksum(context, key, key_usage,
+                                        &plain, &sum, &valid);
+           if (err)
+               goto error;
+           if (!valid) {
+               *minor_status = 0;
+               return GSS_S_BAD_SIG;
+           }
+           message_buffer->length = plain.length - 16;
+           message_buffer->value = malloc(message_buffer->length);
+           if (message_buffer->value == NULL)
+               goto no_mem;
+           memcpy(message_buffer->value, plain.data, message_buffer->length);
+       }
+       err = g_order_check(&ctx->seqstate, seqnum);
+       *minor_status = 0;
+       return err;
+    } else if (toktype == KG_TOK_MIC_MSG) {
+       /* wrap token, no confidentiality */
+       if (load_16_be(ptr) != 0x0404)
+           goto defective;
+    verify_mic_1:
+       if (ptr[3] != 0xff)
+           goto defective;
+       if (load_32_be(ptr+4) != 0xffffffffL)
+           goto defective;
+       seqnum = load_64_be(ptr+8);
+       plain.length = message_buffer->length + 16;
+       plain.data = malloc(plain.length);
+       if (plain.data == NULL)
+           goto no_mem;
+       if (message_buffer->length)
+           memcpy(plain.data, message_buffer->value, message_buffer->length);
+       memcpy(plain.data + message_buffer->length, ptr, 16);
+       sum.length = bodysize - 16;
+       sum.contents = ptr + 16;
+       sum.checksum_type = ctx->cksumtype;
+       err = krb5_c_verify_checksum(context, key, key_usage,
+                                    &plain, &sum, &valid);
+       if (err) {
+       error:
+           free(plain.data);
+           *minor_status = err;
+           return GSS_S_BAD_SIG; /* XXX */
+       }
+       if (!valid) {
+           free(plain.data);
+           *minor_status = 0;
+           return GSS_S_BAD_SIG;
+       }
+       err = g_order_check(&ctx->seqstate, seqnum);
+       *minor_status = 0;
+       return err;
+    } else if (toktype == KG_TOK_DEL_CTX) {
+       if (load_16_be(ptr) != 0x0405)
+           goto defective;
+       message_buffer = &empty_message;
+       goto verify_mic_1;
+    } else {
+       goto defective;
+    }
+}
index e678311f9a47815ec082cc0c8ba37f80b246d02f..6851352eee927d43eebda6651d46fbeae51b2a59 100644 (file)
@@ -490,6 +490,7 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer,
     unsigned char *ptr;
     unsigned int bodysize;
     int err;
+    int toktype2;
 
     /* validate the context handle */
     if (! kg_validate_ctx_id(context_handle)) {
@@ -510,14 +511,38 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer,
 
     ptr = (unsigned char *) input_token_buffer->value;
 
-    if (!(err = g_verify_token_header((gss_OID) ctx->mech_used,
-                                     &bodysize, &ptr, toktype,
-                                     input_token_buffer->length))) {
-       return(kg_unseal_v1(context, minor_status, ctx, ptr, bodysize,
-                           message_buffer, conf_state, qop_state,
-                           toktype));
+    if (ctx->proto)
+       switch (toktype) {
+       case KG_TOK_SIGN_MSG:
+           toktype2 = 0x0404;
+           break;
+       case KG_TOK_SEAL_MSG:
+           toktype2 = 0x0504;
+           break;
+       case KG_TOK_DEL_CTX:
+           toktype2 = 0x0405;
+           break;
+       default:
+           toktype2 = toktype;
+           break;
+       }
+    else
+       toktype2 = toktype;
+    err = g_verify_token_header((gss_OID) ctx->mech_used,
+                               &bodysize, &ptr, toktype2,
+                               input_token_buffer->length,
+                               !ctx->proto);
+    if (err) {
+       *minor_status = err;
+       return GSS_S_DEFECTIVE_TOKEN;
     }
 
-    *minor_status = err;
-    return(GSS_S_DEFECTIVE_TOKEN);
+    if (ctx->proto == 0)
+       return kg_unseal_v1(context, minor_status, ctx, ptr, bodysize,
+                           message_buffer, conf_state, qop_state,
+                           toktype);
+    else
+       return gss_krb5int_unseal_token_v3(context, minor_status, ctx,
+                                          ptr, bodysize, message_buffer,
+                                          conf_state, qop_state, toktype);
 }
index 8ab9401c3ec6dbd82d760e14e6f086e687a85c2b..f6314928e56e6ab8fce60568e5317067954c8649 100644 (file)
@@ -237,7 +237,6 @@ kg_ctx_size(kcontext, arg, sizep)
     if ((ctx = (krb5_gss_ctx_id_rec *) arg)) {
        required = 16*sizeof(krb5_int32);
        required += sizeof(ctx->seed);
-       required += ctx->nctypes*sizeof(krb5_int32);
 
        kret = 0;
        if (!kret && ctx->here)
@@ -337,16 +336,14 @@ kg_ctx_externalize(kcontext, arg, buffer, lenremain)
                                       &bp, &remain);
            (void) krb5_ser_pack_int32((krb5_int32) ctx->krb_flags,
                                       &bp, &remain);
-           (void) krb5_ser_pack_int32((krb5_int32) ctx->seq_send,
+           (void) krb5_ser_pack_int64((krb5_int64) ctx->seq_send,
                                       &bp, &remain);
-           (void) krb5_ser_pack_int32((krb5_int32) ctx->seq_recv,
+           (void) krb5_ser_pack_int64((krb5_int64) ctx->seq_recv,
                                       &bp, &remain);
            (void) krb5_ser_pack_int32((krb5_int32) ctx->established,
                                       &bp, &remain);
            (void) krb5_ser_pack_int32((krb5_int32) ctx->big_endian,
                                       &bp, &remain);
-           (void) krb5_ser_pack_int32((krb5_int32) ctx->nctypes,
-                                      &bp, &remain);
 
            /* Now dynamic data */
            kret = 0;
@@ -395,13 +392,6 @@ kg_ctx_externalize(kcontext, arg, buffer, lenremain)
                                               (krb5_pointer) ctx->auth_context,
                                               &bp, &remain);
 
-           for (i=0; i<ctx->nctypes; i++) {
-               if (!kret) {
-                   kret = krb5_ser_pack_int32((krb5_int32) ctx->ctypes[i],
-                                              &bp, &remain);
-               }
-           }
-           
            if (!kret) {
                (void) krb5_ser_pack_int32(KG_CONTEXT, &bp, &remain);
                *buffer = bp;
@@ -464,14 +454,12 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain)
            ctx->endtime = (krb5_timestamp) ibuf;
            (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
            ctx->krb_flags = (krb5_flags) ibuf;
-           (void) krb5_ser_unpack_int32(&ctx->seq_send, &bp, &remain);
-           (void) krb5_ser_unpack_int32(&ctx->seq_recv, &bp, &remain);
+           (void) krb5_ser_unpack_int64(&ctx->seq_send, &bp, &remain);
+           (void) krb5_ser_unpack_int64(&ctx->seq_recv, &bp, &remain);
            (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
            ctx->established = (int) ibuf;
            (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
            ctx->big_endian = (int) ibuf;
-           (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
-           ctx->nctypes = (int) ibuf;
 
            if ((kret = kg_oid_internalize(kcontext, &ctx->mech_used, &bp,
                                           &remain))) {
@@ -531,22 +519,6 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain)
                                               KV5M_AUTH_CONTEXT,
                                       (krb5_pointer *) &ctx->auth_context,
                                               &bp, &remain);
-               
-           if (!kret) {
-               if (ctx->nctypes) {
-                   if ((ctx->ctypes = (krb5_cksumtype *)
-                        malloc(ctx->nctypes*sizeof(krb5_cksumtype))) == NULL){
-                       kret = ENOMEM;
-                   }
-
-                   for (i=0; i<ctx->nctypes; i++) {
-                       if (!kret) {
-                           kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
-                           ctx->ctypes[i] = (krb5_cksumtype) ibuf;
-                       }
-                   }
-               }
-           }
 
            /* Get trailer */
            if (!kret &&
index 43ccc6415bf05c3d835b753355ac952070ae8e1b..59bf30e4cad8aeb9fcad8ea0cd70ea522e5cdb80 100644 (file)
@@ -110,6 +110,32 @@ krb5_gss_wrap_size_limit(minor_status, context_handle, conf_req_flag,
        return(GSS_S_NO_CONTEXT);
     }
 
+    if (ctx->proto == 1) {
+       /* No pseudo-ASN.1 wrapper overhead, so no sequence length and
+          OID.  */
+       OM_uint32 sz = req_output_size;
+       /* Token header: 16 octets.  */
+       if (conf_req_flag) {
+           while (sz > 0 && krb5_encrypt_size(sz, ctx->enc->enctype) + 16 > req_output_size)
+               sz--;
+       } else {
+           if (sz < 16 + ctx->cksum_size)
+               sz = 0;
+           else
+               sz -= (16 + ctx->cksum_size);
+       }
+
+       /* While testing only!  */
+       if (sz < 65536)
+           sz = 0;
+       else
+           sz -= 65535;
+
+       *max_input_size = sz;
+       *minor_status = 0;
+       return GSS_S_COMPLETE;
+    }
+
     /* Calculate the token size and subtract that from the output size */
     overhead = 7 + ctx->mech_used->length;
     data_size = req_output_size;