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