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