1 /*
2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * Copyright 2000 by the Massachusetts Institute of Technology.
10  * All Rights Reserved.
11  *
12  * Export of this software from the United States of America may
13  *   require a specific license from the United States Government.
14  *   It is the responsibility of any person or organization contemplating
15  *   export to obtain such a license before exporting.
16  *
17  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18  * distribute this software and its documentation for any purpose and
19  * without fee is hereby granted, provided that the above copyright
20  * notice appear in all copies and that both that copyright notice and
21  * this permission notice appear in supporting documentation, and that
22  * the name of M.I.T. not be used in advertising or publicity pertaining
23  * to distribution of the software without specific, written prior
24  * permission.  Furthermore if you modify this software you must label
25  * your software as modified software and not distribute it in such a
26  * fashion that it might be confused with the original M.I.T. software.
27  * M.I.T. makes no representations about the suitability of
28  * this software for any purpose.  It is provided "as is" without express
29  * or implied warranty.
30  *
31  */
32 /*
33  * Copyright 1993 by OpenVision Technologies, Inc.
34  *
35  * Permission to use, copy, modify, distribute, and sell this software
36  * and its documentation for any purpose is hereby granted without fee,
37  * provided that the above copyright notice appears in all copies and
38  * that both that copyright notice and this permission notice appear in
39  * supporting documentation, and that the name of OpenVision not be used
40  * in advertising or publicity pertaining to distribution of the software
41  * without specific, written prior permission. OpenVision makes no
42  * representations about the suitability of this software for any
43  * purpose.  It is provided "as is" without express or implied warranty.
44  *
45  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
46  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
47  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
48  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
49  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
50  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
51  * PERFORMANCE OF THIS SOFTWARE.
52  */
53 
54 /*
55  * Copyright (C) 1998 by the FundsXpress, INC.
56  *
57  * All rights reserved.
58  *
59  * Export of this software from the United States of America may require
60  * a specific license from the United States Government.  It is the
61  * responsibility of any person or organization contemplating export to
62  * obtain such a license before exporting.
63  *
64  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
65  * distribute this software and its documentation for any purpose and
66  * without fee is hereby granted, provided that the above copyright
67  * notice appear in all copies and that both that copyright notice and
68  * this permission notice appear in supporting documentation, and that
69  * the name of FundsXpress. not be used in advertising or publicity pertaining
70  * to distribution of the software without specific, written prior
71  * permission.  FundsXpress makes no representations about the suitability of
72  * this software for any purpose.  It is provided "as is" without express
73  * or implied warranty.
74  *
75  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
76  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
77  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
78  */
79 
80 #include <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 	/*
912 	 * Solaris Kerberos
913 	 * Use krb5_getuid() to select the mechanism to obtain the uid.
914 	 */
915 	uid_t uid = krb5_getuid();
916 	krb5_context context = (krb5_context)ct;
917 
918 	KRB5_LOG0(KRB5_INFO, "get_default_cred() start\n");
919 
920 	/* Get the default cred for user */
921 	if (((major = kg_get_defcred(minor_status, cred_handle)) != NULL) &&
922 	    GSS_ERROR(major)) {
923 
924 		/* If we're not root we're done */
925    		if (uid != ROOT_UID)
926 	 		return (major);
927 
928 		/*
929 		 * Try and get root's cred in the cache using keytab.
930 		 *
931 		 * First try "root" and then try "host" - this allows
932 		 * Secure NFS to use the host principal for mounting if
933 		 * there is no root principal.
934 		 *
935 		 * Then try "host/<anything>" to match any instance (needed
936 		 * for DHCP clients).
937 		 */
938 		major = load_root_cred_using_keytab(minor_status,
939 						    context, "root", 1);
940 
941 		if (major != GSS_S_COMPLETE)
942 			major = load_root_cred_using_keytab(minor_status,
943 							    context, "host", 1);
944 		if (major != GSS_S_COMPLETE)
945 			major = load_root_cred_using_keytab(minor_status,
946 							    context, "host", 0);
947 
948 		if (major != GSS_S_COMPLETE)
949 			return (major);
950 
951 		/* We should have valid tgt now in the cache, so get it. */
952 		major = kg_get_defcred(minor_status, cred_handle);
953 
954 		return (major);
955       	}
956 
957 	/* We've got a gss cred handle that is a kerberos cred handle. */
958 	cred = (krb5_gss_cred_id_t)*cred_handle;
959 
960 	/* If we can't get the time, assume the worst. */
961 	if (krb5_timeofday(context, &now)) {
962 		(void) krb5_gss_release_cred_no_lock(ct, &mntmp, cred_handle);
963 		return (GSS_S_CREDENTIALS_EXPIRED);
964 	}
965 
966 	/* If root's cred has expired re-get it */
967 	if (cred->tgt_expire < now + MIN_REFRESH_TIME && uid == ROOT_UID) {
968 		(void) krb5_gss_release_cred_no_lock(ct, &mntmp, cred_handle);
969 
970 		major = load_root_cred_using_keytab(minor_status,
971 						    context, "root", 1);
972 
973 		if (major != GSS_S_COMPLETE)
974 			major = load_root_cred_using_keytab(minor_status,
975 							    context, "host", 1);
976 
977 		if (major != GSS_S_COMPLETE)
978 			major = load_root_cred_using_keytab(minor_status,
979 							    context, "host", 0);
980 
981 		if (major != GSS_S_COMPLETE)
982 			return (major);
983 
984 		major = kg_get_defcred(minor_status, cred_handle);
985 		if (major != GSS_S_COMPLETE)
986 			return (major);
987 
988 	/* Any body else is SOL unless we can renew their credential cache */
989 	} else if ((cred->tgt_expire < now + MIN_RENEW_TIME) &&
990 			(cred->tgt_expire > now)) {
991 		(void) krb5_gss_release_cred_no_lock(ct, &mntmp, cred_handle);
992 
993 		major = renew_ccache(minor_status, context, uid);
994 		if ((major != GSS_S_COMPLETE) &&
995 			(major != KDC_ERR_BADOPTION))
996 			return (major);
997 
998 		major = kg_get_defcred(minor_status, cred_handle);
999 		if (major != GSS_S_COMPLETE)
1000 			return (major);
1001 
1002 	}
1003 
1004 	/* Otherwise we got non expired creds */
1005 
1006 	KRB5_LOG0(KRB5_INFO, "get_default_cred() end\n");
1007 
1008 	return (GSS_S_COMPLETE);
1009 }
1010 
1011 /*
1012  * setup_enc
1013  *
1014  * Fill in the encryption descriptors.	Called after AP-REQ is made.
1015  */
1016 static OM_uint32
1017 setup_enc(
1018    OM_uint32 *minor_status,
1019    krb5_gss_ctx_id_rec *ctx,
1020    krb5_context context)
1021 {
1022    krb5_error_code code;
1023    OM_uint32 ret = GSS_S_COMPLETE;
1024    int i;
1025 
1026    ctx->have_acceptor_subkey = 0;
1027    ctx->proto = 0;
1028    ctx->cksumtype = 0;
1029 
1030    KRB5_LOG(KRB5_ERR, "setup_enc() enctype = %d\n",
1031 	ctx->subkey->enctype);
1032 
1033    switch(ctx->subkey->enctype) {
1034    case ENCTYPE_DES_CBC_MD5:
1035    case ENCTYPE_DES_CBC_MD4:
1036    case ENCTYPE_DES_CBC_CRC:
1037 	ctx->subkey->enctype = ENCTYPE_DES_CBC_RAW;
1038 	ctx->signalg = SGN_ALG_DES_MAC_MD5;
1039 	ctx->cksum_size = 8;
1040 	ctx->sealalg = SEAL_ALG_DES;
1041 
1042 	/* The encryption key is the session key XOR
1043 	 0xf0f0f0f0f0f0f0f0.  */
1044 	if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc)))
1045 	    goto fail;
1046 
1047 	for (i=0; i<ctx->enc->length; i++)
1048 	    ctx->enc->contents[i] ^= 0xf0;
1049 
1050 	goto copy_subkey_to_seq;
1051 
1052    case ENCTYPE_DES3_CBC_SHA1:
1053 	/* MIT extension */
1054 	ctx->subkey->enctype = ENCTYPE_DES3_CBC_RAW;
1055 	ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD;
1056 	ctx->cksum_size = 20;
1057 	ctx->sealalg = SEAL_ALG_DES3KD;
1058 
1059    copy_subkey:
1060 	code = krb5_copy_keyblock (context, ctx->subkey, &ctx->enc);
1061 	if (code)
1062 	    goto fail;
1063    copy_subkey_to_seq:
1064 	code = krb5_copy_keyblock (context, ctx->subkey, &ctx->seq);
1065 	if (code) {
1066 	    krb5_free_keyblock (context, ctx->enc);
1067 	    goto fail;
1068 	}
1069 	break;
1070 
1071    case ENCTYPE_ARCFOUR_HMAC:
1072 	/* Microsoft extension */
1073 	ctx->signalg = SGN_ALG_HMAC_MD5 ;
1074 	ctx->cksum_size = 8;
1075 	ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ;
1076 
1077 	goto copy_subkey;
1078 
1079    default:
1080        /* Fill some fields we shouldn't be using on this path
1081 	  with garbage.	 */
1082 	ctx->signalg = -10;
1083 	ctx->sealalg = -10;
1084 
1085 	ctx->proto = 1;
1086 	code = krb5int_c_mandatory_cksumtype(context, ctx->subkey->enctype,
1087 		&ctx->cksumtype);
1088 	if (code)
1089 	    goto fail;
1090 	code = krb5_c_checksum_length(context, ctx->cksumtype,
1091 				(size_t *)&ctx->cksum_size);
1092 	if (code)
1093 	    goto fail;
1094 	goto copy_subkey;
1095    }
1096 fail:
1097    if (code) {
1098 	*minor_status = code;
1099 	ret = GSS_S_FAILURE;
1100    }
1101 success:
1102    return (ret);
1103 }
1104 
1105 /*
1106  * new_connection
1107  *
1108  * Do the grunt work of setting up a new context.
1109  */
1110 static OM_uint32
1111 new_connection(
1112    OM_uint32 *minor_status,
1113    krb5_gss_cred_id_t cred,
1114    gss_ctx_id_t *context_handle,
1115    gss_name_t target_name,
1116    gss_OID mech_type,
1117    OM_uint32 req_flags,
1118    OM_uint32 time_req,
1119    gss_channel_bindings_t input_chan_bindings,
1120    gss_buffer_t input_token,
1121    gss_OID *actual_mech_type,
1122    gss_buffer_t output_token,
1123    OM_uint32 *ret_flags,
1124    OM_uint32 *time_rec,
1125    krb5_context context,
1126    int default_mech)
1127 {
1128    OM_uint32 major_status;
1129    krb5_error_code code;
1130    krb5_creds *k_cred;
1131    krb5_gss_ctx_id_rec *ctx, *ctx_free;
1132    krb5_timestamp now;
1133    gss_buffer_desc token;
1134 
1135    major_status = GSS_S_FAILURE;
1136    token.length = 0;
1137    token.value = NULL;
1138 
1139    /* make sure the cred is usable for init */
1140 
1141    if ((cred->usage != GSS_C_INITIATE) &&
1142 	(cred->usage != GSS_C_BOTH)) {
1143 	*minor_status = 0;
1144 	return(GSS_S_NO_CRED);
1145    }
1146 
1147    /* complain if the input token is non-null */
1148 
1149    if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
1150        *minor_status = 0;
1151 	return(GSS_S_DEFECTIVE_TOKEN);
1152    }
1153 
1154    /* create the ctx */
1155 
1156    if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
1157 	== NULL) {
1158 	*minor_status = ENOMEM;
1159 	return(GSS_S_FAILURE);
1160    }
1161 
1162    /* fill in the ctx */
1163    memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
1164    ctx_free = ctx;
1165    if ((code = krb5_auth_con_init(context, &ctx->auth_context)))
1166 	goto fail;
1167    krb5_auth_con_setflags(context, ctx->auth_context,
1168                           KRB5_AUTH_CONTEXT_DO_SEQUENCE);
1169    ctx->initiate = 1;
1170    ctx->gss_flags = (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
1171                       GSS_C_TRANS_FLAG |  GSS_C_PROT_READY_FLAG |
1172                       ((req_flags) & (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
1173                                       GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG)));
1174    ctx->seed_init = 0;
1175    ctx->big_endian = 0;	 /* all initiators do little-endian, as per spec */
1176    ctx->seqstate = 0;
1177    if ((code = krb5_timeofday(context, &now)))
1178 	goto fail;
1179 
1180    if (time_req == 0 || time_req == GSS_C_INDEFINITE) {
1181 	ctx->endtime = 0;
1182    } else {
1183       ctx->endtime = now + time_req;
1184    }
1185 
1186    if ((code = krb5_copy_principal(context, cred->princ, &ctx->here)))
1187 	goto fail;
1188 
1189    if ((code = krb5_copy_principal(context, (krb5_principal) target_name,
1190                                    &ctx->there)))
1191 	goto fail;
1192 
1193    code = get_credentials(context, cred, ctx->there, now,
1194                           ctx->endtime, &k_cred);
1195    if (code)
1196       goto fail;
1197 
1198    if (default_mech) {
1199 	mech_type = (gss_OID) gss_mech_krb5;
1200    }
1201       /* Solaris Kerberos:  we allocate the memory for mech_used here
1202        * because we store mech_used as a gss_OID and not a (gss_OID *)
1203        */
1204    ctx->mech_used.elements = malloc(mech_type->length);
1205    if ( (ctx->mech_used.elements) == NULL ) {
1206 	code = ENOMEM;
1207 	major_status = GSS_S_FAILURE;
1208 	goto fail;
1209    }
1210    ctx->mech_used.length = mech_type->length;
1211    memcpy(ctx->mech_used.elements, mech_type->elements, mech_type->length);
1212 
1213    /*
1214     * Now try to make it static if at all possible....
1215     */
1216    /* Solaris Kerberos:  our mech_used is part of the ctx structure */
1217    /* ctx->mech_used = krb5_gss_convert_static_mech_oid(&(ctx->mech_used)); */
1218    {
1219       /* gsskrb5 v1 */
1220 	krb5_ui_4 seq_temp;
1221 	if ((code = make_ap_req_v1(context, ctx,
1222                                  cred, k_cred, input_chan_bindings,
1223                                  mech_type, &token))) {
1224 	 if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) ||
1225              (code == KG_EMPTY_CCACHE))
1226             major_status = GSS_S_NO_CRED;
1227 	 if (code == KRB5KRB_AP_ERR_TKT_EXPIRED)
1228             major_status = GSS_S_CREDENTIALS_EXPIRED;
1229 	 goto fail;
1230 	}
1231 
1232 	krb5_auth_con_getlocalseqnumber(context, ctx->auth_context,
1233 		(krb5_int32 *)&seq_temp);
1234 	ctx->seq_send = seq_temp;
1235 	krb5_auth_con_getsendsubkey(context, ctx->auth_context,
1236                                   &ctx->subkey);
1237    }
1238 
1239    major_status = setup_enc(minor_status, ctx, context);
1240 
1241    if (k_cred) {
1242 	krb5_free_creds(context, k_cred);
1243 	k_cred = 0;
1244    }
1245 
1246    /* at this point, the context is constructed and valid,
1247 	hence, releaseable */
1248 
1249    /* intern the context handle */
1250 
1251    if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) {
1252 	code = G_VALIDATE_FAILED;
1253 	goto fail;
1254    }
1255    *context_handle = (gss_ctx_id_t) ctx;
1256    ctx_free = 0;
1257    /* compute time_rec */
1258    if (time_rec) {
1259 	if ((code = krb5_timeofday(context, &now)))
1260 	    goto fail;
1261 	*time_rec = ctx->endtime - now;
1262    }
1263 
1264    /* set the other returns */
1265    *output_token = token;
1266 
1267    if (ret_flags)
1268 	*ret_flags = ctx->gss_flags;
1269 
1270    if (actual_mech_type)
1271 	*actual_mech_type = mech_type;
1272 
1273    /* return successfully */
1274 
1275    *minor_status = 0;
1276    if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
1277 	ctx->established = 0;
1278 	return(GSS_S_CONTINUE_NEEDED);
1279    } else {
1280       ctx->seq_recv = ctx->seq_send;
1281 	g_order_init(&(ctx->seqstate), ctx->seq_recv,
1282                    (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
1283                    (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
1284 	ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
1285 	ctx->established = 1;
1286 	return(GSS_S_COMPLETE);
1287    }
1288 
1289 fail:
1290    if (ctx_free) {
1291 	if (ctx_free->auth_context)
1292 	   krb5_auth_con_free(context, ctx_free->auth_context);
1293 	if (ctx_free->here)
1294 	   krb5_free_principal(context, ctx_free->here);
1295 	if (ctx_free->there)
1296 	   krb5_free_principal(context, ctx_free->there);
1297 	if (ctx_free->subkey)
1298 	   krb5_free_keyblock(context, ctx_free->subkey);
1299 	xfree(ctx_free);
1300    } else {
1301         (void)krb5_gss_delete_sec_context_no_lock(context, minor_status,
1302 	    context_handle, NULL);
1303    }
1304 
1305    *minor_status = code;
1306    return (major_status);
1307 }
1308 
1309 /*
1310  * mutual_auth
1311  *
1312  * Handle the reply from the acceptor, if we're doing mutual auth.
1313  */
1314 static OM_uint32
1315 mutual_auth(
1316    OM_uint32 *minor_status,
1317    krb5_gss_cred_id_t cred,
1318    gss_ctx_id_t *context_handle,
1319    gss_name_t target_name,
1320    gss_OID mech_type,
1321    OM_uint32 req_flags,
1322    OM_uint32 time_req,
1323    gss_channel_bindings_t input_chan_bindings,
1324    gss_buffer_t input_token,
1325    gss_OID *actual_mech_type,
1326    gss_buffer_t output_token,
1327    OM_uint32 *ret_flags,
1328    OM_uint32 *time_rec,
1329    krb5_context context)
1330 {
1331    OM_uint32 major_status;
1332    unsigned char *ptr;
1333    char *sptr;
1334    krb5_data ap_rep;
1335    krb5_ap_rep_enc_part *ap_rep_data;
1336    krb5_timestamp now;
1337    krb5_gss_ctx_id_rec *ctx;
1338    krb5_error *krb_error;
1339    krb5_error_code code;
1340 
1341    major_status = GSS_S_FAILURE;
1342 
1343    /* validate the context handle */
1344    /*SUPPRESS 29*/
1345    if (! kg_validate_ctx_id(*context_handle)) {
1346 	*minor_status = (OM_uint32) G_VALIDATE_FAILED;
1347 	return(GSS_S_NO_CONTEXT);
1348    }
1349 
1350    ctx = (krb5_gss_ctx_id_rec *) *context_handle;
1351 
1352    /* make sure the context is non-established, and that certain
1353 	arguments are unchanged */
1354 
1355    if ((ctx->established) ||
1356 	((ctx->gss_flags & GSS_C_MUTUAL_FLAG) == 0)) {
1357 	code = KG_CONTEXT_ESTABLISHED;
1358 	goto fail;
1359    }
1360 
1361    if (! krb5_principal_compare(context, ctx->there,
1362 				(krb5_principal) target_name)) {
1363 	(void)krb5_gss_delete_sec_context_no_lock(context, minor_status,
1364                                         context_handle, NULL);
1365 	code = 0;
1366 	major_status = GSS_S_BAD_NAME;
1367 	goto fail;
1368    }
1369 
1370    /* verify the token and leave the AP_REP message in ap_rep */
1371 
1372    if (input_token == GSS_C_NO_BUFFER) {
1373 	(void)krb5_gss_delete_sec_context_no_lock(context, minor_status,
1374                                         context_handle, NULL);
1375 	code = 0;
1376 	major_status = GSS_S_DEFECTIVE_TOKEN;
1377 	goto fail;
1378    }
1379 
1380    ptr = (unsigned char *) input_token->value;
1381 
1382    if (g_verify_token_header(&ctx->mech_used,
1383                              (uint32_t *)&(ap_rep.length),
1384                              &ptr, KG_TOK_CTX_AP_REP,
1385                              input_token->length, 1)) {
1386 	if (g_verify_token_header(&ctx->mech_used,
1387 				(uint32_t *)&(ap_rep.length),
1388 				&ptr, KG_TOK_CTX_ERROR,
1389 				input_token->length, 1) == 0) {
1390 
1391 	 /* Handle a KRB_ERROR message from the server */
1392 
1393 	 sptr = (char *) ptr;		/* PC compiler bug */
1394 	 TREAD_STR(sptr, ap_rep.data, ap_rep.length);
1395 
1396 	 code = krb5_rd_error(context, &ap_rep, &krb_error);
1397 	 if (code)
1398             goto fail;
1399 	 if (krb_error->error)
1400             code = krb_error->error + ERROR_TABLE_BASE_krb5;
1401 	 else
1402             code = 0;
1403 	 krb5_free_error(context, krb_error);
1404 	 goto fail;
1405 	} else {
1406 	 *minor_status = 0;
1407 	 return(GSS_S_DEFECTIVE_TOKEN);
1408 	}
1409    }
1410 
1411    sptr = (char *) ptr;                      /* PC compiler bug */
1412    TREAD_STR(sptr, ap_rep.data, ap_rep.length);
1413 
1414    /* decode the ap_rep */
1415    if ((code = krb5_rd_rep(context, ctx->auth_context, &ap_rep,
1416                            &ap_rep_data))) {
1417 	/*
1418        * XXX A hack for backwards compatiblity.
1419 	* To be removed in 1999 -- proven
1420 	*/
1421       krb5_auth_con_setuseruserkey(context, ctx->auth_context,
1422                                    ctx->subkey);
1423 	if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep,
1424 			&ap_rep_data)))
1425 	 goto fail;
1426    }
1427 
1428    /* store away the sequence number */
1429    ctx->seq_recv = ap_rep_data->seq_number;
1430    g_order_init(&(ctx->seqstate), ctx->seq_recv,
1431 		(ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
1432 		(ctx->gss_flags & GSS_C_SEQUENCE_FLAG) !=0, ctx->proto);
1433 
1434    if (ctx->proto == 1 && ap_rep_data->subkey) {
1435 	/* Keep acceptor's subkey.  */
1436 	ctx->have_acceptor_subkey = 1;
1437 	code = krb5_copy_keyblock(context, ap_rep_data->subkey,
1438                                  &ctx->acceptor_subkey);
1439 	if (code)
1440            goto fail;
1441 	code = krb5int_c_mandatory_cksumtype(context,
1442 		ctx->acceptor_subkey->enctype,
1443 		&ctx->acceptor_subkey_cksumtype);
1444 	if (code)
1445            goto fail;
1446    }
1447 
1448    /* free the ap_rep_data */
1449    krb5_free_ap_rep_enc_part(context, ap_rep_data);
1450 
1451    /* set established */
1452    ctx->established = 1;
1453 
1454    /* set returns */
1455 
1456    if (time_rec) {
1457 	if ((code = krb5_timeofday(context, &now)))
1458 	 goto fail;
1459 	*time_rec = ctx->endtime - now;
1460    }
1461    if (ret_flags)
1462 	*ret_flags = ctx->gss_flags;
1463 
1464    if (actual_mech_type)
1465 	*actual_mech_type = mech_type;
1466 
1467    /* success */
1468 
1469    *minor_status = 0;
1470    return GSS_S_COMPLETE;
1471 
1472 fail:
1473    (void)krb5_gss_delete_sec_context_no_lock(context, minor_status,
1474        context_handle, NULL);
1475 
1476    *minor_status = code;
1477    return (major_status);
1478 }
1479 
1480 /*
1481  * krb5_gss_init_sec_context
1482  * This has been broken up into smaller chunks for CFX support.
1483  * MIT KRB5 1.3.2
1484  */
1485 OM_uint32
1486 krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle,
1487 			  context_handle, target_name, mech_type,
1488 			  req_flags, time_req, input_chan_bindings,
1489 			  input_token, actual_mech_type, output_token,
1490 			  ret_flags, time_rec)
1491     void      *ct;
1492     OM_uint32 *minor_status;
1493     gss_cred_id_t claimant_cred_handle;
1494     gss_ctx_id_t *context_handle;
1495     gss_name_t target_name;
1496     gss_OID mech_type;
1497     OM_uint32 req_flags;
1498     OM_uint32 time_req;
1499     gss_channel_bindings_t input_chan_bindings;
1500     gss_buffer_t input_token;
1501     gss_OID *actual_mech_type;
1502     gss_buffer_t output_token;
1503     OM_uint32 *ret_flags;
1504     OM_uint32 *time_rec;
1505 {
1506    krb5_context context;
1507    krb5_gss_cred_id_t cred = NULL;
1508    int err;
1509    int default_mech = 0;
1510    OM_uint32 major_status;
1511    OM_uint32 tmp_min_stat;
1512 
1513    /* Solaris Kerberos:  for MT safety, we avoid the use of a default
1514     * context via kg_get_context() */
1515 #if 0
1516    if (GSS_ERROR(kg_get_context(minor_status, &context)))
1517       return(GSS_S_FAILURE);
1518 #endif
1519 
1520    KRB5_LOG0(KRB5_INFO, "krb5_gss_init_sec_context() start\n");
1521 
1522    mutex_lock(&krb5_mutex);
1523    context = ct;
1524 
1525    /* set up return values so they can be "freed" successfully */
1526 
1527    major_status = GSS_S_FAILURE; /* Default major code */
1528    output_token->length = 0;
1529    output_token->value = NULL;
1530    if (actual_mech_type)
1531       *actual_mech_type = NULL;
1532 
1533    /* verify that the target_name is valid and usable */
1534 
1535    if (! kg_validate_name(target_name)) {
1536       *minor_status = (OM_uint32) G_VALIDATE_FAILED;
1537       major_status = (GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
1538       goto unlock;
1539    }
1540 
1541    /* verify the credential, or use the default */
1542    /*SUPPRESS 29*/
1543    if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
1544       /*
1545        * Solaris Kerberos: here we are using the Solaris specific
1546        * function get_default_cred() to handle the special case of a
1547        * root principal
1548        */
1549       major_status = get_default_cred(minor_status, ct, (gss_cred_id_t *)&cred);
1550       if (major_status && GSS_ERROR(major_status)) {
1551 	    KRB5_LOG(KRB5_ERR, "krb5_gss_init_sec_context() end, error "
1552 		   "major_status = %d\n", major_status);
1553 	    goto unlock;
1554       }
1555    } else {
1556       major_status = krb5_gss_validate_cred_no_lock(ct, minor_status,
1557 					     claimant_cred_handle);
1558       if (GSS_ERROR(major_status)) {
1559 	  KRB5_LOG(KRB5_ERR, "krb5_gss_init_sec_context() end, error "
1560 		   "major_status = %d\n", major_status);
1561 	  goto unlock;
1562       }
1563       cred = (krb5_gss_cred_id_t) claimant_cred_handle;
1564    }
1565 
1566    /* verify the mech_type */
1567 
1568    err = 0;
1569    if (mech_type == GSS_C_NULL_OID) {
1570        default_mech = 1;
1571        if (cred->rfc_mech) {
1572 	   mech_type = (gss_OID) gss_mech_krb5;
1573        } else if (cred->prerfc_mech) {
1574 	   mech_type = (gss_OID) gss_mech_krb5_old;
1575        } else {
1576 	   err = 1;
1577        }
1578    } else if (g_OID_equal(mech_type, gss_mech_krb5)) {
1579        if (!cred->rfc_mech)
1580 	   err = 1;
1581    } else if (g_OID_equal(mech_type, gss_mech_krb5_old)) {
1582        if (!cred->prerfc_mech)
1583 	   err = 1;
1584    } else {
1585        err = 1;
1586    }
1587 
1588    if (err) {
1589       *minor_status = 0;
1590       major_status = GSS_S_BAD_MECH;
1591       goto unlock;
1592    }
1593 
1594    /* is this a new connection or not? */
1595 
1596    /*SUPPRESS 29*/
1597    if (*context_handle == GSS_C_NO_CONTEXT) {
1598       major_status = new_connection(minor_status, cred, context_handle,
1599                                     target_name, mech_type, req_flags,
1600                                     time_req, input_chan_bindings,
1601                                     input_token, actual_mech_type,
1602                                     output_token, ret_flags, time_rec,
1603                                     context, default_mech);
1604    } else {
1605       major_status = mutual_auth(minor_status, cred, context_handle,
1606                                  target_name, mech_type, req_flags,
1607                                  time_req, input_chan_bindings,
1608                                  input_token, actual_mech_type,
1609                                  output_token, ret_flags, time_rec,
1610                                  context);
1611    }
1612 
1613 unlock:
1614    if (claimant_cred_handle == GSS_C_NO_CREDENTIAL && cred != NULL)
1615 	krb5_gss_release_cred_no_lock(context, &tmp_min_stat, (gss_cred_id_t *)cred);
1616 
1617    mutex_unlock(&krb5_mutex);
1618 
1619    KRB5_LOG1(KRB5_ERR, "krb5_gss_init_sec_context() end, error "
1620 	   "major_status = %d, minor_status = %d\n",
1621 	   major_status, *minor_status);
1622 
1623    return (major_status);
1624 }
1625