Home | History | Annotate | Download | only in mech
      1 /*
      2  * Copyright 2000, 2004  by the Massachusetts Institute of Technology.
      3  * All Rights Reserved.
      4  *
      5  * Export of this software from the United States of America may
      6  *   require a specific license from the United States Government.
      7  *   It is the responsibility of any person or organization contemplating
      8  *   export to obtain such a license before exporting.
      9  *
     10  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
     11  * distribute this software and its documentation for any purpose and
     12  * without fee is hereby granted, provided that the above copyright
     13  * notice appear in all copies and that both that copyright notice and
     14  * this permission notice appear in supporting documentation, and that
     15  * the name of M.I.T. not be used in advertising or publicity pertaining
     16  * to distribution of the software without specific, written prior
     17  * permission.  Furthermore if you modify this software you must label
     18  * your software as modified software and not distribute it in such a
     19  * fashion that it might be confused with the original M.I.T. software.
     20  * M.I.T. makes no representations about the suitability of
     21  * this software for any purpose.  It is provided "as is" without express
     22  * or implied warranty.
     23  *
     24  */
     25 /*
     26  * Copyright 1993 by OpenVision Technologies, Inc.
     27  *
     28  * Permission to use, copy, modify, distribute, and sell this software
     29  * and its documentation for any purpose is hereby granted without fee,
     30  * provided that the above copyright notice appears in all copies and
     31  * that both that copyright notice and this permission notice appear in
     32  * supporting documentation, and that the name of OpenVision not be used
     33  * in advertising or publicity pertaining to distribution of the software
     34  * without specific, written prior permission. OpenVision makes no
     35  * representations about the suitability of this software for any
     36  * purpose.  It is provided "as is" without express or implied warranty.
     37  *
     38  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
     39  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
     40  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
     41  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
     42  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
     43  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
     44  * PERFORMANCE OF THIS SOFTWARE.
     45  */
     46 
     47 /*
     48  * Copyright (C) 1998 by the FundsXpress, INC.
     49  *
     50  * All rights reserved.
     51  *
     52  * Export of this software from the United States of America may require
     53  * a specific license from the United States Government.  It is the
     54  * responsibility of any person or organization contemplating export to
     55  * obtain such a license before exporting.
     56  *
     57  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
     58  * distribute this software and its documentation for any purpose and
     59  * without fee is hereby granted, provided that the above copyright
     60  * notice appear in all copies and that both that copyright notice and
     61  * this permission notice appear in supporting documentation, and that
     62  * the name of FundsXpress. not be used in advertising or publicity pertaining
     63  * to distribution of the software without specific, written prior
     64  * permission.  FundsXpress makes no representations about the suitability of
     65  * this software for any purpose.  It is provided "as is" without express
     66  * or implied warranty.
     67  *
     68  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
     69  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
     70  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     71  */
     72 
     73 /*
     74  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     75  * Use is subject to license terms.
     76  */
     77 
     78 #include "k5-int.h"
     79 #include "gssapiP_krb5.h"
     80 #ifdef HAVE_MEMORY_H
     81 #include <memory.h>
     82 #endif
     83 #include <assert.h>
     84 #include "auth_con.h"
     85 
     86 #ifdef CFX_EXERCISE
     87 #define CFX_ACCEPTOR_SUBKEY (time(0) & 1)
     88 #else
     89 #define CFX_ACCEPTOR_SUBKEY 1
     90 #endif
     91 
     92 /*
     93  * Decode, decrypt and store the forwarded creds in the local ccache.
     94  * and populate the callers delegated credential handle if it
     95  * was provided.
     96  */
     97 static krb5_error_code
     98 rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
     99     krb5_context context;
    100     krb5_auth_context auth_context;
    101     krb5_data *inbuf;
    102     krb5_gss_cred_id_t *out_cred;
    103 {
    104     krb5_creds ** creds = NULL;
    105     krb5_error_code retval;
    106     krb5_ccache ccache = NULL;
    107     krb5_gss_cred_id_t cred = NULL;
    108     krb5_auth_context new_auth_ctx = NULL;
    109 	krb5_int32 flags_org;
    110 
    111     /* Solaris Kerberos */
    112     KRB5_LOG0(KRB5_INFO, "rd_and_store_for_creds() start");
    113 
    114 	if ((retval = krb5_auth_con_getflags(context, auth_context, &flags_org)))
    115 		return retval;
    116 	krb5_auth_con_setflags(context, auth_context,
    117 			       0);
    118 
    119 	/*
    120 	 * By the time krb5_rd_cred is called here (after krb5_rd_req has been
    121 	 * called in krb5_gss_accept_sec_context), the "keyblock" field of
    122 	 * auth_context contains a pointer to the session key, and the
    123 	 * "recv_subkey" field might contain a session subkey.  Either of
    124 	 * these (the "recv_subkey" if it isn't NULL, otherwise the
    125 	 * "keyblock") might have been used to encrypt the encrypted part of
    126 	 * the KRB_CRED message that contains the forwarded credentials.  (The
    127 	 * Java Crypto and Security Implementation from the DSTC in Australia
    128 	 * always uses the session key.  But apparently it never negotiates a
    129 	 * subkey, so this code works fine against a JCSI client.)  Up to the
    130 	 * present, though, GSSAPI clients linked against the MIT code (which
    131 	 * is almost all GSSAPI clients) don't encrypt the KRB_CRED message at
    132 	 * all -- at this level.  So if the first call to krb5_rd_cred fails,
    133 	 * we should call it a second time with another auth context freshly
    134 	 * created by krb5_auth_con_init.  All of its keyblock fields will be
    135 	 * NULL, so krb5_rd_cred will assume that the KRB_CRED message is
    136 	 * unencrypted.  (The MIT code doesn't actually send the KRB_CRED
    137 	 * message in the clear -- the "authenticator" whose "checksum" ends up
    138 	 * containing the KRB_CRED message does get encrypted.)
    139 	 */
    140     /* Solaris Kerberos */
    141     if ((retval = krb5_rd_cred(context, auth_context, inbuf, &creds, NULL))) {
    142 	krb5_enctype enctype = ENCTYPE_NULL;
    143 	/*
    144 	 * If the client is using non-DES enctypes it really ought to
    145 	 * send encrypted KRB-CREDs...
    146 	 */
    147 	if (auth_context->keyblock != NULL)
    148 	    enctype = auth_context->keyblock->enctype;
    149 	switch (enctype) {
    150 	case ENCTYPE_DES_CBC_MD5:
    151 	case ENCTYPE_DES_CBC_CRC:
    152 	case ENCTYPE_DES3_CBC_SHA1:
    153 	    break;
    154 	default:
    155 	    KRB5_LOG(KRB5_ERR, "rd_and_store_for_creds() error "
    156 		    "krb5_rd_cred() retval = %d\n", retval);
    157 	    goto cleanup;
    158 	    /* NOTREACHED */
    159 	    break;
    160 	}
    161 
    162 	/* Try to krb5_rd_cred() likely unencrypted KRB-CRED */
    163 		if ((retval = krb5_auth_con_init(context, &new_auth_ctx)))
    164 			goto cleanup;
    165 		krb5_auth_con_setflags(context, new_auth_ctx, 0);
    166 		if ((retval = krb5_rd_cred(context, new_auth_ctx, inbuf,
    167 					   &creds, NULL))) {
    168 			/* Solaris Kerberos */
    169 			KRB5_LOG(KRB5_ERR, "rd_and_store_for_creds() error "
    170 			    "krb5_rd_cred() retval = %d\n", retval);
    171 			goto cleanup;
    172 		}
    173     }
    174 
    175     if ((retval = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache))) {
    176 	ccache = NULL;
    177         goto cleanup;
    178     }
    179 
    180     if ((retval = krb5_cc_initialize(context, ccache, creds[0]->client))) {
    181 	/* Solaris Kerberos */
    182 	KRB5_LOG(KRB5_ERR, "rd_and_store_for_creds() error "
    183 		"krb5_cc_initialize() retval = %d\n", retval);
    184 	goto cleanup;
    185     }
    186 
    187     if ((retval = krb5_cc_store_cred(context, ccache, creds[0]))) {
    188 	/* Solaris Kerberos */
    189 	KRB5_LOG(KRB5_ERR, "rd_and_store_for_creds() error "
    190 		"krb5_cc_store_cred() retval = %d\n", retval);
    191 	goto cleanup;
    192     }
    193 
    194     /* generate a delegated credential handle */
    195     if (out_cred) {
    196 	/* allocate memory for a cred_t... */
    197 	if (!(cred =
    198 	      (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))) {
    199 	    retval = ENOMEM; /* out of memory? */
    200 	    *out_cred = NULL;
    201 	    goto cleanup;
    202 	}
    203 
    204 	/* zero it out... */
    205 	/* Solaris Kerberos */
    206 	(void) memset(cred, 0, sizeof(krb5_gss_cred_id_rec));
    207 
    208 	retval = k5_mutex_init(&cred->lock);
    209 	if (retval) {
    210 	    xfree(cred);
    211 	    cred = NULL;
    212 	    goto cleanup;
    213 	}
    214 
    215 	/* copy the client principle into it... */
    216 	if ((retval =
    217 	     krb5_copy_principal(context, creds[0]->client, &(cred->princ)))) {
    218 	     /* Solaris Kerberos */
    219 	    KRB5_LOG(KRB5_ERR, "rd_and_store_for_creds() error "
    220 		    "krb5_copy_principal() retval = %d\n", retval);
    221 	    k5_mutex_destroy(&cred->lock);
    222 	    retval = ENOMEM; /* out of memory? */
    223 	    xfree(cred); /* clean up memory on failure */
    224 	    cred = NULL;
    225 	    goto cleanup;
    226 	}
    227 
    228 	cred->usage = GSS_C_INITIATE; /* we can't accept with this */
    229 	/* cred->princ already set */
    230 	cred->prerfc_mech = 1; /* this cred will work with all three mechs */
    231 	cred->rfc_mech = 1;
    232 	cred->keytab = NULL; /* no keytab associated with this... */
    233 	cred->tgt_expire = creds[0]->times.endtime; /* store the end time */
    234 	cred->ccache = ccache; /* the ccache containing the credential */
    235 	ccache = NULL; /* cred takes ownership so don't destroy */
    236     }
    237 
    238     /* If there were errors, there might have been a memory leak
    239        if (!cred)
    240        if ((retval = krb5_cc_close(context, ccache)))
    241        goto cleanup;
    242     */
    243 cleanup:
    244     if (creds)
    245 	krb5_free_tgt_creds(context, creds);
    246 
    247     if (ccache)
    248 	(void)krb5_cc_destroy(context, ccache);
    249 
    250     if (out_cred)
    251 	*out_cred = cred; /* return credential */
    252 
    253     if (new_auth_ctx)
    254 	krb5_auth_con_free(context, new_auth_ctx);
    255 
    256     krb5_auth_con_setflags(context, auth_context, flags_org);
    257 
    258     /* Solaris Kerberos */
    259     KRB5_LOG(KRB5_INFO, "rd_and_store_for_creds() end retval %d", retval);
    260     return retval;
    261 }
    262 
    263 /*
    264  * SUNW15resync
    265  * Most of the logic here left "as is" because of lots of fixes MIT
    266  * does not have yet
    267  */
    268 OM_uint32
    269 krb5_gss_accept_sec_context(minor_status, context_handle,
    270 			    verifier_cred_handle, input_token,
    271 			    input_chan_bindings, src_name, mech_type,
    272 			    output_token, ret_flags, time_rec,
    273 			    delegated_cred_handle)
    274      OM_uint32 *minor_status;
    275      gss_ctx_id_t *context_handle;
    276      gss_cred_id_t verifier_cred_handle;
    277      gss_buffer_t input_token;
    278      gss_channel_bindings_t input_chan_bindings;
    279      gss_name_t *src_name;
    280      gss_OID *mech_type;
    281      gss_buffer_t output_token;
    282      OM_uint32 *ret_flags;
    283      OM_uint32 *time_rec;
    284      gss_cred_id_t *delegated_cred_handle;
    285 {
    286    krb5_context context;
    287    unsigned char *ptr, *ptr2;
    288    krb5_gss_ctx_id_rec *ctx = 0;
    289    char *sptr;
    290    long tmp;
    291    size_t md5len;
    292    int bigend;
    293    krb5_gss_cred_id_t cred = 0;
    294    krb5_data ap_rep, ap_req;
    295    krb5_ap_req *request = NULL;
    296    int i;
    297    krb5_error_code code;
    298    krb5_address addr, *paddr;
    299    krb5_authenticator *authdat = 0;
    300    krb5_checksum reqcksum;
    301    krb5_principal name = NULL;
    302    krb5_ui_4 gss_flags = 0;
    303    krb5_timestamp now;
    304    gss_buffer_desc token;
    305    krb5_auth_context auth_context = NULL;
    306    krb5_ticket * ticket = NULL;
    307    int option_id;
    308    krb5_data option;
    309    const gss_OID_desc *mech_used = NULL;
    310    OM_uint32 major_status = GSS_S_FAILURE;
    311    krb5_error krb_error_data;
    312    krb5_data scratch;
    313    gss_cred_id_t cred_handle = NULL;
    314    krb5_gss_cred_id_t deleg_cred = NULL;
    315    OM_uint32 saved_ap_options = 0;
    316    krb5int_access kaccess;
    317    int cred_rcache = 0;
    318    int no_encap;
    319    OM_uint32 t_minor_status = 0;
    320 
    321    KRB5_LOG0(KRB5_INFO,"krb5_gss_accept_sec_context() start");
    322 
    323    code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
    324    if (code) {
    325        *minor_status = code;
    326        return(GSS_S_FAILURE);
    327    }
    328 
    329    code = krb5_gss_init_context(&context);
    330    if (code) {
    331        *minor_status = code;
    332        return GSS_S_FAILURE;
    333    }
    334 
    335    /* set up returns to be freeable */
    336 
    337    if (src_name)
    338       *src_name = (gss_name_t) NULL;
    339    output_token->length = 0;
    340    output_token->value = NULL;
    341    token.value = 0;
    342    reqcksum.contents = 0;
    343    ap_req.data = 0;
    344    ap_rep.data = 0;
    345 
    346    if (mech_type)
    347       *mech_type = GSS_C_NULL_OID;
    348    /* initialize the delegated cred handle to NO_CREDENTIAL for now */
    349    if (delegated_cred_handle)
    350       *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
    351 
    352    /*
    353     * Context handle must be unspecified.  Actually, it must be
    354     * non-established, but currently, accept_sec_context never returns
    355     * a non-established context handle.
    356     */
    357    /*SUPPRESS 29*/
    358    if (*context_handle != GSS_C_NO_CONTEXT) {
    359       *minor_status = 0;
    360 
    361        /* Solaris kerberos: the original Solaris code returned GSS_S_NO_CONTEXT
    362 	* for this error.  This conflicts somewhat with RFC2743 which states
    363 	* GSS_S_NO_CONTEXT should be returned only for sucessor calls following
    364 	* GSS_S_CONTINUE_NEEDED status returns.  Note the MIT code doesn't
    365 	* return GSS_S_NO_CONTEXT at all.
    366 	*/
    367 
    368       major_status = GSS_S_NO_CONTEXT;
    369       KRB5_LOG0(KRB5_ERR,"krb5_gss_accept_sec_context() "
    370 	      "error GSS_S_NO_CONTEXT");
    371       goto cleanup;
    372    }
    373 
    374    /* verify the token's integrity, and leave the token in ap_req.
    375       figure out which mech oid was used, and save it */
    376 
    377    ptr = (unsigned char *) input_token->value;
    378 
    379    if (!(code = g_verify_token_header(gss_mech_krb5,
    380 				      (uint32_t *)&(ap_req.length),
    381 				      &ptr, KG_TOK_CTX_AP_REQ,
    382 				      input_token->length, 1))) {
    383        mech_used = gss_mech_krb5;
    384    } else if ((code == G_WRONG_MECH) &&
    385 		!(code = g_verify_token_header(gss_mech_krb5_wrong,
    386 						(uint32_t *)&(ap_req.length),
    387 						&ptr, KG_TOK_CTX_AP_REQ,
    388 						input_token->length, 1))) {
    389 	mech_used = gss_mech_krb5_wrong;
    390    } else if ((code == G_WRONG_MECH) &&
    391 	      !(code = g_verify_token_header(gss_mech_krb5_old,
    392 					     (uint32_t *)&(ap_req.length),
    393 					     &ptr, KG_TOK_CTX_AP_REQ,
    394 					     input_token->length, 1))) {
    395        /*
    396 	* Previous versions of this library used the old mech_id
    397 	* and some broken behavior (wrong IV on checksum
    398 	* encryption).  We support the old mech_id for
    399 	* compatibility, and use it to decide when to use the
    400 	* old behavior.
    401 	*/
    402        mech_used = gss_mech_krb5_old;
    403   } else if (code == G_WRONG_TOKID) {
    404 	major_status = GSS_S_CONTINUE_NEEDED;
    405         code = KRB5KRB_AP_ERR_MSG_TYPE;
    406         mech_used = gss_mech_krb5;
    407         goto fail;
    408    } else if (code == G_BAD_TOK_HEADER) {
    409 	/* DCE style not encapsulated */
    410         ap_req.length = input_token->length;
    411         ap_req.data = input_token->value;
    412         mech_used = gss_mech_krb5;
    413         no_encap = 1;
    414    } else {
    415 	major_status = GSS_S_DEFECTIVE_TOKEN;
    416         goto fail;
    417    }
    418 
    419    sptr = (char *) ptr;
    420    TREAD_STR(sptr, ap_req.data, ap_req.length);
    421 
    422    /*
    423     * Solaris Kerberos:
    424     *  We need to decode the request now so that we can get the
    425     *  service principal in order to try and acquire a cred for it.
    426     *  below in the "handle default cred handle" code block.
    427     */
    428    if (!krb5_is_ap_req(&ap_req)) {
    429        code = KRB5KRB_AP_ERR_MSG_TYPE;
    430        goto fail;
    431    }
    432    /* decode the AP-REQ into request */
    433    if ((code = decode_krb5_ap_req(&ap_req, &request))) {
    434        if (code == KRB5_BADMSGTYPE)
    435            code = KRB5KRB_AP_ERR_BADVERSION;
    436        goto fail;
    437    }
    438 
    439    /* handle default cred handle */
    440    /*
    441     * Solaris Kerberos:
    442     * If there is no princ associated with the cred then treat it the
    443     * the same as GSS_C_NO_CREDENTIAL.
    444     */
    445    if (verifier_cred_handle == GSS_C_NO_CREDENTIAL ||
    446     ((krb5_gss_cred_id_t)verifier_cred_handle)->princ == NULL) {
    447        /* Note that we try to acquire a cred for the service principal
    448 	* named in the AP-REQ. This allows us to implement option (ii)
    449 	* of the recommended behaviour for GSS_Accept_sec_context() as
    450 	* described in section 1.1.1.3 of RFC2743.
    451 
    452 	* This is far more useful that option (i), for which we would
    453 	* acquire a cred for GSS_C_NO_NAME.
    454 	*/
    455        /* copy the princ from the ap-req or we'll lose it when we free
    456 	  the ap-req */
    457        krb5_principal princ;
    458        if ((code = krb5_copy_principal(context, request->ticket->server,
    459 				       &princ))) {
    460            KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
    461 	            "krb5_copy_principal() error code %d", code);
    462 	   major_status = GSS_S_FAILURE;
    463 	   goto fail;
    464        }
    465        /* intern the acceptor name */
    466        if (! kg_save_name((gss_name_t) princ)) {
    467 	   code = G_VALIDATE_FAILED;
    468 	   major_status = GSS_S_FAILURE;
    469 	   goto fail;
    470        }
    471        major_status = krb5_gss_acquire_cred((OM_uint32*) &code,
    472 					    (gss_name_t) princ,
    473 					    GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
    474 					    GSS_C_ACCEPT, &cred_handle,
    475 					    NULL, NULL);
    476 
    477        if (major_status != GSS_S_COMPLETE){
    478 
    479 	   /* Solaris kerberos: RFC2743 indicate this should be returned if we
    480 	    * can't aquire a default cred.
    481 	    */
    482 	   KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() "
    483 		  "krb5_gss_acquire_cred() error"
    484 		   "orig major_status = %d, now = GSS_S_NO_CRED\n",
    485 		   major_status);
    486 
    487 	   major_status = GSS_S_NO_CRED;
    488 	   goto fail;
    489        }
    490 
    491    } else {
    492        cred_handle = verifier_cred_handle;
    493    }
    494 
    495    major_status = krb5_gss_validate_cred((OM_uint32*) &code,
    496 						 cred_handle);
    497 
    498    if (GSS_ERROR(major_status)){
    499 
    500        /* Solaris kerberos: RFC2743 indicate GSS_S_NO_CRED should be returned if
    501 	* the supplied cred isn't valid.
    502 	*/
    503 
    504        KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() "
    505 	      "krb5_gss_validate_cred() error"
    506 	       "orig major_status = %d, now = GSS_S_NO_CRED\n",
    507 	       major_status);
    508 
    509        major_status = GSS_S_NO_CRED;
    510        goto fail;
    511    }
    512 
    513    cred = (krb5_gss_cred_id_t) cred_handle;
    514 
    515    /* make sure the supplied credentials are valid for accept */
    516 
    517    if ((cred->usage != GSS_C_ACCEPT) &&
    518        (cred->usage != GSS_C_BOTH)) {
    519        code = 0;
    520       KRB5_LOG0(KRB5_ERR,"krb5_gss_accept_sec_context() "
    521 	      "error GSS_S_NO_CONTEXT");
    522        major_status = GSS_S_NO_CRED;
    523        goto fail;
    524    }
    525 
    526    /* construct the sender_addr */
    527 
    528    if ((input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) &&
    529        (input_chan_bindings->initiator_addrtype == GSS_C_AF_INET)) {
    530        /* XXX is this right? */
    531        addr.addrtype = ADDRTYPE_INET;
    532        addr.length = input_chan_bindings->initiator_address.length;
    533        addr.contents = input_chan_bindings->initiator_address.value;
    534 
    535        paddr = &addr;
    536    } else {
    537        paddr = NULL;
    538    }
    539 
    540    /* verify the AP_REQ message - setup the auth_context and rcache */
    541 
    542    if ((code = krb5_auth_con_init(context, &auth_context))) {
    543        major_status = GSS_S_FAILURE;
    544        /* Solaris Kerberos */
    545        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
    546 	      "krb5_auth_con_init() error code %d", code);
    547        goto fail;
    548    }
    549 
    550    (void) krb5_auth_con_setflags(context, auth_context,
    551                           KRB5_AUTH_CONTEXT_DO_SEQUENCE);
    552 
    553    if (cred->rcache) {
    554        cred_rcache = 1;
    555        if ((code = krb5_auth_con_setrcache(context, auth_context, cred->rcache))) {
    556 	   major_status = GSS_S_FAILURE;
    557            /* Solaris Kerberos */
    558 	   KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
    559 		    "krb5_auth_con_setrcache() error code %d", code);
    560 	   goto fail;
    561        }
    562    }
    563    if ((code = krb5_auth_con_setaddrs(context, auth_context, NULL, paddr))) {
    564        major_status = GSS_S_FAILURE;
    565        /* Solaris Kerberos */
    566        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
    567 	      "krb5_auth_con_setaddrs() error code %d", code);
    568        goto fail;
    569    }
    570 
    571    if ((code = krb5_rd_req_decoded(context, &auth_context, request,
    572 			   cred->princ, cred->keytab, NULL, &ticket))) {
    573       KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
    574 	      "krb5_rd_req() error code %d", code);
    575        if (code == KRB5_KT_KVNONOTFOUND || code == KRB5_KT_NOTFOUND) {
    576            major_status = GSS_S_DEFECTIVE_CREDENTIAL;
    577 	   code = KRB5KRB_AP_ERR_NOKEY;
    578        }
    579        else if (code == KRB5KRB_AP_WRONG_PRINC) {
    580            major_status = GSS_S_NO_CRED;
    581 	   code = KRB5KRB_AP_ERR_NOT_US;
    582        }
    583        else if (code == KRB5KRB_AP_ERR_REPEAT)
    584            major_status = GSS_S_DUPLICATE_TOKEN;
    585        else
    586            major_status = GSS_S_FAILURE;
    587        goto fail;
    588    }
    589    krb5_auth_con_setflags(context, auth_context,
    590 			  KRB5_AUTH_CONTEXT_DO_SEQUENCE);
    591 
    592    krb5_auth_con_getauthenticator(context, auth_context, &authdat);
    593 
    594 #if 0
    595    /* make sure the necessary parts of the authdat are present */
    596 
    597    if ((authdat->authenticator->subkey == NULL) ||
    598        (authdat->ticket->enc_part2 == NULL)) {
    599 	   code = KG_NO_SUBKEY;
    600 	   major_status = GSS_S_FAILURE;
    601 	   goto fail;
    602    }
    603 #endif
    604 
    605    {
    606        /* gss krb5 v1 */
    607 
    608        /* stash this now, for later. */
    609        code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &md5len);
    610        if (code) {
    611 	   /* Solaris Kerberos */
    612 	   KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
    613 		  "krb5_c_checksum_length() error code %d", code);
    614 	   major_status = GSS_S_FAILURE;
    615 	   goto fail;
    616        }
    617 
    618        /* verify that the checksum is correct */
    619 
    620        /*
    621 	 The checksum may be either exactly 24 bytes, in which case
    622 	 no options are specified, or greater than 24 bytes, in which case
    623 	 one or more options are specified. Currently, the only valid
    624 	 option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ).
    625        */
    626 
    627        if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) ||
    628 	   (authdat->checksum->length < 24)) {
    629 	   code = 0;
    630 	   major_status = GSS_S_BAD_BINDINGS;
    631 	   goto fail;
    632        }
    633 
    634        /*
    635 	 "Be liberal in what you accept, and
    636 	 conservative in what you send"
    637 	 -- rfc1123
    638 
    639 	 This code will let this acceptor interoperate with an initiator
    640 	 using little-endian or big-endian integer encoding.
    641        */
    642 
    643        ptr = (unsigned char *) authdat->checksum->contents;
    644        bigend = 0;
    645 
    646        TREAD_INT(ptr, tmp, bigend);
    647 
    648        if (tmp != md5len) {
    649 	   ptr = (unsigned char *) authdat->checksum->contents;
    650 	   bigend = 1;
    651 
    652 	   TREAD_INT(ptr, tmp, bigend);
    653 
    654 	   if (tmp != md5len) {
    655 	       code = KG_BAD_LENGTH;
    656 	       major_status = GSS_S_FAILURE;
    657 	       goto fail;
    658 	   }
    659        }
    660 
    661        /* at this point, bigend is set according to the initiator's
    662 	  byte order */
    663 
    664 
    665        /*
    666           The following section of code attempts to implement the
    667           optional channel binding facility as described in RFC2743.
    668 
    669           Since this facility is optional channel binding may or may
    670           not have been provided by either the client or the server.
    671 
    672           If the server has specified input_chan_bindings equal to
    673           GSS_C_NO_CHANNEL_BINDINGS then we skip the check.  If
    674           the server does provide channel bindings then we compute
    675           a checksum and compare against those provided by the
    676           client.  If the check fails we test the clients checksum
    677           to see whether the client specified GSS_C_NO_CHANNEL_BINDINGS.
    678           If either test succeeds we continue without error.
    679         */
    680        if ((code = kg_checksum_channel_bindings(context,
    681 						input_chan_bindings,
    682 						&reqcksum, bigend))) {
    683 	   /* Solaris Kerberos */
    684 	   KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
    685 		  "kg_checksum_channel_bindings() error code %d", code);
    686 	 major_status = GSS_S_BAD_BINDINGS;
    687 	 goto fail;
    688        }
    689 
    690        TREAD_STR(ptr, ptr2, reqcksum.length);
    691 
    692        if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS ) {
    693            if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
    694                xfree(reqcksum.contents);
    695                reqcksum.contents = 0;
    696 		if ((code = kg_checksum_channel_bindings(context,
    697                                                   GSS_C_NO_CHANNEL_BINDINGS,
    698                                                   &reqcksum, bigend))) {
    699                    major_status = GSS_S_BAD_BINDINGS;
    700                    goto fail;
    701 		}
    702                if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
    703                    code = 0;
    704                    major_status = GSS_S_BAD_BINDINGS;
    705                    goto fail;
    706 		}
    707            }
    708 
    709        }
    710 
    711        TREAD_INT(ptr, gss_flags, bigend);
    712 
    713        /* if the checksum length > 24, there are options to process */
    714 
    715        if(authdat->checksum->length > 24 && (gss_flags & GSS_C_DELEG_FLAG)) {
    716 
    717 	   i = authdat->checksum->length - 24;
    718 
    719 	   if (i >= 4) {
    720 
    721 	       TREAD_INT16(ptr, option_id, bigend);
    722 
    723 	       TREAD_INT16(ptr, option.length, bigend);
    724 
    725 	       i -= 4;
    726 
    727 	       if (i < option.length || option.length < 0) {
    728 		   code = KG_BAD_LENGTH;
    729 		   major_status = GSS_S_FAILURE;
    730 		   goto fail;
    731 	       }
    732 
    733 	       /* have to use ptr2, since option.data is wrong type and
    734 		  macro uses ptr as both lvalue and rvalue */
    735 
    736 	       TREAD_STR(ptr, ptr2, option.length);
    737 	       option.data = (char *) ptr2;
    738 
    739 	       i -= option.length;
    740 
    741 	       if (option_id != KRB5_GSS_FOR_CREDS_OPTION) {
    742 		   major_status = GSS_S_FAILURE;
    743 		   goto fail;
    744 	       }
    745 
    746 		   /* store the delegated credential */
    747 
    748 		   code = rd_and_store_for_creds(context, auth_context, &option,
    749 						 (delegated_cred_handle) ?
    750 						 &deleg_cred : NULL);
    751 		   if (code) {
    752 		       major_status = GSS_S_FAILURE;
    753 		       goto fail;
    754 		   }
    755 
    756 	   } /* if i >= 4 */
    757 	   /* ignore any additional trailing data, for now */
    758 #ifdef CFX_EXERCISE
    759 	   {
    760 	       FILE *f = fopen("/tmp/gsslog", "a");
    761 	       if (f) {
    762 		   fprintf(f,
    763 			   "initial context token with delegation, %d extra bytes\n",
    764 			   i);
    765 		   fclose(f);
    766 	       }
    767 	   }
    768 #endif
    769        } else {
    770 #ifdef CFX_EXERCISE
    771 	   {
    772 	       FILE *f = fopen("/tmp/gsslog", "a");
    773 	       if (f) {
    774 		   if (gss_flags & GSS_C_DELEG_FLAG)
    775 		       fprintf(f,
    776 			       "initial context token, delegation flag but too small\n");
    777 		   else
    778 		       /* no deleg flag, length might still be too big */
    779 		       fprintf(f,
    780 			       "initial context token, %d extra bytes\n",
    781 			       authdat->checksum->length - 24);
    782 		   fclose(f);
    783 	       }
    784 	   }
    785 #endif
    786        }
    787    }
    788 
    789    /* create the ctx struct and start filling it in */
    790 
    791    if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
    792        == NULL) {
    793        code = ENOMEM;
    794        major_status = GSS_S_FAILURE;
    795        goto fail;
    796    }
    797 
    798    memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
    799    ctx->mech_used = (gss_OID) mech_used;
    800    ctx->auth_context = auth_context;
    801    ctx->initiate = 0;
    802    ctx->gss_flags = (GSS_C_TRANS_FLAG |
    803                     ((gss_flags) & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
    804                             GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
    805                             GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG)));
    806    ctx->seed_init = 0;
    807    ctx->big_endian = bigend;
    808    ctx->cred_rcache = cred_rcache;
    809 
    810    /* Intern the ctx pointer so that delete_sec_context works */
    811    if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) {
    812        xfree(ctx);
    813        ctx = 0;
    814 
    815 	/* Solaris Kerberos */
    816        KRB5_LOG0(KRB5_ERR, "krb5_gss_accept_sec_context() "
    817 	      "kg_save_ctx_id() error");
    818        code = G_VALIDATE_FAILED;
    819        major_status = GSS_S_FAILURE;
    820        goto fail;
    821    }
    822 
    823    /* XXX move this into gss_name_t */
    824    if ((code = krb5_merge_authdata(context,
    825 				ticket->enc_part2->authorization_data,
    826 				authdat->authorization_data,
    827 				&ctx->authdata))) {
    828 	major_status = GSS_S_FAILURE;
    829         goto fail;
    830    }
    831 
    832    if ((code = krb5_copy_principal(context, cred->princ, &ctx->here))) {
    833 	/* Solaris Kerberos */
    834        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
    835 	      "krb5_copy_principal() error code %d", code);
    836        major_status = GSS_S_FAILURE;
    837        goto fail;
    838    }
    839 
    840    if ((code = krb5_copy_principal(context, authdat->client, &ctx->there))) {
    841 	/* Solaris Kerberos */
    842        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
    843 	      "krb5_copy_principal() 2 error code %d", code);
    844        major_status = GSS_S_FAILURE;
    845        goto fail;
    846    }
    847 
    848    if ((code = krb5_auth_con_getrecvsubkey(context, auth_context,
    849 					   &ctx->subkey))) {
    850 	/* Solaris Kerberos */
    851        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
    852 	      "krb5_auth_con_getremotesubkey() error code %d", code);
    853        major_status = GSS_S_FAILURE;
    854        goto fail;
    855    }
    856 
    857    /* use the session key if the subkey isn't present */
    858 
    859    if (ctx->subkey == NULL) {
    860        if ((code = krb5_auth_con_getkey(context, auth_context,
    861 					&ctx->subkey))) {
    862 	/* Solaris Kerberos */
    863 	   KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
    864 		      "krb5_auth_con_getkey() error code %d", code);
    865            *minor_status = (OM_uint32) KRB5KDC_ERR_NULL_KEY;
    866 	   major_status = GSS_S_FAILURE;
    867 	   goto fail;
    868        }
    869    }
    870 
    871    if (ctx->subkey == NULL) {
    872        /* this isn't a very good error, but it's not clear to me this
    873 	  can actually happen */
    874        major_status = GSS_S_FAILURE;
    875        code = KRB5KDC_ERR_NULL_KEY;
    876        goto fail;
    877    }
    878 
    879     /* Solaris Kerberos */
    880    KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() "
    881 	   "ctx->subkey->enctype=%d", ctx->subkey->enctype);
    882 
    883    ctx->proto = 0;
    884    switch(ctx->subkey->enctype) {
    885    case ENCTYPE_DES_CBC_MD5:
    886    case ENCTYPE_DES_CBC_CRC:
    887        ctx->subkey->enctype = ENCTYPE_DES_CBC_RAW;
    888        ctx->signalg = SGN_ALG_DES_MAC_MD5;
    889        ctx->cksum_size = 8;
    890        ctx->sealalg = SEAL_ALG_DES;
    891 
    892        /* fill in the encryption descriptors */
    893 
    894        if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) {
    895 	   major_status = GSS_S_FAILURE;
    896 	   goto fail;
    897        }
    898 
    899        for (i=0; i<ctx->enc->length; i++)
    900 	   /*SUPPRESS 113*/
    901 	   ctx->enc->contents[i] ^= 0xf0;
    902 
    903        goto copy_subkey_to_seq;
    904        break;
    905 
    906    case ENCTYPE_DES3_CBC_SHA1:
    907        ctx->subkey->enctype = ENCTYPE_DES3_CBC_RAW;
    908        ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD;
    909        ctx->cksum_size = 20;
    910        ctx->sealalg = SEAL_ALG_DES3KD;
    911 
    912        /* fill in the encryption descriptors */
    913    copy_subkey:
    914        if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) {
    915 	   major_status = GSS_S_FAILURE;
    916 	   goto fail;
    917        }
    918    copy_subkey_to_seq:
    919        if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq))) {
    920 	   major_status = GSS_S_FAILURE;
    921 	   goto fail;
    922        }
    923        break;
    924 
    925    case ENCTYPE_ARCFOUR_HMAC:
    926        ctx->signalg = SGN_ALG_HMAC_MD5 ;
    927        ctx->cksum_size = 8;
    928        ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ;
    929        goto copy_subkey;
    930 
    931    default:
    932        ctx->signalg = -1;
    933        ctx->sealalg = -1;
    934        ctx->proto = 1;
    935        code = (*kaccess.krb5int_c_mandatory_cksumtype)(context, ctx->subkey->enctype,
    936 					    &ctx->cksumtype);
    937        if (code)
    938 	   goto fail;
    939        code = krb5_c_checksum_length(context, ctx->cksumtype,
    940 		(size_t *)&ctx->cksum_size);
    941        if (code)
    942 	   goto fail;
    943        ctx->have_acceptor_subkey = 0;
    944        goto copy_subkey;
    945    }
    946 
    947     /* Solaris Kerberos */
    948    KRB5_LOG1(KRB5_ERR, "accept_sec_context:  subkey enctype = %d proto = %d",
    949 	ctx->subkey->enctype, ctx->proto);
    950 
    951    ctx->endtime = ticket->enc_part2->times.endtime;
    952    ctx->krb_flags = ticket->enc_part2->flags;
    953 
    954    krb5_free_ticket(context, ticket); /* Done with ticket */
    955 
    956    {
    957        krb5_ui_4 seq_temp;
    958        krb5_auth_con_getremoteseqnumber(context, auth_context,
    959 		(krb5_int32 *)&seq_temp);
    960        ctx->seq_recv = seq_temp;
    961    }
    962 
    963    if ((code = krb5_timeofday(context, &now))) {
    964        major_status = GSS_S_FAILURE;
    965        goto fail;
    966    }
    967 
    968    if (ctx->endtime < now) {
    969        code = 0;
    970        major_status = GSS_S_CREDENTIALS_EXPIRED;
    971        goto fail;
    972    }
    973 
    974    g_order_init(&(ctx->seqstate), ctx->seq_recv,
    975 		(ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
    976 		(ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
    977 
    978    /* at this point, the entire context structure is filled in,
    979       so it can be released.  */
    980 
    981    /* generate an AP_REP if necessary */
    982 
    983    if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
    984        unsigned char * ptr3;
    985        krb5_ui_4 seq_temp;
    986        int cfx_generate_subkey;
    987 
    988        if (ctx->proto == 1)
    989 	   cfx_generate_subkey = CFX_ACCEPTOR_SUBKEY;
    990        else
    991 	   cfx_generate_subkey = 0;
    992 
    993        if (cfx_generate_subkey) {
    994 	   krb5_int32 acflags;
    995 	   code = krb5_auth_con_getflags(context, auth_context, &acflags);
    996 	   if (code == 0) {
    997 	       acflags |= KRB5_AUTH_CONTEXT_USE_SUBKEY;
    998 	       code = krb5_auth_con_setflags(context, auth_context, acflags);
    999 	   }
   1000 	   if (code) {
   1001 	       major_status = GSS_S_FAILURE;
   1002 	       goto fail;
   1003 	   }
   1004        }
   1005 
   1006        if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) {
   1007 	   major_status = GSS_S_FAILURE;
   1008 	   goto fail;
   1009        }
   1010 
   1011        krb5_auth_con_getlocalseqnumber(context, auth_context,
   1012 		(krb5_int32 *)&seq_temp);
   1013        ctx->seq_send = seq_temp & 0xffffffffL;
   1014 
   1015        if (cfx_generate_subkey) {
   1016 	   /* Get the new acceptor subkey.  With the code above, there
   1017 	      should always be one if we make it to this point.  */
   1018 	   code = krb5_auth_con_getsendsubkey(context, auth_context,
   1019 					      &ctx->acceptor_subkey);
   1020 	   if (code != 0) {
   1021 	       major_status = GSS_S_FAILURE;
   1022 	       goto fail;
   1023 	   }
   1024 	   code = (*kaccess.krb5int_c_mandatory_cksumtype)(context,
   1025 						ctx->acceptor_subkey->enctype,
   1026 						&ctx->acceptor_subkey_cksumtype);
   1027 	   if (code) {
   1028 	       major_status = GSS_S_FAILURE;
   1029 	       goto fail;
   1030 	   }
   1031 	   ctx->have_acceptor_subkey = 1;
   1032        }
   1033 
   1034        /* the reply token hasn't been sent yet, but that's ok. */
   1035        ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
   1036        ctx->established = 1;
   1037 
   1038        token.length = g_token_size(mech_used, ap_rep.length);
   1039 
   1040        if ((token.value = (unsigned char *) xmalloc(token.length))
   1041 	   == NULL) {
   1042 	   major_status = GSS_S_FAILURE;
   1043 	   code = ENOMEM;
   1044 	   goto fail;
   1045        }
   1046        ptr3 = token.value;
   1047        g_make_token_header(mech_used, ap_rep.length,
   1048 			   &ptr3, KG_TOK_CTX_AP_REP);
   1049 
   1050        TWRITE_STR(ptr3, ap_rep.data, ap_rep.length);
   1051 
   1052        ctx->established = 1;
   1053 
   1054    } else {
   1055        token.length = 0;
   1056        token.value = NULL;
   1057        ctx->seq_send = ctx->seq_recv;
   1058 
   1059        ctx->established = 1;
   1060    }
   1061 
   1062    /* set the return arguments */
   1063 
   1064    if (src_name) {
   1065        if ((code = krb5_copy_principal(context, ctx->there, &name))) {
   1066 	   major_status = GSS_S_FAILURE;
   1067 	   goto fail;
   1068        }
   1069        /* intern the src_name */
   1070        if (! kg_save_name((gss_name_t) name)) {
   1071 	   code = G_VALIDATE_FAILED;
   1072 	   major_status = GSS_S_FAILURE;
   1073 	   goto fail;
   1074        }
   1075    }
   1076 
   1077    if (mech_type)
   1078       *mech_type = (gss_OID) mech_used;
   1079 
   1080    if (time_rec)
   1081       *time_rec = ctx->endtime - now;
   1082 
   1083    if (ret_flags)
   1084       *ret_flags = ctx->gss_flags;
   1085 
   1086    *context_handle = (gss_ctx_id_t)ctx;
   1087    *output_token = token;
   1088 
   1089    if (src_name)
   1090       *src_name = (gss_name_t) name;
   1091 
   1092    if (delegated_cred_handle && deleg_cred) {
   1093        if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) {
   1094            /* Solaris Kerberos */
   1095 	   KRB5_LOG0(KRB5_ERR, "krb5_gss_accept_sec_context() "
   1096 		      "kg_save_cred_id() error");
   1097 	   major_status = GSS_S_FAILURE;
   1098 	   code = (OM_uint32) G_VALIDATE_FAILED;
   1099 	   goto fail;
   1100        }
   1101 
   1102        *delegated_cred_handle = (gss_cred_id_t) deleg_cred;
   1103    }
   1104 
   1105    /* finally! */
   1106 
   1107    *minor_status = 0;
   1108    major_status = GSS_S_COMPLETE;
   1109 
   1110  fail:
   1111 
   1112    if (authdat)
   1113        krb5_free_authenticator(context, authdat);
   1114    /* The ctx structure has the handle of the auth_context */
   1115    if (auth_context && !ctx) {
   1116        if (cred_rcache)
   1117 	   (void)krb5_auth_con_setrcache(context, auth_context, NULL);
   1118 
   1119        krb5_auth_con_free(context, auth_context);
   1120    }
   1121    if (reqcksum.contents)
   1122        xfree(reqcksum.contents);
   1123    if (ap_rep.data)
   1124        xfree(ap_rep.data);
   1125 
   1126    if (request != NULL) {
   1127 	saved_ap_options = request->ap_options;
   1128 	krb5_free_ap_req(context, request);
   1129 	request = NULL;
   1130    }
   1131 
   1132    if (!GSS_ERROR(major_status) && major_status != GSS_S_CONTINUE_NEEDED) {
   1133 	if (!verifier_cred_handle && cred_handle) {
   1134 		krb5_gss_release_cred(minor_status, &cred_handle);
   1135 	}
   1136 
   1137 	if (ctx)
   1138 	    ctx->k5_context = context;
   1139 
   1140         return(major_status);
   1141    }
   1142 
   1143    /* from here on is the real "fail" code */
   1144 
   1145    if (ctx)
   1146        (void) krb5_gss_delete_sec_context(minor_status,
   1147 					  (gss_ctx_id_t *) &ctx, NULL);
   1148    if (deleg_cred) { /* free memory associated with the deleg credential */
   1149        if (deleg_cred->ccache)
   1150 	   (void)krb5_cc_close(context, deleg_cred->ccache);
   1151        if (deleg_cred->princ)
   1152 	   krb5_free_principal(context, deleg_cred->princ);
   1153        xfree(deleg_cred);
   1154    }
   1155    if (token.value)
   1156        xfree(token.value);
   1157    if (name) {
   1158        (void) kg_delete_name((gss_name_t) name);
   1159        krb5_free_principal(context, name);
   1160    }
   1161 
   1162    *minor_status = code;
   1163 
   1164    if (saved_ap_options & AP_OPTS_MUTUAL_REQUIRED)
   1165 	gss_flags |= GSS_C_MUTUAL_FLAG;
   1166 
   1167    if (cred
   1168        && ((gss_flags & GSS_C_MUTUAL_FLAG)
   1169 	   || (major_status == GSS_S_CONTINUE_NEEDED))) {
   1170        unsigned int tmsglen;
   1171        int toktype;
   1172 
   1173        /*
   1174 	* The client is expecting a response, so we can send an
   1175 	* error token back
   1176 	*/
   1177        memset(&krb_error_data, 0, sizeof(krb_error_data));
   1178 
   1179        /*
   1180         * Solaris Kerberos: We need to remap error conditions for buggy
   1181         * Windows clients if the MS_INTEROP env var has been set.
   1182         */
   1183        if ((code == KRB5KRB_AP_ERR_BAD_INTEGRITY ||
   1184 	  code == KRB5KRB_AP_ERR_NOKEY || code == KRB5KRB_AP_ERR_BADKEYVER)
   1185 	  && krb5_getenv("MS_INTEROP")) {
   1186            code = KRB5KRB_AP_ERR_MODIFIED;
   1187 	   major_status = GSS_S_CONTINUE_NEEDED;
   1188        }
   1189 
   1190 	    /*
   1191 	     * SUNW17PACresync / Solaris Kerberos
   1192 	     * Set e-data to Windows constant.
   1193 	     * (verified by MSFT)
   1194 	     *
   1195 	     * This facilitates the Windows CIFS client clock skew
   1196 	     * recovery feature.
   1197 	     */
   1198        if (code == KRB5KRB_AP_ERR_SKEW && krb5_getenv("MS_INTEROP")) {
   1199 	    char *ms_e_data = "\x30\x05\xa1\x03\x02\x01\x02";
   1200 	    int len = strlen(ms_e_data);
   1201 
   1202 	    krb_error_data.e_data.data = malloc(len);
   1203 	    if (krb_error_data.e_data.data) {
   1204 		    (void) memcpy(krb_error_data.e_data.data, ms_e_data, len);
   1205 		    krb_error_data.e_data.length = len;
   1206 	    }
   1207 	    major_status = GSS_S_CONTINUE_NEEDED;
   1208 	}
   1209 
   1210        code -= ERROR_TABLE_BASE_krb5;
   1211        if (code < 0 || code > 128)
   1212 	   code = 60 /* KRB_ERR_GENERIC */;
   1213 
   1214        krb_error_data.error = code;
   1215        (void) krb5_us_timeofday(context, &krb_error_data.stime,
   1216 				&krb_error_data.susec);
   1217        krb_error_data.server = cred->princ;
   1218 
   1219        code = krb5_mk_error(context, &krb_error_data, &scratch);
   1220        if (code)
   1221            goto cleanup;
   1222 
   1223        tmsglen = scratch.length;
   1224        toktype = KG_TOK_CTX_ERROR;
   1225 
   1226        token.length = g_token_size(mech_used, tmsglen);
   1227        token.value = (unsigned char *) xmalloc(token.length);
   1228        if (!token.value)
   1229 	  goto cleanup;
   1230 
   1231        ptr = token.value;
   1232        g_make_token_header(mech_used, tmsglen, &ptr, toktype);
   1233 
   1234        TWRITE_STR(ptr, scratch.data, scratch.length);
   1235        xfree(scratch.data);
   1236 
   1237        *output_token = token;
   1238    }
   1239 
   1240 cleanup:
   1241    if (!verifier_cred_handle && cred_handle) {
   1242 	krb5_gss_release_cred(&t_minor_status, &cred_handle);
   1243    }
   1244    krb5_free_context(context);
   1245 
   1246    /* Solaris Kerberos */
   1247    KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() end, "
   1248 	      "major_status = %d", major_status);
   1249    return (major_status);
   1250 }
   1251