krb5_wrap: Rename smb_get_enctype_from_kt_entry()
[Samba.git] / source3 / librpc / crypto / gse_krb5.c
blob999a1d18c47e520669134470385b19f916e8d573
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"
25 #include "libads/kerberos_proto.h"
27 #ifdef HAVE_KRB5
29 static krb5_error_code flush_keytab(krb5_context krbctx, krb5_keytab keytab)
31 krb5_error_code ret;
32 krb5_kt_cursor kt_cursor;
33 krb5_keytab_entry kt_entry;
35 ZERO_STRUCT(kt_entry);
37 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
38 if (ret == KRB5_KT_END || ret == ENOENT ) {
39 /* no entries */
40 return 0;
43 ret = krb5_kt_next_entry(krbctx, keytab, &kt_entry, &kt_cursor);
44 while (ret == 0) {
46 /* we need to close and reopen enumeration because we modify
47 * the keytab */
48 ret = krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
49 if (ret) {
50 DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
51 "failed (%s)\n", error_message(ret)));
52 goto out;
55 /* remove the entry */
56 ret = krb5_kt_remove_entry(krbctx, keytab, &kt_entry);
57 if (ret) {
58 DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
59 "failed (%s)\n", error_message(ret)));
60 goto out;
62 ret = smb_krb5_kt_free_entry(krbctx, &kt_entry);
63 ZERO_STRUCT(kt_entry);
65 /* now reopen */
66 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
67 if (ret) {
68 DEBUG(1, (__location__ ": krb5_kt_start_seq() failed "
69 "(%s)\n", error_message(ret)));
70 goto out;
73 ret = krb5_kt_next_entry(krbctx, keytab,
74 &kt_entry, &kt_cursor);
77 if (ret != KRB5_KT_END && ret != ENOENT) {
78 DEBUG(1, (__location__ ": flushing keytab we got [%s]!\n",
79 error_message(ret)));
82 ret = 0;
84 out:
85 return ret;
88 static krb5_error_code get_host_principal(krb5_context krbctx,
89 krb5_principal *host_princ)
91 krb5_error_code ret;
92 char *host_princ_s = NULL;
93 int err;
95 err = asprintf(&host_princ_s, "%s$@%s", lp_netbios_name(), lp_realm());
96 if (err == -1) {
97 return -1;
100 if (!strlower_m(host_princ_s)) {
101 SAFE_FREE(host_princ_s);
102 return -1;
104 ret = smb_krb5_parse_name(krbctx, host_princ_s, host_princ);
105 if (ret) {
106 DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
107 "failed (%s)\n",
108 host_princ_s, error_message(ret)));
111 SAFE_FREE(host_princ_s);
112 return ret;
115 static krb5_error_code fill_keytab_from_password(krb5_context krbctx,
116 krb5_keytab keytab,
117 krb5_principal princ,
118 krb5_kvno vno,
119 krb5_data *password)
121 krb5_error_code ret;
122 krb5_enctype *enctypes;
123 krb5_keytab_entry kt_entry;
124 unsigned int i;
126 ret = smb_krb5_get_allowed_etypes(krbctx, &enctypes);
127 if (ret) {
128 DEBUG(1, (__location__
129 ": Can't determine permitted enctypes!\n"));
130 return ret;
133 for (i = 0; enctypes[i]; i++) {
134 krb5_keyblock *key = NULL;
135 krb5_principal salt_princ = NULL;
136 char *salt_princ_s;
137 char *princ_s;
138 int rc;
140 if (!(key = SMB_MALLOC_P(krb5_keyblock))) {
141 ret = ENOMEM;
142 goto out;
145 ret = krb5_unparse_name(krbctx, princ, &princ_s);
146 if (ret != 0) {
147 SAFE_FREE(key);
148 continue;
151 salt_princ_s = kerberos_fetch_salt_princ_for_host_princ(krbctx,
152 princ_s,
153 enctypes[i]);
154 SAFE_FREE(princ_s);
155 if (salt_princ_s == NULL) {
156 SAFE_FREE(key);
157 continue;
160 ret = krb5_parse_name(krbctx, salt_princ_s, &salt_princ);
161 SAFE_FREE(salt_princ_s);
162 if (ret != 0) {
163 SAFE_FREE(key);
164 continue;
167 rc = create_kerberos_key_from_string(krbctx,
168 princ,
169 salt_princ,
170 password,
171 key,
172 enctypes[i],
173 false);
174 krb5_free_principal(krbctx, salt_princ);
175 if (rc != 0) {
176 DEBUG(10, ("Failed to create key for enctype %d "
177 "(error: %s)\n",
178 enctypes[i], error_message(ret)));
179 SAFE_FREE(key);
180 continue;
183 kt_entry.principal = princ;
184 kt_entry.vno = vno;
185 *(KRB5_KT_KEY(&kt_entry)) = *key;
187 ret = krb5_kt_add_entry(krbctx, keytab, &kt_entry);
188 if (ret) {
189 DEBUG(1, (__location__ ": Failed to add entry to "
190 "keytab for enctype %d (error: %s)\n",
191 enctypes[i], error_message(ret)));
192 krb5_free_keyblock(krbctx, key);
193 goto out;
196 krb5_free_keyblock(krbctx, key);
199 ret = 0;
201 out:
202 SAFE_FREE(enctypes);
203 return ret;
206 #define SRV_MEM_KEYTAB_NAME "MEMORY:cifs_srv_keytab"
207 #define CLEARTEXT_PRIV_ENCTYPE -99
209 static krb5_error_code fill_mem_keytab_from_secrets(krb5_context krbctx,
210 krb5_keytab *keytab)
212 krb5_error_code ret;
213 char *pwd = NULL;
214 size_t pwd_len;
215 krb5_kt_cursor kt_cursor;
216 krb5_keytab_entry kt_entry;
217 krb5_data password;
218 krb5_principal princ = NULL;
219 krb5_kvno kvno = 0; /* FIXME: fetch current vno from KDC ? */
220 char *pwd_old = NULL;
222 if (!secrets_init()) {
223 DEBUG(1, (__location__ ": secrets_init failed\n"));
224 return KRB5_CONFIG_CANTOPEN;
227 pwd = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
228 if (!pwd) {
229 DEBUG(2, (__location__ ": failed to fetch machine password\n"));
230 return KRB5_LIBOS_CANTREADPWD;
232 pwd_len = strlen(pwd);
234 ZERO_STRUCT(kt_entry);
235 ZERO_STRUCT(kt_cursor);
237 /* check if the keytab already has any entry */
238 ret = krb5_kt_start_seq_get(krbctx, *keytab, &kt_cursor);
239 if (ret != KRB5_KT_END && ret != ENOENT ) {
240 /* check if we have our special enctype used to hold
241 * the clear text password. If so, check it out so that
242 * we can verify if the keytab needs to be upgraded */
243 while ((ret = krb5_kt_next_entry(krbctx, *keytab,
244 &kt_entry, &kt_cursor)) == 0) {
245 if (smb_krb5_kt_get_enctype_from_entry(&kt_entry) ==
246 CLEARTEXT_PRIV_ENCTYPE) {
247 break;
249 smb_krb5_kt_free_entry(krbctx, &kt_entry);
250 ZERO_STRUCT(kt_entry);
253 if (ret != 0 && ret != KRB5_KT_END && ret != ENOENT ) {
254 /* Error parsing keytab */
255 DEBUG(1, (__location__ ": Failed to parse memory "
256 "keytab!\n"));
257 goto out;
260 if (ret == 0) {
261 /* found private entry,
262 * check if keytab is up to date */
264 if ((pwd_len == KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry))) &&
265 (memcmp(KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)),
266 pwd, pwd_len) == 0)) {
267 /* keytab is already up to date, return */
268 smb_krb5_kt_free_entry(krbctx, &kt_entry);
269 goto out;
272 smb_krb5_kt_free_entry(krbctx, &kt_entry);
273 ZERO_STRUCT(kt_entry);
276 /* flush keytab, we need to regen it */
277 ret = flush_keytab(krbctx, *keytab);
278 if (ret) {
279 DEBUG(1, (__location__ ": Failed to flush "
280 "memory keytab!\n"));
281 goto out;
287 krb5_kt_cursor zero_csr;
288 ZERO_STRUCT(zero_csr);
289 if ((memcmp(&kt_cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && *keytab) {
290 krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
294 /* keytab is not up to date, fill it up */
296 ret = get_host_principal(krbctx, &princ);
297 if (ret) {
298 DEBUG(1, (__location__ ": Failed to get host principal!\n"));
299 goto out;
302 password.data = pwd;
303 password.length = pwd_len;
304 ret = fill_keytab_from_password(krbctx, *keytab,
305 princ, kvno, &password);
306 if (ret) {
307 DEBUG(1, (__location__ ": Failed to fill memory keytab!\n"));
308 goto out;
311 pwd_old = secrets_fetch_prev_machine_password(lp_workgroup());
312 if (!pwd_old) {
313 DEBUG(10, (__location__ ": no prev machine password\n"));
314 } else {
315 password.data = pwd_old;
316 password.length = strlen(pwd_old);
317 ret = fill_keytab_from_password(krbctx, *keytab,
318 princ, kvno -1, &password);
319 if (ret) {
320 DEBUG(1, (__location__
321 ": Failed to fill memory keytab!\n"));
322 goto out;
326 /* add our private enctype + cleartext password so that we can
327 * update the keytab if secrets change later on */
328 ZERO_STRUCT(kt_entry);
329 kt_entry.principal = princ;
330 kt_entry.vno = 0;
332 KRB5_KEY_TYPE(KRB5_KT_KEY(&kt_entry)) = CLEARTEXT_PRIV_ENCTYPE;
333 KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry)) = pwd_len;
334 KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)) = (uint8_t *)pwd;
336 ret = krb5_kt_add_entry(krbctx, *keytab, &kt_entry);
337 if (ret) {
338 DEBUG(1, (__location__ ": Failed to add entry to "
339 "keytab for private enctype (%d) (error: %s)\n",
340 CLEARTEXT_PRIV_ENCTYPE, error_message(ret)));
341 goto out;
344 ret = 0;
346 out:
347 SAFE_FREE(pwd);
348 SAFE_FREE(pwd_old);
351 krb5_kt_cursor zero_csr;
352 ZERO_STRUCT(zero_csr);
353 if ((memcmp(&kt_cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && *keytab) {
354 krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
358 if (princ) {
359 krb5_free_principal(krbctx, princ);
362 return ret;
365 static krb5_error_code fill_mem_keytab_from_system_keytab(krb5_context krbctx,
366 krb5_keytab *mkeytab)
368 krb5_error_code ret = 0;
369 krb5_keytab keytab = NULL;
370 krb5_kt_cursor kt_cursor;
371 krb5_keytab_entry kt_entry;
372 char *valid_princ_formats[7] = { NULL, NULL, NULL,
373 NULL, NULL, NULL, NULL };
374 char *entry_princ_s = NULL;
375 fstring my_name, my_fqdn;
376 int i;
377 int err;
379 /* Generate the list of principal names which we expect
380 * clients might want to use for authenticating to the file
381 * service. We allow name$,{host,cifs}/{name,fqdn,name.REALM}. */
383 fstrcpy(my_name, lp_netbios_name());
385 my_fqdn[0] = '\0';
386 name_to_fqdn(my_fqdn, lp_netbios_name());
388 err = asprintf(&valid_princ_formats[0],
389 "%s$@%s", my_name, lp_realm());
390 if (err == -1) {
391 ret = ENOMEM;
392 goto out;
394 err = asprintf(&valid_princ_formats[1],
395 "host/%s@%s", my_name, lp_realm());
396 if (err == -1) {
397 ret = ENOMEM;
398 goto out;
400 err = asprintf(&valid_princ_formats[2],
401 "host/%s@%s", my_fqdn, lp_realm());
402 if (err == -1) {
403 ret = ENOMEM;
404 goto out;
406 err = asprintf(&valid_princ_formats[3],
407 "host/%s.%s@%s", my_name, lp_realm(), lp_realm());
408 if (err == -1) {
409 ret = ENOMEM;
410 goto out;
412 err = asprintf(&valid_princ_formats[4],
413 "cifs/%s@%s", my_name, lp_realm());
414 if (err == -1) {
415 ret = ENOMEM;
416 goto out;
418 err = asprintf(&valid_princ_formats[5],
419 "cifs/%s@%s", my_fqdn, lp_realm());
420 if (err == -1) {
421 ret = ENOMEM;
422 goto out;
424 err = asprintf(&valid_princ_formats[6],
425 "cifs/%s.%s@%s", my_name, lp_realm(), lp_realm());
426 if (err == -1) {
427 ret = ENOMEM;
428 goto out;
431 ZERO_STRUCT(kt_entry);
432 ZERO_STRUCT(kt_cursor);
434 ret = smb_krb5_open_keytab(krbctx, NULL, false, &keytab);
435 if (ret) {
436 DEBUG(1, (__location__ ": smb_krb5_open_keytab failed (%s)\n",
437 error_message(ret)));
438 goto out;
442 * Iterate through the keytab. For each key, if the principal
443 * name case-insensitively matches one of the allowed formats,
444 * copy it to the memory keytab.
447 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
448 if (ret) {
449 DEBUG(1, (__location__ ": krb5_kt_start_seq_get failed (%s)\n",
450 error_message(ret)));
451 goto out;
454 while ((krb5_kt_next_entry(krbctx, keytab,
455 &kt_entry, &kt_cursor) == 0)) {
456 ret = smb_krb5_unparse_name(talloc_tos(), krbctx,
457 kt_entry.principal,
458 &entry_princ_s);
459 if (ret) {
460 DEBUG(1, (__location__ ": smb_krb5_unparse_name "
461 "failed (%s)\n", error_message(ret)));
462 goto out;
465 for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) {
467 if (!strequal(entry_princ_s, valid_princ_formats[i])) {
468 continue;
471 ret = krb5_kt_add_entry(krbctx, *mkeytab, &kt_entry);
472 if (ret) {
473 DEBUG(1, (__location__ ": smb_krb5_unparse_name "
474 "failed (%s)\n", error_message(ret)));
475 goto out;
479 /* Free the name we parsed. */
480 TALLOC_FREE(entry_princ_s);
482 /* Free the entry we just read. */
483 smb_krb5_kt_free_entry(krbctx, &kt_entry);
484 ZERO_STRUCT(kt_entry);
486 krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
488 ZERO_STRUCT(kt_cursor);
490 out:
492 for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) {
493 SAFE_FREE(valid_princ_formats[i]);
496 TALLOC_FREE(entry_princ_s);
499 krb5_keytab_entry zero_kt_entry;
500 ZERO_STRUCT(zero_kt_entry);
501 if (memcmp(&zero_kt_entry, &kt_entry,
502 sizeof(krb5_keytab_entry))) {
503 smb_krb5_kt_free_entry(krbctx, &kt_entry);
508 krb5_kt_cursor zero_csr;
509 ZERO_STRUCT(zero_csr);
510 if ((memcmp(&kt_cursor, &zero_csr,
511 sizeof(krb5_kt_cursor)) != 0) && keytab) {
512 krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
516 if (keytab) {
517 krb5_kt_close(krbctx, keytab);
520 return ret;
523 static krb5_error_code fill_mem_keytab_from_dedicated_keytab(krb5_context krbctx,
524 krb5_keytab *mkeytab)
526 krb5_error_code ret = 0;
527 krb5_keytab keytab = NULL;
528 krb5_kt_cursor kt_cursor;
529 krb5_keytab_entry kt_entry;
531 ret = smb_krb5_open_keytab(krbctx, lp_dedicated_keytab_file(),
532 false, &keytab);
533 if (ret) {
534 DEBUG(1, (__location__ ": smb_krb5_open_keytab failed (%s)\n",
535 error_message(ret)));
536 return ret;
540 * Iterate through the keytab. For each key, if the principal
541 * name case-insensitively matches one of the allowed formats,
542 * copy it to the memory keytab.
545 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
546 if (ret) {
547 DEBUG(1, (__location__ ": krb5_kt_start_seq_get failed (%s)\n",
548 error_message(ret)));
549 goto out;
552 while ((krb5_kt_next_entry(krbctx, keytab,
553 &kt_entry, &kt_cursor) == 0)) {
555 ret = krb5_kt_add_entry(krbctx, *mkeytab, &kt_entry);
557 /* Free the entry we just read. */
558 smb_krb5_kt_free_entry(krbctx, &kt_entry);
560 if (ret) {
561 DEBUG(1, (__location__ ": smb_krb5_unparse_name "
562 "failed (%s)\n", error_message(ret)));
563 break;
566 krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
568 out:
570 krb5_kt_close(krbctx, keytab);
572 return ret;
575 krb5_error_code gse_krb5_get_server_keytab(krb5_context krbctx,
576 krb5_keytab *keytab)
578 krb5_error_code ret = 0;
579 krb5_error_code ret1 = 0;
580 krb5_error_code ret2 = 0;
582 *keytab = NULL;
584 /* create memory keytab */
585 ret = krb5_kt_resolve(krbctx, SRV_MEM_KEYTAB_NAME, keytab);
586 if (ret) {
587 DEBUG(1, (__location__ ": Failed to get memory "
588 "keytab!\n"));
589 return ret;
592 switch (lp_kerberos_method()) {
593 default:
594 case KERBEROS_VERIFY_SECRETS:
595 ret = fill_mem_keytab_from_secrets(krbctx, keytab);
596 break;
597 case KERBEROS_VERIFY_SYSTEM_KEYTAB:
598 ret = fill_mem_keytab_from_system_keytab(krbctx, keytab);
599 break;
600 case KERBEROS_VERIFY_DEDICATED_KEYTAB:
601 /* just use whatever keytab is configured */
602 ret = fill_mem_keytab_from_dedicated_keytab(krbctx, keytab);
603 break;
604 case KERBEROS_VERIFY_SECRETS_AND_KEYTAB:
605 ret1 = fill_mem_keytab_from_secrets(krbctx, keytab);
606 if (ret1) {
607 DEBUG(3, (__location__ ": Warning! Unable to set mem "
608 "keytab from secrets!\n"));
610 /* Now append system keytab keys too */
611 ret2 = fill_mem_keytab_from_system_keytab(krbctx, keytab);
612 if (ret2) {
613 DEBUG(3, (__location__ ": Warning! Unable to set mem "
614 "keytab from system keytab!\n"));
616 if (ret1 == 0 || ret2 == 0) {
617 ret = 0;
618 } else {
619 ret = ret1;
621 break;
624 if (ret) {
625 krb5_kt_close(krbctx, *keytab);
626 *keytab = NULL;
627 DEBUG(1,("%s: Error! Unable to set mem keytab - %d\n",
628 __location__, ret));
631 return ret;
634 #endif /* HAVE_KRB5 */