LCOV - code coverage report
Current view: top level - source3/winbindd - winbindd_ccache_access.c (source / functions) Hit Total Coverage
Test: coverage report for fix-15632 9995c5c2 Lines: 120 183 65.6 %
Date: 2024-04-13 12:30:31 Functions: 5 5 100.0 %

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

Generated by: LCOV version 1.14