Merge tag 'cifs-bug-fixes-for-4.13' of git://git.samba.org/sfrench/cifs-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 8 Jul 2017 00:44:36 +0000 (17:44 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 8 Jul 2017 00:44:36 +0000 (17:44 -0700)
Pull cifs fixes from Steve French:
 "First set of CIFS/SMB3 fixes for the merge window. Also improves POSIX
  character mapping for SMB3"

* tag 'cifs-bug-fixes-for-4.13' of git://git.samba.org/sfrench/cifs-2.6:
  CIFS: fix circular locking dependency
  cifs: set oparms.create_options rather than or'ing in CREATE_OPEN_BACKUP_INTENT
  cifs: Do not modify mid entry after submitting I/O in cifs_call_async
  CIFS: add SFM mapping for 0x01-0x1F
  cifs: hide unused functions
  cifs: Use smb 2 - 3 and cifsacl mount options getacl functions
  cifs: prototype declaration and definition for smb 2 - 3 and cifsacl mount options
  CIFS: add CONFIG_CIFS_DEBUG_KEYS to dump encryption keys
  cifs: set mapping error when page writeback fails in writepage or launder_pages
  SMB3: Enable encryption for SMB3.1.1

fs/cifs/Kconfig
fs/cifs/cifs_unicode.c
fs/cifs/file.c
fs/cifs/smb2ops.c
fs/cifs/smb2pdu.c
fs/cifs/smb2proto.h
fs/cifs/smb2transport.c
fs/cifs/transport.c

index 034f00f..afeefe7 100644 (file)
@@ -146,6 +146,15 @@ config CIFS_DEBUG2
           option can be turned off unless you are debugging
           cifs problems.  If unsure, say N.
 
+config CIFS_DEBUG_DUMP_KEYS
+       bool "Dump encryption keys for offline decryption (Unsafe)"
+       depends on CIFS_DEBUG && CIFS_SMB2
+       help
+          Enabling this will dump the encryption and decryption keys
+          used to communicate on an encrypted share connection on the
+          console. This allows Wireshark to decrypt and dissect
+          encrypted network captures. Enable this carefully.
+
 config CIFS_DFS_UPCALL
          bool "DFS feature support"
          depends on CIFS && KEYS
index a0b3e7d..e0445e2 100644 (file)
@@ -79,6 +79,10 @@ convert_sfu_char(const __u16 src_char, char *target)
 static bool
 convert_sfm_char(const __u16 src_char, char *target)
 {
+       if (src_char >= 0xF001 && src_char <= 0xF01F) {
+               *target = src_char - 0xF000;
+               return true;
+       }
        switch (src_char) {
        case SFM_COLON:
                *target = ':';
@@ -417,6 +421,10 @@ static __le16 convert_to_sfm_char(char src_char, bool end_of_string)
 {
        __le16 dest_char;
 
+       if (src_char >= 0x01 && src_char <= 0x1F) {
+               dest_char = cpu_to_le16(src_char + 0xF000);
+               return dest_char;
+       }
        switch (src_char) {
        case ':':
                dest_char = cpu_to_le16(SFM_COLON);
index fcef706..bc09df6 100644 (file)
@@ -2234,14 +2234,16 @@ cifs_writepage_locked(struct page *page, struct writeback_control *wbc)
        set_page_writeback(page);
 retry_write:
        rc = cifs_partialpagewrite(page, 0, PAGE_SIZE);
-       if (rc == -EAGAIN && wbc->sync_mode == WB_SYNC_ALL)
-               goto retry_write;
-       else if (rc == -EAGAIN)
+       if (rc == -EAGAIN) {
+               if (wbc->sync_mode == WB_SYNC_ALL)
+                       goto retry_write;
                redirty_page_for_writepage(wbc, page);
-       else if (rc != 0)
+       } else if (rc != 0) {
                SetPageError(page);
-       else
+               mapping_set_error(page->mapping, rc);
+       } else {
                SetPageUptodate(page);
+       }
        end_page_writeback(page);
        put_page(page);
        free_xid(xid);
@@ -2810,12 +2812,12 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from)
        struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
        ssize_t rc;
 
+       inode_lock(inode);
        /*
         * We need to hold the sem to be sure nobody modifies lock list
         * with a brlock that prevents writing.
         */
        down_read(&cinode->lock_sem);
-       inode_lock(inode);
 
        rc = generic_write_checks(iocb, from);
        if (rc <= 0)
@@ -2828,11 +2830,11 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from)
        else
                rc = -EACCES;
 out:
+       up_read(&cinode->lock_sem);
        inode_unlock(inode);
 
        if (rc > 0)
                rc = generic_write_sync(iocb, rc);
-       up_read(&cinode->lock_sem);
        return rc;
 }
 
index 7e48561..ccbb397 100644 (file)
@@ -1288,6 +1288,108 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
        return rc;
 }
 
