Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Kerberos utility functions for GENSEC
5 :
6 : Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "includes.h"
24 : #include "system/kerberos.h"
25 : #include "auth/kerberos/kerberos.h"
26 : #include "auth/credentials/credentials.h"
27 : #include "auth/credentials/credentials_krb5.h"
28 : #include "auth/kerberos/kerberos_credentials.h"
29 : #include "auth/kerberos/kerberos_util.h"
30 :
31 : struct principal_container {
32 : struct smb_krb5_context *smb_krb5_context;
33 : krb5_principal principal;
34 : const char *string_form; /* Optional */
35 : };
36 :
37 65913 : static krb5_error_code free_principal(struct principal_container *pc)
38 : {
39 : /* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
40 65913 : krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
41 :
42 65913 : return 0;
43 : }
44 :
45 :
46 81421 : static krb5_error_code parse_principal(TALLOC_CTX *parent_ctx,
47 : const char *princ_string,
48 : struct smb_krb5_context *smb_krb5_context,
49 : krb5_principal *princ,
50 : const char **error_string)
51 : {
52 3327 : int ret;
53 3327 : struct principal_container *mem_ctx;
54 81421 : if (princ_string == NULL) {
55 15508 : *princ = NULL;
56 15508 : return 0;
57 : }
58 :
59 : /*
60 : * Start with talloc(), talloc_reference() and only then call
61 : * krb5_parse_name(). If any of them fails, the cleanup code is simpler.
62 : */
63 65913 : mem_ctx = talloc(parent_ctx, struct principal_container);
64 65913 : if (!mem_ctx) {
65 0 : (*error_string) = error_message(ENOMEM);
66 0 : return ENOMEM;
67 : }
68 :
69 65913 : mem_ctx->smb_krb5_context = talloc_reference(mem_ctx,
70 : smb_krb5_context);
71 65913 : if (mem_ctx->smb_krb5_context == NULL) {
72 0 : (*error_string) = error_message(ENOMEM);
73 0 : talloc_free(mem_ctx);
74 0 : return ENOMEM;
75 : }
76 :
77 65913 : ret = krb5_parse_name(smb_krb5_context->krb5_context,
78 : princ_string, princ);
79 :
80 65913 : if (ret) {
81 0 : (*error_string) = smb_get_krb5_error_message(
82 : smb_krb5_context->krb5_context,
83 : ret, parent_ctx);
84 0 : talloc_free(mem_ctx);
85 0 : return ret;
86 : }
87 :
88 : /* This song-and-dance effectively puts the principal
89 : * into talloc, so we can't lose it. */
90 65913 : mem_ctx->principal = *princ;
91 65913 : talloc_set_destructor(mem_ctx, free_principal);
92 65913 : return 0;
93 : }
94 :
95 : /* Obtain the principal set on this context. Requires a
96 : * smb_krb5_context because we are doing krb5 principal parsing with
97 : * the library routines. The returned princ is placed in the talloc
98 : * system by means of a destructor (do *not* free). */
99 :
100 65884 : krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
101 : struct cli_credentials *credentials,
102 : struct smb_krb5_context *smb_krb5_context,
103 : krb5_principal *princ,
104 : enum credentials_obtained *obtained,
105 : const char **error_string)
106 : {
107 2742 : krb5_error_code ret;
108 2742 : const char *princ_string;
109 65884 : TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
110 65884 : *obtained = CRED_UNINITIALISED;
111 :
112 65884 : if (!mem_ctx) {
113 0 : (*error_string) = error_message(ENOMEM);
114 0 : return ENOMEM;
115 : }
116 65884 : princ_string = cli_credentials_get_principal_and_obtained(credentials,
117 : mem_ctx,
118 : obtained);
119 65884 : if (!princ_string) {
120 6 : *princ = NULL;
121 6 : return 0;
122 : }
123 :
124 65878 : ret = parse_principal(parent_ctx, princ_string,
125 : smb_krb5_context, princ, error_string);
126 65878 : talloc_free(mem_ctx);
127 65878 : return ret;
128 : }
129 :
130 : /* Obtain the principal set on this context. Requires a
131 : * smb_krb5_context because we are doing krb5 principal parsing with
132 : * the library routines. The returned princ is placed in the talloc
133 : * system by means of a destructor (do *not* free). */
134 :
135 15543 : static krb5_error_code impersonate_principal_from_credentials(
136 : TALLOC_CTX *parent_ctx,
137 : struct cli_credentials *credentials,
138 : struct smb_krb5_context *smb_krb5_context,
139 : krb5_principal *princ,
140 : const char **error_string)
141 : {
142 15543 : return parse_principal(parent_ctx,
143 : cli_credentials_get_impersonate_principal(credentials),
144 : smb_krb5_context, princ, error_string);
145 : }
146 :
147 348 : krb5_error_code smb_krb5_create_principals_array(TALLOC_CTX *mem_ctx,
148 : krb5_context context,
149 : const char *account_name,
150 : const char *realm,
151 : uint32_t num_spns,
152 : const char *spns[],
153 : uint32_t *pnum_principals,
154 : krb5_principal **pprincipals,
155 : const char **error_string)
156 : {
157 26 : krb5_error_code code;
158 26 : TALLOC_CTX *tmp_ctx;
159 348 : uint32_t num_principals = 0;
160 26 : krb5_principal *principals;
161 26 : uint32_t i;
162 :
163 348 : tmp_ctx = talloc_new(mem_ctx);
164 348 : if (tmp_ctx == NULL) {
165 0 : *error_string = "Cannot allocate tmp_ctx";
166 0 : return ENOMEM;
167 : }
168 :
169 348 : if (realm == NULL) {
170 0 : *error_string = "Cannot create principal without a realm";
171 0 : code = EINVAL;
172 0 : goto done;
173 : }
174 :
175 348 : if (account_name == NULL && (num_spns == 0 || spns == NULL)) {
176 0 : *error_string = "Cannot create principal without an account or SPN";
177 0 : code = EINVAL;
178 0 : goto done;
179 : }
180 :
181 348 : if (account_name != NULL && account_name[0] != '\0') {
182 348 : num_principals++;
183 : }
184 348 : num_principals += num_spns;
185 :
186 348 : principals = talloc_zero_array(tmp_ctx,
187 : krb5_principal,
188 : num_principals);
189 348 : if (principals == NULL) {
190 0 : *error_string = "Cannot allocate principals";
191 0 : code = ENOMEM;
192 0 : goto done;
193 : }
194 :
195 830 : for (i = 0; i < num_spns; i++) {
196 482 : code = krb5_parse_name(context, spns[i], &(principals[i]));
197 482 : if (code != 0) {
198 0 : *error_string = smb_get_krb5_error_message(context,
199 : code,
200 : mem_ctx);
201 0 : goto done;
202 : }
203 : }
204 :
205 348 : if (account_name != NULL && account_name[0] != '\0') {
206 374 : code = smb_krb5_make_principal(context,
207 348 : &(principals[i]),
208 : realm,
209 : account_name,
210 : NULL);
211 348 : if (code != 0) {
212 0 : *error_string = smb_get_krb5_error_message(context,
213 : code,
214 : mem_ctx);
215 0 : goto done;
216 : }
217 : }
218 :
219 348 : if (pnum_principals != NULL) {
220 348 : *pnum_principals = num_principals;
221 :
222 348 : if (pprincipals != NULL) {
223 348 : *pprincipals = talloc_steal(mem_ctx, principals);
224 : }
225 : }
226 :
227 322 : code = 0;
228 348 : done:
229 348 : talloc_free(tmp_ctx);
230 348 : return code;
231 : }
232 :
233 : /**
234 : * Return a freshly allocated ccache (destroyed by destructor on child
235 : * of parent_ctx), for a given set of client credentials
236 : */
237 :
238 15545 : krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
239 : struct cli_credentials *credentials,
240 : struct smb_krb5_context *smb_krb5_context,
241 : struct loadparm_context *lp_ctx,
242 : struct tevent_context *event_ctx,
243 : krb5_ccache ccache,
244 : enum credentials_obtained *obtained,
245 : const char **error_string)
246 : {
247 585 : krb5_error_code ret;
248 585 : const char *password;
249 585 : const char *self_service;
250 585 : const char *target_service;
251 15545 : time_t kdc_time = 0;
252 585 : krb5_principal princ;
253 585 : krb5_principal impersonate_principal;
254 585 : int tries;
255 15545 : TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
256 585 : krb5_get_init_creds_opt *krb_options;
257 585 : struct cli_credentials *fast_creds;
258 :
259 15545 : if (!mem_ctx) {
260 0 : (*error_string) = strerror(ENOMEM);
261 0 : return ENOMEM;
262 : }
263 :
264 15545 : ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ, obtained, error_string);
265 15545 : if (ret) {
266 0 : talloc_free(mem_ctx);
267 0 : return ret;
268 : }
269 :
270 15545 : if (princ == NULL) {
271 2 : (*error_string) = talloc_asprintf(credentials, "principal, username or realm was not specified in the credentials");
272 2 : talloc_free(mem_ctx);
273 2 : return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
274 : }
275 :
276 15543 : ret = impersonate_principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &impersonate_principal, error_string);
277 15543 : if (ret) {
278 0 : talloc_free(mem_ctx);
279 0 : return ret;
280 : }
281 :
282 15543 : self_service = cli_credentials_get_self_service(credentials);
283 15543 : target_service = cli_credentials_get_target_service(credentials);
284 :
285 15543 : password = cli_credentials_get_password(credentials);
286 :
287 : /* setup the krb5 options we want */
288 15543 : if ((ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options))) {
289 0 : (*error_string) = talloc_asprintf(credentials, "krb5_get_init_creds_opt_alloc failed (%s)\n",
290 : smb_get_krb5_error_message(smb_krb5_context->krb5_context,
291 : ret, mem_ctx));
292 0 : talloc_free(mem_ctx);
293 0 : return ret;
294 : }
295 :
296 : #ifdef SAMBA4_USES_HEIMDAL /* Disable for now MIT reads defaults when needed */
297 : /* get the defaults */
298 11874 : krb5_get_init_creds_opt_set_default_flags(smb_krb5_context->krb5_context, NULL, NULL, krb_options);
299 : #endif
300 : /* set if we want a forwardable ticket */
301 15543 : switch (cli_credentials_get_krb_forwardable(credentials)) {
302 14956 : case CRED_AUTO_KRB_FORWARDABLE:
303 14956 : break;
304 2 : case CRED_NO_KRB_FORWARDABLE:
305 2 : krb5_get_init_creds_opt_set_forwardable(krb_options, FALSE);
306 2 : break;
307 0 : case CRED_FORCE_KRB_FORWARDABLE:
308 0 : krb5_get_init_creds_opt_set_forwardable(krb_options, TRUE);
309 0 : break;
310 : }
311 :
312 : #ifdef SAMBA4_USES_HEIMDAL /* FIXME: MIT does not have this yet */
313 : /*
314 : * In order to work against windows KDCs even if we use
315 : * the netbios domain name as realm, we need to add the following
316 : * flags:
317 : * KRB5_INIT_CREDS_NO_C_CANON_CHECK;
318 : * KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK;
319 : *
320 : * On MIT: Set pkinit_eku_checking to none
321 : */
322 11874 : krb5_get_init_creds_opt_set_win2k(smb_krb5_context->krb5_context,
323 : krb_options, true);
324 11874 : krb5_get_init_creds_opt_set_canonicalize(smb_krb5_context->krb5_context,
325 : krb_options, true);
326 : #else /* MIT */
327 3669 : krb5_get_init_creds_opt_set_canonicalize(krb_options, true);
328 : #endif
329 :
330 15543 : fast_creds = cli_credentials_get_krb5_fast_armor_credentials(credentials);
331 :
332 15543 : if (fast_creds != NULL) {
333 : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_CCACHE
334 11 : struct ccache_container *fast_ccc = NULL;
335 11 : const char *fast_error_string = NULL;
336 11 : ret = cli_credentials_get_ccache(fast_creds, event_ctx, lp_ctx, &fast_ccc, &fast_error_string);
337 11 : if (ret != 0) {
338 0 : (*error_string) = talloc_asprintf(credentials,
339 : "Obtaining the Kerberos FAST armor credentials failed: %s\n",
340 : fast_error_string);
341 0 : return ret;
342 : }
343 11 : krb5_get_init_creds_opt_set_fast_ccache(smb_krb5_context->krb5_context,
344 : krb_options,
345 11 : fast_ccc->ccache);
346 : #else
347 0 : *error_string = talloc_strdup(credentials,
348 : "Using Kerberos FAST "
349 : "armor credentials not possible "
350 : "with this Kerberos library. "
351 : "Modern MIT or Samba's embedded "
352 : "Heimdal required");
353 0 : return EINVAL;
354 : #endif
355 : }
356 :
357 : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_FLAGS
358 : {
359 585 : bool require_fast;
360 : /*
361 : * This ensures that if FAST was required, but no armor
362 : * credentials cache was specified, we proceed with (eg)
363 : * anonymous PKINIT
364 : */
365 15543 : require_fast = cli_credentials_get_krb5_require_fast_armor(credentials);
366 15543 : if (require_fast) {
367 11 : krb5_get_init_creds_opt_set_fast_flags(smb_krb5_context->krb5_context,
368 : krb_options,
369 : KRB5_FAST_REQUIRED);
370 : }
371 : }
372 : #endif
373 :
374 14958 : tries = 2;
375 15543 : while (tries--) {
376 : #ifdef SAMBA4_USES_HEIMDAL
377 585 : struct tevent_context *previous_ev;
378 : /* Do this every time, in case we have weird recursive issues here */
379 11874 : ret = smb_krb5_context_set_event_ctx(smb_krb5_context, event_ctx, &previous_ev);
380 11874 : if (ret) {
381 0 : talloc_free(mem_ctx);
382 0 : krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
383 1 : return ret;
384 : }
385 : #endif
386 15543 : if (password) {
387 15523 : if (impersonate_principal) {
388 35 : ret = smb_krb5_kinit_s4u2_ccache(smb_krb5_context->krb5_context,
389 : ccache,
390 : princ,
391 : password,
392 : impersonate_principal,
393 : self_service,
394 : target_service,
395 : krb_options,
396 : NULL,
397 : &kdc_time);
398 : } else {
399 15488 : ret = smb_krb5_kinit_password_ccache(smb_krb5_context->krb5_context,
400 : ccache,
401 : princ,
402 : password,
403 : target_service,
404 : krb_options,
405 : NULL,
406 : &kdc_time);
407 : }
408 20 : } else if (impersonate_principal) {
409 0 : talloc_free(mem_ctx);
410 0 : (*error_string) = "INTERNAL error: Cannot impersonate principal with just a keyblock. A password must be specified in the credentials";
411 0 : krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
412 : #ifdef SAMBA4_USES_HEIMDAL
413 0 : smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
414 : #endif
415 0 : return EINVAL;
416 : } else {
417 : /* No password available, try to use a keyblock instead */
418 :
419 3 : krb5_keyblock keyblock;
420 3 : const struct samr_Password *mach_pwd;
421 20 : mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
422 20 : if (!mach_pwd) {
423 2 : talloc_free(mem_ctx);
424 2 : (*error_string) = "kinit_to_ccache: No password available for kinit\n";
425 2 : krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
426 : #ifdef SAMBA4_USES_HEIMDAL
427 1 : smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
428 : #endif
429 2 : return EINVAL;
430 : }
431 21 : ret = smb_krb5_keyblock_init_contents(smb_krb5_context->krb5_context,
432 : ENCTYPE_ARCFOUR_HMAC,
433 18 : mach_pwd->hash, sizeof(mach_pwd->hash),
434 : &keyblock);
435 :
436 18 : if (ret == 0) {
437 18 : ret = smb_krb5_kinit_keyblock_ccache(smb_krb5_context->krb5_context,
438 : ccache,
439 : princ,
440 : &keyblock,
441 : target_service,
442 : krb_options,
443 : NULL,
444 : &kdc_time);
445 18 : krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
446 : }
447 : }
448 :
449 : #ifdef SAMBA4_USES_HEIMDAL
450 11873 : smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
451 : #endif
452 :
453 15541 : if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
454 : /* Perhaps we have been given an invalid skew, so try again without it */
455 0 : time_t t = time(NULL);
456 0 : krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
457 : } else {
458 : /* not a skew problem */
459 : break;
460 : }
461 : }
462 :
463 15541 : krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
464 :
465 15541 : if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
466 0 : (*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
467 : cli_credentials_get_principal(credentials, mem_ctx),
468 : smb_get_krb5_error_message(smb_krb5_context->krb5_context,
469 : ret, mem_ctx));
470 0 : talloc_free(mem_ctx);
471 0 : return ret;
472 : }
473 :
474 : /* cope with ticket being in the future due to clock skew */
475 15541 : if ((unsigned)kdc_time > time(NULL)) {
476 0 : time_t t = time(NULL);
477 0 : int time_offset =(unsigned)kdc_time-t;
478 0 : DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
479 0 : krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
480 : }
481 :
482 15541 : if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
483 0 : ret = kinit_to_ccache(parent_ctx,
484 : credentials,
485 : smb_krb5_context,
486 : lp_ctx,
487 : event_ctx,
488 : ccache, obtained,
489 : error_string);
490 : }
491 :
492 15541 : if (ret) {
493 1418 : (*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
494 : cli_credentials_get_principal(credentials, mem_ctx),
495 : smb_get_krb5_error_message(smb_krb5_context->krb5_context,
496 : ret, mem_ctx));
497 1418 : talloc_free(mem_ctx);
498 1418 : return ret;
499 : }
500 :
501 14123 : DEBUG(10,("kinit for %s succeeded\n",
502 : cli_credentials_get_principal(credentials, mem_ctx)));
503 :
504 :
505 14123 : talloc_free(mem_ctx);
506 14123 : return 0;
507 : }
508 :
509 67792 : static krb5_error_code free_keytab_container(struct keytab_container *ktc)
510 : {
511 67792 : return krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
512 : }
513 :
514 67630 : krb5_error_code smb_krb5_get_keytab_container(TALLOC_CTX *mem_ctx,
515 : struct smb_krb5_context *smb_krb5_context,
516 : krb5_keytab opt_keytab,
517 : const char *keytab_name,
518 : struct keytab_container **ktc)
519 : {
520 2655 : krb5_keytab keytab;
521 2655 : krb5_error_code ret;
522 :
523 : /*
524 : * Start with talloc(), talloc_reference() and only then call
525 : * krb5_kt_resolve(). If any of them fails, the cleanup code is simpler.
526 : */
527 67630 : *ktc = talloc(mem_ctx, struct keytab_container);
528 67630 : if (!*ktc) {
529 0 : return ENOMEM;
530 : }
531 :
532 67630 : (*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
533 67630 : if ((*ktc)->smb_krb5_context == NULL) {
534 0 : TALLOC_FREE(*ktc);
535 0 : return ENOMEM;
536 : }
537 :
538 67630 : if (opt_keytab) {
539 97 : keytab = opt_keytab;
540 : } else {
541 67533 : ret = krb5_kt_resolve(smb_krb5_context->krb5_context,
542 : keytab_name, &keytab);
543 67533 : if (ret) {
544 0 : DEBUG(1,("failed to open krb5 keytab: %s\n",
545 : smb_get_krb5_error_message(
546 : smb_krb5_context->krb5_context,
547 : ret, mem_ctx)));
548 0 : TALLOC_FREE(*ktc);
549 0 : return ret;
550 : }
551 : }
552 :
553 67630 : (*ktc)->keytab = keytab;
554 67630 : (*ktc)->password_based = false;
555 67630 : talloc_set_destructor(*ktc, free_keytab_container);
556 :
557 67630 : return 0;
558 : }
559 :
560 : /*
561 : * Walk the keytab, looking for entries of this principal name,
562 : * with KVNO other than current kvno -1.
563 : *
564 : * These entries are now stale,
565 : * we only keep the current and previous entries around.
566 : *
567 : * Inspired by the code in Samba3 for 'use kerberos keytab'.
568 : */
569 386 : krb5_error_code smb_krb5_remove_obsolete_keytab_entries(TALLOC_CTX *mem_ctx,
570 : krb5_context context,
571 : krb5_keytab keytab,
572 : uint32_t num_principals,
573 : krb5_principal *principals,
574 : krb5_kvno kvno,
575 : bool *found_previous,
576 : const char **error_string)
577 : {
578 28 : TALLOC_CTX *tmp_ctx;
579 28 : krb5_error_code code;
580 28 : krb5_kt_cursor cursor;
581 :
582 386 : tmp_ctx = talloc_new(mem_ctx);
583 386 : if (tmp_ctx == NULL) {
584 0 : *error_string = "Cannot allocate tmp_ctx";
585 0 : return ENOMEM;
586 : }
587 :
588 386 : *found_previous = true;
589 :
590 386 : code = krb5_kt_start_seq_get(context, keytab, &cursor);
591 386 : switch (code) {
592 161 : case 0:
593 161 : break;
594 : #ifdef HEIM_ERR_OPNOTSUPP
595 0 : case HEIM_ERR_OPNOTSUPP:
596 : #endif
597 219 : case ENOENT:
598 : case KRB5_KT_END:
599 : /* no point enumerating if there isn't anything here */
600 219 : code = 0;
601 219 : goto done;
602 0 : default:
603 0 : *error_string = talloc_asprintf(mem_ctx,
604 : "failed to open keytab for read of old entries: %s\n",
605 : smb_get_krb5_error_message(context, code, tmp_ctx));
606 0 : goto done;
607 : }
608 :
609 202 : do {
610 2439 : krb5_kvno old_kvno = kvno - 1;
611 202 : krb5_keytab_entry entry;
612 2439 : bool matched = false;
613 202 : uint32_t i;
614 :
615 2439 : code = krb5_kt_next_entry(context, keytab, &entry, &cursor);
616 2439 : if (code) {
617 161 : break;
618 : }
619 :
620 5236 : for (i = 0; i < num_principals; i++) {
621 410 : krb5_boolean ok;
622 :
623 5118 : ok = smb_krb5_kt_compare(context,
624 : &entry,
625 4708 : principals[i],
626 : 0,
627 : 0);
628 4708 : if (ok) {
629 1587 : matched = true;
630 1587 : break;
631 : }
632 : }
633 :
634 2272 : if (!matched) {
635 : /*
636 : * Free the entry, it wasn't the one we were looking
637 : * for anyway
638 : */
639 528 : krb5_kt_free_entry(context, &entry);
640 : /* Make sure we do not double free */
641 528 : ZERO_STRUCT(entry);
642 528 : continue;
643 : }
644 :
645 : /*
646 : * Delete it, if it is not kvno - 1.
647 : *
648 : * Some keytab files store the kvno only in 8bits. Limit the
649 : * compare to 8bits, so that we don't miss old keys and delete
650 : * them.
651 : */
652 1744 : if ((entry.vno & 0xff) != (old_kvno & 0xff)) {
653 43 : krb5_error_code rc;
654 :
655 : /* Release the enumeration. We are going to
656 : * have to start this from the top again,
657 : * because deletes during enumeration may not
658 : * always be consistent.
659 : *
660 : * Also, the enumeration locks a FILE: keytab
661 : */
662 387 : krb5_kt_end_seq_get(context, keytab, &cursor);
663 :
664 387 : code = krb5_kt_remove_entry(context, keytab, &entry);
665 387 : krb5_kt_free_entry(context, &entry);
666 :
667 : /* Make sure we do not double free */
668 387 : ZERO_STRUCT(entry);
669 :
670 : /* Deleted: Restart from the top */
671 387 : rc = krb5_kt_start_seq_get(context, keytab, &cursor);
672 387 : if (rc != 0) {
673 0 : krb5_kt_free_entry(context, &entry);
674 :
675 : /* Make sure we do not double free */
676 0 : ZERO_STRUCT(entry);
677 :
678 0 : DEBUG(1, ("failed to restart enumeration of keytab: %s\n",
679 : smb_get_krb5_error_message(context,
680 : code,
681 : tmp_ctx)));
682 :
683 0 : talloc_free(tmp_ctx);
684 0 : return rc;
685 : }
686 :
687 387 : if (code != 0) {
688 0 : break;
689 : }
690 :
691 : } else {
692 1357 : *found_previous = true;
693 : }
694 :
695 : /* Free the entry, we don't need it any more */
696 1744 : krb5_kt_free_entry(context, &entry);
697 : /* Make sure we do not double free */
698 1744 : ZERO_STRUCT(entry);
699 2076 : } while (code == 0);
700 :
701 167 : krb5_kt_end_seq_get(context, keytab, &cursor);
702 :
703 167 : switch (code) {
704 0 : case 0:
705 0 : break;
706 161 : case ENOENT:
707 : case KRB5_KT_END:
708 161 : break;
709 0 : default:
710 0 : *error_string = talloc_asprintf(mem_ctx,
711 : "failed in deleting old entries for principal: %s\n",
712 : smb_get_krb5_error_message(context,
713 : code,
714 : tmp_ctx));
715 0 : goto done;
716 : }
717 :
718 161 : code = 0;
719 386 : done:
720 386 : talloc_free(tmp_ctx);
721 386 : return code;
722 : }
723 :
724 : /*
725 : * Walk the keytab, looking for entries of this principal name,
726 : * with KVNO and key equal
727 : *
728 : * These entries do not need to be replaced, so we want to tell the caller not to add them again
729 : *
730 : * Inspired by the code in Samba3 for 'use kerberos keytab'.
731 : */
732 2928 : krb5_error_code smb_krb5_is_exact_entry_in_keytab(TALLOC_CTX *mem_ctx,
733 : krb5_context context,
734 : krb5_keytab keytab,
735 : krb5_keytab_entry *to_match,
736 : bool *found,
737 : const char **error_string)
738 : {
739 231 : TALLOC_CTX *tmp_ctx;
740 231 : krb5_error_code code;
741 231 : krb5_kt_cursor cursor;
742 :
743 2928 : tmp_ctx = talloc_new(mem_ctx);
744 2928 : if (tmp_ctx == NULL) {
745 0 : *error_string = "Cannot allocate tmp_ctx";
746 0 : return ENOMEM;
747 : }
748 :
749 2928 : *found = false;
750 :
751 2928 : code = krb5_kt_start_seq_get(context, keytab, &cursor);
752 2928 : switch (code) {
753 2494 : case 0:
754 2494 : break;
755 : #ifdef HEIM_ERR_OPNOTSUPP
756 0 : case HEIM_ERR_OPNOTSUPP:
757 : #endif
758 225 : case ENOENT:
759 : case KRB5_KT_END:
760 : /* no point enumerating if there isn't anything here */
761 225 : code = 0;
762 225 : goto done;
763 0 : default:
764 0 : *error_string = talloc_asprintf(mem_ctx,
765 : "failed to open keytab for read of existing entries: %s\n",
766 : smb_get_krb5_error_message(context, code, tmp_ctx));
767 0 : goto done;
768 : }
769 :
770 1448 : do {
771 1448 : krb5_keytab_entry entry;
772 20646 : bool matched = false;
773 1448 : krb5_boolean ok;
774 :
775 20646 : code = krb5_kt_next_entry(context, keytab, &entry, &cursor);
776 20646 : if (code) {
777 2494 : break;
778 : }
779 :
780 19316 : ok = smb_krb5_kt_compare(context,
781 : &entry,
782 18077 : to_match->principal,
783 : to_match->vno,
784 13154 : KRB5_KEY_TYPE(KRB5_KT_KEY(to_match)));
785 18077 : if (ok) {
786 : /* This is not a security check, constant time is not required */
787 134 : if ((KRB5_KEY_LENGTH(KRB5_KT_KEY(&entry)) == KRB5_KEY_LENGTH(KRB5_KT_KEY(to_match)))
788 134 : && memcmp(KRB5_KEY_DATA(KRB5_KT_KEY(&entry)), KRB5_KEY_DATA(KRB5_KT_KEY(to_match)),
789 69 : KRB5_KEY_LENGTH(KRB5_KT_KEY(&entry))) == 0) {
790 134 : matched = true;
791 : }
792 : }
793 :
794 : /* Free the entry, we don't need it any more */
795 18077 : krb5_kt_free_entry(context, &entry);
796 : /* Make sure we do not double free */
797 18077 : ZERO_STRUCT(entry);
798 18077 : if (matched) {
799 134 : *found = true;
800 134 : break;
801 : }
802 17943 : } while (code == 0);
803 :
804 2703 : krb5_kt_end_seq_get(context, keytab, &cursor);
805 :
806 2703 : switch (code) {
807 134 : case 0:
808 134 : break;
809 2360 : case ENOENT:
810 : case KRB5_KT_END:
811 2360 : break;
812 0 : default:
813 0 : *error_string = talloc_asprintf(mem_ctx,
814 : "failed in checking old entries for principal: %s\n",
815 : smb_get_krb5_error_message(context,
816 : code,
817 : tmp_ctx));
818 0 : goto done;
819 : }
820 :
821 2494 : code = 0;
822 2928 : done:
823 2928 : talloc_free(tmp_ctx);
824 2928 : return code;
825 : }
|