1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 
7 /*
8  * Copyright 1993 by OpenVision Technologies, Inc.
9  *
10  * Permission to use, copy, modify, distribute, and sell this software
11  * and its documentation for any purpose is hereby granted without fee,
12  * provided that the above copyright notice appears in all copies and
13  * that both that copyright notice and this permission notice appear in
14  * supporting documentation, and that the name of OpenVision not be used
15  * in advertising or publicity pertaining to distribution of the software
16  * without specific, written prior permission. OpenVision makes no
17  * representations about the suitability of this software for any
18  * purpose.  It is provided "as is" without express or implied warranty.
19  *
20  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
21  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
22  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
23  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
24  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
25  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
26  * PERFORMANCE OF THIS SOFTWARE.
27  */
28 
29 /*
30  * Copyright (C) 1998 by the FundsXpress, INC.
31  *
32  * All rights reserved.
33  *
34  * Export of this software from the United States of America may require
35  * a specific license from the United States Government.  It is the
36  * responsibility of any person or organization contemplating export to
37  * obtain such a license before exporting.
38  *
39  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
40  * distribute this software and its documentation for any purpose and
41  * without fee is hereby granted, provided that the above copyright
42  * notice appear in all copies and that both that copyright notice and
43  * this permission notice appear in supporting documentation, and that
44  * the name of FundsXpress. not be used in advertising or publicity pertaining
45  * to distribution of the software without specific, written prior
46  * permission.  FundsXpress makes no representations about the suitability of
47  * this software for any purpose.  It is provided "as is" without express
48  * or implied warranty.
49  *
50  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
51  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
52  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
53  */
54 
55 /*
56  * $Id: gssapi_krb5.c 18343 2006-07-19 18:14:01Z lxs $
57  */
58 
59 
60 /* For declaration of krb5_ser_context_init */
61 #include "k5-int.h"
62 #include "gssapiP_krb5.h"
63 
64 /*
65  * Solaris Kerberos
66  * Kernel kgssd module debugging aid. The global variable "krb5_log" is a bit
67  * mask which allows various types of log messages to be printed out.
68  *
69  * The log levels are defined in:
70  * usr/src/uts/common/gssapi/mechs/krb5/include/k5-int.h
71  *
72  * Note, KRB5_LOG_LVL can be assigned via the make invocation.
73  * See KRB5_DEFS in the various Makefiles.
74  */
75 
76 #ifdef KRB5_LOG_LVL
77 /* set the log level to that specified */
78 u_int krb5_log = KRB5_LOG_LVL;
79 #else
80 /* default log level */
81 u_int krb5_log = 0;
82 #endif /* KRB5_LOG_LVL */
83 
84 /** exported constants defined in gssapi_krb5{,_nx}.h **/
85 
86 /* these are bogus, but will compile */
87 
88 /*
89  * The OID of the draft krb5 mechanism, assigned by IETF, is:
90  * 	iso(1) org(3) dod(5) internet(1) security(5)
91  *	kerberosv5(2) = 1.3.5.1.5.2
92  * The OID of the krb5_name type is:
93  * 	iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
94  * 	krb5(2) krb5_name(1) = 1.2.840.113554.1.2.2.1
95  * The OID of the krb5_principal type is:
96  * 	iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
97  * 	krb5(2) krb5_principal(2) = 1.2.840.113554.1.2.2.2
98  * The OID of the proposed standard krb5 mechanism is:
99  * 	iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
100  * 	krb5(2) = 1.2.840.113554.1.2.2
101  * The OID of the proposed standard krb5 v2 mechanism is:
102  * 	iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
103  * 	krb5v2(3) = 1.2.840.113554.1.2.3
104  *
105  */
106 
107 /*
108  * Encoding rules: The first two values are encoded in one byte as 40
109  * * value1 + value2.  Subsequent values are encoded base 128, most
110  * significant digit first, with the high bit (\200) set on all octets
111  * except the last in each value's encoding.
112  */
113 
114 const gss_OID_desc krb5_gss_oid_array[] = {
115    /* this is the official, rfc-specified OID */
116    {GSS_MECH_KRB5_OID_LENGTH, GSS_MECH_KRB5_OID},
117    /* this pre-RFC mech OID */
118    {GSS_MECH_KRB5_OLD_OID_LENGTH, GSS_MECH_KRB5_OLD_OID},
119    /* this is the unofficial, incorrect mech OID emitted by MS */
120    {GSS_MECH_KRB5_WRONG_OID_LENGTH, GSS_MECH_KRB5_WRONG_OID},
121    /* this is the v2 assigned OID */
122    {9, "\052\206\110\206\367\022\001\002\003"},
123    /* these two are name type OID's */
124 
125     /* 2.1.1. Kerberos Principal Name Form:  (rfc 1964)
126      * This name form shall be represented by the Object Identifier {iso(1)
127      * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
128      * krb5(2) krb5_name(1)}.  The recommended symbolic name for this type
129      * is "GSS_KRB5_NT_PRINCIPAL_NAME". */
130    {10, "\052\206\110\206\367\022\001\002\002\001"},
131 
132    /* gss_nt_krb5_principal.  Object identifier for a krb5_principal. Do not use. */
133    {10, "\052\206\110\206\367\022\001\002\002\002"},
134    { 0, 0 }
135 };
136 
137 const gss_OID_desc * const gss_mech_krb5              = krb5_gss_oid_array+0;
138 const gss_OID_desc * const gss_mech_krb5_old          = krb5_gss_oid_array+1;
139 const gss_OID_desc * const gss_mech_krb5_wrong        = krb5_gss_oid_array+2;
140 const gss_OID_desc * const gss_nt_krb5_name           = krb5_gss_oid_array+4;
141 const gss_OID_desc * const gss_nt_krb5_principal      = krb5_gss_oid_array+5;
142 const gss_OID_desc * const GSS_KRB5_NT_PRINCIPAL_NAME = krb5_gss_oid_array+4;
143 
144 static const gss_OID_set_desc oidsets[] = {
145    {1, (gss_OID) krb5_gss_oid_array+0},
146    {1, (gss_OID) krb5_gss_oid_array+1},
147    {3, (gss_OID) krb5_gss_oid_array+0},
148    {1, (gss_OID) krb5_gss_oid_array+2},
149    {3, (gss_OID) krb5_gss_oid_array+0},
150 };
151 
152 const gss_OID_set_desc * const gss_mech_set_krb5 = oidsets+0;
153 const gss_OID_set_desc * const gss_mech_set_krb5_old = oidsets+1;
154 const gss_OID_set_desc * const gss_mech_set_krb5_both = oidsets+2;
155 
156 g_set kg_vdb = G_SET_INIT;
157 
158 /** default credential support */
159 
160 #ifndef  _KERNEL
161 
162 /*
163  * init_sec_context() will explicitly re-acquire default credentials,
164  * so handling the expiration/invalidation condition here isn't needed.
165  */
166 OM_uint32
167 kg_get_defcred(minor_status, cred)
168      OM_uint32 *minor_status;
169      gss_cred_id_t *cred;
170 {
171     OM_uint32 major;
172 
173     if ((major = krb5_gss_acquire_cred(minor_status,
174 				      (gss_name_t) NULL, GSS_C_INDEFINITE,
175 				      GSS_C_NULL_OID_SET, GSS_C_INITIATE,
176 				      cred, NULL, NULL)) && GSS_ERROR(major)) {
177       return(major);
178    }
179    *minor_status = 0;
180    return(GSS_S_COMPLETE);
181 }
182 
183 OM_uint32
184 kg_sync_ccache_name (krb5_context context, OM_uint32 *minor_status)
185 {
186     OM_uint32 err = 0;
187 
188     /*
189      * Sync up the context ccache name with the GSSAPI ccache name.
190      * If kg_ccache_name is NULL -- normal unless someone has called
191      * gss_krb5_ccache_name() -- then the system default ccache will
192      * be picked up and used by resetting the context default ccache.
193      * This is needed for platforms which support multiple ccaches.
194      */
195 
196     if (!err) {
197         /* if NULL, resets the context default ccache */
198         err = krb5_cc_set_default_name(context,
199 				       (char *) k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME));
200     }
201 
202     *minor_status = err;
203     return (*minor_status == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
204 }
205 
206 /* This function returns whether or not the caller set a cccache name.  Used by
207  * gss_acquire_cred to figure out if the caller wants to only look at this
208  * ccache or search the cache collection for the desired name */
209 OM_uint32
210 kg_caller_provided_ccache_name (OM_uint32 *minor_status,
211 int *out_caller_provided_name)
212 {
213     if (out_caller_provided_name) {
214         *out_caller_provided_name =
215 	  (k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME) != NULL);
216     }
217 
218     *minor_status = 0;
219     return GSS_S_COMPLETE;
220 }
221 
222 OM_uint32
223 kg_get_ccache_name (OM_uint32 *minor_status, const char **out_name)
224 {
225     const char *name = NULL;
226     OM_uint32 err = 0;
227     char *kg_ccache_name;
228 
229     kg_ccache_name = k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME);
230 
231     if (kg_ccache_name != NULL) {
232 	name = strdup(kg_ccache_name);
233 	if (name == NULL)
234 	    err = errno;
235     } else {
236 	krb5_context context = NULL;
237 
238 	/* Reset the context default ccache (see text above), and then
239 	   retrieve it.  */
240 	err = krb5_gss_init_context(&context);
241 	if (!err)
242 	    err = krb5_cc_set_default_name (context, NULL);
243 	if (!err) {
244 	    name = krb5_cc_default_name(context);
245 	    if (name) {
246 		name = strdup(name);
247 		if (name == NULL)
248 		    err = errno;
249 	    }
250 	}
251 	if (context)
252 	    krb5_free_context(context);
253     }
254 
255     if (!err) {
256         if (out_name) {
257             *out_name = name;
258         }
259     }
260 
261     *minor_status = err;
262     return (*minor_status == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
263 }
264 
265 OM_uint32
266 kg_set_ccache_name (OM_uint32 *minor_status, const char *name)
267 {
268     char *new_name = NULL;
269     char *swap = NULL;
270     char *kg_ccache_name;
271     krb5_error_code kerr;
272 
273     if (name) {
274 	new_name = malloc(strlen(name) + 1);
275 	if (new_name == NULL) {
276 	    *minor_status = ENOMEM;
277 	    return GSS_S_FAILURE;
278 	}
279 	strcpy(new_name, name);
280     }
281 
282     kg_ccache_name = k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME);
283     swap = kg_ccache_name;
284     kg_ccache_name = new_name;
285     new_name = swap;
286     kerr = k5_setspecific(K5_KEY_GSS_KRB5_CCACHE_NAME, kg_ccache_name);
287     if (kerr != 0) {
288 	/* Can't store, so free up the storage.  */
289 	free(kg_ccache_name);
290 	/* ??? free(new_name); */
291 	*minor_status = kerr;
292 	return GSS_S_FAILURE;
293     }
294 
295     free (new_name);
296     *minor_status = 0;
297     return GSS_S_COMPLETE;
298 }
299 
300 #define g_OID_prefix_equal(o1, o2) \
301         (((o1)->length >= (o2)->length) && \
302         (memcmp((o1)->elements, (o2)->elements, (o2)->length) == 0))
303 
304 /*
305  * gss_inquire_sec_context_by_oid() methods
306  */
307 static struct {
308     gss_OID_desc oid;
309     OM_uint32 (*func)(OM_uint32 *, const gss_ctx_id_t, const gss_OID, gss_buffer_set_t *);
310 } krb5_gss_inquire_sec_context_by_oid_ops[] = {
311     {
312         {GSS_KRB5_GET_TKT_FLAGS_OID_LENGTH, GSS_KRB5_GET_TKT_FLAGS_OID},
313         gss_krb5int_get_tkt_flags
314     },
315     {
316         {GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID},
317         gss_krb5int_extract_authz_data_from_sec_context
318     },
319     {
320         {GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH, GSS_KRB5_INQ_SSPI_SESSION_KEY_OID},
321         gss_krb5int_inq_session_key
322     },
323     {
324         {GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID},
325         gss_krb5int_export_lucid_sec_context
326     },
327     {
328         {GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID},
329         gss_krb5int_extract_authtime_from_sec_context
330     }
331 };
332 
333 OM_uint32
334 krb5_gss_inquire_sec_context_by_oid (OM_uint32 *minor_status,
335                                      const gss_ctx_id_t context_handle,
336                                      const gss_OID desired_object,
337                                      gss_buffer_set_t *data_set)
338 {
339     krb5_gss_ctx_id_rec *ctx;
340     size_t i;
341 
342     if (minor_status == NULL)
343         return GSS_S_CALL_INACCESSIBLE_WRITE;
344 
345     *minor_status = 0;
346 
347     if (desired_object == GSS_C_NO_OID)
348         return GSS_S_CALL_INACCESSIBLE_READ;
349 
350     if (data_set == NULL)
351         return GSS_S_CALL_INACCESSIBLE_WRITE;
352 
353     *data_set = GSS_C_NO_BUFFER_SET;
354 
355     if (!kg_validate_ctx_id(context_handle))
356         return GSS_S_NO_CONTEXT;
357 
358     ctx = (krb5_gss_ctx_id_rec *) context_handle;
359 
360     if (!ctx->established)
361         return GSS_S_NO_CONTEXT;
362 
363     for (i = 0; i < sizeof(krb5_gss_inquire_sec_context_by_oid_ops)/
364                     sizeof(krb5_gss_inquire_sec_context_by_oid_ops[0]); i++) {
365         if (g_OID_prefix_equal(desired_object, &krb5_gss_inquire_sec_context_by_oid_ops[i].oid)) {
366             return (*krb5_gss_inquire_sec_context_by_oid_ops[i].func)(minor_status,
367                                                                       context_handle,
368                                                                       desired_object,
369                                                                       data_set);
370         }
371     }
372 
373     *minor_status = EINVAL;
374 
375     return GSS_S_UNAVAILABLE;
376 }
377 
378 #endif
379