s3: krb5: keytab - The done label can be jumped to with context == NULL.
[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_zero_array(frame, char *, found + 1);
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
678         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
679         if (ret == KRB5_KT_END || ret == ENOENT) {
680                 krb5_kt_end_seq_get(context, keytab, &cursor);
681                 ZERO_STRUCT(cursor);
682                 goto done;
683         }
684
685         while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
686                 if (kt_entry.vno != kvno) {
687                         char *ktprinc = NULL;
688                         char *p;
689
690                         /* This returns a malloc'ed string in ktprinc. */
691                         ret = smb_krb5_unparse_name(oldEntries, context,
692                                                     kt_entry.principal,
693                                                     &ktprinc);
694                         if (ret) {
695                                 DEBUG(1, (__location__
696                                          ": smb_krb5_unparse_name failed "
697                                          "(%s)\n", error_message(ret)));
698                                 goto done;
699                         }
700                         /*
701                          * From looking at the krb5 source they don't seem to
702                          * take locale or mb strings into account.
703                          * Maybe this is because they assume utf8 ?
704                          * In this case we may need to convert from utf8 to
705                          * mb charset here ? JRA.
706                          */
707                         p = strchr_m(ktprinc, '@');
708                         if (p) {
709                                 *p = '\0';
710                         }
711
712                         p = strchr_m(ktprinc, '/');
713                         if (p) {
714                                 *p = '\0';
715                         }
716                         for (i = 0; i < found; i++) {
717                                 if (!oldEntries[i]) {
718                                         oldEntries[i] = ktprinc;
719                                         break;
720                                 }
721                                 if (!strcmp(oldEntries[i], ktprinc)) {
722                                         TALLOC_FREE(ktprinc);
723                                         break;
724                                 }
725                         }
726                         if (i == found) {
727                                 TALLOC_FREE(ktprinc);
728                         }
729                 }
730                 smb_krb5_kt_free_entry(context, &kt_entry);
731                 ZERO_STRUCT(kt_entry);
732         }
733         ret = 0;
734         for (i = 0; oldEntries[i]; i++) {
735                 ret |= ads_keytab_add_entry(ads, oldEntries[i]);
736                 TALLOC_FREE(oldEntries[i]);
737         }
738         krb5_kt_end_seq_get(context, keytab, &cursor);
739         ZERO_STRUCT(cursor);
740
741 done:
742         TALLOC_FREE(oldEntries);
743         TALLOC_FREE(frame);
744
745         if (context) {
746                 krb5_keytab_entry zero_kt_entry;
747                 krb5_kt_cursor zero_csr;
748
749                 ZERO_STRUCT(zero_kt_entry);
750                 ZERO_STRUCT(zero_csr);
751
752                 if (memcmp(&zero_kt_entry, &kt_entry,
753                                 sizeof(krb5_keytab_entry))) {
754                         smb_krb5_kt_free_entry(context, &kt_entry);
755                 }
756                 if ((memcmp(&cursor, &zero_csr,
757                                 sizeof(krb5_kt_cursor)) != 0) && keytab) {
758                         krb5_kt_end_seq_get(context, keytab, &cursor);
759                 }
760                 if (keytab) {
761                         krb5_kt_close(context, keytab);
762                 }
763                 krb5_free_context(context);
764         }
765         return ret;
766 }
767
768 #endif /* HAVE_ADS */
769
770 /**********************************************************************
771  List system keytab.
772 ***********************************************************************/
773
774 int ads_keytab_list(const char *keytab_name)
775 {
776         krb5_error_code ret = 0;
777         krb5_context context = NULL;
778         krb5_keytab keytab = NULL;
779         krb5_kt_cursor cursor;
780         krb5_keytab_entry kt_entry;
781
782         ZERO_STRUCT(kt_entry);
783         ZERO_STRUCT(cursor);
784
785         initialize_krb5_error_table();
786         ret = krb5_init_context(&context);
787         if (ret) {
788                 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
789                           error_message(ret)));
790                 return ret;
791         }
792
793         ret = smb_krb5_open_keytab(context, keytab_name, False, &keytab);
794         if (ret) {
795                 DEBUG(1, (__location__ ": smb_krb5_open_keytab 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_get_enctype_from_kt_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         {
851                 krb5_keytab_entry zero_kt_entry;
852                 ZERO_STRUCT(zero_kt_entry);
853                 if (memcmp(&zero_kt_entry, &kt_entry,
854                                 sizeof(krb5_keytab_entry))) {
855                         smb_krb5_kt_free_entry(context, &kt_entry);
856                 }
857         }
858         {
859                 krb5_kt_cursor zero_csr;
860                 ZERO_STRUCT(zero_csr);
861                 if ((memcmp(&cursor, &zero_csr,
862                                 sizeof(krb5_kt_cursor)) != 0) && keytab) {
863                         krb5_kt_end_seq_get(context, keytab, &cursor);
864                 }
865         }
866
867         if (keytab) {
868                 krb5_kt_close(context, keytab);
869         }
870         if (context) {
871                 krb5_free_context(context);
872         }
873         return ret;
874 }
875
876 #endif /* HAVE_KRB5 */