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

Generated by: LCOV version 1.13