/src/samba/source3/smbd/smb2_getinfo.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | Core SMB2 server |
4 | | |
5 | | Copyright (C) Stefan Metzmacher 2009 |
6 | | Copyright (C) Jeremy Allison 2010 |
7 | | |
8 | | This program is free software; you can redistribute it and/or modify |
9 | | it under the terms of the GNU General Public License as published by |
10 | | the Free Software Foundation; either version 3 of the License, or |
11 | | (at your option) any later version. |
12 | | |
13 | | This program is distributed in the hope that it will be useful, |
14 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | GNU General Public License for more details. |
17 | | |
18 | | You should have received a copy of the GNU General Public License |
19 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 | | */ |
21 | | |
22 | | #include "includes.h" |
23 | | #include "smbd/smbd.h" |
24 | | #include "smbd/globals.h" |
25 | | #include "../libcli/smb/smb_common.h" |
26 | | #include "trans2.h" |
27 | | #include "../lib/util/tevent_ntstatus.h" |
28 | | #include "librpc/gen_ndr/ndr_quota.h" |
29 | | #include "librpc/gen_ndr/ndr_security.h" |
30 | | |
31 | | #undef DBGC_CLASS |
32 | 0 | #define DBGC_CLASS DBGC_SMB2 |
33 | | |
34 | | static struct tevent_req *smbd_smb2_getinfo_send(TALLOC_CTX *mem_ctx, |
35 | | struct tevent_context *ev, |
36 | | struct smbd_smb2_request *smb2req, |
37 | | struct files_struct *in_fsp, |
38 | | uint8_t in_info_type, |
39 | | uint8_t in_file_info_class, |
40 | | uint32_t in_output_buffer_length, |
41 | | DATA_BLOB in_input_buffer, |
42 | | uint32_t in_additional_information, |
43 | | uint32_t in_flags); |
44 | | static NTSTATUS smbd_smb2_getinfo_recv(struct tevent_req *req, |
45 | | TALLOC_CTX *mem_ctx, |
46 | | DATA_BLOB *out_output_buffer, |
47 | | NTSTATUS *p_call_status); |
48 | | |
49 | | static void smbd_smb2_request_getinfo_done(struct tevent_req *subreq); |
50 | | NTSTATUS smbd_smb2_request_process_getinfo(struct smbd_smb2_request *req) |
51 | 0 | { |
52 | 0 | struct smbXsrv_connection *xconn = req->xconn; |
53 | 0 | NTSTATUS status; |
54 | 0 | const uint8_t *inbody; |
55 | 0 | uint8_t in_info_type; |
56 | 0 | uint8_t in_file_info_class; |
57 | 0 | uint32_t in_output_buffer_length; |
58 | 0 | uint16_t in_input_buffer_offset; |
59 | 0 | uint32_t in_input_buffer_length; |
60 | 0 | DATA_BLOB in_input_buffer; |
61 | 0 | uint32_t in_additional_information; |
62 | 0 | uint32_t in_flags; |
63 | 0 | uint64_t in_file_id_persistent; |
64 | 0 | uint64_t in_file_id_volatile; |
65 | 0 | struct files_struct *in_fsp; |
66 | 0 | struct tevent_req *subreq; |
67 | |
|
68 | 0 | status = smbd_smb2_request_verify_sizes(req, 0x29); |
69 | 0 | if (!NT_STATUS_IS_OK(status)) { |
70 | 0 | return smbd_smb2_request_error(req, status); |
71 | 0 | } |
72 | 0 | inbody = SMBD_SMB2_IN_BODY_PTR(req); |
73 | |
|
74 | 0 | in_info_type = CVAL(inbody, 0x02); |
75 | 0 | in_file_info_class = CVAL(inbody, 0x03); |
76 | 0 | in_output_buffer_length = IVAL(inbody, 0x04); |
77 | 0 | in_input_buffer_offset = SVAL(inbody, 0x08); |
78 | | /* 0x0A 2 bytes reserved */ |
79 | 0 | in_input_buffer_length = IVAL(inbody, 0x0C); |
80 | 0 | in_additional_information = IVAL(inbody, 0x10); |
81 | 0 | in_flags = IVAL(inbody, 0x14); |
82 | 0 | in_file_id_persistent = BVAL(inbody, 0x18); |
83 | 0 | in_file_id_volatile = BVAL(inbody, 0x20); |
84 | |
|
85 | 0 | if (in_input_buffer_offset == 0 && in_input_buffer_length == 0) { |
86 | | /* This is ok */ |
87 | 0 | } else if (in_input_buffer_offset != |
88 | 0 | (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) { |
89 | 0 | return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); |
90 | 0 | } |
91 | | |
92 | 0 | if (in_input_buffer_length > SMBD_SMB2_IN_DYN_LEN(req)) { |
93 | 0 | return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); |
94 | 0 | } |
95 | | |
96 | 0 | in_input_buffer.data = SMBD_SMB2_IN_DYN_PTR(req); |
97 | 0 | in_input_buffer.length = in_input_buffer_length; |
98 | |
|
99 | 0 | if (in_input_buffer.length > xconn->smb2.server.max_trans) { |
100 | 0 | DEBUG(2,("smbd_smb2_request_process_getinfo: " |
101 | 0 | "client ignored max trans: %s: 0x%08X: 0x%08X\n", |
102 | 0 | __location__, (unsigned)in_input_buffer.length, |
103 | 0 | (unsigned)xconn->smb2.server.max_trans)); |
104 | 0 | return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); |
105 | 0 | } |
106 | 0 | if (in_output_buffer_length > xconn->smb2.server.max_trans) { |
107 | 0 | DEBUG(2,("smbd_smb2_request_process_getinfo: " |
108 | 0 | "client ignored max trans: %s: 0x%08X: 0x%08X\n", |
109 | 0 | __location__, in_output_buffer_length, |
110 | 0 | xconn->smb2.server.max_trans)); |
111 | 0 | return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); |
112 | 0 | } |
113 | | |
114 | 0 | status = smbd_smb2_request_verify_creditcharge(req, |
115 | 0 | MAX(in_input_buffer.length,in_output_buffer_length)); |
116 | 0 | if (!NT_STATUS_IS_OK(status)) { |
117 | 0 | return smbd_smb2_request_error(req, status); |
118 | 0 | } |
119 | | |
120 | 0 | in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile); |
121 | 0 | if (in_fsp == NULL) { |
122 | 0 | return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); |
123 | 0 | } |
124 | | |
125 | 0 | subreq = smbd_smb2_getinfo_send(req, req->sconn->ev_ctx, |
126 | 0 | req, in_fsp, |
127 | 0 | in_info_type, |
128 | 0 | in_file_info_class, |
129 | 0 | in_output_buffer_length, |
130 | 0 | in_input_buffer, |
131 | 0 | in_additional_information, |
132 | 0 | in_flags); |
133 | 0 | if (subreq == NULL) { |
134 | 0 | return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); |
135 | 0 | } |
136 | 0 | tevent_req_set_callback(subreq, smbd_smb2_request_getinfo_done, req); |
137 | |
|
138 | 0 | return smbd_smb2_request_pending_queue(req, subreq, 500); |
139 | 0 | } |
140 | | |
141 | | static void smbd_smb2_request_getinfo_done(struct tevent_req *subreq) |
142 | 0 | { |
143 | 0 | struct smbd_smb2_request *req = tevent_req_callback_data(subreq, |
144 | 0 | struct smbd_smb2_request); |
145 | 0 | DATA_BLOB outbody; |
146 | 0 | DATA_BLOB outdyn; |
147 | 0 | uint16_t out_output_buffer_offset; |
148 | 0 | DATA_BLOB out_output_buffer = data_blob_null; |
149 | 0 | NTSTATUS status; |
150 | 0 | NTSTATUS call_status = NT_STATUS_OK; |
151 | 0 | NTSTATUS error; /* transport error */ |
152 | |
|
153 | 0 | status = smbd_smb2_getinfo_recv(subreq, |
154 | 0 | req, |
155 | 0 | &out_output_buffer, |
156 | 0 | &call_status); |
157 | 0 | TALLOC_FREE(subreq); |
158 | 0 | if (!NT_STATUS_IS_OK(status)) { |
159 | 0 | error = smbd_smb2_request_error(req, status); |
160 | 0 | if (!NT_STATUS_IS_OK(error)) { |
161 | 0 | smbd_server_connection_terminate(req->xconn, |
162 | 0 | nt_errstr(error)); |
163 | 0 | return; |
164 | 0 | } |
165 | 0 | return; |
166 | 0 | } |
167 | | |
168 | | /* some GetInfo responses set STATUS_BUFFER_OVERFLOW and return partial, |
169 | | but valid data */ |
170 | 0 | if (!(NT_STATUS_IS_OK(call_status) || |
171 | 0 | NT_STATUS_EQUAL(call_status, STATUS_BUFFER_OVERFLOW))) { |
172 | | /* Return a specific error with data. */ |
173 | 0 | error = smbd_smb2_request_error_ex(req, |
174 | 0 | call_status, |
175 | 0 | 0, |
176 | 0 | &out_output_buffer, |
177 | 0 | __location__); |
178 | 0 | if (!NT_STATUS_IS_OK(error)) { |
179 | 0 | smbd_server_connection_terminate(req->xconn, |
180 | 0 | nt_errstr(error)); |
181 | 0 | return; |
182 | 0 | } |
183 | 0 | return; |
184 | 0 | } |
185 | | |
186 | 0 | out_output_buffer_offset = SMB2_HDR_BODY + 0x08; |
187 | |
|
188 | 0 | outbody = smbd_smb2_generate_outbody(req, 0x08); |
189 | 0 | if (outbody.data == NULL) { |
190 | 0 | error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); |
191 | 0 | if (!NT_STATUS_IS_OK(error)) { |
192 | 0 | smbd_server_connection_terminate(req->xconn, |
193 | 0 | nt_errstr(error)); |
194 | 0 | return; |
195 | 0 | } |
196 | 0 | return; |
197 | 0 | } |
198 | | |
199 | 0 | SSVAL(outbody.data, 0x00, 0x08 + 1); /* struct size */ |
200 | 0 | SSVAL(outbody.data, 0x02, |
201 | 0 | out_output_buffer_offset); /* output buffer offset */ |
202 | 0 | SIVAL(outbody.data, 0x04, |
203 | 0 | out_output_buffer.length); /* output buffer length */ |
204 | |
|
205 | 0 | outdyn = out_output_buffer; |
206 | |
|
207 | 0 | error = smbd_smb2_request_done_ex(req, call_status, outbody, &outdyn, __location__); |
208 | 0 | if (!NT_STATUS_IS_OK(error)) { |
209 | 0 | smbd_server_connection_terminate(req->xconn, |
210 | 0 | nt_errstr(error)); |
211 | 0 | return; |
212 | 0 | } |
213 | 0 | } |
214 | | |
215 | | struct smbd_smb2_getinfo_state { |
216 | | struct smbd_smb2_request *smb2req; |
217 | | NTSTATUS status; |
218 | | DATA_BLOB out_output_buffer; |
219 | | }; |
220 | | |
221 | | static void smb2_ipc_getinfo(struct tevent_req *req, |
222 | | struct smbd_smb2_getinfo_state *state, |
223 | | struct tevent_context *ev, |
224 | | uint8_t in_info_type, |
225 | | uint8_t in_file_info_class) |
226 | 0 | { |
227 | | /* We want to reply to SMB2_GETINFO_FILE |
228 | | with a class of SMB2_FILE_STANDARD_INFO as |
229 | | otherwise a Win7 client issues this request |
230 | | twice (2xroundtrips) if we return NOT_SUPPORTED. |
231 | | NB. We do the same for SMB1 in call_trans2qpipeinfo() */ |
232 | |
|
233 | 0 | if (in_info_type == 0x01 && /* SMB2_GETINFO_FILE */ |
234 | 0 | in_file_info_class == 0x05) { /* SMB2_FILE_STANDARD_INFO */ |
235 | 0 | state->out_output_buffer = data_blob_talloc(state, |
236 | 0 | NULL, 24); |
237 | 0 | if (tevent_req_nomem(state->out_output_buffer.data, req)) { |
238 | 0 | return; |
239 | 0 | } |
240 | | |
241 | 0 | memset(state->out_output_buffer.data,0,24); |
242 | 0 | SOFF_T(state->out_output_buffer.data,0,4096LL); |
243 | 0 | SIVAL(state->out_output_buffer.data,16,1); |
244 | 0 | SIVAL(state->out_output_buffer.data,20,1); |
245 | 0 | tevent_req_done(req); |
246 | 0 | } else { |
247 | 0 | tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); |
248 | 0 | } |
249 | 0 | } |
250 | | |
251 | | static struct tevent_req *smbd_smb2_getinfo_send(TALLOC_CTX *mem_ctx, |
252 | | struct tevent_context *ev, |
253 | | struct smbd_smb2_request *smb2req, |
254 | | struct files_struct *fsp, |
255 | | uint8_t in_info_type, |
256 | | uint8_t in_file_info_class, |
257 | | uint32_t in_output_buffer_length, |
258 | | DATA_BLOB in_input_buffer, |
259 | | uint32_t in_additional_information, |
260 | | uint32_t in_flags) |
261 | 0 | { |
262 | 0 | struct tevent_req *req; |
263 | 0 | struct smbd_smb2_getinfo_state *state; |
264 | 0 | struct smb_request *smbreq; |
265 | 0 | connection_struct *conn = smb2req->tcon->compat; |
266 | 0 | NTSTATUS status; |
267 | |
|
268 | 0 | req = tevent_req_create(mem_ctx, &state, |
269 | 0 | struct smbd_smb2_getinfo_state); |
270 | 0 | if (req == NULL) { |
271 | 0 | return NULL; |
272 | 0 | } |
273 | 0 | state->smb2req = smb2req; |
274 | 0 | state->status = NT_STATUS_OK; |
275 | 0 | state->out_output_buffer = data_blob_null; |
276 | |
|
277 | 0 | DEBUG(10,("smbd_smb2_getinfo_send: %s - %s\n", |
278 | 0 | fsp_str_dbg(fsp), fsp_fnum_dbg(fsp))); |
279 | |
|
280 | 0 | smbreq = smbd_smb2_fake_smb_request(smb2req, fsp); |
281 | 0 | if (tevent_req_nomem(smbreq, req)) { |
282 | 0 | return tevent_req_post(req, ev); |
283 | 0 | } |
284 | | |
285 | 0 | if (IS_IPC(conn)) { |
286 | 0 | smb2_ipc_getinfo(req, state, ev, |
287 | 0 | in_info_type, in_file_info_class); |
288 | 0 | return tevent_req_post(req, ev); |
289 | 0 | } |
290 | | |
291 | 0 | switch (in_info_type) { |
292 | 0 | case SMB2_0_INFO_FILE: |
293 | 0 | { |
294 | 0 | uint16_t file_info_level; |
295 | 0 | char *data = NULL; |
296 | 0 | unsigned int data_size = 0; |
297 | 0 | bool delete_pending = false; |
298 | 0 | struct file_id fileid; |
299 | 0 | size_t fixed_portion; |
300 | | |
301 | | |
302 | | /* |
303 | | * MS-SMB2 3.3.5.20.1 "Handling SMB2_0_INFO_FILE" |
304 | | * |
305 | | * FileBasicInformation, FileAllInformation, |
306 | | * FileNetworkOpenInformation, FileAttributeTagInformation |
307 | | * require FILE_READ_ATTRIBUTES. |
308 | | * |
309 | | * FileFullEaInformation requires FILE_READ_EA. |
310 | | */ |
311 | 0 | switch (in_file_info_class) { |
312 | 0 | case FSCC_FILE_BASIC_INFORMATION: |
313 | 0 | case FSCC_FILE_ALL_INFORMATION: |
314 | 0 | case FSCC_FILE_NETWORK_OPEN_INFORMATION: |
315 | 0 | case FSCC_FILE_ATTRIBUTE_TAG_INFORMATION: |
316 | 0 | status = check_any_access_fsp(fsp, SEC_FILE_READ_ATTRIBUTE); |
317 | 0 | if (tevent_req_nterror(req, status)) { |
318 | 0 | return tevent_req_post(req, ev); |
319 | 0 | } |
320 | 0 | break; |
321 | | |
322 | 0 | case FSCC_FILE_FULL_EA_INFORMATION: |
323 | 0 | status = check_any_access_fsp(fsp, SEC_FILE_READ_EA); |
324 | 0 | if (tevent_req_nterror(req, status)) { |
325 | 0 | return tevent_req_post(req, ev); |
326 | 0 | } |
327 | 0 | break; |
328 | 0 | } |
329 | | |
330 | 0 | switch (in_file_info_class) { |
331 | 0 | case FSCC_FILE_FULL_EA_INFORMATION: |
332 | 0 | file_info_level = SMB2_FILE_FULL_EA_INFORMATION; |
333 | 0 | break; |
334 | | |
335 | 0 | case FSCC_FILE_ALL_INFORMATION: |
336 | 0 | file_info_level = SMB2_FILE_ALL_INFORMATION; |
337 | 0 | break; |
338 | | |
339 | 0 | case FSCC_FILE_POSIX_INFORMATION: |
340 | 0 | if (!fsp->fsp_flags.posix_open) { |
341 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_LEVEL); |
342 | 0 | return tevent_req_post(req, ev); |
343 | 0 | } |
344 | 0 | file_info_level = in_file_info_class; |
345 | 0 | break; |
346 | | |
347 | 0 | default: |
348 | | /* the levels directly map to the passthru levels */ |
349 | 0 | file_info_level = in_file_info_class + NT_PASSTHROUGH_OFFSET; |
350 | 0 | break; |
351 | 0 | } |
352 | | |
353 | 0 | switch (file_info_level) { |
354 | 0 | case SMB_FILE_NORMALIZED_NAME_INFORMATION: |
355 | 0 | if (smb2req->xconn->protocol < PROTOCOL_SMB3_11) { |
356 | 0 | tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); |
357 | 0 | return tevent_req_post(req, ev); |
358 | 0 | } |
359 | 0 | break; |
360 | 0 | } |
361 | | |
362 | 0 | if (fsp->fake_file_handle) { |
363 | | /* |
364 | | * This is actually for the QUOTA_FAKE_FILE --metze |
365 | | */ |
366 | | |
367 | | /* We know this name is ok, it's already passed the checks. */ |
368 | |
|
369 | 0 | } else if (fsp_get_pathref_fd(fsp) == -1) { |
370 | | /* |
371 | | * This is actually a QFILEINFO on a directory |
372 | | * handle (returned from an NT SMB). NT5.0 seems |
373 | | * to do this call. JRA. |
374 | | */ |
375 | 0 | int ret = vfs_stat(conn, fsp->fsp_name); |
376 | 0 | if (ret != 0) { |
377 | 0 | DBG_NOTICE("vfs_stat of %s failed (%s)\n", |
378 | 0 | fsp_str_dbg(fsp), |
379 | 0 | strerror(errno)); |
380 | 0 | status = map_nt_error_from_unix(errno); |
381 | 0 | tevent_req_nterror(req, status); |
382 | 0 | return tevent_req_post(req, ev); |
383 | 0 | } |
384 | | |
385 | 0 | if (fsp_getinfo_ask_sharemode(fsp)) { |
386 | 0 | fileid = vfs_file_id_from_sbuf( |
387 | 0 | conn, &fsp->fsp_name->st); |
388 | 0 | get_file_infos(fileid, fsp->name_hash, |
389 | 0 | &delete_pending); |
390 | 0 | } |
391 | 0 | } else { |
392 | | /* |
393 | | * Original code - this is an open file. |
394 | | */ |
395 | |
|
396 | 0 | status = vfs_stat_fsp(fsp); |
397 | 0 | if (!NT_STATUS_IS_OK(status)) { |
398 | 0 | DEBUG(3, ("smbd_smb2_getinfo_send: " |
399 | 0 | "fstat of %s failed (%s)\n", |
400 | 0 | fsp_fnum_dbg(fsp), nt_errstr(status))); |
401 | 0 | tevent_req_nterror(req, status); |
402 | 0 | return tevent_req_post(req, ev); |
403 | 0 | } |
404 | 0 | if (fsp_getinfo_ask_sharemode(fsp)) { |
405 | 0 | fileid = vfs_file_id_from_sbuf( |
406 | 0 | conn, &fsp->fsp_name->st); |
407 | 0 | get_file_infos(fileid, fsp->name_hash, |
408 | 0 | &delete_pending); |
409 | 0 | } |
410 | 0 | } |
411 | | |
412 | 0 | status = smbd_do_qfilepathinfo(conn, state, |
413 | 0 | smbreq, |
414 | 0 | file_info_level, |
415 | 0 | fsp, |
416 | 0 | fsp->fsp_name, |
417 | 0 | delete_pending, |
418 | 0 | NULL, |
419 | 0 | STR_UNICODE, |
420 | 0 | in_output_buffer_length, |
421 | 0 | &fixed_portion, |
422 | 0 | &data, |
423 | 0 | &data_size); |
424 | 0 | if (!NT_STATUS_IS_OK(status)) { |
425 | 0 | SAFE_FREE(data); |
426 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL)) { |
427 | 0 | status = NT_STATUS_INVALID_INFO_CLASS; |
428 | 0 | } |
429 | 0 | tevent_req_nterror(req, status); |
430 | 0 | return tevent_req_post(req, ev); |
431 | 0 | } |
432 | 0 | if (in_output_buffer_length < fixed_portion) { |
433 | 0 | SAFE_FREE(data); |
434 | 0 | tevent_req_nterror( |
435 | 0 | req, NT_STATUS_INFO_LENGTH_MISMATCH); |
436 | 0 | return tevent_req_post(req, ev); |
437 | 0 | } |
438 | 0 | if (data_size > 0) { |
439 | 0 | state->out_output_buffer = data_blob_talloc(state, |
440 | 0 | data, |
441 | 0 | data_size); |
442 | 0 | SAFE_FREE(data); |
443 | 0 | if (tevent_req_nomem(state->out_output_buffer.data, req)) { |
444 | 0 | return tevent_req_post(req, ev); |
445 | 0 | } |
446 | 0 | if (data_size > in_output_buffer_length) { |
447 | 0 | state->out_output_buffer.length = |
448 | 0 | in_output_buffer_length; |
449 | 0 | status = STATUS_BUFFER_OVERFLOW; |
450 | 0 | } |
451 | 0 | } |
452 | 0 | SAFE_FREE(data); |
453 | 0 | break; |
454 | 0 | } |
455 | | |
456 | 0 | case SMB2_0_INFO_FILESYSTEM: |
457 | 0 | { |
458 | 0 | uint16_t file_info_level; |
459 | 0 | char *data = NULL; |
460 | 0 | int data_size = 0; |
461 | 0 | size_t fixed_portion; |
462 | |
|
463 | 0 | switch (in_file_info_class) { |
464 | 0 | case FSCC_FS_POSIX_INFORMATION: |
465 | 0 | file_info_level = in_file_info_class; |
466 | 0 | break; |
467 | 0 | default: |
468 | | /* the levels directly map to the passthru levels */ |
469 | 0 | file_info_level = in_file_info_class + NT_PASSTHROUGH_OFFSET; |
470 | 0 | break; |
471 | 0 | } |
472 | | |
473 | 0 | status = smbd_do_qfsinfo(smb2req->xconn, |
474 | 0 | conn, |
475 | 0 | state, |
476 | 0 | file_info_level, |
477 | 0 | STR_UNICODE, |
478 | 0 | in_output_buffer_length, |
479 | 0 | &fixed_portion, |
480 | 0 | fsp, |
481 | 0 | &data, |
482 | 0 | &data_size); |
483 | | /* some responses set STATUS_BUFFER_OVERFLOW and return |
484 | | partial, but valid data */ |
485 | 0 | if (!(NT_STATUS_IS_OK(status) || |
486 | 0 | NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW))) { |
487 | 0 | SAFE_FREE(data); |
488 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL)) { |
489 | 0 | status = NT_STATUS_INVALID_INFO_CLASS; |
490 | 0 | } |
491 | 0 | tevent_req_nterror(req, status); |
492 | 0 | return tevent_req_post(req, ev); |
493 | 0 | } |
494 | 0 | if (in_output_buffer_length < fixed_portion) { |
495 | 0 | SAFE_FREE(data); |
496 | 0 | tevent_req_nterror( |
497 | 0 | req, NT_STATUS_INFO_LENGTH_MISMATCH); |
498 | 0 | return tevent_req_post(req, ev); |
499 | 0 | } |
500 | 0 | if (data_size > 0) { |
501 | 0 | state->out_output_buffer = data_blob_talloc(state, |
502 | 0 | data, |
503 | 0 | data_size); |
504 | 0 | SAFE_FREE(data); |
505 | 0 | if (tevent_req_nomem(state->out_output_buffer.data, req)) { |
506 | 0 | return tevent_req_post(req, ev); |
507 | 0 | } |
508 | 0 | if (data_size > in_output_buffer_length) { |
509 | 0 | state->out_output_buffer.length = |
510 | 0 | in_output_buffer_length; |
511 | 0 | status = STATUS_BUFFER_OVERFLOW; |
512 | 0 | } |
513 | 0 | } |
514 | 0 | SAFE_FREE(data); |
515 | 0 | break; |
516 | 0 | } |
517 | | |
518 | 0 | case SMB2_0_INFO_SECURITY: |
519 | 0 | { |
520 | 0 | uint8_t *p_marshalled_sd = NULL; |
521 | 0 | size_t sd_size = 0; |
522 | |
|
523 | 0 | status = smbd_do_query_security_desc(conn, |
524 | 0 | state, |
525 | 0 | fsp, |
526 | | /* Security info wanted. */ |
527 | 0 | in_additional_information & |
528 | 0 | SMB_SUPPORTED_SECINFO_FLAGS, |
529 | 0 | in_output_buffer_length, |
530 | 0 | &p_marshalled_sd, |
531 | 0 | &sd_size); |
532 | |
|
533 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_BUFFER_TOO_SMALL)) { |
534 | | /* Return needed size. */ |
535 | 0 | state->out_output_buffer = data_blob_talloc(state, |
536 | 0 | NULL, |
537 | 0 | 4); |
538 | 0 | if (tevent_req_nomem(state->out_output_buffer.data, req)) { |
539 | 0 | return tevent_req_post(req, ev); |
540 | 0 | } |
541 | 0 | SIVAL(state->out_output_buffer.data,0,(uint32_t)sd_size); |
542 | 0 | state->status = NT_STATUS_BUFFER_TOO_SMALL; |
543 | 0 | break; |
544 | 0 | } |
545 | 0 | if (!NT_STATUS_IS_OK(status)) { |
546 | 0 | DEBUG(10,("smbd_smb2_getinfo_send: " |
547 | 0 | "smbd_do_query_security_desc of %s failed " |
548 | 0 | "(%s)\n", fsp_str_dbg(fsp), |
549 | 0 | nt_errstr(status))); |
550 | 0 | tevent_req_nterror(req, status); |
551 | 0 | return tevent_req_post(req, ev); |
552 | 0 | } |
553 | | |
554 | 0 | if (sd_size > 0) { |
555 | 0 | state->out_output_buffer = data_blob_talloc(state, |
556 | 0 | p_marshalled_sd, |
557 | 0 | sd_size); |
558 | 0 | if (tevent_req_nomem(state->out_output_buffer.data, req)) { |
559 | 0 | return tevent_req_post(req, ev); |
560 | 0 | } |
561 | 0 | } |
562 | 0 | break; |
563 | 0 | } |
564 | | |
565 | 0 | case SMB2_0_INFO_QUOTA: { |
566 | 0 | #ifdef HAVE_SYS_QUOTAS |
567 | 0 | struct smb2_query_quota_info info; |
568 | 0 | enum ndr_err_code err; |
569 | 0 | uint8_t *data = NULL; |
570 | 0 | uint32_t data_size = 0; |
571 | 0 | struct ndr_pull *ndr_pull = NULL; |
572 | 0 | DATA_BLOB sid_buf = data_blob_null; |
573 | 0 | TALLOC_CTX *tmp_ctx = talloc_init("geninfo_quota"); |
574 | 0 | bool ok; |
575 | |
|
576 | 0 | if (!tmp_ctx) { |
577 | 0 | tevent_req_nterror(req, NT_STATUS_NO_MEMORY); |
578 | 0 | return tevent_req_post(req, ev); |
579 | 0 | } |
580 | | |
581 | 0 | ok = check_fsp_ntquota_handle(conn, smbreq, fsp); |
582 | 0 | if (!ok) { |
583 | 0 | DBG_INFO("no valid QUOTA HANDLE\n"); |
584 | 0 | TALLOC_FREE(tmp_ctx); |
585 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE); |
586 | 0 | return tevent_req_post(req, ev); |
587 | 0 | } |
588 | | |
589 | 0 | ndr_pull = ndr_pull_init_blob(&in_input_buffer, tmp_ctx); |
590 | 0 | if (!ndr_pull) { |
591 | 0 | TALLOC_FREE(tmp_ctx); |
592 | 0 | tevent_req_nterror(req, NT_STATUS_NO_MEMORY); |
593 | 0 | return tevent_req_post(req, ev); |
594 | 0 | } |
595 | | |
596 | 0 | err = ndr_pull_smb2_query_quota_info(ndr_pull, |
597 | 0 | NDR_SCALARS | NDR_BUFFERS, |
598 | 0 | &info); |
599 | |
|
600 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(err)) { |
601 | 0 | DBG_DEBUG("failed to pull smb2_query_quota_info\n"); |
602 | 0 | TALLOC_FREE(tmp_ctx); |
603 | 0 | tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); |
604 | 0 | return tevent_req_post(req, ev); |
605 | 0 | } |
606 | | |
607 | 0 | DBG_DEBUG("quota list returnsingle %u, restartscan %u, " |
608 | 0 | "sid_list_length %u, start_sid_length %u, " |
609 | 0 | "startsidoffset %u\n", |
610 | 0 | (unsigned int)info.return_single, |
611 | 0 | (unsigned int)info.restart_scan, |
612 | 0 | (unsigned int)info.sid_list_length, |
613 | 0 | (unsigned int)info.start_sid_length, |
614 | 0 | (unsigned int)info.start_sid_offset); |
615 | | |
616 | | /* Currently we do not support the single start sid format */ |
617 | 0 | if (info.start_sid_length != 0 || info.start_sid_offset != 0 ) { |
618 | 0 | DBG_INFO("illegal single sid query\n"); |
619 | 0 | TALLOC_FREE(tmp_ctx); |
620 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
621 | 0 | return tevent_req_post(req, ev); |
622 | 0 | } |
623 | | |
624 | 0 | if (in_input_buffer.length < ndr_pull->offset) { |
625 | 0 | DBG_INFO("Invalid buffer length\n"); |
626 | 0 | TALLOC_FREE(tmp_ctx); |
627 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
628 | 0 | return tevent_req_post(req, ev); |
629 | 0 | } |
630 | | |
631 | 0 | sid_buf.data = in_input_buffer.data + ndr_pull->offset; |
632 | 0 | sid_buf.length = in_input_buffer.length - ndr_pull->offset; |
633 | |
|
634 | 0 | status = smbd_do_query_getinfo_quota(tmp_ctx, |
635 | 0 | fsp, |
636 | 0 | info.restart_scan, |
637 | 0 | info.return_single, |
638 | 0 | info.sid_list_length, |
639 | 0 | &sid_buf, |
640 | 0 | in_output_buffer_length, |
641 | 0 | &data, |
642 | 0 | &data_size); |
643 | |
|
644 | 0 | if (!NT_STATUS_IS_OK(status)) { |
645 | 0 | TALLOC_FREE(tmp_ctx); |
646 | 0 | tevent_req_nterror(req, status); |
647 | 0 | return tevent_req_post(req, ev); |
648 | 0 | } |
649 | | |
650 | 0 | state->out_output_buffer = |
651 | 0 | data_blob_talloc(state, data, data_size); |
652 | 0 | status = NT_STATUS_OK; |
653 | 0 | TALLOC_FREE(tmp_ctx); |
654 | 0 | break; |
655 | | #else |
656 | | tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); |
657 | | return tevent_req_post(req, ev); |
658 | | #endif |
659 | 0 | } |
660 | | |
661 | 0 | default: |
662 | 0 | DEBUG(10,("smbd_smb2_getinfo_send: " |
663 | 0 | "unknown in_info_type of %u " |
664 | 0 | " for file %s\n", |
665 | 0 | (unsigned int)in_info_type, |
666 | 0 | fsp_str_dbg(fsp) )); |
667 | |
|
668 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
669 | 0 | return tevent_req_post(req, ev); |
670 | 0 | } |
671 | | |
672 | 0 | state->status = status; |
673 | 0 | tevent_req_done(req); |
674 | 0 | return tevent_req_post(req, ev); |
675 | 0 | } |
676 | | |
677 | | static NTSTATUS smbd_smb2_getinfo_recv(struct tevent_req *req, |
678 | | TALLOC_CTX *mem_ctx, |
679 | | DATA_BLOB *out_output_buffer, |
680 | | NTSTATUS *pstatus) |
681 | 0 | { |
682 | 0 | NTSTATUS status; |
683 | 0 | struct smbd_smb2_getinfo_state *state = tevent_req_data(req, |
684 | 0 | struct smbd_smb2_getinfo_state); |
685 | |
|
686 | 0 | if (tevent_req_is_nterror(req, &status)) { |
687 | 0 | tevent_req_received(req); |
688 | 0 | return status; |
689 | 0 | } |
690 | | |
691 | 0 | *out_output_buffer = state->out_output_buffer; |
692 | 0 | talloc_steal(mem_ctx, out_output_buffer->data); |
693 | 0 | *pstatus = state->status; |
694 | |
|
695 | 0 | tevent_req_received(req); |
696 | 0 | return NT_STATUS_OK; |
697 | 0 | } |