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_oplock_break_send(TALLOC_CTX *mem_ctx, 26 struct tevent_context *ev, 27 struct smbd_smb2_request *smb2req, 28 uint8_t in_oplock_level, 29 uint64_t in_file_id_volatile); 30static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req, 31 uint8_t *out_oplock_level); 32 33static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq); 34NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req) 35{ 36 const uint8_t *inhdr; 37 const uint8_t *inbody; 38 int i = req->current_idx; 39 size_t expected_body_size = 0x18; 40 size_t body_size; 41 uint8_t in_oplock_level; 42 uint64_t in_file_id_persistent; 43 uint64_t in_file_id_volatile; 44 struct tevent_req *subreq; 45 46 inhdr = (const uint8_t *)req->in.vector[i+0].iov_base; 47 if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) { 48 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 49 } 50 51 inbody = (const uint8_t *)req->in.vector[i+1].iov_base; 52 53 body_size = SVAL(inbody, 0x00); 54 if (body_size != expected_body_size) { 55 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 56 } 57 58 in_oplock_level = CVAL(inbody, 0x02); 59 /* 0x03 1 bytes reserved */ 60 /* 0x04 4 bytes reserved */ 61 in_file_id_persistent = BVAL(inbody, 0x08); 62 in_file_id_volatile = BVAL(inbody, 0x10); 63 64 if (req->compat_chain_fsp) { 65 /* skip check */ 66 } else if (in_file_id_persistent != 0) { 67 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); 68 } 69 70 subreq = smbd_smb2_oplock_break_send(req, 71 req->sconn->smb2.event_ctx, 72 req, 73 in_oplock_level, 74 in_file_id_volatile); 75 if (subreq == NULL) { 76 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); 77 } 78 tevent_req_set_callback(subreq, smbd_smb2_request_oplock_break_done, req); 79 80 return smbd_smb2_request_pending_queue(req, subreq); 81} 82 83static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq) 84{ 85 struct smbd_smb2_request *req = tevent_req_callback_data(subreq, 86 struct smbd_smb2_request); 87 const uint8_t *inbody; 88 int i = req->current_idx; 89 uint64_t in_file_id_persistent; 90 uint64_t in_file_id_volatile; 91 uint8_t out_oplock_level = 0; 92 DATA_BLOB outbody; 93 NTSTATUS status; 94 NTSTATUS error; /* transport error */ 95 96 status = smbd_smb2_oplock_break_recv(subreq, &out_oplock_level); 97 TALLOC_FREE(subreq); 98 if (!NT_STATUS_IS_OK(status)) { 99 error = smbd_smb2_request_error(req, status); 100 if (!NT_STATUS_IS_OK(error)) { 101 smbd_server_connection_terminate(req->sconn, 102 nt_errstr(error)); 103 return; 104 } 105 return; 106 } 107 108 inbody = (const uint8_t *)req->in.vector[i+1].iov_base; 109 110 in_file_id_persistent = BVAL(inbody, 0x08); 111 in_file_id_volatile = BVAL(inbody, 0x10); 112 113 outbody = data_blob_talloc(req->out.vector, NULL, 0x18); 114 if (outbody.data == NULL) { 115 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); 116 if (!NT_STATUS_IS_OK(error)) { 117 smbd_server_connection_terminate(req->sconn, 118 nt_errstr(error)); 119 return; 120 } 121 return; 122 } 123 124 SSVAL(outbody.data, 0x00, 0x18); /* struct size */ 125 SCVAL(outbody.data, 0x02, 126 out_oplock_level); /* oplock level */ 127 SCVAL(outbody.data, 0x03, 0); /* reserved */ 128 SIVAL(outbody.data, 0x04, 0); /* reserved */ 129 SBVAL(outbody.data, 0x08, 130 in_file_id_persistent); /* file id (persistent) */ 131 SBVAL(outbody.data, 0x10, 132 in_file_id_volatile); /* file id (volatile) */ 133 134 error = smbd_smb2_request_done(req, outbody, NULL); 135 if (!NT_STATUS_IS_OK(error)) { 136 smbd_server_connection_terminate(req->sconn, 137 nt_errstr(error)); 138 return; 139 } 140} 141 142struct smbd_smb2_oplock_break_state { 143 struct smbd_smb2_request *smb2req; 144 uint8_t out_oplock_level; 145}; 146 147static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx, 148 struct tevent_context *ev, 149 struct smbd_smb2_request *smb2req, 150 uint8_t in_oplock_level, 151 uint64_t in_file_id_volatile) 152{ 153 struct tevent_req *req; 154 struct smbd_smb2_oplock_break_state *state; 155 struct smb_request *smbreq; 156 connection_struct *conn = smb2req->tcon->compat_conn; 157 files_struct *fsp; 158 159 req = tevent_req_create(mem_ctx, &state, 160 struct smbd_smb2_oplock_break_state); 161 if (req == NULL) { 162 return NULL; 163 } 164 state->smb2req = smb2req; 165 state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE; 166 167 DEBUG(10,("smbd_smb2_oplock_break_send: file_id[0x%016llX]\n", 168 (unsigned long long)in_file_id_volatile)); 169 170 smbreq = smbd_smb2_fake_smb_request(smb2req); 171 if (tevent_req_nomem(smbreq, req)) { 172 return tevent_req_post(req, ev); 173 } 174 175 fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile); 176 if (fsp == NULL) { 177 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 178 return tevent_req_post(req, ev); 179 } 180 if (conn != fsp->conn) { 181 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 182 return tevent_req_post(req, ev); 183 } 184 if (smb2req->session->vuid != fsp->vuid) { 185 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 186 return tevent_req_post(req, ev); 187 } 188 189 tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED); 190 return tevent_req_post(req, ev); 191} 192 193static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req, 194 uint8_t *out_oplock_level) 195{ 196 NTSTATUS status; 197 struct smbd_smb2_oplock_break_state *state = 198 tevent_req_data(req, 199 struct smbd_smb2_oplock_break_state); 200 201 if (tevent_req_is_nterror(req, &status)) { 202 tevent_req_received(req); 203 return status; 204 } 205 206 *out_oplock_level = state->out_oplock_level; 207 208 tevent_req_received(req); 209 return NT_STATUS_OK; 210} 211