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