2 * GSSAPI Security Extensions
4 * Copyright (C) Simo Sorce 2010.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
23 #include "librpc/gen_ndr/secrets.h"
25 #include "lib/param/loadparm.h"
26 #include "libads/kerberos_proto.h"
30 static krb5_error_code flush_keytab(krb5_context krbctx, krb5_keytab keytab)
33 krb5_kt_cursor kt_cursor;
34 krb5_keytab_entry kt_entry;
36 ZERO_STRUCT(kt_entry);
38 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
39 if (ret == KRB5_KT_END || ret == ENOENT ) {
44 ret = krb5_kt_next_entry(krbctx, keytab, &kt_entry, &kt_cursor);
47 /* we need to close and reopen enumeration because we modify
49 ret = krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
51 DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
52 "failed (%s)\n", error_message(ret)));
56 /* remove the entry */
57 ret = krb5_kt_remove_entry(krbctx, keytab, &kt_entry);
59 DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
60 "failed (%s)\n", error_message(ret)));
63 ret = smb_krb5_kt_free_entry(krbctx, &kt_entry);
64 ZERO_STRUCT(kt_entry);
67 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
69 DEBUG(1, (__location__ ": krb5_kt_start_seq() failed "
70 "(%s)\n", error_message(ret)));
74 ret = krb5_kt_next_entry(krbctx, keytab,
75 &kt_entry, &kt_cursor);
78 if (ret != KRB5_KT_END && ret != ENOENT) {
79 DEBUG(1, (__location__ ": flushing keytab we got [%s]!\n",
89 static krb5_error_code fill_keytab_from_password(krb5_context krbctx,
93 struct secrets_domain_info1_password *pw)
96 krb5_enctype *enctypes;
99 ret = smb_krb5_get_allowed_etypes(krbctx, &enctypes);
101 DEBUG(1, (__location__
102 ": Can't determine permitted enctypes!\n"));
106 for (i = 0; i < pw->num_keys; i++) {
107 krb5_keytab_entry kt_entry;
108 krb5_keyblock *key = NULL;
110 bool found_etype = false;
112 for (ei=0; enctypes[ei] != 0; ei++) {
113 if ((uint32_t)enctypes[ei] != pw->keys[i].keytype) {
125 ZERO_STRUCT(kt_entry);
126 kt_entry.principal = princ;
129 key = KRB5_KT_KEY(&kt_entry);
130 KRB5_KEY_TYPE(key) = pw->keys[i].keytype;
131 KRB5_KEY_DATA(key) = pw->keys[i].value.data;
132 KRB5_KEY_LENGTH(key) = pw->keys[i].value.length;
134 ret = krb5_kt_add_entry(krbctx, keytab, &kt_entry);
136 DEBUG(1, (__location__ ": Failed to add entry to "
137 "keytab for enctype %d (error: %s)\n",
138 (unsigned)pw->keys[i].keytype,
139 error_message(ret)));
151 #define SRV_MEM_KEYTAB_NAME "MEMORY:cifs_srv_keytab"
152 #define CLEARTEXT_PRIV_ENCTYPE -99
154 static krb5_error_code fill_mem_keytab_from_secrets(krb5_context krbctx,
157 TALLOC_CTX *frame = talloc_stackframe();
159 const char *domain = lp_workgroup();
160 struct secrets_domain_info1 *info = NULL;
161 const char *realm = NULL;
162 const DATA_BLOB *ct = NULL;
163 krb5_kt_cursor kt_cursor;
164 krb5_keytab_entry kt_entry;
165 krb5_principal princ = NULL;
166 krb5_kvno kvno = 0; /* FIXME: fetch current vno from KDC ? */
169 if (!secrets_init()) {
170 DEBUG(1, (__location__ ": secrets_init failed\n"));
172 return KRB5_CONFIG_CANTOPEN;
175 status = secrets_fetch_or_upgrade_domain_info(domain,
178 if (!NT_STATUS_IS_OK(status)) {
179 DBG_WARNING("secrets_fetch_or_upgrade_domain_info(%s) - %s\n",
180 domain, nt_errstr(status));
182 return KRB5_LIBOS_CANTREADPWD;
184 ct = &info->password->cleartext_blob;
186 if (info->domain_info.dns_domain.string != NULL) {
187 realm = strupper_talloc(frame,
188 info->domain_info.dns_domain.string);
195 ZERO_STRUCT(kt_entry);
196 ZERO_STRUCT(kt_cursor);
198 /* check if the keytab already has any entry */
199 ret = krb5_kt_start_seq_get(krbctx, *keytab, &kt_cursor);
200 if (ret != KRB5_KT_END && ret != ENOENT ) {
201 /* check if we have our special enctype used to hold
202 * the clear text password. If so, check it out so that
203 * we can verify if the keytab needs to be upgraded */
204 while ((ret = krb5_kt_next_entry(krbctx, *keytab,
205 &kt_entry, &kt_cursor)) == 0) {
206 if (smb_krb5_kt_get_enctype_from_entry(&kt_entry) ==
207 CLEARTEXT_PRIV_ENCTYPE) {
210 smb_krb5_kt_free_entry(krbctx, &kt_entry);
211 ZERO_STRUCT(kt_entry);
214 if (ret != 0 && ret != KRB5_KT_END && ret != ENOENT ) {
215 /* Error parsing keytab */
216 DEBUG(1, (__location__ ": Failed to parse memory "
222 /* found private entry,
223 * check if keytab is up to date */
225 if ((ct->length == KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry))) &&
226 (memcmp(KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)),
227 ct->data, ct->length) == 0)) {
228 /* keytab is already up to date, return */
229 smb_krb5_kt_free_entry(krbctx, &kt_entry);
233 smb_krb5_kt_free_entry(krbctx, &kt_entry);
234 ZERO_STRUCT(kt_entry);
237 /* flush keytab, we need to regen it */
238 ret = flush_keytab(krbctx, *keytab);
240 DEBUG(1, (__location__ ": Failed to flush "
241 "memory keytab!\n"));
247 if (!all_zero((uint8_t *)&kt_cursor, sizeof(kt_cursor)) && *keytab) {
248 krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
251 /* keytab is not up to date, fill it up */
253 ret = smb_krb5_make_principal(krbctx, &princ, realm,
254 info->account_name, NULL);
256 DEBUG(1, (__location__ ": Failed to get host principal!\n"));
260 ret = fill_keytab_from_password(krbctx, *keytab,
264 DBG_WARNING("fill_keytab_from_password() failed for "
265 "info->password.\n.");
269 if (info->old_password != NULL) {
270 ret = fill_keytab_from_password(krbctx, *keytab,
274 DBG_WARNING("fill_keytab_from_password() failed for "
275 "info->old_password.\n.");
280 if (info->older_password != NULL) {
281 ret = fill_keytab_from_password(krbctx, *keytab,
283 info->older_password);
285 DBG_WARNING("fill_keytab_from_password() failed for "
286 "info->older_password.\n.");
291 if (info->next_change != NULL) {
292 ret = fill_keytab_from_password(krbctx, *keytab,
294 info->next_change->password);
296 DBG_WARNING("fill_keytab_from_password() failed for "
297 "info->next_change->password.\n.");
302 /* add our private enctype + cleartext password so that we can
303 * update the keytab if secrets change later on */
304 ZERO_STRUCT(kt_entry);
305 kt_entry.principal = princ;
308 KRB5_KEY_TYPE(KRB5_KT_KEY(&kt_entry)) = CLEARTEXT_PRIV_ENCTYPE;
309 KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry)) = ct->length;
310 KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)) = ct->data;
312 ret = krb5_kt_add_entry(krbctx, *keytab, &kt_entry);
314 DEBUG(1, (__location__ ": Failed to add entry to "
315 "keytab for private enctype (%d) (error: %s)\n",
316 CLEARTEXT_PRIV_ENCTYPE, error_message(ret)));
323 if (!all_zero((uint8_t *)&kt_cursor, sizeof(kt_cursor)) && *keytab) {
324 krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
328 krb5_free_principal(krbctx, princ);
335 static krb5_error_code fill_mem_keytab_from_system_keytab(krb5_context krbctx,
336 krb5_keytab *mkeytab)
338 krb5_error_code ret = 0;
339 krb5_keytab keytab = NULL;
340 krb5_kt_cursor kt_cursor = { 0, };
341 krb5_keytab_entry kt_entry = { 0, };
342 char *valid_princ_formats[7] = { NULL, NULL, NULL,
343 NULL, NULL, NULL, NULL };
344 char *entry_princ_s = NULL;
345 fstring my_name, my_fqdn;
349 /* Generate the list of principal names which we expect
350 * clients might want to use for authenticating to the file
351 * service. We allow name$,{host,cifs}/{name,fqdn,name.REALM}. */
353 fstrcpy(my_name, lp_netbios_name());
356 name_to_fqdn(my_fqdn, lp_netbios_name());
358 err = asprintf(&valid_princ_formats[0],
359 "%s$@%s", my_name, lp_realm());
364 err = asprintf(&valid_princ_formats[1],
365 "host/%s@%s", my_name, lp_realm());
370 err = asprintf(&valid_princ_formats[2],
371 "host/%s@%s", my_fqdn, lp_realm());
376 err = asprintf(&valid_princ_formats[3],
377 "host/%s.%s@%s", my_name, lp_realm(), lp_realm());
382 err = asprintf(&valid_princ_formats[4],
383 "cifs/%s@%s", my_name, lp_realm());
388 err = asprintf(&valid_princ_formats[5],
389 "cifs/%s@%s", my_fqdn, lp_realm());
394 err = asprintf(&valid_princ_formats[6],
395 "cifs/%s.%s@%s", my_name, lp_realm(), lp_realm());
401 ret = smb_krb5_kt_open_relative(krbctx, NULL, false, &keytab);
403 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
404 error_message(ret)));
409 * Iterate through the keytab. For each key, if the principal
410 * name case-insensitively matches one of the allowed formats,
411 * copy it to the memory keytab.
414 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
416 DEBUG(1, (__location__ ": krb5_kt_start_seq_get failed (%s)\n",
417 error_message(ret)));
419 * krb5_kt_start_seq_get() may leaves bogus data
420 * in kt_cursor. And we want to use the all_zero()
425 ZERO_STRUCT(kt_cursor);
429 while ((krb5_kt_next_entry(krbctx, keytab,
430 &kt_entry, &kt_cursor) == 0)) {
431 ret = smb_krb5_unparse_name(talloc_tos(), krbctx,
435 DEBUG(1, (__location__ ": smb_krb5_unparse_name "
436 "failed (%s)\n", error_message(ret)));
440 for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) {
442 if (!strequal(entry_princ_s, valid_princ_formats[i])) {
446 ret = krb5_kt_add_entry(krbctx, *mkeytab, &kt_entry);
448 DEBUG(1, (__location__ ": smb_krb5_unparse_name "
449 "failed (%s)\n", error_message(ret)));
454 /* Free the name we parsed. */
455 TALLOC_FREE(entry_princ_s);
457 /* Free the entry we just read. */
458 smb_krb5_kt_free_entry(krbctx, &kt_entry);
459 ZERO_STRUCT(kt_entry);
461 krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
463 ZERO_STRUCT(kt_cursor);
467 for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) {
468 SAFE_FREE(valid_princ_formats[i]);
471 TALLOC_FREE(entry_princ_s);
473 if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
474 smb_krb5_kt_free_entry(krbctx, &kt_entry);
477 if (!all_zero((uint8_t *)&kt_cursor, sizeof(kt_cursor)) && keytab) {
478 krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
482 krb5_kt_close(krbctx, keytab);
488 static krb5_error_code fill_mem_keytab_from_dedicated_keytab(krb5_context krbctx,
489 krb5_keytab *mkeytab)
491 krb5_error_code ret = 0;
492 krb5_keytab keytab = NULL;
493 krb5_kt_cursor kt_cursor;
494 krb5_keytab_entry kt_entry;
496 ret = smb_krb5_kt_open(krbctx, lp_dedicated_keytab_file(),
499 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
500 error_message(ret)));
505 * Copy the dedicated keyab to our in-memory keytab.
508 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
510 DEBUG(1, (__location__ ": krb5_kt_start_seq_get failed (%s)\n",
511 error_message(ret)));
515 while ((krb5_kt_next_entry(krbctx, keytab,
516 &kt_entry, &kt_cursor) == 0)) {
518 ret = krb5_kt_add_entry(krbctx, *mkeytab, &kt_entry);
520 /* Free the entry we just read. */
521 smb_krb5_kt_free_entry(krbctx, &kt_entry);
524 DEBUG(1, (__location__ ": smb_krb5_unparse_name "
525 "failed (%s)\n", error_message(ret)));
529 krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
533 krb5_kt_close(krbctx, keytab);
538 krb5_error_code gse_krb5_get_server_keytab(krb5_context krbctx,
541 krb5_error_code ret = 0;
542 krb5_error_code ret1 = 0;
543 krb5_error_code ret2 = 0;
547 /* create memory keytab */
548 ret = krb5_kt_resolve(krbctx, SRV_MEM_KEYTAB_NAME, keytab);
550 DEBUG(1, (__location__ ": Failed to get memory "
555 switch (lp_kerberos_method()) {
557 case KERBEROS_VERIFY_SECRETS:
558 ret = fill_mem_keytab_from_secrets(krbctx, keytab);
560 case KERBEROS_VERIFY_SYSTEM_KEYTAB:
561 ret = fill_mem_keytab_from_system_keytab(krbctx, keytab);
563 case KERBEROS_VERIFY_DEDICATED_KEYTAB:
564 /* just use whatever keytab is configured */
565 ret = fill_mem_keytab_from_dedicated_keytab(krbctx, keytab);
567 case KERBEROS_VERIFY_SECRETS_AND_KEYTAB:
568 ret1 = fill_mem_keytab_from_secrets(krbctx, keytab);
570 DEBUG(3, (__location__ ": Warning! Unable to set mem "
571 "keytab from secrets!\n"));
573 /* Now append system keytab keys too */
574 ret2 = fill_mem_keytab_from_system_keytab(krbctx, keytab);
576 DEBUG(3, (__location__ ": Warning! Unable to set mem "
577 "keytab from system keytab!\n"));
579 if (ret1 == 0 || ret2 == 0) {
588 krb5_kt_close(krbctx, *keytab);
590 DEBUG(1,("%s: Error! Unable to set mem keytab - %d\n",
597 #endif /* HAVE_KRB5 */