]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
add code to implement a new krb5 v2 gssapi mechanism.
authorMarc Horowitz <marc@mit.edu>
Wed, 5 Aug 1998 06:04:55 +0000 (06:04 +0000)
committerMarc Horowitz <marc@mit.edu>
Wed, 5 Aug 1998 06:04:55 +0000 (06:04 +0000)
this implementation is complete and functional, but the draft
spec and the code do not yet completely match.

git-svn-id: svn://anonsvn.mit.edu/krb5/branches/marc-3des@10776 dc483132-0cff-0310-8789-dd5450dbe970

18 files changed:
src/lib/gssapi/krb5/accept_sec_context.c
src/lib/gssapi/krb5/acquire_cred.c
src/lib/gssapi/krb5/canon_name.c
src/lib/gssapi/krb5/delete_sec_context.c
src/lib/gssapi/krb5/disp_status.c
src/lib/gssapi/krb5/gssapiP_krb5.h
src/lib/gssapi/krb5/gssapi_err_krb5.et
src/lib/gssapi/krb5/gssapi_krb5.c
src/lib/gssapi/krb5/gssapi_krb5.h
src/lib/gssapi/krb5/init_sec_context.c
src/lib/gssapi/krb5/inq_cred.c
src/lib/gssapi/krb5/inq_names.c
src/lib/gssapi/krb5/k5seal.c
src/lib/gssapi/krb5/k5unseal.c
src/lib/gssapi/krb5/rel_oid.c
src/lib/gssapi/krb5/ser_sctx.c
src/lib/gssapi/krb5/util_ctxsetup.c [new file with mode: 0644]
src/lib/gssapi/krb5/wrap_size_limit.c

index 28f4bac6b0f3717885083202501cb43d2db30e01..d293220334f2eff3647aed13490ed9c66b902d32 100644 (file)
@@ -58,9 +58,8 @@ rd_req_keyproc(krb5_pointer keyprocarg, krb5_principal server,
 
 /* 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)
+rd_and_store_for_creds(context, inbuf, out_cred)
     krb5_context context;
-    krb5_auth_context auth_context;
     krb5_data *inbuf;
     krb5_gss_cred_id_t *out_cred;
 {
@@ -69,10 +68,16 @@ rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
     krb5_ccache ccache;
     krb5_gss_cred_id_t cred = NULL;
     extern krb5_cc_ops krb5_mcc_ops;
+    krb5_auth_context auth_context = NULL;
 
-    if ((retval = krb5_rd_cred(context, auth_context, inbuf, &creds, NULL))) 
+    if ((retval = krb5_auth_con_init(context, &auth_context)))
        return(retval);
 
+    krb5_auth_con_setflags(context, auth_context, 0);
+
+    if ((retval = krb5_rd_cred(context, auth_context, inbuf, &creds, NULL))) 
+       goto cleanup;
+
     /* Lots of kludging going on here... Some day the ccache interface
        will be rewritten though */
 
@@ -91,47 +96,51 @@ rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
 
     /* generate a delegated credential handle */
     if (out_cred) {
-      /* allocate memory for a cred_t... */
-      if (!(cred =
-           (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))) {
-       retval = ENOMEM; /* out of memory? */
-       goto cleanup;
-      }
+       /* allocate memory for a cred_t... */
+       if (!(cred =
+             (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))) {
+           retval = ENOMEM; /* out of memory? */
+           goto cleanup;
+       }
 
-      /* zero it out... */
-      memset(cred, 0, sizeof(krb5_gss_cred_id_rec));
+       /* zero it out... */
+       memset(cred, 0, sizeof(krb5_gss_cred_id_rec));
 
-      /* copy the client principle into it... */
-      if ((retval =
-          krb5_copy_principal(context, creds[0]->client, &(cred->princ)))) {
-       retval = ENOMEM; /* out of memory? */
-       xfree(cred); /* clean up memory on failure */
-       cred = NULL;
-       goto cleanup;
-      }
-
-      cred->usage = GSS_C_INITIATE; /* we can't accept with this */
-      /* cred->princ already set */
-      cred->actual_mechs = gss_mech_set_krb5_both; /* both mechs work */
-      cred->prerfc_mech = cred->rfc_mech = 1; /* Ibid. */
-      cred->keytab = NULL; /* no keytab associated with this... */
-      cred->ccache = ccache; /* but there is a credential cache */
-      cred->tgt_expire = creds[0]->times.endtime; /* store the end time */
+       /* copy the client principle into it... */
+       if ((retval =
+            krb5_copy_principal(context, creds[0]->client, &(cred->princ)))) {
+           retval = ENOMEM; /* out of memory? */
+           xfree(cred); /* clean up memory on failure */
+           cred = NULL;
+           goto cleanup;
+       }
+
+       cred->usage = GSS_C_INITIATE; /* we can't accept with this */
+       /* cred->princ already set */
+       cred->prerfc_mech = 1; /* this cred will work with all three mechs */
+       cred->rfc_mech = 1;
+       cred->rfcv2_mech = 1; 
+       cred->keytab = NULL; /* no keytab associated with this... */
+       cred->ccache = ccache; /* but there is a credential cache */
+       cred->tgt_expire = creds[0]->times.endtime; /* store the end time */
     }
 
     /* If there were errors, there might have been a memory leak
-    if (!cred)
-      if ((retval = krb5_cc_close(context, ccache)))
-       goto cleanup;
-       */
+       if (!cred)
+       if ((retval = krb5_cc_close(context, ccache)))
+       goto cleanup;
+    */
 cleanup:
     krb5_free_tgt_creds(context, creds);
 
     if (!cred && ccache)
-      (void)krb5_cc_close(context, ccache);
+       (void)krb5_cc_close(context, ccache);
 
     if (out_cred)
-      *out_cred = cred; /* return credential */
+       *out_cred = cred; /* return credential */
+
+    if (auth_context)
+       krb5_auth_con_free(context, auth_context);
 
     return retval;
 }
@@ -161,14 +170,14 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
    size_t md5len;
    int bigend;
    krb5_gss_cred_id_t cred = 0;
-   krb5_data ap_req;
+   krb5_data ap_rep, ap_req, mic;
    int i;
    krb5_error_code code;
    krb5_address addr, *paddr;
    krb5_authenticator *authdat = 0;
-   krb5_checksum md5;
+   krb5_checksum reqcksum;
    krb5_principal name = NULL;
-   int gss_flags = 0;
+   krb5_ui_4 gss_flags = 0;
    int decode_req_message = 0;
    krb5_gss_ctx_id_rec *ctx = 0;
    krb5_enctype enctype;
@@ -178,14 +187,18 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
    krb5_auth_context auth_context = NULL;
    krb5_ticket * ticket = NULL;
    int option_id;
-   krb5_data option;
-   krb5_auth_context auth_context_cred = NULL;
+   krb5_data option, cksumdata;
    const gss_OID_desc *mech_used = NULL;
    OM_uint32 major_status = GSS_S_FAILURE;
    krb5_error krb_error_data;
    krb5_data scratch;
    gss_cred_id_t cred_handle = NULL;
    krb5_gss_cred_id_t deleg_cred = NULL;
+   int token_length;
+   int gsskrb5_vers;
+   int nctypes;
+   krb5_cksumtype *ctypes;
+   struct kg2_option fwcred;
 
    if (GSS_ERROR(kg_get_context(minor_status, &context)))
       return(GSS_S_FAILURE);
@@ -197,7 +210,10 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
    output_token->length = 0;
    output_token->value = NULL;
    token.value = 0;
-   md5.contents = 0;
+   reqcksum.contents = 0;
+   mic.data = 0;
+   ap_req.data = 0;
+   ap_rep.data = 0;
    
    if (mech_type)
       *mech_type = GSS_C_NULL_OID;
@@ -205,12 +221,6 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
    if (delegated_cred_handle)
       *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
 
-   /* stash this now, for later. */
-   if (code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &md5len)) {
-       *minor_status = code;
-       return(GSS_S_FAILURE);
-   }
-
    /*
     * Context handle must be unspecified.  Actually, it must be
     * non-established, but currently, accept_sec_context never returns
@@ -224,18 +234,19 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
 
    /* handle default cred handle */
    if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
-          major_status = krb5_gss_acquire_cred(&code, GSS_C_NO_NAME,
-                               GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
-                               GSS_C_ACCEPT, &cred_handle,
-                               NULL, NULL);
-          if (major_status != GSS_S_COMPLETE)
-                  goto fail;
-   } else
-          cred_handle = verifier_cred_handle;
+       major_status = krb5_gss_acquire_cred(&code, GSS_C_NO_NAME,
+                                           GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
+                                           GSS_C_ACCEPT, &cred_handle,
+                                           NULL, NULL);
+       if (major_status != GSS_S_COMPLETE)
+          goto fail;
+   } else {
+       cred_handle = verifier_cred_handle;
+   }
 
-   major_status = krb5_gss_validate_cred(minor_status, verifier_cred_handle);
+   major_status = krb5_gss_validate_cred(&code, verifier_cred_handle);
    if (GSS_ERROR(major_status))
-          goto fail;
+       goto fail;
 
    cred = (krb5_gss_cred_id_t) cred_handle;
 
@@ -243,9 +254,9 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
 
    if ((cred->usage != GSS_C_ACCEPT) &&
        (cred->usage != GSS_C_BOTH)) {
-          code = 0;
-          major_status = GSS_S_NO_CRED;
-          goto fail;
+       code = 0;
+       major_status = GSS_S_NO_CRED;
+       goto fail;
    }
 
    /* verify the token's integrity, and leave the token in ap_req.
@@ -253,60 +264,98 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
 
    ptr = (unsigned char *) input_token->value;
 
-   if ((err = g_verify_token_header((gss_OID) gss_mech_krb5, &(ap_req.length),
-                                   &ptr, KG_TOK_CTX_AP_REQ,
-                                   input_token->length))) {
-       /*
-        * Previous versions of this library used the old mech_id
-        * and some broken behavior (wrong IV on checksum
-        * encryption).  We support the old mech_id for
-        * compatibility, and use it to decide when to use the
-        * old behavior.
-        */
-       if (err != 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))) {
-               major_status = GSS_S_DEFECTIVE_TOKEN;
-               goto fail;
-       } else {
-#if 0       /* Don't restrict mechanisms when accepting contexts */
-            if (! cred->prerfc_mech) {
-                    code = G_WRONG_MECH;
-                    major_status = GSS_S_DEFECTIVE_TOKEN;
-                    goto fail;
-            }
-#endif
-            mech_used = gss_mech_krb5_old;
-       }
-   } else {
+   if (!(code = g_verify_token_header((gss_OID) gss_mech_krb5,
+                                     &(ap_req.length),
+                                     &ptr, KG_TOK_CTX_AP_REQ,
+                                     input_token->length))) {
 #if 0  /* Don't restrict mechanisms when accepting contexts */
-       if (! cred->rfc_mech) {
-               code = G_WRONG_MECH;
-               major_status = GSS_S_DEFECTIVE_TOKEN;
-               goto fail;
-       }
+       if (! cred->rfc_mech) {
+          code = G_WRONG_MECH;
+          major_status = GSS_S_DEFECTIVE_TOKEN;
+          goto fail;
+       }
+#endif
+       mech_used = gss_mech_krb5;
+       gsskrb5_vers = 1000;
+   } 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))) {
+       /*
+       * Previous versions of this library used the old mech_id
+       * and some broken behavior (wrong IV on checksum
+       * encryption).  We support the old mech_id for
+       * compatibility, and use it to decide when to use the
+       * old behavior.
+       */
+#if 0  /* Don't restrict mechanisms when accepting contexts */
+       if (! cred->prerfc_mech) {
+          code = G_WRONG_MECH;
+          major_status = GSS_S_DEFECTIVE_TOKEN;
+          goto fail;
+       }
+#endif
+       mech_used = gss_mech_krb5_old;
+       gsskrb5_vers = 1000;
+   } else if ((code == G_WRONG_MECH) &&
+             !(code = g_verify_token_header((gss_OID) gss_mech_krb5_v2,
+                                            &token_length, 
+                                            &ptr, KG2_TOK_INITIAL,
+                                            input_token->length))) {
+#if 0  /* Don't restrict mechanisms when accepting contexts */
+       if (! cred->rfcv2_mech) {
+          code = G_WRONG_MECH;
+          major_status = GSS_S_DEFECTIVE_TOKEN;
+          goto fail;
+       }
 #endif
-       mech_used = gss_mech_krb5;
+       mech_used = gss_mech_krb5_v2;
+       gsskrb5_vers = 2000;
+   } else {
+       major_status = GSS_S_DEFECTIVE_TOKEN;
+       goto fail;
    }
 
-   sptr = (char *) ptr;
-   TREAD_STR(sptr, ap_req.data, ap_req.length);
-   decode_req_message = 1;
+   if (gsskrb5_vers == 2000) {
+       /* gss krb5 v2 */
+
+       fwcred.option_id = KRB5_GSS_FOR_CREDS_OPTION;
+       fwcred.data = NULL;
+
+       if (GSS_ERROR(major_status =
+                    kg2_parse_token(&code, ptr, token_length,
+                                    &gss_flags, &nctypes, &ctypes,
+                                    delegated_cred_handle?1:0,
+                                    &fwcred, &ap_req, NULL))) {
+          goto fail;
+       }
+
+       gss_flags = (ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3];
+
+       gss_flags &= ~GSS_C_DELEG_FLAG; /* mask out the delegation flag;
+                                         if there's a delegation, we'll
+                                         set it below */
+   } else {
+       /* gss krb5 v1 */
+
+       sptr = (char *) ptr;
+       TREAD_STR(sptr, ap_req.data, ap_req.length);
+       decode_req_message = 1;
+   }
 
    /* construct the sender_addr */
 
    if ((input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) &&
        (input_chan_bindings->initiator_addrtype == GSS_C_AF_INET)) {
-      /* XXX is this right? */
-      addr.addrtype = ADDRTYPE_INET;
-      addr.length = input_chan_bindings->initiator_address.length;
-      addr.contents = input_chan_bindings->initiator_address.value;
+       /* XXX is this right? */
+       addr.addrtype = ADDRTYPE_INET;
+       addr.length = input_chan_bindings->initiator_address.length;
+       addr.contents = input_chan_bindings->initiator_address.value;
 
-      paddr = &addr;
+       paddr = &addr;
    } else {
-      paddr = NULL;
+       paddr = NULL;
    }
 
    /* decode the AP_REQ message */
@@ -314,13 +363,18 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
    /* decode the message */
 
    if ((code = krb5_auth_con_init(context, &auth_context))) {
-       *minor_status = code;
-       return(GSS_S_FAILURE);
+       major_status = GSS_S_FAILURE;
+       goto fail;
    }
    if ((code = krb5_auth_con_setrcache(context, auth_context, cred->rcache))) {
-       *minor_status = code;
-       return(GSS_S_FAILURE);
+       major_status = GSS_S_FAILURE;
+       goto fail;
    }
