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 2 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, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23 
24 #include "includes.h"
25 #include "system/kerberos.h"
26 #include "auth/kerberos/kerberos.h"
27 #include "auth/credentials/credentials.h"
28 #include "auth/credentials/credentials_krb5.h"
29 
30 struct principal_container {
31 	struct smb_krb5_context *smb_krb5_context;
32 	krb5_principal principal;
33 };
34 
free_principal(struct principal_container * pc)35 static int free_principal(struct principal_container *pc)
36 {
37 	/* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
38 	krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
39 
40 	return 0;
41 }
42 
salt_principal_from_credentials(TALLOC_CTX * parent_ctx,struct cli_credentials * machine_account,struct smb_krb5_context * smb_krb5_context,krb5_principal * salt_princ)43 static krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx,
44 						       struct cli_credentials *machine_account,
45 						       struct smb_krb5_context *smb_krb5_context,
46 						       krb5_principal *salt_princ)
47 {
48 	krb5_error_code ret;
49 	char *machine_username;
50 	char *salt_body;
51 	char *lower_realm;
52 	const char *salt_principal;
53 	struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
54 	if (!mem_ctx) {
55 		return ENOMEM;
56 	}
57 
58 	salt_principal = cli_credentials_get_salt_principal(machine_account);
59 	if (salt_principal) {
60 		ret = krb5_parse_name(smb_krb5_context->krb5_context, salt_principal, salt_princ);
61 	} else {
62 		machine_username = talloc_strdup(mem_ctx, cli_credentials_get_username(machine_account));
63 
64 		if (!machine_username) {
65 			talloc_free(mem_ctx);
66 			return ENOMEM;
67 		}
68 
69 		if (machine_username[strlen(machine_username)-1] == '$') {
70 			machine_username[strlen(machine_username)-1] = '\0';
71 		}
72 		lower_realm = strlower_talloc(mem_ctx, cli_credentials_get_realm(machine_account));
73 		if (!lower_realm) {
74 			talloc_free(mem_ctx);
75 			return ENOMEM;
76 		}
77 
78 		salt_body = talloc_asprintf(mem_ctx, "%s.%s", machine_username,
79 					    lower_realm);
80 		if (!salt_body) {
81 			talloc_free(mem_ctx);
82 		return ENOMEM;
83 		}
84 
85 		ret = krb5_make_principal(smb_krb5_context->krb5_context, salt_princ,
86 					  cli_credentials_get_realm(machine_account),
87 					  "host", salt_body, NULL);
88 	}
89 
90 	if (ret == 0) {
91 		/* This song-and-dance effectivly puts the principal
92 		 * into talloc, so we can't loose it. */
93 		mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
94 		mem_ctx->principal = *salt_princ;
95 		talloc_set_destructor(mem_ctx, free_principal);
96 	}
97 	return ret;
98 }
99 
100 /* Obtain the principal set on this context.  Requires a
101  * smb_krb5_context because we are doing krb5 principal parsing with
102  * the library routines.  The returned princ is placed in the talloc
103  * system by means of a destructor (do *not* free). */
104 
principal_from_credentials(TALLOC_CTX * parent_ctx,struct cli_credentials * credentials,struct smb_krb5_context * smb_krb5_context,krb5_principal * princ)105 krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
106 					   struct cli_credentials *credentials,
107 					   struct smb_krb5_context *smb_krb5_context,
108 					   krb5_principal *princ)
109 {
110 	krb5_error_code ret;
111 	const char *princ_string;
112 	struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
113 	if (!mem_ctx) {
114 		return ENOMEM;
115 	}
116 
117 	princ_string = cli_credentials_get_principal(credentials, mem_ctx);
118 
119 	/* A NULL here has meaning, as the gssapi server case will
120 	 * then use the principal from the client */
121 	if (!princ_string) {
122 		talloc_free(mem_ctx);
123 		princ = NULL;
124 		return 0;
125 	}
126 
127 	ret = krb5_parse_name(smb_krb5_context->krb5_context,
128 			      princ_string, princ);
129 
130 	if (ret == 0) {
131 		/* This song-and-dance effectivly puts the principal
132 		 * into talloc, so we can't loose it. */
133 		mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
134 		mem_ctx->principal = *princ;
135 		talloc_set_destructor(mem_ctx, free_principal);
136 	}
137 	return ret;
138 }
139 
140 /**
141  * Return a freshly allocated ccache (destroyed by destructor on child
142  * of parent_ctx), for a given set of client credentials
143  */
144 
kinit_to_ccache(TALLOC_CTX * parent_ctx,struct cli_credentials * credentials,struct smb_krb5_context * smb_krb5_context,krb5_ccache ccache)145  krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
146 				 struct cli_credentials *credentials,
147 				 struct smb_krb5_context *smb_krb5_context,
148 				 krb5_ccache ccache)
149 {
150 	krb5_error_code ret;
151 	const char *password;
152 	time_t kdc_time = 0;
153 	krb5_principal princ;
154 	int tries;
155 	TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
156 
157 	if (!mem_ctx) {
158 		return ENOMEM;
159 	}
160 
161 	ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ);
162 	if (ret) {
163 		talloc_free(mem_ctx);
164 		return ret;
165 	}
166 
167 	password = cli_credentials_get_password(credentials);
168 
169 	tries = 2;
170 	while (tries--) {
171 		if (password) {
172 			ret = kerberos_kinit_password_cc(smb_krb5_context->krb5_context, ccache,
173 							 princ,
174 							 password, NULL, &kdc_time);
175 		} else {
176 			/* No password available, try to use a keyblock instead */
177 
178 			krb5_keyblock keyblock;
179 			const struct samr_Password *mach_pwd;
180 			mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
181 			if (!mach_pwd) {
182 				talloc_free(mem_ctx);
183 				DEBUG(1, ("kinit_to_ccache: No password available for kinit\n"));
184 				return EINVAL;
185 			}
186 			ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
187 						 ETYPE_ARCFOUR_HMAC_MD5,
188 						 mach_pwd->hash, sizeof(mach_pwd->hash),
189 						 &keyblock);
190 
191 			if (ret == 0) {
192 				ret = kerberos_kinit_keyblock_cc(smb_krb5_context->krb5_context, ccache,
193 								 princ,
194 								 &keyblock, NULL, &kdc_time);
195 				krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
196 			}
197 		}
198 
199 		if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
200 			/* Perhaps we have been given an invalid skew, so try again without it */
201 			time_t t = time(NULL);
202 			krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
203 		} else {
204 			/* not a skew problem */
205 			break;
206 		}
207 	}
208 
209 	if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
210 		DEBUG(1,("kinit for %s failed (%s)\n",
211 			 cli_credentials_get_principal(credentials, mem_ctx),
212 			 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
213 						    ret, mem_ctx)));
214 		talloc_free(mem_ctx);
215 		return ret;
216 	}
217 
218 	/* cope with ticket being in the future due to clock skew */
219 	if ((unsigned)kdc_time > time(NULL)) {
220 		time_t t = time(NULL);
221 		int time_offset =(unsigned)kdc_time-t;
222 		DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
223 		krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
224 	}
225 
226 	if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
227 		ret = kinit_to_ccache(parent_ctx,
228 				      credentials,
229 				      smb_krb5_context,
230 				      ccache);
231 	}
232 	if (ret) {
233 		DEBUG(1,("kinit for %s failed (%s)\n",
234 			 cli_credentials_get_principal(credentials, mem_ctx),
235 			 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
236 						    ret, mem_ctx)));
237 		talloc_free(mem_ctx);
238 		return ret;
239 	}
240 	return 0;
241 }
242 
free_keytab(struct keytab_container * ktc)243 static int free_keytab(struct keytab_container *ktc)
244 {
245 	krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
246 
247 	return 0;
248 }
249 
smb_krb5_open_keytab(TALLOC_CTX * mem_ctx,struct smb_krb5_context * smb_krb5_context,const char * keytab_name,struct keytab_container ** ktc)250 int smb_krb5_open_keytab(TALLOC_CTX *mem_ctx,
251 			 struct smb_krb5_context *smb_krb5_context,
252 			 const char *keytab_name, struct keytab_container **ktc)
253 {
254 	krb5_keytab keytab;
255 	int ret;
256 	ret = krb5_kt_resolve(smb_krb5_context->krb5_context, keytab_name, &keytab);
257 	if (ret) {
258 		DEBUG(1,("failed to open krb5 keytab: %s\n",
259 			 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
260 						    ret, mem_ctx)));
261 		return ret;
262 	}
263 
264 	*ktc = talloc(mem_ctx, struct keytab_container);
265 	if (!*ktc) {
266 		return ENOMEM;
267 	}
268 
269 	(*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
270 	(*ktc)->keytab = keytab;
271 	talloc_set_destructor(*ktc, free_keytab);
272 
273 	return 0;
274 }
275 
276 struct enctypes_container {
277 	struct smb_krb5_context *smb_krb5_context;
278 	krb5_enctype *enctypes;
279 };
280 
free_enctypes(struct enctypes_container * etc)281 static int free_enctypes(struct enctypes_container *etc)
282 {
283 	free_kerberos_etypes(etc->smb_krb5_context->krb5_context, etc->enctypes);
284 	return 0;
285 }
286 
keytab_add_keys(TALLOC_CTX * parent_ctx,const char * princ_string,krb5_principal princ,krb5_principal salt_princ,int kvno,const char * password_s,struct smb_krb5_context * smb_krb5_context,krb5_keytab keytab)287 static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
288 				       const char *princ_string,
289 				       krb5_principal princ,
290 				       krb5_principal salt_princ,
291 				       int kvno,
292 				       const char *password_s,
293 				       struct smb_krb5_context *smb_krb5_context,
294 				       krb5_keytab keytab)
295 {
296 	int i;
297 	krb5_error_code ret;
298 	krb5_enctype *enctypes;
299 	char *enctype_string;
300 	struct enctypes_container *etc;
301 	krb5_data password;
302 	TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
303 	if (!mem_ctx) {
304 		return ENOMEM;
305 	}
306 
307 	etc = talloc(mem_ctx, struct enctypes_container);
308 	if (!etc) {
309 		talloc_free(mem_ctx);
310 		return ENOMEM;
311 	}
312 	ret = get_kerberos_allowed_etypes(smb_krb5_context->krb5_context,
313 					  &enctypes);
314 	if (ret != 0) {
315 		DEBUG(1,("keytab_add_keys: getting encrption types failed (%s)\n",
316 			 error_message(ret)));
317 		talloc_free(mem_ctx);
318 		return ret;
319 	}
320 
321 	etc->smb_krb5_context = talloc_reference(etc, smb_krb5_context);
322 	etc->enctypes = enctypes;
323 
324 	talloc_set_destructor(etc, free_enctypes);
325 
326 	password.data = discard_const_p(char *, password_s);
327 	password.length = strlen(password_s);
328 
329 	for (i=0; enctypes[i]; i++) {
330 		krb5_keytab_entry entry;
331 		ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context,
332 						      salt_princ, &password, &entry.keyblock, enctypes[i]);
333 		if (ret != 0) {
334 			talloc_free(mem_ctx);
335 			return ret;
336 		}
337 
338                 entry.principal = princ;
339                 entry.vno       = kvno;
340 		ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
341 		enctype_string = NULL;
342 		krb5_enctype_to_string(smb_krb5_context->krb5_context, enctypes[i], &enctype_string);
343 		if (ret != 0) {
344 			DEBUG(1, ("Failed to add %s entry for %s(kvno %d) to keytab: %s\n",
345 				  enctype_string,
346 				  princ_string,
347 				  kvno,
348 				  smb_get_krb5_error_message(smb_krb5_context->krb5_context,
349 							     ret, mem_ctx)));
350 			talloc_free(mem_ctx);
351 			free(enctype_string);
352 			krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
353 			return ret;
354 		}
355 
356 		DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n",
357 			  princ_string, kvno,
358 			  enctype_string));
359 		free(enctype_string);
360 
361 		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
362 	}
363 	talloc_free(mem_ctx);
364 	return 0;
365 }
366 
create_keytab(TALLOC_CTX * parent_ctx,struct cli_credentials * machine_account,struct smb_krb5_context * smb_krb5_context,krb5_keytab keytab,BOOL add_old)367 static int create_keytab(TALLOC_CTX *parent_ctx,
368 			 struct cli_credentials *machine_account,
369 			 struct smb_krb5_context *smb_krb5_context,
370 			 krb5_keytab keytab,
371 			 BOOL add_old)
372 {
373 	krb5_error_code ret;
374 	const char *password_s;
375 	char *enctype_string;
376 	const char *old_secret;
377 	int kvno;
378 	krb5_principal salt_princ;
379 	krb5_principal princ;
380 	const char *princ_string;
381 
382 	TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
383 	if (!mem_ctx) {
384 		return ENOMEM;
385 	}
386 
387 	princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
388 	/* Get the principal we will store the new keytab entries under */
389 	ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
390 	if (ret) {
391 		DEBUG(1,("create_keytab: makeing krb5 principal failed (%s)\n",
392 			 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
393 						    ret, mem_ctx)));
394 		talloc_free(mem_ctx);
395 		return ret;
396 	}
397 
398 	/* The salt used to generate these entries may be different however, fetch that */
399 	ret = salt_principal_from_credentials(mem_ctx, machine_account,
400 					      smb_krb5_context,
401 					      &salt_princ);
402 	if (ret) {
403 		DEBUG(1,("create_keytab: makeing salt principal failed (%s)\n",
404 			 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
405 						    ret, mem_ctx)));
406 		talloc_free(mem_ctx);
407 		return ret;
408 	}
409 
410 	/* Finally, do the dance to get the password to put in the entry */
411 	password_s = cli_credentials_get_password(machine_account);
412 	if (!password_s) {
413 		/* If we don't have the plaintext password, try for
414 		 * the MD4 password hash */
415 
416 		krb5_keytab_entry entry;
417 		const struct samr_Password *mach_pwd;
418 		mach_pwd = cli_credentials_get_nt_hash(machine_account, mem_ctx);
419 		if (!mach_pwd) {
420 			DEBUG(1, ("create_keytab: Domain trust informaton for account %s not available\n",
421 				  cli_credentials_get_principal(machine_account, mem_ctx)));
422 			talloc_free(mem_ctx);
423 			return EINVAL;
424 		}
425 		ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
426 					 ETYPE_ARCFOUR_HMAC_MD5,
427 					 mach_pwd->hash, sizeof(mach_pwd->hash),
428 					 &entry.keyblock);
429 		if (ret) {
430 			DEBUG(1, ("create_keytab: krb5_keyblock_init failed: %s\n",
431 				  smb_get_krb5_error_message(smb_krb5_context->krb5_context,
432 							     ret, mem_ctx)));
433 			talloc_free(mem_ctx);
434 			return ret;
435 		}
436 
437 		entry.principal = princ;
438 		entry.vno       = cli_credentials_get_kvno(machine_account);
439 		ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
440 		if (ret) {
441 			DEBUG(1, ("Failed to add ARCFOUR_HMAC (only) entry for %s to keytab: %s",
442 				  cli_credentials_get_principal(machine_account, mem_ctx),
443 				  smb_get_krb5_error_message(smb_krb5_context->krb5_context,
444 							     ret, mem_ctx)));
445 			talloc_free(mem_ctx);
446 			krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
447 			return ret;
448 		}
449 
450 		krb5_enctype_to_string(smb_krb5_context->krb5_context,
451 				       ETYPE_ARCFOUR_HMAC_MD5,
452 				       &enctype_string);
453 		DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n",
454 			  cli_credentials_get_principal(machine_account, mem_ctx),
455 			  cli_credentials_get_kvno(machine_account),
456 			  enctype_string));
457 		free(enctype_string);
458 
459 		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
460 
461 		/* Can't go any further, we only have this one key */
462 		talloc_free(mem_ctx);
463 		return 0;
464 	}
465 
466 	kvno = cli_credentials_get_kvno(machine_account);
467 	/* good, we actually have the real plaintext */
468 	ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ,
469 			      kvno, password_s, smb_krb5_context, keytab);
470 	if (!ret) {
471 		talloc_free(mem_ctx);
472 		return ret;
473 	}
474 
475 	if (!add_old || kvno == 0) {
476 		talloc_free(mem_ctx);
477 		return 0;
478 	}
479 
480 	old_secret = cli_credentials_get_old_password(machine_account);
481 	if (!old_secret) {
482 		talloc_free(mem_ctx);
483 		return 0;
484 	}
485 
486 	ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ,
487 			      kvno - 1, old_secret, smb_krb5_context, keytab);
488 	if (!ret) {
489 		talloc_free(mem_ctx);
490 		return ret;
491 	}
492 
493 	talloc_free(mem_ctx);
494 	return 0;
495 }
496 
497 
498 /*
499  * Walk the keytab, looking for entries of this principal name, with KVNO other than current kvno -1.
500  *
501  * These entries are now stale, we only keep the current, and previous entries around.
502  *
503  * Inspired by the code in Samba3 for 'use kerberos keytab'.
504  *
505  */
506 
remove_old_entries(TALLOC_CTX * parent_ctx,struct cli_credentials * machine_account,struct smb_krb5_context * smb_krb5_context,krb5_keytab keytab,BOOL * found_previous)507 static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx,
508 					  struct cli_credentials *machine_account,
509 					  struct smb_krb5_context *smb_krb5_context,
510 					  krb5_keytab keytab, BOOL *found_previous)
511 {
512 	krb5_error_code ret, ret2;
513 	krb5_kt_cursor cursor;
514 	krb5_principal princ;
515 	int kvno;
516 	TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
517 	const char *princ_string;
518 	if (!mem_ctx) {
519 		return ENOMEM;
520 	}
521 
522 	*found_previous = False;
523 	princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
524 
525 	/* Get the principal we will store the new keytab entries under */
526 	ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
527 	if (ret) {
528 		DEBUG(1,("update_keytab: makeing krb5 principal failed (%s)\n",
529 			 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
530 						    ret, mem_ctx)));
531 		talloc_free(mem_ctx);
532 		return ret;
533 	}
534 
535 	kvno = cli_credentials_get_kvno(machine_account);
536 
537 	/* for each entry in the keytab */
538 	ret = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
539 	switch (ret) {
540 	case 0:
541 		break;
542 	case HEIM_ERR_OPNOTSUPP:
543 	case ENOENT:
544 	case KRB5_KT_END:
545 		/* no point enumerating if there isn't anything here */
546 		talloc_free(mem_ctx);
547 		return 0;
548 	default:
549 		DEBUG(1,("failed to open keytab for read of old entries: %s\n",
550 			 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
551 						    ret, mem_ctx)));
552 		talloc_free(mem_ctx);
553 		return ret;
554 	}
555 
556 	while (!ret) {
557 		krb5_keytab_entry entry;
558 		ret = krb5_kt_next_entry(smb_krb5_context->krb5_context, keytab, &entry, &cursor);
559 		if (ret) {
560 			break;
561 		}
562 		/* if it matches our principal */
563 		if (!krb5_kt_compare(smb_krb5_context->krb5_context, &entry, princ, 0, 0)) {
564 			/* Free the entry, it wasn't the one we were looking for anyway */
565 			krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
566 			continue;
567 		}
568 
569 		/* delete it, if it is not kvno -1 */
570 		if (entry.vno != (kvno - 1 )) {
571 			/* Release the enumeration.  We are going to
572 			 * have to start this from the top again,
573 			 * because deletes during enumeration may not
574 			 * always be consistant.
575 			 *
576 			 * Also, the enumeration locks a FILE: keytab
577 			 */
578 
579 			krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
580 
581 			ret = krb5_kt_remove_entry(smb_krb5_context->krb5_context, keytab, &entry);
582 			krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
583 
584 			/* Deleted: Restart from the top */
585 			ret2 = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
586 			if (ret2) {
587 				krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
588 				DEBUG(1,("failed to restart enumeration of keytab: %s\n",
589 					 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
590 								    ret, mem_ctx)));
591 
592 				talloc_free(mem_ctx);
593 				return ret2;
594 			}
595 
596 			if (ret) {
597 				break;
598 			}
599 
600 		} else {
601 			*found_previous = True;
602 		}
603 
604 		/* Free the entry, we don't need it any more */
605 		krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
606 
607 
608 	}
609 	krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
610 
611 	switch (ret) {
612 	case 0:
613 		break;
614 	case ENOENT:
615 	case KRB5_KT_END:
616 		ret = 0;
617 		break;
618 	default:
619 		DEBUG(1,("failed in deleting old entries for principal: %s: %s\n",
620 			 princ_string,
621 			 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
622 						    ret, mem_ctx)));
623 	}
624 	talloc_free(mem_ctx);
625 	return ret;
626 }
627 
smb_krb5_update_keytab(TALLOC_CTX * parent_ctx,struct cli_credentials * machine_account,struct smb_krb5_context * smb_krb5_context,struct keytab_container * keytab_container)628 int smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
629 			   struct cli_credentials *machine_account,
630 			   struct smb_krb5_context *smb_krb5_context,
631 			   struct keytab_container *keytab_container)
632 {
633 	krb5_error_code ret;
634 	BOOL found_previous;
635 	TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
636 	if (!mem_ctx) {
637 		return ENOMEM;
638 	}
639 
640 	ret = remove_old_entries(mem_ctx, machine_account,
641 				 smb_krb5_context, keytab_container->keytab, &found_previous);
642 	if (ret != 0) {
643 		talloc_free(mem_ctx);
644 		return ret;
645 	}
646 
647 	/* Create a new keytab.  If during the cleanout we found
648 	 * entires for kvno -1, then don't try and duplicate them.
649 	 * Otherwise, add kvno, and kvno -1 */
650 
651 	ret = create_keytab(mem_ctx, machine_account, smb_krb5_context,
652 			    keytab_container->keytab,
653 			    found_previous ? False : True);
654 	talloc_free(mem_ctx);
655 	return ret;
656 }
657 
smb_krb5_create_memory_keytab(TALLOC_CTX * parent_ctx,struct cli_credentials * machine_account,struct smb_krb5_context * smb_krb5_context,struct keytab_container ** keytab_container)658 _PUBLIC_ int smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
659 				  struct cli_credentials *machine_account,
660 				  struct smb_krb5_context *smb_krb5_context,
661 				  struct keytab_container **keytab_container)
662 {
663 	krb5_error_code ret;
664 	TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
665 	const char *rand_string;
666 	const char *keytab_name;
667 	if (!mem_ctx) {
668 		return ENOMEM;
669 	}
670 
671 	*keytab_container = talloc(mem_ctx, struct keytab_container);
672 
673 	rand_string = generate_random_str(mem_ctx, 16);
674 	if (!rand_string) {
675 		talloc_free(mem_ctx);
676 		return ENOMEM;
677 	}
678 
679 	keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s",
680 				      rand_string);
681 	if (!keytab_name) {
682 		talloc_free(mem_ctx);
683 		return ENOMEM;
684 	}
685 
686 	ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context, keytab_name, keytab_container);
687 	if (ret) {
688 		return ret;
689 	}
690 
691 	ret = smb_krb5_update_keytab(mem_ctx, machine_account, smb_krb5_context, *keytab_container);
692 	if (ret == 0) {
693 		talloc_steal(parent_ctx, *keytab_container);
694 	} else {
695 		*keytab_container = NULL;
696 	}
697 	talloc_free(mem_ctx);
698 	return ret;
699 }
700 
701