Re: [PATCH] cifs: add IOCTL for QUERY_INFO passthrough to userspace

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Steve,

Attached is a trivial program to query and partially decode the security descriptor from userspace
to illustrate how it can be used.



----- Original Message -----
From: "Ronnie Sahlberg" <lsahlber@xxxxxxxxxx>
To: "linux-cifs" <linux-cifs@xxxxxxxxxxxxxxx>
Cc: "Steve French" <smfrench@xxxxxxxxx>
Sent: Thursday, 13 September, 2018 12:57:13 PM
Subject: [PATCH] cifs: add IOCTL for QUERY_INFO passthrough to userspace

This allows userspace tools to query the raw info levels for cifs files
and process the response in userspace.
In particular this is useful for many of those data where there is no
corresponding native data structure in linux.
For example querying the security descriptor for a file and extract the
SIDs.

Signed-off-by: Ronnie Sahlberg <lsahlber@xxxxxxxxxx>
---
 fs/cifs/cifs_ioctl.h |  9 +++++++
 fs/cifs/cifsglob.h   |  4 ++++
 fs/cifs/ioctl.c      | 18 ++++++++++++++
 fs/cifs/smb2ops.c    | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 98 insertions(+)

diff --git a/fs/cifs/cifs_ioctl.h b/fs/cifs/cifs_ioctl.h
index 57ff0756e30c..d8b7353260f6 100644
--- a/fs/cifs/cifs_ioctl.h
+++ b/fs/cifs/cifs_ioctl.h
@@ -43,8 +43,17 @@ struct smb_snapshot_array {
 	/*	snapshots[]; */
 } __packed;
 
+struct smb_query_info {
+	__u32	buffer_length;
+	__u32   info_type;
+	__u32   file_info_class;
+	__u32   additional_information;
+	/* char buffer[]; */
+} __packed;
+
 #define CIFS_IOCTL_MAGIC	0xCF
 #define CIFS_IOC_COPYCHUNK_FILE	_IOW(CIFS_IOCTL_MAGIC, 3, int)
 #define CIFS_IOC_SET_INTEGRITY  _IO(CIFS_IOCTL_MAGIC, 4)
 #define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info)
 #define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array)
