smb3: Fix rmdir compounding regression to strict servers
authorRonnie Sahlberg <lsahlber@redhat.com>
Tue, 18 Dec 2018 23:49:05 +0000 (17:49 -0600)
committerSteve French <stfrench@microsoft.com>
Wed, 19 Dec 2018 13:55:32 +0000 (07:55 -0600)
Some servers require that the setinfo matches the exact size,
and in this case compounding changes introduced by
commit c2e0fe3f5aae ("cifs: make rmdir() use compounding")
caused us to send 8 bytes (padded length) instead of 1 byte
(the size of the structure).  See MS-FSCC section 2.4.11.

Fixing this when we send a SET_INFO command for delete file
disposition, then ends up as an iov of a single byte but this
causes problems with SMB3 and encryption.

To avoid this, instead of creating a one byte iov for the disposition value
and then appending an additional iov with a 7 byte padding we now handle
this as a single 8 byte iov containing both the disposition byte as well as
the padding in one single buffer.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Acked-by: Paulo Alcantara <palcantara@suse.de>
fs/cifs/smb2inode.c
fs/cifs/smb2ops.c
fs/cifs/smb2proto.h

index 9e7ef7e..a8999f9 100644 (file)
@@ -97,7 +97,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
        if (rc)
                goto finished;
 
-       smb2_set_next_command(server, &rqst[num_rqst++]);
+       smb2_set_next_command(server, &rqst[num_rqst++], 0);
 
        /* Operation */
        switch (command) {
@@ -111,7 +111,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
                                SMB2_O_INFO_FILE, 0,
                                sizeof(struct smb2_file_all_info) +
                                          PATH_MAX * 2, 0, NULL);
-               smb2_set_next_command(server, &rqst[num_rqst]);
+               smb2_set_next_command(server, &rqst[num_rqst], 0);
                smb2_set_related(&rqst[num_rqst++]);
                break;
        case SMB2_OP_DELETE:
@@ -127,14 +127,14 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
                rqst[num_rqst].rq_iov = si_iov;
                rqst[num_rqst].rq_nvec = 1;
 
-               size[0] = 8;
+               size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */
                data[0] = &delete_pending[0];
 
                rc = SMB2_set_info_init(tcon, &rqst[num_rqst], COMPOUND_FID,
                                        COMPOUND_FID, current->tgid,
                                        FILE_DISPOSITION_INFORMATION,
                                        SMB2_O_INFO_FILE, 0, data, size);
-               smb2_set_next_command(server, &rqst[num_rqst]);
+               smb2_set_next_command(server, &rqst[num_rqst], 1);
                smb2_set_related(&rqst[num_rqst++]);
                break;
        case SMB2_OP_SET_EOF:
@@ -149,7 +149,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
                                        COMPOUND_FID, current->tgid,
                                        FILE_END_OF_FILE_INFORMATION,
                                        SMB2_O_INFO_FILE, 0, data, size);
-               smb2_set_next_command(server, &rqst[num_rqst]);
+               smb2_set_next_command(server, &rqst[num_rqst], 0);
                smb2_set_related(&rqst[num_rqst++]);
                break;
        case SMB2_OP_SET_INFO:
@@ -165,7 +165,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
                                        COMPOUND_FID, current->tgid,
                                        FILE_BASIC_INFORMATION,
                                        SMB2_O_INFO_FILE, 0, data, size);
-               smb2_set_next_command(server, &rqst[num_rqst]);
+               smb2_set_next_command(server, &rqst[num_rqst], 0);
                smb2_set_related(&rqst[num_rqst++]);
                break;
        case SMB2_OP_RENAME:
@@ -189,7 +189,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
                                        COMPOUND_FID, current->tgid,
                                        FILE_RENAME_INFORMATION,
                                        SMB2_O_INFO_FILE, 0, data, size);
-               smb2_set_next_command(server, &rqst[num_rqst]);
+               smb2_set_next_command(server, &rqst[num_rqst], 0);
                smb2_set_related(&rqst[num_rqst++]);
                break;
        case SMB2_OP_HARDLINK:
