vfs_aio_fork: Use a shorter random delay
[Samba.git] / source3 / libads / kerberos_keytab.c
blobffd100c5636b273b0a76e059dc76f1d93133f290
1 /*
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/>.
28 #include "includes.h"
29 #include "smb_krb5.h"
30 #include "ads.h"
31 #include "secrets.h"
33 #ifdef HAVE_KRB5
35 #ifdef HAVE_ADS
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
40 #endif
42 static krb5_error_code ads_keytab_open(krb5_context context,
43 krb5_keytab *keytab)
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,
53 keytab_str,
54 sizeof(keytab_str) - 2);
55 if (ret != 0) {
56 DBG_WARNING("Failed to get default keytab name");
57 goto out;
59 keytab_name = keytab_str;
60 break;
61 case KERBEROS_VERIFY_DEDICATED_KEYTAB:
62 keytab_name = lp_dedicated_keytab_file();
63 break;
64 default:
65 DBG_ERR("Invalid kerberos method set (%d)\n",
66 lp_kerberos_method());
67 ret = KRB5_KT_BADNAME;
68 goto out;
71 if (keytab_name == NULL || keytab_name[0] == '\0') {
72 DBG_ERR("Invalid keytab name\n");
73 ret = KRB5_KT_BADNAME;
74 goto out;
77 ret = smb_krb5_kt_open(context, keytab_name, true, keytab);
78 if (ret != 0) {
79 DBG_WARNING("smb_krb5_kt_open failed (%s)\n",
80 error_message(ret));
81 goto out;
84 out:
85 return ret;
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;
97 krb5_data password;
98 krb5_kvno kvno;
99 krb5_enctype enctypes[6] = {
100 ENCTYPE_DES_CBC_CRC,
101 ENCTYPE_DES_CBC_MD5,
102 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
103 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
104 #endif
105 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
106 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
107 #endif
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;
115 char *my_fqdn;
116 TALLOC_CTX *tmpctx = NULL;
117 char *machine_name;
118 ADS_STATUS aderr;
119 int i;
121 initialize_krb5_error_table();
122 ret = krb5_init_context(&context);
123 if (ret) {
124 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
125 error_message(ret)));
126 return -1;
129 ret = ads_keytab_open(context, &keytab);
130 if (ret != 0) {
131 goto out;
134 /* retrieve the password */
135 if (!secrets_init()) {
136 DEBUG(1, (__location__ ": secrets_init failed\n"));
137 ret = -1;
138 goto out;
140 password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
141 if (!password_s) {
142 DEBUG(1, (__location__ ": failed to fetch machine password\n"));
143 ret = -1;
144 goto out;
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__);
152 if (!tmpctx) {
153 DEBUG(0, (__location__ ": talloc_init() failed!\n"));
154 ret = -1;
155 goto out;
158 my_fqdn = ads_get_dnshostname(ads, tmpctx, lp_netbios_name());
159 if (!my_fqdn) {
160 DEBUG(0, (__location__ ": unable to determine machine "
161 "account's dns name in AD!\n"));
162 ret = -1;
163 goto out;
166 machine_name = ads_get_samaccountname(ads, tmpctx, lp_netbios_name());
167 if (!machine_name) {
168 DEBUG(0, (__location__ ": unable to determine machine "
169 "account's short name in AD!\n"));
170 ret = -1;
171 goto out;
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);
180 if (!princ_s) {
181 ret = -1;
182 goto out;
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());
188 if (!princ_s) {
189 ret = -1;
190 goto out;
192 } else {
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());
199 if (!princ_s) {
200 ret = -1;
201 goto out;
203 short_princ_s = talloc_asprintf(tmpctx, "%s/%s@%s",
204 srvPrinc, machine_name,
205 lp_realm());
206 if (short_princ_s == NULL) {
207 ret = -1;
208 goto out;
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 "
219 "'%s'\n", princ_s));
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"));
226 goto out;
231 kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
232 if (kvno == -1) {
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"));
236 ret = -1;
237 goto out;
240 salt_princ_s = kerberos_secrets_fetch_salt_princ();
241 if (salt_princ_s == NULL) {
242 DBG_WARNING("kerberos_secrets_fetch_salt_princ() failed\n");
243 ret = -1;
244 goto out;
247 for (i = 0; enctypes[i]; i++) {
249 /* add the fqdn principal to the keytab */
250 ret = smb_krb5_kt_add_entry(context,
251 keytab,
252 kvno,
253 princ_s,
254 salt_princ_s,
255 enctypes[i],
256 &password,
257 false,
258 false);
259 if (ret) {
260 DEBUG(1, (__location__ ": Failed to add entry to keytab\n"));
261 goto out;
264 /* add the short principal name if we have one */
265 if (short_princ_s) {
266 ret = smb_krb5_kt_add_entry(context,
267 keytab,
268 kvno,
269 short_princ_s,
270 salt_princ_s,
271 enctypes[i],
272 &password,
273 false,
274 false);
275 if (ret) {
276 DEBUG(1, (__location__
277 ": Failed to add short entry to keytab\n"));
278 goto out;
283 out:
284 SAFE_FREE(salt_princ_s);
285 TALLOC_FREE(tmpctx);
287 if (keytab) {
288 krb5_kt_close(context, keytab);
290 if (context) {
291 krb5_free_context(context);
293 return (int)ret;
296 /**********************************************************************
297 Flushes all entries from the system keytab.
298 ***********************************************************************/
300 int ads_keytab_flush(ADS_STRUCT *ads)
302 krb5_error_code ret = 0;
303 krb5_context context = NULL;
304 krb5_keytab keytab = NULL;
305 krb5_kvno kvno;
306 ADS_STATUS aderr;
308 initialize_krb5_error_table();
309 ret = krb5_init_context(&context);
310 if (ret) {
311 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
312 error_message(ret)));
313 return ret;
316 ret = ads_keytab_open(context, &keytab);
317 if (ret != 0) {
318 goto out;
321 kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
322 if (kvno == -1) {
323 /* -1 indicates a failure */
324 DEBUG(1, (__location__ ": Error determining the kvno.\n"));
325 goto out;
328 /* Seek and delete old keytab entries */
329 ret = smb_krb5_kt_seek_and_delete_old_entries(context,
330 keytab,
331 kvno,
332 ENCTYPE_NULL,
333 NULL,
334 NULL,
335 true,
336 false);
337 if (ret) {
338 goto out;
341 aderr = ads_clear_service_principal_names(ads, lp_netbios_name());
342 if (!ADS_ERR_OK(aderr)) {
343 DEBUG(1, (__location__ ": Error while clearing service "
344 "principal listings in LDAP.\n"));
345 goto out;
348 out:
349 if (keytab) {
350 krb5_kt_close(context, keytab);
352 if (context) {
353 krb5_free_context(context);
355 return ret;
358 /**********************************************************************
359 Adds all the required service principals to the system keytab.
360 ***********************************************************************/
362 int ads_keytab_create_default(ADS_STRUCT *ads)
364 krb5_error_code ret = 0;
365 krb5_context context = NULL;
366 krb5_keytab keytab = NULL;
367 krb5_kt_cursor cursor = {0};
368 krb5_keytab_entry kt_entry = {0};
369 krb5_kvno kvno;
370 size_t found = 0;
371 char *sam_account_name, *upn;
372 char **oldEntries = NULL, *princ_s[26];
373 TALLOC_CTX *frame;
374 char *machine_name;
375 char **spn_array;
376 size_t num_spns;
377 size_t i;
378 ADS_STATUS status;
380 ZERO_STRUCT(kt_entry);
381 ZERO_STRUCT(cursor);
383 frame = talloc_stackframe();
384 if (frame == NULL) {
385 ret = -1;
386 goto done;
389 status = ads_get_service_principal_names(frame,
390 ads,
391 lp_netbios_name(),
392 &spn_array,
393 &num_spns);
394 if (!ADS_ERR_OK(status)) {
395 ret = -1;
396 goto done;
399 for (i = 0; i < num_spns; i++) {
400 char *srv_princ;
401 char *p;
403 srv_princ = strlower_talloc(frame, spn_array[i]);
404 if (srv_princ == NULL) {
405 ret = -1;
406 goto done;
409 p = strchr_m(srv_princ, '/');
410 if (p == NULL) {
411 continue;
413 p[0] = '\0';
415 /* Add the SPNs found on the DC */
416 ret = ads_keytab_add_entry(ads, srv_princ);
417 if (ret != 0) {
418 DEBUG(1, ("ads_keytab_add_entry failed while "
419 "adding '%s' principal.\n",
420 spn_array[i]));
421 goto done;
425 #if 0 /* don't create the CIFS/... keytab entries since no one except smbd
426 really needs them and we will fall back to verifying against
427 secrets.tdb */
429 ret = ads_keytab_add_entry(ads, "cifs"));
430 if (ret != 0 ) {
431 DEBUG(1, (__location__ ": ads_keytab_add_entry failed while "
432 "adding 'cifs'.\n"));
433 return ret;
435 #endif
437 memset(princ_s, '\0', sizeof(princ_s));
439 initialize_krb5_error_table();
440 ret = krb5_init_context(&context);
441 if (ret) {
442 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
443 error_message(ret)));
444 goto done;
447 machine_name = talloc_strdup(frame, lp_netbios_name());
448 if (!machine_name) {
449 ret = -1;
450 goto done;
453 /* now add the userPrincipalName and sAMAccountName entries */
454 sam_account_name = ads_get_samaccountname(ads, frame, machine_name);
455 if (!sam_account_name) {
456 DEBUG(0, (__location__ ": unable to determine machine "
457 "account's name in AD!\n"));
458 ret = -1;
459 goto done;
462 /* upper case the sAMAccountName to make it easier for apps to
463 know what case to use in the keytab file */
464 if (!strupper_m(sam_account_name)) {
465 ret = -1;
466 goto done;
469 ret = ads_keytab_add_entry(ads, sam_account_name);
470 if (ret != 0) {
471 DEBUG(1, (__location__ ": ads_keytab_add_entry() failed "
472 "while adding sAMAccountName (%s)\n",
473 sam_account_name));
474 goto done;
477 /* remember that not every machine account will have a upn */
478 upn = ads_get_upn(ads, frame, machine_name);
479 if (upn) {
480 ret = ads_keytab_add_entry(ads, upn);
481 if (ret != 0) {
482 DEBUG(1, (__location__ ": ads_keytab_add_entry() "
483 "failed while adding UPN (%s)\n", upn));
484 goto done;
488 /* Now loop through the keytab and update any other existing entries */
489 kvno = (krb5_kvno)ads_get_machine_kvno(ads, machine_name);
490 if (kvno == (krb5_kvno)-1) {
491 DEBUG(1, (__location__ ": ads_get_machine_kvno() failed to "
492 "determine the system's kvno.\n"));
493 goto done;
496 DEBUG(3, (__location__ ": Searching for keytab entries to preserve "
497 "and update.\n"));
499 ret = ads_keytab_open(context, &keytab);
500 if (ret != 0) {
501 goto done;
504 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
505 if (ret != KRB5_KT_END && ret != ENOENT ) {
506 while ((ret = krb5_kt_next_entry(context, keytab,
507 &kt_entry, &cursor)) == 0) {
508 smb_krb5_kt_free_entry(context, &kt_entry);
509 ZERO_STRUCT(kt_entry);
510 found++;
513 krb5_kt_end_seq_get(context, keytab, &cursor);
514 ZERO_STRUCT(cursor);
517 * Hmmm. There is no "rewind" function for the keytab. This means we
518 * have a race condition where someone else could add entries after
519 * we've counted them. Re-open asap to minimise the race. JRA.
521 DEBUG(3, (__location__ ": Found %zd entries in the keytab.\n", found));
522 if (!found) {
523 goto done;
526 oldEntries = talloc_zero_array(frame, char *, found + 1);
527 if (!oldEntries) {
528 DEBUG(1, (__location__ ": Failed to allocate space to store "
529 "the old keytab entries (talloc failed?).\n"));
530 ret = -1;
531 goto done;
534 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
535 if (ret == KRB5_KT_END || ret == ENOENT) {
536 krb5_kt_end_seq_get(context, keytab, &cursor);
537 ZERO_STRUCT(cursor);
538 goto done;
541 while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
542 if (kt_entry.vno != kvno) {
543 char *ktprinc = NULL;
544 char *p;
546 /* This returns a malloc'ed string in ktprinc. */
547 ret = smb_krb5_unparse_name(oldEntries, context,
548 kt_entry.principal,
549 &ktprinc);
550 if (ret) {
551 DEBUG(1, (__location__
552 ": smb_krb5_unparse_name failed "
553 "(%s)\n", error_message(ret)));
554 goto done;
557 * From looking at the krb5 source they don't seem to
558 * take locale or mb strings into account.
559 * Maybe this is because they assume utf8 ?
560 * In this case we may need to convert from utf8 to
561 * mb charset here ? JRA.
563 p = strchr_m(ktprinc, '@');
564 if (p) {
565 *p = '\0';
568 p = strchr_m(ktprinc, '/');
569 if (p) {
570 *p = '\0';
572 for (i = 0; i < found; i++) {
573 if (!oldEntries[i]) {
574 oldEntries[i] = ktprinc;
575 break;
577 if (!strcmp(oldEntries[i], ktprinc)) {
578 TALLOC_FREE(ktprinc);
579 break;
582 if (i == found) {
583 TALLOC_FREE(ktprinc);
586 smb_krb5_kt_free_entry(context, &kt_entry);
587 ZERO_STRUCT(kt_entry);
589 krb5_kt_end_seq_get(context, keytab, &cursor);
590 ZERO_STRUCT(cursor);
592 ret = 0;
593 for (i = 0; oldEntries[i]; i++) {
594 ret |= ads_keytab_add_entry(ads, oldEntries[i]);
595 TALLOC_FREE(oldEntries[i]);
598 done:
599 TALLOC_FREE(oldEntries);
600 TALLOC_FREE(frame);
602 if (context) {
603 if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
604 smb_krb5_kt_free_entry(context, &kt_entry);
606 if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
607 krb5_kt_end_seq_get(context, keytab, &cursor);
609 if (keytab) {
610 krb5_kt_close(context, keytab);
612 krb5_free_context(context);
614 return ret;
617 #endif /* HAVE_ADS */
619 /**********************************************************************
620 List system keytab.
621 ***********************************************************************/
623 int ads_keytab_list(const char *keytab_name)
625 krb5_error_code ret = 0;
626 krb5_context context = NULL;
627 krb5_keytab keytab = NULL;
628 krb5_kt_cursor cursor;
629 krb5_keytab_entry kt_entry;
631 ZERO_STRUCT(kt_entry);
632 ZERO_STRUCT(cursor);
634 initialize_krb5_error_table();
635 ret = krb5_init_context(&context);
636 if (ret) {
637 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
638 error_message(ret)));
639 return ret;
642 if (keytab_name == NULL) {
643 ret = ads_keytab_open(context, &keytab);
644 } else {
645 ret = smb_krb5_kt_open(context, keytab_name, False, &keytab);
647 if (ret) {
648 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
649 error_message(ret)));
650 goto out;
653 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
654 if (ret) {
655 ZERO_STRUCT(cursor);
656 goto out;
659 printf("Vno Type Principal\n");
661 while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
663 char *princ_s = NULL;
664 char *etype_s = NULL;
665 krb5_enctype enctype = 0;
667 ret = smb_krb5_unparse_name(talloc_tos(), context,
668 kt_entry.principal, &princ_s);
669 if (ret) {
670 goto out;
673 enctype = smb_krb5_kt_get_enctype_from_entry(&kt_entry);
675 ret = smb_krb5_enctype_to_string(context, enctype, &etype_s);
676 if (ret &&
677 (asprintf(&etype_s, "UNKNOWN: %d\n", enctype) == -1)) {
678 TALLOC_FREE(princ_s);
679 goto out;
682 printf("%3d %-43s %s\n", kt_entry.vno, etype_s, princ_s);
684 TALLOC_FREE(princ_s);
685 SAFE_FREE(etype_s);
687 ret = smb_krb5_kt_free_entry(context, &kt_entry);
688 if (ret) {
689 goto out;
693 ret = krb5_kt_end_seq_get(context, keytab, &cursor);
694 if (ret) {
695 goto out;
698 /* Ensure we don't double free. */
699 ZERO_STRUCT(kt_entry);
700 ZERO_STRUCT(cursor);
701 out:
703 if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
704 smb_krb5_kt_free_entry(context, &kt_entry);
706 if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
707 krb5_kt_end_seq_get(context, keytab, &cursor);
710 if (keytab) {
711 krb5_kt_close(context, keytab);
713 if (context) {
714 krb5_free_context(context);
716 return ret;
719 #endif /* HAVE_KRB5 */