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