Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : kerberos keytab utility library
4 : Copyright (C) Andrew Tridgell 2001
5 : Copyright (C) Remus Koos 2001
6 : Copyright (C) Luke Howard 2003
7 : Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003
8 : Copyright (C) Guenther Deschner 2003
9 : Copyright (C) Rakesh Patel 2004
10 : Copyright (C) Dan Perry 2004
11 : Copyright (C) Jeremy Allison 2004
12 : Copyright (C) Gerald Carter 2006
13 :
14 : This program is free software; you can redistribute it and/or modify
15 : it under the terms of the GNU General Public License as published by
16 : the Free Software Foundation; either version 3 of the License, or
17 : (at your option) any later version.
18 :
19 : This program is distributed in the hope that it will be useful,
20 : but WITHOUT ANY WARRANTY; without even the implied warranty of
21 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 : GNU General Public License for more details.
23 :
24 : You should have received a copy of the GNU General Public License
25 : along with this program. If not, see <http://www.gnu.org/licenses/>.
26 : */
27 :
28 : #include "includes.h"
29 : #include "smb_krb5.h"
30 : #include "ads.h"
31 : #include "secrets.h"
32 :
33 : #ifdef HAVE_KRB5
34 :
35 : #ifdef HAVE_ADS
36 :
37 : /* This MAX_NAME_LEN is a constant defined in krb5.h */
38 : #ifndef MAX_KEYTAB_NAME_LEN
39 : #define MAX_KEYTAB_NAME_LEN 1100
40 : #endif
41 :
42 126 : static krb5_error_code ads_keytab_open(krb5_context context,
43 : krb5_keytab *keytab)
44 : {
45 126 : char keytab_str[MAX_KEYTAB_NAME_LEN] = {0};
46 126 : const char *keytab_name = NULL;
47 126 : krb5_error_code ret = 0;
48 :
49 126 : switch (lp_kerberos_method()) {
50 0 : case KERBEROS_VERIFY_SYSTEM_KEYTAB:
51 : case KERBEROS_VERIFY_SECRETS_AND_KEYTAB:
52 0 : ret = krb5_kt_default_name(context,
53 : keytab_str,
54 : sizeof(keytab_str) - 2);
55 0 : if (ret != 0) {
56 0 : DBG_WARNING("Failed to get default keytab name\n");
57 0 : goto out;
58 : }
59 0 : keytab_name = keytab_str;
60 0 : break;
61 126 : case KERBEROS_VERIFY_DEDICATED_KEYTAB:
62 126 : keytab_name = lp_dedicated_keytab_file();
63 126 : break;
64 0 : default:
65 0 : DBG_ERR("Invalid kerberos method set (%d)\n",
66 : lp_kerberos_method());
67 0 : ret = KRB5_KT_BADNAME;
68 0 : goto out;
69 : }
70 :
71 126 : if (keytab_name == NULL || keytab_name[0] == '\0') {
72 0 : DBG_ERR("Invalid keytab name\n");
73 0 : ret = KRB5_KT_BADNAME;
74 0 : goto out;
75 : }
76 :
77 126 : ret = smb_krb5_kt_open(context, keytab_name, true, keytab);
78 126 : if (ret != 0) {
79 0 : DBG_WARNING("smb_krb5_kt_open failed (%s)\n",
80 : error_message(ret));
81 0 : goto out;
82 : }
83 :
84 126 : out:
85 126 : return ret;
86 : }
87 :
88 2 : static bool fill_default_spns(TALLOC_CTX *ctx, const char *machine_name,
89 : const char *my_fqdn, const char *spn,
90 : const char ***spns)
91 : {
92 0 : char *psp1, *psp2;
93 :
94 2 : if (*spns == NULL) {
95 2 : *spns = talloc_zero_array(ctx, const char*, 3);
96 2 : if (*spns == NULL) {
97 0 : return false;
98 : }
99 : }
100 :
101 2 : psp1 = talloc_asprintf(ctx,
102 : "%s/%s",
103 : spn,
104 : machine_name);
105 2 : if (psp1 == NULL) {
106 0 : return false;
107 : }
108 :
109 2 : if (!strlower_m(&psp1[strlen(spn) + 1])) {
110 0 : return false;
111 : }
112 2 : (*spns)[0] = psp1;
113 :
114 2 : psp2 = talloc_asprintf(ctx,
115 : "%s/%s",
116 : spn,
117 : my_fqdn);
118 2 : if (psp2 == NULL) {
119 0 : return false;
120 : }
121 :
122 2 : if (!strlower_m(&psp2[strlen(spn) + 1])) {
123 0 : return false;
124 : }
125 :
126 2 : (*spns)[1] = psp2;
127 :
128 2 : return true;
129 : }
130 :
131 2 : static bool ads_set_machine_account_spns(TALLOC_CTX *ctx,
132 : ADS_STRUCT *ads,
133 : const char *service_or_spn,
134 : const char *my_fqdn)
135 : {
136 2 : const char **spn_names = NULL;
137 0 : ADS_STATUS aderr;
138 2 : struct spn_struct* spn_struct = NULL;
139 2 : char *tmp = NULL;
140 :
141 : /* SPN should have '/' */
142 2 : tmp = strchr_m(service_or_spn, '/');
143 2 : if (tmp != NULL) {
144 0 : spn_struct = parse_spn(ctx, service_or_spn);
145 0 : if (spn_struct == NULL) {
146 0 : return false;
147 : }
148 : }
149 :
150 2 : DBG_INFO("Attempting to add/update '%s'\n", service_or_spn);
151 :
152 2 : if (spn_struct != NULL) {
153 0 : spn_names = talloc_zero_array(ctx, const char*, 2);
154 0 : spn_names[0] = service_or_spn;
155 : } else {
156 0 : bool ok;
157 :
158 2 : ok = fill_default_spns(ctx,
159 : lp_netbios_name(),
160 : my_fqdn,
161 : service_or_spn,
162 : &spn_names);
163 2 : if (!ok) {
164 0 : return false;
165 : }
166 : }
167 2 : aderr = ads_add_service_principal_names(ads,
168 : lp_netbios_name(),
169 : spn_names);
170 2 : if (!ADS_ERR_OK(aderr)) {
171 0 : DBG_WARNING("Failed to add service principal name.\n");
172 0 : return false;
173 : }
174 :
175 2 : return true;
176 : }
177 :
178 : /*
179 : * Create kerberos principal(s) from SPN or service name.
180 : */
181 96 : static bool service_or_spn_to_kerberos_princ(TALLOC_CTX *ctx,
182 : const char *service_or_spn,
183 : const char *my_fqdn,
184 : char **p_princ_s,
185 : char **p_short_princ_s)
186 : {
187 96 : char *princ_s = NULL;
188 96 : char *short_princ_s = NULL;
189 96 : const char *service = service_or_spn;
190 96 : const char *host = my_fqdn;
191 96 : struct spn_struct* spn_struct = NULL;
192 96 : char *tmp = NULL;
193 96 : bool ok = true;
194 :
195 : /* SPN should have '/' */
196 96 : tmp = strchr_m(service_or_spn, '/');
197 96 : if (tmp != NULL) {
198 12 : spn_struct = parse_spn(ctx, service_or_spn);
199 12 : if (spn_struct == NULL) {
200 4 : ok = false;
201 4 : goto out;
202 : }
203 : }
204 92 : if (spn_struct != NULL) {
205 8 : service = spn_struct->serviceclass;
206 8 : host = spn_struct->host;
207 : }
208 92 : princ_s = talloc_asprintf(ctx, "%s/%s@%s",
209 : service,
210 : host, lp_realm());
211 92 : if (princ_s == NULL) {
212 0 : ok = false;
213 0 : goto out;
214 : }
215 :
216 92 : if (spn_struct == NULL) {
217 84 : short_princ_s = talloc_asprintf(ctx, "%s/%s@%s",
218 : service, lp_netbios_name(),
219 : lp_realm());
220 84 : if (short_princ_s == NULL) {
221 0 : ok = false;
222 0 : goto out;
223 : }
224 : }
225 92 : *p_princ_s = princ_s;
226 92 : *p_short_princ_s = short_princ_s;
227 96 : out:
228 96 : return ok;
229 : }
230 :
231 108 : static int add_kt_entry_etypes(krb5_context context, TALLOC_CTX *tmpctx,
232 : ADS_STRUCT *ads, const char *salt_princ_s,
233 : krb5_keytab keytab, krb5_kvno kvno,
234 : const char *srvPrinc, const char *my_fqdn,
235 : krb5_data *password, bool update_ads)
236 : {
237 108 : krb5_error_code ret = 0;
238 108 : char *princ_s = NULL;
239 108 : char *short_princ_s = NULL;
240 108 : krb5_enctype enctypes[4] = {
241 : ENCTYPE_AES256_CTS_HMAC_SHA1_96,
242 : ENCTYPE_AES128_CTS_HMAC_SHA1_96,
243 : ENCTYPE_ARCFOUR_HMAC,
244 : 0
245 : };
246 0 : size_t i;
247 :
248 : /* Construct our principal */
249 108 : if (strchr_m(srvPrinc, '@')) {
250 : /* It's a fully-named principal. */
251 4 : princ_s = talloc_asprintf(tmpctx, "%s", srvPrinc);
252 4 : if (!princ_s) {
253 0 : ret = -1;
254 0 : goto out;
255 : }
256 104 : } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
257 : /* It's the machine account, as used by smbclient clients. */
258 16 : princ_s = talloc_asprintf(tmpctx, "%s@%s",
259 : srvPrinc, lp_realm());
260 16 : if (!princ_s) {
261 0 : ret = -1;
262 0 : goto out;
263 : }
264 : } else {
265 : /* It's a normal service principal. Add the SPN now so that we
266 : * can obtain credentials for it and double-check the salt value
267 : * used to generate the service's keys. */
268 :
269 88 : if (!service_or_spn_to_kerberos_princ(tmpctx,
270 : srvPrinc,
271 : my_fqdn,
272 : &princ_s,
273 : &short_princ_s)) {
274 4 : ret = -1;
275 4 : goto out;
276 : }
277 :
278 : /* According to http://support.microsoft.com/kb/326985/en-us,
279 : certain principal names are automatically mapped to the
280 : host/... principal in the AD account.
281 : So only create these in the keytab, not in AD. --jerry */
282 :
283 84 : if (update_ads && !strequal(srvPrinc, "cifs") &&
284 2 : !strequal(srvPrinc, "host")) {
285 2 : if (!ads_set_machine_account_spns(tmpctx,
286 : ads,
287 : srvPrinc,
288 : my_fqdn)) {
289 0 : ret = -1;
290 0 : goto out;
291 : }
292 : }
293 : }
294 :
295 416 : for (i = 0; enctypes[i]; i++) {
296 :
297 : /* add the fqdn principal to the keytab */
298 312 : ret = smb_krb5_kt_add_entry(context,
299 : keytab,
300 : kvno,
301 : princ_s,
302 : salt_princ_s,
303 : enctypes[i],
304 : password,
305 : false); /* no_salt */
306 312 : if (ret) {
307 0 : DBG_WARNING("Failed to add entry to keytab\n");
308 0 : goto out;
309 : }
310 :
311 : /* add the short principal name if we have one */
312 312 : if (short_princ_s) {
313 240 : ret = smb_krb5_kt_add_entry(context,
314 : keytab,
315 : kvno,
316 : short_princ_s,
317 : salt_princ_s,
318 : enctypes[i],
319 : password,
320 : false); /* no_salt */
321 240 : if (ret) {
322 0 : DBG_WARNING("Failed to add short entry to keytab\n");
323 0 : goto out;
324 : }
325 : }
326 : }
327 104 : out:
328 108 : return ret;
329 : }
330 :
331 : /**********************************************************************
332 : Adds a single service principal, i.e. 'host' to the system keytab
333 : ***********************************************************************/
334 :
335 66 : int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc, bool update_ads)
336 : {
337 66 : krb5_error_code ret = 0;
338 66 : krb5_context context = NULL;
339 66 : krb5_keytab keytab = NULL;
340 0 : krb5_data password;
341 0 : krb5_kvno kvno;
342 66 : char *salt_princ_s = NULL;
343 66 : char *password_s = NULL;
344 0 : char *my_fqdn;
345 66 : TALLOC_CTX *tmpctx = NULL;
346 66 : char **hostnames_array = NULL;
347 66 : size_t num_hostnames = 0;
348 :
349 66 : ret = smb_krb5_init_context_common(&context);
350 66 : if (ret) {
351 0 : DBG_ERR("kerberos init context failed (%s)\n",
352 : error_message(ret));
353 0 : return -1;
354 : }
355 :
356 66 : ret = ads_keytab_open(context, &keytab);
357 66 : if (ret != 0) {
358 0 : goto out;
359 : }
360 :
361 : /* retrieve the password */
362 66 : if (!secrets_init()) {
363 0 : DBG_WARNING("secrets_init failed\n");
364 0 : ret = -1;
365 0 : goto out;
366 : }
367 66 : password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
368 66 : if (!password_s) {
369 0 : DBG_WARNING("failed to fetch machine password\n");
370 0 : ret = -1;
371 0 : goto out;
372 : }
373 66 : ZERO_STRUCT(password);
374 66 : password.data = password_s;
375 66 : password.length = strlen(password_s);
376 :
377 : /* we need the dNSHostName value here */
378 66 : tmpctx = talloc_init(__location__);
379 66 : if (!tmpctx) {
380 0 : DBG_ERR("talloc_init() failed!\n");
381 0 : ret = -1;
382 0 : goto out;
383 : }
384 :
385 66 : my_fqdn = ads_get_dnshostname(ads, tmpctx, lp_netbios_name());
386 66 : if (!my_fqdn) {
387 0 : DBG_ERR("unable to determine machine account's dns name in "
388 : "AD!\n");
389 0 : ret = -1;
390 0 : goto out;
391 : }
392 :
393 : /* make sure we have a single instance of the computer account */
394 66 : if (!ads_has_samaccountname(ads, tmpctx, lp_netbios_name())) {
395 0 : DBG_ERR("unable to determine machine account's short name in "
396 : "AD!\n");
397 0 : ret = -1;
398 0 : goto out;
399 : }
400 :
401 66 : kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
402 66 : if (kvno == -1) {
403 : /* -1 indicates failure, everything else is OK */
404 0 : DBG_WARNING("ads_get_machine_kvno failed to determine the "
405 : "system's kvno.\n");
406 0 : ret = -1;
407 0 : goto out;
408 : }
409 :
410 66 : salt_princ_s = kerberos_secrets_fetch_salt_princ();
411 66 : if (salt_princ_s == NULL) {
412 0 : DBG_WARNING("kerberos_secrets_fetch_salt_princ() failed\n");
413 0 : ret = -1;
414 0 : goto out;
415 : }
416 :
417 66 : ret = add_kt_entry_etypes(context, tmpctx, ads, salt_princ_s, keytab,
418 : kvno, srvPrinc, my_fqdn, &password,
419 : update_ads);
420 66 : if (ret != 0) {
421 4 : goto out;
422 : }
423 :
424 62 : if (ADS_ERR_OK(ads_get_additional_dns_hostnames(tmpctx, ads,
425 : lp_netbios_name(),
426 : &hostnames_array,
427 : &num_hostnames))) {
428 : size_t i;
429 :
430 56 : for (i = 0; i < num_hostnames; i++) {
431 :
432 42 : ret = add_kt_entry_etypes(context, tmpctx, ads,
433 : salt_princ_s, keytab,
434 : kvno, srvPrinc,
435 42 : hostnames_array[i],
436 : &password, update_ads);
437 42 : if (ret != 0) {
438 0 : goto out;
439 : }
440 : }
441 : }
442 :
443 62 : out:
444 66 : SAFE_FREE(salt_princ_s);
445 66 : TALLOC_FREE(tmpctx);
446 :
447 66 : if (keytab) {
448 66 : krb5_kt_close(context, keytab);
449 : }
450 66 : if (context) {
451 66 : krb5_free_context(context);
452 : }
453 66 : return (int)ret;
454 : }
455 :
456 : /**********************************************************************
457 : Delete a single service principal, i.e. 'host' from the system keytab
458 : ***********************************************************************/
459 :
460 12 : int ads_keytab_delete_entry(ADS_STRUCT *ads, const char *srvPrinc)
461 : {
462 12 : TALLOC_CTX *frame = talloc_stackframe();
463 12 : krb5_error_code ret = 0;
464 12 : krb5_context context = NULL;
465 12 : krb5_keytab keytab = NULL;
466 12 : char *princ_s = NULL;
467 12 : krb5_principal princ = NULL;
468 12 : char *short_princ_s = NULL;
469 12 : krb5_principal short_princ = NULL;
470 0 : bool ok;
471 :
472 12 : ret = smb_krb5_init_context_common(&context);
473 12 : if (ret) {
474 0 : DBG_ERR("kerberos init context failed (%s)\n",
475 : error_message(ret));
476 0 : goto out;
477 : }
478 :
479 12 : ret = ads_keytab_open(context, &keytab);
480 12 : if (ret != 0) {
481 0 : goto out;
482 : }
483 :
484 : /* Construct our principal */
485 12 : if (strchr_m(srvPrinc, '@')) {
486 : /* It's a fully-named principal. */
487 2 : princ_s = talloc_asprintf(frame, "%s", srvPrinc);
488 2 : if (!princ_s) {
489 0 : ret = -1;
490 0 : goto out;
491 : }
492 10 : } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
493 : /* It's the machine account, as used by smbclient clients. */
494 2 : princ_s = talloc_asprintf(frame, "%s@%s",
495 : srvPrinc, lp_realm());
496 2 : if (!princ_s) {
497 0 : ret = -1;
498 0 : goto out;
499 : }
500 : } else {
501 : /*
502 : * It's a normal service principal.
503 : */
504 8 : char *my_fqdn = NULL;
505 8 : char *tmp = NULL;
506 :
507 : /*
508 : * SPN should have '/' otherwise we
509 : * need to fallback and find our dnshostname
510 : */
511 8 : tmp = strchr_m(srvPrinc, '/');
512 8 : if (tmp == NULL) {
513 4 : my_fqdn = ads_get_dnshostname(ads, frame, lp_netbios_name());
514 4 : if (!my_fqdn) {
515 0 : DBG_ERR("unable to determine machine account's dns name in "
516 : "AD!\n");
517 0 : ret = -1;
518 0 : goto out;
519 : }
520 : }
521 :
522 8 : ok = service_or_spn_to_kerberos_princ(frame,
523 : srvPrinc,
524 : my_fqdn,
525 : &princ_s,
526 : &short_princ_s);
527 8 : if (!ok) {
528 0 : ret = -1;
529 0 : goto out;
530 : }
531 : }
532 :
533 12 : ret = smb_krb5_parse_name(context, princ_s, &princ);
534 12 : if (ret) {
535 0 : DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
536 : "failed (%s)\n", princ_s, error_message(ret)));
537 0 : goto out;
538 : }
539 :
540 12 : if (short_princ_s != NULL) {
541 4 : ret = smb_krb5_parse_name(context, short_princ_s, &short_princ);
542 4 : if (ret) {
543 0 : DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
544 : "failed (%s)\n", short_princ_s, error_message(ret)));
545 0 : goto out;
546 : }
547 : }
548 :
549 : /* Seek and delete old keytab entries */
550 12 : ret = smb_krb5_kt_seek_and_delete_old_entries(context,
551 : keytab,
552 : false, /* keep_old_kvno */
553 : -1,
554 : false, /* enctype_only */
555 : ENCTYPE_NULL,
556 : princ_s,
557 : princ,
558 : false); /* flush */
559 12 : if (ret) {
560 0 : goto out;
561 : }
562 :
563 12 : if (short_princ_s == NULL) {
564 8 : goto out;
565 : }
566 :
567 : /* Seek and delete old keytab entries */
568 4 : ret = smb_krb5_kt_seek_and_delete_old_entries(context,
569 : keytab,
570 : false, /* keep_old_kvno */
571 : -1,
572 : false, /* enctype_only */
573 : ENCTYPE_NULL,
574 : short_princ_s,
575 : short_princ,
576 : false); /* flush */
577 4 : if (ret) {
578 0 : goto out;
579 : }
580 :
581 4 : out:
582 12 : if (princ) {
583 12 : krb5_free_principal(context, princ);
584 : }
585 12 : if (short_princ) {
586 4 : krb5_free_principal(context, short_princ);
587 : }
588 12 : if (keytab) {
589 12 : krb5_kt_close(context, keytab);
590 : }
591 12 : if (context) {
592 12 : krb5_free_context(context);
593 : }
594 12 : TALLOC_FREE(frame);
595 12 : return ret;
596 : }
597 :
598 : /**********************************************************************
599 : Flushes all entries from the system keytab.
600 : ***********************************************************************/
601 :
602 0 : int ads_keytab_flush(ADS_STRUCT *ads)
603 : {
604 0 : krb5_error_code ret = 0;
605 0 : krb5_context context = NULL;
606 0 : krb5_keytab keytab = NULL;
607 0 : ADS_STATUS aderr;
608 :
609 0 : ret = smb_krb5_init_context_common(&context);
610 0 : if (ret) {
611 0 : DBG_ERR("kerberos init context failed (%s)\n",
612 : error_message(ret));
613 0 : return ret;
614 : }
615 :
616 0 : ret = ads_keytab_open(context, &keytab);
617 0 : if (ret != 0) {
618 0 : goto out;
619 : }
620 :
621 : /* Seek and delete all old keytab entries */
622 0 : ret = smb_krb5_kt_seek_and_delete_old_entries(context,
623 : keytab,
624 : false, /* keep_old_kvno */
625 : -1,
626 : false, /* enctype_only */
627 : ENCTYPE_NULL,
628 : NULL,
629 : NULL,
630 : true); /* flush */
631 0 : if (ret) {
632 0 : goto out;
633 : }
634 :
635 0 : aderr = ads_clear_service_principal_names(ads, lp_netbios_name());
636 0 : if (!ADS_ERR_OK(aderr)) {
637 0 : DEBUG(1, (__location__ ": Error while clearing service "
638 : "principal listings in LDAP.\n"));
639 0 : ret = -1;
640 0 : goto out;
641 : }
642 :
643 0 : out:
644 0 : if (keytab) {
645 0 : krb5_kt_close(context, keytab);
646 : }
647 0 : if (context) {
648 0 : krb5_free_context(context);
649 : }
650 0 : return ret;
651 : }
652 :
653 : /**********************************************************************
654 : Adds all the required service principals to the system keytab.
655 : ***********************************************************************/
656 :
657 8 : int ads_keytab_create_default(ADS_STRUCT *ads)
658 : {
659 8 : krb5_error_code ret = 0;
660 8 : krb5_context context = NULL;
661 8 : krb5_keytab keytab = NULL;
662 8 : krb5_kt_cursor cursor = {0};
663 8 : krb5_keytab_entry kt_entry = {0};
664 0 : krb5_kvno kvno;
665 8 : size_t found = 0;
666 0 : char *sam_account_name, *upn;
667 8 : char **oldEntries = NULL, *princ_s[26];
668 0 : TALLOC_CTX *frame;
669 0 : char *machine_name;
670 0 : char **spn_array;
671 0 : size_t num_spns;
672 0 : size_t i;
673 8 : bool ok = false;
674 0 : ADS_STATUS status;
675 :
676 8 : ZERO_STRUCT(kt_entry);
677 8 : ZERO_STRUCT(cursor);
678 :
679 8 : frame = talloc_stackframe();
680 8 : if (frame == NULL) {
681 0 : ret = -1;
682 0 : goto done;
683 : }
684 :
685 8 : status = ads_get_service_principal_names(frame,
686 : ads,
687 : lp_netbios_name(),
688 : &spn_array,
689 : &num_spns);
690 8 : if (!ADS_ERR_OK(status)) {
691 0 : ret = -1;
692 0 : goto done;
693 : }
694 :
695 48 : for (i = 0; i < num_spns; i++) {
696 0 : char *srv_princ;
697 0 : char *p;
698 :
699 40 : srv_princ = strlower_talloc(frame, spn_array[i]);
700 40 : if (srv_princ == NULL) {
701 0 : ret = -1;
702 0 : goto done;
703 : }
704 :
705 40 : p = strchr_m(srv_princ, '/');
706 40 : if (p == NULL) {
707 0 : continue;
708 : }
709 40 : p[0] = '\0';
710 :
711 : /* Add the SPNs found on the DC */
712 40 : ret = ads_keytab_add_entry(ads, srv_princ, false);
713 40 : if (ret != 0) {
714 0 : DEBUG(1, ("ads_keytab_add_entry failed while "
715 : "adding '%s' principal.\n",
716 : spn_array[i]));
717 0 : goto done;
718 : }
719 : }
720 :
721 : #if 0 /* don't create the CIFS/... keytab entries since no one except smbd
722 : really needs them and we will fall back to verifying against
723 : secrets.tdb */
724 :
725 : ret = ads_keytab_add_entry(ads, "cifs", false));
726 : if (ret != 0 ) {
727 : DEBUG(1, (__location__ ": ads_keytab_add_entry failed while "
728 : "adding 'cifs'.\n"));
729 : return ret;
730 : }
731 : #endif
732 :
733 8 : memset(princ_s, '\0', sizeof(princ_s));
734 :
735 8 : ret = smb_krb5_init_context_common(&context);
736 8 : if (ret) {
737 0 : DBG_ERR("kerberos init context failed (%s)\n",
738 : error_message(ret));
739 0 : goto done;
740 : }
741 :
742 8 : machine_name = talloc_strdup(frame, lp_netbios_name());
743 8 : if (!machine_name) {
744 0 : ret = -1;
745 0 : goto done;
746 : }
747 :
748 : /* now add the userPrincipalName and sAMAccountName entries */
749 8 : ok = ads_has_samaccountname(ads, frame, machine_name);
750 8 : if (!ok) {
751 0 : DEBUG(0, (__location__ ": unable to determine machine "
752 : "account's name in AD!\n"));
753 0 : ret = -1;
754 0 : goto done;
755 : }
756 :
757 : /*
758 : * append '$' to netbios name so 'ads_keytab_add_entry' recognises
759 : * it as a machine account rather than a service or Windows SPN.
760 : */
761 8 : sam_account_name = talloc_asprintf(frame, "%s$",machine_name);
762 8 : if (sam_account_name == NULL) {
763 0 : ret = -1;
764 0 : goto done;
765 : }
766 : /* upper case the sAMAccountName to make it easier for apps to
767 : know what case to use in the keytab file */
768 8 : if (!strupper_m(sam_account_name)) {
769 0 : ret = -1;
770 0 : goto done;
771 : }
772 :
773 8 : ret = ads_keytab_add_entry(ads, sam_account_name, false);
774 8 : if (ret != 0) {
775 0 : DEBUG(1, (__location__ ": ads_keytab_add_entry() failed "
776 : "while adding sAMAccountName (%s)\n",
777 : sam_account_name));
778 0 : goto done;
779 : }
780 :
781 : /* remember that not every machine account will have a upn */
782 8 : upn = ads_get_upn(ads, frame, machine_name);
783 8 : if (upn) {
784 2 : ret = ads_keytab_add_entry(ads, upn, false);
785 2 : if (ret != 0) {
786 0 : DEBUG(1, (__location__ ": ads_keytab_add_entry() "
787 : "failed while adding UPN (%s)\n", upn));
788 0 : goto done;
789 : }
790 : }
791 :
792 : /* Now loop through the keytab and update any other existing entries */
793 8 : kvno = (krb5_kvno)ads_get_machine_kvno(ads, machine_name);
794 8 : if (kvno == (krb5_kvno)-1) {
795 0 : DEBUG(1, (__location__ ": ads_get_machine_kvno() failed to "
796 : "determine the system's kvno.\n"));
797 0 : goto done;
798 : }
799 :
800 8 : DEBUG(3, (__location__ ": Searching for keytab entries to preserve "
801 : "and update.\n"));
802 :
803 8 : ret = ads_keytab_open(context, &keytab);
804 8 : if (ret != 0) {
805 0 : goto done;
806 : }
807 :
808 8 : ret = krb5_kt_start_seq_get(context, keytab, &cursor);
809 8 : if (ret != KRB5_KT_END && ret != ENOENT ) {
810 212 : while ((ret = krb5_kt_next_entry(context, keytab,
811 212 : &kt_entry, &cursor)) == 0) {
812 204 : smb_krb5_kt_free_entry(context, &kt_entry);
813 204 : ZERO_STRUCT(kt_entry);
814 204 : found++;
815 : }
816 : }
817 8 : krb5_kt_end_seq_get(context, keytab, &cursor);
818 8 : ZERO_STRUCT(cursor);
819 :
820 : /*
821 : * Hmmm. There is no "rewind" function for the keytab. This means we
822 : * have a race condition where someone else could add entries after
823 : * we've counted them. Re-open asap to minimise the race. JRA.
824 : */
825 8 : DEBUG(3, (__location__ ": Found %zd entries in the keytab.\n", found));
826 8 : if (!found) {
827 0 : goto done;
828 : }
829 :
830 8 : oldEntries = talloc_zero_array(frame, char *, found + 1);
831 8 : if (!oldEntries) {
832 0 : DEBUG(1, (__location__ ": Failed to allocate space to store "
833 : "the old keytab entries (talloc failed?).\n"));
834 0 : ret = -1;
835 0 : goto done;
836 : }
837 :
838 8 : ret = krb5_kt_start_seq_get(context, keytab, &cursor);
839 8 : if (ret == KRB5_KT_END || ret == ENOENT) {
840 0 : krb5_kt_end_seq_get(context, keytab, &cursor);
841 0 : ZERO_STRUCT(cursor);
842 0 : goto done;
843 : }
844 :
845 212 : while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
846 204 : if (kt_entry.vno != kvno) {
847 0 : char *ktprinc = NULL;
848 0 : char *p;
849 :
850 : /* This returns a malloc'ed string in ktprinc. */
851 0 : ret = smb_krb5_unparse_name(oldEntries, context,
852 0 : kt_entry.principal,
853 : &ktprinc);
854 0 : if (ret) {
855 0 : DEBUG(1, (__location__
856 : ": smb_krb5_unparse_name failed "
857 : "(%s)\n", error_message(ret)));
858 0 : goto done;
859 : }
860 : /*
861 : * From looking at the krb5 source they don't seem to
862 : * take locale or mb strings into account.
863 : * Maybe this is because they assume utf8 ?
864 : * In this case we may need to convert from utf8 to
865 : * mb charset here ? JRA.
866 : */
867 0 : p = strchr_m(ktprinc, '@');
868 0 : if (p) {
869 0 : *p = '\0';
870 : }
871 :
872 0 : p = strchr_m(ktprinc, '/');
873 0 : if (p) {
874 0 : *p = '\0';
875 : }
876 0 : for (i = 0; i < found; i++) {
877 0 : if (!oldEntries[i]) {
878 0 : oldEntries[i] = ktprinc;
879 0 : break;
880 : }
881 0 : if (!strcmp(oldEntries[i], ktprinc)) {
882 0 : TALLOC_FREE(ktprinc);
883 0 : break;
884 : }
885 : }
886 0 : if (i == found) {
887 0 : TALLOC_FREE(ktprinc);
888 : }
889 : }
890 204 : smb_krb5_kt_free_entry(context, &kt_entry);
891 204 : ZERO_STRUCT(kt_entry);
892 : }
893 8 : krb5_kt_end_seq_get(context, keytab, &cursor);
894 8 : ZERO_STRUCT(cursor);
895 :
896 8 : ret = 0;
897 8 : for (i = 0; oldEntries[i]; i++) {
898 0 : ret |= ads_keytab_add_entry(ads, oldEntries[i], false);
899 0 : TALLOC_FREE(oldEntries[i]);
900 : }
901 :
902 8 : done:
903 8 : TALLOC_FREE(oldEntries);
904 8 : TALLOC_FREE(frame);
905 :
906 8 : if (context) {
907 8 : if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
908 0 : smb_krb5_kt_free_entry(context, &kt_entry);
909 : }
910 8 : if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
911 0 : krb5_kt_end_seq_get(context, keytab, &cursor);
912 : }
913 8 : if (keytab) {
914 8 : krb5_kt_close(context, keytab);
915 : }
916 8 : krb5_free_context(context);
917 : }
918 8 : return ret;
919 : }
920 :
921 : #endif /* HAVE_ADS */
922 :
923 : /**********************************************************************
924 : List system keytab.
925 : ***********************************************************************/
926 :
927 42 : int ads_keytab_list(const char *keytab_name)
928 : {
929 42 : krb5_error_code ret = 0;
930 42 : krb5_context context = NULL;
931 42 : krb5_keytab keytab = NULL;
932 0 : krb5_kt_cursor cursor;
933 0 : krb5_keytab_entry kt_entry;
934 :
935 42 : ZERO_STRUCT(kt_entry);
936 42 : ZERO_STRUCT(cursor);
937 :
938 42 : ret = smb_krb5_init_context_common(&context);
939 42 : if (ret) {
940 0 : DBG_ERR("kerberos init context failed (%s)\n",
941 : error_message(ret));
942 0 : return ret;
943 : }
944 :
945 42 : if (keytab_name == NULL) {
946 : #ifdef HAVE_ADS
947 40 : ret = ads_keytab_open(context, &keytab);
948 : #else
949 0 : ret = ENOENT;
950 : #endif
951 : } else {
952 2 : ret = smb_krb5_kt_open(context, keytab_name, False, &keytab);
953 : }
954 42 : if (ret) {
955 0 : DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
956 : error_message(ret)));
957 0 : goto out;
958 : }
959 :
960 42 : ret = krb5_kt_start_seq_get(context, keytab, &cursor);
961 42 : if (ret) {
962 0 : ZERO_STRUCT(cursor);
963 0 : goto out;
964 : }
965 :
966 42 : printf("Vno Type Principal\n");
967 :
968 924 : while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
969 :
970 882 : char *princ_s = NULL;
971 882 : char *etype_s = NULL;
972 882 : krb5_enctype enctype = 0;
973 :
974 882 : ret = smb_krb5_unparse_name(talloc_tos(), context,
975 882 : kt_entry.principal, &princ_s);
976 882 : if (ret) {
977 0 : goto out;
978 : }
979 :
980 882 : enctype = smb_krb5_kt_get_enctype_from_entry(&kt_entry);
981 :
982 882 : ret = smb_krb5_enctype_to_string(context, enctype, &etype_s);
983 882 : if (ret &&
984 0 : (asprintf(&etype_s, "UNKNOWN: %d", enctype) == -1)) {
985 0 : TALLOC_FREE(princ_s);
986 0 : goto out;
987 : }
988 :
989 882 : printf("%3d %-43s %s\n", kt_entry.vno, etype_s, princ_s);
990 :
991 882 : TALLOC_FREE(princ_s);
992 882 : SAFE_FREE(etype_s);
993 :
994 882 : ret = smb_krb5_kt_free_entry(context, &kt_entry);
995 882 : if (ret) {
996 0 : goto out;
997 : }
998 : }
999 :
1000 42 : ret = krb5_kt_end_seq_get(context, keytab, &cursor);
1001 42 : if (ret) {
1002 0 : goto out;
1003 : }
1004 :
1005 : /* Ensure we don't double free. */
1006 42 : ZERO_STRUCT(kt_entry);
1007 42 : ZERO_STRUCT(cursor);
1008 42 : out:
1009 :
1010 42 : if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
1011 0 : smb_krb5_kt_free_entry(context, &kt_entry);
1012 : }
1013 42 : if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
1014 0 : krb5_kt_end_seq_get(context, keytab, &cursor);
1015 : }
1016 :
1017 42 : if (keytab) {
1018 42 : krb5_kt_close(context, keytab);
1019 : }
1020 42 : if (context) {
1021 42 : krb5_free_context(context);
1022 : }
1023 42 : return ret;
1024 : }
1025 :
1026 : #endif /* HAVE_KRB5 */
|