ctdb-scripts: Move connection tracking to 10.interface
[samba4-gss.git] / source3 / librpc / crypto / gse_krb5.c
blob8e719e1ae0c19438acafd30314f02553d59391c0
1 /*
2 * GSSAPI Security Extensions
3 * Krb5 helpers
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/>.
20 #include "includes.h"
21 #include "smb_krb5.h"
22 #include "secrets.h"
23 #include "librpc/gen_ndr/secrets.h"
24 #include "gse_krb5.h"
25 #include "lib/param/loadparm.h"
26 #include "libads/kerberos_proto.h"
27 #include "lib/util/string_wrappers.h"
29 #ifdef HAVE_KRB5
31 static krb5_error_code flush_keytab(krb5_context krbctx, krb5_keytab keytab)
33 krb5_error_code ret;
34 krb5_kt_cursor kt_cursor;
35 krb5_keytab_entry kt_entry;
37 ZERO_STRUCT(kt_entry);
39 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
40 if (ret != 0) {
41 return ret;
44 ret = krb5_kt_next_entry(krbctx, keytab, &kt_entry, &kt_cursor);
45 while (ret == 0) {
47 /* we need to close and reopen enumeration because we modify
48 * the keytab */
49 ret = krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
50 if (ret != 0) {
51 DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
52 "failed (%s)\n", error_message(ret)));
53 goto out;
56 /* remove the entry */
57 ret = krb5_kt_remove_entry(krbctx, keytab, &kt_entry);
58 if (ret != 0) {
59 DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
60 "failed (%s)\n", error_message(ret)));
61 goto out;
63 smb_krb5_kt_free_entry(krbctx, &kt_entry);
64 ZERO_STRUCT(kt_entry);
66 /* now reopen */
67 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
68 if (ret != 0) {
69 DEBUG(1, (__location__ ": krb5_kt_start_seq() failed "
70 "(%s)\n", error_message(ret)));
71 goto out;
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",
80 error_message(ret)));
83 ret = krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
84 if (ret != 0) {
85 DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
86 "failed (%s)\n", error_message(ret)));
87 goto out;
89 ret = 0;
91 out:
92 return ret;
95 static krb5_error_code fill_keytab_from_password(krb5_context krbctx,
96 krb5_keytab keytab,
97 krb5_principal princ,
98 krb5_kvno vno,
99 struct secrets_domain_info1_password *pw)
101 krb5_error_code ret;
102 krb5_enctype *enctypes;
103 uint16_t i;
105 ret = smb_krb5_get_allowed_etypes(krbctx, &enctypes);
106 if (ret) {
107 DEBUG(1, (__location__
108 ": Can't determine permitted enctypes!\n"));
109 return ret;
112 for (i = 0; i < pw->num_keys; i++) {
113 krb5_keytab_entry kt_entry;
114 krb5_keyblock *key = NULL;
115 unsigned int ei;
116 bool found_etype = false;
118 for (ei=0; enctypes[ei] != 0; ei++) {
119 if ((uint32_t)enctypes[ei] != pw->keys[i].keytype) {
120 continue;
123 found_etype = true;
124 break;
127 if (!found_etype) {
128 continue;
131 ZERO_STRUCT(kt_entry);
132 kt_entry.principal = princ;
133 kt_entry.vno = vno;
135 key = KRB5_KT_KEY(&kt_entry);
136 KRB5_KEY_TYPE(key) = pw->keys[i].keytype;
137 KRB5_KEY_DATA(key) = pw->keys[i].value.data;
138 KRB5_KEY_LENGTH(key) = pw->keys[i].value.length;
140 ret = krb5_kt_add_entry(krbctx, keytab, &kt_entry);
141 if (ret) {
142 DEBUG(1, (__location__ ": Failed to add entry to "
143 "keytab for enctype %d (error: %s)\n",
144 (unsigned)pw->keys[i].keytype,
145 error_message(ret)));
146 goto out;
150 ret = 0;
152 out:
153 krb5_free_enctypes(krbctx, enctypes);
154 return ret;
157 #define SRV_MEM_KEYTAB_NAME "MEMORY:cifs_srv_keytab"
158 #define CLEARTEXT_PRIV_ENCTYPE -99
160 static krb5_error_code fill_mem_keytab_from_secrets(krb5_context krbctx,
161 krb5_keytab *keytab)
163 TALLOC_CTX *frame = talloc_stackframe();
164 krb5_error_code ret, ret2;
165 const char *domain = lp_workgroup();
166 struct secrets_domain_info1 *info = NULL;
167 const char *realm = NULL;
168 const DATA_BLOB *ct = NULL;
169 krb5_kt_cursor kt_cursor;
170 krb5_keytab_entry kt_entry;
171 krb5_principal princ = NULL;
172 krb5_kvno kvno = 0; /* FIXME: fetch current vno from KDC ? */
173 NTSTATUS status;
175 if (!secrets_init()) {
176 DEBUG(1, (__location__ ": secrets_init failed\n"));
177 TALLOC_FREE(frame);
178 return KRB5_CONFIG_CANTOPEN;
181 status = secrets_fetch_or_upgrade_domain_info(domain,
182 frame,
183 &info);
184 if (!NT_STATUS_IS_OK(status)) {
185 DBG_WARNING("secrets_fetch_or_upgrade_domain_info(%s) - %s\n",
186 domain, nt_errstr(status));
187 TALLOC_FREE(frame);
188 return KRB5_LIBOS_CANTREADPWD;
190 ct = &info->password->cleartext_blob;
192 if (info->domain_info.dns_domain.string != NULL) {
193 realm = strupper_talloc(frame,
194 info->domain_info.dns_domain.string);
195 if (realm == NULL) {
196 TALLOC_FREE(frame);
197 return ENOMEM;
201 ZERO_STRUCT(kt_entry);
202 ZERO_STRUCT(kt_cursor);
204 /* check if the keytab already has any entry */
205 ret = krb5_kt_start_seq_get(krbctx, *keytab, &kt_cursor);
206 if (ret != 0) {
207 goto out;
210 /* check if we have our special enctype used to hold
211 * the clear text password. If so, check it out so that
212 * we can verify if the keytab needs to be upgraded */
213 while ((ret = krb5_kt_next_entry(krbctx, *keytab,
214 &kt_entry, &kt_cursor)) == 0) {
215 if (smb_krb5_kt_get_enctype_from_entry(&kt_entry) ==
216 CLEARTEXT_PRIV_ENCTYPE) {
217 break;
219 smb_krb5_kt_free_entry(krbctx, &kt_entry);
220 ZERO_STRUCT(kt_entry);
223 ret2 = krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
224 if (ret2 != 0) {
225 ret = ret2;
226 DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
227 "failed (%s)\n", error_message(ret)));
228 goto out;
231 if (ret != 0 && ret != KRB5_KT_END && ret != ENOENT ) {
232 /* Error parsing keytab */
233 DEBUG(1, (__location__ ": Failed to parse memory "
234 "keytab!\n"));
235 goto out;
238 if (ret == 0) {
239 /* found private entry,
240 * check if keytab is up to date */
242 if ((ct->length == KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry))) &&
243 (mem_equal_const_time(KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)),
244 ct->data, ct->length))) {
245 /* keytab is already up to date, return */
246 smb_krb5_kt_free_entry(krbctx, &kt_entry);
247 goto out;
250 smb_krb5_kt_free_entry(krbctx, &kt_entry);
251 ZERO_STRUCT(kt_entry);
254 /* flush keytab, we need to regen it */
255 ret = flush_keytab(krbctx, *keytab);
256 if (ret) {
257 DEBUG(1, (__location__ ": Failed to flush "
258 "memory keytab!\n"));
259 goto out;
263 /* keytab is not up to date, fill it up */
265 ret = smb_krb5_make_principal(krbctx, &princ, realm,
266 info->account_name, NULL);
267 if (ret) {
268 DEBUG(1, (__location__ ": Failed to get host principal!\n"));
269 goto out;
272 ret = fill_keytab_from_password(krbctx, *keytab,
273 princ, kvno,
274 info->password);
275 if (ret) {
276 DBG_WARNING("fill_keytab_from_password() failed for "
277 "info->password.\n.");
278 goto out;
281 if (info->old_password != NULL) {
282 ret = fill_keytab_from_password(krbctx, *keytab,
283 princ, kvno - 1,
284 info->old_password);
285 if (ret) {
286 DBG_WARNING("fill_keytab_from_password() failed for "
287 "info->old_password.\n.");
288 goto out;
292 if (info->older_password != NULL) {
293 ret = fill_keytab_from_password(krbctx, *keytab,
294 princ, kvno - 2,
295 info->older_password);
296 if (ret) {
297 DBG_WARNING("fill_keytab_from_password() failed for "
298 "info->older_password.\n.");
299 goto out;
303 if (info->next_change != NULL) {
304 ret = fill_keytab_from_password(krbctx, *keytab,
305 princ, kvno - 3,
306 info->next_change->password);
307 if (ret) {
308 DBG_WARNING("fill_keytab_from_password() failed for "
309 "info->next_change->password.\n.");
310 goto out;
314 /* add our private enctype + cleartext password so that we can
315 * update the keytab if secrets change later on */
316 ZERO_STRUCT(kt_entry);
317 kt_entry.principal = princ;
318 kt_entry.vno = 0;
320 KRB5_KEY_TYPE(KRB5_KT_KEY(&kt_entry)) = CLEARTEXT_PRIV_ENCTYPE;
321 KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry)) = ct->length;
322 KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)) = ct->data;
324 ret = krb5_kt_add_entry(krbctx, *keytab, &kt_entry);
325 if (ret) {
326 DEBUG(1, (__location__ ": Failed to add entry to "
327 "keytab for private enctype (%d) (error: %s)\n",
328 CLEARTEXT_PRIV_ENCTYPE, error_message(ret)));
329 goto out;
332 ret = 0;
334 out:
336 if (princ) {
337 krb5_free_principal(krbctx, princ);
340 TALLOC_FREE(frame);
341 return ret;
344 static krb5_error_code fill_mem_keytab_from_system_keytab(krb5_context krbctx,
345 krb5_keytab *mkeytab)
347 krb5_error_code ret = 0;
348 krb5_keytab keytab = NULL;
349 krb5_kt_cursor kt_cursor = { 0, };
350 krb5_keytab_entry kt_entry = { 0, };
351 char *valid_princ_formats[7] = { NULL, NULL, NULL,
352 NULL, NULL, NULL, NULL };
353 char *entry_princ_s = NULL;
354 fstring my_name, my_fqdn;
355 unsigned i;
356 int err;
357 const char *dns_hostname = NULL;
359 /* Generate the list of principal names which we expect
360 * clients might want to use for authenticating to the file
361 * service. We allow name$,{host,cifs}/{name,fqdn,name.REALM}. */
363 fstrcpy(my_name, lp_netbios_name());
364 dns_hostname = lp_dns_hostname();
365 if (dns_hostname == NULL) {
366 ret = ENOMEM;
367 goto out;
369 fstrcpy(my_fqdn, dns_hostname);
371 err = asprintf(&valid_princ_formats[0],
372 "%s$@%s", my_name, lp_realm());
373 if (err == -1) {
374 ret = ENOMEM;
375 goto out;
377 err = asprintf(&valid_princ_formats[1],
378 "host/%s@%s", my_name, lp_realm());
379 if (err == -1) {
380 ret = ENOMEM;
381 goto out;
383 err = asprintf(&valid_princ_formats[2],
384 "host/%s@%s", my_fqdn, lp_realm());
385 if (err == -1) {
386 ret = ENOMEM;
387 goto out;
389 err = asprintf(&valid_princ_formats[3],
390 "host/%s.%s@%s", my_name, lp_realm(), lp_realm());
391 if (err == -1) {
392 ret = ENOMEM;
393 goto out;
395 err = asprintf(&valid_princ_formats[4],
396 "cifs/%s@%s", my_name, lp_realm());
397 if (err == -1) {
398 ret = ENOMEM;
399 goto out;
401 err = asprintf(&valid_princ_formats[5],
402 "cifs/%s@%s", my_fqdn, lp_realm());
403 if (err == -1) {
404 ret = ENOMEM;
405 goto out;
407 err = asprintf(&valid_princ_formats[6],
408 "cifs/%s.%s@%s", my_name, lp_realm(), lp_realm());
409 if (err == -1) {
410 ret = ENOMEM;
411 goto out;
414 ret = smb_krb5_kt_open_relative(krbctx, NULL, false, &keytab);
415 if (ret) {
416 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
417 error_message(ret)));
418 goto out;
422 * Iterate through the keytab. For each key, if the principal
423 * name case-insensitively matches one of the allowed formats,
424 * copy it to the memory keytab.
427 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
428 if (ret) {
429 DEBUG(1, (__location__ ": krb5_kt_start_seq_get failed (%s)\n",
430 error_message(ret)));
432 * krb5_kt_start_seq_get() may leaves bogus data
433 * in kt_cursor. And we want to use the all_zero()
434 * logic below.
436 * See bug #10490
438 ZERO_STRUCT(kt_cursor);
439 goto out;
442 while ((krb5_kt_next_entry(krbctx, keytab,
443 &kt_entry, &kt_cursor) == 0)) {
444 ret = smb_krb5_unparse_name(talloc_tos(), krbctx,
445 kt_entry.principal,
446 &entry_princ_s);
447 if (ret) {
448 DEBUG(1, (__location__ ": smb_krb5_unparse_name "
449 "failed (%s)\n", error_message(ret)));
450 goto out;
453 for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) {
455 if (!strequal(entry_princ_s, valid_princ_formats[i])) {
456 continue;
459 ret = krb5_kt_add_entry(krbctx, *mkeytab, &kt_entry);
460 if (ret) {
461 DBG_WARNING("krb5_kt_add_entry failed (%s)\n",
462 error_message(ret));
463 goto out;
467 /* Free the name we parsed. */
468 TALLOC_FREE(entry_princ_s);
470 /* Free the entry we just read. */
471 smb_krb5_kt_free_entry(krbctx, &kt_entry);
472 ZERO_STRUCT(kt_entry);
474 krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
476 ZERO_STRUCT(kt_cursor);
478 out:
480 for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) {
481 SAFE_FREE(valid_princ_formats[i]);
484 TALLOC_FREE(entry_princ_s);
486 if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
487 smb_krb5_kt_free_entry(krbctx, &kt_entry);
490 if (!all_zero((uint8_t *)&kt_cursor, sizeof(kt_cursor)) && keytab) {
491 krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
494 if (keytab) {
495 krb5_kt_close(krbctx, keytab);
498 return ret;
501 static krb5_error_code fill_mem_keytab_from_dedicated_keytab(krb5_context krbctx,
502 krb5_keytab *mkeytab)
504 krb5_error_code ret = 0;
505 krb5_keytab keytab = NULL;
506 krb5_kt_cursor kt_cursor;
507 krb5_keytab_entry kt_entry;
509 ret = smb_krb5_kt_open(krbctx, lp_dedicated_keytab_file(),
510 false, &keytab);
511 if (ret) {
512 DEBUG(1, ("smb_krb5_kt_open of %s failed (%s)\n",
513 lp_dedicated_keytab_file(),
514 error_message(ret)));
515 return ret;
519 * Copy the dedicated keyab to our in-memory keytab.
522 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
523 if (ret) {
524 DEBUG(1, (__location__ ": krb5_kt_start_seq_get on %s "
525 "failed (%s)\n",
526 lp_dedicated_keytab_file(),
527 error_message(ret)));
528 goto out;
531 while ((krb5_kt_next_entry(krbctx, keytab,
532 &kt_entry, &kt_cursor) == 0)) {
534 ret = krb5_kt_add_entry(krbctx, *mkeytab, &kt_entry);
536 /* Free the entry we just read. */
537 smb_krb5_kt_free_entry(krbctx, &kt_entry);
539 if (ret) {
540 DEBUG(1, (__location__ ": smb_krb5_unparse_name "
541 "failed (%s)\n", error_message(ret)));
542 break;
545 krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
547 out:
549 krb5_kt_close(krbctx, keytab);
551 return ret;
554 krb5_error_code gse_krb5_get_server_keytab(krb5_context krbctx,
555 krb5_keytab *keytab)
557 krb5_error_code ret = 0;
558 krb5_error_code ret1 = 0;
559 krb5_error_code ret2 = 0;
561 *keytab = NULL;
563 /* create memory keytab */
564 ret = krb5_kt_resolve(krbctx, SRV_MEM_KEYTAB_NAME, keytab);
565 if (ret) {
566 DEBUG(1, (__location__ ": Failed to get memory "
567 "keytab!\n"));
568 return ret;
571 switch (lp_kerberos_method()) {
572 default:
573 case KERBEROS_VERIFY_SECRETS:
574 ret = fill_mem_keytab_from_secrets(krbctx, keytab);
575 break;
576 case KERBEROS_VERIFY_SYSTEM_KEYTAB:
577 ret = fill_mem_keytab_from_system_keytab(krbctx, keytab);
578 break;
579 case KERBEROS_VERIFY_DEDICATED_KEYTAB:
580 /* just use whatever keytab is configured */
581 ret = fill_mem_keytab_from_dedicated_keytab(krbctx, keytab);
582 break;
583 case KERBEROS_VERIFY_SECRETS_AND_KEYTAB:
584 ret1 = fill_mem_keytab_from_secrets(krbctx, keytab);
585 if (ret1) {
586 DEBUG(3, (__location__ ": Warning! Unable to set mem "
587 "keytab from secrets!\n"));
589 /* Now append system keytab keys too */
590 ret2 = fill_mem_keytab_from_system_keytab(krbctx, keytab);
591 if (ret2) {
592 DEBUG(3, (__location__ ": Warning! Unable to set mem "
593 "keytab from system keytab!\n"));
595 if (ret1 == 0 || ret2 == 0) {
596 ret = 0;
597 } else {
598 ret = ret1;
600 break;
603 if (ret) {
604 krb5_kt_close(krbctx, *keytab);
605 *keytab = NULL;
606 DEBUG(1,("%s: Error! Unable to set mem keytab - %d\n",
607 __location__, ret));
610 return ret;
613 #endif /* HAVE_KRB5 */