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