+   if ((code = krb5_auth_con_setaddrs(context, auth_context, NULL, paddr))) {
+       major_status = GSS_S_FAILURE;
+       goto fail;
+   }
+
    if ((code = krb5_rd_req(context, &auth_context, &ap_req, cred->princ,
                           cred->keytab, NULL, &ticket))) {
        major_status = GSS_S_FAILURE;
@@ -335,137 +389,142 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
    if ((authdat->authenticator->subkey == NULL) ||
        (authdat->ticket->enc_part2 == NULL)) {
           code = KG_NO_SUBKEY;
+          major_status = GSS_S_FAILURE;
           goto fail;
    }
 #endif
 
-   /* verify that the checksum is correct */
+   if (gsskrb5_vers == 2000) {
+       bigend = 1;
+   } else {
+       /* gss krb5 v1 */
+
+       /* stash this now, for later. */
+       if (code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5,
+                                        &md5len)) {
+          major_status = GSS_S_FAILURE;
+          goto fail;
+       }
+
+       /* verify that the checksum is correct */
 
-   /*
-      The checksum may be either exactly 24 bytes, in which case
-      no options are specified, or greater than 24 bytes, in which case
-      one or more options are specified. Currently, the only valid
-      option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ).
-   */
-
-   if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) ||
-       (authdat->checksum->length < 24)) {
+       /*
+        The checksum may be either exactly 24 bytes, in which case
+        no options are specified, or greater than 24 bytes, in which case
+        one or more options are specified. Currently, the only valid
+        option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ).
+       */
+
+       if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) ||
+          (authdat->checksum->length < 24)) {
           code = 0;
           major_status = GSS_S_BAD_BINDINGS;
           goto fail;
-   }
-
-   /*
-      "Be liberal in what you accept, and
-       conservative in what you send"
-               -- rfc1123
-
-       This code will let this acceptor interoperate with an initiator
-       using little-endian or big-endian integer encoding.
-   */
+       }
 
-   ptr = (unsigned char *) authdat->checksum->contents;
-   bigend = 0;
+       /*
+        "Be liberal in what you accept, and
+        conservative in what you send"
+        -- rfc1123
 
-   TREAD_INT(ptr, tmp, bigend);
+        This code will let this acceptor interoperate with an initiator
+        using little-endian or big-endian integer encoding.
+       */
 
-   if (tmp != md5len) {
        ptr = (unsigned char *) authdat->checksum->contents;
-       bigend = 1;
+       bigend = 0;
 
        TREAD_INT(ptr, tmp, bigend);
 
        if (tmp != md5len) {
-          major_status = GSS_S_FAILURE;
-          code = KG_BAD_LENGTH;
-          goto fail;
+          ptr = (unsigned char *) authdat->checksum->contents;
+          bigend = 1;
+
+          TREAD_INT(ptr, tmp, bigend);
+
+          if (tmp != md5len) {
+              code = KG_BAD_LENGTH;
+              major_status = GSS_S_FAILURE;
+              goto fail;
+          }
        }
-   }
 
-   /* at this point, bigend is set according to the initiator's byte order */
+       /* at this point, bigend is set according to the initiator's
+         byte order */
 
-   if ((code = kg_checksum_channel_bindings(context, input_chan_bindings, &md5,
-                                           bigend))) {
-       major_status = GSS_S_BAD_BINDINGS;
-       goto fail;
-   }
+       if ((code = kg_checksum_channel_bindings(context, input_chan_bindings,
+                                               &reqcksum, bigend))) {
+          major_status = GSS_S_BAD_BINDINGS;
+          goto fail;
+       }
 
-   TREAD_STR(ptr, ptr2, md5.length);
-   if (memcmp(ptr2, md5.contents, md5.length) != 0) {
+       TREAD_STR(ptr, ptr2, reqcksum.length);
+       if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
           code = 0;
           major_status = GSS_S_BAD_BINDINGS;
           goto fail;
-   }
-
-   xfree(md5.contents);
-   md5.contents = 0;
-
-   TREAD_INT(ptr, gss_flags, bigend);
-   gss_flags &= ~GSS_C_DELEG_FLAG; /* mask out the delegation flag; if there's
-                                     a delegation, we'll set it below */
-   decode_req_message = 0;
-
-   /* if the checksum length > 24, there are options to process */
+       }
 
-   if(authdat->checksum->length > 24) {
+       xfree(reqcksum.contents);
+       reqcksum.contents = 0;
 
-       i = authdat->checksum->length - 24;
+       TREAD_INT(ptr, gss_flags, bigend);
+       gss_flags &= ~GSS_C_DELEG_FLAG; /* mask out the delegation flag; if
+                                         there's a delegation, we'll set
+                                         it below */
+       decode_req_message = 0;
 
-       while(i>0) {
+       /* if the checksum length > 24, there are options to process */
 
-           TREAD_INT16(ptr, option_id, bigend);
+       if(authdat->checksum->length > 24) {
 
-           switch(option_id) {
+          i = authdat->checksum->length - 24;
 
-               case KRB5_GSS_FOR_CREDS_OPTION:
+          while(i>0) {
 
-                   TREAD_INT16(ptr, option.length, bigend);
+              TREAD_INT16(ptr, option_id, bigend);
 
-                   /* have to use ptr2, since option.data is wrong type and
-                      macro uses ptr as both lvalue and rvalue */
+              switch(option_id) {
 
-                   TREAD_STR(ptr, ptr2, bigend);
-                   option.data = (char FAR *) ptr2;
+              case KRB5_GSS_FOR_CREDS_OPTION:
 
-                   /* get a temporary auth_context structure for the
-                      call to rd_and_store_for_creds() and clear its flags */
+                  TREAD_INT16(ptr, option.length, bigend);
 
-                   if ((code = krb5_auth_con_init(context,
-                                                  &auth_context_cred))) {
-                       major_status = GSS_S_FAILURE;
-                       goto fail;
-                   }
+                  /* have to use ptr2, since option.data is wrong type and
+                     macro uses ptr as both lvalue and rvalue */
 
-                   krb5_auth_con_setflags(context, auth_context_cred, 0);
+                  TREAD_STR(ptr, ptr2, bigend);
+                  option.data = (char FAR *) ptr2;
 
-                   /* store the delegated credential */
+                  /* store the delegated credential */
 
-                   rd_and_store_for_creds(context, auth_context_cred,
-                                          &option,
-                                          (delegated_cred_handle) ?
-                                          &deleg_cred : NULL);
+                  if (code = rd_and_store_for_creds(context, &option,
+                                                    (delegated_cred_handle) ?
+                                                    &deleg_cred : NULL)) {
+                      major_status = GSS_S_FAILURE;
+                      goto fail;
+                  }
 
-                   i -= option.length + 4;
+                  i -= option.length + 4;
 
-                   krb5_auth_con_free(context, auth_context_cred);
+                  gss_flags |= GSS_C_DELEG_FLAG; /* got a delegation */
 
-                   gss_flags |= GSS_C_DELEG_FLAG; /* got a delegation */
+                  break;
 
-                   break;
+                  /* default: */
+                  /* unknown options aren't an error */
 
-                   /* default: */
-                   /* unknown options aren't an error */
+              } /* switch */
+          } /* while */
+       } /* if */
+   }
 
-           } /* switch */
-       } /* while */
-    } /* if */
-                       
    /* create the ctx struct and start filling it in */
 
    if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
        == NULL) {
-       major_status = GSS_S_FAILURE;
        code = ENOMEM;
+       major_status = GSS_S_FAILURE;
        goto fail;
    }
 
@@ -476,78 +535,174 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
    ctx->gss_flags = KG_IMPLFLAGS(gss_flags);
    ctx->seed_init = 0;
    ctx->big_endian = bigend;
-
-   major_status = GSS_S_FAILURE;
+   ctx->gsskrb5_version = gsskrb5_vers;
 
    /* Intern the ctx pointer so that delete_sec_context works */
    if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) {
-          code = G_VALIDATE_FAILED;
-          xfree(ctx);
-          ctx = 0;
-          goto fail;
+       xfree(ctx);
+       ctx = 0;
+
+       code = G_VALIDATE_FAILED;
+       major_status = GSS_S_FAILURE;
+       goto fail;
    }
-   
-   if ((code = krb5_copy_principal(context, cred->princ, &ctx->here)))
-          goto fail;
 
-   if ((code = krb5_copy_principal(context, authdat->client, &ctx->there)))
-          goto fail;
+   if ((code = krb5_copy_principal(context, cred->princ, &ctx->here))) {
+       major_status = GSS_S_FAILURE;
+       goto fail;
+   }
 
-   /* done with authdat */
-   krb5_free_authenticator(context, authdat);
-   authdat = 0;
+   if ((code = krb5_copy_principal(context, authdat->client, &ctx->there))) {
+       major_status = GSS_S_FAILURE;
+       goto fail;
+   }
 
    if ((code = krb5_auth_con_getremotesubkey(context, auth_context,
-                                            &ctx->subkey)))
-          goto fail;
+                                            &ctx->subkey))) { 
+       major_status = GSS_S_FAILURE;      
+       goto fail;
+   }
 
    /* use the session key if the subkey isn't present */
 
    if (ctx->subkey == NULL) {
        if ((code = krb5_auth_con_getkey(context, auth_context,
-                                       &ctx->subkey)))
-              goto fail;
+                                       &ctx->subkey))) {
+          major_status = GSS_S_FAILURE;
+          goto fail;
+       }
    }
 
    if (ctx->subkey == NULL) {
        /* this isn't a very good error, but it's not clear to me this
          can actually happen */
+       major_status = GSS_S_FAILURE;
        code = KRB5KDC_ERR_NULL_KEY;
        goto fail;
    }
 
-   switch(ctx->subkey->enctype) {
-   case ENCTYPE_DES_CBC_MD5:
-   case ENCTYPE_DES_CBC_CRC:
-       enctype = ENCTYPE_DES_CBC_RAW;
-       ctx->signalg = 0;
-       ctx->cksum_size = 8;
-       ctx->sealalg = 0;
-       break;
+   if (gsskrb5_vers == 2000) {
+       int cblen;
+       krb5_boolean valid;
+
+       /* intersect the token ctypes with the local ctypes */
+
+       if (code = krb5_c_keyed_checksum_types(context, ctx->subkey->enctype,
+                                             &ctx->nctypes, &ctx->ctypes))
+          goto fail;
+
+       if (nctypes == 0) {
+          code = KRB5_CRYPTO_INTERNAL;
+          goto fail;
+       }
+
+       kg2_intersect_ctypes(&ctx->nctypes, ctx->ctypes, nctypes, ctypes);
+
+       if (nctypes == 0) {
+          code = KG_NO_CTYPES;
+          goto fail;
+       }
+
+       /* process the delegated cred, if any */
+
+       if (fwcred.data) {
+          krb5_data option;
+
+          option.length = fwcred.length;
+          option.data = fwcred.data;
+
+          if (code = rd_and_store_for_creds(context, &option, &deleg_cred)) {
+              major_status = GSS_S_FAILURE;
+              goto fail;
+          }
+
+          gss_flags |= GSS_C_DELEG_FLAG; /* got a delegation */
+       }
+
+       /* construct the checksum buffer */
+
+       cblen = sizeof(krb5_ui_2)*5;
+       if (input_chan_bindings)
+          cblen += (input_chan_bindings->initiator_address.length+
+                    input_chan_bindings->acceptor_address.length+
+                    input_chan_bindings->application_data.length);
+
+       cksumdata.length = cblen + ((char *)(ap_req.data-2) - (char *)(ptr-2));
+
+       if ((cksumdata.data = (char *) malloc(cksumdata.length)) == NULL) {
+          code = ENOMEM;
+          major_status = GSS_S_FAILURE;
+          goto fail;
+       }
+
+       ptr2 = cksumdata.data;
+
+       if (input_chan_bindings) {
+          TWRITE_INT(ptr2, input_chan_bindings->initiator_addrtype, 1);
+          TWRITE_BUF(ptr2, input_chan_bindings->initiator_address, 1);
+          TWRITE_INT(ptr2, input_chan_bindings->acceptor_addrtype, 1);
+          TWRITE_BUF(ptr2, input_chan_bindings->acceptor_address, 1);
+          TWRITE_BUF(ptr2, input_chan_bindings->application_data, 1);
+       } else {
+          memset(ptr2, 0, cblen);
+          ptr2 += cblen;
+       }
+
+       memcpy(ptr2, ptr-2, ((char *)(ap_req.data-2) - (char *)(ptr-2)));
+
+       if (code = krb5_c_verify_checksum(context, ctx->subkey,
+                                        KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM,
+                                        &cksumdata, authdat->checksum,
+                                        &valid)) {
+          major_status = GSS_S_FAILURE;
+          goto fail;
+       }
+
+       if (!valid) {
+          code = 0;
+          major_status = GSS_S_BAD_SIG;
+          goto fail;
+       }
+   } else {
+       /* gss krb5 v1 */
+
+       switch(ctx->subkey->enctype) {
+       case ENCTYPE_DES_CBC_MD5:
+       case ENCTYPE_DES_CBC_CRC:
+          enctype = ENCTYPE_DES_CBC_RAW;
+          ctx->signalg = 0;
+          ctx->cksum_size = 8;
+          ctx->sealalg = 0;
+          break;
 #if 0
-   case ENCTYPE_DES3_CBC_MD5:
-       enctype = ENCTYPE_DES3_CBC_RAW;
-       ctx->signalg = 3;
-       ctx->cksum_size = 16;
-       ctx->sealalg = 1;
-       break;
+       case ENCTYPE_DES3_CBC_MD5:
+          enctype = ENCTYPE_DES3_CBC_RAW;
+          ctx->signalg = 3;
+          ctx->cksum_size = 16;
+          ctx->sealalg = 1;
+          break;
 #endif
-   default:
-       code = KRB5_BAD_ENCTYPE;
-       goto fail;
-   }
+       default:
+          code = KRB5_BAD_ENCTYPE;
+          goto fail;
+       }
 
-   /* fill in the encryption descriptors */
+       /* fill in the encryption descriptors */
 
-   if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc)))
+       if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) {
+          major_status = GSS_S_FAILURE;
           goto fail;
+       }
 
-   for (i=0; i<ctx->enc->length; i++)
-      /*SUPPRESS 113*/
-      ctx->enc->contents[i] ^= 0xf0;
+       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)))
+       if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq))) {
+          major_status = GSS_S_FAILURE;
           goto fail;
+       }
+   }
 
    ctx->endtime = ticket->enc_part2->times.endtime;
    ctx->krb_flags = ticket->enc_part2->flags;
@@ -556,13 +711,15 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
 
    krb5_auth_con_getremoteseqnumber(context, auth_context, &ctx->seq_recv);
 
