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