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
;
138 struct spn_struct
* spn_struct
= NULL
;
141 /* SPN should have '/' */
142 tmp
= strchr_m(service_or_spn
, '/');
144 spn_struct
= parse_spn(ctx
, service_or_spn
);
145 if (spn_struct
== NULL
) {
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
;
158 ok
= fill_default_spns(ctx
,
167 aderr
= ads_add_service_principal_names(ads
,
170 if (!ADS_ERR_OK(aderr
)) {
171 DBG_WARNING("Failed to add service principal name.\n");
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
,
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
;
195 /* SPN should have '/' */
196 tmp
= strchr_m(service_or_spn
, '/');
198 spn_struct
= parse_spn(ctx
, service_or_spn
);
199 if (spn_struct
== NULL
) {
204 if (spn_struct
!= NULL
) {
205 service
= spn_struct
->serviceclass
;
206 host
= spn_struct
->host
;
208 princ_s
= talloc_asprintf(ctx
, "%s/%s@%s",
211 if (princ_s
== NULL
) {
216 if (spn_struct
== NULL
) {
217 short_princ_s
= talloc_asprintf(ctx
, "%s/%s@%s",
218 service
, lp_netbios_name(),
220 if (short_princ_s
== NULL
) {
225 *p_princ_s
= princ_s
;
226 *p_short_princ_s
= short_princ_s
;
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
,
248 /* Construct our principal */
249 if (strchr_m(srvPrinc
, '@')) {
250 /* It's a fully-named principal. */
251 princ_s
= talloc_asprintf(tmpctx
, "%s", srvPrinc
);
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());
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
,
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
,
295 for (i
= 0; enctypes
[i
]; i
++) {
297 /* add the fqdn principal to the keytab */
298 ret
= smb_krb5_kt_add_entry(context
,
305 false); /* no_salt */
307 DBG_WARNING("Failed to add entry to keytab\n");
311 /* add the short principal name if we have one */
313 ret
= smb_krb5_kt_add_entry(context
,
320 false); /* no_salt */
322 DBG_WARNING("Failed to add short entry to keytab\n");
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
;
342 char *salt_princ_s
= NULL
;
343 char *password_s
= NULL
;
345 TALLOC_CTX
*tmpctx
= NULL
;
346 char **hostnames_array
= NULL
;
347 size_t num_hostnames
= 0;
349 ret
= smb_krb5_init_context_common(&context
);
351 DBG_ERR("kerberos init context failed (%s)\n",
356 ret
= ads_keytab_open(context
, &keytab
);
361 /* retrieve the password */
362 if (!secrets_init()) {
363 DBG_WARNING("secrets_init failed\n");
367 password_s
= secrets_fetch_machine_password(lp_workgroup(), NULL
, NULL
);
369 DBG_WARNING("failed to fetch machine password\n");
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__
);
380 DBG_ERR("talloc_init() failed!\n");
385 my_fqdn
= ads_get_dnshostname(ads
, tmpctx
, lp_netbios_name());
387 DBG_ERR("unable to determine machine account's dns name in "
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 "
401 kvno
= (krb5_kvno
)ads_get_machine_kvno(ads
, lp_netbios_name());
403 /* -1 indicates failure, everything else is OK */
404 DBG_WARNING("ads_get_machine_kvno failed to determine the "
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");
417 ret
= add_kt_entry_etypes(context
, tmpctx
, ads
, salt_princ_s
, keytab
,
418 kvno
, srvPrinc
, my_fqdn
, &password
,
424 if (ADS_ERR_OK(ads_get_additional_dns_hostnames(tmpctx
, ads
,
430 for (i
= 0; i
< num_hostnames
; i
++) {
432 ret
= add_kt_entry_etypes(context
, tmpctx
, ads
,
433 salt_princ_s
, keytab
,
436 &password
, update_ads
);
444 SAFE_FREE(salt_princ_s
);
448 krb5_kt_close(context
, keytab
);
451 krb5_free_context(context
);
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
;
472 ret
= smb_krb5_init_context_common(&context
);
474 DBG_ERR("kerberos init context failed (%s)\n",
479 ret
= ads_keytab_open(context
, &keytab
);
484 /* Construct our principal */
485 if (strchr_m(srvPrinc
, '@')) {
486 /* It's a fully-named principal. */
487 princ_s
= talloc_asprintf(frame
, "%s", srvPrinc
);
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());
502 * It's a normal service principal.
504 char *my_fqdn
= NULL
;
508 * SPN should have '/' otherwise we
509 * need to fallback and find our dnshostname
511 tmp
= strchr_m(srvPrinc
, '/');
513 my_fqdn
= ads_get_dnshostname(ads
, frame
, lp_netbios_name());
515 DBG_ERR("unable to determine machine account's dns name in "
522 ok
= service_or_spn_to_kerberos_princ(frame
,
533 ret
= smb_krb5_parse_name(context
, princ_s
, &princ
);
535 DEBUG(1, (__location__
": smb_krb5_parse_name(%s) "
536 "failed (%s)\n", princ_s
, error_message(ret
)));
540 if (short_princ_s
!= NULL
) {
541 ret
= smb_krb5_parse_name(context
, short_princ_s
, &short_princ
);
543 DEBUG(1, (__location__
": smb_krb5_parse_name(%s) "
544 "failed (%s)\n", short_princ_s
, error_message(ret
)));
549 /* Seek and delete old keytab entries */
550 ret
= smb_krb5_kt_seek_and_delete_old_entries(context
,
552 false, /* keep_old_kvno */
554 false, /* enctype_only */
563 if (short_princ_s
== NULL
) {
567 /* Seek and delete old keytab entries */
568 ret
= smb_krb5_kt_seek_and_delete_old_entries(context
,
570 false, /* keep_old_kvno */
572 false, /* enctype_only */
583 krb5_free_principal(context
, princ
);
586 krb5_free_principal(context
, short_princ
);
589 krb5_kt_close(context
, keytab
);
592 krb5_free_context(context
);
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
;
609 ret
= smb_krb5_init_context_common(&context
);
611 DBG_ERR("kerberos init context failed (%s)\n",
616 ret
= ads_keytab_open(context
, &keytab
);
621 /* Seek and delete all old keytab entries */
622 ret
= smb_krb5_kt_seek_and_delete_old_entries(context
,
624 false, /* keep_old_kvno */
626 false, /* enctype_only */
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"));
645 krb5_kt_close(context
, keytab
);
648 krb5_free_context(context
);
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};
666 char *sam_account_name
, *upn
;
667 char **oldEntries
= NULL
, *princ_s
[26];
676 ZERO_STRUCT(kt_entry
);
679 frame
= talloc_stackframe();
685 status
= ads_get_service_principal_names(frame
,
690 if (!ADS_ERR_OK(status
)) {
695 for (i
= 0; i
< num_spns
; i
++) {
699 srv_princ
= strlower_talloc(frame
, spn_array
[i
]);
700 if (srv_princ
== NULL
) {
705 p
= strchr_m(srv_princ
, '/');
711 /* Add the SPNs found on the DC */
712 ret
= ads_keytab_add_entry(ads
, srv_princ
, false);
714 DEBUG(1, ("ads_keytab_add_entry failed while "
715 "adding '%s' principal.\n",
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
725 ret
= ads_keytab_add_entry(ads
, "cifs", false));
727 DEBUG(1, (__location__
": ads_keytab_add_entry failed while "
728 "adding 'cifs'.\n"));
733 memset(princ_s
, '\0', sizeof(princ_s
));
735 ret
= smb_krb5_init_context_common(&context
);
737 DBG_ERR("kerberos init context failed (%s)\n",
742 machine_name
= talloc_strdup(frame
, lp_netbios_name());
748 /* now add the userPrincipalName and sAMAccountName entries */
749 ok
= ads_has_samaccountname(ads
, frame
, machine_name
);
751 DEBUG(0, (__location__
": unable to determine machine "
752 "account's name in AD!\n"));
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
) {
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
)) {
773 ret
= ads_keytab_add_entry(ads
, sam_account_name
, false);
775 DEBUG(1, (__location__
": ads_keytab_add_entry() failed "
776 "while adding sAMAccountName (%s)\n",
781 /* remember that not every machine account will have a upn */
782 upn
= ads_get_upn(ads
, frame
, machine_name
);
784 ret
= ads_keytab_add_entry(ads
, upn
, false);
786 DEBUG(1, (__location__
": ads_keytab_add_entry() "
787 "failed while adding UPN (%s)\n", upn
));
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"));
800 DEBUG(3, (__location__
": Searching for keytab entries to preserve "
803 ret
= ads_keytab_open(context
, &keytab
);
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
);
817 krb5_kt_end_seq_get(context
, keytab
, &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
));
830 oldEntries
= talloc_zero_array(frame
, char *, found
+ 1);
832 DEBUG(1, (__location__
": Failed to allocate space to store "
833 "the old keytab entries (talloc failed?).\n"));
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
);
845 while (krb5_kt_next_entry(context
, keytab
, &kt_entry
, &cursor
) == 0) {
846 if (kt_entry
.vno
!= kvno
) {
847 char *ktprinc
= NULL
;
850 /* This returns a malloc'ed string in ktprinc. */
851 ret
= smb_krb5_unparse_name(oldEntries
, context
,
855 DEBUG(1, (__location__
856 ": smb_krb5_unparse_name failed "
857 "(%s)\n", error_message(ret
)));
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
, '@');
872 p
= strchr_m(ktprinc
, '/');
876 for (i
= 0; i
< found
; i
++) {
877 if (!oldEntries
[i
]) {
878 oldEntries
[i
] = ktprinc
;
881 if (!strcmp(oldEntries
[i
], ktprinc
)) {
882 TALLOC_FREE(ktprinc
);
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
);
897 for (i
= 0; oldEntries
[i
]; i
++) {
898 ret
|= ads_keytab_add_entry(ads
, oldEntries
[i
], false);
899 TALLOC_FREE(oldEntries
[i
]);
903 TALLOC_FREE(oldEntries
);
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
);
914 krb5_kt_close(context
, keytab
);
916 krb5_free_context(context
);
921 #endif /* HAVE_ADS */
923 /**********************************************************************
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
);
938 ret
= smb_krb5_init_context_common(&context
);
940 DBG_ERR("kerberos init context failed (%s)\n",
945 if (keytab_name
== NULL
) {
947 ret
= ads_keytab_open(context
, &keytab
);
952 ret
= smb_krb5_kt_open(context
, keytab_name
, False
, &keytab
);
955 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
956 error_message(ret
)));
960 ret
= krb5_kt_start_seq_get(context
, keytab
, &cursor
);
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
);
980 enctype
= smb_krb5_kt_get_enctype_from_entry(&kt_entry
);
982 ret
= smb_krb5_enctype_to_string(context
, enctype
, &etype_s
);
984 (asprintf(&etype_s
, "UNKNOWN: %d", enctype
) == -1)) {
985 TALLOC_FREE(princ_s
);
989 printf("%3d %-43s %s\n", kt_entry
.vno
, etype_s
, princ_s
);
991 TALLOC_FREE(princ_s
);
994 ret
= smb_krb5_kt_free_entry(context
, &kt_entry
);
1000 ret
= krb5_kt_end_seq_get(context
, keytab
, &cursor
);
1005 /* Ensure we don't double free. */
1006 ZERO_STRUCT(kt_entry
);
1007 ZERO_STRUCT(cursor
);
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
);
1018 krb5_kt_close(context
, keytab
);
1021 krb5_free_context(context
);
1026 #endif /* HAVE_KRB5 */