-   if ((code = krb5_timeofday(context, &now)))
-          goto fail;
+   if ((code = krb5_timeofday(context, &now))) {
+       major_status = GSS_S_FAILURE;
+       goto fail;
+   }
 
    if (ctx->endtime < now) {
-          code = 0;
-          major_status = GSS_S_CREDENTIALS_EXPIRED;
-          goto fail;
+       code = 0;
+       major_status = GSS_S_CREDENTIALS_EXPIRED;
+       goto fail;
    }
 
    g_order_init(&(ctx->seqstate), ctx->seq_recv,
@@ -575,40 +732,153 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
    /* generate an AP_REP if necessary */
 
    if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
-      krb5_data ap_rep;
-      unsigned char * ptr;
-      if ((code = krb5_mk_rep(context, auth_context, &ap_rep)))
-             goto fail;
-
-      krb5_auth_con_getlocalseqnumber(context, auth_context, &ctx->seq_send);
-      token.length = g_token_size((gss_OID) mech_used, ap_rep.length);
-
-      if ((token.value = (unsigned char *) xmalloc(token.length)) == NULL) {
-             code = ENOMEM;
-             goto fail;
-      }
-      ptr = token.value;
-      g_make_token_header((gss_OID) mech_used, ap_rep.length,
-                         &ptr, KG_TOK_CTX_AP_REP);
-
-      TWRITE_STR(ptr, ap_rep.data, ap_rep.length);
-      xfree(ap_rep.data);
+       unsigned char * ptr;
+       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);
+
+       /* the reply token hasn't been sent yet, but that's ok. */
+       ctx->established = 1;
+
+       if (ctx->gsskrb5_version == 2000) {
+          krb5_ui_4 tok_flags;
+
+          tok_flags =
+              (ctx->gss_flags & GSS_C_DELEG_FLAG)?KG2_RESP_FLAG_DELEG_OK:0;
+
+          cksumdata.length = 8 + 4*ctx->nctypes + 4;
+
+          if ((cksumdata.data = (char *) malloc(cksumdata.length)) == NULL) {
+              code = ENOMEM;
+              major_status = GSS_S_FAILURE;
+              goto fail;
+          }
+
+          /* construct the token fields */
+
+          ptr = cksumdata.data;
+
+          ptr[0] = (KG2_TOK_RESPONSE >> 8) & 0xff;
+          ptr[1] = KG2_TOK_RESPONSE & 0xff;
+
+          ptr[2] = (tok_flags >> 24) & 0xff;
+          ptr[3] = (tok_flags >> 16) & 0xff;
+          ptr[4] = (tok_flags >> 8) & 0xff;
+          ptr[5] = tok_flags & 0xff;
+
+          ptr[6] = (ctx->nctypes >> 8) & 0xff;
+          ptr[7] = ctx->nctypes & 0xff;
+
+          ptr += 8;
+
+          for (i=0; i<ctx->nctypes; i++) {
+              ptr[i] = (ctx->ctypes[i] >> 24) & 0xff;
+              ptr[i+1] = (ctx->ctypes[i] >> 16) & 0xff;
+              ptr[i+2] = (ctx->ctypes[i] >> 8) & 0xff;
+              ptr[i+3] = ctx->ctypes[i] & 0xff;
+
+              ptr += 4;
+          }
+
+          memset(ptr, 0, 4);
+
+          /* make the MIC token */
+
+          {
+              gss_buffer_desc text, token;
+
+              text.length = cksumdata.length;
+              text.value = cksumdata.data;
+
+              /* ctx->seq_send must be set before this call */
+
+              if (GSS_ERROR(major_status =
+                            krb5_gss_get_mic(&code, ctx,
+                                             GSS_C_QOP_DEFAULT,
+                                             &text, &token)))
+                  goto fail;
+
+              mic.length = token.length;
+              mic.data = token.value;
+          }
+
+          token.length = g_token_size((gss_OID) mech_used,
+                                      (cksumdata.length-2)+4+ap_rep.length+
+                                      mic.length);
+
+          if ((token.value = (unsigned char *) xmalloc(token.length))
+              == NULL) {
+              code = ENOMEM;
+              major_status = GSS_S_FAILURE;
+              goto fail;
+          }
+          ptr = token.value;
+          g_make_token_header((gss_OID) mech_used,
+                              (cksumdata.length-2)+4+ap_rep.length+mic.length,
+                              &ptr, KG2_TOK_RESPONSE);
+
+          memcpy(ptr, cksumdata.data+2, cksumdata.length-2);
+          ptr += cksumdata.length-2;
+
+          ptr[0] = (ap_rep.length >> 8) & 0xff;
+          ptr[1] = ap_rep.length & 0xff;
+          memcpy(ptr+2, ap_rep.data, ap_rep.length);
+
+          ptr += (2+ap_rep.length);
+
+          ptr[0] = (mic.length >> 8) & 0xff;
+          ptr[1] = mic.length & 0xff;
+          memcpy(ptr+2, mic.data, mic.length);
+
+          ptr += (2+mic.length);
+
+          /* gss krb5 v2 */
+       } else {
+          /* gss krb5 v1 */
+
+          token.length = g_token_size((gss_OID) mech_used, ap_rep.length);
+
+          if ((token.value = (unsigned char *) xmalloc(token.length))
+              == NULL) {
+              major_status = GSS_S_FAILURE;
+              code = ENOMEM;
+              goto fail;
+          }
+          ptr = token.value;
+          g_make_token_header((gss_OID) mech_used, ap_rep.length,
+                              &ptr, KG_TOK_CTX_AP_REP);
+
+          TWRITE_STR(ptr, ap_rep.data, ap_rep.length);
+          xfree(ap_rep.data);
+
+          ctx->established = 1;
+
+       }
    } else {
-      token.length = 0;
-      token.value = NULL;
-      ctx->seq_send = ctx->seq_recv;
+       token.length = 0;
+       token.value = NULL;
+       ctx->seq_send = ctx->seq_recv;
+
+       ctx->established = 1;
    }
 
    /* set the return arguments */
 
    if (src_name) {
-      if ((code = krb5_copy_principal(context, ctx->there, &name)))
-             goto fail;
-      /* intern the src_name */
-      if (! kg_save_name((gss_name_t) name)) {
-             code = G_VALIDATE_FAILED;
-             goto fail;
-      }
+       if ((code = krb5_copy_principal(context, ctx->there, &name))) {
+          major_status = GSS_S_FAILURE;
+          goto fail;
+       }
+       /* intern the src_name */
+       if (! kg_save_name((gss_name_t) name)) {
+          code = G_VALIDATE_FAILED;
+          major_status = GSS_S_FAILURE;
+          goto fail;
+       }
    }
 
    if (mech_type)
@@ -620,7 +890,6 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
    if (ret_flags)
       *ret_flags = ctx->gss_flags;
 
-   ctx->established = 1;
    *context_handle = ctx;
    *output_token = token;
 
@@ -628,12 +897,13 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
       *src_name = (gss_name_t) name;
 
    if (delegated_cred_handle && deleg_cred) {
-     if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) {
-       code = G_VALIDATE_FAILED;
-       goto fail;
-     }
+       if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) {
+          major_status = GSS_S_FAILURE;
+          code = G_VALIDATE_FAILED;
+          goto fail;
+       }
 
-     *delegated_cred_handle = (gss_cred_id_t) deleg_cred;
+       *delegated_cred_handle = (gss_cred_id_t) deleg_cred;
    }
 
    /* finally! */
@@ -641,27 +911,33 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
    *minor_status = 0;
    return(GSS_S_COMPLETE);
 
-fail:
+ fail:
    if (authdat)
-          krb5_free_authenticator(context, authdat);
+       krb5_free_authenticator(context, authdat);
    if (ctx)
-          (void) krb5_gss_delete_sec_context(minor_status, 
-                                             (gss_ctx_id_t *) &ctx, NULL);
+       (void) krb5_gss_delete_sec_context(minor_status, 
+                                         (gss_ctx_id_t *) &ctx, NULL);
    if (token.value)
-          xfree(token.value);
+       xfree(token.value);
    if (name) {
-        (void) kg_delete_name((gss_name_t) name);
-        krb5_free_principal(context, name);
+       (void) kg_delete_name((gss_name_t) name);
+       krb5_free_principal(context, name);
    }
-   if (md5.contents)
-        xfree(md5.contents);
+   if (reqcksum.contents)
+       xfree(reqcksum.contents);
    if (deleg_cred) { /* free memory associated with the deleg credential */
-     if (deleg_cred->ccache)
-       (void)krb5_cc_close(context, deleg_cred->ccache);
-     if (deleg_cred->princ)
-       krb5_free_principal(context, deleg_cred->princ);
-     xfree(deleg_cred);
+       if (deleg_cred->ccache)
+          (void)krb5_cc_close(context, deleg_cred->ccache);
+       if (deleg_cred->princ)
+          krb5_free_principal(context, deleg_cred->princ);
+       xfree(deleg_cred);
    }
+   if (ap_req.data)
+       xfree(ap_req.data);
+   if (ap_rep.data)
+       xfree(ap_rep.data);
+   if (mic.data)
+       xfree(mic.data);
 
    *minor_status = code;
 
@@ -672,48 +948,75 @@ fail:
     * decode the authenticator to read out the gss_flags field.
     */
    if (decode_req_message) {
-          krb5_ap_req  * request;
+       krb5_ap_req     * request;
           
-          if (decode_krb5_ap_req(&ap_req, &request))
-                  return (major_status);
-          if (request->ap_options & AP_OPTS_MUTUAL_REQUIRED)
-                  gss_flags |= GSS_C_MUTUAL_FLAG;
-          krb5_free_ap_req(context, request);
+       if (decode_krb5_ap_req(&ap_req, &request))
+          return (major_status);
+       if (request->ap_options & AP_OPTS_MUTUAL_REQUIRED)
+          gss_flags |= GSS_C_MUTUAL_FLAG;
+       krb5_free_ap_req(context, request);
    }
 
    if (cred && (gss_flags & GSS_C_MUTUAL_FLAG)) {
-          /*
-           * The client is expecting a response, so we can send an
-           * error token back
-           */
-          memset(&krb_error_data, 0, sizeof(krb_error_data));
-
-          code  -= ERROR_TABLE_BASE_krb5;
-          if (code < 0 || code > 128)
-                  code = 60 /* KRB_ERR_GENERIC */;
-
-          krb_error_data.error = code;
-          (void) krb5_us_timeofday(context, &krb_error_data.stime,
-                                   &krb_error_data.susec);
-          krb_error_data.server = cred->princ;
+       int tmsglen, toktype;
+
+       /*
+       * The client is expecting a response, so we can send an
+       * error token back
+       */
+       memset(&krb_error_data, 0, sizeof(krb_error_data));
+
+       code  -= ERROR_TABLE_BASE_krb5;
+       if (code < 0 || code > 128)
+          code = 60 /* KRB_ERR_GENERIC */;
+
+       krb_error_data.error = code;
+       (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);
+       code = krb5_mk_error(context, &krb_error_data, &scratch);
+       if (code)
+          return (major_status);
+
+       if (gsskrb5_vers == 2000) {
+          tmsglen = 12+scratch.length;
+          toktype = KG2_TOK_RESPONSE;
+       } else {
+          tmsglen = scratch.length;
+          toktype = KG_TOK_CTX_ERROR;
+       }
 
-          token.length = g_token_size((gss_OID) mech_used, scratch.length);
-          token.value = (unsigned char *) xmalloc(token.length);
-          if (!token.value)
-                  return (major_status);
+       token.length = g_token_size((gss_OID) mech_used, tmsglen);
+       token.value = (unsigned char *) xmalloc(token.length);
+       if (!token.value)
+          return (major_status);
 
-          ptr = token.value;
-          g_make_token_header((gss_OID) mech_used, scratch.length,
-                              &ptr, KG_TOK_CTX_ERROR);
+       ptr = token.value;
+       g_make_token_header((gss_OID) mech_used, tmsglen, &ptr, toktype);
+
+       if (gsskrb5_vers == 2000) {
+          krb5_ui_4 flags;
+
+          flags = KG2_RESP_FLAG_ERROR;
+
+          ptr[0] = (flags << 24) & 0xff;
+          ptr[1] = (flags << 16) & 0xff;
+          ptr[2] = (flags << 8) & 0xff;
+          ptr[3] = flags & 0xff;
+
+          memset(ptr+4, 0, 6);
+
+          ptr[10] = (scratch.length << 8) & 0xff;
+          ptr[11] = scratch.length & 0xff;
+
+          ptr += 12;
+       }
 
-          TWRITE_STR(ptr, scratch.data, scratch.length);
-          xfree(scratch.data);
+       TWRITE_STR(ptr, scratch.data, scratch.length);
+       xfree(scratch.data);
 
-          *output_token = token;
+       *output_token = token;
    }
    if (!verifier_cred_handle && cred_handle) {
           krb5_gss_release_cred(&code, cred_handle);
index 1ca1bf31a7dfa2a693d7349b4a13bdb192d4c314..eaf4c0fb6d0270d096f35a40cfce46eb2a84f6c2 100644 (file)
@@ -248,8 +248,7 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
    size_t i;
    krb5_gss_cred_id_t cred;
    gss_OID_set ret_mechs;
-   const gss_OID_set_desc FAR * valid_mechs;
-   int req_old, req_new;
+   int req_old, req_new, req_v2;
    OM_uint32 ret;
    krb5_error_code code;
 
@@ -277,27 +276,24 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
       contains krb5 */
 
    if (desired_mechs == GSS_C_NULL_OID_SET) {
-      valid_mechs = gss_mech_set_krb5_both;
       req_old = 1;
       req_new = 1;
+      req_v2 = 1;
    } else {
       req_old = 0;
       req_new = 0;
+      req_v2 = 0;
 
       for (i=0; i<desired_mechs->count; i++) {
         if (g_OID_equal(gss_mech_krb5_old, &(desired_mechs->elements[i])))
            req_old++;
         if (g_OID_equal(gss_mech_krb5, &(desired_mechs->elements[i])))
            req_new++;
+        if (g_OID_equal(gss_mech_krb5_v2, &(desired_mechs->elements[i])))
+           req_v2++;
       }
 
-      if (req_old && req_new) {
-        valid_mechs = gss_mech_set_krb5_both;
-      } else if (req_old) {
-        valid_mechs = gss_mech_set_krb5_old;
-      } else if (req_new) {
-        valid_mechs = gss_mech_set_krb5;
-      } else {
+      if (!req_old && !req_new && !req_v2) {
         *minor_status = 0;
         return(GSS_S_BAD_MECH);
       }
@@ -314,9 +310,9 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
 
    cred->usage = cred_usage;
    cred->princ = NULL;
-   cred->actual_mechs = valid_mechs;
    cred->prerfc_mech = req_old;
    cred->rfc_mech = req_new;
+   cred->rfcv2_mech = req_v2;
 
    cred->keytab = NULL;
    cred->ccache = NULL;
@@ -407,17 +403,30 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
    /* create mechs */
 
    if (actual_mechs) {
-      if (! g_copy_OID_set(cred->actual_mechs, &ret_mechs)) {
-        if (cred->ccache)
-           (void)krb5_cc_close(context, cred->ccache);
-        if (cred->keytab)
-           (void)krb5_kt_close(context, cred->keytab);
-        if (cred->princ)
-           krb5_free_principal(context, cred->princ);
-        xfree(cred);
-        *minor_status = ENOMEM;
-        return(GSS_S_FAILURE);
-      }
+       if (GSS_ERROR(ret = generic_gss_create_empty_oid_set(minor_status,
+                                                           &ret_mechs)) ||
+          (cred->prerfc_mech &&
+           GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
+                                                          gss_mech_krb5_old,
+                                                          &ret_mechs))) ||
+          (cred->rfc_mech &&
+           GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
+                                                          gss_mech_krb5,
+                                                          &ret_mechs))) ||
+          (cred->rfcv2_mech &&
+           GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
+                                                          gss_mech_krb5_v2,
+                                                          &ret_mechs)))) {
+          if (cred->ccache)
+              (void)krb5_cc_close(context, cred->ccache);
+          if (cred->keytab)
+              (void)krb5_kt_close(context, cred->keytab);
+          if (cred->princ)
+              krb5_free_principal(context, cred->princ);
+          xfree(cred);
+          /* *minor_status set above */
+          return(ret);
+       }
    }
 
    /* intern the credential handle */
