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