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