index 652745c7b823ded05f1e789548adbe30ba9108ac..688366e1f96e69da2b8c04eea17a168a1aa8d4af 100644 (file)
@@ -31,13 +31,12 @@ OM_uint32 krb5_gss_canonicalize_name(OM_uint32  *minor_status,
                                     const gss_OID mech_type,
                                     gss_name_t *output_name)
 {
-       if ((mech_type == GSS_C_NULL_OID) ||
-           !g_OID_equal(mech_type, gss_mech_krb5)) {
-               if (minor_status)
-                       *minor_status = 0;
-               return(GSS_S_BAD_MECH);
-       }
-               
-       return gss_duplicate_name(minor_status, input_name,
-                                 output_name);
+    if (!g_OID_equal(gss_mech_krb5_v2, mech_type) &&
+       !g_OID_equal(gss_mech_krb5, mech_type) &&
+       !g_OID_equal(gss_mech_krb5_old, mech_type)) {
+       *minor_status = 0;
+       return(GSS_S_BAD_MECH);
+    }
+
+    return(gss_duplicate_name(minor_status, input_name, output_name));
 }
index 766a17bc5ac26e0b52742346b043d51f6f31c096..28c23589062f6209454d89da0c619509ab3cd027 100644 (file)
@@ -101,6 +101,9 @@ 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 4dc13843ce567b5306d34d341019724e01565a8b..3a6ba7b1ae76f270a8ab0abe3e73c70604eccef1 100644 (file)
@@ -49,10 +49,12 @@ krb5_gss_display_status(minor_status, status_value, status_type,
       return(GSS_S_FAILURE);
 
    if ((mech_type != GSS_C_NULL_OID) &&
-       (! g_OID_equal(gss_mech_krb5, mech_type))) {
-      *minor_status = 0;
-      return(GSS_S_BAD_MECH);
-   }
+       !g_OID_equal(gss_mech_krb5_v2, mech_type) &&
+       !g_OID_equal(gss_mech_krb5, mech_type) &&
+       !g_OID_equal(gss_mech_krb5_old, mech_type)) {
+       *minor_status = 0;
+       return(GSS_S_BAD_MECH);
+    }
 
    if (status_type == GSS_C_GSS_CODE) {
       return(g_display_major_status(minor_status, status_value,
index 984f7078eb037d470eebfc91d64162549cd388ff..bcaa4a930872797f4aae3b3530528045b6563e94 100644 (file)
 #define KG2_TOK_RESPONSE       0x0202
 #define KG2_TOK_MIC            0x0303
 #define KG2_TOK_WRAP_INTEG     0x0404
-#define KG2_TOK_WRAP_PRIVINTEG 0x0505
+#define KG2_TOK_WRAP_PRIV      0x0505
 
 #define KRB5_GSS_FOR_CREDS_OPTION 1
 
+#define KG2_RESP_FLAG_ERROR            0x0001
+#define KG2_RESP_FLAG_DELEG_OK         0x0002
+
 /** internal types **/
 
 typedef krb5_principal krb5_gss_name_t;
@@ -84,10 +87,9 @@ typedef struct _krb5_gss_cred_id_rec {
    /* name/type of credential */
    gss_cred_usage_t usage;
    krb5_principal princ;       /* this is not interned as a gss_name_t */
-   const gss_OID_set_desc *actual_mechs;
    int prerfc_mech;            /* these are a cache of the set above */
    int rfc_mech;
-   int k2_mech;
+   int rfcv2_mech;
 
    /* keytab (accept) data */
    krb5_keytab keytab;
@@ -113,17 +115,31 @@ typedef struct _krb5_gss_ctx_id_rec {
    krb5_keyblock *seq;
    krb5_timestamp endtime;
    krb5_flags krb_flags;
-   krb5_int32 seq_send;
-   krb5_int32 seq_recv;
+   /* 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;
    void *seqstate;
    int established;
    int big_endian;
    krb5_auth_context auth_context;
    gss_OID_desc *mech_used;
+   int gsskrb5_version;
+   int nctypes;
+   krb5_cksumtype *ctypes;
 } krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
 
 extern void *kg_vdb;
 
+struct kg2_option {
+    int option_id;             /* set by caller */
+    int length;                        /* filled in by parser */
+    unsigned char *data;       /* filled in by parser.  points inside
+                                  passed-in token, so nothing needs to
+                                  be freed */
+};
+
 /* helper macros */
 
 #define kg_save_name(name)             g_save_name(&kg_vdb,name)
@@ -225,6 +241,23 @@ krb5_error_code kg_ctx_internalize PROTOTYPE((krb5_context kcontext,
 OM_uint32 kg_get_context PROTOTYPE((OM_uint32 *minor_status,
                                    krb5_context *context));
        
+OM_uint32
+kg2_parse_token PROTOTYPE((OM_uint32 *minor_status,
+                          unsigned char *ptr,
+                          int length,
+                          krb5_ui_4 *flags,
+                          int *nctypes, /* OUT */
+                          krb5_cksumtype **ctypes, /* OUT */
+                          int noptions,
+                          struct kg2_option *options, /* INOUT */
+                          krb5_data *kmsg,
+                          krb5_data *mic));
+
+void kg2_intersect_ctypes PROTOTYPE((int *nc1, 
+                                    krb5_cksumtype *c1,
+                                    int nc2,
+                                    const krb5_cksumtype *c2));
+
 /** declarations of internal name mechanism functions **/
 
 OM_uint32 krb5_gss_acquire_cred
index 54a126518b34fb9ff556ba66bc6b10369c879f96..3c9be6351fb3ebfd11cae9a226cfcca193725102 100644 (file)
@@ -35,4 +35,5 @@ error_code KG_CRED, "Bad magic number for krb5_gss_cred_id_t"
 error_code KG_ENC_DESC, "Bad magic number for krb5_gss_enc_desc"
 error_code KG_BAD_SEQ, "Sequence number in token is corrupt"
 error_code KG_EMPTY_CCACHE, "Credential cache is empty"
+error_code KG_NO_CTYPES, "Acceptor and Initiator share no checksum types"
 end
index c0942c39a63b2173771441d3301d1a296041434e..592ddfbf86ab5cdb3bb1f69d057ab1adf580ad11 100644 (file)
@@ -43,6 +43,9 @@
  * The OID of the proposed standard krb5 mechanism is:
  *     iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
  *     krb5(2) = 1.2.840.113554.1.2.2
+ * The OID of the proposed standard krb5 v2 mechanism is:
+ *     iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
+ *     krb5v2(3) = 1.2.840.113554.1.2.3
  *     
  */
 
@@ -58,8 +61,13 @@ const gss_OID_desc krb5_gss_oid_array[] = {
    {5, "\053\005\001\005\002"},
    /* this is the official, rfc-specified OID */
    {9, "\052\206\110\206\367\022\001\002\002"},
+   /* these two are name type OID's */
    {10, "\052\206\110\206\367\022\001\002\002\001"},
    {10, "\052\206\110\206\367\022\001\002\002\002"},
+   /* this is the v2 assigned OID */
+   {9, "\052\206\110\206\367\022\001\002\003"},
+   /* this is the official, rfc-specified OID again */
+   {9, "\052\206\110\206\367\022\001\002\002"},
    { 0, 0 }
 };
 
@@ -67,16 +75,21 @@ const gss_OID_desc * const gss_mech_krb5_old = krb5_gss_oid_array+0;
 const gss_OID_desc * const gss_mech_krb5 = krb5_gss_oid_array+1;
 const gss_OID_desc * const gss_nt_krb5_name = krb5_gss_oid_array+2;
 const gss_OID_desc * const gss_nt_krb5_principal = krb5_gss_oid_array+3;
+const gss_OID_desc * const gss_mech_krb5_v2 = krb5_gss_oid_array+4;
 
 static const gss_OID_set_desc oidsets[] = {
    {1, (gss_OID) krb5_gss_oid_array+0},
    {1, (gss_OID) krb5_gss_oid_array+1},
    {2, (gss_OID) krb5_gss_oid_array+0},
+   {1, (gss_OID) krb5_gss_oid_array+4},
+   {2, (gss_OID) krb5_gss_oid_array+4},
 };
 
 const gss_OID_set_desc * const gss_mech_set_krb5_old = oidsets+0;
 const gss_OID_set_desc * const gss_mech_set_krb5 = oidsets+1;
 const gss_OID_set_desc * const gss_mech_set_krb5_both = oidsets+2;
+const gss_OID_set_desc * const gss_mech_set_krb5_v2 = oidsets+3;
+const gss_OID_set_desc * const gss_mech_set_krb5_v1v2 = oidsets+4;
 
 void *kg_vdb = NULL;
 
index 63ac530f318b90af312285d0dc6ff9a7a0a2c84e..e4eccbb42920e766746a75f2da270bc33de917c9 100644 (file)
 
 extern const gss_OID_desc * const gss_mech_krb5;
 extern const gss_OID_desc * const gss_mech_krb5_old;
+extern const gss_OID_desc * const gss_mech_krb5_v2;
 extern const gss_OID_set_desc * const gss_mech_set_krb5;
 extern const gss_OID_set_desc * const gss_mech_set_krb5_old;
 extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
+extern const gss_OID_set_desc * const gss_mech_set_krb5_v2;
+extern const gss_OID_set_desc * const gss_mech_set_krb5_v1v2;
 
 extern const gss_OID_desc * const gss_nt_krb5_name;
 extern const gss_OID_desc * const gss_nt_krb5_principal;
index 160550688b8c0d99b60cef20087d548497dd4657..97e2b74cd53d4eaf54413993b29beead9b5902f3 100644 (file)
 int krb5_gss_dbg_client_expcreds = 0;
 
 static krb5_error_code
-make_ap_req(context, auth_context, cred, server, now, endtime, chan_bindings, 
-           req_flags, krb_flags, mech_type, token)
+make_ap_req_v2(context, auth_context, cred, server, now, endtime,
+              chan_bindings, req_flags, krb_flags, mech_type,
+              ret_nctypes, ret_ctypes, token)
+    krb5_context context;
+    krb5_auth_context * auth_context;
+    krb5_gss_cred_id_t cred;
+    krb5_principal server;
+    krb5_timestamp now;
+    krb5_timestamp *endtime;
+    gss_channel_bindings_t chan_bindings;
+    OM_uint32 *req_flags;
+    krb5_flags *krb_flags;
+    gss_OID mech_type;
+    int *ret_nctypes;
+    krb5_cksumtype **ret_ctypes;
+    gss_buffer_t token;
+{
+    krb5_flags mk_req_flags = 0;
+    krb5_int32 con_flags;
+    krb5_error_code code;
+    krb5_creds in_creds, *out_creds = 0;
+    krb5_data credmsg, cksumdata, ap_req;
+    int i, tlen, cblen, nctypes;
+    krb5_cksumtype *ctypes;
+    unsigned char *t, *ptr;
+
+    credmsg.data = 0;
+    cksumdata.data = 0;
+    ap_req.data = 0;
+    ctypes = 0;
+
+    /* this probably isn't necessary */
+    if (*auth_context)
+       krb5_auth_con_free(context, *auth_context);
+
+    *auth_context = 0;
+
+    /* create the option data if necessary */
+
+    if (*req_flags & GSS_C_DELEG_FLAG) {
+       /* first get KRB_CRED message, so we know its length */
+
+       /* clear the time check flag that was set in krb5_auth_con_init() */
+       krb5_auth_con_getflags(context, *auth_context, &con_flags);
+       krb5_auth_con_setflags(context, *auth_context,
+                              con_flags & ~KRB5_AUTH_CONTEXT_DO_TIME);
+
+       code = krb5_fwd_tgt_creds(context, *auth_context, 0,
+                                 cred->princ, server, cred->ccache, 1,
+                                 &credmsg);
+
+       /* turn KRB5_AUTH_CONTEXT_DO_TIME back on */
+       krb5_auth_con_setflags(context, *auth_context, con_flags);
+
+       if (code) {
+           /* don't fail here; just don't accept/do the delegation
+               request */
+           *req_flags &= ~GSS_C_DELEG_FLAG;
+       } else {
+           if (credmsg.length > KRB5_INT16_MAX) {
+               krb5_free_data_contents(context, &credmsg);
+               return(KRB5KRB_ERR_FIELD_TOOLONG);
+           }
+       }
+    } else {
+       credmsg.length = 0;
+    }
+       
+    /*
+     * Get the credential, for the session key etype
+     */
+
+    memset((char *) &in_creds, 0, sizeof(krb5_creds));
+
+    if ((code = krb5_copy_principal(context, cred->princ, &in_creds.client)))
+       goto cleanup;
+    if ((code = krb5_copy_principal(context, server, &in_creds.server)))
+       goto cleanup;
+    in_creds.times.endtime = *endtime;
+
+    if ((code = krb5_get_credentials(context, 0, cred->ccache, 
+                                    &in_creds, &out_creds)))
+       goto cleanup;
+
+    /*
+     * Enforce a stricter limit (without timeskew forgiveness at the
+     * boundaries) because accept_sec_context code is also similarly
+     * non-forgiving.
+     */
+    if (!krb5_gss_dbg_client_expcreds && out_creds->times.endtime < now) {
+       code = KRB5KRB_AP_ERR_TKT_EXPIRED;
+       goto cleanup;
+    }
+
+    /* construct the list of compatible cksum types */
+
+    if (code = krb5_c_keyed_checksum_types(context,
+                                          out_creds->keyblock.enctype,
+                                          &nctypes, &ctypes))
+       goto cleanup;
+
+    if (nctypes == 0) {
+       code = KRB5_CRYPTO_INTERNAL;
+       goto cleanup;
+    }
+
+    /* construct the checksum fields */
+
+    cblen = sizeof(krb5_ui_2)*5;
+    if (chan_bindings)
+       cblen += (chan_bindings->initiator_address.length+
+                 chan_bindings->acceptor_address.length+
+                 chan_bindings->application_data.length);
+
+    cksumdata.length = cblen + 8 + 4*nctypes + 4;
+    if (credmsg.length)
+       cksumdata.length += 4 + credmsg.length;
+
+    if ((cksumdata.data = (char *) malloc(cksumdata.length)) == NULL)
+       goto cleanup;
+
+    /* helper macros.  This code currently depends on a long being 32
+       bits, and htonl dtrt. */
+
+    ptr = cksumdata.data;
+
+    if (chan_bindings) {
+       TWRITE_INT(ptr, chan_bindings->initiator_addrtype, 1);
+       TWRITE_BUF(ptr, chan_bindings->initiator_address, 1);
+       TWRITE_INT(ptr, chan_bindings->acceptor_addrtype, 1);
+       TWRITE_BUF(ptr, chan_bindings->acceptor_address, 1);
+       TWRITE_BUF(ptr, chan_bindings->application_data, 1);
+    } else {
+       memset(ptr, 0, cblen);
+       ptr += cblen;
+    }
+
+    /* construct the token fields */
+
+    ptr[0] = (KG2_TOK_INITIAL >> 8) & 0xff;
+    ptr[1] = KG2_TOK_INITIAL & 0xff;
+
+    ptr[2] = (*req_flags >> 24) & 0xff;
+    ptr[3] = (*req_flags >> 16) & 0xff;
+    ptr[4] = (*req_flags >> 8) & 0xff;
+    ptr[5] = *req_flags & 0xff;
+
+    ptr[6] = (nctypes >> 8) & 0xff;
+    ptr[7] = nctypes & 0xff;
+
+    ptr += 8;
+
+    for (i=0; i<nctypes; i++) {
+       ptr[0] = (ctypes[i] >> 24) & 0xff;
+       ptr[1] = (ctypes[i] >> 16) & 0xff;
+       ptr[2] = (ctypes[i] >> 8) & 0xff;
+       ptr[3] = ctypes[i] & 0xff;
+
+       ptr += 4;
+    }
+
+    if (credmsg.length) {
+       ptr[0] = (KRB5_GSS_FOR_CREDS_OPTION >> 8) & 0xff;
+       ptr[1] = KRB5_GSS_FOR_CREDS_OPTION & 0xff;
+
+       ptr[2] = (credmsg.length >> 8) & 0xff;
+       ptr[3] = credmsg.length & 0xff;
+
+       ptr += 4;
+
+       memcpy(ptr, credmsg.data, credmsg.length);
+
+       ptr += credmsg.length;
+    }
+
+    memset(ptr, 0, 4);
+
+    /* call mk_req.  subkey and ap_req need to be used or destroyed */
+
+    mk_req_flags = AP_OPTS_USE_SUBKEY;
+
+    if (*req_flags & GSS_C_MUTUAL_FLAG)
+       mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED;
+
+    if ((code = krb5_mk_req_extended(context, auth_context, mk_req_flags,
+                                    &cksumdata, out_creds, &ap_req)))
+       goto cleanup;
+
+   /* store the interesting stuff from creds and authent */
+   *endtime = out_creds->times.endtime;
+   *krb_flags = out_creds->ticket_flags;
+
+   /* build up the token */
+
+   /* allocate space for the token */
+   tlen = g_token_size((gss_OID) mech_type,
+                      (cksumdata.length-(2+cblen))+2+ap_req.length);
+
+   if ((t = (unsigned char *) xmalloc(tlen)) == NULL) {
+      code = ENOMEM;
+      goto cleanup;
+   }
+
+   ptr = t;
+
+   g_make_token_header((gss_OID) mech_type,
+                      (cksumdata.length-(2+cblen))+2+ap_req.length,
+                      &ptr, KG2_TOK_INITIAL);
+
+   /* skip over the channel bindings and the token id */
+   memcpy(ptr, cksumdata.data+cblen+2, cksumdata.length-(cblen+2));
+   ptr += cksumdata.length-(cblen+2);
+   ptr[0] = (ap_req.length >> 8) & 0xff;
+   ptr[1] = ap_req.length & 0xff;
+   ptr += 2;
+   memcpy(ptr, ap_req.data, ap_req.length);
+
+   /* pass allocated data back */
+
+   *ret_nctypes = nctypes;
+   *ret_ctypes = ctypes;
+
+   token->length = tlen;
+   token->value = (void *) t;
+
+   code = 0;
+
+cleanup:
+   if (code) {
+       if (*auth_context)
+          krb5_auth_con_free(context, *auth_context);
+   }
+
+   if (ctypes)
+       krb5_free_cksumtypes(context, ctypes);
+   if (out_creds)
+       krb5_free_creds(context, out_creds);
+   krb5_free_cred_contents(context, &in_creds);
+   if (credmsg.data)
+       free(credmsg.data);
+   if (ap_req.data)
+       free(ap_req.data);
+   if (cksumdata.data)
+       free(cksumdata.data);
+
+   return(code);
+}
+
+static krb5_error_code
+make_ap_req_v1(context, auth_context, cred, server, now, endtime,
+              chan_bindings, req_flags, krb_flags, mech_type, token)
     krb5_context context;
     krb5_auth_context * auth_context;
     krb5_gss_cred_id_t cred;
@@ -142,15 +391,16 @@ make_ap_req(context, auth_context, cred, server, now, endtime, chan_bindings,
 
     /* fill in the necessary fields in creds */
     memset((char *) &in_creds, 0, sizeof(krb5_creds));
+
     if ((code = krb5_copy_principal(context, cred->princ, &in_creds.client)))
        goto cleanup;
     if ((code = krb5_copy_principal(context, server, &in_creds.server)))
        goto cleanup;
-    in_creds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
     in_creds.times.endtime = *endtime;
+    in_creds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
     
     /*
-     * Get the credential..., I don't know in 0 is a good value for the
+     * Get the credential..., I don't know if 0 is a good value for the
      * kdcoptions
      */
     if ((code = krb5_get_credentials(context, 0, cred->ccache, 
@@ -222,10 +472,6 @@ cleanup:
    return (code);
 }
 
-#define IS_KRB_ERROR(dat)\
-       ((dat) && (dat)->length && ((dat)->data[0] == 0x7e ||\
-                                   (dat)->data[0] == 0x5e))
-
 OM_uint32
 krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
                          context_handle, target_name, mech_type,
@@ -253,8 +499,10 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
    krb5_timestamp now;
    krb5_enctype enctype;
    gss_buffer_desc token;
-   int i;
-   int err;
+   int gsskrb5_vers;
+   int i, err;
+   krb5_ui_4 resp_flags, field_length, opt_id;
+   OM_uint32 major_status;
 
    if (GSS_ERROR(kg_get_context(minor_status, &context)))
       return(GSS_S_FAILURE);
@@ -289,15 +537,33 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
 
    err = 0;
    if (mech_type == GSS_C_NULL_OID) {
-       mech_type = cred->rfc_mech?gss_mech_krb5:gss_mech_krb5_old;
+       if (cred->rfcv2_mech) {
+          mech_type = gss_mech_krb5_v2;
+          gsskrb5_vers = 2000;
+       } else if (cred->rfc_mech) {
+          mech_type = gss_mech_krb5;
+          gsskrb5_vers = 1000;
+       } else if (cred->prerfc_mech) {
+          mech_type = gss_mech_krb5_old;
+          gsskrb5_vers = 1000;
+       } else {
+          err = 1;
+       }
+   } else if (g_OID_equal(mech_type, gss_mech_krb5_v2)) {
+       if (!cred->rfcv2_mech)
+          err = 1;
+       gsskrb5_vers = 2000;
    } else if (g_OID_equal(mech_type, gss_mech_krb5)) {
        if (!cred->rfc_mech)
           err = 1;
+       gsskrb5_vers = 1000;
    } else if (g_OID_equal(mech_type, gss_mech_krb5_old)) {
        if (!cred->prerfc_mech)
           err = 1;
-   } else
+       gsskrb5_vers = 1000;
+   } else {
        err = 1;
+   }
    
    if (err) {
       *minor_status = 0;
@@ -351,6 +617,9 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
       ctx->seed_init = 0;
       ctx->big_endian = 0;  /* all initiators do little-endian, as per spec */
       ctx->seqstate = 0;
+      ctx->gsskrb5_version = gsskrb5_vers;
+      ctx->nctypes = 0;
+      ctx->ctypes = 0;
 
       if ((code = krb5_timeofday(context, &now))) {
        free(ctx);
@@ -377,59 +646,94 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
         return(GSS_S_FAILURE);
       }
 
-      if ((code = make_ap_req(context, &(ctx->auth_context), cred, 
-                             ctx->there, now, &ctx->endtime,
-                             input_chan_bindings, 
-                             &ctx->gss_flags, &ctx->krb_flags, mech_type,
-                             &token))) {
-        krb5_free_principal(context, ctx->here);
-        krb5_free_principal(context, ctx->there);
-        xfree(ctx);
-        *minor_status = code;
-
-        if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) ||
-            (code == KG_EMPTY_CCACHE))
-            return GSS_S_NO_CRED;
-        if (code == KRB5KRB_AP_ERR_TKT_EXPIRED)
-                return GSS_S_CREDENTIALS_EXPIRED;
-        return(GSS_S_FAILURE);
-      }
-
-      krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &ctx->seq_send);
-      krb5_auth_con_getlocalsubkey(context, ctx->auth_context, &ctx->subkey);
-
-      /* fill in the encryption descriptors */
-
-      switch(ctx->subkey->enctype) {
-      case ENCTYPE_DES_CBC_MD5:
-      case ENCTYPE_DES_CBC_CRC:
-         enctype = ENCTYPE_DES_CBC_RAW;
-         ctx->signalg = 0;
-         ctx->cksum_size = 8;
-         ctx->sealalg = 0;
-         break;
+      if (ctx->gsskrb5_version == 2000) {
+         /* gsskrb5 v2 */
+
+         ctx->gss_flags & ~GSS_C_DELEG_FLAG;
+
+         if ((code = make_ap_req_v2(context, &(ctx->auth_context), cred, 
+                                    ctx->there, now, &ctx->endtime,
+                                    input_chan_bindings, 
+                                    &ctx->gss_flags, &ctx->krb_flags,
+                                    mech_type, &ctx->nctypes, &ctx->ctypes,
+                                    &token))) {
+             krb5_free_principal(context, ctx->here);
+             krb5_free_principal(context, ctx->there);
+             xfree(ctx);
+             *minor_status = code;
+
+             if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) ||
+                 (code == KG_EMPTY_CCACHE))
+                 return GSS_S_NO_CRED;
+             if (code == KRB5KRB_AP_ERR_TKT_EXPIRED)
+                 return GSS_S_CREDENTIALS_EXPIRED;
+             return(GSS_S_FAILURE);
+         }
+
+         krb5_auth_con_getlocalseqnumber(context, ctx->auth_context,
+                                         &ctx->seq_send);
+         krb5_auth_con_getlocalsubkey(context, ctx->auth_context,
+                                      &ctx->subkey);
+      } else {
+         /* gsskrb5 v1 */
+
+         if ((code = make_ap_req_v1(context, &(ctx->auth_context), cred, 
+                                    ctx->there, now, &ctx->endtime,
+                                    input_chan_bindings, 
+                                    &ctx->gss_flags, &ctx->krb_flags,
+                                    mech_type,
+                                    &token))) {
+             krb5_free_principal(context, ctx->here);
+             krb5_free_principal(context, ctx->there);
+             xfree(ctx);
+             *minor_status = code;
+
+             if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) ||
+                 (code == KG_EMPTY_CCACHE))
+                 return GSS_S_NO_CRED;
+             if (code == KRB5KRB_AP_ERR_TKT_EXPIRED)
+                 return GSS_S_CREDENTIALS_EXPIRED;
+             return(GSS_S_FAILURE);
+         }
+
+         krb5_auth_con_getlocalseqnumber(context, ctx->auth_context,
+                                         &ctx->seq_send);
+         krb5_auth_con_getlocalsubkey(context, ctx->auth_context,
+                                      &ctx->subkey);
+
+         /* fill in the encryption descriptors */
+
+         switch(ctx->subkey->enctype) {
+         case ENCTYPE_DES_CBC_MD5:
+         case ENCTYPE_DES_CBC_CRC:
+             enctype = ENCTYPE_DES_CBC_RAW;
+             ctx->signalg = 0;
+             ctx->cksum_size = 8;
+             ctx->sealalg = 0;
+             break;
 #if 0
-      case ENCTYPE_DES3_CBC_MD5:
-         enctype = ENCTYPE_DES3_CBC_RAW;
-         ctx->signalg = 3;
-         ctx->cksum_size = 16;
-         ctx->sealalg = 1;
-         break;
+         case ENCTYPE_DES3_CBC_MD5:
+             enctype = ENCTYPE_DES3_CBC_RAW;
+             ctx->signalg = 3;
+             ctx->cksum_size = 16;
+             ctx->sealalg = 1;
+             break;
 #endif
-      default:
-         return GSS_S_FAILURE;
-      }
+         default:
+             return GSS_S_FAILURE;
+         }
 
-      /* the encryption key is the session key XOR 0xf0f0f0f0f0f0f0f0 */
+         /* the encryption key is the session key XOR 0xf0f0f0f0f0f0f0f0 */
 
-      if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc)))
-        return(code);
-      for (i=0; i<ctx->enc->length; i++)
-        /*SUPPRESS 113*/
-        ctx->enc->contents[i] ^= 0xf0;
+         if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc)))
+             return(code);
+         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)))
-         return(code);
+         if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq)))
+             return(code);
+      }
 
       /* at this point, the context is constructed and valid,
         hence, releaseable */
