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