s3:files: factor fsp_free() out of file_free()
[Samba/gebeck_regimport.git] / source4 / auth / kerberos / srv_keytab.c
blobc3c96163e021bae6d93951750ca32a3a48ef976f
1 /*
2 Unix SMB/CIFS implementation.
4 Kerberos utility functions
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
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.
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.
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/>.
24 #include "includes.h"
25 #include "system/kerberos.h"
26 #include "auth/kerberos/kerberos.h"
27 #include "auth/kerberos/kerberos_srv_keytab.h"
29 static void keytab_principals_free(krb5_context context, krb5_principal *set)
31 int i;
32 for (i = 0; set[i] != NULL; i++) {
33 krb5_free_principal(context, set[i]);
37 static krb5_error_code principals_from_list(TALLOC_CTX *parent_ctx,
38 const char *samAccountName,
39 const char *realm,
40 const char **SPNs, int num_SPNs,
41 krb5_context context,
42 krb5_principal **principals_out,
43 const char **error_string)
45 unsigned int i;
46 krb5_error_code ret;
47 char *upper_realm;
48 TALLOC_CTX *tmp_ctx;
49 krb5_principal *principals = NULL;
50 tmp_ctx = talloc_new(parent_ctx);
51 if (!tmp_ctx) {
52 *error_string = "Cannot allocate tmp_ctx";
53 return ENOMEM;
56 if (!realm) {
57 *error_string = "Cannot make principal without a realm";
58 ret = EINVAL;
59 goto done;
62 upper_realm = strupper_talloc(tmp_ctx, realm);
63 if (!upper_realm) {
64 *error_string = "Cannot allocate full upper case realm";
65 ret = ENOMEM;
66 goto done;
69 principals = talloc_zero_array(tmp_ctx, krb5_principal,
70 num_SPNs ? (num_SPNs + 2) : 2);
72 for (i = 0; num_SPNs && i < num_SPNs; i++) {
73 ret = krb5_parse_name(context, SPNs[i], &principals[i]);
75 if (ret) {
76 *error_string = smb_get_krb5_error_message(context, ret,
77 parent_ctx);
78 goto done;
82 if (samAccountName) {
83 ret = smb_krb5_make_principal(context, &principals[i],
84 upper_realm, samAccountName,
85 NULL);
86 if (ret) {
87 *error_string = smb_get_krb5_error_message(context, ret,
88 parent_ctx);
89 goto done;
93 done:
94 if (ret) {
95 keytab_principals_free(context, principals);
96 } else {
97 *principals_out = talloc_steal(parent_ctx, principals);
99 talloc_free(tmp_ctx);
100 return ret;
103 static krb5_error_code salt_principal(TALLOC_CTX *parent_ctx,
104 const char *samAccountName,
105 const char *realm,
106 const char *saltPrincipal,
107 krb5_context context,
108 krb5_principal *salt_princ,
109 const char **error_string)
112 krb5_error_code ret;
113 char *machine_username;
114 char *salt_body;
115 char *lower_realm;
116 char *upper_realm;
118 TALLOC_CTX *tmp_ctx;
120 if (saltPrincipal) {
121 ret = krb5_parse_name(context, saltPrincipal, salt_princ);
122 if (ret) {
123 *error_string = smb_get_krb5_error_message(
124 context, ret, parent_ctx);
126 return ret;
129 if (!samAccountName) {
130 (*error_string) = "Cannot determine salt principal, no "
131 "saltPrincipal or samAccountName specified";
132 return EINVAL;
135 if (!realm) {
136 *error_string = "Cannot make principal without a realm";
137 return EINVAL;
140 tmp_ctx = talloc_new(parent_ctx);
141 if (!tmp_ctx) {
142 *error_string = "Cannot allocate tmp_ctx";
143 return ENOMEM;
146 machine_username = talloc_strdup(tmp_ctx, samAccountName);
147 if (!machine_username) {
148 *error_string = "Cannot duplicate samAccountName";
149 talloc_free(tmp_ctx);
150 return ENOMEM;
153 if (machine_username[strlen(machine_username)-1] == '$') {
154 machine_username[strlen(machine_username)-1] = '\0';
157 lower_realm = strlower_talloc(tmp_ctx, realm);
158 if (!lower_realm) {
159 *error_string = "Cannot allocate to lower case realm";
160 talloc_free(tmp_ctx);
161 return ENOMEM;
164 upper_realm = strupper_talloc(tmp_ctx, realm);
165 if (!upper_realm) {
166 *error_string = "Cannot allocate to upper case realm";
167 talloc_free(tmp_ctx);
168 return ENOMEM;
171 salt_body = talloc_asprintf(tmp_ctx, "%s.%s",
172 machine_username, lower_realm);
173 if (!salt_body) {
174 *error_string = "Cannot form salt principal body";
175 talloc_free(tmp_ctx);
176 return ENOMEM;
179 ret = smb_krb5_make_principal(context, salt_princ, upper_realm,
180 "host", salt_body, NULL);
181 if (ret) {
182 *error_string = smb_get_krb5_error_message(context,
183 ret, parent_ctx);
186 talloc_free(tmp_ctx);
187 return ret;
190 /* Translate between the Microsoft msDS-SupportedEncryptionTypes values
191 * and the IETF encryption type values */
192 static krb5_enctype ms_suptype_to_ietf_enctype(uint32_t enctype_bitmap)
194 switch (enctype_bitmap) {
195 case ENC_CRC32:
196 return ENCTYPE_DES_CBC_CRC;
197 case ENC_RSA_MD5:
198 return ENCTYPE_DES_CBC_MD5;
199 case ENC_RC4_HMAC_MD5:
200 return ENCTYPE_ARCFOUR_HMAC;
201 case ENC_HMAC_SHA1_96_AES128:
202 return ENCTYPE_AES128_CTS_HMAC_SHA1_96;
203 case ENC_HMAC_SHA1_96_AES256:
204 return ENCTYPE_AES256_CTS_HMAC_SHA1_96;
205 default:
206 return 0;
210 /* Return an array of krb5_enctype values */
211 static krb5_error_code ms_suptypes_to_ietf_enctypes(TALLOC_CTX *mem_ctx,
212 uint32_t enctype_bitmap,
213 krb5_enctype **enctypes)
215 unsigned int i, j = 0;
216 *enctypes = talloc_zero_array(mem_ctx, krb5_enctype,
217 (8 * sizeof(enctype_bitmap)) + 1);
218 if (!*enctypes) {
219 return ENOMEM;
221 for (i = 0; i < (8 * sizeof(enctype_bitmap)); i++) {
222 uint32_t bit_value = (1 << i) & enctype_bitmap;
223 if (bit_value & enctype_bitmap) {
224 (*enctypes)[j] = ms_suptype_to_ietf_enctype(bit_value);
225 if (!(*enctypes)[j]) {
226 continue;
228 j++;
231 (*enctypes)[j] = 0;
232 return 0;
235 static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
236 krb5_principal *principals,
237 krb5_principal salt_princ,
238 int kvno,
239 const char *password_s,
240 krb5_context context,
241 krb5_enctype *enctypes,
242 krb5_keytab keytab,
243 const char **error_string)
245 unsigned int i, p;
246 krb5_error_code ret;
247 krb5_data password;
248 char *unparsed;
250 password.data = discard_const_p(char, password_s);
251 password.length = strlen(password_s);
253 for (i = 0; enctypes[i]; i++) {
254 krb5_keytab_entry entry;
256 ZERO_STRUCT(entry);
258 ret = create_kerberos_key_from_string_direct(context,
259 salt_princ, &password,
260 KRB5_KT_KEY(&entry),
261 enctypes[i]);
262 if (ret != 0) {
263 return ret;
266 entry.vno = kvno;
268 for (p = 0; principals[p]; p++) {
269 unparsed = NULL;
270 entry.principal = principals[p];
271 ret = krb5_kt_add_entry(context, keytab, &entry);
272 if (ret != 0) {
273 char *k5_error_string =
274 smb_get_krb5_error_message(context,
275 ret, NULL);
276 krb5_unparse_name(context,
277 principals[p], &unparsed);
278 *error_string = talloc_asprintf(parent_ctx,
279 "Failed to add enctype %d entry for "
280 "%s(kvno %d) to keytab: %s\n",
281 (int)enctypes[i], unparsed,
282 kvno, k5_error_string);
284 free(unparsed);
285 talloc_free(k5_error_string);
286 krb5_free_keyblock_contents(context,
287 KRB5_KT_KEY(&entry));
288 return ret;
291 DEBUG(5, ("Added key (kvno %d) to keytab (enctype %d)\n",
292 kvno, (int)enctypes[i]));
294 krb5_free_keyblock_contents(context, KRB5_KT_KEY(&entry));
296 return 0;
299 static krb5_error_code create_keytab(TALLOC_CTX *parent_ctx,
300 const char *samAccountName,
301 const char *realm,
302 const char *saltPrincipal,
303 int kvno,
304 const char *new_secret,
305 const char *old_secret,
306 uint32_t supp_enctypes,
307 krb5_principal *principals,
308 krb5_context context,
309 krb5_keytab keytab,
310 bool add_old,
311 const char **error_string)
313 krb5_error_code ret;
314 krb5_principal salt_princ = NULL;
315 krb5_enctype *enctypes;
316 TALLOC_CTX *mem_ctx;
318 if (!new_secret) {
319 /* There is no password here, so nothing to do */
320 return 0;
323 mem_ctx = talloc_new(parent_ctx);
324 if (!mem_ctx) {
325 *error_string = "unable to allocate tmp_ctx for create_keytab";
326 return ENOMEM;
329 /* The salt used to generate these entries may be different however,
330 * fetch that */
331 ret = salt_principal(mem_ctx, samAccountName, realm, saltPrincipal,
332 context, &salt_princ, error_string);
333 if (ret) {
334 talloc_free(mem_ctx);
335 return ret;
338 ret = ms_suptypes_to_ietf_enctypes(mem_ctx, supp_enctypes, &enctypes);
339 if (ret) {
340 *error_string = talloc_asprintf(parent_ctx,
341 "create_keytab: generating list of "
342 "encryption types failed (%s)\n",
343 smb_get_krb5_error_message(context,
344 ret, mem_ctx));
345 goto done;
348 ret = keytab_add_keys(mem_ctx, principals,
349 salt_princ, kvno, new_secret,
350 context, enctypes, keytab, error_string);
351 if (ret) {
352 goto done;
355 if (old_secret && add_old && kvno != 0) {
356 ret = keytab_add_keys(mem_ctx, principals,
357 salt_princ, kvno - 1, old_secret,
358 context, enctypes, keytab, error_string);
361 done:
362 krb5_free_principal(context, salt_princ);
363 talloc_free(mem_ctx);
364 return ret;
368 * Walk the keytab, looking for entries of this principal name,
369 * with KVNO other than current kvno -1.
371 * These entries are now stale,
372 * we only keep the current and previous entries around.
374 * Inspired by the code in Samba3 for 'use kerberos keytab'.
377 static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx,
378 int kvno,
379 krb5_principal *principals,
380 bool delete_all_kvno,
381 krb5_context context,
382 krb5_keytab keytab,
383 bool *found_previous,
384 const char **error_string)
386 krb5_error_code ret, ret2;
387 krb5_kt_cursor cursor;
388 TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
390 if (!mem_ctx) {
391 return ENOMEM;
394 *found_previous = false;
396 /* for each entry in the keytab */
397 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
398 switch (ret) {
399 case 0:
400 break;
401 #ifdef HEIM_ERR_OPNOTSUPP
402 case HEIM_ERR_OPNOTSUPP:
403 #endif
404 case ENOENT:
405 case KRB5_KT_END:
406 /* no point enumerating if there isn't anything here */
407 talloc_free(mem_ctx);
408 return 0;
409 default:
410 *error_string = talloc_asprintf(parent_ctx,
411 "failed to open keytab for read of old entries: %s\n",
412 smb_get_krb5_error_message(context, ret, mem_ctx));
413 talloc_free(mem_ctx);
414 return ret;
417 while (!ret) {
418 unsigned int i;
419 bool matched = false;
420 krb5_keytab_entry entry;
421 ret = krb5_kt_next_entry(context, keytab, &entry, &cursor);
422 if (ret) {
423 break;
425 for (i = 0; principals[i]; i++) {
426 /* if it matches our principal */
427 if (smb_krb5_kt_compare(context, &entry,
428 principals[i], 0, 0)) {
429 matched = true;
430 break;
434 if (!matched) {
435 /* Free the entry,
436 * it wasn't the one we were looking for anyway */
437 krb5_kt_free_entry(context, &entry);
438 continue;
441 /* delete it, if it is not kvno -1 */
442 if (entry.vno != (kvno - 1 )) {
443 /* Release the enumeration. We are going to
444 * have to start this from the top again,
445 * because deletes during enumeration may not
446 * always be consistent.
448 * Also, the enumeration locks a FILE: keytab
451 krb5_kt_end_seq_get(context, keytab, &cursor);
453 ret = krb5_kt_remove_entry(context, keytab, &entry);
454 krb5_kt_free_entry(context, &entry);
456 /* Deleted: Restart from the top */
457 ret2 = krb5_kt_start_seq_get(context, keytab, &cursor);
458 if (ret2) {
459 krb5_kt_free_entry(context, &entry);
460 DEBUG(1, ("failed to restart enumeration of keytab: %s\n",
461 smb_get_krb5_error_message(context,
462 ret, mem_ctx)));
464 talloc_free(mem_ctx);
465 return ret2;
468 if (ret) {
469 break;
472 } else {
473 *found_previous = true;
476 /* Free the entry, we don't need it any more */
477 krb5_kt_free_entry(context, &entry);
479 krb5_kt_end_seq_get(context, keytab, &cursor);
481 switch (ret) {
482 case 0:
483 break;
484 case ENOENT:
485 case KRB5_KT_END:
486 ret = 0;
487 break;
488 default:
489 *error_string = talloc_asprintf(parent_ctx,
490 "failed in deleting old entries for principal: %s\n",
491 smb_get_krb5_error_message(context, ret, mem_ctx));
493 talloc_free(mem_ctx);
494 return ret;
497 krb5_error_code smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
498 krb5_context context,
499 const char *keytab_name,
500 const char *samAccountName,
501 const char *realm,
502 const char **SPNs,
503 int num_SPNs,
504 const char *saltPrincipal,
505 const char *new_secret,
506 const char *old_secret,
507 int kvno,
508 uint32_t supp_enctypes,
509 bool delete_all_kvno,
510 krb5_keytab *_keytab,
511 const char **error_string)
513 krb5_keytab keytab;
514 krb5_error_code ret;
515 bool found_previous;
516 TALLOC_CTX *tmp_ctx;
517 krb5_principal *principals = NULL;
519 if (keytab_name == NULL) {
520 return ENOENT;
523 ret = krb5_kt_resolve(context, keytab_name, &keytab);
524 if (ret) {
525 *error_string = smb_get_krb5_error_message(context,
526 ret, parent_ctx);
527 return ret;
530 DEBUG(5, ("Opened keytab %s\n", keytab_name));
532 tmp_ctx = talloc_new(parent_ctx);
533 if (!tmp_ctx) {
534 return ENOMEM;
537 /* Get the principal we will store the new keytab entries under */
538 ret = principals_from_list(tmp_ctx,
539 samAccountName, realm, SPNs, num_SPNs,
540 context, &principals, error_string);
542 if (ret != 0) {
543 *error_string = talloc_asprintf(parent_ctx,
544 "Failed to load principals from ldb message: %s\n",
545 *error_string);
546 goto done;
549 ret = remove_old_entries(tmp_ctx, kvno, principals, delete_all_kvno,
550 context, keytab, &found_previous, error_string);
551 if (ret != 0) {
552 *error_string = talloc_asprintf(parent_ctx,
553 "Failed to remove old principals from keytab: %s\n",
554 *error_string);
555 goto done;
558 if (!delete_all_kvno) {
559 /* Create a new keytab. If during the cleanout we found
560 * entires for kvno -1, then don't try and duplicate them.
561 * Otherwise, add kvno, and kvno -1 */
563 ret = create_keytab(tmp_ctx,
564 samAccountName, realm, saltPrincipal,
565 kvno, new_secret, old_secret,
566 supp_enctypes, principals,
567 context, keytab,
568 found_previous ? false : true,
569 error_string);
570 if (ret) {
571 talloc_steal(parent_ctx, *error_string);
575 if (ret == 0 && _keytab != NULL) {
576 /* caller wants the keytab handle back */
577 *_keytab = keytab;
580 done:
581 keytab_principals_free(context, principals);
582 if (ret != 0 || _keytab == NULL) {
583 krb5_kt_close(context, keytab);
585 talloc_free(tmp_ctx);
586 return ret;
589 krb5_error_code smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
590 krb5_context context,
591 const char *new_secret,
592 const char *samAccountName,
593 const char *realm,
594 int kvno,
595 krb5_keytab *keytab,
596 const char **keytab_name)
598 krb5_error_code ret;
599 TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
600 const char *rand_string;
601 const char *error_string;
602 if (!mem_ctx) {
603 return ENOMEM;
606 rand_string = generate_random_str(mem_ctx, 16);
607 if (!rand_string) {
608 talloc_free(mem_ctx);
609 return ENOMEM;
612 *keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s", rand_string);
613 if (*keytab_name == NULL) {
614 talloc_free(mem_ctx);
615 return ENOMEM;
619 ret = smb_krb5_update_keytab(mem_ctx, context,
620 *keytab_name, samAccountName, realm,
621 NULL, 0, NULL, new_secret, NULL,
622 kvno, ENC_ALL_TYPES,
623 false, keytab, &error_string);
624 if (ret == 0) {
625 talloc_steal(parent_ctx, *keytab_name);
626 } else {
627 DEBUG(0, ("Failed to create in-memory keytab: %s\n",
628 error_string));
629 *keytab_name = NULL;
631 talloc_free(mem_ctx);
632 return ret;