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