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