LCOV - code coverage report
Current view: top level - source4/auth/kerberos - kerberos_util.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 264 354 74.6 %
Date: 2024-04-21 15:09:00 Functions: 10 10 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       65913 : 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       65913 :         krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
      41             : 
      42       65913 :         return 0;
      43             : }
      44             : 
      45             : 
      46       81421 : 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       81421 :         if (princ_string == NULL) {
      55       15508 :                  *princ = NULL;
      56       15508 :                  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       65913 :         mem_ctx = talloc(parent_ctx, struct principal_container);
      64       65913 :         if (!mem_ctx) {
      65           0 :                 (*error_string) = error_message(ENOMEM);
      66           0 :                 return ENOMEM;
      67             :         }
      68             : 
      69       65913 :         mem_ctx->smb_krb5_context = talloc_reference(mem_ctx,
      70             :                                                      smb_krb5_context);
      71       65913 :         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       65913 :         ret = krb5_parse_name(smb_krb5_context->krb5_context,
      78             :                               princ_string, princ);
      79             : 
      80       65913 :         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 effectively puts the principal
      89             :          * into talloc, so we can't lose it. */
      90       65913 :         mem_ctx->principal = *princ;
      91       65913 :         talloc_set_destructor(mem_ctx, free_principal);
      92       65913 :         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       65884 : 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       65884 :         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
     110       65884 :         *obtained = CRED_UNINITIALISED;
     111             : 
     112       65884 :         if (!mem_ctx) {
     113           0 :                 (*error_string) = error_message(ENOMEM);
     114           0 :                 return ENOMEM;
     115             :         }
     116       65884 :         princ_string = cli_credentials_get_principal_and_obtained(credentials,
     117             :                                                                   mem_ctx,
     118             :                                                                   obtained);
     119       65884 :         if (!princ_string) {
     120           6 :                 *princ = NULL;
     121           6 :                 return 0;
     122             :         }
     123             : 
     124       65878 :         ret = parse_principal(parent_ctx, princ_string,
     125             :                               smb_krb5_context, princ, error_string);
     126       65878 :         talloc_free(mem_ctx);
     127       65878 :         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       15543 : 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       15543 :         return parse_principal(parent_ctx,
     143             :                         cli_credentials_get_impersonate_principal(credentials),
     144             :                         smb_krb5_context, princ, error_string);
     145             : }
     146             : 
     147         348 : 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         348 :         uint32_t num_principals = 0;
     160          26 :         krb5_principal *principals;
     161          26 :         uint32_t i;
     162             : 
     163         348 :         tmp_ctx = talloc_new(mem_ctx);
     164         348 :         if (tmp_ctx == NULL) {
     165           0 :                 *error_string = "Cannot allocate tmp_ctx";
     166           0 :                 return ENOMEM;
     167             :         }
     168             : 
     169         348 :         if (realm == NULL) {
     170           0 :                 *error_string = "Cannot create principal without a realm";
     171           0 :                 code = EINVAL;
     172           0 :                 goto done;
     173             :         }
     174             : 
     175         348 :         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         348 :         if (account_name != NULL && account_name[0] != '\0') {
     182         348 :                 num_principals++;
     183             :         }
     184         348 :         num_principals += num_spns;
     185             : 
     186         348 :         principals = talloc_zero_array(tmp_ctx,
     187             :                                        krb5_principal,
     188             :                                        num_principals);
     189         348 :         if (principals == NULL) {
     190           0 :                 *error_string = "Cannot allocate principals";
     191           0 :                 code = ENOMEM;
     192           0 :                 goto done;
     193             :         }
     194             : 
     195         830 :         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         348 :         if (account_name != NULL && account_name[0] != '\0') {
     206         374 :                 code = smb_krb5_make_principal(context,
     207         348 :                                                &(principals[i]),
     208             :                                                realm,
     209             :                                                account_name,
     210             :                                                NULL);
     211         348 :                 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         348 :         if (pnum_principals != NULL) {
     220         348 :                 *pnum_principals = num_principals;
     221             : 
     222         348 :                 if (pprincipals != NULL) {
     223         348 :                         *pprincipals = talloc_steal(mem_ctx, principals);
     224             :                 }
     225             :         }
     226             : 
     227         322 :         code = 0;
     228         348 : done:
     229         348 :         talloc_free(tmp_ctx);
     230         348 :         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       15545 :  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         585 :         krb5_error_code ret;
     248         585 :         const char *password;
     249         585 :         const char *self_service;
     250         585 :         const char *target_service;
     251       15545 :         time_t kdc_time = 0;
     252         585 :         krb5_principal princ;
     253         585 :         krb5_principal impersonate_principal;
     254         585 :         int tries;
     255       15545 :         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
     256         585 :         krb5_get_init_creds_opt *krb_options;
     257         585 :         struct cli_credentials *fast_creds;
     258             : 
     259       15545 :         if (!mem_ctx) {
     260           0 :                 (*error_string) = strerror(ENOMEM);
     261           0 :                 return ENOMEM;
     262             :         }
     263             : 
     264       15545 :         ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ, obtained, error_string);
     265       15545 :         if (ret) {
     266           0 :                 talloc_free(mem_ctx);
     267           0 :                 return ret;
     268             :         }
     269             : 
     270       15545 :         if (princ == NULL) {
     271           2 :                 (*error_string) = talloc_asprintf(credentials, "principal, username or realm was not specified in the credentials");
     272           2 :                 talloc_free(mem_ctx);
     273           2 :                 return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
     274             :         }
     275             : 
     276       15543 :         ret = impersonate_principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &impersonate_principal, error_string);
     277       15543 :         if (ret) {
     278           0 :                 talloc_free(mem_ctx);
     279           0 :                 return ret;
     280             :         }
     281             : 
     282       15543 :         self_service = cli_credentials_get_self_service(credentials);
     283       15543 :         target_service = cli_credentials_get_target_service(credentials);
     284             : 
     285       15543 :         password = cli_credentials_get_password(credentials);
     286             : 
     287             :         /* setup the krb5 options we want */
     288       15543 :         if ((ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options))) {
     289           0 :                 (*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           0 :                 talloc_free(mem_ctx);
     293           0 :                 return ret;
     294             :         }
     295             : 
     296             : #ifdef SAMBA4_USES_HEIMDAL /* Disable for now MIT reads defaults when needed */
     297             :         /* get the defaults */
     298       11874 :         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       15543 :         switch (cli_credentials_get_krb_forwardable(credentials)) {
     302       14956 :         case CRED_AUTO_KRB_FORWARDABLE:
     303       14956 :                 break;
     304           2 :         case CRED_NO_KRB_FORWARDABLE:
     305           2 :                 krb5_get_init_creds_opt_set_forwardable(krb_options, FALSE);
     306           2 :                 break;
     307           0 :         case CRED_FORCE_KRB_FORWARDABLE:
     308           0 :                 krb5_get_init_creds_opt_set_forwardable(krb_options, TRUE);
     309           0 :                 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       11874 :         krb5_get_init_creds_opt_set_win2k(smb_krb5_context->krb5_context,
     323             :                                           krb_options, true);
     324       11874 :         krb5_get_init_creds_opt_set_canonicalize(smb_krb5_context->krb5_context,
     325             :                                                  krb_options, true);
     326             : #else /* MIT */
     327        3669 :         krb5_get_init_creds_opt_set_canonicalize(krb_options, true);
     328             : #endif
     329             : 
     330       15543 :         fast_creds = cli_credentials_get_krb5_fast_armor_credentials(credentials);
     331             : 
     332       15543 :         if (fast_creds != NULL) {
     333             : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_CCACHE
     334          11 :                 struct ccache_container *fast_ccc = NULL;
     335          11 :                 const char *fast_error_string = NULL;
     336          11 :                 ret = cli_credentials_get_ccache(fast_creds, event_ctx, lp_ctx, &fast_ccc, &fast_error_string);
     337          11 :                 if (ret != 0) {
     338           0 :                         (*error_string) = talloc_asprintf(credentials,
     339             :                                                           "Obtaining the Kerberos FAST armor credentials failed: %s\n",
     340             :                                                           fast_error_string);
     341           0 :                         return ret;
     342             :                 }
     343          11 :                 krb5_get_init_creds_opt_set_fast_ccache(smb_krb5_context->krb5_context,
     344             :                                                         krb_options,
     345          11 :                                                         fast_ccc->ccache);
     346             : #else
     347           0 :                 *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           0 :                 return EINVAL;
     354             : #endif
     355             :         }
     356             : 
     357             : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_FLAGS
     358             :         {
     359         585 :                 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       15543 :                 require_fast = cli_credentials_get_krb5_require_fast_armor(credentials);
     366       15543 :                 if (require_fast) {
     367          11 :                         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       14958 :         tries = 2;
     375       15543 :         while (tries--) {
     376             : #ifdef SAMBA4_USES_HEIMDAL
     377         585 :                 struct tevent_context *previous_ev;
     378             :                 /* Do this every time, in case we have weird recursive issues here */
     379       11874 :                 ret = smb_krb5_context_set_event_ctx(smb_krb5_context, event_ctx, &previous_ev);
     380       11874 :                 if (ret) {
     381           0 :                         talloc_free(mem_ctx);
     382           0 :                         krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
     383           1 :                         return ret;
     384             :                 }
     385             : #endif
     386       15543 :                 if (password) {
     387       15523 :                         if (impersonate_principal) {
     388          35 :                                 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       15488 :                                 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          20 :                 } else if (impersonate_principal) {
     409           0 :                         talloc_free(mem_ctx);
     410           0 :                         (*error_string) = "INTERNAL error: Cannot impersonate principal with just a keyblock.  A password must be specified in the credentials";
     411           0 :                         krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
     412             : #ifdef SAMBA4_USES_HEIMDAL
     413           0 :                         smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
     414             : #endif
     415           0 :                         return EINVAL;
     416             :                 } else {
     417             :                         /* No password available, try to use a keyblock instead */
     418             : 
     419           3 :                         krb5_keyblock keyblock;
     420           3 :                         const struct samr_Password *mach_pwd;
     421          20 :                         mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
     422          20 :                         if (!mach_pwd) {
     423           2 :                                 talloc_free(mem_ctx);
     424           2 :                                 (*error_string) = "kinit_to_ccache: No password available for kinit\n";
     425           2 :                                 krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
     426             : #ifdef SAMBA4_USES_HEIMDAL
     427           1 :                                 smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
     428             : #endif
     429           2 :                                 return EINVAL;
     430             :                         }
     431          21 :                         ret = smb_krb5_keyblock_init_contents(smb_krb5_context->krb5_context,
     432             :                                                  ENCTYPE_ARCFOUR_HMAC,
     433          18 :                                                  mach_pwd->hash, sizeof(mach_pwd->hash),
     434             :                                                  &keyblock);
     435             : 
     436          18 :                         if (ret == 0) {
     437          18 :                                 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          18 :                                 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
     446             :                         }
     447             :                 }
     448             : 
     449             : #ifdef SAMBA4_USES_HEIMDAL
     450       11873 :                 smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
     451             : #endif
     452             : 
     453       15541 :                 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           0 :                         time_t t = time(NULL);
     456           0 :                         krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
     457             :                 } else {
     458             :                         /* not a skew problem */
     459             :                         break;
     460             :                 }
     461             :         }
     462             : 
     463       15541 :         krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
     464             : 
     465       15541 :         if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
     466           0 :                 (*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           0 :                 talloc_free(mem_ctx);
     471           0 :                 return ret;
     472             :         }
     473             : 
     474             :         /* cope with ticket being in the future due to clock skew */
     475       15541 :         if ((unsigned)kdc_time > time(NULL)) {
     476           0 :                 time_t t = time(NULL);
     477           0 :                 int time_offset =(unsigned)kdc_time-t;
     478           0 :                 DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
     479           0 :                 krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
     480             :         }
     481             : 
     482       15541 :         if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
     483           0 :                 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       15541 :         if (ret) {
     493        1418 :                 (*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        1418 :                 talloc_free(mem_ctx);
     498        1418 :                 return ret;
     499             :         }
     500             : 
     501       14123 :         DEBUG(10,("kinit for %s succeeded\n",
     502             :                 cli_credentials_get_principal(credentials, mem_ctx)));
     503             : 
     504             : 
     505       14123 :         talloc_free(mem_ctx);
     506       14123 :         return 0;
     507             : }
     508             : 
     509       67792 : static krb5_error_code free_keytab_container(struct keytab_container *ktc)
     510             : {
     511       67792 :         return krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
     512             : }
     513             : 
     514       67630 : 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        2655 :         krb5_keytab keytab;
     521        2655 :         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       67630 :         *ktc = talloc(mem_ctx, struct keytab_container);
     528       67630 :         if (!*ktc) {
     529           0 :                 return ENOMEM;
     530             :         }
     531             : 
     532       67630 :         (*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
     533       67630 :         if ((*ktc)->smb_krb5_context == NULL) {
     534           0 :                 TALLOC_FREE(*ktc);
     535           0 :                 return ENOMEM;
     536             :         }
     537             : 
     538       67630 :         if (opt_keytab) {
     539          97 :                 keytab = opt_keytab;
     540             :         } else {
     541       67533 :                 ret = krb5_kt_resolve(smb_krb5_context->krb5_context,
     542             :                                                 keytab_name, &keytab);
     543       67533 :                 if (ret) {
     544           0 :                         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           0 :                         TALLOC_FREE(*ktc);
     549           0 :                         return ret;
     550             :                 }
     551             :         }
     552             : 
     553       67630 :         (*ktc)->keytab = keytab;
     554       67630 :         (*ktc)->password_based = false;
     555       67630 :         talloc_set_destructor(*ktc, free_keytab_container);
     556             : 
     557       67630 :         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         386 : 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          28 :         TALLOC_CTX *tmp_ctx;
     579          28 :         krb5_error_code code;
     580          28 :         krb5_kt_cursor cursor;
     581             : 
     582         386 :         tmp_ctx = talloc_new(mem_ctx);
     583         386 :         if (tmp_ctx == NULL) {
     584           0 :                 *error_string = "Cannot allocate tmp_ctx";
     585           0 :                 return ENOMEM;
     586             :         }
     587             : 
     588         386 :         *found_previous = true;
     589             : 
     590         386 :         code = krb5_kt_start_seq_get(context, keytab, &cursor);
     591         386 :         switch (code) {
     592         161 :         case 0:
     593         161 :                 break;
     594             : #ifdef HEIM_ERR_OPNOTSUPP
     595           0 :         case HEIM_ERR_OPNOTSUPP:
     596             : #endif
     597         219 :         case ENOENT:
     598             :         case KRB5_KT_END:
     599             :                 /* no point enumerating if there isn't anything here */
     600         219 :                 code = 0;
     601         219 :                 goto done;
     602           0 :         default:
     603           0 :                 *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           0 :                 goto done;
     607             :         }
     608             : 
     609         202 :         do {
     610        2439 :                 krb5_kvno old_kvno = kvno - 1;
     611         202 :                 krb5_keytab_entry entry;
     612        2439 :                 bool matched = false;
     613         202 :                 uint32_t i;
     614             : 
     615        2439 :                 code = krb5_kt_next_entry(context, keytab, &entry, &cursor);
     616        2439 :                 if (code) {
     617         161 :                         break;
     618             :                 }
     619             : 
     620        5236 :                 for (i = 0; i < num_principals; i++) {
     621         410 :                         krb5_boolean ok;
     622             : 
     623        5118 :                         ok = smb_krb5_kt_compare(context,
     624             :                                                 &entry,
     625        4708 :                                                 principals[i],
     626             :                                                 0,
     627             :                                                 0);
     628        4708 :                         if (ok) {
     629        1587 :                                 matched = true;
     630        1587 :                                 break;
     631             :                         }
     632             :                 }
     633             : 
     634        2272 :                 if (!matched) {
     635             :                         /*
     636             :                          * Free the entry, it wasn't the one we were looking
     637             :                          * for anyway
     638             :                          */
     639         528 :                         krb5_kt_free_entry(context, &entry);
     640             :                         /* Make sure we do not double free */
     641         528 :                         ZERO_STRUCT(entry);
     642         528 :                         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        1744 :                 if ((entry.vno & 0xff) != (old_kvno & 0xff)) {
     653          43 :                         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         387 :                         krb5_kt_end_seq_get(context, keytab, &cursor);
     663             : 
     664         387 :                         code = krb5_kt_remove_entry(context, keytab, &entry);
     665         387 :                         krb5_kt_free_entry(context, &entry);
     666             : 
     667             :                         /* Make sure we do not double free */
     668         387 :                         ZERO_STRUCT(entry);
     669             : 
     670             :                         /* Deleted: Restart from the top */
     671         387 :                         rc = krb5_kt_start_seq_get(context, keytab, &cursor);
     672         387 :                         if (rc != 0) {
     673           0 :                                 krb5_kt_free_entry(context, &entry);
     674             : 
     675             :                                 /* Make sure we do not double free */
     676           0 :                                 ZERO_STRUCT(entry);
     677             : 
     678           0 :                                 DEBUG(1, ("failed to restart enumeration of keytab: %s\n",
     679             :                                           smb_get_krb5_error_message(context,
     680             :                                                                      code,
     681             :                                                                      tmp_ctx)));
     682             : 
     683           0 :                                 talloc_free(tmp_ctx);
     684           0 :                                 return rc;
     685             :                         }
     686             : 
     687         387 :                         if (code != 0) {
     688           0 :                                 break;
     689             :                         }
     690             : 
     691             :                 } else {
     692        1357 :                         *found_previous = true;
     693             :                 }
     694             : 
     695             :                 /* Free the entry, we don't need it any more */
     696        1744 :                 krb5_kt_free_entry(context, &entry);
     697             :                 /* Make sure we do not double free */
     698        1744 :                 ZERO_STRUCT(entry);
     699        2076 :         } while (code == 0);
     700             : 
     701         167 :         krb5_kt_end_seq_get(context, keytab, &cursor);
     702             : 
     703         167 :         switch (code) {
     704           0 :         case 0:
     705           0 :                 break;
     706         161 :         case ENOENT:
     707             :         case KRB5_KT_END:
     708         161 :                 break;
     709           0 :         default:
     710           0 :                 *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           0 :                 goto done;
     716             :         }
     717             : 
     718         161 :         code = 0;
     719         386 : done:
     720         386 :         talloc_free(tmp_ctx);
     721         386 :         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        2928 : 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         231 :         TALLOC_CTX *tmp_ctx;
     740         231 :         krb5_error_code code;
     741         231 :         krb5_kt_cursor cursor;
     742             : 
     743        2928 :         tmp_ctx = talloc_new(mem_ctx);
     744        2928 :         if (tmp_ctx == NULL) {
     745           0 :                 *error_string = "Cannot allocate tmp_ctx";
     746           0 :                 return ENOMEM;
     747             :         }
     748             : 
     749        2928 :         *found = false;
     750             : 
     751        2928 :         code = krb5_kt_start_seq_get(context, keytab, &cursor);
     752        2928 :         switch (code) {
     753        2494 :         case 0:
     754        2494 :                 break;
     755             : #ifdef HEIM_ERR_OPNOTSUPP
     756           0 :         case HEIM_ERR_OPNOTSUPP:
     757             : #endif
     758         225 :         case ENOENT:
     759             :         case KRB5_KT_END:
     760             :                 /* no point enumerating if there isn't anything here */
     761         225 :                 code = 0;
     762         225 :                 goto done;
     763           0 :         default:
     764           0 :                 *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           0 :                 goto done;
     768             :         }
     769             : 
     770        1448 :         do {
     771        1448 :                 krb5_keytab_entry entry;
     772       20646 :                 bool matched = false;
     773        1448 :                 krb5_boolean ok;
     774             : 
     775       20646 :                 code = krb5_kt_next_entry(context, keytab, &entry, &cursor);
     776       20646 :                 if (code) {
     777        2494 :                         break;
     778             :                 }
     779             : 
     780       19316 :                 ok = smb_krb5_kt_compare(context,
     781             :                                          &entry,
     782       18077 :                                          to_match->principal,
     783             :                                          to_match->vno,
     784       13154 :                                          KRB5_KEY_TYPE(KRB5_KT_KEY(to_match)));
     785       18077 :                 if (ok) {
     786             :                         /* This is not a security check, constant time is not required */
     787         134 :                         if ((KRB5_KEY_LENGTH(KRB5_KT_KEY(&entry)) == KRB5_KEY_LENGTH(KRB5_KT_KEY(to_match)))
     788         134 :                             && memcmp(KRB5_KEY_DATA(KRB5_KT_KEY(&entry)), KRB5_KEY_DATA(KRB5_KT_KEY(to_match)),
     789          69 :                                       KRB5_KEY_LENGTH(KRB5_KT_KEY(&entry))) == 0) {
     790         134 :                                 matched = true;
     791             :                         }
     792             :                 }
     793             : 
     794             :                 /* Free the entry, we don't need it any more */
     795       18077 :                 krb5_kt_free_entry(context, &entry);
     796             :                 /* Make sure we do not double free */
     797       18077 :                 ZERO_STRUCT(entry);
     798       18077 :                 if (matched) {
     799         134 :                         *found = true;
     800         134 :                         break;
     801             :                 }
     802       17943 :         } while (code == 0);
     803             : 
     804        2703 :         krb5_kt_end_seq_get(context, keytab, &cursor);
     805             : 
     806        2703 :         switch (code) {
     807         134 :         case 0:
     808         134 :                 break;
     809        2360 :         case ENOENT:
     810             :         case KRB5_KT_END:
     811        2360 :                 break;
     812           0 :         default:
     813           0 :                 *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           0 :                 goto done;
     819             :         }
     820             : 
     821        2494 :         code = 0;
     822        2928 : done:
     823        2928 :         talloc_free(tmp_ctx);
     824        2928 :         return code;
     825             : }

Generated by: LCOV version 1.14