@@ -489,7 +793,7 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
    } else {
       unsigned char *ptr;
       char *sptr;
-      krb5_data ap_rep;
+      krb5_data ap_rep, mic;
       krb5_ap_rep_enc_part *ap_rep_data;
       krb5_error *krb_error;
 
@@ -508,11 +812,8 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
       if ((ctx->established) ||
          (((gss_cred_id_t) cred) != claimant_cred_handle) ||
          ((ctx->gss_flags & GSS_C_MUTUAL_FLAG) == 0)) {
-        (void)krb5_gss_delete_sec_context(minor_status, 
-                                          context_handle, NULL);
-        /* XXX this minor status is wrong if an arg was changed */
-        *minor_status = KG_CONTEXT_ESTABLISHED;
-        return(GSS_S_FAILURE);
+         code = KG_CONTEXT_ESTABLISHED;
+         goto fail;
       }
 
       if (! krb5_principal_compare(context, ctx->there, 
@@ -534,47 +835,104 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
 
       ptr = (unsigned char *) input_token->value;
 
-      if ((err = g_verify_token_header((gss_OID) mech_type, &(ap_rep.length),
-                                      &ptr, KG_TOK_CTX_AP_REP,
-                                      input_token->length))) {
+      if (ctx->gsskrb5_version == 2000) {
+         int token_length;
+         int nctypes;
+         krb5_cksumtype *ctypes;
+
+         /* gsskrb5 v2 */
+
+         if ((err = g_verify_token_header((gss_OID) mech_type,
+                                          &token_length,
+                                          &ptr, KG2_TOK_RESPONSE,
+                                          input_token->length))) {
+             (void)krb5_gss_delete_sec_context(minor_status, 
+                                               context_handle, NULL);
+             *minor_status = err;
+             return(GSS_S_DEFECTIVE_TOKEN);
+         }
+
+         if (GSS_ERROR(major_status =
+                       kg2_parse_token(minor_status, ptr, token_length,
+                                       &resp_flags, &nctypes, &ctypes,
+                                       0, NULL, &ap_rep, &mic))) {
+             (void)krb5_gss_delete_sec_context(minor_status, 
+                                               context_handle, NULL);
+             return(major_status);
+         }
+
+         kg2_intersect_ctypes(&ctx->nctypes, ctx->ctypes, nctypes, ctypes);
+
+         if (ctx->nctypes == 0) {
+             code = KG_NO_CTYPES;
+             goto fail;
+         }
+
+         if (resp_flags & KG2_RESP_FLAG_ERROR) {
+             if (code = krb5_rd_error(context, &ap_rep, &krb_error))
+                 goto fail;
+
+             if (krb_error->error)
+                 code = krb_error->error + ERROR_TABLE_BASE_krb5;
+             else
+                 code = 0;
+
+             krb5_free_error(context, krb_error);
+
+             goto fail;
+         }
+
+         if (resp_flags & KG2_RESP_FLAG_DELEG_OK)
+             ctx->gss_flags |= GSS_C_DELEG_FLAG;
+
+         /* drop through to ap_rep handling */
+      } else {
+         /* gsskrb5 v1 */
+
+         if ((err = g_verify_token_header((gss_OID) mech_type,
+                                          &(ap_rep.length),
+                                          &ptr, KG_TOK_CTX_AP_REP,
+                                          input_token->length))) {
              if (g_verify_token_header((gss_OID) mech_type, &(ap_rep.length),
                                        &ptr, KG_TOK_CTX_ERROR,
                                        input_token->length) == 0) {
 
-                     /* Handle a KRB_ERROR message from the server */
+                 /* Handle a KRB_ERROR message from the server */
 
-                     sptr = (char *) ptr;           /* PC compiler bug */
-                     TREAD_STR(sptr, ap_rep.data, ap_rep.length);
+                 sptr = (char *) ptr;           /* PC compiler bug */
+                 TREAD_STR(sptr, ap_rep.data, ap_rep.length);
                      
-                     code = krb5_rd_error(context, &ap_rep, &krb_error);
-                     if (code)
-                             goto fail;
-                     if (krb_error->error)
-                             code = krb_error->error + ERROR_TABLE_BASE_krb5;
-                     else
-                             code = 0;
-                     krb5_free_error(context, krb_error);
+                 code = krb5_rd_error(context, &ap_rep, &krb_error);
+                 if (code)
                      goto fail;
+                 if (krb_error->error)
+                     code = krb_error->error + ERROR_TABLE_BASE_krb5;
+                 else
+                     code = 0;
+                 krb5_free_error(context, krb_error);
+                 goto fail;
              } else {
-                     *minor_status = err;
-                     return(GSS_S_DEFECTIVE_TOKEN);
+                 *minor_status = 0;
+                 return(GSS_S_DEFECTIVE_TOKEN);
              }
-      }
+         }
 
-      sptr = (char *) ptr;                      /* PC compiler bug */
-      TREAD_STR(sptr, ap_rep.data, ap_rep.length);
+         sptr = (char *) ptr;                      /* PC compiler bug */
+         TREAD_STR(sptr, ap_rep.data, ap_rep.length);
+      }
 
       /* decode the ap_rep */
-      if ((code = krb5_rd_rep(context,ctx->auth_context,&ap_rep,
+      if ((code = krb5_rd_rep(context, ctx->auth_context, &ap_rep,
                              &ap_rep_data))) {
-             /*
-              * XXX A hack for backwards compatiblity.
-              * To be removed in 1999 -- proven 
-              */
-             krb5_auth_con_setuseruserkey(context,ctx->auth_context,ctx->subkey);
-             if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep,
-                            &ap_rep_data)))
-                     goto fail;
+         /*
+          * XXX A hack for backwards compatiblity.
+          * To be removed in 1999 -- proven 
+          */
+         krb5_auth_con_setuseruserkey(context, ctx->auth_context,
+                                      ctx->subkey);
+         if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep,
+                          &ap_rep_data)))
+             goto fail;
       }
 
       /* store away the sequence number */
