1/* 2 Unix SMB2 implementation. 3 4 Copyright (C) Stefan Metzmacher 2006 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18*/ 19 20#include "includes.h" 21#include "libcli/smb2/smb2.h" 22#include "libcli/smb2/smb2_calls.h" 23#include "smb_server/smb_server.h" 24#include "smb_server/smb2/smb2_server.h" 25#include "ntvfs/ntvfs.h" 26#include "librpc/gen_ndr/ndr_security.h" 27 28struct smb2srv_getinfo_op { 29 struct smb2srv_request *req; 30 struct smb2_getinfo *info; 31 void *io_ptr; 32 NTSTATUS (*send_fn)(struct smb2srv_getinfo_op *op); 33}; 34 35static void smb2srv_getinfo_send(struct ntvfs_request *ntvfs) 36{ 37 struct smb2srv_getinfo_op *op; 38 struct smb2srv_request *req; 39 40 /* 41 * SMB2 uses NT_STATUS_INVALID_INFO_CLASS 42 * so we need to translated it here 43 */ 44 if (NT_STATUS_EQUAL(NT_STATUS_INVALID_LEVEL, ntvfs->async_states->status)) { 45 ntvfs->async_states->status = NT_STATUS_INVALID_INFO_CLASS; 46 } 47 48 SMB2SRV_CHECK_ASYNC_STATUS(op, struct smb2srv_getinfo_op); 49 50 ZERO_STRUCT(op->info->out); 51 if (op->send_fn) { 52 SMB2SRV_CHECK(op->send_fn(op)); 53 } 54 55 if (op->info->in.output_buffer_length < op->info->out.blob.length) { 56 smb2srv_send_error(req, NT_STATUS_INFO_LENGTH_MISMATCH); 57 return; 58 } 59 60 SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, true, op->info->out.blob.length)); 61 62 SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, op->info->out.blob)); 63 SSVAL(req->out.body, 0x06, 0); 64 65 smb2srv_send_reply(req); 66} 67 68static NTSTATUS smb2srv_getinfo_file_send(struct smb2srv_getinfo_op *op) 69{ 70 union smb_fileinfo *io = talloc_get_type(op->io_ptr, union smb_fileinfo); 71 NTSTATUS status; 72 73 status = smbsrv_push_passthru_fileinfo(op->req, 74 &op->info->out.blob, 75 io->generic.level, io, 76 STR_UNICODE); 77 NT_STATUS_NOT_OK_RETURN(status); 78 79 return NT_STATUS_OK; 80} 81 82static NTSTATUS smb2srv_getinfo_file(struct smb2srv_getinfo_op *op, uint8_t smb2_level) 83{ 84 union smb_fileinfo *io; 85 uint16_t level; 86 87 io = talloc(op, union smb_fileinfo); 88 NT_STATUS_HAVE_NO_MEMORY(io); 89 90 level = op->info->in.info_type | (op->info->in.info_class << 8); 91 switch (level) { 92 case RAW_FILEINFO_SMB2_ALL_EAS: 93 io->all_eas.level = level; 94 io->all_eas.in.file.ntvfs = op->info->in.file.ntvfs; 95 io->all_eas.in.continue_flags = op->info->in.getinfo_flags; 96 break; 97 98 case RAW_FILEINFO_SMB2_ALL_INFORMATION: 99 io->all_info2.level = level; 100 io->all_info2.in.file.ntvfs = op->info->in.file.ntvfs; 101 break; 102 103 default: 104 /* the rest directly maps to the passthru levels */ 105 io->generic.level = smb2_level + 1000; 106 io->generic.in.file.ntvfs = op->info->in.file.ntvfs; 107 break; 108 } 109 110 op->io_ptr = io; 111 op->send_fn = smb2srv_getinfo_file_send; 112 113 return ntvfs_qfileinfo(op->req->ntvfs, io); 114} 115 116static NTSTATUS smb2srv_getinfo_fs_send(struct smb2srv_getinfo_op *op) 117{ 118 union smb_fsinfo *io = talloc_get_type(op->io_ptr, union smb_fsinfo); 119 NTSTATUS status; 120 121 status = smbsrv_push_passthru_fsinfo(op->req, 122 &op->info->out.blob, 123 io->generic.level, io, 124 STR_UNICODE); 125 NT_STATUS_NOT_OK_RETURN(status); 126 127 return NT_STATUS_OK; 128} 129 130static NTSTATUS smb2srv_getinfo_fs(struct smb2srv_getinfo_op *op, uint8_t smb2_level) 131{ 132 union smb_fsinfo *io; 133 134 io = talloc(op, union smb_fsinfo); 135 NT_STATUS_HAVE_NO_MEMORY(io); 136 137 /* the rest directly maps to the passthru levels */ 138 io->generic.level = smb2_level + 1000; 139 140 /* TODO: allow qfsinfo only the share root directory handle */ 141 142 op->io_ptr = io; 143 op->send_fn = smb2srv_getinfo_fs_send; 144 145 return ntvfs_fsinfo(op->req->ntvfs, io); 146} 147 148static NTSTATUS smb2srv_getinfo_security_send(struct smb2srv_getinfo_op *op) 149{ 150 union smb_fileinfo *io = talloc_get_type(op->io_ptr, union smb_fileinfo); 151 enum ndr_err_code ndr_err; 152 153 ndr_err = ndr_push_struct_blob(&op->info->out.blob, op->req, NULL, 154 io->query_secdesc.out.sd, 155 (ndr_push_flags_fn_t)ndr_push_security_descriptor); 156 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 157 return ndr_map_error2ntstatus(ndr_err); 158 } 159 160 return NT_STATUS_OK; 161} 162 163static NTSTATUS smb2srv_getinfo_security(struct smb2srv_getinfo_op *op, uint8_t smb2_level) 164{ 165 union smb_fileinfo *io; 166 167 switch (smb2_level) { 168 case 0x00: 169 io = talloc(op, union smb_fileinfo); 170 NT_STATUS_HAVE_NO_MEMORY(io); 171 172 io->query_secdesc.level = RAW_FILEINFO_SEC_DESC; 173 io->query_secdesc.in.file.ntvfs = op->info->in.file.ntvfs; 174 io->query_secdesc.in.secinfo_flags = op->info->in.additional_information; 175 176 op->io_ptr = io; 177 op->send_fn = smb2srv_getinfo_security_send; 178 179 return ntvfs_qfileinfo(op->req->ntvfs, io); 180 } 181 182 return NT_STATUS_INVALID_PARAMETER; 183} 184 185static NTSTATUS smb2srv_getinfo_backend(struct smb2srv_getinfo_op *op) 186{ 187 switch (op->info->in.info_type) { 188 case SMB2_GETINFO_FILE: 189 return smb2srv_getinfo_file(op, op->info->in.info_class); 190 191 case SMB2_GETINFO_FS: 192 return smb2srv_getinfo_fs(op, op->info->in.info_class); 193 194 case SMB2_GETINFO_SECURITY: 195 return smb2srv_getinfo_security(op, op->info->in.info_class); 196 197 case SMB2_GETINFO_QUOTA: 198 return NT_STATUS_NOT_SUPPORTED; 199 } 200 201 return NT_STATUS_INVALID_PARAMETER; 202} 203 204void smb2srv_getinfo_recv(struct smb2srv_request *req) 205{ 206 struct smb2_getinfo *info; 207 struct smb2srv_getinfo_op *op; 208 209 SMB2SRV_CHECK_BODY_SIZE(req, 0x28, true); 210 SMB2SRV_TALLOC_IO_PTR(info, struct smb2_getinfo); 211 /* this overwrites req->io_ptr !*/ 212 SMB2SRV_TALLOC_IO_PTR(op, struct smb2srv_getinfo_op); 213 op->req = req; 214 op->info = info; 215 op->io_ptr = NULL; 216 op->send_fn = NULL; 217 SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_getinfo_send, NTVFS_ASYNC_STATE_MAY_ASYNC); 218 219 info->in.info_type = CVAL(req->in.body, 0x02); 220 info->in.info_class = CVAL(req->in.body, 0x03); 221 info->in.output_buffer_length = IVAL(req->in.body, 0x04); 222 info->in.reserved = IVAL(req->in.body, 0x0C); 223 info->in.additional_information = IVAL(req->in.body, 0x10); 224 info->in.getinfo_flags = IVAL(req->in.body, 0x14); 225 info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x18); 226 SMB2SRV_CHECK(smb2_pull_o16As32_blob(&req->in, op, 227 req->in.body+0x08, &info->in.blob)); 228 229 SMB2SRV_CHECK_FILE_HANDLE(info->in.file.ntvfs); 230 SMB2SRV_CALL_NTVFS_BACKEND(smb2srv_getinfo_backend(op)); 231} 232 233struct smb2srv_setinfo_op { 234 struct smb2srv_request *req; 235 struct smb2_setinfo *info; 236}; 237 238static void smb2srv_setinfo_send(struct ntvfs_request *ntvfs) 239{ 240 struct smb2srv_setinfo_op *op; 241 struct smb2srv_request *req; 242 243 /* 244 * SMB2 uses NT_STATUS_INVALID_INFO_CLASS 245 * so we need to translated it here 246 */ 247 if (NT_STATUS_EQUAL(NT_STATUS_INVALID_LEVEL, ntvfs->async_states->status)) { 248 ntvfs->async_states->status = NT_STATUS_INVALID_INFO_CLASS; 249 } 250 251 SMB2SRV_CHECK_ASYNC_STATUS(op, struct smb2srv_setinfo_op); 252 253 SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x02, false, 0)); 254 255 smb2srv_send_reply(req); 256} 257 258static NTSTATUS smb2srv_setinfo_file(struct smb2srv_setinfo_op *op, uint8_t smb2_level) 259{ 260 union smb_setfileinfo *io; 261 NTSTATUS status; 262 263 io = talloc(op, union smb_setfileinfo); 264 NT_STATUS_HAVE_NO_MEMORY(io); 265 266 /* the levels directly map to the passthru levels */ 267 io->generic.level = smb2_level + 1000; 268 io->generic.in.file.ntvfs = op->info->in.file.ntvfs; 269 270 /* handle cases that don't map directly */ 271 if (io->generic.level == RAW_SFILEINFO_RENAME_INFORMATION) { 272 io->generic.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2; 273 } 274 275 status = smbsrv_pull_passthru_sfileinfo(io, io->generic.level, io, 276 &op->info->in.blob, 277 STR_UNICODE, &op->req->in.bufinfo); 278 NT_STATUS_NOT_OK_RETURN(status); 279 280 return ntvfs_setfileinfo(op->req->ntvfs, io); 281} 282 283static NTSTATUS smb2srv_setinfo_fs(struct smb2srv_setinfo_op *op, uint8_t smb2_level) 284{ 285 switch (smb2_level) { 286 case 0x02: 287 return NT_STATUS_NOT_IMPLEMENTED; 288 289 case 0x06: 290 return NT_STATUS_ACCESS_DENIED; 291 292 case 0x08: 293 return NT_STATUS_ACCESS_DENIED; 294 295 case 0x0A: 296 return NT_STATUS_ACCESS_DENIED; 297 } 298 299 return NT_STATUS_INVALID_INFO_CLASS; 300} 301 302static NTSTATUS smb2srv_setinfo_security(struct smb2srv_setinfo_op *op, uint8_t smb2_level) 303{ 304 union smb_setfileinfo *io; 305 enum ndr_err_code ndr_err; 306 307 switch (smb2_level) { 308 case 0x00: 309 io = talloc(op, union smb_setfileinfo); 310 NT_STATUS_HAVE_NO_MEMORY(io); 311 312 io->set_secdesc.level = RAW_SFILEINFO_SEC_DESC; 313 io->set_secdesc.in.file.ntvfs = op->info->in.file.ntvfs; 314 io->set_secdesc.in.secinfo_flags = op->info->in.flags; 315 316 io->set_secdesc.in.sd = talloc(io, struct security_descriptor); 317 NT_STATUS_HAVE_NO_MEMORY(io->set_secdesc.in.sd); 318 319 ndr_err = ndr_pull_struct_blob(&op->info->in.blob, io, NULL, 320 io->set_secdesc.in.sd, 321 (ndr_pull_flags_fn_t)ndr_pull_security_descriptor); 322 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 323 return ndr_map_error2ntstatus(ndr_err); 324 } 325 326 return ntvfs_setfileinfo(op->req->ntvfs, io); 327 } 328 329 return NT_STATUS_INVALID_INFO_CLASS; 330} 331 332static NTSTATUS smb2srv_setinfo_backend(struct smb2srv_setinfo_op *op) 333{ 334 uint8_t smb2_class; 335 uint8_t smb2_level; 336 337 smb2_class = 0xFF & op->info->in.level; 338 smb2_level = 0xFF & (op->info->in.level>>8); 339 340 switch (smb2_class) { 341 case SMB2_GETINFO_FILE: 342 return smb2srv_setinfo_file(op, smb2_level); 343 344 case SMB2_GETINFO_FS: 345 return smb2srv_setinfo_fs(op, smb2_level); 346 347 case SMB2_GETINFO_SECURITY: 348 return smb2srv_setinfo_security(op, smb2_level); 349 350 case 0x04: 351 return NT_STATUS_NOT_SUPPORTED; 352 } 353 354 return NT_STATUS_INVALID_PARAMETER; 355} 356 357void smb2srv_setinfo_recv(struct smb2srv_request *req) 358{ 359 struct smb2_setinfo *info; 360 struct smb2srv_setinfo_op *op; 361 362 SMB2SRV_CHECK_BODY_SIZE(req, 0x20, true); 363 SMB2SRV_TALLOC_IO_PTR(info, struct smb2_setinfo); 364 /* this overwrites req->io_ptr !*/ 365 SMB2SRV_TALLOC_IO_PTR(op, struct smb2srv_setinfo_op); 366 op->req = req; 367 op->info = info; 368 SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_setinfo_send, NTVFS_ASYNC_STATE_MAY_ASYNC); 369 370 info->in.level = SVAL(req->in.body, 0x02); 371 SMB2SRV_CHECK(smb2_pull_s32o16_blob(&req->in, info, req->in.body+0x04, &info->in.blob)); 372 info->in.flags = IVAL(req->in.body, 0x0C); 373 info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x10); 374 375 SMB2SRV_CHECK_FILE_HANDLE(info->in.file.ntvfs); 376 SMB2SRV_CALL_NTVFS_BACKEND(smb2srv_setinfo_backend(op)); 377} 378