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