ctdb-server: Clean up connection tracking functions
[samba4-gss.git] / source3 / libads / kerberos_keytab.c
blobdbf8af44c1ff395b68505cbcfe3c79f3dbba955b
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"
32 #include "librpc/gen_ndr/ndr_secrets.h"
34 #ifdef HAVE_KRB5
36 #ifdef HAVE_ADS
38 /* This MAX_NAME_LEN is a constant defined in krb5.h */
39 #ifndef MAX_KEYTAB_NAME_LEN
40 #define MAX_KEYTAB_NAME_LEN 1100
41 #endif
43 enum spn_spec_type {
44 SPN_SPEC_DEFAULT,
45 SPN_SPEC_SYNC,
46 SPN_SPEC_FULL,
47 SPN_SPEC_PREFIX
50 /* pw2kt_conf contains 1 parsed line from "sync machine password to keytab" */
51 struct pw2kt_conf {
52 enum spn_spec_type spn_spec;
53 char *keytab;
54 bool sync_etypes;
55 bool sync_kvno;
56 bool additional_dns_hostnames;
57 bool netbios_aliases;
58 bool machine_password;
59 char **spn_spec_array;
60 size_t num_spn_spec;
63 /* State used by pw2kt */
64 struct pw2kt_state {
65 /* Array of parsed lines from "sync machine password to keytab" */
66 struct pw2kt_conf *keytabs;
67 size_t num_keytabs;
68 bool sync_etypes;
69 bool sync_kvno;
70 bool sync_spns;
71 /* These are from DC */
72 krb5_kvno ad_kvno;
73 uint32_t ad_etypes;
74 char **ad_spn_array;
75 size_t ad_num_spns;
76 /* This is from secrets.db */
77 struct secrets_domain_info1 *info;
80 /* State used by pw2kt_process_keytab */
81 struct pw2kt_process_state {
82 krb5_keytab keytab;
83 krb5_context context;
84 krb5_keytab_entry *array1;
85 krb5_keytab_entry *array2;
86 krb5_principal *princ_array;
87 krb5_enctype *enctypes;
88 krb5_enctype preferred_etype;
91 static ADS_STATUS pw2kt_scan_add_spn(TALLOC_CTX *ctx,
92 const char *spn,
93 struct pw2kt_conf *conf)
95 conf->spn_spec_array = talloc_realloc(ctx,
96 conf->spn_spec_array,
97 char *,
98 conf->num_spn_spec + 1);
99 if (conf->spn_spec_array == NULL) {
100 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
102 conf->spn_spec_array[conf->num_spn_spec] = talloc_strdup(
103 conf->spn_spec_array, spn);
104 if (conf->spn_spec_array[conf->num_spn_spec] == NULL) {
105 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
107 conf->num_spn_spec++;
109 return ADS_SUCCESS;
113 * Parse the smb.conf and find out if it is needed to read from DC:
114 * - servicePrincipalNames
115 * - msDs-KeyVersionNumber
117 static ADS_STATUS pw2kt_scan_line(const char *line, struct pw2kt_state *state)
119 char *keytabname = NULL;
120 char *spn_spec = NULL;
121 char *spn_val = NULL;
122 char *option = NULL;
123 struct pw2kt_conf *conf = NULL;
124 ADS_STATUS status;
126 state->keytabs = talloc_realloc(state,
127 state->keytabs,
128 struct pw2kt_conf,
129 state->num_keytabs + 1);
130 if (state->keytabs == NULL) {
131 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
133 conf = &state->keytabs[state->num_keytabs];
134 state->num_keytabs++;
136 keytabname = talloc_strdup(state->keytabs, line);
137 if (keytabname == NULL) {
138 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
141 ZERO_STRUCT(*conf);
142 conf->keytab = keytabname;
143 spn_spec = strchr_m(keytabname, ':');
144 if (spn_spec == NULL) {
145 DBG_ERR("Invalid format! ':' expected in '%s'\n", keytabname);
146 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
148 *spn_spec++ = 0;
150 /* reverse match with strrchr_m() */
151 while ((option = strrchr_m(spn_spec, ':')) != NULL) {
152 *option++ = 0;
153 if (strequal(option, "sync_kvno")) {
154 conf->sync_kvno = state->sync_kvno = true;
155 } else if (strequal(option, "sync_etypes")) {
156 conf->sync_etypes = state->sync_etypes = true;
157 } else if (strequal(option, "additional_dns_hostnames")) {
158 conf->additional_dns_hostnames = true;
159 } else if (strequal(option, "netbios_aliases")) {
160 conf->netbios_aliases = true;
161 } else if (strequal(option, "machine_password")) {
162 conf->machine_password = true;
163 } else {
164 DBG_WARNING("Unknown option '%s'!\n", option);
165 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
169 spn_val = strchr_m(spn_spec, '=');
170 if (spn_val != NULL) {
171 *spn_val++ = 0;
174 if (strcmp(spn_spec, "account_name") == 0) {
175 conf->spn_spec = SPN_SPEC_DEFAULT;
176 } else if (strcmp(spn_spec, "sync_spns") == 0) {
177 conf->spn_spec = SPN_SPEC_SYNC;
178 state->sync_spns = true;
179 } else if (strcmp(spn_spec, "spns") == 0 ||
180 strcmp(spn_spec, "spn_prefixes") == 0)
182 char *spn = NULL, *tmp = NULL;
184 conf->spn_spec = strcmp(spn_spec, "spns") == 0
185 ? SPN_SPEC_FULL
186 : SPN_SPEC_PREFIX;
187 conf->num_spn_spec = 0;
188 spn = spn_val;
189 while ((tmp = strchr_m(spn, ',')) != NULL) {
190 *tmp++ = 0;
191 status = pw2kt_scan_add_spn(state->keytabs, spn, conf);
192 if (!ADS_ERR_OK(status)) {
193 return status;
195 spn = tmp;
197 /* Do not forget the last entry */
198 return pw2kt_scan_add_spn(state->keytabs, spn, conf);
199 } else {
200 DBG_WARNING("Invalid SPN specifier: %s\n", spn_spec);
201 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
204 return ADS_SUCCESS;
208 * Fill struct pw2kt_state with defaults if "sync machine password to keytab"
209 * is missing in smb.conf
211 static ADS_STATUS pw2kt_default_cfg(const char *name, struct pw2kt_state *state)
213 char *keytabname = NULL;
214 struct pw2kt_conf *conf = NULL;
216 state->keytabs = talloc_zero_array(state->keytabs,
217 struct pw2kt_conf,
219 if (state->keytabs == NULL) {
220 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
222 conf = &state->keytabs[0];
223 state->num_keytabs = 1;
225 keytabname = talloc_strdup(state->keytabs, name);
226 if (keytabname == NULL) {
227 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
230 conf->spn_spec = SPN_SPEC_SYNC;
231 conf->keytab = keytabname;
232 conf->machine_password = true;
233 conf->sync_kvno = state->sync_kvno = true;
234 state->sync_spns = true;
236 return ADS_SUCCESS;
240 * For the given principal add to the array entries created from all pw->keys[]
242 static krb5_error_code pw2kt_process_add_pw(
243 struct pw2kt_process_state *state2,
244 krb5_principal princ,
245 krb5_kvno vno,
246 struct secrets_domain_info1_password *pw)
248 uint16_t i;
249 size_t len = talloc_array_length(state2->array1);
251 for (i = 0; i < pw->num_keys; i++) {
252 krb5_keytab_entry *kt_entry = NULL;
253 krb5_keyblock *key = NULL;
254 krb5_keytab_entry *tmp_a = NULL;
256 if (state2->preferred_etype != -1 &&
257 state2->preferred_etype != pw->keys[i].keytype)
259 DBG_DEBUG("Skip enc type '%d'.\n", pw->keys[i].keytype);
260 continue;
263 len++;
264 tmp_a = talloc_realloc(state2,
265 state2->array1,
266 krb5_keytab_entry,
267 len);
268 if (tmp_a == NULL) {
269 return ENOMEM;
271 state2->array1 = tmp_a;
272 kt_entry = &state2->array1[len - 1];
273 ZERO_STRUCT(*kt_entry);
274 kt_entry->principal = princ;
275 kt_entry->vno = vno;
277 key = KRB5_KT_KEY(kt_entry);
278 KRB5_KEY_TYPE(key) = pw->keys[i].keytype;
279 KRB5_KEY_DATA(key) = pw->keys[i].value.data;
280 KRB5_KEY_LENGTH(key) = pw->keys[i].value.length;
283 return 0;
287 * For the given principal add to the array entries based on password,
288 * old_password, older_password and next_change->password.
290 static krb5_error_code pw2kt_process_add_info(
291 struct pw2kt_process_state *state2,
292 krb5_kvno kvno,
293 const char *princs,
294 struct secrets_domain_info1 *info)
296 krb5_error_code ret;
297 krb5_principal princ = NULL;
298 krb5_principal *a = NULL;
299 size_t len;
301 ret = smb_krb5_parse_name(state2->context, princs, &princ);
302 if (ret != 0) {
303 DBG_ERR("Failed to parse principal: %s\n", princs);
304 return ret;
306 len = talloc_array_length(state2->princ_array);
307 a = talloc_realloc(state2,
308 state2->princ_array,
309 krb5_principal,
310 len + 1);
311 if (a == NULL) {
312 (void)krb5_free_principal(state2->context, princ);
313 return ENOMEM;
315 a[len] = princ;
316 state2->princ_array = a;
318 #define ADD_PW(K, P) \
319 if (info->P != NULL) { \
320 ret = pw2kt_process_add_pw(state2, princ, (K), info->P); \
321 if (ret != 0) { \
322 DBG_ERR("Failed adding %s keys for %s.\n", \
323 #P, \
324 princs); \
325 return ret; \
329 ADD_PW(kvno, password);
330 ADD_PW(kvno - 1, old_password);
331 ADD_PW(kvno - 2, older_password);
332 if (info->next_change) {
333 ADD_PW(kvno == -1 ? -4 : kvno + 1, next_change->password);
336 return ret;
339 static int pw2kt_process_state_destructor(struct pw2kt_process_state *state2)
341 int i;
342 size_t len2 = talloc_array_length(state2->array2);
343 size_t len_p = talloc_array_length(state2->princ_array);
345 for (i = 0; i < len2; i++) {
346 (void)smb_krb5_kt_free_entry(state2->context,
347 &state2->array2[i]);
349 for (i = 0; i < len_p; i++) {
350 (void)krb5_free_principal(state2->context,
351 state2->princ_array[i]);
353 (void)krb5_free_enctypes(state2->context, state2->enctypes);
355 return 0;
358 /* Read the whole keytab to krb5_keytab_entry array */
359 static krb5_error_code pw2kt_process_kt2ar(struct pw2kt_process_state *state2)
361 krb5_error_code ret = 0, ret2 = 0;
362 krb5_kt_cursor cursor;
363 krb5_keytab_entry *a = NULL;
364 krb5_keytab_entry e;
365 size_t num = 0;
367 ZERO_STRUCT(cursor);
369 ret = krb5_kt_start_seq_get(state2->context, state2->keytab, &cursor);
370 if (ret != 0) {
371 if (ret == KRB5_KT_END || ret == ENOENT) {
372 ret = 0;
374 return ret;
377 for (;;) {
378 ret = samba_krb5_kt_next_entry(state2->context,
379 state2->keytab,
381 &cursor);
382 if (ret != 0) {
383 break;
385 a = talloc_realloc(state2,
386 state2->array2,
387 krb5_keytab_entry,
388 num + 1);
389 if (a == NULL) {
390 smb_krb5_kt_free_entry(state2->context, &e);
391 return ENOMEM;
393 a[num++] = e;
394 state2->array2 = a;
397 if (ret == KRB5_KT_END || ret == ENOENT) {
398 ret = 0;
400 ret2 = krb5_kt_end_seq_get(state2->context, state2->keytab, &cursor);
402 return ret != 0 ? ret : ret2;
405 static ADS_STATUS pw2kt_process_keytab(struct pw2kt_state *state,
406 struct pw2kt_conf *keytabptr)
408 krb5_error_code ret = 0;
409 krb5_kvno kvno = -1;
410 size_t i, j, len1 = 0, len2 = 0;
411 char *princ_s = NULL;
412 const char **netbios_alias = NULL;
413 const char **addl_hostnames = NULL;
414 size_t *index_array1 = NULL;
415 size_t *index_array2 = NULL;
416 struct pw2kt_process_state *state2 = NULL;
418 if (!keytabptr->machine_password) {
419 DBG_ERR("No 'machine_password' option for '%s'. Skip it.\n",
420 keytabptr->keytab);
421 return ADS_SUCCESS;
424 state2 = talloc_zero(state, struct pw2kt_process_state);
425 if (state2 == NULL) {
426 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
428 talloc_set_destructor(state2, pw2kt_process_state_destructor);
430 ret = smb_krb5_init_context_common(&state2->context);
431 if (ret != 0) {
432 DBG_ERR("Krb init context failed (%s)\n", error_message(ret));
433 return ADS_ERROR_KRB5(ret);
436 #define MATCH_ENCTYPE(TYPES, TYPE) \
437 ({ \
438 int ei, result = 0; \
439 for (ei = 0; (TYPES)[ei] != 0; ei++) { \
440 if ((uint32_t)(TYPES)[ei] != (TYPE)) { \
441 continue; \
443 result = 1; \
444 break; \
446 result; \
449 #define COMMON_ENCTYPE(ETYPE) \
450 MATCH_ENCTYPE((state2->enctypes), (ETYPE)) && \
451 ((state->ad_etypes) & (ETYPE))
454 * -1 means there is no information about supported encryption types
455 * from DC and all encoding types will be added to the keytab.
457 state2->preferred_etype = -1;
459 /* Find the highest common enc type for AD and KRB5 lib */
460 if (keytabptr->sync_etypes) {
461 ret = smb_krb5_get_allowed_etypes(state2->context,
462 &state2->enctypes);
463 if (ret != 0) {
464 DBG_ERR("Failed to get allowed enc types.\n");
465 return ADS_ERROR_KRB5(ret);
468 if (COMMON_ENCTYPE(ENCTYPE_AES256_CTS_HMAC_SHA1_96)) {
469 state2->preferred_etype =
470 ENCTYPE_AES256_CTS_HMAC_SHA1_96;
471 } else if (COMMON_ENCTYPE(ENCTYPE_AES128_CTS_HMAC_SHA1_96)) {
472 state2->preferred_etype =
473 ENCTYPE_AES128_CTS_HMAC_SHA1_96;
474 } else if (COMMON_ENCTYPE(ENCTYPE_ARCFOUR_HMAC)) {
475 state2->preferred_etype = ENCTYPE_ARCFOUR_HMAC;
476 } else {
477 DBG_ERR("No common enctype for AD and KRB5 lib.\n");
478 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
482 if (keytabptr->sync_kvno) {
483 kvno = state->ad_kvno;
486 #define ADD_INFO(P) \
487 ret = pw2kt_process_add_info(state2, kvno, (P), state->info); \
488 if (ret != 0) { \
489 return ADS_ERROR_KRB5(ret); \
492 /* Add ACCOUNTNAME$ entries */
493 switch (keytabptr->spn_spec) {
494 case SPN_SPEC_DEFAULT:
495 ADD_INFO(state->info->account_name);
496 break;
497 case SPN_SPEC_SYNC:
498 for (i = 0; i < state->ad_num_spns; i++) {
499 ADD_INFO(state->ad_spn_array[i]);
501 break;
502 case SPN_SPEC_FULL:
503 for (i = 0; i < keytabptr->num_spn_spec; i++) {
504 ADD_INFO(keytabptr->spn_spec_array[i]);
506 break;
507 case SPN_SPEC_PREFIX:
508 for (i = 0; i < keytabptr->num_spn_spec; i++) {
509 princ_s = talloc_asprintf(talloc_tos(),
510 "%s/%s@%s",
511 keytabptr->spn_spec_array[i],
512 lp_netbios_name(),
513 lp_realm());
514 if (princ_s == NULL) {
515 return ADS_ERROR_KRB5(ENOMEM);
517 ADD_INFO(princ_s);
519 if (!keytabptr->netbios_aliases) {
520 goto additional_dns_hostnames;
522 for (netbios_alias = lp_netbios_aliases();
523 netbios_alias != NULL && *netbios_alias != NULL;
524 netbios_alias++)
526 /* Add PREFIX/netbiosname@REALM */
527 princ_s = talloc_asprintf(
528 talloc_tos(),
529 "%s/%s@%s",
530 keytabptr->spn_spec_array[i],
531 *netbios_alias,
532 lp_realm());
533 if (princ_s == NULL) {
534 return ADS_ERROR_KRB5(ENOMEM);
536 ADD_INFO(princ_s);
538 /* Add PREFIX/netbiosname.domainname@REALM */
539 princ_s = talloc_asprintf(
540 talloc_tos(),
541 "%s/%s.%s@%s",
542 keytabptr->spn_spec_array[i],
543 *netbios_alias,
544 lp_dnsdomain(),
545 lp_realm());
546 if (princ_s == NULL) {
547 return ADS_ERROR_KRB5(ENOMEM);
549 ADD_INFO(princ_s);
552 additional_dns_hostnames:
553 if (!keytabptr->additional_dns_hostnames) {
554 continue;
556 for (addl_hostnames = lp_additional_dns_hostnames();
557 addl_hostnames != NULL && *addl_hostnames != NULL;
558 addl_hostnames++)
560 /* Add PREFIX/netbiosname@REALM */
561 princ_s = talloc_asprintf(
562 talloc_tos(),
563 "%s/%s@%s",
564 keytabptr->spn_spec_array[i],
565 *addl_hostnames,
566 lp_realm());
567 if (princ_s == NULL) {
568 return ADS_ERROR_KRB5(ENOMEM);
570 ADD_INFO(princ_s);
573 break;
574 default:
575 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
578 ret = smb_krb5_kt_open(state2->context,
579 keytabptr->keytab,
580 true,
581 &state2->keytab);
582 if (ret != 0) {
583 return ADS_ERROR_KRB5(ret);
586 /* The new entries are in array1. Read existing entries to array2. */
587 ret = pw2kt_process_kt2ar(state2);
588 if (ret != 0) {
589 return ADS_ERROR_KRB5(ret);
592 len1 = talloc_array_length(state2->array1);
593 len2 = talloc_array_length(state2->array2);
595 if (keytabptr->sync_kvno) {
596 goto sync_kvno;
599 /* copy existing entries VNO -1, -2, -3, -4 to VNO -11, -12, -13, -14 */
600 for (j = 0; j < len2; j++) {
601 krb5_keytab_entry e = state2->array2[j];
602 /* vno type is 'krb5_kvno' which is 'unsigned int' */
603 if (e.vno != -1 && e.vno != -2 && e.vno != -3 && e.vno != -4) {
604 DBG_WARNING("Unexpected keytab entry with VNO = %d (it "
605 "should be -1, -2, -3, -4) in %s\n",
606 e.vno,
607 keytabptr->keytab);
608 continue;
610 e.vno = state2->array2[j].vno - 10;
611 ret = samba_krb5_kt_add_entry(state2->context,
612 state2->keytab,
613 &e);
614 if (ret != 0) {
615 return ADS_ERROR_KRB5(ret);
618 /* remove all old entries (they should have VNO -1, -2, -3, -4) */
619 for (j = 0; j < len2; j++) {
620 krb5_keytab_entry e = state2->array2[j];
621 if (e.vno != -1 && e.vno != -2 && e.vno != -3 && e.vno != -4) {
622 DBG_WARNING("Unexpected keytab entry with VNO = %d (it "
623 "should be -1, -2, -3, -4) in %s\n",
624 e.vno,
625 keytabptr->keytab);
627 ret = samba_krb5_kt_remove_entry(state2->context,
628 state2->keytab,
629 &state2->array2[j]);
630 if (ret != 0) {
631 D_WARNING("Failed to remove keytab entry from %s\n",
632 keytabptr->keytab);
633 ret = 0; /* Be fault tolerant */
636 /* add new entries with VNO -1, -2, -3, -4 */
637 for (i = 0; i < len1; i++) {
638 ret = samba_krb5_kt_add_entry(state2->context,
639 state2->keytab,
640 &state2->array1[i]);
641 if (ret != 0) {
642 return ADS_ERROR_KRB5(ret);
645 /* remove entries with VNO -11, -12, -13, -14 */
646 for (j = 0; j < len2; j++) {
647 krb5_keytab_entry e = state2->array2[j];
648 e.vno = state2->array2[j].vno - 10;
649 ret = samba_krb5_kt_remove_entry(state2->context,
650 state2->keytab,
651 &e);
652 if (ret != 0) {
653 D_WARNING("Failed to remove keytab entry from %s\n",
654 keytabptr->keytab);
655 ret = 0; /* Be fault tolerant */
659 ret = krb5_kt_close(state2->context, state2->keytab);
660 return ADS_ERROR_KRB5(ret);
662 sync_kvno:
664 index_array1 = talloc_zero_array(state2, size_t, len1);
665 index_array2 = talloc_zero_array(state2, size_t, len2);
666 if (index_array1 == NULL || index_array2 == NULL) {
667 return ADS_ERROR_KRB5(ENOMEM);
670 * Mark entries that are present in both arrays.
671 * These will not be added or removed.
673 for (i = 0; i < len1; i++) {
674 for (j = 0; j < len2; j++) {
675 krb5_keytab_entry e2 = state2->array2[j];
676 if (smb_krb5_kt_compare(
677 state2->context,
678 &state2->array1[i],
679 e2.principal,
680 e2.vno,
681 KRB5_KEY_TYPE(KRB5_KT_KEY(&e2))
684 index_array1[i] = 1;
685 index_array2[j] = 1;
690 /* First add the new entries to the keytab.*/
691 for (i = 0; i < len1; i++) {
692 if (index_array1[i] == 0) {
693 ret = samba_krb5_kt_add_entry(state2->context,
694 state2->keytab,
695 &state2->array1[i]);
696 if (ret != 0) {
697 return ADS_ERROR_KRB5(ret);
702 /* Now, remove the old entries from the keytab. */
703 for (j = 0; j < len2; j++) {
704 if (index_array2[j] == 0) {
705 ret = samba_krb5_kt_remove_entry(state2->context,
706 state2->keytab,
707 &state2->array2[j]);
708 if (ret != 0) {
709 D_WARNING("Failed to remove keytab entry from "
710 "%s\n",
711 keytabptr->keytab);
712 ret = 0; /* Be fault tolerant */
717 ret = krb5_kt_close(state2->context, state2->keytab);
718 return ADS_ERROR_KRB5(ret);
721 static ADS_STATUS pw2kt_get_dc_info(struct pw2kt_state *state)
723 ADS_STATUS status;
724 LDAPMessage *res = NULL;
725 int count;
726 bool ok;
727 TALLOC_CTX *tmp_ctx = talloc_stackframe();
728 ADS_STRUCT *ads = ads_init(
729 tmp_ctx, lp_realm(), lp_workgroup(), NULL, ADS_SASL_SIGN);
731 if (ads == NULL) {
732 DBG_ERR("ads_init() failed\n");
733 TALLOC_FREE(tmp_ctx);
734 return ADS_ERROR(LDAP_NO_MEMORY);
737 status = ads_connect_machine(ads);
738 if (!ADS_ERR_OK(status)) {
739 DBG_ERR("Failed to refresh keytab, ads_connect() returned %s\n",
740 ads_errstr(status));
741 TALLOC_FREE(tmp_ctx);
742 return status;
745 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
746 if (!ADS_ERR_OK(status)) {
747 TALLOC_FREE(tmp_ctx);
748 return status;
751 count = ads_count_replies(ads, res);
752 if (count != 1) {
753 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
754 ads_msgfree(ads, res);
755 TALLOC_FREE(tmp_ctx);
756 return status;
759 if (state->sync_etypes) {
760 ok = ads_pull_uint32(ads,
761 res,
762 "msDS-SupportedEncryptionTypes",
763 &state->ad_etypes);
764 if (!ok) {
765 DBG_WARNING("Failed to determine encryption types.\n");
766 ads_msgfree(ads, res);
767 TALLOC_FREE(tmp_ctx);
768 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
772 if (state->sync_kvno) {
773 uint32_t kvno = -1;
774 ok = ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno);
775 if (!ok) {
776 DBG_WARNING("Failed to determine the system's kvno.\n");
777 ads_msgfree(ads, res);
778 TALLOC_FREE(tmp_ctx);
779 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
781 state->ad_kvno = (krb5_kvno) kvno;
784 if (state->sync_spns) {
785 state->ad_spn_array = ads_pull_strings(ads,
786 state,
787 res,
788 "servicePrincipalName",
789 &state->ad_num_spns);
790 if (state->ad_spn_array == NULL) {
791 DBG_WARNING("Failed to determine SPNs.\n");
792 ads_msgfree(ads, res);
793 TALLOC_FREE(tmp_ctx);
794 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
798 ads_msgfree(ads, res);
799 TALLOC_FREE(tmp_ctx);
800 return status;
803 static bool pw2kt_default_keytab_name(char *name_str, size_t name_size)
805 char keytab_str[MAX_KEYTAB_NAME_LEN] = {0};
806 const char *keytab_name = NULL;
807 krb5_context context = 0;
808 krb5_error_code ret;
810 switch (lp_kerberos_method()) {
811 case KERBEROS_VERIFY_SYSTEM_KEYTAB:
812 case KERBEROS_VERIFY_SECRETS_AND_KEYTAB:
813 ret = smb_krb5_init_context_common(&context);
814 if (ret) {
815 DBG_ERR("kerberos init context failed (%s)\n",
816 error_message(ret));
817 return false;
819 ret = krb5_kt_default_name(context,
820 keytab_str,
821 sizeof(keytab_str) - 2);
822 krb5_free_context(context);
823 if (ret != 0) {
824 DBG_WARNING("Failed to get default keytab name\n");
825 return false;
827 if (strncmp(keytab_str, "WRFILE:", 7) == 0) {
828 keytab_name = keytab_str + 7;
829 } else if (strncmp(keytab_str, "FILE:", 5) == 0) {
830 keytab_name = keytab_str + 5;
831 } else {
832 keytab_name = keytab_str;
834 break;
836 case KERBEROS_VERIFY_DEDICATED_KEYTAB:
837 keytab_name = lp_dedicated_keytab_file();
838 break;
840 default:
841 DBG_NOTICE("'kerberos method' is 'secrets only' but "
842 "'sync machine password to keytab' is not set "
843 "==> no keytab will be generated.\n");
844 return false;
847 if (keytab_name == NULL || keytab_name[0] == '\0') {
848 DBG_ERR("Invalid keytab name\n");
849 return false;
852 if (strlen(keytab_name) + 1 > name_size) {
853 DBG_ERR("Too long keytab name\n");
854 return false;
857 (void)strncpy(name_str, keytab_name, name_size);
859 return true;
862 NTSTATUS sync_pw2keytabs(void)
864 TALLOC_CTX *frame = talloc_stackframe();
865 const struct loadparm_substitution *lp_sub =
866 loadparm_s3_global_substitution();
867 struct pw2kt_state *state = NULL;
868 const char **line = NULL;
869 const char **lp_ptr = NULL;
870 const char *pwsync_script = NULL;
871 NTSTATUS status_nt;
872 ADS_STATUS status_ads;
873 int i;
875 DBG_DEBUG("Syncing machine password from secrets to keytabs.\n");
877 if (lp_server_role() != ROLE_DOMAIN_MEMBER) {
878 TALLOC_FREE(frame);
879 return NT_STATUS_OK; /* nothing todo */
882 state = talloc_zero(frame, struct pw2kt_state);
883 if (state == NULL) {
884 TALLOC_FREE(frame);
885 return NT_STATUS_NO_MEMORY;
888 lp_ptr = lp_sync_machine_password_to_keytab();
889 if (lp_ptr == NULL) {
890 char name[MAX_KEYTAB_NAME_LEN] = {0};
891 bool ok = pw2kt_default_keytab_name(name, sizeof(name));
893 if (!ok) {
894 TALLOC_FREE(frame);
895 DBG_WARNING("No default keytab name.\n");
896 return NT_STATUS_OK; /* nothing todo */
898 status_ads = pw2kt_default_cfg(name, state);
899 if (!ADS_ERR_OK(status_ads)) {
900 DBG_WARNING("Cannot create default configuration.\n");
901 TALLOC_FREE(frame);
902 return NT_STATUS_INTERNAL_ERROR;
904 goto params_ready;
907 if ((*lp_ptr != NULL) && strequal_m(*lp_ptr, "disabled")) {
908 DBG_DEBUG("'sync machine password to keytab' is explicitly disabled.\n");
909 return NT_STATUS_OK;
912 line = lp_ptr;
913 while (*line) {
914 DBG_DEBUG("Scanning line: %s\n", *line);
915 status_ads = pw2kt_scan_line(*line, state);
916 if (!ADS_ERR_OK(status_ads)) {
917 TALLOC_FREE(frame);
918 return NT_STATUS_INTERNAL_ERROR;
920 line++;
923 params_ready:
924 if (state->sync_etypes || state->sync_kvno || state->sync_spns) {
925 status_ads = pw2kt_get_dc_info(state);
926 if (!ADS_ERR_OK(status_ads)) {
927 DBG_WARNING("cannot read from DC\n");
928 TALLOC_FREE(frame);
929 return NT_STATUS_INTERNAL_ERROR;
931 } else {
932 DBG_DEBUG("No 'sync_etypes', 'sync_kvno' and 'sync_spns' in "
933 "parameter 'sync machine password to keytab' => "
934 "no need to talk to DC.\n");
937 if (!secrets_init()) {
938 DBG_WARNING("secrets_init failed\n");
939 TALLOC_FREE(frame);
940 return NT_STATUS_INTERNAL_ERROR;
943 status_nt = secrets_fetch_or_upgrade_domain_info(lp_workgroup(),
944 frame,
945 &state->info);
946 if (!NT_STATUS_IS_OK(status_nt)) {
947 DBG_WARNING("secrets_fetch_or_upgrade_domain_info(%s) - %s\n",
948 lp_workgroup(),
949 nt_errstr(status_nt));
950 TALLOC_FREE(frame);
951 return status_nt;
954 for (i = 0; i < state->num_keytabs; i++) {
955 status_ads = pw2kt_process_keytab(state, &state->keytabs[i]);
956 if (!ADS_ERR_OK(status_ads)) {
957 TALLOC_FREE(frame);
958 return NT_STATUS_INTERNAL_ERROR;
962 pwsync_script = lp_sync_machine_password_script(frame, lp_sub);
963 if (pwsync_script != NULL && pwsync_script[0] != '\0') {
964 int ret;
966 DBG_DEBUG("Running script: '%s'\n.", pwsync_script);
967 ret = smbrun(pwsync_script, NULL, NULL);
968 if (ret != 0) {
969 DBG_ERR("Script '%s' failed with: %d.\n",
970 pwsync_script,
971 ret);
972 TALLOC_FREE(frame);
973 return NT_STATUS_INTERNAL_ERROR;
977 TALLOC_FREE(frame);
978 return NT_STATUS_OK;
981 static krb5_error_code ads_keytab_open(krb5_context context,
982 krb5_keytab *keytab)
984 char keytab_str[MAX_KEYTAB_NAME_LEN] = {0};
985 const char *keytab_name = NULL;
986 krb5_error_code ret = 0;
988 switch (lp_kerberos_method()) {
989 case KERBEROS_VERIFY_SYSTEM_KEYTAB:
990 case KERBEROS_VERIFY_SECRETS_AND_KEYTAB:
991 ret = krb5_kt_default_name(context,
992 keytab_str,
993 sizeof(keytab_str) - 2);
994 if (ret != 0) {
995 DBG_WARNING("Failed to get default keytab name\n");
996 goto out;
998 keytab_name = keytab_str;
999 break;
1000 case KERBEROS_VERIFY_DEDICATED_KEYTAB:
1001 keytab_name = lp_dedicated_keytab_file();
1002 break;
1003 default:
1004 DBG_ERR("Invalid kerberos method set (%d)\n",
1005 lp_kerberos_method());
1006 ret = KRB5_KT_BADNAME;
1007 goto out;
1010 if (keytab_name == NULL || keytab_name[0] == '\0') {
1011 DBG_ERR("Invalid keytab name\n");
1012 ret = KRB5_KT_BADNAME;
1013 goto out;
1016 ret = smb_krb5_kt_open(context, keytab_name, true, keytab);
1017 if (ret != 0) {
1018 DBG_WARNING("smb_krb5_kt_open failed (%s)\n",
1019 error_message(ret));
1020 goto out;
1023 out:
1024 return ret;
1027 /**********************************************************************
1028 Flushes all entries from the system keytab.
1029 ***********************************************************************/
1031 int ads_keytab_flush(ADS_STRUCT *ads)
1033 krb5_error_code ret = 0;
1034 krb5_context context = NULL;
1035 krb5_keytab keytab = NULL;
1036 ADS_STATUS aderr;
1038 ret = smb_krb5_init_context_common(&context);
1039 if (ret) {
1040 DBG_ERR("kerberos init context failed (%s)\n",
1041 error_message(ret));
1042 return ret;
1045 ret = ads_keytab_open(context, &keytab);
1046 if (ret != 0) {
1047 goto out;
1050 /* Seek and delete all old keytab entries */
1051 ret = smb_krb5_kt_seek_and_delete_old_entries(context,
1052 keytab,
1053 false, /* keep_old_kvno */
1055 false, /* enctype_only */
1056 ENCTYPE_NULL,
1057 NULL,
1058 NULL,
1059 true); /* flush */
1060 if (ret) {
1061 goto out;
1064 aderr = ads_clear_service_principal_names(ads, lp_netbios_name());
1065 if (!ADS_ERR_OK(aderr)) {
1066 DEBUG(1, (__location__ ": Error while clearing service "
1067 "principal listings in LDAP.\n"));
1068 ret = -1;
1069 goto out;
1072 out:
1073 if (keytab) {
1074 krb5_kt_close(context, keytab);
1076 if (context) {
1077 krb5_free_context(context);
1079 return ret;
1082 #endif /* HAVE_ADS */
1084 /**********************************************************************
1085 List system keytab.
1086 ***********************************************************************/
1088 int ads_keytab_list(const char *keytab_name)
1090 krb5_error_code ret = 0;
1091 krb5_context context = NULL;
1092 krb5_keytab keytab = NULL;
1093 krb5_kt_cursor cursor;
1094 krb5_keytab_entry kt_entry;
1096 ZERO_STRUCT(kt_entry);
1097 ZERO_STRUCT(cursor);
1099 ret = smb_krb5_init_context_common(&context);
1100 if (ret) {
1101 DBG_ERR("kerberos init context failed (%s)\n",
1102 error_message(ret));
1103 return ret;
1106 if (keytab_name == NULL) {
1107 #ifdef HAVE_ADS
1108 ret = ads_keytab_open(context, &keytab);
1109 #else
1110 ret = ENOENT;
1111 #endif
1112 } else {
1113 ret = smb_krb5_kt_open(context, keytab_name, False, &keytab);
1115 if (ret) {
1116 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
1117 error_message(ret)));
1118 goto out;
1121 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1122 if (ret) {
1123 ZERO_STRUCT(cursor);
1124 goto out;
1127 printf("Vno Type Principal\n");
1129 while (samba_krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) ==
1133 char *princ_s = NULL;
1134 char *etype_s = NULL;
1135 krb5_enctype enctype = 0;
1137 ret = smb_krb5_unparse_name(talloc_tos(), context,
1138 kt_entry.principal, &princ_s);
1139 if (ret) {
1140 goto out;
1143 enctype = smb_krb5_kt_get_enctype_from_entry(&kt_entry);
1145 ret = smb_krb5_enctype_to_string(context, enctype, &etype_s);
1146 if (ret &&
1147 (asprintf(&etype_s, "UNKNOWN: %d", enctype) == -1)) {
1148 TALLOC_FREE(princ_s);
1149 goto out;
1152 printf("%3d %-43s %s\n", kt_entry.vno, etype_s, princ_s);
1154 TALLOC_FREE(princ_s);
1155 SAFE_FREE(etype_s);
1157 ret = smb_krb5_kt_free_entry(context, &kt_entry);
1158 if (ret) {
1159 goto out;
1163 ret = krb5_kt_end_seq_get(context, keytab, &cursor);
1164 if (ret) {
1165 goto out;
1168 /* Ensure we don't double free. */
1169 ZERO_STRUCT(kt_entry);
1170 ZERO_STRUCT(cursor);
1171 out:
1173 if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
1174 smb_krb5_kt_free_entry(context, &kt_entry);
1176 if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
1177 krb5_kt_end_seq_get(context, keytab, &cursor);
1180 if (keytab) {
1181 krb5_kt_close(context, keytab);
1183 if (context) {
1184 krb5_free_context(context);
1186 return ret;
1189 #endif /* HAVE_KRB5 */