/src/krb5/src/lib/gssapi/krb5/acquire_cred.c
Line | Count | Source |
1 | | /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
2 | | /* |
3 | | * Copyright 2000, 2007-2010 by the Massachusetts Institute of Technology. |
4 | | * All Rights Reserved. |
5 | | * |
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. |
10 | | * |
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. |
24 | | */ |
25 | | /* |
26 | | * Copyright 1993 by OpenVision Technologies, Inc. |
27 | | * |
28 | | * Permission to use, copy, modify, distribute, and sell this software |
29 | | * and its documentation for any purpose is hereby granted without fee, |
30 | | * provided that the above copyright notice appears in all copies and |
31 | | * that both that copyright notice and this permission notice appear in |
32 | | * supporting documentation, and that the name of OpenVision not be used |
33 | | * in advertising or publicity pertaining to distribution of the software |
34 | | * without specific, written prior permission. OpenVision makes no |
35 | | * representations about the suitability of this software for any |
36 | | * purpose. It is provided "as is" without express or implied warranty. |
37 | | * |
38 | | * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
39 | | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
40 | | * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
41 | | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF |
42 | | * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR |
43 | | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
44 | | * PERFORMANCE OF THIS SOFTWARE. |
45 | | */ |
46 | | |
47 | | /* |
48 | | * Copyright (C) 1998 by the FundsXpress, INC. |
49 | | * |
50 | | * All rights reserved. |
51 | | * |
52 | | * Export of this software from the United States of America may require |
53 | | * a specific license from the United States Government. It is the |
54 | | * responsibility of any person or organization contemplating export to |
55 | | * obtain such a license before exporting. |
56 | | * |
57 | | * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and |
58 | | * distribute this software and its documentation for any purpose and |
59 | | * without fee is hereby granted, provided that the above copyright |
60 | | * notice appear in all copies and that both that copyright notice and |
61 | | * this permission notice appear in supporting documentation, and that |
62 | | * the name of FundsXpress. not be used in advertising or publicity pertaining |
63 | | * to distribution of the software without specific, written prior |
64 | | * permission. FundsXpress makes no representations about the suitability of |
65 | | * this software for any purpose. It is provided "as is" without express |
66 | | * or implied warranty. |
67 | | * |
68 | | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
69 | | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED |
70 | | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
71 | | */ |
72 | | |
73 | | #include "k5-int.h" |
74 | | #include "gssapiP_krb5.h" |
75 | | #ifdef HAVE_STRING_H |
76 | | #include <string.h> |
77 | | #else |
78 | | #include <strings.h> |
79 | | #endif |
80 | | |
81 | | #ifdef USE_LEASH |
82 | | #ifdef _WIN64 |
83 | | #define LEASH_DLL "leashw64.dll" |
84 | | #else |
85 | | #define LEASH_DLL "leashw32.dll" |
86 | | #endif |
87 | | static void (*pLeash_AcquireInitialTicketsIfNeeded)(krb5_context,krb5_principal,char*,int) = NULL; |
88 | | static HANDLE hLeashDLL = INVALID_HANDLE_VALUE; |
89 | | #endif |
90 | | |
91 | | #ifndef LEAN_CLIENT |
92 | | k5_mutex_t gssint_krb5_keytab_lock = K5_MUTEX_PARTIAL_INITIALIZER; |
93 | | static char *krb5_gss_keytab = NULL; |
94 | | |
95 | | /* Heimdal calls this gsskrb5_register_acceptor_identity. */ |
96 | | OM_uint32 |
97 | | gss_krb5int_register_acceptor_identity(OM_uint32 *minor_status, |
98 | | const gss_OID desired_mech, |
99 | | const gss_OID desired_object, |
100 | | gss_buffer_t value) |
101 | 0 | { |
102 | 0 | char *new = NULL, *old; |
103 | 0 | int err; |
104 | |
|
105 | 0 | err = gss_krb5int_initialize_library(); |
106 | 0 | if (err != 0) |
107 | 0 | return GSS_S_FAILURE; |
108 | | |
109 | 0 | if (value->value != NULL) { |
110 | 0 | new = strdup((char *)value->value); |
111 | 0 | if (new == NULL) |
112 | 0 | return GSS_S_FAILURE; |
113 | 0 | } |
114 | | |
115 | 0 | k5_mutex_lock(&gssint_krb5_keytab_lock); |
116 | 0 | old = krb5_gss_keytab; |
117 | 0 | krb5_gss_keytab = new; |
118 | 0 | k5_mutex_unlock(&gssint_krb5_keytab_lock); |
119 | 0 | free(old); |
120 | 0 | return GSS_S_COMPLETE; |
121 | 0 | } |
122 | | |
123 | | /* Try to verify that keytab contains at least one entry for name. Return 0 if |
124 | | * it does, KRB5_KT_NOTFOUND if it doesn't, or another error as appropriate. */ |
125 | | static krb5_error_code |
126 | | check_keytab(krb5_context context, krb5_keytab kt, krb5_gss_name_t name, |
127 | | krb5_principal mprinc) |
128 | 0 | { |
129 | 0 | krb5_error_code code; |
130 | 0 | krb5_keytab_entry ent; |
131 | 0 | char *princname; |
132 | |
|
133 | 0 | if (name->service == NULL) { |
134 | 0 | code = krb5_kt_get_entry(context, kt, name->princ, 0, 0, &ent); |
135 | 0 | if (code == 0) |
136 | 0 | krb5_kt_free_entry(context, &ent); |
137 | 0 | return code; |
138 | 0 | } |
139 | | |
140 | | /* If we can't iterate through the keytab, skip this check. */ |
141 | 0 | if (kt->ops->start_seq_get == NULL) |
142 | 0 | return 0; |
143 | | |
144 | | /* Scan the keytab for host-based entries matching mprinc. */ |
145 | 0 | code = k5_kt_have_match(context, kt, mprinc); |
146 | 0 | if (code == KRB5_KT_NOTFOUND) { |
147 | 0 | if (krb5_unparse_name(context, mprinc, &princname) == 0) { |
148 | 0 | k5_setmsg(context, code, _("No key table entry found matching %s"), |
149 | 0 | princname); |
150 | 0 | free(princname); |
151 | 0 | } |
152 | 0 | } |
153 | 0 | return code; |
154 | 0 | } |
155 | | |
156 | | /* get credentials corresponding to a key in the krb5 keytab. |
157 | | If successful, set the keytab-specific fields in cred |
158 | | */ |
159 | | |
160 | | static OM_uint32 |
161 | | acquire_accept_cred(krb5_context context, OM_uint32 *minor_status, |
162 | | krb5_keytab req_keytab, const char *rcname, |
163 | | krb5_gss_cred_id_rec *cred) |
164 | 41 | { |
165 | 41 | OM_uint32 major; |
166 | 41 | krb5_error_code code; |
167 | 41 | krb5_keytab kt = NULL; |
168 | 41 | krb5_rcache rc = NULL; |
169 | | |
170 | 41 | assert(cred->keytab == NULL); |
171 | | |
172 | | /* If we have an explicit rcache name, open it. */ |
173 | 41 | if (rcname != NULL) { |
174 | 0 | code = k5_rc_resolve(context, rcname, &rc); |
175 | 0 | if (code) { |
176 | 0 | major = GSS_S_FAILURE; |
177 | 0 | goto cleanup; |
178 | 0 | } |
179 | 0 | } |
180 | | |
181 | 41 | if (req_keytab != NULL) { |
182 | 0 | code = krb5_kt_dup(context, req_keytab, &kt); |
183 | 41 | } else { |
184 | 41 | k5_mutex_lock(&gssint_krb5_keytab_lock); |
185 | 41 | if (krb5_gss_keytab != NULL) { |
186 | 0 | code = krb5_kt_resolve(context, krb5_gss_keytab, &kt); |
187 | 0 | k5_mutex_unlock(&gssint_krb5_keytab_lock); |
188 | 41 | } else { |
189 | 41 | k5_mutex_unlock(&gssint_krb5_keytab_lock); |
190 | 41 | code = krb5_kt_default(context, &kt); |
191 | 41 | } |
192 | 41 | } |
193 | 41 | if (code) { |
194 | 0 | major = GSS_S_NO_CRED; |
195 | 0 | goto cleanup; |
196 | 0 | } |
197 | | |
198 | 41 | if (cred->name != NULL) { |
199 | 0 | code = kg_acceptor_princ(context, cred->name, &cred->acceptor_mprinc); |
200 | 0 | if (code) { |
201 | 0 | major = GSS_S_FAILURE; |
202 | 0 | goto cleanup; |
203 | 0 | } |
204 | | |
205 | | /* Make sure we have keys matching the desired name in the keytab. */ |
206 | 0 | code = check_keytab(context, kt, cred->name, cred->acceptor_mprinc); |
207 | 0 | if (code) { |
208 | 0 | if (code == KRB5_KT_NOTFOUND) { |
209 | 0 | k5_change_error_message_code(context, code, KG_KEYTAB_NOMATCH); |
210 | 0 | code = KG_KEYTAB_NOMATCH; |
211 | 0 | } |
212 | 0 | major = GSS_S_NO_CRED; |
213 | 0 | goto cleanup; |
214 | 0 | } |
215 | | |
216 | 0 | if (rc == NULL) { |
217 | | /* Open the replay cache for this principal. */ |
218 | 0 | code = krb5_get_server_rcache(context, &cred->name->princ->data[0], |
219 | 0 | &rc); |
220 | 0 | if (code) { |
221 | 0 | major = GSS_S_FAILURE; |
222 | 0 | goto cleanup; |
223 | 0 | } |
224 | 0 | } |
225 | 41 | } else { |
226 | | /* Make sure we have a keytab with keys in it. */ |
227 | 41 | code = krb5_kt_have_content(context, kt); |
228 | 41 | if (code) { |
229 | 41 | major = GSS_S_NO_CRED; |
230 | 41 | goto cleanup; |
231 | 41 | } |
232 | 41 | } |
233 | | |
234 | 0 | cred->keytab = kt; |
235 | 0 | kt = NULL; |
236 | 0 | cred->rcache = rc; |
237 | 0 | rc = NULL; |
238 | 0 | major = GSS_S_COMPLETE; |
239 | |
|
240 | 41 | cleanup: |
241 | 41 | if (kt != NULL) |
242 | 41 | krb5_kt_close(context, kt); |
243 | 41 | if (rc != NULL) |
244 | 0 | k5_rc_close(context, rc); |
245 | 41 | *minor_status = code; |
246 | 41 | return major; |
247 | 0 | } |
248 | | #endif /* LEAN_CLIENT */ |
249 | | |
250 | | #ifdef USE_LEASH |
251 | | static krb5_error_code |
252 | | get_ccache_leash(krb5_context context, krb5_principal desired_princ, |
253 | | krb5_ccache *ccache_out) |
254 | | { |
255 | | krb5_error_code code; |
256 | | krb5_ccache ccache; |
257 | | char ccname[256] = ""; |
258 | | |
259 | | *ccache_out = NULL; |
260 | | |
261 | | if (hLeashDLL == INVALID_HANDLE_VALUE) { |
262 | | hLeashDLL = LoadLibrary(LEASH_DLL); |
263 | | if (hLeashDLL != INVALID_HANDLE_VALUE) { |
264 | | (FARPROC) pLeash_AcquireInitialTicketsIfNeeded = |
265 | | GetProcAddress(hLeashDLL, "not_an_API_Leash_AcquireInitialTicketsIfNeeded"); |
266 | | } |
267 | | } |
268 | | |
269 | | if (pLeash_AcquireInitialTicketsIfNeeded) { |
270 | | pLeash_AcquireInitialTicketsIfNeeded(context, desired_princ, ccname, |
271 | | sizeof(ccname)); |
272 | | if (!ccname[0]) |
273 | | return KRB5_CC_NOTFOUND; |
274 | | |
275 | | code = krb5_cc_resolve(context, ccname, &ccache); |
276 | | if (code) |
277 | | return code; |
278 | | } else { |
279 | | /* leash dll not available, open the default credential cache. */ |
280 | | code = krb5int_cc_default(context, &ccache); |
281 | | if (code) |
282 | | return code; |
283 | | } |
284 | | |
285 | | *ccache_out = ccache; |
286 | | return 0; |
287 | | } |
288 | | #endif /* USE_LEASH */ |
289 | | |
290 | | /* Set fields in cred according to a ccache config entry whose key (in |
291 | | * principal form) is config_princ and whose value is value. */ |
292 | | static krb5_error_code |
293 | | scan_cc_config(krb5_context context, krb5_gss_cred_id_rec *cred, |
294 | | krb5_const_principal config_princ, const krb5_data *value) |
295 | 0 | { |
296 | 0 | krb5_error_code code; |
297 | 0 | krb5_data data0 = empty_data(); |
298 | |
|
299 | 0 | if (config_princ->length != 2) |
300 | 0 | return 0; |
301 | 0 | if (data_eq_string(config_princ->data[1], KRB5_CC_CONF_PROXY_IMPERSONATOR) |
302 | 0 | && cred->impersonator == NULL) { |
303 | 0 | code = krb5int_copy_data_contents_add0(context, value, &data0); |
304 | 0 | if (code) |
305 | 0 | return code; |
306 | 0 | code = krb5_parse_name(context, data0.data, &cred->impersonator); |
307 | 0 | krb5_free_data_contents(context, &data0); |
308 | 0 | if (code) |
309 | 0 | return code; |
310 | 0 | } else if (data_eq_string(config_princ->data[1], KRB5_CC_CONF_REFRESH_TIME) |
311 | 0 | && cred->refresh_time == 0) { |
312 | 0 | code = krb5int_copy_data_contents_add0(context, value, &data0); |
313 | 0 | if (code) |
314 | 0 | return code; |
315 | 0 | cred->refresh_time = atol(data0.data); |
316 | 0 | krb5_free_data_contents(context, &data0); |
317 | 0 | } |
318 | 0 | return 0; |
319 | 0 | } |
320 | | |
321 | | /* Return true if it appears that we can non-interactively get initial |
322 | | * tickets for cred. */ |
323 | | static krb5_boolean |
324 | | can_get_initial_creds(krb5_context context, krb5_gss_cred_id_rec *cred) |
325 | 0 | { |
326 | 0 | krb5_error_code code; |
327 | |
|
328 | 0 | if (cred->password != NULL) |
329 | 0 | return TRUE; |
330 | | |
331 | 0 | if (cred->client_keytab == NULL) |
332 | 0 | return FALSE; |
333 | | |
334 | | /* If we don't know the client principal yet, check for any keytab keys. */ |
335 | 0 | if (cred->name == NULL) |
336 | 0 | return !krb5_kt_have_content(context, cred->client_keytab); |
337 | | |
338 | | /* |
339 | | * Check if we have a keytab key for the client principal. This is a bit |
340 | | * more permissive than we really want because krb5_kt_have_match() |
341 | | * supports wildcarding and obeys ignore_acceptor_hostname, but that should |
342 | | * generally be harmless. |
343 | | */ |
344 | 0 | code = k5_kt_have_match(context, cred->client_keytab, cred->name->princ); |
345 | 0 | return code == 0; |
346 | 0 | } |
347 | | |
348 | | /* Scan cred->ccache for name, expiry time, impersonator, refresh time. If |
349 | | * check_name is true, verify the cache name against the credential name. */ |
350 | | static krb5_error_code |
351 | | scan_ccache(krb5_context context, krb5_gss_cred_id_rec *cred, |
352 | | krb5_boolean check_name) |
353 | 0 | { |
354 | 0 | krb5_error_code code; |
355 | 0 | krb5_ccache ccache = cred->ccache; |
356 | 0 | krb5_principal ccache_princ = NULL, tgt_princ = NULL; |
357 | 0 | krb5_data *realm; |
358 | 0 | krb5_cc_cursor cursor; |
359 | 0 | krb5_creds creds; |
360 | 0 | krb5_timestamp endtime; |
361 | 0 | krb5_boolean is_tgt; |
362 | | |
363 | | /* Turn on NOTICKET, as we don't need session keys here. */ |
364 | 0 | code = krb5_cc_set_flags(context, ccache, KRB5_TC_NOTICKET); |
365 | 0 | if (code) |
366 | 0 | return code; |
367 | | |
368 | 0 | code = krb5_cc_get_principal(context, ccache, &ccache_princ); |
369 | 0 | if (code != 0) |
370 | 0 | goto cleanup; |
371 | | |
372 | 0 | if (cred->name == NULL) { |
373 | | /* Save the ccache principal as the credential name. */ |
374 | 0 | code = kg_init_name(context, ccache_princ, NULL, NULL, NULL, |
375 | 0 | KG_INIT_NAME_NO_COPY, &cred->name); |
376 | 0 | if (code) |
377 | 0 | goto cleanup; |
378 | 0 | ccache_princ = NULL; |
379 | 0 | } else { |
380 | | /* Check against the desired name if needed. */ |
381 | 0 | if (check_name) { |
382 | 0 | if (!k5_sname_compare(context, cred->name->princ, ccache_princ)) { |
383 | 0 | code = KG_CCACHE_NOMATCH; |
384 | 0 | goto cleanup; |
385 | 0 | } |
386 | 0 | } |
387 | | |
388 | | /* Replace the credential name principal with the canonical client |
389 | | * principal, retaining acceptor_mprinc if set. */ |
390 | 0 | krb5_free_principal(context, cred->name->princ); |
391 | 0 | cred->name->princ = ccache_princ; |
392 | 0 | ccache_princ = NULL; |
393 | 0 | } |
394 | | |
395 | 0 | assert(cred->name->princ != NULL); |
396 | 0 | realm = krb5_princ_realm(context, cred->name->princ); |
397 | 0 | code = krb5_build_principal_ext(context, &tgt_princ, |
398 | 0 | realm->length, realm->data, |
399 | 0 | KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME, |
400 | 0 | realm->length, realm->data, |
401 | 0 | 0); |
402 | 0 | if (code) |
403 | 0 | return code; |
404 | | |
405 | | /* If there's a tgt for the principal's local realm in here, use its expiry |
406 | | * time. Otherwise use the first key. */ |
407 | 0 | code = krb5_cc_start_seq_get(context, ccache, &cursor); |
408 | 0 | if (code) { |
409 | 0 | krb5_free_principal(context, tgt_princ); |
410 | 0 | return code; |
411 | 0 | } |
412 | 0 | while (!(code = krb5_cc_next_cred(context, ccache, &cursor, &creds))) { |
413 | 0 | if (krb5_is_config_principal(context, creds.server)) { |
414 | 0 | code = scan_cc_config(context, cred, creds.server, &creds.ticket); |
415 | 0 | krb5_free_cred_contents(context, &creds); |
416 | 0 | if (code) |
417 | 0 | break; |
418 | 0 | continue; |
419 | 0 | } |
420 | 0 | is_tgt = krb5_principal_compare(context, tgt_princ, creds.server); |
421 | 0 | endtime = creds.times.endtime; |
422 | 0 | krb5_free_cred_contents(context, &creds); |
423 | 0 | if (is_tgt) |
424 | 0 | cred->have_tgt = TRUE; |
425 | 0 | if (is_tgt || cred->expire == 0) |
426 | 0 | cred->expire = endtime; |
427 | 0 | } |
428 | 0 | krb5_cc_end_seq_get(context, ccache, &cursor); |
429 | 0 | if (code && code != KRB5_CC_END) |
430 | 0 | goto cleanup; |
431 | 0 | code = 0; |
432 | |
|
433 | 0 | if (cred->expire == 0 && !can_get_initial_creds(context, cred)) { |
434 | 0 | code = KG_EMPTY_CCACHE; |
435 | 0 | goto cleanup; |
436 | 0 | } |
437 | | |
438 | 0 | cleanup: |
439 | 0 | (void)krb5_cc_set_flags(context, ccache, 0); |
440 | 0 | krb5_free_principal(context, ccache_princ); |
441 | 0 | krb5_free_principal(context, tgt_princ); |
442 | 0 | return code; |
443 | 0 | } |
444 | | |
445 | | /* Find an existing or destination ccache for cred->name. */ |
446 | | static krb5_error_code |
447 | | get_cache_for_name(krb5_context context, krb5_gss_cred_id_rec *cred) |
448 | 0 | { |
449 | 0 | krb5_error_code code; |
450 | 0 | krb5_boolean can_get, have_collection; |
451 | 0 | krb5_ccache defcc = NULL; |
452 | 0 | krb5_principal princ = NULL; |
453 | 0 | const char *cctype; |
454 | |
|
455 | 0 | assert(cred->name != NULL && cred->ccache == NULL); |
456 | | #ifdef USE_LEASH |
457 | | code = get_ccache_leash(context, cred->name->princ, &cred->ccache); |
458 | | return code ? code : scan_ccache(context, cred, TRUE); |
459 | | #else |
460 | | /* Check first whether we can acquire tickets, to avoid overwriting the |
461 | | * extended error message from krb5_cc_cache_match. */ |
462 | 0 | can_get = can_get_initial_creds(context, cred); |
463 | | |
464 | | /* Look for an existing cache for the client principal. */ |
465 | 0 | code = krb5_cc_cache_match(context, cred->name->princ, &cred->ccache); |
466 | 0 | if (code == 0) |
467 | 0 | return scan_ccache(context, cred, FALSE); |
468 | 0 | if (code != KRB5_CC_NOTFOUND || !can_get) |
469 | 0 | return code; |
470 | 0 | krb5_clear_error_message(context); |
471 | | |
472 | | /* There is no existing ccache, but we can acquire credentials. Get the |
473 | | * default ccache to help decide where we should put them. */ |
474 | 0 | code = krb5_cc_default(context, &defcc); |
475 | 0 | if (code) |
476 | 0 | return code; |
477 | 0 | cctype = krb5_cc_get_type(context, defcc); |
478 | 0 | have_collection = krb5_cc_support_switch(context, cctype); |
479 | | |
480 | | /* We can use an empty default ccache if we're using a password or if |
481 | | * there's no collection. */ |
482 | 0 | if (cred->password != NULL || !have_collection) { |
483 | 0 | if (krb5_cc_get_principal(context, defcc, &princ) == KRB5_FCC_NOFILE) { |
484 | 0 | cred->ccache = defcc; |
485 | 0 | defcc = NULL; |
486 | 0 | } |
487 | 0 | krb5_clear_error_message(context); |
488 | 0 | } |
489 | | |
490 | | /* Otherwise, try to use a new cache in the collection. */ |
491 | 0 | if (cred->ccache == NULL) { |
492 | 0 | if (!have_collection) { |
493 | 0 | code = KG_CCACHE_NOMATCH; |
494 | 0 | goto cleanup; |
495 | 0 | } |
496 | 0 | code = krb5_cc_new_unique(context, cctype, NULL, &cred->ccache); |
497 | 0 | if (code) |
498 | 0 | goto cleanup; |
499 | 0 | } |
500 | | |
501 | 0 | cleanup: |
502 | 0 | krb5_free_principal(context, princ); |
503 | 0 | if (defcc != NULL) |
504 | 0 | krb5_cc_close(context, defcc); |
505 | 0 | return code; |
506 | 0 | #endif /* not USE_LEASH */ |
507 | 0 | } |
508 | | |
509 | | /* Try to set cred->name using the client keytab. */ |
510 | | static krb5_error_code |
511 | | get_name_from_client_keytab(krb5_context context, krb5_gss_cred_id_rec *cred) |
512 | 0 | { |
513 | 0 | krb5_error_code code; |
514 | 0 | krb5_principal princ; |
515 | |
|
516 | 0 | assert(cred->name == NULL); |
517 | |
|
518 | 0 | if (cred->client_keytab == NULL) |
519 | 0 | return KRB5_KT_NOTFOUND; |
520 | | |
521 | 0 | code = k5_kt_get_principal(context, cred->client_keytab, &princ); |
522 | 0 | if (code) |
523 | 0 | return code; |
524 | 0 | code = kg_init_name(context, princ, NULL, NULL, NULL, KG_INIT_NAME_NO_COPY, |
525 | 0 | &cred->name); |
526 | 0 | if (code) { |
527 | 0 | krb5_free_principal(context, princ); |
528 | 0 | return code; |
529 | 0 | } |
530 | 0 | return 0; |
531 | 0 | } |
532 | | |
533 | | /* Make a note in ccache that we should attempt to refresh it from the client |
534 | | * keytab at refresh_time. */ |
535 | | static void |
536 | | set_refresh_time(krb5_context context, krb5_ccache ccache, |
537 | | krb5_timestamp refresh_time) |
538 | 0 | { |
539 | 0 | char buf[128]; |
540 | 0 | krb5_data d; |
541 | |
|
542 | 0 | snprintf(buf, sizeof(buf), "%u", (unsigned int)ts2tt(refresh_time)); |
543 | 0 | d = string2data(buf); |
544 | 0 | (void)krb5_cc_set_config(context, ccache, NULL, KRB5_CC_CONF_REFRESH_TIME, |
545 | 0 | &d); |
546 | 0 | krb5_clear_error_message(context); |
547 | 0 | } |
548 | | |
549 | | /* Return true if it's time to refresh cred from the client keytab. */ |
550 | | krb5_boolean |
551 | | kg_cred_time_to_refresh(krb5_context context, krb5_gss_cred_id_rec *cred) |
552 | 0 | { |
553 | 0 | krb5_timestamp now, soon; |
554 | |
|
555 | 0 | if (krb5_timeofday(context, &now)) |
556 | 0 | return FALSE; |
557 | 0 | soon = ts_incr(now, 30); |
558 | | |
559 | | /* If a refresh time is set and has elapsed, attempt a refresh, and set a |
560 | | * new refresh time to avoid retrying for 30 seconds. */ |
561 | 0 | if (cred->refresh_time != 0 && !ts_after(cred->refresh_time, now)) { |
562 | 0 | set_refresh_time(context, cred->ccache, soon); |
563 | 0 | return TRUE; |
564 | 0 | } |
565 | | |
566 | | /* If the creds will expire soon, attempt a refresh even if they weren't |
567 | | * acquired with a client keytab. */ |
568 | 0 | if (ts_after(soon, cred->expire)) |
569 | 0 | return TRUE; |
570 | | |
571 | 0 | return FALSE; |
572 | 0 | } |
573 | | |
574 | | /* If appropriate, make a note to refresh cred from the client keytab when it |
575 | | * is halfway to expired. */ |
576 | | void |
577 | | kg_cred_set_initial_refresh(krb5_context context, krb5_gss_cred_id_rec *cred, |
578 | | krb5_ticket_times *times) |
579 | 0 | { |
580 | 0 | krb5_timestamp refresh; |
581 | | |
582 | | /* For now, we only mark keytab-acquired credentials for refresh. */ |
583 | 0 | if (cred->password != NULL) |
584 | 0 | return; |
585 | | |
586 | | /* Make a note to refresh these when they are halfway to expired. */ |
587 | 0 | refresh = ts_incr(times->starttime, |
588 | 0 | ts_delta(times->endtime, times->starttime) / 2); |
589 | 0 | set_refresh_time(context, cred->ccache, refresh); |
590 | 0 | } |
591 | | |
592 | | struct verify_params { |
593 | | krb5_principal princ; |
594 | | krb5_keytab keytab; |
595 | | }; |
596 | | |
597 | | static krb5_error_code |
598 | | verify_initial_cred(krb5_context context, krb5_creds *creds, |
599 | | const struct verify_params *verify) |
600 | 0 | { |
601 | 0 | krb5_verify_init_creds_opt vopts; |
602 | |
|
603 | 0 | krb5_verify_init_creds_opt_init(&vopts); |
604 | 0 | krb5_verify_init_creds_opt_set_ap_req_nofail(&vopts, TRUE); |
605 | 0 | return krb5_verify_init_creds(context, creds, verify->princ, |
606 | 0 | verify->keytab, NULL, &vopts); |
607 | 0 | } |
608 | | |
609 | | /* Get initial credentials using the supplied password or client keytab. */ |
610 | | static krb5_error_code |
611 | | get_initial_cred(krb5_context context, const struct verify_params *verify, |
612 | | krb5_gss_cred_id_rec *cred) |
613 | 0 | { |
614 | 0 | krb5_error_code code; |
615 | 0 | krb5_get_init_creds_opt *opt = NULL; |
616 | 0 | krb5_creds creds; |
617 | |
|
618 | 0 | code = krb5_get_init_creds_opt_alloc(context, &opt); |
619 | 0 | if (code) |
620 | 0 | return code; |
621 | 0 | code = krb5_get_init_creds_opt_set_out_ccache(context, opt, cred->ccache); |
622 | 0 | if (code) |
623 | 0 | goto cleanup; |
624 | 0 | if (cred->password != NULL) { |
625 | 0 | code = krb5_get_init_creds_password(context, &creds, cred->name->princ, |
626 | 0 | cred->password, NULL, NULL, 0, |
627 | 0 | NULL, opt); |
628 | 0 | } else if (cred->client_keytab != NULL) { |
629 | 0 | code = krb5_get_init_creds_keytab(context, &creds, cred->name->princ, |
630 | 0 | cred->client_keytab, 0, NULL, opt); |
631 | 0 | } else { |
632 | 0 | code = KRB5_KT_NOTFOUND; |
633 | 0 | } |
634 | 0 | if (code) |
635 | 0 | goto cleanup; |
636 | 0 | if (cred->password != NULL && verify != NULL) { |
637 | 0 | code = verify_initial_cred(context, &creds, verify); |
638 | 0 | if (code) |
639 | 0 | goto cleanup; |
640 | 0 | } |
641 | 0 | kg_cred_set_initial_refresh(context, cred, &creds.times); |
642 | 0 | cred->have_tgt = TRUE; |
643 | 0 | cred->expire = creds.times.endtime; |
644 | | |
645 | | /* Steal the canonical client principal name from creds and save it in the |
646 | | * credential name, retaining acceptor_mprinc if set. */ |
647 | 0 | krb5_free_principal(context, cred->name->princ); |
648 | 0 | cred->name->princ = creds.client; |
649 | 0 | creds.client = NULL; |
650 | |
|
651 | 0 | krb5_free_cred_contents(context, &creds); |
652 | 0 | cleanup: |
653 | 0 | krb5_get_init_creds_opt_free(context, opt); |
654 | 0 | return code; |
655 | 0 | } |
656 | | |
657 | | /* Get initial credentials if we ought to and are able to. */ |
658 | | static krb5_error_code |
659 | | maybe_get_initial_cred(krb5_context context, |
660 | | const struct verify_params *verify, |
661 | | krb5_gss_cred_id_rec *cred) |
662 | 0 | { |
663 | 0 | krb5_error_code code; |
664 | | |
665 | | /* Don't get creds if we don't know the name or are doing IAKERB. */ |
666 | 0 | if (cred->name == NULL || cred->iakerb_mech) |
667 | 0 | return 0; |
668 | | |
669 | | /* Get creds if we have none or if it's time to refresh. */ |
670 | 0 | if (cred->expire == 0 || kg_cred_time_to_refresh(context, cred)) { |
671 | 0 | code = get_initial_cred(context, verify, cred); |
672 | | /* If we were trying to refresh and failed, we can keep going. */ |
673 | 0 | if (code && cred->expire == 0) |
674 | 0 | return code; |
675 | 0 | krb5_clear_error_message(context); |
676 | 0 | } |
677 | 0 | return 0; |
678 | 0 | } |
679 | | |
680 | | static OM_uint32 |
681 | | acquire_init_cred(krb5_context context, OM_uint32 *minor_status, |
682 | | krb5_ccache req_ccache, gss_buffer_t password, |
683 | | krb5_keytab client_keytab, |
684 | | const struct verify_params *verify, |
685 | | krb5_gss_cred_id_rec *cred) |
686 | 0 | { |
687 | 0 | krb5_error_code code; |
688 | 0 | krb5_data pwdata, pwcopy; |
689 | 0 | int caller_ccname = 0; |
690 | | |
691 | | /* Get ccache from caller if available. */ |
692 | 0 | if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) |
693 | 0 | return GSS_S_FAILURE; |
694 | 0 | if (GSS_ERROR(kg_caller_provided_ccache_name(minor_status, |
695 | 0 | &caller_ccname))) |
696 | 0 | return GSS_S_FAILURE; |
697 | | |
698 | 0 | if (password != GSS_C_NO_BUFFER) { |
699 | 0 | pwdata = make_data(password->value, password->length); |
700 | 0 | code = krb5int_copy_data_contents_add0(context, &pwdata, &pwcopy); |
701 | 0 | if (code) |
702 | 0 | goto error; |
703 | 0 | cred->password = pwcopy.data; |
704 | | |
705 | | /* We will fetch the credential into a private memory ccache. */ |
706 | 0 | assert(req_ccache == NULL); |
707 | 0 | code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache); |
708 | 0 | if (code) |
709 | 0 | goto error; |
710 | 0 | cred->destroy_ccache = 1; |
711 | 0 | } else if (req_ccache != NULL) { |
712 | 0 | code = krb5_cc_dup(context, req_ccache, &cred->ccache); |
713 | 0 | if (code) |
714 | 0 | goto error; |
715 | 0 | } else if (caller_ccname) { |
716 | | /* Caller's ccache name has been set as the context default. */ |
717 | 0 | code = krb5int_cc_default(context, &cred->ccache); |
718 | 0 | if (code) |
719 | 0 | goto error; |
720 | 0 | } |
721 | | |
722 | 0 | if (client_keytab != NULL) { |
723 | 0 | code = krb5_kt_dup(context, client_keytab, &cred->client_keytab); |
724 | 0 | } else { |
725 | 0 | code = krb5_kt_client_default(context, &cred->client_keytab); |
726 | 0 | if (code) { |
727 | | /* Treat resolution failure similarly to a client keytab which |
728 | | * resolves but doesn't exist or has no content. */ |
729 | 0 | TRACE_GSS_CLIENT_KEYTAB_FAIL(context, code); |
730 | 0 | krb5_clear_error_message(context); |
731 | 0 | code = 0; |
732 | 0 | } |
733 | 0 | } |
734 | 0 | if (code) |
735 | 0 | goto error; |
736 | | |
737 | 0 | if (cred->ccache != NULL) { |
738 | | /* The caller specified a ccache; check what's in it. */ |
739 | 0 | code = scan_ccache(context, cred, TRUE); |
740 | 0 | if (code == KRB5_FCC_NOFILE) { |
741 | | /* See if we can get initial creds. If the caller didn't specify |
742 | | * a name, pick one from the client keytab. */ |
743 | 0 | if (cred->name == NULL) { |
744 | 0 | if (!get_name_from_client_keytab(context, cred)) |
745 | 0 | code = 0; |
746 | 0 | } else if (can_get_initial_creds(context, cred)) { |
747 | 0 | code = 0; |
748 | 0 | } |
749 | 0 | } |
750 | 0 | if (code) |
751 | 0 | goto error; |
752 | 0 | } else if (cred->name != NULL) { |
753 | | /* The caller specified a name but not a ccache; pick a cache. */ |
754 | 0 | code = get_cache_for_name(context, cred); |
755 | 0 | if (code) |
756 | 0 | goto error; |
757 | 0 | } |
758 | | |
759 | 0 | #ifndef USE_LEASH |
760 | | /* If we haven't picked a name, make sure we have or can get any creds, |
761 | | * unless we're using Leash and might be able to get them interactively. */ |
762 | 0 | if (cred->name == NULL && !can_get_initial_creds(context, cred)) { |
763 | 0 | code = krb5_cccol_have_content(context); |
764 | 0 | if (code) |
765 | 0 | goto error; |
766 | 0 | } |
767 | 0 | #endif |
768 | | |
769 | 0 | code = maybe_get_initial_cred(context, verify, cred); |
770 | 0 | if (code) |
771 | 0 | goto error; |
772 | | |
773 | 0 | *minor_status = 0; |
774 | 0 | return GSS_S_COMPLETE; |
775 | | |
776 | 0 | error: |
777 | 0 | *minor_status = code; |
778 | 0 | return GSS_S_NO_CRED; |
779 | 0 | } |
780 | | |
781 | | static OM_uint32 |
782 | | acquire_cred_context(krb5_context context, OM_uint32 *minor_status, |
783 | | gss_name_t desired_name, gss_buffer_t password, |
784 | | OM_uint32 time_req, gss_cred_usage_t cred_usage, |
785 | | krb5_ccache ccache, krb5_keytab client_keytab, |
786 | | krb5_keytab keytab, const char *rcname, |
787 | | const struct verify_params *verify, |
788 | | krb5_boolean iakerb, gss_cred_id_t *output_cred_handle, |
789 | | OM_uint32 *time_rec) |
790 | 41 | { |
791 | 41 | krb5_gss_cred_id_t cred = NULL; |
792 | 41 | krb5_gss_name_t name = (krb5_gss_name_t)desired_name; |
793 | 41 | OM_uint32 ret; |
794 | 41 | krb5_error_code code = 0; |
795 | | |
796 | | /* make sure all outputs are valid */ |
797 | 41 | *output_cred_handle = GSS_C_NO_CREDENTIAL; |
798 | 41 | if (time_rec) |
799 | 0 | *time_rec = 0; |
800 | | |
801 | | /* create the gss cred structure */ |
802 | 41 | cred = k5alloc(sizeof(krb5_gss_cred_id_rec), &code); |
803 | 41 | if (cred == NULL) |
804 | 0 | goto krb_error_out; |
805 | | |
806 | 41 | cred->usage = cred_usage; |
807 | 41 | cred->name = NULL; |
808 | 41 | cred->impersonator = NULL; |
809 | 41 | cred->iakerb_mech = iakerb; |
810 | 41 | cred->default_identity = (name == NULL); |
811 | 41 | #ifndef LEAN_CLIENT |
812 | 41 | cred->keytab = NULL; |
813 | 41 | #endif /* LEAN_CLIENT */ |
814 | 41 | cred->destroy_ccache = 0; |
815 | 41 | cred->suppress_ci_flags = 0; |
816 | 41 | cred->ccache = NULL; |
817 | | |
818 | 41 | code = k5_mutex_init(&cred->lock); |
819 | 41 | if (code) |
820 | 0 | goto krb_error_out; |
821 | | |
822 | 41 | switch (cred_usage) { |
823 | 0 | case GSS_C_INITIATE: |
824 | 41 | case GSS_C_ACCEPT: |
825 | 41 | case GSS_C_BOTH: |
826 | 41 | break; |
827 | 0 | default: |
828 | 0 | ret = GSS_S_FAILURE; |
829 | 0 | *minor_status = (OM_uint32) G_BAD_USAGE; |
830 | 0 | goto error_out; |
831 | 41 | } |
832 | | |
833 | 41 | if (name != NULL) { |
834 | 0 | code = kg_duplicate_name(context, name, &cred->name); |
835 | 0 | if (code) |
836 | 0 | goto krb_error_out; |
837 | 0 | } |
838 | | |
839 | 41 | #ifndef LEAN_CLIENT |
840 | | /* |
841 | | * If requested, acquire credentials for accepting. This will fill |
842 | | * in cred->name if desired_princ is specified. |
843 | | */ |
844 | 41 | if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) { |
845 | 41 | ret = acquire_accept_cred(context, minor_status, keytab, rcname, cred); |
846 | 41 | if (ret != GSS_S_COMPLETE) |
847 | 41 | goto error_out; |
848 | 41 | } |
849 | 0 | #endif /* LEAN_CLIENT */ |
850 | | |
851 | | /* |
852 | | * If requested, acquire credentials for initiation. This will fill |
853 | | * in cred->name if it wasn't set above. |
854 | | */ |
855 | 0 | if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) { |
856 | 0 | ret = acquire_init_cred(context, minor_status, ccache, password, |
857 | 0 | client_keytab, verify, cred); |
858 | 0 | if (ret != GSS_S_COMPLETE) |
859 | 0 | goto error_out; |
860 | 0 | } |
861 | | |
862 | 0 | assert(cred->default_identity || cred->name != NULL); |
863 | | |
864 | | /*** at this point, the cred structure has been completely created */ |
865 | |
|
866 | 0 | if (cred_usage == GSS_C_ACCEPT) { |
867 | 0 | if (time_rec) |
868 | 0 | *time_rec = GSS_C_INDEFINITE; |
869 | 0 | } else { |
870 | 0 | krb5_timestamp now; |
871 | |
|
872 | 0 | code = krb5_timeofday(context, &now); |
873 | 0 | if (code != 0) |
874 | 0 | goto krb_error_out; |
875 | | |
876 | 0 | if (time_rec) { |
877 | | /* Resolve cred now to determine the expiration time. */ |
878 | 0 | ret = kg_cred_resolve(minor_status, context, (gss_cred_id_t)cred, |
879 | 0 | GSS_C_NO_NAME); |
880 | 0 | if (GSS_ERROR(ret)) |
881 | 0 | goto error_out; |
882 | 0 | *time_rec = ts_interval(now, cred->expire); |
883 | 0 | k5_mutex_unlock(&cred->lock); |
884 | 0 | } |
885 | 0 | } |
886 | | |
887 | 0 | *minor_status = 0; |
888 | 0 | *output_cred_handle = (gss_cred_id_t) cred; |
889 | |
|
890 | 0 | return GSS_S_COMPLETE; |
891 | | |
892 | 0 | krb_error_out: |
893 | 0 | *minor_status = code; |
894 | 0 | ret = GSS_S_FAILURE; |
895 | |
|
896 | 41 | error_out: |
897 | 41 | if (cred != NULL) { |
898 | 41 | if (cred->ccache) { |
899 | 0 | if (cred->destroy_ccache) |
900 | 0 | krb5_cc_destroy(context, cred->ccache); |
901 | 0 | else |
902 | 0 | krb5_cc_close(context, cred->ccache); |
903 | 0 | } |
904 | 41 | if (cred->client_keytab) |
905 | 0 | krb5_kt_close(context, cred->client_keytab); |
906 | 41 | #ifndef LEAN_CLIENT |
907 | 41 | if (cred->keytab) |
908 | 0 | krb5_kt_close(context, cred->keytab); |
909 | 41 | #endif /* LEAN_CLIENT */ |
910 | 41 | if (cred->rcache) |
911 | 0 | k5_rc_close(context, cred->rcache); |
912 | 41 | if (cred->name) |
913 | 0 | kg_release_name(context, &cred->name); |
914 | 41 | krb5_free_principal(context, cred->impersonator); |
915 | 41 | zapfreestr(cred->password); |
916 | 41 | k5_mutex_destroy(&cred->lock); |
917 | 41 | xfree(cred); |
918 | 41 | } |
919 | 41 | save_error_info(*minor_status, context); |
920 | 41 | return ret; |
921 | 0 | } |
922 | | |
923 | | static OM_uint32 |
924 | | acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name, |
925 | | gss_buffer_t password, OM_uint32 time_req, |
926 | | gss_cred_usage_t cred_usage, krb5_ccache ccache, |
927 | | krb5_keytab keytab, krb5_boolean iakerb, |
928 | | gss_cred_id_t *output_cred_handle, OM_uint32 *time_rec) |
929 | 3 | { |
930 | 3 | krb5_context context = NULL; |
931 | 3 | krb5_error_code code = 0; |
932 | 3 | OM_uint32 ret; |
933 | | |
934 | 3 | code = gss_krb5int_initialize_library(); |
935 | 3 | if (code) { |
936 | 0 | *minor_status = code; |
937 | 0 | ret = GSS_S_FAILURE; |
938 | 0 | goto out; |
939 | 0 | } |
940 | | |
941 | 3 | code = krb5_gss_init_context(&context); |
942 | 3 | if (code) { |
943 | 0 | *minor_status = code; |
944 | 0 | ret = GSS_S_FAILURE; |
945 | 0 | goto out; |
946 | 0 | } |
947 | | |
948 | 3 | ret = acquire_cred_context(context, minor_status, desired_name, password, |
949 | 3 | time_req, cred_usage, ccache, NULL, keytab, |
950 | 3 | NULL, NULL, iakerb, output_cred_handle, |
951 | 3 | time_rec); |
952 | | |
953 | 3 | out: |
954 | 3 | krb5_free_context(context); |
955 | 3 | return ret; |
956 | 3 | } |
957 | | |
958 | | /* |
959 | | * Resolve the name and ccache for an initiator credential if it has not yet |
960 | | * been done. If specified, use the target name to pick an appropriate ccache |
961 | | * within the collection. Validates cred_handle and leaves it locked on |
962 | | * success. |
963 | | */ |
964 | | OM_uint32 |
965 | | kg_cred_resolve(OM_uint32 *minor_status, krb5_context context, |
966 | | gss_cred_id_t cred_handle, gss_name_t target_name) |
967 | 0 | { |
968 | 0 | OM_uint32 maj; |
969 | 0 | krb5_error_code code; |
970 | 0 | krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t)cred_handle; |
971 | 0 | krb5_gss_name_t tname = (krb5_gss_name_t)target_name; |
972 | 0 | krb5_principal client_princ; |
973 | |
|
974 | 0 | *minor_status = 0; |
975 | |
|
976 | 0 | maj = krb5_gss_validate_cred_1(minor_status, cred_handle, context); |
977 | 0 | if (maj != 0) |
978 | 0 | return maj; |
979 | 0 | k5_mutex_assert_locked(&cred->lock); |
980 | |
|
981 | 0 | if (cred->usage == GSS_C_ACCEPT || cred->name != NULL) |
982 | 0 | return GSS_S_COMPLETE; |
983 | | /* acquire_init_cred should have set both name and ccache, or neither. */ |
984 | 0 | assert(cred->ccache == NULL); |
985 | |
|
986 | 0 | if (tname != NULL) { |
987 | | /* Use the target name to select an existing ccache or a principal. */ |
988 | 0 | code = krb5_cc_select(context, tname->princ, &cred->ccache, |
989 | 0 | &client_princ); |
990 | 0 | if (code && code != KRB5_CC_NOTFOUND) |
991 | 0 | goto kerr; |
992 | 0 | if (client_princ != NULL) { |
993 | 0 | code = kg_init_name(context, client_princ, NULL, NULL, NULL, |
994 | 0 | KG_INIT_NAME_NO_COPY, &cred->name); |
995 | 0 | if (code) { |
996 | 0 | krb5_free_principal(context, client_princ); |
997 | 0 | goto kerr; |
998 | 0 | } |
999 | 0 | } |
1000 | 0 | if (cred->ccache != NULL) { |
1001 | 0 | code = scan_ccache(context, cred, FALSE); |
1002 | 0 | if (code) |
1003 | 0 | goto kerr; |
1004 | 0 | } |
1005 | 0 | } |
1006 | | |
1007 | | /* If we still haven't picked a client principal, try using an existing |
1008 | | * default ccache. (On Windows, this may acquire initial creds.) */ |
1009 | 0 | if (cred->name == NULL) { |
1010 | 0 | code = krb5int_cc_default(context, &cred->ccache); |
1011 | 0 | if (code) |
1012 | 0 | goto kerr; |
1013 | 0 | code = scan_ccache(context, cred, FALSE); |
1014 | 0 | if (code == KRB5_FCC_NOFILE) { |
1015 | | /* Default ccache doesn't exist; fall through to client keytab. */ |
1016 | 0 | krb5_cc_close(context, cred->ccache); |
1017 | 0 | cred->ccache = NULL; |
1018 | 0 | } else if (code) { |
1019 | 0 | goto kerr; |
1020 | 0 | } |
1021 | 0 | } |
1022 | | |
1023 | | /* If that didn't work, try getting a name from the client keytab. */ |
1024 | 0 | if (cred->name == NULL) { |
1025 | 0 | code = get_name_from_client_keytab(context, cred); |
1026 | 0 | if (code) { |
1027 | 0 | code = KG_EMPTY_CCACHE; |
1028 | 0 | goto kerr; |
1029 | 0 | } |
1030 | 0 | } |
1031 | | |
1032 | 0 | if (cred->name != NULL && cred->ccache == NULL) { |
1033 | | /* Pick a cache for the name we chose (from krb5_cc_select or from the |
1034 | | * client keytab). */ |
1035 | 0 | code = get_cache_for_name(context, cred); |
1036 | 0 | if (code) |
1037 | 0 | goto kerr; |
1038 | 0 | } |
1039 | | |
1040 | | /* Resolve name to ccache and possibly get initial credentials. */ |
1041 | 0 | code = maybe_get_initial_cred(context, NULL, cred); |
1042 | 0 | if (code) |
1043 | 0 | goto kerr; |
1044 | | |
1045 | 0 | return GSS_S_COMPLETE; |
1046 | | |
1047 | 0 | kerr: |
1048 | 0 | k5_mutex_unlock(&cred->lock); |
1049 | 0 | save_error_info(code, context); |
1050 | 0 | *minor_status = code; |
1051 | 0 | return GSS_S_NO_CRED; |
1052 | 0 | } |
1053 | | |
1054 | | OM_uint32 |
1055 | | gss_krb5int_set_cred_rcache(OM_uint32 *minor_status, |
1056 | | gss_cred_id_t *cred_handle, |
1057 | | const gss_OID desired_oid, |
1058 | | const gss_buffer_t value) |
1059 | 0 | { |
1060 | 0 | krb5_gss_cred_id_t cred; |
1061 | 0 | krb5_error_code code; |
1062 | 0 | krb5_context context; |
1063 | 0 | krb5_rcache rcache; |
1064 | |
|
1065 | 0 | assert(value->length == sizeof(rcache)); |
1066 | |
|
1067 | 0 | if (value->length != sizeof(rcache)) |
1068 | 0 | return GSS_S_FAILURE; |
1069 | | |
1070 | 0 | rcache = (krb5_rcache)value->value; |
1071 | |
|
1072 | 0 | cred = (krb5_gss_cred_id_t)*cred_handle; |
1073 | |
|
1074 | 0 | code = krb5_gss_init_context(&context); |
1075 | 0 | if (code) { |
1076 | 0 | *minor_status = code; |
1077 | 0 | return GSS_S_FAILURE; |
1078 | 0 | } |
1079 | 0 | if (cred->rcache != NULL) |
1080 | 0 | k5_rc_close(context, cred->rcache); |
1081 | |
|
1082 | 0 | cred->rcache = rcache; |
1083 | |
|
1084 | 0 | krb5_free_context(context); |
1085 | |
|
1086 | 0 | *minor_status = 0; |
1087 | 0 | return GSS_S_COMPLETE; |
1088 | 0 | } |
1089 | | |
1090 | | /* |
1091 | | * krb5 and IAKERB mech API functions follow. The mechglue always passes null |
1092 | | * desired_mechs and actual_mechs, so we ignore those parameters. |
1093 | | */ |
1094 | | |
1095 | | OM_uint32 KRB5_CALLCONV |
1096 | | krb5_gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name, |
1097 | | OM_uint32 time_req, gss_OID_set desired_mechs, |
1098 | | gss_cred_usage_t cred_usage, |
1099 | | gss_cred_id_t *output_cred_handle, |
1100 | | gss_OID_set *actual_mechs, OM_uint32 *time_rec) |
1101 | 3 | { |
1102 | 3 | return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage, |
1103 | 3 | NULL, NULL, FALSE, output_cred_handle, time_rec); |
1104 | 3 | } |
1105 | | |
1106 | | OM_uint32 KRB5_CALLCONV |
1107 | | iakerb_gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name, |
1108 | | OM_uint32 time_req, gss_OID_set desired_mechs, |
1109 | | gss_cred_usage_t cred_usage, |
1110 | | gss_cred_id_t *output_cred_handle, |
1111 | | gss_OID_set *actual_mechs, OM_uint32 *time_rec) |
1112 | 0 | { |
1113 | 0 | return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage, |
1114 | 0 | NULL, NULL, TRUE, output_cred_handle, time_rec); |
1115 | 0 | } |
1116 | | |
1117 | | OM_uint32 KRB5_CALLCONV |
1118 | | krb5_gss_acquire_cred_with_password(OM_uint32 *minor_status, |
1119 | | const gss_name_t desired_name, |
1120 | | const gss_buffer_t password, |
1121 | | OM_uint32 time_req, |
1122 | | const gss_OID_set desired_mechs, |
1123 | | int cred_usage, |
1124 | | gss_cred_id_t *output_cred_handle, |
1125 | | gss_OID_set *actual_mechs, |
1126 | | OM_uint32 *time_rec) |
1127 | 0 | { |
1128 | 0 | return acquire_cred(minor_status, desired_name, password, time_req, |
1129 | 0 | cred_usage, NULL, NULL, FALSE, output_cred_handle, |
1130 | 0 | time_rec); |
1131 | 0 | } |
1132 | | |
1133 | | OM_uint32 KRB5_CALLCONV |
1134 | | iakerb_gss_acquire_cred_with_password(OM_uint32 *minor_status, |
1135 | | const gss_name_t desired_name, |
1136 | | const gss_buffer_t password, |
1137 | | OM_uint32 time_req, |
1138 | | const gss_OID_set desired_mechs, |
1139 | | int cred_usage, |
1140 | | gss_cred_id_t *output_cred_handle, |
1141 | | gss_OID_set *actual_mechs, |
1142 | | OM_uint32 *time_rec) |
1143 | 0 | { |
1144 | 0 | return acquire_cred(minor_status, desired_name, password, time_req, |
1145 | 0 | cred_usage, NULL, NULL, TRUE, output_cred_handle, |
1146 | 0 | time_rec); |
1147 | 0 | } |
1148 | | |
1149 | | OM_uint32 |
1150 | | gss_krb5int_import_cred(OM_uint32 *minor_status, |
1151 | | gss_cred_id_t *cred_handle, |
1152 | | const gss_OID desired_oid, |
1153 | | const gss_buffer_t value) |
1154 | 0 | { |
1155 | 0 | struct krb5_gss_import_cred_req *req; |
1156 | 0 | krb5_gss_name_rec name; |
1157 | 0 | OM_uint32 time_rec; |
1158 | 0 | krb5_error_code code; |
1159 | 0 | gss_cred_usage_t usage; |
1160 | 0 | gss_name_t desired_name = GSS_C_NO_NAME; |
1161 | |
|
1162 | 0 | assert(value->length == sizeof(*req)); |
1163 | |
|
1164 | 0 | if (value->length != sizeof(*req)) |
1165 | 0 | return GSS_S_FAILURE; |
1166 | | |
1167 | 0 | req = (struct krb5_gss_import_cred_req *)value->value; |
1168 | |
|
1169 | 0 | if (req->id != NULL) { |
1170 | 0 | usage = (req->keytab != NULL) ? GSS_C_BOTH : GSS_C_INITIATE; |
1171 | 0 | } else if (req->keytab != NULL) { |
1172 | 0 | usage = GSS_C_ACCEPT; |
1173 | 0 | } else { |
1174 | 0 | *minor_status = EINVAL; |
1175 | 0 | return GSS_S_FAILURE; |
1176 | 0 | } |
1177 | | |
1178 | 0 | if (req->keytab_principal != NULL) { |
1179 | 0 | memset(&name, 0, sizeof(name)); |
1180 | 0 | code = k5_mutex_init(&name.lock); |
1181 | 0 | if (code != 0) { |
1182 | 0 | *minor_status = code; |
1183 | 0 | return GSS_S_FAILURE; |
1184 | 0 | } |
1185 | 0 | name.princ = req->keytab_principal; |
1186 | 0 | desired_name = (gss_name_t)&name; |
1187 | 0 | } |
1188 | | |
1189 | 0 | code = acquire_cred(minor_status, desired_name, NULL, GSS_C_INDEFINITE, |
1190 | 0 | usage, req->id, req->keytab, FALSE, cred_handle, |
1191 | 0 | &time_rec); |
1192 | 0 | if (req->keytab_principal != NULL) |
1193 | 0 | k5_mutex_destroy(&name.lock); |
1194 | 0 | return code; |
1195 | 0 | } |
1196 | | |
1197 | | static OM_uint32 |
1198 | | acquire_cred_from(OM_uint32 *minor_status, const gss_name_t desired_name, |
1199 | | OM_uint32 time_req, const gss_OID_set desired_mechs, |
1200 | | gss_cred_usage_t cred_usage, |
1201 | | gss_const_key_value_set_t cred_store, krb5_boolean iakerb, |
1202 | | gss_cred_id_t *output_cred_handle, |
1203 | | gss_OID_set *actual_mechs, OM_uint32 *time_rec) |
1204 | 38 | { |
1205 | 38 | krb5_context context = NULL; |
1206 | 38 | krb5_error_code code = 0; |
1207 | 38 | krb5_keytab client_keytab = NULL; |
1208 | 38 | krb5_keytab keytab = NULL; |
1209 | 38 | krb5_ccache ccache = NULL; |
1210 | 38 | krb5_principal verify_princ = NULL; |
1211 | 38 | const char *rcname, *value; |
1212 | 38 | struct verify_params vparams = { NULL }; |
1213 | 38 | const struct verify_params *verify = NULL; |
1214 | 38 | gss_buffer_desc pwbuf; |
1215 | 38 | gss_buffer_t password = NULL; |
1216 | 38 | OM_uint32 ret; |
1217 | | |
1218 | 38 | code = gss_krb5int_initialize_library(); |
1219 | 38 | if (code) { |
1220 | 0 | *minor_status = code; |
1221 | 0 | ret = GSS_S_FAILURE; |
1222 | 0 | goto out; |
1223 | 0 | } |
1224 | | |
1225 | 38 | code = krb5_gss_init_context(&context); |
1226 | 38 | if (code) { |
1227 | 0 | *minor_status = code; |
1228 | 0 | ret = GSS_S_FAILURE; |
1229 | 0 | goto out; |
1230 | 0 | } |
1231 | | |
1232 | 38 | ret = kg_value_from_cred_store(cred_store, KRB5_CS_CCACHE_URN, &value); |
1233 | 38 | if (GSS_ERROR(ret)) |
1234 | 0 | goto out; |
1235 | | |
1236 | 38 | if (value) { |
1237 | 0 | code = krb5_cc_resolve(context, value, &ccache); |
1238 | 0 | if (code != 0) { |
1239 | 0 | *minor_status = code; |
1240 | 0 | ret = GSS_S_NO_CRED; |
1241 | 0 | goto out; |
1242 | 0 | } |
1243 | 0 | } |
1244 | | |
1245 | 38 | ret = kg_value_from_cred_store(cred_store, KRB5_CS_CLI_KEYTAB_URN, &value); |
1246 | 38 | if (GSS_ERROR(ret)) |
1247 | 0 | goto out; |
1248 | | |
1249 | 38 | if (value) { |
1250 | 0 | code = krb5_kt_resolve(context, value, &client_keytab); |
1251 | 0 | if (code != 0) { |
1252 | 0 | *minor_status = code; |
1253 | 0 | ret = GSS_S_NO_CRED; |
1254 | 0 | goto out; |
1255 | 0 | } |
1256 | 0 | } |
1257 | | |
1258 | 38 | ret = kg_value_from_cred_store(cred_store, KRB5_CS_KEYTAB_URN, &value); |
1259 | 38 | if (GSS_ERROR(ret)) |
1260 | 0 | goto out; |
1261 | | |
1262 | 38 | if (value) { |
1263 | 0 | code = krb5_kt_resolve(context, value, &keytab); |
1264 | 0 | if (code != 0) { |
1265 | 0 | *minor_status = code; |
1266 | 0 | ret = GSS_S_NO_CRED; |
1267 | 0 | goto out; |
1268 | 0 | } |
1269 | 0 | } |
1270 | | |
1271 | 38 | ret = kg_value_from_cred_store(cred_store, KRB5_CS_RCACHE_URN, &rcname); |
1272 | 38 | if (GSS_ERROR(ret)) |
1273 | 0 | goto out; |
1274 | | |
1275 | 38 | ret = kg_value_from_cred_store(cred_store, KRB5_CS_PASSWORD_URN, &value); |
1276 | 38 | if (GSS_ERROR(ret)) |
1277 | 0 | goto out; |
1278 | | |
1279 | 38 | if (value) { |
1280 | | /* We must be acquiring an initiator cred with an explicit name. A |
1281 | | * password is mutually exclusive with a client keytab or ccache. */ |
1282 | 0 | if (desired_name == GSS_C_NO_NAME) { |
1283 | 0 | ret = GSS_S_BAD_NAME; |
1284 | 0 | goto out; |
1285 | 0 | } |
1286 | 0 | if (cred_usage == GSS_C_ACCEPT || ccache != NULL || |
1287 | 0 | client_keytab != NULL) { |
1288 | 0 | *minor_status = (OM_uint32)G_BAD_USAGE; |
1289 | 0 | ret = GSS_S_FAILURE; |
1290 | 0 | goto out; |
1291 | 0 | } |
1292 | 0 | pwbuf.length = strlen(value); |
1293 | 0 | pwbuf.value = (void *)value; |
1294 | 0 | password = &pwbuf; |
1295 | 0 | } |
1296 | | |
1297 | 38 | ret = kg_value_from_cred_store(cred_store, KRB5_CS_VERIFY_URN, &value); |
1298 | 38 | if (GSS_ERROR(ret)) |
1299 | 0 | goto out; |
1300 | 38 | if (value != NULL) { |
1301 | 0 | if (iakerb || password == NULL) { |
1302 | | /* Only valid if acquiring cred with password, and not supported |
1303 | | * with IAKERB. */ |
1304 | 0 | *minor_status = G_BAD_USAGE; |
1305 | 0 | ret = GSS_S_FAILURE; |
1306 | 0 | goto out; |
1307 | 0 | } |
1308 | 0 | if (*value != '\0') { |
1309 | 0 | code = krb5_parse_name(context, value, &verify_princ); |
1310 | 0 | if (code != 0) { |
1311 | 0 | *minor_status = code; |
1312 | 0 | ret = GSS_S_FAILURE; |
1313 | 0 | goto out; |
1314 | 0 | } |
1315 | 0 | } |
1316 | 0 | vparams.princ = verify_princ; |
1317 | 0 | vparams.keytab = keytab; |
1318 | 0 | verify = &vparams; |
1319 | 0 | } |
1320 | 38 | ret = acquire_cred_context(context, minor_status, desired_name, password, |
1321 | 38 | time_req, cred_usage, ccache, client_keytab, |
1322 | 38 | keytab, rcname, verify, iakerb, |
1323 | 38 | output_cred_handle, time_rec); |
1324 | | |
1325 | 38 | out: |
1326 | 38 | if (ccache != NULL) |
1327 | 0 | krb5_cc_close(context, ccache); |
1328 | 38 | if (client_keytab != NULL) |
1329 | 0 | krb5_kt_close(context, client_keytab); |
1330 | 38 | if (keytab != NULL) |
1331 | 0 | krb5_kt_close(context, keytab); |
1332 | 38 | krb5_free_principal(context, verify_princ); |
1333 | 38 | krb5_free_context(context); |
1334 | 38 | return ret; |
1335 | 38 | } |
1336 | | |
1337 | | OM_uint32 KRB5_CALLCONV |
1338 | | krb5_gss_acquire_cred_from(OM_uint32 *minor_status, |
1339 | | const gss_name_t desired_name, |
1340 | | OM_uint32 time_req, |
1341 | | const gss_OID_set desired_mechs, |
1342 | | gss_cred_usage_t cred_usage, |
1343 | | gss_const_key_value_set_t cred_store, |
1344 | | gss_cred_id_t *output_cred_handle, |
1345 | | gss_OID_set *actual_mechs, |
1346 | | OM_uint32 *time_rec) |
1347 | 38 | { |
1348 | 38 | return acquire_cred_from(minor_status, desired_name, time_req, |
1349 | 38 | desired_mechs, cred_usage, cred_store, |
1350 | 38 | FALSE, output_cred_handle, actual_mechs, |
1351 | 38 | time_rec); |
1352 | 38 | } |
1353 | | |
1354 | | OM_uint32 KRB5_CALLCONV |
1355 | | iakerb_gss_acquire_cred_from(OM_uint32 *minor_status, |
1356 | | const gss_name_t desired_name, |
1357 | | OM_uint32 time_req, |
1358 | | const gss_OID_set desired_mechs, |
1359 | | gss_cred_usage_t cred_usage, |
1360 | | gss_const_key_value_set_t cred_store, |
1361 | | gss_cred_id_t *output_cred_handle, |
1362 | | gss_OID_set *actual_mechs, |
1363 | | OM_uint32 *time_rec) |
1364 | 0 | { |
1365 | 0 | return acquire_cred_from(minor_status, desired_name, time_req, |
1366 | 0 | desired_mechs, cred_usage, cred_store, |
1367 | 0 | TRUE, output_cred_handle, actual_mechs, |
1368 | 0 | time_rec); |
1369 | 0 | } |