Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 738f9de5 authored by Pavel Shilovsky's avatar Pavel Shilovsky Committed by Steve French
Browse files

CIFS: Send RFC1001 length in a separate iov



In order to simplify further encryption support we need to separate
RFC1001 length and SMB2 header when sending a request. Put the length
field in iov[0] and the rest of the packet into following iovs.

Signed-off-by: default avatarPavel Shilovsky <pshilov@microsoft.com>
parent fb2036d8
Loading
Loading
Loading
Loading
+22 −16
Original line number Diff line number Diff line
@@ -75,24 +75,20 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
	struct kvec *iov = rqst->rq_iov;
	int n_vec = rqst->rq_nvec;

	for (i = 0; i < n_vec; i++) {
	if (n_vec < 2 || iov[0].iov_len != 4)
		return -EIO;

	for (i = 1; i < n_vec; i++) {
		if (iov[i].iov_len == 0)
			continue;
		if (iov[i].iov_base == NULL) {
			cifs_dbg(VFS, "null iovec entry\n");
			return -EIO;
		}
		/* The first entry includes a length field (which does not get
		   signed that occupies the first 4 bytes before the header */
		if (i == 0) {
			if (iov[0].iov_len <= 8) /* cmd field at offset 9 */
		if (i == 1 && iov[1].iov_len <= 4)
			break; /* nothing to sign or corrupt header */
			rc = crypto_shash_update(shash,
				iov[i].iov_base + 4, iov[i].iov_len - 4);
		} else {
		rc = crypto_shash_update(shash,
					 iov[i].iov_base, iov[i].iov_len);
		}
		if (rc) {
			cifs_dbg(VFS, "%s: Could not update with payload\n",
				 __func__);
@@ -168,6 +164,10 @@ int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
	char smb_signature[20];
	struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base;

	if (rqst->rq_iov[0].iov_len != 4 ||
	    rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
		return -EIO;

	if ((cifs_pdu == NULL) || (server == NULL))
		return -EINVAL;

@@ -209,12 +209,14 @@ int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
		  __u32 *pexpected_response_sequence_number)
{
	struct kvec iov;
	struct kvec iov[2];

	iov.iov_base = cifs_pdu;
	iov.iov_len = be32_to_cpu(cifs_pdu->smb_buf_length) + 4;
	iov[0].iov_base = cifs_pdu;
	iov[0].iov_len = 4;
	iov[1].iov_base = (char *)cifs_pdu + 4;
	iov[1].iov_len = be32_to_cpu(cifs_pdu->smb_buf_length);

	return cifs_sign_smbv(&iov, 1, server,
	return cifs_sign_smbv(iov, 2, server,
			      pexpected_response_sequence_number);
}

@@ -227,6 +229,10 @@ int cifs_verify_signature(struct smb_rqst *rqst,
	char what_we_think_sig_should_be[20];
	struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base;

	if (rqst->rq_iov[0].iov_len != 4 ||
	    rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
		return -EIO;

	if (cifs_pdu == NULL || server == NULL)
		return -EINVAL;

+1 −1
Original line number Diff line number Diff line
@@ -1119,7 +1119,7 @@ struct cifs_readdata {
	int (*read_into_pages)(struct TCP_Server_Info *server,
				struct cifs_readdata *rdata,
				unsigned int len);
	struct kvec			iov;
	struct kvec			iov[2];
	unsigned int			pagesz;
	unsigned int			tailsz;
	unsigned int			credits;
+30 −21
Original line number Diff line number Diff line
@@ -708,9 +708,9 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
{
	ECHO_REQ *smb;
	int rc = 0;
	struct kvec iov;
	struct smb_rqst rqst = { .rq_iov = &iov,
				 .rq_nvec = 1 };
	struct kvec iov[2];
	struct smb_rqst rqst = { .rq_iov = iov,
				 .rq_nvec = 2 };

	cifs_dbg(FYI, "In echo request\n");

@@ -725,8 +725,11 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
	put_bcc(1, &smb->hdr);
	smb->Data[0] = 'a';
	inc_rfc1001_len(smb, 3);
	iov.iov_base = smb;
	iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4;

	iov[0].iov_len = 4;
	iov[0].iov_base = smb;
	iov[1].iov_len = get_rfc1002_length(smb);
	iov[1].iov_base = (char *)smb + 4;

	rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback,
			     server, CIFS_ASYNC_OP | CIFS_ECHO_OP);
@@ -1509,10 +1512,12 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
	}

	/* set up first iov for signature check */
	rdata->iov.iov_base = buf;
	rdata->iov.iov_len = server->total_read;
	cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
		 rdata->iov.iov_base, rdata->iov.iov_len);
	rdata->iov[0].iov_base = buf;
	rdata->iov[0].iov_len = 4;
	rdata->iov[1].iov_base = buf + 4;
	rdata->iov[1].iov_len = server->total_read - 4;
	cifs_dbg(FYI, "0: iov_base=%p iov_len=%u\n",
		 rdata->iov[0].iov_base, server->total_read);

	/* how much data is in the response? */
	data_len = server->ops->read_data_length(buf);
@@ -1545,8 +1550,8 @@ cifs_readv_callback(struct mid_q_entry *mid)
	struct cifs_readdata *rdata = mid->callback_data;
	struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
	struct TCP_Server_Info *server = tcon->ses->server;
	struct smb_rqst rqst = { .rq_iov = &rdata->iov,
				 .rq_nvec = 1,
	struct smb_rqst rqst = { .rq_iov = rdata->iov,
				 .rq_nvec = 2,
				 .rq_pages = rdata->pages,
				 .rq_npages = rdata->nr_pages,
				 .rq_pagesz = rdata->pagesz,
@@ -1601,8 +1606,8 @@ cifs_async_readv(struct cifs_readdata *rdata)
	READ_REQ *smb = NULL;
	int wct;
	struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
	struct smb_rqst rqst = { .rq_iov = &rdata->iov,
				 .rq_nvec = 1 };
	struct smb_rqst rqst = { .rq_iov = rdata->iov,
				 .rq_nvec = 2 };

	cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n",
		 __func__, rdata->offset, rdata->bytes);
@@ -1642,8 +1647,10 @@ cifs_async_readv(struct cifs_readdata *rdata)
	}

	/* 4 for RFC1001 length + 1 for BCC */
	rdata->iov.iov_base = smb;
	rdata->iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4;
	rdata->iov[0].iov_base = smb;
	rdata->iov[0].iov_len = 4;
	rdata->iov[1].iov_base = (char *)smb + 4;
	rdata->iov[1].iov_len = get_rfc1002_length(smb);

	kref_get(&rdata->refcount);
	rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive,
@@ -2096,7 +2103,7 @@ cifs_async_writev(struct cifs_writedata *wdata,
	WRITE_REQ *smb = NULL;
	int wct;
	struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
	struct kvec iov;
	struct kvec iov[2];
	struct smb_rqst rqst = { };

	if (tcon->ses->capabilities & CAP_LARGE_FILES) {
@@ -2129,11 +2136,13 @@ cifs_async_writev(struct cifs_writedata *wdata,
	    cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);

	/* 4 for RFC1001 length + 1 for BCC */
	iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4 + 1;
	iov.iov_base = smb;
	iov[0].iov_len = 4;
	iov[0].iov_base = smb;
	iov[1].iov_len = get_rfc1002_length(smb) + 1;
	iov[1].iov_base = (char *)smb + 4;

	rqst.rq_iov = &iov;
	rqst.rq_nvec = 1;
	rqst.rq_iov = iov;
	rqst.rq_nvec = 2;
	rqst.rq_pages = wdata->pages;
	rqst.rq_npages = wdata->nr_pages;
	rqst.rq_pagesz = wdata->pagesz;
@@ -2154,7 +2163,7 @@ cifs_async_writev(struct cifs_writedata *wdata,
				(struct smb_com_writex_req *)smb;
		inc_rfc1001_len(&smbw->hdr, wdata->bytes + 5);
		put_bcc(wdata->bytes + 5, &smbw->hdr);
		iov.iov_len += 4; /* pad bigger by four bytes */
		iov[1].iov_len += 4; /* pad bigger by four bytes */
	}

	kref_get(&wdata->refcount);
+39 −25
Original line number Diff line number Diff line
@@ -2047,9 +2047,9 @@ SMB2_echo(struct TCP_Server_Info *server)
{
	struct smb2_echo_req *req;
	int rc = 0;
	struct kvec iov;
	struct smb_rqst rqst = { .rq_iov = &iov,
				 .rq_nvec = 1 };
	struct kvec iov[2];
	struct smb_rqst rqst = { .rq_iov = iov,
				 .rq_nvec = 2 };

	cifs_dbg(FYI, "In echo request\n");

@@ -2065,9 +2065,11 @@ SMB2_echo(struct TCP_Server_Info *server)

	req->hdr.sync_hdr.CreditRequest = cpu_to_le16(1);

	iov.iov_base = (char *)req;
	/* 4 for rfc1002 length field */
	iov.iov_len = get_rfc1002_length(req) + 4;
	iov[0].iov_len = 4;
	iov[0].iov_base = (char *)req;
	iov[1].iov_len = get_rfc1002_length(req);
	iov[1].iov_base = (char *)req + 4;

	rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, server,
			     CIFS_ECHO_OP);
@@ -2123,8 +2125,9 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
 * have the end_of_chain boolean set to true.
 */
static int
smb2_new_read_req(struct kvec *iov, struct cifs_io_parms *io_parms,
		  unsigned int remaining_bytes, int request_type)
smb2_new_read_req(void **buf, unsigned int *total_len,
		  struct cifs_io_parms *io_parms, unsigned int remaining_bytes,
		  int request_type)
{
	int rc = -EACCES;
	struct smb2_read_req *req = NULL;
@@ -2172,9 +2175,9 @@ smb2_new_read_req(struct kvec *iov, struct cifs_io_parms *io_parms,
	else
		req->RemainingBytes = 0;

	iov[0].iov_base = (char *)req;
	*buf = req;
	/* 4 for rfc1002 length field */
	iov[0].iov_len = get_rfc1002_length(req) + 4;
	*total_len = get_rfc1002_length(req) + 4;
	return rc;
}

@@ -2184,10 +2187,11 @@ smb2_readv_callback(struct mid_q_entry *mid)
	struct cifs_readdata *rdata = mid->callback_data;
	struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
	struct TCP_Server_Info *server = tcon->ses->server;
	struct smb2_sync_hdr *shdr = get_sync_hdr(rdata->iov.iov_base);
	struct smb2_sync_hdr *shdr =
				(struct smb2_sync_hdr *)rdata->iov[1].iov_base;
	unsigned int credits_received = 1;
	struct smb_rqst rqst = { .rq_iov = &rdata->iov,
				 .rq_nvec = 1,
	struct smb_rqst rqst = { .rq_iov = rdata->iov,
				 .rq_nvec = 2,
				 .rq_pages = rdata->pages,
				 .rq_npages = rdata->nr_pages,
				 .rq_pagesz = rdata->pagesz,
@@ -2238,7 +2242,7 @@ smb2_readv_callback(struct mid_q_entry *mid)
	add_credits(server, credits_received, 0);
}

/* smb2_async_readv - send an async write, and set up mid to handle result */
/* smb2_async_readv - send an async read, and set up mid to handle result */
int
smb2_async_readv(struct cifs_readdata *rdata)
{
@@ -2246,9 +2250,10 @@ smb2_async_readv(struct cifs_readdata *rdata)
	char *buf;
	struct smb2_sync_hdr *shdr;
	struct cifs_io_parms io_parms;
	struct smb_rqst rqst = { .rq_iov = &rdata->iov,
				 .rq_nvec = 1 };
	struct smb_rqst rqst = { .rq_iov = rdata->iov,
				 .rq_nvec = 2 };
	struct TCP_Server_Info *server;
	unsigned int total_len;

	cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n",
		 __func__, rdata->offset, rdata->bytes);
@@ -2262,7 +2267,7 @@ smb2_async_readv(struct cifs_readdata *rdata)

	server = io_parms.tcon->ses->server;

	rc = smb2_new_read_req(&rdata->iov, &io_parms, 0, 0);
	rc = smb2_new_read_req((void **) &buf, &total_len, &io_parms, 0, 0);
	if (rc) {
		if (rc == -EAGAIN && rdata->credits) {
			/* credits was reset by reconnect */
@@ -2275,10 +2280,12 @@ smb2_async_readv(struct cifs_readdata *rdata)
		return rc;
	}

	buf = rdata->iov.iov_base;
	shdr = get_sync_hdr(buf);
	/* 4 for rfc1002 length field */
	rdata->iov.iov_len = get_rfc1002_length(rdata->iov.iov_base) + 4;
	rdata->iov[0].iov_len = 4;
	rdata->iov[0].iov_base = buf;
	rdata->iov[1].iov_len = total_len - 4;
	rdata->iov[1].iov_base = buf + 4;

	if (rdata->credits) {
		shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
@@ -2314,12 +2321,17 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
	struct smb2_sync_hdr *shdr;
	struct kvec iov[1];
	struct kvec rsp_iov;
	unsigned int total_len;
	char *req;

	*nbytes = 0;
	rc = smb2_new_read_req(iov, io_parms, 0, 0);
	rc = smb2_new_read_req((void **)&req, &total_len, io_parms, 0, 0);
	if (rc)
		return rc;

	iov[0].iov_base = buf;
	iov[0].iov_len = total_len;

	rc = SendReceive2(xid, io_parms->tcon->ses, iov, 1,
			  &resp_buftype, CIFS_LOG_ERROR, &rsp_iov);
	cifs_small_buf_release(iov[0].iov_base);
@@ -2424,8 +2436,8 @@ smb2_async_writev(struct cifs_writedata *wdata,
	struct smb2_sync_hdr *shdr;
	struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
	struct TCP_Server_Info *server = tcon->ses->server;
	struct kvec iov;
	struct smb_rqst rqst;
	struct kvec iov[2];
	struct smb_rqst rqst = { };

	rc = small_smb2_init(SMB2_WRITE, tcon, (void **) &req);
	if (rc) {
@@ -2455,11 +2467,13 @@ smb2_async_writev(struct cifs_writedata *wdata,
	req->RemainingBytes = 0;

	/* 4 for rfc1002 length field and 1 for Buffer */
	iov.iov_len = get_rfc1002_length(req) + 4 - 1;
	iov.iov_base = req;
	iov[0].iov_len = 4;
	iov[0].iov_base = req;
	iov[1].iov_len = get_rfc1002_length(req) - 1;
	iov[1].iov_base = (char *)req + 4;

	rqst.rq_iov = &iov;
	rqst.rq_nvec = 1;
	rqst.rq_iov = iov;
	rqst.rq_nvec = 2;
	rqst.rq_pages = wdata->pages;
	rqst.rq_npages = wdata->nr_pages;
	rqst.rq_pagesz = wdata->pagesz;
+17 −11
Original line number Diff line number Diff line
@@ -138,7 +138,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
	unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
	unsigned char *sigptr = smb2_signature;
	struct kvec *iov = rqst->rq_iov;
	struct smb2_sync_hdr *shdr = get_sync_hdr(iov[0].iov_base);
	struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base;
	struct cifs_ses *ses;

	ses = smb2_find_smb_ses(shdr, server);
@@ -355,7 +355,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
	unsigned char smb3_signature[SMB2_CMACAES_SIZE];
	unsigned char *sigptr = smb3_signature;
	struct kvec *iov = rqst->rq_iov;
	struct smb2_sync_hdr *shdr = get_sync_hdr(iov[0].iov_base);
	struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base;
	struct cifs_ses *ses;

	ses = smb2_find_smb_ses(shdr, server);
@@ -400,7 +400,8 @@ static int
smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
{
	int rc = 0;
	struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base);
	struct smb2_sync_hdr *shdr =
			(struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base;

	if (!(shdr->Flags & SMB2_FLAGS_SIGNED) ||
	    server->tcpStatus == CifsNeedNegotiate)
@@ -421,7 +422,8 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
{
	unsigned int rc;
	char server_response_sig[16];
	struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base);
	struct smb2_sync_hdr *shdr =
			(struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base;

	if ((shdr->Command == SMB2_NEGOTIATE) ||
	    (shdr->Command == SMB2_SESSION_SETUP) ||
@@ -550,12 +552,14 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
		   bool log_error)
{
	unsigned int len = get_rfc1002_length(mid->resp_buf);
	struct kvec iov;
	struct smb_rqst rqst = { .rq_iov = &iov,
				 .rq_nvec = 1 };
	struct kvec iov[2];
	struct smb_rqst rqst = { .rq_iov = iov,
				 .rq_nvec = 2 };

	iov.iov_base = (char *)mid->resp_buf;
	iov.iov_len = get_rfc1002_length(mid->resp_buf) + 4;
	iov[0].iov_base = (char *)mid->resp_buf;
	iov[0].iov_len = 4;
	iov[1].iov_base = (char *)mid->resp_buf + 4;
	iov[1].iov_len = len;

	dump_smb(mid->resp_buf, min_t(u32, 80, len));
	/* convert the length into a more usable form */
@@ -575,7 +579,8 @@ struct mid_q_entry *
smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
{
	int rc;
	struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base);
	struct smb2_sync_hdr *shdr =
			(struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base;
	struct mid_q_entry *mid;

	smb2_seq_num_into_buf(ses->server, shdr);
@@ -595,7 +600,8 @@ struct mid_q_entry *
smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
{
	int rc;
	struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base);
	struct smb2_sync_hdr *shdr =
			(struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base;
	struct mid_q_entry *mid;

	smb2_seq_num_into_buf(server, shdr);
Loading