linux/fs/cifs/smb2transport.c
<<
>>
Prefs
   1/*
   2 *   fs/cifs/smb2transport.c
   3 *
   4 *   Copyright (C) International Business Machines  Corp., 2002, 2011
   5 *                 Etersoft, 2012
   6 *   Author(s): Steve French (sfrench@us.ibm.com)
   7 *              Jeremy Allison (jra@samba.org) 2006
   8 *              Pavel Shilovsky (pshilovsky@samba.org) 2012
   9 *
  10 *   This library is free software; you can redistribute it and/or modify
  11 *   it under the terms of the GNU Lesser General Public License as published
  12 *   by the Free Software Foundation; either version 2.1 of the License, or
  13 *   (at your option) any later version.
  14 *
  15 *   This library 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
  18 *   the GNU Lesser General Public License for more details.
  19 *
  20 *   You should have received a copy of the GNU Lesser General Public License
  21 *   along with this library; if not, write to the Free Software
  22 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  23 */
  24
  25#include <linux/fs.h>
  26#include <linux/list.h>
  27#include <linux/wait.h>
  28#include <linux/net.h>
  29#include <linux/delay.h>
  30#include <linux/uaccess.h>
  31#include <asm/processor.h>
  32#include <linux/mempool.h>
  33#include <linux/highmem.h>
  34#include "smb2pdu.h"
  35#include "cifsglob.h"
  36#include "cifsproto.h"
  37#include "smb2proto.h"
  38#include "cifs_debug.h"
  39#include "smb2status.h"
  40#include "smb2glob.h"
  41
  42static int
  43smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
  44{
  45        int i, rc;
  46        unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
  47        unsigned char *sigptr = smb2_signature;
  48        struct kvec *iov = rqst->rq_iov;
  49        int n_vec = rqst->rq_nvec;
  50        struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;
  51
  52        memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
  53        memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
  54
  55        rc = crypto_shash_setkey(server->secmech.hmacsha256,
  56                server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
  57        if (rc) {
  58                cERROR(1, "%s: Could not update with response\n", __func__);
  59                return rc;
  60        }
  61
  62        rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash);
  63        if (rc) {
  64                cERROR(1, "%s: Could not init md5\n", __func__);
  65                return rc;
  66        }
  67
  68        for (i = 0; i < n_vec; i++) {
  69                if (iov[i].iov_len == 0)
  70                        continue;
  71                if (iov[i].iov_base == NULL) {
  72                        cERROR(1, "null iovec entry");
  73                        return -EIO;
  74                }
  75                /*
  76                 * The first entry includes a length field (which does not get
  77                 * signed that occupies the first 4 bytes before the header).
  78                 */
  79                if (i == 0) {
  80                        if (iov[0].iov_len <= 8) /* cmd field at offset 9 */
  81                                break; /* nothing to sign or corrupt header */
  82                        rc =
  83                        crypto_shash_update(
  84                                &server->secmech.sdeschmacsha256->shash,
  85                                iov[i].iov_base + 4, iov[i].iov_len - 4);
  86                } else {
  87                        rc =
  88                        crypto_shash_update(
  89                                &server->secmech.sdeschmacsha256->shash,
  90                                iov[i].iov_base, iov[i].iov_len);
  91                }
  92                if (rc) {
  93                        cERROR(1, "%s: Could not update with payload\n",
  94                                                        __func__);
  95                        return rc;
  96                }
  97        }
  98
  99        /* now hash over the rq_pages array */
 100        for (i = 0; i < rqst->rq_npages; i++) {
 101                struct kvec p_iov;
 102
 103                cifs_rqst_page_to_kvec(rqst, i, &p_iov);
 104                crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
 105                                        p_iov.iov_base, p_iov.iov_len);
 106                kunmap(rqst->rq_pages[i]);
 107        }
 108
 109        rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash,
 110                                sigptr);
 111        if (rc)
 112                cERROR(1, "%s: Could not generate sha256 hash\n", __func__);
 113
 114        memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE);
 115
 116        return rc;
 117}
 118
 119/* must be called with server->srv_mutex held */
 120static int
 121smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
 122{
 123        int rc = 0;
 124        struct smb2_hdr *smb2_pdu = rqst->rq_iov[0].iov_base;
 125
 126        if (!(smb2_pdu->Flags & SMB2_FLAGS_SIGNED) ||
 127            server->tcpStatus == CifsNeedNegotiate)
 128                return rc;
 129
 130        if (!server->session_estab) {
 131                strncpy(smb2_pdu->Signature, "BSRSPYL", 8);
 132                return rc;
 133        }
 134
 135        rc = smb2_calc_signature(rqst, server);
 136
 137        return rc;
 138}
 139
 140int
 141smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
 142{
 143        unsigned int rc;
 144        char server_response_sig[16];
 145        struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
 146
 147        if ((smb2_pdu->Command == SMB2_NEGOTIATE) ||
 148            (smb2_pdu->Command == SMB2_OPLOCK_BREAK) ||
 149            (!server->session_estab))
 150                return 0;
 151
 152        /*
 153         * BB what if signatures are supposed to be on for session but
 154         * server does not send one? BB
 155         */
 156
 157        /* Do not need to verify session setups with signature "BSRSPYL " */
 158        if (memcmp(smb2_pdu->Signature, "BSRSPYL ", 8) == 0)
 159                cFYI(1, "dummy signature received for smb command 0x%x",
 160                        smb2_pdu->Command);
 161
 162        /*
 163         * Save off the origiginal signature so we can modify the smb and check
 164         * our calculated signature against what the server sent.
 165         */
 166        memcpy(server_response_sig, smb2_pdu->Signature, SMB2_SIGNATURE_SIZE);
 167
 168        memset(smb2_pdu->Signature, 0, SMB2_SIGNATURE_SIZE);
 169
 170        mutex_lock(&server->srv_mutex);
 171        rc = smb2_calc_signature(rqst, server);
 172        mutex_unlock(&server->srv_mutex);
 173
 174        if (rc)
 175                return rc;
 176
 177        if (memcmp(server_response_sig, smb2_pdu->Signature,
 178                   SMB2_SIGNATURE_SIZE))
 179                return -EACCES;
 180        else
 181                return 0;
 182}
 183
 184/*
 185 * Set message id for the request. Should be called after wait_for_free_request
 186 * and when srv_mutex is held.
 187 */
 188static inline void
 189smb2_seq_num_into_buf(struct TCP_Server_Info *server, struct smb2_hdr *hdr)
 190{
 191        hdr->MessageId = get_next_mid(server);
 192}
 193
 194static struct mid_q_entry *
 195smb2_mid_entry_alloc(const struct smb2_hdr *smb_buffer,
 196                     struct TCP_Server_Info *server)
 197{
 198        struct mid_q_entry *temp;
 199
 200        if (server == NULL) {
 201                cERROR(1, "Null TCP session in smb2_mid_entry_alloc");
 202                return NULL;
 203        }
 204
 205        temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
 206        if (temp == NULL)
 207                return temp;
 208        else {
 209                memset(temp, 0, sizeof(struct mid_q_entry));
 210                temp->mid = smb_buffer->MessageId;      /* always LE */
 211                temp->pid = current->pid;
 212                temp->command = smb_buffer->Command;    /* Always LE */
 213                temp->when_alloc = jiffies;
 214                temp->server = server;
 215
 216                /*
 217                 * The default is for the mid to be synchronous, so the
 218                 * default callback just wakes up the current task.
 219                 */
 220                temp->callback = cifs_wake_up_task;
 221                temp->callback_data = current;
 222        }
 223
 224        atomic_inc(&midCount);
 225        temp->mid_state = MID_REQUEST_ALLOCATED;
 226        return temp;
 227}
 228
 229static int
 230smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_hdr *buf,
 231                   struct mid_q_entry **mid)
 232{
 233        if (ses->server->tcpStatus == CifsExiting)
 234                return -ENOENT;
 235
 236        if (ses->server->tcpStatus == CifsNeedReconnect) {
 237                cFYI(1, "tcp session dead - return to caller to retry");
 238                return -EAGAIN;
 239        }
 240
 241        if (ses->status != CifsGood) {
 242                /* check if SMB2 session is bad because we are setting it up */
 243                if ((buf->Command != SMB2_SESSION_SETUP) &&
 244                    (buf->Command != SMB2_NEGOTIATE))
 245                        return -EAGAIN;
 246                /* else ok - we are setting up session */
 247        }
 248        *mid = smb2_mid_entry_alloc(buf, ses->server);
 249        if (*mid == NULL)
 250                return -ENOMEM;
 251        spin_lock(&GlobalMid_Lock);
 252        list_add_tail(&(*mid)->qhead, &ses->server->pending_mid_q);
 253        spin_unlock(&GlobalMid_Lock);
 254        return 0;
 255}
 256
 257int
 258smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
 259                   bool log_error)
 260{
 261        unsigned int len = get_rfc1002_length(mid->resp_buf);
 262        struct kvec iov;
 263        struct smb_rqst rqst = { .rq_iov = &iov,
 264                                 .rq_nvec = 1 };
 265
 266        iov.iov_base = (char *)mid->resp_buf;
 267        iov.iov_len = get_rfc1002_length(mid->resp_buf) + 4;
 268
 269        dump_smb(mid->resp_buf, min_t(u32, 80, len));
 270        /* convert the length into a more usable form */
 271        if ((len > 24) &&
 272            (server->sec_mode & (SECMODE_SIGN_REQUIRED|SECMODE_SIGN_ENABLED))) {
 273                int rc;
 274
 275                rc = smb2_verify_signature(&rqst, server);
 276                if (rc)
 277                        cERROR(1, "SMB signature verification returned error = "
 278                               "%d", rc);
 279        }
 280
 281        return map_smb2_to_linux_error(mid->resp_buf, log_error);
 282}
 283
 284struct mid_q_entry *
 285smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
 286{
 287        int rc;
 288        struct smb2_hdr *hdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
 289        struct mid_q_entry *mid;
 290
 291        smb2_seq_num_into_buf(ses->server, hdr);
 292
 293        rc = smb2_get_mid_entry(ses, hdr, &mid);
 294        if (rc)
 295                return ERR_PTR(rc);
 296        rc = smb2_sign_rqst(rqst, ses->server);
 297        if (rc) {
 298                cifs_delete_mid(mid);
 299                return ERR_PTR(rc);
 300        }
 301        return mid;
 302}
 303
 304struct mid_q_entry *
 305smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
 306{
 307        int rc;
 308        struct smb2_hdr *hdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
 309        struct mid_q_entry *mid;
 310
 311        smb2_seq_num_into_buf(server, hdr);
 312
 313        mid = smb2_mid_entry_alloc(hdr, server);
 314        if (mid == NULL)
 315                return ERR_PTR(-ENOMEM);
 316
 317        rc = smb2_sign_rqst(rqst, server);
 318        if (rc) {
 319                DeleteMidQEntry(mid);
 320                return ERR_PTR(rc);
 321        }
 322
 323        return mid;
 324}
 325
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.