LCOV - code coverage report
Current view: top level - auth/credentials - credentials_krb5.c (source / functions) Hit Total Coverage
Test: coverage report for support-claim-type-attributes 6b5c566e Lines: 477 729 65.4 %
Date: 2023-11-21 12:31:41 Functions: 30 34 88.2 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    Handle user credentials (as regards krb5)
       5             : 
       6             :    Copyright (C) Jelmer Vernooij 2005
       7             :    Copyright (C) Tim Potter 2001
       8             :    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
       9             : 
      10             :    This program is free software; you can redistribute it and/or modify
      11             :    it under the terms of the GNU General Public License as published by
      12             :    the Free Software Foundation; either version 3 of the License, or
      13             :    (at your option) any later version.
      14             : 
      15             :    This program is distributed in the hope that it will be useful,
      16             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :    GNU General Public License for more details.
      19             : 
      20             :    You should have received a copy of the GNU General Public License
      21             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      22             : */
      23             : 
      24             : #include "includes.h"
      25             : #include "system/kerberos.h"
      26             : #include "system/gssapi.h"
      27             : #include "auth/kerberos/kerberos.h"
      28             : #include "auth/credentials/credentials.h"
      29             : #include "auth/credentials/credentials_internal.h"
      30             : #include "auth/credentials/credentials_krb5.h"
      31             : #include "auth/kerberos/kerberos_credentials.h"
      32             : #include "auth/kerberos/kerberos_srv_keytab.h"
      33             : #include "auth/kerberos/kerberos_util.h"
      34             : #include "auth/kerberos/pac_utils.h"
      35             : #include "param/param.h"
      36             : #include "../libds/common/flags.h"
      37             : 
      38             : #undef DBGC_CLASS
      39             : #define DBGC_CLASS DBGC_AUTH
      40             : 
      41             : #undef strncasecmp
      42             : 
      43             : static void cli_credentials_invalidate_client_gss_creds(
      44             :                                         struct cli_credentials *cred,
      45             :                                         enum credentials_obtained obtained);
      46             : 
      47             : /* Free a memory ccache */
      48       46016 : static int free_mccache(struct ccache_container *ccc)
      49             : {
      50       46016 :         if (ccc->ccache != NULL) {
      51       46016 :                 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
      52             :                                 ccc->ccache);
      53       46016 :                 ccc->ccache = NULL;
      54             :         }
      55             : 
      56       46016 :         return 0;
      57             : }
      58             : 
      59             : /* Free a disk-based ccache */
      60       97743 : static int free_dccache(struct ccache_container *ccc)
      61             : {
      62       97743 :         if (ccc->ccache != NULL) {
      63       97743 :                 krb5_cc_close(ccc->smb_krb5_context->krb5_context,
      64             :                               ccc->ccache);
      65       97743 :                 ccc->ccache = NULL;
      66             :         }
      67             : 
      68       97743 :         return 0;
      69             : }
      70             : 
      71       30793 : static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat,
      72             :                                          gss_cred_id_t cred,
      73             :                                          struct ccache_container *ccc)
      74             : {
      75             : #ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */
      76        7591 :         krb5_context context = ccc->smb_krb5_context->krb5_context;
      77        7591 :         krb5_ccache dummy_ccache = NULL;
      78        7591 :         krb5_creds creds = {0};
      79        7591 :         krb5_cc_cursor cursor = NULL;
      80        7591 :         krb5_principal princ = NULL;
      81             :         krb5_error_code code;
      82             :         char *dummy_name;
      83        7591 :         uint32_t maj_stat = GSS_S_FAILURE;
      84             : 
      85        7591 :         dummy_name = talloc_asprintf(ccc,
      86             :                                      "MEMORY:gss_krb5_copy_ccache-%p",
      87             :                                      &ccc->ccache);
      88        7591 :         if (dummy_name == NULL) {
      89           0 :                 *min_stat = ENOMEM;
      90           0 :                 return GSS_S_FAILURE;
      91             :         }
      92             : 
      93             :         /*
      94             :          * Create a dummy ccache, so we can iterate over the credentials
      95             :          * and find the default principal for the ccache we want to
      96             :          * copy. The new ccache needs to be initialized with this
      97             :          * principal.
      98             :          */
      99        7591 :         code = krb5_cc_resolve(context, dummy_name, &dummy_ccache);
     100        7591 :         TALLOC_FREE(dummy_name);
     101        7591 :         if (code != 0) {
     102           0 :                 *min_stat = code;
     103           0 :                 return GSS_S_FAILURE;
     104             :         }
     105             : 
     106             :         /*
     107             :          * We do not need set a default principal on the temporary dummy
     108             :          * ccache, as we do consume it at all in this function.
     109             :          */
     110        7591 :         maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache);
     111        7591 :         if (maj_stat != 0) {
     112           0 :                 krb5_cc_close(context, dummy_ccache);
     113           0 :                 return maj_stat;
     114             :         }
     115             : 
     116        7591 :         code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor);
     117        7591 :         if (code != 0) {
     118           0 :                 krb5_cc_close(context, dummy_ccache);
     119           0 :                 *min_stat = EINVAL;
     120           0 :                 return GSS_S_FAILURE;
     121             :         }
     122             : 
     123        7591 :         code = krb5_cc_next_cred(context,
     124             :                                  dummy_ccache,
     125             :                                  &cursor,
     126             :                                  &creds);
     127        7591 :         if (code != 0) {
     128           0 :                 krb5_cc_close(context, dummy_ccache);
     129           0 :                 *min_stat = EINVAL;
     130           0 :                 return GSS_S_FAILURE;
     131             :         }
     132             : 
     133             :         do {
     134        7591 :                 if (creds.ticket_flags & TKT_FLG_PRE_AUTH) {
     135             :                         krb5_data *tgs;
     136             : 
     137        7591 :                         tgs = krb5_princ_component(context,
     138             :                                                    creds.server,
     139             :                                                    0);
     140        7591 :                         if (tgs != NULL && tgs->length >= 1) {
     141             :                                 int cmp;
     142             : 
     143        7591 :                                 cmp = memcmp(tgs->data,
     144             :                                              KRB5_TGS_NAME,
     145        7591 :                                              tgs->length);
     146        7591 :                                 if (cmp == 0 && creds.client != NULL) {
     147        7591 :                                         princ = creds.client;
     148        7591 :                                         code = KRB5_CC_END;
     149        7591 :                                         break;
     150             :                                 }
     151             :                         }
     152             :                 }
     153             : 
     154           0 :                 krb5_free_cred_contents(context, &creds);
     155             : 
     156           0 :                 code = krb5_cc_next_cred(context,
     157             :                                          dummy_ccache,
     158             :                                          &cursor,
     159             :                                          &creds);
     160           0 :         } while (code == 0);
     161             : 
     162        7591 :         if (code == KRB5_CC_END) {
     163        7591 :                 krb5_cc_end_seq_get(context, dummy_ccache, &cursor);
     164        7591 :                 code = 0;
     165             :         }
     166        7591 :         krb5_cc_close(context, dummy_ccache);
     167             : 
     168        7591 :         if (code != 0 || princ == NULL) {
     169           0 :                 krb5_free_cred_contents(context, &creds);
     170           0 :                 *min_stat = EINVAL;
     171           0 :                 return GSS_S_FAILURE;
     172             :         }
     173             : 
     174             :         /*
     175             :          * Set the default principal for the cache we copy
     176             :          * into. This is needed to be able that other calls
     177             :          * can read it with e.g. gss_acquire_cred() or
     178             :          * krb5_cc_get_principal().
     179             :          */
     180        7591 :         code = krb5_cc_initialize(context, ccc->ccache, princ);
     181        7591 :         if (code != 0) {
     182           0 :                 krb5_free_cred_contents(context, &creds);
     183           0 :                 *min_stat = EINVAL;
     184           0 :                 return GSS_S_FAILURE;
     185             :         }
     186        7591 :         krb5_free_cred_contents(context, &creds);
     187             : 
     188             : #endif /* SAMBA4_USES_HEIMDAL */
     189             : 
     190       30793 :         return gss_krb5_copy_ccache(min_stat,
     191             :                                     cred,
     192             :                                     ccc->ccache);
     193             : }
     194             : 
     195      259912 : _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
     196             :                                      struct loadparm_context *lp_ctx,
     197             :                                      struct smb_krb5_context **smb_krb5_context)
     198             : {
     199        6699 :         int ret;
     200      259912 :         if (cred->smb_krb5_context) {
     201       67314 :                 *smb_krb5_context = cred->smb_krb5_context;
     202       67314 :                 return 0;
     203             :         }
     204             : 
     205      192598 :         ret = smb_krb5_init_context(cred, lp_ctx,
     206             :                                     &cred->smb_krb5_context);
     207      192598 :         if (ret) {
     208           0 :                 cred->smb_krb5_context = NULL;
     209           0 :                 return ret;
     210             :         }
     211      192598 :         *smb_krb5_context = cred->smb_krb5_context;
     212      192598 :         return 0;
     213             : }
     214             : 
     215             : /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
     216             :  * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
     217             :  */
     218         122 : _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
     219             :                                           struct smb_krb5_context *smb_krb5_context)
     220             : {
     221         122 :         if (smb_krb5_context == NULL) {
     222           0 :                 talloc_unlink(cred, cred->smb_krb5_context);
     223           0 :                 cred->smb_krb5_context = NULL;
     224           0 :                 return NT_STATUS_OK;
     225             :         }
     226             : 
     227         122 :         if (!talloc_reference(cred, smb_krb5_context)) {
     228           0 :                 return NT_STATUS_NO_MEMORY;
     229             :         }
     230         122 :         cred->smb_krb5_context = smb_krb5_context;
     231         122 :         return NT_STATUS_OK;
     232             : }
     233             : 
     234       51580 : static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
     235             :                                            struct ccache_container *ccache,
     236             :                                            enum credentials_obtained obtained,
     237             :                                            const char **error_string)
     238             : {
     239        1466 :         bool ok;
     240        1466 :         char *realm;
     241        1466 :         krb5_principal princ;
     242        1466 :         krb5_error_code ret;
     243        1466 :         char *name;
     244             : 
     245       51580 :         if (cred->ccache_obtained > obtained) {
     246        4975 :                 return 0;
     247             :         }
     248             : 
     249       46605 :         ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
     250             :                                     ccache->ccache, &princ);
     251             : 
     252       46605 :         if (ret) {
     253           0 :                 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
     254           0 :                                                   smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
     255             :                                                                              ret, cred));
     256           0 :                 return ret;
     257             :         }
     258             : 
     259       46605 :         ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
     260       46605 :         if (ret) {
     261           0 :                 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
     262           0 :                                                   smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
     263             :                                                                              ret, cred));
     264           0 :                 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
     265           0 :                 return ret;
     266             :         }
     267             : 
     268       46605 :         ok = cli_credentials_set_principal(cred, name, obtained);
     269       46605 :         krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
     270       46605 :         if (!ok) {
     271          25 :                 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
     272          25 :                 return ENOMEM;
     273             :         }
     274             : 
     275       48046 :         realm = smb_krb5_principal_get_realm(
     276       46580 :                 cred, ccache->smb_krb5_context->krb5_context, princ);
     277       46580 :         krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
     278       46580 :         if (realm == NULL) {
     279           0 :                 return ENOMEM;
     280             :         }
     281       46580 :         ok = cli_credentials_set_realm(cred, realm, obtained);
     282       46580 :         TALLOC_FREE(realm);
     283       46580 :         if (!ok) {
     284          26 :                 return ENOMEM;
     285             :         }
     286             : 
     287             :         /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
     288       46554 :         cred->ccache_obtained = obtained;
     289             : 
     290       46554 :         return 0;
     291             : }
     292             : 
     293       99971 : _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
     294             :                                         struct loadparm_context *lp_ctx,
     295             :                                         const char *name,
     296             :                                         enum credentials_obtained obtained,
     297             :                                         const char **error_string)
     298             : {
     299         421 :         krb5_error_code ret;
     300         421 :         krb5_principal princ;
     301         421 :         struct ccache_container *ccc;
     302       99971 :         if (cred->ccache_obtained > obtained) {
     303        2191 :                 return 0;
     304             :         }
     305             : 
     306       97780 :         ccc = talloc(cred, struct ccache_container);
     307       97780 :         if (!ccc) {
     308           0 :                 (*error_string) = error_message(ENOMEM);
     309           0 :                 return ENOMEM;
     310             :         }
     311             : 
     312       97780 :         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
     313             :                                                &ccc->smb_krb5_context);
     314       97780 :         if (ret) {
     315           0 :                 (*error_string) = error_message(ret);
     316           0 :                 talloc_free(ccc);
     317           0 :                 return ret;
     318             :         }
     319       97780 :         if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
     320           0 :                 talloc_free(ccc);
     321           0 :                 (*error_string) = error_message(ENOMEM);
     322           0 :                 return ENOMEM;
     323             :         }
     324             : 
     325       97780 :         if (name) {
     326        2229 :                 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
     327        2229 :                 if (ret) {
     328           0 :                         (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
     329             :                                                           name,
     330           0 :                                                           smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
     331             :                                                                                      ret, ccc));
     332           0 :                         talloc_free(ccc);
     333           0 :                         return ret;
     334             :                 }
     335             :         } else {
     336       95551 :                 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
     337       95551 :                 if (ret) {
     338           0 :                         (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
     339           0 :                                                           smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
     340             :                                                                                      ret, ccc));
     341           0 :                         talloc_free(ccc);
     342           0 :                         return ret;
     343             :                 }
     344             :         }
     345             : 
     346       97780 :         talloc_set_destructor(ccc, free_dccache);
     347             : 
     348       97780 :         ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
     349             : 
     350       97780 :         if (ret == 0) {
     351        7116 :                 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
     352        7116 :                 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
     353             : 
     354        7116 :                 if (ret) {
     355          51 :                         (*error_string) = error_message(ret);
     356          51 :                         TALLOC_FREE(ccc);
     357          51 :                         return ret;
     358             :                 }
     359             :         }
     360             : 
     361       97729 :         cred->ccache = ccc;
     362       97729 :         cred->ccache_obtained = obtained;
     363             : 
     364       97729 :         cli_credentials_invalidate_client_gss_creds(
     365             :                 cred, cred->ccache_obtained);
     366             : 
     367       97729 :         return 0;
     368             : }
     369             : 
     370             : #ifndef SAMBA4_USES_HEIMDAL
     371             : /*
     372             :  * This function is a workaround for old MIT Kerberos versions which did not
     373             :  * implement the krb5_cc_remove_cred function. It creates a temporary
     374             :  * credentials cache to copy the credentials in the current cache
     375             :  * except the one we want to remove and then overwrites the contents of the
     376             :  * current cache with the temporary copy.
     377             :  */
     378           0 : static krb5_error_code krb5_cc_remove_cred_wrap(struct ccache_container *ccc,
     379             :                                                 krb5_creds *creds)
     380             : {
     381           0 :         krb5_ccache dummy_ccache = NULL;
     382           0 :         krb5_creds cached_creds = {0};
     383           0 :         krb5_cc_cursor cursor = NULL;
     384             :         krb5_error_code code;
     385             :         char *dummy_name;
     386             : 
     387           0 :         dummy_name = talloc_asprintf(ccc,
     388             :                                      "MEMORY:copy_ccache-%p",
     389             :                                      &ccc->ccache);
     390           0 :         if (dummy_name == NULL) {
     391           0 :                 return KRB5_CC_NOMEM;
     392             :         }
     393             : 
     394           0 :         code = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
     395             :                                dummy_name,
     396             :                                &dummy_ccache);
     397           0 :         if (code != 0) {
     398           0 :                 DBG_ERR("krb5_cc_resolve failed: %s\n",
     399             :                         smb_get_krb5_error_message(
     400             :                                 ccc->smb_krb5_context->krb5_context,
     401             :                                 code, ccc));
     402           0 :                 TALLOC_FREE(dummy_name);
     403           0 :                 return code;
     404             :         }
     405             : 
     406           0 :         TALLOC_FREE(dummy_name);
     407             : 
     408           0 :         code = krb5_cc_start_seq_get(ccc->smb_krb5_context->krb5_context,
     409             :                                      ccc->ccache,
     410             :                                      &cursor);
     411           0 :         if (code != 0) {
     412           0 :                 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
     413             :                                 dummy_ccache);
     414             : 
     415           0 :                 DBG_ERR("krb5_cc_start_seq_get failed: %s\n",
     416             :                         smb_get_krb5_error_message(
     417             :                                 ccc->smb_krb5_context->krb5_context,
     418             :                                 code, ccc));
     419           0 :                 return code;
     420             :         }
     421             : 
     422           0 :         while ((code = krb5_cc_next_cred(ccc->smb_krb5_context->krb5_context,
     423             :                                          ccc->ccache,
     424             :                                          &cursor,
     425           0 :                                          &cached_creds)) == 0) {
     426             :                 /* If the principal matches skip it and do not copy to the
     427             :                  * temporary cache as this is the one we want to remove */
     428           0 :                 if (krb5_principal_compare_flags(
     429           0 :                                 ccc->smb_krb5_context->krb5_context,
     430           0 :                                 creds->server,
     431           0 :                                 cached_creds.server,
     432             :                                 0)) {
     433           0 :                         continue;
     434             :                 }
     435             : 
     436           0 :                 code = krb5_cc_store_cred(
     437           0 :                                 ccc->smb_krb5_context->krb5_context,
     438             :                                 dummy_ccache,
     439             :                                 &cached_creds);
     440           0 :                 if (code != 0) {
     441           0 :                         krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
     442             :                                         dummy_ccache);
     443           0 :                         DBG_ERR("krb5_cc_store_cred failed: %s\n",
     444             :                                 smb_get_krb5_error_message(
     445             :                                         ccc->smb_krb5_context->krb5_context,
     446             :                                         code, ccc));
     447           0 :                         return code;
     448             :                 }
     449             :         }
     450             : 
     451           0 :         if (code == KRB5_CC_END) {
     452           0 :                 krb5_cc_end_seq_get(ccc->smb_krb5_context->krb5_context,
     453             :                                     dummy_ccache,
     454             :                                     &cursor);
     455           0 :                 code = 0;
     456             :         }
     457             : 
     458           0 :         if (code != 0) {
     459           0 :                 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
     460             :                                 dummy_ccache);
     461           0 :                 DBG_ERR("krb5_cc_next_cred failed: %s\n",
     462             :                         smb_get_krb5_error_message(
     463             :                                 ccc->smb_krb5_context->krb5_context,
     464             :                                 code, ccc));
     465           0 :                 return code;
     466             :         }
     467             : 
     468           0 :         code = krb5_cc_initialize(ccc->smb_krb5_context->krb5_context,
     469             :                                   ccc->ccache,
     470             :                                   creds->client);
     471           0 :         if (code != 0) {
     472           0 :                 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
     473             :                                 dummy_ccache);
     474           0 :                 DBG_ERR("krb5_cc_initialize failed: %s\n",
     475             :                         smb_get_krb5_error_message(
     476             :                                 ccc->smb_krb5_context->krb5_context,
     477             :                                 code, ccc));
     478           0 :                 return code;
     479             :         }
     480             : 
     481           0 :         code = krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
     482             :                                   dummy_ccache,
     483             :                                   ccc->ccache);
     484           0 :         if (code != 0) {
     485           0 :                 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
     486             :                                 dummy_ccache);
     487           0 :                 DBG_ERR("krb5_cc_copy_creds failed: %s\n",
     488             :                         smb_get_krb5_error_message(
     489             :                                 ccc->smb_krb5_context->krb5_context,
     490             :                                 code, ccc));
     491           0 :                 return code;
     492             :         }
     493             : 
     494           0 :         code = krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
     495             :                                dummy_ccache);
     496           0 :         if (code != 0) {
     497           0 :                 DBG_ERR("krb5_cc_destroy failed: %s\n",
     498             :                         smb_get_krb5_error_message(
     499             :                                 ccc->smb_krb5_context->krb5_context,
     500             :                                 code, ccc));
     501           0 :                 return code;
     502             :         }
     503             : 
     504           0 :         return code;
     505             : }
     506             : #endif
     507             : 
     508             : /*
     509             :  * Indicate that we failed to log in to this service/host with these
     510             :  * credentials.  The caller passes an unsigned int which they
     511             :  * initialise to the number of times they would like to retry.
     512             :  *
     513             :  * This method is used to support re-trying with freshly fetched
     514             :  * credentials in case a server is rebuilt while clients have
     515             :  * non-expired tickets. When the client code gets a logon failure they
     516             :  * throw away the existing credentials for the server and retry.
     517             :  */
     518        1387 : _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
     519             :                                                     const char *principal,
     520             :                                                     unsigned int *count)
     521             : {
     522           6 :         struct ccache_container *ccc;
     523           6 :         krb5_creds creds, creds2;
     524           6 :         int ret;
     525             : 
     526        1387 :         if (principal == NULL) {
     527             :                 /* no way to delete if we don't know the principal */
     528           0 :                 return false;
     529             :         }
     530             : 
     531        1387 :         ccc = cred->ccache;
     532        1387 :         if (ccc == NULL) {
     533             :                 /* not a kerberos connection */
     534        1361 :                 return false;
     535             :         }
     536             : 
     537          26 :         if (*count > 0) {
     538             :                 /* We have already tried discarding the credentials */
     539           0 :                 return false;
     540             :         }
     541          26 :         (*count)++;
     542             : 
     543          26 :         ZERO_STRUCT(creds);
     544          26 :         ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
     545          26 :         if (ret != 0) {
     546           0 :                 return false;
     547             :         }
     548             : 
     549             :         /* MIT kerberos requires creds.client to match against cached
     550             :          * credentials */
     551          26 :         ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context,
     552             :                                     ccc->ccache,
     553             :                                     &creds.client);
     554          26 :         if (ret != 0) {
     555           0 :                 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context,
     556             :                                         &creds);
     557           0 :                 DBG_ERR("krb5_cc_get_principal failed: %s\n",
     558             :                         smb_get_krb5_error_message(
     559             :                                 ccc->smb_krb5_context->krb5_context,
     560             :                                 ret, ccc));
     561           0 :                 return false;
     562             :         }
     563             : 
     564          26 :         ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
     565          26 :         if (ret != 0) {
     566             :                 /* don't retry - we didn't find these credentials to remove */
     567          18 :                 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
     568          18 :                 return false;
     569             :         }
     570             : 
     571           8 :         ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
     572             : #ifndef SAMBA4_USES_HEIMDAL
     573           6 :         if (ret == KRB5_CC_NOSUPP) {
     574             :                 /* Old MIT kerberos versions did not implement
     575             :                  * krb5_cc_remove_cred */
     576           0 :                 ret = krb5_cc_remove_cred_wrap(ccc, &creds);
     577             :         }
     578             : #endif
     579           8 :         krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
     580           8 :         krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
     581           8 :         if (ret != 0) {
     582             :                 /* don't retry - we didn't find these credentials to
     583             :                  * remove. Note that with the current backend this
     584             :                  * never happens, as it always returns 0 even if the
     585             :                  * creds don't exist, which is why we do a separate
     586             :                  * krb5_cc_retrieve_cred() above.
     587             :                  */
     588           0 :                 DBG_ERR("krb5_cc_remove_cred failed: %s\n",
     589             :                         smb_get_krb5_error_message(
     590             :                                 ccc->smb_krb5_context->krb5_context,
     591             :                                 ret, ccc));
     592           0 :                 return false;
     593             :         }
     594           8 :         return true;
     595             : }
     596             : 
     597             : 
     598       45578 : static int cli_credentials_new_ccache(struct cli_credentials *cred,
     599             :                                       struct loadparm_context *lp_ctx,
     600             :                                       char *ccache_name,
     601             :                                       struct ccache_container **_ccc,
     602             :                                       const char **error_string)
     603             : {
     604       45578 :         bool must_free_cc_name = false;
     605        1466 :         krb5_error_code ret;
     606       45578 :         struct ccache_container *ccc = talloc(cred, struct ccache_container);
     607       45578 :         if (!ccc) {
     608           0 :                 return ENOMEM;
     609             :         }
     610             : 
     611       45578 :         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
     612             :                                                &ccc->smb_krb5_context);
     613       45578 :         if (ret) {
     614           0 :                 talloc_free(ccc);
     615           0 :                 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
     616             :                                                   error_message(ret));
     617           0 :                 return ret;
     618             :         }
     619       45578 :         if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
     620           0 :                 talloc_free(ccc);
     621           0 :                 (*error_string) = strerror(ENOMEM);
     622           0 :                 return ENOMEM;
     623             :         }
     624             : 
     625       45578 :         if (!ccache_name) {
     626       45566 :                 must_free_cc_name = true;
     627             : 
     628       45566 :                 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
     629           0 :                         ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
     630           0 :                                                       (unsigned int)getpid(), ccc);
     631             :                 } else {
     632       45566 :                         ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
     633             :                                                       ccc);
     634             :                 }
     635             : 
     636       45566 :                 if (!ccache_name) {
     637           0 :                         talloc_free(ccc);
     638           0 :                         (*error_string) = strerror(ENOMEM);
     639           0 :                         return ENOMEM;
     640             :                 }
     641             :         }
     642             : 
     643       45578 :         ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
     644             :                               &ccc->ccache);
     645       45578 :         if (ret) {
     646           0 :                 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
     647             :                                                   ccache_name,
     648           0 :                                                   smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
     649             :                                                                              ret, ccc));
     650           0 :                 talloc_free(ccache_name);
     651           0 :                 talloc_free(ccc);
     652           0 :                 return ret;
     653             :         }
     654             : 
     655       45578 :         if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
     656       45567 :                 talloc_set_destructor(ccc, free_mccache);
     657             :         } else {
     658          11 :                 talloc_set_destructor(ccc, free_dccache);
     659             :         }
     660             : 
     661       45578 :         if (must_free_cc_name) {
     662       45566 :                 talloc_free(ccache_name);
     663             :         }
     664             : 
     665       45578 :         *_ccc = ccc;
     666             : 
     667       45578 :         return 0;
     668             : }
     669             : 
     670       18906 : _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
     671             :                                               struct tevent_context *event_ctx,
     672             :                                               struct loadparm_context *lp_ctx,
     673             :                                               char *ccache_name,
     674             :                                               struct ccache_container **ccc,
     675             :                                               const char **error_string)
     676             : {
     677         585 :         krb5_error_code ret;
     678         585 :         enum credentials_obtained obtained;
     679             : 
     680       18906 :         if (cred->machine_account_pending) {
     681           0 :                 cli_credentials_set_machine_account(cred, lp_ctx);
     682             :         }
     683             : 
     684       18906 :         if (cred->ccache_obtained >= cred->ccache_threshold &&
     685        4121 :             cred->ccache_obtained > CRED_UNINITIALISED) {
     686           0 :                 time_t lifetime;
     687        4121 :                 bool expired = false;
     688        4121 :                 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
     689        4121 :                                                cred->ccache->ccache, &lifetime);
     690        4121 :                 if (ret == KRB5_CC_END || ret == ENOENT) {
     691             :                         /* If we have a particular ccache set, without
     692             :                          * an initial ticket, then assume there is a
     693             :                          * good reason */
     694        4118 :                 } else if (ret == 0) {
     695        4118 :                         if (lifetime == 0) {
     696           0 :                                 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
     697             :                                           cli_credentials_get_principal(cred, cred)));
     698           0 :                                 expired = true;
     699        4118 :                         } else if (lifetime < 300) {
     700           0 :                                 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
     701             :                                           cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
     702           0 :                                 expired = true;
     703             :                         }
     704             :                 } else {
     705           0 :                         (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
     706           0 :                                                           smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
     707             :                                                                                      ret, cred));
     708        4121 :                         return ret;
     709             :                 }
     710             : 
     711        4121 :                 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
     712             :                           cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
     713             : 
     714        4121 :                 if (!expired) {
     715        4121 :                         *ccc = cred->ccache;
     716        4121 :                         return 0;
     717             :                 }
     718             :         }
     719       14785 :         if (cli_credentials_is_anonymous(cred)) {
     720           0 :                 (*error_string) = "Cannot get anonymous kerberos credentials";
     721           0 :                 return EINVAL;
     722             :         }
     723             : 
     724       14785 :         ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
     725       14785 :         if (ret) {
     726           0 :                 return ret;
     727             :         }
     728             : 
     729       14785 :         ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
     730       14785 :         if (ret) {
     731        1114 :                 return ret;
     732             :         }
     733             : 
     734       13671 :         ret = cli_credentials_set_from_ccache(cred, *ccc,
     735             :                                               obtained, error_string);
     736             : 
     737       13671 :         cred->ccache = *ccc;
     738       13671 :         cred->ccache_obtained = cred->principal_obtained;
     739       13671 :         if (ret) {
     740           0 :                 return ret;
     741             :         }
     742       13671 :         cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
     743       13671 :         return 0;
     744             : }
     745             : 
     746       17038 : _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
     747             :                                         struct tevent_context *event_ctx,
     748             :                                         struct loadparm_context *lp_ctx,
     749             :                                         struct ccache_container **ccc,
     750             :                                         const char **error_string)
     751             : {
     752       17038 :         return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
     753             : }
     754             : 
     755             : /* We have good reason to think the ccache in these credentials is invalid - blow it away */
     756           0 : static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
     757             : {
     758           0 :         if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
     759           0 :                 talloc_unlink(cred, cred->client_gss_creds);
     760           0 :                 cred->client_gss_creds = NULL;
     761             :         }
     762           0 :         cred->client_gss_creds_obtained = CRED_UNINITIALISED;
     763           0 : }
     764             : 
     765     1582985 : void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
     766             :                                                  enum credentials_obtained obtained)
     767             : {
     768             :         /* If the caller just changed the username/password etc, then
     769             :          * any cached credentials are now invalid */
     770     1582985 :         if (obtained >= cred->client_gss_creds_obtained) {
     771     1582969 :                 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
     772        4953 :                         talloc_unlink(cred, cred->client_gss_creds);
     773        4953 :                         cred->client_gss_creds = NULL;
     774             :                 }
     775     1582969 :                 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
     776             :         }
     777             :         /* Now that we know that the data is 'this specified', then
     778             :          * don't allow something less 'known' to be returned as a
     779             :          * ccache.  Ie, if the username is on the command line, we
     780             :          * don't want to later guess to use a file-based ccache */
     781     1582985 :         if (obtained > cred->client_gss_creds_threshold) {
     782      610217 :                 cred->client_gss_creds_threshold = obtained;
     783             :         }
     784     1582985 : }
     785             : 
     786             : /* We have good reason to think this CCACHE is invalid.  Blow it away */
     787           0 : static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
     788             : {
     789           0 :         if (cred->ccache_obtained > CRED_UNINITIALISED) {
     790           0 :                 talloc_unlink(cred, cred->ccache);
     791           0 :                 cred->ccache = NULL;
     792             :         }
     793           0 :         cred->ccache_obtained = CRED_UNINITIALISED;
     794             : 
     795           0 :         cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
     796           0 : }
     797             : 
     798     1471585 : _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
     799             :                                        enum credentials_obtained obtained)
     800             : {
     801             :         /* If the caller just changed the username/password etc, then
     802             :          * any cached credentials are now invalid */
     803     1471585 :         if (obtained >= cred->ccache_obtained) {
     804     1454346 :                 if (cred->ccache_obtained > CRED_UNINITIALISED) {
     805       72569 :                         talloc_unlink(cred, cred->ccache);
     806       72569 :                         cred->ccache = NULL;
     807             :                 }
     808     1454346 :                 cred->ccache_obtained = CRED_UNINITIALISED;
     809             :         }
     810             :         /* Now that we know that the data is 'this specified', then
     811             :          * don't allow something less 'known' to be returned as a
     812             :          * ccache.  i.e, if the username is on the command line, we
     813             :          * don't want to later guess to use a file-based ccache */
     814     1471585 :         if (obtained > cred->ccache_threshold) {
     815      541640 :                 cred->ccache_threshold  = obtained;
     816             :         }
     817             : 
     818     1471585 :         cli_credentials_invalidate_client_gss_creds(cred,
     819             :                                                     obtained);
     820     1471585 : }
     821             : 
     822       92290 : static int free_gssapi_creds(struct gssapi_creds_container *gcc)
     823             : {
     824        3617 :         OM_uint32 min_stat;
     825       92290 :         (void)gss_release_cred(&min_stat, &gcc->creds);
     826       92290 :         return 0;
     827             : }
     828             : 
     829       32250 : _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
     830             :                                                   struct tevent_context *event_ctx,
     831             :                                                   struct loadparm_context *lp_ctx,
     832             :                                                   struct gssapi_creds_container **_gcc,
     833             :                                                   const char **error_string)
     834             : {
     835       32250 :         int ret = 0;
     836        1035 :         OM_uint32 maj_stat, min_stat;
     837        1035 :         struct gssapi_creds_container *gcc;
     838        1035 :         struct ccache_container *ccache;
     839             : #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
     840       32250 :         gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
     841       32250 :         gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
     842             : #endif
     843       32250 :         krb5_enctype *etypes = NULL;
     844             : 
     845       32250 :         if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
     846       14965 :             cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
     847       15415 :                 bool expired = false;
     848       15415 :                 OM_uint32 lifetime = 0;
     849       15415 :                 gss_cred_usage_t usage = 0;
     850       15415 :                 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
     851             :                                             NULL, &lifetime, &usage, NULL);
     852       15415 :                 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
     853           0 :                         DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
     854           0 :                         expired = true;
     855       15415 :                 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
     856           0 :                         DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
     857           0 :                         expired = true;
     858       15415 :                 } else if (maj_stat != GSS_S_COMPLETE) {
     859           0 :                         *error_string = talloc_asprintf(cred, "inquiry of credential lifetime via GSSAPI gss_inquire_cred failed: %s\n",
     860             :                                                         gssapi_error_string(cred, maj_stat, min_stat, NULL));
     861       15415 :                         return EINVAL;
     862             :                 }
     863       15415 :                 if (expired) {
     864           0 :                         cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
     865             :                 } else {
     866       15415 :                         DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
     867             :                                   cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
     868             : 
     869       15415 :                         *_gcc = cred->client_gss_creds;
     870       15415 :                         return 0;
     871             :                 }
     872             :         }
     873             : 
     874       16835 :         ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
     875             :                                          &ccache, error_string);
     876       16835 :         if (ret) {
     877        1114 :                 if (cli_credentials_get_kerberos_state(cred) == CRED_USE_KERBEROS_REQUIRED) {
     878         175 :                         DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
     879             :                 } else {
     880         939 :                         DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
     881             :                 }
     882        1114 :                 return ret;
     883             :         }
     884             : 
     885       15721 :         gcc = talloc(cred, struct gssapi_creds_container);
     886       15721 :         if (!gcc) {
     887           0 :                 (*error_string) = error_message(ENOMEM);
     888           0 :                 return ENOMEM;
     889             :         }
     890             : 
     891       16306 :         maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
     892       15721 :                                             ccache->ccache, NULL, NULL,
     893             :                                             &gcc->creds);
     894       15721 :         if ((maj_stat == GSS_S_FAILURE) &&
     895           0 :             (min_stat == (OM_uint32)KRB5_CC_END ||
     896           0 :              min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
     897           0 :              min_stat == (OM_uint32)KRB5_FCC_NOFILE))
     898             :         {
     899             :                 /* This CCACHE is no good.  Ensure we don't use it again */
     900           0 :                 cli_credentials_unconditionally_invalidate_ccache(cred);
     901             : 
     902             :                 /* Now try again to get a ccache */
     903           0 :                 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
     904             :                                                  &ccache, error_string);
     905           0 :                 if (ret) {
     906           0 :                         DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
     907           0 :                         return ret;
     908             :                 }
     909             : 
     910           0 :                 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
     911           0 :                                                     ccache->ccache, NULL, NULL,
     912             :                                                     &gcc->creds);
     913             : 
     914             :         }
     915             : 
     916       15721 :         if (maj_stat) {
     917           0 :                 talloc_free(gcc);
     918           0 :                 if (min_stat) {
     919           0 :                         ret = min_stat;
     920             :                 } else {
     921           0 :                         ret = EINVAL;
     922             :                 }
     923           0 :                 (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
     924           0 :                 return ret;
     925             :         }
     926             : 
     927             : 
     928             :         /*
     929             :          * transfer the enctypes from the smb_krb5_context to the gssapi layer
     930             :          *
     931             :          * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
     932             :          * to configure the enctypes via the krb5.conf.
     933             :          *
     934             :          * And the gss_init_sec_context() creates it's own krb5_context and
     935             :          * the TGS-REQ had all enctypes in it and only the ones configured
     936             :          * and used for the AS-REQ, so it wasn't possible to disable the usage
     937             :          * of AES keys.
     938             :          */
     939       15721 :         min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
     940             :                                                &etypes);
     941       15721 :         if (min_stat == 0) {
     942             :                 OM_uint32 num_ktypes;
     943             : 
     944      111288 :                 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
     945             : 
     946       15721 :                 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
     947             :                                                            num_ktypes,
     948             :                                                            (int32_t *) etypes);
     949       15721 :                 krb5_free_enctypes(ccache->smb_krb5_context->krb5_context,
     950             :                                    etypes);
     951       15721 :                 if (maj_stat) {
     952           0 :                         talloc_free(gcc);
     953           0 :                         if (min_stat) {
     954           0 :                                 ret = min_stat;
     955             :                         } else {
     956           0 :                                 ret = EINVAL;
     957             :                         }
     958           0 :                         (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
     959           0 :                         return ret;
     960             :                 }
     961             :         }
     962             : 
     963             : #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
     964             :         /*
     965             :          * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
     966             :          *
     967             :          * This allows us to disable SIGN and SEAL on a TLS connection with
     968             :          * GSS-SPNENO. For example ldaps:// connections.
     969             :          *
     970             :          * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
     971             :          * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
     972             :          */
     973       15721 :         maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
     974             :                                        oid,
     975             :                                        &empty_buffer);
     976       15721 :         if (maj_stat) {
     977           0 :                 talloc_free(gcc);
     978           0 :                 if (min_stat) {
     979           0 :                         ret = min_stat;
     980             :                 } else {
     981           0 :                         ret = EINVAL;
     982             :                 }
     983           0 :                 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
     984           0 :                 return ret;
     985             :         }
     986             : #endif
     987       15721 :         cred->client_gss_creds_obtained = cred->ccache_obtained;
     988       15721 :         talloc_set_destructor(gcc, free_gssapi_creds);
     989       15721 :         cred->client_gss_creds = gcc;
     990       15721 :         *_gcc = gcc;
     991       15721 :         return 0;
     992             : }
     993             : 
     994             : /**
     995             :    Set a gssapi cred_id_t into the credentials system. (Client case)
     996             : 
     997             :    This grabs the credentials both 'intact' and getting the krb5
     998             :    ccache out of it.  This routine can be generalised in future for
     999             :    the case where we deal with GSSAPI mechs other than krb5.
    1000             : 
    1001             :    On success, the caller must not free gssapi_cred, as it now belongs
    1002             :    to the credentials system.
    1003             : */
    1004             : 
    1005       30793 :  int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
    1006             :                                           struct loadparm_context *lp_ctx,
    1007             :                                           gss_cred_id_t gssapi_cred,
    1008             :                                           enum credentials_obtained obtained,
    1009             :                                           const char **error_string)
    1010             : {
    1011         881 :         int ret;
    1012         881 :         OM_uint32 maj_stat, min_stat;
    1013       30793 :         struct ccache_container *ccc = NULL;
    1014       30793 :         struct gssapi_creds_container *gcc = NULL;
    1015       30793 :         if (cred->client_gss_creds_obtained > obtained) {
    1016           0 :                 return 0;
    1017             :         }
    1018             : 
    1019       30793 :         gcc = talloc(cred, struct gssapi_creds_container);
    1020       30793 :         if (!gcc) {
    1021           0 :                 (*error_string) = error_message(ENOMEM);
    1022           0 :                 return ENOMEM;
    1023             :         }
    1024             : 
    1025       30793 :         ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
    1026       30793 :         if (ret != 0) {
    1027           0 :                 return ret;
    1028             :         }
    1029             : 
    1030       30793 :         maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
    1031             :                                             gssapi_cred,
    1032             :                                             ccc);
    1033       30793 :         if (maj_stat) {
    1034           0 :                 if (min_stat) {
    1035           0 :                         ret = min_stat;
    1036             :                 } else {
    1037           0 :                         ret = EINVAL;
    1038             :                 }
    1039           0 :                 if (ret) {
    1040           0 :                         (*error_string) = error_message(ENOMEM);
    1041             :                 }
    1042             :         }
    1043             : 
    1044       30793 :         if (ret == 0) {
    1045       30793 :                 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
    1046             :         }
    1047       30793 :         cred->ccache = ccc;
    1048       30793 :         cred->ccache_obtained = obtained;
    1049       30793 :         if (ret == 0) {
    1050       30793 :                 gcc->creds = gssapi_cred;
    1051       30793 :                 talloc_set_destructor(gcc, free_gssapi_creds);
    1052             : 
    1053             :                 /* set the client_gss_creds_obtained here, as it just
    1054             :                    got set to UNINITIALISED by the calls above */
    1055       30793 :                 cred->client_gss_creds_obtained = obtained;
    1056       30793 :                 cred->client_gss_creds = gcc;
    1057             :         }
    1058       29912 :         return ret;
    1059             : }
    1060             : 
    1061         586 : static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
    1062             : {
    1063          77 :         krb5_error_code ret;
    1064         586 :         const struct ccache_container *old_ccc = NULL;
    1065          77 :         enum credentials_obtained old_obtained;
    1066         586 :         struct ccache_container *ccc = NULL;
    1067         586 :         char *ccache_name = NULL;
    1068          77 :         krb5_principal princ;
    1069             : 
    1070         586 :         old_obtained = cred->ccache_obtained;
    1071         586 :         old_ccc = cred->ccache;
    1072         586 :         if (old_ccc == NULL) {
    1073         235 :                 return 0;
    1074             :         }
    1075             : 
    1076         310 :         cred->ccache = NULL;
    1077         310 :         cred->ccache_obtained = CRED_UNINITIALISED;
    1078         310 :         cred->client_gss_creds = NULL;
    1079         310 :         cred->client_gss_creds_obtained = CRED_UNINITIALISED;
    1080             : 
    1081         346 :         ret = krb5_cc_get_principal(
    1082         310 :                 old_ccc->smb_krb5_context->krb5_context,
    1083         310 :                 old_ccc->ccache,
    1084             :                 &princ);
    1085         310 :         if (ret != 0) {
    1086             :                 /*
    1087             :                  * This is an empty ccache. No point in copying anything.
    1088             :                  */
    1089           0 :                 return 0;
    1090             :         }
    1091         310 :         krb5_free_principal(old_ccc->smb_krb5_context->krb5_context, princ);
    1092             : 
    1093         310 :         ccc = talloc(cred, struct ccache_container);
    1094         310 :         if (ccc == NULL) {
    1095           0 :                 return ENOMEM;
    1096             :         }
    1097         310 :         *ccc = *old_ccc;
    1098         310 :         ccc->ccache = NULL;
    1099             : 
    1100         310 :         ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
    1101             : 
    1102         310 :         ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
    1103             :                               ccache_name, &ccc->ccache);
    1104         310 :         if (ret != 0) {
    1105           0 :                 TALLOC_FREE(ccc);
    1106           0 :                 return ret;
    1107             :         }
    1108             : 
    1109         310 :         talloc_set_destructor(ccc, free_mccache);
    1110             : 
    1111         310 :         TALLOC_FREE(ccache_name);
    1112             : 
    1113         346 :         ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
    1114         310 :                                      old_ccc->ccache, ccc->ccache);
    1115         310 :         if (ret != 0) {
    1116           0 :                 TALLOC_FREE(ccc);
    1117           0 :                 return ret;
    1118             :         }
    1119             : 
    1120         310 :         cred->ccache = ccc;
    1121         310 :         cred->ccache_obtained = old_obtained;
    1122         310 :         return ret;
    1123             : }
    1124             : 
    1125         586 : _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
    1126             :                                                 struct cli_credentials *src)
    1127             : {
    1128          77 :         struct cli_credentials *dst;
    1129          77 :         int ret;
    1130             : 
    1131         586 :         dst = talloc(mem_ctx, struct cli_credentials);
    1132         586 :         if (dst == NULL) {
    1133           0 :                 return NULL;
    1134             :         }
    1135             : 
    1136         586 :         *dst = *src;
    1137             : 
    1138         586 :         ret = cli_credentials_shallow_ccache(dst);
    1139         586 :         if (ret != 0) {
    1140           0 :                 TALLOC_FREE(dst);
    1141           0 :                 return NULL;
    1142             :         }
    1143             : 
    1144         509 :         return dst;
    1145             : }
    1146             : 
    1147             : /* Get the keytab (actually, a container containing the krb5_keytab)
    1148             :  * attached to this context.  If this hasn't been done or set before,
    1149             :  * it will be generated from the password.
    1150             :  */
    1151       46901 : _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
    1152             :                                         struct loadparm_context *lp_ctx,
    1153             :                                         struct keytab_container **_ktc)
    1154             : {
    1155        2157 :         krb5_error_code ret;
    1156        2157 :         struct keytab_container *ktc;
    1157        2157 :         struct smb_krb5_context *smb_krb5_context;
    1158        2157 :         const char *keytab_name;
    1159        2157 :         krb5_keytab keytab;
    1160        2157 :         TALLOC_CTX *mem_ctx;
    1161       46901 :         const char *username = cli_credentials_get_username(cred);
    1162       46901 :         const char *upn = NULL;
    1163       46901 :         const char *realm = cli_credentials_get_realm(cred);
    1164       46901 :         char *salt_principal = NULL;
    1165       46901 :         uint32_t uac_flags = 0;
    1166             : 
    1167       46901 :         if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
    1168             :                                           cred->username_obtained))) {
    1169       46820 :                 *_ktc = cred->keytab;
    1170       46820 :                 return 0;
    1171             :         }
    1172             : 
    1173          81 :         if (cli_credentials_is_anonymous(cred)) {
    1174           0 :                 return EINVAL;
    1175             :         }
    1176             : 
    1177          81 :         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
    1178             :                                                &smb_krb5_context);
    1179          81 :         if (ret) {
    1180           0 :                 return ret;
    1181             :         }
    1182             : 
    1183          81 :         mem_ctx = talloc_new(cred);
    1184          81 :         if (!mem_ctx) {
    1185           0 :                 return ENOMEM;
    1186             :         }
    1187             : 
    1188          81 :         switch (cred->secure_channel_type) {
    1189          49 :         case SEC_CHAN_WKSTA:
    1190             :         case SEC_CHAN_RODC:
    1191          49 :                 uac_flags = UF_WORKSTATION_TRUST_ACCOUNT;
    1192          49 :                 break;
    1193          30 :         case SEC_CHAN_BDC:
    1194          30 :                 uac_flags = UF_SERVER_TRUST_ACCOUNT;
    1195          30 :                 break;
    1196           0 :         case SEC_CHAN_DOMAIN:
    1197             :         case SEC_CHAN_DNS_DOMAIN:
    1198           0 :                 uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT;
    1199           0 :                 break;
    1200           2 :         default:
    1201           2 :                 upn = cli_credentials_get_principal(cred, mem_ctx);
    1202           2 :                 if (upn == NULL) {
    1203           0 :                         TALLOC_FREE(mem_ctx);
    1204           0 :                         return ENOMEM;
    1205             :                 }
    1206           2 :                 uac_flags = UF_NORMAL_ACCOUNT;
    1207           2 :                 break;
    1208             :         }
    1209             : 
    1210          81 :         ret = smb_krb5_salt_principal_str(realm,
    1211             :                                           username, /* sAMAccountName */
    1212             :                                           upn, /* userPrincipalName */
    1213             :                                           uac_flags,
    1214             :                                           mem_ctx,
    1215             :                                           &salt_principal);
    1216          81 :         if (ret) {
    1217           0 :                 talloc_free(mem_ctx);
    1218           0 :                 return ret;
    1219             :         }
    1220             : 
    1221          81 :         ret = smb_krb5_create_memory_keytab(mem_ctx,
    1222          81 :                                             smb_krb5_context->krb5_context,
    1223             :                                             cli_credentials_get_password(cred),
    1224             :                                             username,
    1225             :                                             realm,
    1226             :                                             salt_principal,
    1227             :                                             cli_credentials_get_kvno(cred),
    1228             :                                             &keytab,
    1229             :                                             &keytab_name);
    1230          81 :         if (ret) {
    1231           0 :                 talloc_free(mem_ctx);
    1232           0 :                 return ret;
    1233             :         }
    1234             : 
    1235          81 :         ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
    1236             :                                             keytab, keytab_name, &ktc);
    1237          81 :         if (ret) {
    1238           0 :                 talloc_free(mem_ctx);
    1239           0 :                 return ret;
    1240             :         }
    1241             : 
    1242          81 :         cred->keytab_obtained = (MAX(cred->principal_obtained,
    1243             :                                      cred->username_obtained));
    1244             : 
    1245             :         /* We make this keytab up based on a password.  Therefore
    1246             :          * match-by-key is acceptable, we can't match on the wrong
    1247             :          * principal */
    1248          81 :         ktc->password_based = true;
    1249             : 
    1250          81 :         talloc_steal(cred, ktc);
    1251          81 :         cred->keytab = ktc;
    1252          81 :         *_ktc = cred->keytab;
    1253          81 :         talloc_free(mem_ctx);
    1254          81 :         return ret;
    1255             : }
    1256             : 
    1257             : /* Given the name of a keytab (presumably in the format
    1258             :  * FILE:/etc/krb5.keytab), open it and attach it */
    1259             : 
    1260       66490 : _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
    1261             :                                              struct loadparm_context *lp_ctx,
    1262             :                                              const char *keytab_name,
    1263             :                                              enum credentials_obtained obtained)
    1264             : {
    1265        2655 :         krb5_error_code ret;
    1266        2655 :         struct keytab_container *ktc;
    1267        2655 :         struct smb_krb5_context *smb_krb5_context;
    1268        2655 :         TALLOC_CTX *mem_ctx;
    1269             : 
    1270       66490 :         if (cred->keytab_obtained >= obtained) {
    1271           0 :                 return 0;
    1272             :         }
    1273             : 
    1274       66490 :         ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
    1275       66490 :         if (ret) {
    1276           0 :                 return ret;
    1277             :         }
    1278             : 
    1279       66490 :         mem_ctx = talloc_new(cred);
    1280       66490 :         if (!mem_ctx) {
    1281           0 :                 return ENOMEM;
    1282             :         }
    1283             : 
    1284       66490 :         ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
    1285             :                                             NULL, keytab_name, &ktc);
    1286       66490 :         if (ret) {
    1287           0 :                 return ret;
    1288             :         }
    1289             : 
    1290       66490 :         cred->keytab_obtained = obtained;
    1291             : 
    1292       66490 :         talloc_steal(cred, ktc);
    1293       66490 :         cred->keytab = ktc;
    1294       66490 :         talloc_free(mem_ctx);
    1295             : 
    1296       66490 :         return ret;
    1297             : }
    1298             : 
    1299             : /* Get server gss credentials (in gsskrb5, this means the keytab) */
    1300             : 
    1301       48399 : _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
    1302             :                                                   struct loadparm_context *lp_ctx,
    1303             :                                                   struct gssapi_creds_container **_gcc)
    1304             : {
    1305       48399 :         int ret = 0;
    1306        2157 :         OM_uint32 maj_stat, min_stat;
    1307        2157 :         struct gssapi_creds_container *gcc;
    1308        2157 :         struct keytab_container *ktc;
    1309        2157 :         struct smb_krb5_context *smb_krb5_context;
    1310        2157 :         TALLOC_CTX *mem_ctx;
    1311        2157 :         krb5_principal princ;
    1312        2157 :         const char *error_string;
    1313        2157 :         enum credentials_obtained obtained;
    1314             : 
    1315       48399 :         mem_ctx = talloc_new(cred);
    1316       48399 :         if (!mem_ctx) {
    1317           0 :                 return ENOMEM;
    1318             :         }
    1319             : 
    1320       48399 :         ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
    1321       48399 :         if (ret) {
    1322           0 :                 return ret;
    1323             :         }
    1324             : 
    1325       48399 :         ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
    1326       48399 :         if (ret) {
    1327           0 :                 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
    1328             :                          error_string));
    1329           0 :                 talloc_free(mem_ctx);
    1330           0 :                 return ret;
    1331             :         }
    1332             : 
    1333       48399 :         if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
    1334        2772 :                 talloc_free(mem_ctx);
    1335        2772 :                 *_gcc = cred->server_gss_creds;
    1336        2772 :                 return 0;
    1337             :         }
    1338             : 
    1339       45627 :         ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
    1340       45627 :         if (ret) {
    1341           0 :                 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
    1342           0 :                 return ret;
    1343             :         }
    1344             : 
    1345       45627 :         gcc = talloc(cred, struct gssapi_creds_container);
    1346       45627 :         if (!gcc) {
    1347           0 :                 talloc_free(mem_ctx);
    1348           0 :                 return ENOMEM;
    1349             :         }
    1350             : 
    1351       45627 :         if (ktc->password_based || obtained < CRED_SPECIFIED) {
    1352             :                 /*
    1353             :                  * This creates a GSSAPI cred_id_t for match-by-key with only
    1354             :                  * the keytab set
    1355             :                  */
    1356          81 :                 princ = NULL;
    1357             :         }
    1358       47784 :         maj_stat = smb_gss_krb5_import_cred(&min_stat,
    1359       45627 :                                             smb_krb5_context->krb5_context,
    1360             :                                             NULL, princ,
    1361       43470 :                                             ktc->keytab,
    1362             :                                             &gcc->creds);
    1363       45627 :         if (maj_stat) {
    1364           0 :                 if (min_stat) {
    1365           0 :                         ret = min_stat;
    1366             :                 } else {
    1367           0 :                         ret = EINVAL;
    1368             :                 }
    1369             :         }
    1370       45627 :         if (ret == 0) {
    1371       45627 :                 cred->server_gss_creds_obtained = cred->keytab_obtained;
    1372       45627 :                 talloc_set_destructor(gcc, free_gssapi_creds);
    1373       45627 :                 cred->server_gss_creds = gcc;
    1374       45627 :                 *_gcc = gcc;
    1375             :         }
    1376       45627 :         talloc_free(mem_ctx);
    1377       45627 :         return ret;
    1378             : }
    1379             : 
    1380             : /**
    1381             :  * Set Kerberos KVNO
    1382             :  */
    1383             : 
    1384       67040 : _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
    1385             :                               int kvno)
    1386             : {
    1387       67040 :         cred->kvno = kvno;
    1388       67040 : }
    1389             : 
    1390             : /**
    1391             :  * Return Kerberos KVNO
    1392             :  */
    1393             : 
    1394          87 : _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
    1395             : {
    1396          87 :         return cred->kvno;
    1397             : }
    1398             : 
    1399             : 
    1400           0 : const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
    1401             : {
    1402           0 :         return cred->salt_principal;
    1403             : }
    1404             : 
    1405       66368 : _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
    1406             : {
    1407       66368 :         talloc_free(cred->salt_principal);
    1408       66368 :         cred->salt_principal = talloc_strdup(cred, principal);
    1409       66368 : }
    1410             : 
    1411             : /* The 'impersonate_principal' is used to allow one Kerberos principal
    1412             :  * (and it's associated keytab etc) to impersonate another.  The
    1413             :  * ability to do this is controlled by the KDC, but it is generally
    1414             :  * permitted to impersonate anyone to yourself.  This allows any
    1415             :  * member of the domain to get the groups of a user.  This is also
    1416             :  * known as S4U2Self */
    1417             : 
    1418       45829 : _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
    1419             : {
    1420       45829 :         return cred->impersonate_principal;
    1421             : }
    1422             : 
    1423             : /*
    1424             :  * The 'self_service' is the service principal that
    1425             :  * represents the same object (by its objectSid)
    1426             :  * as the client principal (typically our machine account).
    1427             :  * When trying to impersonate 'impersonate_principal' with
    1428             :  * S4U2Self.
    1429             :  */
    1430       14783 : _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
    1431             : {
    1432       14783 :         return cred->self_service;
    1433             : }
    1434             : 
    1435          55 : _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
    1436             :                                                         const char *principal,
    1437             :                                                         const char *self_service)
    1438             : {
    1439          55 :         talloc_free(cred->impersonate_principal);
    1440          55 :         cred->impersonate_principal = talloc_strdup(cred, principal);
    1441          55 :         talloc_free(cred->self_service);
    1442          55 :         cred->self_service = talloc_strdup(cred, self_service);
    1443          55 :         cli_credentials_set_kerberos_state(cred,
    1444             :                                            CRED_USE_KERBEROS_REQUIRED,
    1445             :                                            CRED_SPECIFIED);
    1446          55 : }
    1447             : 
    1448             : /*
    1449             :  * when impersonating for S4U2proxy we need to set the target principal.
    1450             :  * Similarly, we may only be authorized to do general impersonation to
    1451             :  * some particular services.
    1452             :  *
    1453             :  * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
    1454             :  *
    1455             :  * NULL means that tickets will be obtained for the krbtgt service.
    1456             : */
    1457             : 
    1458       14783 : const char *cli_credentials_get_target_service(struct cli_credentials *cred)
    1459             : {
    1460       14783 :         return cred->target_service;
    1461             : }
    1462             : 
    1463          35 : _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
    1464             : {
    1465          35 :         talloc_free(cred->target_service);
    1466          35 :         cred->target_service = talloc_strdup(cred, target_service);
    1467          35 : }
    1468             : 
    1469         112 : _PUBLIC_ int cli_credentials_get_aes256_key(struct cli_credentials *cred,
    1470             :                                             TALLOC_CTX *mem_ctx,
    1471             :                                             struct loadparm_context *lp_ctx,
    1472             :                                             const char *salt,
    1473             :                                             DATA_BLOB *aes_256)
    1474             : {
    1475         112 :         struct smb_krb5_context *smb_krb5_context = NULL;
    1476           0 :         krb5_error_code krb5_ret;
    1477           0 :         int ret;
    1478         112 :         const char *password = NULL;
    1479           0 :         krb5_data cleartext_data;
    1480         112 :         krb5_data salt_data = {
    1481             :                 .length = 0,
    1482             :         };
    1483           0 :         krb5_keyblock key;
    1484             : 
    1485         112 :         if (cred->password_will_be_nt_hash) {
    1486           0 :                 DEBUG(1,("cli_credentials_get_aes256_key: cannot generate AES256 key using NT hash\n"));
    1487           0 :                 return EINVAL;
    1488             :         }
    1489             : 
    1490         112 :         password = cli_credentials_get_password(cred);
    1491         112 :         if (password == NULL) {
    1492           0 :                 return EINVAL;
    1493             :         }
    1494             : 
    1495         112 :         cleartext_data.data = discard_const_p(char, password);
    1496         112 :         cleartext_data.length = strlen(password);
    1497             : 
    1498         112 :         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
    1499             :                                                &smb_krb5_context);
    1500         112 :         if (ret != 0) {
    1501           0 :                 return ret;
    1502             :         }
    1503             : 
    1504         112 :         salt_data.data = discard_const_p(char, salt);
    1505         112 :         salt_data.length = strlen(salt);
    1506             : 
    1507             :         /*
    1508             :          * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
    1509             :          * the salt and the cleartext password
    1510             :          */
    1511         112 :         krb5_ret = smb_krb5_create_key_from_string(smb_krb5_context->krb5_context,
    1512             :                                                    NULL,
    1513             :                                                    &salt_data,
    1514             :                                                    &cleartext_data,
    1515             :                                                    ENCTYPE_AES256_CTS_HMAC_SHA1_96,
    1516             :                                                    &key);
    1517         112 :         if (krb5_ret != 0) {
    1518           0 :                 DEBUG(1,("cli_credentials_get_aes256_key: "
    1519             :                          "generation of a aes256-cts-hmac-sha1-96 key failed: %s\n",
    1520             :                          smb_get_krb5_error_message(smb_krb5_context->krb5_context,
    1521             :                                                     krb5_ret, mem_ctx)));
    1522           0 :                 return EINVAL;
    1523             :         }
    1524         112 :         *aes_256 = data_blob_talloc(mem_ctx,
    1525             :                                     KRB5_KEY_DATA(&key),
    1526             :                                     KRB5_KEY_LENGTH(&key));
    1527         112 :         krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &key);
    1528         112 :         if (aes_256->data == NULL) {
    1529           0 :                 return ENOMEM;
    1530             :         }
    1531         112 :         talloc_keep_secret(aes_256->data);
    1532             : 
    1533         112 :         return 0;
    1534             : }

Generated by: LCOV version 1.14