@@ -589,6 +947,26 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
       /* set established */
       ctx->established = 1;
 
+      if (ctx->gsskrb5_version == 2000) {
+         gss_buffer_desc mic_data, mic_token;
+
+         /* start with the token id */
+         mic_data.value = ptr-2;
+         /* end before the ap-rep length */
+         mic_data.length = ((char*)(ap_rep.data-2)-(char*)(ptr-2));
+
+         mic_token.length = mic.length;
+         mic_token.value = mic.data;
+
+         if (GSS_ERROR(major_status = 
+                       krb5_gss_verify_mic(minor_status, *context_handle,
+                                           &mic_data, &mic_token, NULL))) {
+             (void)krb5_gss_delete_sec_context(minor_status, 
+                                               context_handle, NULL);
+             return(major_status);
+         }
+      }
+
       /* set returns */
 
       if (time_rec) {
@@ -598,7 +976,7 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
       }
 
       if (ret_flags)
-        *ret_flags = KG_IMPLFLAGS(req_flags);
+        *ret_flags = ctx->gss_flags;
 
       if (actual_mech_type)
         *actual_mech_type = mech_type;
@@ -612,8 +990,8 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
    return(GSS_S_COMPLETE);
 
 fail:
-   (void)krb5_gss_delete_sec_context(minor_status, 
-                                    (gss_ctx_id_t) ctx, NULL);
+   (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL);
+
    *minor_status = code;
    return(GSS_S_FAILURE);
 }
index ee5d436c19bafb798cc5848693b372c7614911fd..e1963c4c1edf2948e0ccac457e919932650422e0 100644 (file)
@@ -39,6 +39,7 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret,
    krb5_deltat lifetime;
    krb5_principal ret_name;
    gss_OID_set mechs;
+   OM_uint32 ret;
 
    if (GSS_ERROR(kg_get_context(minor_status, &context)))
       return(GSS_S_FAILURE);
@@ -84,12 +85,26 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret,
       }
    }
 
