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