1 /*
2  * Copyright 2005 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 <gssapiP_krb5.h>
81 #include <k5-int.h>
82 #include <memory.h>
83 #include <stdlib.h>
84 #include <syslog.h>
85 #include <assert.h>
86 #define ROOT_UID 0
87 #define KRB5_DEFAULT_LIFE 60*60*10
88 #define CACHE_FILENAME_LEN 35
89 
90 /*
91  * $Id: init_sec_context.c,v 1.51.2.7 2000/06/28 02:48:22 tlyu Exp $
92  */
93 
94 extern int
95 safechown(const char *src, uid_t uid, gid_t gid, int mode);
96 
97 /*
98  * XXX This is for debugging only!!!  Should become a real bitfield
99  * at some point
100  */
101 int krb5_gss_dbg_client_expcreds = 0;
102 
103 /*
104  * Common code which fetches the correct krb5 credentials from the
105  * ccache.
106  */
107 static krb5_error_code get_credentials(context, cred, server, now,
108 				       endtime, out_creds)
109     krb5_context context;
110     krb5_gss_cred_id_t cred;
111     krb5_principal server;
112     krb5_timestamp now;
113     krb5_timestamp endtime;
114     krb5_creds **out_creds;
115 {
116     krb5_error_code	code;
117     krb5_creds 		in_creds;
118 
119     KRB5_LOG0(KRB5_INFO, "get_credentials() start\n");
120 
121     memset((char *) &in_creds, 0, sizeof(krb5_creds));
122 
123     if ((code = krb5_copy_principal(context, cred->princ, &in_creds.client)))
124 	goto cleanup;
125     if ((code = krb5_copy_principal(context, server, &in_creds.server)))
126 	goto cleanup;
127     in_creds.times.endtime = endtime;
128 
129     in_creds.keyblock.enctype = 0;
130 
131     code = krb5_get_credentials(context, 0, cred->ccache,
132 				&in_creds, out_creds);
133     if (code)
134 	goto cleanup;
135 
136     /*
137      * Enforce a stricter limit (without timeskew forgiveness at the
138      * boundaries) because accept_sec_context code is also similarly
139      * non-forgiving.
140      */
141     if (!krb5_gss_dbg_client_expcreds && *out_creds != NULL &&
142 	(*out_creds)->times.endtime < now) {
143 	code = KRB5KRB_AP_ERR_TKT_EXPIRED;
144 	goto cleanup;
145     }
146 
147 cleanup:
148     if (in_creds.client)
149 	    krb5_free_principal(context, in_creds.client);
150     if (in_creds.server)
151 	    krb5_free_principal(context, in_creds.server);
152 
153     KRB5_LOG(KRB5_INFO, "get_credentials() end, code = %d\n", code);
154 
155     return code;
156 }
157 
158 struct gss_checksum_data {
159     krb5_gss_ctx_id_rec *ctx;
160     krb5_gss_cred_id_t cred;
161     krb5_checksum md5;
162     krb5_data checksum_data;
163 };
164 
165 static krb5_error_code KRB5_CALLCONV
166 make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
167                    void *cksum_data, krb5_data **out)
168 {
169     krb5_error_code code;
170     krb5_int32 con_flags;
171     unsigned char *ptr;
172     struct gss_checksum_data *data = cksum_data;
173     krb5_data credmsg;
174     int junk;
175 
176     data->checksum_data.data = 0;
177     credmsg.data = 0;
178     /* build the checksum field */
179 
180     if (data->ctx->gss_flags & GSS_C_DELEG_FLAG) {
181 	/* first get KRB_CRED message, so we know its length */
182 
183 	/* clear the time check flag that was set in krb5_auth_con_init() */
184 	krb5_auth_con_getflags(context, auth_context, &con_flags);
185 	krb5_auth_con_setflags(context, auth_context,
186 		con_flags & ~KRB5_AUTH_CONTEXT_DO_TIME);
187 
188 	code = krb5_fwd_tgt_creds(context, auth_context, 0,
189 		data->cred->princ, data->ctx->there,
190 		data->cred->ccache, 1,
191 		&credmsg);
192 
193 	/* turn KRB5_AUTH_CONTEXT_DO_TIME back on */
194 	krb5_auth_con_setflags(context, auth_context, con_flags);
195 
196 	if (code) {
197 	    /* don't fail here; just don't accept/do the delegation
198 		request */
199 	    data->ctx->gss_flags &= ~GSS_C_DELEG_FLAG;
200 
201 	    data->checksum_data.length = 24;
202 	} else {
203 	    if (credmsg.length+28 > KRB5_INT16_MAX) {
204 		krb5_free_data_contents(context, &credmsg);
205 		return(KRB5KRB_ERR_FIELD_TOOLONG);
206             }
207 
208             data->checksum_data.length = 28+credmsg.length;
209 	}
210     } else {
211 	data->checksum_data.length = 24;
212     }
213 #ifdef CFX_EXERCISE
214     if (data->ctx->auth_context->keyblock->enctype == 18) {
215 	srand(time(0) ^ getpid());
216 	/* Our ftp client code stupidly assumes a base64-encoded
217 	   version of the token will fit in 10K, so don't make this
218 	   too big.  */
219 	junk = rand() & 0xff;
220     } else
221         junk = 0;
222 #else
223     junk = 0;
224 #endif
225 
226     data->checksum_data.length += junk;
227 
228     /* now allocate a buffer to hold the checksum data and
229 	(maybe) KRB_CRED msg */
230 
231     if ((data->checksum_data.data =
232 	 (char *) xmalloc(data->checksum_data.length)) == NULL) {
233 	if (credmsg.data)
234             krb5_free_data_contents(context, &credmsg);
235 	return(ENOMEM);
236     }
237 
238     ptr = (uchar_t *)data->checksum_data.data;
239 
240     TWRITE_INT(ptr, data->md5.length, 0);
241     TWRITE_STR(ptr, (unsigned char *) data->md5.contents, data->md5.length);
242     TWRITE_INT(ptr, data->ctx->gss_flags, 0);
243 
244     /* done with this, free it */
245     xfree(data->md5.contents);
246 
247     if (credmsg.data) {
248 	TWRITE_INT16(ptr, KRB5_GSS_FOR_CREDS_OPTION, 0);
249 	TWRITE_INT16(ptr, credmsg.length, 0);
250 	TWRITE_STR(ptr, (unsigned char *) credmsg.data, credmsg.length);
251 
252 	/* free credmsg data */
253 	krb5_free_data_contents(context, &credmsg);
254     }
255     if (junk)
256 	memset(ptr, 'i', junk);
257     *out = &data->checksum_data;
258     return 0;
259 }
260 
261 static krb5_error_code
262 make_ap_req_v1(context, ctx, cred, k_cred, chan_bindings, mech_type, token)
263     krb5_context context;
264     krb5_gss_ctx_id_rec *ctx;
265     krb5_gss_cred_id_t cred;
266     krb5_creds *k_cred;
267     gss_channel_bindings_t chan_bindings;
268     gss_OID mech_type;
269     gss_buffer_t token;
270 {
271     krb5_flags mk_req_flags = 0;
272     krb5_error_code code;
273     struct gss_checksum_data cksum_struct;
274     krb5_checksum md5;
275     krb5_data ap_req;
276     krb5_data *checksum_data = NULL;
277     unsigned char *ptr;
278     unsigned char *t;
279     int tlen;
280 
281     ap_req.data = 0;
282 
283     /* build the checksum buffer */
284     KRB5_LOG0(KRB5_INFO, "make_ap_req_v1() start\n");
285 
286     /* compute the hash of the channel bindings */
287 
288     if ((code = kg_checksum_channel_bindings(context, chan_bindings, &md5, 0)))
289         return(code);
290 
291     krb5_auth_con_set_req_cksumtype(context, ctx->auth_context,
292 				    CKSUMTYPE_KG_CB);
293 
294     cksum_struct.md5 = md5;
295     cksum_struct.ctx = ctx;
296     cksum_struct.cred = cred;
297     cksum_struct.checksum_data.data = NULL;
298     switch (k_cred->keyblock.enctype) {
299     case ENCTYPE_DES_CBC_CRC:
300     case ENCTYPE_DES_CBC_MD4:
301     case ENCTYPE_DES_CBC_MD5:
302     case ENCTYPE_DES3_CBC_SHA1:
303 	code = make_gss_checksum(context, ctx->auth_context, &cksum_struct,
304 		&checksum_data);
305 	if (code)
306 		goto cleanup;
307         break;
308     default:
309 	krb5_auth_con_set_checksum_func(context, ctx->auth_context,
310 		make_gss_checksum, &cksum_struct);
311 	break;
312     }
313 
314     /* call mk_req.  subkey and ap_req need to be used or destroyed */
315 
316     mk_req_flags = AP_OPTS_USE_SUBKEY;
317 
318     if (ctx->gss_flags & GSS_C_MUTUAL_FLAG)
319 	mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED;
320 
321     if ((code = krb5_mk_req_extended(context, &ctx->auth_context, mk_req_flags,
322 				     checksum_data, k_cred, &ap_req)))
323 	goto cleanup;
324 
325    /* store the interesting stuff from creds and authent */
326    ctx->endtime = k_cred->times.endtime;
327    ctx->krb_flags = k_cred->ticket_flags;
328 
329    /* build up the token */
330 
331    /* allocate space for the token */
332    tlen = g_token_size((gss_OID) mech_type, ap_req.length);
333 
334    if ((t = (unsigned char *) xmalloc(tlen)) == NULL) {
335       code = ENOMEM;
336       goto cleanup;
337    }
338 
339    /* fill in the buffer */
340 
341    ptr = t;
342 
343    g_make_token_header((gss_OID) mech_type, ap_req.length,
344 		       &ptr, KG_TOK_CTX_AP_REQ);
345 
346    TWRITE_STR(ptr, (unsigned char *) ap_req.data, ap_req.length);
347 
348    /* pass it back */
349 
350    token->length = tlen;
351    token->value = (void *) t;
352 
353    code = 0;
354 
355 cleanup:
356    /*
357     * We only free cksum_struct.checksum_data here, because checksum_data
358     * could point to cksum_struct.checksum_data or NULL.
359     */
360    if (cksum_struct.checksum_data.data)
361 	krb5_free_data_contents(context, &cksum_struct.checksum_data);
362    if (ap_req.data)
363        xfree(ap_req.data);
364 
365    KRB5_LOG(KRB5_INFO, "make_ap_req_v1() end, code = %d\n", code);
366 
367    return (code);
368 }
369 
370 
371 
372 static krb5_boolean
373 principal_ignore_inst_compare(context, princ1, princ2)
374     krb5_context context;
375     krb5_const_principal princ1;
376     krb5_const_principal princ2;
377 {
378     krb5_int32 nelem;
379 
380     nelem = krb5_princ_size(context, princ1);
381     if (nelem != krb5_princ_size(context, princ2))
382 	return FALSE;
383 
384     if (! krb5_realm_compare(context, princ1, princ2))
385 	return FALSE;
386 
387     /*
388      * Solaris Kerberos
389      * If princ1 is elem1/metachar@REALM, compare just elem1 (and REALM).
390      */
391     if (nelem == 2) {
392         const krb5_data *p = krb5_princ_component(context, princ1, 1);
393 
394 	if (p->length == 1) {
395 	    const char *s = p->data;
396 
397 	    if (s[0] == '*') {
398 		const krb5_data *p1 = krb5_princ_component(context, princ1, 0);
399 		const krb5_data *p2 = krb5_princ_component(context, princ2, 0);
400 
401 		if (p1->length != p2->length ||
402 		        memcmp(p1->data, p2->data, p1->length))
403 		    return FALSE;
404 
405 		return TRUE;
406 	    }
407 	}
408     }
409 
410     return FALSE;
411 }
412 
413 /*
414  * Solaris Kerberos
415  * This is a dup of krb5_ktfile_get_entry (sigh) but is necessary to
416  * to get a custom princ compare above (principal_ignore_inst_compare)
417  * and thus avoid mucking w/important krb5 internal
418  * api (krb5_principal_compare)
419  */
420 #include "../krb5/keytab/file/ktfile.h"
421 
422 static krb5_error_code KRB5_CALLCONV
423 ktfile_get_entry(context, id, principal, kvno, enctype, entry)
424    krb5_context context;
425    krb5_keytab id;
426    krb5_const_principal principal;
427    krb5_kvno kvno;
428    krb5_enctype enctype;
429    krb5_keytab_entry * entry;
430 {
431     krb5_keytab_entry cur_entry, new_entry;
432     krb5_error_code kerror = 0;
433     int found_wrong_kvno = 0;
434     krb5_boolean similar;
435     int kvno_offset = 0;
436 
437     KRB5_LOG0(KRB5_INFO, "ktfile_get_entry() start\n");
438 
439     /* Open the keyfile for reading */
440     if ((kerror = krb5_ktfileint_openr(context, id))){
441 	KRB5_LOG(KRB5_ERR, "ktfile_get_entry() end, ktfileint_openr() "
442 		"kerror= %d\n", kerror);
443 	return(kerror);
444     }
445 
446     /*
447      * For efficiency and simplicity, we'll use a while true that
448      * is exited with a break statement.
449      */
450     cur_entry.principal = 0;
451     cur_entry.vno = 0;
452     cur_entry.key.contents = 0;
453     /*CONSTCOND*/
454     while (TRUE) {
455 	if ((kerror = krb5_ktfileint_read_entry(context, id, &new_entry)))
456 	    break;
457 
458 	/*
459 	 * by the time this loop exits, it must either free cur_entry,
460 	 * and copy new_entry there, or free new_entry.  Otherwise, it
461 	 * leaks.
462 	 */
463 
464 	/*
465 	 * if the principal isn't the one requested, free new_entry
466 	 * and continue to the next.
467 	 */
468 
469 	if (!principal_ignore_inst_compare(context, principal,
470 					new_entry.principal)) {
471 		krb5_kt_free_entry(context, &new_entry);
472 	    continue;
473 	}
474 
475 	/*
476 	 * if the enctype is not ignored and doesn't match, free new_entry
477 	 * and continue to the next
478 	 */
479 
480 	if (enctype != IGNORE_ENCTYPE) {
481 	    if ((kerror = krb5_c_enctype_compare(context, enctype,
482 						 new_entry.key.enctype,
483 						 &similar))) {
484 		krb5_kt_free_entry(context, &new_entry);
485 		break;
486 	    }
487 
488 	    if (!similar) {
489 		krb5_kt_free_entry(context, &new_entry);
490 		continue;
491 	    }
492 	    /*
493 	     * Coerce the enctype of the output keyblock in case we
494 	     * got an inexact match on the enctype.
495 	     */
496 	    new_entry.key.enctype = enctype;
497 	}
498 
499 	if (kvno == IGNORE_VNO) {
500 	    /*
501 	     * if this is the first match, or if the new vno is
502 	     * bigger, free the current and keep the new.  Otherwise,
503 	     * free the new.
504 	     */
505 	    /*
506 	     * A 1.2.x keytab contains only the low 8 bits of the key
507 	     * version number.  Since it can be much bigger, and thus
508 	     * the 8-bit value can wrap, we need some heuristics to
509 	     * figure out the "highest" numbered key if some numbers
510 	     * close to 255 and some near 0 are used.
511 	     *
512 	     * The heuristic here:
513 
514 	     * If we have any keys with versions over 240, then assume
515 	     * that all version numbers 0-127 refer to 256+N instead.
516 	     * Not perfect, but maybe good enough?
517 	     */
518 
519 #define M(VNO) (((VNO) - kvno_offset + 256) % 256)
520 
521 	    if (new_entry.vno > 240)
522 		kvno_offset = 128;
523 	    if (! cur_entry.principal ||
524 		M(new_entry.vno) > M(cur_entry.vno)) {
525 		krb5_kt_free_entry(context, &cur_entry);
526 		cur_entry = new_entry;
527 	    } else {
528 		krb5_kt_free_entry(context, &new_entry);
529 	    }
530 	} else {
531 	    /*
532 	     * if this kvno matches, free the current (will there ever
533 	     * be one?), keep the new, and break out.  Otherwise, remember
534 	     * that we were here so we can return the right error, and
535 	     * free the new
536 	     */
537 	    /*
538 	     * Yuck.  The krb5-1.2.x keytab format only stores one byte
539 	     * for the kvno, so we're toast if the kvno requested is
540 	     * higher than that.  Short-term workaround: only compare
541 	     * the low 8 bits.
542 	     */
543 
544 	    if (new_entry.vno == (kvno & 0xff)) {
545 		krb5_kt_free_entry(context, &cur_entry);
546 		cur_entry = new_entry;
547 		break;
548 	    } else {
549 		found_wrong_kvno++;
550 		krb5_kt_free_entry(context, &new_entry);
551 	    }
552 	}
553     }
554 
555     if (kerror == KRB5_KT_END) {
556 	 if (cur_entry.principal)
557 	      kerror = 0;
558 	 else if (found_wrong_kvno)
559 	      kerror = KRB5_KT_KVNONOTFOUND;
560 	 else
561 	      kerror = KRB5_KT_NOTFOUND;
562     }
563     if (kerror) {
564 	(void) krb5_ktfileint_close(context, id);
565 	krb5_kt_free_entry(context, &cur_entry);
566 	KRB5_LOG(KRB5_ERR,"ktfile_get_entry() end, kerror="
567 		    "%d\n", kerror);
568 	return kerror;
569     }
570     if ((kerror = krb5_ktfileint_close(context, id)) != 0) {
571 	krb5_kt_free_entry(context, &cur_entry);
572 	KRB5_LOG(KRB5_ERR,"ktfile_get_entry() end, ktfileint_close() "
573 	       "kerror= %d\n", kerror);
574 	return kerror;
575     }
576     *entry = cur_entry;
577 
578     /* Let us close the file before we leave */
579     (void) krb5_ktfileint_close(context, id);
580 
581     KRB5_LOG0(KRB5_INFO, "ktfile_get_entry() end");
582 
583     return 0;
584 }
585 
586 
587 /*
588  * Solaris Kerberos
589  * Given a princ of name/instance@LOCALREALM, search the keytab
590  * for a match of name and LOCALREALM and if found, return instance
591  * as a string.
592  *
593  * Caller must free returned string.
594  */
595 static krb5_error_code
596 get_instance_keytab(
597 	krb5_context context,
598 	const char *sname,
599 	krb5_keytab keytab,
600 	char  **instance)  /* out */
601 {
602 	krb5_error_code ret=0;
603 	krb5_keytab_entry kt_ent;
604 	krb5_int32 nelem, free_kt_ent=0;
605 	register const krb5_data *p;
606 	char *realm=NULL, *s=NULL;
607 	krb5_principal client=NULL, princ=NULL;
608 
609 	if (!keytab)
610 		return EINVAL;
611 
612 	if (ret = krb5_get_default_realm(context, &realm))
613 		return ret;
614 
615 	ret = krb5_build_principal(context, &client, strlen(realm),
616 				      realm, sname, "*",
617 				      (char *)0);
618 	if (ret)
619 		goto out;
620 
621 	ret = ktfile_get_entry(context, keytab, client,
622 				0, /* don't have vno available */
623 				0, &kt_ent);
624 	if (ret)
625 		goto out;
626 
627 	free_kt_ent++;  /* kt_ent is not a ptr */
628 
629 	princ = kt_ent.principal;
630 	nelem = krb5_princ_size(context, princ);
631 	if (nelem != 2) {
632 		ret = KRB5_PRINC_NOMATCH;
633 		goto out;
634 	}
635 
636 	p = krb5_princ_component(context, princ, 1);
637 	s = calloc(p->length + sizeof(char), sizeof(char));
638 	if (!s) {
639 		ret = ENOMEM;
640 		goto out;
641 	}
642 
643 	(void) memcpy(s, p->data, p->length);
644 
645 
646 out:
647 	free(realm);
648 	if (client)
649 		krb5_free_principal(context, client);
650 	if (free_kt_ent)
651 		(void) krb5_kt_free_entry(context, &kt_ent);
652 
653 	if (ret == 0)
654 		*instance = s;
655 	return ret;
656 }
657 
658 static OM_uint32
659 load_root_cred_using_keytab(
660 	OM_uint32 *minor_status,
661 	krb5_context context,
662 	const char *sname,
663 	int use_nodename)
664 {
665 	krb5_creds my_creds;
666 	krb5_principal me;
667 	krb5_principal server;
668 	krb5_error_code code;
669 	krb5_ccache ccache = NULL;
670 	krb5_keytab keytab = NULL;
671 	krb5_timestamp now;
672 	krb5_deltat lifetime = KRB5_DEFAULT_LIFE;   /* -l option */
673 	krb5_get_init_creds_opt opt;
674 	krb5_data tgtname = {
675 		0,
676 		KRB5_TGS_NAME_SIZE,
677 		KRB5_TGS_NAME
678 	};
679 	char *svcname = NULL;
680 
681 	KRB5_LOG0(KRB5_INFO, "load_root_cred_using_keytab() start \n");
682 
683 	if (!sname)
684 		return (GSS_S_FAILURE);
685 
686 	memset((char *)&my_creds, 0, sizeof(my_creds));
687 
688 	if (code = krb5_kt_default(context, &keytab)) {
689 		*minor_status = code;
690 		return (GSS_S_FAILURE);
691 	}
692 
693 	if (!use_nodename) {
694 		char *instance = NULL;
695 
696 		code = get_instance_keytab(context, sname, keytab, &instance);
697 		if (code == 0) {
698 			code = krb5_sname_to_principal(context,
699 						    instance, sname,
700 						    KRB5_NT_UNKNOWN, &me);
701 			free(instance);
702 		}
703 	} else {
704 		code = krb5_sname_to_principal(context, NULL, sname,
705 					    KRB5_NT_SRV_HST, &me);
706 	}
707 	if (code) {
708 		(void) krb5_kt_close(context, keytab);
709 		*minor_status = code;
710 		return (GSS_S_FAILURE);
711 	}
712 	my_creds.client = me;
713 
714 	if((code = krb5_build_principal_ext(context, &server,
715 					krb5_princ_realm(context, me)->length,
716 					krb5_princ_realm(context, me)->data,
717 					tgtname.length, tgtname.data,
718 					krb5_princ_realm(context, me)->length,
719 					krb5_princ_realm(context, me)->data,
720 					0))) {
721 		*minor_status = code;
722 		krb5_free_cred_contents(context, &my_creds);
723 		(void) krb5_kt_close(context, keytab);
724 
725 		return (GSS_S_FAILURE);
726 	}
727 
728 	my_creds.server = server;
729 	my_creds.times.starttime = 0;     /* start timer
730 					   * when request
731 					   * gets to KDC
732 					   */
733 	if ((code = krb5_timeofday(context, &now))) {
734 		*minor_status = code;
735 		krb5_free_cred_contents(context, &my_creds);
736 		(void) krb5_kt_close(context, keytab);
737 
738 		return (GSS_S_FAILURE);
739 	}
740 	my_creds.times.endtime = now + lifetime;
741 	my_creds.times.renew_till = 0;
742 
743 	memset(&opt, 0, sizeof (opt));
744 	krb5_get_init_creds_opt_init(&opt);
745 	krb5_get_init_creds_opt_set_tkt_life(&opt, lifetime);
746 
747 	code = krb5_unparse_name(context, server, &svcname);
748 	if (code != 0) {
749 		*minor_status = code;
750 		krb5_free_cred_contents(context, &my_creds);
751 		(void) krb5_kt_close(context, keytab);
752 
753 		return (GSS_S_FAILURE);
754 	}
755 	code = krb5_get_init_creds_keytab(context,
756                                 &my_creds, me, keytab,
757                                 0, svcname, &opt);
758 
759 	(void) krb5_kt_close(context, keytab);
760 
761 	if (svcname != NULL)
762 		free(svcname);
763 	if (code) {
764 		*minor_status = code;
765 		krb5_free_cred_contents(context, &my_creds);
766 
767 		return (GSS_S_FAILURE);
768 	}
769 	code = krb5_cc_resolve (context,
770 				krb5_cc_default_name(context),
771 				&ccache);
772 	if (code != 0) {
773 		*minor_status = code;
774 		krb5_free_cred_contents(context, &my_creds);
775 
776 		return (GSS_S_FAILURE);
777 	}
778 	code = krb5_cc_initialize (context, ccache, me);
779 	if (code != 0) {
780 		*minor_status = code;
781 		krb5_free_cred_contents(context, &my_creds);
782 		(void) krb5_cc_close(context, ccache);
783 
784 		return (GSS_S_FAILURE);
785 	}
786 
787 	code = krb5_cc_store_cred(context, ccache,
788 				  &my_creds);
789 	krb5_free_cred_contents(context, &my_creds);
790 	(void) krb5_cc_close(context, ccache);
791 
792 	if (code) {
793 		*minor_status = code;
794 
795 		KRB5_LOG(KRB5_ERR, "load_root_cred_using_keytab() end, error "
796 			"code = %d\n", code);
797 
798 		return (GSS_S_FAILURE);
799 	}
800 
801 	KRB5_LOG0(KRB5_INFO, "load_root_cred_using_keytab() end \n");
802 
803 	return (GSS_S_COMPLETE);
804 }
805 
806 static OM_uint32
807 renew_ccache(OM_uint32 *minor_status, krb5_context context, uid_t uid)
808 {
809 	krb5_principal me;
810 	krb5_principal server;
811 	krb5_creds	creds;
812 	krb5_creds	tmpcreds;
813 	krb5_creds	*out_creds;
814 	krb5_error_code code;
815 	krb5_ccache ccache = NULL;
816 	static char ccache_name_buf[CACHE_FILENAME_LEN];
817 	int options = 0;
818 	krb5_data tgtname = {
819 		0,
820 		KRB5_TGS_NAME_SIZE,
821 		KRB5_TGS_NAME
822 	};
823 	gid_t gid = getgid();
824 
825 	memset((char *)&creds, 0, sizeof(creds));
826 	memset((char *)&tmpcreds, 0, sizeof(creds));
827 
828 	if ((code = krb5_cc_default(context, &ccache))) {
829 		*minor_status = code;
830 		(void) krb5_cc_close(context, ccache);
831 		return (GSS_S_FAILURE);
832 	}
833 
834 	if ((code = krb5_cc_get_principal(context, ccache, &me)) != 0) {
835 		*minor_status = code;
836 		(void) krb5_cc_close(context, ccache);
837 		return (GSS_S_FAILURE);
838 	}
839 
840 	creds.client = me;
841 
842 	if((code = krb5_build_principal_ext(context, &server,
843 					krb5_princ_realm(context, me)->length,
844 					krb5_princ_realm(context, me)->data,
845 					tgtname.length, tgtname.data,
846 					krb5_princ_realm(context, me)->length,
847 					krb5_princ_realm(context, me)->data,
848 					0))) {
849 		krb5_free_principal(context, me);
850 		(void) krb5_cc_close(context, ccache);
851 		*minor_status = code;
852 		return (GSS_S_FAILURE);
853 	}
854 
855 	creds.server = server;
856 	creds.ticket_flags = TKT_FLG_RENEWABLE;
857 
858 	if ((krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_FLAGS,
859 			&creds, &tmpcreds))) {
860 		(void) krb5_cc_close(context, ccache);
861 		return (KDC_ERR_BADOPTION);
862 	}
863 
864 	creds.ticket_flags = 0;
865         code = krb5_get_credentials_renew(context, options, ccache,
866 						&creds, &out_creds);
867 	krb5_free_cred_contents(context, &creds);
868 	krb5_free_cred_contents(context, &tmpcreds);
869 
870 	if (code) {
871 		*minor_status = code;
872 		return (GSS_S_FAILURE);
873 	}
874 
875 	krb5_free_creds(context, out_creds);
876 	snprintf(ccache_name_buf, CACHE_FILENAME_LEN, "/tmp/krb5cc_%d",
877 		uid, -1);
878 	code = safechown(ccache_name_buf, uid, gid, -1);
879 
880 	if (code == -1) {
881 		(void) krb5_cc_destroy(context, ccache);
882 		*minor_status = code;
883 		return (GSS_S_FAILURE);
884 	}
885 
886 	(void) krb5_cc_close(context, ccache);
887 
888 	return (GSS_S_COMPLETE);
889 
890 }
891 
892 /*
893  * Solaris Kerberos:
894  * We enforce a minimum refresh time on the root cred. This avoids problems for
895  * the higher level communication protocol for having valid creds and
896  * setting up a valid context, only to have it expire before or while
897  * it is being used. For non root users we don't care since we do not refresh
898  * there creds, they get what they can get.
899  */
900 #define MIN_REFRESH_TIME 300
901 #define MIN_RENEW_TIME 1500
902 
903 /* get_default_cred() must be called with the krb5_mutex lock held */
904 static OM_uint32
905 get_default_cred(OM_uint32 *minor_status, void *ct, gss_cred_id_t *cred_handle)
906 {
907 	krb5_timestamp now;
908 	krb5_gss_cred_id_t cred;
909 	OM_uint32 major;
910 	OM_uint32 mntmp;
911 	uid_t uid = getuid();
912 	krb5_context context = (krb5_context)ct;
913 
914 	KRB5_LOG0(KRB5_INFO, "get_default_cred() start\n");
915 
916 	/* Get the default cred for user */
917 	if (((major = kg_get_defcred(minor_status, cred_handle)) != NULL) &&
918 	    GSS_ERROR(major)) {
919 
920 		/* If we're not root we're done */
921    		if (uid != ROOT_UID)
922 	 		return (major);
923 
924 		/*
925 		 * Try and get root's cred in the cache using keytab.
926 		 *
927 		 * First try "root" and then try "host" - this allows
928 		 * Secure NFS to use the host principal for mounting if
929 		 * there is no root principal.
930 		 *
931 		 * Then try "host/<anything>" to match any instance (needed
932 		 * for DHCP clients).
933 		 */
934 		major = load_root_cred_using_keytab(minor_status,
935 						    context, "root", 1);
936 
937 		if (major != GSS_S_COMPLETE)
938 			major = load_root_cred_using_keytab(minor_status,
939 							    context, "host", 1);
940 		if (major != GSS_S_COMPLETE)
941 			major = load_root_cred_using_keytab(minor_status,
942 							    context, "host", 0);
943 
944 		if (major != GSS_S_COMPLETE)
945 			return (major);
946 
947 		/* We should have valid tgt now in the cache, so get it. */
948 		major = kg_get_defcred(minor_status, cred_handle);
949 
950 		return (major);
951       	}
952 
953 	/* We've got a gss cred handle that is a kerberos cred handle. */
954 	cred = (krb5_gss_cred_id_t)*cred_handle;
955 
956 	/* If we can't get the time, assume the worst. */
957 	if (krb5_timeofday(context, &now)) {
958 		(void) krb5_gss_release_cred_no_lock(ct, &mntmp, cred_handle);
959 		return (GSS_S_CREDENTIALS_EXPIRED);
960 	}
961 
962 	/* If root's cred has expired re-get it */
963 	if (cred->tgt_expire < now + MIN_REFRESH_TIME && uid == ROOT_UID) {
964 		(void) krb5_gss_release_cred_no_lock(ct, &mntmp, cred_handle);
965 
966 		major = load_root_cred_using_keytab(minor_status,
967 						    context, "root", 1);
968 
969 		if (major != GSS_S_COMPLETE)
970 			major = load_root_cred_using_keytab(minor_status,
971 							    context, "host", 1);
972 
973 		if (major != GSS_S_COMPLETE)
974 			major = load_root_cred_using_keytab(minor_status,
975 							    context, "host", 0);
976 
977 		if (major != GSS_S_COMPLETE)
978 			return (major);
979 
980 		major = kg_get_defcred(minor_status, cred_handle);
981 		if (major != GSS_S_COMPLETE)
982 			return (major);
983 
984 	/* Any body else is SOL unless we can renew their credential cache */
985 	} else if ((cred->tgt_expire < now + MIN_RENEW_TIME) &&
986 			(cred->tgt_expire > now)) {
987 		(void) krb5_gss_release_cred_no_lock(ct, &mntmp, cred_handle);
988 
989 		major = renew_ccache(minor_status, context, uid);
990 		if ((major != GSS_S_COMPLETE) &&
991 			(major != KDC_ERR_BADOPTION))
992 			return (major);
993 
994 		major = kg_get_defcred(minor_status, cred_handle);
995 		if (major != GSS_S_COMPLETE)
996 			return (major);
997 
998 	}
999 
1000 	/* Otherwise we got non expired creds */
1001 
1002 	KRB5_LOG0(KRB5_INFO, "get_default_cred() end\n");
1003 
1004 	return (GSS_S_COMPLETE);
1005 }
1006 
1007 /*
1008  * setup_enc
1009  *
1010  * Fill in the encryption descriptors.	Called after AP-REQ is made.
1011  */
1012 static OM_uint32
1013 setup_enc(
1014    OM_uint32 *minor_status,
1015    krb5_gss_ctx_id_rec *ctx,
1016    krb5_context context)
1017 {
1018    krb5_error_code code;
1019    OM_uint32 ret = GSS_S_COMPLETE;
1020    int i;
1021 
1022    ctx->have_acceptor_subkey = 0;
1023    ctx->proto = 0;
1024    ctx->cksumtype = 0;
1025 
1026    KRB5_LOG(KRB5_ERR, "setup_enc() enctype = %d\n",
1027 	ctx->subkey->enctype);
1028 
1029    switch(ctx->subkey->enctype) {
1030    case ENCTYPE_DES_CBC_MD5:
1031    case ENCTYPE_DES_CBC_MD4:
1032    case ENCTYPE_DES_CBC_CRC:
1033 	ctx->subkey->enctype = ENCTYPE_DES_CBC_RAW;
1034 	ctx->signalg = SGN_ALG_DES_MAC_MD5;
1035 	ctx->cksum_size = 8;
1036 	ctx->sealalg = SEAL_ALG_DES;
1037 
1038 	/* The encryption key is the session key XOR
1039 	 0xf0f0f0f0f0f0f0f0.  */
1040 	if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc)))
1041 	    goto fail;
1042 
1043 	for (i=0; i<ctx->enc->length; i++)
1044 	    ctx->enc->contents[i] ^= 0xf0;
1045 
1046 	goto copy_subkey_to_seq;
1047 
1048    case ENCTYPE_DES3_CBC_SHA1:
1049 	/* MIT extension */
1050 	ctx->subkey->enctype = ENCTYPE_DES3_CBC_RAW;
1051 	ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD;
1052 	ctx->cksum_size = 20;
1053 	ctx->sealalg = SEAL_ALG_DES3KD;
1054 
1055    copy_subkey:
1056 	code = krb5_copy_keyblock (context, ctx->subkey, &ctx->enc);
1057 	if (code)
1058 	    goto fail;
1059    copy_subkey_to_seq:
1060 	code = krb5_copy_keyblock (context, ctx->subkey, &ctx->seq);
1061 	if (code) {
1062 	    krb5_free_keyblock (context, ctx->enc);
1063 	    goto fail;
1064 	}
1065 	break;
1066 
1067    case ENCTYPE_ARCFOUR_HMAC:
1068 	/* Microsoft extension */
1069 	ctx->signalg = SGN_ALG_HMAC_MD5 ;
1070 	ctx->cksum_size = 8;
1071 	ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ;
1072 
1073 	goto copy_subkey;
1074 
1075    default:
1076        /* Fill some fields we shouldn't be using on this path
1077 	  with garbage.	 */
1078 	ctx->signalg = -10;
1079 	ctx->sealalg = -10;
1080 
1081 	ctx->proto = 1;
1082 	code = krb5int_c_mandatory_cksumtype(context, ctx->subkey->enctype,
1083 		&ctx->cksumtype);
1084 	if (code)
1085 	    goto fail;
1086 	code = krb5_c_checksum_length(context, ctx->cksumtype,
1087 				(size_t *)&ctx->cksum_size);
1088 	if (code)
1089 	    goto fail;
1090 	goto copy_subkey;
1091    }
1092 fail:
1093    if (code) {
1094 	*minor_status = code;
1095 	ret = GSS_S_FAILURE;
1096    }
1097 success:
1098    return (ret);
1099 }
1100 
1101 /*
1102  * new_connection
1103  *
1104  * Do the grunt work of setting up a new context.
1105  */
1106 static OM_uint32
1107 new_connection(
1108    OM_uint32 *minor_status,
1109    krb5_gss_cred_id_t cred,
1110    gss_ctx_id_t *context_handle,
1111    gss_name_t target_name,
1112    gss_OID mech_type,
1113    OM_uint32 req_flags,
1114    OM_uint32 time_req,
1115    gss_channel_bindings_t input_chan_bindings,
1116    gss_buffer_t input_token,
1117    gss_OID *actual_mech_type,
1118    gss_buffer_t output_token,
1119    OM_uint32 *ret_flags,
1120    OM_uint32 *time_rec,
1121    krb5_context context,
1122    int default_mech)
1123 {
1124    OM_uint32 major_status;
1125    krb5_error_code code;
1126    krb5_creds *k_cred;
1127    krb5_gss_ctx_id_rec *ctx, *ctx_free;
1128    krb5_timestamp now;
1129    gss_buffer_desc token;
1130 
1131    major_status = GSS_S_FAILURE;
1132    token.length = 0;
1133    token.value = NULL;
1134 
1135    /* make sure the cred is usable for init */
1136 
1137    if ((cred->usage != GSS_C_INITIATE) &&
1138 	(cred->usage != GSS_C_BOTH)) {
1139 	*minor_status = 0;
1140 	return(GSS_S_NO_CRED);
1141    }
1142 
1143    /* complain if the input token is non-null */
1144 
1145    if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
1146        *minor_status = 0;
1147 	return(GSS_S_DEFECTIVE_TOKEN);
1148    }
1149 
1150    /* create the ctx */
1151 
1152    if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
1153 	== NULL) {
1154 	*minor_status = ENOMEM;
1155 	return(GSS_S_FAILURE);
1156    }
1157 
1158    /* fill in the ctx */
1159    memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
1160    ctx_free = ctx;
1161    if ((code = krb5_auth_con_init(context, &ctx->auth_context)))
1162 	goto fail;
1163    krb5_auth_con_setflags(context, ctx->auth_context,
1164                           KRB5_AUTH_CONTEXT_DO_SEQUENCE);
1165    ctx->initiate = 1;
1166    ctx->gss_flags = (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
1167                       GSS_C_TRANS_FLAG |  GSS_C_PROT_READY_FLAG |
1168                       ((req_flags) & (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
1169                                       GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG)));
1170    ctx->seed_init = 0;
1171    ctx->big_endian = 0;	 /* all initiators do little-endian, as per spec */
1172    ctx->seqstate = 0;
1173    if ((code = krb5_timeofday(context, &now)))
1174 	goto fail;
1175 
1176    if (time_req == 0 || time_req == GSS_C_INDEFINITE) {
1177 	ctx->endtime = 0;
1178    } else {
1179       ctx->endtime = now + time_req;
1180    }
1181 
1182    if ((code = krb5_copy_principal(context, cred->princ, &ctx->here)))
1183 	goto fail;
1184 
1185    if ((code = krb5_copy_principal(context, (krb5_principal) target_name,
1186                                    &ctx->there)))
1187 	goto fail;
1188 
1189    code = get_credentials(context, cred, ctx->there, now,
1190                           ctx->endtime, &k_cred);
1191    if (code)
1192       goto fail;
1193 
1194    if (default_mech) {
1195 	mech_type = (gss_OID) gss_mech_krb5;
1196    }
1197       /* Solaris Kerberos:  we allocate the memory for mech_used here
1198        * because we store mech_used as a gss_OID and not a (gss_OID *)
1199        */
1200    ctx->mech_used.elements = malloc(mech_type->length);
1201    if ( (ctx->mech_used.elements) == NULL ) {
1202 	code = ENOMEM;
1203 	major_status = GSS_S_FAILURE;
1204 	goto fail;
1205    }
1206    ctx->mech_used.length = mech_type->length;
1207    memcpy(ctx->mech_used.elements, mech_type->elements, mech_type->length);
1208 
1209    /*
1210     * Now try to make it static if at all possible....
1211     */
1212    /* Solaris Kerberos:  our mech_used is part of the ctx structure */
1213    /* ctx->mech_used = krb5_gss_convert_static_mech_oid(&(ctx->mech_used)); */
1214    {
1215       /* gsskrb5 v1 */
1216 	krb5_ui_4 seq_temp;
1217 	if ((code = make_ap_req_v1(context, ctx,
1218                                  cred, k_cred, input_chan_bindings,
1219                                  mech_type, &token))) {
1220 	 if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) ||
1221              (code == KG_EMPTY_CCACHE))
1222             major_status = GSS_S_NO_CRED;
1223 	 if (code == KRB5KRB_AP_ERR_TKT_EXPIRED)
1224             major_status = GSS_S_CREDENTIALS_EXPIRED;
1225 	 goto fail;
1226 	}
1227 
1228 	krb5_auth_con_getlocalseqnumber(context, ctx->auth_context,
1229 		(krb5_int32 *)&seq_temp);
1230 	ctx->seq_send = seq_temp;
1231 	krb5_auth_con_getsendsubkey(context, ctx->auth_context,
1232                                   &ctx->subkey);
1233    }
1234 
1235    major_status = setup_enc(minor_status, ctx, context);
1236 
1237    if (k_cred) {
1238 	krb5_free_creds(context, k_cred);
1239 	k_cred = 0;
1240    }
1241 
1242    /* at this point, the context is constructed and valid,
1243 	hence, releaseable */
1244 
1245    /* intern the context handle */
1246 
1247    if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) {
1248 	code = G_VALIDATE_FAILED;
1249 	goto fail;
1250    }
1251    *context_handle = (gss_ctx_id_t) ctx;
1252    ctx_free = 0;
1253    /* compute time_rec */
1254    if (time_rec) {
1255 	if ((code = krb5_timeofday(context, &now)))
1256 	    goto fail;
1257 	*time_rec = ctx->endtime - now;
1258    }
1259 
1260    /* set the other returns */
1261    *output_token = token;
1262 
1263    if (ret_flags)
1264 	*ret_flags = ctx->gss_flags;
1265 
1266    if (actual_mech_type)
1267 	*actual_mech_type = mech_type;
1268 
1269    /* return successfully */
1270 
1271    *minor_status = 0;
1272    if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
1273 	ctx->established = 0;
1274 	return(GSS_S_CONTINUE_NEEDED);
1275    } else {
1276       ctx->seq_recv = ctx->seq_send;
1277 	g_order_init(&(ctx->seqstate), ctx->seq_recv,
1278                    (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
1279                    (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
1280 	ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
1281 	ctx->established = 1;
1282 	return(GSS_S_COMPLETE);
1283    }
1284 
1285 fail:
1286    if (ctx_free) {
1287 	if (ctx_free->auth_context)
1288 	   krb5_auth_con_free(context, ctx_free->auth_context);
1289 	if (ctx_free->here)
1290 	   krb5_free_principal(context, ctx_free->here);
1291 	if (ctx_free->there)
1292 	   krb5_free_principal(context, ctx_free->there);
1293 	if (ctx_free->subkey)
1294 	   krb5_free_keyblock(context, ctx_free->subkey);
1295 	xfree(ctx_free);
1296    } else {
1297         (void)krb5_gss_delete_sec_context_no_lock(context, minor_status,
1298 	    context_handle, NULL);
1299    }
1300 
1301    *minor_status = code;
1302    return (major_status);
1303 }
1304 
1305 /*
1306  * mutual_auth
1307  *
1308  * Handle the reply from the acceptor, if we're doing mutual auth.
1309  */
1310 static OM_uint32
1311 mutual_auth(
1312    OM_uint32 *minor_status,
1313    krb5_gss_cred_id_t cred,
1314    gss_ctx_id_t *context_handle,
1315    gss_name_t target_name,
1316    gss_OID mech_type,
1317    OM_uint32 req_flags,
1318    OM_uint32 time_req,
1319    gss_channel_bindings_t input_chan_bindings,
1320    gss_buffer_t input_token,
1321    gss_OID *actual_mech_type,
1322    gss_buffer_t output_token,
1323    OM_uint32 *ret_flags,
1324    OM_uint32 *time_rec,
1325    krb5_context context)
1326 {
1327    OM_uint32 major_status;
1328    unsigned char *ptr;
1329    char *sptr;
1330    krb5_data ap_rep;
1331    krb5_ap_rep_enc_part *ap_rep_data;
1332    krb5_timestamp now;
1333    krb5_gss_ctx_id_rec *ctx;
1334    krb5_error *krb_error;
1335    krb5_error_code code;
1336 
1337    major_status = GSS_S_FAILURE;
1338 
1339    /* validate the context handle */
1340    /*SUPPRESS 29*/
1341    if (! kg_validate_ctx_id(*context_handle)) {
1342 	*minor_status = (OM_uint32) G_VALIDATE_FAILED;
1343 	return(GSS_S_NO_CONTEXT);
1344    }
1345 
1346    ctx = (krb5_gss_ctx_id_rec *) *context_handle;
1347 
1348    /* make sure the context is non-established, and that certain
1349 	arguments are unchanged */
1350 
1351    if ((ctx->established) ||
1352 	((ctx->gss_flags & GSS_C_MUTUAL_FLAG) == 0)) {
1353 	code = KG_CONTEXT_ESTABLISHED;
1354 	goto fail;
1355    }
1356 
1357    if (! krb5_principal_compare(context, ctx->there,
1358 				(krb5_principal) target_name)) {
1359 	(void)krb5_gss_delete_sec_context_no_lock(context, minor_status,
1360                                         context_handle, NULL);
1361 	code = 0;
1362 	major_status = GSS_S_BAD_NAME;
1363 	goto fail;
1364    }
1365 
1366    /* verify the token and leave the AP_REP message in ap_rep */
1367 
1368    if (input_token == GSS_C_NO_BUFFER) {
1369 	(void)krb5_gss_delete_sec_context_no_lock(context, minor_status,
1370                                         context_handle, NULL);
1371 	code = 0;
1372 	major_status = GSS_S_DEFECTIVE_TOKEN;
1373 	goto fail;
1374    }
1375 
1376    ptr = (unsigned char *) input_token->value;
1377 
1378    if (g_verify_token_header(&ctx->mech_used,
1379                              (uint32_t *)&(ap_rep.length),
1380                              &ptr, KG_TOK_CTX_AP_REP,
1381                              input_token->length, 1)) {
1382 	if (g_verify_token_header(&ctx->mech_used,
1383 				(uint32_t *)&(ap_rep.length),
1384 				&ptr, KG_TOK_CTX_ERROR,
1385 				input_token->length, 1) == 0) {
1386 
1387 	 /* Handle a KRB_ERROR message from the server */
1388 
1389 	 sptr = (char *) ptr;		/* PC compiler bug */
1390 	 TREAD_STR(sptr, ap_rep.data, ap_rep.length);
1391 
1392 	 code = krb5_rd_error(context, &ap_rep, &krb_error);
1393 	 if (code)
1394             goto fail;
1395 	 if (krb_error->error)
1396             code = krb_error->error + ERROR_TABLE_BASE_krb5;
1397 	 else
1398             code = 0;
1399 	 krb5_free_error(context, krb_error);
1400 	 goto fail;
1401 	} else {
1402 	 *minor_status = 0;
1403 	 return(GSS_S_DEFECTIVE_TOKEN);
1404 	}
1405    }
1406 
1407    sptr = (char *) ptr;                      /* PC compiler bug */
1408    TREAD_STR(sptr, ap_rep.data, ap_rep.length);
1409 
1410    /* decode the ap_rep */
1411    if ((code = krb5_rd_rep(context, ctx->auth_context, &ap_rep,
1412                            &ap_rep_data))) {
1413 	/*
1414        * XXX A hack for backwards compatiblity.
1415 	* To be removed in 1999 -- proven
1416 	*/
1417       krb5_auth_con_setuseruserkey(context, ctx->auth_context,
1418                                    ctx->subkey);
1419 	if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep,
1420 			&ap_rep_data)))
1421 	 goto fail;
1422    }
1423 
1424    /* store away the sequence number */
1425    ctx->seq_recv = ap_rep_data->seq_number;
1426    g_order_init(&(ctx->seqstate), ctx->seq_recv,
1427 		(ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
1428 		(ctx->gss_flags & GSS_C_SEQUENCE_FLAG) !=0, ctx->proto);
1429 
1430    if (ctx->proto == 1 && ap_rep_data->subkey) {
1431 	/* Keep acceptor's subkey.  */
1432 	ctx->have_acceptor_subkey = 1;
1433 	code = krb5_copy_keyblock(context, ap_rep_data->subkey,
1434                                  &ctx->acceptor_subkey);
1435 	if (code)
1436            goto fail;
1437 	code = krb5int_c_mandatory_cksumtype(context,
1438 		ctx->acceptor_subkey->enctype,
1439 		&ctx->acceptor_subkey_cksumtype);
1440 	if (code)
1441            goto fail;
1442    }
1443 
1444    /* free the ap_rep_data */
1445    krb5_free_ap_rep_enc_part(context, ap_rep_data);
1446 
1447    /* set established */
1448    ctx->established = 1;
1449 
1450    /* set returns */
1451 
1452    if (time_rec) {
1453 	if ((code = krb5_timeofday(context, &now)))
1454 	 goto fail;
1455 	*time_rec = ctx->endtime - now;
1456    }
1457    if (ret_flags)
1458 	*ret_flags = ctx->gss_flags;
1459 
1460    if (actual_mech_type)
1461 	*actual_mech_type = mech_type;
1462 
1463    /* success */
1464 
1465    *minor_status = 0;
1466    return GSS_S_COMPLETE;
1467 
1468 fail:
1469    (void)krb5_gss_delete_sec_context_no_lock(context, minor_status,
1470        context_handle, NULL);
1471 
1472    *minor_status = code;
1473    return (major_status);
1474 }
1475 
1476 /*
1477  * krb5_gss_init_sec_context
1478  * This has been broken up into smaller chunks for CFX support.
1479  * MIT KRB5 1.3.2
1480  */
1481 OM_uint32
1482 krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle,
1483 			  context_handle, target_name, mech_type,
1484 			  req_flags, time_req, input_chan_bindings,
1485 			  input_token, actual_mech_type, output_token,
1486 			  ret_flags, time_rec)
1487     void      *ct;
1488     OM_uint32 *minor_status;
1489     gss_cred_id_t claimant_cred_handle;
1490     gss_ctx_id_t *context_handle;
1491     gss_name_t target_name;
1492     gss_OID mech_type;
1493     OM_uint32 req_flags;
1494     OM_uint32 time_req;
1495     gss_channel_bindings_t input_chan_bindings;
1496     gss_buffer_t input_token;
1497     gss_OID *actual_mech_type;
1498     gss_buffer_t output_token;
1499     OM_uint32 *ret_flags;
1500     OM_uint32 *time_rec;
1501 {
1502    krb5_context context;
1503    krb5_gss_cred_id_t cred = NULL;
1504    int err;
1505    int default_mech = 0;
1506    OM_uint32 major_status;
1507    OM_uint32 tmp_min_stat;
1508 
1509    /* Solaris Kerberos:  for MT safety, we avoid the use of a default
1510     * context via kg_get_context() */
1511 #if 0
1512    if (GSS_ERROR(kg_get_context(minor_status, &context)))
1513       return(GSS_S_FAILURE);
1514 #endif
1515 
1516    KRB5_LOG0(KRB5_INFO, "krb5_gss_init_sec_context() start\n");
1517 
1518    mutex_lock(&krb5_mutex);
1519    context = ct;
1520 
1521    /* set up return values so they can be "freed" successfully */
1522 
1523    major_status = GSS_S_FAILURE; /* Default major code */
1524    output_token->length = 0;
1525    output_token->value = NULL;
1526    if (actual_mech_type)
1527       *actual_mech_type = NULL;
1528 
1529    /* verify that the target_name is valid and usable */
1530 
1531    if (! kg_validate_name(target_name)) {
1532       *minor_status = (OM_uint32) G_VALIDATE_FAILED;
1533       major_status = (GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
1534       goto unlock;
1535    }
1536 
1537    /* verify the credential, or use the default */
1538    /*SUPPRESS 29*/
1539    if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
1540       /*
1541        * Solaris Kerberos: here we are using the Solaris specific
1542        * function get_default_cred() to handle the special case of a
1543        * root principal
1544        */
1545       major_status = get_default_cred(minor_status, ct, (gss_cred_id_t *)&cred);
1546       if (major_status && GSS_ERROR(major_status)) {
1547 	    KRB5_LOG(KRB5_ERR, "krb5_gss_init_sec_context() end, error "
1548 		   "major_status = %d\n", major_status);
1549 	    goto unlock;
1550       }
1551    } else {
1552       major_status = krb5_gss_validate_cred_no_lock(ct, minor_status,
1553 					     claimant_cred_handle);
1554       if (GSS_ERROR(major_status)) {
1555 	  KRB5_LOG(KRB5_ERR, "krb5_gss_init_sec_context() end, error "
1556 		   "major_status = %d\n", major_status);
1557 	  goto unlock;
1558       }
1559       cred = (krb5_gss_cred_id_t) claimant_cred_handle;
1560    }
1561 
1562    /* verify the mech_type */
1563 
1564    err = 0;
1565    if (mech_type == GSS_C_NULL_OID) {
1566        default_mech = 1;
1567        if (cred->rfc_mech) {
1568 	   mech_type = (gss_OID) gss_mech_krb5;
1569        } else if (cred->prerfc_mech) {
1570 	   mech_type = (gss_OID) gss_mech_krb5_old;
1571        } else {
1572 	   err = 1;
1573        }
1574    } else if (g_OID_equal(mech_type, gss_mech_krb5)) {
1575        if (!cred->rfc_mech)
1576 	   err = 1;
1577    } else if (g_OID_equal(mech_type, gss_mech_krb5_old)) {
1578        if (!cred->prerfc_mech)
1579 	   err = 1;
1580    } else {
1581        err = 1;
1582    }
1583 
1584    if (err) {
1585       *minor_status = 0;
1586       major_status = GSS_S_BAD_MECH;
1587       goto unlock;
1588    }
1589 
1590    /* is this a new connection or not? */
1591 
1592    /*SUPPRESS 29*/
1593    if (*context_handle == GSS_C_NO_CONTEXT) {
1594       major_status = new_connection(minor_status, cred, context_handle,
1595                                     target_name, mech_type, req_flags,
1596                                     time_req, input_chan_bindings,
1597                                     input_token, actual_mech_type,
1598                                     output_token, ret_flags, time_rec,
1599                                     context, default_mech);
1600    } else {
1601       major_status = mutual_auth(minor_status, cred, context_handle,
1602                                  target_name, mech_type, req_flags,
1603                                  time_req, input_chan_bindings,
1604                                  input_token, actual_mech_type,
1605                                  output_token, ret_flags, time_rec,
1606                                  context);
1607    }
1608 
1609 unlock:
1610    if (claimant_cred_handle == GSS_C_NO_CREDENTIAL && cred != NULL)
1611 	krb5_gss_release_cred_no_lock(context, &tmp_min_stat, (gss_cred_id_t *)cred);
1612 
1613    mutex_unlock(&krb5_mutex);
1614 
1615    KRB5_LOG1(KRB5_ERR, "krb5_gss_init_sec_context() end, error "
1616 	   "major_status = %d, minor_status = %d\n",
1617 	   major_status, *minor_status);
1618 
1619    return (major_status);
1620 }
1621