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 29642 : static int free_mccache(struct ccache_container *ccc)
49 : {
50 29642 : if (ccc->ccache != NULL) {
51 29642 : krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
52 : ccc->ccache);
53 29642 : ccc->ccache = NULL;
54 : }
55 :
56 29642 : 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 21546 : 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 3825 : krb5_context context = ccc->smb_krb5_context->krb5_context;
77 3825 : krb5_ccache dummy_ccache = NULL;
78 3825 : krb5_creds creds = {0};
79 3825 : krb5_cc_cursor cursor = NULL;
80 3825 : krb5_principal princ = NULL;
81 : krb5_error_code code;
82 : char *dummy_name;
83 3825 : uint32_t maj_stat = GSS_S_FAILURE;
84 :
85 3825 : dummy_name = talloc_asprintf(ccc,
86 : "MEMORY:gss_krb5_copy_ccache-%p",
87 : &ccc->ccache);
88 3825 : 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 3825 : code = krb5_cc_resolve(context, dummy_name, &dummy_ccache);
100 3825 : TALLOC_FREE(dummy_name);
101 3825 : 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 3825 : maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache);
111 3825 : if (maj_stat != 0) {
112 0 : krb5_cc_close(context, dummy_ccache);
113 0 : return maj_stat;
114 : }
115 :
116 3825 : code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor);
117 3825 : 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 3825 : code = krb5_cc_next_cred(context,
124 : dummy_ccache,
125 : &cursor,
126 : &creds);
127 3825 : 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 3825 : if (creds.ticket_flags & TKT_FLG_PRE_AUTH) {
135 : krb5_data *tgs;
136 :
137 3825 : tgs = krb5_princ_component(context,
138 : creds.server,
139 : 0);
140 3825 : if (tgs != NULL && tgs->length >= 1) {
141 : int cmp;
142 :
143 3825 : cmp = memcmp(tgs->data,
144 : KRB5_TGS_NAME,
145 3825 : tgs->length);
146 3825 : if (cmp == 0 && creds.client != NULL) {
147 3825 : princ = creds.client;
148 3825 : code = KRB5_CC_END;
149 3825 : 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 3825 : if (code == KRB5_CC_END) {
163 3825 : krb5_cc_end_seq_get(context, dummy_ccache, &cursor);
164 3825 : code = 0;
165 : }
166 3825 : krb5_cc_close(context, dummy_ccache);
167 :
168 3825 : 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 3825 : code = krb5_cc_initialize(context, ccc->ccache, princ);
181 3825 : if (code != 0) {
182 0 : krb5_free_cred_contents(context, &creds);
183 0 : *min_stat = EINVAL;
184 0 : return GSS_S_FAILURE;
185 : }
186 3825 : krb5_free_cred_contents(context, &creds);
187 :
188 : #endif /* SAMBA4_USES_HEIMDAL */
189 :
190 21546 : return gss_krb5_copy_ccache(min_stat,
191 : cred,
192 : ccc->ccache);
193 : }
194 :
195 120414 : _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 120414 : if (cred->smb_krb5_context) {
201 41110 : *smb_krb5_context = cred->smb_krb5_context;
202 41110 : return 0;
203 : }
204 :
205 79304 : ret = smb_krb5_init_context(cred, lp_ctx,
206 : &cred->smb_krb5_context);
207 79304 : if (ret) {
208 0 : cred->smb_krb5_context = NULL;
209 0 : return ret;
210 : }
211 79304 : *smb_krb5_context = cred->smb_krb5_context;
212 79304 : 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 33918 : 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 33918 : if (cred->ccache_obtained > obtained) {
246 2831 : return 0;
247 : }
248 :
249 31087 : ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
250 : ccache->ccache, &princ);
251 :
252 31087 : 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 31087 : ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
260 31087 : 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 31087 : ok = cli_credentials_set_principal(cred, name, obtained);
268 31087 : krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
269 31087 : if (!ok) {
270 19 : krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
271 19 : return ENOMEM;
272 : }
273 :
274 56928 : realm = smb_krb5_principal_get_realm(
275 31068 : cred, ccache->smb_krb5_context->krb5_context, princ);
276 31068 : krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
277 31068 : if (realm == NULL) {
278 0 : return ENOMEM;
279 : }
280 31068 : ok = cli_credentials_set_realm(cred, realm, obtained);
281 31068 : TALLOC_FREE(realm);
282 31068 : 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 31056 : cred->ccache_obtained = obtained;
288 :
289 31056 : return 0;
290 : }
291 :
292 19795 : _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 19795 : if (cred->ccache_obtained > obtained) {
302 2033 : return 0;
303 : }
304 :
305 17762 : ccc = talloc(cred, struct ccache_container);
306 17762 : if (!ccc) {
307 0 : (*error_string) = error_message(ENOMEM);
308 0 : return ENOMEM;
309 : }
310 :
311 17762 : ret = cli_credentials_get_krb5_context(cred, lp_ctx,
312 : &ccc->smb_krb5_context);
313 17762 : if (ret) {
314 0 : (*error_string) = error_message(ret);
315 0 : talloc_free(ccc);
316 0 : return ret;
317 : }
318 17762 : 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 17762 : if (name) {
325 2064 : ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
326 2064 : 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 15698 : ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
336 15698 : 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 17762 : talloc_set_destructor(ccc, free_dccache);
346 :
347 17762 : ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
348 :
349 17762 : if (ret == 0) {
350 4949 : krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
351 4949 : ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
352 :
353 4949 : if (ret) {
354 31 : (*error_string) = error_message(ret);
355 31 : TALLOC_FREE(ccc);
356 31 : return ret;
357 : }
358 : }
359 :
360 17731 : cred->ccache = ccc;
361 17731 : cred->ccache_obtained = obtained;
362 :
363 17731 : cli_credentials_invalidate_client_gss_creds(
364 : cred, cred->ccache_obtained);
365 :
366 17731 : 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 29354 : 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 29354 : bool must_free_cc_name = false;
604 : krb5_error_code ret;
605 29354 : struct ccache_container *ccc = talloc(cred, struct ccache_container);
606 29354 : if (!ccc) {
607 0 : return ENOMEM;
608 : }
609 :
610 29354 : ret = cli_credentials_get_krb5_context(cred, lp_ctx,
611 : &ccc->smb_krb5_context);
612 29354 : 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 29354 : 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 29354 : if (!ccache_name) {
625 29330 : must_free_cc_name = true;
626 :
627 29330 : 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 29330 : ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
632 : ccc);
633 : }
634 :
635 29330 : if (!ccache_name) {
636 0 : talloc_free(ccc);
637 0 : (*error_string) = strerror(ENOMEM);
638 0 : return ENOMEM;
639 : }
640 : }
641 :
642 29354 : ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
643 : &ccc->ccache);
644 29354 : 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 29354 : if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
655 29331 : talloc_set_destructor(ccc, free_mccache);
656 : } else {
657 23 : talloc_set_destructor(ccc, free_dccache);
658 : }
659 :
660 29354 : if (must_free_cc_name) {
661 29330 : talloc_free(ccache_name);
662 : }
663 :
664 29354 : *_ccc = ccc;
665 :
666 29354 : return 0;
667 : }
668 :
669 11890 : _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 11890 : if (cred->machine_account_pending) {
680 0 : cli_credentials_set_machine_account(cred, lp_ctx);
681 : }
682 :
683 15831 : if (cred->ccache_obtained >= cred->ccache_threshold &&
684 4082 : cred->ccache_obtained > CRED_UNINITIALISED) {
685 : time_t lifetime;
686 4082 : bool expired = false;
687 4082 : ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
688 4082 : cred->ccache->ccache, &lifetime);
689 4082 : 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 4079 : } else if (ret == 0) {
694 4079 : 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 4079 : } 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 4082 : return ret;
708 : }
709 :
710 4082 : 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 4082 : if (!expired) {
714 4082 : *ccc = cred->ccache;
715 4082 : return 0;
716 : }
717 : }
718 7808 : if (cli_credentials_is_anonymous(cred)) {
719 0 : (*error_string) = "Cannot get anonymous kerberos credentials";
720 0 : return EINVAL;
721 : }
722 :
723 7808 : ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
724 7808 : if (ret) {
725 0 : return ret;
726 : }
727 :
728 7808 : ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
729 7808 : if (ret) {
730 385 : return ret;
731 : }
732 :
733 7423 : ret = cli_credentials_set_from_ccache(cred, *ccc,
734 : obtained, error_string);
735 :
736 7423 : cred->ccache = *ccc;
737 7423 : cred->ccache_obtained = cred->principal_obtained;
738 7423 : if (ret) {
739 0 : return ret;
740 : }
741 7423 : cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
742 7423 : return 0;
743 : }
744 :
745 10016 : _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 10016 : 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 720956 : 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 720956 : if (obtained >= cred->client_gss_creds_obtained) {
770 720940 : if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
771 1870 : talloc_unlink(cred, cred->client_gss_creds);
772 1870 : cred->client_gss_creds = NULL;
773 : }
774 720940 : 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 720956 : if (obtained > cred->client_gss_creds_threshold) {
781 247972 : cred->client_gss_creds_threshold = obtained;
782 : }
783 720956 : }
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 695802 : _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 695802 : if (obtained >= cred->ccache_obtained) {
803 684181 : if (cred->ccache_obtained > CRED_UNINITIALISED) {
804 9641 : talloc_unlink(cred, cred->ccache);
805 9641 : cred->ccache = NULL;
806 : }
807 684181 : 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 695802 : if (obtained > cred->ccache_threshold) {
814 241280 : cred->ccache_threshold = obtained;
815 : }
816 :
817 695802 : cli_credentials_invalidate_client_gss_creds(cred,
818 : obtained);
819 695802 : }
820 :
821 59312 : static int free_gssapi_creds(struct gssapi_creds_container *gcc)
822 : {
823 : OM_uint32 min_stat;
824 59312 : (void)gss_release_cred(&min_stat, &gcc->creds);
825 59312 : return 0;
826 : }
827 :
828 22942 : _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 22942 : 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 22942 : gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
840 22942 : gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
841 : #endif
842 22942 : krb5_enctype *etypes = NULL;
843 :
844 32477 : if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
845 13127 : cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
846 13127 : bool expired = false;
847 13127 : OM_uint32 lifetime = 0;
848 13127 : gss_cred_usage_t usage = 0;
849 13127 : maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
850 : NULL, &lifetime, &usage, NULL);
851 13127 : 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 13127 : } 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 13127 : } 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 13127 : return EINVAL;
861 : }
862 13127 : if (expired) {
863 0 : cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
864 : } else {
865 13127 : DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
866 : cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
867 :
868 13127 : *_gcc = cred->client_gss_creds;
869 13127 : return 0;
870 : }
871 : }
872 :
873 9815 : ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
874 : &ccache, error_string);
875 9815 : if (ret) {
876 385 : 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 230 : DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
880 : }
881 385 : return ret;
882 : }
883 :
884 9430 : gcc = talloc(cred, struct gssapi_creds_container);
885 9430 : if (!gcc) {
886 0 : (*error_string) = error_message(ENOMEM);
887 0 : return ENOMEM;
888 : }
889 :
890 18025 : maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
891 9430 : ccache->ccache, NULL, NULL,
892 : &gcc->creds);
893 9430 : 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 9430 : 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 9430 : min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
939 : &etypes);
940 9430 : if (min_stat == 0) {
941 : OM_uint32 num_ktypes;
942 :
943 14494 : for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
944 :
945 9430 : maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
946 : num_ktypes,
947 : (int32_t *) etypes);
948 9430 : SAFE_FREE(etypes);
949 9430 : 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 9430 : maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
972 : oid,
973 : &empty_buffer);
974 9430 : 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 9430 : cred->client_gss_creds_obtained = cred->ccache_obtained;
986 9430 : talloc_set_destructor(gcc, free_gssapi_creds);
987 9430 : cred->client_gss_creds = gcc;
988 9430 : *_gcc = gcc;
989 9430 : 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 21546 : 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 21546 : struct ccache_container *ccc = NULL;
1012 21546 : struct gssapi_creds_container *gcc = NULL;
1013 21546 : if (cred->client_gss_creds_obtained > obtained) {
1014 0 : return 0;
1015 : }
1016 :
1017 21546 : gcc = talloc(cred, struct gssapi_creds_container);
1018 21546 : if (!gcc) {
1019 0 : (*error_string) = error_message(ENOMEM);
1020 0 : return ENOMEM;
1021 : }
1022 :
1023 21546 : ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
1024 21546 : if (ret != 0) {
1025 0 : return ret;
1026 : }
1027 :
1028 21546 : maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
1029 : gssapi_cred,
1030 : ccc);
1031 21546 : 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 21546 : if (ret == 0) {
1043 21546 : ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
1044 : }
1045 21546 : cred->ccache = ccc;
1046 21546 : cred->ccache_obtained = obtained;
1047 21546 : if (ret == 0) {
1048 21546 : gcc->creds = gssapi_cred;
1049 21546 : 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 21546 : cred->client_gss_creds_obtained = obtained;
1054 21546 : cred->client_gss_creds = gcc;
1055 : }
1056 21546 : 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 284 : ret = krb5_cc_get_principal(
1080 176 : old_ccc->smb_krb5_context->krb5_context,
1081 68 : 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 68 : 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 29382 : _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 29382 : const char *username = cli_credentials_get_username(cred);
1160 29382 : const char *upn = NULL;
1161 29382 : const char *realm = cli_credentials_get_realm(cred);
1162 29382 : char *salt_principal = NULL;
1163 29382 : uint32_t uac_flags = 0;
1164 :
1165 29382 : if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
1166 : cred->username_obtained))) {
1167 29315 : *_ktc = cred->keytab;
1168 29315 : 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 115 : 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 40933 : _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 40933 : if (cred->keytab_obtained >= obtained) {
1269 0 : return 0;
1270 : }
1271 :
1272 40933 : ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1273 40933 : if (ret) {
1274 0 : return ret;
1275 : }
1276 :
1277 40933 : mem_ctx = talloc_new(cred);
1278 40933 : if (!mem_ctx) {
1279 0 : return ENOMEM;
1280 : }
1281 :
1282 40933 : ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1283 : NULL, keytab_name, &ktc);
1284 40933 : if (ret) {
1285 0 : return ret;
1286 : }
1287 :
1288 40933 : cred->keytab_obtained = obtained;
1289 :
1290 40933 : talloc_steal(cred, ktc);
1291 40933 : cred->keytab = ktc;
1292 40933 : talloc_free(mem_ctx);
1293 :
1294 40933 : return ret;
1295 : }
1296 :
1297 : /* Get server gss credentials (in gsskrb5, this means the keytab) */
1298 :
1299 30910 : _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 30910 : 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 30910 : mem_ctx = talloc_new(cred);
1314 30910 : if (!mem_ctx) {
1315 0 : return ENOMEM;
1316 : }
1317 :
1318 30910 : ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1319 30910 : if (ret) {
1320 0 : return ret;
1321 : }
1322 :
1323 30910 : ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1324 30910 : 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 30910 : if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1332 2720 : talloc_free(mem_ctx);
1333 2720 : *_gcc = cred->server_gss_creds;
1334 2720 : return 0;
1335 : }
1336 :
1337 28190 : ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1338 28190 : 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 28190 : gcc = talloc(cred, struct gssapi_creds_container);
1344 28190 : if (!gcc) {
1345 0 : talloc_free(mem_ctx);
1346 0 : return ENOMEM;
1347 : }
1348 :
1349 28190 : 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 73388 : maj_stat = smb_gss_krb5_import_cred(&min_stat,
1357 28190 : smb_krb5_context->krb5_context,
1358 : NULL, princ,
1359 28190 : ktc->keytab,
1360 : &gcc->creds);
1361 28190 : if (maj_stat) {
1362 0 : if (min_stat) {
1363 0 : ret = min_stat;
1364 : } else {
1365 0 : ret = EINVAL;
1366 : }
1367 : }
1368 28190 : if (ret == 0) {
1369 28190 : cred->server_gss_creds_obtained = cred->keytab_obtained;
1370 28190 : talloc_set_destructor(gcc, free_gssapi_creds);
1371 28190 : cred->server_gss_creds = gcc;
1372 28190 : *_gcc = gcc;
1373 : }
1374 28190 : talloc_free(mem_ctx);
1375 28190 : return ret;
1376 : }
1377 :
1378 : /**
1379 : * Set Kerberos KVNO
1380 : */
1381 :
1382 41273 : _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1383 : int kvno)
1384 : {
1385 41273 : cred->kvno = kvno;
1386 41273 : }
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 1 : const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1399 : {
1400 1 : return cred->salt_principal;
1401 : }
1402 :
1403 40891 : _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1404 : {
1405 40891 : talloc_free(cred->salt_principal);
1406 40891 : cred->salt_principal = talloc_strdup(cred, principal);
1407 40891 : }
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 29492 : _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1417 : {
1418 29492 : 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 7806 : _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1429 : {
1430 7806 : 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 7806 : const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1457 : {
1458 7806 : 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 : }
|