root/source3/winbindd/winbindd_ccache_access.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. client_can_access_ccache_entry
  2. do_ntlm_auth_with_hashes
  3. check_client_uid
  4. winbindd_ccache_ntlm_auth
  5. winbindd_dual_ccache_ntlm_auth

   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    
  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 "winbindd.h"
  26 
  27 #undef DBGC_CLASS
  28 #define DBGC_CLASS DBGC_WINBIND
  29 
  30 static bool client_can_access_ccache_entry(uid_t client_uid,
     /* [<][>][^][v][top][bottom][index][help] */
  31                                         struct WINBINDD_MEMORY_CREDS *entry)
  32 {
  33         if (client_uid == entry->uid || client_uid == 0) {
  34                 DEBUG(10, ("Access granted to uid %u\n", (unsigned int)client_uid));
  35                 return True;
  36         }
  37 
  38         DEBUG(1, ("Access denied to uid %u (expected %u)\n",
  39                 (unsigned int)client_uid, (unsigned int)entry->uid));
  40         return False;
  41 }
  42 
  43 static NTSTATUS do_ntlm_auth_with_hashes(const char *username,
     /* [<][>][^][v][top][bottom][index][help] */
  44                                         const char *domain,
  45                                         const unsigned char lm_hash[LM_HASH_LEN],
  46                                         const unsigned char nt_hash[NT_HASH_LEN],
  47                                         const DATA_BLOB initial_msg,
  48                                         const DATA_BLOB challenge_msg,
  49                                         DATA_BLOB *auth_msg)
  50 {
  51         NTSTATUS status;
  52         NTLMSSP_STATE *ntlmssp_state = NULL;
  53         DATA_BLOB dummy_msg, reply;
  54 
  55         status = ntlmssp_client_start(&ntlmssp_state);
  56 
  57         if (!NT_STATUS_IS_OK(status)) {
  58                 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
  59                         nt_errstr(status)));
  60                 goto done;
  61         }
  62 
  63         status = ntlmssp_set_username(ntlmssp_state, username);
  64 
  65         if (!NT_STATUS_IS_OK(status)) {
  66                 DEBUG(1, ("Could not set username: %s\n",
  67                         nt_errstr(status)));
  68                 goto done;
  69         }
  70 
  71         status = ntlmssp_set_domain(ntlmssp_state, domain);
  72 
  73         if (!NT_STATUS_IS_OK(status)) {
  74                 DEBUG(1, ("Could not set domain: %s\n",
  75                         nt_errstr(status)));
  76                 goto done;
  77         }
  78 
  79         status = ntlmssp_set_hashes(ntlmssp_state, lm_hash, nt_hash);
  80         
  81         if (!NT_STATUS_IS_OK(status)) {
  82                 DEBUG(1, ("Could not set hashes: %s\n",
  83                         nt_errstr(status)));
  84                 goto done;
  85         }
  86 
  87         /* We need to get our protocol handler into the right state. So first
  88            we ask it to generate the initial message. Actually the client has already
  89            sent its own initial message, so we're going to drop this one on the floor.
  90            The client might have sent a different message, for example with different
  91            negotiation options, but as far as I can tell this won't hurt us. (Unless
  92            the client sent a different username or domain, in which case that's their
  93            problem for telling us the wrong username or domain.)
  94            Since we have a copy of the initial message that the client sent, we could
  95            resolve any discrepancies if we had to.
  96         */
  97         dummy_msg = data_blob_null;
  98         reply = data_blob_null;
  99         status = ntlmssp_update(ntlmssp_state, dummy_msg, &reply);
 100         data_blob_free(&dummy_msg);
 101         data_blob_free(&reply);
 102 
 103         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
 104                 DEBUG(1, ("Failed to create initial message! [%s]\n",
 105                         nt_errstr(status)));
 106                 goto done;
 107         }
 108 
 109         /* Now we are ready to handle the server's actual response. */
 110         status = ntlmssp_update(ntlmssp_state, challenge_msg, &reply);
 111 
 112         if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
 113                 DEBUG(1, ("We didn't get a response to the challenge! [%s]\n",
 114                         nt_errstr(status)));
 115                 data_blob_free(&reply);
 116                 goto done;
 117         }
 118         *auth_msg = reply;
 119         status = NT_STATUS_OK;
 120 
 121 done:
 122         ntlmssp_end(&ntlmssp_state);
 123         return status;
 124 }
 125 
 126 static bool check_client_uid(struct winbindd_cli_state *state, uid_t uid)
     /* [<][>][^][v][top][bottom][index][help] */
 127 {
 128         int ret;
 129         uid_t ret_uid;
 130 
 131         ret_uid = (uid_t)-1;
 132 
 133         ret = sys_getpeereid(state->sock, &ret_uid);
 134         if (ret != 0) {
 135                 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
 136                         "denying access\n", strerror(errno)));
 137                 return False;
 138         }
 139 
 140         if (uid != ret_uid) {
 141                 DEBUG(1, ("check_client_uid: Client lied about its uid: said %u, "
 142                         "actually was %u; denying access\n",
 143                         (unsigned int)uid, (unsigned int)ret_uid));
 144                 return False;
 145         }
 146 
 147         return True;
 148 }
 149 
 150 void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state)
     /* [<][>][^][v][top][bottom][index][help] */
 151 {
 152         struct winbindd_domain *domain;
 153         fstring name_domain, name_user;
 154 
 155         /* Ensure null termination */
 156         state->request.data.ccache_ntlm_auth.user[
 157                         sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0';
 158 
 159         DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state->pid,
 160                 state->request.data.ccache_ntlm_auth.user));
 161 
 162         /* Parse domain and username */
 163 
 164         if (!canonicalize_username(state->request.data.ccache_ntlm_auth.user,
 165                                 name_domain, name_user)) {
 166                 DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n",
 167                         state->request.data.ccache_ntlm_auth.user));
 168                 request_error(state);
 169                 return;
 170         }
 171 
 172         domain = find_auth_domain(state, name_domain);
 173 
 174         if (domain == NULL) {
 175                 DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n",
 176                         name_domain));
 177                 request_error(state);
 178                 return;
 179         }
 180 
 181         if (!check_client_uid(state, state->request.data.ccache_ntlm_auth.uid)) {
 182                 request_error(state);
 183                 return;
 184         }
 185 
 186         sendto_domain(state, domain);
 187 }
 188 
 189 enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *domain,
     /* [<][>][^][v][top][bottom][index][help] */
 190                                                 struct winbindd_cli_state *state)
 191 {
 192         NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
 193         struct WINBINDD_MEMORY_CREDS *entry;
 194         DATA_BLOB initial, challenge, auth;
 195         fstring name_domain, name_user;
 196         uint32 initial_blob_len, challenge_blob_len, extra_len;
 197 
 198         /* Ensure null termination */
 199         state->request.data.ccache_ntlm_auth.user[
 200                 sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0';
 201 
 202         DEBUG(3, ("winbindd_dual_ccache_ntlm_auth: [%5lu]: perform NTLM auth on "
 203                 "behalf of user %s (dual)\n", (unsigned long)state->pid,
 204                 state->request.data.ccache_ntlm_auth.user));
 205 
 206         /* validate blob lengths */
 207         initial_blob_len = state->request.data.ccache_ntlm_auth.initial_blob_len;
 208         challenge_blob_len = state->request.data.ccache_ntlm_auth.challenge_blob_len;
 209         extra_len = state->request.extra_len;
 210 
 211         if (initial_blob_len > extra_len || challenge_blob_len > extra_len ||
 212                 initial_blob_len + challenge_blob_len > extra_len ||
 213                 initial_blob_len + challenge_blob_len < initial_blob_len ||
 214                 initial_blob_len + challenge_blob_len < challenge_blob_len) {
 215 
 216                 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
 217                         "or wrap. Buffer [%d+%d > %d]\n",
 218                         initial_blob_len,
 219                         challenge_blob_len,
 220                         extra_len));
 221                 goto process_result;
 222         }
 223 
 224         /* Parse domain and username */
 225         if (!parse_domain_user(state->request.data.ccache_ntlm_auth.user, name_domain, name_user)) {
 226                 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse "
 227                         "domain and user from name [%s]\n",
 228                         state->request.data.ccache_ntlm_auth.user));
 229                 goto process_result;
 230         }
 231 
 232         entry = find_memory_creds_by_name(state->request.data.ccache_ntlm_auth.user);
 233         if (entry == NULL || entry->nt_hash == NULL || entry->lm_hash == NULL) {
 234                 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find "
 235                         "credentials for user %s\n", 
 236                         state->request.data.ccache_ntlm_auth.user));
 237                 goto process_result;
 238         }
 239 
 240         DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username));
 241 
 242         if (!client_can_access_ccache_entry(state->request.data.ccache_ntlm_auth.uid, entry)) {
 243                 goto process_result;
 244         }
 245 
 246         if (initial_blob_len == 0 && challenge_blob_len == 0) {
 247                 /* this is just a probe to see if credentials are available. */
 248                 result = NT_STATUS_OK;
 249                 state->response.data.ccache_ntlm_auth.auth_blob_len = 0;
 250                 goto process_result;
 251         }
 252 
 253         initial = data_blob(state->request.extra_data.data, initial_blob_len);
 254         challenge = data_blob(state->request.extra_data.data + initial_blob_len, 
 255                                 state->request.data.ccache_ntlm_auth.challenge_blob_len);
 256 
 257         if (!initial.data || !challenge.data) {
 258                 result = NT_STATUS_NO_MEMORY;
 259         } else {
 260                 result = do_ntlm_auth_with_hashes(name_user, name_domain,
 261                                                 entry->lm_hash, entry->nt_hash,
 262                                                 initial, challenge, &auth);
 263         }
 264 
 265         data_blob_free(&initial);
 266         data_blob_free(&challenge);
 267 
 268         if (!NT_STATUS_IS_OK(result)) {
 269                 goto process_result;
 270         }
 271 
 272         state->response.extra_data.data = smb_xmemdup(auth.data, auth.length);
 273         if (!state->response.extra_data.data) {
 274                 result = NT_STATUS_NO_MEMORY;
 275                 goto process_result;
 276         }
 277         state->response.length += auth.length;
 278         state->response.data.ccache_ntlm_auth.auth_blob_len = auth.length;
 279 
 280         data_blob_free(&auth);
 281 
 282   process_result:
 283         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
 284 }

/* [<][>][^][v][top][bottom][index][help] */