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