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

Generated by: LCOV version 1.14