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