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