smbd: Convert recursive_rmdir to synthetic_smb_fname
[Samba/bjacke.git] / source3 / librpc / crypto / gse_krb5.c
blob43f545ad4cd221dce4dff99ea6bf83e30aa7cbbc
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 "gse_krb5.h"
24 #include "lib/param/loadparm.h"
26 #ifdef HAVE_KRB5
28 static krb5_error_code flush_keytab(krb5_context krbctx, krb5_keytab keytab)
30 krb5_error_code ret;
31 krb5_kt_cursor kt_cursor;
32 krb5_keytab_entry kt_entry;
34 ZERO_STRUCT(kt_entry);
36 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
37 if (ret == KRB5_KT_END || ret == ENOENT ) {
38 /* no entries */
39 return 0;
42 ret = krb5_kt_next_entry(krbctx, keytab, &kt_entry, &kt_cursor);
43 while (ret == 0) {
45 /* we need to close and reopen enumeration because we modify
46 * the keytab */
47 ret = krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
48 if (ret) {
49 DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
50 "failed (%s)\n", error_message(ret)));
51 goto out;
54 /* remove the entry */
55 ret = krb5_kt_remove_entry(krbctx, keytab, &kt_entry);
56 if (ret) {
57 DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
58 "failed (%s)\n", error_message(ret)));
59 goto out;
61 ret = smb_krb5_kt_free_entry(krbctx, &kt_entry);
62 ZERO_STRUCT(kt_entry);
64 /* now reopen */
65 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
66 if (ret) {
67 DEBUG(1, (__location__ ": krb5_kt_start_seq() failed "
68 "(%s)\n", error_message(ret)));
69 goto out;
72 ret = krb5_kt_next_entry(krbctx, keytab,
73 &kt_entry, &kt_cursor);
76 if (ret != KRB5_KT_END && ret != ENOENT) {
77 DEBUG(1, (__location__ ": flushing keytab we got [%s]!\n",
78 error_message(ret)));
81 ret = 0;
83 out:
84 return ret;
87 static krb5_error_code get_host_principal(krb5_context krbctx,
88 krb5_principal *host_princ)
90 krb5_error_code ret;
91 char *host_princ_s = NULL;
92 int err;
94 err = asprintf(&host_princ_s, "%s$@%s", lp_netbios_name(), lp_realm());
95 if (err == -1) {
96 return -1;
99 if (!strlower_m(host_princ_s)) {
100 SAFE_FREE(host_princ_s);
101 return -1;
103 ret = smb_krb5_parse_name(krbctx, host_princ_s, host_princ);
104 if (ret) {
105 DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
106 "failed (%s)\n",
107 host_princ_s, error_message(ret)));
110 SAFE_FREE(host_princ_s);
111 return ret;
114 static krb5_error_code fill_keytab_from_password(krb5_context krbctx,
115 krb5_keytab keytab,
116 krb5_principal princ,
117 krb5_kvno vno,
118 krb5_data *password)
120 krb5_error_code ret;
121 krb5_enctype *enctypes;
122 krb5_keytab_entry kt_entry;
123 unsigned int i;
125 ret = get_kerberos_allowed_etypes(krbctx, &enctypes);
126 if (ret) {
127 DEBUG(1, (__location__
128 ": Can't determine permitted enctypes!\n"));
129 return ret;
132 for (i = 0; enctypes[i]; i++) {
133 krb5_keyblock *key = NULL;
135 if (!(key = SMB_MALLOC_P(krb5_keyblock))) {
136 ret = ENOMEM;
137 goto out;
140 if (create_kerberos_key_from_string(krbctx, princ,
141 password, key,
142 enctypes[i], false)) {
143 DEBUG(10, ("Failed to create key for enctype %d "
144 "(error: %s)\n",
145 enctypes[i], error_message(ret)));
146 SAFE_FREE(key);
147 continue;
150 kt_entry.principal = princ;
151 kt_entry.vno = vno;
152 *(KRB5_KT_KEY(&kt_entry)) = *key;
154 ret = krb5_kt_add_entry(krbctx, keytab, &kt_entry);
155 if (ret) {
156 DEBUG(1, (__location__ ": Failed to add entry to "
157 "keytab for enctype %d (error: %s)\n",
158 enctypes[i], error_message(ret)));
159 krb5_free_keyblock(krbctx, key);
160 goto out;
163 krb5_free_keyblock(krbctx, key);
166 ret = 0;
168 out:
169 SAFE_FREE(enctypes);
170 return ret;
173 #define SRV_MEM_KEYTAB_NAME "MEMORY:cifs_srv_keytab"
174 #define CLEARTEXT_PRIV_ENCTYPE -99
176 static krb5_error_code fill_mem_keytab_from_secrets(krb5_context krbctx,
177 krb5_keytab *keytab)
179 krb5_error_code ret;
180 char *pwd = NULL;
181 size_t pwd_len;
182 krb5_kt_cursor kt_cursor;
183 krb5_keytab_entry kt_entry;
184 krb5_data password;
185 krb5_principal princ = NULL;
186 krb5_kvno kvno = 0; /* FIXME: fetch current vno from KDC ? */
187 char *pwd_old = NULL;
189 if (!secrets_init()) {
190 DEBUG(1, (__location__ ": secrets_init failed\n"));
191 return KRB5_CONFIG_CANTOPEN;
194 pwd = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
195 if (!pwd) {
196 DEBUG(2, (__location__ ": failed to fetch machine password\n"));
197 return KRB5_LIBOS_CANTREADPWD;
199 pwd_len = strlen(pwd);
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 != KRB5_KT_END && ret != ENOENT ) {
207 /* check if we have our special enctype used to hold
208 * the clear text password. If so, check it out so that
209 * we can verify if the keytab needs to be upgraded */
210 while ((ret = krb5_kt_next_entry(krbctx, *keytab,
211 &kt_entry, &kt_cursor)) == 0) {
212 if (smb_get_enctype_from_kt_entry(&kt_entry) == CLEARTEXT_PRIV_ENCTYPE) {
213 break;
215 smb_krb5_kt_free_entry(krbctx, &kt_entry);
216 ZERO_STRUCT(kt_entry);
219 if (ret != 0 && ret != KRB5_KT_END && ret != ENOENT ) {
220 /* Error parsing keytab */
221 DEBUG(1, (__location__ ": Failed to parse memory "
222 "keytab!\n"));
223 goto out;
226 if (ret == 0) {
227 /* found private entry,
228 * check if keytab is up to date */
230 if ((pwd_len == KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry))) &&
231 (memcmp(KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)),
232 pwd, pwd_len) == 0)) {
233 /* keytab is already up to date, return */
234 smb_krb5_kt_free_entry(krbctx, &kt_entry);
235 goto out;
238 smb_krb5_kt_free_entry(krbctx, &kt_entry);
239 ZERO_STRUCT(kt_entry);
242 /* flush keytab, we need to regen it */
243 ret = flush_keytab(krbctx, *keytab);
244 if (ret) {
245 DEBUG(1, (__location__ ": Failed to flush "
246 "memory keytab!\n"));
247 goto out;
253 krb5_kt_cursor zero_csr;
254 ZERO_STRUCT(zero_csr);
255 if ((memcmp(&kt_cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && *keytab) {
256 krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
260 /* keytab is not up to date, fill it up */
262 ret = get_host_principal(krbctx, &princ);
263 if (ret) {
264 DEBUG(1, (__location__ ": Failed to get host principal!\n"));
265 goto out;
268 password.data = pwd;
269 password.length = pwd_len;
270 ret = fill_keytab_from_password(krbctx, *keytab,
271 princ, kvno, &password);
272 if (ret) {
273 DEBUG(1, (__location__ ": Failed to fill memory keytab!\n"));
274 goto out;
277 pwd_old = secrets_fetch_prev_machine_password(lp_workgroup());
278 if (!pwd_old) {
279 DEBUG(10, (__location__ ": no prev machine password\n"));
280 } else {
281 password.data = pwd_old;
282 password.length = strlen(pwd_old);
283 ret = fill_keytab_from_password(krbctx, *keytab,
284 princ, kvno -1, &password);
285 if (ret) {
286 DEBUG(1, (__location__
287 ": Failed to fill memory keytab!\n"));
288 goto out;
292 /* add our private enctype + cleartext password so that we can
293 * update the keytab if secrets change later on */
294 ZERO_STRUCT(kt_entry);
295 kt_entry.principal = princ;
296 kt_entry.vno = 0;
298 KRB5_KEY_TYPE(KRB5_KT_KEY(&kt_entry)) = CLEARTEXT_PRIV_ENCTYPE;
299 KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry)) = pwd_len;
300 KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)) = (uint8_t *)pwd;
302 ret = krb5_kt_add_entry(krbctx, *keytab, &kt_entry);
303 if (ret) {
304 DEBUG(1, (__location__ ": Failed to add entry to "
305 "keytab for private enctype (%d) (error: %s)\n",
306 CLEARTEXT_PRIV_ENCTYPE, error_message(ret)));
307 goto out;
310 ret = 0;
312 out:
313 SAFE_FREE(pwd);
314 SAFE_FREE(pwd_old);
317 krb5_kt_cursor zero_csr;
318 ZERO_STRUCT(zero_csr);
319 if ((memcmp(&kt_cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && *keytab) {
320 krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
324 if (princ) {
325 krb5_free_principal(krbctx, princ);
328 return ret;
331 static krb5_error_code fill_mem_keytab_from_system_keytab(krb5_context krbctx,
332 krb5_keytab *mkeytab)
334 krb5_error_code ret = 0;
335 krb5_keytab keytab = NULL;
336 krb5_kt_cursor kt_cursor;
337 krb5_keytab_entry kt_entry;
338 char *valid_princ_formats[7] = { NULL, NULL, NULL,
339 NULL, NULL, NULL, NULL };
340 char *entry_princ_s = NULL;
341 fstring my_name, my_fqdn;
342 int i;
343 int err;
345 /* Generate the list of principal names which we expect
346 * clients might want to use for authenticating to the file
347 * service. We allow name$,{host,cifs}/{name,fqdn,name.REALM}. */
349 fstrcpy(my_name, lp_netbios_name());
351 my_fqdn[0] = '\0';
352 name_to_fqdn(my_fqdn, lp_netbios_name());
354 err = asprintf(&valid_princ_formats[0],
355 "%s$@%s", my_name, lp_realm());
356 if (err == -1) {
357 ret = ENOMEM;
358 goto out;
360 err = asprintf(&valid_princ_formats[1],
361 "host/%s@%s", my_name, lp_realm());
362 if (err == -1) {
363 ret = ENOMEM;
364 goto out;
366 err = asprintf(&valid_princ_formats[2],
367 "host/%s@%s", my_fqdn, lp_realm());
368 if (err == -1) {
369 ret = ENOMEM;
370 goto out;
372 err = asprintf(&valid_princ_formats[3],
373 "host/%s.%s@%s", my_name, lp_realm(), lp_realm());
374 if (err == -1) {
375 ret = ENOMEM;
376 goto out;
378 err = asprintf(&valid_princ_formats[4],
379 "cifs/%s@%s", my_name, lp_realm());
380 if (err == -1) {
381 ret = ENOMEM;
382 goto out;
384 err = asprintf(&valid_princ_formats[5],
385 "cifs/%s@%s", my_fqdn, lp_realm());
386 if (err == -1) {
387 ret = ENOMEM;
388 goto out;
390 err = asprintf(&valid_princ_formats[6],
391 "cifs/%s.%s@%s", my_name, lp_realm(), lp_realm());
392 if (err == -1) {
393 ret = ENOMEM;
394 goto out;
397 ZERO_STRUCT(kt_entry);
398 ZERO_STRUCT(kt_cursor);
400 ret = smb_krb5_open_keytab(krbctx, NULL, false, &keytab);
401 if (ret) {
402 DEBUG(1, (__location__ ": smb_krb5_open_keytab failed (%s)\n",
403 error_message(ret)));
404 goto out;
408 * Iterate through the keytab. For each key, if the principal
409 * name case-insensitively matches one of the allowed formats,
410 * copy it to the memory keytab.
413 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
414 if (ret) {
415 DEBUG(1, (__location__ ": krb5_kt_start_seq_get failed (%s)\n",
416 error_message(ret)));
417 goto out;
420 while ((krb5_kt_next_entry(krbctx, keytab,
421 &kt_entry, &kt_cursor) == 0)) {
422 ret = smb_krb5_unparse_name(talloc_tos(), krbctx,
423 kt_entry.principal,
424 &entry_princ_s);
425 if (ret) {
426 DEBUG(1, (__location__ ": smb_krb5_unparse_name "
427 "failed (%s)\n", error_message(ret)));
428 goto out;
431 for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) {
433 if (!strequal(entry_princ_s, valid_princ_formats[i])) {
434 continue;
437 ret = krb5_kt_add_entry(krbctx, *mkeytab, &kt_entry);
438 if (ret) {
439 DEBUG(1, (__location__ ": smb_krb5_unparse_name "
440 "failed (%s)\n", error_message(ret)));
441 goto out;
445 /* Free the name we parsed. */
446 TALLOC_FREE(entry_princ_s);
448 /* Free the entry we just read. */
449 smb_krb5_kt_free_entry(krbctx, &kt_entry);
450 ZERO_STRUCT(kt_entry);
452 krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
454 ZERO_STRUCT(kt_cursor);
456 out:
458 for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) {
459 SAFE_FREE(valid_princ_formats[i]);
462 TALLOC_FREE(entry_princ_s);
465 krb5_keytab_entry zero_kt_entry;
466 ZERO_STRUCT(zero_kt_entry);
467 if (memcmp(&zero_kt_entry, &kt_entry,
468 sizeof(krb5_keytab_entry))) {
469 smb_krb5_kt_free_entry(krbctx, &kt_entry);
474 krb5_kt_cursor zero_csr;
475 ZERO_STRUCT(zero_csr);
476 if ((memcmp(&kt_cursor, &zero_csr,
477 sizeof(krb5_kt_cursor)) != 0) && keytab) {
478 krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
482 if (keytab) {
483 krb5_kt_close(krbctx, keytab);
486 return ret;
489 static krb5_error_code fill_mem_keytab_from_dedicated_keytab(krb5_context krbctx,
490 krb5_keytab *mkeytab)
492 krb5_error_code ret = 0;
493 krb5_keytab keytab = NULL;
494 krb5_kt_cursor kt_cursor;
495 krb5_keytab_entry kt_entry;
497 ZERO_STRUCT(kt_entry);
498 ZERO_STRUCT(kt_cursor);
500 ret = smb_krb5_open_keytab(krbctx, lp_dedicated_keytab_file(),
501 false, &keytab);
502 if (ret) {
503 DEBUG(1, (__location__ ": smb_krb5_open_keytab failed (%s)\n",
504 error_message(ret)));
505 goto out;
509 * Iterate through the keytab. For each key, if the principal
510 * name case-insensitively matches one of the allowed formats,
511 * copy it to the memory keytab.
514 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
515 if (ret) {
516 DEBUG(1, (__location__ ": krb5_kt_start_seq_get failed (%s)\n",
517 error_message(ret)));
518 goto out;
521 while ((krb5_kt_next_entry(krbctx, keytab,
522 &kt_entry, &kt_cursor) == 0)) {
524 ret = krb5_kt_add_entry(krbctx, *mkeytab, &kt_entry);
525 if (ret) {
526 DEBUG(1, (__location__ ": smb_krb5_unparse_name "
527 "failed (%s)\n", error_message(ret)));
528 goto out;
531 /* Free the entry we just read. */
532 smb_krb5_kt_free_entry(krbctx, &kt_entry);
533 ZERO_STRUCT(kt_entry);
535 krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
537 ZERO_STRUCT(kt_cursor);
539 out:
542 krb5_keytab_entry zero_kt_entry;
543 ZERO_STRUCT(zero_kt_entry);
544 if (memcmp(&zero_kt_entry, &kt_entry,
545 sizeof(krb5_keytab_entry))) {
546 smb_krb5_kt_free_entry(krbctx, &kt_entry);
551 krb5_kt_cursor zero_csr;
552 ZERO_STRUCT(zero_csr);
553 if ((memcmp(&kt_cursor, &zero_csr,
554 sizeof(krb5_kt_cursor)) != 0) && keytab) {
555 krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
559 if (keytab) {
560 krb5_kt_close(krbctx, keytab);
563 return ret;
566 krb5_error_code gse_krb5_get_server_keytab(krb5_context krbctx,
567 krb5_keytab *keytab)
569 krb5_error_code ret = 0;
570 krb5_error_code ret1 = 0;
571 krb5_error_code ret2 = 0;
573 *keytab = NULL;
575 /* create memory keytab */
576 ret = krb5_kt_resolve(krbctx, SRV_MEM_KEYTAB_NAME, keytab);
577 if (ret) {
578 DEBUG(1, (__location__ ": Failed to get memory "
579 "keytab!\n"));
580 return ret;
583 switch (lp_kerberos_method()) {
584 default:
585 case KERBEROS_VERIFY_SECRETS:
586 ret = fill_mem_keytab_from_secrets(krbctx, keytab);
587 break;
588 case KERBEROS_VERIFY_SYSTEM_KEYTAB:
589 ret = fill_mem_keytab_from_system_keytab(krbctx, keytab);
590 break;
591 case KERBEROS_VERIFY_DEDICATED_KEYTAB:
592 /* just use whatever keytab is configured */
593 ret = fill_mem_keytab_from_dedicated_keytab(krbctx, keytab);
594 break;
595 case KERBEROS_VERIFY_SECRETS_AND_KEYTAB:
596 ret1 = fill_mem_keytab_from_secrets(krbctx, keytab);
597 if (ret1) {
598 DEBUG(3, (__location__ ": Warning! Unable to set mem "
599 "keytab from secrets!\n"));
601 /* Now append system keytab keys too */
602 ret2 = fill_mem_keytab_from_system_keytab(krbctx, keytab);
603 if (ret2) {
604 DEBUG(3, (__location__ ": Warning! Unable to set mem "
605 "keytab from system keytab!\n"));
607 if (ret1 == 0 || ret2 == 0) {
608 ret = 0;
609 } else {
610 ret = ret1;
612 break;
615 if (ret) {
616 krb5_kt_close(krbctx, *keytab);
617 *keytab = NULL;
618 DEBUG(1,("%s: Error! Unable to set mem keytab - %d\n",
619 __location__, ret));
622 return ret;
625 #endif /* HAVE_KRB5 */