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 46402 : static int free_mccache(struct ccache_container *ccc)
49 : {
50 46402 : if (ccc->ccache != NULL) {
51 46402 : krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
52 : ccc->ccache);
53 46402 : ccc->ccache = NULL;
54 : }
55 :
56 46402 : return 0;
57 : }
58 :
59 : /* Free a disk-based ccache */
60 98229 : static int free_dccache(struct ccache_container *ccc)
61 : {
62 98229 : if (ccc->ccache != NULL) {
63 98229 : krb5_cc_close(ccc->smb_krb5_context->krb5_context,
64 : ccc->ccache);
65 98229 : ccc->ccache = NULL;
66 : }
67 :
68 98229 : return 0;
69 : }
70 :
71 30908 : 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 7605 : krb5_context context = ccc->smb_krb5_context->krb5_context;
77 7605 : krb5_ccache dummy_ccache = NULL;
78 7605 : krb5_creds creds = {0};
79 7605 : krb5_cc_cursor cursor = NULL;
80 7605 : krb5_principal princ = NULL;
81 : krb5_error_code code;
82 : char *dummy_name;
83 7605 : uint32_t maj_stat = GSS_S_FAILURE;
84 :
85 7605 : dummy_name = talloc_asprintf(ccc,
86 : "MEMORY:gss_krb5_copy_ccache-%p",
87 : &ccc->ccache);
88 7605 : 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 7605 : code = krb5_cc_resolve(context, dummy_name, &dummy_ccache);
100 7605 : TALLOC_FREE(dummy_name);
101 7605 : 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 7605 : maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache);
111 7605 : if (maj_stat != 0) {
112 0 : krb5_cc_close(context, dummy_ccache);
113 0 : return maj_stat;
114 : }
115 :
116 7605 : code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor);
117 7605 : 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 7605 : code = krb5_cc_next_cred(context,
124 : dummy_ccache,
125 : &cursor,
126 : &creds);
127 7605 : 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 7605 : if (creds.ticket_flags & TKT_FLG_PRE_AUTH) {
135 : krb5_data *tgs;
136 :
137 7605 : tgs = krb5_princ_component(context,
138 : creds.server,
139 : 0);
140 7605 : if (tgs != NULL && tgs->length >= 1) {
141 : int cmp;
142 :
143 7605 : cmp = memcmp(tgs->data,
144 : KRB5_TGS_NAME,
145 7605 : tgs->length);
146 7605 : if (cmp == 0 && creds.client != NULL) {
147 7605 : princ = creds.client;
148 7605 : code = KRB5_CC_END;
149 7605 : 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 7605 : if (code == KRB5_CC_END) {
163 7605 : krb5_cc_end_seq_get(context, dummy_ccache, &cursor);
164 7605 : code = 0;
165 : }
166 7605 : krb5_cc_close(context, dummy_ccache);
167 :
168 7605 : 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 7605 : code = krb5_cc_initialize(context, ccc->ccache, princ);
181 7605 : if (code != 0) {
182 0 : krb5_free_cred_contents(context, &creds);
183 0 : *min_stat = EINVAL;
184 0 : return GSS_S_FAILURE;
185 : }
186 7605 : krb5_free_cred_contents(context, &creds);
187 :
188 : #endif /* SAMBA4_USES_HEIMDAL */
189 :
190 30908 : return gss_krb5_copy_ccache(min_stat,
191 : cred,
192 : ccc->ccache);
193 : }
194 :
195 261454 : _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 261454 : if (cred->smb_krb5_context) {
201 67623 : *smb_krb5_context = cred->smb_krb5_context;
202 67623 : return 0;
203 : }
204 :
205 193831 : ret = smb_krb5_init_context(cred, lp_ctx,
206 : &cred->smb_krb5_context);
207 193831 : if (ret) {
208 0 : cred->smb_krb5_context = NULL;
209 0 : return ret;
210 : }
211 193831 : *smb_krb5_context = cred->smb_krb5_context;
212 193831 : 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 51843 : 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 51843 : if (cred->ccache_obtained > obtained) {
246 5102 : return 0;
247 : }
248 :
249 46741 : ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
250 : ccache->ccache, &princ);
251 :
252 46741 : 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 46741 : ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
260 46741 : 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 46741 : ok = cli_credentials_set_principal(cred, name, obtained);
269 46741 : krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
270 46741 : if (!ok) {
271 25 : krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
272 25 : return ENOMEM;
273 : }
274 :
275 48182 : realm = smb_krb5_principal_get_realm(
276 46716 : cred, ccache->smb_krb5_context->krb5_context, princ);
277 46716 : krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
278 46716 : if (realm == NULL) {
279 0 : return ENOMEM;
280 : }
281 46716 : ok = cli_credentials_set_realm(cred, realm, obtained);
282 46716 : TALLOC_FREE(realm);
283 46716 : 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 46690 : cred->ccache_obtained = obtained;
289 :
290 46690 : return 0;
291 : }
292 :
293 100445 : _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 100445 : if (cred->ccache_obtained > obtained) {
303 2191 : return 0;
304 : }
305 :
306 98254 : ccc = talloc(cred, struct ccache_container);
307 98254 : if (!ccc) {
308 0 : (*error_string) = error_message(ENOMEM);
309 0 : return ENOMEM;
310 : }
311 :
312 98254 : ret = cli_credentials_get_krb5_context(cred, lp_ctx,
313 : &ccc->smb_krb5_context);
314 98254 : if (ret) {
315 0 : (*error_string) = error_message(ret);
316 0 : talloc_free(ccc);
317 0 : return ret;
318 : }
319 98254 : 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 98254 : if (name) {
326 2235 : ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
327 2235 : 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 96019 : ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
337 96019 : 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 98254 : talloc_set_destructor(ccc, free_dccache);
347 :
348 98254 : ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
349 :
350 98254 : 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 98203 : cred->ccache = ccc;
362 98203 : cred->ccache_obtained = obtained;
363 :
364 98203 : cli_credentials_invalidate_client_gss_creds(
365 : cred, cred->ccache_obtained);
366 :
367 98203 : 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 1572 : _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 1572 : if (principal == NULL) {
527 : /* no way to delete if we don't know the principal */
528 0 : return false;
529 : }
530 :
531 1572 : ccc = cred->ccache;
532 1572 : if (ccc == NULL) {
533 : /* not a kerberos connection */
534 1546 : 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 45976 : 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 45976 : bool must_free_cc_name = false;
605 1466 : krb5_error_code ret;
606 45976 : struct ccache_container *ccc = talloc(cred, struct ccache_container);
607 45976 : if (!ccc) {
608 0 : return ENOMEM;
609 : }
610 :
611 45976 : ret = cli_credentials_get_krb5_context(cred, lp_ctx,
612 : &ccc->smb_krb5_context);
613 45976 : 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 45976 : 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 45976 : if (!ccache_name) {
626 45952 : must_free_cc_name = true;
627 :
628 45952 : 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 45952 : ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
633 : ccc);
634 : }
635 :
636 45952 : if (!ccache_name) {
637 0 : talloc_free(ccc);
638 0 : (*error_string) = strerror(ENOMEM);
639 0 : return ENOMEM;
640 : }
641 : }
642 :
643 45976 : ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
644 : &ccc->ccache);
645 45976 : 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 45976 : if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
656 45953 : talloc_set_destructor(ccc, free_mccache);
657 : } else {
658 23 : talloc_set_destructor(ccc, free_dccache);
659 : }
660 :
661 45976 : if (must_free_cc_name) {
662 45952 : talloc_free(ccache_name);
663 : }
664 :
665 45976 : *_ccc = ccc;
666 :
667 45976 : return 0;
668 : }
669 :
670 19195 : _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 19195 : if (cred->machine_account_pending) {
681 0 : cli_credentials_set_machine_account(cred, lp_ctx);
682 : }
683 :
684 19195 : if (cred->ccache_obtained >= cred->ccache_threshold &&
685 4127 : cred->ccache_obtained > CRED_UNINITIALISED) {
686 0 : time_t lifetime;
687 4127 : bool expired = false;
688 4127 : ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
689 4127 : cred->ccache->ccache, &lifetime);
690 4127 : 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 4124 : } else if (ret == 0) {
695 4124 : 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 4124 : } 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 4127 : return ret;
709 : }
710 :
711 4127 : 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 4127 : if (!expired) {
715 4127 : *ccc = cred->ccache;
716 4127 : return 0;
717 : }
718 : }
719 15068 : if (cli_credentials_is_anonymous(cred)) {
720 0 : (*error_string) = "Cannot get anonymous kerberos credentials";
721 0 : return EINVAL;
722 : }
723 :
724 15068 : ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
725 15068 : if (ret) {
726 0 : return ret;
727 : }
728 :
729 15653 : ret = kinit_to_ccache(cred,
730 : cred,
731 14483 : (*ccc)->smb_krb5_context,
732 : lp_ctx,
733 : event_ctx,
734 15068 : (*ccc)->ccache,
735 : &obtained,
736 : error_string);
737 15068 : if (ret) {
738 1254 : return ret;
739 : }
740 :
741 13814 : ret = cli_credentials_set_from_ccache(cred, *ccc,
742 : obtained, error_string);
743 :
744 13814 : cred->ccache = *ccc;
745 13814 : cred->ccache_obtained = cred->principal_obtained;
746 13814 : if (ret) {
747 0 : return ret;
748 : }
749 13814 : cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
750 13814 : return 0;
751 : }
752 :
753 17315 : _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 17315 : 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 1589480 : 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 1589480 : if (obtained >= cred->client_gss_creds_obtained) {
778 1589464 : if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
779 4959 : talloc_unlink(cred, cred->client_gss_creds);
780 4959 : cred->client_gss_creds = NULL;
781 : }
782 1589464 : 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 1589480 : if (obtained > cred->client_gss_creds_threshold) {
789 612613 : cred->client_gss_creds_threshold = obtained;
790 : }
791 1589480 : }
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 1477463 : _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 1477463 : if (obtained >= cred->ccache_obtained) {
811 1459911 : if (cred->ccache_obtained > CRED_UNINITIALISED) {
812 72597 : talloc_unlink(cred, cred->ccache);
813 72597 : cred->ccache = NULL;
814 : }
815 1459911 : 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 1477463 : if (obtained > cred->ccache_threshold) {
822 543717 : cred->ccache_threshold = obtained;
823 : }
824 :
825 1477463 : cli_credentials_invalidate_client_gss_creds(cred,
826 : obtained);
827 1477463 : }
828 :
829 92671 : static int free_gssapi_creds(struct gssapi_creds_container *gcc)
830 : {
831 3617 : OM_uint32 min_stat;
832 92671 : (void)gss_release_cred(&min_stat, &gcc->creds);
833 92671 : return 0;
834 : }
835 :
836 32516 : _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 32516 : 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 32516 : gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
848 32516 : gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
849 : #endif
850 32516 : krb5_enctype *etypes = NULL;
851 :
852 32516 : if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
853 14965 : cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
854 15415 : bool expired = false;
855 15415 : OM_uint32 lifetime = 0;
856 15415 : gss_cred_usage_t usage = 0;
857 15415 : maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
858 : NULL, &lifetime, &usage, NULL);
859 15415 : 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 15415 : } 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 15415 : } 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 15415 : return EINVAL;
869 : }
870 15415 : if (expired) {
871 0 : cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
872 : } else {
873 15415 : DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
874 : cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
875 :
876 15415 : *_gcc = cred->client_gss_creds;
877 15415 : return 0;
878 : }
879 : }
880 :
881 17101 : ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
882 : &ccache, error_string);
883 17101 : if (ret) {
884 1248 : if (cli_credentials_get_kerberos_state(cred) == CRED_USE_KERBEROS_REQUIRED) {
885 175 : DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
886 : } else {
887 1073 : DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
888 : }
889 1248 : return ret;
890 : }
891 :
892 15853 : gcc = talloc(cred, struct gssapi_creds_container);
893 15853 : if (!gcc) {
894 0 : (*error_string) = error_message(ENOMEM);
895 0 : return ENOMEM;
896 : }
897 :
898 16438 : maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
899 15853 : ccache->ccache, NULL, NULL,
900 : &gcc->creds);
901 15853 : 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 15853 : 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 15853 : min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
947 : &etypes);
948 15853 : if (min_stat == 0) {
949 : OM_uint32 num_ktypes;
950 :
951 112227 : for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
952 :
953 15853 : maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
954 : num_ktypes,
955 : (int32_t *) etypes);
956 15853 : krb5_free_enctypes(ccache->smb_krb5_context->krb5_context,
957 : etypes);
958 15853 : 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 15853 : maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
981 : oid,
982 : &empty_buffer);
983 15853 : 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 15853 : cred->client_gss_creds_obtained = cred->ccache_obtained;
995 15853 : talloc_set_destructor(gcc, free_gssapi_creds);
996 15853 : cred->client_gss_creds = gcc;
997 15853 : *_gcc = gcc;
998 15853 : 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 30908 : 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 30908 : struct ccache_container *ccc = NULL;
1021 30908 : struct gssapi_creds_container *gcc = NULL;
1022 30908 : if (cred->client_gss_creds_obtained > obtained) {
1023 0 : return 0;
1024 : }
1025 :
1026 30908 : gcc = talloc(cred, struct gssapi_creds_container);
1027 30908 : if (!gcc) {
1028 0 : (*error_string) = error_message(ENOMEM);
1029 0 : return ENOMEM;
1030 : }
1031 :
1032 30908 : ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
1033 30908 : if (ret != 0) {
1034 0 : return ret;
1035 : }
1036 :
1037 30908 : maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
1038 : gssapi_cred,
1039 : ccc);
1040 30908 : 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 30908 : if (ret == 0) {
1052 30908 : ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
1053 : }
1054 30908 : cred->ccache = ccc;
1055 30908 : cred->ccache_obtained = obtained;
1056 30908 : if (ret == 0) {
1057 30908 : gcc->creds = gssapi_cred;
1058 30908 : 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 30908 : cred->client_gss_creds_obtained = obtained;
1063 30908 : cred->client_gss_creds = gcc;
1064 : }
1065 30027 : 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 47035 : _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 47035 : const char *username = cli_credentials_get_username(cred);
1177 47035 : const char *upn = NULL;
1178 47035 : const char *realm = cli_credentials_get_realm(cred);
1179 47035 : char *salt_principal = NULL;
1180 47035 : uint32_t uac_flags = 0;
1181 :
1182 47035 : if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
1183 : cred->username_obtained))) {
1184 46938 : *_ktc = cred->keytab;
1185 46938 : return 0;
1186 : }
1187 :
1188 97 : if (cli_credentials_is_anonymous(cred)) {
1189 0 : return EINVAL;
1190 : }
1191 :
1192 97 : ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1193 : &smb_krb5_context);
1194 97 : if (ret) {
1195 0 : return ret;
1196 : }
1197 :
1198 97 : mem_ctx = talloc_new(cred);
1199 97 : if (!mem_ctx) {
1200 0 : return ENOMEM;
1201 : }
1202 :
1203 97 : switch (cred->secure_channel_type) {
1204 49 : case SEC_CHAN_WKSTA:
1205 : case SEC_CHAN_RODC:
1206 49 : uac_flags = UF_WORKSTATION_TRUST_ACCOUNT;
1207 49 : break;
1208 30 : case SEC_CHAN_BDC:
1209 30 : uac_flags = UF_SERVER_TRUST_ACCOUNT;
1210 30 : break;
1211 0 : case SEC_CHAN_DOMAIN:
1212 : case SEC_CHAN_DNS_DOMAIN:
1213 0 : uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT;
1214 0 : break;
1215 18 : default:
1216 18 : upn = cli_credentials_get_principal(cred, mem_ctx);
1217 18 : if (upn == NULL) {
1218 0 : TALLOC_FREE(mem_ctx);
1219 0 : return ENOMEM;
1220 : }
1221 18 : uac_flags = UF_NORMAL_ACCOUNT;
1222 18 : break;
1223 : }
1224 :
1225 97 : ret = smb_krb5_salt_principal_str(realm,
1226 : username, /* sAMAccountName */
1227 : upn, /* userPrincipalName */
1228 : uac_flags,
1229 : mem_ctx,
1230 : &salt_principal);
1231 97 : if (ret) {
1232 0 : talloc_free(mem_ctx);
1233 0 : return ret;
1234 : }
1235 :
1236 97 : ret = smb_krb5_create_memory_keytab(mem_ctx,
1237 97 : smb_krb5_context->krb5_context,
1238 : cli_credentials_get_password(cred),
1239 : username,
1240 : realm,
1241 : salt_principal,
1242 : cli_credentials_get_kvno(cred),
1243 : &keytab,
1244 : &keytab_name);
1245 97 : if (ret) {
1246 0 : talloc_free(mem_ctx);
1247 0 : return ret;
1248 : }
1249 :
1250 97 : ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1251 : keytab, keytab_name, &ktc);
1252 97 : if (ret) {
1253 0 : talloc_free(mem_ctx);
1254 0 : return ret;
1255 : }
1256 :
1257 97 : cred->keytab_obtained = (MAX(cred->principal_obtained,
1258 : cred->username_obtained));
1259 :
1260 : /* We make this keytab up based on a password. Therefore
1261 : * match-by-key is acceptable, we can't match on the wrong
1262 : * principal */
1263 97 : ktc->password_based = true;
1264 :
1265 97 : talloc_steal(cred, ktc);
1266 97 : cred->keytab = ktc;
1267 97 : *_ktc = cred->keytab;
1268 97 : talloc_free(mem_ctx);
1269 97 : return ret;
1270 : }
1271 :
1272 : /* Given the name of a keytab (presumably in the format
1273 : * FILE:/etc/krb5.keytab), open it and attach it */
1274 :
1275 67005 : _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
1276 : struct loadparm_context *lp_ctx,
1277 : const char *keytab_name,
1278 : enum credentials_obtained obtained)
1279 : {
1280 2655 : krb5_error_code ret;
1281 2655 : struct keytab_container *ktc;
1282 2655 : struct smb_krb5_context *smb_krb5_context;
1283 2655 : TALLOC_CTX *mem_ctx;
1284 :
1285 67005 : if (cred->keytab_obtained >= obtained) {
1286 0 : return 0;
1287 : }
1288 :
1289 67005 : ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1290 67005 : if (ret) {
1291 0 : return ret;
1292 : }
1293 :
1294 67005 : mem_ctx = talloc_new(cred);
1295 67005 : if (!mem_ctx) {
1296 0 : return ENOMEM;
1297 : }
1298 :
1299 67005 : ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1300 : NULL, keytab_name, &ktc);
1301 67005 : if (ret) {
1302 0 : return ret;
1303 : }
1304 :
1305 67005 : cred->keytab_obtained = obtained;
1306 :
1307 67005 : talloc_steal(cred, ktc);
1308 67005 : cred->keytab = ktc;
1309 67005 : talloc_free(mem_ctx);
1310 :
1311 67005 : return ret;
1312 : }
1313 :
1314 : /* Get server gss credentials (in gsskrb5, this means the keytab) */
1315 :
1316 48536 : _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1317 : struct loadparm_context *lp_ctx,
1318 : struct gssapi_creds_container **_gcc)
1319 : {
1320 48536 : int ret = 0;
1321 2157 : OM_uint32 maj_stat, min_stat;
1322 2157 : struct gssapi_creds_container *gcc;
1323 2157 : struct keytab_container *ktc;
1324 2157 : struct smb_krb5_context *smb_krb5_context;
1325 2157 : TALLOC_CTX *mem_ctx;
1326 2157 : krb5_principal princ;
1327 2157 : const char *error_string;
1328 2157 : enum credentials_obtained obtained;
1329 :
1330 48536 : mem_ctx = talloc_new(cred);
1331 48536 : if (!mem_ctx) {
1332 0 : return ENOMEM;
1333 : }
1334 :
1335 48536 : ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1336 48536 : if (ret) {
1337 0 : return ret;
1338 : }
1339 :
1340 48536 : ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1341 48536 : if (ret) {
1342 0 : DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1343 : error_string));
1344 0 : talloc_free(mem_ctx);
1345 0 : return ret;
1346 : }
1347 :
1348 48536 : if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1349 2775 : talloc_free(mem_ctx);
1350 2775 : *_gcc = cred->server_gss_creds;
1351 2775 : return 0;
1352 : }
1353 :
1354 45761 : ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1355 45761 : if (ret) {
1356 0 : DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1357 0 : return ret;
1358 : }
1359 :
1360 45761 : gcc = talloc(cred, struct gssapi_creds_container);
1361 45761 : if (!gcc) {
1362 0 : talloc_free(mem_ctx);
1363 0 : return ENOMEM;
1364 : }
1365 :
1366 45761 : if (ktc->password_based || obtained < CRED_SPECIFIED) {
1367 : /*
1368 : * This creates a GSSAPI cred_id_t for match-by-key with only
1369 : * the keytab set
1370 : */
1371 97 : princ = NULL;
1372 : }
1373 47918 : maj_stat = smb_gss_krb5_import_cred(&min_stat,
1374 45761 : smb_krb5_context->krb5_context,
1375 : NULL, princ,
1376 43604 : ktc->keytab,
1377 : &gcc->creds);
1378 45761 : if (maj_stat) {
1379 0 : if (min_stat) {
1380 0 : ret = min_stat;
1381 : } else {
1382 0 : ret = EINVAL;
1383 : }
1384 : }
1385 45761 : if (ret == 0) {
1386 45761 : cred->server_gss_creds_obtained = cred->keytab_obtained;
1387 45761 : talloc_set_destructor(gcc, free_gssapi_creds);
1388 45761 : cred->server_gss_creds = gcc;
1389 45761 : *_gcc = gcc;
1390 : }
1391 45761 : talloc_free(mem_ctx);
1392 45761 : return ret;
1393 : }
1394 :
1395 : /**
1396 : * Set Kerberos KVNO
1397 : */
1398 :
1399 67555 : _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1400 : int kvno)
1401 : {
1402 67555 : cred->kvno = kvno;
1403 67555 : }
1404 :
1405 : /**
1406 : * Return Kerberos KVNO
1407 : */
1408 :
1409 103 : _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1410 : {
1411 103 : return cred->kvno;
1412 : }
1413 :
1414 :
1415 0 : const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1416 : {
1417 0 : return cred->salt_principal;
1418 : }
1419 :
1420 66883 : _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1421 : {
1422 66883 : talloc_free(cred->salt_principal);
1423 66883 : cred->salt_principal = talloc_strdup(cred, principal);
1424 66883 : }
1425 :
1426 : /* The 'impersonate_principal' is used to allow one Kerberos principal
1427 : * (and it's associated keytab etc) to impersonate another. The
1428 : * ability to do this is controlled by the KDC, but it is generally
1429 : * permitted to impersonate anyone to yourself. This allows any
1430 : * member of the domain to get the groups of a user. This is also
1431 : * known as S4U2Self */
1432 :
1433 46375 : _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1434 : {
1435 46375 : return cred->impersonate_principal;
1436 : }
1437 :
1438 : /*
1439 : * The 'self_service' is the service principal that
1440 : * represents the same object (by its objectSid)
1441 : * as the client principal (typically our machine account).
1442 : * When trying to impersonate 'impersonate_principal' with
1443 : * S4U2Self.
1444 : */
1445 15066 : _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1446 : {
1447 15066 : return cred->self_service;
1448 : }
1449 :
1450 55 : _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1451 : const char *principal,
1452 : const char *self_service)
1453 : {
1454 55 : talloc_free(cred->impersonate_principal);
1455 55 : cred->impersonate_principal = talloc_strdup(cred, principal);
1456 55 : talloc_free(cred->self_service);
1457 55 : cred->self_service = talloc_strdup(cred, self_service);
1458 55 : cli_credentials_set_kerberos_state(cred,
1459 : CRED_USE_KERBEROS_REQUIRED,
1460 : CRED_SPECIFIED);
1461 55 : }
1462 :
1463 : /*
1464 : * when impersonating for S4U2proxy we need to set the target principal.
1465 : * Similarly, we may only be authorized to do general impersonation to
1466 : * some particular services.
1467 : *
1468 : * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1469 : *
1470 : * NULL means that tickets will be obtained for the krbtgt service.
1471 : */
1472 :
1473 15066 : const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1474 : {
1475 15066 : return cred->target_service;
1476 : }
1477 :
1478 35 : _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1479 : {
1480 35 : talloc_free(cred->target_service);
1481 35 : cred->target_service = talloc_strdup(cred, target_service);
1482 35 : }
1483 :
1484 114 : _PUBLIC_ int cli_credentials_get_aes256_key(struct cli_credentials *cred,
1485 : TALLOC_CTX *mem_ctx,
1486 : struct loadparm_context *lp_ctx,
1487 : const char *salt,
1488 : DATA_BLOB *aes_256)
1489 : {
1490 114 : struct smb_krb5_context *smb_krb5_context = NULL;
1491 0 : krb5_error_code krb5_ret;
1492 0 : int ret;
1493 114 : const char *password = NULL;
1494 0 : krb5_data cleartext_data;
1495 114 : krb5_data salt_data = {
1496 : .length = 0,
1497 : };
1498 0 : krb5_keyblock key;
1499 :
1500 114 : if (cred->password_will_be_nt_hash) {
1501 0 : DEBUG(1,("cli_credentials_get_aes256_key: cannot generate AES256 key using NT hash\n"));
1502 0 : return EINVAL;
1503 : }
1504 :
1505 114 : password = cli_credentials_get_password(cred);
1506 114 : if (password == NULL) {
1507 0 : return EINVAL;
1508 : }
1509 :
1510 114 : cleartext_data.data = discard_const_p(char, password);
1511 114 : cleartext_data.length = strlen(password);
1512 :
1513 114 : ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1514 : &smb_krb5_context);
1515 114 : if (ret != 0) {
1516 0 : return ret;
1517 : }
1518 :
1519 114 : salt_data.data = discard_const_p(char, salt);
1520 114 : salt_data.length = strlen(salt);
1521 :
1522 : /*
1523 : * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
1524 : * the salt and the cleartext password
1525 : */
1526 114 : krb5_ret = smb_krb5_create_key_from_string(smb_krb5_context->krb5_context,
1527 : NULL,
1528 : &salt_data,
1529 : &cleartext_data,
1530 : ENCTYPE_AES256_CTS_HMAC_SHA1_96,
1531 : &key);
1532 114 : if (krb5_ret != 0) {
1533 0 : DEBUG(1,("cli_credentials_get_aes256_key: "
1534 : "generation of a aes256-cts-hmac-sha1-96 key failed: %s\n",
1535 : smb_get_krb5_error_message(smb_krb5_context->krb5_context,
1536 : krb5_ret, mem_ctx)));
1537 0 : return EINVAL;
1538 : }
1539 114 : *aes_256 = data_blob_talloc(mem_ctx,
1540 : KRB5_KEY_DATA(&key),
1541 : KRB5_KEY_LENGTH(&key));
1542 114 : krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &key);
1543 114 : if (aes_256->data == NULL) {
1544 0 : return ENOMEM;
1545 : }
1546 114 : talloc_keep_secret(aes_256->data);
1547 :
1548 114 : return 0;
1549 : }
1550 :
1551 : /* This take a reference to the armor credentials to ensure the lifetime is appropriate */
1552 :
1553 13 : NTSTATUS cli_credentials_set_krb5_fast_armor_credentials(struct cli_credentials *creds,
1554 : struct cli_credentials *armor_creds,
1555 : bool require_fast_armor)
1556 : {
1557 13 : talloc_unlink(creds, creds->krb5_fast_armor_credentials);
1558 13 : if (armor_creds == NULL) {
1559 2 : creds->krb5_fast_armor_credentials = NULL;
1560 2 : return NT_STATUS_OK;
1561 : }
1562 :
1563 11 : creds->krb5_fast_armor_credentials = talloc_reference(creds, armor_creds);
1564 11 : if (creds->krb5_fast_armor_credentials == NULL) {
1565 0 : return NT_STATUS_NO_MEMORY;
1566 : }
1567 :
1568 11 : creds->krb5_require_fast_armor = require_fast_armor;
1569 :
1570 11 : return NT_STATUS_OK;
1571 : }
1572 :
1573 15066 : struct cli_credentials *cli_credentials_get_krb5_fast_armor_credentials(struct cli_credentials *creds)
1574 : {
1575 15066 : return creds->krb5_fast_armor_credentials;
1576 : }
1577 :
1578 15066 : bool cli_credentials_get_krb5_require_fast_armor(struct cli_credentials *creds)
1579 : {
1580 15066 : return creds->krb5_require_fast_armor;
1581 : }
|