Merge branch 'master' of ssh://git.samba.org/data/git/samba into selftest
[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 3 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, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "system/kerberos.h"
25 #include "auth/kerberos/kerberos.h"
26 #include "auth/credentials/credentials.h"
27 #include "auth/credentials/credentials_proto.h"
28 #include "auth/credentials/credentials_krb5.h"
29
30 struct principal_container {
31         struct smb_krb5_context *smb_krb5_context;
32         krb5_principal principal;
33 };
34
35 static krb5_error_code free_principal(struct principal_container *pc)
36 {
37         /* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
38         krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
39
40         return 0;
41 }
42
43 static krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx, 
44                                                        struct cli_credentials *machine_account, 
45                                                        struct smb_krb5_context *smb_krb5_context,
46                                                        krb5_principal *salt_princ)
47 {
48         krb5_error_code ret;
49         char *machine_username;
50         char *salt_body;
51         char *lower_realm;
52         const char *salt_principal;
53         struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
54         if (!mem_ctx) {
55                 return ENOMEM;
56         }
57
58         salt_principal = cli_credentials_get_salt_principal(machine_account);
59         if (salt_principal) {
60                 ret = krb5_parse_name(smb_krb5_context->krb5_context, salt_principal, salt_princ); 
61         } else {
62                 machine_username = talloc_strdup(mem_ctx, cli_credentials_get_username(machine_account));
63                 
64                 if (!machine_username) {
65                         talloc_free(mem_ctx);
66                         return ENOMEM;
67                 }
68                 
69                 if (machine_username[strlen(machine_username)-1] == '$') {
70                         machine_username[strlen(machine_username)-1] = '\0';
71                 }
72                 lower_realm = strlower_talloc(mem_ctx, cli_credentials_get_realm(machine_account));
73                 if (!lower_realm) {
74                         talloc_free(mem_ctx);
75                         return ENOMEM;
76                 }
77                 
78                 salt_body = talloc_asprintf(mem_ctx, "%s.%s", machine_username, 
79                                             lower_realm);
80                 if (!salt_body) {
81                         talloc_free(mem_ctx);
82                 return ENOMEM;
83                 }
84                 
85                 ret = krb5_make_principal(smb_krb5_context->krb5_context, salt_princ, 
86                                           cli_credentials_get_realm(machine_account), 
87                                           "host", salt_body, NULL);
88         } 
89
90         if (ret == 0) {
91                 /* This song-and-dance effectivly puts the principal
92                  * into talloc, so we can't loose it. */
93                 mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
94                 mem_ctx->principal = *salt_princ;
95                 talloc_set_destructor(mem_ctx, free_principal);
96         }
97         return ret;
98 }
99
100 /* Obtain the principal set on this context.  Requires a
101  * smb_krb5_context because we are doing krb5 principal parsing with
102  * the library routines.  The returned princ is placed in the talloc
103  * system by means of a destructor (do *not* free). */
104
105  krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx, 
106                                             struct cli_credentials *credentials, 
107                                             struct smb_krb5_context *smb_krb5_context,
108                                             krb5_principal *princ)
109 {
110         krb5_error_code ret;
111         const char *princ_string;
112         struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
113         if (!mem_ctx) {
114                 return ENOMEM;
115         }
116         
117         princ_string = cli_credentials_get_principal(credentials, mem_ctx);
118
119         /* A NULL here has meaning, as the gssapi server case will
120          * then use the principal from the client */
121         if (!princ_string) {
122                 talloc_free(mem_ctx);
123                 princ = NULL;
124                 return 0;
125         }
126
127         ret = krb5_parse_name(smb_krb5_context->krb5_context,
128                               princ_string, princ);
129
130         if (ret == 0) {
131                 /* This song-and-dance effectivly puts the principal
132                  * into talloc, so we can't loose it. */
133                 mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
134                 mem_ctx->principal = *princ;
135                 talloc_set_destructor(mem_ctx, free_principal);
136         }
137         return ret;
138 }
139
140 /**
141  * Return a freshly allocated ccache (destroyed by destructor on child
142  * of parent_ctx), for a given set of client credentials 
143  */
144
145  krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
146                                  struct cli_credentials *credentials,
147                                  struct smb_krb5_context *smb_krb5_context,
148                                  krb5_ccache ccache) 
149 {
150         krb5_error_code ret;
151         const char *password;
152         time_t kdc_time = 0;
153         krb5_principal princ;
154         int tries;
155         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
156
157         if (!mem_ctx) {
158                 return ENOMEM;
159         }
160
161         ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ);
162         if (ret) {
163                 talloc_free(mem_ctx);
164                 return ret;
165         }
166
167         password = cli_credentials_get_password(credentials);
168
169         tries = 2;
170         while (tries--) {
171                 if (password) {
172                         ret = kerberos_kinit_password_cc(smb_krb5_context->krb5_context, ccache, 
173                                                          princ, 
174                                                          password, NULL, &kdc_time);
175                 } else {
176                         /* No password available, try to use a keyblock instead */
177                         
178                         krb5_keyblock keyblock;
179                         const struct samr_Password *mach_pwd;
180                         mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
181                         if (!mach_pwd) {
182                                 talloc_free(mem_ctx);
183                                 DEBUG(1, ("kinit_to_ccache: No password available for kinit\n"));
184                                 return EINVAL;
185                         }
186                         ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
187                                                  ETYPE_ARCFOUR_HMAC_MD5,
188                                                  mach_pwd->hash, sizeof(mach_pwd->hash), 
189                                                  &keyblock);
190                         
191                         if (ret == 0) {
192                                 ret = kerberos_kinit_keyblock_cc(smb_krb5_context->krb5_context, ccache, 
193                                                                  princ,
194                                                                  &keyblock, NULL, &kdc_time);
195                                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
196                         }
197                 }
198
199                 if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
200                         /* Perhaps we have been given an invalid skew, so try again without it */
201                         time_t t = time(NULL);
202                         krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
203                 } else {
204                         /* not a skew problem */
205                         break;
206                 }
207         }
208
209         if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
210                 DEBUG(1,("kinit for %s failed (%s)\n", 
211                          cli_credentials_get_principal(credentials, mem_ctx), 
212                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
213                                                     ret, mem_ctx)));
214                 talloc_free(mem_ctx);
215                 return ret;
216         }
217
218         /* cope with ticket being in the future due to clock skew */
219         if ((unsigned)kdc_time > time(NULL)) {
220                 time_t t = time(NULL);
221                 int time_offset =(unsigned)kdc_time-t;
222                 DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
223                 krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
224         }
225         
226         if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
227                 ret = kinit_to_ccache(parent_ctx,
228                                       credentials,
229                                       smb_krb5_context,
230                                       ccache); 
231         }
232         if (ret) {
233                 DEBUG(1,("kinit for %s failed (%s)\n", 
234                          cli_credentials_get_principal(credentials, mem_ctx), 
235                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
236                                                     ret, mem_ctx)));
237                 talloc_free(mem_ctx);
238                 return ret;
239         } 
240         talloc_free(mem_ctx);
241         return 0;
242 }
243
244 static krb5_error_code free_keytab(struct keytab_container *ktc)
245 {
246         return krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
247 }
248
249 krb5_error_code 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         krb5_error_code 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 static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
276                                        const char *princ_string,
277                                        krb5_principal princ,
278                                        krb5_principal salt_princ,
279                                        int kvno,
280                                        const char *password_s,
281                                        struct smb_krb5_context *smb_krb5_context,
282                                        const char **enctype_strings,
283                                        krb5_keytab keytab)
284 {
285         int i;
286         krb5_error_code ret;
287         krb5_data password;
288         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
289         if (!mem_ctx) {
290                 return ENOMEM;
291         }
292
293         password.data = discard_const_p(char *, password_s);
294         password.length = strlen(password_s);
295
296         for (i=0; enctype_strings[i]; i++) {
297                 krb5_keytab_entry entry;
298                 krb5_enctype enctype;
299                 ret = krb5_string_to_enctype(smb_krb5_context->krb5_context, enctype_strings[i], &enctype);
300                 if (ret != 0) {
301                         DEBUG(1, ("Failed to interpret %s as a krb5 encryption type: %s\n",                               
302                                   enctype_strings[i],
303                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
304                                                              ret, mem_ctx)));
305                         talloc_free(mem_ctx);
306                         return ret;
307                 }
308                 ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context, 
309                                                       salt_princ, &password, &entry.keyblock, enctype);
310                 if (ret != 0) {
311                         talloc_free(mem_ctx);
312                         return ret;
313                 }
314
315                 entry.principal = princ;
316                 entry.vno       = kvno;
317                 ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
318                 if (ret != 0) {
319                         DEBUG(1, ("Failed to add %s entry for %s(kvno %d) to keytab: %s\n",
320                                   enctype_strings[i],
321                                   princ_string,
322                                   kvno,
323                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
324                                                              ret, mem_ctx)));
325                         talloc_free(mem_ctx);
326                         krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
327                         return ret;
328                 }
329
330                 DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n", 
331                           princ_string, kvno,
332                           enctype_strings[i]));
333                 
334                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
335         }
336         talloc_free(mem_ctx);
337         return 0;
338 }
339
340 static krb5_error_code create_keytab(TALLOC_CTX *parent_ctx,
341                          struct cli_credentials *machine_account,
342                          struct smb_krb5_context *smb_krb5_context,
343                          const char **enctype_strings,
344                          krb5_keytab keytab,
345                          bool add_old) 
346 {
347         krb5_error_code ret;
348         const char *password_s;
349         const char *old_secret;
350         int kvno;
351         krb5_principal salt_princ;
352         krb5_principal princ;
353         const char *princ_string;
354
355         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
356         if (!mem_ctx) {
357                 return ENOMEM;
358         }
359
360         princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
361         /* Get the principal we will store the new keytab entries under */
362         ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
363         if (ret) {
364                 DEBUG(1,("create_keytab: makeing krb5 principal failed (%s)\n",
365                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
366                                                     ret, mem_ctx)));
367                 talloc_free(mem_ctx);
368                 return ret;
369         }
370
371         /* The salt used to generate these entries may be different however, fetch that */
372         ret = salt_principal_from_credentials(mem_ctx, machine_account, 
373                                               smb_krb5_context, 
374                                               &salt_princ);
375         if (ret) {
376                 DEBUG(1,("create_keytab: makeing salt principal failed (%s)\n",
377                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
378                                                     ret, mem_ctx)));
379                 talloc_free(mem_ctx);
380                 return ret;
381         }
382
383         /* Finally, do the dance to get the password to put in the entry */
384         password_s = cli_credentials_get_password(machine_account);
385         if (!password_s) {
386                 krb5_keytab_entry entry;
387                 const struct samr_Password *mach_pwd;
388
389                 if (!str_list_check(enctype_strings, "arcfour-hmac-md5")) {
390                         DEBUG(1, ("Asked to create keytab, but with only an NT hash supplied, "
391                                   "but not listing arcfour-hmac-md5 as an enc type to include in the keytab!\n"));
392                         talloc_free(mem_ctx);
393                         return EINVAL;
394                 }
395
396                 /* If we don't have the plaintext password, try for
397                  * the MD4 password hash */
398                 mach_pwd = cli_credentials_get_nt_hash(machine_account, mem_ctx);
399                 if (!mach_pwd) {
400                         /* OK, nothing to do here */
401                         talloc_free(mem_ctx);
402                         return 0;
403                 }
404                 ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
405                                          ETYPE_ARCFOUR_HMAC_MD5,
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                         talloc_free(mem_ctx);
413                         return ret;
414                 }
415
416                 entry.principal = princ;
417                 entry.vno       = cli_credentials_get_kvno(machine_account);
418                 ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
419                 if (ret) {
420                         DEBUG(1, ("Failed to add ARCFOUR_HMAC (only) entry for %s to keytab: %s",
421                                   cli_credentials_get_principal(machine_account, mem_ctx), 
422                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
423                                                              ret, mem_ctx)));
424                         talloc_free(mem_ctx);
425                         krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
426                         return ret;
427                 }
428                 
429                 DEBUG(5, ("Added %s(kvno %d) to keytab (arcfour-hmac-md5)\n", 
430                           cli_credentials_get_principal(machine_account, mem_ctx),
431                           cli_credentials_get_kvno(machine_account)));
432
433                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
434
435                 /* Can't go any further, we only have this one key */
436                 talloc_free(mem_ctx);
437                 return 0;
438         }
439         
440         kvno = cli_credentials_get_kvno(machine_account);
441         /* good, we actually have the real plaintext */
442         ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ, 
443                               kvno, password_s, smb_krb5_context, 
444                               enctype_strings, keytab);
445         if (!ret) {
446                 talloc_free(mem_ctx);
447                 return ret;
448         }
449
450         if (!add_old || kvno == 0) {
451                 talloc_free(mem_ctx);
452                 return 0;
453         }
454
455         old_secret = cli_credentials_get_old_password(machine_account);
456         if (!old_secret) {
457                 talloc_free(mem_ctx);
458                 return 0;
459         }
460         
461         ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ, 
462                               kvno - 1, old_secret, smb_krb5_context, 
463                               enctype_strings, 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 HEIM_ERR_OPNOTSUPP:
519         case ENOENT:
520         case KRB5_KT_END:
521                 /* no point enumerating if there isn't anything here */
522                 talloc_free(mem_ctx);
523                 return 0;
524         default:
525                 DEBUG(1,("failed to open keytab for read of old entries: %s\n",
526                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
527                                                     ret, mem_ctx)));
528                 talloc_free(mem_ctx);
529                 return ret;
530         }
531
532         while (!ret) {
533                 krb5_keytab_entry entry;
534                 ret = krb5_kt_next_entry(smb_krb5_context->krb5_context, keytab, &entry, &cursor);
535                 if (ret) {
536                         break;
537                 }
538                 /* if it matches our principal */
539                 if (!krb5_kt_compare(smb_krb5_context->krb5_context, &entry, princ, 0, 0)) {
540                         /* Free the entry, it wasn't the one we were looking for anyway */
541                         krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
542                         continue;
543                 }
544
545                 /* delete it, if it is not kvno -1 */
546                 if (entry.vno != (kvno - 1 )) {
547                         /* Release the enumeration.  We are going to
548                          * have to start this from the top again,
549                          * because deletes during enumeration may not
550                          * always be consistant.
551                          *
552                          * Also, the enumeration locks a FILE: keytab
553                          */
554                 
555                         krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
556
557                         ret = krb5_kt_remove_entry(smb_krb5_context->krb5_context, keytab, &entry);
558                         krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
559
560                         /* Deleted: Restart from the top */
561                         ret2 = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
562                         if (ret2) {
563                                 krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
564                                 DEBUG(1,("failed to restart enumeration of keytab: %s\n",
565                                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
566                                                                     ret, mem_ctx)));
567                                 
568                                 talloc_free(mem_ctx);
569                                 return ret2;
570                         }
571
572                         if (ret) {
573                                 break;
574                         }
575                         
576                 } else {
577                         *found_previous = true;
578                 }
579                 
580                 /* Free the entry, we don't need it any more */
581                 krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
582                 
583                 
584         }
585         krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
586
587         switch (ret) {
588         case 0:
589                 break;
590         case ENOENT:
591         case KRB5_KT_END:
592                 ret = 0;
593                 break;
594         default:
595                 DEBUG(1,("failed in deleting old entries for principal: %s: %s\n",
596                          princ_string, 
597                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
598                                                     ret, mem_ctx)));
599         }
600         talloc_free(mem_ctx);
601         return ret;
602 }
603
604 krb5_error_code smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
605                            struct cli_credentials *machine_account,
606                            struct smb_krb5_context *smb_krb5_context,
607                            const char **enctype_strings,
608                            struct keytab_container *keytab_container) 
609 {
610         krb5_error_code ret;
611         bool found_previous;
612         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
613         if (!mem_ctx) {
614                 return ENOMEM;
615         }
616
617         ret = remove_old_entries(mem_ctx, machine_account, 
618                                  smb_krb5_context, keytab_container->keytab, &found_previous);
619         if (ret != 0) {
620                 talloc_free(mem_ctx);
621                 return ret;
622         }
623         
624         /* Create a new keytab.  If during the cleanout we found
625          * entires for kvno -1, then don't try and duplicate them.
626          * Otherwise, add kvno, and kvno -1 */
627         
628         ret = create_keytab(mem_ctx, machine_account, smb_krb5_context, 
629                             enctype_strings, 
630                             keytab_container->keytab, 
631                             found_previous ? false : true);
632         talloc_free(mem_ctx);
633         return ret;
634 }
635
636 krb5_error_code smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
637                                            struct cli_credentials *machine_account,
638                                            struct smb_krb5_context *smb_krb5_context,
639                                            const char **enctype_strings,
640                                            struct keytab_container **keytab_container) 
641 {
642         krb5_error_code ret;
643         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
644         const char *rand_string;
645         const char *keytab_name;
646         if (!mem_ctx) {
647                 return ENOMEM;
648         }
649         
650         *keytab_container = talloc(mem_ctx, struct keytab_container);
651
652         rand_string = generate_random_str(mem_ctx, 16);
653         if (!rand_string) {
654                 talloc_free(mem_ctx);
655                 return ENOMEM;
656         }
657
658         keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s", 
659                                       rand_string);
660         if (!keytab_name) {
661                 talloc_free(mem_ctx);
662                 return ENOMEM;
663         }
664
665         ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context, keytab_name, keytab_container);
666         if (ret) {
667                 return ret;
668         }
669
670         ret = smb_krb5_update_keytab(mem_ctx, machine_account, smb_krb5_context, enctype_strings, *keytab_container);
671         if (ret == 0) {
672                 talloc_steal(parent_ctx, *keytab_container);
673         } else {
674                 *keytab_container = NULL;
675         }
676         talloc_free(mem_ctx);
677         return ret;
678 }
679