VERSION: Bump version up to 4.10.8...
[Samba.git] / source3 / libads / kerberos_keytab.c
blob97d5535041c5a43fbb18fd3b2bf090cd1d65223f
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
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.
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.
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/>.
28 #include "includes.h"
29 #include "smb_krb5.h"
30 #include "ads.h"
31 #include "secrets.h"
33 #ifdef HAVE_KRB5
35 #ifdef HAVE_ADS
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
42 static krb5_error_code ads_keytab_open(krb5_context context,
43 krb5_keytab *keytab)
45 char keytab_str[MAX_KEYTAB_NAME_LEN] = {0};
46 const char *keytab_name = NULL;
47 krb5_error_code ret = 0;
49 switch (lp_kerberos_method()) {
50 case KERBEROS_VERIFY_SYSTEM_KEYTAB:
51 case KERBEROS_VERIFY_SECRETS_AND_KEYTAB:
52 ret = krb5_kt_default_name(context,
53 keytab_str,
54 sizeof(keytab_str) - 2);
55 if (ret != 0) {
56 DBG_WARNING("Failed to get default keytab name");
57 goto out;
59 keytab_name = keytab_str;
60 break;
61 case KERBEROS_VERIFY_DEDICATED_KEYTAB:
62 keytab_name = lp_dedicated_keytab_file();
63 break;
64 default:
65 DBG_ERR("Invalid kerberos method set (%d)\n",
66 lp_kerberos_method());
67 ret = KRB5_KT_BADNAME;
68 goto out;
71 if (keytab_name == NULL || keytab_name[0] == '\0') {
72 DBG_ERR("Invalid keytab name\n");
73 ret = KRB5_KT_BADNAME;
74 goto out;
77 ret = smb_krb5_kt_open(context, keytab_name, true, keytab);
78 if (ret != 0) {
79 DBG_WARNING("smb_krb5_kt_open failed (%s)\n",
80 error_message(ret));
81 goto out;
84 out:
85 return ret;
88 static bool fill_default_spns(TALLOC_CTX *ctx, const char *machine_name,
89 const char *my_fqdn, const char *spn,
90 const char ***spns)
92 char *psp1, *psp2;
94 if (*spns == NULL) {
95 *spns = talloc_zero_array(ctx, const char*, 3);
96 if (*spns == NULL) {
97 return false;
101 psp1 = talloc_asprintf(ctx,
102 "%s/%s",
103 spn,
104 machine_name);
105 if (psp1 == NULL) {
106 return false;
109 if (!strlower_m(&psp1[strlen(spn) + 1])) {
110 return false;
112 (*spns)[0] = psp1;
114 psp2 = talloc_asprintf(ctx,
115 "%s/%s",
116 spn,
117 my_fqdn);
118 if (psp2 == NULL) {
119 return false;
122 if (!strlower_m(&psp2[strlen(spn) + 1])) {
123 return false;
126 (*spns)[1] = psp2;
128 return true;
131 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)
136 const char **spn_names = NULL;
137 ADS_STATUS aderr;
138 struct spn_struct* spn_struct = NULL;
139 char *tmp = NULL;
141 /* SPN should have '/' */
142 tmp = strchr_m(service_or_spn, '/');
143 if (tmp != NULL) {
144 spn_struct = parse_spn(ctx, service_or_spn);
145 if (spn_struct == NULL) {
146 return false;
150 DBG_INFO("Attempting to add/update '%s'\n", service_or_spn);
152 if (spn_struct != NULL) {
153 spn_names = talloc_zero_array(ctx, const char*, 2);
154 spn_names[0] = service_or_spn;
155 } else {
156 bool ok;
158 ok = fill_default_spns(ctx,
159 lp_netbios_name(),
160 my_fqdn,
161 service_or_spn,
162 &spn_names);
163 if (!ok) {
164 return false;
167 aderr = ads_add_service_principal_names(ads,
168 lp_netbios_name(),
169 spn_names);
170 if (!ADS_ERR_OK(aderr)) {
171 DBG_WARNING("Failed to add service principal name.\n");
172 return false;
175 return true;
179 * Create kerberos principal(s) from SPN or service name.
181 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)
187 char *princ_s = NULL;
188 char *short_princ_s = NULL;
189 const char *service = service_or_spn;
190 const char *host = my_fqdn;
191 struct spn_struct* spn_struct = NULL;
192 char *tmp = NULL;
193 bool ok = true;
195 /* SPN should have '/' */
196 tmp = strchr_m(service_or_spn, '/');
197 if (tmp != NULL) {
198 spn_struct = parse_spn(ctx, service_or_spn);
199 if (spn_struct == NULL) {
200 ok = false;
201 goto out;
204 if (spn_struct != NULL) {
205 service = spn_struct->serviceclass;
206 host = spn_struct->host;
208 princ_s = talloc_asprintf(ctx, "%s/%s@%s",
209 service,
210 host, lp_realm());
211 if (princ_s == NULL) {
212 ok = false;
213 goto out;
216 if (spn_struct == NULL) {
217 short_princ_s = talloc_asprintf(ctx, "%s/%s@%s",
218 service, lp_netbios_name(),
219 lp_realm());
220 if (short_princ_s == NULL) {
221 ok = false;
222 goto out;
225 *p_princ_s = princ_s;
226 *p_short_princ_s = short_princ_s;
227 out:
228 return ok;
231 /**********************************************************************
232 Adds a single service principal, i.e. 'host' to the system keytab
233 ***********************************************************************/
235 int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc, bool update_ads)
237 krb5_error_code ret = 0;
238 krb5_context context = NULL;
239 krb5_keytab keytab = NULL;
240 krb5_data password;
241 krb5_kvno kvno;
242 krb5_enctype enctypes[6] = {
243 ENCTYPE_DES_CBC_CRC,
244 ENCTYPE_DES_CBC_MD5,
245 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
246 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
247 #endif
248 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
249 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
250 #endif
251 ENCTYPE_ARCFOUR_HMAC,
254 char *princ_s = NULL;
255 char *short_princ_s = NULL;
256 char *salt_princ_s = NULL;
257 char *password_s = NULL;
258 char *my_fqdn;
259 TALLOC_CTX *tmpctx = NULL;
260 int i;
262 ret = smb_krb5_init_context_common(&context);
263 if (ret) {
264 DBG_ERR("kerberos init context failed (%s)\n",
265 error_message(ret));
266 return -1;
269 ret = ads_keytab_open(context, &keytab);
270 if (ret != 0) {
271 goto out;
274 /* retrieve the password */
275 if (!secrets_init()) {
276 DEBUG(1, (__location__ ": secrets_init failed\n"));
277 ret = -1;
278 goto out;
280 password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
281 if (!password_s) {
282 DEBUG(1, (__location__ ": failed to fetch machine password\n"));
283 ret = -1;
284 goto out;
286 ZERO_STRUCT(password);
287 password.data = password_s;
288 password.length = strlen(password_s);
290 /* we need the dNSHostName value here */
291 tmpctx = talloc_init(__location__);
292 if (!tmpctx) {
293 DEBUG(0, (__location__ ": talloc_init() failed!\n"));
294 ret = -1;
295 goto out;
298 my_fqdn = ads_get_dnshostname(ads, tmpctx, lp_netbios_name());
299 if (!my_fqdn) {
300 DEBUG(0, (__location__ ": unable to determine machine "
301 "account's dns name in AD!\n"));
302 ret = -1;
303 goto out;
306 /* make sure we have a single instance of a the computer account */
307 if (!ads_has_samaccountname(ads, tmpctx, lp_netbios_name())) {
308 DEBUG(0, (__location__ ": unable to determine machine "
309 "account's short name in AD!\n"));
310 ret = -1;
311 goto out;
314 /* Construct our principal */
315 if (strchr_m(srvPrinc, '@')) {
316 /* It's a fully-named principal. */
317 princ_s = talloc_asprintf(tmpctx, "%s", srvPrinc);
318 if (!princ_s) {
319 ret = -1;
320 goto out;
322 } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
323 /* It's the machine account, as used by smbclient clients. */
324 princ_s = talloc_asprintf(tmpctx, "%s@%s",
325 srvPrinc, lp_realm());
326 if (!princ_s) {
327 ret = -1;
328 goto out;
330 } else {
331 /* It's a normal service principal. Add the SPN now so that we
332 * can obtain credentials for it and double-check the salt value
333 * used to generate the service's keys. */
335 if (!service_or_spn_to_kerberos_princ(tmpctx,
336 srvPrinc,
337 my_fqdn,
338 &princ_s,
339 &short_princ_s)) {
340 ret = -1;
341 goto out;
344 /* According to http://support.microsoft.com/kb/326985/en-us,
345 certain principal names are automatically mapped to the
346 host/... principal in the AD account.
347 So only create these in the keytab, not in AD. --jerry */
349 if (update_ads && !strequal(srvPrinc, "cifs") &&
350 !strequal(srvPrinc, "host")) {
351 if (!ads_set_machine_account_spns(tmpctx,
352 ads,
353 srvPrinc,
354 my_fqdn)) {
355 ret = -1;
356 goto out;
361 kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
362 if (kvno == -1) {
363 /* -1 indicates failure, everything else is OK */
364 DEBUG(1, (__location__ ": ads_get_machine_kvno failed to "
365 "determine the system's kvno.\n"));
366 ret = -1;
367 goto out;
370 salt_princ_s = kerberos_secrets_fetch_salt_princ();
371 if (salt_princ_s == NULL) {
372 DBG_WARNING("kerberos_secrets_fetch_salt_princ() failed\n");
373 ret = -1;
374 goto out;
377 for (i = 0; enctypes[i]; i++) {
379 /* add the fqdn principal to the keytab */
380 ret = smb_krb5_kt_add_entry(context,
381 keytab,
382 kvno,
383 princ_s,
384 salt_princ_s,
385 enctypes[i],
386 &password,
387 false,
388 false);
389 if (ret) {
390 DEBUG(1, (__location__ ": Failed to add entry to keytab\n"));
391 goto out;
394 /* add the short principal name if we have one */
395 if (short_princ_s) {
396 ret = smb_krb5_kt_add_entry(context,
397 keytab,
398 kvno,
399 short_princ_s,
400 salt_princ_s,
401 enctypes[i],
402 &password,
403 false,
404 false);
405 if (ret) {
406 DEBUG(1, (__location__
407 ": Failed to add short entry to keytab\n"));
408 goto out;
413 out:
414 SAFE_FREE(salt_princ_s);
415 TALLOC_FREE(tmpctx);
417 if (keytab) {
418 krb5_kt_close(context, keytab);
420 if (context) {
421 krb5_free_context(context);
423 return (int)ret;
426 /**********************************************************************
427 Flushes all entries from the system keytab.
428 ***********************************************************************/
430 int ads_keytab_flush(ADS_STRUCT *ads)
432 krb5_error_code ret = 0;
433 krb5_context context = NULL;
434 krb5_keytab keytab = NULL;
435 krb5_kvno kvno;
436 ADS_STATUS aderr;
438 ret = smb_krb5_init_context_common(&context);
439 if (ret) {
440 DBG_ERR("kerberos init context failed (%s)\n",
441 error_message(ret));
442 return ret;
445 ret = ads_keytab_open(context, &keytab);
446 if (ret != 0) {
447 goto out;
450 kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
451 if (kvno == -1) {
452 /* -1 indicates a failure */
453 DEBUG(1, (__location__ ": Error determining the kvno.\n"));
454 ret = -1;
455 goto out;
458 /* Seek and delete old keytab entries */
459 ret = smb_krb5_kt_seek_and_delete_old_entries(context,
460 keytab,
461 kvno,
462 ENCTYPE_NULL,
463 NULL,
464 NULL,
465 true,
466 false);
467 if (ret) {
468 goto out;
471 aderr = ads_clear_service_principal_names(ads, lp_netbios_name());
472 if (!ADS_ERR_OK(aderr)) {
473 DEBUG(1, (__location__ ": Error while clearing service "
474 "principal listings in LDAP.\n"));
475 ret = -1;
476 goto out;
479 out:
480 if (keytab) {
481 krb5_kt_close(context, keytab);
483 if (context) {
484 krb5_free_context(context);
486 return ret;
489 /**********************************************************************
490 Adds all the required service principals to the system keytab.
491 ***********************************************************************/
493 int ads_keytab_create_default(ADS_STRUCT *ads)
495 krb5_error_code ret = 0;
496 krb5_context context = NULL;
497 krb5_keytab keytab = NULL;
498 krb5_kt_cursor cursor = {0};
499 krb5_keytab_entry kt_entry = {0};
500 krb5_kvno kvno;
501 size_t found = 0;
502 char *sam_account_name, *upn;
503 char **oldEntries = NULL, *princ_s[26];
504 TALLOC_CTX *frame;
505 char *machine_name;
506 char **spn_array;
507 size_t num_spns;
508 size_t i;
509 bool ok = false;
510 ADS_STATUS status;
512 ZERO_STRUCT(kt_entry);
513 ZERO_STRUCT(cursor);
515 frame = talloc_stackframe();
516 if (frame == NULL) {
517 ret = -1;
518 goto done;
521 status = ads_get_service_principal_names(frame,
522 ads,
523 lp_netbios_name(),
524 &spn_array,
525 &num_spns);
526 if (!ADS_ERR_OK(status)) {
527 ret = -1;
528 goto done;
531 for (i = 0; i < num_spns; i++) {
532 char *srv_princ;
533 char *p;
535 srv_princ = strlower_talloc(frame, spn_array[i]);
536 if (srv_princ == NULL) {
537 ret = -1;
538 goto done;
541 p = strchr_m(srv_princ, '/');
542 if (p == NULL) {
543 continue;
545 p[0] = '\0';
547 /* Add the SPNs found on the DC */
548 ret = ads_keytab_add_entry(ads, srv_princ, false);
549 if (ret != 0) {
550 DEBUG(1, ("ads_keytab_add_entry failed while "
551 "adding '%s' principal.\n",
552 spn_array[i]));
553 goto done;
557 #if 0 /* don't create the CIFS/... keytab entries since no one except smbd
558 really needs them and we will fall back to verifying against
559 secrets.tdb */
561 ret = ads_keytab_add_entry(ads, "cifs", false));
562 if (ret != 0 ) {
563 DEBUG(1, (__location__ ": ads_keytab_add_entry failed while "
564 "adding 'cifs'.\n"));
565 return ret;
567 #endif
569 memset(princ_s, '\0', sizeof(princ_s));
571 ret = smb_krb5_init_context_common(&context);
572 if (ret) {
573 DBG_ERR("kerberos init context failed (%s)\n",
574 error_message(ret));
575 goto done;
578 machine_name = talloc_strdup(frame, lp_netbios_name());
579 if (!machine_name) {
580 ret = -1;
581 goto done;
584 /* now add the userPrincipalName and sAMAccountName entries */
585 ok = ads_has_samaccountname(ads, frame, machine_name);
586 if (!ok) {
587 DEBUG(0, (__location__ ": unable to determine machine "
588 "account's name in AD!\n"));
589 ret = -1;
590 goto done;
594 * append '$' to netbios name so 'ads_keytab_add_entry' recognises
595 * it as a machine account rather than a service or Windows SPN.
597 sam_account_name = talloc_asprintf(frame, "%s$",machine_name);
598 if (sam_account_name == NULL) {
599 ret = -1;
600 goto done;
602 /* upper case the sAMAccountName to make it easier for apps to
603 know what case to use in the keytab file */
604 if (!strupper_m(sam_account_name)) {
605 ret = -1;
606 goto done;
609 ret = ads_keytab_add_entry(ads, sam_account_name, false);
610 if (ret != 0) {
611 DEBUG(1, (__location__ ": ads_keytab_add_entry() failed "
612 "while adding sAMAccountName (%s)\n",
613 sam_account_name));
614 goto done;
617 /* remember that not every machine account will have a upn */
618 upn = ads_get_upn(ads, frame, machine_name);
619 if (upn) {
620 ret = ads_keytab_add_entry(ads, upn, false);
621 if (ret != 0) {
622 DEBUG(1, (__location__ ": ads_keytab_add_entry() "
623 "failed while adding UPN (%s)\n", upn));
624 goto done;
628 /* Now loop through the keytab and update any other existing entries */
629 kvno = (krb5_kvno)ads_get_machine_kvno(ads, machine_name);
630 if (kvno == (krb5_kvno)-1) {
631 DEBUG(1, (__location__ ": ads_get_machine_kvno() failed to "
632 "determine the system's kvno.\n"));
633 goto done;
636 DEBUG(3, (__location__ ": Searching for keytab entries to preserve "
637 "and update.\n"));
639 ret = ads_keytab_open(context, &keytab);
640 if (ret != 0) {
641 goto done;
644 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
645 if (ret != KRB5_KT_END && ret != ENOENT ) {
646 while ((ret = krb5_kt_next_entry(context, keytab,
647 &kt_entry, &cursor)) == 0) {
648 smb_krb5_kt_free_entry(context, &kt_entry);
649 ZERO_STRUCT(kt_entry);
650 found++;
653 krb5_kt_end_seq_get(context, keytab, &cursor);
654 ZERO_STRUCT(cursor);
657 * Hmmm. There is no "rewind" function for the keytab. This means we
658 * have a race condition where someone else could add entries after
659 * we've counted them. Re-open asap to minimise the race. JRA.
661 DEBUG(3, (__location__ ": Found %zd entries in the keytab.\n", found));
662 if (!found) {
663 goto done;
666 oldEntries = talloc_zero_array(frame, char *, found + 1);
667 if (!oldEntries) {
668 DEBUG(1, (__location__ ": Failed to allocate space to store "
669 "the old keytab entries (talloc failed?).\n"));
670 ret = -1;
671 goto done;
674 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
675 if (ret == KRB5_KT_END || ret == ENOENT) {
676 krb5_kt_end_seq_get(context, keytab, &cursor);
677 ZERO_STRUCT(cursor);
678 goto done;
681 while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
682 if (kt_entry.vno != kvno) {
683 char *ktprinc = NULL;
684 char *p;
686 /* This returns a malloc'ed string in ktprinc. */
687 ret = smb_krb5_unparse_name(oldEntries, context,
688 kt_entry.principal,
689 &ktprinc);
690 if (ret) {
691 DEBUG(1, (__location__
692 ": smb_krb5_unparse_name failed "
693 "(%s)\n", error_message(ret)));
694 goto done;
697 * From looking at the krb5 source they don't seem to
698 * take locale or mb strings into account.
699 * Maybe this is because they assume utf8 ?
700 * In this case we may need to convert from utf8 to
701 * mb charset here ? JRA.
703 p = strchr_m(ktprinc, '@');
704 if (p) {
705 *p = '\0';
708 p = strchr_m(ktprinc, '/');
709 if (p) {
710 *p = '\0';
712 for (i = 0; i < found; i++) {
713 if (!oldEntries[i]) {
714 oldEntries[i] = ktprinc;
715 break;
717 if (!strcmp(oldEntries[i], ktprinc)) {
718 TALLOC_FREE(ktprinc);
719 break;
722 if (i == found) {
723 TALLOC_FREE(ktprinc);
726 smb_krb5_kt_free_entry(context, &kt_entry);
727 ZERO_STRUCT(kt_entry);
729 krb5_kt_end_seq_get(context, keytab, &cursor);
730 ZERO_STRUCT(cursor);
732 ret = 0;
733 for (i = 0; oldEntries[i]; i++) {
734 ret |= ads_keytab_add_entry(ads, oldEntries[i], false);
735 TALLOC_FREE(oldEntries[i]);
738 done:
739 TALLOC_FREE(oldEntries);
740 TALLOC_FREE(frame);
742 if (context) {
743 if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
744 smb_krb5_kt_free_entry(context, &kt_entry);
746 if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
747 krb5_kt_end_seq_get(context, keytab, &cursor);
749 if (keytab) {
750 krb5_kt_close(context, keytab);
752 krb5_free_context(context);
754 return ret;
757 #endif /* HAVE_ADS */
759 /**********************************************************************
760 List system keytab.
761 ***********************************************************************/
763 int ads_keytab_list(const char *keytab_name)
765 krb5_error_code ret = 0;
766 krb5_context context = NULL;
767 krb5_keytab keytab = NULL;
768 krb5_kt_cursor cursor;
769 krb5_keytab_entry kt_entry;
771 ZERO_STRUCT(kt_entry);
772 ZERO_STRUCT(cursor);
774 ret = smb_krb5_init_context_common(&context);
775 if (ret) {
776 DBG_ERR("kerberos init context failed (%s)\n",
777 error_message(ret));
778 return ret;
781 if (keytab_name == NULL) {
782 #ifdef HAVE_ADS
783 ret = ads_keytab_open(context, &keytab);
784 #else
785 ret = ENOENT;
786 #endif
787 } else {
788 ret = smb_krb5_kt_open(context, keytab_name, False, &keytab);
790 if (ret) {
791 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
792 error_message(ret)));
793 goto out;
796 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
797 if (ret) {
798 ZERO_STRUCT(cursor);
799 goto out;
802 printf("Vno Type Principal\n");
804 while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
806 char *princ_s = NULL;
807 char *etype_s = NULL;
808 krb5_enctype enctype = 0;
810 ret = smb_krb5_unparse_name(talloc_tos(), context,
811 kt_entry.principal, &princ_s);
812 if (ret) {
813 goto out;
816 enctype = smb_krb5_kt_get_enctype_from_entry(&kt_entry);
818 ret = smb_krb5_enctype_to_string(context, enctype, &etype_s);
819 if (ret &&
820 (asprintf(&etype_s, "UNKNOWN: %d\n", enctype) == -1)) {
821 TALLOC_FREE(princ_s);
822 goto out;
825 printf("%3d %-43s %s\n", kt_entry.vno, etype_s, princ_s);
827 TALLOC_FREE(princ_s);
828 SAFE_FREE(etype_s);
830 ret = smb_krb5_kt_free_entry(context, &kt_entry);
831 if (ret) {
832 goto out;
836 ret = krb5_kt_end_seq_get(context, keytab, &cursor);
837 if (ret) {
838 goto out;
841 /* Ensure we don't double free. */
842 ZERO_STRUCT(kt_entry);
843 ZERO_STRUCT(cursor);
844 out:
846 if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
847 smb_krb5_kt_free_entry(context, &kt_entry);
849 if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
850 krb5_kt_end_seq_get(context, keytab, &cursor);
853 if (keytab) {
854 krb5_kt_close(context, keytab);
856 if (context) {
857 krb5_free_context(context);
859 return ret;
862 #endif /* HAVE_KRB5 */