r19568: When we get back a skew error, try with no skew. This allows us to
[kai/samba-autobuild/.git] / source4 / auth / kerberos / kerberos_util.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Kerberos utility functions for GENSEC
5    
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "system/kerberos.h"
26 #include "auth/kerberos/kerberos.h"
27 #include "auth/credentials/credentials.h"
28
29 struct principal_container {
30         struct smb_krb5_context *smb_krb5_context;
31         krb5_principal principal;
32 };
33
34 static int free_principal(struct principal_container *pc)
35 {
36         /* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
37         krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
38
39         return 0;
40 }
41
42 static krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx, 
43                                                        struct cli_credentials *machine_account, 
44                                                        struct smb_krb5_context *smb_krb5_context,
45                                                        krb5_principal *salt_princ)
46 {
47         krb5_error_code ret;
48         char *machine_username;
49         char *salt_body;
50         char *lower_realm;
51         const char *salt_principal;
52         struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
53         if (!mem_ctx) {
54                 return ENOMEM;
55         }
56
57         salt_principal = cli_credentials_get_salt_principal(machine_account);
58         if (salt_principal) {
59                 ret = krb5_parse_name(smb_krb5_context->krb5_context, salt_principal, salt_princ); 
60         } else {
61                 machine_username = talloc_strdup(mem_ctx, cli_credentials_get_username(machine_account));
62                 
63                 if (!machine_username) {
64                         talloc_free(mem_ctx);
65                         return ENOMEM;
66                 }
67                 
68                 if (machine_username[strlen(machine_username)-1] == '$') {
69                         machine_username[strlen(machine_username)-1] = '\0';
70                 }
71                 lower_realm = strlower_talloc(mem_ctx, cli_credentials_get_realm(machine_account));
72                 if (!lower_realm) {
73                         talloc_free(mem_ctx);
74                         return ENOMEM;
75                 }
76                 
77                 salt_body = talloc_asprintf(mem_ctx, "%s.%s", machine_username, 
78                                             lower_realm);
79                 if (!salt_body) {
80                         talloc_free(mem_ctx);
81                 return ENOMEM;
82                 }
83                 
84                 ret = krb5_make_principal(smb_krb5_context->krb5_context, salt_princ, 
85                                           cli_credentials_get_realm(machine_account), 
86                                           "host", salt_body, NULL);
87         } 
88
89         if (ret == 0) {
90                 /* This song-and-dance effectivly puts the principal
91                  * into talloc, so we can't loose it. */
92                 mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
93                 mem_ctx->principal = *salt_princ;
94                 talloc_set_destructor(mem_ctx, free_principal);
95         }
96         return ret;
97 }
98
99 /* Obtain the principal set on this context.  Requires a
100  * smb_krb5_context because we are doing krb5 principal parsing with
101  * the library routines.  The returned princ is placed in the talloc
102  * system by means of a destructor (do *not* free). */
103
104 krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx, 
105                                            struct cli_credentials *credentials, 
106                                            struct smb_krb5_context *smb_krb5_context,
107                                            krb5_principal *princ)
108 {
109         krb5_error_code ret;
110         const char *princ_string;
111         struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
112         if (!mem_ctx) {
113                 return ENOMEM;
114         }
115         
116         princ_string = cli_credentials_get_principal(credentials, mem_ctx);
117
118         /* A NULL here has meaning, as the gssapi server case will
119          * then use the principal from the client */
120         if (!princ_string) {
121                 talloc_free(mem_ctx);
122                 princ = NULL;
123                 return 0;
124         }
125
126         ret = krb5_parse_name(smb_krb5_context->krb5_context,
127                               princ_string, princ);
128
129         if (ret == 0) {
130                 /* This song-and-dance effectivly puts the principal
131                  * into talloc, so we can't loose it. */
132                 mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
133                 mem_ctx->principal = *princ;
134                 talloc_set_destructor(mem_ctx, free_principal);
135         }
136         return ret;
137 }
138
139 /**
140  * Return a freshly allocated ccache (destroyed by destructor on child
141  * of parent_ctx), for a given set of client credentials 
142  */
143
144  krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
145                                  struct cli_credentials *credentials,
146                                  struct smb_krb5_context *smb_krb5_context,
147                                  krb5_ccache ccache) 
148 {
149         krb5_error_code ret;
150         const char *password;
151         time_t kdc_time = 0;
152         krb5_principal princ;
153         int tries;
154         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
155
156         if (!mem_ctx) {
157                 return ENOMEM;
158         }
159
160         ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ);
161         if (ret) {
162                 talloc_free(mem_ctx);
163                 return ret;
164         }
165
166         password = cli_credentials_get_password(credentials);
167
168         tries = 2;
169         while (tries--) {
170                 if (password) {
171                         ret = kerberos_kinit_password_cc(smb_krb5_context->krb5_context, ccache, 
172                                                          princ, 
173                                                          password, NULL, &kdc_time);
174                 } else {
175                         /* No password available, try to use a keyblock instead */
176                         
177                         krb5_keyblock keyblock;
178                         const struct samr_Password *mach_pwd;
179                         mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
180                         if (!mach_pwd) {
181                                 talloc_free(mem_ctx);
182                                 DEBUG(1, ("kinit_to_ccache: No password available for kinit\n"));
183                                 return EINVAL;
184                         }
185                         ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
186                                                  ETYPE_ARCFOUR_HMAC_MD5,
187                                                  mach_pwd->hash, sizeof(mach_pwd->hash), 
188                                                  &keyblock);
189                         
190                         if (ret == 0) {
191                                 ret = kerberos_kinit_keyblock_cc(smb_krb5_context->krb5_context, ccache, 
192                                                                  princ,
193                                                                  &keyblock, NULL, &kdc_time);
194                                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
195                         }
196                 }
197
198                 if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
199                         /* Perhaps we have been given an invalid skew, so try again without it */
200                         time_t t = time(NULL);
201                         krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
202                 } else {
203                         /* not a skew problem */
204                         break;
205                 }
206         }
207
208         if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
209                 DEBUG(1,("kinit for %s failed (%s)\n", 
210                          cli_credentials_get_principal(credentials, mem_ctx), 
211                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
212                                                     ret, mem_ctx)));
213                 talloc_free(mem_ctx);
214                 return ret;
215         }
216
217         /* cope with ticket being in the future due to clock skew */
218         if ((unsigned)kdc_time > time(NULL)) {
219                 time_t t = time(NULL);
220                 int time_offset =(unsigned)kdc_time-t;
221                 DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
222                 krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
223         }
224         
225         if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
226                 ret = kinit_to_ccache(parent_ctx,
227                                       credentials,
228                                       smb_krb5_context,
229                                       ccache); 
230         }
231         if (ret) {
232                 DEBUG(1,("kinit for %s failed (%s)\n", 
233                          cli_credentials_get_principal(credentials, mem_ctx), 
234                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
235                                                     ret, mem_ctx)));
236                 talloc_free(mem_ctx);
237                 return ret;
238         } 
239         return 0;
240 }
241
242 static int free_keytab(struct keytab_container *ktc)
243 {
244         krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
245
246         return 0;
247 }
248
249 int smb_krb5_open_keytab(TALLOC_CTX *mem_ctx,
250                          struct smb_krb5_context *smb_krb5_context, 
251                          const char *keytab_name, struct keytab_container **ktc) 
252 {
253         krb5_keytab keytab;
254         int ret;
255         ret = krb5_kt_resolve(smb_krb5_context->krb5_context, keytab_name, &keytab);
256         if (ret) {
257                 DEBUG(1,("failed to open krb5 keytab: %s\n", 
258                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
259                                                     ret, mem_ctx)));
260                 return ret;
261         }
262
263         *ktc = talloc(mem_ctx, struct keytab_container);
264         if (!*ktc) {
265                 return ENOMEM;
266         }
267
268         (*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
269         (*ktc)->keytab = keytab;
270         talloc_set_destructor(*ktc, free_keytab);
271
272         return 0;
273 }
274
275 struct enctypes_container {
276         struct smb_krb5_context *smb_krb5_context;
277         krb5_enctype *enctypes;
278 };
279
280 static int free_enctypes(struct enctypes_container *etc)
281 {
282         free_kerberos_etypes(etc->smb_krb5_context->krb5_context, etc->enctypes);
283         return 0;
284 }
285
286 static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
287                                        const char *princ_string,
288                                        krb5_principal princ,
289                                        krb5_principal salt_princ,
290                                        int kvno,
291                                        const char *password_s,
292                                        struct smb_krb5_context *smb_krb5_context,
293                                        krb5_keytab keytab)
294 {
295         int i;
296         krb5_error_code ret;
297         krb5_enctype *enctypes;
298         char *enctype_string;
299         struct enctypes_container *etc;
300         krb5_data password;
301         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
302         if (!mem_ctx) {
303                 return ENOMEM;
304         }
305
306         etc = talloc(mem_ctx, struct enctypes_container);
307         if (!etc) {
308                 talloc_free(mem_ctx);
309                 return ENOMEM;
310         }
311         ret = get_kerberos_allowed_etypes(smb_krb5_context->krb5_context, 
312                                           &enctypes);
313         if (ret != 0) {
314                 DEBUG(1,("keytab_add_keys: getting encrption types failed (%s)\n",
315                          error_message(ret)));
316                 talloc_free(mem_ctx);
317                 return ret;
318         }
319
320         etc->smb_krb5_context = talloc_reference(etc, smb_krb5_context);
321         etc->enctypes = enctypes;
322
323         talloc_set_destructor(etc, free_enctypes);
324
325         password.data = discard_const_p(char *, password_s);
326         password.length = strlen(password_s);
327
328         for (i=0; enctypes[i]; i++) {
329                 krb5_keytab_entry entry;
330                 ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context, 
331                                                       salt_princ, &password, &entry.keyblock, enctypes[i]);
332                 if (ret != 0) {
333                         talloc_free(mem_ctx);
334                         return ret;
335                 }
336
337                 entry.principal = princ;
338                 entry.vno       = kvno;
339                 ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
340                 enctype_string = NULL;
341                 krb5_enctype_to_string(smb_krb5_context->krb5_context, enctypes[i], &enctype_string);
342                 if (ret != 0) {
343                         DEBUG(1, ("Failed to add %s entry for %s(kvno %d) to keytab: %s\n",
344                                   enctype_string,
345                                   princ_string,
346                                   kvno,
347                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
348                                                              ret, mem_ctx)));
349                         talloc_free(mem_ctx);
350                         free(enctype_string);           
351                         krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
352                         return ret;
353                 }
354
355                 DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n", 
356                           princ_string, kvno,
357                           enctype_string));
358                 free(enctype_string);           
359                 
360                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
361         }
362         talloc_free(mem_ctx);
363         return 0;
364 }
365
366 static int create_keytab(TALLOC_CTX *parent_ctx,
367                          struct cli_credentials *machine_account,
368                          struct smb_krb5_context *smb_krb5_context,
369                          krb5_keytab keytab,
370                          BOOL add_old) 
371 {
372         krb5_error_code ret;
373         const char *password_s;
374         char *enctype_string;
375         const char *old_secret;
376         int kvno;
377         krb5_principal salt_princ;
378         krb5_principal princ;
379         const char *princ_string;
380
381         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
382         if (!mem_ctx) {
383                 return ENOMEM;
384         }
385
386         princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
387         /* Get the principal we will store the new keytab entries under */
388         ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
389         if (ret) {
390                 DEBUG(1,("create_keytab: makeing krb5 principal failed (%s)\n",
391                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
392                                                     ret, mem_ctx)));
393                 talloc_free(mem_ctx);
394                 return ret;
395         }
396
397         /* The salt used to generate these entries may be different however, fetch that */
398         ret = salt_principal_from_credentials(mem_ctx, machine_account, 
399                                               smb_krb5_context, 
400                                               &salt_princ);
401         if (ret) {
402                 DEBUG(1,("create_keytab: makeing salt principal failed (%s)\n",
403                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
404                                                     ret, mem_ctx)));
405                 talloc_free(mem_ctx);
406                 return ret;
407         }
408
409         /* Finally, do the dance to get the password to put in the entry */
410         password_s = cli_credentials_get_password(machine_account);
411         if (!password_s) {
412                 /* If we don't have the plaintext password, try for
413                  * the MD4 password hash */
414
415                 krb5_keytab_entry entry;
416                 const struct samr_Password *mach_pwd;
417                 mach_pwd = cli_credentials_get_nt_hash(machine_account, mem_ctx);
418                 if (!mach_pwd) {
419                         DEBUG(1, ("create_keytab: Domain trust informaton for account %s not available\n",
420                                   cli_credentials_get_principal(machine_account, mem_ctx)));
421                         talloc_free(mem_ctx);
422                         return EINVAL;
423                 }
424                 ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
425                                          ETYPE_ARCFOUR_HMAC_MD5,
426                                          mach_pwd->hash, sizeof(mach_pwd->hash), 
427                                          &entry.keyblock);
428                 if (ret) {
429                         DEBUG(1, ("create_keytab: krb5_keyblock_init failed: %s\n",
430                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
431                                                              ret, mem_ctx)));
432                         talloc_free(mem_ctx);
433                         return ret;
434                 }
435
436                 entry.principal = princ;
437                 entry.vno       = cli_credentials_get_kvno(machine_account);
438                 ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
439                 if (ret) {
440                         DEBUG(1, ("Failed to add ARCFOUR_HMAC (only) entry for %s to keytab: %s",
441                                   cli_credentials_get_principal(machine_account, mem_ctx), 
442                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
443                                                              ret, mem_ctx)));
444                         talloc_free(mem_ctx);
445                         krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
446                         return ret;
447                 }
448                 
449                 krb5_enctype_to_string(smb_krb5_context->krb5_context, 
450                                        ETYPE_ARCFOUR_HMAC_MD5,
451                                        &enctype_string);
452                 DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n", 
453                           cli_credentials_get_principal(machine_account, mem_ctx),
454                           cli_credentials_get_kvno(machine_account),
455                           enctype_string));
456                 free(enctype_string);           
457
458                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
459
460                 /* Can't go any further, we only have this one key */
461                 talloc_free(mem_ctx);
462                 return 0;
463         }
464         
465         kvno = cli_credentials_get_kvno(machine_account);
466         /* good, we actually have the real plaintext */
467         ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ, 
468                               kvno, password_s, smb_krb5_context, keytab);
469         if (!ret) {
470                 talloc_free(mem_ctx);
471                 return ret;
472         }
473
474         if (!add_old || kvno == 0) {
475                 talloc_free(mem_ctx);
476                 return 0;
477         }
478
479         old_secret = cli_credentials_get_old_password(machine_account);
480         if (!old_secret) {
481                 talloc_free(mem_ctx);
482                 return 0;
483         }
484         
485         ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ, 
486                               kvno - 1, old_secret, smb_krb5_context, keytab);
487         if (!ret) {
488                 talloc_free(mem_ctx);
489                 return ret;
490         }
491
492         talloc_free(mem_ctx);
493         return 0;
494 }
495
496
497 /*
498  * Walk the keytab, looking for entries of this principal name, with KVNO other than current kvno -1.
499  *
500  * These entries are now stale, we only keep the current, and previous entries around.
501  *
502  * Inspired by the code in Samba3 for 'use kerberos keytab'.
503  *
504  */
505
506 static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx,
507                                           struct cli_credentials *machine_account,
508                                           struct smb_krb5_context *smb_krb5_context,
509                                           krb5_keytab keytab, BOOL *found_previous)
510 {
511         krb5_error_code ret, ret2;
512         krb5_kt_cursor cursor;
513         krb5_principal princ;
514         int kvno;
515         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
516         const char *princ_string;
517         if (!mem_ctx) {
518                 return ENOMEM;
519         }
520
521         *found_previous = False;
522         princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
523
524         /* Get the principal we will store the new keytab entries under */
525         ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
526         if (ret) {
527                 DEBUG(1,("update_keytab: makeing krb5 principal failed (%s)\n",
528                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
529                                                     ret, mem_ctx)));
530                 talloc_free(mem_ctx);
531                 return ret;
532         }
533
534         kvno = cli_credentials_get_kvno(machine_account);
535
536         /* for each entry in the keytab */
537         ret = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
538         switch (ret) {
539         case 0:
540                 break;
541         case HEIM_ERR_OPNOTSUPP:
542         case ENOENT:
543         case KRB5_KT_END:
544                 /* no point enumerating if there isn't anything here */
545                 talloc_free(mem_ctx);
546                 return 0;
547         default:
548                 DEBUG(1,("failed to open keytab for read of old entries: %s\n",
549                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
550                                                     ret, mem_ctx)));
551                 talloc_free(mem_ctx);
552                 return ret;
553         }
554
555         while (!ret) {
556                 krb5_keytab_entry entry;
557                 ret = krb5_kt_next_entry(smb_krb5_context->krb5_context, keytab, &entry, &cursor);
558                 if (ret) {
559                         break;
560                 }
561                 /* if it matches our principal */
562                 if (!krb5_kt_compare(smb_krb5_context->krb5_context, &entry, princ, 0, 0)) {
563                         /* Free the entry, it wasn't the one we were looking for anyway */
564                         krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
565                         continue;
566                 }
567
568                 /* delete it, if it is not kvno -1 */
569                 if (entry.vno != (kvno - 1 )) {
570                         /* Release the enumeration.  We are going to
571                          * have to start this from the top again,
572                          * because deletes during enumeration may not
573                          * always be consistant.
574                          *
575                          * Also, the enumeration locks a FILE: keytab
576                          */
577                 
578                         krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
579
580                         ret = krb5_kt_remove_entry(smb_krb5_context->krb5_context, keytab, &entry);
581                         krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
582
583                         /* Deleted: Restart from the top */
584                         ret2 = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
585                         if (ret2) {
586                                 krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
587                                 DEBUG(1,("failed to restart enumeration of keytab: %s\n",
588                                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
589                                                                     ret, mem_ctx)));
590                                 
591                                 talloc_free(mem_ctx);
592                                 return ret2;
593                         }
594
595                         if (ret) {
596                                 break;
597                         }
598                         
599                 } else {
600                         *found_previous = True;
601                 }
602                 
603                 /* Free the entry, we don't need it any more */
604                 krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
605                 
606                 
607         }
608         krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
609
610         switch (ret) {
611         case 0:
612                 break;
613         case ENOENT:
614         case KRB5_KT_END:
615                 ret = 0;
616                 break;
617         default:
618                 DEBUG(1,("failed in deleting old entries for principal: %s: %s\n",
619                          princ_string, 
620                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
621                                                     ret, mem_ctx)));
622         }
623         talloc_free(mem_ctx);
624         return ret;
625 }
626
627 int smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
628                            struct cli_credentials *machine_account,
629                            struct smb_krb5_context *smb_krb5_context,
630                            struct keytab_container *keytab_container) 
631 {
632         krb5_error_code ret;
633         BOOL found_previous;
634         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
635         if (!mem_ctx) {
636                 return ENOMEM;
637         }
638         
639         ret = remove_old_entries(mem_ctx, machine_account, 
640                                  smb_krb5_context, keytab_container->keytab, &found_previous);
641         if (ret != 0) {
642                 talloc_free(mem_ctx);
643                 return ret;
644         }
645         
646         /* Create a new keytab.  If during the cleanout we found
647          * entires for kvno -1, then don't try and duplicate them.
648          * Otherwise, add kvno, and kvno -1 */
649         
650         ret = create_keytab(mem_ctx, machine_account, smb_krb5_context, 
651                             keytab_container->keytab, 
652                             found_previous ? False : True);
653         talloc_free(mem_ctx);
654         return ret;
655 }
656
657 int smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
658                                   struct cli_credentials *machine_account,
659                                   struct smb_krb5_context *smb_krb5_context,
660                                   struct keytab_container **keytab_container) 
661 {
662         krb5_error_code ret;
663         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
664         const char *rand_string;
665         const char *keytab_name;
666         if (!mem_ctx) {
667                 return ENOMEM;
668         }
669         
670         *keytab_container = talloc(mem_ctx, struct keytab_container);
671
672         rand_string = generate_random_str(mem_ctx, 16);
673         if (!rand_string) {
674                 talloc_free(mem_ctx);
675                 return ENOMEM;
676         }
677
678         keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s", 
679                                       rand_string);
680         if (!keytab_name) {
681                 talloc_free(mem_ctx);
682                 return ENOMEM;
683         }
684
685         ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context, keytab_name, keytab_container);
686         if (ret) {
687                 return ret;
688         }
689
690         ret = smb_krb5_update_keytab(mem_ctx, machine_account, smb_krb5_context, *keytab_container);
691         if (ret == 0) {
692                 talloc_steal(parent_ctx, *keytab_container);
693         } else {
694                 *keytab_container = NULL;
695         }
696         talloc_free(mem_ctx);
697         return ret;
698 }
699