1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
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 
85 #ifdef CFX_EXERCISE
86 #define CFX_ACCEPTOR_SUBKEY (time(0) & 1)
87 #else
88 #define CFX_ACCEPTOR_SUBKEY 1
89 #endif
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 name = NULL;
301    krb5_ui_4 gss_flags = 0;
302    krb5_timestamp now;
303    gss_buffer_desc token;
304    krb5_auth_context auth_context = NULL;
305    krb5_ticket * ticket = NULL;
306    int option_id;
307    krb5_data option;
308    const gss_OID_desc *mech_used = NULL;
309    OM_uint32 major_status = GSS_S_FAILURE;
310    krb5_error krb_error_data;
311    krb5_data scratch;
312    gss_cred_id_t cred_handle = NULL;
313    krb5_gss_cred_id_t deleg_cred = NULL;
314    OM_uint32 saved_ap_options = 0;
315    krb5int_access kaccess;
316    int cred_rcache = 0;
317    int no_encap;
318    OM_uint32 t_minor_status = 0;
319 
320    KRB5_LOG0(KRB5_INFO,"krb5_gss_accept_sec_context() start");
321 
322    code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
323    if (code) {
324        *minor_status = code;
325        return(GSS_S_FAILURE);
326    }
327 
328    code = krb5_gss_init_context(&context);
329    if (code) {
330        *minor_status = code;
331        return GSS_S_FAILURE;
332    }
333 
334    /* set up returns to be freeable */
335 
336    if (src_name)
337       *src_name = (gss_name_t) NULL;
338    output_token->length = 0;
339    output_token->value = NULL;
340    token.value = 0;
341    reqcksum.contents = 0;
342    ap_req.data = 0;
343    ap_rep.data = 0;
344 
345    if (mech_type)
346       *mech_type = GSS_C_NULL_OID;
347    /* initialize the delegated cred handle to NO_CREDENTIAL for now */
348    if (delegated_cred_handle)
349       *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
350 
351    /*
352     * Context handle must be unspecified.  Actually, it must be
353     * non-established, but currently, accept_sec_context never returns
354     * a non-established context handle.
355     */
356    /*SUPPRESS 29*/
357    if (*context_handle != GSS_C_NO_CONTEXT) {
358       *minor_status = 0;
359 
360        /* Solaris kerberos: the original Solaris code returned GSS_S_NO_CONTEXT
361 	* for this error.  This conflicts somewhat with RFC2743 which states
362 	* GSS_S_NO_CONTEXT should be returned only for sucessor calls following
363 	* GSS_S_CONTINUE_NEEDED status returns.  Note the MIT code doesn't
364 	* return GSS_S_NO_CONTEXT at all.
365 	*/
366 
367       major_status = GSS_S_NO_CONTEXT;
368       KRB5_LOG0(KRB5_ERR,"krb5_gss_accept_sec_context() "
369 	      "error GSS_S_NO_CONTEXT");
370       goto cleanup;
371    }
372 
373    /* verify the token's integrity, and leave the token in ap_req.
374       figure out which mech oid was used, and save it */
375 
376    ptr = (unsigned char *) input_token->value;
377 
378    if (!(code = g_verify_token_header(gss_mech_krb5,
379 				      (uint32_t *)&(ap_req.length),
380 				      &ptr, KG_TOK_CTX_AP_REQ,
381 				      input_token->length, 1))) {
382        mech_used = gss_mech_krb5;
383    } else if ((code == G_WRONG_MECH) &&
384 	      !(code = g_verify_token_header(gss_mech_krb5_old,
385 					     (uint32_t *)&(ap_req.length),
386 					     &ptr, KG_TOK_CTX_AP_REQ,
387 					     input_token->length, 1))) {
388        /*
389 	* Previous versions of this library used the old mech_id
390 	* and some broken behavior (wrong IV on checksum
391 	* encryption).  We support the old mech_id for
392 	* compatibility, and use it to decide when to use the
393 	* old behavior.
394 	*/
395        mech_used = gss_mech_krb5_old;
396   } else if (code == G_WRONG_TOKID) {
397 	major_status = GSS_S_CONTINUE_NEEDED;
398         code = KRB5KRB_AP_ERR_MSG_TYPE;
399         mech_used = gss_mech_krb5;
400         goto fail;
401    } else if (code == G_BAD_TOK_HEADER) {
402 	/* DCE style not encapsulated */
403         ap_req.length = input_token->length;
404         ap_req.data = input_token->value;
405         mech_used = gss_mech_krb5;
406         no_encap = 1;
407    } else {
408 	major_status = GSS_S_DEFECTIVE_TOKEN;
409         goto fail;
410    }
411 
412    sptr = (char *) ptr;
413    TREAD_STR(sptr, ap_req.data, ap_req.length);
414 
415    /*
416     * Solaris Kerberos:
417     *  We need to decode the request now so that we can get the
418     *  service principal in order to try and acquire a cred for it.
419     *  below in the "handle default cred handle" code block.
420     */
421    if (!krb5_is_ap_req(&ap_req)) {
422        code = KRB5KRB_AP_ERR_MSG_TYPE;
423        goto fail;
424    }
425    /* decode the AP-REQ into request */
426    if ((code = decode_krb5_ap_req(&ap_req, &request))) {
427        if (code == KRB5_BADMSGTYPE)
428            code = KRB5KRB_AP_ERR_BADVERSION;
429        goto fail;
430    }
431 
432    /* handle default cred handle */
433    /*
434     * Solaris Kerberos:
435     * If there is no princ associated with the cred then treat it the
436     * the same as GSS_C_NO_CREDENTIAL.
437     */
438    if (verifier_cred_handle == GSS_C_NO_CREDENTIAL ||
439     ((krb5_gss_cred_id_t)verifier_cred_handle)->princ == NULL) {
440        /* Note that we try to acquire a cred for the service principal
441 	* named in the AP-REQ. This allows us to implement option (ii)
442 	* of the recommended behaviour for GSS_Accept_sec_context() as
443 	* described in section 1.1.1.3 of RFC2743.
444 
445 	* This is far more useful that option (i), for which we would
446 	* acquire a cred for GSS_C_NO_NAME.
447 	*/
448        /* copy the princ from the ap-req or we'll lose it when we free
449 	  the ap-req */
450        krb5_principal princ;
451        if ((code = krb5_copy_principal(context, request->ticket->server,
452 				       &princ))) {
453            KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
454 	            "krb5_copy_principal() error code %d", code);
455 	   major_status = GSS_S_FAILURE;
456 	   goto fail;
457        }
458        /* intern the acceptor name */
459        if (! kg_save_name((gss_name_t) princ)) {
460 	   code = G_VALIDATE_FAILED;
461 	   major_status = GSS_S_FAILURE;
462 	   goto fail;
463        }
464        major_status = krb5_gss_acquire_cred((OM_uint32*) &code,
465 					    (gss_name_t) princ,
466 					    GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
467 					    GSS_C_ACCEPT, &cred_handle,
468 					    NULL, NULL);
469 
470        if (major_status != GSS_S_COMPLETE){
471 
472 	   /* Solaris kerberos: RFC2743 indicate this should be returned if we
473 	    * can't aquire a default cred.
474 	    */
475 	   KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() "
476 		  "krb5_gss_acquire_cred() error"
477 		   "orig major_status = %d, now = GSS_S_NO_CRED\n",
478 		   major_status);
479 
480 	   major_status = GSS_S_NO_CRED;
481 	   goto fail;
482        }
483 
484    } else {
485        cred_handle = verifier_cred_handle;
486    }
487 
488    major_status = krb5_gss_validate_cred((OM_uint32*) &code,
489 						 cred_handle);
490 
491    if (GSS_ERROR(major_status)){
492 
493        /* Solaris kerberos: RFC2743 indicate GSS_S_NO_CRED should be returned if
494 	* the supplied cred isn't valid.
495 	*/
496 
497        KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() "
498 	      "krb5_gss_validate_cred() error"
499 	       "orig major_status = %d, now = GSS_S_NO_CRED\n",
500 	       major_status);
501 
502        major_status = GSS_S_NO_CRED;
503        goto fail;
504    }
505 
506    cred = (krb5_gss_cred_id_t) cred_handle;
507 
508    /* make sure the supplied credentials are valid for accept */
509 
510    if ((cred->usage != GSS_C_ACCEPT) &&
511        (cred->usage != GSS_C_BOTH)) {
512        code = 0;
513       KRB5_LOG0(KRB5_ERR,"krb5_gss_accept_sec_context() "
514 	      "error GSS_S_NO_CONTEXT");
515        major_status = GSS_S_NO_CRED;
516        goto fail;
517    }
518 
519    /* construct the sender_addr */
520 
521    if ((input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) &&
522        (input_chan_bindings->initiator_addrtype == GSS_C_AF_INET)) {
523        /* XXX is this right? */
524        addr.addrtype = ADDRTYPE_INET;
525        addr.length = input_chan_bindings->initiator_address.length;
526        addr.contents = input_chan_bindings->initiator_address.value;
527 
528        paddr = &addr;
529    } else {
530        paddr = NULL;
531    }
532 
533    /* verify the AP_REQ message - setup the auth_context and rcache */
534 
535    if ((code = krb5_auth_con_init(context, &auth_context))) {
536        major_status = GSS_S_FAILURE;
537        /* Solaris Kerberos */
538        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
539 	      "krb5_auth_con_init() error code %d", code);
540        goto fail;
541    }
542 
543    (void) krb5_auth_con_setflags(context, auth_context,
544                           KRB5_AUTH_CONTEXT_DO_SEQUENCE);
545 
546    if (cred->rcache) {
547        cred_rcache = 1;
548        if ((code = krb5_auth_con_setrcache(context, auth_context, cred->rcache))) {
549 	   major_status = GSS_S_FAILURE;
550            /* Solaris Kerberos */
551 	   KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
552 		    "krb5_auth_con_setrcache() error code %d", code);
553 	   goto fail;
554        }
555    }
556    if ((code = krb5_auth_con_setaddrs(context, auth_context, NULL, paddr))) {
557        major_status = GSS_S_FAILURE;
558        /* Solaris Kerberos */
559        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
560 	      "krb5_auth_con_setaddrs() error code %d", code);
561        goto fail;
562    }
563 
564    if ((code = krb5_rd_req_decoded(context, &auth_context, request,
565 			   cred->princ, cred->keytab, NULL, &ticket))) {
566       KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
567 	      "krb5_rd_req() error code %d", code);
568        if (code == KRB5_KT_KVNONOTFOUND || code == KRB5_KT_NOTFOUND) {
569            major_status = GSS_S_DEFECTIVE_CREDENTIAL;
570 	   code = KRB5KRB_AP_ERR_NOKEY;
571        }
572        else if (code == KRB5KRB_AP_WRONG_PRINC) {
573            major_status = GSS_S_NO_CRED;
574 	   code = KRB5KRB_AP_ERR_NOT_US;
575        }
576        else if (code == KRB5KRB_AP_ERR_REPEAT)
577            major_status = GSS_S_DUPLICATE_TOKEN;
578        else
579            major_status = GSS_S_FAILURE;
580        goto fail;
581    }
582    krb5_auth_con_setflags(context, auth_context,
583 			  KRB5_AUTH_CONTEXT_DO_SEQUENCE);
584 
585    krb5_auth_con_getauthenticator(context, auth_context, &authdat);
586 
587 #if 0
588    /* make sure the necessary parts of the authdat are present */
589 
590    if ((authdat->authenticator->subkey == NULL) ||
591        (authdat->ticket->enc_part2 == NULL)) {
592 	   code = KG_NO_SUBKEY;
593 	   major_status = GSS_S_FAILURE;
594 	   goto fail;
595    }
596 #endif
597 
598    {
599        /* gss krb5 v1 */
600 
601        /* stash this now, for later. */
602        code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &md5len);
603        if (code) {
604 	   /* Solaris Kerberos */
605 	   KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
606 		  "krb5_c_checksum_length() error code %d", code);
607 	   major_status = GSS_S_FAILURE;
608 	   goto fail;
609        }
610 
611        /* verify that the checksum is correct */
612 
613        /*
614 	 The checksum may be either exactly 24 bytes, in which case
615 	 no options are specified, or greater than 24 bytes, in which case
616 	 one or more options are specified. Currently, the only valid
617 	 option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ).
618        */
619 
620        if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) ||
621 	   (authdat->checksum->length < 24)) {
622 	   code = 0;
623 	   major_status = GSS_S_BAD_BINDINGS;
624 	   goto fail;
625        }
626 
627        /*
628 	 "Be liberal in what you accept, and
629 	 conservative in what you send"
630 	 -- rfc1123
631 
632 	 This code will let this acceptor interoperate with an initiator
633 	 using little-endian or big-endian integer encoding.
634        */
635 
636        ptr = (unsigned char *) authdat->checksum->contents;
637        bigend = 0;
638 
639        TREAD_INT(ptr, tmp, bigend);
640 
641        if (tmp != md5len) {
642 	   ptr = (unsigned char *) authdat->checksum->contents;
643 	   bigend = 1;
644 
645 	   TREAD_INT(ptr, tmp, bigend);
646 
647 	   if (tmp != md5len) {
648 	       code = KG_BAD_LENGTH;
649 	       major_status = GSS_S_FAILURE;
650 	       goto fail;
651 	   }
652        }
653 
654        /* at this point, bigend is set according to the initiator's
655 	  byte order */
656 
657 
658        /*
659           The following section of code attempts to implement the
660           optional channel binding facility as described in RFC2743.
661 
662           Since this facility is optional channel binding may or may
663           not have been provided by either the client or the server.
664 
665           If the server has specified input_chan_bindings equal to
666           GSS_C_NO_CHANNEL_BINDINGS then we skip the check.  If
667           the server does provide channel bindings then we compute
668           a checksum and compare against those provided by the
669           client.  If the check fails we test the clients checksum
670           to see whether the client specified GSS_C_NO_CHANNEL_BINDINGS.
671           If either test succeeds we continue without error.
672         */
673        if ((code = kg_checksum_channel_bindings(context,
674 						input_chan_bindings,
675 						&reqcksum, bigend))) {
676 	   /* Solaris Kerberos */
677 	   KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
678 		  "kg_checksum_channel_bindings() error code %d", code);
679 	 major_status = GSS_S_BAD_BINDINGS;
680 	 goto fail;
681        }
682 
683        TREAD_STR(ptr, ptr2, reqcksum.length);
684 
685        if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS ) {
686            if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
687                xfree(reqcksum.contents);
688                reqcksum.contents = 0;
689 		if ((code = kg_checksum_channel_bindings(context,
690                                                   GSS_C_NO_CHANNEL_BINDINGS,
691                                                   &reqcksum, bigend))) {
692                    major_status = GSS_S_BAD_BINDINGS;
693                    goto fail;
694 		}
695                if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
696                    code = 0;
697                    major_status = GSS_S_BAD_BINDINGS;
698                    goto fail;
699 		}
700            }
701 
702        }
703 
704        TREAD_INT(ptr, gss_flags, bigend);
705 
706        /* if the checksum length > 24, there are options to process */
707 
708        if(authdat->checksum->length > 24 && (gss_flags & GSS_C_DELEG_FLAG)) {
709 
710 	   i = authdat->checksum->length - 24;
711 
712 	   if (i >= 4) {
713 
714 	       TREAD_INT16(ptr, option_id, bigend);
715 
716 	       TREAD_INT16(ptr, option.length, bigend);
717 
718 	       i -= 4;
719 
720 	       if (i < option.length || option.length < 0) {
721 		   code = KG_BAD_LENGTH;
722 		   major_status = GSS_S_FAILURE;
723 		   goto fail;
724 	       }
725 
726 	       /* have to use ptr2, since option.data is wrong type and
727 		  macro uses ptr as both lvalue and rvalue */
728 
729 	       TREAD_STR(ptr, ptr2, option.length);
730 	       option.data = (char *) ptr2;
731 
732 	       i -= option.length;
733 
734 	       if (option_id != KRB5_GSS_FOR_CREDS_OPTION) {
735 		   major_status = GSS_S_FAILURE;
736 		   goto fail;
737 	       }
738 
739 		   /* store the delegated credential */
740 
741 		   code = rd_and_store_for_creds(context, auth_context, &option,
742 						 (delegated_cred_handle) ?
743 						 &deleg_cred : NULL);
744 		   if (code) {
745 		       major_status = GSS_S_FAILURE;
746 		       goto fail;
747 		   }
748 
749 	   } /* if i >= 4 */
750 	   /* ignore any additional trailing data, for now */
751 #ifdef CFX_EXERCISE
752 	   {
753 	       FILE *f = fopen("/tmp/gsslog", "a");
754 	       if (f) {
755 		   fprintf(f,
756 			   "initial context token with delegation, %d extra bytes\n",
757 			   i);
758 		   fclose(f);
759 	       }
760 	   }
761 #endif
762        } else {
763 #ifdef CFX_EXERCISE
764 	   {
765 	       FILE *f = fopen("/tmp/gsslog", "a");
766 	       if (f) {
767 		   if (gss_flags & GSS_C_DELEG_FLAG)
768 		       fprintf(f,
769 			       "initial context token, delegation flag but too small\n");
770 		   else
771 		       /* no deleg flag, length might still be too big */
772 		       fprintf(f,
773 			       "initial context token, %d extra bytes\n",
774 			       authdat->checksum->length - 24);
775 		   fclose(f);
776 	       }
777 	   }
778 #endif
779        }
780    }
781 
782    /* create the ctx struct and start filling it in */
783 
784    if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
785        == NULL) {
786        code = ENOMEM;
787        major_status = GSS_S_FAILURE;
788        goto fail;
789    }
790 
791    memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
792    ctx->mech_used = (gss_OID) mech_used;
793    ctx->auth_context = auth_context;
794    ctx->initiate = 0;
795    ctx->gss_flags = (GSS_C_TRANS_FLAG |
796                     ((gss_flags) & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
797                             GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
798                             GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG)));
799    ctx->seed_init = 0;
800    ctx->big_endian = bigend;
801    ctx->cred_rcache = cred_rcache;
802 
803    /* Intern the ctx pointer so that delete_sec_context works */
804    if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) {
805        xfree(ctx);
806        ctx = 0;
807 
808 	/* Solaris Kerberos */
809        KRB5_LOG0(KRB5_ERR, "krb5_gss_accept_sec_context() "
810 	      "kg_save_ctx_id() error");
811        code = G_VALIDATE_FAILED;
812        major_status = GSS_S_FAILURE;
813        goto fail;
814    }
815 
816    /* XXX move this into gss_name_t */
817    if ((code = krb5_merge_authdata(context,
818 				ticket->enc_part2->authorization_data,
819 				authdat->authorization_data,
820 				&ctx->authdata))) {
821 	major_status = GSS_S_FAILURE;
822         goto fail;
823    }
824 
825    if ((code = krb5_copy_principal(context, cred->princ, &ctx->here))) {
826 	/* Solaris Kerberos */
827        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
828 	      "krb5_copy_principal() error code %d", code);
829        major_status = GSS_S_FAILURE;
830        goto fail;
831    }
832 
833    if ((code = krb5_copy_principal(context, authdat->client, &ctx->there))) {
834 	/* Solaris Kerberos */
835        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
836 	      "krb5_copy_principal() 2 error code %d", code);
837        major_status = GSS_S_FAILURE;
838        goto fail;
839    }
840 
841    if ((code = krb5_auth_con_getrecvsubkey(context, auth_context,
842 					   &ctx->subkey))) {
843 	/* Solaris Kerberos */
844        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
845 	      "krb5_auth_con_getremotesubkey() error code %d", code);
846        major_status = GSS_S_FAILURE;
847        goto fail;
848    }
849 
850    /* use the session key if the subkey isn't present */
851 
852    if (ctx->subkey == NULL) {
853        if ((code = krb5_auth_con_getkey(context, auth_context,
854 					&ctx->subkey))) {
855 	/* Solaris Kerberos */
856 	   KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
857 		      "krb5_auth_con_getkey() error code %d", code);
858            *minor_status = (OM_uint32) KRB5KDC_ERR_NULL_KEY;
859 	   major_status = GSS_S_FAILURE;
860 	   goto fail;
861        }
862    }
863 
864    if (ctx->subkey == NULL) {
865        /* this isn't a very good error, but it's not clear to me this
866 	  can actually happen */
867        major_status = GSS_S_FAILURE;
868        code = KRB5KDC_ERR_NULL_KEY;
869        goto fail;
870    }
871 
872     /* Solaris Kerberos */
873    KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() "
874 	   "ctx->subkey->enctype=%d", ctx->subkey->enctype);
875 
876    ctx->proto = 0;
877    switch(ctx->subkey->enctype) {
878    case ENCTYPE_DES_CBC_MD5:
879    case ENCTYPE_DES_CBC_CRC:
880        ctx->subkey->enctype = ENCTYPE_DES_CBC_RAW;
881        ctx->signalg = SGN_ALG_DES_MAC_MD5;
882        ctx->cksum_size = 8;
883        ctx->sealalg = SEAL_ALG_DES;
884 
885        /* fill in the encryption descriptors */
886 
887        if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) {
888 	   major_status = GSS_S_FAILURE;
889 	   goto fail;
890        }
891 
892        for (i=0; i<ctx->enc->length; i++)
893 	   /*SUPPRESS 113*/
894 	   ctx->enc->contents[i] ^= 0xf0;
895 
896        goto copy_subkey_to_seq;
897        break;
898 
899    case ENCTYPE_DES3_CBC_SHA1:
900        ctx->subkey->enctype = ENCTYPE_DES3_CBC_RAW;
901        ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD;
902        ctx->cksum_size = 20;
903        ctx->sealalg = SEAL_ALG_DES3KD;
904 
905        /* fill in the encryption descriptors */
906    copy_subkey:
907        if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) {
908 	   major_status = GSS_S_FAILURE;
909 	   goto fail;
910        }
911    copy_subkey_to_seq:
912        if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq))) {
913 	   major_status = GSS_S_FAILURE;
914 	   goto fail;
915        }
916        break;
917 
918    case ENCTYPE_ARCFOUR_HMAC:
919        ctx->signalg = SGN_ALG_HMAC_MD5 ;
920        ctx->cksum_size = 8;
921        ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ;
922        goto copy_subkey;
923 
924    default:
925        ctx->signalg = -1;
926        ctx->sealalg = -1;
927        ctx->proto = 1;
928        code = (*kaccess.krb5int_c_mandatory_cksumtype)(context, ctx->subkey->enctype,
929 					    &ctx->cksumtype);
930        if (code)
931 	   goto fail;
932        code = krb5_c_checksum_length(context, ctx->cksumtype,
933 		(size_t *)&ctx->cksum_size);
934        if (code)
935 	   goto fail;
936        ctx->have_acceptor_subkey = 0;
937        goto copy_subkey;
938    }
939 
940     /* Solaris Kerberos */
941    KRB5_LOG1(KRB5_ERR, "accept_sec_context:  subkey enctype = %d proto = %d",
942 	ctx->subkey->enctype, ctx->proto);
943 
944    ctx->endtime = ticket->enc_part2->times.endtime;
945    ctx->krb_flags = ticket->enc_part2->flags;
946 
947    krb5_free_ticket(context, ticket); /* Done with ticket */
948 
949    {
950        krb5_ui_4 seq_temp;
951        krb5_auth_con_getremoteseqnumber(context, auth_context,
952 		(krb5_int32 *)&seq_temp);
953        ctx->seq_recv = seq_temp;
954    }
955 
956    if ((code = krb5_timeofday(context, &now))) {
957        major_status = GSS_S_FAILURE;
958        goto fail;
959    }
960 
961    if (ctx->endtime < now) {
962        code = 0;
963        major_status = GSS_S_CREDENTIALS_EXPIRED;
964        goto fail;
965    }
966 
967    g_order_init(&(ctx->seqstate), ctx->seq_recv,
968 		(ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
969 		(ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
970 
971    /* at this point, the entire context structure is filled in,
972       so it can be released.  */
973 
974    /* generate an AP_REP if necessary */
975 
976    if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
977        unsigned char * ptr3;
978        krb5_ui_4 seq_temp;
979        int cfx_generate_subkey;
980 
981        if (ctx->proto == 1)
982 	   cfx_generate_subkey = CFX_ACCEPTOR_SUBKEY;
983        else
984 	   cfx_generate_subkey = 0;
985 
986        if (cfx_generate_subkey) {
987 	   krb5_int32 acflags;
988 	   code = krb5_auth_con_getflags(context, auth_context, &acflags);
989 	   if (code == 0) {
990 	       acflags |= KRB5_AUTH_CONTEXT_USE_SUBKEY;
991 	       code = krb5_auth_con_setflags(context, auth_context, acflags);
992 	   }
993 	   if (code) {
994 	       major_status = GSS_S_FAILURE;
995 	       goto fail;
996 	   }
997        }
998 
999        if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) {
1000 	   major_status = GSS_S_FAILURE;
1001 	   goto fail;
1002        }
1003 
1004        krb5_auth_con_getlocalseqnumber(context, auth_context,
1005 		(krb5_int32 *)&seq_temp);
1006        ctx->seq_send = seq_temp & 0xffffffffL;
1007 
1008        if (cfx_generate_subkey) {
1009 	   /* Get the new acceptor subkey.  With the code above, there
1010 	      should always be one if we make it to this point.  */
1011 	   code = krb5_auth_con_getsendsubkey(context, auth_context,
1012 					      &ctx->acceptor_subkey);
1013 	   if (code != 0) {
1014 	       major_status = GSS_S_FAILURE;
1015 	       goto fail;
1016 	   }
1017 	   code = (*kaccess.krb5int_c_mandatory_cksumtype)(context,
1018 						ctx->acceptor_subkey->enctype,
1019 						&ctx->acceptor_subkey_cksumtype);
1020 	   if (code) {
1021 	       major_status = GSS_S_FAILURE;
1022 	       goto fail;
1023 	   }
1024 	   ctx->have_acceptor_subkey = 1;
1025        }
1026 
1027        /* the reply token hasn't been sent yet, but that's ok. */
1028        ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
1029        ctx->established = 1;
1030 
1031        token.length = g_token_size(mech_used, ap_rep.length);
1032 
1033        if ((token.value = (unsigned char *) xmalloc(token.length))
1034 	   == NULL) {
1035 	   major_status = GSS_S_FAILURE;
1036 	   code = ENOMEM;
1037 	   goto fail;
1038        }
1039        ptr3 = token.value;
1040        g_make_token_header(mech_used, ap_rep.length,
1041 			   &ptr3, KG_TOK_CTX_AP_REP);
1042 
1043        TWRITE_STR(ptr3, ap_rep.data, ap_rep.length);
1044 
1045        ctx->established = 1;
1046 
1047    } else {
1048        token.length = 0;
1049        token.value = NULL;
1050        ctx->seq_send = ctx->seq_recv;
1051 
1052        ctx->established = 1;
1053    }
1054 
1055    /* set the return arguments */
1056 
1057    if (src_name) {
1058        if ((code = krb5_copy_principal(context, ctx->there, &name))) {
1059 	   major_status = GSS_S_FAILURE;
1060 	   goto fail;
1061        }
1062        /* intern the src_name */
1063        if (! kg_save_name((gss_name_t) name)) {
1064 	   code = G_VALIDATE_FAILED;
1065 	   major_status = GSS_S_FAILURE;
1066 	   goto fail;
1067        }
1068    }
1069 
1070    if (mech_type)
1071       *mech_type = (gss_OID) mech_used;
1072 
1073    if (time_rec)
1074       *time_rec = ctx->endtime - now;
1075 
1076    if (ret_flags)
1077       *ret_flags = ctx->gss_flags;
1078 
1079    *context_handle = (gss_ctx_id_t)ctx;
1080    *output_token = token;
1081 
1082    if (src_name)
1083       *src_name = (gss_name_t) name;
1084 
1085    if (delegated_cred_handle && deleg_cred) {
1086        if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) {
1087            /* Solaris Kerberos */
1088 	   KRB5_LOG0(KRB5_ERR, "krb5_gss_accept_sec_context() "
1089 		      "kg_save_cred_id() error");
1090 	   major_status = GSS_S_FAILURE;
1091 	   code = (OM_uint32) G_VALIDATE_FAILED;
1092 	   goto fail;
1093        }
1094 
1095        *delegated_cred_handle = (gss_cred_id_t) deleg_cred;
1096    }
1097 
1098    /* finally! */
1099 
1100    *minor_status = 0;
1101    major_status = GSS_S_COMPLETE;
1102 
1103  fail:
1104 
1105    if (authdat)
1106        krb5_free_authenticator(context, authdat);
1107    /* The ctx structure has the handle of the auth_context */
1108    if (auth_context && !ctx) {
1109        if (cred_rcache)
1110 	   (void)krb5_auth_con_setrcache(context, auth_context, NULL);
1111 
1112        krb5_auth_con_free(context, auth_context);
1113    }
1114    if (reqcksum.contents)
1115        xfree(reqcksum.contents);
1116    if (ap_rep.data)
1117        xfree(ap_rep.data);
1118 
1119    if (request != NULL) {
1120 	saved_ap_options = request->ap_options;
1121 	krb5_free_ap_req(context, request);
1122 	request = NULL;
1123    }
1124 
1125    if (!GSS_ERROR(major_status) && major_status != GSS_S_CONTINUE_NEEDED) {
1126 	if (!verifier_cred_handle && cred_handle) {
1127 		krb5_gss_release_cred(minor_status, &cred_handle);
1128 	}
1129 
1130 	if (ctx)
1131 	    ctx->k5_context = context;
1132 
1133         return(major_status);
1134    }
1135 
1136    /* from here on is the real "fail" code */
1137 
1138    if (ctx)
1139        (void) krb5_gss_delete_sec_context(minor_status,
1140 					  (gss_ctx_id_t *) &ctx, NULL);
1141    if (deleg_cred) { /* free memory associated with the deleg credential */
1142        if (deleg_cred->ccache)
1143 	   (void)krb5_cc_close(context, deleg_cred->ccache);
1144        if (deleg_cred->princ)
1145 	   krb5_free_principal(context, deleg_cred->princ);
1146        xfree(deleg_cred);
1147    }
1148    if (token.value)
1149        xfree(token.value);
1150    if (name) {
1151        (void) kg_delete_name((gss_name_t) name);
1152        krb5_free_principal(context, name);
1153    }
1154 
1155    *minor_status = code;
1156 
1157    if (saved_ap_options & AP_OPTS_MUTUAL_REQUIRED)
1158 	gss_flags |= GSS_C_MUTUAL_FLAG;
1159 
1160    if (cred
1161        && ((gss_flags & GSS_C_MUTUAL_FLAG)
1162 	   || (major_status == GSS_S_CONTINUE_NEEDED))) {
1163        unsigned int tmsglen;
1164        int toktype;
1165 
1166        /*
1167 	* The client is expecting a response, so we can send an
1168 	* error token back
1169 	*/
1170        memset(&krb_error_data, 0, sizeof(krb_error_data));
1171 
1172        code -= ERROR_TABLE_BASE_krb5;
1173        if (code < 0 || code > 128)
1174 	   code = 60 /* KRB_ERR_GENERIC */;
1175 
1176        krb_error_data.error = code;
1177        (void) krb5_us_timeofday(context, &krb_error_data.stime,
1178 				&krb_error_data.susec);
1179        krb_error_data.server = cred->princ;
1180 
1181        if (code == KRB5KRB_AP_ERR_SKEW) {
1182 	    /*
1183 	     * SUNW17PACresync / Solaris Kerberos
1184 	     * Set e-data to Windows constant.
1185 	     * (verified by MSFT)
1186 	     *
1187 	     * This facilitates the Windows CIFS client clock skew
1188 	     * recovery feature.
1189 	     */
1190 	    char *ms_e_data = "\x30\x05\xa1\x03\x02\x01\x02";
1191 	    int len = strlen(ms_e_data);
1192 
1193 	    krb_error_data.e_data.data = malloc(len);
1194 	    if (krb_error_data.e_data.data) {
1195 		    (void) memcpy(krb_error_data.e_data.data, ms_e_data, len);
1196 		    krb_error_data.e_data.length = len;
1197 	    }
1198        }
1199 
1200        code = krb5_mk_error(context, &krb_error_data, &scratch);
1201        if (code)
1202            goto cleanup;
1203 
1204        tmsglen = scratch.length;
1205        toktype = KG_TOK_CTX_ERROR;
1206 
1207        token.length = g_token_size(mech_used, tmsglen);
1208        token.value = (unsigned char *) xmalloc(token.length);
1209        if (!token.value)
1210 	  goto cleanup;
1211 
1212        ptr = token.value;
1213        g_make_token_header(mech_used, tmsglen, &ptr, toktype);
1214 
1215        TWRITE_STR(ptr, scratch.data, scratch.length);
1216        xfree(scratch.data);
1217 
1218        *output_token = token;
1219    }
1220 
1221 cleanup:
1222    if (!verifier_cred_handle && cred_handle) {
1223 	krb5_gss_release_cred(&t_minor_status, &cred_handle);
1224    }
1225    krb5_free_context(context);
1226 
1227    /* Solaris Kerberos */
1228    KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() end, "
1229 	      "major_status = %d", major_status);
1230    return (major_status);
1231 }
1232