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