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