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