17c478bd9Sstevel@tonic-gate /*
2*159d09a2SMark Phalan  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate 
77c478bd9Sstevel@tonic-gate /*
8ab9b2e15Sgtb  * Copyright 2000,2002, 2003 by the Massachusetts Institute of Technology.
97c478bd9Sstevel@tonic-gate  * All Rights Reserved.
107c478bd9Sstevel@tonic-gate  *
117c478bd9Sstevel@tonic-gate  * Export of this software from the United States of America may
127c478bd9Sstevel@tonic-gate  *   require a specific license from the United States Government.
137c478bd9Sstevel@tonic-gate  *   It is the responsibility of any person or organization contemplating
147c478bd9Sstevel@tonic-gate  *   export to obtain such a license before exporting.
15ab9b2e15Sgtb  *
167c478bd9Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
177c478bd9Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
187c478bd9Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
197c478bd9Sstevel@tonic-gate  * notice appear in all copies and that both that copyright notice and
207c478bd9Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
217c478bd9Sstevel@tonic-gate  * the name of M.I.T. not be used in advertising or publicity pertaining
227c478bd9Sstevel@tonic-gate  * to distribution of the software without specific, written prior
237c478bd9Sstevel@tonic-gate  * permission.  Furthermore if you modify this software you must label
247c478bd9Sstevel@tonic-gate  * your software as modified software and not distribute it in such a
257c478bd9Sstevel@tonic-gate  * fashion that it might be confused with the original M.I.T. software.
267c478bd9Sstevel@tonic-gate  * M.I.T. makes no representations about the suitability of
277c478bd9Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
287c478bd9Sstevel@tonic-gate  * or implied warranty.
29ab9b2e15Sgtb  *
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate /*
327c478bd9Sstevel@tonic-gate  * Copyright 1993 by OpenVision Technologies, Inc.
33ab9b2e15Sgtb  *
347c478bd9Sstevel@tonic-gate  * Permission to use, copy, modify, distribute, and sell this software
357c478bd9Sstevel@tonic-gate  * and its documentation for any purpose is hereby granted without fee,
367c478bd9Sstevel@tonic-gate  * provided that the above copyright notice appears in all copies and
377c478bd9Sstevel@tonic-gate  * that both that copyright notice and this permission notice appear in
387c478bd9Sstevel@tonic-gate  * supporting documentation, and that the name of OpenVision not be used
397c478bd9Sstevel@tonic-gate  * in advertising or publicity pertaining to distribution of the software
407c478bd9Sstevel@tonic-gate  * without specific, written prior permission. OpenVision makes no
417c478bd9Sstevel@tonic-gate  * representations about the suitability of this software for any
427c478bd9Sstevel@tonic-gate  * purpose.  It is provided "as is" without express or implied warranty.
43ab9b2e15Sgtb  *
447c478bd9Sstevel@tonic-gate  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
457c478bd9Sstevel@tonic-gate  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
467c478bd9Sstevel@tonic-gate  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
477c478bd9Sstevel@tonic-gate  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
487c478bd9Sstevel@tonic-gate  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
497c478bd9Sstevel@tonic-gate  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
507c478bd9Sstevel@tonic-gate  * PERFORMANCE OF THIS SOFTWARE.
517c478bd9Sstevel@tonic-gate  */
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate /*
547c478bd9Sstevel@tonic-gate  * Copyright (C) 1998 by the FundsXpress, INC.
55ab9b2e15Sgtb  *
567c478bd9Sstevel@tonic-gate  * All rights reserved.
57ab9b2e15Sgtb  *
587c478bd9Sstevel@tonic-gate  * Export of this software from the United States of America may require
597c478bd9Sstevel@tonic-gate  * a specific license from the United States Government.  It is the
607c478bd9Sstevel@tonic-gate  * responsibility of any person or organization contemplating export to
617c478bd9Sstevel@tonic-gate  * obtain such a license before exporting.
62ab9b2e15Sgtb  *
637c478bd9Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
647c478bd9Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
657c478bd9Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
667c478bd9Sstevel@tonic-gate  * notice appear in all copies and that both that copyright notice and
677c478bd9Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
687c478bd9Sstevel@tonic-gate  * the name of FundsXpress. not be used in advertising or publicity pertaining
697c478bd9Sstevel@tonic-gate  * to distribution of the software without specific, written prior
707c478bd9Sstevel@tonic-gate  * permission.  FundsXpress makes no representations about the suitability of
717c478bd9Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
727c478bd9Sstevel@tonic-gate  * or implied warranty.
73ab9b2e15Sgtb  *
747c478bd9Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
757c478bd9Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
767c478bd9Sstevel@tonic-gate  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
777c478bd9Sstevel@tonic-gate  */
787c478bd9Sstevel@tonic-gate 
79ab9b2e15Sgtb #include "k5-int.h"
80ab9b2e15Sgtb #include "gss_libinit.h"
81*159d09a2SMark Phalan #include "gssapiP_krb5.h"
82ab9b2e15Sgtb #include "mglueP.h"
83ab9b2e15Sgtb #ifdef HAVE_MEMORY_H
847c478bd9Sstevel@tonic-gate #include <memory.h>
85ab9b2e15Sgtb #endif
867c478bd9Sstevel@tonic-gate #include <stdlib.h>
877c478bd9Sstevel@tonic-gate #include <assert.h>
887c478bd9Sstevel@tonic-gate 
89ab9b2e15Sgtb /* Solaris Kerberos start */
90ab9b2e15Sgtb static OM_uint32 get_default_cred(OM_uint32 *, void *, gss_cred_id_t *);
91ab9b2e15Sgtb /* Solaris Kerberos end */
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate /*
94*159d09a2SMark Phalan  * $Id: init_sec_context.c 18721 2006-10-16 16:18:29Z epeisach $
957c478bd9Sstevel@tonic-gate  */
96ab9b2e15Sgtb 
97ab9b2e15Sgtb /* XXX This is for debugging only!!!  Should become a real bitfield
98ab9b2e15Sgtb    at some point */
997c478bd9Sstevel@tonic-gate int krb5_gss_dbg_client_expcreds = 0;
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate /*
1027c478bd9Sstevel@tonic-gate  * Common code which fetches the correct krb5 credentials from the
1037c478bd9Sstevel@tonic-gate  * ccache.
1047c478bd9Sstevel@tonic-gate  */
1057c478bd9Sstevel@tonic-gate static krb5_error_code get_credentials(context, cred, server, now,
1067c478bd9Sstevel@tonic-gate 				       endtime, out_creds)
1077c478bd9Sstevel@tonic-gate     krb5_context context;
1087c478bd9Sstevel@tonic-gate     krb5_gss_cred_id_t cred;
1097c478bd9Sstevel@tonic-gate     krb5_principal server;
1107c478bd9Sstevel@tonic-gate     krb5_timestamp now;
1117c478bd9Sstevel@tonic-gate     krb5_timestamp endtime;
1127c478bd9Sstevel@tonic-gate     krb5_creds **out_creds;
1137c478bd9Sstevel@tonic-gate {
1147c478bd9Sstevel@tonic-gate     krb5_error_code	code;
1157c478bd9Sstevel@tonic-gate     krb5_creds 		in_creds;
1167c478bd9Sstevel@tonic-gate 
117ab9b2e15Sgtb     k5_mutex_assert_locked(&cred->lock);
1187c478bd9Sstevel@tonic-gate     memset((char *) &in_creds, 0, sizeof(krb5_creds));
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate     if ((code = krb5_copy_principal(context, cred->princ, &in_creds.client)))
1217c478bd9Sstevel@tonic-gate 	goto cleanup;
1227c478bd9Sstevel@tonic-gate     if ((code = krb5_copy_principal(context, server, &in_creds.server)))
1237c478bd9Sstevel@tonic-gate 	goto cleanup;
1247c478bd9Sstevel@tonic-gate     in_creds.times.endtime = endtime;
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate     in_creds.keyblock.enctype = 0;
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate     code = krb5_get_credentials(context, 0, cred->ccache,
1297c478bd9Sstevel@tonic-gate 				&in_creds, out_creds);
1307c478bd9Sstevel@tonic-gate     if (code)
1317c478bd9Sstevel@tonic-gate 	goto cleanup;
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate     /*
1347c478bd9Sstevel@tonic-gate      * Enforce a stricter limit (without timeskew forgiveness at the
1357c478bd9Sstevel@tonic-gate      * boundaries) because accept_sec_context code is also similarly
1367c478bd9Sstevel@tonic-gate      * non-forgiving.
1377c478bd9Sstevel@tonic-gate      */
1387c478bd9Sstevel@tonic-gate     if (!krb5_gss_dbg_client_expcreds && *out_creds != NULL &&
1397c478bd9Sstevel@tonic-gate 	(*out_creds)->times.endtime < now) {
1407c478bd9Sstevel@tonic-gate 	code = KRB5KRB_AP_ERR_TKT_EXPIRED;
1417c478bd9Sstevel@tonic-gate 	goto cleanup;
1427c478bd9Sstevel@tonic-gate     }
143ab9b2e15Sgtb 
1447c478bd9Sstevel@tonic-gate cleanup:
1457c478bd9Sstevel@tonic-gate     if (in_creds.client)
1467c478bd9Sstevel@tonic-gate 	    krb5_free_principal(context, in_creds.client);
1477c478bd9Sstevel@tonic-gate     if (in_creds.server)
1487c478bd9Sstevel@tonic-gate 	    krb5_free_principal(context, in_creds.server);
1497c478bd9Sstevel@tonic-gate     return code;
1507c478bd9Sstevel@tonic-gate }
1517c478bd9Sstevel@tonic-gate struct gss_checksum_data {
1527c478bd9Sstevel@tonic-gate     krb5_gss_ctx_id_rec *ctx;
1537c478bd9Sstevel@tonic-gate     krb5_gss_cred_id_t cred;
1547c478bd9Sstevel@tonic-gate     krb5_checksum md5;
1557c478bd9Sstevel@tonic-gate     krb5_data checksum_data;
1567c478bd9Sstevel@tonic-gate };
1577c478bd9Sstevel@tonic-gate 
158ab9b2e15Sgtb #ifdef CFX_EXERCISE
159ab9b2e15Sgtb #include "../../krb5/krb/auth_con.h"
160ab9b2e15Sgtb #endif
1617c478bd9Sstevel@tonic-gate static krb5_error_code KRB5_CALLCONV
1627c478bd9Sstevel@tonic-gate make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
163ab9b2e15Sgtb 		   void *cksum_data, krb5_data **out)
1647c478bd9Sstevel@tonic-gate {
1657c478bd9Sstevel@tonic-gate     krb5_error_code code;
1667c478bd9Sstevel@tonic-gate     krb5_int32 con_flags;
1677c478bd9Sstevel@tonic-gate     unsigned char *ptr;
1687c478bd9Sstevel@tonic-gate     struct gss_checksum_data *data = cksum_data;
1697c478bd9Sstevel@tonic-gate     krb5_data credmsg;
170ab9b2e15Sgtb     unsigned int junk;
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate     data->checksum_data.data = 0;
1737c478bd9Sstevel@tonic-gate     credmsg.data = 0;
1747c478bd9Sstevel@tonic-gate     /* build the checksum field */
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate     if (data->ctx->gss_flags & GSS_C_DELEG_FLAG) {
1777c478bd9Sstevel@tonic-gate 	/* first get KRB_CRED message, so we know its length */
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate 	/* clear the time check flag that was set in krb5_auth_con_init() */
1807c478bd9Sstevel@tonic-gate 	krb5_auth_con_getflags(context, auth_context, &con_flags);
1817c478bd9Sstevel@tonic-gate 	krb5_auth_con_setflags(context, auth_context,
182ab9b2e15Sgtb 			       con_flags & ~KRB5_AUTH_CONTEXT_DO_TIME);
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 	code = krb5_fwd_tgt_creds(context, auth_context, 0,
185ab9b2e15Sgtb 				  data->cred->princ, data->ctx->there,
186ab9b2e15Sgtb 				  data->cred->ccache, 1,
187ab9b2e15Sgtb 				  &credmsg);
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 	/* turn KRB5_AUTH_CONTEXT_DO_TIME back on */
1907c478bd9Sstevel@tonic-gate 	krb5_auth_con_setflags(context, auth_context, con_flags);
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	if (code) {
1937c478bd9Sstevel@tonic-gate 	    /* don't fail here; just don't accept/do the delegation
194ab9b2e15Sgtb                request */
1957c478bd9Sstevel@tonic-gate 	    data->ctx->gss_flags &= ~GSS_C_DELEG_FLAG;
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 	    data->checksum_data.length = 24;
1987c478bd9Sstevel@tonic-gate 	} else {
1997c478bd9Sstevel@tonic-gate 	    if (credmsg.length+28 > KRB5_INT16_MAX) {
2007c478bd9Sstevel@tonic-gate 		krb5_free_data_contents(context, &credmsg);
2017c478bd9Sstevel@tonic-gate 		return(KRB5KRB_ERR_FIELD_TOOLONG);
202ab9b2e15Sgtb 	    }
2037c478bd9Sstevel@tonic-gate 
204ab9b2e15Sgtb 	    data->checksum_data.length = 28+credmsg.length;
2057c478bd9Sstevel@tonic-gate 	}
2067c478bd9Sstevel@tonic-gate     } else {
2077c478bd9Sstevel@tonic-gate 	data->checksum_data.length = 24;
2087c478bd9Sstevel@tonic-gate     }
2097c478bd9Sstevel@tonic-gate #ifdef CFX_EXERCISE
210ab9b2e15Sgtb     if (data->ctx->auth_context->keyblock != NULL
211ab9b2e15Sgtb 	&& data->ctx->auth_context->keyblock->enctype == 18) {
2127c478bd9Sstevel@tonic-gate 	srand(time(0) ^ getpid());
2137c478bd9Sstevel@tonic-gate 	/* Our ftp client code stupidly assumes a base64-encoded
2147c478bd9Sstevel@tonic-gate 	   version of the token will fit in 10K, so don't make this
2157c478bd9Sstevel@tonic-gate 	   too big.  */
2167c478bd9Sstevel@tonic-gate 	junk = rand() & 0xff;
2177c478bd9Sstevel@tonic-gate     } else
218ab9b2e15Sgtb 	junk = 0;
2197c478bd9Sstevel@tonic-gate #else
2207c478bd9Sstevel@tonic-gate     junk = 0;
2217c478bd9Sstevel@tonic-gate #endif
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate     data->checksum_data.length += junk;
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate     /* now allocate a buffer to hold the checksum data and
226ab9b2e15Sgtb        (maybe) KRB_CRED msg */
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate     if ((data->checksum_data.data =
2297c478bd9Sstevel@tonic-gate 	 (char *) xmalloc(data->checksum_data.length)) == NULL) {
2307c478bd9Sstevel@tonic-gate 	if (credmsg.data)
231ab9b2e15Sgtb 	    krb5_free_data_contents(context, &credmsg);
2327c478bd9Sstevel@tonic-gate 	return(ENOMEM);
2337c478bd9Sstevel@tonic-gate     }
234*159d09a2SMark Phalan     /* Solaris Kerberos */
235ab9b2e15Sgtb     ptr = (uchar_t *)data->checksum_data.data; /* SUNW15resync */
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate     TWRITE_INT(ptr, data->md5.length, 0);
2387c478bd9Sstevel@tonic-gate     TWRITE_STR(ptr, (unsigned char *) data->md5.contents, data->md5.length);
2397c478bd9Sstevel@tonic-gate     TWRITE_INT(ptr, data->ctx->gss_flags, 0);
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate     /* done with this, free it */
2427c478bd9Sstevel@tonic-gate     xfree(data->md5.contents);
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate     if (credmsg.data) {
2457c478bd9Sstevel@tonic-gate 	TWRITE_INT16(ptr, KRB5_GSS_FOR_CREDS_OPTION, 0);
2467c478bd9Sstevel@tonic-gate 	TWRITE_INT16(ptr, credmsg.length, 0);
2477c478bd9Sstevel@tonic-gate 	TWRITE_STR(ptr, (unsigned char *) credmsg.data, credmsg.length);
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate 	/* free credmsg data */
2507c478bd9Sstevel@tonic-gate 	krb5_free_data_contents(context, &credmsg);
2517c478bd9Sstevel@tonic-gate     }
2527c478bd9Sstevel@tonic-gate     if (junk)
2537c478bd9Sstevel@tonic-gate 	memset(ptr, 'i', junk);
2547c478bd9Sstevel@tonic-gate     *out = &data->checksum_data;
2557c478bd9Sstevel@tonic-gate     return 0;
2567c478bd9Sstevel@tonic-gate }
257ab9b2e15Sgtb 
2587c478bd9Sstevel@tonic-gate static krb5_error_code
2597c478bd9Sstevel@tonic-gate make_ap_req_v1(context, ctx, cred, k_cred, chan_bindings, mech_type, token)
2607c478bd9Sstevel@tonic-gate     krb5_context context;
2617c478bd9Sstevel@tonic-gate     krb5_gss_ctx_id_rec *ctx;
2627c478bd9Sstevel@tonic-gate     krb5_gss_cred_id_t cred;
2637c478bd9Sstevel@tonic-gate     krb5_creds *k_cred;
2647c478bd9Sstevel@tonic-gate     gss_channel_bindings_t chan_bindings;
2657c478bd9Sstevel@tonic-gate     gss_OID mech_type;
2667c478bd9Sstevel@tonic-gate     gss_buffer_t token;
2677c478bd9Sstevel@tonic-gate {
2687c478bd9Sstevel@tonic-gate     krb5_flags mk_req_flags = 0;
2697c478bd9Sstevel@tonic-gate     krb5_error_code code;
2707c478bd9Sstevel@tonic-gate     struct gss_checksum_data cksum_struct;
2717c478bd9Sstevel@tonic-gate     krb5_checksum md5;
2727c478bd9Sstevel@tonic-gate     krb5_data ap_req;
2737c478bd9Sstevel@tonic-gate     krb5_data *checksum_data = NULL;
2747c478bd9Sstevel@tonic-gate     unsigned char *ptr;
2757c478bd9Sstevel@tonic-gate     unsigned char *t;
276ab9b2e15Sgtb     unsigned int tlen;
2777c478bd9Sstevel@tonic-gate 
278ab9b2e15Sgtb     k5_mutex_assert_locked(&cred->lock);
2797c478bd9Sstevel@tonic-gate     ap_req.data = 0;
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate     /* compute the hash of the channel bindings */
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate     if ((code = kg_checksum_channel_bindings(context, chan_bindings, &md5, 0)))
2847c478bd9Sstevel@tonic-gate         return(code);
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate     krb5_auth_con_set_req_cksumtype(context, ctx->auth_context,
2877c478bd9Sstevel@tonic-gate 				    CKSUMTYPE_KG_CB);
2887c478bd9Sstevel@tonic-gate     cksum_struct.md5 = md5;
2897c478bd9Sstevel@tonic-gate     cksum_struct.ctx = ctx;
2907c478bd9Sstevel@tonic-gate     cksum_struct.cred = cred;
2917c478bd9Sstevel@tonic-gate     cksum_struct.checksum_data.data = NULL;
2927c478bd9Sstevel@tonic-gate     switch (k_cred->keyblock.enctype) {
2937c478bd9Sstevel@tonic-gate     case ENCTYPE_DES_CBC_CRC:
2947c478bd9Sstevel@tonic-gate     case ENCTYPE_DES_CBC_MD4:
2957c478bd9Sstevel@tonic-gate     case ENCTYPE_DES_CBC_MD5:
2967c478bd9Sstevel@tonic-gate     case ENCTYPE_DES3_CBC_SHA1:
297ab9b2e15Sgtb       code = make_gss_checksum(context, ctx->auth_context, &cksum_struct,
298ab9b2e15Sgtb 				 &checksum_data);
299ab9b2e15Sgtb 	    if (code)
3007c478bd9Sstevel@tonic-gate 		goto cleanup;
301ab9b2e15Sgtb 	break;
3027c478bd9Sstevel@tonic-gate     default:
3037c478bd9Sstevel@tonic-gate 	krb5_auth_con_set_checksum_func(context, ctx->auth_context,
304ab9b2e15Sgtb 					make_gss_checksum, &cksum_struct);
305ab9b2e15Sgtb 	    break;
3067c478bd9Sstevel@tonic-gate     }
3077c478bd9Sstevel@tonic-gate 
308ab9b2e15Sgtb 
3097c478bd9Sstevel@tonic-gate     /* call mk_req.  subkey and ap_req need to be used or destroyed */
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate     mk_req_flags = AP_OPTS_USE_SUBKEY;
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate     if (ctx->gss_flags & GSS_C_MUTUAL_FLAG)
3147c478bd9Sstevel@tonic-gate 	mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED;
3157c478bd9Sstevel@tonic-gate 
316ab9b2e15Sgtb     code = krb5_mk_req_extended(context, &ctx->auth_context, mk_req_flags,
317ab9b2e15Sgtb 				checksum_data, k_cred, &ap_req);
318ab9b2e15Sgtb     krb5_free_data_contents(context, &cksum_struct.checksum_data);
319ab9b2e15Sgtb     if (code)
3207c478bd9Sstevel@tonic-gate 	goto cleanup;
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate    /* store the interesting stuff from creds and authent */
3237c478bd9Sstevel@tonic-gate    ctx->endtime = k_cred->times.endtime;
3247c478bd9Sstevel@tonic-gate    ctx->krb_flags = k_cred->ticket_flags;
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate    /* build up the token */
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate    /* allocate space for the token */
3297c478bd9Sstevel@tonic-gate    tlen = g_token_size((gss_OID) mech_type, ap_req.length);
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate    if ((t = (unsigned char *) xmalloc(tlen)) == NULL) {
3327c478bd9Sstevel@tonic-gate       code = ENOMEM;
3337c478bd9Sstevel@tonic-gate       goto cleanup;
3347c478bd9Sstevel@tonic-gate    }
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate    /* fill in the buffer */
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate    ptr = t;
3397c478bd9Sstevel@tonic-gate 
340ab9b2e15Sgtb    g_make_token_header(mech_type, ap_req.length,
3417c478bd9Sstevel@tonic-gate 		       &ptr, KG_TOK_CTX_AP_REQ);
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate    TWRITE_STR(ptr, (unsigned char *) ap_req.data, ap_req.length);
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate    /* pass it back */
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate    token->length = tlen;
3487c478bd9Sstevel@tonic-gate    token->value = (void *) t;
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate    code = 0;
351ab9b2e15Sgtb 
352ab9b2e15Sgtb  cleanup:
353ab9b2e15Sgtb    if (checksum_data && checksum_data->data)
354ab9b2e15Sgtb        krb5_free_data_contents(context, checksum_data);
3557c478bd9Sstevel@tonic-gate    if (ap_req.data)
356ab9b2e15Sgtb        krb5_free_data_contents(context, &ap_req);
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate    return (code);
3597c478bd9Sstevel@tonic-gate }
3607c478bd9Sstevel@tonic-gate 
361ab9b2e15Sgtb /*
362ab9b2e15Sgtb  * setup_enc
363ab9b2e15Sgtb  *
364ab9b2e15Sgtb  * Fill in the encryption descriptors.  Called after AP-REQ is made.
365ab9b2e15Sgtb  */
366ab9b2e15Sgtb static OM_uint32
367ab9b2e15Sgtb setup_enc(
368ab9b2e15Sgtb    OM_uint32 *minor_status,
369ab9b2e15Sgtb    krb5_gss_ctx_id_rec *ctx,
370ab9b2e15Sgtb    krb5_context context)
371ab9b2e15Sgtb {
372ab9b2e15Sgtb    krb5_error_code code;
373ab9b2e15Sgtb    int i;
374ab9b2e15Sgtb    krb5int_access kaccess;
3757c478bd9Sstevel@tonic-gate 
376ab9b2e15Sgtb    code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
377ab9b2e15Sgtb    if (code)
378ab9b2e15Sgtb        goto fail;
3797c478bd9Sstevel@tonic-gate 
380ab9b2e15Sgtb    ctx->have_acceptor_subkey = 0;
381ab9b2e15Sgtb    ctx->proto = 0;
382ab9b2e15Sgtb    ctx->cksumtype = 0;
383ab9b2e15Sgtb    switch(ctx->subkey->enctype) {
384ab9b2e15Sgtb    case ENCTYPE_DES_CBC_MD5:
385ab9b2e15Sgtb    case ENCTYPE_DES_CBC_MD4:
386ab9b2e15Sgtb    case ENCTYPE_DES_CBC_CRC:
387ab9b2e15Sgtb       ctx->subkey->enctype = ENCTYPE_DES_CBC_RAW;
388ab9b2e15Sgtb       ctx->signalg = SGN_ALG_DES_MAC_MD5;
389ab9b2e15Sgtb       ctx->cksum_size = 8;
390ab9b2e15Sgtb       ctx->sealalg = SEAL_ALG_DES;
3917c478bd9Sstevel@tonic-gate 
392ab9b2e15Sgtb       /* The encryption key is the session key XOR
393ab9b2e15Sgtb 	 0xf0f0f0f0f0f0f0f0.  */
394ab9b2e15Sgtb       if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc)))
395ab9b2e15Sgtb 	 goto fail;
3967c478bd9Sstevel@tonic-gate 
397ab9b2e15Sgtb       for (i=0; i<ctx->enc->length; i++)
398ab9b2e15Sgtb 	 ctx->enc->contents[i] ^= 0xf0;
3997c478bd9Sstevel@tonic-gate 
400ab9b2e15Sgtb       goto copy_subkey_to_seq;
4017c478bd9Sstevel@tonic-gate 
402ab9b2e15Sgtb    case ENCTYPE_DES3_CBC_SHA1:
403ab9b2e15Sgtb        /* MIT extension */
404ab9b2e15Sgtb       ctx->subkey->enctype = ENCTYPE_DES3_CBC_RAW;
405ab9b2e15Sgtb       ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD;
406ab9b2e15Sgtb       ctx->cksum_size = 20;
407ab9b2e15Sgtb       ctx->sealalg = SEAL_ALG_DES3KD;
408ab9b2e15Sgtb 
409ab9b2e15Sgtb    copy_subkey:
410ab9b2e15Sgtb       code = krb5_copy_keyblock (context, ctx->subkey, &ctx->enc);
411ab9b2e15Sgtb       if (code)
412ab9b2e15Sgtb 	 goto fail;
413ab9b2e15Sgtb    copy_subkey_to_seq:
414ab9b2e15Sgtb       code = krb5_copy_keyblock (context, ctx->subkey, &ctx->seq);
415ab9b2e15Sgtb       if (code) {
416ab9b2e15Sgtb 	 krb5_free_keyblock (context, ctx->enc);
417ab9b2e15Sgtb 	 goto fail;
418ab9b2e15Sgtb       }
419ab9b2e15Sgtb       goto success;
420ab9b2e15Sgtb 
421ab9b2e15Sgtb    case ENCTYPE_ARCFOUR_HMAC:
422ab9b2e15Sgtb        /* Microsoft extension */
423ab9b2e15Sgtb       ctx->signalg = SGN_ALG_HMAC_MD5 ;
424ab9b2e15Sgtb       ctx->cksum_size = 8;
425ab9b2e15Sgtb       ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ;
426ab9b2e15Sgtb 
427ab9b2e15Sgtb       goto copy_subkey;
428ab9b2e15Sgtb 
429ab9b2e15Sgtb    default:
430ab9b2e15Sgtb        /* Fill some fields we shouldn't be using on this path
431ab9b2e15Sgtb 	  with garbage.  */
432ab9b2e15Sgtb        ctx->signalg = -10;
433ab9b2e15Sgtb        ctx->sealalg = -10;
434ab9b2e15Sgtb 
435ab9b2e15Sgtb        ctx->proto = 1;
436ab9b2e15Sgtb        code = (*kaccess.krb5int_c_mandatory_cksumtype)(context, ctx->subkey->enctype,
437ab9b2e15Sgtb 					    &ctx->cksumtype);
438ab9b2e15Sgtb        if (code)
439ab9b2e15Sgtb 	   goto fail;
440ab9b2e15Sgtb        code = krb5_c_checksum_length(context, ctx->cksumtype,
441ab9b2e15Sgtb 				     &ctx->cksum_size);
442ab9b2e15Sgtb        if (code)
443ab9b2e15Sgtb 	   goto fail;
444ab9b2e15Sgtb        goto copy_subkey;
445ab9b2e15Sgtb    }
446ab9b2e15Sgtb fail:
447ab9b2e15Sgtb    /* SUNW15resync - (as in prev snv code) add if-code and success label fix */
448ab9b2e15Sgtb   if (code) {
449ab9b2e15Sgtb       *minor_status = code;
450ab9b2e15Sgtb       return GSS_S_FAILURE;
451ab9b2e15Sgtb   }
452ab9b2e15Sgtb 
453ab9b2e15Sgtb success:
454*159d09a2SMark Phalan    return (GSS_S_COMPLETE);
4557c478bd9Sstevel@tonic-gate }
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate /*
458ab9b2e15Sgtb  * new_connection
459ab9b2e15Sgtb  *
460ab9b2e15Sgtb  * Do the grunt work of setting up a new context.
4617c478bd9Sstevel@tonic-gate  */
462ab9b2e15Sgtb static OM_uint32
463ab9b2e15Sgtb new_connection(
464ab9b2e15Sgtb    OM_uint32 *minor_status,
465ab9b2e15Sgtb    krb5_gss_cred_id_t cred,
466ab9b2e15Sgtb    gss_ctx_id_t *context_handle,
467ab9b2e15Sgtb    gss_name_t target_name,
468ab9b2e15Sgtb    gss_OID mech_type,
469ab9b2e15Sgtb    OM_uint32 req_flags,
470ab9b2e15Sgtb    OM_uint32 time_req,
471ab9b2e15Sgtb    gss_channel_bindings_t input_chan_bindings,
472ab9b2e15Sgtb    gss_buffer_t input_token,
473ab9b2e15Sgtb    gss_OID *actual_mech_type,
474ab9b2e15Sgtb    gss_buffer_t output_token,
475ab9b2e15Sgtb    OM_uint32 *ret_flags,
476ab9b2e15Sgtb    OM_uint32 *time_rec,
477ab9b2e15Sgtb    krb5_context context,
478ab9b2e15Sgtb    int default_mech)
4797c478bd9Sstevel@tonic-gate {
480ab9b2e15Sgtb    OM_uint32 major_status;
481ab9b2e15Sgtb    krb5_error_code code;
482ab9b2e15Sgtb    krb5_creds *k_cred;
483ab9b2e15Sgtb    krb5_gss_ctx_id_rec *ctx, *ctx_free;
484ab9b2e15Sgtb    krb5_timestamp now;
485ab9b2e15Sgtb    gss_buffer_desc token;
4867c478bd9Sstevel@tonic-gate 
487ab9b2e15Sgtb    k5_mutex_assert_locked(&cred->lock);
488ab9b2e15Sgtb    major_status = GSS_S_FAILURE;
489ab9b2e15Sgtb    token.length = 0;
490ab9b2e15Sgtb    token.value = NULL;
4917c478bd9Sstevel@tonic-gate 
492ab9b2e15Sgtb    /* make sure the cred is usable for init */
4937c478bd9Sstevel@tonic-gate 
494ab9b2e15Sgtb    if ((cred->usage != GSS_C_INITIATE) &&
495ab9b2e15Sgtb        (cred->usage != GSS_C_BOTH)) {
496ab9b2e15Sgtb       *minor_status = 0;
497ab9b2e15Sgtb       return(GSS_S_NO_CRED);
498ab9b2e15Sgtb    }
4997c478bd9Sstevel@tonic-gate 
500ab9b2e15Sgtb    /* complain if the input token is non-null */
5017c478bd9Sstevel@tonic-gate 
502ab9b2e15Sgtb    if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
503ab9b2e15Sgtb        *minor_status = 0;
504ab9b2e15Sgtb        return(GSS_S_DEFECTIVE_TOKEN);
505ab9b2e15Sgtb    }
5067c478bd9Sstevel@tonic-gate 
507ab9b2e15Sgtb    /* create the ctx */
5087c478bd9Sstevel@tonic-gate 
509ab9b2e15Sgtb    if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
510ab9b2e15Sgtb        == NULL) {
511ab9b2e15Sgtb       *minor_status = ENOMEM;
512ab9b2e15Sgtb       return(GSS_S_FAILURE);
513ab9b2e15Sgtb    }
5147c478bd9Sstevel@tonic-gate 
515ab9b2e15Sgtb    /* fill in the ctx */
516ab9b2e15Sgtb    memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
517ab9b2e15Sgtb    ctx_free = ctx;
518ab9b2e15Sgtb    if ((code = krb5_auth_con_init(context, &ctx->auth_context)))
519ab9b2e15Sgtb       goto fail;
520ab9b2e15Sgtb    krb5_auth_con_setflags(context, ctx->auth_context,
521ab9b2e15Sgtb 			  KRB5_AUTH_CONTEXT_DO_SEQUENCE);
5227c478bd9Sstevel@tonic-gate 
523ab9b2e15Sgtb    /* limit the encryption types negotiated (if requested) */
524ab9b2e15Sgtb    if (cred->req_enctypes) {
525ab9b2e15Sgtb 	if ((code = krb5_set_default_tgs_enctypes(context,
526ab9b2e15Sgtb 						  cred->req_enctypes))) {
527ab9b2e15Sgtb 	    goto fail;
5287c478bd9Sstevel@tonic-gate 	}
529ab9b2e15Sgtb    }
5307c478bd9Sstevel@tonic-gate 
531ab9b2e15Sgtb    ctx->initiate = 1;
532ab9b2e15Sgtb    ctx->gss_flags = (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
533ab9b2e15Sgtb                      GSS_C_TRANS_FLAG |
534ab9b2e15Sgtb                      ((req_flags) & (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
535ab9b2e15Sgtb                                      GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG)));
536ab9b2e15Sgtb    ctx->seed_init = 0;
537ab9b2e15Sgtb    ctx->big_endian = 0;  /* all initiators do little-endian, as per spec */
538ab9b2e15Sgtb    ctx->seqstate = 0;
5397c478bd9Sstevel@tonic-gate 
540ab9b2e15Sgtb    if ((code = krb5_timeofday(context, &now)))
541ab9b2e15Sgtb       goto fail;
5427c478bd9Sstevel@tonic-gate 
543ab9b2e15Sgtb    if (time_req == 0 || time_req == GSS_C_INDEFINITE) {
544ab9b2e15Sgtb       ctx->endtime = 0;
545ab9b2e15Sgtb    } else {
546ab9b2e15Sgtb       ctx->endtime = now + time_req;
547ab9b2e15Sgtb    }
5487c478bd9Sstevel@tonic-gate 
549ab9b2e15Sgtb    if ((code = krb5_copy_principal(context, cred->princ, &ctx->here)))
550ab9b2e15Sgtb       goto fail;
551ab9b2e15Sgtb 
552ab9b2e15Sgtb    if ((code = krb5_copy_principal(context, (krb5_principal) target_name,
553ab9b2e15Sgtb 				   &ctx->there)))
554ab9b2e15Sgtb       goto fail;
5557c478bd9Sstevel@tonic-gate 
556ab9b2e15Sgtb    code = get_credentials(context, cred, ctx->there, now,
557ab9b2e15Sgtb 			  ctx->endtime, &k_cred);
558ab9b2e15Sgtb    if (code)
559ab9b2e15Sgtb       goto fail;
5607c478bd9Sstevel@tonic-gate 
561ab9b2e15Sgtb    if (default_mech) {
562ab9b2e15Sgtb       mech_type = (gss_OID) gss_mech_krb5;
563ab9b2e15Sgtb    }
5647c478bd9Sstevel@tonic-gate 
565ab9b2e15Sgtb    if (generic_gss_copy_oid(minor_status, mech_type, &ctx->mech_used)
566ab9b2e15Sgtb        != GSS_S_COMPLETE) {
567ab9b2e15Sgtb       code = *minor_status;
568ab9b2e15Sgtb       goto fail;
569ab9b2e15Sgtb    }
570ab9b2e15Sgtb    /*
571ab9b2e15Sgtb     * Now try to make it static if at all possible....
572ab9b2e15Sgtb     */
573ab9b2e15Sgtb    ctx->mech_used = krb5_gss_convert_static_mech_oid(ctx->mech_used);
5747c478bd9Sstevel@tonic-gate 
575ab9b2e15Sgtb    {
576ab9b2e15Sgtb       /* gsskrb5 v1 */
577ab9b2e15Sgtb       krb5_ui_4 seq_temp;
578ab9b2e15Sgtb       if ((code = make_ap_req_v1(context, ctx,
579ab9b2e15Sgtb 				 cred, k_cred, input_chan_bindings,
580ab9b2e15Sgtb 				 mech_type, &token))) {
581ab9b2e15Sgtb 	 if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) ||
582ab9b2e15Sgtb 	     (code == KG_EMPTY_CCACHE))
583ab9b2e15Sgtb 	    major_status = GSS_S_NO_CRED;
584ab9b2e15Sgtb 	 if (code == KRB5KRB_AP_ERR_TKT_EXPIRED)
585ab9b2e15Sgtb 	    major_status = GSS_S_CREDENTIALS_EXPIRED;
586ab9b2e15Sgtb 	 goto fail;
587ab9b2e15Sgtb       }
5887c478bd9Sstevel@tonic-gate 
589ab9b2e15Sgtb       krb5_auth_con_getlocalseqnumber(context, ctx->auth_context,
590ab9b2e15Sgtb 			    (krb5_int32 *)&seq_temp); /* SUNW15resync */
591ab9b2e15Sgtb       ctx->seq_send = seq_temp;
592ab9b2e15Sgtb       krb5_auth_con_getsendsubkey(context, ctx->auth_context,
593ab9b2e15Sgtb 				  &ctx->subkey);
594ab9b2e15Sgtb    }
5957c478bd9Sstevel@tonic-gate 
596ab9b2e15Sgtb    major_status = setup_enc(minor_status, ctx, context);
5977c478bd9Sstevel@tonic-gate 
598ab9b2e15Sgtb    if (k_cred) {
599ab9b2e15Sgtb       krb5_free_creds(context, k_cred);
600ab9b2e15Sgtb       k_cred = 0;
601ab9b2e15Sgtb    }
602ab9b2e15Sgtb 
603ab9b2e15Sgtb    /* at this point, the context is constructed and valid,
604ab9b2e15Sgtb       hence, releaseable */
6057c478bd9Sstevel@tonic-gate 
606ab9b2e15Sgtb    /* intern the context handle */
6077c478bd9Sstevel@tonic-gate 
608ab9b2e15Sgtb    if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) {
609ab9b2e15Sgtb       code = G_VALIDATE_FAILED;
610ab9b2e15Sgtb       goto fail;
611ab9b2e15Sgtb    }
612ab9b2e15Sgtb    *context_handle = (gss_ctx_id_t) ctx;
613ab9b2e15Sgtb    ctx_free = 0;
6147c478bd9Sstevel@tonic-gate 
615ab9b2e15Sgtb    /* compute time_rec */
616ab9b2e15Sgtb    if (time_rec) {
617ab9b2e15Sgtb       if ((code = krb5_timeofday(context, &now)))
618ab9b2e15Sgtb 	 goto fail;
619ab9b2e15Sgtb       *time_rec = ctx->endtime - now;
620ab9b2e15Sgtb    }
6217c478bd9Sstevel@tonic-gate 
622ab9b2e15Sgtb    /* set the other returns */
623ab9b2e15Sgtb    *output_token = token;
6247c478bd9Sstevel@tonic-gate 
625ab9b2e15Sgtb    if (ret_flags)
626ab9b2e15Sgtb       *ret_flags = ctx->gss_flags;
6277c478bd9Sstevel@tonic-gate 
628ab9b2e15Sgtb    if (actual_mech_type)
629ab9b2e15Sgtb       *actual_mech_type = mech_type;
6307c478bd9Sstevel@tonic-gate 
631ab9b2e15Sgtb    /* return successfully */
6327c478bd9Sstevel@tonic-gate 
633ab9b2e15Sgtb    *minor_status = 0;
634ab9b2e15Sgtb    if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
635ab9b2e15Sgtb       ctx->established = 0;
636ab9b2e15Sgtb       return(GSS_S_CONTINUE_NEEDED);
637ab9b2e15Sgtb    } else {
638ab9b2e15Sgtb       ctx->seq_recv = ctx->seq_send;
639ab9b2e15Sgtb       g_order_init(&(ctx->seqstate), ctx->seq_recv,
640ab9b2e15Sgtb 		   (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
641ab9b2e15Sgtb 		   (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
642ab9b2e15Sgtb       ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
643ab9b2e15Sgtb       ctx->established = 1;
644ab9b2e15Sgtb       return(GSS_S_COMPLETE);
645ab9b2e15Sgtb    }
6467c478bd9Sstevel@tonic-gate 
647ab9b2e15Sgtb fail:
648ab9b2e15Sgtb    if (ctx_free) {
649ab9b2e15Sgtb        if (ctx_free->auth_context)
650ab9b2e15Sgtb 	   krb5_auth_con_free(context, ctx_free->auth_context);
651ab9b2e15Sgtb        if (ctx_free->here)
652ab9b2e15Sgtb 	   krb5_free_principal(context, ctx_free->here);
653ab9b2e15Sgtb        if (ctx_free->there)
654ab9b2e15Sgtb 	   krb5_free_principal(context, ctx_free->there);
655ab9b2e15Sgtb        if (ctx_free->subkey)
656ab9b2e15Sgtb 	   krb5_free_keyblock(context, ctx_free->subkey);
657ab9b2e15Sgtb        xfree(ctx_free);
658ab9b2e15Sgtb    } else
659ab9b2e15Sgtb 	(void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL);
660ab9b2e15Sgtb 
661ab9b2e15Sgtb    *minor_status = code;
662ab9b2e15Sgtb    return (major_status);
6637c478bd9Sstevel@tonic-gate }
6647c478bd9Sstevel@tonic-gate 
665ab9b2e15Sgtb /*
666ab9b2e15Sgtb  * mutual_auth
667ab9b2e15Sgtb  *
668ab9b2e15Sgtb  * Handle the reply from the acceptor, if we're doing mutual auth.
669ab9b2e15Sgtb  */
6707c478bd9Sstevel@tonic-gate static OM_uint32
671ab9b2e15Sgtb mutual_auth(
672ab9b2e15Sgtb    OM_uint32 *minor_status,
673ab9b2e15Sgtb    gss_ctx_id_t *context_handle,
674ab9b2e15Sgtb    gss_name_t target_name,
675ab9b2e15Sgtb    gss_OID mech_type,
676ab9b2e15Sgtb    OM_uint32 req_flags,
677ab9b2e15Sgtb    OM_uint32 time_req,
678ab9b2e15Sgtb    gss_channel_bindings_t input_chan_bindings,
679ab9b2e15Sgtb    gss_buffer_t input_token,
680ab9b2e15Sgtb    gss_OID *actual_mech_type,
681ab9b2e15Sgtb    gss_buffer_t output_token,
682ab9b2e15Sgtb    OM_uint32 *ret_flags,
683ab9b2e15Sgtb    OM_uint32 *time_rec,
684ab9b2e15Sgtb    krb5_context context)
6857c478bd9Sstevel@tonic-gate {
686ab9b2e15Sgtb    OM_uint32 major_status;
687ab9b2e15Sgtb    unsigned char *ptr;
688ab9b2e15Sgtb    char *sptr;
689ab9b2e15Sgtb    krb5_data ap_rep;
690ab9b2e15Sgtb    krb5_ap_rep_enc_part *ap_rep_data;
691ab9b2e15Sgtb    krb5_timestamp now;
692ab9b2e15Sgtb    krb5_gss_ctx_id_rec *ctx;
693ab9b2e15Sgtb    krb5_error *krb_error;
694ab9b2e15Sgtb    krb5_error_code code;
695ab9b2e15Sgtb    krb5int_access kaccess;
6967c478bd9Sstevel@tonic-gate 
697ab9b2e15Sgtb    major_status = GSS_S_FAILURE;
6987c478bd9Sstevel@tonic-gate 
699ab9b2e15Sgtb    code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
700ab9b2e15Sgtb    if (code)
701ab9b2e15Sgtb        goto fail;
7027c478bd9Sstevel@tonic-gate 
703ab9b2e15Sgtb    /* validate the context handle */
704ab9b2e15Sgtb    /*SUPPRESS 29*/
705ab9b2e15Sgtb    if (! kg_validate_ctx_id(*context_handle)) {
706ab9b2e15Sgtb       *minor_status = (OM_uint32) G_VALIDATE_FAILED;
707ab9b2e15Sgtb       return(GSS_S_NO_CONTEXT);
708ab9b2e15Sgtb    }
7097c478bd9Sstevel@tonic-gate 
710*159d09a2SMark Phalan    ctx = (krb5_gss_ctx_id_t) *context_handle;
7117c478bd9Sstevel@tonic-gate 
712ab9b2e15Sgtb    /* make sure the context is non-established, and that certain
713ab9b2e15Sgtb       arguments are unchanged */
7147c478bd9Sstevel@tonic-gate 
715ab9b2e15Sgtb    if ((ctx->established) ||
716ab9b2e15Sgtb        ((ctx->gss_flags & GSS_C_MUTUAL_FLAG) == 0)) {
717ab9b2e15Sgtb       code = KG_CONTEXT_ESTABLISHED;
718ab9b2e15Sgtb       goto fail;
719ab9b2e15Sgtb    }
720fe598cdcSmp 
721ab9b2e15Sgtb    if (! krb5_principal_compare(context, ctx->there,
722ab9b2e15Sgtb 				(krb5_principal) target_name)) {
723ab9b2e15Sgtb       (void)krb5_gss_delete_sec_context(minor_status,
724ab9b2e15Sgtb 					context_handle, NULL);
725ab9b2e15Sgtb       code = 0;
726ab9b2e15Sgtb       major_status = GSS_S_BAD_NAME;
727ab9b2e15Sgtb       goto fail;
728ab9b2e15Sgtb    }
729fe598cdcSmp 
730ab9b2e15Sgtb    /* verify the token and leave the AP_REP message in ap_rep */
731fe598cdcSmp 
732ab9b2e15Sgtb    if (input_token == GSS_C_NO_BUFFER) {
733ab9b2e15Sgtb       (void)krb5_gss_delete_sec_context(minor_status,
734ab9b2e15Sgtb 					context_handle, NULL);
735ab9b2e15Sgtb       code = 0;
736ab9b2e15Sgtb       major_status = GSS_S_DEFECTIVE_TOKEN;
737ab9b2e15Sgtb       goto fail;
738ab9b2e15Sgtb    }
739fe598cdcSmp 
740ab9b2e15Sgtb    ptr = (unsigned char *) input_token->value;
7417c478bd9Sstevel@tonic-gate 
742ab9b2e15Sgtb    if (g_verify_token_header(ctx->mech_used,
743ab9b2e15Sgtb 			     &(ap_rep.length),
744ab9b2e15Sgtb 			     &ptr, KG_TOK_CTX_AP_REP,
745ab9b2e15Sgtb 			     input_token->length, 1)) {
746ab9b2e15Sgtb       if (g_verify_token_header((gss_OID) ctx->mech_used,
747ab9b2e15Sgtb 				&(ap_rep.length),
748ab9b2e15Sgtb 				&ptr, KG_TOK_CTX_ERROR,
749ab9b2e15Sgtb 				input_token->length, 1) == 0) {
7507c478bd9Sstevel@tonic-gate 
751ab9b2e15Sgtb 	 /* Handle a KRB_ERROR message from the server */
7527c478bd9Sstevel@tonic-gate 
753ab9b2e15Sgtb 	 sptr = (char *) ptr;           /* PC compiler bug */
754ab9b2e15Sgtb 	 TREAD_STR(sptr, ap_rep.data, ap_rep.length);
755ab9b2e15Sgtb 
756ab9b2e15Sgtb 	 code = krb5_rd_error(context, &ap_rep, &krb_error);
757ab9b2e15Sgtb 	 if (code)
758ab9b2e15Sgtb 	    goto fail;
759ab9b2e15Sgtb 	 if (krb_error->error)
760ab9b2e15Sgtb 	    code = krb_error->error + ERROR_TABLE_BASE_krb5;
761ab9b2e15Sgtb 	 else
762ab9b2e15Sgtb 	    code = 0;
763ab9b2e15Sgtb 	 krb5_free_error(context, krb_error);
764ab9b2e15Sgtb 	 goto fail;
765ab9b2e15Sgtb       } else {
766ab9b2e15Sgtb 	 *minor_status = 0;
767ab9b2e15Sgtb 	 return(GSS_S_DEFECTIVE_TOKEN);
768ab9b2e15Sgtb       }
769ab9b2e15Sgtb    }
7707c478bd9Sstevel@tonic-gate 
771ab9b2e15Sgtb    sptr = (char *) ptr;                      /* PC compiler bug */
772ab9b2e15Sgtb    TREAD_STR(sptr, ap_rep.data, ap_rep.length);
7737c478bd9Sstevel@tonic-gate 
774ab9b2e15Sgtb    /* decode the ap_rep */
775ab9b2e15Sgtb    if ((code = krb5_rd_rep(context, ctx->auth_context, &ap_rep,
776ab9b2e15Sgtb 			   &ap_rep_data))) {
777ab9b2e15Sgtb       /*
778ab9b2e15Sgtb        * XXX A hack for backwards compatiblity.
779ab9b2e15Sgtb        * To be removed in 1999 -- proven
780ab9b2e15Sgtb        */
781ab9b2e15Sgtb       krb5_auth_con_setuseruserkey(context, ctx->auth_context,
782ab9b2e15Sgtb 				   ctx->subkey);
783ab9b2e15Sgtb       if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep,
784ab9b2e15Sgtb 		       &ap_rep_data)))
785ab9b2e15Sgtb 	 goto fail;
786ab9b2e15Sgtb    }
7877c478bd9Sstevel@tonic-gate 
788ab9b2e15Sgtb    /* store away the sequence number */
789ab9b2e15Sgtb    ctx->seq_recv = ap_rep_data->seq_number;
790ab9b2e15Sgtb    g_order_init(&(ctx->seqstate), ctx->seq_recv,
791ab9b2e15Sgtb 		(ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
792ab9b2e15Sgtb 		(ctx->gss_flags & GSS_C_SEQUENCE_FLAG) !=0, ctx->proto);
7937c478bd9Sstevel@tonic-gate 
794ab9b2e15Sgtb    if (ctx->proto == 1 && ap_rep_data->subkey) {
795ab9b2e15Sgtb        /* Keep acceptor's subkey.  */
796ab9b2e15Sgtb        ctx->have_acceptor_subkey = 1;
797ab9b2e15Sgtb        code = krb5_copy_keyblock(context, ap_rep_data->subkey,
798ab9b2e15Sgtb 				 &ctx->acceptor_subkey);
799ab9b2e15Sgtb        if (code)
800ab9b2e15Sgtb 	   goto fail;
801ab9b2e15Sgtb        code = (*kaccess.krb5int_c_mandatory_cksumtype)(context,
802ab9b2e15Sgtb 					    ctx->acceptor_subkey->enctype,
803ab9b2e15Sgtb 					    &ctx->acceptor_subkey_cksumtype);
804ab9b2e15Sgtb        if (code)
805ab9b2e15Sgtb 	   goto fail;
806ab9b2e15Sgtb    }
8077c478bd9Sstevel@tonic-gate 
808ab9b2e15Sgtb    /* free the ap_rep_data */
809ab9b2e15Sgtb    krb5_free_ap_rep_enc_part(context, ap_rep_data);
8107c478bd9Sstevel@tonic-gate 
811ab9b2e15Sgtb    /* set established */
812ab9b2e15Sgtb    ctx->established = 1;
8137c478bd9Sstevel@tonic-gate 
814ab9b2e15Sgtb    /* set returns */
8157c478bd9Sstevel@tonic-gate 
816ab9b2e15Sgtb    if (time_rec) {
817ab9b2e15Sgtb       if ((code = krb5_timeofday(context, &now)))
818ab9b2e15Sgtb 	 goto fail;
819ab9b2e15Sgtb       *time_rec = ctx->endtime - now;
820ab9b2e15Sgtb    }
8217c478bd9Sstevel@tonic-gate 
822ab9b2e15Sgtb    if (ret_flags)
823ab9b2e15Sgtb       *ret_flags = ctx->gss_flags;
8247c478bd9Sstevel@tonic-gate 
825ab9b2e15Sgtb    if (actual_mech_type)
826ab9b2e15Sgtb       *actual_mech_type = mech_type;
8277c478bd9Sstevel@tonic-gate 
828ab9b2e15Sgtb    /* success */
8297c478bd9Sstevel@tonic-gate 
830ab9b2e15Sgtb    *minor_status = 0;
831ab9b2e15Sgtb    return GSS_S_COMPLETE;
8327c478bd9Sstevel@tonic-gate 
833ab9b2e15Sgtb fail:
834ab9b2e15Sgtb    (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL);
8357c478bd9Sstevel@tonic-gate 
836ab9b2e15Sgtb    *minor_status = code;
837ab9b2e15Sgtb    return (major_status);
8387c478bd9Sstevel@tonic-gate }
8397c478bd9Sstevel@tonic-gate 
840ab9b2e15Sgtb OM_uint32
841ab9b2e15Sgtb krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
842ab9b2e15Sgtb 			  context_handle, target_name, mech_type,
843ab9b2e15Sgtb 			  req_flags, time_req, input_chan_bindings,
844ab9b2e15Sgtb 			  input_token, actual_mech_type, output_token,
845ab9b2e15Sgtb 			  ret_flags, time_rec)
846ab9b2e15Sgtb     OM_uint32 *minor_status;
847ab9b2e15Sgtb     gss_cred_id_t claimant_cred_handle;
848ab9b2e15Sgtb     gss_ctx_id_t *context_handle;
849ab9b2e15Sgtb     gss_name_t target_name;
850ab9b2e15Sgtb     gss_OID mech_type;
851ab9b2e15Sgtb     OM_uint32 req_flags;
852ab9b2e15Sgtb     OM_uint32 time_req;
853ab9b2e15Sgtb     gss_channel_bindings_t input_chan_bindings;
854ab9b2e15Sgtb     gss_buffer_t input_token;
855ab9b2e15Sgtb     gss_OID *actual_mech_type;
856ab9b2e15Sgtb     gss_buffer_t output_token;
857ab9b2e15Sgtb     OM_uint32 *ret_flags;
858ab9b2e15Sgtb     OM_uint32 *time_rec;
8597c478bd9Sstevel@tonic-gate {
860ab9b2e15Sgtb    krb5_context context;
861ab9b2e15Sgtb    krb5_gss_cred_id_t cred;
862ab9b2e15Sgtb    int err;
863ab9b2e15Sgtb    krb5_error_code kerr;
864ab9b2e15Sgtb    int default_mech = 0;
865ab9b2e15Sgtb    OM_uint32 major_status;
866ab9b2e15Sgtb    OM_uint32 tmp_min_stat;
8677c478bd9Sstevel@tonic-gate 
868ab9b2e15Sgtb    if (*context_handle == GSS_C_NO_CONTEXT) {
869ab9b2e15Sgtb        kerr = krb5_gss_init_context(&context);
870ab9b2e15Sgtb        if (kerr) {
871ab9b2e15Sgtb 	   *minor_status = kerr;
872ab9b2e15Sgtb 	   return GSS_S_FAILURE;
873ab9b2e15Sgtb        }
874ab9b2e15Sgtb        if (GSS_ERROR(kg_sync_ccache_name(context, minor_status)))
875ab9b2e15Sgtb 	   return GSS_S_FAILURE;
876ab9b2e15Sgtb    } else {
877ab9b2e15Sgtb        context = ((krb5_gss_ctx_id_rec *)*context_handle)->k5_context;
878ab9b2e15Sgtb    }
8797c478bd9Sstevel@tonic-gate 
880ab9b2e15Sgtb    /* set up return values so they can be "freed" successfully */
8817c478bd9Sstevel@tonic-gate 
882ab9b2e15Sgtb    major_status = GSS_S_FAILURE; /* Default major code */
883ab9b2e15Sgtb    output_token->length = 0;
884ab9b2e15Sgtb    output_token->value = NULL;
885ab9b2e15Sgtb    if (actual_mech_type)
886ab9b2e15Sgtb       *actual_mech_type = NULL;
8877c478bd9Sstevel@tonic-gate 
888ab9b2e15Sgtb    /* verify that the target_name is valid and usable */
8897c478bd9Sstevel@tonic-gate 
890ab9b2e15Sgtb    if (! kg_validate_name(target_name)) {
891ab9b2e15Sgtb       *minor_status = (OM_uint32) G_VALIDATE_FAILED;
892ab9b2e15Sgtb       if (*context_handle == GSS_C_NO_CONTEXT)
893ab9b2e15Sgtb 	  krb5_free_context(context);
894ab9b2e15Sgtb       return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
895ab9b2e15Sgtb    }
8967c478bd9Sstevel@tonic-gate 
897ab9b2e15Sgtb    /* verify the credential, or use the default */
898ab9b2e15Sgtb    /*SUPPRESS 29*/
899ab9b2e15Sgtb    if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
900ab9b2e15Sgtb       /*
901ab9b2e15Sgtb        * Solaris Kerberos: here we are using the Solaris specific
902ab9b2e15Sgtb        * function get_default_cred() to handle the special case of a
903ab9b2e15Sgtb        * root principal
904ab9b2e15Sgtb        */
905ab9b2e15Sgtb       major_status = get_default_cred(minor_status, context,
906ab9b2e15Sgtb 				    (gss_cred_id_t *)&cred);
907ab9b2e15Sgtb       if (major_status && GSS_ERROR(major_status)) {
908ab9b2e15Sgtb 	 if (*context_handle == GSS_C_NO_CONTEXT)
909ab9b2e15Sgtb 	    krb5_free_context(context);
910ab9b2e15Sgtb 	 return(major_status);
911ab9b2e15Sgtb       }
912ab9b2e15Sgtb    } else {
913ab9b2e15Sgtb       major_status = krb5_gss_validate_cred(minor_status, claimant_cred_handle);
914ab9b2e15Sgtb       if (GSS_ERROR(major_status)) {
915ab9b2e15Sgtb 	  if (*context_handle == GSS_C_NO_CONTEXT)
916ab9b2e15Sgtb 	      krb5_free_context(context);
917ab9b2e15Sgtb 	  return(major_status);
918ab9b2e15Sgtb       }
919ab9b2e15Sgtb       cred = (krb5_gss_cred_id_t) claimant_cred_handle;
920ab9b2e15Sgtb    }
921ab9b2e15Sgtb    kerr = k5_mutex_lock(&cred->lock);
922ab9b2e15Sgtb    if (kerr) {
923ab9b2e15Sgtb        krb5_free_context(context);
924ab9b2e15Sgtb        *minor_status = kerr;
925ab9b2e15Sgtb        return GSS_S_FAILURE;
926ab9b2e15Sgtb    }
9277c478bd9Sstevel@tonic-gate 
928ab9b2e15Sgtb    /* verify the mech_type */
9297c478bd9Sstevel@tonic-gate 
930ab9b2e15Sgtb    err = 0;
931ab9b2e15Sgtb    if (mech_type == GSS_C_NULL_OID) {
932ab9b2e15Sgtb        default_mech = 1;
933ab9b2e15Sgtb        if (cred->rfc_mech) {
934ab9b2e15Sgtb 	   mech_type = (gss_OID) gss_mech_krb5;
935ab9b2e15Sgtb        } else if (cred->prerfc_mech) {
936ab9b2e15Sgtb 	   mech_type = (gss_OID) gss_mech_krb5_old;
937ab9b2e15Sgtb        } else {
938ab9b2e15Sgtb 	   err = 1;
939ab9b2e15Sgtb        }
940ab9b2e15Sgtb    } else if (g_OID_equal(mech_type, gss_mech_krb5)) {
941ab9b2e15Sgtb        if (!cred->rfc_mech)
942ab9b2e15Sgtb 	   err = 1;
943ab9b2e15Sgtb    } else if (g_OID_equal(mech_type, gss_mech_krb5_old)) {
944ab9b2e15Sgtb        if (!cred->prerfc_mech)
945ab9b2e15Sgtb 	   err = 1;
946ab9b2e15Sgtb    } else if (g_OID_equal(mech_type, gss_mech_krb5_wrong)) {
947ab9b2e15Sgtb        if (!cred->rfc_mech)
948ab9b2e15Sgtb 	   err = 1;
949ab9b2e15Sgtb    } else {
950ab9b2e15Sgtb        err = 1;
951ab9b2e15Sgtb    }
952ab9b2e15Sgtb 
953ab9b2e15Sgtb    if (err) {
954ab9b2e15Sgtb       k5_mutex_unlock(&cred->lock);
955ab9b2e15Sgtb       if (claimant_cred_handle == GSS_C_NO_CREDENTIAL)
956ab9b2e15Sgtb 	 krb5_gss_release_cred(minor_status, (gss_cred_id_t *)&cred);
957ab9b2e15Sgtb       *minor_status = 0;
958ab9b2e15Sgtb       if (*context_handle == GSS_C_NO_CONTEXT)
959ab9b2e15Sgtb 	 krb5_free_context(context);
960ab9b2e15Sgtb       return(GSS_S_BAD_MECH);
961ab9b2e15Sgtb    }
9627c478bd9Sstevel@tonic-gate 
963ab9b2e15Sgtb    /* is this a new connection or not? */
9647c478bd9Sstevel@tonic-gate 
965ab9b2e15Sgtb    /*SUPPRESS 29*/
966ab9b2e15Sgtb    if (*context_handle == GSS_C_NO_CONTEXT) {
967ab9b2e15Sgtb       major_status = new_connection(minor_status, cred, context_handle,
968ab9b2e15Sgtb 				    target_name, mech_type, req_flags,
969ab9b2e15Sgtb 				    time_req, input_chan_bindings,
970ab9b2e15Sgtb 				    input_token, actual_mech_type,
971ab9b2e15Sgtb 				    output_token, ret_flags, time_rec,
972ab9b2e15Sgtb 				    context, default_mech);
973ab9b2e15Sgtb       k5_mutex_unlock(&cred->lock);
974ab9b2e15Sgtb       if (*context_handle == GSS_C_NO_CONTEXT)
975ab9b2e15Sgtb 	  krb5_free_context(context);
976ab9b2e15Sgtb       else
977ab9b2e15Sgtb 	  ((krb5_gss_ctx_id_rec *) *context_handle)->k5_context = context;
978ab9b2e15Sgtb    } else {
979ab9b2e15Sgtb       /* mutual_auth doesn't care about the credentials */
980ab9b2e15Sgtb       k5_mutex_unlock(&cred->lock);
981ab9b2e15Sgtb       major_status = mutual_auth(minor_status, context_handle,
982ab9b2e15Sgtb 				 target_name, mech_type, req_flags,
983ab9b2e15Sgtb 				 time_req, input_chan_bindings,
984ab9b2e15Sgtb 				 input_token, actual_mech_type,
985ab9b2e15Sgtb 				 output_token, ret_flags, time_rec,
986ab9b2e15Sgtb 				 context);
987ab9b2e15Sgtb       /* If context_handle is now NO_CONTEXT, mutual_auth called
988ab9b2e15Sgtb 	 delete_sec_context, which would've zapped the krb5 context
989ab9b2e15Sgtb 	 too.  */
990ab9b2e15Sgtb    }
9917c478bd9Sstevel@tonic-gate 
992ab9b2e15Sgtb    if (claimant_cred_handle == GSS_C_NO_CREDENTIAL)
993ab9b2e15Sgtb       krb5_gss_release_cred(&tmp_min_stat, (gss_cred_id_t *)&cred);
9947c478bd9Sstevel@tonic-gate 
995ab9b2e15Sgtb    return(major_status);
9967c478bd9Sstevel@tonic-gate }
9977c478bd9Sstevel@tonic-gate 
998*159d09a2SMark Phalan #ifndef _WIN32
999ab9b2e15Sgtb k5_mutex_t kg_kdc_flag_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
1000ab9b2e15Sgtb static int kdc_flag = 0;
1001ab9b2e15Sgtb #endif
10027c478bd9Sstevel@tonic-gate 
1003ab9b2e15Sgtb krb5_error_code
1004ab9b2e15Sgtb krb5_gss_init_context (krb5_context *ctxp)
10057c478bd9Sstevel@tonic-gate {
1006ab9b2e15Sgtb     krb5_error_code err;
1007*159d09a2SMark Phalan #ifndef _WIN32
1008ab9b2e15Sgtb     int is_kdc;
1009*159d09a2SMark Phalan #endif
1010ab9b2e15Sgtb 
1011ab9b2e15Sgtb     err = gssint_initialize_library();
1012ab9b2e15Sgtb     if (err)
1013ab9b2e15Sgtb 	return err;
1014*159d09a2SMark Phalan #ifndef _WIN32
1015ab9b2e15Sgtb     err = k5_mutex_lock(&kg_kdc_flag_mutex);
1016ab9b2e15Sgtb     if (err)
1017ab9b2e15Sgtb 	return err;
1018ab9b2e15Sgtb     is_kdc = kdc_flag;
1019ab9b2e15Sgtb     k5_mutex_unlock(&kg_kdc_flag_mutex);
1020ab9b2e15Sgtb 
1021ab9b2e15Sgtb     if (is_kdc)
1022ab9b2e15Sgtb 	return krb5int_init_context_kdc(ctxp);
1023ab9b2e15Sgtb #endif
10247c478bd9Sstevel@tonic-gate 
1025*159d09a2SMark Phalan     return krb5_init_context(ctxp);
1026ab9b2e15Sgtb }
10277c478bd9Sstevel@tonic-gate 
1028*159d09a2SMark Phalan #ifndef _WIN32
1029ab9b2e15Sgtb krb5_error_code
1030ab9b2e15Sgtb krb5_gss_use_kdc_context()
1031ab9b2e15Sgtb {
1032ab9b2e15Sgtb     krb5_error_code err;
1033ab9b2e15Sgtb 
1034ab9b2e15Sgtb     err = gssint_initialize_library();
1035ab9b2e15Sgtb     if (err)
1036ab9b2e15Sgtb 	return err;
1037ab9b2e15Sgtb     err = k5_mutex_lock(&kg_kdc_flag_mutex);
1038ab9b2e15Sgtb     if (err)
1039ab9b2e15Sgtb 	return err;
1040ab9b2e15Sgtb     kdc_flag = 1;
1041ab9b2e15Sgtb     k5_mutex_unlock(&kg_kdc_flag_mutex);
1042ab9b2e15Sgtb     return 0;
1043ab9b2e15Sgtb }
1044ab9b2e15Sgtb #endif
10457c478bd9Sstevel@tonic-gate 
1046ab9b2e15Sgtb /* Solaris Kerberos specific routines start */
10477c478bd9Sstevel@tonic-gate 
1048ab9b2e15Sgtb #define ROOT_UID 0
1049ab9b2e15Sgtb #define KRB5_DEFAULT_LIFE 60*60*10
1050ab9b2e15Sgtb #define CACHE_FILENAME_LEN 35
10517c478bd9Sstevel@tonic-gate 
1052ab9b2e15Sgtb extern int
1053ab9b2e15Sgtb safechown(const char *src, uid_t uid, gid_t gid, int mode);
10547c478bd9Sstevel@tonic-gate 
1055ab9b2e15Sgtb static krb5_boolean
1056ab9b2e15Sgtb principal_ignore_inst_compare(context, princ1, princ2)
1057ab9b2e15Sgtb     krb5_context context;
1058ab9b2e15Sgtb     krb5_const_principal princ1;
1059ab9b2e15Sgtb     krb5_const_principal princ2;
1060ab9b2e15Sgtb {
1061ab9b2e15Sgtb     krb5_int32 nelem;
10627c478bd9Sstevel@tonic-gate 
1063ab9b2e15Sgtb     nelem = krb5_princ_size(context, princ1);
1064ab9b2e15Sgtb     if (nelem != krb5_princ_size(context, princ2))
1065ab9b2e15Sgtb 	return FALSE;
10667c478bd9Sstevel@tonic-gate 
1067ab9b2e15Sgtb     if (! krb5_realm_compare(context, princ1, princ2))
1068ab9b2e15Sgtb 	return FALSE;
10697c478bd9Sstevel@tonic-gate 
1070ab9b2e15Sgtb     /*
1071ab9b2e15Sgtb      * Solaris Kerberos
1072ab9b2e15Sgtb      * If princ1 is elem1/metachar@REALM, compare just elem1 (and REALM).
1073ab9b2e15Sgtb      */
1074ab9b2e15Sgtb     if (nelem == 2) {
1075ab9b2e15Sgtb         const krb5_data *p = krb5_princ_component(context, princ1, 1);
1076ab9b2e15Sgtb 
1077ab9b2e15Sgtb 	if (p->length == 1) {
1078ab9b2e15Sgtb 	    const char *s = p->data;
10797c478bd9Sstevel@tonic-gate 
1080ab9b2e15Sgtb 	    if (s[0] == '*') {
1081ab9b2e15Sgtb 		const krb5_data *p1 = krb5_princ_component(context, princ1, 0);
1082ab9b2e15Sgtb 		const krb5_data *p2 = krb5_princ_component(context, princ2, 0);
10837c478bd9Sstevel@tonic-gate 
1084ab9b2e15Sgtb 		if (p1->length != p2->length ||
1085ab9b2e15Sgtb 		        memcmp(p1->data, p2->data, p1->length))
1086ab9b2e15Sgtb 		    return FALSE;
1087ab9b2e15Sgtb 
1088ab9b2e15Sgtb 		return TRUE;
1089ab9b2e15Sgtb 	    }
1090ab9b2e15Sgtb 	}
1091ab9b2e15Sgtb     }
1092ab9b2e15Sgtb 
1093ab9b2e15Sgtb     return FALSE;
1094ab9b2e15Sgtb }
10957c478bd9Sstevel@tonic-gate 
1096ab9b2e15Sgtb /*
1097ab9b2e15Sgtb  * Solaris Kerberos
1098ab9b2e15Sgtb  * This is a dup of krb5_ktfile_get_entry (sigh) but is necessary to
1099ab9b2e15Sgtb  * to get a custom princ compare above (principal_ignore_inst_compare)
1100ab9b2e15Sgtb  * and thus avoid mucking w/important krb5 internal
1101ab9b2e15Sgtb  * api (krb5_principal_compare)
1102ab9b2e15Sgtb  */
1103ab9b2e15Sgtb #include "../krb5/keytab/file/ktfile.h"
11047c478bd9Sstevel@tonic-gate 
1105ab9b2e15Sgtb static krb5_error_code KRB5_CALLCONV
1106ab9b2e15Sgtb ktfile_get_entry(context, id, principal, kvno, enctype, entry)
1107ab9b2e15Sgtb    krb5_context context;
1108ab9b2e15Sgtb    krb5_keytab id;
1109ab9b2e15Sgtb    krb5_const_principal principal;
1110ab9b2e15Sgtb    krb5_kvno kvno;
1111ab9b2e15Sgtb    krb5_enctype enctype;
1112ab9b2e15Sgtb    krb5_keytab_entry * entry;
1113ab9b2e15Sgtb {
1114ab9b2e15Sgtb     krb5_keytab_entry cur_entry, new_entry;
1115ab9b2e15Sgtb     krb5_error_code kerror = 0;
1116ab9b2e15Sgtb     int found_wrong_kvno = 0;
1117ab9b2e15Sgtb     krb5_boolean similar;
1118ab9b2e15Sgtb     int kvno_offset = 0;
11197c478bd9Sstevel@tonic-gate 
1120ab9b2e15Sgtb     KRB5_LOG0(KRB5_INFO, "ktfile_get_entry() start\n");
11217c478bd9Sstevel@tonic-gate 
1122ab9b2e15Sgtb     /* Open the keyfile for reading */
1123ab9b2e15Sgtb     if ((kerror = krb5_ktfileint_openr(context, id))){
1124ab9b2e15Sgtb 	KRB5_LOG(KRB5_ERR, "ktfile_get_entry() end, ktfileint_openr() "
1125ab9b2e15Sgtb 		"kerror= %d\n", kerror);
1126ab9b2e15Sgtb 	return(kerror);
1127ab9b2e15Sgtb     }
11287c478bd9Sstevel@tonic-gate 
1129ab9b2e15Sgtb     /*
1130ab9b2e15Sgtb      * For efficiency and simplicity, we'll use a while true that
1131ab9b2e15Sgtb      * is exited with a break statement.
1132ab9b2e15Sgtb      */
1133ab9b2e15Sgtb     cur_entry.principal = 0;
1134ab9b2e15Sgtb     cur_entry.vno = 0;
1135ab9b2e15Sgtb     cur_entry.key.contents = 0;
1136ab9b2e15Sgtb     /*CONSTCOND*/
1137ab9b2e15Sgtb     while (TRUE) {
1138ab9b2e15Sgtb 	if ((kerror = krb5_ktfileint_read_entry(context, id, &new_entry)))
1139ab9b2e15Sgtb 	    break;
11407c478bd9Sstevel@tonic-gate 
1141ab9b2e15Sgtb 	/*
1142ab9b2e15Sgtb 	 * by the time this loop exits, it must either free cur_entry,
1143ab9b2e15Sgtb 	 * and copy new_entry there, or free new_entry.  Otherwise, it
1144ab9b2e15Sgtb 	 * leaks.
1145ab9b2e15Sgtb 	 */
1146ab9b2e15Sgtb 
1147ab9b2e15Sgtb 	/*
1148ab9b2e15Sgtb 	 * if the principal isn't the one requested, free new_entry
1149ab9b2e15Sgtb 	 * and continue to the next.
1150ab9b2e15Sgtb 	 */
11517c478bd9Sstevel@tonic-gate 
1152ab9b2e15Sgtb 	if (!principal_ignore_inst_compare(context, principal,
1153ab9b2e15Sgtb 					new_entry.principal)) {
1154ab9b2e15Sgtb 		krb5_kt_free_entry(context, &new_entry);
1155ab9b2e15Sgtb 	    continue;
11567c478bd9Sstevel@tonic-gate 	}
11577c478bd9Sstevel@tonic-gate 
1158ab9b2e15Sgtb 	/*
1159ab9b2e15Sgtb 	 * if the enctype is not ignored and doesn't match, free new_entry
1160ab9b2e15Sgtb 	 * and continue to the next
1161ab9b2e15Sgtb 	 */
11627c478bd9Sstevel@tonic-gate 
1163ab9b2e15Sgtb 	if (enctype != IGNORE_ENCTYPE) {
1164ab9b2e15Sgtb 	    if ((kerror = krb5_c_enctype_compare(context, enctype,
1165ab9b2e15Sgtb 						 new_entry.key.enctype,
1166ab9b2e15Sgtb 						 &similar))) {
1167ab9b2e15Sgtb 		krb5_kt_free_entry(context, &new_entry);
1168ab9b2e15Sgtb 		break;
1169ab9b2e15Sgtb 	    }
11707c478bd9Sstevel@tonic-gate 
1171ab9b2e15Sgtb 	    if (!similar) {
1172ab9b2e15Sgtb 		krb5_kt_free_entry(context, &new_entry);
1173ab9b2e15Sgtb 		continue;
1174ab9b2e15Sgtb 	    }
1175ab9b2e15Sgtb 	    /*
1176ab9b2e15Sgtb 	     * Coerce the enctype of the output keyblock in case we
1177ab9b2e15Sgtb 	     * got an inexact match on the enctype.
1178ab9b2e15Sgtb 	     */
1179ab9b2e15Sgtb 	    new_entry.key.enctype = enctype;
1180ab9b2e15Sgtb 	}
11817c478bd9Sstevel@tonic-gate 
1182ab9b2e15Sgtb 	if (kvno == IGNORE_VNO) {
1183ab9b2e15Sgtb 	    /*
1184ab9b2e15Sgtb 	     * if this is the first match, or if the new vno is
1185ab9b2e15Sgtb 	     * bigger, free the current and keep the new.  Otherwise,
1186ab9b2e15Sgtb 	     * free the new.
1187ab9b2e15Sgtb 	     */
1188ab9b2e15Sgtb 	    /*
1189ab9b2e15Sgtb 	     * A 1.2.x keytab contains only the low 8 bits of the key
1190ab9b2e15Sgtb 	     * version number.  Since it can be much bigger, and thus
1191ab9b2e15Sgtb 	     * the 8-bit value can wrap, we need some heuristics to
1192ab9b2e15Sgtb 	     * figure out the "highest" numbered key if some numbers
1193ab9b2e15Sgtb 	     * close to 255 and some near 0 are used.
1194ab9b2e15Sgtb 	     *
1195ab9b2e15Sgtb 	     * The heuristic here:
11967c478bd9Sstevel@tonic-gate 
1197ab9b2e15Sgtb 	     * If we have any keys with versions over 240, then assume
1198ab9b2e15Sgtb 	     * that all version numbers 0-127 refer to 256+N instead.
1199ab9b2e15Sgtb 	     * Not perfect, but maybe good enough?
1200ab9b2e15Sgtb 	     */
12017c478bd9Sstevel@tonic-gate 
1202ab9b2e15Sgtb #define M(VNO) (((VNO) - kvno_offset + 256) % 256)
12037c478bd9Sstevel@tonic-gate 
1204ab9b2e15Sgtb 	    if (new_entry.vno > 240)
1205ab9b2e15Sgtb 		kvno_offset = 128;
1206ab9b2e15Sgtb 	    if (! cur_entry.principal ||
1207ab9b2e15Sgtb 		M(new_entry.vno) > M(cur_entry.vno)) {
1208ab9b2e15Sgtb 		krb5_kt_free_entry(context, &cur_entry);
1209ab9b2e15Sgtb 		cur_entry = new_entry;
1210ab9b2e15Sgtb 	    } else {
1211ab9b2e15Sgtb 		krb5_kt_free_entry(context, &new_entry);
1212ab9b2e15Sgtb 	    }
1213ab9b2e15Sgtb 	} else {
1214ab9b2e15Sgtb 	    /*
1215ab9b2e15Sgtb 	     * if this kvno matches, free the current (will there ever
1216ab9b2e15Sgtb 	     * be one?), keep the new, and break out.  Otherwise, remember
1217ab9b2e15Sgtb 	     * that we were here so we can return the right error, and
1218ab9b2e15Sgtb 	     * free the new
1219ab9b2e15Sgtb 	     */
1220ab9b2e15Sgtb 	    /*
1221ab9b2e15Sgtb 	     * Yuck.  The krb5-1.2.x keytab format only stores one byte
1222ab9b2e15Sgtb 	     * for the kvno, so we're toast if the kvno requested is
1223ab9b2e15Sgtb 	     * higher than that.  Short-term workaround: only compare
1224ab9b2e15Sgtb 	     * the low 8 bits.
1225ab9b2e15Sgtb 	     */
12267c478bd9Sstevel@tonic-gate 
1227ab9b2e15Sgtb 	    if (new_entry.vno == (kvno & 0xff)) {
1228ab9b2e15Sgtb 		krb5_kt_free_entry(context, &cur_entry);
1229ab9b2e15Sgtb 		cur_entry = new_entry;
1230ab9b2e15Sgtb 		break;
1231ab9b2e15Sgtb 	    } else {
1232ab9b2e15Sgtb 		found_wrong_kvno++;
1233ab9b2e15Sgtb 		krb5_kt_free_entry(context, &new_entry);
1234ab9b2e15Sgtb 	    }
12357c478bd9Sstevel@tonic-gate 	}
1236ab9b2e15Sgtb     }
12377c478bd9Sstevel@tonic-gate 
1238ab9b2e15Sgtb     if (kerror == KRB5_KT_END) {
1239ab9b2e15Sgtb 	 if (cur_entry.principal)
1240ab9b2e15Sgtb 	      kerror = 0;
1241ab9b2e15Sgtb 	 else if (found_wrong_kvno)
1242ab9b2e15Sgtb 	      kerror = KRB5_KT_KVNONOTFOUND;
1243ab9b2e15Sgtb 	 else
1244ab9b2e15Sgtb 	      kerror = KRB5_KT_NOTFOUND;
1245ab9b2e15Sgtb     }
1246ab9b2e15Sgtb     if (kerror) {
1247ab9b2e15Sgtb 	(void) krb5_ktfileint_close(context, id);
1248ab9b2e15Sgtb 	krb5_kt_free_entry(context, &cur_entry);
1249ab9b2e15Sgtb 	KRB5_LOG(KRB5_ERR,"ktfile_get_entry() end, kerror="
1250ab9b2e15Sgtb 		    "%d\n", kerror);
1251ab9b2e15Sgtb 	return kerror;
1252ab9b2e15Sgtb     }
1253ab9b2e15Sgtb     if ((kerror = krb5_ktfileint_close(context, id)) != 0) {
1254ab9b2e15Sgtb 	krb5_kt_free_entry(context, &cur_entry);
1255ab9b2e15Sgtb 	KRB5_LOG(KRB5_ERR,"ktfile_get_entry() end, ktfileint_close() "
1256ab9b2e15Sgtb 	       "kerror= %d\n", kerror);
1257ab9b2e15Sgtb 	return kerror;
1258ab9b2e15Sgtb     }
1259ab9b2e15Sgtb     *entry = cur_entry;
12607c478bd9Sstevel@tonic-gate 
1261ab9b2e15Sgtb     /* Let us close the file before we leave */
1262ab9b2e15Sgtb     (void) krb5_ktfileint_close(context, id);
12637c478bd9Sstevel@tonic-gate 
1264ab9b2e15Sgtb     KRB5_LOG0(KRB5_INFO, "ktfile_get_entry() end");
1265ab9b2e15Sgtb 
1266ab9b2e15Sgtb     return 0;
12677c478bd9Sstevel@tonic-gate }
12687c478bd9Sstevel@tonic-gate 
1269ab9b2e15Sgtb 
12707c478bd9Sstevel@tonic-gate /*
1271ab9b2e15Sgtb  * Solaris Kerberos
1272ab9b2e15Sgtb  * Given a princ of name/instance@LOCALREALM, search the keytab
1273ab9b2e15Sgtb  * for a match of name and LOCALREALM and if found, return instance
1274ab9b2e15Sgtb  * as a string.
12757c478bd9Sstevel@tonic-gate  *
1276ab9b2e15Sgtb  * Caller must free returned string.
12777c478bd9Sstevel@tonic-gate  */
1278ab9b2e15Sgtb static krb5_error_code
1279ab9b2e15Sgtb get_instance_keytab(
1280ab9b2e15Sgtb 	krb5_context context,
1281ab9b2e15Sgtb 	const char *sname,
1282ab9b2e15Sgtb 	krb5_keytab keytab,
1283ab9b2e15Sgtb 	char  **instance)  /* out */
12847c478bd9Sstevel@tonic-gate {
1285ab9b2e15Sgtb 	krb5_error_code ret=0;
1286ab9b2e15Sgtb 	krb5_keytab_entry kt_ent;
1287ab9b2e15Sgtb 	krb5_int32 nelem, free_kt_ent=0;
1288ab9b2e15Sgtb 	register const krb5_data *p;
1289ab9b2e15Sgtb 	char *realm=NULL, *s=NULL;
1290ab9b2e15Sgtb 	krb5_principal client=NULL, princ=NULL;
12917c478bd9Sstevel@tonic-gate 
1292ab9b2e15Sgtb 	if (!keytab)
1293ab9b2e15Sgtb 		return EINVAL;
12947c478bd9Sstevel@tonic-gate 
1295ab9b2e15Sgtb 	if (ret = krb5_get_default_realm(context, &realm))
1296ab9b2e15Sgtb 		return ret;
12977c478bd9Sstevel@tonic-gate 
1298ab9b2e15Sgtb 	ret = krb5_build_principal(context, &client, strlen(realm),
1299ab9b2e15Sgtb 				      realm, sname, "*",
1300ab9b2e15Sgtb 				      (char *)0);
1301ab9b2e15Sgtb 	if (ret)
1302ab9b2e15Sgtb 		goto out;
13037c478bd9Sstevel@tonic-gate 
1304ab9b2e15Sgtb 	ret = ktfile_get_entry(context, keytab, client,
1305ab9b2e15Sgtb 				0, /* don't have vno available */
1306ab9b2e15Sgtb 				0, &kt_ent);
1307ab9b2e15Sgtb 	if (ret)
1308ab9b2e15Sgtb 		goto out;
13097c478bd9Sstevel@tonic-gate 
1310ab9b2e15Sgtb 	free_kt_ent++;  /* kt_ent is not a ptr */
13117c478bd9Sstevel@tonic-gate 
1312ab9b2e15Sgtb 	princ = kt_ent.principal;
1313ab9b2e15Sgtb 	nelem = krb5_princ_size(context, princ);
1314ab9b2e15Sgtb 	if (nelem != 2) {
1315ab9b2e15Sgtb 		ret = KRB5_PRINC_NOMATCH;
1316ab9b2e15Sgtb 		goto out;
1317ab9b2e15Sgtb 	}
13187c478bd9Sstevel@tonic-gate 
1319ab9b2e15Sgtb 	p = krb5_princ_component(context, princ, 1);
1320ab9b2e15Sgtb 	s = calloc(p->length + sizeof(char), sizeof(char));
1321ab9b2e15Sgtb 	if (!s) {
1322ab9b2e15Sgtb 		ret = ENOMEM;
1323ab9b2e15Sgtb 		goto out;
1324ab9b2e15Sgtb 	}
13257c478bd9Sstevel@tonic-gate 
1326ab9b2e15Sgtb 	(void) memcpy(s, p->data, p->length);
13277c478bd9Sstevel@tonic-gate 
13287c478bd9Sstevel@tonic-gate 
1329ab9b2e15Sgtb out:
1330ab9b2e15Sgtb 	free(realm);
1331ab9b2e15Sgtb 	if (client)
1332ab9b2e15Sgtb 		krb5_free_principal(context, client);
1333ab9b2e15Sgtb 	if (free_kt_ent)
1334ab9b2e15Sgtb 		(void) krb5_kt_free_entry(context, &kt_ent);
1335ab9b2e15Sgtb 
1336ab9b2e15Sgtb 	if (ret == 0)
1337ab9b2e15Sgtb 		*instance = s;
1338ab9b2e15Sgtb 	return ret;
1339ab9b2e15Sgtb }
13407c478bd9Sstevel@tonic-gate 
1341ab9b2e15Sgtb static OM_uint32
1342ab9b2e15Sgtb load_root_cred_using_keytab(
1343ab9b2e15Sgtb 	OM_uint32 *minor_status,
1344ab9b2e15Sgtb 	krb5_context context,
1345ab9b2e15Sgtb 	const char *sname,
1346ab9b2e15Sgtb 	int use_nodename)
1347ab9b2e15Sgtb {
1348ab9b2e15Sgtb 	krb5_creds my_creds;
1349ab9b2e15Sgtb 	krb5_principal me;
1350ab9b2e15Sgtb 	krb5_principal server;
1351ab9b2e15Sgtb 	krb5_error_code code;
1352ab9b2e15Sgtb 	krb5_ccache ccache = NULL;
1353ab9b2e15Sgtb 	krb5_keytab keytab = NULL;
1354ab9b2e15Sgtb 	krb5_timestamp now;
1355ab9b2e15Sgtb 	krb5_deltat lifetime = KRB5_DEFAULT_LIFE;   /* -l option */
1356ab9b2e15Sgtb 	krb5_get_init_creds_opt opt;
1357ab9b2e15Sgtb 	krb5_data tgtname = {
1358ab9b2e15Sgtb 		0,
1359ab9b2e15Sgtb 		KRB5_TGS_NAME_SIZE,
1360ab9b2e15Sgtb 		KRB5_TGS_NAME
1361ab9b2e15Sgtb 	};
1362ab9b2e15Sgtb 	char *svcname = NULL;
13637c478bd9Sstevel@tonic-gate 
1364ab9b2e15Sgtb 	KRB5_LOG0(KRB5_INFO, "load_root_cred_using_keytab() start \n");
13657c478bd9Sstevel@tonic-gate 
1366ab9b2e15Sgtb 	if (!sname)
1367ab9b2e15Sgtb 		return (GSS_S_FAILURE);
13687c478bd9Sstevel@tonic-gate 
1369ab9b2e15Sgtb 	memset((char *)&my_creds, 0, sizeof(my_creds));
13707c478bd9Sstevel@tonic-gate 
1371ab9b2e15Sgtb 	if (code = krb5_kt_default(context, &keytab)) {
1372ab9b2e15Sgtb 		*minor_status = code;
1373ab9b2e15Sgtb 		return (GSS_S_FAILURE);
1374ab9b2e15Sgtb 	}
13757c478bd9Sstevel@tonic-gate 
1376ab9b2e15Sgtb 	if (!use_nodename) {
1377ab9b2e15Sgtb 		char *instance = NULL;
13787c478bd9Sstevel@tonic-gate 
1379ab9b2e15Sgtb 		code = get_instance_keytab(context, sname, keytab, &instance);
1380ab9b2e15Sgtb 		if (code == 0) {
1381ab9b2e15Sgtb 			code = krb5_sname_to_principal(context,
1382ab9b2e15Sgtb 						    instance, sname,
1383ab9b2e15Sgtb 						    KRB5_NT_UNKNOWN, &me);
1384ab9b2e15Sgtb 			free(instance);
1385ab9b2e15Sgtb 		}
1386ab9b2e15Sgtb 	} else {
1387ab9b2e15Sgtb 		code = krb5_sname_to_principal(context, NULL, sname,
1388ab9b2e15Sgtb 					    KRB5_NT_SRV_HST, &me);
1389ab9b2e15Sgtb 	}
1390ab9b2e15Sgtb 	if (code) {
1391ab9b2e15Sgtb 		(void) krb5_kt_close(context, keytab);
1392ab9b2e15Sgtb 		*minor_status = code;
1393ab9b2e15Sgtb 		return (GSS_S_FAILURE);
1394ab9b2e15Sgtb 	}
13957c478bd9Sstevel@tonic-gate 
1396ab9b2e15Sgtb 	my_creds.client = me;
13977c478bd9Sstevel@tonic-gate 
1398ab9b2e15Sgtb 	if((code = krb5_build_principal_ext(context, &server,
1399ab9b2e15Sgtb 					krb5_princ_realm(context, me)->length,
1400ab9b2e15Sgtb 					krb5_princ_realm(context, me)->data,
1401ab9b2e15Sgtb 					tgtname.length, tgtname.data,
1402ab9b2e15Sgtb 					krb5_princ_realm(context, me)->length,
1403ab9b2e15Sgtb 					krb5_princ_realm(context, me)->data,
1404ab9b2e15Sgtb 					0))) {
1405ab9b2e15Sgtb 		*minor_status = code;
1406ab9b2e15Sgtb 		krb5_free_cred_contents(context, &my_creds);
1407ab9b2e15Sgtb 		(void) krb5_kt_close(context, keytab);
14087c478bd9Sstevel@tonic-gate 
1409ab9b2e15Sgtb 		return (GSS_S_FAILURE);
1410ab9b2e15Sgtb 	}
14117c478bd9Sstevel@tonic-gate 
1412ab9b2e15Sgtb 	my_creds.server = server;
1413ab9b2e15Sgtb 	my_creds.times.starttime = 0;     /* start timer
1414ab9b2e15Sgtb 					   * when request
1415ab9b2e15Sgtb 					   * gets to KDC
1416ab9b2e15Sgtb 					   */
1417ab9b2e15Sgtb 	if ((code = krb5_timeofday(context, &now))) {
1418ab9b2e15Sgtb 		*minor_status = code;
1419ab9b2e15Sgtb 		krb5_free_cred_contents(context, &my_creds);
1420ab9b2e15Sgtb 		(void) krb5_kt_close(context, keytab);
14217c478bd9Sstevel@tonic-gate 
1422ab9b2e15Sgtb 		return (GSS_S_FAILURE);
1423ab9b2e15Sgtb 	}
1424ab9b2e15Sgtb 	my_creds.times.endtime = now + lifetime;
1425ab9b2e15Sgtb 	my_creds.times.renew_till = 0;
14267c478bd9Sstevel@tonic-gate 
1427ab9b2e15Sgtb 	memset(&opt, 0, sizeof (opt));
1428ab9b2e15Sgtb 	krb5_get_init_creds_opt_init(&opt);
1429ab9b2e15Sgtb 	krb5_get_init_creds_opt_set_tkt_life(&opt, lifetime);
14307c478bd9Sstevel@tonic-gate 
1431ab9b2e15Sgtb 	code = krb5_unparse_name(context, server, &svcname);
1432ab9b2e15Sgtb 	if (code != 0) {
1433ab9b2e15Sgtb 		*minor_status = code;
1434ab9b2e15Sgtb 		krb5_free_cred_contents(context, &my_creds);
1435ab9b2e15Sgtb 		(void) krb5_kt_close(context, keytab);
14367c478bd9Sstevel@tonic-gate 
1437ab9b2e15Sgtb 		return (GSS_S_FAILURE);
1438ab9b2e15Sgtb 	}
1439ab9b2e15Sgtb 	/*
1440ab9b2e15Sgtb 	 * Evidently (sigh), on success, krb5_get_init_creds_keytab
1441ab9b2e15Sgtb 	 * changes the my_creds princ ptrs so we need to free those
1442ab9b2e15Sgtb 	 * princs (me&server) as well as freeing all of my_creds contents.
1443ab9b2e15Sgtb 	 */
1444ab9b2e15Sgtb 	code = krb5_get_init_creds_keytab(context,
1445ab9b2e15Sgtb                                 &my_creds, me, keytab,
1446ab9b2e15Sgtb                                 0, svcname, &opt);
14477c478bd9Sstevel@tonic-gate 
1448ab9b2e15Sgtb 	(void) krb5_kt_close(context, keytab);
14497c478bd9Sstevel@tonic-gate 
1450ab9b2e15Sgtb 	if (svcname != NULL)
1451ab9b2e15Sgtb 		free(svcname);
1452ab9b2e15Sgtb 	if (code) {
1453ab9b2e15Sgtb 		*minor_status = code;
1454ab9b2e15Sgtb 		krb5_free_cred_contents(context, &my_creds);
14557c478bd9Sstevel@tonic-gate 
1456ab9b2e15Sgtb 		return (GSS_S_FAILURE);
1457ab9b2e15Sgtb 	}
14587c478bd9Sstevel@tonic-gate 
1459ab9b2e15Sgtb 	krb5_free_principal(context, server);
1460ab9b2e15Sgtb 	server = NULL;
14617c478bd9Sstevel@tonic-gate 
1462ab9b2e15Sgtb 	code = krb5_cc_resolve (context,
1463ab9b2e15Sgtb 				krb5_cc_default_name(context),
1464ab9b2e15Sgtb 				&ccache);
1465ab9b2e15Sgtb 	if (code != 0) {
1466ab9b2e15Sgtb 		*minor_status = code;
1467ab9b2e15Sgtb 		krb5_free_cred_contents(context, &my_creds);
1468ab9b2e15Sgtb 		krb5_free_principal(context, me);
14697c478bd9Sstevel@tonic-gate 
1470ab9b2e15Sgtb 		return (GSS_S_FAILURE);
1471ab9b2e15Sgtb 	}
1472ab9b2e15Sgtb 	code = krb5_cc_initialize (context, ccache, me);
1473ab9b2e15Sgtb 	krb5_free_principal(context, me);
1474ab9b2e15Sgtb 	me = NULL;
1475ab9b2e15Sgtb 	if (code != 0) {
1476ab9b2e15Sgtb 		*minor_status = code;
1477ab9b2e15Sgtb 		krb5_free_cred_contents(context, &my_creds);
1478ab9b2e15Sgtb 		(void) krb5_cc_close(context, ccache);
14797c478bd9Sstevel@tonic-gate 
1480ab9b2e15Sgtb 		return (GSS_S_FAILURE);
1481ab9b2e15Sgtb 	}
14827c478bd9Sstevel@tonic-gate 
1483ab9b2e15Sgtb 	code = krb5_cc_store_cred(context, ccache,
1484ab9b2e15Sgtb 				  &my_creds);
1485ab9b2e15Sgtb 	krb5_free_cred_contents(context, &my_creds);
1486ab9b2e15Sgtb 	(void) krb5_cc_close(context, ccache);
14877c478bd9Sstevel@tonic-gate 
1488ab9b2e15Sgtb 	if (code) {
1489ab9b2e15Sgtb 		*minor_status = code;
14907c478bd9Sstevel@tonic-gate 
1491ab9b2e15Sgtb 		KRB5_LOG(KRB5_ERR, "load_root_cred_using_keytab() end, error "
1492ab9b2e15Sgtb 			"code = %d\n", code);
14937c478bd9Sstevel@tonic-gate 
1494ab9b2e15Sgtb 		return (GSS_S_FAILURE);
14957c478bd9Sstevel@tonic-gate 	}
1496ab9b2e15Sgtb 
1497ab9b2e15Sgtb 	KRB5_LOG0(KRB5_INFO, "load_root_cred_using_keytab() end \n");
14987c478bd9Sstevel@tonic-gate 
1499ab9b2e15Sgtb 	return (GSS_S_COMPLETE);
1500ab9b2e15Sgtb }
15017c478bd9Sstevel@tonic-gate 
1502ab9b2e15Sgtb static OM_uint32
1503ab9b2e15Sgtb renew_ccache(OM_uint32 *minor_status, krb5_context context, uid_t uid)
1504ab9b2e15Sgtb {
1505ab9b2e15Sgtb 	krb5_principal me;
1506ab9b2e15Sgtb 	krb5_principal server;
1507ab9b2e15Sgtb 	krb5_creds	creds;
1508ab9b2e15Sgtb 	krb5_creds	tmpcreds;
1509ab9b2e15Sgtb 	krb5_creds	*out_creds;
1510ab9b2e15Sgtb 	krb5_error_code code;
1511ab9b2e15Sgtb 	krb5_ccache ccache = NULL;
1512ab9b2e15Sgtb 	static char ccache_name_buf[CACHE_FILENAME_LEN];
1513ab9b2e15Sgtb 	int options = 0;
1514ab9b2e15Sgtb 	krb5_data tgtname = {
1515ab9b2e15Sgtb 		0,
1516ab9b2e15Sgtb 		KRB5_TGS_NAME_SIZE,
1517ab9b2e15Sgtb 		KRB5_TGS_NAME
1518ab9b2e15Sgtb 	};
1519ab9b2e15Sgtb 	gid_t gid = getgid();
15207c478bd9Sstevel@tonic-gate 
1521ab9b2e15Sgtb 	memset((char *)&creds, 0, sizeof(creds));
1522ab9b2e15Sgtb 	memset((char *)&tmpcreds, 0, sizeof(creds));
15237c478bd9Sstevel@tonic-gate 
1524ab9b2e15Sgtb 	if ((code = krb5_cc_default(context, &ccache))) {
1525ab9b2e15Sgtb 		*minor_status = code;
1526ab9b2e15Sgtb 		(void) krb5_cc_close(context, ccache);
1527ab9b2e15Sgtb 		return (GSS_S_FAILURE);
1528ab9b2e15Sgtb 	}
15297c478bd9Sstevel@tonic-gate 
1530ab9b2e15Sgtb 	if ((code = krb5_cc_get_principal(context, ccache, &me)) != 0) {
1531ab9b2e15Sgtb 		*minor_status = code;
1532ab9b2e15Sgtb 		(void) krb5_cc_close(context, ccache);
1533ab9b2e15Sgtb 		return (GSS_S_FAILURE);
1534ab9b2e15Sgtb 	}
15357c478bd9Sstevel@tonic-gate 
1536ab9b2e15Sgtb 	creds.client = me;
15377c478bd9Sstevel@tonic-gate 
1538ab9b2e15Sgtb 	if((code = krb5_build_principal_ext(context, &server,
1539ab9b2e15Sgtb 					krb5_princ_realm(context, me)->length,
1540ab9b2e15Sgtb 					krb5_princ_realm(context, me)->data,
1541ab9b2e15Sgtb 					tgtname.length, tgtname.data,
1542ab9b2e15Sgtb 					krb5_princ_realm(context, me)->length,
1543ab9b2e15Sgtb 					krb5_princ_realm(context, me)->data,
1544ab9b2e15Sgtb 					0))) {
1545ab9b2e15Sgtb 		krb5_free_principal(context, me);
1546ab9b2e15Sgtb 		(void) krb5_cc_close(context, ccache);
1547ab9b2e15Sgtb 		*minor_status = code;
1548ab9b2e15Sgtb 		return (GSS_S_FAILURE);
1549ab9b2e15Sgtb 	}
15507c478bd9Sstevel@tonic-gate 
1551ab9b2e15Sgtb 	creds.server = server;
1552ab9b2e15Sgtb 	creds.ticket_flags = TKT_FLG_RENEWABLE;
1553ab9b2e15Sgtb 
1554ab9b2e15Sgtb 	if ((krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_FLAGS,
1555ab9b2e15Sgtb 			&creds, &tmpcreds))) {
1556ab9b2e15Sgtb 		(void) krb5_cc_close(context, ccache);
1557ab9b2e15Sgtb 		return (KDC_ERR_BADOPTION);
1558ab9b2e15Sgtb 	}
1559ab9b2e15Sgtb 
1560ab9b2e15Sgtb 	creds.ticket_flags = 0;
1561ab9b2e15Sgtb         code = krb5_get_credentials_renew(context, options, ccache,
1562ab9b2e15Sgtb 						&creds, &out_creds);
1563ab9b2e15Sgtb 	krb5_free_cred_contents(context, &creds);
1564ab9b2e15Sgtb 	krb5_free_cred_contents(context, &tmpcreds);
15657c478bd9Sstevel@tonic-gate 
1566ab9b2e15Sgtb 	if (code) {
1567ab9b2e15Sgtb 		*minor_status = code;
1568ab9b2e15Sgtb 		return (GSS_S_FAILURE);
1569ab9b2e15Sgtb 	}
15707c478bd9Sstevel@tonic-gate 
1571ab9b2e15Sgtb 	krb5_free_creds(context, out_creds);
1572ab9b2e15Sgtb 	snprintf(ccache_name_buf, CACHE_FILENAME_LEN, "/tmp/krb5cc_%d",
1573ab9b2e15Sgtb 		uid, -1);
1574ab9b2e15Sgtb 	code = safechown(ccache_name_buf, uid, gid, -1);
15757c478bd9Sstevel@tonic-gate 
1576ab9b2e15Sgtb 	if (code == -1) {
1577ab9b2e15Sgtb 		(void) krb5_cc_destroy(context, ccache);
1578ab9b2e15Sgtb 		*minor_status = code;
1579ab9b2e15Sgtb 		return (GSS_S_FAILURE);
1580ab9b2e15Sgtb 	}
15817c478bd9Sstevel@tonic-gate 
1582ab9b2e15Sgtb 	(void) krb5_cc_close(context, ccache);
1583ab9b2e15Sgtb 
1584ab9b2e15Sgtb 	return (GSS_S_COMPLETE);
15857c478bd9Sstevel@tonic-gate 
15867c478bd9Sstevel@tonic-gate }
15877c478bd9Sstevel@tonic-gate 
15887c478bd9Sstevel@tonic-gate /*
1589ab9b2e15Sgtb  * Solaris Kerberos:
1590ab9b2e15Sgtb  * We enforce a minimum refresh time on the root cred. This avoids problems for
1591ab9b2e15Sgtb  * the higher level communication protocol for having valid creds and
1592ab9b2e15Sgtb  * setting up a valid context, only to have it expire before or while
1593ab9b2e15Sgtb  * it is being used. For non root users we don't care since we do not refresh
1594ab9b2e15Sgtb  * there creds, they get what they can get.
15957c478bd9Sstevel@tonic-gate  */
1596ab9b2e15Sgtb #define MIN_REFRESH_TIME 300
1597ab9b2e15Sgtb #define MIN_RENEW_TIME 1500
1598ab9b2e15Sgtb 
1599ab9b2e15Sgtb /* get_default_cred() must be called with the krb5_mutex lock held */
1600ab9b2e15Sgtb static OM_uint32
1601ab9b2e15Sgtb get_default_cred(OM_uint32 *minor_status, void *ct, gss_cred_id_t *cred_handle)
16027c478bd9Sstevel@tonic-gate {
1603ab9b2e15Sgtb 	krb5_timestamp now;
1604ab9b2e15Sgtb 	krb5_gss_cred_id_t cred;
1605ab9b2e15Sgtb 	OM_uint32 major;
1606ab9b2e15Sgtb 	OM_uint32 mntmp;
1607ab9b2e15Sgtb 	/*
1608ab9b2e15Sgtb 	 * Solaris Kerberos
1609ab9b2e15Sgtb 	 * Use krb5_getuid() to select the mechanism to obtain the uid.
1610ab9b2e15Sgtb 	 */
1611ab9b2e15Sgtb 	uid_t uid = krb5_getuid();
1612ab9b2e15Sgtb 	krb5_context context = (krb5_context)ct;
16137c478bd9Sstevel@tonic-gate 
1614ab9b2e15Sgtb 	KRB5_LOG0(KRB5_INFO, "get_default_cred() start\n");
16157c478bd9Sstevel@tonic-gate 
1616ab9b2e15Sgtb 	/* Get the default cred for user */
1617ab9b2e15Sgtb 	if (((major = kg_get_defcred(minor_status, cred_handle)) != NULL) &&
1618ab9b2e15Sgtb 	    GSS_ERROR(major)) {
16197c478bd9Sstevel@tonic-gate 
1620ab9b2e15Sgtb 		/* If we're not root we're done */
1621ab9b2e15Sgtb    		if (uid != ROOT_UID)
1622ab9b2e15Sgtb 	 		return (major);
16237c478bd9Sstevel@tonic-gate 
1624ab9b2e15Sgtb 		/*
1625ab9b2e15Sgtb 		 * Try and get root's cred in the cache using keytab.
1626ab9b2e15Sgtb 		 *
1627ab9b2e15Sgtb 		 * First try "root" and then try "host" - this allows
1628ab9b2e15Sgtb 		 * Secure NFS to use the host principal for mounting if
1629ab9b2e15Sgtb 		 * there is no root principal.
1630ab9b2e15Sgtb 		 *
1631ab9b2e15Sgtb 		 * Then try "host/<anything>" to match any instance (needed
1632ab9b2e15Sgtb 		 * for DHCP clients).
1633ab9b2e15Sgtb 		 */
1634ab9b2e15Sgtb 		major = load_root_cred_using_keytab(minor_status,
1635ab9b2e15Sgtb 						    context, "root", 1);
16367c478bd9Sstevel@tonic-gate 
1637ab9b2e15Sgtb 		if (major != GSS_S_COMPLETE)
1638ab9b2e15Sgtb 			major = load_root_cred_using_keytab(minor_status,
1639ab9b2e15Sgtb 							    context, "host", 1);
1640ab9b2e15Sgtb 		if (major != GSS_S_COMPLETE)
1641ab9b2e15Sgtb 			major = load_root_cred_using_keytab(minor_status,
1642ab9b2e15Sgtb 							    context, "host", 0);
16437c478bd9Sstevel@tonic-gate 
1644ab9b2e15Sgtb 		if (major != GSS_S_COMPLETE)
1645ab9b2e15Sgtb 			return (major);
16467c478bd9Sstevel@tonic-gate 
1647ab9b2e15Sgtb 		/* We should have valid tgt now in the cache, so get it. */
1648ab9b2e15Sgtb 		major = kg_get_defcred(minor_status, cred_handle);
16497c478bd9Sstevel@tonic-gate 
1650ab9b2e15Sgtb 		return (major);
1651ab9b2e15Sgtb       	}
16527c478bd9Sstevel@tonic-gate 
1653ab9b2e15Sgtb 	/* We've got a gss cred handle that is a kerberos cred handle. */
1654ab9b2e15Sgtb 	cred = (krb5_gss_cred_id_t)*cred_handle;
1655ab9b2e15Sgtb 
1656ab9b2e15Sgtb 	/* If we can't get the time, assume the worst. */
1657ab9b2e15Sgtb 	if (krb5_timeofday(context, &now)) {
1658ab9b2e15Sgtb 		(void) krb5_gss_release_cred(&mntmp, cred_handle);
1659ab9b2e15Sgtb 		return (GSS_S_CREDENTIALS_EXPIRED);
1660ab9b2e15Sgtb 	}
16617c478bd9Sstevel@tonic-gate 
1662ab9b2e15Sgtb 	/* If root's cred has expired re-get it */
1663ab9b2e15Sgtb 	if (cred->tgt_expire < now + MIN_REFRESH_TIME && uid == ROOT_UID) {
1664ab9b2e15Sgtb 		(void) krb5_gss_release_cred(&mntmp, cred_handle);
16657c478bd9Sstevel@tonic-gate 
1666ab9b2e15Sgtb 		major = load_root_cred_using_keytab(minor_status,
1667ab9b2e15Sgtb 						    context, "root", 1);
16687c478bd9Sstevel@tonic-gate 
1669ab9b2e15Sgtb 		if (major != GSS_S_COMPLETE)
1670ab9b2e15Sgtb 			major = load_root_cred_using_keytab(minor_status,
1671ab9b2e15Sgtb 							    context, "host", 1);
16727c478bd9Sstevel@tonic-gate 
1673ab9b2e15Sgtb 		if (major != GSS_S_COMPLETE)
1674ab9b2e15Sgtb 			major = load_root_cred_using_keytab(minor_status,
1675ab9b2e15Sgtb 							    context, "host", 0);
1676ab9b2e15Sgtb 
1677ab9b2e15Sgtb 		if (major != GSS_S_COMPLETE)
1678ab9b2e15Sgtb 			return (major);
1679ab9b2e15Sgtb 
1680ab9b2e15Sgtb 		major = kg_get_defcred(minor_status, cred_handle);
1681ab9b2e15Sgtb 		if (major != GSS_S_COMPLETE)
1682ab9b2e15Sgtb 			return (major);
1683ab9b2e15Sgtb 
1684ab9b2e15Sgtb 	/* Any body else is SOL unless we can renew their credential cache */
1685ab9b2e15Sgtb 	} else if ((cred->tgt_expire < now + MIN_RENEW_TIME) &&
1686ab9b2e15Sgtb 			(cred->tgt_expire > now)) {
1687ab9b2e15Sgtb 		(void) krb5_gss_release_cred(&mntmp, cred_handle);
1688ab9b2e15Sgtb 
1689ab9b2e15Sgtb 		major = renew_ccache(minor_status, context, uid);
1690ab9b2e15Sgtb 		if ((major != GSS_S_COMPLETE) &&
1691ab9b2e15Sgtb 			(major != KDC_ERR_BADOPTION))
1692ab9b2e15Sgtb 			return (major);
1693ab9b2e15Sgtb 
1694ab9b2e15Sgtb 		major = kg_get_defcred(minor_status, cred_handle);
1695ab9b2e15Sgtb 		if (major != GSS_S_COMPLETE)
1696ab9b2e15Sgtb 			return (major);
16977c478bd9Sstevel@tonic-gate 
1698ab9b2e15Sgtb 	}
16997c478bd9Sstevel@tonic-gate 
1700ab9b2e15Sgtb 	/* Otherwise we got non expired creds */
17017c478bd9Sstevel@tonic-gate 
1702ab9b2e15Sgtb 	KRB5_LOG0(KRB5_INFO, "get_default_cred() end\n");
17037c478bd9Sstevel@tonic-gate 
1704ab9b2e15Sgtb 	return (GSS_S_COMPLETE);
17057c478bd9Sstevel@tonic-gate }
1706ab9b2e15Sgtb 
1707ab9b2e15Sgtb /* Solaris Kerberos specific routines end */
1708