1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * Copyright 2000, 2007-2010 by the Massachusetts Institute of Technology.
6 * Export of this software from the United States of America may
7 * require a specific license from the United States Government.
8 * It is the responsibility of any person or organization contemplating
9 * export to obtain such a license before exporting.
11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12 * distribute this software and its documentation for any purpose and
13 * without fee is hereby granted, provided that the above copyright
14 * notice appear in all copies and that both that copyright notice and
15 * this permission notice appear in supporting documentation, and that
16 * the name of M.I.T. not be used in advertising or publicity pertaining
17 * to distribution of the software without specific, written prior
18 * permission. Furthermore if you modify this software you must label
19 * your software as modified software and not distribute it in such a
20 * fashion that it might be confused with the original M.I.T. software.
21 * M.I.T. makes no representations about the suitability of
22 * this software for any purpose. It is provided "as is" without express
23 * or implied warranty.
27 * Copyright 1993 by OpenVision Technologies, Inc.
29 * Permission to use, copy, modify, distribute, and sell this software
30 * and its documentation for any purpose is hereby granted without fee,
31 * provided that the above copyright notice appears in all copies and
32 * that both that copyright notice and this permission notice appear in
33 * supporting documentation, and that the name of OpenVision not be used
34 * in advertising or publicity pertaining to distribution of the software
35 * without specific, written prior permission. OpenVision makes no
36 * representations about the suitability of this software for any
37 * purpose. It is provided "as is" without express or implied warranty.
39 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
40 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
41 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
42 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
43 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
44 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
45 * PERFORMANCE OF THIS SOFTWARE.
49 * Copyright (C) 1998 by the FundsXpress, INC.
51 * All rights reserved.
53 * Export of this software from the United States of America may require
54 * a specific license from the United States Government. It is the
55 * responsibility of any person or organization contemplating export to
56 * obtain such a license before exporting.
58 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
59 * distribute this software and its documentation for any purpose and
60 * without fee is hereby granted, provided that the above copyright
61 * notice appear in all copies and that both that copyright notice and
62 * this permission notice appear in supporting documentation, and that
63 * the name of FundsXpress. not be used in advertising or publicity pertaining
64 * to distribution of the software without specific, written prior
65 * permission. FundsXpress makes no representations about the suitability of
66 * this software for any purpose. It is provided "as is" without express
67 * or implied warranty.
69 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
70 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
71 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
75 #include "gssapiP_krb5.h"
84 #include "kim_library_private.h"
85 #elif defined(USE_LEASH)
87 #define LEASH_DLL "leashw64.dll"
89 #define LEASH_DLL "leashw32.dll"
91 static void (*pLeash_AcquireInitialTicketsIfNeeded)(krb5_context,krb5_principal,char*,int) = NULL;
92 static HANDLE hLeashDLL = INVALID_HANDLE_VALUE;
96 k5_mutex_t gssint_krb5_keytab_lock = K5_MUTEX_PARTIAL_INITIALIZER;
97 static char *krb5_gss_keytab = NULL;
99 /* Heimdal calls this gsskrb5_register_acceptor_identity. */
101 gss_krb5int_register_acceptor_identity(OM_uint32 *minor_status,
102 const gss_OID desired_mech,
103 const gss_OID desired_object,
106 char *new = NULL, *old;
109 err = gss_krb5int_initialize_library();
111 return GSS_S_FAILURE;
113 if (value->value != NULL) {
114 new = strdup((char *)value->value);
116 return GSS_S_FAILURE;
119 err = k5_mutex_lock(&gssint_krb5_keytab_lock);
122 return GSS_S_FAILURE;
124 old = krb5_gss_keytab;
125 krb5_gss_keytab = new;
126 k5_mutex_unlock(&gssint_krb5_keytab_lock);
128 return GSS_S_COMPLETE;
131 /* get credentials corresponding to a key in the krb5 keytab.
132 If successful, set the keytab-specific fields in cred
136 acquire_accept_cred(krb5_context context,
137 OM_uint32 *minor_status,
138 krb5_principal desired_princ,
139 krb5_keytab req_keytab,
140 krb5_gss_cred_id_rec *cred)
142 krb5_error_code code;
144 krb5_keytab_entry entry;
146 assert(cred->keytab == NULL);
148 if (req_keytab != NULL) {
151 /* Duplicate keytab handle */
152 code = krb5_kt_get_name(context, req_keytab, ktname, sizeof(ktname));
154 *minor_status = code;
155 return GSS_S_CRED_UNAVAIL;
157 code = krb5_kt_resolve(context, ktname, &kt);
159 code = k5_mutex_lock(&gssint_krb5_keytab_lock);
161 *minor_status = code;
162 return GSS_S_FAILURE;
164 if (krb5_gss_keytab != NULL) {
165 code = krb5_kt_resolve(context, krb5_gss_keytab, &kt);
166 k5_mutex_unlock(&gssint_krb5_keytab_lock);
168 k5_mutex_unlock(&gssint_krb5_keytab_lock);
169 code = krb5_kt_default(context, &kt);
173 *minor_status = code;
174 return GSS_S_CRED_UNAVAIL;
177 if (desired_princ != NULL) {
178 code = krb5_kt_get_entry(context, kt, desired_princ, 0, 0, &entry);
180 krb5_kt_close(context, kt);
181 if (code == KRB5_KT_NOTFOUND) {
182 char *errstr = (char *)krb5_get_error_message(context, code);
183 krb5_set_error_message(context, KG_KEYTAB_NOMATCH, "%s", errstr);
184 krb5_free_error_message(context, errstr);
185 *minor_status = KG_KEYTAB_NOMATCH;
187 *minor_status = code;
188 return GSS_S_CRED_UNAVAIL;
190 krb5_kt_free_entry(context, &entry);
192 assert(cred->name == NULL);
193 code = kg_init_name(context, desired_princ, NULL, 0, &cred->name);
195 *minor_status = code;
196 return GSS_S_FAILURE;
199 /* Open the replay cache for this principal. */
200 code = krb5_get_server_rcache(context,
201 krb5_princ_component(context, desired_princ, 0),
204 *minor_status = code;
205 return GSS_S_FAILURE;
211 return GSS_S_COMPLETE;
213 #endif /* LEAN_CLIENT */
215 /* get credentials corresponding to the default credential cache.
216 If successful, set the ccache-specific fields in cred.
220 acquire_init_cred(krb5_context context,
221 OM_uint32 *minor_status,
222 krb5_ccache req_ccache,
223 krb5_principal desired_princ,
224 gss_buffer_t password,
225 krb5_gss_cred_id_rec *cred)
227 krb5_error_code code;
229 krb5_principal ccache_princ = NULL, tmp_princ;
233 int caller_provided_ccache_name = 0;
234 krb5_data password_data, *cred_princ_realm;
238 /* load the GSS ccache name into the kg_context */
240 if (GSS_ERROR(kg_sync_ccache_name(context, minor_status)))
241 return GSS_S_FAILURE;
243 /* check to see if the caller provided a ccache name if so
244 * we will just use that and not search the cache collection */
245 if (GSS_ERROR(kg_caller_provided_ccache_name (minor_status, &caller_provided_ccache_name))) {
246 return GSS_S_FAILURE;
249 #if defined(USE_KIM) || defined(USE_LEASH)
250 if (desired_princ && !caller_provided_ccache_name && !req_ccache) {
252 kim_error err = KIM_NO_ERROR;
253 kim_ccache kimccache = NULL;
254 kim_identity identity = NULL;
255 kim_credential_state state;
257 err = kim_identity_create_from_krb5_principal (&identity,
262 err = kim_ccache_create_from_client_identity (&kimccache, identity);
266 err = kim_ccache_get_state (kimccache, &state);
269 if (!err && state != kim_credentials_state_valid) {
270 if (state == kim_credentials_state_needs_validation) {
271 err = kim_ccache_validate (kimccache, KIM_OPTIONS_DEFAULT);
273 kim_ccache_free (&kimccache);
278 if (!kimccache && kim_library_allow_automatic_prompting ()) {
279 /* ccache does not already exist, create a new one */
280 err = kim_ccache_create_new (&kimccache, identity,
281 KIM_OPTIONS_DEFAULT);
285 err = kim_ccache_get_krb5_ccache (kimccache, context, &ccache);
288 kim_ccache_free (&kimccache);
289 kim_identity_free (&identity);
293 return GSS_S_CRED_UNAVAIL;
296 #elif defined(USE_LEASH)
297 if ( hLeashDLL == INVALID_HANDLE_VALUE ) {
298 hLeashDLL = LoadLibrary(LEASH_DLL);
299 if ( hLeashDLL != INVALID_HANDLE_VALUE ) {
300 (FARPROC) pLeash_AcquireInitialTicketsIfNeeded =
301 GetProcAddress(hLeashDLL, "not_an_API_Leash_AcquireInitialTicketsIfNeeded");
305 if ( pLeash_AcquireInitialTicketsIfNeeded ) {
307 pLeash_AcquireInitialTicketsIfNeeded(context, desired_princ, ccname, sizeof(ccname));
309 *minor_status = KRB5_CC_NOTFOUND;
310 return GSS_S_CRED_UNAVAIL;
313 if ((code = krb5_cc_resolve (context, ccname, &ccache))) {
314 *minor_status = code;
315 return GSS_S_CRED_UNAVAIL;
318 /* leash dll not available, open the default credential cache */
320 if ((code = krb5int_cc_default(context, &ccache))) {
321 *minor_status = code;
322 return GSS_S_CRED_UNAVAIL;
325 #endif /* USE_LEASH */
327 #endif /* USE_KIM || USE_LEASH */
329 if (req_ccache != NULL) {
330 /* Duplicate ccache handle */
331 code = krb5_cc_dup(context, req_ccache, &ccache);
333 /* Open the default credential cache */
334 code = krb5int_cc_default(context, &ccache);
337 *minor_status = code;
338 return GSS_S_CRED_UNAVAIL;
342 /* turn off OPENCLOSE mode while extensive frobbing is going on */
343 code = krb5_cc_set_flags(context, ccache, 0);
344 if (code == KRB5_FCC_NOFILE &&
345 password != GSS_C_NO_BUFFER && desired_princ != NULL) {
346 /* We will get initial creds later. */
347 code = krb5_cc_initialize(context, ccache, desired_princ);
349 code = krb5_cc_set_flags(context, ccache, 0);
352 krb5_cc_close(context, ccache);
353 *minor_status = code;
354 return GSS_S_CRED_UNAVAIL;
358 * Credentials cache principal must match either the acceptor principal
359 * name or the desired_princ argument (they may be the same).
361 if (cred->name != NULL && desired_princ == NULL)
362 desired_princ = cred->name->princ;
364 code = krb5_cc_get_principal(context, ccache, &ccache_princ);
366 krb5_cc_close(context, ccache);
367 *minor_status = code;
368 return GSS_S_FAILURE;
371 if (desired_princ != NULL) {
372 if (!krb5_principal_compare(context, ccache_princ, desired_princ)) {
373 krb5_free_principal(context, ccache_princ);
374 krb5_cc_close(context, ccache);
375 *minor_status = KG_CCACHE_NOMATCH;
376 return GSS_S_CRED_UNAVAIL;
381 * If we are acquiring initiator-only default credentials, then set
382 * cred->name to the credentials cache principal name.
384 if (cred->name == NULL) {
385 if ((code = kg_init_name(context, ccache_princ, NULL,
386 KG_INIT_NAME_NO_COPY, &cred->name))) {
387 krb5_free_principal(context, ccache_princ);
388 krb5_cc_close(context, ccache);
389 *minor_status = code;
390 return GSS_S_FAILURE;
393 krb5_free_principal(context, ccache_princ);
396 assert(cred->name->princ != NULL);
397 cred_princ_realm = krb5_princ_realm(context, cred->name->princ);
399 if (password != GSS_C_NO_BUFFER) {
400 /* stash the password for later */
401 password_data.length = password->length;
402 password_data.data = (char *)password->value;
404 code = krb5int_copy_data_contents_add0(context, &password_data,
407 krb5_cc_close(context, ccache);
408 *minor_status = code;
409 return GSS_S_FAILURE;
412 /* restore the OPENCLOSE flag */
413 code = krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
415 krb5_cc_close(context, ccache);
416 *minor_status = code;
417 return GSS_S_FAILURE;
420 cred->ccache = ccache;
421 return GSS_S_COMPLETE;
424 /* iterate over the ccache, find the tgt */
426 if ((code = krb5_cc_start_seq_get(context, ccache, &cur))) {
427 krb5_cc_close(context, ccache);
428 *minor_status = code;
429 return GSS_S_FAILURE;
432 /* this is hairy. If there's a tgt for the principal's local realm
433 in here, that's what we want for the expire time. But if
434 there's not, then we want to use the first key. */
438 code = krb5_build_principal_ext(context, &tmp_princ,
439 cred_princ_realm->length,
440 cred_princ_realm->data,
441 KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
442 cred_princ_realm->length,
443 cred_princ_realm->data,
446 krb5_cc_close(context, ccache);
447 *minor_status = code;
448 return GSS_S_FAILURE;
450 while (!(code = krb5_cc_next_cred(context, ccache, &cur, &creds))) {
451 if (krb5_principal_compare(context, tmp_princ, creds.server)) {
452 cred->tgt_expire = creds.times.endtime;
456 krb5_free_cred_contents(context, &creds);
459 if (got_endtime == 0) {
460 cred->tgt_expire = creds.times.endtime;
463 krb5_free_cred_contents(context, &creds);
465 krb5_free_principal(context, tmp_princ);
467 if (code && code != KRB5_CC_END) {
468 /* this means some error occurred reading the ccache */
469 krb5_cc_end_seq_get(context, ccache, &cur);
470 krb5_cc_close(context, ccache);
471 *minor_status = code;
472 return GSS_S_FAILURE;
473 } else if (! got_endtime) {
474 /* this means the ccache was entirely empty */
475 krb5_cc_end_seq_get(context, ccache, &cur);
476 krb5_cc_close(context, ccache);
477 *minor_status = KG_EMPTY_CCACHE;
478 return GSS_S_FAILURE;
480 /* this means that we found an endtime to use. */
481 if ((code = krb5_cc_end_seq_get(context, ccache, &cur))) {
482 krb5_cc_close(context, ccache);
483 *minor_status = code;
484 return GSS_S_FAILURE;
486 if ((code = krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE))) {
487 krb5_cc_close(context, ccache);
488 *minor_status = code;
489 return GSS_S_FAILURE;
493 /* the credentials match and are valid */
495 cred->ccache = ccache;
496 /* minor_status is set while we are iterating over the ccache */
497 return GSS_S_COMPLETE;
500 struct acquire_cred_args {
501 gss_name_t desired_name;
502 gss_buffer_t password;
504 gss_OID_set desired_mechs;
505 gss_cred_usage_t cred_usage;
513 acquire_cred(OM_uint32 *minor_status,
514 const struct acquire_cred_args *args,
515 gss_cred_id_t *output_cred_handle,
518 krb5_context context = NULL;
519 krb5_gss_cred_id_t cred = NULL;
521 krb5_error_code code = 0;
522 krb5_principal desired_princ = NULL;
524 /* make sure all outputs are valid */
525 *output_cred_handle = GSS_C_NO_CREDENTIAL;
529 code = gss_krb5int_initialize_library();
533 code = krb5_gss_init_context(&context);
537 /* create the gss cred structure */
538 cred = k5alloc(sizeof(krb5_gss_cred_id_rec), &code);
542 cred->usage = args->cred_usage;
544 cred->iakerb_mech = args->iakerb;
545 cred->default_identity = (args->desired_name == GSS_C_NO_NAME);
548 #endif /* LEAN_CLIENT */
551 code = k5_mutex_init(&cred->lock);
555 switch (args->cred_usage) {
562 *minor_status = (OM_uint32) G_BAD_USAGE;
566 if (args->desired_name != GSS_C_NO_NAME)
567 desired_princ = ((krb5_gss_name_t)args->desired_name)->princ;
571 * If requested, acquire credentials for accepting. This will fill
572 * in cred->name if desired_princ is specified.
574 if (args->cred_usage == GSS_C_ACCEPT || args->cred_usage == GSS_C_BOTH) {
575 ret = acquire_accept_cred(context, minor_status,
578 if (ret != GSS_S_COMPLETE)
581 #endif /* LEAN_CLIENT */
584 * If requested, acquire credentials for initiation. This will fill
585 * in cred->name if it wasn't set above.
587 if (args->cred_usage == GSS_C_INITIATE || args->cred_usage == GSS_C_BOTH) {
588 ret = acquire_init_cred(context, minor_status, args->ccache,
589 desired_princ, args->password, cred);
590 if (ret != GSS_S_COMPLETE)
594 assert(cred->default_identity || cred->name != NULL);
596 /*** at this point, the cred structure has been completely created */
598 if (args->cred_usage == GSS_C_ACCEPT) {
600 *time_rec = GSS_C_INDEFINITE;
604 code = krb5_timeofday(context, &now);
609 *time_rec = (cred->tgt_expire > now) ? (cred->tgt_expire - now) : 0;
612 if (!kg_save_cred_id((gss_cred_id_t)cred)) {
618 *output_cred_handle = (gss_cred_id_t) cred;
620 krb5_free_context(context);
621 return GSS_S_COMPLETE;
624 *minor_status = code;
630 krb5_cc_close(context, cred->ccache);
633 krb5_kt_close(context, cred->keytab);
634 #endif /* LEAN_CLIENT */
636 kg_release_name(context, 0, &cred->name);
637 k5_mutex_destroy(&cred->lock);
640 save_error_info(*minor_status, context);
641 krb5_free_context(context);
646 gss_krb5int_set_cred_rcache(OM_uint32 *minor_status,
647 gss_cred_id_t *cred_handle,
648 const gss_OID desired_oid,
649 const gss_buffer_t value)
651 krb5_gss_cred_id_t cred;
652 krb5_error_code code;
653 krb5_context context;
656 assert(value->length == sizeof(rcache));
658 if (value->length != sizeof(rcache))
659 return GSS_S_FAILURE;
661 rcache = (krb5_rcache)value->value;
663 cred = (krb5_gss_cred_id_t)*cred_handle;
665 code = krb5_gss_init_context(&context);
667 *minor_status = code;
668 return GSS_S_FAILURE;
670 if (cred->rcache != NULL) {
671 code = krb5_rc_close(context, cred->rcache);
673 *minor_status = code;
674 krb5_free_context(context);
675 return GSS_S_FAILURE;
679 cred->rcache = rcache;
681 krb5_free_context(context);
684 return GSS_S_COMPLETE;
688 * krb5 and IAKERB mech API functions follow. The mechglue always passes null
689 * desired_mechs and actual_mechs, so we ignore those parameters.
693 krb5_gss_acquire_cred(minor_status, desired_name, time_req,
694 desired_mechs, cred_usage, output_cred_handle,
695 actual_mechs, time_rec)
696 OM_uint32 *minor_status;
697 gss_name_t desired_name;
699 gss_OID_set desired_mechs;
700 gss_cred_usage_t cred_usage;
701 gss_cred_id_t *output_cred_handle;
702 gss_OID_set *actual_mechs;
705 struct acquire_cred_args args;
707 if (desired_name && !kg_validate_name(desired_name)) {
708 *minor_status = G_VALIDATE_FAILED;
709 return GSS_S_FAILURE;
712 memset(&args, 0, sizeof(args));
713 args.desired_name = desired_name;
714 args.time_req = time_req;
715 args.desired_mechs = desired_mechs;
716 args.cred_usage = cred_usage;
719 return acquire_cred(minor_status, &args, output_cred_handle, time_rec);
723 iakerb_gss_acquire_cred(minor_status, desired_name, time_req,
724 desired_mechs, cred_usage, output_cred_handle,
725 actual_mechs, time_rec)
726 OM_uint32 *minor_status;
727 gss_name_t desired_name;
729 gss_OID_set desired_mechs;
730 gss_cred_usage_t cred_usage;
731 gss_cred_id_t *output_cred_handle;
732 gss_OID_set *actual_mechs;
735 struct acquire_cred_args args;
737 if (desired_name && !kg_validate_name(desired_name)) {
738 *minor_status = G_VALIDATE_FAILED;
739 return GSS_S_FAILURE;
742 memset(&args, 0, sizeof(args));
743 args.desired_name = desired_name;
744 args.time_req = time_req;
745 args.desired_mechs = desired_mechs;
746 args.cred_usage = cred_usage;
749 return acquire_cred(minor_status, &args, output_cred_handle, time_rec);
753 krb5_gss_acquire_cred_with_password(OM_uint32 *minor_status,
754 const gss_name_t desired_name,
755 const gss_buffer_t password,
757 const gss_OID_set desired_mechs,
759 gss_cred_id_t *output_cred_handle,
760 gss_OID_set *actual_mechs,
763 struct acquire_cred_args args;
765 if (desired_name && !kg_validate_name(desired_name)) {
766 *minor_status = G_VALIDATE_FAILED;
767 return GSS_S_FAILURE;
770 memset(&args, 0, sizeof(args));
771 args.desired_name = desired_name;
772 args.password = password;
773 args.time_req = time_req;
774 args.desired_mechs = desired_mechs;
775 args.cred_usage = cred_usage;
778 return acquire_cred(minor_status, &args, output_cred_handle, time_rec);
782 iakerb_gss_acquire_cred_with_password(OM_uint32 *minor_status,
783 const gss_name_t desired_name,
784 const gss_buffer_t password,
786 const gss_OID_set desired_mechs,
788 gss_cred_id_t *output_cred_handle,
789 gss_OID_set *actual_mechs,
792 struct acquire_cred_args args;
794 if (desired_name && !kg_validate_name(desired_name)) {
795 *minor_status = G_VALIDATE_FAILED;
796 return GSS_S_FAILURE;
799 memset(&args, 0, sizeof(args));
800 args.desired_name = desired_name;
801 args.password = password;
802 args.time_req = time_req;
803 args.desired_mechs = desired_mechs;
804 args.cred_usage = cred_usage;
807 return acquire_cred(minor_status, &args, output_cred_handle, time_rec);
811 gss_krb5int_import_cred(OM_uint32 *minor_status,
812 gss_cred_id_t *cred_handle,
813 const gss_OID desired_oid,
814 const gss_buffer_t value)
816 struct krb5_gss_import_cred_req *req;
817 struct acquire_cred_args args;
818 krb5_gss_name_rec name;
821 assert(value->length == sizeof(*req));
823 if (value->length != sizeof(*req))
824 return GSS_S_FAILURE;
826 req = (struct krb5_gss_import_cred_req *)value->value;
828 memset(&args, 0, sizeof(args));
830 if (req->keytab_principal) {
831 memset(&name, 0, sizeof(name));
832 name.princ = req->keytab_principal;
833 args.desired_name = (gss_name_t)&name;
836 args.ccache = req->id;
837 args.keytab = req->keytab;
839 if (req->id && req->keytab)
840 args.cred_usage = GSS_C_BOTH;
842 args.cred_usage = GSS_C_INITIATE;
843 else if (req->keytab)
844 args.cred_usage = GSS_C_ACCEPT;
846 *minor_status = EINVAL;
847 return GSS_S_FAILURE;
850 return acquire_cred(minor_status, &args, cred_handle, &time_rec);