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