+#ifdef CONFIG_CIFS_ACL
+static struct cifs_ntsd *
+get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb,
+               const struct cifs_fid *cifsfid, u32 *pacllen)
+{
+       struct cifs_ntsd *pntsd = NULL;
+       unsigned int xid;
+       int rc = -EOPNOTSUPP;
+       struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
+
+       if (IS_ERR(tlink))
+               return ERR_CAST(tlink);
+
+       xid = get_xid();
+       cifs_dbg(FYI, "trying to get acl\n");
+
+       rc = SMB2_query_acl(xid, tlink_tcon(tlink), cifsfid->persistent_fid,
+                           cifsfid->volatile_fid, (void **)&pntsd, pacllen);
+       free_xid(xid);
+
+       cifs_put_tlink(tlink);
+
+       cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen);
+       if (rc)
+               return ERR_PTR(rc);
+       return pntsd;
+
+}
+
+static struct cifs_ntsd *
+get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb,
+               const char *path, u32 *pacllen)
+{
+       struct cifs_ntsd *pntsd = NULL;
+       u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+       unsigned int xid;
+       int rc;
+       struct cifs_tcon *tcon;
+       struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
+       struct cifs_fid fid;
+       struct cifs_open_parms oparms;
+       __le16 *utf16_path;
+
+       cifs_dbg(FYI, "get smb3 acl for path %s\n", path);
+       if (IS_ERR(tlink))
+               return ERR_CAST(tlink);
+
+       tcon = tlink_tcon(tlink);
+       xid = get_xid();
+
+       if (backup_cred(cifs_sb))
+               oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
+       else
+               oparms.create_options = 0;
+
+       utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+       if (!utf16_path)
+               return ERR_PTR(-ENOMEM);
+
+       oparms.tcon = tcon;
+       oparms.desired_access = READ_CONTROL;
+       oparms.disposition = FILE_OPEN;
+       oparms.fid = &fid;
+       oparms.reconnect = false;
+
+       rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
+       kfree(utf16_path);
+       if (!rc) {
+               rc = SMB2_query_acl(xid, tlink_tcon(tlink), fid.persistent_fid,
+                           fid.volatile_fid, (void **)&pntsd, pacllen);
+               SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+       }
+
+       cifs_put_tlink(tlink);
+       free_xid(xid);
+
+       cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen);
+       if (rc)
+               return ERR_PTR(rc);
+       return pntsd;
+}
+
+/* Retrieve an ACL from the server */
+static struct cifs_ntsd *
+get_smb2_acl(struct cifs_sb_info *cifs_sb,
+                                     struct inode *inode, const char *path,
+                                     u32 *pacllen)
+{
+       struct cifs_ntsd *pntsd = NULL;
+       struct cifsFileInfo *open_file = NULL;
+
+       if (inode)
+               open_file = find_readable_file(CIFS_I(inode), true);
+       if (!open_file)
+               return get_smb2_acl_by_path(cifs_sb, path, pacllen);
+
+       pntsd = get_smb2_acl_by_fid(cifs_sb, &open_file->fid, pacllen);
+       cifsFileInfo_put(open_file);
+       return pntsd;
+}
+#endif
+
 static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
                            loff_t offset, loff_t len, bool keep_size)
 {
@@ -2393,6 +2495,11 @@ struct smb_version_operations smb20_operations = {
        .dir_needs_close = smb2_dir_needs_close,
        .get_dfs_refer = smb2_get_dfs_refer,
        .select_sectype = smb2_select_sectype,
+#ifdef CONFIG_CIFS_ACL
+       .get_acl = get_smb2_acl,
+       .get_acl_by_fid = get_smb2_acl_by_fid,
+/*     .set_acl = set_smb3_acl, */
+#endif /* CIFS_ACL */
 };
 
 struct smb_version_operations smb21_operations = {
@@ -2477,6 +2584,11 @@ struct smb_version_operations smb21_operations = {
        .enum_snapshots = smb3_enum_snapshots,
        .get_dfs_refer = smb2_get_dfs_refer,
        .select_sectype = smb2_select_sectype,
+#ifdef CONFIG_CIFS_ACL
+       .get_acl = get_smb2_acl,
+       .get_acl_by_fid = get_smb2_acl_by_fid,
+/*     .set_acl = set_smb3_acl, */
+#endif /* CIFS_ACL */
 };
 
 struct smb_version_operations smb30_operations = {
@@ -2571,6 +2683,11 @@ struct smb_version_operations smb30_operations = {
        .receive_transform = smb3_receive_transform,
        .get_dfs_refer = smb2_get_dfs_refer,
        .select_sectype = smb2_select_sectype,
+#ifdef CONFIG_CIFS_ACL
+       .get_acl = get_smb2_acl,
+       .get_acl_by_fid = get_smb2_acl_by_fid,
+/*     .set_acl = set_smb3_acl, */
+#endif /* CIFS_ACL */
 };
 
 #ifdef CONFIG_CIFS_SMB311
@@ -2753,7 +2870,7 @@ struct smb_version_values smb302_values = {
 struct smb_version_values smb311_values = {
        .version_string = SMB311_VERSION_STRING,
        .protocol_id = SMB311_PROT_ID,
-       .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES,
+       .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION,
        .large_lock_type = 0,
        .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
        .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
index e4afdaa..4938e8b 100644 (file)
@@ -2081,8 +2081,9 @@ validate_and_copy_buf(unsigned int offset, unsigned int buffer_length,
 
 static int
 query_info(const unsigned int xid, struct cifs_tcon *tcon,
-          u64 persistent_fid, u64 volatile_fid, u8 info_class,
-          size_t output_len, size_t min_len, void *data)
+          u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type,
+          u32 additional_info, size_t output_len, size_t min_len, void **data,
+               u32 *dlen)
 {
        struct smb2_query_info_req *req;
        struct smb2_query_info_rsp *rsp = NULL;
@@ -2108,10 +2109,11 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
        if (encryption_required(tcon))
                flags |= CIFS_TRANSFORM_REQ;
 
-       req->InfoType = SMB2_O_INFO_FILE;
+       req->InfoType = info_type;
        req->FileInfoClass = info_class;
        req->PersistentFileId = persistent_fid;
        req->VolatileFileId = volatile_fid;
+       req->AdditionalInformation = cpu_to_le32(additional_info);
        /* 4 for rfc1002 length field and 1 for Buffer */
        req->InputBufferOffset =
                cpu_to_le16(sizeof(struct smb2_query_info_req) - 1 - 4);
@@ -2130,24 +2132,51 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
                goto qinf_exit;
        }
 
+       if (dlen) {
+               *dlen = le32_to_cpu(rsp->OutputBufferLength);
+               if (!*data) {
+                       *data = kmalloc(*dlen, GFP_KERNEL);
+                       if (!*data) {
+                               cifs_dbg(VFS,
+                                       "Error %d allocating memory for acl\n",
+                                       rc);
+                               *dlen = 0;
+                               goto qinf_exit;
+                       }
+               }
+       }
+
        rc = validate_and_copy_buf(le16_to_cpu(rsp->OutputBufferOffset),
                                   le32_to_cpu(rsp->OutputBufferLength),
-                                  &rsp->hdr, min_len, data);
+                                  &rsp->hdr, min_len, *data);
 
 qinf_exit:
        free_rsp_buf(resp_buftype, rsp);
        return rc;
 }
 
+int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
+       u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data)
+{
+       return query_info(xid, tcon, persistent_fid, volatile_fid,
+                         FILE_ALL_INFORMATION, SMB2_O_INFO_FILE, 0,
+                         sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
+                         sizeof(struct smb2_file_all_info), (void **)&data,
+                         NULL);
+}
+
 int
-SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
+SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
                u64 persistent_fid, u64 volatile_fid,
-               struct smb2_file_all_info *data)
+               void **data, u32 *plen)
 {
+       __u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO;
+       *plen = 0;
+
        return query_info(xid, tcon, persistent_fid, volatile_fid,
-                         FILE_ALL_INFORMATION,
-                         sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
-                         sizeof(struct smb2_file_all_info), data);
+                         0, SMB2_O_INFO_SECURITY, additional_info,
+                         SMB2_MAX_BUFFER_SIZE,
+                         sizeof(struct smb2_file_all_info), data, plen);
 }
 
 int