+#define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info)
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 9dcaed031843..bb9e5b90923d 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -465,6 +465,10 @@ struct smb_version_operations {
 	enum securityEnum (*select_sectype)(struct TCP_Server_Info *,
 			    enum securityEnum);
 	int (*next_header)(char *);
+	/* ioctl passthrough for query_info */
+	int (*ioctl_query_info)(const unsigned int xid,
+				struct cifsFileInfo *file,
+				unsigned long p);
 };
 
 struct smb_version_values {
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index 54f32f9143a9..1e6b399ebb63 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -34,6 +34,21 @@
 #include "cifs_ioctl.h"
 #include <linux/btrfs.h>
 
+static long cifs_ioctl_query_info(unsigned int xid, struct file *filep,
+				  unsigned long p)
+{
+	struct cifsFileInfo *pSMBFile = filep->private_data;
+	struct cifs_tcon *tcon;
+
+	tcon = tlink_tcon(pSMBFile->tlink);
+
+	if (tcon->ses->server->ops->ioctl_query_info)
+		return tcon->ses->server->ops->ioctl_query_info(
+				xid, pSMBFile, p);
+	else
+		return -EOPNOTSUPP;
+}
+
 static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
 			unsigned long srcfd)
 {
@@ -196,6 +211,9 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
 		case CIFS_IOC_COPYCHUNK_FILE:
 			rc = cifs_ioctl_copychunk(xid, filep, arg);
 			break;
+		case CIFS_QUERY_INFO:
+			rc = cifs_ioctl_query_info(xid, filep, arg);
+			break;
 		case CIFS_IOC_SET_INTEGRITY:
 			if (pSMBFile == NULL)
 				break;
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 23118b7a2bbc..8c9f1c638e0f 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -1057,6 +1057,69 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+static int
+smb2_ioctl_query_info(const unsigned int xid,
+		      struct cifsFileInfo *file,
+		      unsigned long p)
+{
+	struct cifs_tcon *tcon = tlink_tcon(file->tlink);
+	struct cifs_ses *ses = tcon->ses;
+	void __user *arg = (void __user *)p;
+	struct smb_query_info qi, *pqi;
+	int rc = 0;
+	int flags = 0;
+	struct smb_rqst rqst;
+	struct kvec iov[1];
+	struct kvec rsp_iov;
+	int resp_buftype;
+	struct smb2_query_info_rsp *rsp = NULL;
+
+	if (copy_from_user(&qi, arg, sizeof(struct smb_query_info)))
+		return -EFAULT;
+
+	if (!ses || !(ses->server))
+		return -EIO;
+
+	if (smb3_encryption_required(tcon))
+		flags |= CIFS_TRANSFORM_REQ;
+
+	memset(&rqst, 0, sizeof(struct smb_rqst));
+	memset(&iov, 0, sizeof(iov));
+	rqst.rq_iov = iov;
+	rqst.rq_nvec = 1;
+
+	rc = SMB2_query_info_init(tcon, &rqst, file->fid.persistent_fid,
+				  file->fid.volatile_fid,
+				  qi.file_info_class, qi.info_type,
+				  qi.additional_information,
+				  qi.buffer_length);
+	if (rc)
+		goto iqinf_exit;
+
+	rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
+	rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
+	if (rc)
+		goto iqinf_exit;
+
+	pqi = arg;
+	if (le32_to_cpu(rsp->OutputBufferLength) < qi.buffer_length)
+		qi.buffer_length = le32_to_cpu(rsp->OutputBufferLength);
+	if (copy_to_user(&pqi->buffer_length, &qi.buffer_length,
+			 sizeof(qi.buffer_length))) {
+		rc = -EFAULT;
+		goto iqinf_exit;
+	}
+	if (copy_to_user(pqi + 1, rsp->Buffer, qi.buffer_length)) {
+		rc = -EFAULT;
+		goto iqinf_exit;
+	}
+
+ iqinf_exit:
+	SMB2_query_info_free(&rqst);
+	free_rsp_buf(resp_buftype, rsp);
+	return rc;
+}
+
 static ssize_t
 smb2_copychunk_range(const unsigned int xid,
 			struct cifsFileInfo *srcfile,
@@ -3303,6 +3366,7 @@ struct smb_version_operations smb20_operations = {
 	.set_acl = set_smb2_acl,
 #endif /* CIFS_ACL */
 	.next_header = smb2_next_header,
+	.ioctl_query_info = smb2_ioctl_query_info,
 };
 
 struct smb_version_operations smb21_operations = {
@@ -3398,6 +3462,7 @@ struct smb_version_operations smb21_operations = {
 	.set_acl = set_smb2_acl,
 #endif /* CIFS_ACL */
 	.next_header = smb2_next_header,
+	.ioctl_query_info = smb2_ioctl_query_info,
 };
 
 struct smb_version_operations smb30_operations = {
@@ -3502,6 +3567,7 @@ struct smb_version_operations smb30_operations = {
 	.set_acl = set_smb2_acl,
 #endif /* CIFS_ACL */
 	.next_header = smb2_next_header,
+	.ioctl_query_info = smb2_ioctl_query_info,
 };
 
 struct smb_version_operations smb311_operations = {
@@ -3607,6 +3673,7 @@ struct smb_version_operations smb311_operations = {
 	.set_acl = set_smb2_acl,
 #endif /* CIFS_ACL */
 	.next_header = smb2_next_header,
+	.ioctl_query_info = smb2_ioctl_query_info,
 };
 
 struct smb_version_values smb20_values = {
-- 
2.13.3

#include <endian.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define CIFS_IOCTL_MAGIC 0xCF
struct smb_query_info {
	uint32_t input_buffer_length;
	uint32_t info_type;
	uint32_t file_info_class;
	uint32_t additional_information;
} __packed;

#define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info)

static void
usage(char *name)
{
	fprintf(stderr, "Usage: %s <file>\n"
		"  Prints the security descriptor for cifs file.\n",
		name);
	exit(1);
}

static void
print_sid(unsigned char *sd)
{
	int i;
	uint32_t subauth;
	uint64_t idauth;

	if (sd[0] != 1) {
		fprintf(stderr, "Unknown SID revision\n");
		return;
	}

	idauth = 0;
	for (i = 0; i < 6; i++)
		idauth = (idauth << 8) | sd[2 + i];
	
	printf("S-1-%d", idauth);
	for (i = 0; i < sd[1]; i++) {
		memcpy(&subauth, &sd[8 + 4 * i], 4);
		subauth = le32toh(subauth);
		printf("-%d", subauth);
	}
}
	
static void
print_acl(unsigned char *sd)
{
	int i, j, off;
	uint16_t count, size;

	if (sd[0] != 2) {
		fprintf(stderr, "Unknown ACL revision\n");
		return;
	}

	memcpy(&count, &sd[4], 2);
	count = le16toh(count);
	off = 8;
	for (i = 0; i < count; i++) {
		printf("Type:%02x Flags:%02x ", sd[off], sd[off + 1]);
		memcpy(&size, &sd[off + 2], 2);
		size = le16toh(size);

		for (j = 0; j < size; j++)
			printf("%02x", sd[off + 4 + j]);

		off += size;
		printf("\n");
	}
}

static void
print_sd(unsigned char *sd)
{
	int offset_owner, offset_group, offset_dacl;

	printf("Revision:%d\n", sd[0]);
	if (sd[0] != 1) {
		fprintf(stderr, "Unknown SD revision\n");
		exit(1);
	}

	printf("Control: %02x%02x\n", sd[2], sd[3]);

	memcpy(&offset_owner, &sd[4], 4);
	offset_owner = le32toh(offset_owner);
	memcpy(&offset_group, &sd[8], 4);
	offset_group = le32toh(offset_group);
	memcpy(&offset_dacl, &sd[16], 4);
	offset_dacl = le32toh(offset_dacl);

	if (offset_owner) {
		printf("Owner: ");
		print_sid(&sd[offset_owner]);
		printf("\n");
	}
	if (offset_group) {
		printf("Group: ");
		print_sid(&sd[offset_group]);
		printf("\n");
	}
	if (offset_dacl) {
		printf("DACL:\n");
		print_acl(&sd[offset_dacl]);
	}
}


int main(int argc, char *argv[])
{
	int f;
	struct smb_query_info *qi;

	if (argc != 2)
		usage(argv[0]);
	  
	if ((f = open(argv[1], O_RDONLY)) < 0) {
		fprintf(stderr, "Failed to open %s\n", argv[1]);
		exit(1);
	}


	qi = malloc(sizeof(struct smb_query_info) + 16384);
	qi->info_type = 0x03;
	qi->file_info_class = 0;
	qi->additional_information = 0x00000007; /* Owner, Group, Dacl */
	qi->input_buffer_length = 16384;

	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
		fprintf(stderr, "ioctl failed with %d\n", errno);
		exit(1);
	}

	print_sd((char *)(&qi[1]));

	close(f);
	return 0;
}

[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux