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/>.
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
42 static krb5_error_code ads_keytab_open(krb5_context context,
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,
54 sizeof(keytab_str) - 2);
56 DBG_WARNING("Failed to get default keytab name");
59 keytab_name = keytab_str;
61 case KERBEROS_VERIFY_DEDICATED_KEYTAB:
62 keytab_name = lp_dedicated_keytab_file();
65 DBG_ERR("Invalid kerberos method set (%d)\n",
66 lp_kerberos_method());
67 ret = KRB5_KT_BADNAME;
71 if (keytab_name == NULL || keytab_name[0] == '\0') {
72 DBG_ERR("Invalid keytab name\n");
73 ret = KRB5_KT_BADNAME;
77 ret = smb_krb5_kt_open(context, keytab_name, true, keytab);
79 DBG_WARNING("smb_krb5_kt_open failed (%s)\n",
88 static bool fill_default_spns(TALLOC_CTX *ctx, const char *machine_name,
89 const char *my_fqdn, const char *spn,
95 *spns = talloc_zero_array(ctx, const char*, 3);
101 psp1 = talloc_asprintf(ctx,
109 if (!strlower_m(&psp1[strlen(spn) + 1])) {
114 psp2 = talloc_asprintf(ctx,
122 if (!strlower_m(&psp2[strlen(spn) + 1])) {
131 static bool ads_set_machine_account_spns(TALLOC_CTX *ctx,
133 const char *service_or_spn,
136 const char **spn_names = NULL;
140 DBG_INFO("Attempting to add/update '%s'\n", service_or_spn);
142 ok = fill_default_spns(ctx,
151 aderr = ads_add_service_principal_names(ads,
154 if (!ADS_ERR_OK(aderr)) {
155 DBG_WARNING("Failed to add service principal name.\n");
163 * Create kerberos principal(s) from SPN or service name.
165 static bool service_or_spn_to_kerberos_princ(TALLOC_CTX *ctx,
166 const char *service_or_spn,
169 char **p_short_princ_s)
171 char *princ_s = NULL;
172 char *short_princ_s = NULL;
173 const char *service = service_or_spn;
174 const char *host = my_fqdn;
175 struct spn_struct* spn_struct = NULL;
179 /* SPN should have '/' */
180 tmp = strchr_m(service_or_spn, '/');
182 spn_struct = parse_spn(ctx, service_or_spn);
183 if (spn_struct == NULL) {
188 if (spn_struct != NULL) {
189 service = spn_struct->serviceclass;
190 host = spn_struct->host;
192 princ_s = talloc_asprintf(ctx, "%s/%s@%s",
195 if (princ_s == NULL) {
200 if (spn_struct == NULL) {
201 short_princ_s = talloc_asprintf(ctx, "%s/%s@%s",
202 service, lp_netbios_name(),
204 if (short_princ_s == NULL) {
209 *p_princ_s = princ_s;
210 *p_short_princ_s = short_princ_s;
215 /**********************************************************************
216 Adds a single service principal, i.e. 'host' to the system keytab
217 ***********************************************************************/
219 int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc)
221 krb5_error_code ret = 0;
222 krb5_context context = NULL;
223 krb5_keytab keytab = NULL;
226 krb5_enctype enctypes[6] = {
229 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
230 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
232 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
233 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
235 ENCTYPE_ARCFOUR_HMAC,
238 char *princ_s = NULL;
239 char *short_princ_s = NULL;
240 char *salt_princ_s = NULL;
241 char *password_s = NULL;
243 TALLOC_CTX *tmpctx = NULL;
246 initialize_krb5_error_table();
247 ret = krb5_init_context(&context);
249 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
250 error_message(ret)));
254 ret = ads_keytab_open(context, &keytab);
259 /* retrieve the password */
260 if (!secrets_init()) {
261 DEBUG(1, (__location__ ": secrets_init failed\n"));
265 password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
267 DEBUG(1, (__location__ ": failed to fetch machine password\n"));
271 ZERO_STRUCT(password);
272 password.data = password_s;
273 password.length = strlen(password_s);
275 /* we need the dNSHostName value here */
276 tmpctx = talloc_init(__location__);
278 DEBUG(0, (__location__ ": talloc_init() failed!\n"));
283 my_fqdn = ads_get_dnshostname(ads, tmpctx, lp_netbios_name());
285 DEBUG(0, (__location__ ": unable to determine machine "
286 "account's dns name in AD!\n"));
291 /* make sure we have a single instance of a the computer account */
292 if (!ads_has_samaccountname(ads, tmpctx, lp_netbios_name())) {
293 DEBUG(0, (__location__ ": unable to determine machine "
294 "account's short name in AD!\n"));
299 /* Construct our principal */
300 if (strchr_m(srvPrinc, '@')) {
301 /* It's a fully-named principal. */
302 princ_s = talloc_asprintf(tmpctx, "%s", srvPrinc);
307 } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
308 /* It's the machine account, as used by smbclient clients. */
309 princ_s = talloc_asprintf(tmpctx, "%s@%s",
310 srvPrinc, lp_realm());
316 /* It's a normal service principal. Add the SPN now so that we
317 * can obtain credentials for it and double-check the salt value
318 * used to generate the service's keys. */
320 if (!service_or_spn_to_kerberos_princ(tmpctx,
329 /* According to http://support.microsoft.com/kb/326985/en-us,
330 certain principal names are automatically mapped to the
331 host/... principal in the AD account.
332 So only create these in the keytab, not in AD. --jerry */
334 if (!strequal(srvPrinc, "cifs") &&
335 !strequal(srvPrinc, "host")) {
336 if (!ads_set_machine_account_spns(tmpctx,
346 kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
348 /* -1 indicates failure, everything else is OK */
349 DEBUG(1, (__location__ ": ads_get_machine_kvno failed to "
350 "determine the system's kvno.\n"));
355 salt_princ_s = kerberos_secrets_fetch_salt_princ();
356 if (salt_princ_s == NULL) {
357 DBG_WARNING("kerberos_secrets_fetch_salt_princ() failed\n");
362 for (i = 0; enctypes[i]; i++) {
364 /* add the fqdn principal to the keytab */
365 ret = smb_krb5_kt_add_entry(context,
375 DEBUG(1, (__location__ ": Failed to add entry to keytab\n"));
379 /* add the short principal name if we have one */
381 ret = smb_krb5_kt_add_entry(context,
391 DEBUG(1, (__location__
392 ": Failed to add short entry to keytab\n"));
399 SAFE_FREE(salt_princ_s);
403 krb5_kt_close(context, keytab);
406 krb5_free_context(context);
411 /**********************************************************************
412 Flushes all entries from the system keytab.
413 ***********************************************************************/
415 int ads_keytab_flush(ADS_STRUCT *ads)
417 krb5_error_code ret = 0;
418 krb5_context context = NULL;
419 krb5_keytab keytab = NULL;
423 initialize_krb5_error_table();
424 ret = krb5_init_context(&context);
426 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
427 error_message(ret)));
431 ret = ads_keytab_open(context, &keytab);
436 kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
438 /* -1 indicates a failure */
439 DEBUG(1, (__location__ ": Error determining the kvno.\n"));
443 /* Seek and delete old keytab entries */
444 ret = smb_krb5_kt_seek_and_delete_old_entries(context,
456 aderr = ads_clear_service_principal_names(ads, lp_netbios_name());
457 if (!ADS_ERR_OK(aderr)) {
458 DEBUG(1, (__location__ ": Error while clearing service "
459 "principal listings in LDAP.\n"));
465 krb5_kt_close(context, keytab);
468 krb5_free_context(context);
473 /**********************************************************************
474 Adds all the required service principals to the system keytab.
475 ***********************************************************************/
477 int ads_keytab_create_default(ADS_STRUCT *ads)
479 krb5_error_code ret = 0;
480 krb5_context context = NULL;
481 krb5_keytab keytab = NULL;
482 krb5_kt_cursor cursor = {0};
483 krb5_keytab_entry kt_entry = {0};
486 char *sam_account_name, *upn;
487 char **oldEntries = NULL, *princ_s[26];
496 ZERO_STRUCT(kt_entry);
499 frame = talloc_stackframe();
505 status = ads_get_service_principal_names(frame,
510 if (!ADS_ERR_OK(status)) {
515 for (i = 0; i < num_spns; i++) {
519 srv_princ = strlower_talloc(frame, spn_array[i]);
520 if (srv_princ == NULL) {
525 p = strchr_m(srv_princ, '/');
531 /* Add the SPNs found on the DC */
532 ret = ads_keytab_add_entry(ads, srv_princ);
534 DEBUG(1, ("ads_keytab_add_entry failed while "
535 "adding '%s' principal.\n",
541 #if 0 /* don't create the CIFS/... keytab entries since no one except smbd
542 really needs them and we will fall back to verifying against
545 ret = ads_keytab_add_entry(ads, "cifs"));
547 DEBUG(1, (__location__ ": ads_keytab_add_entry failed while "
548 "adding 'cifs'.\n"));
553 memset(princ_s, '\0', sizeof(princ_s));
555 initialize_krb5_error_table();
556 ret = krb5_init_context(&context);
558 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
559 error_message(ret)));
563 machine_name = talloc_strdup(frame, lp_netbios_name());
569 /* now add the userPrincipalName and sAMAccountName entries */
570 ok = ads_has_samaccountname(ads, frame, machine_name);
572 DEBUG(0, (__location__ ": unable to determine machine "
573 "account's name in AD!\n"));
579 * append '$' to netbios name so 'ads_keytab_add_entry' recognises
580 * it as a machine account rather than a service or Windows SPN.
582 sam_account_name = talloc_asprintf(frame, "%s$",machine_name);
583 if (sam_account_name == NULL) {
587 /* upper case the sAMAccountName to make it easier for apps to
588 know what case to use in the keytab file */
589 if (!strupper_m(sam_account_name)) {
594 ret = ads_keytab_add_entry(ads, sam_account_name);
596 DEBUG(1, (__location__ ": ads_keytab_add_entry() failed "
597 "while adding sAMAccountName (%s)\n",
602 /* remember that not every machine account will have a upn */
603 upn = ads_get_upn(ads, frame, machine_name);
605 ret = ads_keytab_add_entry(ads, upn);
607 DEBUG(1, (__location__ ": ads_keytab_add_entry() "
608 "failed while adding UPN (%s)\n", upn));
613 /* Now loop through the keytab and update any other existing entries */
614 kvno = (krb5_kvno)ads_get_machine_kvno(ads, machine_name);
615 if (kvno == (krb5_kvno)-1) {
616 DEBUG(1, (__location__ ": ads_get_machine_kvno() failed to "
617 "determine the system's kvno.\n"));
621 DEBUG(3, (__location__ ": Searching for keytab entries to preserve "
624 ret = ads_keytab_open(context, &keytab);
629 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
630 if (ret != KRB5_KT_END && ret != ENOENT ) {
631 while ((ret = krb5_kt_next_entry(context, keytab,
632 &kt_entry, &cursor)) == 0) {
633 smb_krb5_kt_free_entry(context, &kt_entry);
634 ZERO_STRUCT(kt_entry);
638 krb5_kt_end_seq_get(context, keytab, &cursor);
642 * Hmmm. There is no "rewind" function for the keytab. This means we
643 * have a race condition where someone else could add entries after
644 * we've counted them. Re-open asap to minimise the race. JRA.
646 DEBUG(3, (__location__ ": Found %zd entries in the keytab.\n", found));
651 oldEntries = talloc_zero_array(frame, char *, found + 1);
653 DEBUG(1, (__location__ ": Failed to allocate space to store "
654 "the old keytab entries (talloc failed?).\n"));
659 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
660 if (ret == KRB5_KT_END || ret == ENOENT) {
661 krb5_kt_end_seq_get(context, keytab, &cursor);
666 while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
667 if (kt_entry.vno != kvno) {
668 char *ktprinc = NULL;
671 /* This returns a malloc'ed string in ktprinc. */
672 ret = smb_krb5_unparse_name(oldEntries, context,
676 DEBUG(1, (__location__
677 ": smb_krb5_unparse_name failed "
678 "(%s)\n", error_message(ret)));
682 * From looking at the krb5 source they don't seem to
683 * take locale or mb strings into account.
684 * Maybe this is because they assume utf8 ?
685 * In this case we may need to convert from utf8 to
686 * mb charset here ? JRA.
688 p = strchr_m(ktprinc, '@');
693 p = strchr_m(ktprinc, '/');
697 for (i = 0; i < found; i++) {
698 if (!oldEntries[i]) {
699 oldEntries[i] = ktprinc;
702 if (!strcmp(oldEntries[i], ktprinc)) {
703 TALLOC_FREE(ktprinc);
708 TALLOC_FREE(ktprinc);
711 smb_krb5_kt_free_entry(context, &kt_entry);
712 ZERO_STRUCT(kt_entry);
714 krb5_kt_end_seq_get(context, keytab, &cursor);
718 for (i = 0; oldEntries[i]; i++) {
719 ret |= ads_keytab_add_entry(ads, oldEntries[i]);
720 TALLOC_FREE(oldEntries[i]);
724 TALLOC_FREE(oldEntries);
728 if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
729 smb_krb5_kt_free_entry(context, &kt_entry);
731 if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
732 krb5_kt_end_seq_get(context, keytab, &cursor);
735 krb5_kt_close(context, keytab);
737 krb5_free_context(context);
742 #endif /* HAVE_ADS */
744 /**********************************************************************
746 ***********************************************************************/
748 int ads_keytab_list(const char *keytab_name)
750 krb5_error_code ret = 0;
751 krb5_context context = NULL;
752 krb5_keytab keytab = NULL;
753 krb5_kt_cursor cursor;
754 krb5_keytab_entry kt_entry;
756 ZERO_STRUCT(kt_entry);
759 initialize_krb5_error_table();
760 ret = krb5_init_context(&context);
762 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
763 error_message(ret)));
767 if (keytab_name == NULL) {
769 ret = ads_keytab_open(context, &keytab);
774 ret = smb_krb5_kt_open(context, keytab_name, False, &keytab);
777 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
778 error_message(ret)));
782 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
788 printf("Vno Type Principal\n");
790 while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
792 char *princ_s = NULL;
793 char *etype_s = NULL;
794 krb5_enctype enctype = 0;
796 ret = smb_krb5_unparse_name(talloc_tos(), context,
797 kt_entry.principal, &princ_s);
802 enctype = smb_krb5_kt_get_enctype_from_entry(&kt_entry);
804 ret = smb_krb5_enctype_to_string(context, enctype, &etype_s);
806 (asprintf(&etype_s, "UNKNOWN: %d\n", enctype) == -1)) {
807 TALLOC_FREE(princ_s);
811 printf("%3d %-43s %s\n", kt_entry.vno, etype_s, princ_s);
813 TALLOC_FREE(princ_s);
816 ret = smb_krb5_kt_free_entry(context, &kt_entry);
822 ret = krb5_kt_end_seq_get(context, keytab, &cursor);
827 /* Ensure we don't double free. */
828 ZERO_STRUCT(kt_entry);
832 if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
833 smb_krb5_kt_free_entry(context, &kt_entry);
835 if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
836 krb5_kt_end_seq_get(context, keytab, &cursor);
840 krb5_kt_close(context, keytab);
843 krb5_free_context(context);
848 #endif /* HAVE_KRB5 */