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/>.
34 /**********************************************************************
35 **********************************************************************/
37 int smb_krb5_kt_add_entry_ext(krb5_context context,
41 krb5_enctype *enctypes,
44 bool keep_old_entries)
46 krb5_error_code ret = 0;
47 krb5_kt_cursor cursor;
48 krb5_keytab_entry kt_entry;
49 krb5_principal princ = NULL;
53 ZERO_STRUCT(kt_entry);
56 ret = smb_krb5_parse_name(context, princ_s, &princ);
58 DEBUG(1,("smb_krb5_kt_add_entry_ext: smb_krb5_parse_name(%s) failed (%s)\n", princ_s, error_message(ret)));
62 /* Seek and delete old keytab entries */
63 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
64 if (ret != KRB5_KT_END && ret != ENOENT ) {
65 DEBUG(3,("smb_krb5_kt_add_entry_ext: Will try to delete old keytab entries\n"));
66 while(!krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) {
67 bool compare_name_ok = False;
69 ret = smb_krb5_unparse_name(talloc_tos(), context, kt_entry.principal, &ktprinc);
71 DEBUG(1,("smb_krb5_kt_add_entry_ext: smb_krb5_unparse_name failed (%s)\n",
76 /*---------------------------------------------------------------------------
77 * Save the entries with kvno - 1. This is what microsoft does
78 * to allow people with existing sessions that have kvno - 1 to still
79 * work. Otherwise, when the password for the machine changes, all
80 * kerberizied sessions will 'break' until either the client reboots or
81 * the client's session key expires and they get a new session ticket
85 #ifdef HAVE_KRB5_KT_COMPARE
86 compare_name_ok = (krb5_kt_compare(context, &kt_entry, princ, 0, 0) == True);
88 compare_name_ok = (strcmp(ktprinc, princ_s) == 0);
91 if (!compare_name_ok) {
92 DEBUG(10,("smb_krb5_kt_add_entry_ext: ignoring keytab entry principal %s, kvno = %d\n",
93 ktprinc, kt_entry.vno));
98 if (compare_name_ok) {
99 if (kt_entry.vno == kvno - 1) {
100 DEBUG(5,("smb_krb5_kt_add_entry_ext: Saving previous (kvno %d) entry for principal: %s.\n",
102 } else if (!keep_old_entries) {
103 DEBUG(5,("smb_krb5_kt_add_entry_ext: Found old entry for principal: %s (kvno %d) - trying to remove it.\n",
104 princ_s, kt_entry.vno));
105 ret = krb5_kt_end_seq_get(context, keytab, &cursor);
108 DEBUG(1,("smb_krb5_kt_add_entry_ext: krb5_kt_end_seq_get() failed (%s)\n",
109 error_message(ret)));
112 ret = krb5_kt_remove_entry(context, keytab, &kt_entry);
114 DEBUG(1,("smb_krb5_kt_add_entry_ext: krb5_kt_remove_entry failed (%s)\n",
115 error_message(ret)));
119 DEBUG(5,("smb_krb5_kt_add_entry_ext: removed old entry for principal: %s (kvno %d).\n",
120 princ_s, kt_entry.vno));
122 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
124 DEBUG(1,("smb_krb5_kt_add_entry_ext: krb5_kt_start_seq failed (%s)\n",
125 error_message(ret)));
128 ret = smb_krb5_kt_free_entry(context, &kt_entry);
129 ZERO_STRUCT(kt_entry);
131 DEBUG(1,("smb_krb5_kt_add_entry_ext: krb5_kt_remove_entry failed (%s)\n",
132 error_message(ret)));
139 /* Not a match, just free this entry and continue. */
140 ret = smb_krb5_kt_free_entry(context, &kt_entry);
141 ZERO_STRUCT(kt_entry);
143 DEBUG(1,("smb_krb5_kt_add_entry_ext: smb_krb5_kt_free_entry failed (%s)\n", error_message(ret)));
148 ret = krb5_kt_end_seq_get(context, keytab, &cursor);
151 DEBUG(1,("smb_krb5_kt_add_entry_ext: krb5_kt_end_seq_get failed (%s)\n",error_message(ret)));
156 /* Ensure we don't double free. */
157 ZERO_STRUCT(kt_entry);
160 /* If we get here, we have deleted all the old entries with kvno's not equal to the current kvno-1. */
162 /* Now add keytab entries for all encryption types */
163 for (i = 0; enctypes[i]; i++) {
166 keyp = KRB5_KT_KEY(&kt_entry);
168 if (create_kerberos_key_from_string(context, princ, &password, keyp, enctypes[i], no_salt)) {
172 kt_entry.principal = princ;
175 DEBUG(3,("smb_krb5_kt_add_entry_ext: adding keytab entry for (%s) with encryption type (%d) and version (%d)\n",
176 princ_s, enctypes[i], kt_entry.vno));
177 ret = krb5_kt_add_entry(context, keytab, &kt_entry);
178 krb5_free_keyblock_contents(context, keyp);
179 ZERO_STRUCT(kt_entry);
181 DEBUG(1,("smb_krb5_kt_add_entry_ext: adding entry to keytab failed (%s)\n", error_message(ret)));
189 krb5_keytab_entry zero_kt_entry;
190 ZERO_STRUCT(zero_kt_entry);
191 if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) {
192 smb_krb5_kt_free_entry(context, &kt_entry);
196 krb5_free_principal(context, princ);
200 krb5_kt_cursor zero_csr;
201 ZERO_STRUCT(zero_csr);
202 if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) {
203 krb5_kt_end_seq_get(context, keytab, &cursor);
210 static int smb_krb5_kt_add_entry(krb5_context context,
214 krb5_enctype *enctypes,
217 return smb_krb5_kt_add_entry_ext(context,
227 /**********************************************************************
228 Adds a single service principal, i.e. 'host' to the system keytab
229 ***********************************************************************/
231 int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc)
233 krb5_error_code ret = 0;
234 krb5_context context = NULL;
235 krb5_keytab keytab = NULL;
238 krb5_enctype enctypes[4] = { ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, 0, 0 };
239 char *princ_s = NULL, *short_princ_s = NULL;
240 char *password_s = NULL;
242 TALLOC_CTX *ctx = NULL;
245 #if defined(ENCTYPE_ARCFOUR_HMAC)
246 enctypes[2] = ENCTYPE_ARCFOUR_HMAC;
249 initialize_krb5_error_table();
250 ret = krb5_init_context(&context);
252 DEBUG(1,("ads_keytab_add_entry: could not krb5_init_context: %s\n",error_message(ret)));
256 ret = smb_krb5_open_keytab(context, NULL, True, &keytab);
258 DEBUG(1,("ads_keytab_add_entry: smb_krb5_open_keytab failed (%s)\n", error_message(ret)));
262 /* retrieve the password */
263 if (!secrets_init()) {
264 DEBUG(1,("ads_keytab_add_entry: secrets_init failed\n"));
268 password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
270 DEBUG(1,("ads_keytab_add_entry: failed to fetch machine password\n"));
274 ZERO_STRUCT(password);
275 password.data = password_s;
276 password.length = strlen(password_s);
278 /* we need the dNSHostName value here */
280 if ( (ctx = talloc_init("ads_keytab_add_entry")) == NULL ) {
281 DEBUG(0,("ads_keytab_add_entry: talloc() failed!\n"));
286 if ( (my_fqdn = ads_get_dnshostname( ads, ctx, global_myname())) == NULL ) {
287 DEBUG(0,("ads_keytab_add_entry: unable to determine machine account's dns name in AD!\n"));
292 if ( (machine_name = ads_get_samaccountname( ads, ctx, global_myname())) == NULL ) {
293 DEBUG(0,("ads_keytab_add_entry: unable to determine machine account's short name in AD!\n"));
297 /*strip the trailing '$' */
298 machine_name[strlen(machine_name)-1] = '\0';
300 /* Construct our principal */
302 if (strchr_m(srvPrinc, '@')) {
303 /* It's a fully-named principal. */
304 if (asprintf(&princ_s, "%s", srvPrinc) == -1) {
308 } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
309 /* It's the machine account, as used by smbclient clients. */
310 if (asprintf(&princ_s, "%s@%s", srvPrinc, lp_realm()) == -1) {
315 /* It's a normal service principal. Add the SPN now so that we
316 * can obtain credentials for it and double-check the salt value
317 * used to generate the service's keys. */
319 if (asprintf(&princ_s, "%s/%s@%s", srvPrinc, my_fqdn, lp_realm()) == -1) {
323 if (asprintf(&short_princ_s, "%s/%s@%s", srvPrinc, machine_name, lp_realm()) == -1) {
328 /* According to http://support.microsoft.com/kb/326985/en-us,
329 certain principal names are automatically mapped to the host/...
330 principal in the AD account. So only create these in the
331 keytab, not in AD. --jerry */
333 if ( !strequal( srvPrinc, "cifs" ) && !strequal(srvPrinc, "host" ) ) {
334 DEBUG(3,("ads_keytab_add_entry: Attempting to add/update '%s'\n", princ_s));
336 if (!ADS_ERR_OK(ads_add_service_principal_name(ads, global_myname(), my_fqdn, srvPrinc))) {
337 DEBUG(1,("ads_keytab_add_entry: ads_add_service_principal_name failed.\n"));
343 kvno = (krb5_kvno) ads_get_machine_kvno(ads, global_myname());
344 if (kvno == -1) { /* -1 indicates failure, everything else is OK */
345 DEBUG(1,("ads_keytab_add_entry: ads_get_machine_kvno failed to determine the system's kvno.\n"));
350 /* add the fqdn principal to the keytab */
352 ret = smb_krb5_kt_add_entry( context, keytab, kvno, princ_s, enctypes, password );
354 DEBUG(1,("ads_keytab_add_entry: Failed to add entry to keytab file\n"));
358 /* add the short principal name if we have one */
360 if ( short_princ_s ) {
361 ret = smb_krb5_kt_add_entry( context, keytab, kvno, short_princ_s, enctypes, password );
363 DEBUG(1,("ads_keytab_add_entry: Failed to add short entry to keytab file\n"));
369 SAFE_FREE( princ_s );
370 SAFE_FREE( short_princ_s );
374 krb5_kt_close(context, keytab);
377 krb5_free_context(context);
382 /**********************************************************************
383 Flushes all entries from the system keytab.
384 ***********************************************************************/
386 int ads_keytab_flush(ADS_STRUCT *ads)
388 krb5_error_code ret = 0;
389 krb5_context context = NULL;
390 krb5_keytab keytab = NULL;
391 krb5_kt_cursor cursor;
392 krb5_keytab_entry kt_entry;
395 ZERO_STRUCT(kt_entry);
398 initialize_krb5_error_table();
399 ret = krb5_init_context(&context);
401 DEBUG(1,("ads_keytab_flush: could not krb5_init_context: %s\n",error_message(ret)));
405 ret = smb_krb5_open_keytab(context, NULL, True, &keytab);
407 DEBUG(1,("ads_keytab_flush: smb_krb5_open_keytab failed (%s)\n", error_message(ret)));
411 kvno = (krb5_kvno) ads_get_machine_kvno(ads, global_myname());
412 if (kvno == -1) { /* -1 indicates a failure */
413 DEBUG(1,("ads_keytab_flush: Error determining the system's kvno.\n"));
417 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
418 if (ret != KRB5_KT_END && ret != ENOENT) {
419 while (!krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) {
420 ret = krb5_kt_end_seq_get(context, keytab, &cursor);
423 DEBUG(1,("ads_keytab_flush: krb5_kt_end_seq_get() failed (%s)\n",error_message(ret)));
426 ret = krb5_kt_remove_entry(context, keytab, &kt_entry);
428 DEBUG(1,("ads_keytab_flush: krb5_kt_remove_entry failed (%s)\n",error_message(ret)));
431 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
433 DEBUG(1,("ads_keytab_flush: krb5_kt_start_seq failed (%s)\n",error_message(ret)));
436 ret = smb_krb5_kt_free_entry(context, &kt_entry);
437 ZERO_STRUCT(kt_entry);
439 DEBUG(1,("ads_keytab_flush: krb5_kt_remove_entry failed (%s)\n",error_message(ret)));
445 /* Ensure we don't double free. */
446 ZERO_STRUCT(kt_entry);
449 if (!ADS_ERR_OK(ads_clear_service_principal_names(ads, global_myname()))) {
450 DEBUG(1,("ads_keytab_flush: Error while clearing service principal listings in LDAP.\n"));
457 krb5_keytab_entry zero_kt_entry;
458 ZERO_STRUCT(zero_kt_entry);
459 if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) {
460 smb_krb5_kt_free_entry(context, &kt_entry);
464 krb5_kt_cursor zero_csr;
465 ZERO_STRUCT(zero_csr);
466 if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) {
467 krb5_kt_end_seq_get(context, keytab, &cursor);
471 krb5_kt_close(context, keytab);
474 krb5_free_context(context);
479 /**********************************************************************
480 Adds all the required service principals to the system keytab.
481 ***********************************************************************/
483 int ads_keytab_create_default(ADS_STRUCT *ads)
485 krb5_error_code ret = 0;
486 krb5_context context = NULL;
487 krb5_keytab keytab = NULL;
488 krb5_kt_cursor cursor;
489 krb5_keytab_entry kt_entry;
492 char *sam_account_name, *upn;
493 char **oldEntries = NULL, *princ_s[26];
494 TALLOC_CTX *ctx = NULL;
495 fstring machine_name;
497 memset(princ_s, '\0', sizeof(princ_s));
499 fstrcpy( machine_name, global_myname() );
501 /* these are the main ones we need */
503 if ( (ret = ads_keytab_add_entry(ads, "host") ) != 0 ) {
504 DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding 'host'.\n"));
509 #if 0 /* don't create the CIFS/... keytab entries since no one except smbd
510 really needs them and we will fall back to verifying against secrets.tdb */
512 if ( (ret = ads_keytab_add_entry(ads, "cifs")) != 0 ) {
513 DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding 'cifs'.\n"));
518 if ( (ctx = talloc_init("ads_keytab_create_default")) == NULL ) {
519 DEBUG(0,("ads_keytab_create_default: talloc() failed!\n"));
523 /* now add the userPrincipalName and sAMAccountName entries */
525 if ( (sam_account_name = ads_get_samaccountname( ads, ctx, machine_name)) == NULL ) {
526 DEBUG(0,("ads_keytab_add_entry: unable to determine machine account's name in AD!\n"));
531 /* upper case the sAMAccountName to make it easier for apps to
532 know what case to use in the keytab file */
534 strupper_m( sam_account_name );
536 if ( (ret = ads_keytab_add_entry(ads, sam_account_name )) != 0 ) {
537 DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding sAMAccountName (%s)\n",
542 /* remember that not every machine account will have a upn */
544 upn = ads_get_upn( ads, ctx, machine_name);
546 if ( (ret = ads_keytab_add_entry(ads, upn)) != 0 ) {
547 DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding UPN (%s)\n",
554 /* Now loop through the keytab and update any other existing entries... */
556 kvno = (krb5_kvno) ads_get_machine_kvno(ads, machine_name);
558 DEBUG(1,("ads_keytab_create_default: ads_get_machine_kvno failed to determine the system's kvno.\n"));
563 DEBUG(3,("ads_keytab_create_default: Searching for keytab entries to "
564 "preserve and update.\n"));
566 ZERO_STRUCT(kt_entry);
569 initialize_krb5_error_table();
570 ret = krb5_init_context(&context);
572 DEBUG(1,("ads_keytab_create_default: could not krb5_init_context: %s\n",error_message(ret)));
577 ret = smb_krb5_open_keytab(context, NULL, True, &keytab);
579 DEBUG(1,("ads_keytab_create_default: smb_krb5_open_keytab failed (%s)\n", error_message(ret)));
583 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
584 if (ret != KRB5_KT_END && ret != ENOENT ) {
585 while ((ret = krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) == 0) {
586 smb_krb5_kt_free_entry(context, &kt_entry);
587 ZERO_STRUCT(kt_entry);
591 krb5_kt_end_seq_get(context, keytab, &cursor);
595 * Hmmm. There is no "rewind" function for the keytab. This means we have a race condition
596 * where someone else could add entries after we've counted them. Re-open asap to minimise
600 DEBUG(3, ("ads_keytab_create_default: Found %d entries in the keytab.\n", found));
604 oldEntries = talloc_array(ctx, char *, found );
606 DEBUG(1,("ads_keytab_create_default: Failed to allocate space to store the old keytab entries (malloc failed?).\n"));
610 memset(oldEntries, '\0', found * sizeof(char *));
612 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
613 if (ret != KRB5_KT_END && ret != ENOENT ) {
614 while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
615 if (kt_entry.vno != kvno) {
616 char *ktprinc = NULL;
619 /* This returns a malloc'ed string in ktprinc. */
620 ret = smb_krb5_unparse_name(oldEntries, context, kt_entry.principal, &ktprinc);
622 DEBUG(1,("smb_krb5_unparse_name failed (%s)\n", error_message(ret)));
626 * From looking at the krb5 source they don't seem to take locale
627 * or mb strings into account. Maybe this is because they assume utf8 ?
628 * In this case we may need to convert from utf8 to mb charset here ? JRA.
630 p = strchr_m(ktprinc, '@');
635 p = strchr_m(ktprinc, '/');
639 for (i = 0; i < found; i++) {
640 if (!oldEntries[i]) {
641 oldEntries[i] = ktprinc;
644 if (!strcmp(oldEntries[i], ktprinc)) {
645 TALLOC_FREE(ktprinc);
650 TALLOC_FREE(ktprinc);
653 smb_krb5_kt_free_entry(context, &kt_entry);
654 ZERO_STRUCT(kt_entry);
657 for (i = 0; oldEntries[i]; i++) {
658 ret |= ads_keytab_add_entry(ads, oldEntries[i]);
659 TALLOC_FREE(oldEntries[i]);
661 krb5_kt_end_seq_get(context, keytab, &cursor);
667 TALLOC_FREE(oldEntries);
671 krb5_keytab_entry zero_kt_entry;
672 ZERO_STRUCT(zero_kt_entry);
673 if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) {
674 smb_krb5_kt_free_entry(context, &kt_entry);
678 krb5_kt_cursor zero_csr;
679 ZERO_STRUCT(zero_csr);
680 if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) {
681 krb5_kt_end_seq_get(context, keytab, &cursor);
685 krb5_kt_close(context, keytab);
688 krb5_free_context(context);
693 /**********************************************************************
695 ***********************************************************************/
697 int ads_keytab_list(const char *keytab_name)
699 krb5_error_code ret = 0;
700 krb5_context context = NULL;
701 krb5_keytab keytab = NULL;
702 krb5_kt_cursor cursor;
703 krb5_keytab_entry kt_entry;
705 ZERO_STRUCT(kt_entry);
708 initialize_krb5_error_table();
709 ret = krb5_init_context(&context);
711 DEBUG(1,("ads_keytab_list: could not krb5_init_context: %s\n",error_message(ret)));
715 ret = smb_krb5_open_keytab(context, keytab_name, False, &keytab);
717 DEBUG(1,("ads_keytab_list: smb_krb5_open_keytab failed (%s)\n", error_message(ret)));
721 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
726 printf("Vno Type Principal\n");
728 while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
730 char *princ_s = NULL;
731 char *etype_s = NULL;
732 krb5_enctype enctype = 0;
734 ret = smb_krb5_unparse_name(talloc_tos(), context, kt_entry.principal, &princ_s);
739 enctype = smb_get_enctype_from_kt_entry(&kt_entry);
741 ret = smb_krb5_enctype_to_string(context, enctype, &etype_s);
743 if (asprintf(&etype_s, "UNKNOWN: %d\n", enctype) == -1)
745 TALLOC_FREE(princ_s);
750 printf("%3d %s\t\t %s\n", kt_entry.vno, etype_s, princ_s);
752 TALLOC_FREE(princ_s);
755 ret = smb_krb5_kt_free_entry(context, &kt_entry);
761 ret = krb5_kt_end_seq_get(context, keytab, &cursor);
766 /* Ensure we don't double free. */
767 ZERO_STRUCT(kt_entry);
772 krb5_keytab_entry zero_kt_entry;
773 ZERO_STRUCT(zero_kt_entry);
774 if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) {
775 smb_krb5_kt_free_entry(context, &kt_entry);
779 krb5_kt_cursor zero_csr;
780 ZERO_STRUCT(zero_csr);
781 if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) {
782 krb5_kt_end_seq_get(context, keytab, &cursor);
787 krb5_kt_close(context, keytab);
790 krb5_free_context(context);
795 #endif /* HAVE_KRB5 */