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 102 : static krb5_error_code ads_keytab_open(krb5_context context,
43 : krb5_keytab *keytab)
44 : {
45 102 : char keytab_str[MAX_KEYTAB_NAME_LEN] = {0};
46 102 : const char *keytab_name = NULL;
47 102 : krb5_error_code ret = 0;
48 :
49 102 : 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");
57 0 : goto out;
58 : }
59 0 : keytab_name = keytab_str;
60 0 : break;
61 102 : case KERBEROS_VERIFY_DEDICATED_KEYTAB:
62 102 : keytab_name = lp_dedicated_keytab_file();
63 102 : 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 102 : 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 102 : ret = smb_krb5_kt_open(context, keytab_name, true, keytab);
78 102 : 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 153 : out:
85 102 : 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 : 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 : 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 : 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 88 : 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 88 : char *princ_s = NULL;
188 88 : char *short_princ_s = NULL;
189 88 : const char *service = service_or_spn;
190 88 : const char *host = my_fqdn;
191 88 : struct spn_struct* spn_struct = NULL;
192 88 : char *tmp = NULL;
193 88 : bool ok = true;
194 :
195 : /* SPN should have '/' */
196 88 : tmp = strchr_m(service_or_spn, '/');
197 88 : if (tmp != NULL) {
198 8 : spn_struct = parse_spn(ctx, service_or_spn);
199 8 : if (spn_struct == NULL) {
200 4 : ok = false;
201 4 : goto out;
202 : }
203 : }
204 84 : if (spn_struct != NULL) {
205 4 : service = spn_struct->serviceclass;
206 4 : host = spn_struct->host;
207 : }
208 84 : princ_s = talloc_asprintf(ctx, "%s/%s@%s",
209 : service,
210 : host, lp_realm());
211 84 : if (princ_s == NULL) {
212 0 : ok = false;
213 0 : goto out;
214 : }
215 :
216 84 : if (spn_struct == NULL) {
217 80 : short_princ_s = talloc_asprintf(ctx, "%s/%s@%s",
218 : service, lp_netbios_name(),
219 : lp_realm());
220 80 : if (short_princ_s == NULL) {
221 0 : ok = false;
222 0 : goto out;
223 : }
224 : }
225 84 : *p_princ_s = princ_s;
226 84 : *p_short_princ_s = short_princ_s;
227 88 : out:
228 88 : 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 : #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
242 : ENCTYPE_AES256_CTS_HMAC_SHA1_96,
243 : #endif
244 : #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
245 : ENCTYPE_AES128_CTS_HMAC_SHA1_96,
246 : #endif
247 : ENCTYPE_ARCFOUR_HMAC,
248 : 0
249 : };
250 : size_t i;
251 :
252 : /* Construct our principal */
253 108 : if (strchr_m(srvPrinc, '@')) {
254 : /* It's a fully-named principal. */
255 4 : princ_s = talloc_asprintf(tmpctx, "%s", srvPrinc);
256 4 : if (!princ_s) {
257 0 : ret = -1;
258 0 : goto out;
259 : }
260 104 : } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
261 : /* It's the machine account, as used by smbclient clients. */
262 16 : princ_s = talloc_asprintf(tmpctx, "%s@%s",
263 : srvPrinc, lp_realm());
264 16 : if (!princ_s) {
265 0 : ret = -1;
266 0 : goto out;
267 : }
268 : } else {
269 : /* It's a normal service principal. Add the SPN now so that we
270 : * can obtain credentials for it and double-check the salt value
271 : * used to generate the service's keys. */
272 :
273 88 : if (!service_or_spn_to_kerberos_princ(tmpctx,
274 : srvPrinc,
275 : my_fqdn,
276 : &princ_s,
277 : &short_princ_s)) {
278 4 : ret = -1;
279 4 : goto out;
280 : }
281 :
282 : /* According to http://support.microsoft.com/kb/326985/en-us,
283 : certain principal names are automatically mapped to the
284 : host/... principal in the AD account.
285 : So only create these in the keytab, not in AD. --jerry */
286 :
287 85 : if (update_ads && !strequal(srvPrinc, "cifs") &&
288 2 : !strequal(srvPrinc, "host")) {
289 2 : if (!ads_set_machine_account_spns(tmpctx,
290 : ads,
291 : srvPrinc,
292 : my_fqdn)) {
293 0 : ret = -1;
294 0 : goto out;
295 : }
296 : }
297 : }
298 :
299 416 : for (i = 0; enctypes[i]; i++) {
300 :
301 : /* add the fqdn principal to the keytab */
302 312 : ret = smb_krb5_kt_add_entry(context,
303 : keytab,
304 : kvno,
305 : princ_s,
306 : salt_princ_s,
307 : enctypes[i],
308 : password,
309 : false,
310 : false);
311 312 : if (ret) {
312 0 : DBG_WARNING("Failed to add entry to keytab\n");
313 0 : goto out;
314 : }
315 :
316 : /* add the short principal name if we have one */
317 312 : if (short_princ_s) {
318 240 : ret = smb_krb5_kt_add_entry(context,
319 : keytab,
320 : kvno,
321 : short_princ_s,
322 : salt_princ_s,
323 : enctypes[i],
324 : password,
325 : false,
326 : false);
327 240 : if (ret) {
328 0 : DBG_WARNING("Failed to add short entry to keytab\n");
329 0 : goto out;
330 : }
331 : }
332 : }
333 104 : out:
334 108 : return ret;
335 : }
336 :
337 : /**********************************************************************
338 : Adds a single service principal, i.e. 'host' to the system keytab
339 : ***********************************************************************/
340 :
341 66 : int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc, bool update_ads)
342 : {
343 66 : krb5_error_code ret = 0;
344 66 : krb5_context context = NULL;
345 66 : krb5_keytab keytab = NULL;
346 : krb5_data password;
347 : krb5_kvno kvno;
348 66 : char *salt_princ_s = NULL;
349 66 : char *password_s = NULL;
350 : char *my_fqdn;
351 66 : TALLOC_CTX *tmpctx = NULL;
352 66 : char **hostnames_array = NULL;
353 66 : size_t num_hostnames = 0;
354 :
355 66 : ret = smb_krb5_init_context_common(&context);
356 66 : if (ret) {
357 0 : DBG_ERR("kerberos init context failed (%s)\n",
358 : error_message(ret));
359 0 : return -1;
360 : }
361 :
362 66 : ret = ads_keytab_open(context, &keytab);
363 66 : if (ret != 0) {
364 0 : goto out;
365 : }
366 :
367 : /* retrieve the password */
368 66 : if (!secrets_init()) {
369 0 : DBG_WARNING("secrets_init failed\n");
370 0 : ret = -1;
371 0 : goto out;
372 : }
373 66 : password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
374 66 : if (!password_s) {
375 0 : DBG_WARNING("failed to fetch machine password\n");
376 0 : ret = -1;
377 0 : goto out;
378 : }
379 66 : ZERO_STRUCT(password);
380 66 : password.data = password_s;
381 66 : password.length = strlen(password_s);
382 :
383 : /* we need the dNSHostName value here */
384 66 : tmpctx = talloc_init(__location__);
385 66 : if (!tmpctx) {
386 0 : DBG_ERR("talloc_init() failed!\n");
387 0 : ret = -1;
388 0 : goto out;
389 : }
390 :
391 66 : my_fqdn = ads_get_dnshostname(ads, tmpctx, lp_netbios_name());
392 66 : if (!my_fqdn) {
393 0 : DBG_ERR("unable to determine machine account's dns name in "
394 : "AD!\n");
395 0 : ret = -1;
396 0 : goto out;
397 : }
398 :
399 : /* make sure we have a single instance of a the computer account */
400 66 : if (!ads_has_samaccountname(ads, tmpctx, lp_netbios_name())) {
401 0 : DBG_ERR("unable to determine machine account's short name in "
402 : "AD!\n");
403 0 : ret = -1;
404 0 : goto out;
405 : }
406 :
407 66 : kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
408 66 : if (kvno == -1) {
409 : /* -1 indicates failure, everything else is OK */
410 0 : DBG_WARNING("ads_get_machine_kvno failed to determine the "
411 : "system's kvno.\n");
412 0 : ret = -1;
413 0 : goto out;
414 : }
415 :
416 66 : salt_princ_s = kerberos_secrets_fetch_salt_princ();
417 66 : if (salt_princ_s == NULL) {
418 0 : DBG_WARNING("kerberos_secrets_fetch_salt_princ() failed\n");
419 0 : ret = -1;
420 0 : goto out;
421 : }
422 :
423 66 : ret = add_kt_entry_etypes(context, tmpctx, ads, salt_princ_s, keytab,
424 : kvno, srvPrinc, my_fqdn, &password,
425 : update_ads);
426 66 : if (ret != 0) {
427 4 : goto out;
428 : }
429 :
430 62 : if (ADS_ERR_OK(ads_get_additional_dns_hostnames(tmpctx, ads,
431 : lp_netbios_name(),
432 : &hostnames_array,
433 : &num_hostnames))) {
434 : size_t i;
435 :
436 56 : for (i = 0; i < num_hostnames; i++) {
437 :
438 63 : ret = add_kt_entry_etypes(context, tmpctx, ads,
439 : salt_princ_s, keytab,
440 : kvno, srvPrinc,
441 42 : hostnames_array[i],
442 : &password, update_ads);
443 42 : if (ret != 0) {
444 0 : goto out;
445 : }
446 : }
447 : }
448 :
449 62 : out:
450 66 : SAFE_FREE(salt_princ_s);
451 66 : TALLOC_FREE(tmpctx);
452 :
453 66 : if (keytab) {
454 66 : krb5_kt_close(context, keytab);
455 : }
456 66 : if (context) {
457 66 : krb5_free_context(context);
458 : }
459 66 : return (int)ret;
460 : }
461 :
462 : /**********************************************************************
463 : Flushes all entries from the system keytab.
464 : ***********************************************************************/
465 :
466 0 : int ads_keytab_flush(ADS_STRUCT *ads)
467 : {
468 0 : krb5_error_code ret = 0;
469 0 : krb5_context context = NULL;
470 0 : krb5_keytab keytab = NULL;
471 : krb5_kvno kvno;
472 : ADS_STATUS aderr;
473 :
474 0 : ret = smb_krb5_init_context_common(&context);
475 0 : if (ret) {
476 0 : DBG_ERR("kerberos init context failed (%s)\n",
477 : error_message(ret));
478 0 : return ret;
479 : }
480 :
481 0 : ret = ads_keytab_open(context, &keytab);
482 0 : if (ret != 0) {
483 0 : goto out;
484 : }
485 :
486 0 : kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
487 0 : if (kvno == -1) {
488 : /* -1 indicates a failure */
489 0 : DEBUG(1, (__location__ ": Error determining the kvno.\n"));
490 0 : ret = -1;
491 0 : goto out;
492 : }
493 :
494 : /* Seek and delete old keytab entries */
495 0 : ret = smb_krb5_kt_seek_and_delete_old_entries(context,
496 : keytab,
497 : kvno,
498 : ENCTYPE_NULL,
499 : NULL,
500 : NULL,
501 : true,
502 : false);
503 0 : if (ret) {
504 0 : goto out;
505 : }
506 :
507 0 : aderr = ads_clear_service_principal_names(ads, lp_netbios_name());
508 0 : if (!ADS_ERR_OK(aderr)) {
509 0 : DEBUG(1, (__location__ ": Error while clearing service "
510 : "principal listings in LDAP.\n"));
511 0 : ret = -1;
512 0 : goto out;
513 : }
514 :
515 0 : out:
516 0 : if (keytab) {
517 0 : krb5_kt_close(context, keytab);
518 : }
519 0 : if (context) {
520 0 : krb5_free_context(context);
521 : }
522 0 : return ret;
523 : }
524 :
525 : /**********************************************************************
526 : Adds all the required service principals to the system keytab.
527 : ***********************************************************************/
528 :
529 8 : int ads_keytab_create_default(ADS_STRUCT *ads)
530 : {
531 8 : krb5_error_code ret = 0;
532 8 : krb5_context context = NULL;
533 8 : krb5_keytab keytab = NULL;
534 8 : krb5_kt_cursor cursor = {0};
535 8 : krb5_keytab_entry kt_entry = {0};
536 : krb5_kvno kvno;
537 8 : size_t found = 0;
538 : char *sam_account_name, *upn;
539 8 : char **oldEntries = NULL, *princ_s[26];
540 : TALLOC_CTX *frame;
541 : char *machine_name;
542 : char **spn_array;
543 : size_t num_spns;
544 : size_t i;
545 8 : bool ok = false;
546 : ADS_STATUS status;
547 :
548 8 : ZERO_STRUCT(kt_entry);
549 8 : ZERO_STRUCT(cursor);
550 :
551 8 : frame = talloc_stackframe();
552 8 : if (frame == NULL) {
553 0 : ret = -1;
554 0 : goto done;
555 : }
556 :
557 8 : status = ads_get_service_principal_names(frame,
558 : ads,
559 : lp_netbios_name(),
560 : &spn_array,
561 : &num_spns);
562 8 : if (!ADS_ERR_OK(status)) {
563 0 : ret = -1;
564 0 : goto done;
565 : }
566 :
567 48 : for (i = 0; i < num_spns; i++) {
568 : char *srv_princ;
569 : char *p;
570 :
571 40 : srv_princ = strlower_talloc(frame, spn_array[i]);
572 40 : if (srv_princ == NULL) {
573 0 : ret = -1;
574 0 : goto done;
575 : }
576 :
577 40 : p = strchr_m(srv_princ, '/');
578 40 : if (p == NULL) {
579 0 : continue;
580 : }
581 40 : p[0] = '\0';
582 :
583 : /* Add the SPNs found on the DC */
584 40 : ret = ads_keytab_add_entry(ads, srv_princ, false);
585 40 : if (ret != 0) {
586 0 : DEBUG(1, ("ads_keytab_add_entry failed while "
587 : "adding '%s' principal.\n",
588 : spn_array[i]));
589 0 : goto done;
590 : }
591 : }
592 :
593 : #if 0 /* don't create the CIFS/... keytab entries since no one except smbd
594 : really needs them and we will fall back to verifying against
595 : secrets.tdb */
596 :
597 : ret = ads_keytab_add_entry(ads, "cifs", false));
598 : if (ret != 0 ) {
599 : DEBUG(1, (__location__ ": ads_keytab_add_entry failed while "
600 : "adding 'cifs'.\n"));
601 : return ret;
602 : }
603 : #endif
604 :
605 8 : memset(princ_s, '\0', sizeof(princ_s));
606 :
607 8 : ret = smb_krb5_init_context_common(&context);
608 8 : if (ret) {
609 0 : DBG_ERR("kerberos init context failed (%s)\n",
610 : error_message(ret));
611 0 : goto done;
612 : }
613 :
614 8 : machine_name = talloc_strdup(frame, lp_netbios_name());
615 8 : if (!machine_name) {
616 0 : ret = -1;
617 0 : goto done;
618 : }
619 :
620 : /* now add the userPrincipalName and sAMAccountName entries */
621 8 : ok = ads_has_samaccountname(ads, frame, machine_name);
622 8 : if (!ok) {
623 0 : DEBUG(0, (__location__ ": unable to determine machine "
624 : "account's name in AD!\n"));
625 0 : ret = -1;
626 0 : goto done;
627 : }
628 :
629 : /*
630 : * append '$' to netbios name so 'ads_keytab_add_entry' recognises
631 : * it as a machine account rather than a service or Windows SPN.
632 : */
633 8 : sam_account_name = talloc_asprintf(frame, "%s$",machine_name);
634 8 : if (sam_account_name == NULL) {
635 0 : ret = -1;
636 0 : goto done;
637 : }
638 : /* upper case the sAMAccountName to make it easier for apps to
639 : know what case to use in the keytab file */
640 8 : if (!strupper_m(sam_account_name)) {
641 0 : ret = -1;
642 0 : goto done;
643 : }
644 :
645 8 : ret = ads_keytab_add_entry(ads, sam_account_name, false);
646 8 : if (ret != 0) {
647 0 : DEBUG(1, (__location__ ": ads_keytab_add_entry() failed "
648 : "while adding sAMAccountName (%s)\n",
649 : sam_account_name));
650 0 : goto done;
651 : }
652 :
653 : /* remember that not every machine account will have a upn */
654 8 : upn = ads_get_upn(ads, frame, machine_name);
655 8 : if (upn) {
656 2 : ret = ads_keytab_add_entry(ads, upn, false);
657 2 : if (ret != 0) {
658 0 : DEBUG(1, (__location__ ": ads_keytab_add_entry() "
659 : "failed while adding UPN (%s)\n", upn));
660 0 : goto done;
661 : }
662 : }
663 :
664 : /* Now loop through the keytab and update any other existing entries */
665 8 : kvno = (krb5_kvno)ads_get_machine_kvno(ads, machine_name);
666 8 : if (kvno == (krb5_kvno)-1) {
667 0 : DEBUG(1, (__location__ ": ads_get_machine_kvno() failed to "
668 : "determine the system's kvno.\n"));
669 0 : goto done;
670 : }
671 :
672 8 : DEBUG(3, (__location__ ": Searching for keytab entries to preserve "
673 : "and update.\n"));
674 :
675 8 : ret = ads_keytab_open(context, &keytab);
676 8 : if (ret != 0) {
677 0 : goto done;
678 : }
679 :
680 8 : ret = krb5_kt_start_seq_get(context, keytab, &cursor);
681 8 : if (ret != KRB5_KT_END && ret != ENOENT ) {
682 216 : while ((ret = krb5_kt_next_entry(context, keytab,
683 106 : &kt_entry, &cursor)) == 0) {
684 204 : smb_krb5_kt_free_entry(context, &kt_entry);
685 204 : ZERO_STRUCT(kt_entry);
686 204 : found++;
687 : }
688 : }
689 8 : krb5_kt_end_seq_get(context, keytab, &cursor);
690 8 : ZERO_STRUCT(cursor);
691 :
692 : /*
693 : * Hmmm. There is no "rewind" function for the keytab. This means we
694 : * have a race condition where someone else could add entries after
695 : * we've counted them. Re-open asap to minimise the race. JRA.
696 : */
697 8 : DEBUG(3, (__location__ ": Found %zd entries in the keytab.\n", found));
698 8 : if (!found) {
699 0 : goto done;
700 : }
701 :
702 8 : oldEntries = talloc_zero_array(frame, char *, found + 1);
703 8 : if (!oldEntries) {
704 0 : DEBUG(1, (__location__ ": Failed to allocate space to store "
705 : "the old keytab entries (talloc failed?).\n"));
706 0 : ret = -1;
707 0 : goto done;
708 : }
709 :
710 8 : ret = krb5_kt_start_seq_get(context, keytab, &cursor);
711 8 : if (ret == KRB5_KT_END || ret == ENOENT) {
712 0 : krb5_kt_end_seq_get(context, keytab, &cursor);
713 0 : ZERO_STRUCT(cursor);
714 0 : goto done;
715 : }
716 :
717 216 : while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
718 204 : if (kt_entry.vno != kvno) {
719 0 : char *ktprinc = NULL;
720 : char *p;
721 :
722 : /* This returns a malloc'ed string in ktprinc. */
723 0 : ret = smb_krb5_unparse_name(oldEntries, context,
724 0 : kt_entry.principal,
725 : &ktprinc);
726 0 : if (ret) {
727 0 : DEBUG(1, (__location__
728 : ": smb_krb5_unparse_name failed "
729 : "(%s)\n", error_message(ret)));
730 0 : goto done;
731 : }
732 : /*
733 : * From looking at the krb5 source they don't seem to
734 : * take locale or mb strings into account.
735 : * Maybe this is because they assume utf8 ?
736 : * In this case we may need to convert from utf8 to
737 : * mb charset here ? JRA.
738 : */
739 0 : p = strchr_m(ktprinc, '@');
740 0 : if (p) {
741 0 : *p = '\0';
742 : }
743 :
744 0 : p = strchr_m(ktprinc, '/');
745 0 : if (p) {
746 0 : *p = '\0';
747 : }
748 0 : for (i = 0; i < found; i++) {
749 0 : if (!oldEntries[i]) {
750 0 : oldEntries[i] = ktprinc;
751 0 : break;
752 : }
753 0 : if (!strcmp(oldEntries[i], ktprinc)) {
754 0 : TALLOC_FREE(ktprinc);
755 0 : break;
756 : }
757 : }
758 0 : if (i == found) {
759 0 : TALLOC_FREE(ktprinc);
760 : }
761 : }
762 204 : smb_krb5_kt_free_entry(context, &kt_entry);
763 204 : ZERO_STRUCT(kt_entry);
764 : }
765 8 : krb5_kt_end_seq_get(context, keytab, &cursor);
766 8 : ZERO_STRUCT(cursor);
767 :
768 8 : ret = 0;
769 8 : for (i = 0; oldEntries[i]; i++) {
770 0 : ret |= ads_keytab_add_entry(ads, oldEntries[i], false);
771 0 : TALLOC_FREE(oldEntries[i]);
772 : }
773 :
774 8 : done:
775 8 : TALLOC_FREE(oldEntries);
776 8 : TALLOC_FREE(frame);
777 :
778 8 : if (context) {
779 8 : if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
780 0 : smb_krb5_kt_free_entry(context, &kt_entry);
781 : }
782 8 : if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
783 0 : krb5_kt_end_seq_get(context, keytab, &cursor);
784 : }
785 8 : if (keytab) {
786 8 : krb5_kt_close(context, keytab);
787 : }
788 8 : krb5_free_context(context);
789 : }
790 8 : return ret;
791 : }
792 :
793 : #endif /* HAVE_ADS */
794 :
795 : /**********************************************************************
796 : List system keytab.
797 : ***********************************************************************/
798 :
799 30 : int ads_keytab_list(const char *keytab_name)
800 : {
801 30 : krb5_error_code ret = 0;
802 30 : krb5_context context = NULL;
803 30 : krb5_keytab keytab = NULL;
804 : krb5_kt_cursor cursor;
805 : krb5_keytab_entry kt_entry;
806 :
807 30 : ZERO_STRUCT(kt_entry);
808 30 : ZERO_STRUCT(cursor);
809 :
810 30 : ret = smb_krb5_init_context_common(&context);
811 30 : if (ret) {
812 0 : DBG_ERR("kerberos init context failed (%s)\n",
813 : error_message(ret));
814 0 : return ret;
815 : }
816 :
817 30 : if (keytab_name == NULL) {
818 : #ifdef HAVE_ADS
819 28 : ret = ads_keytab_open(context, &keytab);
820 : #else
821 0 : ret = ENOENT;
822 : #endif
823 : } else {
824 2 : ret = smb_krb5_kt_open(context, keytab_name, False, &keytab);
825 : }
826 30 : if (ret) {
827 0 : DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
828 : error_message(ret)));
829 0 : goto out;
830 : }
831 :
832 30 : ret = krb5_kt_start_seq_get(context, keytab, &cursor);
833 30 : if (ret) {
834 0 : ZERO_STRUCT(cursor);
835 0 : goto out;
836 : }
837 :
838 30 : printf("Vno Type Principal\n");
839 :
840 369 : while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
841 :
842 678 : char *princ_s = NULL;
843 678 : char *etype_s = NULL;
844 678 : krb5_enctype enctype = 0;
845 :
846 678 : ret = smb_krb5_unparse_name(talloc_tos(), context,
847 678 : kt_entry.principal, &princ_s);
848 678 : if (ret) {
849 0 : goto out;
850 : }
851 :
852 678 : enctype = smb_krb5_kt_get_enctype_from_entry(&kt_entry);
853 :
854 678 : ret = smb_krb5_enctype_to_string(context, enctype, &etype_s);
855 678 : if (ret &&
856 0 : (asprintf(&etype_s, "UNKNOWN: %d", enctype) == -1)) {
857 0 : TALLOC_FREE(princ_s);
858 0 : goto out;
859 : }
860 :
861 678 : printf("%3d %-43s %s\n", kt_entry.vno, etype_s, princ_s);
862 :
863 678 : TALLOC_FREE(princ_s);
864 678 : SAFE_FREE(etype_s);
865 :
866 678 : ret = smb_krb5_kt_free_entry(context, &kt_entry);
867 678 : if (ret) {
868 0 : goto out;
869 : }
870 : }
871 :
872 30 : ret = krb5_kt_end_seq_get(context, keytab, &cursor);
873 30 : if (ret) {
874 0 : goto out;
875 : }
876 :
877 : /* Ensure we don't double free. */
878 30 : ZERO_STRUCT(kt_entry);
879 30 : ZERO_STRUCT(cursor);
880 30 : out:
881 :
882 30 : if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
883 0 : smb_krb5_kt_free_entry(context, &kt_entry);
884 : }
885 30 : if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
886 0 : krb5_kt_end_seq_get(context, keytab, &cursor);
887 : }
888 :
889 30 : if (keytab) {
890 30 : krb5_kt_close(context, keytab);
891 : }
892 30 : if (context) {
893 30 : krb5_free_context(context);
894 : }
895 30 : return ret;
896 : }
897 :
898 : #endif /* HAVE_KRB5 */
|