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