1/* 2 Unix SMB/CIFS implementation. 3 Core SMB2 server 4 5 Copyright (C) Stefan Metzmacher 2009 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. 19*/ 20 21#include "includes.h" 22#include "smbd/globals.h" 23#include "../libcli/smb/smb_common.h" 24 25static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx, 26 struct tevent_context *ev, 27 struct smbd_smb2_request *smb2req, 28 uint8_t in_info_type, 29 uint8_t in_file_info_class, 30 DATA_BLOB in_input_buffer, 31 uint32_t in_additional_information, 32 uint64_t in_file_id_volatile); 33static NTSTATUS smbd_smb2_setinfo_recv(struct tevent_req *req); 34 35static void smbd_smb2_request_setinfo_done(struct tevent_req *subreq); 36NTSTATUS smbd_smb2_request_process_setinfo(struct smbd_smb2_request *req) 37{ 38 const uint8_t *inhdr; 39 const uint8_t *inbody; 40 int i = req->current_idx; 41 size_t expected_body_size = 0x21; 42 size_t body_size; 43 uint8_t in_info_type; 44 uint8_t in_file_info_class; 45 uint16_t in_input_buffer_offset; 46 uint32_t in_input_buffer_length; 47 DATA_BLOB in_input_buffer; 48 uint32_t in_additional_information; 49 uint64_t in_file_id_persistent; 50 uint64_t in_file_id_volatile; 51 struct tevent_req *subreq; 52 53 inhdr = (const uint8_t *)req->in.vector[i+0].iov_base; 54 if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) { 55 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 56 } 57 58 inbody = (const uint8_t *)req->in.vector[i+1].iov_base; 59 60 body_size = SVAL(inbody, 0x00); 61 if (body_size != expected_body_size) { 62 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 63 } 64 65 in_info_type = CVAL(inbody, 0x02); 66 in_file_info_class = CVAL(inbody, 0x03); 67 in_input_buffer_length = IVAL(inbody, 0x04); 68 in_input_buffer_offset = SVAL(inbody, 0x08); 69 /* 0x0A 2 bytes reserved */ 70 in_additional_information = IVAL(inbody, 0x0C); 71 in_file_id_persistent = BVAL(inbody, 0x10); 72 in_file_id_volatile = BVAL(inbody, 0x18); 73 74 if (in_input_buffer_offset == 0 && in_input_buffer_length == 0) { 75 /* This is ok */ 76 } else if (in_input_buffer_offset != 77 (SMB2_HDR_BODY + (body_size & 0xFFFFFFFE))) { 78 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 79 } 80 81 if (in_input_buffer_length > req->in.vector[i+2].iov_len) { 82 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 83 } 84 85 in_input_buffer.data = (uint8_t *)req->in.vector[i+2].iov_base; 86 in_input_buffer.length = in_input_buffer_length; 87 88 if (req->compat_chain_fsp) { 89 /* skip check */ 90 } else if (in_file_id_persistent != 0) { 91 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); 92 } 93 94 subreq = smbd_smb2_setinfo_send(req, 95 req->sconn->smb2.event_ctx, 96 req, 97 in_info_type, 98 in_file_info_class, 99 in_input_buffer, 100 in_additional_information, 101 in_file_id_volatile); 102 if (subreq == NULL) { 103 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); 104 } 105 tevent_req_set_callback(subreq, smbd_smb2_request_setinfo_done, req); 106 107 return smbd_smb2_request_pending_queue(req, subreq); 108} 109 110static void smbd_smb2_request_setinfo_done(struct tevent_req *subreq) 111{ 112 struct smbd_smb2_request *req = tevent_req_callback_data(subreq, 113 struct smbd_smb2_request); 114 DATA_BLOB outbody; 115 NTSTATUS status; 116 NTSTATUS error; /* transport error */ 117 118 status = smbd_smb2_setinfo_recv(subreq); 119 TALLOC_FREE(subreq); 120 if (!NT_STATUS_IS_OK(status)) { 121 error = smbd_smb2_request_error(req, status); 122 if (!NT_STATUS_IS_OK(error)) { 123 smbd_server_connection_terminate(req->sconn, 124 nt_errstr(error)); 125 return; 126 } 127 return; 128 } 129 130 outbody = data_blob_talloc(req->out.vector, NULL, 0x02); 131 if (outbody.data == NULL) { 132 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); 133 if (!NT_STATUS_IS_OK(error)) { 134 smbd_server_connection_terminate(req->sconn, 135 nt_errstr(error)); 136 return; 137 } 138 return; 139 } 140 141 SSVAL(outbody.data, 0x00, 0x02); /* struct size */ 142 143 error = smbd_smb2_request_done(req, outbody, NULL); 144 if (!NT_STATUS_IS_OK(error)) { 145 smbd_server_connection_terminate(req->sconn, 146 nt_errstr(error)); 147 return; 148 } 149} 150 151struct smbd_smb2_setinfo_state { 152 struct smbd_smb2_request *smb2req; 153}; 154 155static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx, 156 struct tevent_context *ev, 157 struct smbd_smb2_request *smb2req, 158 uint8_t in_info_type, 159 uint8_t in_file_info_class, 160 DATA_BLOB in_input_buffer, 161 uint32_t in_additional_information, 162 uint64_t in_file_id_volatile) 163{ 164 struct tevent_req *req; 165 struct smbd_smb2_setinfo_state *state; 166 struct smb_request *smbreq; 167 connection_struct *conn = smb2req->tcon->compat_conn; 168 files_struct *fsp; 169 170 req = tevent_req_create(mem_ctx, &state, 171 struct smbd_smb2_setinfo_state); 172 if (req == NULL) { 173 return NULL; 174 } 175 state->smb2req = smb2req; 176 177 DEBUG(10,("smbd_smb2_setinfo_send: file_id[0x%016llX]\n", 178 (unsigned long long)in_file_id_volatile)); 179 180 smbreq = smbd_smb2_fake_smb_request(smb2req); 181 if (tevent_req_nomem(smbreq, req)) { 182 return tevent_req_post(req, ev); 183 } 184 185 fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile); 186 if (fsp == NULL) { 187 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 188 return tevent_req_post(req, ev); 189 } 190 if (conn != fsp->conn) { 191 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 192 return tevent_req_post(req, ev); 193 } 194 if (smb2req->session->vuid != fsp->vuid) { 195 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 196 return tevent_req_post(req, ev); 197 } 198 199 if (IS_IPC(conn)) { 200 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); 201 return tevent_req_post(req, ev); 202 } 203 204 switch (in_info_type) { 205 case 0x01:/* SMB2_SETINFO_FILE */ 206 { 207 uint16_t file_info_level; 208 char *data; 209 int data_size; 210 int ret_size = 0; 211 NTSTATUS status; 212 213 214 file_info_level = in_file_info_class + 1000; 215 if (file_info_level == SMB_FILE_RENAME_INFORMATION) { 216 file_info_level = 0xFF00 + in_file_info_class; 217 } 218 219 if (fsp->is_directory || fsp->fh->fd == -1) { 220 /* 221 * This is actually a SETFILEINFO on a directory 222 * handle (returned from an NT SMB). NT5.0 seems 223 * to do this call. JRA. 224 */ 225 if (INFO_LEVEL_IS_UNIX(file_info_level)) { 226 /* Always do lstat for UNIX calls. */ 227 if (SMB_VFS_LSTAT(conn, fsp->fsp_name)) { 228 DEBUG(3,("smbd_smb2_setinfo_send: " 229 "SMB_VFS_LSTAT of %s failed " 230 "(%s)\n", fsp_str_dbg(fsp), 231 strerror(errno))); 232 status = map_nt_error_from_unix(errno); 233 tevent_req_nterror(req, status); 234 return tevent_req_post(req, ev); 235 } 236 } else { 237 if (SMB_VFS_STAT(conn, fsp->fsp_name) != 0) { 238 DEBUG(3,("smbd_smb2_setinfo_send: " 239 "fileinfo of %s failed (%s)\n", 240 fsp_str_dbg(fsp), 241 strerror(errno))); 242 status = map_nt_error_from_unix(errno); 243 tevent_req_nterror(req, status); 244 return tevent_req_post(req, ev); 245 } 246 } 247 } else if (fsp->print_file) { 248 /* 249 * Doing a DELETE_ON_CLOSE should cancel a print job. 250 */ 251 if ((file_info_level == SMB_SET_FILE_DISPOSITION_INFO) 252 && in_input_buffer.length >= 1 253 && CVAL(in_input_buffer.data,0)) { 254 fsp->fh->private_options |= FILE_DELETE_ON_CLOSE; 255 256 DEBUG(3,("smbd_smb2_setinfo_send: " 257 "Cancelling print job (%s)\n", 258 fsp_str_dbg(fsp))); 259 260 tevent_req_done(req); 261 return tevent_req_post(req, ev); 262 } else { 263 tevent_req_nterror(req, 264 NT_STATUS_OBJECT_PATH_INVALID); 265 return tevent_req_post(req, ev); 266 } 267 } else { 268 /* 269 * Original code - this is an open file. 270 */ 271 272 if (SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st) != 0) { 273 DEBUG(3,("smbd_smb2_setinfo_send: fstat " 274 "of fnum %d failed (%s)\n", fsp->fnum, 275 strerror(errno))); 276 status = map_nt_error_from_unix(errno); 277 tevent_req_nterror(req, status); 278 return tevent_req_post(req, ev); 279 } 280 } 281 282 data = NULL; 283 data_size = in_input_buffer.length; 284 if (data_size > 0) { 285 data = (char *)SMB_MALLOC_ARRAY(char, data_size); 286 if (tevent_req_nomem(data, req)) { 287 288 } 289 memcpy(data, in_input_buffer.data, data_size); 290 } 291 292 status = smbd_do_setfilepathinfo(conn, smbreq, state, 293 file_info_level, 294 fsp, 295 fsp->fsp_name, 296 &data, 297 data_size, 298 &ret_size); 299 SAFE_FREE(data); 300 if (!NT_STATUS_IS_OK(status)) { 301 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL)) { 302 status = NT_STATUS_INVALID_INFO_CLASS; 303 } 304 tevent_req_nterror(req, status); 305 return tevent_req_post(req, ev); 306 } 307 break; 308 } 309 310 default: 311 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); 312 return tevent_req_post(req, ev); 313 } 314 315 tevent_req_done(req); 316 return tevent_req_post(req, ev); 317} 318 319static NTSTATUS smbd_smb2_setinfo_recv(struct tevent_req *req) 320{ 321 NTSTATUS status; 322 323 if (tevent_req_is_nterror(req, &status)) { 324 tevent_req_received(req); 325 return status; 326 } 327 328 tevent_req_received(req); 329 return NT_STATUS_OK; 330} 331