Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+187 -22
+1
fs/cifs/cifsglob.h
··· 384 384 int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file, 385 385 struct cifsFileInfo *target_file, u64 src_off, u64 len, 386 386 u64 dest_off); 387 + int (*validate_negotiate)(const unsigned int, struct cifs_tcon *); 387 388 }; 388 389 389 390 struct smb_version_values {
+4 -2
fs/cifs/ioctl.c
··· 26 26 #include <linux/mount.h> 27 27 #include <linux/mm.h> 28 28 #include <linux/pagemap.h> 29 - #include <linux/btrfs.h> 30 29 #include "cifspdu.h" 31 30 #include "cifsglob.h" 32 31 #include "cifsproto.h" 33 32 #include "cifs_debug.h" 34 33 #include "cifsfs.h" 34 + 35 + #define CIFS_IOCTL_MAGIC 0xCF 36 + #define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int) 35 37 36 38 static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file, 37 39 unsigned long srcfd, u64 off, u64 len, u64 destoff) ··· 215 213 cifs_dbg(FYI, "set compress flag rc %d\n", rc); 216 214 } 217 215 break; 218 - case BTRFS_IOC_CLONE: 216 + case CIFS_IOC_COPYCHUNK_FILE: 219 217 rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0); 220 218 break; 221 219 default:
+84 -11
fs/cifs/smb2ops.c
··· 532 532 int rc; 533 533 unsigned int ret_data_len; 534 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 540 pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); 538 541 ··· 550 547 551 548 /* Note: request_res_key sets res_key null only if rc !=0 */ 552 549 if (rc) 553 - return rc; 550 + goto cchunk_out; 554 551 555 552 /* For now array only one chunk long, will make more flexible later */ 556 553 pcchunk->ChunkCount = __constant_cpu_to_le32(1); 557 554 pcchunk->Reserved = 0; 558 - pcchunk->SourceOffset = cpu_to_le64(src_off); 559 - pcchunk->TargetOffset = cpu_to_le64(dest_off); 560 - pcchunk->Length = cpu_to_le32(len); 561 555 pcchunk->Reserved2 = 0; 562 556 563 - /* Request that server copy to target from src file identified by key */ 564 - rc = SMB2_ioctl(xid, tlink_tcon(trgtfile->tlink), 565 - trgtfile->fid.persistent_fid, 557 + tcon = tlink_tcon(trgtfile->tlink); 558 + 559 + while (len > 0) { 560 + pcchunk->SourceOffset = cpu_to_le64(src_off); 561 + pcchunk->TargetOffset = cpu_to_le64(dest_off); 562 + pcchunk->Length = 563 + cpu_to_le32(min_t(u32, len, tcon->max_bytes_chunk)); 564 + 565 + /* Request server copy to target from src identified by key */ 566 + rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, 566 567 trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, 567 568 true /* is_fsctl */, (char *)pcchunk, 568 - sizeof(struct copychunk_ioctl), &retbuf, &ret_data_len); 569 + sizeof(struct copychunk_ioctl), (char **)&retbuf, 570 + &ret_data_len); 571 + if (rc == 0) { 572 + if (ret_data_len != 573 + sizeof(struct copychunk_ioctl_rsp)) { 574 + cifs_dbg(VFS, "invalid cchunk response size\n"); 575 + rc = -EIO; 576 + goto cchunk_out; 577 + } 578 + if (retbuf->TotalBytesWritten == 0) { 579 + cifs_dbg(FYI, "no bytes copied\n"); 580 + rc = -EIO; 581 + goto cchunk_out; 582 + } 583 + /* 584 + * Check if server claimed to write more than we asked 585 + */ 586 + if (le32_to_cpu(retbuf->TotalBytesWritten) > 587 + le32_to_cpu(pcchunk->Length)) { 588 + cifs_dbg(VFS, "invalid copy chunk response\n"); 589 + rc = -EIO; 590 + goto cchunk_out; 591 + } 592 + if (le32_to_cpu(retbuf->ChunksWritten) != 1) { 593 + cifs_dbg(VFS, "invalid num chunks written\n"); 594 + rc = -EIO; 595 + goto cchunk_out; 596 + } 597 + chunks_copied++; 569 598 570 - /* BB need to special case rc = EINVAL to alter chunk size */ 599 + src_off += le32_to_cpu(retbuf->TotalBytesWritten); 600 + dest_off += le32_to_cpu(retbuf->TotalBytesWritten); 601 + len -= le32_to_cpu(retbuf->TotalBytesWritten); 571 602 572 - cifs_dbg(FYI, "rc %d data length out %d\n", rc, ret_data_len); 603 + cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %d\n", 604 + le32_to_cpu(retbuf->ChunksWritten), 605 + le32_to_cpu(retbuf->ChunkBytesWritten), 606 + le32_to_cpu(retbuf->TotalBytesWritten)); 607 + } else if (rc == -EINVAL) { 608 + if (ret_data_len != sizeof(struct copychunk_ioctl_rsp)) 609 + goto cchunk_out; 573 610 611 + cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n", 612 + le32_to_cpu(retbuf->ChunksWritten), 613 + le32_to_cpu(retbuf->ChunkBytesWritten), 614 + le32_to_cpu(retbuf->TotalBytesWritten)); 615 + 616 + /* 617 + * Check if this is the first request using these sizes, 618 + * (ie check if copy succeed once with original sizes 619 + * and check if the server gave us different sizes after 620 + * we already updated max sizes on previous request). 621 + * if not then why is the server returning an error now 622 + */ 623 + if ((chunks_copied != 0) || chunk_sizes_updated) 624 + goto cchunk_out; 625 + 626 + /* Check that server is not asking us to grow size */ 627 + if (le32_to_cpu(retbuf->ChunkBytesWritten) < 628 + tcon->max_bytes_chunk) 629 + tcon->max_bytes_chunk = 630 + le32_to_cpu(retbuf->ChunkBytesWritten); 631 + else 632 + goto cchunk_out; /* server gave us bogus size */ 633 + 634 + /* No need to change MaxChunks since already set to 1 */ 635 + chunk_sizes_updated = true; 636 + } 637 + } 638 + 639 + cchunk_out: 574 640 kfree(pcchunk); 575 641 return rc; 576 642 } ··· 1319 1247 .create_lease_buf = smb3_create_lease_buf, 1320 1248 .parse_lease_buf = smb3_parse_lease_buf, 1321 1249 .clone_range = smb2_clone_range, 1250 + .validate_negotiate = smb3_validate_negotiate, 1322 1251 }; 1323 1252 1324 1253 struct smb_version_values smb20_values = {
+87 -5
fs/cifs/smb2pdu.c
··· 454 454 return rc; 455 455 } 456 456 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 + 457 532 int 458 533 SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, 459 534 const struct nls_table *nls_cp) ··· 904 829 ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) 905 830 cifs_dbg(VFS, "DFS capability contradicts DFS flag\n"); 906 831 init_copy_chunk_defaults(tcon); 832 + if (tcon->ses->server->ops->validate_negotiate) 833 + rc = tcon->ses->server->ops->validate_negotiate(xid, tcon); 907 834 tcon_exit: 908 835 free_rsp_buf(resp_buftype, rsp); 909 836 kfree(unc_path); ··· 1291 1214 rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); 1292 1215 rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base; 1293 1216 1294 - if (rc != 0) { 1217 + if ((rc != 0) && (rc != -EINVAL)) { 1295 1218 if (tcon) 1296 1219 cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); 1297 1220 goto ioctl_exit; 1221 + } else if (rc == -EINVAL) { 1222 + if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) && 1223 + (opcode != FSCTL_SRV_COPYCHUNK)) { 1224 + if (tcon) 1225 + cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); 1226 + goto ioctl_exit; 1227 + } 1298 1228 } 1299 1229 1300 1230 /* check if caller wants to look at return data or just return rc */ ··· 2238 2154 rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0); 2239 2155 rsp = (struct smb2_set_info_rsp *)iov[0].iov_base; 2240 2156 2241 - if (rc != 0) { 2157 + if (rc != 0) 2242 2158 cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE); 2243 - goto out; 2244 - } 2245 - out: 2159 + 2246 2160 free_rsp_buf(resp_buftype, rsp); 2247 2161 kfree(iov); 2248 2162 return rc;
+9 -3
fs/cifs/smb2pdu.h
··· 577 577 __le32 TotalBytesWritten; 578 578 } __packed; 579 579 580 - /* Response and Request are the same format */ 581 - struct validate_negotiate_info { 580 + struct validate_negotiate_info_req { 582 581 __le32 Capabilities; 583 582 __u8 Guid[SMB2_CLIENT_GUID_SIZE]; 584 583 __le16 SecurityMode; 585 584 __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 */ 587 593 } __packed; 588 594 589 595 #define RSS_CAPABLE 0x00000001
+1
fs/cifs/smb2proto.h
··· 162 162 struct smb2_lock_element *buf); 163 163 extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, 164 164 __u8 *lease_key, const __le32 lease_state); 165 + extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *); 165 166 166 167 #endif /* _SMB2PROTO_H */
+1 -1
fs/cifs/smbfsctl.h
··· 90 90 #define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 /* BB add struct */ 91 91 #define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ 92 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 94 /* Perform server-side data movement */ 95 95 #define FSCTL_SRV_COPYCHUNK 0x001440F2 96 96 #define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2