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