/src/samba/auth/credentials/credentials_krb5.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | Handle user credentials (as regards krb5) |
5 | | |
6 | | Copyright (C) Jelmer Vernooij 2005 |
7 | | Copyright (C) Tim Potter 2001 |
8 | | Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005 |
9 | | |
10 | | This program is free software; you can redistribute it and/or modify |
11 | | it under the terms of the GNU General Public License as published by |
12 | | the Free Software Foundation; either version 3 of the License, or |
13 | | (at your option) any later version. |
14 | | |
15 | | This program is distributed in the hope that it will be useful, |
16 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | GNU General Public License for more details. |
19 | | |
20 | | You should have received a copy of the GNU General Public License |
21 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
22 | | */ |
23 | | |
24 | | #include "includes.h" |
25 | | #include "system/kerberos.h" |
26 | | #include "system/gssapi.h" |
27 | | #include "auth/kerberos/kerberos.h" |
28 | | #include "auth/credentials/credentials.h" |
29 | | #include "auth/credentials/credentials_internal.h" |
30 | | #include "auth/credentials/credentials_krb5.h" |
31 | | #include "auth/kerberos/kerberos_credentials.h" |
32 | | #include "auth/kerberos/kerberos_srv_keytab.h" |
33 | | #include "auth/kerberos/kerberos_util.h" |
34 | | #include "auth/kerberos/pac_utils.h" |
35 | | #include "param/param.h" |
36 | | #include "../libds/common/flags.h" |
37 | | |
38 | | #undef DBGC_CLASS |
39 | 0 | #define DBGC_CLASS DBGC_AUTH |
40 | | |
41 | | #undef strncasecmp |
42 | | |
43 | | static void cli_credentials_invalidate_client_gss_creds( |
44 | | struct cli_credentials *cred, |
45 | | enum credentials_obtained obtained); |
46 | | |
47 | | /* Free a memory ccache */ |
48 | | static int free_mccache(struct ccache_container *ccc) |
49 | 0 | { |
50 | 0 | if (ccc->ccache != NULL) { |
51 | 0 | krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, |
52 | 0 | ccc->ccache); |
53 | 0 | ccc->ccache = NULL; |
54 | 0 | } |
55 | |
|
56 | 0 | return 0; |
57 | 0 | } |
58 | | |
59 | | /* Free a disk-based ccache */ |
60 | | static int free_dccache(struct ccache_container *ccc) |
61 | 0 | { |
62 | 0 | if (ccc->ccache != NULL) { |
63 | 0 | krb5_cc_close(ccc->smb_krb5_context->krb5_context, |
64 | 0 | ccc->ccache); |
65 | 0 | ccc->ccache = NULL; |
66 | 0 | } |
67 | |
|
68 | 0 | return 0; |
69 | 0 | } |
70 | | |
71 | | static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat, |
72 | | gss_cred_id_t cred, |
73 | | struct ccache_container *ccc) |
74 | 0 | { |
75 | | #ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */ |
76 | | krb5_context context = ccc->smb_krb5_context->krb5_context; |
77 | | krb5_ccache dummy_ccache = NULL; |
78 | | krb5_creds creds = {0}; |
79 | | krb5_cc_cursor cursor = NULL; |
80 | | krb5_principal princ = NULL; |
81 | | krb5_error_code code; |
82 | | uint32_t maj_stat = GSS_S_FAILURE; |
83 | | |
84 | | /* |
85 | | * Create a dummy ccache, so we can iterate over the credentials |
86 | | * and find the default principal for the ccache we want to |
87 | | * copy. The new ccache needs to be initialized with this |
88 | | * principal. |
89 | | */ |
90 | | code = smb_krb5_cc_new_unique_memory(context, NULL, NULL, &dummy_ccache); |
91 | | if (code != 0) { |
92 | | *min_stat = code; |
93 | | return GSS_S_FAILURE; |
94 | | } |
95 | | |
96 | | /* |
97 | | * We do not need set a default principal on the temporary dummy |
98 | | * ccache, as we do consume it at all in this function. |
99 | | */ |
100 | | maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache); |
101 | | if (maj_stat != 0) { |
102 | | krb5_cc_destroy(context, dummy_ccache); |
103 | | return maj_stat; |
104 | | } |
105 | | |
106 | | code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor); |
107 | | if (code != 0) { |
108 | | krb5_cc_destroy(context, dummy_ccache); |
109 | | *min_stat = EINVAL; |
110 | | return GSS_S_FAILURE; |
111 | | } |
112 | | |
113 | | code = krb5_cc_next_cred(context, |
114 | | dummy_ccache, |
115 | | &cursor, |
116 | | &creds); |
117 | | if (code != 0) { |
118 | | krb5_cc_destroy(context, dummy_ccache); |
119 | | *min_stat = EINVAL; |
120 | | return GSS_S_FAILURE; |
121 | | } |
122 | | |
123 | | do { |
124 | | if (creds.ticket_flags & TKT_FLG_PRE_AUTH) { |
125 | | krb5_data *tgs; |
126 | | |
127 | | tgs = krb5_princ_component(context, |
128 | | creds.server, |
129 | | 0); |
130 | | if (tgs != NULL && tgs->length >= 1) { |
131 | | int cmp; |
132 | | |
133 | | cmp = memcmp(tgs->data, |
134 | | KRB5_TGS_NAME, |
135 | | tgs->length); |
136 | | if (cmp == 0 && creds.client != NULL) { |
137 | | princ = creds.client; |
138 | | code = KRB5_CC_END; |
139 | | break; |
140 | | } |
141 | | } |
142 | | } |
143 | | |
144 | | krb5_free_cred_contents(context, &creds); |
145 | | |
146 | | code = krb5_cc_next_cred(context, |
147 | | dummy_ccache, |
148 | | &cursor, |
149 | | &creds); |
150 | | } while (code == 0); |
151 | | |
152 | | if (code == KRB5_CC_END) { |
153 | | krb5_cc_end_seq_get(context, dummy_ccache, &cursor); |
154 | | code = 0; |
155 | | } |
156 | | krb5_cc_destroy(context, dummy_ccache); |
157 | | |
158 | | if (code != 0 || princ == NULL) { |
159 | | krb5_free_cred_contents(context, &creds); |
160 | | *min_stat = EINVAL; |
161 | | return GSS_S_FAILURE; |
162 | | } |
163 | | |
164 | | /* |
165 | | * Set the default principal for the cache we copy |
166 | | * into. This is needed to be able that other calls |
167 | | * can read it with e.g. gss_acquire_cred() or |
168 | | * krb5_cc_get_principal(). |
169 | | */ |
170 | | code = krb5_cc_initialize(context, ccc->ccache, princ); |
171 | | if (code != 0) { |
172 | | krb5_free_cred_contents(context, &creds); |
173 | | *min_stat = EINVAL; |
174 | | return GSS_S_FAILURE; |
175 | | } |
176 | | krb5_free_cred_contents(context, &creds); |
177 | | |
178 | | #endif /* SAMBA4_USES_HEIMDAL */ |
179 | |
|
180 | 0 | return gss_krb5_copy_ccache(min_stat, |
181 | 0 | cred, |
182 | 0 | ccc->ccache); |
183 | 0 | } |
184 | | |
185 | | _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred, |
186 | | struct loadparm_context *lp_ctx, |
187 | | struct smb_krb5_context **smb_krb5_context) |
188 | 0 | { |
189 | 0 | int ret; |
190 | 0 | if (cred->smb_krb5_context) { |
191 | 0 | *smb_krb5_context = cred->smb_krb5_context; |
192 | 0 | return 0; |
193 | 0 | } |
194 | | |
195 | 0 | ret = smb_krb5_init_context(cred, lp_ctx, |
196 | 0 | &cred->smb_krb5_context); |
197 | 0 | if (ret) { |
198 | 0 | cred->smb_krb5_context = NULL; |
199 | 0 | return ret; |
200 | 0 | } |
201 | 0 | *smb_krb5_context = cred->smb_krb5_context; |
202 | 0 | return 0; |
203 | 0 | } |
204 | | |
205 | | /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(), |
206 | | * otherwise we may still have references to the old smb_krb5_context in a credential cache etc |
207 | | */ |
208 | | _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred, |
209 | | struct smb_krb5_context *smb_krb5_context) |
210 | 0 | { |
211 | 0 | if (smb_krb5_context == NULL) { |
212 | 0 | talloc_unlink(cred, cred->smb_krb5_context); |
213 | 0 | cred->smb_krb5_context = NULL; |
214 | 0 | return NT_STATUS_OK; |
215 | 0 | } |
216 | | |
217 | 0 | if (!talloc_reference(cred, smb_krb5_context)) { |
218 | 0 | return NT_STATUS_NO_MEMORY; |
219 | 0 | } |
220 | 0 | cred->smb_krb5_context = smb_krb5_context; |
221 | 0 | return NT_STATUS_OK; |
222 | 0 | } |
223 | | |
224 | | static int cli_credentials_set_from_ccache(struct cli_credentials *cred, |
225 | | struct ccache_container *ccache, |
226 | | enum credentials_obtained obtained, |
227 | | const char **error_string) |
228 | 0 | { |
229 | 0 | bool ok; |
230 | 0 | char *realm; |
231 | 0 | krb5_principal princ; |
232 | 0 | krb5_error_code ret; |
233 | 0 | char *name; |
234 | |
|
235 | 0 | if (cred->ccache_obtained > obtained) { |
236 | 0 | return 0; |
237 | 0 | } |
238 | | |
239 | 0 | ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context, |
240 | 0 | ccache->ccache, &princ); |
241 | |
|
242 | 0 | if (ret) { |
243 | 0 | (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n", |
244 | 0 | smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context, |
245 | 0 | ret, cred)); |
246 | 0 | return ret; |
247 | 0 | } |
248 | | |
249 | 0 | ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name); |
250 | 0 | if (ret) { |
251 | 0 | (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n", |
252 | 0 | smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context, |
253 | 0 | ret, cred)); |
254 | 0 | krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ); |
255 | 0 | return ret; |
256 | 0 | } |
257 | | |
258 | 0 | ok = cli_credentials_set_principal(cred, name, obtained); |
259 | 0 | krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name); |
260 | 0 | if (!ok) { |
261 | 0 | krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ); |
262 | 0 | return ENOMEM; |
263 | 0 | } |
264 | | |
265 | 0 | realm = smb_krb5_principal_get_realm( |
266 | 0 | cred, ccache->smb_krb5_context->krb5_context, princ); |
267 | 0 | krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ); |
268 | 0 | if (realm == NULL) { |
269 | 0 | return ENOMEM; |
270 | 0 | } |
271 | 0 | ok = cli_credentials_set_realm(cred, realm, obtained); |
272 | 0 | TALLOC_FREE(realm); |
273 | 0 | if (!ok) { |
274 | 0 | return ENOMEM; |
275 | 0 | } |
276 | | |
277 | | /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */ |
278 | 0 | cred->ccache_obtained = obtained; |
279 | |
|
280 | 0 | return 0; |
281 | 0 | } |
282 | | |
283 | | _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred, |
284 | | struct loadparm_context *lp_ctx, |
285 | | const char *name, |
286 | | enum credentials_obtained obtained, |
287 | | const char **error_string) |
288 | 0 | { |
289 | 0 | krb5_error_code ret; |
290 | 0 | krb5_principal princ; |
291 | 0 | struct ccache_container *ccc; |
292 | 0 | if (cred->ccache_obtained > obtained) { |
293 | 0 | return 0; |
294 | 0 | } |
295 | | |
296 | 0 | ccc = talloc(cred, struct ccache_container); |
297 | 0 | if (!ccc) { |
298 | 0 | (*error_string) = error_message(ENOMEM); |
299 | 0 | return ENOMEM; |
300 | 0 | } |
301 | | |
302 | 0 | ret = cli_credentials_get_krb5_context(cred, lp_ctx, |
303 | 0 | &ccc->smb_krb5_context); |
304 | 0 | if (ret) { |
305 | 0 | (*error_string) = error_message(ret); |
306 | 0 | talloc_free(ccc); |
307 | 0 | return ret; |
308 | 0 | } |
309 | 0 | if (!talloc_reference(ccc, ccc->smb_krb5_context)) { |
310 | 0 | talloc_free(ccc); |
311 | 0 | (*error_string) = error_message(ENOMEM); |
312 | 0 | return ENOMEM; |
313 | 0 | } |
314 | | |
315 | 0 | if (name) { |
316 | 0 | ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache); |
317 | 0 | if (ret) { |
318 | 0 | (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n", |
319 | 0 | name, |
320 | 0 | smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, |
321 | 0 | ret, ccc)); |
322 | 0 | talloc_free(ccc); |
323 | 0 | return ret; |
324 | 0 | } |
325 | 0 | } else { |
326 | | /* |
327 | | * This is where the caller really wants to use |
328 | | * the default krb5 ccache. |
329 | | */ |
330 | 0 | ret = smb_force_krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache); |
331 | 0 | if (ret) { |
332 | 0 | (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n", |
333 | 0 | smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, |
334 | 0 | ret, ccc)); |
335 | 0 | talloc_free(ccc); |
336 | 0 | return ret; |
337 | 0 | } |
338 | 0 | } |
339 | | |
340 | 0 | talloc_set_destructor(ccc, free_dccache); |
341 | |
|
342 | 0 | ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ); |
343 | |
|
344 | 0 | if (ret == 0) { |
345 | 0 | krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ); |
346 | 0 | ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string); |
347 | |
|
348 | 0 | if (ret) { |
349 | 0 | (*error_string) = error_message(ret); |
350 | 0 | TALLOC_FREE(ccc); |
351 | 0 | return ret; |
352 | 0 | } |
353 | 0 | } |
354 | | |
355 | 0 | cred->ccache = ccc; |
356 | 0 | cred->ccache_obtained = obtained; |
357 | |
|
358 | 0 | cli_credentials_invalidate_client_gss_creds( |
359 | 0 | cred, cred->ccache_obtained); |
360 | |
|
361 | 0 | return 0; |
362 | 0 | } |
363 | | |
364 | | #ifndef SAMBA4_USES_HEIMDAL |
365 | | /* |
366 | | * This function is a workaround for old MIT Kerberos versions which did not |
367 | | * implement the krb5_cc_remove_cred function. It creates a temporary |
368 | | * credentials cache to copy the credentials in the current cache |
369 | | * except the one we want to remove and then overwrites the contents of the |
370 | | * current cache with the temporary copy. |
371 | | */ |
372 | | static krb5_error_code krb5_cc_remove_cred_wrap(struct ccache_container *ccc, |
373 | | krb5_creds *creds) |
374 | | { |
375 | | krb5_ccache dummy_ccache = NULL; |
376 | | krb5_creds cached_creds = {0}; |
377 | | krb5_cc_cursor cursor = NULL; |
378 | | krb5_error_code code; |
379 | | |
380 | | code = smb_krb5_cc_new_unique_memory(ccc->smb_krb5_context->krb5_context, |
381 | | NULL, NULL, |
382 | | &dummy_ccache); |
383 | | if (code != 0) { |
384 | | DBG_ERR("krb5_cc_resolve failed: %s\n", |
385 | | smb_get_krb5_error_message( |
386 | | ccc->smb_krb5_context->krb5_context, |
387 | | code, ccc)); |
388 | | return code; |
389 | | } |
390 | | |
391 | | code = krb5_cc_start_seq_get(ccc->smb_krb5_context->krb5_context, |
392 | | ccc->ccache, |
393 | | &cursor); |
394 | | if (code != 0) { |
395 | | krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, |
396 | | dummy_ccache); |
397 | | |
398 | | DBG_ERR("krb5_cc_start_seq_get failed: %s\n", |
399 | | smb_get_krb5_error_message( |
400 | | ccc->smb_krb5_context->krb5_context, |
401 | | code, ccc)); |
402 | | return code; |
403 | | } |
404 | | |
405 | | while ((code = krb5_cc_next_cred(ccc->smb_krb5_context->krb5_context, |
406 | | ccc->ccache, |
407 | | &cursor, |
408 | | &cached_creds)) == 0) { |
409 | | /* If the principal matches skip it and do not copy to the |
410 | | * temporary cache as this is the one we want to remove */ |
411 | | if (krb5_principal_compare_flags( |
412 | | ccc->smb_krb5_context->krb5_context, |
413 | | creds->server, |
414 | | cached_creds.server, |
415 | | 0)) { |
416 | | continue; |
417 | | } |
418 | | |
419 | | code = krb5_cc_store_cred( |
420 | | ccc->smb_krb5_context->krb5_context, |
421 | | dummy_ccache, |
422 | | &cached_creds); |
423 | | if (code != 0) { |
424 | | krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, |
425 | | dummy_ccache); |
426 | | DBG_ERR("krb5_cc_store_cred failed: %s\n", |
427 | | smb_get_krb5_error_message( |
428 | | ccc->smb_krb5_context->krb5_context, |
429 | | code, ccc)); |
430 | | return code; |
431 | | } |
432 | | } |
433 | | |
434 | | if (code == KRB5_CC_END) { |
435 | | krb5_cc_end_seq_get(ccc->smb_krb5_context->krb5_context, |
436 | | dummy_ccache, |
437 | | &cursor); |
438 | | code = 0; |
439 | | } |
440 | | |
441 | | if (code != 0) { |
442 | | krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, |
443 | | dummy_ccache); |
444 | | DBG_ERR("krb5_cc_next_cred failed: %s\n", |
445 | | smb_get_krb5_error_message( |
446 | | ccc->smb_krb5_context->krb5_context, |
447 | | code, ccc)); |
448 | | return code; |
449 | | } |
450 | | |
451 | | code = krb5_cc_initialize(ccc->smb_krb5_context->krb5_context, |
452 | | ccc->ccache, |
453 | | creds->client); |
454 | | if (code != 0) { |
455 | | krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, |
456 | | dummy_ccache); |
457 | | DBG_ERR("krb5_cc_initialize failed: %s\n", |
458 | | smb_get_krb5_error_message( |
459 | | ccc->smb_krb5_context->krb5_context, |
460 | | code, ccc)); |
461 | | return code; |
462 | | } |
463 | | |
464 | | code = krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context, |
465 | | dummy_ccache, |
466 | | ccc->ccache); |
467 | | if (code != 0) { |
468 | | krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, |
469 | | dummy_ccache); |
470 | | DBG_ERR("krb5_cc_copy_creds failed: %s\n", |
471 | | smb_get_krb5_error_message( |
472 | | ccc->smb_krb5_context->krb5_context, |
473 | | code, ccc)); |
474 | | return code; |
475 | | } |
476 | | |
477 | | code = krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, |
478 | | dummy_ccache); |
479 | | if (code != 0) { |
480 | | DBG_ERR("krb5_cc_destroy failed: %s\n", |
481 | | smb_get_krb5_error_message( |
482 | | ccc->smb_krb5_context->krb5_context, |
483 | | code, ccc)); |
484 | | return code; |
485 | | } |
486 | | |
487 | | return code; |
488 | | } |
489 | | #endif |
490 | | |
491 | | /* |
492 | | * Indicate that we failed to log in to this service/host with these |
493 | | * credentials. The caller passes an unsigned int which they |
494 | | * initialise to the number of times they would like to retry. |
495 | | * |
496 | | * This method is used to support re-trying with freshly fetched |
497 | | * credentials in case a server is rebuilt while clients have |
498 | | * non-expired tickets. When the client code gets a logon failure they |
499 | | * throw away the existing credentials for the server and retry. |
500 | | */ |
501 | | _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred, |
502 | | const char *principal, |
503 | | unsigned int *count) |
504 | 0 | { |
505 | 0 | struct ccache_container *ccc; |
506 | 0 | krb5_creds creds, creds2; |
507 | 0 | int ret; |
508 | |
|
509 | 0 | if (principal == NULL) { |
510 | | /* no way to delete if we don't know the principal */ |
511 | 0 | return false; |
512 | 0 | } |
513 | | |
514 | 0 | ccc = cred->ccache; |
515 | 0 | if (ccc == NULL) { |
516 | | /* not a kerberos connection */ |
517 | 0 | return false; |
518 | 0 | } |
519 | | |
520 | 0 | if (*count > 0) { |
521 | | /* We have already tried discarding the credentials */ |
522 | 0 | return false; |
523 | 0 | } |
524 | 0 | (*count)++; |
525 | |
|
526 | 0 | ZERO_STRUCT(creds); |
527 | 0 | ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server); |
528 | 0 | if (ret != 0) { |
529 | 0 | return false; |
530 | 0 | } |
531 | | |
532 | | /* MIT kerberos requires creds.client to match against cached |
533 | | * credentials */ |
534 | 0 | ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, |
535 | 0 | ccc->ccache, |
536 | 0 | &creds.client); |
537 | 0 | if (ret != 0) { |
538 | 0 | krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, |
539 | 0 | &creds); |
540 | 0 | DBG_ERR("krb5_cc_get_principal failed: %s\n", |
541 | 0 | smb_get_krb5_error_message( |
542 | 0 | ccc->smb_krb5_context->krb5_context, |
543 | 0 | ret, ccc)); |
544 | 0 | return false; |
545 | 0 | } |
546 | | |
547 | 0 | ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2); |
548 | 0 | if (ret != 0) { |
549 | | /* don't retry - we didn't find these credentials to remove */ |
550 | 0 | krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds); |
551 | 0 | return false; |
552 | 0 | } |
553 | | |
554 | 0 | ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds); |
555 | | #ifndef SAMBA4_USES_HEIMDAL |
556 | | if (ret == KRB5_CC_NOSUPP) { |
557 | | /* Old MIT kerberos versions did not implement |
558 | | * krb5_cc_remove_cred */ |
559 | | ret = krb5_cc_remove_cred_wrap(ccc, &creds); |
560 | | } |
561 | | #endif |
562 | 0 | krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds); |
563 | 0 | krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2); |
564 | 0 | if (ret != 0) { |
565 | | /* don't retry - we didn't find these credentials to |
566 | | * remove. Note that with the current backend this |
567 | | * never happens, as it always returns 0 even if the |
568 | | * creds don't exist, which is why we do a separate |
569 | | * krb5_cc_retrieve_cred() above. |
570 | | */ |
571 | 0 | DBG_ERR("krb5_cc_remove_cred failed: %s\n", |
572 | 0 | smb_get_krb5_error_message( |
573 | 0 | ccc->smb_krb5_context->krb5_context, |
574 | 0 | ret, ccc)); |
575 | 0 | return false; |
576 | 0 | } |
577 | 0 | return true; |
578 | 0 | } |
579 | | |
580 | | |
581 | | static int cli_credentials_new_ccache(struct cli_credentials *cred, |
582 | | struct loadparm_context *lp_ctx, |
583 | | char *given_ccache_name, |
584 | | struct ccache_container **_ccc, |
585 | | const char **error_string) |
586 | 0 | { |
587 | 0 | char *ccache_name = given_ccache_name; |
588 | 0 | bool must_free_cc_name = false; |
589 | 0 | krb5_error_code ret; |
590 | 0 | struct ccache_container *ccc = talloc(cred, struct ccache_container); |
591 | 0 | if (!ccc) { |
592 | 0 | return ENOMEM; |
593 | 0 | } |
594 | | |
595 | 0 | ret = cli_credentials_get_krb5_context(cred, lp_ctx, |
596 | 0 | &ccc->smb_krb5_context); |
597 | 0 | if (ret) { |
598 | 0 | talloc_free(ccc); |
599 | 0 | (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s", |
600 | 0 | error_message(ret)); |
601 | 0 | return ret; |
602 | 0 | } |
603 | 0 | if (!talloc_reference(ccc, ccc->smb_krb5_context)) { |
604 | 0 | talloc_free(ccc); |
605 | 0 | (*error_string) = strerror(ENOMEM); |
606 | 0 | return ENOMEM; |
607 | 0 | } |
608 | | |
609 | 0 | if (!ccache_name) { |
610 | 0 | if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) { |
611 | 0 | ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p", |
612 | 0 | (unsigned int)getpid(), ccc); |
613 | 0 | if (ccache_name == NULL) { |
614 | 0 | talloc_free(ccc); |
615 | 0 | (*error_string) = strerror(ENOMEM); |
616 | 0 | return ENOMEM; |
617 | 0 | } |
618 | 0 | must_free_cc_name = true; |
619 | 0 | } |
620 | 0 | } |
621 | | |
622 | 0 | if (ccache_name != NULL) { |
623 | 0 | ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, |
624 | 0 | &ccc->ccache); |
625 | 0 | } else { |
626 | 0 | ret = smb_krb5_cc_new_unique_memory(ccc->smb_krb5_context->krb5_context, |
627 | 0 | ccc, &ccache_name, |
628 | 0 | &ccc->ccache); |
629 | 0 | must_free_cc_name = true; |
630 | 0 | } |
631 | 0 | if (ret) { |
632 | 0 | (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n", |
633 | 0 | ccache_name, |
634 | 0 | smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, |
635 | 0 | ret, ccc)); |
636 | 0 | talloc_free(ccc); |
637 | 0 | return ret; |
638 | 0 | } |
639 | | |
640 | 0 | if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) { |
641 | 0 | talloc_set_destructor(ccc, free_mccache); |
642 | 0 | } else { |
643 | 0 | talloc_set_destructor(ccc, free_dccache); |
644 | 0 | } |
645 | |
|
646 | 0 | if (must_free_cc_name) { |
647 | 0 | talloc_free(ccache_name); |
648 | 0 | } |
649 | |
|
650 | 0 | *_ccc = ccc; |
651 | |
|
652 | 0 | return 0; |
653 | 0 | } |
654 | | |
655 | | _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred, |
656 | | struct tevent_context *event_ctx, |
657 | | struct loadparm_context *lp_ctx, |
658 | | char *ccache_name, |
659 | | struct ccache_container **ccc, |
660 | | const char **error_string) |
661 | 0 | { |
662 | 0 | krb5_error_code ret; |
663 | 0 | enum credentials_obtained obtained; |
664 | |
|
665 | 0 | if (cred->machine_account_pending) { |
666 | 0 | cli_credentials_set_machine_account(cred, lp_ctx); |
667 | 0 | } |
668 | |
|
669 | 0 | if (cred->ccache_obtained >= cred->ccache_threshold && |
670 | 0 | cred->ccache_obtained > CRED_UNINITIALISED) { |
671 | 0 | time_t lifetime; |
672 | 0 | enum credentials_obtained pass_obtained = |
673 | 0 | cli_credentials_get_password_obtained(cred); |
674 | 0 | bool kinit_required = false; |
675 | 0 | ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context, |
676 | 0 | cred->ccache->ccache, &lifetime); |
677 | 0 | if (ret == KRB5_CC_END || ret == ENOENT) { |
678 | 0 | kinit_required = true; |
679 | 0 | } else if (ret == 0) { |
680 | 0 | if (lifetime == 0) { |
681 | 0 | kinit_required = true; |
682 | 0 | } else if (lifetime < 300) { |
683 | 0 | kinit_required = true; |
684 | 0 | } |
685 | 0 | } else { |
686 | 0 | kinit_required = true; |
687 | 0 | } |
688 | |
|
689 | 0 | if (!kinit_required) { |
690 | 0 | *ccc = cred->ccache; |
691 | 0 | return 0; |
692 | 0 | } |
693 | 0 | if (pass_obtained < cred->ccache_obtained) { |
694 | 0 | (*error_string) = "The credential cache is invalid"; |
695 | 0 | return EINVAL; |
696 | 0 | } |
697 | 0 | } |
698 | 0 | if (cli_credentials_is_anonymous(cred)) { |
699 | 0 | (*error_string) = "Cannot get anonymous kerberos credentials"; |
700 | 0 | return EINVAL; |
701 | 0 | } |
702 | | |
703 | 0 | ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string); |
704 | 0 | if (ret) { |
705 | 0 | return ret; |
706 | 0 | } |
707 | | |
708 | 0 | ret = kinit_to_ccache(cred, |
709 | 0 | cred, |
710 | 0 | (*ccc)->smb_krb5_context, |
711 | 0 | lp_ctx, |
712 | 0 | event_ctx, |
713 | 0 | (*ccc)->ccache, |
714 | 0 | &obtained, |
715 | 0 | error_string); |
716 | 0 | if (ret) { |
717 | 0 | return ret; |
718 | 0 | } |
719 | | |
720 | 0 | ret = cli_credentials_set_from_ccache(cred, *ccc, |
721 | 0 | obtained, error_string); |
722 | |
|
723 | 0 | cred->ccache = *ccc; |
724 | 0 | cred->ccache_obtained = cred->principal_obtained; |
725 | 0 | if (ret) { |
726 | 0 | return ret; |
727 | 0 | } |
728 | 0 | cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained); |
729 | 0 | return 0; |
730 | 0 | } |
731 | | |
732 | | _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred, |
733 | | struct tevent_context *event_ctx, |
734 | | struct loadparm_context *lp_ctx, |
735 | | struct ccache_container **ccc, |
736 | | const char **error_string) |
737 | 0 | { |
738 | 0 | return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string); |
739 | 0 | } |
740 | | |
741 | | /** |
742 | | * @brief Check if a valid Kerberos credential cache is attached. |
743 | | * |
744 | | * This will not ask for a password nor do a kinit. |
745 | | * |
746 | | * @param cred The credentials context. |
747 | | * |
748 | | * @param mem_ctx A memory context to allocate the ccache_name. |
749 | | * |
750 | | * @param ccache_name A pointer to a string to store the ccache name. |
751 | | * |
752 | | * @param obtained A pointer to store the information how the ccache was |
753 | | * obtained. |
754 | | * |
755 | | * @return True if a credential cache is attached, false if not or an error |
756 | | * occurred. |
757 | | */ |
758 | | _PUBLIC_ bool cli_credentials_get_ccache_name_obtained( |
759 | | struct cli_credentials *cred, |
760 | | TALLOC_CTX *mem_ctx, |
761 | | char **ccache_name, |
762 | | enum credentials_obtained *obtained) |
763 | 0 | { |
764 | 0 | if (ccache_name != NULL) { |
765 | 0 | *ccache_name = NULL; |
766 | 0 | } |
767 | |
|
768 | 0 | if (obtained != NULL) { |
769 | 0 | *obtained = CRED_UNINITIALISED; |
770 | 0 | } |
771 | |
|
772 | 0 | if (cred->machine_account_pending) { |
773 | 0 | return false; |
774 | 0 | } |
775 | | |
776 | 0 | if (cred->ccache_obtained == CRED_UNINITIALISED) { |
777 | 0 | return false; |
778 | 0 | } |
779 | | |
780 | 0 | if (cred->ccache_obtained >= cred->ccache_threshold) { |
781 | 0 | krb5_context k5ctx = cred->ccache->smb_krb5_context->krb5_context; |
782 | 0 | krb5_ccache k5ccache = cred->ccache->ccache; |
783 | 0 | krb5_error_code ret; |
784 | 0 | time_t lifetime = 0; |
785 | |
|
786 | 0 | ret = smb_krb5_cc_get_lifetime(k5ctx, k5ccache, &lifetime); |
787 | 0 | if (ret == KRB5_CC_END || ret == ENOENT) { |
788 | 0 | return false; |
789 | 0 | } |
790 | 0 | if (ret != 0) { |
791 | 0 | return false; |
792 | 0 | } |
793 | 0 | if (lifetime == 0) { |
794 | 0 | return false; |
795 | 0 | } else if (lifetime < 300) { |
796 | 0 | if (cred->password_obtained >= cred->ccache_obtained) { |
797 | | /* |
798 | | * we have a password to re-kinit |
799 | | * so let the caller try that. |
800 | | */ |
801 | 0 | return false; |
802 | 0 | } |
803 | 0 | } |
804 | | |
805 | 0 | if (ccache_name != NULL) { |
806 | 0 | char *name = NULL; |
807 | |
|
808 | 0 | ret = krb5_cc_get_full_name(k5ctx, k5ccache, &name); |
809 | 0 | if (ret != 0) { |
810 | 0 | return false; |
811 | 0 | } |
812 | | |
813 | 0 | *ccache_name = talloc_strdup(mem_ctx, name); |
814 | 0 | SAFE_FREE(name); |
815 | 0 | if (*ccache_name == NULL) { |
816 | 0 | return false; |
817 | 0 | } |
818 | 0 | } |
819 | | |
820 | 0 | if (obtained != NULL) { |
821 | 0 | *obtained = cred->ccache_obtained; |
822 | 0 | } |
823 | |
|
824 | 0 | return true; |
825 | 0 | } |
826 | | |
827 | 0 | return false; |
828 | 0 | } |
829 | | |
830 | | /* We have good reason to think the ccache in these credentials is invalid - blow it away */ |
831 | | static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred) |
832 | 0 | { |
833 | 0 | if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) { |
834 | 0 | talloc_unlink(cred, cred->client_gss_creds); |
835 | 0 | cred->client_gss_creds = NULL; |
836 | 0 | } |
837 | 0 | cred->client_gss_creds_obtained = CRED_UNINITIALISED; |
838 | 0 | } |
839 | | |
840 | | void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred, |
841 | | enum credentials_obtained obtained) |
842 | 761 | { |
843 | | /* If the caller just changed the username/password etc, then |
844 | | * any cached credentials are now invalid */ |
845 | 761 | if (obtained >= cred->client_gss_creds_obtained) { |
846 | 761 | if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) { |
847 | 0 | talloc_unlink(cred, cred->client_gss_creds); |
848 | 0 | cred->client_gss_creds = NULL; |
849 | 0 | } |
850 | 761 | cred->client_gss_creds_obtained = CRED_UNINITIALISED; |
851 | 761 | } |
852 | | /* Now that we know that the data is 'this specified', then |
853 | | * don't allow something less 'known' to be returned as a |
854 | | * ccache. Ie, if the username is on the command line, we |
855 | | * don't want to later guess to use a file-based ccache */ |
856 | 761 | if (obtained > cred->client_gss_creds_threshold) { |
857 | 1 | cred->client_gss_creds_threshold = obtained; |
858 | 1 | } |
859 | 761 | } |
860 | | |
861 | | /* We have good reason to think this CCACHE is invalid. Blow it away */ |
862 | | static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred) |
863 | 0 | { |
864 | 0 | if (cred->ccache_obtained > CRED_UNINITIALISED) { |
865 | 0 | talloc_unlink(cred, cred->ccache); |
866 | 0 | cred->ccache = NULL; |
867 | 0 | } |
868 | 0 | cred->ccache_obtained = CRED_UNINITIALISED; |
869 | |
|
870 | 0 | cli_credentials_unconditionally_invalidate_client_gss_creds(cred); |
871 | 0 | } |
872 | | |
873 | | _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred, |
874 | | enum credentials_obtained obtained) |
875 | 761 | { |
876 | | /* If the caller just changed the username/password etc, then |
877 | | * any cached credentials are now invalid */ |
878 | 761 | if (obtained >= cred->ccache_obtained) { |
879 | 761 | if (cred->ccache_obtained > CRED_UNINITIALISED) { |
880 | 0 | talloc_unlink(cred, cred->ccache); |
881 | 0 | cred->ccache = NULL; |
882 | 0 | } |
883 | 761 | cred->ccache_obtained = CRED_UNINITIALISED; |
884 | 761 | } |
885 | | /* Now that we know that the data is 'this specified', then |
886 | | * don't allow something less 'known' to be returned as a |
887 | | * ccache. i.e, if the username is on the command line, we |
888 | | * don't want to later guess to use a file-based ccache */ |
889 | 761 | if (obtained > cred->ccache_threshold) { |
890 | 1 | cred->ccache_threshold = obtained; |
891 | 1 | } |
892 | | |
893 | 761 | cli_credentials_invalidate_client_gss_creds(cred, |
894 | 761 | obtained); |
895 | 761 | } |
896 | | |
897 | | static int free_gssapi_creds(struct gssapi_creds_container *gcc) |
898 | 0 | { |
899 | 0 | OM_uint32 min_stat; |
900 | 0 | (void)gss_release_cred(&min_stat, &gcc->creds); |
901 | 0 | return 0; |
902 | 0 | } |
903 | | |
904 | | _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, |
905 | | struct tevent_context *event_ctx, |
906 | | struct loadparm_context *lp_ctx, |
907 | | struct gssapi_creds_container **_gcc, |
908 | | const char **error_string) |
909 | 0 | { |
910 | 0 | int ret = 0; |
911 | 0 | OM_uint32 maj_stat, min_stat; |
912 | 0 | struct gssapi_creds_container *gcc; |
913 | 0 | struct ccache_container *ccache; |
914 | 0 | #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X |
915 | 0 | gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER; |
916 | 0 | gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X); |
917 | 0 | #endif |
918 | 0 | krb5_enctype *etypes = NULL; |
919 | |
|
920 | 0 | if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold && |
921 | 0 | cred->client_gss_creds_obtained > CRED_UNINITIALISED) { |
922 | 0 | bool expired = false; |
923 | 0 | OM_uint32 lifetime = 0; |
924 | 0 | gss_cred_usage_t usage = 0; |
925 | 0 | maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds, |
926 | 0 | NULL, &lifetime, &usage, NULL); |
927 | 0 | if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) { |
928 | 0 | DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred))); |
929 | 0 | expired = true; |
930 | 0 | } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) { |
931 | 0 | DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime)); |
932 | 0 | expired = true; |
933 | 0 | } else if (maj_stat != GSS_S_COMPLETE) { |
934 | 0 | *error_string = talloc_asprintf(cred, "inquiry of credential lifetime via GSSAPI gss_inquire_cred failed: %s\n", |
935 | 0 | gssapi_error_string(cred, maj_stat, min_stat, NULL)); |
936 | 0 | return EINVAL; |
937 | 0 | } |
938 | 0 | if (expired) { |
939 | 0 | cli_credentials_unconditionally_invalidate_client_gss_creds(cred); |
940 | 0 | } else { |
941 | 0 | DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n", |
942 | 0 | cli_credentials_get_principal(cred, cred), (unsigned int)lifetime)); |
943 | |
|
944 | 0 | *_gcc = cred->client_gss_creds; |
945 | 0 | return 0; |
946 | 0 | } |
947 | 0 | } |
948 | | |
949 | 0 | ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx, |
950 | 0 | &ccache, error_string); |
951 | 0 | if (ret) { |
952 | 0 | if (cli_credentials_get_kerberos_state(cred) == CRED_USE_KERBEROS_REQUIRED) { |
953 | 0 | DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string)); |
954 | 0 | } else { |
955 | 0 | DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string)); |
956 | 0 | } |
957 | 0 | return ret; |
958 | 0 | } |
959 | | |
960 | 0 | gcc = talloc(cred, struct gssapi_creds_container); |
961 | 0 | if (!gcc) { |
962 | 0 | (*error_string) = error_message(ENOMEM); |
963 | 0 | return ENOMEM; |
964 | 0 | } |
965 | | |
966 | 0 | maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context, |
967 | 0 | ccache->ccache, NULL, NULL, |
968 | 0 | &gcc->creds); |
969 | 0 | if ((maj_stat == GSS_S_FAILURE) && |
970 | 0 | (min_stat == (OM_uint32)KRB5_CC_END || |
971 | 0 | min_stat == (OM_uint32)KRB5_CC_NOTFOUND || |
972 | 0 | min_stat == (OM_uint32)KRB5_FCC_NOFILE)) |
973 | 0 | { |
974 | | /* This CCACHE is no good. Ensure we don't use it again */ |
975 | 0 | cli_credentials_unconditionally_invalidate_ccache(cred); |
976 | | |
977 | | /* Now try again to get a ccache */ |
978 | 0 | ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx, |
979 | 0 | &ccache, error_string); |
980 | 0 | if (ret) { |
981 | 0 | DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret))); |
982 | 0 | return ret; |
983 | 0 | } |
984 | | |
985 | 0 | maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context, |
986 | 0 | ccache->ccache, NULL, NULL, |
987 | 0 | &gcc->creds); |
988 | |
|
989 | 0 | } |
990 | | |
991 | 0 | if (maj_stat) { |
992 | 0 | talloc_free(gcc); |
993 | 0 | if (min_stat) { |
994 | 0 | ret = min_stat; |
995 | 0 | } else { |
996 | 0 | ret = EINVAL; |
997 | 0 | } |
998 | 0 | (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret)); |
999 | 0 | return ret; |
1000 | 0 | } |
1001 | | |
1002 | | |
1003 | | /* |
1004 | | * transfer the enctypes from the smb_krb5_context to the gssapi layer |
1005 | | * |
1006 | | * We use 'our' smb_krb5_context to do the AS-REQ and it is possible |
1007 | | * to configure the enctypes via the krb5.conf. |
1008 | | * |
1009 | | * And the gss_init_sec_context() creates it's own krb5_context and |
1010 | | * the TGS-REQ had all enctypes in it and only the ones configured |
1011 | | * and used for the AS-REQ, so it wasn't possible to disable the usage |
1012 | | * of AES keys. |
1013 | | */ |
1014 | 0 | min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context, |
1015 | 0 | &etypes); |
1016 | 0 | if (min_stat == 0) { |
1017 | 0 | OM_uint32 num_ktypes; |
1018 | |
|
1019 | 0 | for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++); |
1020 | |
|
1021 | 0 | maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds, |
1022 | 0 | num_ktypes, |
1023 | 0 | (int32_t *) etypes); |
1024 | 0 | krb5_free_enctypes(ccache->smb_krb5_context->krb5_context, |
1025 | 0 | etypes); |
1026 | 0 | if (maj_stat) { |
1027 | 0 | talloc_free(gcc); |
1028 | 0 | if (min_stat) { |
1029 | 0 | ret = min_stat; |
1030 | 0 | } else { |
1031 | 0 | ret = EINVAL; |
1032 | 0 | } |
1033 | 0 | (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret)); |
1034 | 0 | return ret; |
1035 | 0 | } |
1036 | 0 | } |
1037 | | |
1038 | 0 | #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X |
1039 | | /* |
1040 | | * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG. |
1041 | | * |
1042 | | * This allows us to disable SIGN and SEAL on a TLS connection with |
1043 | | * GSS-SPNENO. For example ldaps:// connections. |
1044 | | * |
1045 | | * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575 |
1046 | | * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938 |
1047 | | */ |
1048 | 0 | maj_stat = gss_set_cred_option(&min_stat, &gcc->creds, |
1049 | 0 | oid, |
1050 | 0 | &empty_buffer); |
1051 | 0 | if (maj_stat) { |
1052 | 0 | talloc_free(gcc); |
1053 | 0 | if (min_stat) { |
1054 | 0 | ret = min_stat; |
1055 | 0 | } else { |
1056 | 0 | ret = EINVAL; |
1057 | 0 | } |
1058 | 0 | (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret)); |
1059 | 0 | return ret; |
1060 | 0 | } |
1061 | 0 | #endif |
1062 | 0 | cred->client_gss_creds_obtained = cred->ccache_obtained; |
1063 | 0 | talloc_set_destructor(gcc, free_gssapi_creds); |
1064 | 0 | cred->client_gss_creds = gcc; |
1065 | 0 | *_gcc = gcc; |
1066 | 0 | return 0; |
1067 | 0 | } |
1068 | | |
1069 | | /** |
1070 | | Set a gssapi cred_id_t into the credentials system. (Client case) |
1071 | | |
1072 | | This grabs the credentials both 'intact' and getting the krb5 |
1073 | | ccache out of it. This routine can be generalised in future for |
1074 | | the case where we deal with GSSAPI mechs other than krb5. |
1075 | | |
1076 | | On success, the caller must not free gssapi_cred, as it now belongs |
1077 | | to the credentials system. |
1078 | | */ |
1079 | | |
1080 | | int cli_credentials_set_client_gss_creds(struct cli_credentials *cred, |
1081 | | struct loadparm_context *lp_ctx, |
1082 | | gss_cred_id_t gssapi_cred, |
1083 | | enum credentials_obtained obtained, |
1084 | | const char **error_string) |
1085 | 0 | { |
1086 | 0 | int ret; |
1087 | 0 | OM_uint32 maj_stat, min_stat; |
1088 | 0 | struct ccache_container *ccc = NULL; |
1089 | 0 | struct gssapi_creds_container *gcc = NULL; |
1090 | 0 | if (cred->client_gss_creds_obtained > obtained) { |
1091 | 0 | return 0; |
1092 | 0 | } |
1093 | | |
1094 | 0 | gcc = talloc(cred, struct gssapi_creds_container); |
1095 | 0 | if (!gcc) { |
1096 | 0 | (*error_string) = error_message(ENOMEM); |
1097 | 0 | return ENOMEM; |
1098 | 0 | } |
1099 | | |
1100 | 0 | ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string); |
1101 | 0 | if (ret != 0) { |
1102 | 0 | return ret; |
1103 | 0 | } |
1104 | | |
1105 | 0 | maj_stat = smb_gss_krb5_copy_ccache(&min_stat, |
1106 | 0 | gssapi_cred, |
1107 | 0 | ccc); |
1108 | 0 | if (maj_stat) { |
1109 | 0 | if (min_stat) { |
1110 | 0 | ret = min_stat; |
1111 | 0 | } else { |
1112 | 0 | ret = EINVAL; |
1113 | 0 | } |
1114 | 0 | if (ret) { |
1115 | 0 | (*error_string) = error_message(ENOMEM); |
1116 | 0 | } |
1117 | 0 | } |
1118 | |
|
1119 | 0 | if (ret == 0) { |
1120 | 0 | ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string); |
1121 | 0 | } |
1122 | 0 | cred->ccache = ccc; |
1123 | 0 | cred->ccache_obtained = obtained; |
1124 | 0 | if (ret == 0) { |
1125 | 0 | gcc->creds = gssapi_cred; |
1126 | 0 | talloc_set_destructor(gcc, free_gssapi_creds); |
1127 | | |
1128 | | /* set the client_gss_creds_obtained here, as it just |
1129 | | got set to UNINITIALISED by the calls above */ |
1130 | 0 | cred->client_gss_creds_obtained = obtained; |
1131 | 0 | cred->client_gss_creds = gcc; |
1132 | 0 | } |
1133 | 0 | return ret; |
1134 | 0 | } |
1135 | | |
1136 | | static int cli_credentials_shallow_ccache(struct cli_credentials *cred) |
1137 | 0 | { |
1138 | 0 | krb5_error_code ret; |
1139 | 0 | const struct ccache_container *old_ccc = NULL; |
1140 | 0 | enum credentials_obtained old_obtained; |
1141 | 0 | struct ccache_container *ccc = NULL; |
1142 | 0 | krb5_principal princ; |
1143 | |
|
1144 | 0 | old_obtained = cred->ccache_obtained; |
1145 | 0 | old_ccc = cred->ccache; |
1146 | 0 | if (old_ccc == NULL) { |
1147 | 0 | return 0; |
1148 | 0 | } |
1149 | | |
1150 | 0 | cred->ccache = NULL; |
1151 | 0 | cred->ccache_obtained = CRED_UNINITIALISED; |
1152 | 0 | cred->client_gss_creds = NULL; |
1153 | 0 | cred->client_gss_creds_obtained = CRED_UNINITIALISED; |
1154 | |
|
1155 | 0 | ret = krb5_cc_get_principal( |
1156 | 0 | old_ccc->smb_krb5_context->krb5_context, |
1157 | 0 | old_ccc->ccache, |
1158 | 0 | &princ); |
1159 | 0 | if (ret != 0) { |
1160 | | /* |
1161 | | * This is an empty ccache. No point in copying anything. |
1162 | | */ |
1163 | 0 | return 0; |
1164 | 0 | } |
1165 | 0 | krb5_free_principal(old_ccc->smb_krb5_context->krb5_context, princ); |
1166 | |
|
1167 | 0 | ccc = talloc(cred, struct ccache_container); |
1168 | 0 | if (ccc == NULL) { |
1169 | 0 | return ENOMEM; |
1170 | 0 | } |
1171 | 0 | *ccc = *old_ccc; |
1172 | 0 | ccc->ccache = NULL; |
1173 | |
|
1174 | 0 | ret = smb_krb5_cc_new_unique_memory(ccc->smb_krb5_context->krb5_context, |
1175 | 0 | NULL, |
1176 | 0 | NULL, |
1177 | 0 | &ccc->ccache); |
1178 | 0 | if (ret != 0) { |
1179 | 0 | TALLOC_FREE(ccc); |
1180 | 0 | return ret; |
1181 | 0 | } |
1182 | | |
1183 | 0 | talloc_set_destructor(ccc, free_mccache); |
1184 | |
|
1185 | 0 | ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context, |
1186 | 0 | old_ccc->ccache, ccc->ccache); |
1187 | 0 | if (ret != 0) { |
1188 | 0 | TALLOC_FREE(ccc); |
1189 | 0 | return ret; |
1190 | 0 | } |
1191 | | |
1192 | 0 | cred->ccache = ccc; |
1193 | 0 | cred->ccache_obtained = old_obtained; |
1194 | 0 | return ret; |
1195 | 0 | } |
1196 | | |
1197 | | _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx, |
1198 | | struct cli_credentials *src) |
1199 | 0 | { |
1200 | 0 | struct cli_credentials *dst, *armor_credentials; |
1201 | 0 | int ret; |
1202 | |
|
1203 | 0 | dst = talloc(mem_ctx, struct cli_credentials); |
1204 | 0 | if (dst == NULL) { |
1205 | 0 | return NULL; |
1206 | 0 | } |
1207 | | |
1208 | 0 | *dst = *src; |
1209 | |
|
1210 | 0 | if (dst->krb5_fast_armor_credentials != NULL) { |
1211 | 0 | armor_credentials = talloc_reference(dst, dst->krb5_fast_armor_credentials); |
1212 | 0 | if (armor_credentials == NULL) { |
1213 | 0 | TALLOC_FREE(dst); |
1214 | 0 | return NULL; |
1215 | 0 | } |
1216 | 0 | } |
1217 | | |
1218 | 0 | ret = cli_credentials_shallow_ccache(dst); |
1219 | 0 | if (ret != 0) { |
1220 | 0 | TALLOC_FREE(dst); |
1221 | 0 | return NULL; |
1222 | 0 | } |
1223 | | |
1224 | 0 | return dst; |
1225 | 0 | } |
1226 | | |
1227 | | /* Get the keytab (actually, a container containing the krb5_keytab) |
1228 | | * attached to this context. If this hasn't been done or set before, |
1229 | | * it will be generated from the password. |
1230 | | */ |
1231 | | _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred, |
1232 | | struct loadparm_context *lp_ctx, |
1233 | | struct keytab_container **_ktc) |
1234 | 0 | { |
1235 | 0 | krb5_error_code ret; |
1236 | 0 | struct keytab_container *ktc; |
1237 | 0 | struct smb_krb5_context *smb_krb5_context; |
1238 | 0 | const char *keytab_name; |
1239 | 0 | krb5_keytab keytab; |
1240 | 0 | TALLOC_CTX *mem_ctx; |
1241 | 0 | const char *username = cli_credentials_get_username(cred); |
1242 | 0 | const char *realm = cli_credentials_get_realm(cred); |
1243 | 0 | char *salt_principal = NULL; |
1244 | |
|
1245 | 0 | if (cred->keytab_obtained >= (MAX(cred->principal_obtained, |
1246 | 0 | cred->username_obtained))) { |
1247 | 0 | *_ktc = cred->keytab; |
1248 | 0 | return 0; |
1249 | 0 | } |
1250 | | |
1251 | 0 | if (cli_credentials_is_anonymous(cred)) { |
1252 | 0 | return EINVAL; |
1253 | 0 | } |
1254 | | |
1255 | 0 | ret = cli_credentials_get_krb5_context(cred, lp_ctx, |
1256 | 0 | &smb_krb5_context); |
1257 | 0 | if (ret) { |
1258 | 0 | return ret; |
1259 | 0 | } |
1260 | | |
1261 | 0 | mem_ctx = talloc_new(cred); |
1262 | 0 | if (!mem_ctx) { |
1263 | 0 | return ENOMEM; |
1264 | 0 | } |
1265 | | |
1266 | 0 | salt_principal = cli_credentials_get_salt_principal(cred, mem_ctx); |
1267 | 0 | if (salt_principal == NULL) { |
1268 | 0 | talloc_free(mem_ctx); |
1269 | 0 | return ENOMEM; |
1270 | 0 | } |
1271 | | |
1272 | 0 | ret = smb_krb5_create_memory_keytab(mem_ctx, |
1273 | 0 | smb_krb5_context->krb5_context, |
1274 | 0 | cli_credentials_get_password(cred), |
1275 | 0 | username, |
1276 | 0 | realm, |
1277 | 0 | salt_principal, |
1278 | 0 | cli_credentials_get_kvno(cred), |
1279 | 0 | &keytab, |
1280 | 0 | &keytab_name); |
1281 | 0 | if (ret) { |
1282 | 0 | talloc_free(mem_ctx); |
1283 | 0 | return ret; |
1284 | 0 | } |
1285 | | |
1286 | 0 | ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context, |
1287 | 0 | keytab, keytab_name, &ktc); |
1288 | 0 | if (ret) { |
1289 | 0 | talloc_free(mem_ctx); |
1290 | 0 | return ret; |
1291 | 0 | } |
1292 | | |
1293 | 0 | cred->keytab_obtained = (MAX(cred->principal_obtained, |
1294 | 0 | cred->username_obtained)); |
1295 | | |
1296 | | /* We make this keytab up based on a password. Therefore |
1297 | | * match-by-key is acceptable, we can't match on the wrong |
1298 | | * principal */ |
1299 | 0 | ktc->password_based = true; |
1300 | |
|
1301 | 0 | talloc_steal(cred, ktc); |
1302 | 0 | cred->keytab = ktc; |
1303 | 0 | *_ktc = cred->keytab; |
1304 | 0 | talloc_free(mem_ctx); |
1305 | 0 | return ret; |
1306 | 0 | } |
1307 | | |
1308 | | /* Given the name of a keytab (presumably in the format |
1309 | | * FILE:/etc/krb5.keytab), open it and attach it */ |
1310 | | |
1311 | | _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred, |
1312 | | struct loadparm_context *lp_ctx, |
1313 | | const char *keytab_name, |
1314 | | enum credentials_obtained obtained) |
1315 | 0 | { |
1316 | 0 | krb5_error_code ret; |
1317 | 0 | struct keytab_container *ktc; |
1318 | 0 | struct smb_krb5_context *smb_krb5_context; |
1319 | 0 | TALLOC_CTX *mem_ctx; |
1320 | |
|
1321 | 0 | if (cred->keytab_obtained >= obtained) { |
1322 | 0 | return 0; |
1323 | 0 | } |
1324 | | |
1325 | 0 | ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context); |
1326 | 0 | if (ret) { |
1327 | 0 | return ret; |
1328 | 0 | } |
1329 | | |
1330 | 0 | mem_ctx = talloc_new(cred); |
1331 | 0 | if (!mem_ctx) { |
1332 | 0 | return ENOMEM; |
1333 | 0 | } |
1334 | | |
1335 | 0 | ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context, |
1336 | 0 | NULL, keytab_name, &ktc); |
1337 | 0 | if (ret) { |
1338 | 0 | return ret; |
1339 | 0 | } |
1340 | | |
1341 | 0 | cred->keytab_obtained = obtained; |
1342 | |
|
1343 | 0 | talloc_steal(cred, ktc); |
1344 | 0 | cred->keytab = ktc; |
1345 | 0 | talloc_free(mem_ctx); |
1346 | |
|
1347 | 0 | return ret; |
1348 | 0 | } |
1349 | | |
1350 | | /* Get server gss credentials (in gsskrb5, this means the keytab) */ |
1351 | | |
1352 | | _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred, |
1353 | | struct loadparm_context *lp_ctx, |
1354 | | struct gssapi_creds_container **_gcc) |
1355 | 0 | { |
1356 | 0 | int ret = 0; |
1357 | 0 | OM_uint32 maj_stat, min_stat; |
1358 | 0 | struct gssapi_creds_container *gcc; |
1359 | 0 | struct keytab_container *ktc; |
1360 | 0 | struct smb_krb5_context *smb_krb5_context; |
1361 | 0 | TALLOC_CTX *mem_ctx; |
1362 | 0 | krb5_principal princ; |
1363 | 0 | const char *error_string; |
1364 | 0 | enum credentials_obtained obtained; |
1365 | |
|
1366 | 0 | mem_ctx = talloc_new(cred); |
1367 | 0 | if (!mem_ctx) { |
1368 | 0 | return ENOMEM; |
1369 | 0 | } |
1370 | | |
1371 | 0 | ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context); |
1372 | 0 | if (ret) { |
1373 | 0 | return ret; |
1374 | 0 | } |
1375 | | |
1376 | 0 | ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string); |
1377 | 0 | if (ret) { |
1378 | 0 | DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n", |
1379 | 0 | error_string)); |
1380 | 0 | talloc_free(mem_ctx); |
1381 | 0 | return ret; |
1382 | 0 | } |
1383 | | |
1384 | 0 | if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) { |
1385 | 0 | talloc_free(mem_ctx); |
1386 | 0 | *_gcc = cred->server_gss_creds; |
1387 | 0 | return 0; |
1388 | 0 | } |
1389 | | |
1390 | 0 | ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc); |
1391 | 0 | if (ret) { |
1392 | 0 | DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret))); |
1393 | 0 | return ret; |
1394 | 0 | } |
1395 | | |
1396 | 0 | gcc = talloc(cred, struct gssapi_creds_container); |
1397 | 0 | if (!gcc) { |
1398 | 0 | talloc_free(mem_ctx); |
1399 | 0 | return ENOMEM; |
1400 | 0 | } |
1401 | | |
1402 | 0 | if (ktc->password_based || obtained < CRED_SPECIFIED) { |
1403 | | /* |
1404 | | * This creates a GSSAPI cred_id_t for match-by-key with only |
1405 | | * the keytab set |
1406 | | */ |
1407 | 0 | princ = NULL; |
1408 | 0 | } |
1409 | 0 | maj_stat = smb_gss_krb5_import_cred(&min_stat, |
1410 | 0 | smb_krb5_context->krb5_context, |
1411 | 0 | NULL, princ, |
1412 | 0 | ktc->keytab, |
1413 | 0 | &gcc->creds); |
1414 | 0 | if (maj_stat) { |
1415 | 0 | if (min_stat) { |
1416 | 0 | ret = min_stat; |
1417 | 0 | } else { |
1418 | 0 | ret = EINVAL; |
1419 | 0 | } |
1420 | 0 | } |
1421 | 0 | if (ret == 0) { |
1422 | 0 | cred->server_gss_creds_obtained = cred->keytab_obtained; |
1423 | 0 | talloc_set_destructor(gcc, free_gssapi_creds); |
1424 | 0 | cred->server_gss_creds = gcc; |
1425 | 0 | *_gcc = gcc; |
1426 | 0 | } |
1427 | 0 | talloc_free(mem_ctx); |
1428 | 0 | return ret; |
1429 | 0 | } |
1430 | | |
1431 | | /** |
1432 | | * Set Kerberos KVNO |
1433 | | */ |
1434 | | |
1435 | | _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred, |
1436 | | int kvno) |
1437 | 0 | { |
1438 | 0 | cred->kvno = kvno; |
1439 | 0 | } |
1440 | | |
1441 | | /** |
1442 | | * Return Kerberos KVNO |
1443 | | */ |
1444 | | |
1445 | | _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred) |
1446 | 0 | { |
1447 | 0 | return cred->kvno; |
1448 | 0 | } |
1449 | | |
1450 | | |
1451 | | char *cli_credentials_get_salt_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx) |
1452 | 0 | { |
1453 | 0 | TALLOC_CTX *frame = NULL; |
1454 | 0 | const char *realm = NULL; |
1455 | 0 | const char *username = NULL; |
1456 | 0 | uint32_t uac_flags = 0; |
1457 | 0 | char *salt_principal = NULL; |
1458 | 0 | const char *upn = NULL; |
1459 | 0 | int ret; |
1460 | | |
1461 | | /* If specified, use the specified value */ |
1462 | 0 | if (cred->salt_principal != NULL) { |
1463 | 0 | return talloc_strdup(mem_ctx, cred->salt_principal); |
1464 | 0 | } |
1465 | | |
1466 | 0 | frame = talloc_stackframe(); |
1467 | |
|
1468 | 0 | switch (cred->secure_channel_type) { |
1469 | 0 | case SEC_CHAN_WKSTA: |
1470 | 0 | case SEC_CHAN_RODC: |
1471 | 0 | uac_flags = UF_WORKSTATION_TRUST_ACCOUNT; |
1472 | 0 | break; |
1473 | 0 | case SEC_CHAN_BDC: |
1474 | 0 | uac_flags = UF_SERVER_TRUST_ACCOUNT; |
1475 | 0 | break; |
1476 | 0 | case SEC_CHAN_DOMAIN: |
1477 | 0 | case SEC_CHAN_DNS_DOMAIN: |
1478 | 0 | uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT; |
1479 | 0 | break; |
1480 | 0 | default: |
1481 | 0 | upn = cli_credentials_get_principal(cred, frame); |
1482 | 0 | if (upn == NULL) { |
1483 | 0 | TALLOC_FREE(frame); |
1484 | 0 | return NULL; |
1485 | 0 | } |
1486 | 0 | uac_flags = UF_NORMAL_ACCOUNT; |
1487 | 0 | break; |
1488 | 0 | } |
1489 | | |
1490 | 0 | realm = cli_credentials_get_realm(cred); |
1491 | 0 | username = cli_credentials_get_username(cred); |
1492 | |
|
1493 | 0 | ret = smb_krb5_salt_principal_str(realm, |
1494 | 0 | username, /* sAMAccountName */ |
1495 | 0 | upn, /* userPrincipalName */ |
1496 | 0 | uac_flags, |
1497 | 0 | mem_ctx, |
1498 | 0 | &salt_principal); |
1499 | 0 | if (ret) { |
1500 | 0 | TALLOC_FREE(frame); |
1501 | 0 | return NULL; |
1502 | 0 | } |
1503 | | |
1504 | 0 | TALLOC_FREE(frame); |
1505 | 0 | return salt_principal; |
1506 | 0 | } |
1507 | | |
1508 | | _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal) |
1509 | 0 | { |
1510 | 0 | talloc_free(cred->salt_principal); |
1511 | 0 | cred->salt_principal = talloc_strdup(cred, principal); |
1512 | 0 | } |
1513 | | |
1514 | | /* The 'impersonate_principal' is used to allow one Kerberos principal |
1515 | | * (and it's associated keytab etc) to impersonate another. The |
1516 | | * ability to do this is controlled by the KDC, but it is generally |
1517 | | * permitted to impersonate anyone to yourself. This allows any |
1518 | | * member of the domain to get the groups of a user. This is also |
1519 | | * known as S4U2Self */ |
1520 | | |
1521 | | _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred) |
1522 | 0 | { |
1523 | 0 | return cred->impersonate_principal; |
1524 | 0 | } |
1525 | | |
1526 | | /* |
1527 | | * The 'self_service' is the service principal that |
1528 | | * represents the same object (by its objectSid) |
1529 | | * as the client principal (typically our machine account). |
1530 | | * When trying to impersonate 'impersonate_principal' with |
1531 | | * S4U2Self. |
1532 | | */ |
1533 | | _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred) |
1534 | 0 | { |
1535 | 0 | return cred->self_service; |
1536 | 0 | } |
1537 | | |
1538 | | _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred, |
1539 | | const char *principal, |
1540 | | const char *self_service) |
1541 | 0 | { |
1542 | 0 | talloc_free(cred->impersonate_principal); |
1543 | 0 | cred->impersonate_principal = talloc_strdup(cred, principal); |
1544 | 0 | talloc_free(cred->self_service); |
1545 | 0 | cred->self_service = talloc_strdup(cred, self_service); |
1546 | 0 | cli_credentials_set_kerberos_state(cred, |
1547 | 0 | CRED_USE_KERBEROS_REQUIRED, |
1548 | 0 | CRED_SPECIFIED); |
1549 | 0 | } |
1550 | | |
1551 | | /* |
1552 | | * when impersonating for S4U2proxy we need to set the target principal. |
1553 | | * Similarly, we may only be authorized to do general impersonation to |
1554 | | * some particular services. |
1555 | | * |
1556 | | * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT |
1557 | | * |
1558 | | * NULL means that tickets will be obtained for the krbtgt service. |
1559 | | */ |
1560 | | |
1561 | | const char *cli_credentials_get_target_service(struct cli_credentials *cred) |
1562 | 0 | { |
1563 | 0 | return cred->target_service; |
1564 | 0 | } |
1565 | | |
1566 | | _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service) |
1567 | 0 | { |
1568 | 0 | talloc_free(cred->target_service); |
1569 | 0 | cred->target_service = talloc_strdup(cred, target_service); |
1570 | 0 | } |
1571 | | |
1572 | | _PUBLIC_ int cli_credentials_get_kerberos_key(struct cli_credentials *cred, |
1573 | | TALLOC_CTX *mem_ctx, |
1574 | | struct loadparm_context *lp_ctx, |
1575 | | krb5_enctype enctype, |
1576 | | bool previous, |
1577 | | DATA_BLOB *key_blob) |
1578 | 0 | { |
1579 | 0 | struct smb_krb5_context *smb_krb5_context = NULL; |
1580 | 0 | krb5_error_code krb5_ret; |
1581 | 0 | int ret; |
1582 | 0 | const char *password = NULL; |
1583 | 0 | const char *salt = NULL; |
1584 | 0 | krb5_data cleartext_data; |
1585 | 0 | krb5_data salt_data = { |
1586 | 0 | .length = 0, |
1587 | 0 | }; |
1588 | 0 | krb5_keyblock key; |
1589 | |
|
1590 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
1591 | |
|
1592 | 0 | if ((int)enctype == (int)ENCTYPE_ARCFOUR_HMAC) { |
1593 | 0 | struct samr_Password *nt_hash; |
1594 | |
|
1595 | 0 | if (previous) { |
1596 | 0 | nt_hash = cli_credentials_get_old_nt_hash(cred, frame); |
1597 | 0 | } else { |
1598 | 0 | nt_hash = cli_credentials_get_nt_hash(cred, frame); |
1599 | 0 | } |
1600 | |
|
1601 | 0 | if (nt_hash == NULL) { |
1602 | 0 | TALLOC_FREE(frame); |
1603 | 0 | return EINVAL; |
1604 | 0 | } |
1605 | 0 | *key_blob = data_blob_talloc(mem_ctx, |
1606 | 0 | nt_hash->hash, |
1607 | 0 | sizeof(nt_hash->hash)); |
1608 | 0 | if (key_blob->data == NULL) { |
1609 | 0 | TALLOC_FREE(frame); |
1610 | 0 | return ENOMEM; |
1611 | 0 | } |
1612 | 0 | TALLOC_FREE(frame); |
1613 | 0 | return 0; |
1614 | 0 | } |
1615 | | |
1616 | 0 | if (cred->password_will_be_nt_hash) { |
1617 | 0 | DEBUG(1,("cli_credentials_get_kerberos_key: cannot generate Kerberos key using NT hash\n")); |
1618 | 0 | TALLOC_FREE(frame); |
1619 | 0 | return EINVAL; |
1620 | 0 | } |
1621 | | |
1622 | 0 | salt = cli_credentials_get_salt_principal(cred, frame); |
1623 | 0 | if (salt == NULL) { |
1624 | 0 | TALLOC_FREE(frame); |
1625 | 0 | return EINVAL; |
1626 | 0 | } |
1627 | | |
1628 | 0 | if (previous) { |
1629 | 0 | password = cli_credentials_get_old_password(cred); |
1630 | 0 | } else { |
1631 | 0 | password = cli_credentials_get_password(cred); |
1632 | 0 | } |
1633 | 0 | if (password == NULL) { |
1634 | 0 | TALLOC_FREE(frame); |
1635 | 0 | return EINVAL; |
1636 | 0 | } |
1637 | | |
1638 | 0 | cleartext_data.data = discard_const_p(char, password); |
1639 | 0 | cleartext_data.length = strlen(password); |
1640 | |
|
1641 | 0 | ret = cli_credentials_get_krb5_context(cred, lp_ctx, |
1642 | 0 | &smb_krb5_context); |
1643 | 0 | if (ret != 0) { |
1644 | 0 | TALLOC_FREE(frame); |
1645 | 0 | return ret; |
1646 | 0 | } |
1647 | | |
1648 | 0 | salt_data.data = discard_const_p(char, salt); |
1649 | 0 | salt_data.length = strlen(salt); |
1650 | | |
1651 | | /* |
1652 | | * create Kerberos key out of |
1653 | | * the salt and the cleartext password |
1654 | | */ |
1655 | 0 | krb5_ret = smb_krb5_create_key_from_string(smb_krb5_context->krb5_context, |
1656 | 0 | NULL, |
1657 | 0 | &salt_data, |
1658 | 0 | &cleartext_data, |
1659 | 0 | enctype, |
1660 | 0 | &key); |
1661 | 0 | if (krb5_ret != 0) { |
1662 | 0 | DEBUG(1,("cli_credentials_get_aes256_key: " |
1663 | 0 | "generation of a aes256-cts-hmac-sha1-96 key failed: %s\n", |
1664 | 0 | smb_get_krb5_error_message(smb_krb5_context->krb5_context, |
1665 | 0 | krb5_ret, mem_ctx))); |
1666 | 0 | TALLOC_FREE(frame); |
1667 | 0 | return EINVAL; |
1668 | 0 | } |
1669 | 0 | *key_blob = data_blob_talloc(mem_ctx, |
1670 | 0 | KRB5_KEY_DATA(&key), |
1671 | 0 | KRB5_KEY_LENGTH(&key)); |
1672 | 0 | krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &key); |
1673 | 0 | if (key_blob->data == NULL) { |
1674 | 0 | TALLOC_FREE(frame); |
1675 | 0 | return ENOMEM; |
1676 | 0 | } |
1677 | 0 | talloc_keep_secret(key_blob->data); |
1678 | |
|
1679 | 0 | TALLOC_FREE(frame); |
1680 | 0 | return 0; |
1681 | 0 | } |
1682 | | |
1683 | | /* This take a reference to the armor credentials to ensure the lifetime is appropriate */ |
1684 | | |
1685 | | NTSTATUS cli_credentials_set_krb5_fast_armor_credentials(struct cli_credentials *creds, |
1686 | | struct cli_credentials *armor_creds, |
1687 | | bool require_fast_armor) |
1688 | 0 | { |
1689 | 0 | talloc_unlink(creds, creds->krb5_fast_armor_credentials); |
1690 | 0 | if (armor_creds == NULL) { |
1691 | 0 | creds->krb5_fast_armor_credentials = NULL; |
1692 | 0 | return NT_STATUS_OK; |
1693 | 0 | } |
1694 | | |
1695 | 0 | creds->krb5_fast_armor_credentials = talloc_reference(creds, armor_creds); |
1696 | 0 | if (creds->krb5_fast_armor_credentials == NULL) { |
1697 | 0 | return NT_STATUS_NO_MEMORY; |
1698 | 0 | } |
1699 | | |
1700 | 0 | creds->krb5_require_fast_armor = require_fast_armor; |
1701 | |
|
1702 | 0 | return NT_STATUS_OK; |
1703 | 0 | } |
1704 | | |
1705 | | struct cli_credentials *cli_credentials_get_krb5_fast_armor_credentials(struct cli_credentials *creds) |
1706 | 0 | { |
1707 | 0 | return creds->krb5_fast_armor_credentials; |
1708 | 0 | } |
1709 | | |
1710 | | bool cli_credentials_get_krb5_require_fast_armor(struct cli_credentials *creds) |
1711 | 0 | { |
1712 | 0 | return creds->krb5_require_fast_armor; |
1713 | 0 | } |