CVE-2023-4154 dsdb/tests: Add test for SEARCH_FLAG_RODC_ATTRIBUTE behaviour
[Samba.git] / source3 / libads / kerberos_keytab.c
blob13b102a5aa6fb5c92da2be56139df4b3c001b9a8
1 /*
2 Unix SMB/CIFS implementation.
3 kerberos keytab utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Luke Howard 2003
7 Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003
8 Copyright (C) Guenther Deschner 2003
9 Copyright (C) Rakesh Patel 2004
10 Copyright (C) Dan Perry 2004
11 Copyright (C) Jeremy Allison 2004
12 Copyright (C) Gerald Carter 2006
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 3 of the License, or
17 (at your option) any later version.
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include "includes.h"
29 #include "smb_krb5.h"
30 #include "ads.h"
31 #include "secrets.h"
33 #ifdef HAVE_KRB5
35 #ifdef HAVE_ADS
37 /* This MAX_NAME_LEN is a constant defined in krb5.h */
38 #ifndef MAX_KEYTAB_NAME_LEN
39 #define MAX_KEYTAB_NAME_LEN 1100
40 #endif
42 static krb5_error_code ads_keytab_open(krb5_context context,
43 krb5_keytab *keytab)
45 char keytab_str[MAX_KEYTAB_NAME_LEN] = {0};
46 const char *keytab_name = NULL;
47 krb5_error_code ret = 0;
49 switch (lp_kerberos_method()) {
50 case KERBEROS_VERIFY_SYSTEM_KEYTAB:
51 case KERBEROS_VERIFY_SECRETS_AND_KEYTAB:
52 ret = krb5_kt_default_name(context,
53 keytab_str,
54 sizeof(keytab_str) - 2);
55 if (ret != 0) {
56 DBG_WARNING("Failed to get default keytab name");
57 goto out;
59 keytab_name = keytab_str;
60 break;
61 case KERBEROS_VERIFY_DEDICATED_KEYTAB:
62 keytab_name = lp_dedicated_keytab_file();
63 break;
64 default:
65 DBG_ERR("Invalid kerberos method set (%d)\n",
66 lp_kerberos_method());
67 ret = KRB5_KT_BADNAME;
68 goto out;
71 if (keytab_name == NULL || keytab_name[0] == '\0') {
72 DBG_ERR("Invalid keytab name\n");
73 ret = KRB5_KT_BADNAME;
74 goto out;
77 ret = smb_krb5_kt_open(context, keytab_name, true, keytab);
78 if (ret != 0) {
79 DBG_WARNING("smb_krb5_kt_open failed (%s)\n",
80 error_message(ret));
81 goto out;
84 out:
85 return ret;
88 static bool fill_default_spns(TALLOC_CTX *ctx, const char *machine_name,
89 const char *my_fqdn, const char *spn,
90 const char ***spns)
92 char *psp1, *psp2;
94 if (*spns == NULL) {
95 *spns = talloc_zero_array(ctx, const char*, 3);
96 if (*spns == NULL) {
97 return false;
101 psp1 = talloc_asprintf(ctx,
102 "%s/%s",
103 spn,
104 machine_name);
105 if (psp1 == NULL) {
106 return false;
109 if (!strlower_m(&psp1[strlen(spn) + 1])) {
110 return false;
112 (*spns)[0] = psp1;
114 psp2 = talloc_asprintf(ctx,
115 "%s/%s",
116 spn,
117 my_fqdn);
118 if (psp2 == NULL) {
119 return false;
122 if (!strlower_m(&psp2[strlen(spn) + 1])) {
123 return false;
126 (*spns)[1] = psp2;
128 return true;
131 static bool ads_set_machine_account_spns(TALLOC_CTX *ctx,
132 ADS_STRUCT *ads,
133 const char *service_or_spn,
134 const char *my_fqdn)
136 const char **spn_names = NULL;
137 ADS_STATUS aderr;
138 struct spn_struct* spn_struct = NULL;
139 char *tmp = NULL;
141 /* SPN should have '/' */
142 tmp = strchr_m(service_or_spn, '/');
143 if (tmp != NULL) {
144 spn_struct = parse_spn(ctx, service_or_spn);
145 if (spn_struct == NULL) {
146 return false;
150 DBG_INFO("Attempting to add/update '%s'\n", service_or_spn);
152 if (spn_struct != NULL) {
153 spn_names = talloc_zero_array(ctx, const char*, 2);
154 spn_names[0] = service_or_spn;
155 } else {
156 bool ok;
158 ok = fill_default_spns(ctx,
159 lp_netbios_name(),
160 my_fqdn,
161 service_or_spn,
162 &spn_names);
163 if (!ok) {
164 return false;
167 aderr = ads_add_service_principal_names(ads,
168 lp_netbios_name(),
169 spn_names);
170 if (!ADS_ERR_OK(aderr)) {
171 DBG_WARNING("Failed to add service principal name.\n");
172 return false;
175 return true;
179 * Create kerberos principal(s) from SPN or service name.
181 static bool service_or_spn_to_kerberos_princ(TALLOC_CTX *ctx,
182 const char *service_or_spn,
183 const char *my_fqdn,
184 char **p_princ_s,
185 char **p_short_princ_s)
187 char *princ_s = NULL;
188 char *short_princ_s = NULL;
189 const char *service = service_or_spn;
190 const char *host = my_fqdn;
191 struct spn_struct* spn_struct = NULL;
192 char *tmp = NULL;
193 bool ok = true;
195 /* SPN should have '/' */
196 tmp = strchr_m(service_or_spn, '/');
197 if (tmp != NULL) {
198 spn_struct = parse_spn(ctx, service_or_spn);
199 if (spn_struct == NULL) {
200 ok = false;
201 goto out;
204 if (spn_struct != NULL) {
205 service = spn_struct->serviceclass;
206 host = spn_struct->host;
208 princ_s = talloc_asprintf(ctx, "%s/%s@%s",
209 service,
210 host, lp_realm());
211 if (princ_s == NULL) {
212 ok = false;
213 goto out;
216 if (spn_struct == NULL) {
217 short_princ_s = talloc_asprintf(ctx, "%s/%s@%s",
218 service, lp_netbios_name(),
219 lp_realm());
220 if (short_princ_s == NULL) {
221 ok = false;
222 goto out;
225 *p_princ_s = princ_s;
226 *p_short_princ_s = short_princ_s;
227 out:
228 return ok;
231 static int add_kt_entry_etypes(krb5_context context, TALLOC_CTX *tmpctx,
232 ADS_STRUCT *ads, const char *salt_princ_s,
233 krb5_keytab keytab, krb5_kvno kvno,
234 const char *srvPrinc, const char *my_fqdn,
235 krb5_data *password, bool update_ads)
237 krb5_error_code ret = 0;
238 char *princ_s = NULL;
239 char *short_princ_s = NULL;
240 krb5_enctype enctypes[4] = {
241 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
242 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
243 ENCTYPE_ARCFOUR_HMAC,
246 size_t i;
248 /* Construct our principal */
249 if (strchr_m(srvPrinc, '@')) {
250 /* It's a fully-named principal. */
251 princ_s = talloc_asprintf(tmpctx, "%s", srvPrinc);
252 if (!princ_s) {
253 ret = -1;
254 goto out;
256 } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
257 /* It's the machine account, as used by smbclient clients. */
258 princ_s = talloc_asprintf(tmpctx, "%s@%s",
259 srvPrinc, lp_realm());
260 if (!princ_s) {
261 ret = -1;
262 goto out;
264 } else {
265 /* It's a normal service principal. Add the SPN now so that we
266 * can obtain credentials for it and double-check the salt value
267 * used to generate the service's keys. */
269 if (!service_or_spn_to_kerberos_princ(tmpctx,
270 srvPrinc,
271 my_fqdn,
272 &princ_s,
273 &short_princ_s)) {
274 ret = -1;
275 goto out;
278 /* According to http://support.microsoft.com/kb/326985/en-us,
279 certain principal names are automatically mapped to the
280 host/... principal in the AD account.
281 So only create these in the keytab, not in AD. --jerry */
283 if (update_ads && !strequal(srvPrinc, "cifs") &&
284 !strequal(srvPrinc, "host")) {
285 if (!ads_set_machine_account_spns(tmpctx,
286 ads,
287 srvPrinc,
288 my_fqdn)) {
289 ret = -1;
290 goto out;
295 for (i = 0; enctypes[i]; i++) {
297 /* add the fqdn principal to the keytab */
298 ret = smb_krb5_kt_add_entry(context,
299 keytab,
300 kvno,
301 princ_s,
302 salt_princ_s,
303 enctypes[i],
304 password,
305 false); /* no_salt */
306 if (ret) {
307 DBG_WARNING("Failed to add entry to keytab\n");
308 goto out;
311 /* add the short principal name if we have one */
312 if (short_princ_s) {
313 ret = smb_krb5_kt_add_entry(context,
314 keytab,
315 kvno,
316 short_princ_s,
317 salt_princ_s,
318 enctypes[i],
319 password,
320 false); /* no_salt */
321 if (ret) {
322 DBG_WARNING("Failed to add short entry to keytab\n");
323 goto out;
327 out:
328 return ret;
331 /**********************************************************************
332 Adds a single service principal, i.e. 'host' to the system keytab
333 ***********************************************************************/
335 int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc, bool update_ads)
337 krb5_error_code ret = 0;
338 krb5_context context = NULL;
339 krb5_keytab keytab = NULL;
340 krb5_data password;
341 krb5_kvno kvno;
342 char *salt_princ_s = NULL;
343 char *password_s = NULL;
344 char *my_fqdn;
345 TALLOC_CTX *tmpctx = NULL;
346 char **hostnames_array = NULL;
347 size_t num_hostnames = 0;
349 ret = smb_krb5_init_context_common(&context);
350 if (ret) {
351 DBG_ERR("kerberos init context failed (%s)\n",
352 error_message(ret));
353 return -1;
356 ret = ads_keytab_open(context, &keytab);
357 if (ret != 0) {
358 goto out;
361 /* retrieve the password */
362 if (!secrets_init()) {
363 DBG_WARNING("secrets_init failed\n");
364 ret = -1;
365 goto out;
367 password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
368 if (!password_s) {
369 DBG_WARNING("failed to fetch machine password\n");
370 ret = -1;
371 goto out;
373 ZERO_STRUCT(password);
374 password.data = password_s;
375 password.length = strlen(password_s);
377 /* we need the dNSHostName value here */
378 tmpctx = talloc_init(__location__);
379 if (!tmpctx) {
380 DBG_ERR("talloc_init() failed!\n");
381 ret = -1;
382 goto out;
385 my_fqdn = ads_get_dnshostname(ads, tmpctx, lp_netbios_name());
386 if (!my_fqdn) {
387 DBG_ERR("unable to determine machine account's dns name in "
388 "AD!\n");
389 ret = -1;
390 goto out;
393 /* make sure we have a single instance of a the computer account */
394 if (!ads_has_samaccountname(ads, tmpctx, lp_netbios_name())) {
395 DBG_ERR("unable to determine machine account's short name in "
396 "AD!\n");
397 ret = -1;
398 goto out;
401 kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
402 if (kvno == -1) {
403 /* -1 indicates failure, everything else is OK */
404 DBG_WARNING("ads_get_machine_kvno failed to determine the "
405 "system's kvno.\n");
406 ret = -1;
407 goto out;
410 salt_princ_s = kerberos_secrets_fetch_salt_princ();
411 if (salt_princ_s == NULL) {
412 DBG_WARNING("kerberos_secrets_fetch_salt_princ() failed\n");
413 ret = -1;
414 goto out;
417 ret = add_kt_entry_etypes(context, tmpctx, ads, salt_princ_s, keytab,
418 kvno, srvPrinc, my_fqdn, &password,
419 update_ads);
420 if (ret != 0) {
421 goto out;
424 if (ADS_ERR_OK(ads_get_additional_dns_hostnames(tmpctx, ads,
425 lp_netbios_name(),
426 &hostnames_array,
427 &num_hostnames))) {
428 size_t i;
430 for (i = 0; i < num_hostnames; i++) {
432 ret = add_kt_entry_etypes(context, tmpctx, ads,
433 salt_princ_s, keytab,
434 kvno, srvPrinc,
435 hostnames_array[i],
436 &password, update_ads);
437 if (ret != 0) {
438 goto out;
443 out:
444 SAFE_FREE(salt_princ_s);
445 TALLOC_FREE(tmpctx);
447 if (keytab) {
448 krb5_kt_close(context, keytab);
450 if (context) {
451 krb5_free_context(context);
453 return (int)ret;
456 /**********************************************************************
457 Delete a single service principal, i.e. 'host' from the system keytab
458 ***********************************************************************/
460 int ads_keytab_delete_entry(ADS_STRUCT *ads, const char *srvPrinc)
462 TALLOC_CTX *frame = talloc_stackframe();
463 krb5_error_code ret = 0;
464 krb5_context context = NULL;
465 krb5_keytab keytab = NULL;
466 char *princ_s = NULL;
467 krb5_principal princ = NULL;
468 char *short_princ_s = NULL;
469 krb5_principal short_princ = NULL;
470 bool ok;
472 ret = smb_krb5_init_context_common(&context);
473 if (ret) {
474 DBG_ERR("kerberos init context failed (%s)\n",
475 error_message(ret));
476 goto out;
479 ret = ads_keytab_open(context, &keytab);
480 if (ret != 0) {
481 goto out;
484 /* Construct our principal */
485 if (strchr_m(srvPrinc, '@')) {
486 /* It's a fully-named principal. */
487 princ_s = talloc_asprintf(frame, "%s", srvPrinc);
488 if (!princ_s) {
489 ret = -1;
490 goto out;
492 } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
493 /* It's the machine account, as used by smbclient clients. */
494 princ_s = talloc_asprintf(frame, "%s@%s",
495 srvPrinc, lp_realm());
496 if (!princ_s) {
497 ret = -1;
498 goto out;
500 } else {
502 * It's a normal service principal.
504 char *my_fqdn = NULL;
505 char *tmp = NULL;
508 * SPN should have '/' otherwise we
509 * need to fallback and find our dnshostname
511 tmp = strchr_m(srvPrinc, '/');
512 if (tmp == NULL) {
513 my_fqdn = ads_get_dnshostname(ads, frame, lp_netbios_name());
514 if (!my_fqdn) {
515 DBG_ERR("unable to determine machine account's dns name in "
516 "AD!\n");
517 ret = -1;
518 goto out;
522 ok = service_or_spn_to_kerberos_princ(frame,
523 srvPrinc,
524 my_fqdn,
525 &princ_s,
526 &short_princ_s);
527 if (!ok) {
528 ret = -1;
529 goto out;
533 ret = smb_krb5_parse_name(context, princ_s, &princ);
534 if (ret) {
535 DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
536 "failed (%s)\n", princ_s, error_message(ret)));
537 goto out;
540 if (short_princ_s != NULL) {
541 ret = smb_krb5_parse_name(context, short_princ_s, &short_princ);
542 if (ret) {
543 DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
544 "failed (%s)\n", short_princ_s, error_message(ret)));
545 goto out;
549 /* Seek and delete old keytab entries */
550 ret = smb_krb5_kt_seek_and_delete_old_entries(context,
551 keytab,
552 false, /* keep_old_kvno */
554 false, /* enctype_only */
555 ENCTYPE_NULL,
556 princ_s,
557 princ,
558 false); /* flush */
559 if (ret) {
560 goto out;
563 if (short_princ_s == NULL) {
564 goto out;
567 /* Seek and delete old keytab entries */
568 ret = smb_krb5_kt_seek_and_delete_old_entries(context,
569 keytab,
570 false, /* keep_old_kvno */
572 false, /* enctype_only */
573 ENCTYPE_NULL,
574 short_princ_s,
575 short_princ,
576 false); /* flush */
577 if (ret) {
578 goto out;
581 out:
582 if (princ) {
583 krb5_free_principal(context, princ);
585 if (short_princ) {
586 krb5_free_principal(context, short_princ);
588 if (keytab) {
589 krb5_kt_close(context, keytab);
591 if (context) {
592 krb5_free_context(context);
594 TALLOC_FREE(frame);
595 return ret;
598 /**********************************************************************
599 Flushes all entries from the system keytab.
600 ***********************************************************************/
602 int ads_keytab_flush(ADS_STRUCT *ads)
604 krb5_error_code ret = 0;
605 krb5_context context = NULL;
606 krb5_keytab keytab = NULL;
607 ADS_STATUS aderr;
609 ret = smb_krb5_init_context_common(&context);
610 if (ret) {
611 DBG_ERR("kerberos init context failed (%s)\n",
612 error_message(ret));
613 return ret;
616 ret = ads_keytab_open(context, &keytab);
617 if (ret != 0) {
618 goto out;
621 /* Seek and delete all old keytab entries */
622 ret = smb_krb5_kt_seek_and_delete_old_entries(context,
623 keytab,
624 false, /* keep_old_kvno */
626 false, /* enctype_only */
627 ENCTYPE_NULL,
628 NULL,
629 NULL,
630 true); /* flush */
631 if (ret) {
632 goto out;
635 aderr = ads_clear_service_principal_names(ads, lp_netbios_name());
636 if (!ADS_ERR_OK(aderr)) {
637 DEBUG(1, (__location__ ": Error while clearing service "
638 "principal listings in LDAP.\n"));
639 ret = -1;
640 goto out;
643 out:
644 if (keytab) {
645 krb5_kt_close(context, keytab);
647 if (context) {
648 krb5_free_context(context);
650 return ret;
653 /**********************************************************************
654 Adds all the required service principals to the system keytab.
655 ***********************************************************************/
657 int ads_keytab_create_default(ADS_STRUCT *ads)
659 krb5_error_code ret = 0;
660 krb5_context context = NULL;
661 krb5_keytab keytab = NULL;
662 krb5_kt_cursor cursor = {0};
663 krb5_keytab_entry kt_entry = {0};
664 krb5_kvno kvno;
665 size_t found = 0;
666 char *sam_account_name, *upn;
667 char **oldEntries = NULL, *princ_s[26];
668 TALLOC_CTX *frame;
669 char *machine_name;
670 char **spn_array;
671 size_t num_spns;
672 size_t i;
673 bool ok = false;
674 ADS_STATUS status;
676 ZERO_STRUCT(kt_entry);
677 ZERO_STRUCT(cursor);
679 frame = talloc_stackframe();
680 if (frame == NULL) {
681 ret = -1;
682 goto done;
685 status = ads_get_service_principal_names(frame,
686 ads,
687 lp_netbios_name(),
688 &spn_array,
689 &num_spns);
690 if (!ADS_ERR_OK(status)) {
691 ret = -1;
692 goto done;
695 for (i = 0; i < num_spns; i++) {
696 char *srv_princ;
697 char *p;
699 srv_princ = strlower_talloc(frame, spn_array[i]);
700 if (srv_princ == NULL) {
701 ret = -1;
702 goto done;
705 p = strchr_m(srv_princ, '/');
706 if (p == NULL) {
707 continue;
709 p[0] = '\0';
711 /* Add the SPNs found on the DC */
712 ret = ads_keytab_add_entry(ads, srv_princ, false);
713 if (ret != 0) {
714 DEBUG(1, ("ads_keytab_add_entry failed while "
715 "adding '%s' principal.\n",
716 spn_array[i]));
717 goto done;
721 #if 0 /* don't create the CIFS/... keytab entries since no one except smbd
722 really needs them and we will fall back to verifying against
723 secrets.tdb */
725 ret = ads_keytab_add_entry(ads, "cifs", false));
726 if (ret != 0 ) {
727 DEBUG(1, (__location__ ": ads_keytab_add_entry failed while "
728 "adding 'cifs'.\n"));
729 return ret;
731 #endif
733 memset(princ_s, '\0', sizeof(princ_s));
735 ret = smb_krb5_init_context_common(&context);
736 if (ret) {
737 DBG_ERR("kerberos init context failed (%s)\n",
738 error_message(ret));
739 goto done;
742 machine_name = talloc_strdup(frame, lp_netbios_name());
743 if (!machine_name) {
744 ret = -1;
745 goto done;
748 /* now add the userPrincipalName and sAMAccountName entries */
749 ok = ads_has_samaccountname(ads, frame, machine_name);
750 if (!ok) {
751 DEBUG(0, (__location__ ": unable to determine machine "
752 "account's name in AD!\n"));
753 ret = -1;
754 goto done;
758 * append '$' to netbios name so 'ads_keytab_add_entry' recognises
759 * it as a machine account rather than a service or Windows SPN.
761 sam_account_name = talloc_asprintf(frame, "%s$",machine_name);
762 if (sam_account_name == NULL) {
763 ret = -1;
764 goto done;
766 /* upper case the sAMAccountName to make it easier for apps to
767 know what case to use in the keytab file */
768 if (!strupper_m(sam_account_name)) {
769 ret = -1;
770 goto done;
773 ret = ads_keytab_add_entry(ads, sam_account_name, false);
774 if (ret != 0) {
775 DEBUG(1, (__location__ ": ads_keytab_add_entry() failed "
776 "while adding sAMAccountName (%s)\n",
777 sam_account_name));
778 goto done;
781 /* remember that not every machine account will have a upn */
782 upn = ads_get_upn(ads, frame, machine_name);
783 if (upn) {
784 ret = ads_keytab_add_entry(ads, upn, false);
785 if (ret != 0) {
786 DEBUG(1, (__location__ ": ads_keytab_add_entry() "
787 "failed while adding UPN (%s)\n", upn));
788 goto done;
792 /* Now loop through the keytab and update any other existing entries */
793 kvno = (krb5_kvno)ads_get_machine_kvno(ads, machine_name);
794 if (kvno == (krb5_kvno)-1) {
795 DEBUG(1, (__location__ ": ads_get_machine_kvno() failed to "
796 "determine the system's kvno.\n"));
797 goto done;
800 DEBUG(3, (__location__ ": Searching for keytab entries to preserve "
801 "and update.\n"));
803 ret = ads_keytab_open(context, &keytab);
804 if (ret != 0) {
805 goto done;
808 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
809 if (ret != KRB5_KT_END && ret != ENOENT ) {
810 while ((ret = krb5_kt_next_entry(context, keytab,
811 &kt_entry, &cursor)) == 0) {
812 smb_krb5_kt_free_entry(context, &kt_entry);
813 ZERO_STRUCT(kt_entry);
814 found++;
817 krb5_kt_end_seq_get(context, keytab, &cursor);
818 ZERO_STRUCT(cursor);
821 * Hmmm. There is no "rewind" function for the keytab. This means we
822 * have a race condition where someone else could add entries after
823 * we've counted them. Re-open asap to minimise the race. JRA.
825 DEBUG(3, (__location__ ": Found %zd entries in the keytab.\n", found));
826 if (!found) {
827 goto done;
830 oldEntries = talloc_zero_array(frame, char *, found + 1);
831 if (!oldEntries) {
832 DEBUG(1, (__location__ ": Failed to allocate space to store "
833 "the old keytab entries (talloc failed?).\n"));
834 ret = -1;
835 goto done;
838 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
839 if (ret == KRB5_KT_END || ret == ENOENT) {
840 krb5_kt_end_seq_get(context, keytab, &cursor);
841 ZERO_STRUCT(cursor);
842 goto done;
845 while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
846 if (kt_entry.vno != kvno) {
847 char *ktprinc = NULL;
848 char *p;
850 /* This returns a malloc'ed string in ktprinc. */
851 ret = smb_krb5_unparse_name(oldEntries, context,
852 kt_entry.principal,
853 &ktprinc);
854 if (ret) {
855 DEBUG(1, (__location__
856 ": smb_krb5_unparse_name failed "
857 "(%s)\n", error_message(ret)));
858 goto done;
861 * From looking at the krb5 source they don't seem to
862 * take locale or mb strings into account.
863 * Maybe this is because they assume utf8 ?
864 * In this case we may need to convert from utf8 to
865 * mb charset here ? JRA.
867 p = strchr_m(ktprinc, '@');
868 if (p) {
869 *p = '\0';
872 p = strchr_m(ktprinc, '/');
873 if (p) {
874 *p = '\0';
876 for (i = 0; i < found; i++) {
877 if (!oldEntries[i]) {
878 oldEntries[i] = ktprinc;
879 break;
881 if (!strcmp(oldEntries[i], ktprinc)) {
882 TALLOC_FREE(ktprinc);
883 break;
886 if (i == found) {
887 TALLOC_FREE(ktprinc);
890 smb_krb5_kt_free_entry(context, &kt_entry);
891 ZERO_STRUCT(kt_entry);
893 krb5_kt_end_seq_get(context, keytab, &cursor);
894 ZERO_STRUCT(cursor);
896 ret = 0;
897 for (i = 0; oldEntries[i]; i++) {
898 ret |= ads_keytab_add_entry(ads, oldEntries[i], false);
899 TALLOC_FREE(oldEntries[i]);
902 done:
903 TALLOC_FREE(oldEntries);
904 TALLOC_FREE(frame);
906 if (context) {
907 if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
908 smb_krb5_kt_free_entry(context, &kt_entry);
910 if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
911 krb5_kt_end_seq_get(context, keytab, &cursor);
913 if (keytab) {
914 krb5_kt_close(context, keytab);
916 krb5_free_context(context);
918 return ret;
921 #endif /* HAVE_ADS */
923 /**********************************************************************
924 List system keytab.
925 ***********************************************************************/
927 int ads_keytab_list(const char *keytab_name)
929 krb5_error_code ret = 0;
930 krb5_context context = NULL;
931 krb5_keytab keytab = NULL;
932 krb5_kt_cursor cursor;
933 krb5_keytab_entry kt_entry;
935 ZERO_STRUCT(kt_entry);
936 ZERO_STRUCT(cursor);
938 ret = smb_krb5_init_context_common(&context);
939 if (ret) {
940 DBG_ERR("kerberos init context failed (%s)\n",
941 error_message(ret));
942 return ret;
945 if (keytab_name == NULL) {
946 #ifdef HAVE_ADS
947 ret = ads_keytab_open(context, &keytab);
948 #else
949 ret = ENOENT;
950 #endif
951 } else {
952 ret = smb_krb5_kt_open(context, keytab_name, False, &keytab);
954 if (ret) {
955 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
956 error_message(ret)));
957 goto out;
960 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
961 if (ret) {
962 ZERO_STRUCT(cursor);
963 goto out;
966 printf("Vno Type Principal\n");
968 while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
970 char *princ_s = NULL;
971 char *etype_s = NULL;
972 krb5_enctype enctype = 0;
974 ret = smb_krb5_unparse_name(talloc_tos(), context,
975 kt_entry.principal, &princ_s);
976 if (ret) {
977 goto out;
980 enctype = smb_krb5_kt_get_enctype_from_entry(&kt_entry);
982 ret = smb_krb5_enctype_to_string(context, enctype, &etype_s);
983 if (ret &&
984 (asprintf(&etype_s, "UNKNOWN: %d", enctype) == -1)) {
985 TALLOC_FREE(princ_s);
986 goto out;
989 printf("%3d %-43s %s\n", kt_entry.vno, etype_s, princ_s);
991 TALLOC_FREE(princ_s);
992 SAFE_FREE(etype_s);
994 ret = smb_krb5_kt_free_entry(context, &kt_entry);
995 if (ret) {
996 goto out;
1000 ret = krb5_kt_end_seq_get(context, keytab, &cursor);
1001 if (ret) {
1002 goto out;
1005 /* Ensure we don't double free. */
1006 ZERO_STRUCT(kt_entry);
1007 ZERO_STRUCT(cursor);
1008 out:
1010 if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
1011 smb_krb5_kt_free_entry(context, &kt_entry);
1013 if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
1014 krb5_kt_end_seq_get(context, keytab, &cursor);
1017 if (keytab) {
1018 krb5_kt_close(context, keytab);
1020 if (context) {
1021 krb5_free_context(context);
1023 return ret;
1026 #endif /* HAVE_KRB5 */