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