@@ -213,7 +213,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
                                        COMPOUND_FID, current->tgid,
                                        FILE_LINK_INFORMATION,
                                        SMB2_O_INFO_FILE, 0, data, size);
-               smb2_set_next_command(server, &rqst[num_rqst]);
+               smb2_set_next_command(server, &rqst[num_rqst], 0);
                smb2_set_related(&rqst[num_rqst++]);
                break;
        default:
index 225fec1..e25c7aa 100644 (file)
@@ -1194,7 +1194,7 @@ smb2_ioctl_query_info(const unsigned int xid,
        rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, path);
        if (rc)
                goto iqinf_exit;
-       smb2_set_next_command(ses->server, &rqst[0]);
+       smb2_set_next_command(ses->server, &rqst[0], 0);
 
        /* Query */
        memset(&qi_iov, 0, sizeof(qi_iov));
@@ -1208,7 +1208,7 @@ smb2_ioctl_query_info(const unsigned int xid,
                                  qi.output_buffer_length, buffer);
        if (rc)
                goto iqinf_exit;
-       smb2_set_next_command(ses->server, &rqst[1]);
+       smb2_set_next_command(ses->server, &rqst[1], 0);
        smb2_set_related(&rqst[1]);
 
        /* Close */
@@ -1761,16 +1761,23 @@ smb2_set_related(struct smb_rqst *rqst)
 char smb2_padding[7] = {0, 0, 0, 0, 0, 0, 0};
 
 void
-smb2_set_next_command(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+smb2_set_next_command(struct TCP_Server_Info *server, struct smb_rqst *rqst,
+                     bool has_space_for_padding)
 {
        struct smb2_sync_hdr *shdr;
        unsigned long len = smb_rqst_len(server, rqst);
 
        /* SMB headers in a compound are 8 byte aligned. */
        if (len & 7) {
-               rqst->rq_iov[rqst->rq_nvec].iov_base = smb2_padding;
-               rqst->rq_iov[rqst->rq_nvec].iov_len = 8 - (len & 7);
-               rqst->rq_nvec++;
+               if (has_space_for_padding) {
+                       len = rqst->rq_iov[rqst->rq_nvec - 1].iov_len;
+                       rqst->rq_iov[rqst->rq_nvec - 1].iov_len =
+                               (len + 7) & ~7;
+               } else {
+                       rqst->rq_iov[rqst->rq_nvec].iov_base = smb2_padding;
+                       rqst->rq_iov[rqst->rq_nvec].iov_len = 8 - (len & 7);
+                       rqst->rq_nvec++;
+               }
                len = smb_rqst_len(server, rqst);
        }
 
@@ -1820,7 +1827,7 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
        rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, &srch_path);
        if (rc)
                goto qfs_exit;
-       smb2_set_next_command(server, &rqst[0]);
+       smb2_set_next_command(server, &rqst[0], 0);
 
        memset(&qi_iov, 0, sizeof(qi_iov));
        rqst[1].rq_iov = qi_iov;
@@ -1833,7 +1840,7 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
                                  NULL);
        if (rc)
                goto qfs_exit;
-       smb2_set_next_command(server, &rqst[1]);
+       smb2_set_next_command(server, &rqst[1], 0);
        smb2_set_related(&rqst[1]);
 
        memset(&close_iov, 0, sizeof(close_iov));
index 9f4e9ed..2fe78ac 100644 (file)
@@ -117,7 +117,8 @@ extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server);
 extern unsigned long smb_rqst_len(struct TCP_Server_Info *server,
                                  struct smb_rqst *rqst);
 extern void smb2_set_next_command(struct TCP_Server_Info *server,
-                                 struct smb_rqst *rqst);
+                                 struct smb_rqst *rqst,
+                                 bool has_space_for_padding);
 extern void smb2_set_related(struct smb_rqst *rqst);
 
 /*