kerberos: make smb_krb5_kt_add_entry() static.
[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         password.data = password_s;
280         password.length = strlen(password_s);
281
282         /* we need the dNSHostName value here */
283         
284         if ( (ctx = talloc_init("ads_keytab_add_entry")) == NULL ) {
285                 DEBUG(0,("ads_keytab_add_entry: talloc() failed!\n"));
286                 ret = -1;
287                 goto out;
288         }
289         
290         if ( (my_fqdn = ads_get_dnshostname( ads, ctx, global_myname())) == NULL ) {
291                 DEBUG(0,("ads_keytab_add_entry: unable to determine machine account's dns name in AD!\n"));
292                 ret = -1;
293                 goto out;       
294         }
295         
296         if ( (machine_name = ads_get_samaccountname( ads, ctx, global_myname())) == NULL ) {
297                 DEBUG(0,("ads_keytab_add_entry: unable to determine machine account's short name in AD!\n"));
298                 ret = -1;
299                 goto out;       
300         }
301         /*strip the trailing '$' */
302         machine_name[strlen(machine_name)-1] = '\0';
303                 
304         /* Construct our principal */
305
306         if (strchr_m(srvPrinc, '@')) {
307                 /* It's a fully-named principal. */
308                 asprintf(&princ_s, "%s", srvPrinc);
309         } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
310                 /* It's the machine account, as used by smbclient clients. */
311                 asprintf(&princ_s, "%s@%s", srvPrinc, lp_realm());
312         } else {
313                 /* It's a normal service principal.  Add the SPN now so that we
314                  * can obtain credentials for it and double-check the salt value
315                  * used to generate the service's keys. */
316                  
317                 asprintf(&princ_s, "%s/%s@%s", srvPrinc, my_fqdn, lp_realm());
318                 asprintf(&short_princ_s, "%s/%s@%s", srvPrinc, machine_name, lp_realm());
319                 
320                 /* According to http://support.microsoft.com/kb/326985/en-us, 
321                    certain principal names are automatically mapped to the host/...
322                    principal in the AD account.  So only create these in the 
323                    keytab, not in AD.  --jerry */
324                    
325                 if ( !strequal( srvPrinc, "cifs" ) && !strequal(srvPrinc, "host" ) ) {
326                         DEBUG(3,("ads_keytab_add_entry: Attempting to add/update '%s'\n", princ_s));
327                         
328                         if (!ADS_ERR_OK(ads_add_service_principal_name(ads, global_myname(), my_fqdn, srvPrinc))) {
329                                 DEBUG(1,("ads_keytab_add_entry: ads_add_service_principal_name failed.\n"));
330                                 goto out;
331                         }
332                 }
333         }
334
335         kvno = (krb5_kvno) ads_get_machine_kvno(ads, global_myname());
336         if (kvno == -1) {       /* -1 indicates failure, everything else is OK */
337                 DEBUG(1,("ads_keytab_add_entry: ads_get_machine_kvno failed to determine the system's kvno.\n"));
338                 ret = -1;
339                 goto out;
340         }
341         
342         /* add the fqdn principal to the keytab */
343         
344         ret = smb_krb5_kt_add_entry( context, keytab, kvno, princ_s, enctypes, password );
345         if ( ret ) {
346                 DEBUG(1,("ads_keytab_add_entry: Failed to add entry to keytab file\n"));
347                 goto out;
348         }
349         
350         /* add the short principal name if we have one */
351         
352         if ( short_princ_s ) {
353                 ret = smb_krb5_kt_add_entry( context, keytab, kvno, short_princ_s, enctypes, password );
354                 if ( ret ) {
355                         DEBUG(1,("ads_keytab_add_entry: Failed to add short entry to keytab file\n"));
356                         goto out;
357                 }
358         }
359
360 out:
361         SAFE_FREE( princ_s );
362         SAFE_FREE( short_princ_s );
363         TALLOC_FREE( ctx );
364         
365         if (keytab) {
366                 krb5_kt_close(context, keytab);
367         }
368         if (context) {
369                 krb5_free_context(context);
370         }
371         return (int)ret;
372 }
373
374 /**********************************************************************
375  Flushes all entries from the system keytab.
376 ***********************************************************************/
377
378 int ads_keytab_flush(ADS_STRUCT *ads)
379 {
380         krb5_error_code ret = 0;
381         krb5_context context = NULL;
382         krb5_keytab keytab = NULL;
383         krb5_kt_cursor cursor;
384         krb5_keytab_entry kt_entry;
385         krb5_kvno kvno;
386
387         ZERO_STRUCT(kt_entry);
388         ZERO_STRUCT(cursor);
389
390         initialize_krb5_error_table();
391         ret = krb5_init_context(&context);
392         if (ret) {
393                 DEBUG(1,("ads_keytab_flush: could not krb5_init_context: %s\n",error_message(ret)));
394                 return ret;
395         }
396
397         ret = smb_krb5_open_keytab(context, NULL, True, &keytab);
398         if (ret) {
399                 DEBUG(1,("ads_keytab_flush: smb_krb5_open_keytab failed (%s)\n", error_message(ret)));
400                 goto out;
401         }
402
403         kvno = (krb5_kvno) ads_get_machine_kvno(ads, global_myname());
404         if (kvno == -1) {       /* -1 indicates a failure */
405                 DEBUG(1,("ads_keytab_flush: Error determining the system's kvno.\n"));
406                 goto out;
407         }
408
409         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
410         if (ret != KRB5_KT_END && ret != ENOENT) {
411                 while (!krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) {
412                         ret = krb5_kt_end_seq_get(context, keytab, &cursor);
413                         ZERO_STRUCT(cursor);
414                         if (ret) {
415                                 DEBUG(1,("ads_keytab_flush: krb5_kt_end_seq_get() failed (%s)\n",error_message(ret)));
416                                 goto out;
417                         }
418                         ret = krb5_kt_remove_entry(context, keytab, &kt_entry);
419                         if (ret) {
420                                 DEBUG(1,("ads_keytab_flush: krb5_kt_remove_entry failed (%s)\n",error_message(ret)));
421                                 goto out;
422                         }
423                         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
424                         if (ret) {
425                                 DEBUG(1,("ads_keytab_flush: krb5_kt_start_seq failed (%s)\n",error_message(ret)));
426                                 goto out;
427                         }
428                         ret = smb_krb5_kt_free_entry(context, &kt_entry);
429                         ZERO_STRUCT(kt_entry);
430                         if (ret) {
431                                 DEBUG(1,("ads_keytab_flush: krb5_kt_remove_entry failed (%s)\n",error_message(ret)));
432                                 goto out;
433                         }
434                 }
435         }
436
437         /* Ensure we don't double free. */
438         ZERO_STRUCT(kt_entry);
439         ZERO_STRUCT(cursor);
440
441         if (!ADS_ERR_OK(ads_clear_service_principal_names(ads, global_myname()))) {
442                 DEBUG(1,("ads_keytab_flush: Error while clearing service principal listings in LDAP.\n"));
443                 goto out;
444         }
445
446 out:
447
448         {
449                 krb5_keytab_entry zero_kt_entry;
450                 ZERO_STRUCT(zero_kt_entry);
451                 if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) {
452                         smb_krb5_kt_free_entry(context, &kt_entry);
453                 }
454         }
455         {
456                 krb5_kt_cursor zero_csr;
457                 ZERO_STRUCT(zero_csr);
458                 if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) {
459                         krb5_kt_end_seq_get(context, keytab, &cursor);  
460                 }
461         }
462         if (keytab) {
463                 krb5_kt_close(context, keytab);
464         }
465         if (context) {
466                 krb5_free_context(context);
467         }
468         return ret;
469 }
470
471 /**********************************************************************
472  Adds all the required service principals to the system keytab.
473 ***********************************************************************/
474
475 int ads_keytab_create_default(ADS_STRUCT *ads)
476 {
477         krb5_error_code ret = 0;
478         krb5_context context = NULL;
479         krb5_keytab keytab = NULL;
480         krb5_kt_cursor cursor;
481         krb5_keytab_entry kt_entry;
482         krb5_kvno kvno;
483         int i, found = 0;
484         char *sam_account_name, *upn;
485         char **oldEntries = NULL, *princ_s[26];
486         TALLOC_CTX *ctx = NULL;
487         fstring machine_name;
488
489         memset(princ_s, '\0', sizeof(princ_s));
490
491         fstrcpy( machine_name, global_myname() );
492
493         /* these are the main ones we need */
494         
495         if ( (ret = ads_keytab_add_entry(ads, "host") ) != 0 ) {
496                 DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding 'host'.\n"));
497                 return ret;
498         }
499
500
501 #if 0   /* don't create the CIFS/... keytab entries since no one except smbd 
502            really needs them and we will fall back to verifying against secrets.tdb */
503            
504         if ( (ret = ads_keytab_add_entry(ads, "cifs")) != 0 ) {
505                 DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding 'cifs'.\n"));
506                 return ret;
507         }
508 #endif
509
510         if ( (ctx = talloc_init("ads_keytab_create_default")) == NULL ) {
511                 DEBUG(0,("ads_keytab_create_default: talloc() failed!\n"));
512                 return -1;
513         }
514
515         /* now add the userPrincipalName and sAMAccountName entries */
516
517         if ( (sam_account_name = ads_get_samaccountname( ads, ctx, machine_name)) == NULL ) {
518                 DEBUG(0,("ads_keytab_add_entry: unable to determine machine account's name in AD!\n"));
519                 TALLOC_FREE( ctx );
520                 return -1;      
521         }
522
523         /* upper case the sAMAccountName to make it easier for apps to 
524            know what case to use in the keytab file */
525
526         strupper_m( sam_account_name ); 
527
528         if ( (ret = ads_keytab_add_entry(ads, sam_account_name )) != 0 ) {
529                 DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding sAMAccountName (%s)\n",
530                         sam_account_name));
531                 return ret;
532         }
533         
534         /* remember that not every machine account will have a upn */
535                 
536         upn = ads_get_upn( ads, ctx, machine_name);
537         if ( upn ) {
538                 if ( (ret = ads_keytab_add_entry(ads, upn)) != 0 ) {
539                         DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding UPN (%s)\n",
540                                 upn));
541                         TALLOC_FREE( ctx );
542                         return ret;
543                 }
544         }
545
546         TALLOC_FREE( ctx );
547
548         /* Now loop through the keytab and update any other existing entries... */
549         
550         kvno = (krb5_kvno) ads_get_machine_kvno(ads, machine_name);
551         if (kvno == -1) {
552                 DEBUG(1,("ads_keytab_create_default: ads_get_machine_kvno failed to determine the system's kvno.\n"));
553                 return -1;
554         }
555         
556         DEBUG(3,("ads_keytab_create_default: Searching for keytab entries to "
557                 "preserve and update.\n"));
558
559         ZERO_STRUCT(kt_entry);
560         ZERO_STRUCT(cursor);
561
562         initialize_krb5_error_table();
563         ret = krb5_init_context(&context);
564         if (ret) {
565                 DEBUG(1,("ads_keytab_create_default: could not krb5_init_context: %s\n",error_message(ret)));
566                 return ret;
567         }
568
569         ret = smb_krb5_open_keytab(context, NULL, True, &keytab);
570         if (ret) {
571                 DEBUG(1,("ads_keytab_create_default: smb_krb5_open_keytab failed (%s)\n", error_message(ret)));
572                 goto done;
573         }
574
575         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
576         if (ret != KRB5_KT_END && ret != ENOENT ) {
577                 while ((ret = krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) == 0) {
578                         smb_krb5_kt_free_entry(context, &kt_entry);
579                         ZERO_STRUCT(kt_entry);
580                         found++;
581                 }
582         }
583         krb5_kt_end_seq_get(context, keytab, &cursor);
584         ZERO_STRUCT(cursor);
585
586         /*
587          * Hmmm. There is no "rewind" function for the keytab. This means we have a race condition
588          * where someone else could add entries after we've counted them. Re-open asap to minimise
589          * the race. JRA.
590          */
591         
592         DEBUG(3, ("ads_keytab_create_default: Found %d entries in the keytab.\n", found));
593         if (!found) {
594                 goto done;
595         }
596         oldEntries = SMB_MALLOC_ARRAY(char *, found );
597         if (!oldEntries) {
598                 DEBUG(1,("ads_keytab_create_default: Failed to allocate space to store the old keytab entries (malloc failed?).\n"));
599                 ret = -1;
600                 goto done;
601         }
602         memset(oldEntries, '\0', found * sizeof(char *));
603
604         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
605         if (ret != KRB5_KT_END && ret != ENOENT ) {
606                 while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
607                         if (kt_entry.vno != kvno) {
608                                 char *ktprinc = NULL;
609                                 char *p;
610
611                                 /* This returns a malloc'ed string in ktprinc. */
612                                 ret = smb_krb5_unparse_name(context, kt_entry.principal, &ktprinc);
613                                 if (ret) {
614                                         DEBUG(1,("smb_krb5_unparse_name failed (%s)\n", error_message(ret)));
615                                         goto done;
616                                 }
617                                 /*
618                                  * From looking at the krb5 source they don't seem to take locale
619                                  * or mb strings into account. Maybe this is because they assume utf8 ?
620                                  * In this case we may need to convert from utf8 to mb charset here ? JRA.
621                                  */
622                                 p = strchr_m(ktprinc, '@');
623                                 if (p) {
624                                         *p = '\0';
625                                 }
626
627                                 p = strchr_m(ktprinc, '/');
628                                 if (p) {
629                                         *p = '\0';
630                                 }
631                                 for (i = 0; i < found; i++) {
632                                         if (!oldEntries[i]) {
633                                                 oldEntries[i] = ktprinc;
634                                                 break;
635                                         }
636                                         if (!strcmp(oldEntries[i], ktprinc)) {
637                                                 SAFE_FREE(ktprinc);
638                                                 break;
639                                         }
640                                 }
641                                 if (i == found) {
642                                         SAFE_FREE(ktprinc);
643                                 }
644                         }
645                         smb_krb5_kt_free_entry(context, &kt_entry);
646                         ZERO_STRUCT(kt_entry);
647                 }
648                 ret = 0;
649                 for (i = 0; oldEntries[i]; i++) {
650                         ret |= ads_keytab_add_entry(ads, oldEntries[i]);
651                         SAFE_FREE(oldEntries[i]);
652                 }
653                 krb5_kt_end_seq_get(context, keytab, &cursor);
654         }
655         ZERO_STRUCT(cursor);
656
657 done:
658
659         SAFE_FREE(oldEntries);
660
661         {
662                 krb5_keytab_entry zero_kt_entry;
663                 ZERO_STRUCT(zero_kt_entry);
664                 if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) {
665                         smb_krb5_kt_free_entry(context, &kt_entry);
666                 }
667         }
668         {
669                 krb5_kt_cursor zero_csr;
670                 ZERO_STRUCT(zero_csr);
671                 if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) {
672                         krb5_kt_end_seq_get(context, keytab, &cursor);  
673                 }
674         }
675         if (keytab) {
676                 krb5_kt_close(context, keytab);
677         }
678         if (context) {
679                 krb5_free_context(context);
680         }
681         return ret;
682 }
683
684 /**********************************************************************
685  List system keytab.
686 ***********************************************************************/
687
688 int ads_keytab_list(const char *keytab_name)
689 {
690         krb5_error_code ret = 0;
691         krb5_context context = NULL;
692         krb5_keytab keytab = NULL;
693         krb5_kt_cursor cursor;
694         krb5_keytab_entry kt_entry;
695
696         ZERO_STRUCT(kt_entry);
697         ZERO_STRUCT(cursor);
698
699         initialize_krb5_error_table();
700         ret = krb5_init_context(&context);
701         if (ret) {
702                 DEBUG(1,("ads_keytab_list: could not krb5_init_context: %s\n",error_message(ret)));
703                 return ret;
704         }
705
706         ret = smb_krb5_open_keytab(context, keytab_name, False, &keytab);
707         if (ret) {
708                 DEBUG(1,("ads_keytab_list: smb_krb5_open_keytab failed (%s)\n", error_message(ret)));
709                 goto out;
710         }
711
712         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
713         if (ret) {
714                 goto out;
715         }
716
717         printf("Vno  Type        Principal\n");
718
719         while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
720         
721                 char *princ_s = NULL;
722                 char *etype_s = NULL;
723                 krb5_enctype enctype = 0;
724
725                 ret = smb_krb5_unparse_name(context, kt_entry.principal, &princ_s);
726                 if (ret) {
727                         goto out;
728                 }
729
730                 enctype = smb_get_enctype_from_kt_entry(&kt_entry);
731
732                 ret = smb_krb5_enctype_to_string(context, enctype, &etype_s);
733                 if (ret) {
734                         SAFE_FREE(princ_s);
735                         goto out;
736                 }
737
738                 printf("%3d  %s\t\t %s\n", kt_entry.vno, etype_s, princ_s);
739
740                 SAFE_FREE(princ_s);
741                 SAFE_FREE(etype_s);
742
743                 ret = smb_krb5_kt_free_entry(context, &kt_entry);
744                 if (ret) {
745                         goto out;
746                 }
747         }
748
749         ret = krb5_kt_end_seq_get(context, keytab, &cursor);
750         if (ret) {
751                 goto out;
752         }
753
754         /* Ensure we don't double free. */
755         ZERO_STRUCT(kt_entry);
756         ZERO_STRUCT(cursor);
757 out:
758
759         {
760                 krb5_keytab_entry zero_kt_entry;
761                 ZERO_STRUCT(zero_kt_entry);
762                 if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) {
763                         smb_krb5_kt_free_entry(context, &kt_entry);
764                 }
765         }
766         {
767                 krb5_kt_cursor zero_csr;
768                 ZERO_STRUCT(zero_csr);
769                 if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) {
770                         krb5_kt_end_seq_get(context, keytab, &cursor);  
771                 }
772         }
773
774         if (keytab) {
775                 krb5_kt_close(context, keytab);
776         }
777         if (context) {
778                 krb5_free_context(context);
779         }
780         return ret;
781 }
782
783 #endif /* HAVE_KRB5 */