2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
5 * Copyright 2000 by the Massachusetts Institute of Technology.
8 * Export of this software from the United States of America may
9 * require a specific license from the United States Government.
10 * It is the responsibility of any person or organization contemplating
11 * export to obtain such a license before exporting.
13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14 * distribute this software and its documentation for any purpose and
15 * without fee is hereby granted, provided that the above copyright
16 * notice appear in all copies and that both that copyright notice and
17 * this permission notice appear in supporting documentation, and that
18 * the name of M.I.T. not be used in advertising or publicity pertaining
19 * to distribution of the software without specific, written prior
20 * permission. Furthermore if you modify this software you must label
21 * your software as modified software and not distribute it in such a
22 * fashion that it might be confused with the original M.I.T. software.
23 * M.I.T. makes no representations about the suitability of
24 * this software for any purpose. It is provided "as is" without express
25 * or implied warranty.
29 * Copyright 1993 by OpenVision Technologies, Inc.
31 * Permission to use, copy, modify, distribute, and sell this software
32 * and its documentation for any purpose is hereby granted without fee,
33 * provided that the above copyright notice appears in all copies and
34 * that both that copyright notice and this permission notice appear in
35 * supporting documentation, and that the name of OpenVision not be used
36 * in advertising or publicity pertaining to distribution of the software
37 * without specific, written prior permission. OpenVision makes no
38 * representations about the suitability of this software for any
39 * purpose. It is provided "as is" without express or implied warranty.
41 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
42 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
43 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
44 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
45 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
46 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
47 * PERFORMANCE OF THIS SOFTWARE.
51 * Copyright (C) 1998 by the FundsXpress, INC.
53 * All rights reserved.
55 * Export of this software from the United States of America may require
56 * a specific license from the United States Government. It is the
57 * responsibility of any person or organization contemplating export to
58 * obtain such a license before exporting.
60 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
61 * distribute this software and its documentation for any purpose and
62 * without fee is hereby granted, provided that the above copyright
63 * notice appear in all copies and that both that copyright notice and
64 * this permission notice appear in supporting documentation, and that
65 * the name of FundsXpress. not be used in advertising or publicity pertaining
66 * to distribution of the software without specific, written prior
67 * permission. FundsXpress makes no representations about the suitability of
68 * this software for any purpose. It is provided "as is" without express
69 * or implied warranty.
71 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
72 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
73 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
77 #include "gss_libinit.h"
78 #include "gssapiP_krb5.h"
86 #include <locale.h> /* Solaris Kerberos */
87 #include "file/ktfile.h" /* Solaris Kerberos */
89 #if defined(USE_LOGIN_LIBRARY)
90 #include <Kerberos/KerberosLoginPrivate.h>
91 #elif defined(USE_LEASH)
93 #define LEASH_DLL "leashw64.dll"
95 #define LEASH_DLL "leashw32.dll"
97 static void (*pLeash_AcquireInitialTicketsIfNeeded
)(krb5_context
,krb5_principal
,char*,int) = NULL
;
98 static HANDLE hLeashDLL
= INVALID_HANDLE_VALUE
;
101 k5_mutex_t gssint_krb5_keytab_lock
= K5_MUTEX_PARTIAL_INITIALIZER
;
102 static char *krb5_gss_keytab
= NULL
;
104 /* Heimdal calls this gsskrb5_register_acceptor_identity. */
105 OM_uint32 KRB5_CALLCONV
106 krb5_gss_register_acceptor_identity(const char *keytab
)
112 err
= gssint_initialize_library();
114 return GSS_S_FAILURE
;
117 return GSS_S_FAILURE
;
119 len
= strlen(keytab
);
120 new = malloc(len
+ 1);
122 return GSS_S_FAILURE
;
125 err
= k5_mutex_lock(&gssint_krb5_keytab_lock
);
128 return GSS_S_FAILURE
;
130 old
= krb5_gss_keytab
;
131 krb5_gss_keytab
= new;
132 k5_mutex_unlock(&gssint_krb5_keytab_lock
);
134 return GSS_S_COMPLETE
;
137 /* get credentials corresponding to a key in the krb5 keytab.
138 If the default name is requested, return the name in output_princ.
139 If output_princ is non-NULL, the caller will use or free it, regardless
141 If successful, set the keytab-specific fields in cred
145 acquire_accept_cred(context
, minor_status
, desired_name
, output_princ
, cred
)
146 krb5_context context
;
147 OM_uint32
*minor_status
;
148 gss_name_t desired_name
;
149 krb5_principal
*output_princ
;
150 krb5_gss_cred_id_rec
*cred
;
152 krb5_error_code code
;
153 krb5_principal princ
;
155 krb5_keytab_entry entry
;
157 *output_princ
= NULL
;
160 /* open the default keytab */
162 code
= gssint_initialize_library();
164 *minor_status
= code
;
165 return GSS_S_FAILURE
;
167 code
= k5_mutex_lock(&gssint_krb5_keytab_lock
);
169 *minor_status
= code
;
170 return GSS_S_FAILURE
;
172 if (krb5_gss_keytab
!= NULL
) {
173 code
= krb5_kt_resolve(context
, krb5_gss_keytab
, &kt
);
174 k5_mutex_unlock(&gssint_krb5_keytab_lock
);
176 k5_mutex_unlock(&gssint_krb5_keytab_lock
);
177 code
= krb5_kt_default(context
, &kt
);
181 *minor_status
= code
;
182 /* Solaris Kerb NOTE: GSS_S_CRED_UNAVAIL is not RFC 2743 compliant */
183 return(GSS_S_NO_CRED
);
186 if (desired_name
!= GSS_C_NO_NAME
) {
187 princ
= (krb5_principal
) desired_name
;
188 if ((code
= krb5_kt_get_entry(context
, kt
, princ
, 0, 0, &entry
))) {
189 if (code
== KRB5_KT_NOTFOUND
) {
191 if (krb5_unparse_name(context
, princ
, &s_name
) == 0) {
192 krb5_set_error_message(context
, KG_KEYTAB_NOMATCH
,
193 dgettext(TEXT_DOMAIN
,
194 "No principal in keytab ('%s') matches desired name %s"),
197 krb5_free_unparsed_name(context
, s_name
);
199 *minor_status
= KG_KEYTAB_NOMATCH
;
201 *minor_status
= code
;
202 /* Solaris Kerb NOTE: GSS_S_CRED_UNAVAIL is not RFC 2743 compliant */
203 (void) krb5_kt_close(context
, kt
);
204 return(GSS_S_NO_CRED
);
206 krb5_kt_free_entry(context
, &entry
);
208 /* Open the replay cache for this principal. */
209 if ((code
= krb5_get_server_rcache(context
,
210 krb5_princ_component(context
, princ
, 0),
212 *minor_status
= code
;
213 return(GSS_S_FAILURE
);
218 /* hooray. we made it */
222 return(GSS_S_COMPLETE
);
225 /* get credentials corresponding to the default credential cache.
226 If the default name is requested, return the name in output_princ.
227 If output_princ is non-NULL, the caller will use or free it, regardless
229 If successful, set the ccache-specific fields in cred.
233 acquire_init_cred(context
, minor_status
, desired_name
, output_princ
, cred
)
234 krb5_context context
;
235 OM_uint32
*minor_status
;
236 gss_name_t desired_name
;
237 krb5_principal
*output_princ
;
238 krb5_gss_cred_id_rec
*cred
;
240 krb5_error_code code
;
242 krb5_principal princ
, tmp_princ
;
247 int caller_provided_ccache_name
= 0;
251 /* load the GSS ccache name into the kg_context */
253 if (GSS_ERROR(kg_sync_ccache_name(context
, minor_status
)))
254 return(GSS_S_FAILURE
);
256 /* check to see if the caller provided a ccache name if so
257 * we will just use that and not search the cache collection */
258 if (GSS_ERROR(kg_caller_provided_ccache_name (minor_status
, &caller_provided_ccache_name
))) {
259 return(GSS_S_FAILURE
);
262 #if defined(USE_LOGIN_LIBRARY) || defined(USE_LEASH)
263 if (desired_name
&& !caller_provided_ccache_name
) {
264 #if defined(USE_LOGIN_LIBRARY)
265 KLStatus err
= klNoErr
;
266 char *ccache_name
= NULL
;
267 KLPrincipal kl_desired_princ
= NULL
;
269 err
= __KLCreatePrincipalFromKerberos5Principal ((krb5_principal
) desired_name
,
273 err
= KLAcquireInitialTickets (kl_desired_princ
, NULL
, NULL
, &ccache_name
);
277 err
= krb5_cc_resolve (context
, ccache_name
, &ccache
);
282 return(GSS_S_CRED_UNAVAIL
);
285 if (kl_desired_princ
!= NULL
) { KLDisposePrincipal (kl_desired_princ
); }
286 if (ccache_name
!= NULL
) { KLDisposeString (ccache_name
); }
288 #elif defined(USE_LEASH)
289 if ( hLeashDLL
== INVALID_HANDLE_VALUE
) {
290 hLeashDLL
= LoadLibrary(LEASH_DLL
);
291 if ( hLeashDLL
!= INVALID_HANDLE_VALUE
) {
292 (FARPROC
) pLeash_AcquireInitialTicketsIfNeeded
=
293 GetProcAddress(hLeashDLL
, "not_an_API_Leash_AcquireInitialTicketsIfNeeded");
297 if ( pLeash_AcquireInitialTicketsIfNeeded
) {
299 pLeash_AcquireInitialTicketsIfNeeded(context
, (krb5_principal
) desired_name
, ccname
, sizeof(ccname
));
301 *minor_status
= KRB5_CC_NOTFOUND
;
302 return(GSS_S_NO_CRED
);
305 if ((code
= krb5_cc_resolve (context
, ccname
, &ccache
))) {
306 *minor_status
= code
;
307 return(GSS_S_NO_CRED
);
310 /* leash dll not available, open the default credential cache */
312 if ((code
= krb5int_cc_default(context
, &ccache
))) {
313 *minor_status
= code
;
314 return(GSS_S_NO_CRED
);
317 #endif /* USE_LEASH */
319 #endif /* USE_LOGIN_LIBRARY || USE_LEASH */
321 /* open the default credential cache */
323 if ((code
= krb5int_cc_default(context
, &ccache
))) {
324 *minor_status
= code
;
325 return(GSS_S_NO_CRED
);
329 /* turn off OPENCLOSE mode while extensive frobbing is going on */
332 * Added calls to krb5_cc_set_flags(... KRB5_TC_OPENCLOSE)
333 * on the error returns cuz the 1.4 krb5_cc_close does not always close
334 * the file like it used to and caused STC test gss.27 to fail.
336 flags
= 0; /* turns off OPENCLOSE mode */
337 if ((code
= krb5_cc_set_flags(context
, ccache
, flags
))) {
338 (void)krb5_cc_close(context
, ccache
);
339 *minor_status
= code
;
340 return(GSS_S_NO_CRED
);
343 /* get out the principal name and see if it matches */
345 if ((code
= krb5_cc_get_principal(context
, ccache
, &princ
))) {
346 /* Solaris Kerberos */
347 (void)krb5_cc_set_flags(context
, ccache
, KRB5_TC_OPENCLOSE
);
348 (void)krb5_cc_close(context
, ccache
);
349 *minor_status
= code
;
350 return(GSS_S_FAILURE
);
353 if (desired_name
!= (gss_name_t
) NULL
) {
354 if (! krb5_principal_compare(context
, princ
, (krb5_principal
) desired_name
)) {
355 (void)krb5_free_principal(context
, princ
);
356 /* Solaris Kerberos */
357 (void)krb5_cc_set_flags(context
, ccache
, KRB5_TC_OPENCLOSE
);
358 (void)krb5_cc_close(context
, ccache
);
359 *minor_status
= KG_CCACHE_NOMATCH
;
360 return(GSS_S_NO_CRED
);
362 (void)krb5_free_principal(context
, princ
);
363 princ
= (krb5_principal
) desired_name
;
365 *output_princ
= princ
;
368 /* iterate over the ccache, find the tgt */
370 if ((code
= krb5_cc_start_seq_get(context
, ccache
, &cur
))) {
371 /* Solaris Kerberos */
372 (void)krb5_cc_set_flags(context
, ccache
, KRB5_TC_OPENCLOSE
);
373 (void)krb5_cc_close(context
, ccache
);
374 *minor_status
= code
;
375 return(GSS_S_FAILURE
);
378 /* this is hairy. If there's a tgt for the principal's local realm
379 in here, that's what we want for the expire time. But if
380 there's not, then we want to use the first key. */
384 code
= krb5_build_principal_ext(context
, &tmp_princ
,
385 krb5_princ_realm(context
, princ
)->length
,
386 krb5_princ_realm(context
, princ
)->data
,
388 krb5_princ_realm(context
, princ
)->length
,
389 krb5_princ_realm(context
, princ
)->data
,
392 /* Solaris Kerberos */
393 (void)krb5_cc_set_flags(context
, ccache
, KRB5_TC_OPENCLOSE
);
394 (void)krb5_cc_close(context
, ccache
);
395 *minor_status
= code
;
396 return(GSS_S_FAILURE
);
398 while (!(code
= krb5_cc_next_cred(context
, ccache
, &cur
, &creds
))) {
399 if (krb5_principal_compare(context
, tmp_princ
, creds
.server
)) {
400 cred
->tgt_expire
= creds
.times
.endtime
;
404 krb5_free_cred_contents(context
, &creds
);
407 if (got_endtime
== 0) {
408 cred
->tgt_expire
= creds
.times
.endtime
;
411 krb5_free_cred_contents(context
, &creds
);
413 krb5_free_principal(context
, tmp_princ
);
415 if (code
&& code
!= KRB5_CC_END
) {
416 /* this means some error occurred reading the ccache */
417 (void)krb5_cc_end_seq_get(context
, ccache
, &cur
);
418 /* Solaris Kerberos */
419 (void)krb5_cc_set_flags(context
, ccache
, KRB5_TC_OPENCLOSE
);
420 (void)krb5_cc_close(context
, ccache
);
421 *minor_status
= code
;
422 return(GSS_S_FAILURE
);
423 } else if (! got_endtime
) {
424 /* this means the ccache was entirely empty */
425 (void)krb5_cc_end_seq_get(context
, ccache
, &cur
);
426 /* Solaris Kerberos */
427 (void)krb5_cc_set_flags(context
, ccache
, KRB5_TC_OPENCLOSE
);
428 (void)krb5_cc_close(context
, ccache
);
429 *minor_status
= KG_EMPTY_CCACHE
;
430 return(GSS_S_FAILURE
);
432 /* this means that we found an endtime to use. */
433 if ((code
= krb5_cc_end_seq_get(context
, ccache
, &cur
))) {
434 /* Solaris Kerberos */
435 (void)krb5_cc_set_flags(context
, ccache
, KRB5_TC_OPENCLOSE
);
436 (void)krb5_cc_close(context
, ccache
);
437 *minor_status
= code
;
438 return(GSS_S_FAILURE
);
440 flags
= KRB5_TC_OPENCLOSE
; /* turns on OPENCLOSE mode */
441 if ((code
= krb5_cc_set_flags(context
, ccache
, flags
))) {
442 (void)krb5_cc_close(context
, ccache
);
443 *minor_status
= code
;
444 return(GSS_S_FAILURE
);
448 /* the credentials match and are valid */
450 cred
->ccache
= ccache
;
451 /* minor_status is set while we are iterating over the ccache */
452 return(GSS_S_COMPLETE
);
457 krb5_gss_acquire_cred(minor_status
, desired_name
, time_req
,
458 desired_mechs
, cred_usage
, output_cred_handle
,
459 actual_mechs
, time_rec
)
460 OM_uint32
*minor_status
;
461 gss_name_t desired_name
;
463 gss_OID_set desired_mechs
;
464 gss_cred_usage_t cred_usage
;
465 gss_cred_id_t
*output_cred_handle
;
466 gss_OID_set
*actual_mechs
;
469 krb5_context context
;
471 krb5_gss_cred_id_t cred
;
472 gss_OID_set ret_mechs
;
473 int req_old
, req_new
;
475 krb5_error_code code
;
477 code
= gssint_initialize_library();
479 *minor_status
= code
;
480 return GSS_S_FAILURE
;
483 code
= krb5_gss_init_context(&context
);
485 *minor_status
= code
;
486 return GSS_S_FAILURE
;
489 /* make sure all outputs are valid */
491 *output_cred_handle
= NULL
;
493 *actual_mechs
= NULL
;
497 /* validate the name */
500 if ((desired_name
!= (gss_name_t
) NULL
) &&
501 (! kg_validate_name(desired_name
))) {
502 *minor_status
= (OM_uint32
) G_VALIDATE_FAILED
;
503 krb5_free_context(context
);
504 return(GSS_S_CALL_BAD_STRUCTURE
|GSS_S_BAD_NAME
);
507 /* verify that the requested mechanism set is the default, or
510 if (desired_mechs
== GSS_C_NULL_OID_SET
) {
517 for (i
=0; i
<desired_mechs
->count
; i
++) {
518 if (g_OID_equal(gss_mech_krb5_old
, &(desired_mechs
->elements
[i
])))
520 if (g_OID_equal(gss_mech_krb5
, &(desired_mechs
->elements
[i
])))
524 if (!req_old
&& !req_new
) {
526 krb5_free_context(context
);
527 return(GSS_S_BAD_MECH
);
531 /* create the gss cred structure */
534 (krb5_gss_cred_id_t
) xmalloc(sizeof(krb5_gss_cred_id_rec
))) == NULL
) {
535 *minor_status
= ENOMEM
;
536 krb5_free_context(context
);
537 return(GSS_S_FAILURE
);
539 memset(cred
, 0, sizeof(krb5_gss_cred_id_rec
));
541 cred
->usage
= cred_usage
;
543 cred
->prerfc_mech
= req_old
;
544 cred
->rfc_mech
= req_new
;
549 code
= k5_mutex_init(&cred
->lock
);
551 *minor_status
= code
;
552 krb5_free_context(context
);
553 return GSS_S_FAILURE
;
555 /* Note that we don't need to lock this GSSAPI credential record
556 here, because no other thread can gain access to it until we
559 if ((cred_usage
!= GSS_C_INITIATE
) &&
560 (cred_usage
!= GSS_C_ACCEPT
) &&
561 (cred_usage
!= GSS_C_BOTH
)) {
562 k5_mutex_destroy(&cred
->lock
);
564 *minor_status
= (OM_uint32
) G_BAD_USAGE
;
565 krb5_free_context(context
);
566 return(GSS_S_FAILURE
);
569 /* if requested, acquire credentials for accepting */
570 /* this will fill in cred->princ if the desired_name is not specified */
572 if ((cred_usage
== GSS_C_ACCEPT
) ||
573 (cred_usage
== GSS_C_BOTH
))
574 if ((ret
= acquire_accept_cred(context
, minor_status
, desired_name
,
575 &(cred
->princ
), cred
))
578 krb5_free_principal(context
, cred
->princ
);
579 k5_mutex_destroy(&cred
->lock
);
581 /* minor_status set by acquire_accept_cred() */
582 save_error_info(*minor_status
, context
);
583 krb5_free_context(context
);
587 /* if requested, acquire credentials for initiation */
588 /* this will fill in cred->princ if it wasn't set above, and
589 the desired_name is not specified */
591 if ((cred_usage
== GSS_C_INITIATE
) ||
592 (cred_usage
== GSS_C_BOTH
))
594 acquire_init_cred(context
, minor_status
,
595 cred
->princ
?(gss_name_t
)cred
->princ
:desired_name
,
596 &(cred
->princ
), cred
))
599 krb5_kt_close(context
, cred
->keytab
);
601 krb5_free_principal(context
, cred
->princ
);
602 k5_mutex_destroy(&cred
->lock
);
604 /* minor_status set by acquire_init_cred() */
605 save_error_info(*minor_status
, context
);
606 krb5_free_context(context
);
611 * if the princ wasn't filled in already, fill it in now unless
612 * a cred with no associated princ is requested (will invoke default
613 * behaviour when gss_accept_init_context() is called).
614 * Note MIT 1.4 has GSS_C_NO_CREDENTIAL instead of GSS_C_NO_NAME
616 if (!cred
->princ
&& (desired_name
!= GSS_C_NO_NAME
))
617 if ((code
= krb5_copy_principal(context
, (krb5_principal
) desired_name
,
620 (void)krb5_cc_close(context
, cred
->ccache
);
622 (void)krb5_kt_close(context
, cred
->keytab
);
623 k5_mutex_destroy(&cred
->lock
);
625 *minor_status
= code
;
626 save_error_info(*minor_status
, context
);
627 krb5_free_context(context
);
628 return(GSS_S_FAILURE
);
631 /*** at this point, the cred structure has been completely created */
633 /* compute time_rec */
635 if (cred_usage
== GSS_C_ACCEPT
) {
637 *time_rec
= GSS_C_INDEFINITE
;
641 if ((code
= krb5_timeofday(context
, &now
))) {
643 (void)krb5_cc_close(context
, cred
->ccache
);
645 (void)krb5_kt_close(context
, cred
->keytab
);
647 krb5_free_principal(context
, cred
->princ
);
648 k5_mutex_destroy(&cred
->lock
);
650 *minor_status
= code
;
651 save_error_info(*minor_status
, context
);
652 krb5_free_context(context
);
653 return(GSS_S_FAILURE
);
657 *time_rec
= (cred
->tgt_expire
> now
) ? (cred
->tgt_expire
- now
) : 0;
663 if (GSS_ERROR(ret
= generic_gss_create_empty_oid_set(minor_status
,
665 (cred
->prerfc_mech
&&
666 GSS_ERROR(ret
= generic_gss_add_oid_set_member(minor_status
,
667 (const gss_OID
) gss_mech_krb5_old
,
670 GSS_ERROR(ret
= generic_gss_add_oid_set_member(minor_status
,
671 (const gss_OID
) gss_mech_krb5
,
674 (void)krb5_cc_close(context
, cred
->ccache
);
676 (void)krb5_kt_close(context
, cred
->keytab
);
678 krb5_free_principal(context
, cred
->princ
);
679 k5_mutex_destroy(&cred
->lock
);
681 /* *minor_status set above */
682 krb5_free_context(context
);
687 /* intern the credential handle */
689 if (! kg_save_cred_id((gss_cred_id_t
) cred
)) {
690 free(ret_mechs
->elements
);
693 (void)krb5_cc_close(context
, cred
->ccache
);
695 (void)krb5_kt_close(context
, cred
->keytab
);
697 krb5_free_principal(context
, cred
->princ
);
698 k5_mutex_destroy(&cred
->lock
);
700 *minor_status
= (OM_uint32
) G_VALIDATE_FAILED
;
701 save_error_string(*minor_status
, "error saving credentials");
702 krb5_free_context(context
);
703 return(GSS_S_FAILURE
);
709 *output_cred_handle
= (gss_cred_id_t
) cred
;
711 *actual_mechs
= ret_mechs
;
713 krb5_free_context(context
);
714 return(GSS_S_COMPLETE
);