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, 0, smb2_fsctl_resiliency }, 279 { FSCTL_QUERY_NETWORK_INTERFACE_INFO, 280 ITF_NO_FID, smb2_fsctl_notsup }, 281 { FSCTL_VALIDATE_NEGOTIATE_INFO, 282 ITF_NO_FID, smb2_fsctl_vneginfo }, 283 284 /* 285 * End marker 286 */ 287 { 0, 0, 0 } 288 }; 289