Home | History | Annotate | Download | only in mech
      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 (code == 0 && 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