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_password(context,
299 : keytab,
300 : kvno,
301 : princ_s,
302 : salt_princ_s,
303 : enctypes[i],
304 : password);
305 312 : if (ret) {
306 0 : DBG_WARNING("Failed to add entry to keytab\n");
307 0 : goto out;
308 : }
309 :
310 : /* add the short principal name if we have one */
311 312 : if (short_princ_s) {
312 240 : ret = smb_krb5_kt_add_password(context,
313 : keytab,
314 : kvno,
315 : short_princ_s,
316 : salt_princ_s,
317 : enctypes[i],
318 : password);
319 240 : if (ret) {
320 0 : DBG_WARNING("Failed to add short entry to keytab\n");
321 0 : goto out;
322 : }
323 : }
324 : }
325 104 : out:
326 108 : return ret;
327 : }
328 :
329 : /**********************************************************************
330 : Adds a single service principal, i.e. 'host' to the system keytab
331 : ***********************************************************************/
332 :
333 66 : int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc, bool update_ads)
334 : {
335 66 : krb5_error_code ret = 0;
336 66 : krb5_context context = NULL;
337 66 : krb5_keytab keytab = NULL;
338 0 : krb5_data password;
339 0 : krb5_kvno kvno;
340 66 : char *salt_princ_s = NULL;
341 66 : char *password_s = NULL;
342 0 : char *my_fqdn;
343 66 : TALLOC_CTX *tmpctx = NULL;
344 66 : char **hostnames_array = NULL;
345 66 : size_t num_hostnames = 0;
346 :
347 66 : ret = smb_krb5_init_context_common(&context);
348 66 : if (ret) {
349 0 : DBG_ERR("kerberos init context failed (%s)\n",
350 : error_message(ret));
351 0 : return -1;
352 : }
353 :
354 66 : ret = ads_keytab_open(context, &keytab);
355 66 : if (ret != 0) {
356 0 : goto out;
357 : }
358 :
359 : /* retrieve the password */
360 66 : if (!secrets_init()) {
361 0 : DBG_WARNING("secrets_init failed\n");
362 0 : ret = -1;
363 0 : goto out;
364 : }
365 66 : password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
366 66 : if (!password_s) {
367 0 : DBG_WARNING("failed to fetch machine password\n");
368 0 : ret = -1;
369 0 : goto out;
370 : }
371 66 : ZERO_STRUCT(password);
372 66 : password.data = password_s;
373 66 : password.length = strlen(password_s);
374 :
375 : /* we need the dNSHostName value here */
376 66 : tmpctx = talloc_init(__location__);
377 66 : if (!tmpctx) {
378 0 : DBG_ERR("talloc_init() failed!\n");
379 0 : ret = -1;
380 0 : goto out;
381 : }
382 :
383 66 : my_fqdn = ads_get_dnshostname(ads, tmpctx, lp_netbios_name());
384 66 : if (!my_fqdn) {
385 0 : DBG_ERR("unable to determine machine account's dns name in "
386 : "AD!\n");
387 0 : ret = -1;
388 0 : goto out;
389 : }
390 :
391 : /* make sure we have a single instance of the computer account */
392 66 : if (!ads_has_samaccountname(ads, tmpctx, lp_netbios_name())) {
393 0 : DBG_ERR("unable to determine machine account's short name in "
394 : "AD!\n");
395 0 : ret = -1;
396 0 : goto out;
397 : }
398 :
399 66 : kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
400 66 : if (kvno == -1) {
401 : /* -1 indicates failure, everything else is OK */
402 0 : DBG_WARNING("ads_get_machine_kvno failed to determine the "
403 : "system's kvno.\n");
404 0 : ret = -1;
405 0 : goto out;
406 : }
407 :
408 66 : salt_princ_s = kerberos_secrets_fetch_salt_princ();
409 66 : if (salt_princ_s == NULL) {
410 0 : DBG_WARNING("kerberos_secrets_fetch_salt_princ() failed\n");
411 0 : ret = -1;
412 0 : goto out;
413 : }
414 :
415 66 : ret = add_kt_entry_etypes(context, tmpctx, ads, salt_princ_s, keytab,
416 : kvno, srvPrinc, my_fqdn, &password,
417 : update_ads);
418 66 : if (ret != 0) {
419 4 : goto out;
420 : }
421 :
422 62 : if (ADS_ERR_OK(ads_get_additional_dns_hostnames(tmpctx, ads,
423 : lp_netbios_name(),
424 : &hostnames_array,
425 : &num_hostnames))) {
426 : size_t i;
427 :
428 56 : for (i = 0; i < num_hostnames; i++) {
429 :
430 42 : ret = add_kt_entry_etypes(context, tmpctx, ads,
431 : salt_princ_s, keytab,
432 : kvno, srvPrinc,
433 42 : hostnames_array[i],
434 : &password, update_ads);
435 42 : if (ret != 0) {
436 0 : goto out;
437 : }
438 : }
439 : }
440 :
441 62 : out:
442 66 : SAFE_FREE(salt_princ_s);
443 66 : TALLOC_FREE(tmpctx);
444 :
445 66 : if (keytab) {
446 66 : krb5_kt_close(context, keytab);
447 : }
448 66 : if (context) {
449 66 : krb5_free_context(context);
450 : }
451 66 : return (int)ret;
452 : }
453 :
454 : /**********************************************************************
455 : Delete a single service principal, i.e. 'host' from the system keytab
456 : ***********************************************************************/
457 :
458 12 : int ads_keytab_delete_entry(ADS_STRUCT *ads, const char *srvPrinc)
459 : {
460 12 : TALLOC_CTX *frame = talloc_stackframe();
461 12 : krb5_error_code ret = 0;
462 12 : krb5_context context = NULL;
463 12 : krb5_keytab keytab = NULL;
464 12 : char *princ_s = NULL;
465 12 : krb5_principal princ = NULL;
466 12 : char *short_princ_s = NULL;
467 12 : krb5_principal short_princ = NULL;
468 0 : bool ok;
469 :
470 12 : ret = smb_krb5_init_context_common(&context);
471 12 : if (ret) {
472 0 : DBG_ERR("kerberos init context failed (%s)\n",
473 : error_message(ret));
474 0 : goto out;
475 : }
476 :
477 12 : ret = ads_keytab_open(context, &keytab);
478 12 : if (ret != 0) {
479 0 : goto out;
480 : }
481 :
482 : /* Construct our principal */
483 12 : if (strchr_m(srvPrinc, '@')) {
484 : /* It's a fully-named principal. */
485 2 : princ_s = talloc_asprintf(frame, "%s", srvPrinc);
486 2 : if (!princ_s) {
487 0 : ret = -1;
488 0 : goto out;
489 : }
490 10 : } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
491 : /* It's the machine account, as used by smbclient clients. */
492 2 : princ_s = talloc_asprintf(frame, "%s@%s",
493 : srvPrinc, lp_realm());
494 2 : if (!princ_s) {
495 0 : ret = -1;
496 0 : goto out;
497 : }
498 : } else {
499 : /*
500 : * It's a normal service principal.
501 : */
502 8 : char *my_fqdn = NULL;
503 8 : char *tmp = NULL;
504 :
505 : /*
506 : * SPN should have '/' otherwise we
507 : * need to fallback and find our dnshostname
508 : */
509 8 : tmp = strchr_m(srvPrinc, '/');
510 8 : if (tmp == NULL) {
511 4 : my_fqdn = ads_get_dnshostname(ads, frame, lp_netbios_name());
512 4 : if (!my_fqdn) {
513 0 : DBG_ERR("unable to determine machine account's dns name in "
514 : "AD!\n");
515 0 : ret = -1;
516 0 : goto out;
517 : }
518 : }
519 :
520 8 : ok = service_or_spn_to_kerberos_princ(frame,
521 : srvPrinc,
522 : my_fqdn,
523 : &princ_s,
524 : &short_princ_s);
525 8 : if (!ok) {
526 0 : ret = -1;
527 0 : goto out;
528 : }
529 : }
530 :
531 12 : ret = smb_krb5_parse_name(context, princ_s, &princ);
532 12 : if (ret) {
533 0 : DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
534 : "failed (%s)\n", princ_s, error_message(ret)));
535 0 : goto out;
536 : }
537 :
538 12 : if (short_princ_s != NULL) {
539 4 : ret = smb_krb5_parse_name(context, short_princ_s, &short_princ);
540 4 : if (ret) {
541 0 : DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
542 : "failed (%s)\n", short_princ_s, error_message(ret)));
543 0 : goto out;
544 : }
545 : }
546 :
547 : /* Seek and delete old keytab entries */
548 12 : ret = smb_krb5_kt_seek_and_delete_old_entries(context,
549 : keytab,
550 : false, /* keep_old_kvno */
551 : -1,
552 : false, /* enctype_only */
553 : ENCTYPE_NULL,
554 : princ_s,
555 : princ,
556 : false); /* flush */
557 12 : if (ret) {
558 0 : goto out;
559 : }
560 :
561 12 : if (short_princ_s == NULL) {
562 8 : goto out;
563 : }
564 :
565 : /* Seek and delete old keytab entries */
566 4 : ret = smb_krb5_kt_seek_and_delete_old_entries(context,
567 : keytab,
568 : false, /* keep_old_kvno */
569 : -1,
570 : false, /* enctype_only */
571 : ENCTYPE_NULL,
572 : short_princ_s,
573 : short_princ,
574 : false); /* flush */
575 4 : if (ret) {
576 0 : goto out;
577 : }
578 :
579 4 : out:
580 12 : if (princ) {
581 12 : krb5_free_principal(context, princ);
582 : }
583 12 : if (short_princ) {
584 4 : krb5_free_principal(context, short_princ);
585 : }
586 12 : if (keytab) {
587 12 : krb5_kt_close(context, keytab);
588 : }
589 12 : if (context) {
590 12 : krb5_free_context(context);
591 : }
592 12 : TALLOC_FREE(frame);
593 12 : return ret;
594 : }
595 :
596 : /**********************************************************************
597 : Flushes all entries from the system keytab.
598 : ***********************************************************************/
599 :
600 0 : int ads_keytab_flush(ADS_STRUCT *ads)
601 : {
602 0 : krb5_error_code ret = 0;
603 0 : krb5_context context = NULL;
604 0 : krb5_keytab keytab = NULL;
605 0 : ADS_STATUS aderr;
606 :
607 0 : ret = smb_krb5_init_context_common(&context);
608 0 : if (ret) {
609 0 : DBG_ERR("kerberos init context failed (%s)\n",
610 : error_message(ret));
611 0 : return ret;
612 : }
613 :
614 0 : ret = ads_keytab_open(context, &keytab);
615 0 : if (ret != 0) {
616 0 : goto out;
617 : }
618 :
619 : /* Seek and delete all old keytab entries */
620 0 : ret = smb_krb5_kt_seek_and_delete_old_entries(context,
621 : keytab,
622 : false, /* keep_old_kvno */
623 : -1,
624 : false, /* enctype_only */
625 : ENCTYPE_NULL,
626 : NULL,
627 : NULL,
628 : true); /* flush */
629 0 : if (ret) {
630 0 : goto out;
631 : }
632 :
633 0 : aderr = ads_clear_service_principal_names(ads, lp_netbios_name());
634 0 : if (!ADS_ERR_OK(aderr)) {
635 0 : DEBUG(1, (__location__ ": Error while clearing service "
636 : "principal listings in LDAP.\n"));
637 0 : ret = -1;
638 0 : goto out;
639 : }
640 :
641 0 : out:
642 0 : if (keytab) {
643 0 : krb5_kt_close(context, keytab);
644 : }
645 0 : if (context) {
646 0 : krb5_free_context(context);
647 : }
648 0 : return ret;
649 : }
650 :
651 : /**********************************************************************
652 : Adds all the required service principals to the system keytab.
653 : ***********************************************************************/
654 :
655 8 : int ads_keytab_create_default(ADS_STRUCT *ads)
656 : {
657 8 : krb5_error_code ret = 0;
658 8 : krb5_context context = NULL;
659 8 : krb5_keytab keytab = NULL;
660 8 : krb5_kt_cursor cursor = {0};
661 8 : krb5_keytab_entry kt_entry = {0};
662 0 : krb5_kvno kvno;
663 8 : size_t found = 0;
664 0 : char *sam_account_name, *upn;
665 8 : char **oldEntries = NULL, *princ_s[26];
666 0 : TALLOC_CTX *frame;
667 0 : char *machine_name;
668 0 : char **spn_array;
669 0 : size_t num_spns;
670 0 : size_t i;
671 8 : bool ok = false;
672 0 : ADS_STATUS status;
673 :
674 8 : ZERO_STRUCT(kt_entry);
675 8 : ZERO_STRUCT(cursor);
676 :
677 8 : frame = talloc_stackframe();
678 8 : if (frame == NULL) {
679 0 : ret = -1;
680 0 : goto done;
681 : }
682 :
683 8 : status = ads_get_service_principal_names(frame,
684 : ads,
685 : lp_netbios_name(),
686 : &spn_array,
687 : &num_spns);
688 8 : if (!ADS_ERR_OK(status)) {
689 0 : ret = -1;
690 0 : goto done;
691 : }
692 :
693 48 : for (i = 0; i < num_spns; i++) {
694 0 : char *srv_princ;
695 0 : char *p;
696 :
697 40 : srv_princ = strlower_talloc(frame, spn_array[i]);
698 40 : if (srv_princ == NULL) {
699 0 : ret = -1;
700 0 : goto done;
701 : }
702 :
703 40 : p = strchr_m(srv_princ, '/');
704 40 : if (p == NULL) {
705 0 : continue;
706 : }
707 40 : p[0] = '\0';
708 :
709 : /* Add the SPNs found on the DC */
710 40 : ret = ads_keytab_add_entry(ads, srv_princ, false);
711 40 : if (ret != 0) {
712 0 : DEBUG(1, ("ads_keytab_add_entry failed while "
713 : "adding '%s' principal.\n",
714 : spn_array[i]));
715 0 : goto done;
716 : }
717 : }
718 :
719 : #if 0 /* don't create the CIFS/... keytab entries since no one except smbd
720 : really needs them and we will fall back to verifying against
721 : secrets.tdb */
722 :
723 : ret = ads_keytab_add_entry(ads, "cifs", false));
724 : if (ret != 0 ) {
725 : DEBUG(1, (__location__ ": ads_keytab_add_entry failed while "
726 : "adding 'cifs'.\n"));
727 : return ret;
728 : }
729 : #endif
730 :
731 8 : memset(princ_s, '\0', sizeof(princ_s));
732 :
733 8 : ret = smb_krb5_init_context_common(&context);
734 8 : if (ret) {
735 0 : DBG_ERR("kerberos init context failed (%s)\n",
736 : error_message(ret));
737 0 : goto done;
738 : }
739 :
740 8 : machine_name = talloc_strdup(frame, lp_netbios_name());
741 8 : if (!machine_name) {
742 0 : ret = -1;
743 0 : goto done;
744 : }
745 :
746 : /* now add the userPrincipalName and sAMAccountName entries */
747 8 : ok = ads_has_samaccountname(ads, frame, machine_name);
748 8 : if (!ok) {
749 0 : DEBUG(0, (__location__ ": unable to determine machine "
750 : "account's name in AD!\n"));
751 0 : ret = -1;
752 0 : goto done;
753 : }
754 :
755 : /*
756 : * append '$' to netbios name so 'ads_keytab_add_entry' recognises
757 : * it as a machine account rather than a service or Windows SPN.
758 : */
759 8 : sam_account_name = talloc_asprintf(frame, "%s$",machine_name);
760 8 : if (sam_account_name == NULL) {
761 0 : ret = -1;
762 0 : goto done;
763 : }
764 : /* upper case the sAMAccountName to make it easier for apps to
765 : know what case to use in the keytab file */
766 8 : if (!strupper_m(sam_account_name)) {
767 0 : ret = -1;
768 0 : goto done;
769 : }
770 :
771 8 : ret = ads_keytab_add_entry(ads, sam_account_name, false);
772 8 : if (ret != 0) {
773 0 : DEBUG(1, (__location__ ": ads_keytab_add_entry() failed "
774 : "while adding sAMAccountName (%s)\n",
775 : sam_account_name));
776 0 : goto done;
777 : }
778 :
779 : /* remember that not every machine account will have a upn */
780 8 : upn = ads_get_upn(ads, frame, machine_name);
781 8 : if (upn) {
782 2 : ret = ads_keytab_add_entry(ads, upn, false);
783 2 : if (ret != 0) {
784 0 : DEBUG(1, (__location__ ": ads_keytab_add_entry() "
785 : "failed while adding UPN (%s)\n", upn));
786 0 : goto done;
787 : }
788 : }
789 :
790 : /* Now loop through the keytab and update any other existing entries */
791 8 : kvno = (krb5_kvno)ads_get_machine_kvno(ads, machine_name);
792 8 : if (kvno == (krb5_kvno)-1) {
793 0 : DEBUG(1, (__location__ ": ads_get_machine_kvno() failed to "
794 : "determine the system's kvno.\n"));
795 0 : goto done;
796 : }
797 :
798 8 : DEBUG(3, (__location__ ": Searching for keytab entries to preserve "
799 : "and update.\n"));
800 :
801 8 : ret = ads_keytab_open(context, &keytab);
802 8 : if (ret != 0) {
803 0 : goto done;
804 : }
805 :
806 8 : ret = krb5_kt_start_seq_get(context, keytab, &cursor);
807 8 : if (ret != KRB5_KT_END && ret != ENOENT ) {
808 212 : while ((ret = krb5_kt_next_entry(context, keytab,
809 212 : &kt_entry, &cursor)) == 0) {
810 204 : smb_krb5_kt_free_entry(context, &kt_entry);
811 204 : ZERO_STRUCT(kt_entry);
812 204 : found++;
813 : }
814 : }
815 8 : krb5_kt_end_seq_get(context, keytab, &cursor);
816 8 : ZERO_STRUCT(cursor);
817 :
818 : /*
819 : * Hmmm. There is no "rewind" function for the keytab. This means we
820 : * have a race condition where someone else could add entries after
821 : * we've counted them. Re-open asap to minimise the race. JRA.
822 : */
823 8 : DEBUG(3, (__location__ ": Found %zd entries in the keytab.\n", found));
824 8 : if (!found) {
825 0 : goto done;
826 : }
827 :
828 8 : oldEntries = talloc_zero_array(frame, char *, found + 1);
829 8 : if (!oldEntries) {
830 0 : DEBUG(1, (__location__ ": Failed to allocate space to store "
831 : "the old keytab entries (talloc failed?).\n"));
832 0 : ret = -1;
833 0 : goto done;
834 : }
835 :
836 8 : ret = krb5_kt_start_seq_get(context, keytab, &cursor);
837 8 : if (ret == KRB5_KT_END || ret == ENOENT) {
838 0 : krb5_kt_end_seq_get(context, keytab, &cursor);
839 0 : ZERO_STRUCT(cursor);
840 0 : goto done;
841 : }
842 :
843 212 : while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
844 204 : if (kt_entry.vno != kvno) {
845 0 : char *ktprinc = NULL;
846 0 : char *p;
847 :
848 : /* This returns a malloc'ed string in ktprinc. */
849 0 : ret = smb_krb5_unparse_name(oldEntries, context,
850 0 : kt_entry.principal,
851 : &ktprinc);
852 0 : if (ret) {
853 0 : DEBUG(1, (__location__
854 : ": smb_krb5_unparse_name failed "
855 : "(%s)\n", error_message(ret)));
856 0 : goto done;
857 : }
858 : /*
859 : * From looking at the krb5 source they don't seem to
860 : * take locale or mb strings into account.
861 : * Maybe this is because they assume utf8 ?
862 : * In this case we may need to convert from utf8 to
863 : * mb charset here ? JRA.
864 : */
865 0 : p = strchr_m(ktprinc, '@');
866 0 : if (p) {
867 0 : *p = '\0';
868 : }
869 :
870 0 : p = strchr_m(ktprinc, '/');
871 0 : if (p) {
872 0 : *p = '\0';
873 : }
874 0 : for (i = 0; i < found; i++) {
875 0 : if (!oldEntries[i]) {
876 0 : oldEntries[i] = ktprinc;
877 0 : break;
878 : }
879 0 : if (!strcmp(oldEntries[i], ktprinc)) {
880 0 : TALLOC_FREE(ktprinc);
881 0 : break;
882 : }
883 : }
884 0 : if (i == found) {
885 0 : TALLOC_FREE(ktprinc);
886 : }
887 : }
888 204 : smb_krb5_kt_free_entry(context, &kt_entry);
889 204 : ZERO_STRUCT(kt_entry);
890 : }
891 8 : krb5_kt_end_seq_get(context, keytab, &cursor);
892 8 : ZERO_STRUCT(cursor);
893 :
894 8 : ret = 0;
895 8 : for (i = 0; oldEntries[i]; i++) {
896 0 : ret |= ads_keytab_add_entry(ads, oldEntries[i], false);
897 0 : TALLOC_FREE(oldEntries[i]);
898 : }
899 :
900 8 : done:
901 8 : TALLOC_FREE(oldEntries);
902 8 : TALLOC_FREE(frame);
903 :
904 8 : if (context) {
905 8 : if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
906 0 : smb_krb5_kt_free_entry(context, &kt_entry);
907 : }
908 8 : if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
909 0 : krb5_kt_end_seq_get(context, keytab, &cursor);
910 : }
911 8 : if (keytab) {
912 8 : krb5_kt_close(context, keytab);
913 : }
914 8 : krb5_free_context(context);
915 : }
916 8 : return ret;
917 : }
918 :
919 : #endif /* HAVE_ADS */
920 :
921 : /**********************************************************************
922 : List system keytab.
923 : ***********************************************************************/
924 :
925 42 : int ads_keytab_list(const char *keytab_name)
926 : {
927 42 : krb5_error_code ret = 0;
928 42 : krb5_context context = NULL;
929 42 : krb5_keytab keytab = NULL;
930 0 : krb5_kt_cursor cursor;
931 0 : krb5_keytab_entry kt_entry;
932 :
933 42 : ZERO_STRUCT(kt_entry);
934 42 : ZERO_STRUCT(cursor);
935 :
936 42 : ret = smb_krb5_init_context_common(&context);
937 42 : if (ret) {
938 0 : DBG_ERR("kerberos init context failed (%s)\n",
939 : error_message(ret));
940 0 : return ret;
941 : }
942 :
943 42 : if (keytab_name == NULL) {
944 : #ifdef HAVE_ADS
945 40 : ret = ads_keytab_open(context, &keytab);
946 : #else
947 0 : ret = ENOENT;
948 : #endif
949 : } else {
950 2 : ret = smb_krb5_kt_open(context, keytab_name, False, &keytab);
951 : }
952 42 : if (ret) {
953 0 : DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
954 : error_message(ret)));
955 0 : goto out;
956 : }
957 :
958 42 : ret = krb5_kt_start_seq_get(context, keytab, &cursor);
959 42 : if (ret) {
960 0 : ZERO_STRUCT(cursor);
961 0 : goto out;
962 : }
963 :
964 42 : printf("Vno Type Principal\n");
965 :
966 924 : while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
967 :
968 882 : char *princ_s = NULL;
969 882 : char *etype_s = NULL;
970 882 : krb5_enctype enctype = 0;
971 :
972 882 : ret = smb_krb5_unparse_name(talloc_tos(), context,
973 882 : kt_entry.principal, &princ_s);
974 882 : if (ret) {
975 0 : goto out;
976 : }
977 :
978 882 : enctype = smb_krb5_kt_get_enctype_from_entry(&kt_entry);
979 :
980 882 : ret = smb_krb5_enctype_to_string(context, enctype, &etype_s);
981 882 : if (ret &&
982 0 : (asprintf(&etype_s, "UNKNOWN: %d", enctype) == -1)) {
983 0 : TALLOC_FREE(princ_s);
984 0 : goto out;
985 : }
986 :
987 882 : printf("%3d %-43s %s\n", kt_entry.vno, etype_s, princ_s);
988 :
989 882 : TALLOC_FREE(princ_s);
990 882 : SAFE_FREE(etype_s);
991 :
992 882 : ret = smb_krb5_kt_free_entry(context, &kt_entry);
993 882 : if (ret) {
994 0 : goto out;
995 : }
996 : }
997 :
998 42 : ret = krb5_kt_end_seq_get(context, keytab, &cursor);
999 42 : if (ret) {
1000 0 : goto out;
1001 : }
1002 :
1003 : /* Ensure we don't double free. */
1004 42 : ZERO_STRUCT(kt_entry);
1005 42 : ZERO_STRUCT(cursor);
1006 42 : out:
1007 :
1008 42 : if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
1009 0 : smb_krb5_kt_free_entry(context, &kt_entry);
1010 : }
1011 42 : if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
1012 0 : krb5_kt_end_seq_get(context, keytab, &cursor);
1013 : }
1014 :
1015 42 : if (keytab) {
1016 42 : krb5_kt_close(context, keytab);
1017 : }
1018 42 : if (context) {
1019 42 : krb5_free_context(context);
1020 : }
1021 42 : return ret;
1022 : }
1023 :
1024 : #endif /* HAVE_KRB5 */
|