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