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 /**********************************************************************
89 Adds a single service principal, i.e. 'host' to the system keytab
90 ***********************************************************************/
92 int ads_keytab_add_entry(ADS_STRUCT
*ads
, const char *srvPrinc
)
94 krb5_error_code ret
= 0;
95 krb5_context context
= NULL
;
96 krb5_keytab keytab
= NULL
;
99 krb5_enctype enctypes
[6] = {
102 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
103 ENCTYPE_AES128_CTS_HMAC_SHA1_96
,
105 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
106 ENCTYPE_AES256_CTS_HMAC_SHA1_96
,
108 ENCTYPE_ARCFOUR_HMAC
,
111 char *princ_s
= NULL
;
112 char *short_princ_s
= NULL
;
113 char *salt_princ_s
= NULL
;
114 char *password_s
= NULL
;
116 TALLOC_CTX
*tmpctx
= NULL
;
121 initialize_krb5_error_table();
122 ret
= krb5_init_context(&context
);
124 DEBUG(1, (__location__
": could not krb5_init_context: %s\n",
125 error_message(ret
)));
129 ret
= ads_keytab_open(context
, &keytab
);
134 /* retrieve the password */
135 if (!secrets_init()) {
136 DEBUG(1, (__location__
": secrets_init failed\n"));
140 password_s
= secrets_fetch_machine_password(lp_workgroup(), NULL
, NULL
);
142 DEBUG(1, (__location__
": failed to fetch machine password\n"));
146 ZERO_STRUCT(password
);
147 password
.data
= password_s
;
148 password
.length
= strlen(password_s
);
150 /* we need the dNSHostName value here */
151 tmpctx
= talloc_init(__location__
);
153 DEBUG(0, (__location__
": talloc_init() failed!\n"));
158 my_fqdn
= ads_get_dnshostname(ads
, tmpctx
, lp_netbios_name());
160 DEBUG(0, (__location__
": unable to determine machine "
161 "account's dns name in AD!\n"));
166 machine_name
= ads_get_samaccountname(ads
, tmpctx
, lp_netbios_name());
168 DEBUG(0, (__location__
": unable to determine machine "
169 "account's short name in AD!\n"));
173 /*strip the trailing '$' */
174 machine_name
[strlen(machine_name
)-1] = '\0';
176 /* Construct our principal */
177 if (strchr_m(srvPrinc
, '@')) {
178 /* It's a fully-named principal. */
179 princ_s
= talloc_asprintf(tmpctx
, "%s", srvPrinc
);
184 } else if (srvPrinc
[strlen(srvPrinc
)-1] == '$') {
185 /* It's the machine account, as used by smbclient clients. */
186 princ_s
= talloc_asprintf(tmpctx
, "%s@%s",
187 srvPrinc
, lp_realm());
193 /* It's a normal service principal. Add the SPN now so that we
194 * can obtain credentials for it and double-check the salt value
195 * used to generate the service's keys. */
197 princ_s
= talloc_asprintf(tmpctx
, "%s/%s@%s",
198 srvPrinc
, my_fqdn
, lp_realm());
203 short_princ_s
= talloc_asprintf(tmpctx
, "%s/%s@%s",
204 srvPrinc
, machine_name
,
206 if (short_princ_s
== NULL
) {
211 /* According to http://support.microsoft.com/kb/326985/en-us,
212 certain principal names are automatically mapped to the
213 host/... principal in the AD account.
214 So only create these in the keytab, not in AD. --jerry */
216 if (!strequal(srvPrinc
, "cifs") &&
217 !strequal(srvPrinc
, "host")) {
218 DEBUG(3, (__location__
": Attempting to add/update "
221 aderr
= ads_add_service_principal_name(ads
,
222 lp_netbios_name(), my_fqdn
, srvPrinc
);
223 if (!ADS_ERR_OK(aderr
)) {
224 DEBUG(1, (__location__
": failed to "
225 "ads_add_service_principal_name.\n"));
231 kvno
= (krb5_kvno
)ads_get_machine_kvno(ads
, lp_netbios_name());
233 /* -1 indicates failure, everything else is OK */
234 DEBUG(1, (__location__
": ads_get_machine_kvno failed to "
235 "determine the system's kvno.\n"));
240 for (i
= 0; enctypes
[i
]; i
++) {
241 salt_princ_s
= kerberos_fetch_salt_princ_for_host_princ(context
,
245 /* add the fqdn principal to the keytab */
246 ret
= smb_krb5_kt_add_entry(context
,
256 DEBUG(1, (__location__
": Failed to add entry to keytab\n"));
257 SAFE_FREE(salt_princ_s
);
261 /* add the short principal name if we have one */
263 ret
= smb_krb5_kt_add_entry(context
,
273 DEBUG(1, (__location__
274 ": Failed to add short entry to keytab\n"));
275 SAFE_FREE(salt_princ_s
);
279 SAFE_FREE(salt_princ_s
);
286 krb5_kt_close(context
, keytab
);
289 krb5_free_context(context
);
294 /**********************************************************************
295 Flushes all entries from the system keytab.
296 ***********************************************************************/
298 int ads_keytab_flush(ADS_STRUCT
*ads
)
300 krb5_error_code ret
= 0;
301 krb5_context context
= NULL
;
302 krb5_keytab keytab
= NULL
;
306 initialize_krb5_error_table();
307 ret
= krb5_init_context(&context
);
309 DEBUG(1, (__location__
": could not krb5_init_context: %s\n",
310 error_message(ret
)));
314 ret
= ads_keytab_open(context
, &keytab
);
319 kvno
= (krb5_kvno
)ads_get_machine_kvno(ads
, lp_netbios_name());
321 /* -1 indicates a failure */
322 DEBUG(1, (__location__
": Error determining the kvno.\n"));
326 /* Seek and delete old keytab entries */
327 ret
= smb_krb5_kt_seek_and_delete_old_entries(context
,
339 aderr
= ads_clear_service_principal_names(ads
, lp_netbios_name());
340 if (!ADS_ERR_OK(aderr
)) {
341 DEBUG(1, (__location__
": Error while clearing service "
342 "principal listings in LDAP.\n"));
348 krb5_kt_close(context
, keytab
);
351 krb5_free_context(context
);
356 /**********************************************************************
357 Adds all the required service principals to the system keytab.
358 ***********************************************************************/
360 int ads_keytab_create_default(ADS_STRUCT
*ads
)
362 krb5_error_code ret
= 0;
363 krb5_context context
= NULL
;
364 krb5_keytab keytab
= NULL
;
365 krb5_kt_cursor cursor
= {0};
366 krb5_keytab_entry kt_entry
= {0};
369 char *sam_account_name
, *upn
;
370 char **oldEntries
= NULL
, *princ_s
[26];
378 ZERO_STRUCT(kt_entry
);
381 frame
= talloc_stackframe();
387 status
= ads_get_service_principal_names(frame
,
392 if (!ADS_ERR_OK(status
)) {
397 for (i
= 0; i
< num_spns
; i
++) {
401 srv_princ
= strlower_talloc(frame
, spn_array
[i
]);
402 if (srv_princ
== NULL
) {
407 p
= strchr_m(srv_princ
, '/');
413 /* Add the SPNs found on the DC */
414 ret
= ads_keytab_add_entry(ads
, srv_princ
);
416 DEBUG(1, ("ads_keytab_add_entry failed while "
417 "adding '%s' principal.\n",
423 #if 0 /* don't create the CIFS/... keytab entries since no one except smbd
424 really needs them and we will fall back to verifying against
427 ret
= ads_keytab_add_entry(ads
, "cifs"));
429 DEBUG(1, (__location__
": ads_keytab_add_entry failed while "
430 "adding 'cifs'.\n"));
435 memset(princ_s
, '\0', sizeof(princ_s
));
437 initialize_krb5_error_table();
438 ret
= krb5_init_context(&context
);
440 DEBUG(1, (__location__
": could not krb5_init_context: %s\n",
441 error_message(ret
)));
445 machine_name
= talloc_strdup(frame
, lp_netbios_name());
451 /* now add the userPrincipalName and sAMAccountName entries */
452 sam_account_name
= ads_get_samaccountname(ads
, frame
, machine_name
);
453 if (!sam_account_name
) {
454 DEBUG(0, (__location__
": unable to determine machine "
455 "account's name in AD!\n"));
460 /* upper case the sAMAccountName to make it easier for apps to
461 know what case to use in the keytab file */
462 if (!strupper_m(sam_account_name
)) {
467 ret
= ads_keytab_add_entry(ads
, sam_account_name
);
469 DEBUG(1, (__location__
": ads_keytab_add_entry() failed "
470 "while adding sAMAccountName (%s)\n",
475 /* remember that not every machine account will have a upn */
476 upn
= ads_get_upn(ads
, frame
, machine_name
);
478 ret
= ads_keytab_add_entry(ads
, upn
);
480 DEBUG(1, (__location__
": ads_keytab_add_entry() "
481 "failed while adding UPN (%s)\n", upn
));
486 /* Now loop through the keytab and update any other existing entries */
487 kvno
= (krb5_kvno
)ads_get_machine_kvno(ads
, machine_name
);
488 if (kvno
== (krb5_kvno
)-1) {
489 DEBUG(1, (__location__
": ads_get_machine_kvno() failed to "
490 "determine the system's kvno.\n"));
494 DEBUG(3, (__location__
": Searching for keytab entries to preserve "
497 ret
= ads_keytab_open(context
, &keytab
);
502 ret
= krb5_kt_start_seq_get(context
, keytab
, &cursor
);
503 if (ret
!= KRB5_KT_END
&& ret
!= ENOENT
) {
504 while ((ret
= krb5_kt_next_entry(context
, keytab
,
505 &kt_entry
, &cursor
)) == 0) {
506 smb_krb5_kt_free_entry(context
, &kt_entry
);
507 ZERO_STRUCT(kt_entry
);
511 krb5_kt_end_seq_get(context
, keytab
, &cursor
);
515 * Hmmm. There is no "rewind" function for the keytab. This means we
516 * have a race condition where someone else could add entries after
517 * we've counted them. Re-open asap to minimise the race. JRA.
519 DEBUG(3, (__location__
": Found %zd entries in the keytab.\n", found
));
524 oldEntries
= talloc_zero_array(frame
, char *, found
+ 1);
526 DEBUG(1, (__location__
": Failed to allocate space to store "
527 "the old keytab entries (talloc failed?).\n"));
532 ret
= krb5_kt_start_seq_get(context
, keytab
, &cursor
);
533 if (ret
== KRB5_KT_END
|| ret
== ENOENT
) {
534 krb5_kt_end_seq_get(context
, keytab
, &cursor
);
539 while (krb5_kt_next_entry(context
, keytab
, &kt_entry
, &cursor
) == 0) {
540 if (kt_entry
.vno
!= kvno
) {
541 char *ktprinc
= NULL
;
544 /* This returns a malloc'ed string in ktprinc. */
545 ret
= smb_krb5_unparse_name(oldEntries
, context
,
549 DEBUG(1, (__location__
550 ": smb_krb5_unparse_name failed "
551 "(%s)\n", error_message(ret
)));
555 * From looking at the krb5 source they don't seem to
556 * take locale or mb strings into account.
557 * Maybe this is because they assume utf8 ?
558 * In this case we may need to convert from utf8 to
559 * mb charset here ? JRA.
561 p
= strchr_m(ktprinc
, '@');
566 p
= strchr_m(ktprinc
, '/');
570 for (i
= 0; i
< found
; i
++) {
571 if (!oldEntries
[i
]) {
572 oldEntries
[i
] = ktprinc
;
575 if (!strcmp(oldEntries
[i
], ktprinc
)) {
576 TALLOC_FREE(ktprinc
);
581 TALLOC_FREE(ktprinc
);
584 smb_krb5_kt_free_entry(context
, &kt_entry
);
585 ZERO_STRUCT(kt_entry
);
587 krb5_kt_end_seq_get(context
, keytab
, &cursor
);
591 for (i
= 0; oldEntries
[i
]; i
++) {
592 ret
|= ads_keytab_add_entry(ads
, oldEntries
[i
]);
593 TALLOC_FREE(oldEntries
[i
]);
597 TALLOC_FREE(oldEntries
);
601 if (!all_zero((uint8_t *)&kt_entry
, sizeof(kt_entry
))) {
602 smb_krb5_kt_free_entry(context
, &kt_entry
);
604 if (!all_zero((uint8_t *)&cursor
, sizeof(cursor
)) && keytab
) {
605 krb5_kt_end_seq_get(context
, keytab
, &cursor
);
608 krb5_kt_close(context
, keytab
);
610 krb5_free_context(context
);
615 #endif /* HAVE_ADS */
617 /**********************************************************************
619 ***********************************************************************/
621 int ads_keytab_list(const char *keytab_name
)
623 krb5_error_code ret
= 0;
624 krb5_context context
= NULL
;
625 krb5_keytab keytab
= NULL
;
626 krb5_kt_cursor cursor
;
627 krb5_keytab_entry kt_entry
;
629 ZERO_STRUCT(kt_entry
);
632 initialize_krb5_error_table();
633 ret
= krb5_init_context(&context
);
635 DEBUG(1, (__location__
": could not krb5_init_context: %s\n",
636 error_message(ret
)));
640 ret
= smb_krb5_kt_open(context
, keytab_name
, False
, &keytab
);
642 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
643 error_message(ret
)));
647 ret
= krb5_kt_start_seq_get(context
, keytab
, &cursor
);
653 printf("Vno Type Principal\n");
655 while (krb5_kt_next_entry(context
, keytab
, &kt_entry
, &cursor
) == 0) {
657 char *princ_s
= NULL
;
658 char *etype_s
= NULL
;
659 krb5_enctype enctype
= 0;
661 ret
= smb_krb5_unparse_name(talloc_tos(), context
,
662 kt_entry
.principal
, &princ_s
);
667 enctype
= smb_krb5_kt_get_enctype_from_entry(&kt_entry
);
669 ret
= smb_krb5_enctype_to_string(context
, enctype
, &etype_s
);
671 (asprintf(&etype_s
, "UNKNOWN: %d\n", enctype
) == -1)) {
672 TALLOC_FREE(princ_s
);
676 printf("%3d %-43s %s\n", kt_entry
.vno
, etype_s
, princ_s
);
678 TALLOC_FREE(princ_s
);
681 ret
= smb_krb5_kt_free_entry(context
, &kt_entry
);
687 ret
= krb5_kt_end_seq_get(context
, keytab
, &cursor
);
692 /* Ensure we don't double free. */
693 ZERO_STRUCT(kt_entry
);
697 if (!all_zero((uint8_t *)&kt_entry
, sizeof(kt_entry
))) {
698 smb_krb5_kt_free_entry(context
, &kt_entry
);
700 if (!all_zero((uint8_t *)&cursor
, sizeof(cursor
)) && keytab
) {
701 krb5_kt_end_seq_get(context
, keytab
, &cursor
);
705 krb5_kt_close(context
, keytab
);
708 krb5_free_context(context
);
713 #endif /* HAVE_KRB5 */