@@ -2155,9 +2184,10 @@ SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
                 u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid)
 {
        return query_info(xid, tcon, persistent_fid, volatile_fid,
-                         FILE_INTERNAL_INFORMATION,
+                         FILE_INTERNAL_INFORMATION, SMB2_O_INFO_FILE, 0,
+                         sizeof(struct smb2_file_internal_info),
                          sizeof(struct smb2_file_internal_info),
-                         sizeof(struct smb2_file_internal_info), uniqueid);
+                         (void **)&uniqueid, NULL);
 }
 
 /*
index 6853454..3595cd7 100644 (file)
@@ -135,6 +135,9 @@ extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon,
 extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
                           u64 persistent_file_id, u64 volatile_file_id,
                           struct smb2_file_all_info *data);
+extern int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
+                          u64 persistent_file_id, u64 volatile_file_id,
+                          void **data, unsigned int *plen);
 extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
                            u64 persistent_fid, u64 volatile_fid,
                            __le64 *uniqueid);
index c69ec96..67367cf 100644 (file)
@@ -335,9 +335,31 @@ generate_smb3signingkey(struct cifs_ses *ses,
        if (rc)
                return rc;
 
-       return generate_key(ses, ptriplet->decryption.label,
-                           ptriplet->decryption.context,
-                           ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
+       rc = generate_key(ses, ptriplet->decryption.label,
+                         ptriplet->decryption.context,
+                         ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
+
+       if (rc)
+               return rc;
+
+#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS
+       cifs_dbg(VFS, "%s: dumping generated AES session keys\n", __func__);
+       /*
+        * The session id is opaque in terms of endianness, so we can't
+        * print it as a long long. we dump it as we got it on the wire
+        */
+       cifs_dbg(VFS, "Session Id    %*ph\n", (int)sizeof(ses->Suid),
+                       &ses->Suid);
+       cifs_dbg(VFS, "Session Key   %*ph\n",
+                SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response);
+       cifs_dbg(VFS, "Signing Key   %*ph\n",
+                SMB3_SIGN_KEY_SIZE, ses->smb3signingkey);
+       cifs_dbg(VFS, "ServerIn Key  %*ph\n",
+                SMB3_SIGN_KEY_SIZE, ses->smb3encryptionkey);
+       cifs_dbg(VFS, "ServerOut Key %*ph\n",
+                SMB3_SIGN_KEY_SIZE, ses->smb3decryptionkey);
+#endif
+       return rc;
 }
 
 int
index 47a125e..7efbab0 100644 (file)
@@ -536,11 +536,14 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
        list_add_tail(&mid->qhead, &server->pending_mid_q);
        spin_unlock(&GlobalMid_Lock);
 
-
+       /*
+        * Need to store the time in mid before calling I/O. For call_async,
+        * I/O response may come back and free the mid entry on another thread.
+        */
+       cifs_save_when_sent(mid);
        cifs_in_send_inc(server);
        rc = smb_send_rqst(server, rqst, flags);
        cifs_in_send_dec(server);
-       cifs_save_when_sent(mid);
 
        if (rc < 0) {
                server->sequence_number -= 2;