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