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