xref: /illumos-gate/usr/src/lib/gss_mechs/mech_krb5/mech/accept_sec_context.c (revision 058561cbaa119a6f2659bc27ef343e1b47266bb2)
1 /*
2  * Copyright 2007 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    /*
419     * Solaris Kerberos:
420     * If there is no princ associated with the cred then treat it the
421     * the same as GSS_C_NO_CREDENTIAL.
422     */
423    if (verifier_cred_handle == GSS_C_NO_CREDENTIAL ||
424     ((krb5_gss_cred_id_t)verifier_cred_handle)->princ == NULL) {
425        /* Note that we try to acquire a cred for the service principal
426 	* named in the AP-REQ. This allows us to implement option (ii)
427 	* of the recommended behaviour for GSS_Accept_sec_context() as
428 	* described in section 1.1.1.3 of RFC2743.
429 
430 	* This is far more useful that option (i), for which we would
431 	* acquire a cred for GSS_C_NO_NAME.
432 	*/
433        /* copy the princ from the ap-req or we'll lose it when we free
434 	  the ap-req */
435        krb5_principal princ;
436        if ((code = krb5_copy_principal(context, request->ticket->server,
437 				       &princ))) {
438            KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
439 	            "krb5_copy_principal() error code %d", code);
440 	   major_status = GSS_S_FAILURE;
441 	   goto fail;
442        }
443        /* intern the acceptor name */
444        if (! kg_save_name((gss_name_t) princ)) {
445 	   code = G_VALIDATE_FAILED;
446 	   major_status = GSS_S_FAILURE;
447 	   goto fail;
448        }
449        major_status = krb5_gss_acquire_cred_no_lock(context, (OM_uint32*) &code,
450 					    (gss_name_t) princ,
451 					    GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
452 					    GSS_C_ACCEPT, &cred_handle,
453 					    NULL, NULL);
454 
455        if (major_status != GSS_S_COMPLETE){
456 
457 	   /* Solaris kerberos: RFC2743 indicate this should be returned if we
458 	    * can't aquire a default cred.
459 	    */
460 	   KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() "
461 		  "krb5_gss_acquire_cred() error"
462 		   "orig major_status = %d, now = GSS_S_NO_CRED\n",
463 		   major_status);
464 
465 	   major_status = GSS_S_NO_CRED;
466 	   goto fail;
467        }
468 
469    } else {
470        cred_handle = verifier_cred_handle;
471    }
472 
473    major_status = krb5_gss_validate_cred_no_lock(context, (OM_uint32*) &code,
474 						 cred_handle);
475 
476    if (GSS_ERROR(major_status)){
477 
478        /* Solaris kerberos: RFC2743 indicate GSS_S_NO_CRED should be returned if
479 	* the supplied cred isn't valid.
480 	*/
481 
482        KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() "
483 	      "krb5_gss_validate_cred() error"
484 	       "orig major_status = %d, now = GSS_S_NO_CRED\n",
485 	       major_status);
486 
487        major_status = GSS_S_NO_CRED;
488        goto fail;
489    }
490 
491    cred = (krb5_gss_cred_id_t) cred_handle;
492 
493    /* make sure the supplied credentials are valid for accept */
494 
495    if ((cred->usage != GSS_C_ACCEPT) &&
496        (cred->usage != GSS_C_BOTH)) {
497        code = 0;
498       KRB5_LOG0(KRB5_ERR,"krb5_gss_accept_sec_context() "
499 	      "error GSS_S_NO_CONTEXT");
500        major_status = GSS_S_NO_CRED;
501        goto fail;
502    }
503 
504    /* construct the sender_addr */
505 
506    if ((input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) &&
507        (input_chan_bindings->initiator_addrtype == GSS_C_AF_INET)) {
508        /* XXX is this right? */
509        addr.addrtype = ADDRTYPE_INET;
510        addr.length = input_chan_bindings->initiator_address.length;
511        addr.contents = input_chan_bindings->initiator_address.value;
512 
513        paddr = &addr;
514    } else {
515        paddr = NULL;
516    }
517 
518    /* verify the AP_REQ message - setup the auth_context and rcache */
519 
520    if ((code = krb5_auth_con_init(context, &auth_context))) {
521        major_status = GSS_S_FAILURE;
522        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
523 	      "krb5_auth_con_init() error code %d", code);
524        goto fail;
525    }
526 
527    (void) krb5_auth_con_setflags(context, auth_context,
528                           KRB5_AUTH_CONTEXT_DO_SEQUENCE);
529 
530    if (cred->rcache &&
531        (code = krb5_auth_con_setrcache(context, auth_context, cred->rcache))) {
532        major_status = GSS_S_FAILURE;
533        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
534 	      "krb5_auth_con_setrcache() error code %d", code);
535        goto fail;
536    }
537    if ((code = krb5_auth_con_setaddrs(context, auth_context, NULL, paddr))) {
538        major_status = GSS_S_FAILURE;
539        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
540 	      "krb5_auth_con_setaddrs() error code %d", code);
541        goto fail;
542    }
543 
544    if ((code = krb5_rd_req_decoded(context, &auth_context, request,
545 			   cred->princ, cred->keytab, NULL, &ticket))) {
546        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
547 	      "krb5_rd_req() error code %d", code);
548        if (code == KRB5_KT_KVNONOTFOUND || code == KRB5_KT_NOTFOUND) {
549            major_status = GSS_S_DEFECTIVE_CREDENTIAL;
550 	   code = KRB5KRB_AP_ERR_NOKEY;
551        }
552        else if (code == KRB5KRB_AP_WRONG_PRINC) {
553            major_status = GSS_S_NO_CRED;
554 	   code = KRB5KRB_AP_ERR_NOT_US;
555        }
556        else if (code == KRB5KRB_AP_ERR_REPEAT)
557            major_status = GSS_S_DUPLICATE_TOKEN;
558        else
559            major_status = GSS_S_FAILURE;
560        goto fail;
561    }
562 
563    krb5_auth_con_getauthenticator(context, auth_context, &authdat);
564 
565 #if 0
566    /* make sure the necessary parts of the authdat are present */
567 
568    if ((authdat->authenticator->subkey == NULL) ||
569        (authdat->ticket->enc_part2 == NULL)) {
570 	   code = KG_NO_SUBKEY;
571 	   major_status = GSS_S_FAILURE;
572 	   goto fail;
573    }
574 #endif
575 
576    {
577        /* gss krb5 v1 */
578 
579        /* stash this now, for later. */
580        if (code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5,
581 					 &md5len)) {
582 	   KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
583 		  "krb5_c_checksum_length() error code %d", code);
584 	   major_status = GSS_S_FAILURE;
585 	   goto fail;
586        }
587 
588        /* verify that the checksum is correct */
589 
590        /*
591 	 The checksum may be either exactly 24 bytes, in which case
592 	 no options are specified, or greater than 24 bytes, in which case
593 	 one or more options are specified. Currently, the only valid
594 	 option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ).
595        */
596 
597        if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) ||
598 	   (authdat->checksum->length < 24)) {
599 	   code = 0;
600 	   major_status = GSS_S_BAD_BINDINGS;
601 	   goto fail;
602        }
603 
604        /*
605 	 "Be liberal in what you accept, and
606 	 conservative in what you send"
607 	 -- rfc1123
608 
609 	 This code will let this acceptor interoperate with an initiator
610 	 using little-endian or big-endian integer encoding.
611        */
612 
613        ptr = (unsigned char *) authdat->checksum->contents;
614        bigend = 0;
615 
616        TREAD_INT(ptr, tmp, bigend);
617 
618        if (tmp != md5len) {
619 	   ptr = (unsigned char *) authdat->checksum->contents;
620 	   bigend = 1;
621 
622 	   TREAD_INT(ptr, tmp, bigend);
623 
624 	   if (tmp != md5len) {
625 	       code = KG_BAD_LENGTH;
626 	       major_status = GSS_S_FAILURE;
627 	       goto fail;
628 	   }
629        }
630 
631        /* at this point, bigend is set according to the initiator's
632 	  byte order */
633 
634 	/*
635           The following section of code attempts to implement the
636 	  optional channel binding facility as described in RFC2743.
637 
638 	  Since this facility is optional channel binding may or may
639 	  not have been provided by either the client or the server.
640 
641 	  If the server has specified input_chan_bindings equal to
642 	  GSS_C_NO_CHANNEL_BINDINGS then we skip the check.  If
643 	  the server does provide channel bindings then we compute
644 	  a checksum and compare against those provided by the
645 	  client.  If the check fails we test the clients checksum
646 	  to see whether the client specified GSS_C_NO_CHANNEL_BINDINGS.
647 	  If either test succeeds we continue without error.
648 	*/
649        if ((code = kg_checksum_channel_bindings(context, input_chan_bindings,
650 						&reqcksum, bigend))) {
651 	   KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
652 		  "kg_checksum_channel_bindings() error code %d", code);
653 	   major_status = GSS_S_BAD_BINDINGS;
654 	   goto fail;
655        }
656 
657 	TREAD_STR(ptr, ptr2, reqcksum.length);
658 	if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) {
659 	   if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
660 		xfree(reqcksum.contents);
661 		reqcksum.contents = 0;
662 		if ((code = kg_checksum_channel_bindings(context,
663                                                   GSS_C_NO_CHANNEL_BINDINGS,
664                                                   &reqcksum, bigend))) {
665                    major_status = GSS_S_BAD_BINDINGS;
666                    goto fail;
667 		}
668                if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
669                    code = 0;
670                    major_status = GSS_S_BAD_BINDINGS;
671                    goto fail;
672 		}
673            }
674        }
675 
676        TREAD_INT(ptr, gss_flags, bigend);
677 
678        /* if the checksum length > 24, there are options to process */
679 
680        if (authdat->checksum->length > 24 &&
681 	   (gss_flags & GSS_C_DELEG_FLAG)) {
682 	   i = authdat->checksum->length - 24;
683 
684 	   if (i >= 4) {
685 	       TREAD_INT16(ptr, option_id, bigend);
686 
687 	       TREAD_INT16(ptr, option.length, bigend);
688 
689 		i -= 4;
690                if (i < option.length || option.length < 0) {
691                    code = KG_BAD_LENGTH;
692                    major_status = GSS_S_FAILURE;
693                    goto fail;
694 		}
695 
696                /* have to use ptr2, since option.data is wrong type and
697 		  macro uses ptr as both lvalue and rvalue */
698 
699 		TREAD_STR(ptr, ptr2, option.length);
700 		option.data = (char *) ptr2;
701 
702 		i -= option.length;
703 
704 		if (option_id != KRB5_GSS_FOR_CREDS_OPTION) {
705                    major_status = GSS_S_FAILURE;
706                    goto fail;
707 		}
708 
709                    /* store the delegated credential */
710 
711 		   code = rd_and_store_for_creds(context, auth_context, &option,
712 			(delegated_cred_handle) ? &deleg_cred : NULL);
713 		   if (code) {
714 		       major_status = GSS_S_FAILURE;
715 		       goto fail;
716                    }
717 
718 	    } /* if i >= 4 */
719 		/* ignore any additional trailing data, for now */
720        } /* if */
721    } /* krb5 gssapi v1 */
722 
723    /* create the ctx struct and start filling it in */
724 
725    if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
726        == NULL) {
727        code = ENOMEM;
728        major_status = GSS_S_FAILURE;
729        goto fail;
730    }
731 
732    memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
733 
734    /* Solaris Kerberos:  we allocate the memory for mech_used here
735     * because we store mech_used as a gss_OID and not a (gss_OID *)
736     */
737 #if 0
738    ctx->mech_used = mech_used;
739 #else
740    /* begin Solaris Kerberos solution */
741    ctx->mech_used.elements = (void *)malloc(mech_used->length);
742    if ( (ctx->mech_used.elements) == NULL )
743    {
744        code = ENOMEM;
745        major_status = GSS_S_FAILURE;
746        goto fail;
747    }
748    ctx->mech_used.length = mech_used->length;
749    memcpy(ctx->mech_used.elements, mech_used->elements, mech_used->length);
750 #endif
751 
752    ctx->auth_context = auth_context;
753    ctx->initiate = 0;
754    ctx->gss_flags = (GSS_C_TRANS_FLAG |
755                      ((gss_flags) & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
756                              GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
757                              GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG)));
758    ctx->seed_init = 0;
759    ctx->big_endian = bigend;
760 
761    /* Intern the ctx pointer so that delete_sec_context works */
762    if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) {
763        xfree(ctx);
764        ctx = 0;
765 
766        KRB5_LOG0(KRB5_ERR, "krb5_gss_accept_sec_context() "
767 	      "kg_save_ctx_id() error");
768        code = G_VALIDATE_FAILED;
769        major_status = GSS_S_FAILURE;
770        goto fail;
771    }
772 
773    if ((code = krb5_copy_principal(context, cred->princ, &ctx->here))) {
774        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
775 	      "krb5_copy_principal() error code %d", code);
776        major_status = GSS_S_FAILURE;
777        goto fail;
778    }
779 
780    if ((code = krb5_copy_principal(context, authdat->client, &ctx->there))) {
781        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
782 	      "krb5_copy_principal() 2 error code %d", code);
783        major_status = GSS_S_FAILURE;
784        goto fail;
785    }
786 
787    if ((code = krb5_auth_con_getrecvsubkey(context, auth_context,
788 					   &ctx->subkey))) {
789        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
790 	      "krb5_auth_con_getremotesubkey() error code %d", code);
791        major_status = GSS_S_FAILURE;
792        goto fail;
793    }
794 
795    /* use the session key if the subkey isn't present */
796 
797    if (ctx->subkey == NULL) {
798        if ((code = krb5_auth_con_getkey(context, auth_context,
799 					&ctx->subkey))) {
800 	   KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
801 		      "krb5_auth_con_getkey() error code %d", code);
802            *minor_status = (OM_uint32) KRB5KDC_ERR_NULL_KEY;
803 	   major_status = GSS_S_FAILURE;
804 	   goto fail;
805        }
806    }
807 
808    if (ctx->subkey == NULL) {
809        /* this isn't a very good error, but it's not clear to me this
810 	  can actually happen */
811        major_status = GSS_S_FAILURE;
812        code = KRB5KDC_ERR_NULL_KEY;
813        goto fail;
814    }
815 
816    KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() "
817 	   "ctx->subkey->enctype=%d", ctx->subkey->enctype);
818 
819    ctx->proto = 0;
820    switch(ctx->subkey->enctype) {
821    case ENCTYPE_DES_CBC_MD5:
822    case ENCTYPE_DES_CBC_CRC:
823        ctx->subkey->enctype = ENCTYPE_DES_CBC_RAW;
824        ctx->signalg = SGN_ALG_DES_MAC_MD5;
825        ctx->cksum_size = 8;
826        ctx->sealalg = SEAL_ALG_DES;
827 
828        /* fill in the encryption descriptors */
829 
830        if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) {
831 	   major_status = GSS_S_FAILURE;
832 	   goto fail;
833        }
834 
835        for (i=0; i<ctx->enc->length; i++)
836 	   /*SUPPRESS 113*/
837 	   ctx->enc->contents[i] ^= 0xf0;
838 
839        goto copy_subkey_to_seq;
840        break;
841 
842    case ENCTYPE_DES3_CBC_SHA1:
843        ctx->subkey->enctype = ENCTYPE_DES3_CBC_RAW;
844        ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD;
845        ctx->cksum_size = 20;
846        ctx->sealalg = SEAL_ALG_DES3KD;
847 
848        /* fill in the encryption descriptors */
849 
850    copy_subkey:
851        if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) {
852 	   major_status = GSS_S_FAILURE;
853 	   goto fail;
854        }
855 
856    copy_subkey_to_seq:
857        if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq))) {
858 	   major_status = GSS_S_FAILURE;
859 	   goto fail;
860        }
861 
862        break;
863    case ENCTYPE_ARCFOUR_HMAC:
864         ctx->signalg = SGN_ALG_HMAC_MD5 ;
865         ctx->cksum_size = 8;
866         ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ;
867 	goto copy_subkey;
868 
869    default:
870 	ctx->signalg = -1;
871 	ctx->sealalg = -1;
872 	ctx->proto = 1;
873 	code = krb5int_c_mandatory_cksumtype(context, ctx->subkey->enctype,
874 		&ctx->cksumtype);
875 	if (code)
876            goto fail;
877 	code = krb5_c_checksum_length(context, ctx->cksumtype,
878 		(size_t *)&ctx->cksum_size);
879 	if (code)
880            goto fail;
881 	ctx->have_acceptor_subkey = 0;
882 	goto copy_subkey;
883    }
884 
885    KRB5_LOG1(KRB5_ERR, "accept_sec_context:  subkey enctype = %d proto = %d",
886 	ctx->subkey->enctype, ctx->proto);
887 
888    ctx->endtime = ticket->enc_part2->times.endtime;
889    ctx->krb_flags = ticket->enc_part2->flags;
890 
891    krb5_free_ticket(context, ticket); /* Done with ticket */
892    {
893 	krb5_ui_4 seq_temp;
894 	krb5_auth_con_getremoteseqnumber(context, auth_context,
895 		(krb5_int32 *)&seq_temp);
896 	ctx->seq_recv = seq_temp;
897    }
898 
899    if ((code = krb5_timeofday(context, &now))) {
900        major_status = GSS_S_FAILURE;
901        goto fail;
902    }
903 
904    if (ctx->endtime < now) {
905        code = 0;
906        major_status = GSS_S_CREDENTIALS_EXPIRED;
907        goto fail;
908    }
909 
910    g_order_init(&(ctx->seqstate), ctx->seq_recv,
911 		(ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
912 		(ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
913 
914    /* at this point, the entire context structure is filled in,
915       so it can be released.  */
916 
917    /* generate an AP_REP if necessary */
918 
919    if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
920        unsigned char * ptr3;
921 	krb5_ui_4 seq_temp;
922 	int cfx_generate_subkey;
923 
924 	if (ctx->proto == 1)
925 	   cfx_generate_subkey = CFX_ACCEPTOR_SUBKEY;
926 	else
927 	   cfx_generate_subkey = 0;
928 
929 	if (cfx_generate_subkey) {
930 	   krb5_int32 acflags;
931 	   code = krb5_auth_con_getflags(context, auth_context, &acflags);
932 	   if (code == 0) {
933 		acflags |= KRB5_AUTH_CONTEXT_USE_SUBKEY;
934 		code = krb5_auth_con_setflags(context, auth_context, acflags);
935 	   }
936            if (code) {
937                major_status = GSS_S_FAILURE;
938 		goto fail;
939 	   }
940        }
941 
942        if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) {
943 	   major_status = GSS_S_FAILURE;
944 	   goto fail;
945        }
946 
947        krb5_auth_con_getlocalseqnumber(context, auth_context,
948 		(krb5_int32 *)&seq_temp);
949        ctx->seq_send = seq_temp & 0xffffffffL;
950 
951 	if (cfx_generate_subkey) {
952 	   /* Get the new acceptor subkey.  With the code above, there
953 		should always be one if we make it to this point.	 */
954 	   code = krb5_auth_con_getsendsubkey(context, auth_context,
955 		&ctx->acceptor_subkey);
956 	   if (code != 0) {
957 		major_status = GSS_S_FAILURE;
958 		goto fail;
959 	   }
960            code = krb5int_c_mandatory_cksumtype(context,
961 		ctx->acceptor_subkey->enctype,
962 		&ctx->acceptor_subkey_cksumtype);
963 	   if (code) {
964                major_status = GSS_S_FAILURE;
965 		goto fail;
966 	   }
967            ctx->have_acceptor_subkey = 1;
968 	}
969 
970        /* the reply token hasn't been sent yet, but that's ok. */
971        ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
972        ctx->established = 1;
973        token.length = g_token_size((gss_OID) mech_used, ap_rep.length);
974 
975        if ((token.value = (unsigned char *) xmalloc(token.length))
976 	   == NULL) {
977 	   major_status = GSS_S_FAILURE;
978 	   code = ENOMEM;
979 	   goto fail;
980        }
981        ptr = token.value;
982        g_make_token_header((gss_OID) mech_used, ap_rep.length,
983 			   &ptr, KG_TOK_CTX_AP_REP);
984 
985        TWRITE_STR(ptr, ap_rep.data, ap_rep.length);
986    } else {
987        token.length = 0;
988        token.value = NULL;
989        ctx->seq_send = ctx->seq_recv;
990    }
991    ctx->established = 1;
992 
993    /* set the return arguments */
994 
995    if (src_name) {
996        if ((code = krb5_copy_principal(context, ctx->there, &name))) {
997 	   major_status = GSS_S_FAILURE;
998 	   goto fail;
999        }
1000        /* intern the src_name */
1001        if (! kg_save_name((gss_name_t) name)) {
1002 	   code = G_VALIDATE_FAILED;
1003 	   major_status = GSS_S_FAILURE;
1004 	   goto fail;
1005        }
1006    }
1007 
1008    if (mech_type)
1009       *mech_type = (gss_OID) mech_used;
1010 
1011    if (time_rec)
1012       *time_rec = ctx->endtime - now;
1013 
1014    if (ret_flags)
1015       *ret_flags = ctx->gss_flags;
1016 
1017    *context_handle = (gss_ctx_id_t) ctx;
1018    *output_token = token;
1019 
1020    if (src_name)
1021       *src_name = (gss_name_t) name;
1022 
1023    if (delegated_cred_handle && deleg_cred) {
1024 	if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) {
1025 	   KRB5_LOG0(KRB5_ERR, "krb5_gss_accept_sec_context() "
1026 		      "kg_save_cred_id() error");
1027 	   major_status = GSS_S_FAILURE;
1028 	   code = (OM_uint32) G_VALIDATE_FAILED;
1029            goto fail;
1030 	}
1031 
1032        *delegated_cred_handle = (gss_cred_id_t) deleg_cred;
1033    }
1034 
1035    /* finally! */
1036 
1037    *minor_status = 0;
1038    major_status = GSS_S_COMPLETE;
1039 
1040  fail:
1041    if (authdat)
1042        krb5_free_authenticator(context, authdat);
1043    if (auth_context && !ctx) {
1044 	(void)krb5_auth_con_setrcache(context, auth_context, NULL);
1045 	krb5_auth_con_free(context, auth_context);
1046    }
1047    if (reqcksum.contents)
1048        xfree(reqcksum.contents);
1049    if (ap_rep.data)
1050        xfree(ap_rep.data);
1051 
1052    if (request != NULL) {
1053 	saved_ap_options = request->ap_options;
1054 	krb5_free_ap_req(context, request);
1055 	request = NULL;
1056    }
1057 
1058    if (!GSS_ERROR(major_status))
1059        goto unlock;
1060 
1061    /* from here on is the real "fail" code */
1062 
1063    if (ctx)
1064        (void) krb5_gss_delete_sec_context_no_lock(context, minor_status,
1065 					  (gss_ctx_id_t *) &ctx, NULL);
1066    if (deleg_cred) { /* free memory associated with the deleg credential */
1067        if (deleg_cred->ccache)
1068 	   (void)krb5_cc_close(context, deleg_cred->ccache);
1069        if (deleg_cred->princ)
1070 	   krb5_free_principal(context, deleg_cred->princ);
1071        xfree(deleg_cred);
1072    }
1073    if (token.value)
1074        xfree(token.value);
1075    if (name) {
1076        (void) kg_delete_name((gss_name_t) name);
1077        krb5_free_principal(context, name);
1078    }
1079 
1080    *minor_status = code;
1081 
1082    if (saved_ap_options & AP_OPTS_MUTUAL_REQUIRED)
1083 	gss_flags |= GSS_C_MUTUAL_FLAG;
1084 
1085    if (cred && ((gss_flags & GSS_C_MUTUAL_FLAG) ||
1086 	(major_status == GSS_S_CONTINUE_NEEDED))) {
1087        unsigned int tmsglen;
1088        int toktype;
1089 
1090        /*
1091 	* The client is expecting a response, so we can send an
1092 	* error token back
1093 	*/
1094        memset(&krb_error_data, 0, sizeof(krb_error_data));
1095 
1096        code  -= ERROR_TABLE_BASE_krb5;
1097        if (code < 0 || code > 128)
1098 	   code = 60 /* KRB_ERR_GENERIC */;
1099 
1100        krb_error_data.error = code;
1101        (void) krb5_us_timeofday(context, &krb_error_data.stime,
1102 				&krb_error_data.susec);
1103        krb_error_data.server = cred->princ;
1104 
1105        code = krb5_mk_error(context, &krb_error_data, &scratch);
1106        if (code)
1107            goto unlock;
1108 
1109        tmsglen = scratch.length;
1110        toktype = KG_TOK_CTX_ERROR;
1111 
1112        token.length = g_token_size((gss_OID) mech_used, tmsglen);
1113        token.value = (unsigned char *) xmalloc(token.length);
1114        if (!token.value)
1115 	  goto unlock;
1116 
1117        ptr = token.value;
1118        g_make_token_header((gss_OID) mech_used, tmsglen, &ptr, toktype);
1119 
1120        TWRITE_STR(ptr, scratch.data, scratch.length);
1121        xfree(scratch.data);
1122 
1123        *output_token = token;
1124    }
1125 
1126 unlock:
1127    if (!verifier_cred_handle && cred_handle) {
1128       krb5_gss_release_cred_no_lock(context, (OM_uint32*) &code, &cred_handle);
1129    }
1130 
1131    mutex_unlock(&krb5_mutex);
1132    KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() end, "
1133 	      "major_status = %d", major_status);
1134    return (major_status);
1135 }
1136