s3:libads: change ads_add_service_principal_name implementation
[metze/samba/wip.git] / source3 / libads / kerberos_keytab.c
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 static krb5_error_code ads_keytab_open(krb5_context context,
43                                        krb5_keytab *keytab)
44 {
45         char keytab_str[MAX_KEYTAB_NAME_LEN] = {0};
46         const char *keytab_name = NULL;
47         krb5_error_code ret = 0;
48
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;
58                 }
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;
69         }
70
71         if (keytab_name == NULL || keytab_name[0] == '\0') {
72                 DBG_ERR("Invalid keytab name\n");
73                 ret = KRB5_KT_BADNAME;
74                 goto out;
75         }
76
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;
82         }
83
84 out:
85         return ret;
86 }
87
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)
91 {
92         char *psp1, *psp2;
93
94         if (*spns == NULL) {
95                 *spns = talloc_zero_array(ctx, const char*, 3);
96                 if (spns == NULL) {
97                         return false;
98                 }
99         }
100
101         psp1 = talloc_asprintf(ctx,
102                                "%s/%s",
103                                spn,
104                                machine_name);
105         if (psp1 == NULL) {
106                 return false;
107         }
108
109         if (!strlower_m(&psp1[strlen(spn) + 1])) {
110                 return false;
111         }
112         (*spns)[0] = psp1;
113
114         psp2 = talloc_asprintf(ctx,
115                                "%s/%s",
116                                spn,
117                                my_fqdn);
118         if (psp2 == NULL) {
119                 return false;
120         }
121
122         if (!strlower_m(&psp2[strlen(spn) + 1])) {
123                 return false;
124         }
125
126         (*spns)[1] = psp2;
127
128         return true;
129 }
130
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)
135 {
136         const char **spn_names = NULL;
137         ADS_STATUS aderr;
138         bool ok = false;
139
140         DBG_INFO("Attempting to add/update '%s'\n", service_or_spn);
141
142         ok = fill_default_spns(ctx,
143                                lp_netbios_name(),
144                                my_fqdn,
145                                service_or_spn,
146                                &spn_names);
147         if (!ok) {
148                 return false;
149         }
150
151         aderr = ads_add_service_principal_names(ads,
152                                                 lp_netbios_name(),
153                                                 spn_names);
154         if (!ADS_ERR_OK(aderr)) {
155                 DBG_WARNING("Failed to add service principal name.\n");
156                 return false;
157         }
158
159         return true;
160 }
161
162 /**********************************************************************
163  Adds a single service principal, i.e. 'host' to the system keytab
164 ***********************************************************************/
165
166 int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc)
167 {
168         krb5_error_code ret = 0;
169         krb5_context context = NULL;
170         krb5_keytab keytab = NULL;
171         krb5_data password;
172         krb5_kvno kvno;
173         krb5_enctype enctypes[6] = {
174                 ENCTYPE_DES_CBC_CRC,
175                 ENCTYPE_DES_CBC_MD5,
176 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
177                 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
178 #endif
179 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
180                 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
181 #endif
182                 ENCTYPE_ARCFOUR_HMAC,
183                 0
184         };
185         char *princ_s = NULL;
186         char *short_princ_s = NULL;
187         char *salt_princ_s = NULL;
188         char *password_s = NULL;
189         char *my_fqdn;
190         TALLOC_CTX *tmpctx = NULL;
191         int i;
192
193         initialize_krb5_error_table();
194         ret = krb5_init_context(&context);
195         if (ret) {
196                 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
197                           error_message(ret)));
198                 return -1;
199         }
200
201         ret = ads_keytab_open(context, &keytab);
202         if (ret != 0) {
203                 goto out;
204         }
205
206         /* retrieve the password */
207         if (!secrets_init()) {
208                 DEBUG(1, (__location__ ": secrets_init failed\n"));
209                 ret = -1;
210                 goto out;
211         }
212         password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
213         if (!password_s) {
214                 DEBUG(1, (__location__ ": failed to fetch machine password\n"));
215                 ret = -1;
216                 goto out;
217         }
218         ZERO_STRUCT(password);
219         password.data = password_s;
220         password.length = strlen(password_s);
221
222         /* we need the dNSHostName value here */
223         tmpctx = talloc_init(__location__);
224         if (!tmpctx) {
225                 DEBUG(0, (__location__ ": talloc_init() failed!\n"));
226                 ret = -1;
227                 goto out;
228         }
229
230         my_fqdn = ads_get_dnshostname(ads, tmpctx, lp_netbios_name());
231         if (!my_fqdn) {
232                 DEBUG(0, (__location__ ": unable to determine machine "
233                           "account's dns name in AD!\n"));
234                 ret = -1;
235                 goto out;
236         }
237
238         /* make sure we have a single instance of a the computer account */
239         if (!ads_has_samaccountname(ads, tmpctx, lp_netbios_name())) {
240                 DEBUG(0, (__location__ ": unable to determine machine "
241                           "account's short name in AD!\n"));
242                 ret = -1;
243                 goto out;
244         }
245
246         /* Construct our principal */
247         if (strchr_m(srvPrinc, '@')) {
248                 /* It's a fully-named principal. */
249                 princ_s = talloc_asprintf(tmpctx, "%s", srvPrinc);
250                 if (!princ_s) {
251                         ret = -1;
252                         goto out;
253                 }
254         } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
255                 /* It's the machine account, as used by smbclient clients. */
256                 princ_s = talloc_asprintf(tmpctx, "%s@%s",
257                                           srvPrinc, lp_realm());
258                 if (!princ_s) {
259                         ret = -1;
260                         goto out;
261                 }
262         } else {
263                 /* It's a normal service principal.  Add the SPN now so that we
264                  * can obtain credentials for it and double-check the salt value
265                  * used to generate the service's keys. */
266
267                 princ_s = talloc_asprintf(tmpctx, "%s/%s@%s",
268                                           srvPrinc, my_fqdn, lp_realm());
269                 if (!princ_s) {
270                         ret = -1;
271                         goto out;
272                 }
273                 short_princ_s = talloc_asprintf(tmpctx, "%s/%s@%s",
274                                                 srvPrinc, lp_netbios_name(),
275                                                 lp_realm());
276                 if (short_princ_s == NULL) {
277                         ret = -1;
278                         goto out;
279                 }
280
281                 /* According to http://support.microsoft.com/kb/326985/en-us,
282                    certain principal names are automatically mapped to the
283                    host/... principal in the AD account.
284                    So only create these in the keytab, not in AD.  --jerry */
285
286                 if (!strequal(srvPrinc, "cifs") &&
287                     !strequal(srvPrinc, "host")) {
288                         if (!ads_set_machine_account_spns(tmpctx,
289                                                           ads,
290                                                           srvPrinc,
291                                                           my_fqdn)) {
292                                 ret = -1;
293                                 goto out;
294                         }
295                 }
296         }
297
298         kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
299         if (kvno == -1) {
300                 /* -1 indicates failure, everything else is OK */
301                 DEBUG(1, (__location__ ": ads_get_machine_kvno failed to "
302                          "determine the system's kvno.\n"));
303                 ret = -1;
304                 goto out;
305         }
306
307         salt_princ_s = kerberos_secrets_fetch_salt_princ();
308         if (salt_princ_s == NULL) {
309                 DBG_WARNING("kerberos_secrets_fetch_salt_princ() failed\n");
310                 ret = -1;
311                 goto out;
312         }
313
314         for (i = 0; enctypes[i]; i++) {
315
316                 /* add the fqdn principal to the keytab */
317                 ret = smb_krb5_kt_add_entry(context,
318                                             keytab,
319                                             kvno,
320                                             princ_s,
321                                             salt_princ_s,
322                                             enctypes[i],
323                                             &password,
324                                             false,
325                                             false);
326                 if (ret) {
327                         DEBUG(1, (__location__ ": Failed to add entry to keytab\n"));
328                         goto out;
329                 }
330
331                 /* add the short principal name if we have one */
332                 if (short_princ_s) {
333                         ret = smb_krb5_kt_add_entry(context,
334                                                     keytab,
335                                                     kvno,
336                                                     short_princ_s,
337                                                     salt_princ_s,
338                                                     enctypes[i],
339                                                     &password,
340                                                     false,
341                                                     false);
342                         if (ret) {
343                                 DEBUG(1, (__location__
344                                           ": Failed to add short entry to keytab\n"));
345                                 goto out;
346                         }
347                 }
348         }
349
350 out:
351         SAFE_FREE(salt_princ_s);
352         TALLOC_FREE(tmpctx);
353
354         if (keytab) {
355                 krb5_kt_close(context, keytab);
356         }
357         if (context) {
358                 krb5_free_context(context);
359         }
360         return (int)ret;
361 }
362
363 /**********************************************************************
364  Flushes all entries from the system keytab.
365 ***********************************************************************/
366
367 int ads_keytab_flush(ADS_STRUCT *ads)
368 {
369         krb5_error_code ret = 0;
370         krb5_context context = NULL;
371         krb5_keytab keytab = NULL;
372         krb5_kvno kvno;
373         ADS_STATUS aderr;
374
375         initialize_krb5_error_table();
376         ret = krb5_init_context(&context);
377         if (ret) {
378                 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
379                           error_message(ret)));
380                 return ret;
381         }
382
383         ret = ads_keytab_open(context, &keytab);
384         if (ret != 0) {
385                 goto out;
386         }
387
388         kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
389         if (kvno == -1) {
390                 /* -1 indicates a failure */
391                 DEBUG(1, (__location__ ": Error determining the kvno.\n"));
392                 goto out;
393         }
394
395         /* Seek and delete old keytab entries */
396         ret = smb_krb5_kt_seek_and_delete_old_entries(context,
397                                                       keytab,
398                                                       kvno,
399                                                       ENCTYPE_NULL,
400                                                       NULL,
401                                                       NULL,
402                                                       true,
403                                                       false);
404         if (ret) {
405                 goto out;
406         }
407
408         aderr = ads_clear_service_principal_names(ads, lp_netbios_name());
409         if (!ADS_ERR_OK(aderr)) {
410                 DEBUG(1, (__location__ ": Error while clearing service "
411                           "principal listings in LDAP.\n"));
412                 goto out;
413         }
414
415 out:
416         if (keytab) {
417                 krb5_kt_close(context, keytab);
418         }
419         if (context) {
420                 krb5_free_context(context);
421         }
422         return ret;
423 }
424
425 /**********************************************************************
426  Adds all the required service principals to the system keytab.
427 ***********************************************************************/
428
429 int ads_keytab_create_default(ADS_STRUCT *ads)
430 {
431         krb5_error_code ret = 0;
432         krb5_context context = NULL;
433         krb5_keytab keytab = NULL;
434         krb5_kt_cursor cursor = {0};
435         krb5_keytab_entry kt_entry = {0};
436         krb5_kvno kvno;
437         size_t found = 0;
438         char *sam_account_name, *upn;
439         char **oldEntries = NULL, *princ_s[26];
440         TALLOC_CTX *frame;
441         char *machine_name;
442         char **spn_array;
443         size_t num_spns;
444         size_t i;
445         bool ok = false;
446         ADS_STATUS status;
447
448         ZERO_STRUCT(kt_entry);
449         ZERO_STRUCT(cursor);
450
451         frame = talloc_stackframe();
452         if (frame == NULL) {
453                 ret = -1;
454                 goto done;
455         }
456
457         status = ads_get_service_principal_names(frame,
458                                                  ads,
459                                                  lp_netbios_name(),
460                                                  &spn_array,
461                                                  &num_spns);
462         if (!ADS_ERR_OK(status)) {
463                 ret = -1;
464                 goto done;
465         }
466
467         for (i = 0; i < num_spns; i++) {
468                 char *srv_princ;
469                 char *p;
470
471                 srv_princ = strlower_talloc(frame, spn_array[i]);
472                 if (srv_princ == NULL) {
473                         ret = -1;
474                         goto done;
475                 }
476
477                 p = strchr_m(srv_princ, '/');
478                 if (p == NULL) {
479                         continue;
480                 }
481                 p[0] = '\0';
482
483                 /* Add the SPNs found on the DC */
484                 ret = ads_keytab_add_entry(ads, srv_princ);
485                 if (ret != 0) {
486                         DEBUG(1, ("ads_keytab_add_entry failed while "
487                                   "adding '%s' principal.\n",
488                                   spn_array[i]));
489                         goto done;
490                 }
491         }
492
493 #if 0   /* don't create the CIFS/... keytab entries since no one except smbd
494            really needs them and we will fall back to verifying against
495            secrets.tdb */
496
497         ret = ads_keytab_add_entry(ads, "cifs"));
498         if (ret != 0 ) {
499                 DEBUG(1, (__location__ ": ads_keytab_add_entry failed while "
500                           "adding 'cifs'.\n"));
501                 return ret;
502         }
503 #endif
504
505         memset(princ_s, '\0', sizeof(princ_s));
506
507         initialize_krb5_error_table();
508         ret = krb5_init_context(&context);
509         if (ret) {
510                 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
511                           error_message(ret)));
512                 goto done;
513         }
514
515         machine_name = talloc_strdup(frame, lp_netbios_name());
516         if (!machine_name) {
517                 ret = -1;
518                 goto done;
519         }
520
521         /* now add the userPrincipalName and sAMAccountName entries */
522         ok = ads_has_samaccountname(ads, frame, machine_name);
523         if (!ok) {
524                 DEBUG(0, (__location__ ": unable to determine machine "
525                           "account's name in AD!\n"));
526                 ret = -1;
527                 goto done;
528         }
529
530         /*
531          * append '$' to netbios name so 'ads_keytab_add_entry' recognises
532          * it as a machine account rather than a service or Windows SPN.
533          */
534         sam_account_name = talloc_asprintf(frame, "%s$",machine_name);
535         if (sam_account_name == NULL) {
536                 ret = -1;
537                 goto done;
538         }
539         /* upper case the sAMAccountName to make it easier for apps to
540            know what case to use in the keytab file */
541         if (!strupper_m(sam_account_name)) {
542                 ret = -1;
543                 goto done;
544         }
545
546         ret = ads_keytab_add_entry(ads, sam_account_name);
547         if (ret != 0) {
548                 DEBUG(1, (__location__ ": ads_keytab_add_entry() failed "
549                           "while adding sAMAccountName (%s)\n",
550                           sam_account_name));
551                 goto done;
552         }
553
554         /* remember that not every machine account will have a upn */
555         upn = ads_get_upn(ads, frame, machine_name);
556         if (upn) {
557                 ret = ads_keytab_add_entry(ads, upn);
558                 if (ret != 0) {
559                         DEBUG(1, (__location__ ": ads_keytab_add_entry() "
560                                   "failed while adding UPN (%s)\n", upn));
561                         goto done;
562                 }
563         }
564
565         /* Now loop through the keytab and update any other existing entries */
566         kvno = (krb5_kvno)ads_get_machine_kvno(ads, machine_name);
567         if (kvno == (krb5_kvno)-1) {
568                 DEBUG(1, (__location__ ": ads_get_machine_kvno() failed to "
569                           "determine the system's kvno.\n"));
570                 goto done;
571         }
572
573         DEBUG(3, (__location__ ": Searching for keytab entries to preserve "
574                   "and update.\n"));
575
576         ret = ads_keytab_open(context, &keytab);
577         if (ret != 0) {
578                 goto done;
579         }
580
581         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
582         if (ret != KRB5_KT_END && ret != ENOENT ) {
583                 while ((ret = krb5_kt_next_entry(context, keytab,
584                                                  &kt_entry, &cursor)) == 0) {
585                         smb_krb5_kt_free_entry(context, &kt_entry);
586                         ZERO_STRUCT(kt_entry);
587                         found++;
588                 }
589         }
590         krb5_kt_end_seq_get(context, keytab, &cursor);
591         ZERO_STRUCT(cursor);
592
593         /*
594          * Hmmm. There is no "rewind" function for the keytab. This means we
595          * have a race condition where someone else could add entries after
596          * we've counted them. Re-open asap to minimise the race. JRA.
597          */
598         DEBUG(3, (__location__ ": Found %zd entries in the keytab.\n", found));
599         if (!found) {
600                 goto done;
601         }
602
603         oldEntries = talloc_zero_array(frame, char *, found + 1);
604         if (!oldEntries) {
605                 DEBUG(1, (__location__ ": Failed to allocate space to store "
606                           "the old keytab entries (talloc failed?).\n"));
607                 ret = -1;
608                 goto done;
609         }
610
611         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
612         if (ret == KRB5_KT_END || ret == ENOENT) {
613                 krb5_kt_end_seq_get(context, keytab, &cursor);
614                 ZERO_STRUCT(cursor);
615                 goto done;
616         }
617
618         while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
619                 if (kt_entry.vno != kvno) {
620                         char *ktprinc = NULL;
621                         char *p;
622
623                         /* This returns a malloc'ed string in ktprinc. */
624                         ret = smb_krb5_unparse_name(oldEntries, context,
625                                                     kt_entry.principal,
626                                                     &ktprinc);
627                         if (ret) {
628                                 DEBUG(1, (__location__
629                                          ": smb_krb5_unparse_name failed "
630                                          "(%s)\n", error_message(ret)));
631                                 goto done;
632                         }
633                         /*
634                          * From looking at the krb5 source they don't seem to
635                          * take locale or mb strings into account.
636                          * Maybe this is because they assume utf8 ?
637                          * In this case we may need to convert from utf8 to
638                          * mb charset here ? JRA.
639                          */
640                         p = strchr_m(ktprinc, '@');
641                         if (p) {
642                                 *p = '\0';
643                         }
644
645                         p = strchr_m(ktprinc, '/');
646                         if (p) {
647                                 *p = '\0';
648                         }
649                         for (i = 0; i < found; i++) {
650                                 if (!oldEntries[i]) {
651                                         oldEntries[i] = ktprinc;
652                                         break;
653                                 }
654                                 if (!strcmp(oldEntries[i], ktprinc)) {
655                                         TALLOC_FREE(ktprinc);
656                                         break;
657                                 }
658                         }
659                         if (i == found) {
660                                 TALLOC_FREE(ktprinc);
661                         }
662                 }
663                 smb_krb5_kt_free_entry(context, &kt_entry);
664                 ZERO_STRUCT(kt_entry);
665         }
666         krb5_kt_end_seq_get(context, keytab, &cursor);
667         ZERO_STRUCT(cursor);
668
669         ret = 0;
670         for (i = 0; oldEntries[i]; i++) {
671                 ret |= ads_keytab_add_entry(ads, oldEntries[i]);
672                 TALLOC_FREE(oldEntries[i]);
673         }
674
675 done:
676         TALLOC_FREE(oldEntries);
677         TALLOC_FREE(frame);
678
679         if (context) {
680                 if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
681                         smb_krb5_kt_free_entry(context, &kt_entry);
682                 }
683                 if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
684                         krb5_kt_end_seq_get(context, keytab, &cursor);
685                 }
686                 if (keytab) {
687                         krb5_kt_close(context, keytab);
688                 }
689                 krb5_free_context(context);
690         }
691         return ret;
692 }
693
694 #endif /* HAVE_ADS */
695
696 /**********************************************************************
697  List system keytab.
698 ***********************************************************************/
699
700 int ads_keytab_list(const char *keytab_name)
701 {
702         krb5_error_code ret = 0;
703         krb5_context context = NULL;
704         krb5_keytab keytab = NULL;
705         krb5_kt_cursor cursor;
706         krb5_keytab_entry kt_entry;
707
708         ZERO_STRUCT(kt_entry);
709         ZERO_STRUCT(cursor);
710
711         initialize_krb5_error_table();
712         ret = krb5_init_context(&context);
713         if (ret) {
714                 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
715                           error_message(ret)));
716                 return ret;
717         }
718
719         if (keytab_name == NULL) {
720 #ifdef HAVE_ADS
721                 ret = ads_keytab_open(context, &keytab);
722 #else
723                 ret = ENOENT;
724 #endif
725         } else {
726                 ret = smb_krb5_kt_open(context, keytab_name, False, &keytab);
727         }
728         if (ret) {
729                 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
730                           error_message(ret)));
731                 goto out;
732         }
733
734         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
735         if (ret) {
736                 ZERO_STRUCT(cursor);
737                 goto out;
738         }
739
740         printf("Vno  Type                                        Principal\n");
741
742         while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
743
744                 char *princ_s = NULL;
745                 char *etype_s = NULL;
746                 krb5_enctype enctype = 0;
747
748                 ret = smb_krb5_unparse_name(talloc_tos(), context,
749                                             kt_entry.principal, &princ_s);
750                 if (ret) {
751                         goto out;
752                 }
753
754                 enctype = smb_krb5_kt_get_enctype_from_entry(&kt_entry);
755
756                 ret = smb_krb5_enctype_to_string(context, enctype, &etype_s);
757                 if (ret &&
758                     (asprintf(&etype_s, "UNKNOWN: %d\n", enctype) == -1)) {
759                         TALLOC_FREE(princ_s);
760                         goto out;
761                 }
762
763                 printf("%3d  %-43s %s\n", kt_entry.vno, etype_s, princ_s);
764
765                 TALLOC_FREE(princ_s);
766                 SAFE_FREE(etype_s);
767
768                 ret = smb_krb5_kt_free_entry(context, &kt_entry);
769                 if (ret) {
770                         goto out;
771                 }
772         }
773
774         ret = krb5_kt_end_seq_get(context, keytab, &cursor);
775         if (ret) {
776                 goto out;
777         }
778
779         /* Ensure we don't double free. */
780         ZERO_STRUCT(kt_entry);
781         ZERO_STRUCT(cursor);
782 out:
783
784         if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
785                 smb_krb5_kt_free_entry(context, &kt_entry);
786         }
787         if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
788                 krb5_kt_end_seq_get(context, keytab, &cursor);
789         }
790
791         if (keytab) {
792                 krb5_kt_close(context, keytab);
793         }
794         if (context) {
795                 krb5_free_context(context);
796         }
797         return ret;
798 }
799
800 #endif /* HAVE_KRB5 */