-   if (mechanisms)
-      if (! g_copy_OID_set(cred->actual_mechs, &mechs)) {
-        krb5_free_principal(context, ret_name);
-        *minor_status = ENOMEM;
-        return(GSS_S_FAILURE);
-      }
+   if (mechanisms) {
+       if (GSS_ERROR(ret = generic_gss_create_empty_oid_set(minor_status,
+                                                           &mechs)) ||
+          (cred->prerfc_mech &&
+           GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
+                                                          gss_mech_krb5_old,
+                                                          &mechs))) ||
+          (cred->rfc_mech &&
+           GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
+                                                          gss_mech_krb5,
+                                                          &mechs))) ||
+          (cred->rfcv2_mech &&
+           GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
+                                                          gss_mech_krb5_v2,
+                                                          &mechs)))) {
+          krb5_free_principal(context, ret_name);
+          /* *minor_status set above */
+          return(ret);
+       }
+   }
 
    if (name) {
       if (! kg_save_name((gss_name_t) ret_name)) {
index 9c5f4745091206ac5887255b8aaf0f6f0e7b9a1c..01a1994301f106685bce12e115fee5acd56ffdcd 100644 (file)
@@ -43,10 +43,11 @@ krb5_gss_inquire_names_for_mech(minor_status, mechanism, name_types)
      * We only know how to handle our own mechanism.
      */
     if ((mechanism != GSS_C_NULL_OID) &&
+       !g_OID_equal(gss_mech_krb5_v2, mechanism) &&
        !g_OID_equal(gss_mech_krb5, mechanism) &&
        !g_OID_equal(gss_mech_krb5_old, mechanism)) {
        *minor_status = 0;
-       return(GSS_S_FAILURE);
+       return(GSS_S_BAD_MECH);
     }
 
     /* We're okay.  Create an empty OID set */
index 1194a4d088814d1cb306bd2ec0af5773b80bfa20..d515f3433e735fb858721b1ec242a71677535caf 100644 (file)
 #include "gssapiP_krb5.h"
 
 static krb5_error_code
-make_seal_token(context, enc, seq, seqnum, direction, text, token,
-               signalg, cksum_size, sealalg, encrypt, toktype,
-               bigend, oid)
+make_priv_token_v2 PROTOTYPE((krb5_context context,
+                             krb5_keyblock *subkey,
+                             krb5_int32 *seqnum,
+                             int direction,
+                             gss_buffer_t text,
+                             gss_buffer_t token,
+                             gss_OID oid));
+
+static krb5_error_code
+make_priv_token_v2(context, subkey, seqnum, direction, text, token, oid)
+     krb5_context context;
+     krb5_keyblock *subkey;
+     krb5_int32 *seqnum;
+     int direction;
+     gss_buffer_t text;
+     gss_buffer_t token;
+     gss_OID oid;
+{
+   krb5_data plain;
+   krb5_enc_data cipher;
+   krb5_error_code code;
+   size_t enclen;
+   int tlen;
+   unsigned char *t, *ptr;
+
+   plain.data = 0;
+   cipher.ciphertext.data = 0;
+   t = 0;
+
+   plain.length = 7+text->length;
+   if ((plain.data = (void *) malloc(plain.length)) == NULL) {
+       code = ENOMEM;
+       goto cleanup;
+   }
+
+   plain.data[0] = (*seqnum >> 24) & 0xff;
+   plain.data[1] = (*seqnum >> 16) & 0xff;
+   plain.data[2] = (*seqnum >> 8) & 0xff;
+   plain.data[3] = *seqnum & 0xff;
+
+   plain.data[4] = direction?0:0xff;
+   
+   plain.data[5] = (text->length >> 8) & 0xff;
+   plain.data[6] = text->length & 0xff;
+
+   memcpy(plain.data+7, text->value, text->length);
+
+   if (code = krb5_c_encrypt_length(context, subkey->enctype, 
+                                   plain.length, &enclen))
+       goto cleanup;
+
+   tlen = g_token_size((gss_OID) oid, 2+enclen);
+
+   if ((t = (unsigned char *) xmalloc(tlen)) == NULL)
+      return(ENOMEM);
+
+   ptr = t;
+
+   g_make_token_header((gss_OID) oid, 2+enclen, &ptr,
+                      KG2_TOK_WRAP_PRIV);
+
+   ptr[0] = (enclen >> 8) & 0xff;
+   ptr[1] = enclen & 0xff;
+
+   cipher.ciphertext.length = enclen;
+   cipher.ciphertext.data = ptr+2;
+
+   if (code = krb5_c_encrypt(context, subkey,
+                            KRB5_KEYUSAGE_GSS_TOK_WRAP_PRIV,
+                            0, &plain, &cipher))
+       goto cleanup;
+
+   /* that's it.  return the token */
+
+   (*seqnum)++;
+
+   token->length = tlen;
+   token->value = (void *) t;
+
+   code = 0;
+
+cleanup:
+   if (plain.data)
+       free(plain.data);
+   if (t)
+       free(t);
+
+   return(code);
+}
+
+static krb5_error_code
+make_integ_token_v2 PROTOTYPE((krb5_context context,
+                              krb5_keyblock *subkey,
+                              krb5_cksumtype ctype,
+                              krb5_int32 *seqnum,
+                              int direction,
+                              gss_buffer_t text,
+                              gss_buffer_t token,
+                              int toktype,
+                              gss_OID oid));
+
+static krb5_error_code
+make_integ_token_v2(context, subkey, ctype, seqnum, direction, text, token, 
+                   toktype, oid)
+     krb5_context context;
+     krb5_keyblock *subkey;
+     krb5_cksumtype ctype;
+     krb5_int32 *seqnum;
+     int direction;
+     gss_buffer_t text;
+     gss_buffer_t token;
+     int toktype;
+     gss_OID oid;
+{
+    krb5_error_code code;
+    int tmp, tlen;
+    unsigned char *t, *ptr;
+    krb5_data plain;
+    krb5_checksum cksum;
+
+    plain.data = 0;
+    t = 0;
+    cksum.contents = 0;
+
+    /* assemble the checksum buffer and compute the checksum */
+
+    plain.length = 7+text->length;
+
+    if ((plain.data = (char *) malloc(plain.length)) == NULL)
+       goto cleanup;
+
+    plain.data[0] = (*seqnum >> 24) & 0xff;
+    plain.data[1] = (*seqnum >> 16) & 0xff;
+    plain.data[2] = (*seqnum >> 8) & 0xff;
+    plain.data[3] = *seqnum & 0xff;
+
+    plain.data[4] = direction?0:0xff;
+
+    plain.data[5] = (text->length >> 8) & 0xff;
+    plain.data[6] = text->length & 0xff;
+
+    memcpy(plain.data+7, text->value, text->length);
+
+    if (code = krb5_c_make_checksum(context, ctype, subkey,
+                                   (toktype == KG2_TOK_WRAP_INTEG)?
+                                   KRB5_KEYUSAGE_GSS_TOK_WRAP_INTEG:
+                                   KRB5_KEYUSAGE_GSS_TOK_MIC,
+                                   &plain, &cksum))
+       goto cleanup;
+
+    /* assemble the token itself */
+
+    if (toktype == KG2_TOK_WRAP_INTEG)
+       tmp = 4+(7+text->length)+2+cksum.length;
+    else
+       tmp = 4+(5)+2+cksum.length;
+
+    tlen = g_token_size((gss_OID) oid, tmp);
+
+    if ((t = (unsigned char *) xmalloc(tlen)) == NULL)
+       return(ENOMEM);
+
+    ptr = t;
+
+    g_make_token_header((gss_OID) oid, tmp, &ptr, toktype);
+
+    ptr[0] = (ctype >> 24) & 0xff;
+    ptr[1] = (ctype >> 16) & 0xff;
+    ptr[2] = (ctype >> 8) & 0xff;
+    ptr[3] = ctype & 0xff;
+
+    ptr += 4;
+
+    if (toktype == KG2_TOK_WRAP_INTEG) {
+       memcpy(ptr, plain.data, 7+text->length);
+       ptr += 7+text->length;
+    } else {
+       memcpy(ptr, plain.data, 5);
+       ptr += 5;
+    }
+
+    ptr[0] = (cksum.length >> 8) & 0xff;
+    ptr[1] = cksum.length & 0xff;
+    ptr += 2;
+
+    memcpy(ptr, cksum.contents, cksum.length);
+
+    /* that's it.  return the token */
+
+    (*seqnum)++;
+
+    token->length = tlen;
+    token->value = (void *) t;
+
+    code = 0;
+
+cleanup:
+    if (plain.data)
+       free(plain.data);
+    if (cksum.contents)
+       krb5_free_checksum_contents(context, &cksum);
+    if (t)
+       free(t);
+
+   return(code);
+}
+
+static krb5_error_code
+make_seal_token_v1 PROTOTYPE((krb5_context context,
+                             krb5_keyblock *enc,
+                             krb5_keyblock *seq,
+                             krb5_int32 *seqnum,
+                             int direction,
+                             gss_buffer_t text,
+                             gss_buffer_t token,
+                             int signalg,
+                             int cksum_size,
+                             int sealalg,
+                             int encrypt,
+                             int toktype,
+                             int bigend,
+                             gss_OID oid));
+
+static krb5_error_code
+make_seal_token_v1(context, enc, seq, seqnum, direction, text, token,
+                  signalg, cksum_size, sealalg, encrypt, toktype,
+                  bigend, oid)
      krb5_context context;
      krb5_keyblock *enc;
      krb5_keyblock *seq;
@@ -329,17 +553,42 @@ kg_seal(context, minor_status, context_handle, conf_req_flag, qop_req,
       return(GSS_S_FAILURE);
    }
 
-   if ((code = make_seal_token(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))) {
+   if (ctx->gsskrb5_version == 2000) {
+       if (toktype == KG_TOK_WRAP_MSG) {
+          if (conf_req_flag)
+              toktype = KG2_TOK_WRAP_PRIV;
+          else
+              toktype = KG2_TOK_WRAP_INTEG;
+       } else {
+          toktype = KG2_TOK_MIC;
+       }
+
+       if (conf_req_flag) {
+          code = make_priv_token_v2(context, ctx->subkey, &ctx->seq_send,
+                                    ctx->initiate, input_message_buffer,
+                                    output_message_buffer, ctx->mech_used);
+       } else {
+          code = make_integ_token_v2(context, ctx->subkey, ctx->ctypes[0],
+                                     &ctx->seq_send, ctx->initiate,
+                                     input_message_buffer,
+                                     output_message_buffer, toktype,
+                                     ctx->mech_used);
+       }
+   } else {
+       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);
+   }
+
+   if (code) {
       *minor_status = code;
       return(GSS_S_FAILURE);
    }
 
-   if ((toktype == KG_TOK_SEAL_MSG) && conf_state)
+   if (conf_state)
       *conf_state = conf_req_flag;
 
    *minor_status = 0;
index e60348fced660542a5bc8e73ec1a71c10322eaa5..2bfd3d76111c651a108685c6412476603a649478 100644 (file)
  * $Id$
  */
 
+static OM_uint32
+kg2_verify_mic(context, minor_status, ctx, ptr, bodysize,
+              text, qop_state)
+     krb5_context context;
+     OM_uint32 *minor_status;
+     krb5_gss_ctx_id_rec *ctx;
+     unsigned char *ptr;
+     int bodysize;
+     gss_buffer_t text;
+     gss_qop_t *qop_state;
+{
+    size_t cksumlen;
+    krb5_error_code code;
+    krb5_data plain;
+    krb5_cksumtype tctype;
+    krb5_ui_4 tseqnum;
+    int tdirection;
+    krb5_checksum cksum;
+    krb5_boolean ckvalid;
+    krb5_timestamp now;
+    OM_uint32 retval;
+
+    plain.data = 0;
+    cksum.contents = 0;
+
+    /* verify the header */
+
+    if (bodysize < 11) {
+       free(plain.data);
+       *minor_status = G_TOK_TRUNC;
+       return(GSS_S_DEFECTIVE_TOKEN);
+    }
+
+    /* allocate the checksum buffer */
+
+    plain.length = 7+text->length;
+
+    if ((plain.data = (char *) malloc(plain.length)) == NULL) {
+       *minor_status = ENOMEM;
+       return(GSS_S_FAILURE);
+    }
+
+    /* suck out the body parts from the token */
+
+    tctype = (krb5_cksumtype) ((ptr[0]<<24) | (ptr[1]<<16) |
+                              (ptr[2]<<8) | ptr[3]);
+    ptr += 4;
+
+    memcpy(plain.data, ptr, 5);
+    tseqnum = ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]);
+    ptr += 4;
+    tdirection = ptr[0];
+    ptr += 1;
+
+    cksum.length = (ptr[0]<<8) | ptr[1];
+    ptr += 2;
+    bodysize -= 11;
+
+    if (cksum.length != bodysize) {
+       free(plain.data);
+       *minor_status = G_TOK_TRUNC;
+       return(GSS_S_DEFECTIVE_TOKEN);
+    }
+
+    cksum.contents = ptr;
+    cksum.checksum_type = tctype;
+
+    /* finish assembling the checksum buffer and compute the checksum */
+
+    plain.data[5] = (text->length >> 8) & 0xff;
+    plain.data[6] = text->length & 0xff;
+
+    memcpy(plain.data+7, text->value, text->length);
+
+    if (code = krb5_c_verify_checksum(context, ctx->subkey,
+                                     KRB5_KEYUSAGE_GSS_TOK_MIC,
+                                     &plain, &cksum, &ckvalid)) {
+       free(plain.data);
+       *minor_status = code;
+       return(GSS_S_FAILURE);
+    }
+
+    if (!ckvalid) {
+       free(plain.data);
+       *minor_status = 0;
+       return(GSS_S_BAD_SIG);
+    }
+
+    /* check context expiry */
+
+   if ((code = krb5_timeofday(context, &now))) {
+       free(plain.data);
+       *minor_status = code;
+       return(GSS_S_FAILURE);
+   }
+
+   if (now > ctx->endtime) {
+       free(plain.data);
+       *minor_status = 0;
+       return(GSS_S_CONTEXT_EXPIRED);
+   }
+
+   /* do sequencing checks */
+
+   if ((ctx->initiate && tdirection != 0xff) ||
+       (!ctx->initiate && tdirection != 0)) {
+       free(plain.data);
+       *minor_status = G_BAD_DIRECTION;
+       return(GSS_S_BAD_SIG);
+   }
+
+   retval = g_order_check(&(ctx->seqstate), tseqnum);
+
+   free(plain.data);
+
+   if (retval) {
+       *minor_status = 0;
+       return(retval);
+   }
+
+   if (qop_state)
+       *qop_state = GSS_C_QOP_DEFAULT;
+
+   *minor_status = 0;
+   return(GSS_S_COMPLETE);
+}
+
+static OM_uint32
+kg2_unwrap_integ(context, minor_status, ctx, ptr, bodysize, output, qop_state)
+     krb5_context context;
+     OM_uint32 *minor_status;
+     krb5_gss_ctx_id_rec *ctx;
+     unsigned char *ptr;
+     int bodysize;
+     gss_buffer_t output;
+     gss_qop_t *qop_state;
+{
+    krb5_error_code code;
+    OM_uint32 retval;
+    krb5_ui_4 tseqnum;
+    int tdirection;
+    int tmsglen;
+    unsigned char *tmsg;
+    krb5_data plain;
+    krb5_checksum tcksum;
+    krb5_boolean ckvalid;
+    krb5_timestamp now;
+
+    output->length = 0;
+    output->value = NULL;
+
+    /* read the body parts out of the message */
+
+    if (bodysize < 11) {
+       *minor_status = G_TOK_TRUNC;
+       return(GSS_S_DEFECTIVE_TOKEN);
+    }
+
+    tcksum.checksum_type = (krb5_cksumtype) ((ptr[0]<<24) | (ptr[1]<<16) |
+                                            (ptr[2]<<8) | ptr[3]);
+    ptr += 4;
+
+    plain.data = ptr;
+
+    tseqnum = ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]);
+    ptr += 4;
+    tdirection = ptr[0];
+    ptr += 1;
+
+    tmsglen = (ptr[0]<<8) | ptr[1];
+    ptr += 2;
+    bodysize -= 11;
+
+    if (bodysize < tmsglen) {
+       *minor_status = G_TOK_TRUNC;
+       return(GSS_S_DEFECTIVE_TOKEN);
+    }
+
+    tmsg = ptr;
+    ptr += tmsglen;
+    bodysize -= tmsglen;
+
+    plain.length = ((char*)ptr) - ((char *)plain.data);
+
+    tcksum.length = (ptr[0]<<8) | ptr[1];
+    ptr += 2;
+
+    if (bodysize != tcksum.length) {
+       *minor_status = G_TOK_TRUNC;
+       return(GSS_S_DEFECTIVE_TOKEN);
+    }
+
+    tcksum.contents = ptr;
+
+    /* verify the MIC */
+
+    if (code = krb5_c_verify_checksum(context, ctx->subkey,
+                                     KRB5_KEYUSAGE_GSS_TOK_WRAP_INTEG,
+                                     &plain, &tcksum, &ckvalid)) {
+       *minor_status = code;
+       return(GSS_S_FAILURE);
+    }
+
+    if (!ckvalid) {
+       *minor_status = 0;
+       return(GSS_S_BAD_SIG);
+    }
+
+    /* check context expiry */
+
+   if ((code = krb5_timeofday(context, &now))) {
+       *minor_status = code;
+       return(GSS_S_FAILURE);
+   }
+
+   if (now > ctx->endtime) {
+       *minor_status = 0;
+       return(GSS_S_CONTEXT_EXPIRED);
+   }
+
+   /* do sequencing checks */
+
+   if ((ctx->initiate && tdirection != 0xff) ||
+       (!ctx->initiate && tdirection != 0)) {
+       *minor_status = G_BAD_DIRECTION;
+       return(GSS_S_BAD_SIG);
+   }
+
+   if (retval = g_order_check(&(ctx->seqstate), tseqnum)) {
+       *minor_status = 0;
+       return(retval);
+   }
+
+   if ((output->value = (void *) malloc(tmsglen)) == NULL) {
+       *minor_status = ENOMEM;
+       return(GSS_S_FAILURE);
+   }
+
+   memcpy(output->value, tmsg, tmsglen);
+   output->length = tmsglen;
+
+   if (qop_state)
+       *qop_state = GSS_C_QOP_DEFAULT;
+
+   *minor_status = 0;
+   return(GSS_S_COMPLETE);
+}
+
+static OM_uint32
+kg2_unwrap_priv(context, minor_status, ctx, ptr, bodysize, output, qop_state)
+     krb5_context context;
+     OM_uint32 *minor_status;
+     krb5_gss_ctx_id_rec *ctx;
+     unsigned char *ptr;
+     int bodysize;
+     gss_buffer_t output;
+     gss_qop_t *qop_state;
+{
+    krb5_error_code code;
+    OM_uint32 retval;
+    krb5_enc_data cipher;
+    krb5_data plain;
+    krb5_ui_4 tseqnum;
+    int tdirection;
+    int tmsglen;
+    unsigned char *tmsg;
+    krb5_timestamp now;
+
+    output->length = 0;
+    output->value = NULL;
+
+    /* read the body parts out of the message */
+
+    if (bodysize < 2) {
+       *minor_status = G_TOK_TRUNC;
+       return(GSS_S_DEFECTIVE_TOKEN);
+    }
+
+    cipher.ciphertext.length = (ptr[0]<<8) | ptr[1];
+    ptr += 2;
+    bodysize -= 2;
+
+    if (bodysize != cipher.ciphertext.length) {
+       *minor_status = G_TOK_TRUNC;
+       return(GSS_S_DEFECTIVE_TOKEN);
+    }
+
+    cipher.ciphertext.data = ptr;
+    cipher.enctype = ENCTYPE_UNKNOWN;
+
+    plain.length = cipher.ciphertext.length;
+    if ((plain.data = (char *) malloc(plain.length)) == NULL) {
+       *minor_status = 0;
+       return(GSS_S_FAILURE);
+    }
+
+    /* decrypt (and implicitly verify) the encrypted data */
+
+    if (code = krb5_c_decrypt(context, ctx->subkey,
+                             KRB5_KEYUSAGE_GSS_TOK_WRAP_PRIV,
+                             0, &cipher, &plain)) {
+       *minor_status = code;
+       return(GSS_S_FAILURE);
+    }
+
+    /* parse out the encrypted fields */
+
+    ptr = plain.data;
+    bodysize = plain.length;
+
+    if (bodysize < 7) {
+       *minor_status = G_TOK_TRUNC;
+       return(GSS_S_DEFECTIVE_TOKEN);
+    }
+
+    tseqnum = ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]);
+    ptr += 4;
+    tdirection = ptr[0];
+    ptr += 1;
+
+    tmsglen = (ptr[0]<<8) | ptr[1];
+    ptr += 2;
+    bodysize -= 7;
+
+    if (bodysize != tmsglen) {
+       *minor_status = G_TOK_TRUNC;
+       return(GSS_S_DEFECTIVE_TOKEN);
+    }
+
+    tmsg = ptr;
+
+    /* check context expiry */
+
+   if ((code = krb5_timeofday(context, &now))) {
+       *minor_status = code;
+       return(GSS_S_FAILURE);
+   }
+
+   if (now > ctx->endtime) {
+       *minor_status = 0;
+       return(GSS_S_CONTEXT_EXPIRED);
+   }
+
+   /* do sequencing checks */
+
+   if ((ctx->initiate && tdirection != 0xff) ||
+       (!ctx->initiate && tdirection != 0)) {
+       *minor_status = G_BAD_DIRECTION;
+       return(GSS_S_BAD_SIG);
+   }
+
+   if (retval = g_order_check(&(ctx->seqstate), tseqnum)) {
+       *minor_status = 0;
+       return(retval);
+   }
+
+   if ((output->value = (void *) malloc(tmsglen)) == NULL) {
+       *minor_status = ENOMEM;
+       return(GSS_S_FAILURE);
+   }
+
+   memcpy(output->value, tmsg, tmsglen);
+   output->length = tmsglen;
+
+   if (qop_state)
+       *qop_state = GSS_C_QOP_DEFAULT;
+
+   *minor_status = 0;
+   return(GSS_S_COMPLETE);
+}
+
 /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX
-   conf_state is only valid if SEAL.
-   */
+   conf_state is only valid if SEAL. */
 
 OM_uint32
-kg_unseal(context, minor_status, context_handle, input_token_buffer,
-         message_buffer, conf_state, qop_state, toktype)
+kg_unseal_v1(context, minor_status, ctx, ptr, bodysize, message_buffer,
+            conf_state, qop_state, toktype)
      krb5_context context;
      OM_uint32 *minor_status;
-     gss_ctx_id_t context_handle;
-     gss_buffer_t input_token_buffer;
+     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_gss_ctx_id_rec *ctx;
    krb5_error_code code;
-   int bodysize;
    int tmsglen;
    int conflen = 0;
    int signalg;
    int sealalg;
    gss_buffer_desc token;
-   unsigned char *ptr;
    krb5_checksum cksum;
    krb5_checksum desmac;
    krb5_checksum md5cksum;
@@ -72,32 +440,6 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer,
       message_buffer->value = NULL;
    }
 
-   /* validate the context handle */
-   if (! kg_validate_ctx_id(context_handle)) {
-      *minor_status = (OM_uint32) G_VALIDATE_FAILED;
-      return(GSS_S_NO_CONTEXT);
-   }
-
-   ctx = (krb5_gss_ctx_id_rec *) context_handle;
-
-   if (! ctx->established) {
-      *minor_status = KG_CTX_INCOMPLETE;
-      return(GSS_S_NO_CONTEXT);
-   }
-
-   /* parse the token, leave the data in message_buffer, setting conf_state */
-
-   /* verify the header */
-
-   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))) {
-      *minor_status = err;
-      return(GSS_S_DEFECTIVE_TOKEN);
-   }
-
    /* get the sign and seal algorithms */
 
    signalg = ptr[0] + (ptr[1]<<8);
