LCOV - code coverage report
Current view: top level - source4/auth/kerberos - kerberos_util.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 176 245 71.8 %
Date: 2021-09-23 10:06:22 Functions: 9 9 100.0 %

          Line data    Source code
       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       56398 : 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       56398 :         krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
      41             : 
      42       56398 :         return 0;
      43             : }
      44             : 
      45             : 
      46       68856 : 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       68856 :         if (princ_string == NULL) {
      55       12458 :                  *princ = NULL;
      56       12458 :                  return 0;
      57             :         }
      58             : 
      59       56398 :         ret = krb5_parse_name(smb_krb5_context->krb5_context,
      60             :                               princ_string, princ);
      61             : 
      62       56398 :         if (ret) {
      63           0 :                 (*error_string) = smb_get_krb5_error_message(
      64             :                                                 smb_krb5_context->krb5_context,
      65             :                                                 ret, parent_ctx);
      66           0 :                 return ret;
      67             :         }
      68             : 
      69       56398 :         mem_ctx = talloc(parent_ctx, struct principal_container);
      70       56398 :         if (!mem_ctx) {
      71           0 :                 (*error_string) = error_message(ENOMEM);
      72           0 :                 return ENOMEM;
      73             :         }
      74             : 
      75             :         /* This song-and-dance effectivly puts the principal
      76             :          * into talloc, so we can't loose it. */
      77       56398 :         mem_ctx->smb_krb5_context = talloc_reference(mem_ctx,
      78             :                                                      smb_krb5_context);
      79       56398 :         mem_ctx->principal = *princ;
      80       56398 :         talloc_set_destructor(mem_ctx, free_principal);
      81       56398 :         return 0;
      82             : }
      83             : 
      84             : /* Obtain the principal set on this context.  Requires a
      85             :  * smb_krb5_context because we are doing krb5 principal parsing with
      86             :  * the library routines.  The returned princ is placed in the talloc
      87             :  * system by means of a destructor (do *not* free). */
      88             : 
      89       56362 : krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
      90             :                                 struct cli_credentials *credentials,
      91             :                                 struct smb_krb5_context *smb_krb5_context,
      92             :                                 krb5_principal *princ,
      93             :                                 enum credentials_obtained *obtained,
      94             :                                 const char **error_string)
      95             : {
      96             :         krb5_error_code ret;
      97             :         const char *princ_string;
      98       56362 :         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
      99       56362 :         *obtained = CRED_UNINITIALISED;
     100             : 
     101       56362 :         if (!mem_ctx) {
     102           0 :                 (*error_string) = error_message(ENOMEM);
     103           0 :                 return ENOMEM;
     104             :         }
     105       56362 :         princ_string = cli_credentials_get_principal_and_obtained(credentials,
     106             :                                                                   mem_ctx,
     107             :                                                                   obtained);
     108       56362 :         if (!princ_string) {
     109           5 :                 *princ = NULL;
     110           5 :                 return 0;
     111             :         }
     112             : 
     113       56357 :         ret = parse_principal(parent_ctx, princ_string,
     114             :                               smb_krb5_context, princ, error_string);
     115       56357 :         talloc_free(mem_ctx);
     116       56357 :         return ret;
     117             : }
     118             : 
     119             : /* Obtain the principal set on this context.  Requires a
     120             :  * smb_krb5_context because we are doing krb5 principal parsing with
     121             :  * the library routines.  The returned princ is placed in the talloc
     122             :  * system by means of a destructor (do *not* free). */
     123             : 
     124       12204 : static krb5_error_code impersonate_principal_from_credentials(
     125             :                                 TALLOC_CTX *parent_ctx,
     126             :                                 struct cli_credentials *credentials,
     127             :                                 struct smb_krb5_context *smb_krb5_context,
     128             :                                 krb5_principal *princ,
     129             :                                 const char **error_string)
     130             : {
     131       12499 :         return parse_principal(parent_ctx,
     132             :                         cli_credentials_get_impersonate_principal(credentials),
     133             :                         smb_krb5_context, princ, error_string);
     134             : }
     135             : 
     136         326 : krb5_error_code smb_krb5_create_principals_array(TALLOC_CTX *mem_ctx,
     137             :                                                  krb5_context context,
     138             :                                                  const char *account_name,
     139             :                                                  const char *realm,
     140             :                                                  uint32_t num_spns,
     141             :                                                  const char *spns[],
     142             :                                                  uint32_t *pnum_principals,
     143             :                                                  krb5_principal **pprincipals,
     144             :                                                  const char **error_string)
     145             : {
     146             :         krb5_error_code code;
     147             :         TALLOC_CTX *tmp_ctx;
     148         326 :         uint32_t num_principals = 0;
     149             :         krb5_principal *principals;
     150             :         uint32_t i;
     151             : 
     152         326 :         tmp_ctx = talloc_new(mem_ctx);
     153         326 :         if (tmp_ctx == NULL) {
     154           0 :                 *error_string = "Cannot allocate tmp_ctx";
     155           0 :                 return ENOMEM;
     156             :         }
     157             : 
     158         326 :         if (realm == NULL) {
     159           0 :                 *error_string = "Cannot create principal without a realm";
     160           0 :                 code = EINVAL;
     161           0 :                 goto done;
     162             :         }
     163             : 
     164         326 :         if (account_name == NULL && (num_spns == 0 || spns == NULL)) {
     165           0 :                 *error_string = "Cannot create principal without an account or SPN";
     166           0 :                 code = EINVAL;
     167           0 :                 goto done;
     168             :         }
     169             : 
     170         326 :         if (account_name != NULL && account_name[0] != '\0') {
     171         326 :                 num_principals++;
     172             :         }
     173         326 :         num_principals += num_spns;
     174             : 
     175         326 :         principals = talloc_zero_array(tmp_ctx,
     176             :                                        krb5_principal,
     177             :                                        num_principals);
     178         326 :         if (principals == NULL) {
     179           0 :                 *error_string = "Cannot allocate principals";
     180           0 :                 code = ENOMEM;
     181           0 :                 goto done;
     182             :         }
     183             : 
     184         763 :         for (i = 0; i < num_spns; i++) {
     185         462 :                 code = krb5_parse_name(context, spns[i], &(principals[i]));
     186         462 :                 if (code != 0) {
     187           0 :                         *error_string = smb_get_krb5_error_message(context,
     188             :                                                                    code,
     189             :                                                                    mem_ctx);
     190           0 :                         goto done;
     191             :                 }
     192             :         }
     193             : 
     194         326 :         if (account_name != NULL && account_name[0] != '\0') {
     195         326 :                 code = smb_krb5_make_principal(context,
     196         326 :                                                &(principals[i]),
     197             :                                                realm,
     198             :                                                account_name,
     199             :                                                NULL);
     200         326 :                 if (code != 0) {
     201           0 :                         *error_string = smb_get_krb5_error_message(context,
     202             :                                                                    code,
     203             :                                                                    mem_ctx);
     204           0 :                         goto done;
     205             :                 }
     206             :         }
     207             : 
     208         326 :         if (pnum_principals != NULL) {
     209         326 :                 *pnum_principals = num_principals;
     210             : 
     211         326 :                 if (pprincipals != NULL) {
     212         326 :                         *pprincipals = talloc_steal(mem_ctx, principals);
     213             :                 }
     214             :         }
     215             : 
     216         301 :         code = 0;
     217         326 : done:
     218         326 :         talloc_free(tmp_ctx);
     219         326 :         return code;
     220             : }
     221             : 
     222             : /**
     223             :  * Return a freshly allocated ccache (destroyed by destructor on child
     224             :  * of parent_ctx), for a given set of client credentials 
     225             :  */
     226             : 
     227       12501 :  krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
     228             :                                  struct cli_credentials *credentials,
     229             :                                  struct smb_krb5_context *smb_krb5_context,
     230             :                                  struct tevent_context *event_ctx,
     231             :                                  krb5_ccache ccache,
     232             :                                  enum credentials_obtained *obtained,
     233             :                                  const char **error_string)
     234             : {
     235             :         krb5_error_code ret;
     236             :         const char *password;
     237             : #ifdef SAMBA4_USES_HEIMDAL
     238             :         const char *self_service;
     239             : #endif
     240             :         const char *target_service;
     241       12501 :         time_t kdc_time = 0;
     242             :         krb5_principal princ;
     243             :         krb5_principal impersonate_principal;
     244             :         int tries;
     245       12501 :         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
     246             :         krb5_get_init_creds_opt *krb_options;
     247             : 
     248       12501 :         if (!mem_ctx) {
     249           0 :                 (*error_string) = strerror(ENOMEM);
     250           0 :                 return ENOMEM;
     251             :         }
     252             : 
     253       12501 :         ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ, obtained, error_string);
     254       12501 :         if (ret) {
     255           0 :                 talloc_free(mem_ctx);
     256           0 :                 return ret;
     257             :         }
     258             : 
     259       12501 :         if (princ == NULL) {
     260           2 :                 (*error_string) = talloc_asprintf(credentials, "principal, username or realm was not specified in the credentials");
     261           2 :                 talloc_free(mem_ctx);
     262           2 :                 return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
     263             :         }
     264             : 
     265       12499 :         ret = impersonate_principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &impersonate_principal, error_string);
     266       12499 :         if (ret) {
     267           0 :                 talloc_free(mem_ctx);
     268           0 :                 return ret;
     269             :         }
     270             : 
     271             : #ifdef SAMBA4_USES_HEIMDAL
     272        9675 :         self_service = cli_credentials_get_self_service(credentials);
     273             : #endif
     274       12499 :         target_service = cli_credentials_get_target_service(credentials);
     275             : 
     276       12499 :         password = cli_credentials_get_password(credentials);
     277             : 
     278             :         /* setup the krb5 options we want */
     279       12499 :         if ((ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options))) {
     280           0 :                 (*error_string) = talloc_asprintf(credentials, "krb5_get_init_creds_opt_alloc failed (%s)\n",
     281             :                                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context,
     282             :                                                                              ret, mem_ctx));
     283           0 :                 talloc_free(mem_ctx);
     284           0 :                 return ret;
     285             :         }
     286             : 
     287             : #ifdef SAMBA4_USES_HEIMDAL /* Disable for now MIT reads defaults when needed */
     288             :         /* get the defaults */
     289        9675 :         krb5_get_init_creds_opt_set_default_flags(smb_krb5_context->krb5_context, NULL, NULL, krb_options);
     290             : #endif
     291             :         /* set if we want a forwardable ticket */
     292       12499 :         switch (cli_credentials_get_krb_forwardable(credentials)) {
     293       12185 :         case CRED_AUTO_KRB_FORWARDABLE:
     294       12185 :                 break;
     295          19 :         case CRED_NO_KRB_FORWARDABLE:
     296          19 :                 krb5_get_init_creds_opt_set_forwardable(krb_options, FALSE);
     297          19 :                 break;
     298           0 :         case CRED_FORCE_KRB_FORWARDABLE:
     299           0 :                 krb5_get_init_creds_opt_set_forwardable(krb_options, TRUE);
     300           0 :                 break;
     301             :         }
     302             : 
     303             : #ifdef SAMBA4_USES_HEIMDAL /* FIXME: MIT does not have this yet */
     304             :         /*
     305             :          * In order to work against windows KDCs even if we use
     306             :          * the netbios domain name as realm, we need to add the following
     307             :          * flags:
     308             :          * KRB5_INIT_CREDS_NO_C_CANON_CHECK;
     309             :          * KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK;
     310             :          *
     311             :          * On MIT: Set pkinit_eku_checking to none
     312             :          */
     313        9675 :         krb5_get_init_creds_opt_set_win2k(smb_krb5_context->krb5_context,
     314             :                                           krb_options, true);
     315        9675 :         krb5_get_init_creds_opt_set_canonicalize(smb_krb5_context->krb5_context,
     316             :                                                  krb_options, true);
     317             : #else /* MIT */
     318        2824 :         krb5_get_init_creds_opt_set_canonicalize(krb_options, true);
     319             : #endif
     320             : 
     321       12499 :         tries = 2;
     322       21879 :         while (tries--) {
     323             : #ifdef SAMBA4_USES_HEIMDAL
     324             :                 struct tevent_context *previous_ev;
     325             :                 /* Do this every time, in case we have weird recursive issues here */
     326        9675 :                 ret = smb_krb5_context_set_event_ctx(smb_krb5_context, event_ctx, &previous_ev);
     327        9675 :                 if (ret) {
     328           0 :                         talloc_free(mem_ctx);
     329           1 :                         return ret;
     330             :                 }
     331             : #endif
     332       12499 :                 if (password) {
     333       12482 :                         if (impersonate_principal) {
     334             : #ifdef SAMBA4_USES_HEIMDAL
     335          41 :                                 ret = smb_krb5_kinit_s4u2_ccache(smb_krb5_context->krb5_context,
     336             :                                                                  ccache,
     337             :                                                                  princ,
     338             :                                                                  password,
     339             :                                                                  impersonate_principal,
     340             :                                                                  self_service,
     341             :                                                                  target_service,
     342             :                                                                  krb_options,
     343             :                                                                  NULL,
     344             :                                                                  &kdc_time);
     345             : #else
     346           0 :                                 talloc_free(mem_ctx);
     347           0 :                                 (*error_string) = "INTERNAL error: s4u2 ops "
     348             :                                         "are not supported with MIT build yet";
     349           0 :                                 return EINVAL;
     350             : #endif
     351             :                         } else {
     352       12441 :                                 ret = smb_krb5_kinit_password_ccache(smb_krb5_context->krb5_context,
     353             :                                                                      ccache,
     354             :                                                                      princ,
     355             :                                                                      password,
     356             :                                                                      target_service,
     357             :                                                                      krb_options,
     358             :                                                                      NULL,
     359             :                                                                      &kdc_time);
     360             :                         }
     361          17 :                 } else if (impersonate_principal) {
     362           0 :                         talloc_free(mem_ctx);
     363           0 :                         (*error_string) = "INTERNAL error: Cannot impersonate principal with just a keyblock.  A password must be specified in the credentials";
     364           0 :                         return EINVAL;
     365             :                 } else {
     366             :                         /* No password available, try to use a keyblock instead */
     367             :                         
     368             :                         krb5_keyblock keyblock;
     369             :                         const struct samr_Password *mach_pwd;
     370          17 :                         mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
     371          17 :                         if (!mach_pwd) {
     372           2 :                                 talloc_free(mem_ctx);
     373           2 :                                 (*error_string) = "kinit_to_ccache: No password available for kinit\n";
     374           2 :                                 krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
     375             : #ifdef SAMBA4_USES_HEIMDAL
     376           1 :                                 smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
     377             : #endif
     378           2 :                                 return EINVAL;
     379             :                         }
     380          15 :                         ret = smb_krb5_keyblock_init_contents(smb_krb5_context->krb5_context,
     381             :                                                  ENCTYPE_ARCFOUR_HMAC,
     382          15 :                                                  mach_pwd->hash, sizeof(mach_pwd->hash), 
     383             :                                                  &keyblock);
     384             :                         
     385          15 :                         if (ret == 0) {
     386          15 :                                 ret = smb_krb5_kinit_keyblock_ccache(smb_krb5_context->krb5_context,
     387             :                                                                      ccache,
     388             :                                                                      princ,
     389             :                                                                      &keyblock,
     390             :                                                                      target_service,
     391             :                                                                      krb_options,
     392             :                                                                      NULL,
     393             :                                                                      &kdc_time);
     394          15 :                                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
     395             :                         }
     396             :                 }
     397             : 
     398             : #ifdef SAMBA4_USES_HEIMDAL
     399        9674 :                 smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
     400             : #endif
     401             : 
     402       12497 :                 if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
     403             :                         /* Perhaps we have been given an invalid skew, so try again without it */
     404           0 :                         time_t t = time(NULL);
     405           0 :                         krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
     406             :                 } else {
     407             :                         /* not a skew problem */
     408             :                         break;
     409             :                 }
     410             :         }
     411             : 
     412       12497 :         krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
     413             : 
     414       12497 :         if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
     415           0 :                 (*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
     416             :                                                   cli_credentials_get_principal(credentials, mem_ctx),
     417             :                                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context,
     418             :                                                                              ret, mem_ctx));
     419           0 :                 talloc_free(mem_ctx);
     420           0 :                 return ret;
     421             :         }
     422             : 
     423             :         /* cope with ticket being in the future due to clock skew */
     424       12497 :         if ((unsigned)kdc_time > time(NULL)) {
     425           0 :                 time_t t = time(NULL);
     426           0 :                 int time_offset =(unsigned)kdc_time-t;
     427           0 :                 DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
     428           0 :                 krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
     429             :         }
     430             :         
     431       12497 :         if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
     432           0 :                 ret = kinit_to_ccache(parent_ctx,
     433             :                                       credentials,
     434             :                                       smb_krb5_context,
     435             :                                       event_ctx,
     436             :                                       ccache, obtained,
     437             :                                       error_string);
     438             :         }
     439             : 
     440       12497 :         if (ret) {
     441        1067 :                 (*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
     442             :                                                   cli_credentials_get_principal(credentials, mem_ctx),
     443             :                                                   smb_get_krb5_error_message(smb_krb5_context->krb5_context,
     444             :                                                                              ret, mem_ctx));
     445        1067 :                 talloc_free(mem_ctx);
     446        1067 :                 return ret;
     447             :         }
     448             : 
     449       11430 :         DEBUG(10,("kinit for %s succeeded\n",
     450             :                 cli_credentials_get_principal(credentials, mem_ctx)));
     451             : 
     452             : 
     453       11430 :         talloc_free(mem_ctx);
     454       11430 :         return 0;
     455             : }
     456             : 
     457       53516 : static krb5_error_code free_keytab_container(struct keytab_container *ktc)
     458             : {
     459       53516 :         return krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
     460             : }
     461             : 
     462       53465 : krb5_error_code smb_krb5_get_keytab_container(TALLOC_CTX *mem_ctx,
     463             :                                 struct smb_krb5_context *smb_krb5_context,
     464             :                                 krb5_keytab opt_keytab,
     465             :                                 const char *keytab_name,
     466             :                                 struct keytab_container **ktc)
     467             : {
     468             :         krb5_keytab keytab;
     469             :         krb5_error_code ret;
     470             : 
     471       53465 :         if (opt_keytab) {
     472          85 :                 keytab = opt_keytab;
     473             :         } else {
     474       53380 :                 ret = krb5_kt_resolve(smb_krb5_context->krb5_context,
     475             :                                                 keytab_name, &keytab);
     476       53380 :                 if (ret) {
     477           0 :                         DEBUG(1,("failed to open krb5 keytab: %s\n",
     478             :                                  smb_get_krb5_error_message(
     479             :                                         smb_krb5_context->krb5_context,
     480             :                                         ret, mem_ctx)));
     481           0 :                         return ret;
     482             :                 }
     483             :         }
     484             : 
     485       53465 :         *ktc = talloc(mem_ctx, struct keytab_container);
     486       53465 :         if (!*ktc) {
     487           0 :                 return ENOMEM;
     488             :         }
     489             : 
     490       53465 :         (*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
     491       53465 :         (*ktc)->keytab = keytab;
     492       53465 :         (*ktc)->password_based = false;
     493       53465 :         talloc_set_destructor(*ktc, free_keytab_container);
     494             : 
     495       53465 :         return 0;
     496             : }
     497             : 
     498             : /*
     499             :  * Walk the keytab, looking for entries of this principal name,
     500             :  * with KVNO other than current kvno -1.
     501             :  *
     502             :  * These entries are now stale,
     503             :  * we only keep the current and previous entries around.
     504             :  *
     505             :  * Inspired by the code in Samba3 for 'use kerberos keytab'.
     506             :  */
     507         328 : krb5_error_code smb_krb5_remove_obsolete_keytab_entries(TALLOC_CTX *mem_ctx,
     508             :                                                         krb5_context context,
     509             :                                                         krb5_keytab keytab,
     510             :                                                         uint32_t num_principals,
     511             :                                                         krb5_principal *principals,
     512             :                                                         krb5_kvno kvno,
     513             :                                                         bool *found_previous,
     514             :                                                         const char **error_string)
     515             : {
     516             :         TALLOC_CTX *tmp_ctx;
     517             :         krb5_error_code code;
     518             :         krb5_kt_cursor cursor;
     519             : 
     520         328 :         tmp_ctx = talloc_new(mem_ctx);
     521         328 :         if (tmp_ctx == NULL) {
     522           0 :                 *error_string = "Cannot allocate tmp_ctx";
     523           0 :                 return ENOMEM;
     524             :         }
     525             : 
     526         328 :         *found_previous = true;
     527             : 
     528         328 :         code = krb5_kt_start_seq_get(context, keytab, &cursor);
     529         328 :         switch (code) {
     530         141 :         case 0:
     531         141 :                 break;
     532             : #ifdef HEIM_ERR_OPNOTSUPP
     533           0 :         case HEIM_ERR_OPNOTSUPP:
     534             : #endif
     535         160 :         case ENOENT:
     536             :         case KRB5_KT_END:
     537             :                 /* no point enumerating if there isn't anything here */
     538         160 :                 code = 0;
     539         160 :                 goto done;
     540           0 :         default:
     541           0 :                 *error_string = talloc_asprintf(mem_ctx,
     542             :                                                 "failed to open keytab for read of old entries: %s\n",
     543             :                                                 smb_get_krb5_error_message(context, code, mem_ctx));
     544           0 :                 goto done;
     545             :         }
     546             : 
     547        1948 :         do {
     548        2395 :                 krb5_kvno old_kvno = kvno - 1;
     549             :                 krb5_keytab_entry entry;
     550        2395 :                 bool matched = false;
     551             :                 uint32_t i;
     552             : 
     553        2395 :                 code = krb5_kt_next_entry(context, keytab, &entry, &cursor);
     554        2395 :                 if (code) {
     555         255 :                         break;
     556             :                 }
     557             : 
     558        5016 :                 for (i = 0; i < num_principals; i++) {
     559             :                         krb5_boolean ok;
     560             : 
     561        4684 :                         ok = smb_krb5_kt_compare(context,
     562             :                                                 &entry,
     563        4684 :                                                 principals[i],
     564             :                                                 0,
     565             :                                                 0);
     566        4684 :                         if (ok) {
     567        1563 :                                 matched = true;
     568        1563 :                                 break;
     569             :                         }
     570             :                 }
     571             : 
     572        2248 :                 if (!matched) {
     573             :                         /*
     574             :                          * Free the entry, it wasn't the one we were looking
     575             :                          * for anyway
     576             :                          */
     577         528 :                         krb5_kt_free_entry(context, &entry);
     578             :                         /* Make sure we do not double free */
     579         528 :                         ZERO_STRUCT(entry);
     580         528 :                         continue;
     581             :                 }
     582             : 
     583             :                 /*
     584             :                  * Delete it, if it is not kvno - 1.
     585             :                  *
     586             :                  * Some keytab files store the kvno only in 8bits. Limit the
     587             :                  * compare to 8bits, so that we don't miss old keys and delete
     588             :                  * them.
     589             :                  */
     590        1720 :                 if ((entry.vno & 0xff) != (old_kvno & 0xff)) {
     591             :                         krb5_error_code rc;
     592             : 
     593             :                         /* Release the enumeration.  We are going to
     594             :                          * have to start this from the top again,
     595             :                          * because deletes during enumeration may not
     596             :                          * always be consistent.
     597             :                          *
     598             :                          * Also, the enumeration locks a FILE: keytab
     599             :                          */
     600         369 :                         krb5_kt_end_seq_get(context, keytab, &cursor);
     601             : 
     602         369 :                         code = krb5_kt_remove_entry(context, keytab, &entry);
     603         369 :                         krb5_kt_free_entry(context, &entry);
     604             : 
     605             :                         /* Make sure we do not double free */
     606         369 :                         ZERO_STRUCT(entry);
     607             : 
     608             :                         /* Deleted: Restart from the top */
     609         369 :                         rc = krb5_kt_start_seq_get(context, keytab, &cursor);
     610         369 :                         if (rc != 0) {
     611           0 :                                 krb5_kt_free_entry(context, &entry);
     612             : 
     613             :                                 /* Make sure we do not double free */
     614           0 :                                 ZERO_STRUCT(entry);
     615             : 
     616           0 :                                 DEBUG(1, ("failed to restart enumeration of keytab: %s\n",
     617             :                                           smb_get_krb5_error_message(context,
     618             :                                                                      code,
     619             :                                                                      tmp_ctx)));
     620             : 
     621           0 :                                 talloc_free(tmp_ctx);
     622           0 :                                 return rc;
     623             :                         }
     624             : 
     625         369 :                         if (code != 0) {
     626           0 :                                 break;
     627             :                         }
     628             : 
     629             :                 } else {
     630        1351 :                         *found_previous = true;
     631             :                 }
     632             : 
     633             :                 /* Free the entry, we don't need it any more */
     634        1720 :                 krb5_kt_free_entry(context, &entry);
     635             :                 /* Make sure we do not double free */
     636        1720 :                 ZERO_STRUCT(entry);
     637        2248 :         } while (code == 0);
     638             : 
     639         147 :         krb5_kt_end_seq_get(context, keytab, &cursor);
     640             : 
     641         147 :         switch (code) {
     642           0 :         case 0:
     643           0 :                 break;
     644         141 :         case ENOENT:
     645             :         case KRB5_KT_END:
     646         141 :                 break;
     647           0 :         default:
     648           0 :                 *error_string = talloc_asprintf(mem_ctx,
     649             :                                                 "failed in deleting old entries for principal: %s\n",
     650             :                                                 smb_get_krb5_error_message(context,
     651             :                                                                            code,
     652             :                                                                            mem_ctx));
     653             :         }
     654             : 
     655         141 :         code = 0;
     656         328 : done:
     657         328 :         talloc_free(tmp_ctx);
     658         328 :         return code;
     659             : }

Generated by: LCOV version 1.13