History

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

Linus Torvalds committed 12 years ago

Commit 3bad8bb2 parents 5ecbe3c f19e84d7 files · +189 −24Browse files

Unified · Split

7 files
  1. fs/cifs/cifsglob.h +1 −0
  2. fs/cifs/ioctl.c +4 −2
  3. fs/cifs/smb2ops.c +86 −13
  4. fs/cifs/smb2pdu.c +87 −5
  5. fs/cifs/smb2pdu.h +9 −3
  6. fs/cifs/smb2proto.h +1 −0
  7. fs/cifs/smbfsctl.h +1 −1
fs/cifs/cifsglob.h+1−0
@@ -384,6 +384,7 @@ struct smb_version_operations { · 383 unmodified lines
384384 int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file,
385385 struct cifsFileInfo *target_file, u64 src_off, u64 len,
386386 u64 dest_off);
387+ int (*validate_negotiate)(const unsigned int, struct cifs_tcon *);
387388 };
388389
389390 struct smb_version_values {
fs/cifs/ioctl.c+4−2
@@ -26,13 +26,15 @@ · 25 unmodified lines
2626 #include <linux/mount.h>
2727 #include <linux/mm.h>
2828 #include <linux/pagemap.h>
29−#include <linux/btrfs.h>
3029 #include "cifspdu.h"
3130 #include "cifsglob.h"
3231 #include "cifsproto.h"
3332 #include "cifs_debug.h"
3433 #include "cifsfs.h"
3534
35+#define CIFS_IOCTL_MAGIC 0xCF
36+#define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int)
37+
3638 static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
3739 unsigned long srcfd, u64 off, u64 len, u64 destoff)
3840 {
@@ -213,7 +215,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) · 174 unmodified lines
213215 cifs_dbg(FYI, "set compress flag rc %d\n", rc);
214216 }
215217 break;
216− case BTRFS_IOC_CLONE:
218+ case CIFS_IOC_COPYCHUNK_FILE:
217219 rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0);
218220 break;
219221 default:
fs/cifs/smb2ops.c+86−13
@@ -532,7 +532,10 @@ smb2_clone_range(const unsigned int xid, · 531 unmodified lines
532532 int rc;
533533 unsigned int ret_data_len;
534534 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;
536539
537540 pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL);
538541
@@ -547,27 +550,96 @@ smb2_clone_range(const unsigned int xid, · 8 unmodified lines
547550
548551 /* Note: request_res_key sets res_key null only if rc !=0 */
549552 if (rc)
550− return rc;
553+ goto cchunk_out;
551554
552555 /* For now array only one chunk long, will make more flexible later */
553556 pcchunk->ChunkCount = __constant_cpu_to_le32(1);
554557 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);
558558 pcchunk->Reserved2 = 0;
559559
560− /* Request that server copy to target from src file identified by key */
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);
560+ tcon = tlink_tcon(trgtfile->tlink);
566561
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));
568567
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+ }
570641
642+cchunk_out:
571643 kfree(pcchunk);
572644 return rc;
573645 }
@@ -1247,6 +1319,7 @@ struct smb_version_operations smb30_operations = { · 673 unmodified lines
12471319 .create_lease_buf = smb3_create_lease_buf,
12481320 .parse_lease_buf = smb3_parse_lease_buf,
12491321 .clone_range = smb2_clone_range,
1322+ .validate_negotiate = smb3_validate_negotiate,
12501323 };
12511324
12521325 struct smb_version_values smb20_values = {
fs/cifs/smb2pdu.c+87−5
@@ -454,6 +454,81 @@ neg_exit: · 453 unmodified lines
454454 return rc;
455455 }
456456
457+int 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+
527+vneg_out:
528+ cifs_dbg(VFS, "protocol revalidation - security settings mismatch\n");
529+ return -EIO;
530+}
531+
457532 int
458533 SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
459534 const struct nls_table *nls_cp)
@@ -829,6 +904,8 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, · 369 unmodified lines
829904 ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0))
830905 cifs_dbg(VFS, "DFS capability contradicts DFS flag\n");
831906 init_copy_chunk_defaults(tcon);
907+ if (tcon->ses->server->ops->validate_negotiate)
908+ rc = tcon->ses->server->ops->validate_negotiate(xid, tcon);
832909 tcon_exit:
833910 free_rsp_buf(resp_buftype, rsp);
834911 kfree(unc_path);
@@ -1214,10 +1291,17 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, · 379 unmodified lines
12141291 rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
12151292 rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base;
12161293
1217− if (rc != 0) {
1294+ if ((rc != 0) && (rc != -EINVAL)) {
12181295 if (tcon)
12191296 cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
12201297 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+ }
12211305 }
12221306
12231307 /* 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, · 930 unmodified lines
21542238 rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0);
21552239 rsp = (struct smb2_set_info_rsp *)iov[0].iov_base;
21562240
2157− if (rc != 0) {
2241+ if (rc != 0)
21582242 cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE);
2159− goto out;
2160− }
2161−out:
2243+
21622244 free_rsp_buf(resp_buftype, rsp);
21632245 kfree(iov);
21642246 return rc;
fs/cifs/smb2pdu.h+9−3
@@ -577,13 +577,19 @@ struct copychunk_ioctl_rsp { · 576 unmodified lines
577577 __le32 TotalBytesWritten;
578578 } __packed;
579579
580−/* Response and Request are the same format */
581−struct validate_negotiate_info {
580+struct validate_negotiate_info_req {
582581 __le32 Capabilities;
583582 __u8 Guid[SMB2_CLIENT_GUID_SIZE];
584583 __le16 SecurityMode;
585584 __le16 DialectCount;
586− __le16 Dialect[1];
585+ __le16 Dialects[1]; /* dialect (someday maybe list) client asked for */
586+} __packed;
587+
588+struct 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 */
587593 } __packed;
588594
589595 #define RSS_CAPABLE 0x00000001
fs/cifs/smb2proto.h+1−0
@@ -162,5 +162,6 @@ extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, · 161 unmodified lines
162162 struct smb2_lock_element *buf);
163163 extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
164164 __u8 *lease_key, const __le32 lease_state);
165+extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *);
165166
166167 #endif /* _SMB2PROTO_H */
fs/cifs/smbfsctl.h+1−1
@@ -90,7 +90,7 @@ · 89 unmodified lines
9090 #define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 /* BB add struct */
9191 #define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */
9292 #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
9494 /* Perform server-side data movement */
9595 #define FSCTL_SRV_COPYCHUNK 0x001440F2
9696 #define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2