@@ -423,3 +765,86 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer,
    *minor_status = 0;
    return(retval);
 }
+
+/* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX
+   conf_state is only valid if SEAL. */
+
+OM_uint32
+kg_unseal(context, minor_status, context_handle, input_token_buffer,
+         message_buffer, conf_state, qop_state, toktype)
+     krb5_context context;
+     OM_uint32 *minor_status;
+     gss_ctx_id_t context_handle;
+     gss_buffer_t input_token_buffer;
+     gss_buffer_t message_buffer;
+     int *conf_state;
+     int *qop_state;
+     int toktype;
+{
+   krb5_gss_ctx_id_rec *ctx;
+   unsigned char *ptr;
+   int bodysize;
+   int err;
+   OM_uint32 retval;
+
+   /* validate the context handle */
+   if (! kg_validate_ctx_id(context_handle)) {
+      *minor_status = (OM_uint32) G_VALIDATE_FAILED;
+      return(GSS_S_NO_CONTEXT);
+   }
+
+   ctx = (krb5_gss_ctx_id_rec *) context_handle;
+
+   if (! ctx->established) {
+      *minor_status = KG_CTX_INCOMPLETE;
+      return(GSS_S_NO_CONTEXT);
+   }
+
+   /* parse the token, leave the data in message_buffer, setting conf_state */
+
+   /* verify the header */
+
+   ptr = (unsigned char *) input_token_buffer->value;
+
+   if (ctx->gsskrb5_version == 2000) {
+       if (!(err = g_verify_token_header((gss_OID) ctx->mech_used,
+                                        &bodysize, &ptr, KG2_TOK_MIC,
+                                        input_token_buffer->length))) {
+          return(kg2_verify_mic(context, minor_status, ctx, ptr, bodysize,
+                                message_buffer, qop_state));
+       } else if (!(err = g_verify_token_header((gss_OID) ctx->mech_used,
+                                               &bodysize, &ptr,
+                                               KG2_TOK_WRAP_INTEG,
+                                               input_token_buffer->length))) {
+          if (GSS_ERROR(retval = kg2_unwrap_integ(context, minor_status,
+                                                  ctx, ptr, bodysize,
+                                                  message_buffer, qop_state)))
+              return(retval);
+
+          *conf_state = 0;
+          return(GSS_S_COMPLETE);
+       } else if (!(err = g_verify_token_header((gss_OID) ctx->mech_used,
+                                               &bodysize, &ptr,
+                                               KG2_TOK_WRAP_PRIV,
+                                               input_token_buffer->length))) {
+          if (GSS_ERROR(retval = kg2_unwrap_priv(context, minor_status,
+                                                 ctx, ptr, bodysize,
+                                                 message_buffer, qop_state)))
+              return(retval);
+
+          *conf_state = 1;
+          return(GSS_S_COMPLETE);
+       }
+   } else {
+       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));
+       }
+   }
+
+   *minor_status = err;
+   return(GSS_S_DEFECTIVE_TOKEN);
+}
index f35727e3f1b34d384d427bd59fc3b91e0e373c63..afb2171b865f297a09628dd93047eddd6219a48e 100644 (file)
@@ -63,7 +63,8 @@ krb5_gss_internal_release_oid(minor_status, oid)
      * return GSS_S_CONTINUE_NEEDED for any OIDs it does not recognize.
      */
    
-    if ((*oid != gss_mech_krb5) &&
+    if ((*oid != gss_mech_krb5_v2) &&
+       (*oid != gss_mech_krb5) &&
        (*oid != gss_mech_krb5_old) &&
        (*oid != gss_nt_krb5_name) &&
        (*oid != gss_nt_krb5_principal)) {
index fba8feaa3fba499e1f9a7b409c5aba00e1101823..36a16d426e4249e1c067e1b233bd658654c63c6d 100644 (file)
@@ -230,12 +230,15 @@ kg_ctx_size(kcontext, arg, sizep)
      * krb5_int32      for seq_recv.
      * krb5_int32      for established.
      * krb5_int32      for big_endian.
+     * krb5_int32      for gsskrb5_version.
+     * krb5_int32      for nctypes.
      * krb5_int32      for trailer.
      */
     kret = EINVAL;
     if ((ctx = (krb5_gss_ctx_id_rec *) arg)) {
-       required = 14*sizeof(krb5_int32);
+       required = 16*sizeof(krb5_int32);
        required += sizeof(ctx->seed);
+       required += ctx->nctypes*sizeof(krb5_int32);
 
        kret = 0;
        if (!kret && ctx->here)
@@ -302,6 +305,7 @@ kg_ctx_externalize(kcontext, arg, buffer, lenremain)
     size_t             required;
     krb5_octet         *bp;
     size_t             remain;
+    int                        i;
 
     required = 0;
     bp = *buffer;
@@ -342,6 +346,10 @@ kg_ctx_externalize(kcontext, arg, buffer, lenremain)
                                       &bp, &remain);
            (void) krb5_ser_pack_int32((krb5_int32) ctx->big_endian,
                                       &bp, &remain);
+           (void) krb5_ser_pack_int32((krb5_int32) ctx->gsskrb5_version,
+                                      &bp, &remain);
+           (void) krb5_ser_pack_int32((krb5_int32) ctx->nctypes,
+                                      &bp, &remain);
 
            /* Now dynamic data */
            kret = 0;
@@ -389,6 +397,13 @@ kg_ctx_externalize(kcontext, arg, buffer, lenremain)
                                               KV5M_AUTH_CONTEXT,
                                               (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);
@@ -415,6 +430,7 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain)
     krb5_int32         ibuf;
     krb5_octet         *bp;
     size_t             remain;
+    int                        i;
 
     bp = *buffer;
     remain = *lenremain;
@@ -457,6 +473,10 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain)
            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->gsskrb5_version = (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))) {
@@ -517,6 +537,22 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain)
                                       (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 &&
                !(kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)) &&
diff --git a/src/lib/gssapi/krb5/util_ctxsetup.c b/src/lib/gssapi/krb5/util_ctxsetup.c
new file mode 100644 (file)
index 0000000..3113c6a
--- /dev/null
@@ -0,0 +1,182 @@
+#include "gssapiP_krb5.h"
+
+/* from the token, flags is stored directly. nctypes/ctypes is
+   allocated and returns the length and list of ctypes in the token.
+   noptions/options lists all the options which the caller cares
+   about.  Those which are present in the token are filled in; the
+   order and length are not changed.  If an error is returned, the
+   option list is in an indeterminate state. */
+
+OM_uint32
+kg2_parse_token(minor_status, ptr, token_length, flags, nctypes, ctypes,
+               noptions, options, kmsg, mic)
+     OM_uint32 *minor_status;
+     unsigned char *ptr;
+     int token_length;
+     krb5_ui_4 *flags;
+     int *nctypes; /* OUT */
+     krb5_cksumtype **ctypes; /* OUT */
+     int noptions; 
+     struct kg2_option *options; /* INOUT */
+     krb5_data *kmsg;
+     krb5_data *mic;
+{
+    int field_length, i;
+    int opt_id;
+
+    *ctypes = 0;
+
+    /* read the flags */
+
+    if (token_length < 4)
+       goto defective;
+    *flags = (ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3];
+    ptr += 4;
+    token_length -= 4;
+
+    /* read out the token list */
+
+    if (token_length < 2)
+       goto defective;
+    field_length = (ptr[0]<<8) | ptr[1];
+    ptr += 2;
+    token_length -= 2;
+
+    *nctypes = field_length;
+
+    if (*nctypes == 0) {
+       *minor_status = 0;
+       return(GSS_S_DEFECTIVE_TOKEN);
+    }
+
+    if ((*ctypes = (krb5_cksumtype *)
+        malloc((*nctypes) * sizeof(krb5_cksumtype))) == NULL) {
+       *minor_status = ENOMEM;
+       return(GSS_S_FAILURE);
+    }
+
+    for (i=0; i<field_length; i++) {
+       if (token_length < 4)
+           goto defective;
+
+       (*ctypes)[i] = (krb5_cksumtype) ((ptr[0]<<24) | (ptr[1]<<16) |
+                                     (ptr[2]<<8) | ptr[3]);
+       ptr += 4;
+       token_length -= 4;
+    }
+
+    do {
+       if (token_length < 4)
+           goto defective;
+       opt_id = (ptr[0]<<8) | ptr[1];
+       field_length = (ptr[2]<<8) | ptr[3];
+       ptr += 4;
+       token_length -= 4;
+
+       if (token_length < field_length)
+           goto defective;
+
+       for (i=0; i<noptions; i++) {
+           if (options[i].option_id = opt_id) {
+               options[i].length = field_length;
+               options[i].data = ptr;
+           }
+           break;
+       }
+           
+       ptr += field_length;
+       token_length -= field_length;
+    } while (opt_id);
+
+    if (token_length < 2)
+       goto defective;
+    field_length = (ptr[0]<<8) | ptr[1];
+    ptr += 2;
+    token_length -= 2;
+
+    if (token_length < field_length)
+       goto defective;
+
+    kmsg->length = field_length;
+    kmsg->data = ptr;
+
+    ptr += field_length;
+    token_length -= field_length;
+
+    /* if there's anything left, assume it's a mic.  the mic isn't
+       necessarily present */
+
+    if (mic && token_length) {
+       if (token_length < 2)
+           goto defective;
+       field_length = (ptr[0]<<8) | ptr[1];
+       ptr += 2;
+       token_length -= 2;
+
+       if (token_length < field_length)
+           goto defective;
+
+       mic->length = field_length;
+       mic->data = ptr;
+
+       ptr += field_length;
+       token_length -= field_length;
+    } else if (mic) {
+       mic->length = 0;
+       mic->data = ptr;
+    }
+
+    if (token_length)
+       goto defective;
+
+    return(GSS_S_COMPLETE);
+
+defective:
+    if (*ctypes)
+       free(*ctypes);
+
+    *minor_status = 0;
+    return(GSS_S_DEFECTIVE_TOKEN);
+}
+    
+/* nc1/c1 will be modified to contain the intersection of the
+   two lists. */
+
+void
+kg2_intersect_ctypes(nc1, c1, nc2, c2)
+     int *nc1;
+     krb5_cksumtype *c1;
+     int nc2;
+     const krb5_cksumtype *c2;
+{
+    int i, j, count;
+    krb5_cksumtype tmp;
+
+    count = 0;
+
+    for (i=0; i<*nc1; i++) {
+       /* first, check to make sure that c1[i] isn't a duplicate in c1 */
+       for (j=0; j<i; j++)
+           if (c1[i] == c1[j])
+               break;
+       if (j<i)
+           continue;
+       /* check if c1[i] is in c2.  If it is, keep it by swapping
+          it into c1[count] and incrementing count.  If count < i, then
+          that field has already been looked at and skipped as
+          not intersecting, which is ok. */
+
+       for (j=0; j<nc2; j++)
+           if (c1[i] == c2[j])
+               break;
+       if ((j<nc2) && (count != i)) {
+           tmp = c1[count];
+           c1[count] = c1[i];
+           c1[i] = tmp;
+       }
+       count++;
+    }
+
+    *nc1 = count;
+}
+
index 5638cce35b9cc102e2ea22980ec3f1c35655dec6..4833ef5a8338636f92c0458b6fc0b59081d4d187 100644 (file)
@@ -39,8 +39,7 @@ krb5_gss_wrap_size_limit(minor_status, context_handle, conf_req_flag,
 {
     krb5_context       context;
     krb5_gss_ctx_id_rec        *ctx;
-    OM_uint32          cfsize;
-    OM_uint32          ohlen;
+    krb5_error_code code;
 
     if (GSS_ERROR(kg_get_context(minor_status, &context)))
        return(GSS_S_FAILURE);
@@ -63,19 +62,86 @@ krb5_gss_wrap_size_limit(minor_status, context_handle, conf_req_flag,
        return(GSS_S_NO_CONTEXT);
     }
 
-    /* Calculate the token size and subtract that from the output size */
-    cfsize = (conf_req_flag) ? kg_confounder_size(context, ctx->enc) : 0;
-    ohlen = g_token_size((gss_OID) ctx->mech_used,
-                        (unsigned int) cfsize + ctx->cksum_size + 14);
+    if (ctx->gsskrb5_version == 2000) {
+       if (conf_req_flag) {
+           /* this is pretty gross.  take the max output, and call
+              krb5_c_encrypt_length to see how much overhead is added
+              on.  subtract that much, and see if it fits in the
+              requested space.  If not, start subtracting 1 until it
+              does.  This doesn't necessarily give us the optimal
+              packing, but I think that's ok (I could start adding 1
+              until I went over, but that seems like it's not worth
+              the effort).  This is probably O(blocksize), but that's
+              never going to be large. */
 
-    if (ohlen < req_output_size)
+           OM_uint32 headerlen, plainlen;
+           size_t enclen;
+
+           headerlen = g_token_size((gss_OID) ctx->mech_used, 2);
+           plainlen = req_output_size - headerlen;
+
+           if (code = krb5_c_encrypt_length(context, ctx->enc->enctype,
+                                            plainlen, &enclen)) {
+               *minor_status = code;
+               return(GSS_S_FAILURE);
+           }
+
+           plainlen -= plainlen - (enclen - plainlen);
+
+           if (code = krb5_c_encrypt_length(context, ctx->enc->enctype,
+                                            plainlen, &enclen)) {
+               *minor_status = code;
+               return(GSS_S_FAILURE);
+           }
+
+           while (headerlen + enclen > req_output_size) {
+               plainlen--;
+
+               if (code = krb5_c_encrypt_length(context, ctx->enc->enctype,
+                                                plainlen, &enclen)) {
+                   *minor_status = code;
+                   return(GSS_S_FAILURE);
+               }
+           }
+
+           /* subtract off the fixed size inside the encrypted part */
+
+           plainlen -= 7;
+
+           *max_input_size = plainlen;
+       } else {
+           size_t cksumlen;
+           OM_uint32 headerlen;
+
+           if (code = krb5_c_checksum_length(context, ctx->ctypes[0],
+                                             &cksumlen)) {
+               *minor_status = code;
+               return(GSS_S_FAILURE);
+           }
+
+           headerlen = g_token_size((gss_OID) ctx->mech_used, 13 + cksumlen);
+
+           *max_input_size = req_output_size - headerlen;
+       }
+    } else {
+       OM_uint32               cfsize;
+       OM_uint32               ohlen;
+
+       /* Calculate the token size and subtract that from the output size */
+       cfsize = (conf_req_flag) ? kg_confounder_size(context, ctx->enc) : 0;
+       ohlen = g_token_size((gss_OID) ctx->mech_used,
+                            (unsigned int) cfsize + ctx->cksum_size + 14);
+
+       if (ohlen < req_output_size)
            /*
             * Cannot have trailer length that will cause us to pad over
             * our length
             */
            *max_input_size = (req_output_size - ohlen - 1) & (~7);
-    else
+       else
            *max_input_size = 0;
+    }
+
     *minor_status = 0;
     return(GSS_S_COMPLETE);
 }