LCOV - code coverage report
Current view: top level - source3/winbindd - winbindd_ccache_access.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 100 150 66.7 %
Date: 2024-06-13 04:01:37 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    Winbind daemon - cached credentials funcions
       5             : 
       6             :    Copyright (C) Robert O'Callahan 2006
       7             :    Copyright (C) Jeremy Allison 2006 (minor fixes to fit into Samba and
       8             :                                       protect against integer wrap).
       9             :    Copyright (C) Andrew Bartlett 2011
      10             : 
      11             :    This program is free software; you can redistribute it and/or modify
      12             :    it under the terms of the GNU General Public License as published by
      13             :    the Free Software Foundation; either version 3 of the License, or
      14             :    (at your option) any later version.
      15             : 
      16             :    This program is distributed in the hope that it will be useful,
      17             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      18             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      19             :    GNU General Public License for more details.
      20             : 
      21             :    You should have received a copy of the GNU General Public License
      22             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      23             : */
      24             : 
      25             : #include "includes.h"
      26             : #include "winbindd.h"
      27             : #include "auth/gensec/gensec.h"
      28             : #include "auth_generic.h"
      29             : 
      30             : #undef DBGC_CLASS
      31             : #define DBGC_CLASS DBGC_WINBIND
      32             : 
      33          16 : static bool client_can_access_ccache_entry(uid_t client_uid,
      34             :                                         struct WINBINDD_MEMORY_CREDS *entry)
      35             : {
      36          16 :         if (client_uid == entry->uid || client_uid == 0) {
      37          16 :                 DEBUG(10, ("Access granted to uid %u\n", (unsigned int)client_uid));
      38          16 :                 return True;
      39             :         }
      40             : 
      41           0 :         DEBUG(1, ("Access denied to uid %u (expected %u)\n",
      42             :                 (unsigned int)client_uid, (unsigned int)entry->uid));
      43           0 :         return False;
      44             : }
      45             : 
      46          12 : static NTSTATUS do_ntlm_auth_with_stored_pw(const char *namespace,
      47             :                                             const char *domain,
      48             :                                             const char *username,
      49             :                                             const char *password,
      50             :                                             const DATA_BLOB initial_msg,
      51             :                                             const DATA_BLOB challenge_msg,
      52             :                                             TALLOC_CTX *mem_ctx,
      53             :                                             DATA_BLOB *auth_msg,
      54             :                                             uint8_t session_key[16],
      55             :                                             uint8_t *new_spnego)
      56             : {
      57             :         NTSTATUS status;
      58          12 :         struct auth_generic_state *auth_generic_state = NULL;
      59             :         DATA_BLOB reply, session_key_blob;
      60             : 
      61          12 :         status = auth_generic_client_prepare(mem_ctx, &auth_generic_state);
      62             : 
      63          12 :         if (!NT_STATUS_IS_OK(status)) {
      64           0 :                 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
      65             :                         nt_errstr(status)));
      66           0 :                 goto done;
      67             :         }
      68             : 
      69          12 :         status = auth_generic_set_username(auth_generic_state, username);
      70             : 
      71          12 :         if (!NT_STATUS_IS_OK(status)) {
      72           0 :                 DEBUG(1, ("Could not set username: %s\n",
      73             :                         nt_errstr(status)));
      74           0 :                 goto done;
      75             :         }
      76             : 
      77          12 :         status = auth_generic_set_domain(auth_generic_state, domain);
      78             : 
      79          12 :         if (!NT_STATUS_IS_OK(status)) {
      80           0 :                 DEBUG(1, ("Could not set domain: %s\n",
      81             :                         nt_errstr(status)));
      82           0 :                 goto done;
      83             :         }
      84             : 
      85          12 :         status = auth_generic_set_password(auth_generic_state, password);
      86             : 
      87          12 :         if (!NT_STATUS_IS_OK(status)) {
      88           0 :                 DEBUG(1, ("Could not set password: %s\n",
      89             :                         nt_errstr(status)));
      90           0 :                 goto done;
      91             :         }
      92             : 
      93          12 :         if (initial_msg.length == 0) {
      94           0 :                 gensec_want_feature(auth_generic_state->gensec_security,
      95             :                                     GENSEC_FEATURE_SESSION_KEY);
      96             :         }
      97             : 
      98          12 :         status = auth_generic_client_start_by_name(auth_generic_state,
      99             :                                                    "ntlmssp_resume_ccache");
     100          12 :         if (!NT_STATUS_IS_OK(status)) {
     101           0 :                 DEBUG(1, ("Could not start NTLMSSP resume mech: %s\n",
     102             :                         nt_errstr(status)));
     103           0 :                 goto done;
     104             :         }
     105             : 
     106             :         /*
     107             :          * We inject the initial NEGOTIATE message our caller used
     108             :          * in order to get the state machine into the correct position.
     109             :          */
     110          12 :         reply = data_blob_null;
     111          12 :         status = gensec_update(auth_generic_state->gensec_security,
     112             :                                talloc_tos(), initial_msg, &reply);
     113          12 :         data_blob_free(&reply);
     114             : 
     115          12 :         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
     116           0 :                 DEBUG(1, ("Failed to create initial message! [%s]\n",
     117             :                         nt_errstr(status)));
     118           0 :                 goto done;
     119             :         }
     120             : 
     121             :         /* Now we are ready to handle the server's actual response. */
     122          12 :         status = gensec_update(auth_generic_state->gensec_security,
     123             :                                mem_ctx, challenge_msg, &reply);
     124          12 :         if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
     125           0 :                 DEBUG(1, ("We didn't get a response to the challenge! [%s]\n",
     126             :                         nt_errstr(status)));
     127           0 :                 data_blob_free(&reply);
     128           0 :                 goto done;
     129             :         }
     130             : 
     131          12 :         status = gensec_session_key(auth_generic_state->gensec_security,
     132             :                                     talloc_tos(), &session_key_blob);
     133          12 :         if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
     134           0 :                 DEBUG(1, ("We didn't get the session key we requested! [%s]\n",
     135             :                         nt_errstr(status)));
     136           0 :                 data_blob_free(&reply);
     137           0 :                 goto done;
     138             :         }
     139             : 
     140          12 :         if (session_key_blob.length != 16) {
     141           0 :                 DEBUG(1, ("invalid session key length %d\n",
     142             :                           (int)session_key_blob.length));
     143           0 :                 data_blob_free(&reply);
     144           0 :                 goto done;
     145             :         }
     146          12 :         memcpy(session_key, session_key_blob.data, 16);
     147          12 :         data_blob_free(&session_key_blob);
     148          12 :         *auth_msg = reply;
     149          12 :         *new_spnego = gensec_have_feature(auth_generic_state->gensec_security,
     150             :                                           GENSEC_FEATURE_NEW_SPNEGO);
     151          12 :         status = NT_STATUS_OK;
     152             : 
     153          12 : done:
     154          12 :         TALLOC_FREE(auth_generic_state);
     155          12 :         return status;
     156             : }
     157             : 
     158          40 : static bool check_client_uid(struct winbindd_cli_state *state, uid_t uid)
     159             : {
     160             :         int ret;
     161             :         uid_t ret_uid;
     162             :         gid_t ret_gid;
     163             : 
     164          40 :         ret_uid = (uid_t)-1;
     165             : 
     166          40 :         ret = getpeereid(state->sock, &ret_uid, &ret_gid);
     167          40 :         if (ret != 0) {
     168           0 :                 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
     169             :                         "denying access\n", strerror(errno)));
     170           0 :                 return False;
     171             :         }
     172             : 
     173          40 :         if (uid != ret_uid && ret_uid != sec_initial_uid()) {
     174           0 :                 DEBUG(1, ("check_client_uid: Client lied about its uid: said %u, "
     175             :                         "actually was %u; denying access\n",
     176             :                         (unsigned int)uid, (unsigned int)ret_uid));
     177           0 :                 return False;
     178             :         }
     179             : 
     180          40 :         return True;
     181             : }
     182             : 
     183          16 : bool winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state)
     184             : {
     185             :         struct winbindd_domain *domain;
     186             :         fstring name_namespace, name_domain, name_user;
     187          16 :         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
     188             :         struct WINBINDD_MEMORY_CREDS *entry;
     189             :         DATA_BLOB initial, challenge, auth;
     190             :         uint32_t initial_blob_len, challenge_blob_len, extra_len;
     191             :         bool ok;
     192             : 
     193             :         /* Ensure null termination */
     194          16 :         state->request->data.ccache_ntlm_auth.user[
     195          16 :                         sizeof(state->request->data.ccache_ntlm_auth.user)-1]='\0';
     196             : 
     197          16 :         DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state->pid,
     198             :                 state->request->data.ccache_ntlm_auth.user));
     199             : 
     200             :         /* Parse domain and username */
     201             : 
     202          16 :         ok = canonicalize_username(state->request->data.ccache_ntlm_auth.user,
     203             :                                    name_namespace,
     204             :                                    name_domain,
     205             :                                    name_user);
     206          16 :         if (!ok) {
     207           0 :                 DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n",
     208             :                         state->request->data.ccache_ntlm_auth.user));
     209           0 :                 return false;
     210             :         }
     211             : 
     212          16 :         domain = find_auth_domain(state->request->flags, name_domain);
     213             : 
     214          16 :         if (domain == NULL) {
     215           0 :                 DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n",
     216             :                         name_domain));
     217           0 :                 return false;
     218             :         }
     219             : 
     220          16 :         if (!check_client_uid(state, state->request->data.ccache_ntlm_auth.uid)) {
     221           0 :                 return false;
     222             :         }
     223             : 
     224             :         /* validate blob lengths */
     225          16 :         initial_blob_len = state->request->data.ccache_ntlm_auth.initial_blob_len;
     226          16 :         challenge_blob_len = state->request->data.ccache_ntlm_auth.challenge_blob_len;
     227          16 :         extra_len = state->request->extra_len;
     228             : 
     229          26 :         if (initial_blob_len > extra_len || challenge_blob_len > extra_len ||
     230          26 :                 initial_blob_len + challenge_blob_len > extra_len ||
     231          26 :                 initial_blob_len + challenge_blob_len < initial_blob_len ||
     232          16 :                 initial_blob_len + challenge_blob_len < challenge_blob_len) {
     233             : 
     234           0 :                 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
     235             :                         "or wrap. Buffer [%d+%d > %d]\n",
     236             :                         initial_blob_len,
     237             :                         challenge_blob_len,
     238             :                         extra_len));
     239           0 :                 goto process_result;
     240             :         }
     241             : 
     242             :         /* Parse domain and username */
     243          16 :         ok = parse_domain_user(state->request->data.ccache_ntlm_auth.user,
     244             :                                name_namespace,
     245             :                                name_domain,
     246             :                                name_user);
     247          16 :         if (!ok) {
     248           0 :                 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse "
     249             :                         "domain and user from name [%s]\n",
     250             :                         state->request->data.ccache_ntlm_auth.user));
     251           0 :                 goto process_result;
     252             :         }
     253             : 
     254          16 :         entry = find_memory_creds_by_name(state->request->data.ccache_ntlm_auth.user);
     255          16 :         if (entry == NULL || entry->nt_hash == NULL || entry->lm_hash == NULL) {
     256           0 :                 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find "
     257             :                         "credentials for user %s\n", 
     258             :                         state->request->data.ccache_ntlm_auth.user));
     259           0 :                 goto process_result;
     260             :         }
     261             : 
     262          16 :         DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username));
     263             : 
     264          16 :         if (!client_can_access_ccache_entry(state->request->data.ccache_ntlm_auth.uid, entry)) {
     265           0 :                 goto process_result;
     266             :         }
     267             : 
     268          16 :         if (initial_blob_len == 0 && challenge_blob_len == 0) {
     269             :                 /* this is just a probe to see if credentials are available. */
     270           4 :                 result = NT_STATUS_OK;
     271           4 :                 state->response->data.ccache_ntlm_auth.auth_blob_len = 0;
     272           4 :                 goto process_result;
     273             :         }
     274             : 
     275          12 :         initial = data_blob_const(state->request->extra_data.data,
     276             :                                   initial_blob_len);
     277          26 :         challenge = data_blob_const(
     278          19 :                 state->request->extra_data.data + initial_blob_len,
     279          12 :                 state->request->data.ccache_ntlm_auth.challenge_blob_len);
     280             : 
     281          31 :         result = do_ntlm_auth_with_stored_pw(
     282             :                         name_namespace,
     283             :                         name_domain,
     284             :                         name_user,
     285          12 :                         entry->pass,
     286             :                         initial,
     287             :                         challenge,
     288             :                         talloc_tos(),
     289             :                         &auth,
     290          12 :                         state->response->data.ccache_ntlm_auth.session_key,
     291          12 :                         &state->response->data.ccache_ntlm_auth.new_spnego);
     292             : 
     293          12 :         if (!NT_STATUS_IS_OK(result)) {
     294           0 :                 goto process_result;
     295             :         }
     296             : 
     297          12 :         state->response->extra_data.data = talloc_memdup(
     298             :                 state->mem_ctx, auth.data, auth.length);
     299          12 :         if (!state->response->extra_data.data) {
     300           0 :                 result = NT_STATUS_NO_MEMORY;
     301           0 :                 goto process_result;
     302             :         }
     303          12 :         state->response->length += auth.length;
     304          12 :         state->response->data.ccache_ntlm_auth.auth_blob_len = auth.length;
     305             : 
     306          12 :         data_blob_free(&auth);
     307             : 
     308          16 :   process_result:
     309          16 :         return NT_STATUS_IS_OK(result);
     310             : }
     311             : 
     312          24 : bool winbindd_ccache_save(struct winbindd_cli_state *state)
     313             : {
     314             :         struct winbindd_domain *domain;
     315             :         fstring name_namespace, name_domain, name_user;
     316             :         NTSTATUS status;
     317             :         bool ok;
     318             : 
     319             :         /* Ensure null termination */
     320          24 :         state->request->data.ccache_save.user[
     321          24 :                 sizeof(state->request->data.ccache_save.user)-1]='\0';
     322          24 :         state->request->data.ccache_save.pass[
     323          24 :                 sizeof(state->request->data.ccache_save.pass)-1]='\0';
     324             : 
     325          24 :         DEBUG(3, ("[%5lu]: save password of user %s\n",
     326             :                   (unsigned long)state->pid,
     327             :                   state->request->data.ccache_save.user));
     328             : 
     329             :         /* Parse domain and username */
     330             : 
     331          24 :         ok = canonicalize_username(state->request->data.ccache_save.user,
     332             :                                    name_namespace,
     333             :                                    name_domain,
     334             :                                    name_user);
     335          24 :         if (!ok) {
     336           0 :                 DEBUG(5,("winbindd_ccache_save: cannot parse domain and user "
     337             :                          "from name [%s]\n",
     338             :                          state->request->data.ccache_save.user));
     339           0 :                 return false;
     340             :         }
     341             : 
     342             :         /*
     343             :          * The domain is checked here only for compatibility
     344             :          * reasons. We used to do the winbindd memory ccache for
     345             :          * ntlm_auth in the domain child. With that code, we had to
     346             :          * make sure that we do have a domain around to send this
     347             :          * to. Now we do the memory cache in the parent winbindd,
     348             :          * where it would not matter if we have a domain or not.
     349             :          */
     350             : 
     351          24 :         domain = find_auth_domain(state->request->flags, name_domain);
     352          24 :         if (domain == NULL) {
     353           0 :                 DEBUG(5, ("winbindd_ccache_save: can't get domain [%s]\n",
     354             :                           name_domain));
     355           0 :                 return false;
     356             :         }
     357             : 
     358          24 :         if (!check_client_uid(state, state->request->data.ccache_save.uid)) {
     359           0 :                 return false;
     360             :         }
     361             : 
     362          37 :         status = winbindd_add_memory_creds(
     363          24 :                 state->request->data.ccache_save.user,
     364          24 :                 state->request->data.ccache_save.uid,
     365          24 :                 state->request->data.ccache_save.pass);
     366             : 
     367          24 :         if (!NT_STATUS_IS_OK(status)) {
     368           0 :                 DEBUG(1, ("winbindd_add_memory_creds failed %s\n",
     369             :                           nt_errstr(status)));
     370           0 :                 return false;
     371             :         }
     372          24 :         return true;
     373             : }

Generated by: LCOV version 1.13