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 /**********************************************************************
38 Adds a single service principal, i.e. 'host' to the system keytab
39 ***********************************************************************/
41 int ads_keytab_add_entry(ADS_STRUCT
*ads
, const char *srvPrinc
)
43 krb5_error_code ret
= 0;
44 krb5_context context
= NULL
;
45 krb5_keytab keytab
= NULL
;
48 krb5_enctype enctypes
[6] = {
51 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
52 ENCTYPE_AES128_CTS_HMAC_SHA1_96
,
54 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
55 ENCTYPE_AES256_CTS_HMAC_SHA1_96
,
61 char *short_princ_s
= NULL
;
62 char *salt_princ_s
= NULL
;
63 char *password_s
= NULL
;
65 TALLOC_CTX
*tmpctx
= NULL
;
70 initialize_krb5_error_table();
71 ret
= krb5_init_context(&context
);
73 DEBUG(1, (__location__
": could not krb5_init_context: %s\n",
78 ret
= smb_krb5_kt_open(context
, NULL
, True
, &keytab
);
80 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
85 /* retrieve the password */
86 if (!secrets_init()) {
87 DEBUG(1, (__location__
": secrets_init failed\n"));
91 password_s
= secrets_fetch_machine_password(lp_workgroup(), NULL
, NULL
);
93 DEBUG(1, (__location__
": failed to fetch machine password\n"));
97 ZERO_STRUCT(password
);
98 password
.data
= password_s
;
99 password
.length
= strlen(password_s
);
101 /* we need the dNSHostName value here */
102 tmpctx
= talloc_init(__location__
);
104 DEBUG(0, (__location__
": talloc_init() failed!\n"));
109 my_fqdn
= ads_get_dnshostname(ads
, tmpctx
, lp_netbios_name());
111 DEBUG(0, (__location__
": unable to determine machine "
112 "account's dns name in AD!\n"));
117 machine_name
= ads_get_samaccountname(ads
, tmpctx
, lp_netbios_name());
119 DEBUG(0, (__location__
": unable to determine machine "
120 "account's short name in AD!\n"));
124 /*strip the trailing '$' */
125 machine_name
[strlen(machine_name
)-1] = '\0';
127 /* Construct our principal */
128 if (strchr_m(srvPrinc
, '@')) {
129 /* It's a fully-named principal. */
130 princ_s
= talloc_asprintf(tmpctx
, "%s", srvPrinc
);
135 } else if (srvPrinc
[strlen(srvPrinc
)-1] == '$') {
136 /* It's the machine account, as used by smbclient clients. */
137 princ_s
= talloc_asprintf(tmpctx
, "%s@%s",
138 srvPrinc
, lp_realm());
144 /* It's a normal service principal. Add the SPN now so that we
145 * can obtain credentials for it and double-check the salt value
146 * used to generate the service's keys. */
148 princ_s
= talloc_asprintf(tmpctx
, "%s/%s@%s",
149 srvPrinc
, my_fqdn
, lp_realm());
154 short_princ_s
= talloc_asprintf(tmpctx
, "%s/%s@%s",
155 srvPrinc
, machine_name
,
157 if (short_princ_s
== NULL
) {
162 /* According to http://support.microsoft.com/kb/326985/en-us,
163 certain principal names are automatically mapped to the
164 host/... principal in the AD account.
165 So only create these in the keytab, not in AD. --jerry */
167 if (!strequal(srvPrinc
, "cifs") &&
168 !strequal(srvPrinc
, "host")) {
169 DEBUG(3, (__location__
": Attempting to add/update "
172 aderr
= ads_add_service_principal_name(ads
,
173 lp_netbios_name(), my_fqdn
, srvPrinc
);
174 if (!ADS_ERR_OK(aderr
)) {
175 DEBUG(1, (__location__
": failed to "
176 "ads_add_service_principal_name.\n"));
182 kvno
= (krb5_kvno
)ads_get_machine_kvno(ads
, lp_netbios_name());
184 /* -1 indicates failure, everything else is OK */
185 DEBUG(1, (__location__
": ads_get_machine_kvno failed to "
186 "determine the system's kvno.\n"));
191 for (i
= 0; enctypes
[i
]; i
++) {
192 salt_princ_s
= kerberos_fetch_salt_princ_for_host_princ(context
,
196 /* add the fqdn principal to the keytab */
197 ret
= smb_krb5_kt_add_entry(context
,
207 DEBUG(1, (__location__
": Failed to add entry to keytab\n"));
208 SAFE_FREE(salt_princ_s
);
212 /* add the short principal name if we have one */
214 ret
= smb_krb5_kt_add_entry(context
,
224 DEBUG(1, (__location__
225 ": Failed to add short entry to keytab\n"));
226 SAFE_FREE(salt_princ_s
);
230 SAFE_FREE(salt_princ_s
);
237 krb5_kt_close(context
, keytab
);
240 krb5_free_context(context
);
245 /**********************************************************************
246 Flushes all entries from the system keytab.
247 ***********************************************************************/
249 int ads_keytab_flush(ADS_STRUCT
*ads
)
251 krb5_error_code ret
= 0;
252 krb5_context context
= NULL
;
253 krb5_keytab keytab
= NULL
;
257 initialize_krb5_error_table();
258 ret
= krb5_init_context(&context
);
260 DEBUG(1, (__location__
": could not krb5_init_context: %s\n",
261 error_message(ret
)));
265 ret
= smb_krb5_kt_open(context
, NULL
, True
, &keytab
);
267 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
268 error_message(ret
)));
272 kvno
= (krb5_kvno
)ads_get_machine_kvno(ads
, lp_netbios_name());
274 /* -1 indicates a failure */
275 DEBUG(1, (__location__
": Error determining the kvno.\n"));
279 /* Seek and delete old keytab entries */
280 ret
= smb_krb5_kt_seek_and_delete_old_entries(context
,
292 aderr
= ads_clear_service_principal_names(ads
, lp_netbios_name());
293 if (!ADS_ERR_OK(aderr
)) {
294 DEBUG(1, (__location__
": Error while clearing service "
295 "principal listings in LDAP.\n"));
301 krb5_kt_close(context
, keytab
);
304 krb5_free_context(context
);
309 /**********************************************************************
310 Adds all the required service principals to the system keytab.
311 ***********************************************************************/
313 int ads_keytab_create_default(ADS_STRUCT
*ads
)
315 krb5_error_code ret
= 0;
316 krb5_context context
= NULL
;
317 krb5_keytab keytab
= NULL
;
318 krb5_kt_cursor cursor
= {0};
319 krb5_keytab_entry kt_entry
= {0};
322 char *sam_account_name
, *upn
;
323 char **oldEntries
= NULL
, *princ_s
[26];
331 ZERO_STRUCT(kt_entry
);
334 frame
= talloc_stackframe();
340 status
= ads_get_service_principal_names(frame
,
345 if (!ADS_ERR_OK(status
)) {
350 for (i
= 0; i
< num_spns
; i
++) {
354 srv_princ
= strlower_talloc(frame
, spn_array
[i
]);
355 if (srv_princ
== NULL
) {
360 p
= strchr_m(srv_princ
, '/');
366 /* Add the SPNs found on the DC */
367 ret
= ads_keytab_add_entry(ads
, srv_princ
);
369 DEBUG(1, ("ads_keytab_add_entry failed while "
370 "adding '%s' principal.\n",
376 #if 0 /* don't create the CIFS/... keytab entries since no one except smbd
377 really needs them and we will fall back to verifying against
380 ret
= ads_keytab_add_entry(ads
, "cifs"));
382 DEBUG(1, (__location__
": ads_keytab_add_entry failed while "
383 "adding 'cifs'.\n"));
388 memset(princ_s
, '\0', sizeof(princ_s
));
390 initialize_krb5_error_table();
391 ret
= krb5_init_context(&context
);
393 DEBUG(1, (__location__
": could not krb5_init_context: %s\n",
394 error_message(ret
)));
398 machine_name
= talloc_strdup(frame
, lp_netbios_name());
404 /* now add the userPrincipalName and sAMAccountName entries */
405 sam_account_name
= ads_get_samaccountname(ads
, frame
, machine_name
);
406 if (!sam_account_name
) {
407 DEBUG(0, (__location__
": unable to determine machine "
408 "account's name in AD!\n"));
413 /* upper case the sAMAccountName to make it easier for apps to
414 know what case to use in the keytab file */
415 if (!strupper_m(sam_account_name
)) {
420 ret
= ads_keytab_add_entry(ads
, sam_account_name
);
422 DEBUG(1, (__location__
": ads_keytab_add_entry() failed "
423 "while adding sAMAccountName (%s)\n",
428 /* remember that not every machine account will have a upn */
429 upn
= ads_get_upn(ads
, frame
, machine_name
);
431 ret
= ads_keytab_add_entry(ads
, upn
);
433 DEBUG(1, (__location__
": ads_keytab_add_entry() "
434 "failed while adding UPN (%s)\n", upn
));
439 /* Now loop through the keytab and update any other existing entries */
440 kvno
= (krb5_kvno
)ads_get_machine_kvno(ads
, machine_name
);
441 if (kvno
== (krb5_kvno
)-1) {
442 DEBUG(1, (__location__
": ads_get_machine_kvno() failed to "
443 "determine the system's kvno.\n"));
447 DEBUG(3, (__location__
": Searching for keytab entries to preserve "
450 ret
= smb_krb5_kt_open(context
, NULL
, True
, &keytab
);
452 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
453 error_message(ret
)));
457 ret
= krb5_kt_start_seq_get(context
, keytab
, &cursor
);
458 if (ret
!= KRB5_KT_END
&& ret
!= ENOENT
) {
459 while ((ret
= krb5_kt_next_entry(context
, keytab
,
460 &kt_entry
, &cursor
)) == 0) {
461 smb_krb5_kt_free_entry(context
, &kt_entry
);
462 ZERO_STRUCT(kt_entry
);
466 krb5_kt_end_seq_get(context
, keytab
, &cursor
);
470 * Hmmm. There is no "rewind" function for the keytab. This means we
471 * have a race condition where someone else could add entries after
472 * we've counted them. Re-open asap to minimise the race. JRA.
474 DEBUG(3, (__location__
": Found %zd entries in the keytab.\n", found
));
479 oldEntries
= talloc_zero_array(frame
, char *, found
+ 1);
481 DEBUG(1, (__location__
": Failed to allocate space to store "
482 "the old keytab entries (talloc failed?).\n"));
487 ret
= krb5_kt_start_seq_get(context
, keytab
, &cursor
);
488 if (ret
== KRB5_KT_END
|| ret
== ENOENT
) {
489 krb5_kt_end_seq_get(context
, keytab
, &cursor
);
494 while (krb5_kt_next_entry(context
, keytab
, &kt_entry
, &cursor
) == 0) {
495 if (kt_entry
.vno
!= kvno
) {
496 char *ktprinc
= NULL
;
499 /* This returns a malloc'ed string in ktprinc. */
500 ret
= smb_krb5_unparse_name(oldEntries
, context
,
504 DEBUG(1, (__location__
505 ": smb_krb5_unparse_name failed "
506 "(%s)\n", error_message(ret
)));
510 * From looking at the krb5 source they don't seem to
511 * take locale or mb strings into account.
512 * Maybe this is because they assume utf8 ?
513 * In this case we may need to convert from utf8 to
514 * mb charset here ? JRA.
516 p
= strchr_m(ktprinc
, '@');
521 p
= strchr_m(ktprinc
, '/');
525 for (i
= 0; i
< found
; i
++) {
526 if (!oldEntries
[i
]) {
527 oldEntries
[i
] = ktprinc
;
530 if (!strcmp(oldEntries
[i
], ktprinc
)) {
531 TALLOC_FREE(ktprinc
);
536 TALLOC_FREE(ktprinc
);
539 smb_krb5_kt_free_entry(context
, &kt_entry
);
540 ZERO_STRUCT(kt_entry
);
542 krb5_kt_end_seq_get(context
, keytab
, &cursor
);
546 for (i
= 0; oldEntries
[i
]; i
++) {
547 ret
|= ads_keytab_add_entry(ads
, oldEntries
[i
]);
548 TALLOC_FREE(oldEntries
[i
]);
552 TALLOC_FREE(oldEntries
);
556 krb5_keytab_entry zero_kt_entry
;
557 krb5_kt_cursor zero_csr
;
559 ZERO_STRUCT(zero_kt_entry
);
560 ZERO_STRUCT(zero_csr
);
562 if (memcmp(&zero_kt_entry
, &kt_entry
,
563 sizeof(krb5_keytab_entry
))) {
564 smb_krb5_kt_free_entry(context
, &kt_entry
);
566 if ((memcmp(&cursor
, &zero_csr
,
567 sizeof(krb5_kt_cursor
)) != 0) && keytab
) {
568 krb5_kt_end_seq_get(context
, keytab
, &cursor
);
571 krb5_kt_close(context
, keytab
);
573 krb5_free_context(context
);
578 #endif /* HAVE_ADS */
580 /**********************************************************************
582 ***********************************************************************/
584 int ads_keytab_list(const char *keytab_name
)
586 krb5_error_code ret
= 0;
587 krb5_context context
= NULL
;
588 krb5_keytab keytab
= NULL
;
589 krb5_kt_cursor cursor
;
590 krb5_keytab_entry kt_entry
;
592 ZERO_STRUCT(kt_entry
);
595 initialize_krb5_error_table();
596 ret
= krb5_init_context(&context
);
598 DEBUG(1, (__location__
": could not krb5_init_context: %s\n",
599 error_message(ret
)));
603 ret
= smb_krb5_kt_open(context
, keytab_name
, False
, &keytab
);
605 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
606 error_message(ret
)));
610 ret
= krb5_kt_start_seq_get(context
, keytab
, &cursor
);
616 printf("Vno Type Principal\n");
618 while (krb5_kt_next_entry(context
, keytab
, &kt_entry
, &cursor
) == 0) {
620 char *princ_s
= NULL
;
621 char *etype_s
= NULL
;
622 krb5_enctype enctype
= 0;
624 ret
= smb_krb5_unparse_name(talloc_tos(), context
,
625 kt_entry
.principal
, &princ_s
);
630 enctype
= smb_krb5_kt_get_enctype_from_entry(&kt_entry
);
632 ret
= smb_krb5_enctype_to_string(context
, enctype
, &etype_s
);
634 (asprintf(&etype_s
, "UNKNOWN: %d\n", enctype
) == -1)) {
635 TALLOC_FREE(princ_s
);
639 printf("%3d %-43s %s\n", kt_entry
.vno
, etype_s
, princ_s
);
641 TALLOC_FREE(princ_s
);
644 ret
= smb_krb5_kt_free_entry(context
, &kt_entry
);
650 ret
= krb5_kt_end_seq_get(context
, keytab
, &cursor
);
655 /* Ensure we don't double free. */
656 ZERO_STRUCT(kt_entry
);
661 krb5_keytab_entry zero_kt_entry
;
662 ZERO_STRUCT(zero_kt_entry
);
663 if (memcmp(&zero_kt_entry
, &kt_entry
,
664 sizeof(krb5_keytab_entry
))) {
665 smb_krb5_kt_free_entry(context
, &kt_entry
);
669 krb5_kt_cursor zero_csr
;
670 ZERO_STRUCT(zero_csr
);
671 if ((memcmp(&cursor
, &zero_csr
,
672 sizeof(krb5_kt_cursor
)) != 0) && keytab
) {
673 krb5_kt_end_seq_get(context
, keytab
, &cursor
);
678 krb5_kt_close(context
, keytab
);
681 krb5_free_context(context
);
686 #endif /* HAVE_KRB5 */