xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_ioctl.c (revision 93bc28dbaee6387120d48b12b3dc1ba5f7418e6e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
26  */
27 
28 /*
29  * Dispatch function for SMB2_IOCTL
30  * [MS-SMB2] 3.3.5.15
31  */
32 
33 #include <smbsrv/smb2_kproto.h>
34 #include <smb/winioctl.h>
35 
36 struct smb2_ioctbl_ent {
37 	uint32_t	te_code;
38 	uint32_t	te_flags;
39 	uint32_t	(*te_func)(smb_request_t *, smb_fsctl_t *);
40 };
41 static struct smb2_ioctbl_ent smb2_ioc_tbl[];
42 
43 /* te_flags */
44 #define	ITF_IPC_ONLY	1
45 #define	ITF_NO_FID	2
46 #define	ITF_DISK_FID	4
47 
48 smb_sdrc_t
49 smb2_ioctl(smb_request_t *sr)
50 {
51 	smb2fid_t smb2fid;
52 	smb_fsctl_t fsctl;
53 	mbuf_chain_t in_mbc;
54 	struct smb2_ioctbl_ent *te;
55 	uint32_t InputOffset;
56 	uint32_t MaxInputResp;
57 	uint32_t OutputOffset;
58 	uint32_t Flags;
59 	uint32_t status = 0;
60 	uint16_t StructSize;
61 	int rc = 0;
62 
63 	/* Todo: put fsctl in sr->arg.ioctl (visible in dtrace probes) */
64 	bzero(&in_mbc, sizeof (in_mbc));
65 
66 	/*
67 	 * Decode SMB2 Ioctl request
68 	 */
69 	rc = smb_mbc_decodef(
70 	    &sr->smb_data, "w..lqqlllllll4.",
71 	    &StructSize,		/* w */
72 	    /* reserved			  .. */
73 	    &fsctl.CtlCode,		/* l */
74 	    &smb2fid.persistent,	/* q */
75 	    &smb2fid.temporal,		/* q */
76 	    &InputOffset,		/* l */
77 	    &fsctl.InputCount,		/* l */
78 	    &MaxInputResp,		/* l */
79 	    &OutputOffset,		/* l */
80 	    &fsctl.OutputCount,		/* l */
81 	    &fsctl.MaxOutputResp,	/* l */
82 	    &Flags);			/* l */
83 	    /* reserved2		  4. */
84 	if (rc || StructSize != 57)
85 		return (SDRC_ERROR);
86 
87 	/*
88 	 * If there's an input buffer, setup a shadow.
89 	 */
90 	if (fsctl.InputCount) {
91 		if (InputOffset < (SMB2_HDR_SIZE + 56))
92 			return (SDRC_ERROR);
93 		if (fsctl.InputCount > smb2_max_trans)
94 			return (SDRC_ERROR);
95 		rc = MBC_SHADOW_CHAIN(&in_mbc, &sr->smb_data,
96 		    sr->smb2_cmd_hdr + InputOffset, fsctl.InputCount);
97 		if (rc) {
98 			return (SDRC_ERROR);
99 		}
100 	}
101 	fsctl.in_mbc = &in_mbc;
102 
103 	/*
104 	 * If output is possible, setup the output mbuf_chain
105 	 */
106 	if (fsctl.MaxOutputResp > smb2_max_trans)
107 		fsctl.MaxOutputResp = smb2_max_trans;
108 	sr->raw_data.max_bytes = fsctl.MaxOutputResp;
109 	fsctl.out_mbc = &sr->raw_data;
110 
111 	/*
112 	 * [MS-SMB2] 3.3.5.15
113 	 *
114 	 * If the Flags field of the request is not SMB2_0_IOCTL_IS_FSCTL
115 	 * the server MUST fail the request with STATUS_NOT_SUPPORTED.
116 	 *
117 	 * If the CtlCode is any of (... see switch below...) and the
118 	 * value of FileId in the SMB2 Header of the request is not
119 	 * 0xFFFFFFFFFFFFFFFF, then the server MUST fail the request
120 	 * with STATUS_INVALID_PARAMETER.  (Otherwise lookup the FID.)
121 	 */
122 	if (Flags != SMB2_0_IOCTL_IS_FSCTL) {
123 		status = NT_STATUS_NOT_SUPPORTED;
124 	} else switch (fsctl.CtlCode) {
125 	case FSCTL_DFS_GET_REFERRALS:
126 	case FSCTL_DFS_GET_REFERRALS_EX:
127 	case FSCTL_QUERY_NETWORK_INTERFACE_INFO:
128 	case FSCTL_VALIDATE_NEGOTIATE_INFO:
129 	case FSCTL_PIPE_WAIT:
130 		if (smb2fid.temporal != ~0LL ||
131 		    smb2fid.persistent != ~0LL) {
132 			status = NT_STATUS_INVALID_PARAMETER;
133 		}
134 		break;
135 	default:
136 		status = smb2sr_lookup_fid(sr, &smb2fid);
137 		if (status != 0) {
138 			status = NT_STATUS_FILE_CLOSED;
139 		}
140 		break;
141 	}
142 
143 	/*
144 	 * Keep FID lookup before the start probe.
145 	 */
146 	DTRACE_SMB2_START(op__Ioctl, smb_request_t *, sr);
147 
148 	if (status)
149 		goto errout;
150 
151 	for (te = smb2_ioc_tbl; te->te_code; te++) {
152 		if (te->te_code == fsctl.CtlCode)
153 			break;
154 	}
155 	if (te->te_code == 0) {
156 #ifdef	DEBUG
157 		cmn_err(CE_NOTE, "smb2_ioctl: unknown code 0x%x",
158 		    fsctl.CtlCode);
159 #endif
160 		status = NT_STATUS_NOT_SUPPORTED;
161 		goto errout;
162 	}
163 
164 	/*
165 	 * Some requests are only valid on IPC$
166 	 */
167 	if ((te->te_flags & ITF_IPC_ONLY) != 0 &&
168 	    !STYPE_ISIPC(sr->tid_tree->t_res_type)) {
169 		status = NT_STATUS_INVALID_DEVICE_REQUEST;
170 		goto errout;
171 	}
172 
173 	/*
174 	 * Note: some ioctls require a "disk" fid.
175 	 */
176 	if (te->te_flags & ITF_DISK_FID) {
177 		if (sr->fid_ofile == NULL ||
178 		    !SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) {
179 			status = NT_STATUS_INVALID_PARAMETER;
180 			goto errout;
181 		}
182 	}
183 
184 	/*
185 	 * Dispatch to the handler for CtlCode
186 	 */
187 	status = (te->te_func)(sr, &fsctl);
188 
189 errout:
190 	sr->smb2_status = status;
191 	DTRACE_SMB2_DONE(op__Ioctl, smb_request_t *, sr);
192 
193 	if (status != 0) {
194 		if (NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_ERROR) {
195 			/* no error data */
196 			smb2sr_put_error(sr, status);
197 			return (SDRC_SUCCESS);
198 		}
199 		/* Warnings like NT_STATUS_BUFFER_OVERFLOW are OK. */
200 	}
201 
202 	fsctl.InputCount = 0;
203 	InputOffset = SMB2_HDR_SIZE + 48;
204 
205 	fsctl.OutputCount = MBC_LENGTH(&sr->raw_data);
206 	OutputOffset = (fsctl.OutputCount) ? InputOffset : 0;
207 
208 	/*
209 	 * Encode SMB2 Ioctl reply
210 	 */
211 	StructSize = 49;
212 	rc = smb_mbc_encodef(
213 	    &sr->reply, "w..lqqlllll4.#C",
214 	    StructSize,			/* w */
215 	    /* reserved			  .. */
216 	    fsctl.CtlCode,		/* l */
217 	    smb2fid.persistent,		/* q */
218 	    smb2fid.temporal,		/* q */
219 	    InputOffset,		/* l */
220 	    fsctl.InputCount,		/* l */
221 	    OutputOffset,		/* l */
222 	    fsctl.OutputCount,		/* l */
223 	    Flags,			/* l */
224 	    /* reserved2		  4. */
225 	    fsctl.OutputCount,		/* # */
226 	    &sr->raw_data);		/* C */
227 	if (rc)
228 		sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
229 
230 	return (SDRC_SUCCESS);
231 }
232 
233 /* ARGSUSED */
234 static uint32_t
235 smb2_fsctl_notsup(smb_request_t *sr, smb_fsctl_t *fsctl)
236 {
237 	return (NT_STATUS_NOT_SUPPORTED);
238 }
239 
240 static struct smb2_ioctbl_ent
241 smb2_ioc_tbl[] = {
242 
243 	/*
244 	 * FILE_DEVICE_DFS (6)
245 	 */
246 	{ FSCTL_DFS_GET_REFERRALS,
247 	    ITF_IPC_ONLY | ITF_NO_FID,		smb_dfs_get_referrals },
248 	{ FSCTL_DFS_GET_REFERRALS_EX,
249 	    ITF_IPC_ONLY | ITF_NO_FID,		smb_dfs_get_referrals },
250 
251 	/*
252 	 * FILE_DEVICE_FILE_SYSTEM (9)
253 	 */
254 	{ FSCTL_SET_REPARSE_POINT,	0,	smb2_fsctl_notsup },
255 	{ FSCTL_CREATE_OR_GET_OBJECT_ID, 0,	smb2_fsctl_notsup },
256 	{ FSCTL_FILE_LEVEL_TRIM,	0,	smb2_fsctl_notsup },
257 
258 	/*
259 	 * FILE_DEVICE_NAMED_PIPE (17)
260 	 */
261 	{ FSCTL_PIPE_PEEK,
262 	    ITF_IPC_ONLY,			smb_opipe_fsctl },
263 	{ FSCTL_PIPE_TRANSCEIVE,
264 	    ITF_IPC_ONLY,			smb_opipe_fsctl },
265 	{ FSCTL_PIPE_WAIT,
266 	    ITF_IPC_ONLY | ITF_NO_FID,		smb_opipe_fsctl },
267 
268 	/*
269 	 * FILE_DEVICE_NETWORK_FILE_SYSTEM (20)
270 	 */
271 	{ FSCTL_SRV_ENUMERATE_SNAPSHOTS,
272 	    ITF_DISK_FID,			smb_vss_enum_snapshots },
273 	{ FSCTL_SRV_REQUEST_RESUME_KEY,	0,	smb2_fsctl_notsup },
274 	{ FSCTL_SRV_COPYCHUNK,		0,	smb2_fsctl_notsup },
275 	{ FSCTL_SRV_COPYCHUNK_WRITE,	0,	smb2_fsctl_notsup },
276 	{ FSCTL_SRV_READ_HASH,		0,	smb2_fsctl_notsup },
277 
278 	{ FSCTL_LMR_REQUEST_RESILIENCY,
279 	    ITF_NO_FID,		smb2_fsctl_notsup },
280 	{ FSCTL_QUERY_NETWORK_INTERFACE_INFO,
281 	    ITF_NO_FID,		smb2_fsctl_notsup },
282 	{ FSCTL_VALIDATE_NEGOTIATE_INFO,
283 	    ITF_NO_FID,		smb2_fsctl_vneginfo },
284 
285 	/*
286 	 * End marker
287 	 */
288 	{ 0, 0, 0 }
289 };
290