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