s3-auth Rename smb_krb5_open_keytab to avoid a conflict with s3
[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                                  struct tevent_context *event_ctx,
336                                  krb5_ccache ccache,
337                                  enum credentials_obtained *obtained,
338                                  const char **error_string)
339 {
340         krb5_error_code ret;
341         const char *password, *target_service;
342         time_t kdc_time = 0;
343         krb5_principal princ;
344         krb5_principal impersonate_principal;
345         int tries;
346         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
347         krb5_get_init_creds_opt *krb_options;
348
349         if (!mem_ctx) {
350                 (*error_string) = strerror(ENOMEM);
351                 return ENOMEM;
352         }
353
354         ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ, obtained, error_string);
355         if (ret) {
356                 talloc_free(mem_ctx);
357                 return ret;
358         }
359
360         ret = impersonate_principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &impersonate_principal, error_string);
361         if (ret) {
362                 talloc_free(mem_ctx);
363                 return ret;
364         }
365
366         target_service = cli_credentials_get_target_service(credentials);
367
368         password = cli_credentials_get_password(credentials);
369
370         /* setup the krb5 options we want */
371         if ((ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options))) {
372                 (*error_string) = talloc_asprintf(credentials, "krb5_get_init_creds_opt_alloc failed (%s)\n",
373                                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context,
374                                                                              ret, mem_ctx));
375                 talloc_free(mem_ctx);
376                 return ret;
377         }
378
379         /* get the defaults */
380         krb5_get_init_creds_opt_set_default_flags(smb_krb5_context->krb5_context, NULL, NULL, krb_options);
381
382         /* set if we want a forwardable ticket */
383         switch (cli_credentials_get_krb_forwardable(credentials)) {
384         case CRED_AUTO_KRB_FORWARDABLE:
385                 break;
386         case CRED_NO_KRB_FORWARDABLE:
387                 krb5_get_init_creds_opt_set_forwardable(krb_options, FALSE);
388                 break;
389         case CRED_FORCE_KRB_FORWARDABLE:
390                 krb5_get_init_creds_opt_set_forwardable(krb_options, TRUE);
391                 break;
392         }
393
394         tries = 2;
395         while (tries--) {
396                 struct tevent_context *previous_ev;
397                 /* Do this every time, in case we have weird recursive issues here */
398                 ret = smb_krb5_context_set_event_ctx(smb_krb5_context, event_ctx, &previous_ev);
399                 if (ret) {
400                         talloc_free(mem_ctx);
401                         return ret;
402                 }
403                 if (password) {
404                         ret = kerberos_kinit_password_cc(smb_krb5_context->krb5_context, ccache, 
405                                                          princ, password,
406                                                          impersonate_principal, target_service,
407                                                          krb_options,
408                                                          NULL, &kdc_time);
409                 } else if (impersonate_principal) {
410                         talloc_free(mem_ctx);
411                         (*error_string) = "INTERNAL error: Cannot impersonate principal with just a keyblock.  A password must be specified in the credentials";
412                         return EINVAL;
413                 } else {
414                         /* No password available, try to use a keyblock instead */
415                         
416                         krb5_keyblock keyblock;
417                         const struct samr_Password *mach_pwd;
418                         mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
419                         if (!mach_pwd) {
420                                 talloc_free(mem_ctx);
421                                 (*error_string) = "kinit_to_ccache: No password available for kinit\n";
422                                 krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
423                                 smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
424                                 return EINVAL;
425                         }
426                         ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
427                                                  ENCTYPE_ARCFOUR_HMAC,
428                                                  mach_pwd->hash, sizeof(mach_pwd->hash), 
429                                                  &keyblock);
430                         
431                         if (ret == 0) {
432                                 ret = kerberos_kinit_keyblock_cc(smb_krb5_context->krb5_context, ccache, 
433                                                                  princ, &keyblock,
434                                                                  target_service, krb_options,
435                                                                  NULL, &kdc_time);
436                                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
437                         }
438                 }
439
440                 smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
441
442                 if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
443                         /* Perhaps we have been given an invalid skew, so try again without it */
444                         time_t t = time(NULL);
445                         krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
446                 } else {
447                         /* not a skew problem */
448                         break;
449                 }
450         }
451
452         krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
453
454         if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
455                 (*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
456                                                   cli_credentials_get_principal(credentials, mem_ctx),
457                                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context,
458                                                                              ret, mem_ctx));
459                 talloc_free(mem_ctx);
460                 return ret;
461         }
462
463         /* cope with ticket being in the future due to clock skew */
464         if ((unsigned)kdc_time > time(NULL)) {
465                 time_t t = time(NULL);
466                 int time_offset =(unsigned)kdc_time-t;
467                 DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
468                 krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
469         }
470         
471         if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
472                 ret = kinit_to_ccache(parent_ctx,
473                                       credentials,
474                                       smb_krb5_context,
475                                       event_ctx,
476                                       ccache, obtained,
477                                       error_string);
478         }
479
480         if (ret) {
481                 (*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
482                                                   cli_credentials_get_principal(credentials, mem_ctx),
483                                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context,
484                                                                              ret, mem_ctx));
485                 talloc_free(mem_ctx);
486                 return ret;
487         } 
488         talloc_free(mem_ctx);
489         return 0;
490 }
491
492 static krb5_error_code free_keytab_container(struct keytab_container *ktc)
493 {
494         return krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
495 }
496
497 krb5_error_code smb_krb5_get_keytab_container(TALLOC_CTX *mem_ctx,
498                                               struct smb_krb5_context *smb_krb5_context,
499                                               const char *keytab_name, struct keytab_container **ktc)
500 {
501         krb5_keytab keytab;
502         krb5_error_code ret;
503         ret = krb5_kt_resolve(smb_krb5_context->krb5_context, keytab_name, &keytab);
504         if (ret) {
505                 DEBUG(1,("failed to open krb5 keytab: %s\n", 
506                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
507                                                     ret, mem_ctx)));
508                 return ret;
509         }
510
511         *ktc = talloc(mem_ctx, struct keytab_container);
512         if (!*ktc) {
513                 return ENOMEM;
514         }
515
516         (*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
517         (*ktc)->keytab = keytab;
518         talloc_set_destructor(*ktc, free_keytab_container);
519
520         return 0;
521 }
522
523 static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
524                                        struct principal_container **principals,
525                                        krb5_principal salt_princ,
526                                        int kvno,
527                                        const char *password_s,
528                                        struct smb_krb5_context *smb_krb5_context,
529                                        krb5_enctype *enctypes,
530                                        krb5_keytab keytab,
531                                        const char **error_string)
532 {
533         unsigned int i, p;
534         krb5_error_code ret;
535         krb5_data password;
536
537         password.data = discard_const_p(char *, password_s);
538         password.length = strlen(password_s);
539
540         for (i=0; enctypes[i]; i++) {
541                 krb5_keytab_entry entry;
542
543                 ZERO_STRUCT(entry);
544
545                 ret = create_kerberos_key_from_string_direct(smb_krb5_context->krb5_context,
546                                                              salt_princ, &password, &entry.keyblock, enctypes[i]);
547                 if (ret != 0) {
548                         return ret;
549                 }
550
551                 entry.vno = kvno;
552
553                 for (p=0; principals[p]; p++) {
554                         entry.principal = principals[p]->principal;
555                         ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
556                         if (ret != 0) {
557                                 char *k5_error_string = smb_get_krb5_error_message(smb_krb5_context->krb5_context,
558                                                                                    ret, NULL);
559                                 *error_string = talloc_asprintf(parent_ctx, "Failed to add enctype %d entry for %s(kvno %d) to keytab: %s\n",
560                                                                 (int)enctypes[i],
561                                                                 principals[p]->string_form,
562                                                                 kvno,
563                                                                 k5_error_string);
564                                 talloc_free(k5_error_string);
565                                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
566                                 return ret;
567                         }
568
569                         DEBUG(5, ("Added %s(kvno %d) to keytab (enctype %d)\n", 
570                                   principals[p]->string_form, kvno,
571                                   (int)enctypes[i]));
572                 }
573                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
574         }
575         return 0;
576 }
577
578 static krb5_error_code create_keytab(TALLOC_CTX *parent_ctx,
579                                      struct ldb_message *msg,
580                                      struct principal_container **principals,
581                                      struct smb_krb5_context *smb_krb5_context,
582                                      krb5_keytab keytab,
583                                      bool add_old,
584                                      const char **error_string)
585 {
586         krb5_error_code ret;
587         const char *password_s;
588         const char *old_secret;
589         int kvno;
590         uint32_t enctype_bitmap;
591         krb5_principal salt_princ;
592         krb5_enctype *enctypes;
593         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
594         if (!mem_ctx) {
595                 *error_string = "unable to allocate tmp_ctx for create_keytab";
596                 return ENOMEM;
597         }
598
599         /* The salt used to generate these entries may be different however, fetch that */
600         ret = salt_principal_from_msg(mem_ctx, msg,
601                                       smb_krb5_context, 
602                                       &salt_princ, error_string);
603         if (ret) {
604                 talloc_free(mem_ctx);
605                 return ret;
606         }
607
608         kvno = ldb_msg_find_attr_as_int(msg, "msDS-KeyVersionNumber", 0);
609
610         /* Finally, do the dance to get the password to put in the entry */
611         password_s =  ldb_msg_find_attr_as_string(msg, "secret", NULL);
612
613         if (!password_s) {
614                 /* There is no password here, so nothing to do */
615                 talloc_free(mem_ctx);
616                 return 0;
617         }
618
619         if (add_old && kvno != 0) {
620                 old_secret = ldb_msg_find_attr_as_string(msg, "priorSecret", NULL);
621         } else {
622                 old_secret = NULL;
623         }
624
625         enctype_bitmap = (uint32_t)ldb_msg_find_attr_as_int(msg, "msDS-SupportedEncryptionTypes", ENC_ALL_TYPES);
626         
627         ret = kerberos_enctype_bitmap_to_enctypes(mem_ctx, enctype_bitmap, &enctypes);
628         if (ret) {
629                 *error_string = talloc_asprintf(parent_ctx, "create_keytab: generating list of encryption types failed (%s)\n",
630                                                 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
631                                                                            ret, mem_ctx));
632                 talloc_free(mem_ctx);
633                 return ret;
634         }
635
636         ret = keytab_add_keys(mem_ctx, principals,
637                               salt_princ,
638                               kvno, password_s, smb_krb5_context,
639                               enctypes, keytab, error_string);
640         if (ret) {
641                 talloc_free(mem_ctx);
642                 return ret;
643         }
644         
645         if (old_secret) {
646                 ret = keytab_add_keys(mem_ctx, principals,
647                                       salt_princ,
648                                       kvno - 1, old_secret, smb_krb5_context,
649                                       enctypes, keytab, error_string);
650                 if (ret) {
651                         talloc_free(mem_ctx);
652                         return ret;
653                 }
654         }
655
656         talloc_free(mem_ctx);
657         return ret;
658 }
659
660 /*
661  * Walk the keytab, looking for entries of this principal name, with KVNO other than current kvno -1.
662  *
663  * These entries are now stale, we only keep the current, and previous entries around.
664  *
665  * Inspired by the code in Samba3 for 'use kerberos keytab'.
666  *
667  */
668
669 static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx,
670                                           struct ldb_message *msg,
671                                           struct principal_container **principals,
672                                           bool delete_all_kvno,
673                                           struct smb_krb5_context *smb_krb5_context,
674                                           krb5_keytab keytab, bool *found_previous,
675                                           const char **error_string)
676 {
677         krb5_error_code ret, ret2;
678         krb5_kt_cursor cursor;
679         int kvno;
680         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
681
682         if (!mem_ctx) {
683                 return ENOMEM;
684         }
685
686         *found_previous = false;
687
688         kvno = ldb_msg_find_attr_as_int(msg, "msDS-KeyVersionNumber", 0);
689
690         /* for each entry in the keytab */
691         ret = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
692         switch (ret) {
693         case 0:
694                 break;
695         case HEIM_ERR_OPNOTSUPP:
696         case ENOENT:
697         case KRB5_KT_END:
698                 /* no point enumerating if there isn't anything here */
699                 talloc_free(mem_ctx);
700                 return 0;
701         default:
702                 *error_string = talloc_asprintf(parent_ctx, "failed to open keytab for read of old entries: %s\n",
703                                                 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
704                                                                            ret, mem_ctx));
705                 talloc_free(mem_ctx);
706                 return ret;
707         }
708
709         while (!ret) {
710                 unsigned int i;
711                 bool matched = false;
712                 krb5_keytab_entry entry;
713                 ret = krb5_kt_next_entry(smb_krb5_context->krb5_context, keytab, &entry, &cursor);
714                 if (ret) {
715                         break;
716                 }
717                 for (i = 0; principals[i]; i++) {
718                         /* if it matches our principal */
719                         if (krb5_kt_compare(smb_krb5_context->krb5_context, &entry, principals[i]->principal, 0, 0)) {
720                                 matched = true;
721                                 break;
722                         }
723                 }
724
725                 if (!matched) {
726                         /* Free the entry, it wasn't the one we were looking for anyway */
727                         krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
728                         continue;
729                 }
730
731                 /* delete it, if it is not kvno -1 */
732                 if (entry.vno != (kvno - 1 )) {
733                         /* Release the enumeration.  We are going to
734                          * have to start this from the top again,
735                          * because deletes during enumeration may not
736                          * always be consistant.
737                          *
738                          * Also, the enumeration locks a FILE: keytab
739                          */
740                 
741                         krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
742
743                         ret = krb5_kt_remove_entry(smb_krb5_context->krb5_context, keytab, &entry);
744                         krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
745
746                         /* Deleted: Restart from the top */
747                         ret2 = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
748                         if (ret2) {
749                                 krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
750                                 DEBUG(1,("failed to restart enumeration of keytab: %s\n",
751                                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
752                                                                     ret, mem_ctx)));
753                                 
754                                 talloc_free(mem_ctx);
755                                 return ret2;
756                         }
757
758                         if (ret) {
759                                 break;
760                         }
761                         
762                 } else {
763                         *found_previous = true;
764                 }
765                 
766                 /* Free the entry, we don't need it any more */
767                 krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
768                 
769                 
770         }
771         krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
772
773         switch (ret) {
774         case 0:
775                 break;
776         case ENOENT:
777         case KRB5_KT_END:
778                 ret = 0;
779                 break;
780         default:
781                 *error_string = talloc_asprintf(parent_ctx, "failed in deleting old entries for principal: %s\n",
782                                                 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
783                                                                            ret, mem_ctx));
784         }
785         talloc_free(mem_ctx);
786         return ret;
787 }
788
789 krb5_error_code smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
790                                        struct smb_krb5_context *smb_krb5_context,
791                                        struct ldb_context *ldb, 
792                                        struct ldb_message *msg,
793                                        bool delete_all_kvno,
794                                        const char **error_string)
795 {
796         krb5_error_code ret;
797         bool found_previous;
798         TALLOC_CTX *mem_ctx = talloc_new(NULL);
799         struct keytab_container *keytab_container;
800         struct principal_container **principals;
801         const char *keytab_name;
802
803         if (!mem_ctx) {
804                 return ENOMEM;
805         }
806
807         keytab_name = keytab_name_from_msg(mem_ctx, ldb, msg);
808         if (!keytab_name) {
809                 return ENOENT;
810         }
811
812         ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context, keytab_name, &keytab_container);
813
814         if (ret != 0) {
815                 talloc_free(mem_ctx);
816                 return ret;
817         }
818
819         DEBUG(5, ("Opened keytab %s\n", keytab_name));
820
821         /* Get the principal we will store the new keytab entries under */
822         ret = principals_from_msg(mem_ctx, msg, smb_krb5_context, &principals, error_string);
823
824         if (ret != 0) {
825                 *error_string = talloc_asprintf(parent_ctx, "Failed to load principals from ldb message: %s\n", *error_string);
826                 talloc_free(mem_ctx);
827                 return ret;
828         }
829
830         ret = remove_old_entries(mem_ctx, msg, principals, delete_all_kvno,
831                                  smb_krb5_context, keytab_container->keytab, &found_previous, error_string);
832         if (ret != 0) {
833                 *error_string = talloc_asprintf(parent_ctx, "Failed to remove old principals from keytab: %s\n", *error_string);
834                 talloc_free(mem_ctx);
835                 return ret;
836         }
837         
838         if (!delete_all_kvno) {
839                 /* Create a new keytab.  If during the cleanout we found
840                  * entires for kvno -1, then don't try and duplicate them.
841                  * Otherwise, add kvno, and kvno -1 */
842                 
843                 ret = create_keytab(mem_ctx, msg, principals,
844                                     smb_krb5_context,
845                                     keytab_container->keytab, 
846                                     found_previous ? false : true, error_string);
847         }
848         talloc_free(mem_ctx);
849         return ret;
850 }
851
852 krb5_error_code smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
853                                            struct cli_credentials *machine_account,
854                                            struct smb_krb5_context *smb_krb5_context,
855                                            struct keytab_container **keytab_container) 
856 {
857         krb5_error_code ret;
858         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
859         const char *rand_string;
860         const char *keytab_name;
861         struct ldb_message *msg;
862         const char *error_string;
863         if (!mem_ctx) {
864                 return ENOMEM;
865         }
866         
867         *keytab_container = talloc(mem_ctx, struct keytab_container);
868
869         rand_string = generate_random_str(mem_ctx, 16);
870         if (!rand_string) {
871                 talloc_free(mem_ctx);
872                 return ENOMEM;
873         }
874
875         keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s", 
876                                       rand_string);
877         if (!keytab_name) {
878                 talloc_free(mem_ctx);
879                 return ENOMEM;
880         }
881
882         ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context, keytab_name, keytab_container);
883         if (ret) {
884                 return ret;
885         }
886
887         msg = ldb_msg_new(mem_ctx);
888         if (!msg) {
889                 talloc_free(mem_ctx);
890                 return ENOMEM;
891         }
892         ldb_msg_add_string(msg, "krb5Keytab", keytab_name);
893         ldb_msg_add_string(msg, "secret", cli_credentials_get_password(machine_account));
894         ldb_msg_add_string(msg, "samAccountName", cli_credentials_get_username(machine_account));
895         ldb_msg_add_string(msg, "realm", cli_credentials_get_realm(machine_account));
896         ldb_msg_add_fmt(msg, "msDS-KeyVersionNumber", "%d", (int)cli_credentials_get_kvno(machine_account));
897
898         ret = smb_krb5_update_keytab(mem_ctx, smb_krb5_context, NULL, msg, false, &error_string);
899         if (ret == 0) {
900                 talloc_steal(parent_ctx, *keytab_container);
901         } else {
902                 DEBUG(0, ("Failed to create in-memory keytab: %s\n", error_string));
903                 *keytab_container = NULL;
904         }
905         talloc_free(mem_ctx);
906         return ret;
907 }
908 /* Translate between the IETF encryption type values and the Microsoft msDS-SupportedEncryptionTypes values */
909 uint32_t kerberos_enctype_to_bitmap(krb5_enctype enc_type_enum)
910 {
911         switch (enc_type_enum) {
912         case ENCTYPE_DES_CBC_CRC:
913                 return ENC_CRC32;
914         case ENCTYPE_DES_CBC_MD5:
915                 return ENC_RSA_MD5;
916         case ENCTYPE_ARCFOUR_HMAC_MD5:
917                 return ENC_RC4_HMAC_MD5;
918         case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
919                 return ENC_HMAC_SHA1_96_AES128;
920         case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
921                 return ENC_HMAC_SHA1_96_AES256;
922         default:
923                 return 0;
924         }
925 }
926
927 /* Translate between the Microsoft msDS-SupportedEncryptionTypes values and the IETF encryption type values */
928 krb5_enctype kerberos_enctype_bitmap_to_enctype(uint32_t enctype_bitmap)
929 {
930         switch (enctype_bitmap) {
931         case ENC_CRC32:
932                 return ENCTYPE_DES_CBC_CRC;
933         case ENC_RSA_MD5:
934                 return ENCTYPE_DES_CBC_MD5;
935         case ENC_RC4_HMAC_MD5:
936                 return ENCTYPE_ARCFOUR_HMAC_MD5;
937         case ENC_HMAC_SHA1_96_AES128:
938                 return ENCTYPE_AES128_CTS_HMAC_SHA1_96;
939         case ENC_HMAC_SHA1_96_AES256:
940                 return ENCTYPE_AES256_CTS_HMAC_SHA1_96;
941         default:
942                 return 0;
943         }
944 }
945
946 /* Return an array of krb5_enctype values */
947 krb5_error_code kerberos_enctype_bitmap_to_enctypes(TALLOC_CTX *mem_ctx, uint32_t enctype_bitmap, krb5_enctype **enctypes)
948 {
949         unsigned int i, j = 0;
950         *enctypes = talloc_zero_array(mem_ctx, krb5_enctype, (8*sizeof(enctype_bitmap))+1);
951         if (!*enctypes) {
952                 return ENOMEM;
953         }
954         for (i=0; i<(8*sizeof(enctype_bitmap)); i++) {
955                 uint32_t bit_value = (1 << i) & enctype_bitmap;
956                 if (bit_value & enctype_bitmap) {
957                         (*enctypes)[j] = kerberos_enctype_bitmap_to_enctype(bit_value);
958                         if (!(*enctypes)[j]) {
959                                 continue;
960                         }
961                         j++;
962                 }
963         }
964         (*enctypes)[j] = 0;
965         return 0;
966 }