Line data Source code
1 : /*
2 : * GSSAPI Security Extensions
3 : * Krb5 helpers
4 : * Copyright (C) Simo Sorce 2010.
5 : *
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.
10 : *
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.
15 : *
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/>.
18 : */
19 :
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"
28 :
29 : #ifdef HAVE_KRB5
30 :
31 0 : static krb5_error_code flush_keytab(krb5_context krbctx, krb5_keytab keytab)
32 : {
33 : krb5_error_code ret;
34 : krb5_kt_cursor kt_cursor;
35 : krb5_keytab_entry kt_entry;
36 :
37 0 : ZERO_STRUCT(kt_entry);
38 :
39 0 : ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
40 0 : if (ret == KRB5_KT_END || ret == ENOENT ) {
41 : /* no entries */
42 0 : return 0;
43 : }
44 :
45 0 : ret = krb5_kt_next_entry(krbctx, keytab, &kt_entry, &kt_cursor);
46 0 : while (ret == 0) {
47 :
48 : /* we need to close and reopen enumeration because we modify
49 : * the keytab */
50 0 : ret = krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
51 0 : if (ret) {
52 0 : DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
53 : "failed (%s)\n", error_message(ret)));
54 0 : goto out;
55 : }
56 :
57 : /* remove the entry */
58 0 : ret = krb5_kt_remove_entry(krbctx, keytab, &kt_entry);
59 0 : if (ret) {
60 0 : DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
61 : "failed (%s)\n", error_message(ret)));
62 0 : goto out;
63 : }
64 0 : smb_krb5_kt_free_entry(krbctx, &kt_entry);
65 0 : ZERO_STRUCT(kt_entry);
66 :
67 : /* now reopen */
68 0 : ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
69 0 : if (ret) {
70 0 : DEBUG(1, (__location__ ": krb5_kt_start_seq() failed "
71 : "(%s)\n", error_message(ret)));
72 0 : goto out;
73 : }
74 :
75 0 : ret = krb5_kt_next_entry(krbctx, keytab,
76 : &kt_entry, &kt_cursor);
77 : }
78 :
79 0 : if (ret != KRB5_KT_END && ret != ENOENT) {
80 0 : DEBUG(1, (__location__ ": flushing keytab we got [%s]!\n",
81 : error_message(ret)));
82 : }
83 :
84 0 : ret = 0;
85 :
86 0 : out:
87 0 : return ret;
88 : }
89 :
90 5321 : static krb5_error_code fill_keytab_from_password(krb5_context krbctx,
91 : krb5_keytab keytab,
92 : krb5_principal princ,
93 : krb5_kvno vno,
94 : struct secrets_domain_info1_password *pw)
95 : {
96 : krb5_error_code ret;
97 : krb5_enctype *enctypes;
98 : uint16_t i;
99 :
100 5321 : ret = smb_krb5_get_allowed_etypes(krbctx, &enctypes);
101 5321 : if (ret) {
102 0 : DEBUG(1, (__location__
103 : ": Can't determine permitted enctypes!\n"));
104 0 : return ret;
105 : }
106 :
107 21284 : for (i = 0; i < pw->num_keys; i++) {
108 : krb5_keytab_entry kt_entry;
109 15963 : krb5_keyblock *key = NULL;
110 : unsigned int ei;
111 15963 : bool found_etype = false;
112 :
113 64832 : for (ei=0; enctypes[ei] != 0; ei++) {
114 42712 : if ((uint32_t)enctypes[ei] != pw->keys[i].keytype) {
115 26749 : continue;
116 : }
117 :
118 15963 : found_etype = true;
119 15963 : break;
120 : }
121 :
122 15963 : if (!found_etype) {
123 0 : continue;
124 : }
125 :
126 15963 : ZERO_STRUCT(kt_entry);
127 15963 : kt_entry.principal = princ;
128 15963 : kt_entry.vno = vno;
129 :
130 15963 : key = KRB5_KT_KEY(&kt_entry);
131 15963 : KRB5_KEY_TYPE(key) = pw->keys[i].keytype;
132 15963 : KRB5_KEY_DATA(key) = pw->keys[i].value.data;
133 15963 : KRB5_KEY_LENGTH(key) = pw->keys[i].value.length;
134 :
135 15963 : ret = krb5_kt_add_entry(krbctx, keytab, &kt_entry);
136 15963 : if (ret) {
137 0 : DEBUG(1, (__location__ ": Failed to add entry to "
138 : "keytab for enctype %d (error: %s)\n",
139 : (unsigned)pw->keys[i].keytype,
140 : error_message(ret)));
141 0 : goto out;
142 : }
143 : }
144 :
145 5321 : ret = 0;
146 :
147 5321 : out:
148 5321 : SAFE_FREE(enctypes);
149 5321 : return ret;
150 : }
151 :
152 : #define SRV_MEM_KEYTAB_NAME "MEMORY:cifs_srv_keytab"
153 : #define CLEARTEXT_PRIV_ENCTYPE -99
154 :
155 2956 : static krb5_error_code fill_mem_keytab_from_secrets(krb5_context krbctx,
156 : krb5_keytab *keytab)
157 : {
158 2956 : TALLOC_CTX *frame = talloc_stackframe();
159 : krb5_error_code ret;
160 2956 : const char *domain = lp_workgroup();
161 2956 : struct secrets_domain_info1 *info = NULL;
162 2956 : const char *realm = NULL;
163 2956 : const DATA_BLOB *ct = NULL;
164 : krb5_kt_cursor kt_cursor;
165 : krb5_keytab_entry kt_entry;
166 2956 : krb5_principal princ = NULL;
167 2956 : krb5_kvno kvno = 0; /* FIXME: fetch current vno from KDC ? */
168 : NTSTATUS status;
169 :
170 2956 : if (!secrets_init()) {
171 0 : DEBUG(1, (__location__ ": secrets_init failed\n"));
172 0 : TALLOC_FREE(frame);
173 0 : return KRB5_CONFIG_CANTOPEN;
174 : }
175 :
176 2956 : status = secrets_fetch_or_upgrade_domain_info(domain,
177 : frame,
178 : &info);
179 2956 : if (!NT_STATUS_IS_OK(status)) {
180 4 : DBG_WARNING("secrets_fetch_or_upgrade_domain_info(%s) - %s\n",
181 : domain, nt_errstr(status));
182 4 : TALLOC_FREE(frame);
183 4 : return KRB5_LIBOS_CANTREADPWD;
184 : }
185 2952 : ct = &info->password->cleartext_blob;
186 :
187 2952 : if (info->domain_info.dns_domain.string != NULL) {
188 2952 : realm = strupper_talloc(frame,
189 2952 : info->domain_info.dns_domain.string);
190 2952 : if (realm == NULL) {
191 0 : TALLOC_FREE(frame);
192 0 : return ENOMEM;
193 : }
194 : }
195 :
196 2952 : ZERO_STRUCT(kt_entry);
197 2952 : ZERO_STRUCT(kt_cursor);
198 :
199 : /* check if the keytab already has any entry */
200 2952 : ret = krb5_kt_start_seq_get(krbctx, *keytab, &kt_cursor);
201 2952 : if (ret != KRB5_KT_END && ret != ENOENT ) {
202 : /* check if we have our special enctype used to hold
203 : * the clear text password. If so, check it out so that
204 : * we can verify if the keytab needs to be upgraded */
205 4496 : while ((ret = krb5_kt_next_entry(krbctx, *keytab,
206 1432 : &kt_entry, &kt_cursor)) == 0) {
207 30 : if (smb_krb5_kt_get_enctype_from_entry(&kt_entry) ==
208 : CLEARTEXT_PRIV_ENCTYPE) {
209 6 : break;
210 : }
211 24 : smb_krb5_kt_free_entry(krbctx, &kt_entry);
212 24 : ZERO_STRUCT(kt_entry);
213 : }
214 :
215 2952 : if (ret != 0 && ret != KRB5_KT_END && ret != ENOENT ) {
216 : /* Error parsing keytab */
217 0 : DEBUG(1, (__location__ ": Failed to parse memory "
218 : "keytab!\n"));
219 0 : goto out;
220 : }
221 :
222 2952 : if (ret == 0) {
223 : /* found private entry,
224 : * check if keytab is up to date */
225 :
226 10 : if ((ct->length == KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry))) &&
227 10 : (memcmp(KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)),
228 6 : ct->data, ct->length) == 0)) {
229 : /* keytab is already up to date, return */
230 6 : smb_krb5_kt_free_entry(krbctx, &kt_entry);
231 6 : goto out;
232 : }
233 :
234 0 : smb_krb5_kt_free_entry(krbctx, &kt_entry);
235 0 : ZERO_STRUCT(kt_entry);
236 :
237 :
238 : /* flush keytab, we need to regen it */
239 0 : ret = flush_keytab(krbctx, *keytab);
240 0 : if (ret) {
241 0 : DEBUG(1, (__location__ ": Failed to flush "
242 : "memory keytab!\n"));
243 0 : goto out;
244 : }
245 : }
246 : }
247 :
248 2946 : if (!all_zero((uint8_t *)&kt_cursor, sizeof(kt_cursor)) && *keytab) {
249 0 : krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
250 : }
251 :
252 : /* keytab is not up to date, fill it up */
253 :
254 2946 : ret = smb_krb5_make_principal(krbctx, &princ, realm,
255 2946 : info->account_name, NULL);
256 2946 : if (ret) {
257 0 : DEBUG(1, (__location__ ": Failed to get host principal!\n"));
258 0 : goto out;
259 : }
260 :
261 2946 : ret = fill_keytab_from_password(krbctx, *keytab,
262 : princ, kvno,
263 2946 : info->password);
264 2946 : if (ret) {
265 0 : DBG_WARNING("fill_keytab_from_password() failed for "
266 : "info->password.\n.");
267 0 : goto out;
268 : }
269 :
270 2946 : if (info->old_password != NULL) {
271 1303 : ret = fill_keytab_from_password(krbctx, *keytab,
272 : princ, kvno - 1,
273 1303 : info->old_password);
274 1303 : if (ret) {
275 0 : DBG_WARNING("fill_keytab_from_password() failed for "
276 : "info->old_password.\n.");
277 0 : goto out;
278 : }
279 : }
280 :
281 2946 : if (info->older_password != NULL) {
282 1072 : ret = fill_keytab_from_password(krbctx, *keytab,
283 : princ, kvno - 2,
284 1072 : info->older_password);
285 1072 : if (ret) {
286 0 : DBG_WARNING("fill_keytab_from_password() failed for "
287 : "info->older_password.\n.");
288 0 : goto out;
289 : }
290 : }
291 :
292 2946 : if (info->next_change != NULL) {
293 0 : ret = fill_keytab_from_password(krbctx, *keytab,
294 : princ, kvno - 3,
295 0 : info->next_change->password);
296 0 : if (ret) {
297 0 : DBG_WARNING("fill_keytab_from_password() failed for "
298 : "info->next_change->password.\n.");
299 0 : goto out;
300 : }
301 : }
302 :
303 : /* add our private enctype + cleartext password so that we can
304 : * update the keytab if secrets change later on */
305 2946 : ZERO_STRUCT(kt_entry);
306 2946 : kt_entry.principal = princ;
307 2946 : kt_entry.vno = 0;
308 :
309 2946 : KRB5_KEY_TYPE(KRB5_KT_KEY(&kt_entry)) = CLEARTEXT_PRIV_ENCTYPE;
310 2946 : KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry)) = ct->length;
311 2946 : KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)) = ct->data;
312 :
313 2946 : ret = krb5_kt_add_entry(krbctx, *keytab, &kt_entry);
314 2946 : if (ret) {
315 0 : DEBUG(1, (__location__ ": Failed to add entry to "
316 : "keytab for private enctype (%d) (error: %s)\n",
317 : CLEARTEXT_PRIV_ENCTYPE, error_message(ret)));
318 0 : goto out;
319 : }
320 :
321 2946 : ret = 0;
322 :
323 2952 : out:
324 2952 : if (!all_zero((uint8_t *)&kt_cursor, sizeof(kt_cursor)) && *keytab) {
325 6 : krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
326 : }
327 :
328 2952 : if (princ) {
329 2946 : krb5_free_principal(krbctx, princ);
330 : }
331 :
332 2952 : TALLOC_FREE(frame);
333 2952 : return ret;
334 : }
335 :
336 0 : static krb5_error_code fill_mem_keytab_from_system_keytab(krb5_context krbctx,
337 : krb5_keytab *mkeytab)
338 : {
339 0 : krb5_error_code ret = 0;
340 0 : krb5_keytab keytab = NULL;
341 0 : krb5_kt_cursor kt_cursor = { 0, };
342 0 : krb5_keytab_entry kt_entry = { 0, };
343 0 : char *valid_princ_formats[7] = { NULL, NULL, NULL,
344 : NULL, NULL, NULL, NULL };
345 0 : char *entry_princ_s = NULL;
346 : fstring my_name, my_fqdn;
347 : unsigned i;
348 : int err;
349 :
350 : /* Generate the list of principal names which we expect
351 : * clients might want to use for authenticating to the file
352 : * service. We allow name$,{host,cifs}/{name,fqdn,name.REALM}. */
353 :
354 0 : fstrcpy(my_name, lp_netbios_name());
355 :
356 0 : my_fqdn[0] = '\0';
357 0 : name_to_fqdn(my_fqdn, lp_netbios_name());
358 :
359 0 : err = asprintf(&valid_princ_formats[0],
360 : "%s$@%s", my_name, lp_realm());
361 0 : if (err == -1) {
362 0 : ret = ENOMEM;
363 0 : goto out;
364 : }
365 0 : err = asprintf(&valid_princ_formats[1],
366 : "host/%s@%s", my_name, lp_realm());
367 0 : if (err == -1) {
368 0 : ret = ENOMEM;
369 0 : goto out;
370 : }
371 0 : err = asprintf(&valid_princ_formats[2],
372 : "host/%s@%s", my_fqdn, lp_realm());
373 0 : if (err == -1) {
374 0 : ret = ENOMEM;
375 0 : goto out;
376 : }
377 0 : err = asprintf(&valid_princ_formats[3],
378 : "host/%s.%s@%s", my_name, lp_realm(), lp_realm());
379 0 : if (err == -1) {
380 0 : ret = ENOMEM;
381 0 : goto out;
382 : }
383 0 : err = asprintf(&valid_princ_formats[4],
384 : "cifs/%s@%s", my_name, lp_realm());
385 0 : if (err == -1) {
386 0 : ret = ENOMEM;
387 0 : goto out;
388 : }
389 0 : err = asprintf(&valid_princ_formats[5],
390 : "cifs/%s@%s", my_fqdn, lp_realm());
391 0 : if (err == -1) {
392 0 : ret = ENOMEM;
393 0 : goto out;
394 : }
395 0 : err = asprintf(&valid_princ_formats[6],
396 : "cifs/%s.%s@%s", my_name, lp_realm(), lp_realm());
397 0 : if (err == -1) {
398 0 : ret = ENOMEM;
399 0 : goto out;
400 : }
401 :
402 0 : ret = smb_krb5_kt_open_relative(krbctx, NULL, false, &keytab);
403 0 : if (ret) {
404 0 : DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
405 : error_message(ret)));
406 0 : goto out;
407 : }
408 :
409 : /*
410 : * Iterate through the keytab. For each key, if the principal
411 : * name case-insensitively matches one of the allowed formats,
412 : * copy it to the memory keytab.
413 : */
414 :
415 0 : ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
416 0 : if (ret) {
417 0 : DEBUG(1, (__location__ ": krb5_kt_start_seq_get failed (%s)\n",
418 : error_message(ret)));
419 : /*
420 : * krb5_kt_start_seq_get() may leaves bogus data
421 : * in kt_cursor. And we want to use the all_zero()
422 : * logic below.
423 : *
424 : * See bug #10490
425 : */
426 0 : ZERO_STRUCT(kt_cursor);
427 0 : goto out;
428 : }
429 :
430 0 : while ((krb5_kt_next_entry(krbctx, keytab,
431 0 : &kt_entry, &kt_cursor) == 0)) {
432 0 : ret = smb_krb5_unparse_name(talloc_tos(), krbctx,
433 0 : kt_entry.principal,
434 : &entry_princ_s);
435 0 : if (ret) {
436 0 : DEBUG(1, (__location__ ": smb_krb5_unparse_name "
437 : "failed (%s)\n", error_message(ret)));
438 0 : goto out;
439 : }
440 :
441 0 : for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) {
442 :
443 0 : if (!strequal(entry_princ_s, valid_princ_formats[i])) {
444 0 : continue;
445 : }
446 :
447 0 : ret = krb5_kt_add_entry(krbctx, *mkeytab, &kt_entry);
448 0 : if (ret) {
449 0 : DEBUG(1, (__location__ ": smb_krb5_unparse_name "
450 : "failed (%s)\n", error_message(ret)));
451 0 : goto out;
452 : }
453 : }
454 :
455 : /* Free the name we parsed. */
456 0 : TALLOC_FREE(entry_princ_s);
457 :
458 : /* Free the entry we just read. */
459 0 : smb_krb5_kt_free_entry(krbctx, &kt_entry);
460 0 : ZERO_STRUCT(kt_entry);
461 : }
462 0 : krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
463 :
464 0 : ZERO_STRUCT(kt_cursor);
465 :
466 0 : out:
467 :
468 0 : for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) {
469 0 : SAFE_FREE(valid_princ_formats[i]);
470 : }
471 :
472 0 : TALLOC_FREE(entry_princ_s);
473 :
474 0 : if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
475 0 : smb_krb5_kt_free_entry(krbctx, &kt_entry);
476 : }
477 :
478 0 : if (!all_zero((uint8_t *)&kt_cursor, sizeof(kt_cursor)) && keytab) {
479 0 : krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
480 : }
481 :
482 0 : if (keytab) {
483 0 : krb5_kt_close(krbctx, keytab);
484 : }
485 :
486 0 : return ret;
487 : }
488 :
489 6 : static krb5_error_code fill_mem_keytab_from_dedicated_keytab(krb5_context krbctx,
490 : krb5_keytab *mkeytab)
491 : {
492 6 : krb5_error_code ret = 0;
493 6 : krb5_keytab keytab = NULL;
494 : krb5_kt_cursor kt_cursor;
495 : krb5_keytab_entry kt_entry;
496 :
497 6 : ret = smb_krb5_kt_open(krbctx, lp_dedicated_keytab_file(),
498 : false, &keytab);
499 6 : if (ret) {
500 0 : DEBUG(1, ("smb_krb5_kt_open of %s failed (%s)\n",
501 : lp_dedicated_keytab_file(),
502 : error_message(ret)));
503 0 : return ret;
504 : }
505 :
506 : /*
507 : * Copy the dedicated keyab to our in-memory keytab.
508 : */
509 :
510 6 : ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
511 6 : if (ret) {
512 6 : DEBUG(1, (__location__ ": krb5_kt_start_seq_get on %s "
513 : "failed (%s)\n",
514 : lp_dedicated_keytab_file(),
515 : error_message(ret)));
516 6 : goto out;
517 : }
518 :
519 0 : while ((krb5_kt_next_entry(krbctx, keytab,
520 0 : &kt_entry, &kt_cursor) == 0)) {
521 :
522 0 : ret = krb5_kt_add_entry(krbctx, *mkeytab, &kt_entry);
523 :
524 : /* Free the entry we just read. */
525 0 : smb_krb5_kt_free_entry(krbctx, &kt_entry);
526 :
527 0 : if (ret) {
528 0 : DEBUG(1, (__location__ ": smb_krb5_unparse_name "
529 : "failed (%s)\n", error_message(ret)));
530 0 : break;
531 : }
532 : }
533 0 : krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
534 :
535 6 : out:
536 :
537 6 : krb5_kt_close(krbctx, keytab);
538 :
539 6 : return ret;
540 : }
541 :
542 2962 : krb5_error_code gse_krb5_get_server_keytab(krb5_context krbctx,
543 : krb5_keytab *keytab)
544 : {
545 2962 : krb5_error_code ret = 0;
546 2962 : krb5_error_code ret1 = 0;
547 2962 : krb5_error_code ret2 = 0;
548 :
549 2962 : *keytab = NULL;
550 :
551 : /* create memory keytab */
552 2962 : ret = krb5_kt_resolve(krbctx, SRV_MEM_KEYTAB_NAME, keytab);
553 2962 : if (ret) {
554 0 : DEBUG(1, (__location__ ": Failed to get memory "
555 : "keytab!\n"));
556 0 : return ret;
557 : }
558 :
559 2962 : switch (lp_kerberos_method()) {
560 2956 : default:
561 : case KERBEROS_VERIFY_SECRETS:
562 2956 : ret = fill_mem_keytab_from_secrets(krbctx, keytab);
563 2956 : break;
564 0 : case KERBEROS_VERIFY_SYSTEM_KEYTAB:
565 0 : ret = fill_mem_keytab_from_system_keytab(krbctx, keytab);
566 0 : break;
567 6 : case KERBEROS_VERIFY_DEDICATED_KEYTAB:
568 : /* just use whatever keytab is configured */
569 6 : ret = fill_mem_keytab_from_dedicated_keytab(krbctx, keytab);
570 6 : break;
571 0 : case KERBEROS_VERIFY_SECRETS_AND_KEYTAB:
572 0 : ret1 = fill_mem_keytab_from_secrets(krbctx, keytab);
573 0 : if (ret1) {
574 0 : DEBUG(3, (__location__ ": Warning! Unable to set mem "
575 : "keytab from secrets!\n"));
576 : }
577 : /* Now append system keytab keys too */
578 0 : ret2 = fill_mem_keytab_from_system_keytab(krbctx, keytab);
579 0 : if (ret2) {
580 0 : DEBUG(3, (__location__ ": Warning! Unable to set mem "
581 : "keytab from system keytab!\n"));
582 : }
583 0 : if (ret1 == 0 || ret2 == 0) {
584 0 : ret = 0;
585 : } else {
586 0 : ret = ret1;
587 : }
588 0 : break;
589 : }
590 :
591 2962 : if (ret) {
592 10 : krb5_kt_close(krbctx, *keytab);
593 10 : *keytab = NULL;
594 10 : DEBUG(1,("%s: Error! Unable to set mem keytab - %d\n",
595 : __location__, ret));
596 : }
597 :
598 2962 : return ret;
599 : }
600 :
601 : #endif /* HAVE_KRB5 */
|