s4:kerberos Add functions to convert msDS-SupportedEncryptionTypes
[amitay/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 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 #include "auth/kerberos/kerberos_credentials.h"
30
31 struct principal_container {
32         struct smb_krb5_context *smb_krb5_context;
33         krb5_principal principal;
34 };
35
36 static krb5_error_code free_principal(struct principal_container *pc)
37 {
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
45 static krb5_error_code parse_principal(TALLOC_CTX *parent_ctx,
46                                        const char *princ_string,
47                                        struct smb_krb5_context *smb_krb5_context,
48                                        krb5_principal *princ,
49                                        const char **error_string)
50 {
51         int ret;
52         struct principal_container *mem_ctx;
53         if (princ_string == NULL) {
54                  *princ = NULL;
55                  return 0;
56         }
57
58         ret = krb5_parse_name(smb_krb5_context->krb5_context,
59                               princ_string, princ);
60
61         if (ret) {
62                 (*error_string) = smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, parent_ctx);
63                 return ret;
64         }
65
66         mem_ctx = talloc(parent_ctx, struct principal_container);
67         if (!mem_ctx) {
68                 (*error_string) = error_message(ENOMEM);
69                 return ENOMEM;
70         }
71
72         /* This song-and-dance effectivly puts the principal
73          * into talloc, so we can't loose it. */
74         mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
75         mem_ctx->principal = *princ;
76         talloc_set_destructor(mem_ctx, free_principal);
77         return 0;
78 }
79
80 static krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx, 
81                                                        struct cli_credentials *machine_account, 
82                                                        struct smb_krb5_context *smb_krb5_context,
83                                                        krb5_principal *salt_princ)
84 {
85         krb5_error_code ret;
86         char *machine_username;
87         char *salt_body;
88         char *lower_realm;
89         const char *salt_principal;
90         const char *error_string;
91         struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
92         if (!mem_ctx) {
93                 return ENOMEM;
94         }
95
96         salt_principal = cli_credentials_get_salt_principal(machine_account);
97         if (salt_principal) {
98                 ret = parse_principal(parent_ctx, salt_principal, smb_krb5_context, salt_princ, &error_string);
99         } else {
100                 machine_username = talloc_strdup(mem_ctx, cli_credentials_get_username(machine_account));
101                 
102                 if (!machine_username) {
103                         talloc_free(mem_ctx);
104                         return ENOMEM;
105                 }
106                 
107                 if (machine_username[strlen(machine_username)-1] == '$') {
108                         machine_username[strlen(machine_username)-1] = '\0';
109                 }
110                 lower_realm = strlower_talloc(mem_ctx, cli_credentials_get_realm(machine_account));
111                 if (!lower_realm) {
112                         talloc_free(mem_ctx);
113                         return ENOMEM;
114                 }
115                 
116                 salt_body = talloc_asprintf(mem_ctx, "%s.%s", machine_username, 
117                                             lower_realm);
118                 if (!salt_body) {
119                         talloc_free(mem_ctx);
120                 return ENOMEM;
121                 }
122                 
123                 ret = krb5_make_principal(smb_krb5_context->krb5_context, salt_princ, 
124                                           cli_credentials_get_realm(machine_account), 
125                                           "host", salt_body, NULL);
126                 if (ret == 0) {
127                         /* This song-and-dance effectivly puts the principal
128                          * into talloc, so we can't loose it. */
129                         mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
130                         mem_ctx->principal = *salt_princ;
131                         talloc_set_destructor(mem_ctx, free_principal);
132                 }
133         } 
134
135         return ret;
136 }
137
138 /* Obtain the principal set on this context.  Requires a
139  * smb_krb5_context because we are doing krb5 principal parsing with
140  * the library routines.  The returned princ is placed in the talloc
141  * system by means of a destructor (do *not* free). */
142
143  krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx, 
144                                             struct cli_credentials *credentials, 
145                                             struct smb_krb5_context *smb_krb5_context,
146                                             krb5_principal *princ,
147                                             enum credentials_obtained *obtained,
148                                             const char **error_string)
149 {
150         krb5_error_code ret;
151         const char *princ_string;
152         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
153         if (!mem_ctx) {
154                 (*error_string) = error_message(ENOMEM);
155                 return ENOMEM;
156         }
157         princ_string = cli_credentials_get_principal_and_obtained(credentials, mem_ctx, obtained);
158         if (!princ_string) {
159                 (*error_string) = error_message(ENOMEM);
160                 return ENOMEM;
161         }
162
163         ret = parse_principal(parent_ctx, princ_string,
164                               smb_krb5_context, princ, error_string);
165         talloc_free(mem_ctx);
166         return ret;
167 }
168
169 /* Obtain the principal set on this context.  Requires a
170  * smb_krb5_context because we are doing krb5 principal parsing with
171  * the library routines.  The returned princ is placed in the talloc
172  * system by means of a destructor (do *not* free). */
173
174  krb5_error_code impersonate_principal_from_credentials(TALLOC_CTX *parent_ctx,
175                                                         struct cli_credentials *credentials,
176                                                         struct smb_krb5_context *smb_krb5_context,
177                                                         krb5_principal *princ,
178                                                         const char **error_string)
179 {
180         return parse_principal(parent_ctx, cli_credentials_get_impersonate_principal(credentials),
181                                smb_krb5_context, princ, error_string);
182 }
183
184 /**
185  * Return a freshly allocated ccache (destroyed by destructor on child
186  * of parent_ctx), for a given set of client credentials 
187  */
188
189  krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
190                                  struct cli_credentials *credentials,
191                                  struct smb_krb5_context *smb_krb5_context,
192                                  krb5_ccache ccache,
193                                  enum credentials_obtained *obtained,
194                                  const char **error_string)
195 {
196         krb5_error_code ret;
197         const char *password, *target_service;
198         time_t kdc_time = 0;
199         krb5_principal princ;
200         krb5_principal impersonate_principal;
201         int tries;
202         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
203
204         if (!mem_ctx) {
205                 (*error_string) = strerror(ENOMEM);
206                 return ENOMEM;
207         }
208
209         ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ, obtained, error_string);
210         if (ret) {
211                 talloc_free(mem_ctx);
212                 return ret;
213         }
214
215         ret = impersonate_principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &impersonate_principal, error_string);
216         if (ret) {
217                 talloc_free(mem_ctx);
218                 return ret;
219         }
220
221         target_service = cli_credentials_get_target_service(credentials);
222
223         password = cli_credentials_get_password(credentials);
224
225         tries = 2;
226         while (tries--) {
227                 if (password) {
228                         ret = kerberos_kinit_password_cc(smb_krb5_context->krb5_context, ccache, 
229                                                          princ, password,
230                                                          impersonate_principal, target_service,
231                                                          NULL, &kdc_time);
232                 } else if (impersonate_principal) {
233                         (*error_string) = "INTERNAL error: Cannot impersonate principal with just a keyblock.  A password must be specified in the credentials";
234                         return EINVAL;
235                 } else {
236                         /* No password available, try to use a keyblock instead */
237                         
238                         krb5_keyblock keyblock;
239                         const struct samr_Password *mach_pwd;
240                         mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
241                         if (!mach_pwd) {
242                                 talloc_free(mem_ctx);
243                                 (*error_string) = "kinit_to_ccache: No password available for kinit\n";
244                                 return EINVAL;
245                         }
246                         ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
247                                                  ENCTYPE_ARCFOUR_HMAC,
248                                                  mach_pwd->hash, sizeof(mach_pwd->hash), 
249                                                  &keyblock);
250                         
251                         if (ret == 0) {
252                                 ret = kerberos_kinit_keyblock_cc(smb_krb5_context->krb5_context, ccache, 
253                                                                  princ, &keyblock,
254                                                                  target_service,
255                                                                  NULL, &kdc_time);
256                                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
257                         }
258                 }
259
260                 if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
261                         /* Perhaps we have been given an invalid skew, so try again without it */
262                         time_t t = time(NULL);
263                         krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
264                 } else {
265                         /* not a skew problem */
266                         break;
267                 }
268         }
269
270         if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
271                 (*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
272                                                   cli_credentials_get_principal(credentials, mem_ctx),
273                                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context,
274                                                                              ret, mem_ctx));
275                 talloc_free(mem_ctx);
276                 return ret;
277         }
278
279         /* cope with ticket being in the future due to clock skew */
280         if ((unsigned)kdc_time > time(NULL)) {
281                 time_t t = time(NULL);
282                 int time_offset =(unsigned)kdc_time-t;
283                 DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
284                 krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
285         }
286         
287         if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
288                 ret = kinit_to_ccache(parent_ctx,
289                                       credentials,
290                                       smb_krb5_context,
291                                       ccache, obtained,
292                                       error_string);
293         }
294         if (ret) {
295                 (*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
296                                                   cli_credentials_get_principal(credentials, mem_ctx),
297                                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context,
298                                                                              ret, mem_ctx));
299                 talloc_free(mem_ctx);
300                 return ret;
301         } 
302         talloc_free(mem_ctx);
303         return 0;
304 }
305
306 static krb5_error_code free_keytab(struct keytab_container *ktc)
307 {
308         return krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
309 }
310
311 krb5_error_code smb_krb5_open_keytab(TALLOC_CTX *mem_ctx,
312                          struct smb_krb5_context *smb_krb5_context, 
313                          const char *keytab_name, struct keytab_container **ktc) 
314 {
315         krb5_keytab keytab;
316         krb5_error_code ret;
317         ret = krb5_kt_resolve(smb_krb5_context->krb5_context, keytab_name, &keytab);
318         if (ret) {
319                 DEBUG(1,("failed to open krb5 keytab: %s\n", 
320                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
321                                                     ret, mem_ctx)));
322                 return ret;
323         }
324
325         *ktc = talloc(mem_ctx, struct keytab_container);
326         if (!*ktc) {
327                 return ENOMEM;
328         }
329
330         (*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
331         (*ktc)->keytab = keytab;
332         talloc_set_destructor(*ktc, free_keytab);
333
334         return 0;
335 }
336
337 static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
338                                        const char *princ_string,
339                                        krb5_principal princ,
340                                        krb5_principal salt_princ,
341                                        int kvno,
342                                        const char *password_s,
343                                        struct smb_krb5_context *smb_krb5_context,
344                                        const char **enctype_strings,
345                                        krb5_keytab keytab)
346 {
347         int i;
348         krb5_error_code ret;
349         krb5_data password;
350         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
351         if (!mem_ctx) {
352                 return ENOMEM;
353         }
354
355         password.data = discard_const_p(char *, password_s);
356         password.length = strlen(password_s);
357
358         for (i=0; enctype_strings[i]; i++) {
359                 krb5_keytab_entry entry;
360                 krb5_enctype enctype;
361                 ret = krb5_string_to_enctype(smb_krb5_context->krb5_context, enctype_strings[i], &enctype);
362                 if (ret != 0) {
363                         DEBUG(1, ("Failed to interpret %s as a krb5 encryption type: %s\n",                               
364                                   enctype_strings[i],
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                 ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context, 
371                                                       salt_princ, &password, &entry.keyblock, enctype);
372                 if (ret != 0) {
373                         talloc_free(mem_ctx);
374                         return ret;
375                 }
376
377                 entry.principal = princ;
378                 entry.vno       = kvno;
379                 ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
380                 if (ret != 0) {
381                         DEBUG(1, ("Failed to add %s entry for %s(kvno %d) to keytab: %s\n",
382                                   enctype_strings[i],
383                                   princ_string,
384                                   kvno,
385                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
386                                                              ret, mem_ctx)));
387                         talloc_free(mem_ctx);
388                         krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
389                         return ret;
390                 }
391
392                 DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n", 
393                           princ_string, kvno,
394                           enctype_strings[i]));
395                 
396                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
397         }
398         talloc_free(mem_ctx);
399         return 0;
400 }
401
402 static krb5_error_code create_keytab(TALLOC_CTX *parent_ctx,
403                          struct cli_credentials *machine_account,
404                          struct smb_krb5_context *smb_krb5_context,
405                          const char **enctype_strings,
406                          krb5_keytab keytab,
407                          bool add_old) 
408 {
409         krb5_error_code ret;
410         const char *password_s;
411         const char *old_secret;
412         int kvno;
413         krb5_principal salt_princ;
414         krb5_principal princ;
415         const char *princ_string;
416         const char *error_string;
417         enum credentials_obtained obtained;
418
419         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
420         if (!mem_ctx) {
421                 return ENOMEM;
422         }
423
424         princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
425         /* Get the principal we will store the new keytab entries under */
426         ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ, &obtained, &error_string);
427         if (ret) {
428                 DEBUG(1,("create_keytab: makeing krb5 principal failed (%s)\n", error_string));
429                 talloc_free(mem_ctx);
430                 return ret;
431         }
432
433         /* The salt used to generate these entries may be different however, fetch that */
434         ret = salt_principal_from_credentials(mem_ctx, machine_account, 
435                                               smb_krb5_context, 
436                                               &salt_princ);
437         if (ret) {
438                 DEBUG(1,("create_keytab: makeing salt principal failed (%s)\n",
439                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
440                                                     ret, mem_ctx)));
441                 talloc_free(mem_ctx);
442                 return ret;
443         }
444
445         /* Finally, do the dance to get the password to put in the entry */
446         password_s = cli_credentials_get_password(machine_account);
447         if (!password_s) {
448                 krb5_keytab_entry entry;
449                 const struct samr_Password *mach_pwd;
450
451                 if (!str_list_check(enctype_strings, "arcfour-hmac-md5")) {
452                         DEBUG(1, ("Asked to create keytab, but with only an NT hash supplied, "
453                                   "but not listing arcfour-hmac-md5 as an enc type to include in the keytab!\n"));
454                         talloc_free(mem_ctx);
455                         return EINVAL;
456                 }
457
458                 /* If we don't have the plaintext password, try for
459                  * the MD4 password hash */
460                 mach_pwd = cli_credentials_get_nt_hash(machine_account, mem_ctx);
461                 if (!mach_pwd) {
462                         /* OK, nothing to do here */
463                         talloc_free(mem_ctx);
464                         return 0;
465                 }
466                 ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
467                                          ETYPE_ARCFOUR_HMAC_MD5,
468                                          mach_pwd->hash, sizeof(mach_pwd->hash), 
469                                          &entry.keyblock);
470                 if (ret) {
471                         DEBUG(1, ("create_keytab: krb5_keyblock_init failed: %s\n",
472                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
473                                                              ret, mem_ctx)));
474                         talloc_free(mem_ctx);
475                         return ret;
476                 }
477
478                 entry.principal = princ;
479                 entry.vno       = cli_credentials_get_kvno(machine_account);
480                 ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
481                 if (ret) {
482                         DEBUG(1, ("Failed to add ARCFOUR_HMAC (only) entry for %s to keytab: %s",
483                                   cli_credentials_get_principal(machine_account, mem_ctx), 
484                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
485                                                              ret, mem_ctx)));
486                         talloc_free(mem_ctx);
487                         krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
488                         return ret;
489                 }
490                 
491                 DEBUG(5, ("Added %s(kvno %d) to keytab (arcfour-hmac-md5)\n", 
492                           cli_credentials_get_principal(machine_account, mem_ctx),
493                           cli_credentials_get_kvno(machine_account)));
494
495                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
496
497                 /* Can't go any further, we only have this one key */
498                 talloc_free(mem_ctx);
499                 return 0;
500         }
501         
502         kvno = cli_credentials_get_kvno(machine_account);
503         /* good, we actually have the real plaintext */
504         ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ, 
505                               kvno, password_s, smb_krb5_context, 
506                               enctype_strings, keytab);
507         if (!ret) {
508                 talloc_free(mem_ctx);
509                 return ret;
510         }
511
512         if (!add_old || kvno == 0) {
513                 talloc_free(mem_ctx);
514                 return 0;
515         }
516
517         old_secret = cli_credentials_get_old_password(machine_account);
518         if (!old_secret) {
519                 talloc_free(mem_ctx);
520                 return 0;
521         }
522         
523         ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ, 
524                               kvno - 1, old_secret, smb_krb5_context, 
525                               enctype_strings, keytab);
526         if (!ret) {
527                 talloc_free(mem_ctx);
528                 return ret;
529         }
530
531         talloc_free(mem_ctx);
532         return 0;
533 }
534
535
536 /*
537  * Walk the keytab, looking for entries of this principal name, with KVNO other than current kvno -1.
538  *
539  * These entries are now stale, we only keep the current, and previous entries around.
540  *
541  * Inspired by the code in Samba3 for 'use kerberos keytab'.
542  *
543  */
544
545 static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx,
546                                           struct cli_credentials *machine_account,
547                                           struct smb_krb5_context *smb_krb5_context,
548                                           krb5_keytab keytab, bool *found_previous)
549 {
550         krb5_error_code ret, ret2;
551         krb5_kt_cursor cursor;
552         krb5_principal princ;
553         int kvno;
554         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
555         const char *princ_string;
556         const char *error_string;
557         enum credentials_obtained obtained;
558
559         if (!mem_ctx) {
560                 return ENOMEM;
561         }
562
563         *found_previous = false;
564         princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
565
566         /* Get the principal we will store the new keytab entries under */
567         ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ, &obtained, &error_string);
568         if (ret) {
569                 DEBUG(1,("update_keytab: makeing krb5 principal failed (%s)\n", error_string));
570                 talloc_free(mem_ctx);
571                 return ret;
572         }
573
574         kvno = cli_credentials_get_kvno(machine_account);
575
576         /* for each entry in the keytab */
577         ret = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
578         switch (ret) {
579         case 0:
580                 break;
581         case HEIM_ERR_OPNOTSUPP:
582         case ENOENT:
583         case KRB5_KT_END:
584                 /* no point enumerating if there isn't anything here */
585                 talloc_free(mem_ctx);
586                 return 0;
587         default:
588                 DEBUG(1,("failed to open keytab for read of old entries: %s\n",
589                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
590                                                     ret, mem_ctx)));
591                 talloc_free(mem_ctx);
592                 return ret;
593         }
594
595         while (!ret) {
596                 krb5_keytab_entry entry;
597                 ret = krb5_kt_next_entry(smb_krb5_context->krb5_context, keytab, &entry, &cursor);
598                 if (ret) {
599                         break;
600                 }
601                 /* if it matches our principal */
602                 if (!krb5_kt_compare(smb_krb5_context->krb5_context, &entry, princ, 0, 0)) {
603                         /* Free the entry, it wasn't the one we were looking for anyway */
604                         krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
605                         continue;
606                 }
607
608                 /* delete it, if it is not kvno -1 */
609                 if (entry.vno != (kvno - 1 )) {
610                         /* Release the enumeration.  We are going to
611                          * have to start this from the top again,
612                          * because deletes during enumeration may not
613                          * always be consistant.
614                          *
615                          * Also, the enumeration locks a FILE: keytab
616                          */
617                 
618                         krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
619
620                         ret = krb5_kt_remove_entry(smb_krb5_context->krb5_context, keytab, &entry);
621                         krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
622
623                         /* Deleted: Restart from the top */
624                         ret2 = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
625                         if (ret2) {
626                                 krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
627                                 DEBUG(1,("failed to restart enumeration of keytab: %s\n",
628                                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
629                                                                     ret, mem_ctx)));
630                                 
631                                 talloc_free(mem_ctx);
632                                 return ret2;
633                         }
634
635                         if (ret) {
636                                 break;
637                         }
638                         
639                 } else {
640                         *found_previous = true;
641                 }
642                 
643                 /* Free the entry, we don't need it any more */
644                 krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
645                 
646                 
647         }
648         krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
649
650         switch (ret) {
651         case 0:
652                 break;
653         case ENOENT:
654         case KRB5_KT_END:
655                 ret = 0;
656                 break;
657         default:
658                 DEBUG(1,("failed in deleting old entries for principal: %s: %s\n",
659                          princ_string, 
660                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
661                                                     ret, mem_ctx)));
662         }
663         talloc_free(mem_ctx);
664         return ret;
665 }
666
667 krb5_error_code smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
668                            struct cli_credentials *machine_account,
669                            struct smb_krb5_context *smb_krb5_context,
670                            const char **enctype_strings,
671                            struct keytab_container *keytab_container) 
672 {
673         krb5_error_code ret;
674         bool found_previous;
675         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
676         if (!mem_ctx) {
677                 return ENOMEM;
678         }
679
680         ret = remove_old_entries(mem_ctx, machine_account, 
681                                  smb_krb5_context, keytab_container->keytab, &found_previous);
682         if (ret != 0) {
683                 talloc_free(mem_ctx);
684                 return ret;
685         }
686         
687         /* Create a new keytab.  If during the cleanout we found
688          * entires for kvno -1, then don't try and duplicate them.
689          * Otherwise, add kvno, and kvno -1 */
690         
691         ret = create_keytab(mem_ctx, machine_account, smb_krb5_context, 
692                             enctype_strings, 
693                             keytab_container->keytab, 
694                             found_previous ? false : true);
695         talloc_free(mem_ctx);
696         return ret;
697 }
698
699 krb5_error_code smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
700                                            struct cli_credentials *machine_account,
701                                            struct smb_krb5_context *smb_krb5_context,
702                                            const char **enctype_strings,
703                                            struct keytab_container **keytab_container) 
704 {
705         krb5_error_code ret;
706         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
707         const char *rand_string;
708         const char *keytab_name;
709         if (!mem_ctx) {
710                 return ENOMEM;
711         }
712         
713         *keytab_container = talloc(mem_ctx, struct keytab_container);
714
715         rand_string = generate_random_str(mem_ctx, 16);
716         if (!rand_string) {
717                 talloc_free(mem_ctx);
718                 return ENOMEM;
719         }
720
721         keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s", 
722                                       rand_string);
723         if (!keytab_name) {
724                 talloc_free(mem_ctx);
725                 return ENOMEM;
726         }
727
728         ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context, keytab_name, keytab_container);
729         if (ret) {
730                 return ret;
731         }
732
733         ret = smb_krb5_update_keytab(mem_ctx, machine_account, smb_krb5_context, enctype_strings, *keytab_container);
734         if (ret == 0) {
735                 talloc_steal(parent_ctx, *keytab_container);
736         } else {
737                 *keytab_container = NULL;
738         }
739         talloc_free(mem_ctx);
740         return ret;
741 }
742
743 /* Translate between the IETF encryption type values and the Microsoft msDS-SupportedEncryptionTypes values */
744 uint32_t kerberos_enctype_to_bitmap(krb5_enctype enc_type_enum)
745 {
746         switch (enc_type_enum) {
747         case ENCTYPE_DES_CBC_CRC:
748                 return ENC_CRC32;
749         case ENCTYPE_DES_CBC_MD5:
750                 return ENC_RSA_MD5;
751         case ENCTYPE_ARCFOUR_HMAC_MD5:
752                 return ENC_RC4_HMAC_MD5;
753         case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
754                 return ENC_HMAC_SHA1_96_AES128;
755         case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
756                 return ENC_HMAC_SHA1_96_AES256;
757         default:
758                 return 0;
759         }
760 }
761
762 /* Translate between the Microsoft msDS-SupportedEncryptionTypes values and the IETF encryption type values */
763 krb5_enctype kerberos_enctype_bitmap_to_enctype(uint32_t enctype_bitmap)
764 {
765         switch (enctype_bitmap) {
766         case ENC_CRC32:
767                 return ENCTYPE_DES_CBC_CRC;
768         case ENC_RSA_MD5:
769                 return ENCTYPE_DES_CBC_MD5;
770         case ENC_RC4_HMAC_MD5:
771                 return ENCTYPE_ARCFOUR_HMAC_MD5;
772         case ENC_HMAC_SHA1_96_AES128:
773                 return ENCTYPE_AES128_CTS_HMAC_SHA1_96;
774         case ENC_HMAC_SHA1_96_AES256:
775                 return ENCTYPE_AES256_CTS_HMAC_SHA1_96;
776         default:
777                 return 0;
778         }
779 }
780
781 /* Return an array of krb5_enctype values */
782 krb5_error_code kerberos_enctype_bitmap_to_enctypes(TALLOC_CTX *mem_ctx, uint32_t enctype_bitmap, krb5_enctype **enctypes)
783 {
784         unsigned int i, j = 0;
785         *enctypes = talloc_zero_array(mem_ctx, krb5_enctype, 8*sizeof(enctype_bitmap));
786         if (!*enctypes) {
787                 return ENOMEM;
788         }
789         for (i=0; i<(8*sizeof(enctype_bitmap)); i++) {
790                 if ((1 << i) & enctype_bitmap) {
791                         (*enctypes)[j] = kerberos_enctype_bitmap_to_enctype(enctype_bitmap);
792                         if (!(*enctypes)[j]) {
793                                 return KRB5_PROG_ETYPE_NOSUPP;
794                         }
795                         j++;
796                 }
797         }
798         return 0;
799 }