s3:libsmb: allow store_cldap_reply() to work with a ipv6 response
[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_krb5.h"
28 #include "auth/kerberos/kerberos_credentials.h"
29 #include "auth/kerberos/kerberos_util.h"
30
31 struct principal_container {
32         struct smb_krb5_context *smb_krb5_context;
33         krb5_principal principal;
34         const char *string_form; /* Optional */
35 };
36
37 static krb5_error_code free_principal(struct principal_container *pc)
38 {
39         /* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
40         krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
41
42         return 0;
43 }
44
45
46 static krb5_error_code parse_principal(TALLOC_CTX *parent_ctx,
47                                        const char *princ_string,
48                                        struct smb_krb5_context *smb_krb5_context,
49                                        krb5_principal *princ,
50                                        const char **error_string)
51 {
52         int ret;
53         struct principal_container *mem_ctx;
54         if (princ_string == NULL) {
55                  *princ = NULL;
56                  return 0;
57         }
58
59         /*
60          * Start with talloc(), talloc_reference() and only then call
61          * krb5_parse_name(). If any of them fails, the cleanup code is simpler.
62          */
63         mem_ctx = talloc(parent_ctx, struct principal_container);
64         if (!mem_ctx) {
65                 (*error_string) = error_message(ENOMEM);
66                 return ENOMEM;
67         }
68
69         mem_ctx->smb_krb5_context = talloc_reference(mem_ctx,
70                                                      smb_krb5_context);
71         if (mem_ctx->smb_krb5_context == NULL) {
72                 (*error_string) = error_message(ENOMEM);
73                 talloc_free(mem_ctx);
74                 return ENOMEM;
75         }
76
77         ret = krb5_parse_name(smb_krb5_context->krb5_context,
78                               princ_string, princ);
79
80         if (ret) {
81                 (*error_string) = smb_get_krb5_error_message(
82                                                 smb_krb5_context->krb5_context,
83                                                 ret, parent_ctx);
84                 talloc_free(mem_ctx);
85                 return ret;
86         }
87
88         /* This song-and-dance effectively puts the principal
89          * into talloc, so we can't lose it. */
90         mem_ctx->principal = *princ;
91         talloc_set_destructor(mem_ctx, free_principal);
92         return 0;
93 }
94
95 /* Obtain the principal set on this context.  Requires a
96  * smb_krb5_context because we are doing krb5 principal parsing with
97  * the library routines.  The returned princ is placed in the talloc
98  * system by means of a destructor (do *not* free). */
99
100 krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
101                                 struct cli_credentials *credentials,
102                                 struct smb_krb5_context *smb_krb5_context,
103                                 krb5_principal *princ,
104                                 enum credentials_obtained *obtained,
105                                 const char **error_string)
106 {
107         krb5_error_code ret;
108         const char *princ_string;
109         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
110         *obtained = CRED_UNINITIALISED;
111
112         if (!mem_ctx) {
113                 (*error_string) = error_message(ENOMEM);
114                 return ENOMEM;
115         }
116         princ_string = cli_credentials_get_principal_and_obtained(credentials,
117                                                                   mem_ctx,
118                                                                   obtained);
119         if (!princ_string) {
120                 *princ = NULL;
121                 return 0;
122         }
123
124         ret = parse_principal(parent_ctx, princ_string,
125                               smb_krb5_context, princ, error_string);
126         talloc_free(mem_ctx);
127         return ret;
128 }
129
130 /* Obtain the principal set on this context.  Requires a
131  * smb_krb5_context because we are doing krb5 principal parsing with
132  * the library routines.  The returned princ is placed in the talloc
133  * system by means of a destructor (do *not* free). */
134
135 static krb5_error_code impersonate_principal_from_credentials(
136                                 TALLOC_CTX *parent_ctx,
137                                 struct cli_credentials *credentials,
138                                 struct smb_krb5_context *smb_krb5_context,
139                                 krb5_principal *princ,
140                                 const char **error_string)
141 {
142         return parse_principal(parent_ctx,
143                         cli_credentials_get_impersonate_principal(credentials),
144                         smb_krb5_context, princ, error_string);
145 }
146
147 krb5_error_code smb_krb5_create_principals_array(TALLOC_CTX *mem_ctx,
148                                                  krb5_context context,
149                                                  const char *account_name,
150                                                  const char *realm,
151                                                  uint32_t num_spns,
152                                                  const char *spns[],
153                                                  uint32_t *pnum_principals,
154                                                  krb5_principal **pprincipals,
155                                                  const char **error_string)
156 {
157         krb5_error_code code;
158         TALLOC_CTX *tmp_ctx;
159         uint32_t num_principals = 0;
160         krb5_principal *principals;
161         uint32_t i;
162
163         tmp_ctx = talloc_new(mem_ctx);
164         if (tmp_ctx == NULL) {
165                 *error_string = "Cannot allocate tmp_ctx";
166                 return ENOMEM;
167         }
168
169         if (realm == NULL) {
170                 *error_string = "Cannot create principal without a realm";
171                 code = EINVAL;
172                 goto done;
173         }
174
175         if (account_name == NULL && (num_spns == 0 || spns == NULL)) {
176                 *error_string = "Cannot create principal without an account or SPN";
177                 code = EINVAL;
178                 goto done;
179         }
180
181         if (account_name != NULL && account_name[0] != '\0') {
182                 num_principals++;
183         }
184         num_principals += num_spns;
185
186         principals = talloc_zero_array(tmp_ctx,
187                                        krb5_principal,
188                                        num_principals);
189         if (principals == NULL) {
190                 *error_string = "Cannot allocate principals";
191                 code = ENOMEM;
192                 goto done;
193         }
194
195         for (i = 0; i < num_spns; i++) {
196                 code = krb5_parse_name(context, spns[i], &(principals[i]));
197                 if (code != 0) {
198                         *error_string = smb_get_krb5_error_message(context,
199                                                                    code,
200                                                                    mem_ctx);
201                         goto done;
202                 }
203         }
204
205         if (account_name != NULL && account_name[0] != '\0') {
206                 code = smb_krb5_make_principal(context,
207                                                &(principals[i]),
208                                                realm,
209                                                account_name,
210                                                NULL);
211                 if (code != 0) {
212                         *error_string = smb_get_krb5_error_message(context,
213                                                                    code,
214                                                                    mem_ctx);
215                         goto done;
216                 }
217         }
218
219         if (pnum_principals != NULL) {
220                 *pnum_principals = num_principals;
221
222                 if (pprincipals != NULL) {
223                         *pprincipals = talloc_steal(mem_ctx, principals);
224                 }
225         }
226
227         code = 0;
228 done:
229         talloc_free(tmp_ctx);
230         return code;
231 }
232
233 /**
234  * Return a freshly allocated ccache (destroyed by destructor on child
235  * of parent_ctx), for a given set of client credentials
236  */
237
238  krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
239                                  struct cli_credentials *credentials,
240                                  struct smb_krb5_context *smb_krb5_context,
241                                  struct loadparm_context *lp_ctx,
242                                  struct tevent_context *event_ctx,
243                                  krb5_ccache ccache,
244                                  enum credentials_obtained *obtained,
245                                  const char **error_string)
246 {
247         krb5_error_code ret;
248         const char *password;
249         const char *self_service;
250         const char *target_service;
251         time_t kdc_time = 0;
252         krb5_principal princ;
253         krb5_principal impersonate_principal;
254         int tries;
255         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
256         krb5_get_init_creds_opt *krb_options;
257         struct cli_credentials *fast_creds;
258
259         if (!mem_ctx) {
260                 (*error_string) = strerror(ENOMEM);
261                 return ENOMEM;
262         }
263
264         ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ, obtained, error_string);
265         if (ret) {
266                 talloc_free(mem_ctx);
267                 return ret;
268         }
269
270         if (princ == NULL) {
271                 (*error_string) = talloc_asprintf(credentials, "principal, username or realm was not specified in the credentials");
272                 talloc_free(mem_ctx);
273                 return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
274         }
275
276         ret = impersonate_principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &impersonate_principal, error_string);
277         if (ret) {
278                 talloc_free(mem_ctx);
279                 return ret;
280         }
281
282         self_service = cli_credentials_get_self_service(credentials);
283         target_service = cli_credentials_get_target_service(credentials);
284
285         password = cli_credentials_get_password(credentials);
286
287         /* setup the krb5 options we want */
288         if ((ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options))) {
289                 (*error_string) = talloc_asprintf(credentials, "krb5_get_init_creds_opt_alloc failed (%s)\n",
290                                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context,
291                                                                              ret, mem_ctx));
292                 talloc_free(mem_ctx);
293                 return ret;
294         }
295
296 #ifdef SAMBA4_USES_HEIMDAL /* Disable for now MIT reads defaults when needed */
297         /* get the defaults */
298         krb5_get_init_creds_opt_set_default_flags(smb_krb5_context->krb5_context, NULL, NULL, krb_options);
299 #endif
300         /* set if we want a forwardable ticket */
301         switch (cli_credentials_get_krb_forwardable(credentials)) {
302         case CRED_AUTO_KRB_FORWARDABLE:
303                 break;
304         case CRED_NO_KRB_FORWARDABLE:
305                 krb5_get_init_creds_opt_set_forwardable(krb_options, FALSE);
306                 break;
307         case CRED_FORCE_KRB_FORWARDABLE:
308                 krb5_get_init_creds_opt_set_forwardable(krb_options, TRUE);
309                 break;
310         }
311
312 #ifdef SAMBA4_USES_HEIMDAL /* FIXME: MIT does not have this yet */
313         /*
314          * In order to work against windows KDCs even if we use
315          * the netbios domain name as realm, we need to add the following
316          * flags:
317          * KRB5_INIT_CREDS_NO_C_CANON_CHECK;
318          * KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK;
319          *
320          * On MIT: Set pkinit_eku_checking to none
321          */
322         krb5_get_init_creds_opt_set_win2k(smb_krb5_context->krb5_context,
323                                           krb_options, true);
324         krb5_get_init_creds_opt_set_canonicalize(smb_krb5_context->krb5_context,
325                                                  krb_options, true);
326 #else /* MIT */
327         krb5_get_init_creds_opt_set_canonicalize(krb_options, true);
328 #endif
329
330         fast_creds = cli_credentials_get_krb5_fast_armor_credentials(credentials);
331
332         if (fast_creds != NULL) {
333 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_CCACHE
334                 struct ccache_container *fast_ccc = NULL;
335                 const char *fast_error_string = NULL;
336                 ret = cli_credentials_get_ccache(fast_creds, event_ctx, lp_ctx, &fast_ccc, &fast_error_string);
337                 if (ret != 0) {
338                         (*error_string) = talloc_asprintf(credentials,
339                                                           "Obtaining the Kerberos FAST armor credentials failed: %s\n",
340                                                           fast_error_string);
341                         return ret;
342                 }
343                 krb5_get_init_creds_opt_set_fast_ccache(smb_krb5_context->krb5_context,
344                                                         krb_options,
345                                                         fast_ccc->ccache);
346 #else
347                 *error_string = talloc_strdup(credentials,
348                                               "Using Kerberos FAST "
349                                               "armor credentials not possible "
350                                               "with this Kerberos library.  "
351                                               "Modern MIT or Samba's embedded "
352                                               "Heimdal required");
353                 return EINVAL;
354 #endif
355         }
356
357 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_FLAGS
358         {
359                 bool require_fast;
360                 /*
361                  * This ensures that if FAST was required, but no armor
362                  * credentials cache was specified, we proceed with (eg)
363                  * anonymous PKINIT
364                  */
365                 require_fast = cli_credentials_get_krb5_require_fast_armor(credentials);
366                 if (require_fast) {
367                         krb5_get_init_creds_opt_set_fast_flags(smb_krb5_context->krb5_context,
368                                                                krb_options,
369                                                                KRB5_FAST_REQUIRED);
370                 }
371         }
372 #endif
373
374         tries = 2;
375         while (tries--) {
376 #ifdef SAMBA4_USES_HEIMDAL
377                 struct tevent_context *previous_ev;
378                 /* Do this every time, in case we have weird recursive issues here */
379                 ret = smb_krb5_context_set_event_ctx(smb_krb5_context, event_ctx, &previous_ev);
380                 if (ret) {
381                         talloc_free(mem_ctx);
382                         krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
383                         return ret;
384                 }
385 #endif
386                 if (password) {
387                         if (impersonate_principal) {
388                                 ret = smb_krb5_kinit_s4u2_ccache(smb_krb5_context->krb5_context,
389                                                                  ccache,
390                                                                  princ,
391                                                                  password,
392                                                                  impersonate_principal,
393                                                                  self_service,
394                                                                  target_service,
395                                                                  krb_options,
396                                                                  NULL,
397                                                                  &kdc_time);
398                         } else {
399                                 ret = smb_krb5_kinit_password_ccache(smb_krb5_context->krb5_context,
400                                                                      ccache,
401                                                                      princ,
402                                                                      password,
403                                                                      target_service,
404                                                                      krb_options,
405                                                                      NULL,
406                                                                      &kdc_time);
407                         }
408                 } else if (impersonate_principal) {
409                         talloc_free(mem_ctx);
410                         (*error_string) = "INTERNAL error: Cannot impersonate principal with just a keyblock.  A password must be specified in the credentials";
411                         krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
412 #ifdef SAMBA4_USES_HEIMDAL
413                         smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
414 #endif
415                         return EINVAL;
416                 } else {
417                         /* No password available, try to use a keyblock instead */
418
419                         krb5_keyblock keyblock;
420                         const struct samr_Password *mach_pwd;
421                         mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
422                         if (!mach_pwd) {
423                                 talloc_free(mem_ctx);
424                                 (*error_string) = "kinit_to_ccache: No password available for kinit\n";
425                                 krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
426 #ifdef SAMBA4_USES_HEIMDAL
427                                 smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
428 #endif
429                                 return EINVAL;
430                         }
431                         ret = smb_krb5_keyblock_init_contents(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 = smb_krb5_kinit_keyblock_ccache(smb_krb5_context->krb5_context,
438                                                                      ccache,
439                                                                      princ,
440                                                                      &keyblock,
441                                                                      target_service,
442                                                                      krb_options,
443                                                                      NULL,
444                                                                      &kdc_time);
445                                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
446                         }
447                 }
448
449 #ifdef SAMBA4_USES_HEIMDAL
450                 smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
451 #endif
452
453                 if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
454                         /* Perhaps we have been given an invalid skew, so try again without it */
455                         time_t t = time(NULL);
456                         krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
457                 } else {
458                         /* not a skew problem */
459                         break;
460                 }
461         }
462
463         krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
464
465         if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
466                 (*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
467                                                   cli_credentials_get_principal(credentials, mem_ctx),
468                                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context,
469                                                                              ret, mem_ctx));
470                 talloc_free(mem_ctx);
471                 return ret;
472         }
473
474         /* cope with ticket being in the future due to clock skew */
475         if ((unsigned)kdc_time > time(NULL)) {
476                 time_t t = time(NULL);
477                 int time_offset =(unsigned)kdc_time-t;
478                 DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
479                 krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
480         }
481
482         if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
483                 ret = kinit_to_ccache(parent_ctx,
484                                       credentials,
485                                       smb_krb5_context,
486                                       lp_ctx,
487                                       event_ctx,
488                                       ccache, obtained,
489                                       error_string);
490         }
491
492         if (ret) {
493                 (*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
494                                                   cli_credentials_get_principal(credentials, mem_ctx),
495                                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context,
496                                                                              ret, mem_ctx));
497                 talloc_free(mem_ctx);
498                 return ret;
499         }
500
501         DEBUG(10,("kinit for %s succeeded\n",
502                 cli_credentials_get_principal(credentials, mem_ctx)));
503
504
505         talloc_free(mem_ctx);
506         return 0;
507 }
508
509 static krb5_error_code free_keytab_container(struct keytab_container *ktc)
510 {
511         return krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
512 }
513
514 krb5_error_code smb_krb5_get_keytab_container(TALLOC_CTX *mem_ctx,
515                                 struct smb_krb5_context *smb_krb5_context,
516                                 krb5_keytab opt_keytab,
517                                 const char *keytab_name,
518                                 struct keytab_container **ktc)
519 {
520         krb5_keytab keytab;
521         krb5_error_code ret;
522
523         /*
524          * Start with talloc(), talloc_reference() and only then call
525          * krb5_kt_resolve(). If any of them fails, the cleanup code is simpler.
526          */
527         *ktc = talloc(mem_ctx, struct keytab_container);
528         if (!*ktc) {
529                 return ENOMEM;
530         }
531
532         (*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
533         if ((*ktc)->smb_krb5_context == NULL) {
534                 TALLOC_FREE(*ktc);
535                 return ENOMEM;
536         }
537
538         if (opt_keytab) {
539                 keytab = opt_keytab;
540         } else {
541                 ret = krb5_kt_resolve(smb_krb5_context->krb5_context,
542                                                 keytab_name, &keytab);
543                 if (ret) {
544                         DEBUG(1,("failed to open krb5 keytab: %s\n",
545                                  smb_get_krb5_error_message(
546                                         smb_krb5_context->krb5_context,
547                                         ret, mem_ctx)));
548                         TALLOC_FREE(*ktc);
549                         return ret;
550                 }
551         }
552
553         (*ktc)->keytab = keytab;
554         (*ktc)->password_based = false;
555         talloc_set_destructor(*ktc, free_keytab_container);
556
557         return 0;
558 }
559
560 /*
561  * Walk the keytab, looking for entries of this principal name,
562  * with KVNO other than current kvno -1.
563  *
564  * These entries are now stale,
565  * we only keep the current and previous entries around.
566  *
567  * Inspired by the code in Samba3 for 'use kerberos keytab'.
568  */
569 krb5_error_code smb_krb5_remove_obsolete_keytab_entries(TALLOC_CTX *mem_ctx,
570                                                         krb5_context context,
571                                                         krb5_keytab keytab,
572                                                         uint32_t num_principals,
573                                                         krb5_principal *principals,
574                                                         krb5_kvno kvno,
575                                                         bool *found_previous,
576                                                         const char **error_string)
577 {
578         TALLOC_CTX *tmp_ctx;
579         krb5_error_code code;
580         krb5_kt_cursor cursor;
581
582         tmp_ctx = talloc_new(mem_ctx);
583         if (tmp_ctx == NULL) {
584                 *error_string = "Cannot allocate tmp_ctx";
585                 return ENOMEM;
586         }
587
588         *found_previous = true;
589
590         code = krb5_kt_start_seq_get(context, keytab, &cursor);
591         switch (code) {
592         case 0:
593                 break;
594 #ifdef HEIM_ERR_OPNOTSUPP
595         case HEIM_ERR_OPNOTSUPP:
596 #endif
597         case ENOENT:
598         case KRB5_KT_END:
599                 /* no point enumerating if there isn't anything here */
600                 code = 0;
601                 goto done;
602         default:
603                 *error_string = talloc_asprintf(mem_ctx,
604                                                 "failed to open keytab for read of old entries: %s\n",
605                                                 smb_get_krb5_error_message(context, code, tmp_ctx));
606                 goto done;
607         }
608
609         do {
610                 krb5_kvno old_kvno = kvno - 1;
611                 krb5_keytab_entry entry;
612                 bool matched = false;
613                 uint32_t i;
614
615                 code = krb5_kt_next_entry(context, keytab, &entry, &cursor);
616                 if (code) {
617                         break;
618                 }
619
620                 for (i = 0; i < num_principals; i++) {
621                         krb5_boolean ok;
622
623                         ok = smb_krb5_kt_compare(context,
624                                                 &entry,
625                                                 principals[i],
626                                                 0,
627                                                 0);
628                         if (ok) {
629                                 matched = true;
630                                 break;
631                         }
632                 }
633
634                 if (!matched) {
635                         /*
636                          * Free the entry, it wasn't the one we were looking
637                          * for anyway
638                          */
639                         krb5_kt_free_entry(context, &entry);
640                         /* Make sure we do not double free */
641                         ZERO_STRUCT(entry);
642                         continue;
643                 }
644
645                 /*
646                  * Delete it, if it is not kvno - 1.
647                  *
648                  * Some keytab files store the kvno only in 8bits. Limit the
649                  * compare to 8bits, so that we don't miss old keys and delete
650                  * them.
651                  */
652                 if ((entry.vno & 0xff) != (old_kvno & 0xff)) {
653                         krb5_error_code rc;
654
655                         /* Release the enumeration.  We are going to
656                          * have to start this from the top again,
657                          * because deletes during enumeration may not
658                          * always be consistent.
659                          *
660                          * Also, the enumeration locks a FILE: keytab
661                          */
662                         krb5_kt_end_seq_get(context, keytab, &cursor);
663
664                         code = krb5_kt_remove_entry(context, keytab, &entry);
665                         krb5_kt_free_entry(context, &entry);
666
667                         /* Make sure we do not double free */
668                         ZERO_STRUCT(entry);
669
670                         /* Deleted: Restart from the top */
671                         rc = krb5_kt_start_seq_get(context, keytab, &cursor);
672                         if (rc != 0) {
673                                 krb5_kt_free_entry(context, &entry);
674
675                                 /* Make sure we do not double free */
676                                 ZERO_STRUCT(entry);
677
678                                 DEBUG(1, ("failed to restart enumeration of keytab: %s\n",
679                                           smb_get_krb5_error_message(context,
680                                                                      code,
681                                                                      tmp_ctx)));
682
683                                 talloc_free(tmp_ctx);
684                                 return rc;
685                         }
686
687                         if (code != 0) {
688                                 break;
689                         }
690
691                 } else {
692                         *found_previous = true;
693                 }
694
695                 /* Free the entry, we don't need it any more */
696                 krb5_kt_free_entry(context, &entry);
697                 /* Make sure we do not double free */
698                 ZERO_STRUCT(entry);
699         } while (code == 0);
700
701         krb5_kt_end_seq_get(context, keytab, &cursor);
702
703         switch (code) {
704         case 0:
705                 break;
706         case ENOENT:
707         case KRB5_KT_END:
708                 break;
709         default:
710                 *error_string = talloc_asprintf(mem_ctx,
711                                                 "failed in deleting old entries for principal: %s\n",
712                                                 smb_get_krb5_error_message(context,
713                                                                            code,
714                                                                            tmp_ctx));
715                 goto done;
716         }
717
718         code = 0;
719 done:
720         talloc_free(tmp_ctx);
721         return code;
722 }
723
724 /*
725  * Walk the keytab, looking for entries of this principal name,
726  * with KVNO and key equal
727  *
728  * These entries do not need to be replaced, so we want to tell the caller not to add them again
729  *
730  * Inspired by the code in Samba3 for 'use kerberos keytab'.
731  */
732 krb5_error_code smb_krb5_is_exact_entry_in_keytab(TALLOC_CTX *mem_ctx,
733                                                   krb5_context context,
734                                                   krb5_keytab keytab,
735                                                   krb5_keytab_entry *to_match,
736                                                   bool *found,
737                                                   const char **error_string)
738 {
739         TALLOC_CTX *tmp_ctx;
740         krb5_error_code code;
741         krb5_kt_cursor cursor;
742
743         tmp_ctx = talloc_new(mem_ctx);
744         if (tmp_ctx == NULL) {
745                 *error_string = "Cannot allocate tmp_ctx";
746                 return ENOMEM;
747         }
748
749         *found = false;
750
751         code = krb5_kt_start_seq_get(context, keytab, &cursor);
752         switch (code) {
753         case 0:
754                 break;
755 #ifdef HEIM_ERR_OPNOTSUPP
756         case HEIM_ERR_OPNOTSUPP:
757 #endif
758         case ENOENT:
759         case KRB5_KT_END:
760                 /* no point enumerating if there isn't anything here */
761                 code = 0;
762                 goto done;
763         default:
764                 *error_string = talloc_asprintf(mem_ctx,
765                                                 "failed to open keytab for read of existing entries: %s\n",
766                                                 smb_get_krb5_error_message(context, code, tmp_ctx));
767                 goto done;
768         }
769
770         do {
771                 krb5_keytab_entry entry;
772                 bool matched = false;
773                 krb5_boolean ok;
774
775                 code = krb5_kt_next_entry(context, keytab, &entry, &cursor);
776                 if (code) {
777                         break;
778                 }
779
780                 ok = smb_krb5_kt_compare(context,
781                                          &entry,
782                                          to_match->principal,
783                                          to_match->vno,
784                                          KRB5_KEY_TYPE(KRB5_KT_KEY(to_match)));
785                 if (ok) {
786                         /* This is not a security check, constant time is not required */
787                         if ((KRB5_KEY_LENGTH(KRB5_KT_KEY(&entry)) == KRB5_KEY_LENGTH(KRB5_KT_KEY(to_match)))
788                             && memcmp(KRB5_KEY_DATA(KRB5_KT_KEY(&entry)), KRB5_KEY_DATA(KRB5_KT_KEY(to_match)),
789                                       KRB5_KEY_LENGTH(KRB5_KT_KEY(&entry))) == 0) {
790                                 matched = true;
791                         }
792                 }
793
794                 /* Free the entry, we don't need it any more */
795                 krb5_kt_free_entry(context, &entry);
796                 /* Make sure we do not double free */
797                 ZERO_STRUCT(entry);
798                 if (matched) {
799                         *found = true;
800                         break;
801                 }
802         } while (code == 0);
803
804         krb5_kt_end_seq_get(context, keytab, &cursor);
805
806         switch (code) {
807         case 0:
808                 break;
809         case ENOENT:
810         case KRB5_KT_END:
811                 break;
812         default:
813                 *error_string = talloc_asprintf(mem_ctx,
814                                                 "failed in checking old entries for principal: %s\n",
815                                                 smb_get_krb5_error_message(context,
816                                                                            code,
817                                                                            tmp_ctx));
818                 goto done;
819         }
820
821         code = 0;
822 done:
823         talloc_free(tmp_ctx);
824         return code;
825 }