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_proto.h"
28 #include "auth/credentials/credentials_krb5.h"
29 #include "auth/kerberos/kerberos_credentials.h"
30 #include "auth/kerberos/kerberos_util.h"
31 
32 struct principal_container {
33 	struct smb_krb5_context *smb_krb5_context;
34 	krb5_principal principal;
35 	const char *string_form; /* Optional */
36 };
37 
free_principal(struct principal_container * pc)38 static krb5_error_code free_principal(struct principal_container *pc)
39 {
40 	/* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
41 	krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
42 
43 	return 0;
44 }
45 
46 
parse_principal(TALLOC_CTX * parent_ctx,const char * princ_string,struct smb_krb5_context * smb_krb5_context,krb5_principal * princ,const char ** error_string)47 static krb5_error_code parse_principal(TALLOC_CTX *parent_ctx,
48 				       const char *princ_string,
49 				       struct smb_krb5_context *smb_krb5_context,
50 				       krb5_principal *princ,
51 				       const char **error_string)
52 {
53 	int ret;
54 	struct principal_container *mem_ctx;
55 	if (princ_string == NULL) {
56 		 *princ = NULL;
57 		 return 0;
58 	}
59 
60 	ret = krb5_parse_name(smb_krb5_context->krb5_context,
61 			      princ_string, princ);
62 
63 	if (ret) {
64 		(*error_string) = smb_get_krb5_error_message(
65 						smb_krb5_context->krb5_context,
66 						ret, parent_ctx);
67 		return ret;
68 	}
69 
70 	mem_ctx = talloc(parent_ctx, struct principal_container);
71 	if (!mem_ctx) {
72 		(*error_string) = error_message(ENOMEM);
73 		return ENOMEM;
74 	}
75 
76 	/* This song-and-dance effectivly puts the principal
77 	 * into talloc, so we can't loose it. */
78 	mem_ctx->smb_krb5_context = talloc_reference(mem_ctx,
79 						     smb_krb5_context);
80 	mem_ctx->principal = *princ;
81 	talloc_set_destructor(mem_ctx, free_principal);
82 	return 0;
83 }
84 
85 /* Obtain the principal set on this context.  Requires a
86  * smb_krb5_context because we are doing krb5 principal parsing with
87  * the library routines.  The returned princ is placed in the talloc
88  * system by means of a destructor (do *not* free). */
89 
principal_from_credentials(TALLOC_CTX * parent_ctx,struct cli_credentials * credentials,struct smb_krb5_context * smb_krb5_context,krb5_principal * princ,enum credentials_obtained * obtained,const char ** error_string)90 krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
91 				struct cli_credentials *credentials,
92 				struct smb_krb5_context *smb_krb5_context,
93 				krb5_principal *princ,
94 				enum credentials_obtained *obtained,
95 				const char **error_string)
96 {
97 	krb5_error_code ret;
98 	const char *princ_string;
99 	TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
100 	*obtained = CRED_UNINITIALISED;
101 
102 	if (!mem_ctx) {
103 		(*error_string) = error_message(ENOMEM);
104 		return ENOMEM;
105 	}
106 	princ_string = cli_credentials_get_principal_and_obtained(credentials,
107 								  mem_ctx,
108 								  obtained);
109 	if (!princ_string) {
110 		*princ = NULL;
111 		return 0;
112 	}
113 
114 	ret = parse_principal(parent_ctx, princ_string,
115 			      smb_krb5_context, princ, error_string);
116 	talloc_free(mem_ctx);
117 	return ret;
118 }
119 
120 /* Obtain the principal set on this context.  Requires a
121  * smb_krb5_context because we are doing krb5 principal parsing with
122  * the library routines.  The returned princ is placed in the talloc
123  * system by means of a destructor (do *not* free). */
124 
impersonate_principal_from_credentials(TALLOC_CTX * parent_ctx,struct cli_credentials * credentials,struct smb_krb5_context * smb_krb5_context,krb5_principal * princ,const char ** error_string)125 static krb5_error_code impersonate_principal_from_credentials(
126 				TALLOC_CTX *parent_ctx,
127 				struct cli_credentials *credentials,
128 				struct smb_krb5_context *smb_krb5_context,
129 				krb5_principal *princ,
130 				const char **error_string)
131 {
132 	return parse_principal(parent_ctx,
133 			cli_credentials_get_impersonate_principal(credentials),
134 			smb_krb5_context, princ, error_string);
135 }
136 
smb_krb5_create_principals_array(TALLOC_CTX * mem_ctx,krb5_context context,const char * account_name,const char * realm,uint32_t num_spns,const char * spns[],uint32_t * pnum_principals,krb5_principal ** pprincipals,const char ** error_string)137 krb5_error_code smb_krb5_create_principals_array(TALLOC_CTX *mem_ctx,
138 						 krb5_context context,
139 						 const char *account_name,
140 						 const char *realm,
141 						 uint32_t num_spns,
142 						 const char *spns[],
143 						 uint32_t *pnum_principals,
144 						 krb5_principal **pprincipals,
145 						 const char **error_string)
146 {
147 	krb5_error_code code;
148 	TALLOC_CTX *tmp_ctx;
149 	uint32_t num_principals = 0;
150 	krb5_principal *principals;
151 	uint32_t i;
152 
153 	tmp_ctx = talloc_new(mem_ctx);
154 	if (tmp_ctx == NULL) {
155 		*error_string = "Cannot allocate tmp_ctx";
156 		return ENOMEM;
157 	}
158 
159 	if (realm == NULL) {
160 		*error_string = "Cannot create principal without a realm";
161 		code = EINVAL;
162 		goto done;
163 	}
164 
165 	if (account_name == NULL && (num_spns == 0 || spns == NULL)) {
166 		*error_string = "Cannot create principal without an account or SPN";
167 		code = EINVAL;
168 		goto done;
169 	}
170 
171 	if (account_name != NULL && account_name[0] != '\0') {
172 		num_principals++;
173 	}
174 	num_principals += num_spns;
175 
176 	principals = talloc_zero_array(tmp_ctx,
177 				       krb5_principal,
178 				       num_principals);
179 	if (principals == NULL) {
180 		*error_string = "Cannot allocate principals";
181 		code = ENOMEM;
182 		goto done;
183 	}
184 
185 	for (i = 0; i < num_spns; i++) {
186 		code = krb5_parse_name(context, spns[i], &(principals[i]));
187 		if (code != 0) {
188 			*error_string = smb_get_krb5_error_message(context,
189 								   code,
190 								   mem_ctx);
191 			goto done;
192 		}
193 	}
194 
195 	if (account_name != NULL && account_name[0] != '\0') {
196 		code = smb_krb5_make_principal(context,
197 					       &(principals[i]),
198 					       realm,
199 					       account_name,
200 					       NULL);
201 		if (code != 0) {
202 			*error_string = smb_get_krb5_error_message(context,
203 								   code,
204 								   mem_ctx);
205 			goto done;
206 		}
207 	}
208 
209 	if (pnum_principals != NULL) {
210 		*pnum_principals = num_principals;
211 
212 		if (pprincipals != NULL) {
213 			*pprincipals = talloc_steal(mem_ctx, principals);
214 		}
215 	}
216 
217 	code = 0;
218 done:
219 	talloc_free(tmp_ctx);
220 	return code;
221 }
222 
223 /**
224  * Return a freshly allocated ccache (destroyed by destructor on child
225  * of parent_ctx), for a given set of client credentials
226  */
227 
kinit_to_ccache(TALLOC_CTX * parent_ctx,struct cli_credentials * credentials,struct smb_krb5_context * smb_krb5_context,struct tevent_context * event_ctx,krb5_ccache ccache,enum credentials_obtained * obtained,const char ** error_string)228  krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
229 				 struct cli_credentials *credentials,
230 				 struct smb_krb5_context *smb_krb5_context,
231 				 struct tevent_context *event_ctx,
232 				 krb5_ccache ccache,
233 				 enum credentials_obtained *obtained,
234 				 const char **error_string)
235 {
236 	krb5_error_code ret;
237 	const char *password;
238 #ifdef SAMBA4_USES_HEIMDAL
239 	const char *self_service;
240 #endif
241 	const char *target_service;
242 	time_t kdc_time = 0;
243 	krb5_principal princ;
244 	krb5_principal impersonate_principal;
245 	int tries;
246 	TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
247 	krb5_get_init_creds_opt *krb_options;
248 
249 	if (!mem_ctx) {
250 		(*error_string) = strerror(ENOMEM);
251 		return ENOMEM;
252 	}
253 
254 	ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ, obtained, error_string);
255 	if (ret) {
256 		talloc_free(mem_ctx);
257 		return ret;
258 	}
259 
260 	if (princ == NULL) {
261 		(*error_string) = talloc_asprintf(credentials, "principal, username or realm was not specified in the credentials");
262 		talloc_free(mem_ctx);
263 		return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
264 	}
265 
266 	ret = impersonate_principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &impersonate_principal, error_string);
267 	if (ret) {
268 		talloc_free(mem_ctx);
269 		return ret;
270 	}
271 
272 #ifdef SAMBA4_USES_HEIMDAL
273 	self_service = cli_credentials_get_self_service(credentials);
274 #endif
275 	target_service = cli_credentials_get_target_service(credentials);
276 
277 	password = cli_credentials_get_password(credentials);
278 
279 	/* setup the krb5 options we want */
280 	if ((ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options))) {
281 		(*error_string) = talloc_asprintf(credentials, "krb5_get_init_creds_opt_alloc failed (%s)\n",
282 						  smb_get_krb5_error_message(smb_krb5_context->krb5_context,
283 									     ret, mem_ctx));
284 		talloc_free(mem_ctx);
285 		return ret;
286 	}
287 
288 #ifdef SAMBA4_USES_HEIMDAL /* Disable for now MIT reads defaults when needed */
289 	/* get the defaults */
290 	krb5_get_init_creds_opt_set_default_flags(smb_krb5_context->krb5_context, NULL, NULL, krb_options);
291 #endif
292 	/* set if we want a forwardable ticket */
293 	switch (cli_credentials_get_krb_forwardable(credentials)) {
294 	case CRED_AUTO_KRB_FORWARDABLE:
295 		break;
296 	case CRED_NO_KRB_FORWARDABLE:
297 		krb5_get_init_creds_opt_set_forwardable(krb_options, FALSE);
298 		break;
299 	case CRED_FORCE_KRB_FORWARDABLE:
300 		krb5_get_init_creds_opt_set_forwardable(krb_options, TRUE);
301 		break;
302 	}
303 
304 #ifdef SAMBA4_USES_HEIMDAL /* FIXME: MIT does not have this yet */
305 	/*
306 	 * In order to work against windows KDCs even if we use
307 	 * the netbios domain name as realm, we need to add the following
308 	 * flags:
309 	 * KRB5_INIT_CREDS_NO_C_CANON_CHECK;
310 	 * KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK;
311 	 *
312 	 * On MIT: Set pkinit_eku_checking to none
313 	 */
314 	krb5_get_init_creds_opt_set_win2k(smb_krb5_context->krb5_context,
315 					  krb_options, true);
316 	krb5_get_init_creds_opt_set_canonicalize(smb_krb5_context->krb5_context,
317 						 krb_options, true);
318 #else /* MIT */
319 	krb5_get_init_creds_opt_set_canonicalize(krb_options, true);
320 #endif
321 
322 	tries = 2;
323 	while (tries--) {
324 #ifdef SAMBA4_USES_HEIMDAL
325 		struct tevent_context *previous_ev;
326 		/* Do this every time, in case we have weird recursive issues here */
327 		ret = smb_krb5_context_set_event_ctx(smb_krb5_context, event_ctx, &previous_ev);
328 		if (ret) {
329 			talloc_free(mem_ctx);
330 			return ret;
331 		}
332 #endif
333 		if (password) {
334 			if (impersonate_principal) {
335 #ifdef SAMBA4_USES_HEIMDAL
336 				ret = smb_krb5_kinit_s4u2_ccache(smb_krb5_context->krb5_context,
337 								 ccache,
338 								 princ,
339 								 password,
340 								 impersonate_principal,
341 								 self_service,
342 								 target_service,
343 								 krb_options,
344 								 NULL,
345 								 &kdc_time);
346 #else
347 				talloc_free(mem_ctx);
348 				(*error_string) = "INTERNAL error: s4u2 ops "
349 					"are not supported with MIT build yet";
350 				return EINVAL;
351 #endif
352 			} else {
353 				ret = smb_krb5_kinit_password_ccache(smb_krb5_context->krb5_context,
354 								     ccache,
355 								     princ,
356 								     password,
357 								     target_service,
358 								     krb_options,
359 								     NULL,
360 								     &kdc_time);
361 			}
362 		} else if (impersonate_principal) {
363 			talloc_free(mem_ctx);
364 			(*error_string) = "INTERNAL error: Cannot impersonate principal with just a keyblock.  A password must be specified in the credentials";
365 			return EINVAL;
366 		} else {
367 			/* No password available, try to use a keyblock instead */
368 
369 			krb5_keyblock keyblock;
370 			const struct samr_Password *mach_pwd;
371 			mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
372 			if (!mach_pwd) {
373 				talloc_free(mem_ctx);
374 				(*error_string) = "kinit_to_ccache: No password available for kinit\n";
375 				krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
376 #ifdef SAMBA4_USES_HEIMDAL
377 				smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
378 #endif
379 				return EINVAL;
380 			}
381 			ret = smb_krb5_keyblock_init_contents(smb_krb5_context->krb5_context,
382 						 ENCTYPE_ARCFOUR_HMAC,
383 						 mach_pwd->hash, sizeof(mach_pwd->hash),
384 						 &keyblock);
385 
386 			if (ret == 0) {
387 				ret = smb_krb5_kinit_keyblock_ccache(smb_krb5_context->krb5_context,
388 								     ccache,
389 								     princ,
390 								     &keyblock,
391 								     target_service,
392 								     krb_options,
393 								     NULL,
394 								     &kdc_time);
395 				krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
396 			}
397 		}
398 
399 #ifdef SAMBA4_USES_HEIMDAL
400 		smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
401 #endif
402 
403 		if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
404 			/* Perhaps we have been given an invalid skew, so try again without it */
405 			time_t t = time(NULL);
406 			krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
407 		} else {
408 			/* not a skew problem */
409 			break;
410 		}
411 	}
412 
413 	krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
414 
415 	if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
416 		(*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
417 						  cli_credentials_get_principal(credentials, mem_ctx),
418 						  smb_get_krb5_error_message(smb_krb5_context->krb5_context,
419 									     ret, mem_ctx));
420 		talloc_free(mem_ctx);
421 		return ret;
422 	}
423 
424 	/* cope with ticket being in the future due to clock skew */
425 	if ((unsigned)kdc_time > time(NULL)) {
426 		time_t t = time(NULL);
427 		int time_offset =(unsigned)kdc_time-t;
428 		DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
429 		krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
430 	}
431 
432 	if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
433 		ret = kinit_to_ccache(parent_ctx,
434 				      credentials,
435 				      smb_krb5_context,
436 				      event_ctx,
437 				      ccache, obtained,
438 				      error_string);
439 	}
440 
441 	if (ret) {
442 		(*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
443 						  cli_credentials_get_principal(credentials, mem_ctx),
444 						  smb_get_krb5_error_message(smb_krb5_context->krb5_context,
445 									     ret, mem_ctx));
446 		talloc_free(mem_ctx);
447 		return ret;
448 	}
449 
450 	DEBUG(10,("kinit for %s succeeded\n",
451 		cli_credentials_get_principal(credentials, mem_ctx)));
452 
453 
454 	talloc_free(mem_ctx);
455 	return 0;
456 }
457 
free_keytab_container(struct keytab_container * ktc)458 static krb5_error_code free_keytab_container(struct keytab_container *ktc)
459 {
460 	return krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
461 }
462 
smb_krb5_get_keytab_container(TALLOC_CTX * mem_ctx,struct smb_krb5_context * smb_krb5_context,krb5_keytab opt_keytab,const char * keytab_name,struct keytab_container ** ktc)463 krb5_error_code smb_krb5_get_keytab_container(TALLOC_CTX *mem_ctx,
464 				struct smb_krb5_context *smb_krb5_context,
465 				krb5_keytab opt_keytab,
466 				const char *keytab_name,
467 				struct keytab_container **ktc)
468 {
469 	krb5_keytab keytab;
470 	krb5_error_code ret;
471 
472 	if (opt_keytab) {
473 		keytab = opt_keytab;
474 	} else {
475 		ret = krb5_kt_resolve(smb_krb5_context->krb5_context,
476 						keytab_name, &keytab);
477 		if (ret) {
478 			DEBUG(1,("failed to open krb5 keytab: %s\n",
479 				 smb_get_krb5_error_message(
480 					smb_krb5_context->krb5_context,
481 					ret, mem_ctx)));
482 			return ret;
483 		}
484 	}
485 
486 	*ktc = talloc(mem_ctx, struct keytab_container);
487 	if (!*ktc) {
488 		return ENOMEM;
489 	}
490 
491 	(*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
492 	(*ktc)->keytab = keytab;
493 	(*ktc)->password_based = false;
494 	talloc_set_destructor(*ktc, free_keytab_container);
495 
496 	return 0;
497 }
498 
499 /*
500  * Walk the keytab, looking for entries of this principal name,
501  * with KVNO other than current kvno -1.
502  *
503  * These entries are now stale,
504  * we only keep the current and previous entries around.
505  *
506  * Inspired by the code in Samba3 for 'use kerberos keytab'.
507  */
smb_krb5_remove_obsolete_keytab_entries(TALLOC_CTX * mem_ctx,krb5_context context,krb5_keytab keytab,uint32_t num_principals,krb5_principal * principals,krb5_kvno kvno,bool * found_previous,const char ** error_string)508 krb5_error_code smb_krb5_remove_obsolete_keytab_entries(TALLOC_CTX *mem_ctx,
509 							krb5_context context,
510 							krb5_keytab keytab,
511 							uint32_t num_principals,
512 							krb5_principal *principals,
513 							krb5_kvno kvno,
514 							bool *found_previous,
515 							const char **error_string)
516 {
517 	TALLOC_CTX *tmp_ctx;
518 	krb5_error_code code;
519 	krb5_kt_cursor cursor;
520 
521 	tmp_ctx = talloc_new(mem_ctx);
522 	if (tmp_ctx == NULL) {
523 		*error_string = "Cannot allocate tmp_ctx";
524 		return ENOMEM;
525 	}
526 
527 	*found_previous = true;
528 
529 	code = krb5_kt_start_seq_get(context, keytab, &cursor);
530 	switch (code) {
531 	case 0:
532 		break;
533 #ifdef HEIM_ERR_OPNOTSUPP
534 	case HEIM_ERR_OPNOTSUPP:
535 #endif
536 	case ENOENT:
537 	case KRB5_KT_END:
538 		/* no point enumerating if there isn't anything here */
539 		code = 0;
540 		goto done;
541 	default:
542 		*error_string = talloc_asprintf(mem_ctx,
543 						"failed to open keytab for read of old entries: %s\n",
544 						smb_get_krb5_error_message(context, code, mem_ctx));
545 		goto done;
546 	}
547 
548 	do {
549 		krb5_kvno old_kvno = kvno - 1;
550 		krb5_keytab_entry entry;
551 		bool matched = false;
552 		uint32_t i;
553 
554 		code = krb5_kt_next_entry(context, keytab, &entry, &cursor);
555 		if (code) {
556 			break;
557 		}
558 
559 		for (i = 0; i < num_principals; i++) {
560 			krb5_boolean ok;
561 
562 			ok = smb_krb5_kt_compare(context,
563 						&entry,
564 						principals[i],
565 						0,
566 						0);
567 			if (ok) {
568 				matched = true;
569 				break;
570 			}
571 		}
572 
573 		if (!matched) {
574 			/*
575 			 * Free the entry, it wasn't the one we were looking
576 			 * for anyway
577 			 */
578 			krb5_kt_free_entry(context, &entry);
579 			/* Make sure we do not double free */
580 			ZERO_STRUCT(entry);
581 			continue;
582 		}
583 
584 		/*
585 		 * Delete it, if it is not kvno - 1.
586 		 *
587 		 * Some keytab files store the kvno only in 8bits. Limit the
588 		 * compare to 8bits, so that we don't miss old keys and delete
589 		 * them.
590 		 */
591 		if ((entry.vno & 0xff) != (old_kvno & 0xff)) {
592 			krb5_error_code rc;
593 
594 			/* Release the enumeration.  We are going to
595 			 * have to start this from the top again,
596 			 * because deletes during enumeration may not
597 			 * always be consistent.
598 			 *
599 			 * Also, the enumeration locks a FILE: keytab
600 			 */
601 			krb5_kt_end_seq_get(context, keytab, &cursor);
602 
603 			code = krb5_kt_remove_entry(context, keytab, &entry);
604 			krb5_kt_free_entry(context, &entry);
605 
606 			/* Make sure we do not double free */
607 			ZERO_STRUCT(entry);
608 
609 			/* Deleted: Restart from the top */
610 			rc = krb5_kt_start_seq_get(context, keytab, &cursor);
611 			if (rc != 0) {
612 				krb5_kt_free_entry(context, &entry);
613 
614 				/* Make sure we do not double free */
615 				ZERO_STRUCT(entry);
616 
617 				DEBUG(1, ("failed to restart enumeration of keytab: %s\n",
618 					  smb_get_krb5_error_message(context,
619 								     code,
620 								     tmp_ctx)));
621 
622 				talloc_free(tmp_ctx);
623 				return rc;
624 			}
625 
626 			if (code != 0) {
627 				break;
628 			}
629 
630 		} else {
631 			*found_previous = true;
632 		}
633 
634 		/* Free the entry, we don't need it any more */
635 		krb5_kt_free_entry(context, &entry);
636 		/* Make sure we do not double free */
637 		ZERO_STRUCT(entry);
638 	} while (code == 0);
639 
640 	krb5_kt_end_seq_get(context, keytab, &cursor);
641 
642 	switch (code) {
643 	case 0:
644 		break;
645 	case ENOENT:
646 	case KRB5_KT_END:
647 		break;
648 	default:
649 		*error_string = talloc_asprintf(mem_ctx,
650 						"failed in deleting old entries for principal: %s\n",
651 						smb_get_krb5_error_message(context,
652 									   code,
653 									   mem_ctx));
654 	}
655 
656 	code = 0;
657 done:
658 	talloc_free(tmp_ctx);
659 	return code;
660 }
661