aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-11-28 12:50:25 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-11-28 12:50:25 -0500
commit3bad8bb5cd3048a67df43ac6b1e2f191f19d9ff0 (patch)
treebc18e38958f63b6ffb8514e16729f094925e4d5b /fs
parent5ecbe3c3c690b5ab493c730c317475287a9e8b45 (diff)
parentf19e84df37bda502a2248d507a9cf2b9e693279e (diff)
Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
Pull cifs fixes from Steve French: "SMB3 "validate negotiate" is needed to prevent certain types of downgrade attacks. Also changes SMB2/SMB3 copy offload from using the BTRFS copy ioctl (BTRFS_IOC_CLONE) to a cifs specific ioctl (CIFS_IOC_COPYCHUNK_FILE) to address Christoph's comment that there are semantic differences between requesting copy offload in which copy-on-write is mandatory (as in the BTRFS ioctl) and optional in the SMB2/SMB3 case. Also fixes SMB2/SMB3 copychunk for large files" * 'for-next' of git://git.samba.org/sfrench/cifs-2.6: [CIFS] Do not use btrfs refcopy ioctl for SMB2 copy offload Check SMB3 dialects against downgrade attacks Removed duplicated (and unneeded) goto CIFS: Fix SMB2/SMB3 Copy offload support (refcopy) for large files
Diffstat (limited to 'fs')
-rw-r--r--fs/cifs/cifsglob.h1
-rw-r--r--fs/cifs/ioctl.c6
-rw-r--r--fs/cifs/smb2ops.c99
-rw-r--r--fs/cifs/smb2pdu.c92
-rw-r--r--fs/cifs/smb2pdu.h12
-rw-r--r--fs/cifs/smb2proto.h1
-rw-r--r--fs/cifs/smbfsctl.h2
7 files changed, 189 insertions, 24 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index d9ea7ada1378..f918a998a087 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -384,6 +384,7 @@ struct smb_version_operations {
384 int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file, 384 int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file,
385 struct cifsFileInfo *target_file, u64 src_off, u64 len, 385 struct cifsFileInfo *target_file, u64 src_off, u64 len,
386 u64 dest_off); 386 u64 dest_off);
387 int (*validate_negotiate)(const unsigned int, struct cifs_tcon *);
387}; 388};
388 389
389struct smb_version_values { 390struct smb_version_values {
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index 409b45eefe70..77492301cc2b 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -26,13 +26,15 @@
26#include <linux/mount.h> 26#include <linux/mount.h>
27#include <linux/mm.h> 27#include <linux/mm.h>
28#include <linux/pagemap.h> 28#include <linux/pagemap.h>
29#include <linux/btrfs.h>
30#include "cifspdu.h" 29#include "cifspdu.h"
31#include "cifsglob.h" 30#include "cifsglob.h"
32#include "cifsproto.h" 31#include "cifsproto.h"
33#include "cifs_debug.h" 32#include "cifs_debug.h"
34#include "cifsfs.h" 33#include "cifsfs.h"
35 34
35#define CIFS_IOCTL_MAGIC 0xCF
36#define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int)
37
36static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file, 38static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
37 unsigned long srcfd, u64 off, u64 len, u64 destoff) 39 unsigned long srcfd, u64 off, u64 len, u64 destoff)
38{ 40{
@@ -213,7 +215,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
213 cifs_dbg(FYI, "set compress flag rc %d\n", rc); 215 cifs_dbg(FYI, "set compress flag rc %d\n", rc);
214 } 216 }
215 break; 217 break;
216 case BTRFS_IOC_CLONE: 218 case CIFS_IOC_COPYCHUNK_FILE:
217 rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0); 219 rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0);
218 break; 220 break;
219 default: 221 default:
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 11dde4b24f8a..757da3e54d3d 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -532,7 +532,10 @@ smb2_clone_range(const unsigned int xid,
532 int rc; 532 int rc;
533 unsigned int ret_data_len; 533 unsigned int ret_data_len;
534 struct copychunk_ioctl *pcchunk; 534 struct copychunk_ioctl *pcchunk;
535 char *retbuf = NULL; 535 struct copychunk_ioctl_rsp *retbuf = NULL;
536 struct cifs_tcon *tcon;
537 int chunks_copied = 0;
538 bool chunk_sizes_updated = false;
536 539
537 pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); 540 pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL);
538 541
@@ -547,27 +550,96 @@ smb2_clone_range(const unsigned int xid,
547 550
548 /* Note: request_res_key sets res_key null only if rc !=0 */ 551 /* Note: request_res_key sets res_key null only if rc !=0 */
549 if (rc) 552 if (rc)
550 return rc; 553 goto cchunk_out;
551 554
552 /* For now array only one chunk long, will make more flexible later */ 555 /* For now array only one chunk long, will make more flexible later */
553 pcchunk->ChunkCount = __constant_cpu_to_le32(1); 556 pcchunk->ChunkCount = __constant_cpu_to_le32(1);
554 pcchunk->Reserved = 0; 557 pcchunk->Reserved = 0;
555 pcchunk->SourceOffset = cpu_to_le64(src_off);
556 pcchunk->TargetOffset = cpu_to_le64(dest_off);
557 pcchunk->Length = cpu_to_le32(len);
558 pcchunk->Reserved2 = 0; 558 pcchunk->Reserved2 = 0;
559 559
560 /* Request that server copy to target from src file identified by key */ 560 tcon = tlink_tcon(trgtfile->tlink);
561 rc = SMB2_ioctl(xid, tlink_tcon(trgtfile->tlink),
562 trgtfile->fid.persistent_fid,
563 trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
564 true /* is_fsctl */, (char *)pcchunk,
565 sizeof(struct copychunk_ioctl), &retbuf, &ret_data_len);
566 561
567 /* BB need to special case rc = EINVAL to alter chunk size */ 562 while (len > 0) {
563 pcchunk->SourceOffset = cpu_to_le64(src_off);
564 pcchunk->TargetOffset = cpu_to_le64(dest_off);
565 pcchunk->Length =
566 cpu_to_le32(min_t(u32, len, tcon->max_bytes_chunk));
568 567
569 cifs_dbg(FYI, "rc %d data length out %d\n", rc, ret_data_len); 568 /* Request server copy to target from src identified by key */
569 rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
570 trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
571 true /* is_fsctl */, (char *)pcchunk,
572 sizeof(struct copychunk_ioctl), (char **)&retbuf,
573 &ret_data_len);
574 if (rc == 0) {
575 if (ret_data_len !=
576 sizeof(struct copychunk_ioctl_rsp)) {
577 cifs_dbg(VFS, "invalid cchunk response size\n");
578 rc = -EIO;
579 goto cchunk_out;
580 }
581 if (retbuf->TotalBytesWritten == 0) {
582 cifs_dbg(FYI, "no bytes copied\n");
583 rc = -EIO;
584 goto cchunk_out;
585 }
586 /*
587 * Check if server claimed to write more than we asked
588 */
589 if (le32_to_cpu(retbuf->TotalBytesWritten) >
590 le32_to_cpu(pcchunk->Length)) {
591 cifs_dbg(VFS, "invalid copy chunk response\n");
592 rc = -EIO;
593 goto cchunk_out;
594 }
595 if (le32_to_cpu(retbuf->ChunksWritten) != 1) {
596 cifs_dbg(VFS, "invalid num chunks written\n");
597 rc = -EIO;
598 goto cchunk_out;
599 }
600 chunks_copied++;
601
602 src_off += le32_to_cpu(retbuf->TotalBytesWritten);
603 dest_off += le32_to_cpu(retbuf->TotalBytesWritten);
604 len -= le32_to_cpu(retbuf->TotalBytesWritten);
605
606 cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %d\n",
607 le32_to_cpu(retbuf->ChunksWritten),
608 le32_to_cpu(retbuf->ChunkBytesWritten),
609 le32_to_cpu(retbuf->TotalBytesWritten));
610 } else if (rc == -EINVAL) {
611 if (ret_data_len != sizeof(struct copychunk_ioctl_rsp))
612 goto cchunk_out;
613
614 cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n",
615 le32_to_cpu(retbuf->ChunksWritten),
616 le32_to_cpu(retbuf->ChunkBytesWritten),
617 le32_to_cpu(retbuf->TotalBytesWritten));
618
619 /*
620 * Check if this is the first request using these sizes,
621 * (ie check if copy succeed once with original sizes
622 * and check if the server gave us different sizes after
623 * we already updated max sizes on previous request).
624 * if not then why is the server returning an error now
625 */
626 if ((chunks_copied != 0) || chunk_sizes_updated)
627 goto cchunk_out;
628
629 /* Check that server is not asking us to grow size */
630 if (le32_to_cpu(retbuf->ChunkBytesWritten) <
631 tcon->max_bytes_chunk)
632 tcon->max_bytes_chunk =
633 le32_to_cpu(retbuf->ChunkBytesWritten);
634 else
635 goto cchunk_out; /* server gave us bogus size */
636
637 /* No need to change MaxChunks since already set to 1 */
638 chunk_sizes_updated = true;
639 }
640 }
570 641
642cchunk_out:
571 kfree(pcchunk); 643 kfree(pcchunk);
572 return rc; 644 return rc;
573} 645}
@@ -1247,6 +1319,7 @@ struct smb_version_operations smb30_operations = {
1247 .create_lease_buf = smb3_create_lease_buf, 1319 .create_lease_buf = smb3_create_lease_buf,
1248 .parse_lease_buf = smb3_parse_lease_buf, 1320 .parse_lease_buf = smb3_parse_lease_buf,
1249 .clone_range = smb2_clone_range, 1321 .clone_range = smb2_clone_range,
1322 .validate_negotiate = smb3_validate_negotiate,
1250}; 1323};
1251 1324
1252struct smb_version_values smb20_values = { 1325struct smb_version_values smb20_values = {
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index d65270c290a1..2013234b73ad 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -454,6 +454,81 @@ neg_exit:
454 return rc; 454 return rc;
455} 455}
456 456
457int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
458{
459 int rc = 0;
460 struct validate_negotiate_info_req vneg_inbuf;
461 struct validate_negotiate_info_rsp *pneg_rsp;
462 u32 rsplen;
463
464 cifs_dbg(FYI, "validate negotiate\n");
465
466 /*
467 * validation ioctl must be signed, so no point sending this if we
468 * can not sign it. We could eventually change this to selectively
469 * sign just this, the first and only signed request on a connection.
470 * This is good enough for now since a user who wants better security
471 * would also enable signing on the mount. Having validation of
472 * negotiate info for signed connections helps reduce attack vectors
473 */
474 if (tcon->ses->server->sign == false)
475 return 0; /* validation requires signing */
476
477 vneg_inbuf.Capabilities =
478 cpu_to_le32(tcon->ses->server->vals->req_capabilities);
479 memcpy(vneg_inbuf.Guid, cifs_client_guid, SMB2_CLIENT_GUID_SIZE);
480
481 if (tcon->ses->sign)
482 vneg_inbuf.SecurityMode =
483 cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED);
484 else if (global_secflags & CIFSSEC_MAY_SIGN)
485 vneg_inbuf.SecurityMode =
486 cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED);
487 else
488 vneg_inbuf.SecurityMode = 0;
489
490 vneg_inbuf.DialectCount = cpu_to_le16(1);
491 vneg_inbuf.Dialects[0] =
492 cpu_to_le16(tcon->ses->server->vals->protocol_id);
493
494 rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
495 FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,
496 (char *)&vneg_inbuf, sizeof(struct validate_negotiate_info_req),
497 (char **)&pneg_rsp, &rsplen);
498
499 if (rc != 0) {
500 cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc);
501 return -EIO;
502 }
503
504 if (rsplen != sizeof(struct validate_negotiate_info_rsp)) {
505 cifs_dbg(VFS, "invalid size of protocol negotiate response\n");
506 return -EIO;
507 }
508
509 /* check validate negotiate info response matches what we got earlier */
510 if (pneg_rsp->Dialect !=
511 cpu_to_le16(tcon->ses->server->vals->protocol_id))
512 goto vneg_out;
513
514 if (pneg_rsp->SecurityMode != cpu_to_le16(tcon->ses->server->sec_mode))
515 goto vneg_out;
516
517 /* do not validate server guid because not saved at negprot time yet */
518
519 if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND |
520 SMB2_LARGE_FILES) != tcon->ses->server->capabilities)
521 goto vneg_out;
522
523 /* validate negotiate successful */
524 cifs_dbg(FYI, "validate negotiate info successful\n");
525 return 0;
526
527vneg_out:
528 cifs_dbg(VFS, "protocol revalidation - security settings mismatch\n");
529 return -EIO;
530}
531
457int 532int
458SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, 533SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
459 const struct nls_table *nls_cp) 534 const struct nls_table *nls_cp)
@@ -829,6 +904,8 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
829 ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) 904 ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0))
830 cifs_dbg(VFS, "DFS capability contradicts DFS flag\n"); 905 cifs_dbg(VFS, "DFS capability contradicts DFS flag\n");
831 init_copy_chunk_defaults(tcon); 906 init_copy_chunk_defaults(tcon);
907 if (tcon->ses->server->ops->validate_negotiate)
908 rc = tcon->ses->server->ops->validate_negotiate(xid, tcon);
832tcon_exit: 909tcon_exit:
833 free_rsp_buf(resp_buftype, rsp); 910 free_rsp_buf(resp_buftype, rsp);
834 kfree(unc_path); 911 kfree(unc_path);
@@ -1214,10 +1291,17 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
1214 rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); 1291 rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
1215 rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base; 1292 rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base;
1216 1293
1217 if (rc != 0) { 1294 if ((rc != 0) && (rc != -EINVAL)) {
1218 if (tcon) 1295 if (tcon)
1219 cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); 1296 cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
1220 goto ioctl_exit; 1297 goto ioctl_exit;
1298 } else if (rc == -EINVAL) {
1299 if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) &&
1300 (opcode != FSCTL_SRV_COPYCHUNK)) {
1301 if (tcon)
1302 cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
1303 goto ioctl_exit;
1304 }
1221 } 1305 }
1222 1306
1223 /* check if caller wants to look at return data or just return rc */ 1307 /* check if caller wants to look at return data or just return rc */
@@ -2154,11 +2238,9 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
2154 rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0); 2238 rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0);
2155 rsp = (struct smb2_set_info_rsp *)iov[0].iov_base; 2239 rsp = (struct smb2_set_info_rsp *)iov[0].iov_base;
2156 2240
2157 if (rc != 0) { 2241 if (rc != 0)
2158 cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE); 2242 cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE);
2159 goto out; 2243
2160 }
2161out:
2162 free_rsp_buf(resp_buftype, rsp); 2244 free_rsp_buf(resp_buftype, rsp);
2163 kfree(iov); 2245 kfree(iov);
2164 return rc; 2246 return rc;
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index f88320bbb477..2022c542ea3a 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -577,13 +577,19 @@ struct copychunk_ioctl_rsp {
577 __le32 TotalBytesWritten; 577 __le32 TotalBytesWritten;
578} __packed; 578} __packed;
579 579
580/* Response and Request are the same format */ 580struct validate_negotiate_info_req {
581struct validate_negotiate_info {
582 __le32 Capabilities; 581 __le32 Capabilities;
583 __u8 Guid[SMB2_CLIENT_GUID_SIZE]; 582 __u8 Guid[SMB2_CLIENT_GUID_SIZE];
584 __le16 SecurityMode; 583 __le16 SecurityMode;
585 __le16 DialectCount; 584 __le16 DialectCount;
586 __le16 Dialect[1]; 585 __le16 Dialects[1]; /* dialect (someday maybe list) client asked for */
586} __packed;
587
588struct validate_negotiate_info_rsp {
589 __le32 Capabilities;
590 __u8 Guid[SMB2_CLIENT_GUID_SIZE];
591 __le16 SecurityMode;
592 __le16 Dialect; /* Dialect in use for the connection */
587} __packed; 593} __packed;
588 594
589#define RSS_CAPABLE 0x00000001 595#define RSS_CAPABLE 0x00000001
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index b4eea105b08c..93adc64666f3 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -162,5 +162,6 @@ extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
162 struct smb2_lock_element *buf); 162 struct smb2_lock_element *buf);
163extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, 163extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
164 __u8 *lease_key, const __le32 lease_state); 164 __u8 *lease_key, const __le32 lease_state);
165extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *);
165 166
166#endif /* _SMB2PROTO_H */ 167#endif /* _SMB2PROTO_H */
diff --git a/fs/cifs/smbfsctl.h b/fs/cifs/smbfsctl.h
index a4b2391fe66e..0e538b5c9622 100644
--- a/fs/cifs/smbfsctl.h
+++ b/fs/cifs/smbfsctl.h
@@ -90,7 +90,7 @@
90#define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 /* BB add struct */ 90#define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 /* BB add struct */
91#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ 91#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */
92#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ 92#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */
93#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 /* BB add struct */ 93#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204
94/* Perform server-side data movement */ 94/* Perform server-side data movement */
95#define FSCTL_SRV_COPYCHUNK 0x001440F2 95#define FSCTL_SRV_COPYCHUNK 0x001440F2
96#define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2 96#define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2