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