Use gssalloc memory management where appropriate
[krb5.git] / src / lib / gssapi / krb5 / gssapi_krb5.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright 1993 by OpenVision Technologies, Inc.
4  *
5  * Permission to use, copy, modify, distribute, and sell this software
6  * and its documentation for any purpose is hereby granted without fee,
7  * provided that the above copyright notice appears in all copies and
8  * that both that copyright notice and this permission notice appear in
9  * supporting documentation, and that the name of OpenVision not be used
10  * in advertising or publicity pertaining to distribution of the software
11  * without specific, written prior permission. OpenVision makes no
12  * representations about the suitability of this software for any
13  * purpose.  It is provided "as is" without express or implied warranty.
14  *
15  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
19  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
20  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21  * PERFORMANCE OF THIS SOFTWARE.
22  */
23
24 /*
25  * Copyright (C) 1998 by the FundsXpress, INC.
26  *
27  * All rights reserved.
28  *
29  * Export of this software from the United States of America may require
30  * a specific license from the United States Government.  It is the
31  * responsibility of any person or organization contemplating export to
32  * obtain such a license before exporting.
33  *
34  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
35  * distribute this software and its documentation for any purpose and
36  * without fee is hereby granted, provided that the above copyright
37  * notice appear in all copies and that both that copyright notice and
38  * this permission notice appear in supporting documentation, and that
39  * the name of FundsXpress. not be used in advertising or publicity pertaining
40  * to distribution of the software without specific, written prior
41  * permission.  FundsXpress makes no representations about the suitability of
42  * this software for any purpose.  It is provided "as is" without express
43  * or implied warranty.
44  *
45  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
46  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
47  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
48  */
49 /*
50  * Copyright (c) 2006-2008, Novell, Inc.
51  * All rights reserved.
52  *
53  * Redistribution and use in source and binary forms, with or without
54  * modification, are permitted provided that the following conditions are met:
55  *
56  *   * Redistributions of source code must retain the above copyright notice,
57  *       this list of conditions and the following disclaimer.
58  *   * Redistributions in binary form must reproduce the above copyright
59  *       notice, this list of conditions and the following disclaimer in the
60  *       documentation and/or other materials provided with the distribution.
61  *   * The copyright holder's name is not used to endorse or promote products
62  *       derived from this software without specific prior written permission.
63  *
64  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
65  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
66  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
67  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
68  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
69  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
70  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
71  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
72  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
73  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
74  * POSSIBILITY OF SUCH DAMAGE.
75  */
76
77 /*
78  * $Id$
79  */
80
81
82 /* For declaration of krb5_ser_context_init */
83 #include "k5-int.h"
84 #include "gssapiP_krb5.h"
85 #include "mglueP.h"
86
87 #ifndef NO_PASSWORD
88 #include <pwd.h>
89 #endif
90
91 /** exported constants defined in gssapi_krb5{,_nx}.h **/
92
93 /* these are bogus, but will compile */
94
95 /*
96  * The OID of the draft krb5 mechanism, assigned by IETF, is:
97  *      iso(1) org(3) dod(5) internet(1) security(5)
98  *      kerberosv5(2) = 1.3.5.1.5.2
99  * The OID of the krb5_name type is:
100  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
101  *      krb5(2) krb5_name(1) = 1.2.840.113554.1.2.2.1
102  * The OID of the krb5_principal type is:
103  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
104  *      krb5(2) krb5_principal(2) = 1.2.840.113554.1.2.2.2
105  * The OID of the proposed standard krb5 mechanism is:
106  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
107  *      krb5(2) = 1.2.840.113554.1.2.2
108  * The OID of the proposed standard krb5 v2 mechanism is:
109  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
110  *      krb5v2(3) = 1.2.840.113554.1.2.3
111  * Provisionally reserved for Kerberos session key algorithm
112  * identifiers is:
113  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
114  *      krb5(2) krb5_enctype(4) = 1.2.840.113554.1.2.2.4
115  * Provisionally reserved for Kerberos mechanism-specific APIs:
116  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
117  *      krb5(2) krb5_gssapi_ext(5) = 1.2.840.113554.1.2.2.5
118  */
119
120 /*
121  * Encoding rules: The first two values are encoded in one byte as 40
122  * * value1 + value2.  Subsequent values are encoded base 128, most
123  * significant digit first, with the high bit (\200) set on all octets
124  * except the last in each value's encoding.
125  */
126
127 const gss_OID_desc krb5_gss_oid_array[] = {
128     /* this is the official, rfc-specified OID */
129     {GSS_MECH_KRB5_OID_LENGTH, GSS_MECH_KRB5_OID},
130     /* this pre-RFC mech OID */
131     {GSS_MECH_KRB5_OLD_OID_LENGTH, GSS_MECH_KRB5_OLD_OID},
132     /* this is the unofficial, incorrect mech OID emitted by MS */
133     {GSS_MECH_KRB5_WRONG_OID_LENGTH, GSS_MECH_KRB5_WRONG_OID},
134     /* IAKERB OID */
135     {GSS_MECH_IAKERB_OID_LENGTH, GSS_MECH_IAKERB_OID},
136     /* this is the v2 assigned OID */
137     {9, "\052\206\110\206\367\022\001\002\003"},
138     /* these two are name type OID's */
139     /* 2.1.1. Kerberos Principal Name Form:  (rfc 1964)
140      * This name form shall be represented by the Object Identifier {iso(1)
141      * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
142      * krb5(2) krb5_name(1)}.  The recommended symbolic name for this type
143      * is "GSS_KRB5_NT_PRINCIPAL_NAME". */
144     {10, "\052\206\110\206\367\022\001\002\002\001"},
145     /* gss_nt_krb5_principal.  Object identifier for a krb5_principal. Do not use. */
146     {10, "\052\206\110\206\367\022\001\002\002\002"},
147     { 0, 0 }
148 };
149
150 const gss_OID_desc * const gss_mech_krb5              = krb5_gss_oid_array+0;
151 const gss_OID_desc * const gss_mech_krb5_old          = krb5_gss_oid_array+1;
152 const gss_OID_desc * const gss_mech_krb5_wrong        = krb5_gss_oid_array+2;
153 const gss_OID_desc * const gss_mech_iakerb            = krb5_gss_oid_array+3;
154
155
156 const gss_OID_desc * const gss_nt_krb5_name           = krb5_gss_oid_array+5;
157 const gss_OID_desc * const gss_nt_krb5_principal      = krb5_gss_oid_array+6;
158 const gss_OID_desc * const GSS_KRB5_NT_PRINCIPAL_NAME = krb5_gss_oid_array+5;
159
160 static const gss_OID_set_desc oidsets[] = {
161     {1, (gss_OID) krb5_gss_oid_array+0}, /* RFC OID */
162     {1, (gss_OID) krb5_gss_oid_array+1}, /* pre-RFC OID */
163     {4, (gss_OID) krb5_gss_oid_array+0}, /* includes wrong OID & IAKERB */
164     {1, (gss_OID) krb5_gss_oid_array+2},
165     {3, (gss_OID) krb5_gss_oid_array+0},
166 };
167
168 const gss_OID_set_desc * const gss_mech_set_krb5 = oidsets+0;
169 const gss_OID_set_desc * const gss_mech_set_krb5_old = oidsets+1;
170 const gss_OID_set_desc * const gss_mech_set_krb5_both = oidsets+2;
171
172 g_set kg_vdb = G_SET_INIT;
173
174 /** default credential support */
175
176 /*
177  * init_sec_context() will explicitly re-acquire default credentials,
178  * so handling the expiration/invalidation condition here isn't needed.
179  */
180 OM_uint32
181 kg_get_defcred(minor_status, cred)
182     OM_uint32 *minor_status;
183     gss_cred_id_t *cred;
184 {
185     OM_uint32 major;
186
187     if ((major = krb5_gss_acquire_cred(minor_status,
188                                        (gss_name_t) NULL, GSS_C_INDEFINITE,
189                                        GSS_C_NULL_OID_SET, GSS_C_INITIATE,
190                                        cred, NULL, NULL)) && GSS_ERROR(major)) {
191         return(major);
192     }
193     *minor_status = 0;
194     return(GSS_S_COMPLETE);
195 }
196
197 OM_uint32
198 kg_sync_ccache_name (krb5_context context, OM_uint32 *minor_status)
199 {
200     OM_uint32 err = 0;
201
202     /*
203      * Sync up the context ccache name with the GSSAPI ccache name.
204      * If kg_ccache_name is NULL -- normal unless someone has called
205      * gss_krb5_ccache_name() -- then the system default ccache will
206      * be picked up and used by resetting the context default ccache.
207      * This is needed for platforms which support multiple ccaches.
208      */
209
210     if (!err) {
211         /* if NULL, resets the context default ccache */
212         err = krb5_cc_set_default_name(context,
213                                        (char *) k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME));
214     }
215
216     *minor_status = err;
217     return (*minor_status == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
218 }
219
220 /* This function returns whether or not the caller set a cccache name.  Used by
221  * gss_acquire_cred to figure out if the caller wants to only look at this
222  * ccache or search the cache collection for the desired name */
223 OM_uint32
224 kg_caller_provided_ccache_name (OM_uint32 *minor_status,
225                                 int *out_caller_provided_name)
226 {
227     if (out_caller_provided_name) {
228         *out_caller_provided_name =
229             (k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME) != NULL);
230     }
231
232     *minor_status = 0;
233     return GSS_S_COMPLETE;
234 }
235
236 OM_uint32
237 kg_get_ccache_name (OM_uint32 *minor_status, const char **out_name)
238 {
239     const char *name = NULL;
240     OM_uint32 err = 0;
241     char *kg_ccache_name;
242
243     kg_ccache_name = k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME);
244
245     if (kg_ccache_name != NULL) {
246         name = strdup(kg_ccache_name);
247         if (name == NULL)
248             err = ENOMEM;
249     } else {
250         krb5_context context = NULL;
251
252         /* Reset the context default ccache (see text above), and then
253            retrieve it.  */
254         err = krb5_gss_init_context(&context);
255         if (!err)
256             err = krb5_cc_set_default_name (context, NULL);
257         if (!err) {
258             name = krb5_cc_default_name(context);
259             if (name) {
260                 name = strdup(name);
261                 if (name == NULL)
262                     err = ENOMEM;
263             }
264         }
265         if (err && context)
266             save_error_info(err, context);
267         if (context)
268             krb5_free_context(context);
269     }
270
271     if (!err) {
272         if (out_name) {
273             *out_name = name;
274         }
275     }
276
277     *minor_status = err;
278     return (*minor_status == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
279 }
280
281 OM_uint32
282 kg_set_ccache_name (OM_uint32 *minor_status, const char *name)
283 {
284     char *new_name = NULL;
285     char *swap = NULL;
286     char *kg_ccache_name;
287     krb5_error_code kerr;
288
289     if (name) {
290         new_name = strdup(name);
291         if (new_name == NULL) {
292             *minor_status = ENOMEM;
293             return GSS_S_FAILURE;
294         }
295     }
296
297     kg_ccache_name = k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME);
298     swap = kg_ccache_name;
299     kg_ccache_name = new_name;
300     new_name = swap;
301     kerr = k5_setspecific(K5_KEY_GSS_KRB5_CCACHE_NAME, kg_ccache_name);
302     if (kerr != 0) {
303         /* Can't store, so free up the storage.  */
304         free(kg_ccache_name);
305         /* ??? free(new_name); */
306         *minor_status = kerr;
307         return GSS_S_FAILURE;
308     }
309
310     free (new_name);
311     *minor_status = 0;
312     return GSS_S_COMPLETE;
313 }
314
315 #define g_OID_prefix_equal(o1, o2)                                      \
316     (((o1)->length >= (o2)->length) &&                                  \
317      (memcmp((o1)->elements, (o2)->elements, (o2)->length) == 0))
318
319 /*
320  * gss_inquire_sec_context_by_oid() methods
321  */
322 static struct {
323     gss_OID_desc oid;
324     OM_uint32 (*func)(OM_uint32 *, const gss_ctx_id_t, const gss_OID, gss_buffer_set_t *);
325 } krb5_gss_inquire_sec_context_by_oid_ops[] = {
326     {
327         {GSS_KRB5_GET_TKT_FLAGS_OID_LENGTH, GSS_KRB5_GET_TKT_FLAGS_OID},
328         gss_krb5int_get_tkt_flags
329     },
330     {
331         {GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID},
332         gss_krb5int_extract_authz_data_from_sec_context
333     },
334     {
335         {GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH, GSS_KRB5_INQ_SSPI_SESSION_KEY_OID},
336         gss_krb5int_inq_session_key
337     },
338     {
339         {GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID},
340         gss_krb5int_export_lucid_sec_context
341     },
342     {
343         {GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID},
344         gss_krb5int_extract_authtime_from_sec_context
345     }
346 };
347
348 static OM_uint32 KRB5_CALLCONV
349 krb5_gss_inquire_sec_context_by_oid (OM_uint32 *minor_status,
350                                      const gss_ctx_id_t context_handle,
351                                      const gss_OID desired_object,
352                                      gss_buffer_set_t *data_set)
353 {
354     krb5_gss_ctx_id_rec *ctx;
355     size_t i;
356
357     if (minor_status == NULL)
358         return GSS_S_CALL_INACCESSIBLE_WRITE;
359
360     *minor_status = 0;
361
362     if (desired_object == GSS_C_NO_OID)
363         return GSS_S_CALL_INACCESSIBLE_READ;
364
365     if (data_set == NULL)
366         return GSS_S_CALL_INACCESSIBLE_WRITE;
367
368     *data_set = GSS_C_NO_BUFFER_SET;
369
370     ctx = (krb5_gss_ctx_id_rec *) context_handle;
371
372     if (!ctx->established)
373         return GSS_S_NO_CONTEXT;
374
375     for (i = 0; i < sizeof(krb5_gss_inquire_sec_context_by_oid_ops)/
376              sizeof(krb5_gss_inquire_sec_context_by_oid_ops[0]); i++) {
377         if (g_OID_prefix_equal(desired_object, &krb5_gss_inquire_sec_context_by_oid_ops[i].oid)) {
378             return (*krb5_gss_inquire_sec_context_by_oid_ops[i].func)(minor_status,
379                                                                       context_handle,
380                                                                       desired_object,
381                                                                       data_set);
382         }
383     }
384
385     *minor_status = EINVAL;
386
387     return GSS_S_UNAVAILABLE;
388 }
389
390 /*
391  * gss_inquire_cred_by_oid() methods
392  */
393 #if 0
394 static struct {
395     gss_OID_desc oid;
396     OM_uint32 (*func)(OM_uint32 *, const gss_cred_id_t, const gss_OID, gss_buffer_set_t *);
397 } krb5_gss_inquire_cred_by_oid_ops[] = {
398 };
399 #endif
400
401 static OM_uint32 KRB5_CALLCONV
402 krb5_gss_inquire_cred_by_oid(OM_uint32 *minor_status,
403                              const gss_cred_id_t cred_handle,
404                              const gss_OID desired_object,
405                              gss_buffer_set_t *data_set)
406 {
407     OM_uint32 major_status = GSS_S_FAILURE;
408     krb5_gss_cred_id_t cred;
409 #if 0
410     size_t i;
411 #endif
412
413     if (minor_status == NULL)
414         return GSS_S_CALL_INACCESSIBLE_WRITE;
415
416     *minor_status = 0;
417
418     if (desired_object == GSS_C_NO_OID)
419         return GSS_S_CALL_INACCESSIBLE_READ;
420
421     if (data_set == NULL)
422         return GSS_S_CALL_INACCESSIBLE_WRITE;
423
424     *data_set = GSS_C_NO_BUFFER_SET;
425     if (cred_handle == GSS_C_NO_CREDENTIAL) {
426         *minor_status = (OM_uint32)KRB5_NOCREDS_SUPPLIED;
427         return GSS_S_NO_CRED;
428     }
429
430     major_status = krb5_gss_validate_cred(minor_status, cred_handle);
431     if (GSS_ERROR(major_status))
432         return major_status;
433
434     cred = (krb5_gss_cred_id_t) cred_handle;
435
436 #if 0
437     for (i = 0; i < sizeof(krb5_gss_inquire_cred_by_oid_ops)/
438              sizeof(krb5_gss_inquire_cred_by_oid_ops[0]); i++) {
439         if (g_OID_prefix_equal(desired_object, &krb5_gss_inquire_cred_by_oid_ops[i].oid)) {
440             return (*krb5_gss_inquire_cred_by_oid_ops[i].func)(minor_status,
441                                                                cred_handle,
442                                                                desired_object,
443                                                                data_set);
444         }
445     }
446 #endif
447
448     *minor_status = EINVAL;
449
450     return GSS_S_UNAVAILABLE;
451 }
452
453 /*
454  * gss_set_sec_context_option() methods
455  * (Disabled until we have something to populate the array.)
456  */
457 #if 0
458 static struct {
459     gss_OID_desc oid;
460     OM_uint32 (*func)(OM_uint32 *, gss_ctx_id_t *, const gss_OID, const gss_buffer_t);
461 } krb5_gss_set_sec_context_option_ops[] = {
462 };
463 #endif
464
465 static OM_uint32 KRB5_CALLCONV
466 krb5_gss_set_sec_context_option (OM_uint32 *minor_status,
467                                  gss_ctx_id_t *context_handle,
468                                  const gss_OID desired_object,
469                                  const gss_buffer_t value)
470 {
471 #if 0
472     size_t i;
473 #endif
474
475     if (minor_status == NULL)
476         return GSS_S_CALL_INACCESSIBLE_WRITE;
477
478     *minor_status = 0;
479
480     if (context_handle == NULL)
481         return GSS_S_CALL_INACCESSIBLE_READ;
482
483     if (desired_object == GSS_C_NO_OID)
484         return GSS_S_CALL_INACCESSIBLE_READ;
485
486 #if 0
487     for (i = 0; i < sizeof(krb5_gss_set_sec_context_option_ops)/
488              sizeof(krb5_gss_set_sec_context_option_ops[0]); i++) {
489         if (g_OID_prefix_equal(desired_object, &krb5_gss_set_sec_context_option_ops[i].oid)) {
490             return (*krb5_gss_set_sec_context_option_ops[i].func)(minor_status,
491                                                                   context_handle,
492                                                                   desired_object,
493                                                                   value);
494         }
495     }
496 #endif
497
498     *minor_status = EINVAL;
499
500     return GSS_S_UNAVAILABLE;
501 }
502
503 /*
504  * gssspi_set_cred_option() methods
505  */
506 static struct {
507     gss_OID_desc oid;
508     OM_uint32 (*func)(OM_uint32 *, gss_cred_id_t *, const gss_OID, const gss_buffer_t);
509 } krb5_gssspi_set_cred_option_ops[] = {
510     {
511         {GSS_KRB5_COPY_CCACHE_OID_LENGTH, GSS_KRB5_COPY_CCACHE_OID},
512         gss_krb5int_copy_ccache
513     },
514     {
515         {GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID_LENGTH, GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID},
516         gss_krb5int_set_allowable_enctypes
517     },
518     {
519         {GSS_KRB5_SET_CRED_RCACHE_OID_LENGTH, GSS_KRB5_SET_CRED_RCACHE_OID},
520         gss_krb5int_set_cred_rcache
521     },
522     {
523         {GSS_KRB5_IMPORT_CRED_OID_LENGTH, GSS_KRB5_IMPORT_CRED_OID},
524         gss_krb5int_import_cred
525     },
526 };
527
528 static OM_uint32 KRB5_CALLCONV
529 krb5_gssspi_set_cred_option(OM_uint32 *minor_status,
530                             gss_cred_id_t *cred_handle,
531                             const gss_OID desired_object,
532                             const gss_buffer_t value)
533 {
534     OM_uint32 major_status = GSS_S_FAILURE;
535     size_t i;
536
537     if (minor_status == NULL)
538         return GSS_S_CALL_INACCESSIBLE_WRITE;
539
540     if (cred_handle == NULL)
541         return GSS_S_CALL_INACCESSIBLE_WRITE;
542
543     *minor_status = 0;
544
545     if (desired_object == GSS_C_NO_OID)
546         return GSS_S_CALL_INACCESSIBLE_READ;
547
548     if (*cred_handle != GSS_C_NO_CREDENTIAL) {
549         major_status = krb5_gss_validate_cred(minor_status, *cred_handle);
550         if (GSS_ERROR(major_status))
551             return major_status;
552     }
553
554     for (i = 0; i < sizeof(krb5_gssspi_set_cred_option_ops)/
555              sizeof(krb5_gssspi_set_cred_option_ops[0]); i++) {
556         if (g_OID_prefix_equal(desired_object, &krb5_gssspi_set_cred_option_ops[i].oid)) {
557             return (*krb5_gssspi_set_cred_option_ops[i].func)(minor_status,
558                                                               cred_handle,
559                                                               desired_object,
560                                                               value);
561         }
562     }
563
564     *minor_status = EINVAL;
565
566     return GSS_S_UNAVAILABLE;
567 }
568
569 /*
570  * gssspi_mech_invoke() methods
571  */
572 static struct {
573     gss_OID_desc oid;
574     OM_uint32 (*func)(OM_uint32 *, const gss_OID, const gss_OID, gss_buffer_t);
575 } krb5_gssspi_mech_invoke_ops[] = {
576     {
577         {GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_OID_LENGTH, GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_OID},
578         gss_krb5int_register_acceptor_identity
579     },
580     {
581         {GSS_KRB5_CCACHE_NAME_OID_LENGTH, GSS_KRB5_CCACHE_NAME_OID},
582         gss_krb5int_ccache_name
583     },
584     {
585         {GSS_KRB5_FREE_LUCID_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_FREE_LUCID_SEC_CONTEXT_OID},
586         gss_krb5int_free_lucid_sec_context
587     },
588 #ifndef _WIN32
589     {
590         {GSS_KRB5_USE_KDC_CONTEXT_OID_LENGTH, GSS_KRB5_USE_KDC_CONTEXT_OID},
591         krb5int_gss_use_kdc_context
592     },
593 #endif
594 };
595
596 static OM_uint32 KRB5_CALLCONV
597 krb5_gssspi_mech_invoke (OM_uint32 *minor_status,
598                          const gss_OID desired_mech,
599                          const gss_OID desired_object,
600                          gss_buffer_t value)
601 {
602     size_t i;
603
604     if (minor_status == NULL)
605         return GSS_S_CALL_INACCESSIBLE_WRITE;
606
607     *minor_status = 0;
608
609     if (desired_mech == GSS_C_NO_OID)
610         return GSS_S_BAD_MECH;
611
612     if (desired_object == GSS_C_NO_OID)
613         return GSS_S_CALL_INACCESSIBLE_READ;
614
615     for (i = 0; i < sizeof(krb5_gssspi_mech_invoke_ops)/
616              sizeof(krb5_gssspi_mech_invoke_ops[0]); i++) {
617         if (g_OID_prefix_equal(desired_object, &krb5_gssspi_mech_invoke_ops[i].oid)) {
618             return (*krb5_gssspi_mech_invoke_ops[i].func)(minor_status,
619                                                           desired_mech,
620                                                           desired_object,
621                                                           value);
622         }
623     }
624
625     *minor_status = EINVAL;
626
627     return GSS_S_UNAVAILABLE;
628 }
629
630 #define GS2_KRB5_SASL_NAME        "GS2-KRB5"
631 #define GS2_KRB5_SASL_NAME_LEN    (sizeof(GS2_KRB5_SASL_NAME) - 1)
632
633 #define GS2_IAKERB_SASL_NAME      "GS2-IAKERB"
634 #define GS2_IAKERB_SASL_NAME_LEN  (sizeof(GS2_IAKERB_SASL_NAME) - 1)
635
636 static OM_uint32 KRB5_CALLCONV
637 krb5_gss_inquire_mech_for_saslname(OM_uint32 *minor_status,
638                                    const gss_buffer_t sasl_mech_name,
639                                    gss_OID *mech_type)
640 {
641     *minor_status = 0;
642
643     if (sasl_mech_name->length == GS2_KRB5_SASL_NAME_LEN &&
644         memcmp(sasl_mech_name->value,
645                GS2_KRB5_SASL_NAME, GS2_KRB5_SASL_NAME_LEN) == 0) {
646         if (mech_type != NULL)
647             *mech_type = (gss_OID)gss_mech_krb5;
648         return GSS_S_COMPLETE;
649     } else if (sasl_mech_name->length == GS2_IAKERB_SASL_NAME_LEN &&
650                memcmp(sasl_mech_name->value,
651                       GS2_IAKERB_SASL_NAME, GS2_IAKERB_SASL_NAME_LEN) == 0) {
652         if (mech_type != NULL)
653             *mech_type = (gss_OID)gss_mech_iakerb;
654         return GSS_S_COMPLETE;
655     }
656
657     return GSS_S_BAD_MECH;
658 }
659
660 static OM_uint32 KRB5_CALLCONV
661 krb5_gss_inquire_saslname_for_mech(OM_uint32 *minor_status,
662                                    const gss_OID desired_mech,
663                                    gss_buffer_t sasl_mech_name,
664                                    gss_buffer_t mech_name,
665                                    gss_buffer_t mech_description)
666 {
667     if (g_OID_equal(desired_mech, gss_mech_iakerb)) {
668         if (!g_make_string_buffer(GS2_IAKERB_SASL_NAME, sasl_mech_name) ||
669             !g_make_string_buffer("iakerb", mech_name) ||
670             !g_make_string_buffer("Initial and Pass Through Authentication "
671                                   "Kerberos Mechanism (IAKERB)",
672                                   mech_description))
673             goto fail;
674     } else {
675         if (!g_make_string_buffer(GS2_KRB5_SASL_NAME, sasl_mech_name) ||
676             !g_make_string_buffer("krb5", mech_name) ||
677             !g_make_string_buffer("Kerberos 5 GSS-API Mechanism",
678                                   mech_description))
679             goto fail;
680     }
681
682     *minor_status = 0;
683     return GSS_S_COMPLETE;
684
685 fail:
686     *minor_status = ENOMEM;
687     return GSS_S_FAILURE;
688 }
689
690 static OM_uint32 KRB5_CALLCONV
691 krb5_gss_inquire_attrs_for_mech(OM_uint32 *minor_status,
692                                 gss_const_OID mech,
693                                 gss_OID_set *mech_attrs,
694                                 gss_OID_set *known_mech_attrs)
695 {
696     OM_uint32 major, tmpMinor;
697
698     if (mech_attrs == NULL) {
699         *minor_status = 0;
700         return GSS_S_COMPLETE;
701     }
702
703     major = gss_create_empty_oid_set(minor_status, mech_attrs);
704     if (GSS_ERROR(major))
705         goto cleanup;
706
707 #define MA_SUPPORTED(ma)    do {                                        \
708         major = gss_add_oid_set_member(minor_status, (gss_OID)ma,       \
709                                        mech_attrs);                     \
710         if (GSS_ERROR(major))                                           \
711             goto cleanup;                                               \
712     } while (0)
713
714     MA_SUPPORTED(GSS_C_MA_MECH_CONCRETE);
715     MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED);
716     MA_SUPPORTED(GSS_C_MA_AUTH_INIT);
717     MA_SUPPORTED(GSS_C_MA_AUTH_TARG);
718     MA_SUPPORTED(GSS_C_MA_DELEG_CRED);
719     MA_SUPPORTED(GSS_C_MA_INTEG_PROT);
720     MA_SUPPORTED(GSS_C_MA_CONF_PROT);
721     MA_SUPPORTED(GSS_C_MA_MIC);
722     MA_SUPPORTED(GSS_C_MA_WRAP);
723     MA_SUPPORTED(GSS_C_MA_PROT_READY);
724     MA_SUPPORTED(GSS_C_MA_REPLAY_DET);
725     MA_SUPPORTED(GSS_C_MA_OOS_DET);
726     MA_SUPPORTED(GSS_C_MA_CBINDINGS);
727     MA_SUPPORTED(GSS_C_MA_CTX_TRANS);
728
729     if (g_OID_equal(mech, gss_mech_iakerb)) {
730         MA_SUPPORTED(GSS_C_MA_AUTH_INIT_INIT);
731     } else if (!g_OID_equal(mech, gss_mech_krb5)) {
732         MA_SUPPORTED(GSS_C_MA_DEPRECATED);
733     }
734
735 cleanup:
736     if (GSS_ERROR(major))
737         gss_release_oid_set(&tmpMinor, mech_attrs);
738
739     return major;
740 }
741
742 static OM_uint32 KRB5_CALLCONV
743 krb5_gss_localname(OM_uint32 *minor,
744                    const gss_name_t pname,
745                    const gss_const_OID mech_type,
746                    gss_buffer_t localname)
747 {
748     krb5_context context;
749     krb5_error_code code;
750     krb5_gss_name_t kname;
751     char lname[BUFSIZ];
752
753     code = krb5_gss_init_context(&context);
754     if (code != 0) {
755         *minor = code;
756         return GSS_S_FAILURE;
757     }
758
759     kname = (krb5_gss_name_t)pname;
760
761     code = krb5_aname_to_localname(context, kname->princ,
762                                    sizeof(lname), lname);
763     if (code != 0) {
764         *minor = KRB5_NO_LOCALNAME;
765         krb5_free_context(context);
766         return GSS_S_FAILURE;
767     }
768
769
770     krb5_free_context(context);
771     localname->value = gssalloc_strdup(lname);
772     localname->length = strlen(lname);
773
774     return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
775 }
776
777
778 static OM_uint32 KRB5_CALLCONV
779 krb5_gss_authorize_localname(OM_uint32 *minor,
780                              const gss_name_t pname,
781                              gss_const_buffer_t local_user,
782                              gss_const_OID name_type)
783 {
784     krb5_context context;
785     krb5_error_code code;
786     krb5_gss_name_t kname;
787     char *user;
788     int user_ok;
789
790     if (name_type != GSS_C_NO_OID &&
791         !g_OID_equal(name_type, GSS_C_NT_USER_NAME)) {
792         return GSS_S_BAD_NAMETYPE;
793     }
794
795     kname = (krb5_gss_name_t)pname;
796
797     code = krb5_gss_init_context(&context);
798     if (code != 0) {
799         *minor = code;
800         return GSS_S_FAILURE;
801     }
802
803     user = k5alloc(local_user->length + 1, &code);
804     if (user == NULL) {
805         *minor = code;
806         krb5_free_context(context);
807         return GSS_S_FAILURE;
808     }
809
810     memcpy(user, local_user->value, local_user->length);
811     user[local_user->length] = '\0';
812
813     user_ok = krb5_kuserok(context, kname->princ, user);
814
815     free(user);
816     krb5_free_context(context);
817
818     *minor = 0;
819     return user_ok ? GSS_S_COMPLETE : GSS_S_UNAUTHORIZED;
820 }
821
822 static struct gss_config krb5_mechanism = {
823     { GSS_MECH_KRB5_OID_LENGTH, GSS_MECH_KRB5_OID },
824     NULL,
825     krb5_gss_acquire_cred,
826     krb5_gss_release_cred,
827     krb5_gss_init_sec_context,
828 #ifdef LEAN_CLIENT
829     NULL,
830 #else
831     krb5_gss_accept_sec_context,
832 #endif
833     krb5_gss_process_context_token,
834     krb5_gss_delete_sec_context,
835     krb5_gss_context_time,
836     krb5_gss_get_mic,
837     krb5_gss_verify_mic,
838 #if defined(IOV_SHIM_EXERCISE_WRAP) || defined(IOV_SHIM_EXERCISE)
839     NULL,
840 #else
841     krb5_gss_wrap,
842 #endif
843 #if defined(IOV_SHIM_EXERCISE_UNWRAP) || defined(IOV_SHIM_EXERCISE)
844     NULL,
845 #else
846     krb5_gss_unwrap,
847 #endif
848     krb5_gss_display_status,
849     krb5_gss_indicate_mechs,
850     krb5_gss_compare_name,
851     krb5_gss_display_name,
852     krb5_gss_import_name,
853     krb5_gss_release_name,
854     krb5_gss_inquire_cred,
855     NULL,                /* add_cred */
856 #ifdef LEAN_CLIENT
857     NULL,
858     NULL,
859 #else
860     krb5_gss_export_sec_context,
861     krb5_gss_import_sec_context,
862 #endif
863     krb5_gss_inquire_cred_by_mech,
864     krb5_gss_inquire_names_for_mech,
865     krb5_gss_inquire_context,
866     krb5_gss_internal_release_oid,
867     krb5_gss_wrap_size_limit,
868     krb5_gss_localname,
869
870     krb5_gss_authorize_localname,
871     krb5_gss_export_name,
872     krb5_gss_duplicate_name,
873     krb5_gss_store_cred,
874     krb5_gss_inquire_sec_context_by_oid,
875     krb5_gss_inquire_cred_by_oid,
876     krb5_gss_set_sec_context_option,
877     krb5_gssspi_set_cred_option,
878     krb5_gssspi_mech_invoke,
879     NULL,                /* wrap_aead */
880     NULL,                /* unwrap_aead */
881     krb5_gss_wrap_iov,
882     krb5_gss_unwrap_iov,
883     krb5_gss_wrap_iov_length,
884     NULL,               /* complete_auth_token */
885     krb5_gss_acquire_cred_impersonate_name,
886     NULL,               /* krb5_gss_add_cred_impersonate_name */
887     NULL,               /* display_name_ext */
888     krb5_gss_inquire_name,
889     krb5_gss_get_name_attribute,
890     krb5_gss_set_name_attribute,
891     krb5_gss_delete_name_attribute,
892     krb5_gss_export_name_composite,
893     krb5_gss_map_name_to_any,
894     krb5_gss_release_any_name_mapping,
895     krb5_gss_pseudo_random,
896     NULL,               /* set_neg_mechs */
897     krb5_gss_inquire_saslname_for_mech,
898     krb5_gss_inquire_mech_for_saslname,
899     krb5_gss_inquire_attrs_for_mech,
900 };
901
902 static struct gss_config_ext krb5_mechanism_ext = {
903     krb5_gss_acquire_cred_with_password,
904 };
905
906 static struct gss_config_ext iakerb_mechanism_ext = {
907     iakerb_gss_acquire_cred_with_password,
908 };
909
910 #ifdef _GSS_STATIC_LINK
911 #include "mglueP.h"
912 static int gss_iakerbmechglue_init(void)
913 {
914     struct gss_mech_config mech_iakerb;
915     struct gss_config iakerb_mechanism = krb5_mechanism;
916
917     /* IAKERB mechanism mirrors krb5, but with different context SPIs */
918     iakerb_mechanism.gss_accept_sec_context = iakerb_gss_accept_sec_context;
919     iakerb_mechanism.gss_init_sec_context   = iakerb_gss_init_sec_context;
920     iakerb_mechanism.gss_delete_sec_context = iakerb_gss_delete_sec_context;
921     iakerb_mechanism.gss_acquire_cred       = iakerb_gss_acquire_cred;
922
923     memset(&mech_iakerb, 0, sizeof(mech_iakerb));
924     mech_iakerb.mech = &iakerb_mechanism;
925     mech_iakerb.mech_ext = &iakerb_mechanism_ext;
926
927     mech_iakerb.mechNameStr = "iakerb";
928     mech_iakerb.mech_type = (gss_OID)gss_mech_iakerb;
929     gssint_register_mechinfo(&mech_iakerb);
930
931     return 0;
932 }
933
934 static int gss_krb5mechglue_init(void)
935 {
936     struct gss_mech_config mech_krb5;
937
938     memset(&mech_krb5, 0, sizeof(mech_krb5));
939     mech_krb5.mech = &krb5_mechanism;
940     mech_krb5.mech_ext = &krb5_mechanism_ext;
941
942     mech_krb5.mechNameStr = "kerberos_v5";
943     mech_krb5.mech_type = (gss_OID)gss_mech_krb5;
944     gssint_register_mechinfo(&mech_krb5);
945
946     mech_krb5.mechNameStr = "kerberos_v5_old";
947     mech_krb5.mech_type = (gss_OID)gss_mech_krb5_old;
948     gssint_register_mechinfo(&mech_krb5);
949
950     mech_krb5.mechNameStr = "mskrb";
951     mech_krb5.mech_type = (gss_OID)gss_mech_krb5_wrong;
952     gssint_register_mechinfo(&mech_krb5);
953
954     return 0;
955 }
956 #else
957 MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
958 MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
959
960 gss_mechanism KRB5_CALLCONV
961 gss_mech_initialize(void)
962 {
963     return &krb5_mechanism;
964 }
965 #endif /* _GSS_STATIC_LINK */
966
967 int gss_krb5int_lib_init(void)
968 {
969     int err;
970
971 #ifdef SHOW_INITFINI_FUNCS
972     printf("gss_krb5int_lib_init\n");
973 #endif
974
975     add_error_table(&et_k5g_error_table);
976
977 #ifndef LEAN_CLIENT
978     err = k5_mutex_finish_init(&gssint_krb5_keytab_lock);
979     if (err)
980         return err;
981 #endif /* LEAN_CLIENT */
982     err = k5_key_register(K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME, free);
983     if (err)
984         return err;
985     err = k5_key_register(K5_KEY_GSS_KRB5_CCACHE_NAME, free);
986     if (err)
987         return err;
988     err = k5_key_register(K5_KEY_GSS_KRB5_ERROR_MESSAGE,
989                           krb5_gss_delete_error_info);
990     if (err)
991         return err;
992 #ifndef _WIN32
993     err = k5_mutex_finish_init(&kg_kdc_flag_mutex);
994     if (err)
995         return err;
996     err = k5_mutex_finish_init(&kg_vdb.mutex);
997     if (err)
998         return err;
999 #endif
1000 #ifdef _GSS_STATIC_LINK
1001     err = gss_krb5mechglue_init();
1002     if (err)
1003         return err;
1004     err = gss_iakerbmechglue_init();
1005     if (err)
1006         return err;
1007 #endif
1008
1009     return 0;
1010 }
1011
1012 void gss_krb5int_lib_fini(void)
1013 {
1014 #ifndef _GSS_STATIC_LINK
1015     if (!INITIALIZER_RAN(gss_krb5int_lib_init) || PROGRAM_EXITING()) {
1016 # ifdef SHOW_INITFINI_FUNCS
1017         printf("gss_krb5int_lib_fini: skipping\n");
1018 # endif
1019         return;
1020     }
1021 #endif
1022 #ifdef SHOW_INITFINI_FUNCS
1023     printf("gss_krb5int_lib_fini\n");
1024 #endif
1025     remove_error_table(&et_k5g_error_table);
1026
1027     k5_key_delete(K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME);
1028     k5_key_delete(K5_KEY_GSS_KRB5_CCACHE_NAME);
1029     k5_mutex_destroy(&kg_vdb.mutex);
1030 #ifndef _WIN32
1031     k5_mutex_destroy(&kg_kdc_flag_mutex);
1032 #endif
1033 #ifndef LEAN_CLIENT
1034     k5_mutex_destroy(&gssint_krb5_keytab_lock);
1035 #endif /* LEAN_CLIENT */
1036 }
1037
1038 #ifdef _GSS_STATIC_LINK
1039 extern OM_uint32 gssint_lib_init(void);
1040 #endif
1041
1042 OM_uint32 gss_krb5int_initialize_library (void)
1043 {
1044 #ifdef _GSS_STATIC_LINK
1045     return gssint_mechglue_initialize_library();
1046 #else
1047     return CALL_INIT_FUNCTION(gss_krb5int_lib_init);
1048 #endif
1049 }