/src/samba/source3/smbd/smb1_ipc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | Inter-process communication and named pipe handling |
4 | | Copyright (C) Andrew Tridgell 1992-1998 |
5 | | |
6 | | SMB Version handling |
7 | | Copyright (C) John H Terpstra 1995-1998 |
8 | | |
9 | | This program is free software; you can redistribute it and/or modify |
10 | | it under the terms of the GNU General Public License as published by |
11 | | the Free Software Foundation; either version 3 of the License, or |
12 | | (at your option) any later version. |
13 | | |
14 | | This program is distributed in the hope that it will be useful, |
15 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | GNU General Public License for more details. |
18 | | |
19 | | You should have received a copy of the GNU General Public License |
20 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
21 | | */ |
22 | | /* |
23 | | This file handles the named pipe and mailslot calls |
24 | | in the SMBtrans protocol |
25 | | */ |
26 | | |
27 | | #include "includes.h" |
28 | | #include "smbd/smbd.h" |
29 | | #include "smbd/globals.h" |
30 | | #include "smbprofile.h" |
31 | | #include "rpc_server/srv_pipe_hnd.h" |
32 | | #include "source3/lib/substitute.h" |
33 | | |
34 | | #define NERR_notsupported 50 |
35 | | |
36 | | static void api_no_reply(connection_struct *conn, struct smb_request *req); |
37 | | |
38 | | /******************************************************************* |
39 | | copies parameters and data, as needed, into the smb buffer |
40 | | |
41 | | *both* the data and params sections should be aligned. this |
42 | | is fudged in the rpc pipes by |
43 | | at present, only the data section is. this may be a possible |
44 | | cause of some of the ipc problems being experienced. lkcl26dec97 |
45 | | |
46 | | ******************************************************************/ |
47 | | |
48 | | static void copy_trans_params_and_data(char *outbuf, int align, |
49 | | char *rparam, int param_offset, int param_len, |
50 | | char *rdata, int data_offset, int data_len) |
51 | 0 | { |
52 | 0 | char *copy_into = smb_buf(outbuf); |
53 | |
|
54 | 0 | if(param_len < 0) |
55 | 0 | param_len = 0; |
56 | |
|
57 | 0 | if(data_len < 0) |
58 | 0 | data_len = 0; |
59 | |
|
60 | 0 | DEBUG(5,("copy_trans_params_and_data: params[%d..%d] data[%d..%d] (align %d)\n", |
61 | 0 | param_offset, param_offset + param_len, |
62 | 0 | data_offset , data_offset + data_len, |
63 | 0 | align)); |
64 | |
|
65 | 0 | *copy_into = '\0'; |
66 | |
|
67 | 0 | copy_into += 1; |
68 | |
|
69 | 0 | if (param_len) |
70 | 0 | memcpy(copy_into, &rparam[param_offset], param_len); |
71 | |
|
72 | 0 | copy_into += param_len; |
73 | 0 | if (align) { |
74 | 0 | memset(copy_into, '\0', align); |
75 | 0 | } |
76 | |
|
77 | 0 | copy_into += align; |
78 | |
|
79 | 0 | if (data_len ) |
80 | 0 | memcpy(copy_into, &rdata[data_offset], data_len); |
81 | 0 | } |
82 | | |
83 | | /**************************************************************************** |
84 | | Send a trans reply. |
85 | | ****************************************************************************/ |
86 | | |
87 | | void send_trans_reply(connection_struct *conn, |
88 | | struct smb_request *req, |
89 | | char *rparam, int rparam_len, |
90 | | char *rdata, int rdata_len, |
91 | | bool buffer_too_large) |
92 | 0 | { |
93 | 0 | int this_ldata,this_lparam; |
94 | 0 | int tot_data_sent = 0; |
95 | 0 | int tot_param_sent = 0; |
96 | 0 | int align; |
97 | |
|
98 | 0 | int ldata = rdata ? rdata_len : 0; |
99 | 0 | int lparam = rparam ? rparam_len : 0; |
100 | 0 | struct smbXsrv_connection *xconn = req->xconn; |
101 | 0 | int max_send = xconn->smb1.sessions.max_send; |
102 | | /* HACK: make sure we send at least 128 byte in one go */ |
103 | 0 | int hdr_overhead = SMB_BUFFER_SIZE_MIN - 128; |
104 | |
|
105 | 0 | if (buffer_too_large) |
106 | 0 | DEBUG(5,("send_trans_reply: buffer %d too large\n", ldata )); |
107 | |
|
108 | 0 | this_lparam = MIN(lparam,max_send - hdr_overhead); |
109 | 0 | this_ldata = MIN(ldata,max_send - (hdr_overhead+this_lparam)); |
110 | |
|
111 | 0 | align = ((this_lparam)%4); |
112 | |
|
113 | 0 | reply_smb1_outbuf(req, 10, 1+align+this_ldata+this_lparam); |
114 | | |
115 | | /* |
116 | | * We might have SMBtranss in req which was transferred to the outbuf, |
117 | | * fix that. |
118 | | */ |
119 | 0 | SCVAL(req->outbuf, smb_com, SMBtrans); |
120 | |
|
121 | 0 | copy_trans_params_and_data((char *)req->outbuf, align, |
122 | 0 | rparam, tot_param_sent, this_lparam, |
123 | 0 | rdata, tot_data_sent, this_ldata); |
124 | |
|
125 | 0 | SSVAL(req->outbuf,smb_vwv0,lparam); |
126 | 0 | SSVAL(req->outbuf,smb_vwv1,ldata); |
127 | 0 | SSVAL(req->outbuf,smb_vwv3,this_lparam); |
128 | 0 | SSVAL(req->outbuf,smb_vwv4, |
129 | 0 | smb_offset(smb_buf(req->outbuf)+1, req->outbuf)); |
130 | 0 | SSVAL(req->outbuf,smb_vwv5,0); |
131 | 0 | SSVAL(req->outbuf,smb_vwv6,this_ldata); |
132 | 0 | SSVAL(req->outbuf,smb_vwv7, |
133 | 0 | smb_offset(smb_buf(req->outbuf)+1+this_lparam+align, |
134 | 0 | req->outbuf)); |
135 | 0 | SSVAL(req->outbuf,smb_vwv8,0); |
136 | 0 | SSVAL(req->outbuf,smb_vwv9,0); |
137 | |
|
138 | 0 | if (buffer_too_large) { |
139 | 0 | error_packet_set((char *)req->outbuf, ERRDOS, ERRmoredata, |
140 | 0 | STATUS_BUFFER_OVERFLOW, __LINE__, __FILE__); |
141 | 0 | } |
142 | |
|
143 | 0 | show_msg((char *)req->outbuf); |
144 | 0 | if (!smb1_srv_send(xconn, |
145 | 0 | (char *)req->outbuf, |
146 | 0 | true, |
147 | 0 | req->seqnum + 1, |
148 | 0 | IS_CONN_ENCRYPTED(conn))) { |
149 | 0 | exit_server_cleanly("send_trans_reply: smb1_srv_send failed."); |
150 | 0 | } |
151 | | |
152 | 0 | TALLOC_FREE(req->outbuf); |
153 | |
|
154 | 0 | tot_data_sent = this_ldata; |
155 | 0 | tot_param_sent = this_lparam; |
156 | |
|
157 | 0 | while (tot_data_sent < ldata || tot_param_sent < lparam) |
158 | 0 | { |
159 | 0 | this_lparam = MIN(lparam-tot_param_sent, |
160 | 0 | max_send - hdr_overhead); |
161 | 0 | this_ldata = MIN(ldata -tot_data_sent, |
162 | 0 | max_send - (hdr_overhead+this_lparam)); |
163 | |
|
164 | 0 | if(this_lparam < 0) |
165 | 0 | this_lparam = 0; |
166 | |
|
167 | 0 | if(this_ldata < 0) |
168 | 0 | this_ldata = 0; |
169 | |
|
170 | 0 | align = (this_lparam%4); |
171 | |
|
172 | 0 | reply_smb1_outbuf(req, 10, 1+align+this_ldata+this_lparam); |
173 | | |
174 | | /* |
175 | | * We might have SMBtranss in req which was transferred to the |
176 | | * outbuf, fix that. |
177 | | */ |
178 | 0 | SCVAL(req->outbuf, smb_com, SMBtrans); |
179 | |
|
180 | 0 | copy_trans_params_and_data((char *)req->outbuf, align, |
181 | 0 | rparam, tot_param_sent, this_lparam, |
182 | 0 | rdata, tot_data_sent, this_ldata); |
183 | |
|
184 | 0 | SSVAL(req->outbuf,smb_vwv0,lparam); |
185 | 0 | SSVAL(req->outbuf,smb_vwv1,ldata); |
186 | |
|
187 | 0 | SSVAL(req->outbuf,smb_vwv3,this_lparam); |
188 | 0 | SSVAL(req->outbuf,smb_vwv4, |
189 | 0 | smb_offset(smb_buf(req->outbuf)+1,req->outbuf)); |
190 | 0 | SSVAL(req->outbuf,smb_vwv5,tot_param_sent); |
191 | 0 | SSVAL(req->outbuf,smb_vwv6,this_ldata); |
192 | 0 | SSVAL(req->outbuf,smb_vwv7, |
193 | 0 | smb_offset(smb_buf(req->outbuf)+1+this_lparam+align, |
194 | 0 | req->outbuf)); |
195 | 0 | SSVAL(req->outbuf,smb_vwv8,tot_data_sent); |
196 | 0 | SSVAL(req->outbuf,smb_vwv9,0); |
197 | |
|
198 | 0 | if (buffer_too_large) { |
199 | 0 | error_packet_set((char *)req->outbuf, |
200 | 0 | ERRDOS, ERRmoredata, |
201 | 0 | STATUS_BUFFER_OVERFLOW, |
202 | 0 | __LINE__, __FILE__); |
203 | 0 | } |
204 | |
|
205 | 0 | show_msg((char *)req->outbuf); |
206 | 0 | if (!smb1_srv_send(xconn, |
207 | 0 | (char *)req->outbuf, |
208 | 0 | true, |
209 | 0 | req->seqnum + 1, |
210 | 0 | IS_CONN_ENCRYPTED(conn))) { |
211 | 0 | exit_server_cleanly("send_trans_reply: smb1_srv_send " |
212 | 0 | "failed."); |
213 | 0 | } |
214 | | |
215 | 0 | tot_data_sent += this_ldata; |
216 | 0 | tot_param_sent += this_lparam; |
217 | 0 | TALLOC_FREE(req->outbuf); |
218 | 0 | } |
219 | 0 | } |
220 | | |
221 | | /**************************************************************************** |
222 | | Start the first part of an RPC reply which began with an SMBtrans request. |
223 | | ****************************************************************************/ |
224 | | |
225 | | struct dcerpc_cmd_state { |
226 | | struct fake_file_handle *handle; |
227 | | uint8_t *data; |
228 | | size_t num_data; |
229 | | size_t max_read; |
230 | | }; |
231 | | |
232 | | static void api_dcerpc_cmd_write_done(struct tevent_req *subreq); |
233 | | static void api_dcerpc_cmd_read_done(struct tevent_req *subreq); |
234 | | |
235 | | static void api_dcerpc_cmd(connection_struct *conn, struct smb_request *req, |
236 | | files_struct *fsp, uint8_t *data, size_t length, |
237 | | size_t max_read) |
238 | 0 | { |
239 | 0 | struct tevent_req *subreq; |
240 | 0 | struct dcerpc_cmd_state *state; |
241 | 0 | bool busy; |
242 | |
|
243 | 0 | if (!fsp_is_np(fsp)) { |
244 | 0 | api_no_reply(conn, req); |
245 | 0 | return; |
246 | 0 | } |
247 | | |
248 | | /* |
249 | | * Trans requests are only allowed |
250 | | * if no other Trans or Read is active |
251 | | */ |
252 | 0 | busy = np_read_in_progress(fsp->fake_file_handle); |
253 | 0 | if (busy) { |
254 | 0 | reply_nterror(req, NT_STATUS_PIPE_BUSY); |
255 | 0 | return; |
256 | 0 | } |
257 | | |
258 | 0 | state = talloc(req, struct dcerpc_cmd_state); |
259 | 0 | if (state == NULL) { |
260 | 0 | reply_nterror(req, NT_STATUS_NO_MEMORY); |
261 | 0 | return; |
262 | 0 | } |
263 | 0 | req->async_priv = state; |
264 | |
|
265 | 0 | state->handle = fsp->fake_file_handle; |
266 | | |
267 | | /* |
268 | | * This memdup severely sucks. But doing it properly essentially means |
269 | | * to rewrite lanman.c, something which I don't really want to do now. |
270 | | */ |
271 | 0 | state->data = (uint8_t *)talloc_memdup(state, data, length); |
272 | 0 | if (state->data == NULL) { |
273 | 0 | reply_nterror(req, NT_STATUS_NO_MEMORY); |
274 | 0 | return; |
275 | 0 | } |
276 | 0 | state->num_data = length; |
277 | 0 | state->max_read = max_read; |
278 | |
|
279 | 0 | subreq = np_write_send(state, req->sconn->ev_ctx, state->handle, |
280 | 0 | state->data, length); |
281 | 0 | if (subreq == NULL) { |
282 | 0 | TALLOC_FREE(state); |
283 | 0 | reply_nterror(req, NT_STATUS_NO_MEMORY); |
284 | 0 | return; |
285 | 0 | } |
286 | 0 | tevent_req_set_callback(subreq, api_dcerpc_cmd_write_done, |
287 | 0 | talloc_move(conn, &req)); |
288 | 0 | } |
289 | | |
290 | | static void api_dcerpc_cmd_write_done(struct tevent_req *subreq) |
291 | 0 | { |
292 | 0 | struct smb_request *req = tevent_req_callback_data( |
293 | 0 | subreq, struct smb_request); |
294 | 0 | struct dcerpc_cmd_state *state = talloc_get_type_abort( |
295 | 0 | req->async_priv, struct dcerpc_cmd_state); |
296 | 0 | NTSTATUS status; |
297 | 0 | ssize_t nwritten = -1; |
298 | |
|
299 | 0 | status = np_write_recv(subreq, &nwritten); |
300 | 0 | TALLOC_FREE(subreq); |
301 | 0 | if (!NT_STATUS_IS_OK(status)) { |
302 | 0 | NTSTATUS old = status; |
303 | 0 | status = nt_status_np_pipe(old); |
304 | |
|
305 | 0 | DEBUG(10, ("Could not write to pipe: %s%s%s\n", |
306 | 0 | nt_errstr(old), |
307 | 0 | NT_STATUS_EQUAL(old, status)?"":" => ", |
308 | 0 | NT_STATUS_EQUAL(old, status)?"":nt_errstr(status))); |
309 | 0 | reply_nterror(req, status); |
310 | 0 | goto send; |
311 | 0 | } |
312 | 0 | if (nwritten != state->num_data) { |
313 | 0 | status = NT_STATUS_PIPE_NOT_AVAILABLE; |
314 | 0 | DEBUG(10, ("Could not write to pipe: (%d/%d) => %s\n", |
315 | 0 | (int)state->num_data, |
316 | 0 | (int)nwritten, nt_errstr(status))); |
317 | 0 | reply_nterror(req, status); |
318 | 0 | goto send; |
319 | 0 | } |
320 | | |
321 | 0 | state->data = talloc_realloc(state, state->data, uint8_t, |
322 | 0 | state->max_read); |
323 | 0 | if (state->data == NULL) { |
324 | 0 | reply_nterror(req, NT_STATUS_NO_MEMORY); |
325 | 0 | goto send; |
326 | 0 | } |
327 | | |
328 | 0 | subreq = np_read_send(state, req->sconn->ev_ctx, |
329 | 0 | state->handle, state->data, state->max_read); |
330 | 0 | if (subreq == NULL) { |
331 | 0 | reply_nterror(req, NT_STATUS_NO_MEMORY); |
332 | 0 | goto send; |
333 | 0 | } |
334 | 0 | tevent_req_set_callback(subreq, api_dcerpc_cmd_read_done, req); |
335 | 0 | return; |
336 | | |
337 | 0 | send: |
338 | 0 | if (!smb1_srv_send(req->xconn, |
339 | 0 | (char *)req->outbuf, |
340 | 0 | true, |
341 | 0 | req->seqnum + 1, |
342 | 0 | IS_CONN_ENCRYPTED(req->conn) || req->encrypted)) { |
343 | 0 | exit_server_cleanly("api_dcerpc_cmd_write_done: " |
344 | 0 | "smb1_srv_send failed."); |
345 | 0 | } |
346 | 0 | TALLOC_FREE(req); |
347 | 0 | } |
348 | | |
349 | | static void api_dcerpc_cmd_read_done(struct tevent_req *subreq) |
350 | 0 | { |
351 | 0 | struct smb_request *req = tevent_req_callback_data( |
352 | 0 | subreq, struct smb_request); |
353 | 0 | struct dcerpc_cmd_state *state = talloc_get_type_abort( |
354 | 0 | req->async_priv, struct dcerpc_cmd_state); |
355 | 0 | NTSTATUS status; |
356 | 0 | ssize_t nread; |
357 | 0 | bool is_data_outstanding; |
358 | |
|
359 | 0 | status = np_read_recv(subreq, &nread, &is_data_outstanding); |
360 | 0 | TALLOC_FREE(subreq); |
361 | |
|
362 | 0 | if (!NT_STATUS_IS_OK(status)) { |
363 | 0 | NTSTATUS old = status; |
364 | 0 | status = nt_status_np_pipe(old); |
365 | |
|
366 | 0 | DEBUG(10, ("Could not read from to pipe: %s%s%s\n", |
367 | 0 | nt_errstr(old), |
368 | 0 | NT_STATUS_EQUAL(old, status)?"":" => ", |
369 | 0 | NT_STATUS_EQUAL(old, status)?"":nt_errstr(status))); |
370 | 0 | reply_nterror(req, status); |
371 | |
|
372 | 0 | if (!smb1_srv_send(req->xconn, |
373 | 0 | (char *)req->outbuf, |
374 | 0 | true, |
375 | 0 | req->seqnum + 1, |
376 | 0 | IS_CONN_ENCRYPTED(req->conn) || |
377 | 0 | req->encrypted)) { |
378 | 0 | exit_server_cleanly("api_dcerpc_cmd_read_done: " |
379 | 0 | "smb1_srv_send failed."); |
380 | 0 | } |
381 | 0 | TALLOC_FREE(req); |
382 | 0 | return; |
383 | 0 | } |
384 | | |
385 | 0 | send_trans_reply(req->conn, req, NULL, 0, (char *)state->data, nread, |
386 | 0 | is_data_outstanding); |
387 | 0 | TALLOC_FREE(req); |
388 | 0 | } |
389 | | |
390 | | /**************************************************************************** |
391 | | WaitNamedPipeHandleState |
392 | | ****************************************************************************/ |
393 | | |
394 | | static void api_WNPHS(connection_struct *conn, struct smb_request *req, |
395 | | struct files_struct *fsp, char *param, int param_len) |
396 | 0 | { |
397 | 0 | if (!param || param_len < 2) { |
398 | 0 | reply_nterror(req, NT_STATUS_INVALID_PARAMETER); |
399 | 0 | return; |
400 | 0 | } |
401 | | |
402 | 0 | DEBUG(4,("WaitNamedPipeHandleState priority %x\n", |
403 | 0 | (int)SVAL(param,0))); |
404 | |
|
405 | 0 | send_trans_reply(conn, req, NULL, 0, NULL, 0, False); |
406 | 0 | } |
407 | | |
408 | | |
409 | | /**************************************************************************** |
410 | | SetNamedPipeHandleState |
411 | | ****************************************************************************/ |
412 | | |
413 | | static void api_SNPHS(connection_struct *conn, struct smb_request *req, |
414 | | struct files_struct *fsp, char *param, int param_len) |
415 | 0 | { |
416 | 0 | if (!param || param_len < 2) { |
417 | 0 | reply_nterror(req, NT_STATUS_INVALID_PARAMETER); |
418 | 0 | return; |
419 | 0 | } |
420 | | |
421 | 0 | DEBUG(4,("SetNamedPipeHandleState to code %x\n", (int)SVAL(param,0))); |
422 | |
|
423 | 0 | send_trans_reply(conn, req, NULL, 0, NULL, 0, False); |
424 | 0 | } |
425 | | |
426 | | |
427 | | /**************************************************************************** |
428 | | When no reply is generated, indicate unsupported. |
429 | | ****************************************************************************/ |
430 | | |
431 | | static void api_no_reply(connection_struct *conn, struct smb_request *req) |
432 | 0 | { |
433 | 0 | char rparam[4]; |
434 | | |
435 | | /* unsupported */ |
436 | 0 | SSVAL(rparam,0,NERR_notsupported); |
437 | 0 | SSVAL(rparam,2,0); /* converter word */ |
438 | |
|
439 | 0 | DEBUG(3,("Unsupported API fd command\n")); |
440 | | |
441 | | /* now send the reply */ |
442 | 0 | send_trans_reply(conn, req, rparam, 4, NULL, 0, False); |
443 | |
|
444 | 0 | return; |
445 | 0 | } |
446 | | |
447 | | /**************************************************************************** |
448 | | Handle remote api calls delivered to a named pipe already opened. |
449 | | ****************************************************************************/ |
450 | | |
451 | | static void api_fd_reply(connection_struct *conn, uint64_t vuid, |
452 | | struct smb_request *req, |
453 | | uint16_t *setup, uint8_t *data, char *params, |
454 | | int suwcnt, int tdscnt, int tpscnt, |
455 | | int mdrcnt, int mprcnt) |
456 | 0 | { |
457 | 0 | struct files_struct *fsp; |
458 | 0 | int pnum; |
459 | 0 | int subcommand; |
460 | |
|
461 | 0 | DEBUG(5,("api_fd_reply\n")); |
462 | | |
463 | | /* First find out the name of this file. */ |
464 | 0 | if (suwcnt != 2) { |
465 | 0 | DEBUG(0,("Unexpected named pipe transaction.\n")); |
466 | 0 | reply_nterror(req, NT_STATUS_INVALID_PARAMETER); |
467 | 0 | return; |
468 | 0 | } |
469 | | |
470 | | /* Get the file handle and hence the file name. */ |
471 | | /* |
472 | | * NB. The setup array has already been transformed |
473 | | * via SVAL and so is in host byte order. |
474 | | */ |
475 | 0 | pnum = ((int)setup[1]) & 0xFFFF; |
476 | 0 | subcommand = ((int)setup[0]) & 0xFFFF; |
477 | |
|
478 | 0 | fsp = file_fsp(req, pnum); |
479 | |
|
480 | 0 | if (!fsp_is_np(fsp)) { |
481 | 0 | if (subcommand == TRANSACT_WAITNAMEDPIPEHANDLESTATE) { |
482 | | /* Win9x does this call with a unicode pipe name, not a pnum. */ |
483 | | /* Just return success for now... */ |
484 | 0 | DEBUG(3,("Got TRANSACT_WAITNAMEDPIPEHANDLESTATE on text pipe name\n")); |
485 | 0 | send_trans_reply(conn, req, NULL, 0, NULL, 0, False); |
486 | 0 | return; |
487 | 0 | } |
488 | | |
489 | 0 | DEBUG(1,("api_fd_reply: INVALID PIPE HANDLE: %x\n", pnum)); |
490 | 0 | reply_nterror(req, NT_STATUS_INVALID_HANDLE); |
491 | 0 | return; |
492 | 0 | } |
493 | | |
494 | 0 | if (vuid != fsp->vuid) { |
495 | 0 | DEBUG(1, ("Got pipe request (pnum %x) using invalid VUID %llu, " |
496 | 0 | "expected %llu\n", pnum, (unsigned long long)vuid, |
497 | 0 | (unsigned long long)fsp->vuid)); |
498 | 0 | reply_nterror(req, NT_STATUS_INVALID_HANDLE); |
499 | 0 | return; |
500 | 0 | } |
501 | | |
502 | 0 | DEBUG(3,("Got API command 0x%x on pipe \"%s\" (pnum %x)\n", |
503 | 0 | subcommand, fsp_str_dbg(fsp), pnum)); |
504 | |
|
505 | 0 | DEBUG(10, ("api_fd_reply: p:%p max_trans_reply: %d\n", fsp, mdrcnt)); |
506 | |
|
507 | 0 | switch (subcommand) { |
508 | 0 | case TRANSACT_DCERPCCMD: { |
509 | | /* dce/rpc command */ |
510 | 0 | api_dcerpc_cmd(conn, req, fsp, (uint8_t *)data, tdscnt, |
511 | 0 | mdrcnt); |
512 | 0 | break; |
513 | 0 | } |
514 | 0 | case TRANSACT_WAITNAMEDPIPEHANDLESTATE: |
515 | | /* Wait Named Pipe Handle state */ |
516 | 0 | api_WNPHS(conn, req, fsp, params, tpscnt); |
517 | 0 | break; |
518 | 0 | case TRANSACT_SETNAMEDPIPEHANDLESTATE: |
519 | | /* Set Named Pipe Handle state */ |
520 | 0 | api_SNPHS(conn, req, fsp, params, tpscnt); |
521 | 0 | break; |
522 | 0 | default: |
523 | 0 | reply_nterror(req, NT_STATUS_INVALID_PARAMETER); |
524 | 0 | return; |
525 | 0 | } |
526 | 0 | } |
527 | | |
528 | | /**************************************************************************** |
529 | | Handle named pipe commands. |
530 | | ****************************************************************************/ |
531 | | |
532 | | static void named_pipe(connection_struct *conn, uint64_t vuid, |
533 | | struct smb_request *req, |
534 | | const char *name, uint16_t *setup, |
535 | | char *data, char *params, |
536 | | int suwcnt, int tdscnt,int tpscnt, |
537 | | int msrcnt, int mdrcnt, int mprcnt) |
538 | 0 | { |
539 | 0 | DEBUG(3,("named pipe command on <%s> name\n", name)); |
540 | |
|
541 | 0 | if (strequal(name,"LANMAN")) { |
542 | 0 | api_reply(conn, vuid, req, |
543 | 0 | data, params, |
544 | 0 | tdscnt, tpscnt, |
545 | 0 | mdrcnt, mprcnt); |
546 | 0 | return; |
547 | 0 | } |
548 | | |
549 | 0 | if (strequal(name,"WKSSVC") || |
550 | 0 | strequal(name,"SRVSVC") || |
551 | 0 | strequal(name,"WINREG") || |
552 | 0 | strequal(name,"SAMR") || |
553 | 0 | strequal(name,"LSARPC")) { |
554 | |
|
555 | 0 | DEBUG(4,("named pipe command from Win95 (wow!)\n")); |
556 | |
|
557 | 0 | api_fd_reply(conn, vuid, req, |
558 | 0 | setup, (uint8_t *)data, params, |
559 | 0 | suwcnt, tdscnt, tpscnt, |
560 | 0 | mdrcnt, mprcnt); |
561 | 0 | return; |
562 | 0 | } |
563 | | |
564 | 0 | if (strlen(name) < 1) { |
565 | 0 | api_fd_reply(conn, vuid, req, |
566 | 0 | setup, (uint8_t *)data, |
567 | 0 | params, suwcnt, tdscnt, |
568 | 0 | tpscnt, mdrcnt, mprcnt); |
569 | 0 | return; |
570 | 0 | } |
571 | | |
572 | 0 | if (setup) |
573 | 0 | DEBUG(3,("unknown named pipe: setup 0x%X setup1=%d\n", |
574 | 0 | (int)setup[0],(int)setup[1])); |
575 | |
|
576 | 0 | reply_nterror(req, NT_STATUS_NOT_SUPPORTED); |
577 | 0 | return; |
578 | 0 | } |
579 | | |
580 | | static void handle_trans(connection_struct *conn, struct smb_request *req, |
581 | | struct trans_state *state) |
582 | 0 | { |
583 | 0 | char *local_machine_name; |
584 | 0 | int name_offset = 0; |
585 | |
|
586 | 0 | DEBUG(3,("trans <%s> data=%u params=%u setup=%u\n", |
587 | 0 | state->name,(unsigned int)state->total_data,(unsigned int)state->total_param, |
588 | 0 | (unsigned int)state->setup_count)); |
589 | | |
590 | | /* |
591 | | * WinCE weirdness.... |
592 | | */ |
593 | |
|
594 | 0 | local_machine_name = talloc_asprintf(state, "\\%s\\", |
595 | 0 | get_local_machine_name()); |
596 | |
|
597 | 0 | if (local_machine_name == NULL) { |
598 | 0 | reply_nterror(req, NT_STATUS_NO_MEMORY); |
599 | 0 | return; |
600 | 0 | } |
601 | | |
602 | 0 | if (strnequal(state->name, local_machine_name, |
603 | 0 | strlen(local_machine_name))) { |
604 | 0 | name_offset = strlen(local_machine_name)-1; |
605 | 0 | } |
606 | |
|
607 | 0 | if (!strnequal(&state->name[name_offset], "\\PIPE", |
608 | 0 | strlen("\\PIPE"))) { |
609 | 0 | reply_nterror(req, NT_STATUS_NOT_SUPPORTED); |
610 | 0 | return; |
611 | 0 | } |
612 | | |
613 | 0 | name_offset += strlen("\\PIPE"); |
614 | | |
615 | | /* Win9x weirdness. When talking to a unicode server Win9x |
616 | | only sends \PIPE instead of \PIPE\ */ |
617 | |
|
618 | 0 | if (state->name[name_offset] == '\\') |
619 | 0 | name_offset++; |
620 | |
|
621 | 0 | DEBUG(5,("calling named_pipe\n")); |
622 | 0 | named_pipe(conn, state->vuid, req, |
623 | 0 | state->name+name_offset, |
624 | 0 | state->setup,state->data, |
625 | 0 | state->param, |
626 | 0 | state->setup_count,state->total_data, |
627 | 0 | state->total_param, |
628 | 0 | state->max_setup_return, |
629 | 0 | state->max_data_return, |
630 | 0 | state->max_param_return); |
631 | |
|
632 | 0 | if (state->close_on_completion) { |
633 | 0 | struct smbXsrv_tcon *tcon; |
634 | 0 | NTSTATUS status; |
635 | |
|
636 | 0 | tcon = conn->tcon; |
637 | 0 | req->conn = NULL; |
638 | 0 | conn = NULL; |
639 | | |
640 | | /* |
641 | | * TODO: cancel all outstanding requests on the tcon |
642 | | */ |
643 | 0 | status = smbXsrv_tcon_disconnect(tcon, state->vuid); |
644 | 0 | if (!NT_STATUS_IS_OK(status)) { |
645 | 0 | DEBUG(0, ("handle_trans: " |
646 | 0 | "smbXsrv_tcon_disconnect() failed: %s\n", |
647 | 0 | nt_errstr(status))); |
648 | | /* |
649 | | * If we hit this case, there is something completely |
650 | | * wrong, so we better disconnect the transport connection. |
651 | | */ |
652 | 0 | exit_server(__location__ ": smbXsrv_tcon_disconnect failed"); |
653 | 0 | return; |
654 | 0 | } |
655 | | |
656 | 0 | TALLOC_FREE(tcon); |
657 | 0 | } |
658 | | |
659 | 0 | return; |
660 | 0 | } |
661 | | |
662 | | /**************************************************************************** |
663 | | Reply to a SMBtrans. |
664 | | ****************************************************************************/ |
665 | | |
666 | | void reply_trans(struct smb_request *req) |
667 | 0 | { |
668 | 0 | connection_struct *conn = req->conn; |
669 | 0 | unsigned int dsoff; |
670 | 0 | unsigned int dscnt; |
671 | 0 | unsigned int psoff; |
672 | 0 | unsigned int pscnt; |
673 | 0 | struct trans_state *state; |
674 | 0 | NTSTATUS result; |
675 | |
|
676 | 0 | START_PROFILE(SMBtrans); |
677 | |
|
678 | 0 | if (req->wct < 14) { |
679 | 0 | reply_nterror(req, NT_STATUS_INVALID_PARAMETER); |
680 | 0 | END_PROFILE(SMBtrans); |
681 | 0 | return; |
682 | 0 | } |
683 | | |
684 | 0 | dsoff = SVAL(req->vwv+12, 0); |
685 | 0 | dscnt = SVAL(req->vwv+11, 0); |
686 | 0 | psoff = SVAL(req->vwv+10, 0); |
687 | 0 | pscnt = SVAL(req->vwv+9, 0); |
688 | |
|
689 | 0 | result = allow_new_trans(conn->pending_trans, req->mid); |
690 | 0 | if (!NT_STATUS_IS_OK(result)) { |
691 | 0 | DEBUG(2, ("Got invalid trans request: %s\n", |
692 | 0 | nt_errstr(result))); |
693 | 0 | reply_nterror(req, result); |
694 | 0 | END_PROFILE(SMBtrans); |
695 | 0 | return; |
696 | 0 | } |
697 | | |
698 | 0 | if ((state = talloc_zero(conn, struct trans_state)) == NULL) { |
699 | 0 | DEBUG(0, ("talloc failed\n")); |
700 | 0 | reply_nterror(req, NT_STATUS_NO_MEMORY); |
701 | 0 | END_PROFILE(SMBtrans); |
702 | 0 | return; |
703 | 0 | } |
704 | | |
705 | 0 | state->cmd = SMBtrans; |
706 | |
|
707 | 0 | state->mid = req->mid; |
708 | 0 | state->vuid = req->vuid; |
709 | 0 | state->setup_count = CVAL(req->vwv+13, 0); |
710 | 0 | state->setup = NULL; |
711 | 0 | state->total_param = SVAL(req->vwv+0, 0); |
712 | 0 | state->param = NULL; |
713 | 0 | state->total_data = SVAL(req->vwv+1, 0); |
714 | 0 | state->data = NULL; |
715 | 0 | state->max_param_return = SVAL(req->vwv+2, 0); |
716 | 0 | state->max_data_return = SVAL(req->vwv+3, 0); |
717 | 0 | state->max_setup_return = CVAL(req->vwv+4, 0); |
718 | 0 | state->close_on_completion = BITSETW(req->vwv+5, 0); |
719 | 0 | state->one_way = BITSETW(req->vwv+5, 1); |
720 | |
|
721 | 0 | srvstr_pull_req_talloc(state, req, &state->name, req->buf, |
722 | 0 | STR_TERMINATE); |
723 | |
|
724 | 0 | if ((dscnt > state->total_data) || (pscnt > state->total_param) || |
725 | 0 | !state->name) |
726 | 0 | goto bad_param; |
727 | | |
728 | 0 | if (state->total_data) { |
729 | |
|
730 | 0 | if (smb_buffer_oob(state->total_data, 0, dscnt) |
731 | 0 | || smb_buffer_oob(smb_len(req->inbuf), dsoff, dscnt)) { |
732 | 0 | goto bad_param; |
733 | 0 | } |
734 | | |
735 | | /* Can't use talloc here, the core routines do realloc on the |
736 | | * params and data. Out of paranoia, 100 bytes too many. */ |
737 | 0 | state->data = (char *)SMB_MALLOC(state->total_data+100); |
738 | 0 | if (state->data == NULL) { |
739 | 0 | DEBUG(0,("reply_trans: data malloc fail for %u " |
740 | 0 | "bytes !\n", (unsigned int)state->total_data)); |
741 | 0 | TALLOC_FREE(state); |
742 | 0 | reply_nterror(req, NT_STATUS_NO_MEMORY); |
743 | 0 | END_PROFILE(SMBtrans); |
744 | 0 | return; |
745 | 0 | } |
746 | | /* null-terminate the slack space */ |
747 | 0 | memset(&state->data[state->total_data], 0, 100); |
748 | |
|
749 | 0 | memcpy(state->data,smb_base(req->inbuf)+dsoff,dscnt); |
750 | 0 | } |
751 | | |
752 | 0 | if (state->total_param) { |
753 | |
|
754 | 0 | if (smb_buffer_oob(state->total_param, 0, pscnt) |
755 | 0 | || smb_buffer_oob(smb_len(req->inbuf), psoff, pscnt)) { |
756 | 0 | goto bad_param; |
757 | 0 | } |
758 | | |
759 | | /* Can't use talloc here, the core routines do realloc on the |
760 | | * params and data. Out of paranoia, 100 bytes too many */ |
761 | 0 | state->param = (char *)SMB_MALLOC(state->total_param+100); |
762 | 0 | if (state->param == NULL) { |
763 | 0 | DEBUG(0,("reply_trans: param malloc fail for %u " |
764 | 0 | "bytes !\n", (unsigned int)state->total_param)); |
765 | 0 | SAFE_FREE(state->data); |
766 | 0 | TALLOC_FREE(state); |
767 | 0 | reply_nterror(req, NT_STATUS_NO_MEMORY); |
768 | 0 | END_PROFILE(SMBtrans); |
769 | 0 | return; |
770 | 0 | } |
771 | | /* null-terminate the slack space */ |
772 | 0 | memset(&state->param[state->total_param], 0, 100); |
773 | |
|
774 | 0 | memcpy(state->param,smb_base(req->inbuf)+psoff,pscnt); |
775 | 0 | } |
776 | | |
777 | 0 | state->received_data = dscnt; |
778 | 0 | state->received_param = pscnt; |
779 | |
|
780 | 0 | if (state->setup_count) { |
781 | 0 | unsigned int i; |
782 | | |
783 | | /* |
784 | | * No overflow possible here, state->setup_count is an |
785 | | * unsigned int, being filled by a single byte from |
786 | | * CVAL(req->vwv+13, 0) above. The cast in the comparison |
787 | | * below is not necessary, it's here to clarify things. The |
788 | | * validity of req->vwv and req->wct has been checked in |
789 | | * init_smb1_request already. |
790 | | */ |
791 | 0 | if (state->setup_count + 14 > (unsigned int)req->wct) { |
792 | 0 | goto bad_param; |
793 | 0 | } |
794 | | |
795 | 0 | if((state->setup = talloc_array( |
796 | 0 | state, uint16_t, state->setup_count)) == NULL) { |
797 | 0 | DEBUG(0,("reply_trans: setup malloc fail for %u " |
798 | 0 | "bytes !\n", (unsigned int) |
799 | 0 | (state->setup_count * sizeof(uint16_t)))); |
800 | 0 | SAFE_FREE(state->data); |
801 | 0 | SAFE_FREE(state->param); |
802 | 0 | TALLOC_FREE(state); |
803 | 0 | reply_nterror(req, NT_STATUS_NO_MEMORY); |
804 | 0 | END_PROFILE(SMBtrans); |
805 | 0 | return; |
806 | 0 | } |
807 | | |
808 | 0 | for (i=0;i<state->setup_count;i++) { |
809 | 0 | state->setup[i] = SVAL(req->vwv + 14 + i, 0); |
810 | 0 | } |
811 | 0 | } |
812 | | |
813 | 0 | state->received_param = pscnt; |
814 | |
|
815 | 0 | if ((state->received_param != state->total_param) || |
816 | 0 | (state->received_data != state->total_data)) { |
817 | 0 | DLIST_ADD(conn->pending_trans, state); |
818 | | |
819 | | /* We need to send an interim response then receive the rest |
820 | | of the parameter/data bytes */ |
821 | 0 | reply_smb1_outbuf(req, 0, 0); |
822 | 0 | show_msg((char *)req->outbuf); |
823 | 0 | END_PROFILE(SMBtrans); |
824 | 0 | return; |
825 | 0 | } |
826 | | |
827 | 0 | talloc_steal(talloc_tos(), state); |
828 | |
|
829 | 0 | handle_trans(conn, req, state); |
830 | |
|
831 | 0 | SAFE_FREE(state->data); |
832 | 0 | SAFE_FREE(state->param); |
833 | 0 | TALLOC_FREE(state); |
834 | |
|
835 | 0 | END_PROFILE(SMBtrans); |
836 | 0 | return; |
837 | | |
838 | 0 | bad_param: |
839 | |
|
840 | 0 | DEBUG(0,("reply_trans: invalid trans parameters\n")); |
841 | 0 | SAFE_FREE(state->data); |
842 | 0 | SAFE_FREE(state->param); |
843 | 0 | TALLOC_FREE(state); |
844 | 0 | END_PROFILE(SMBtrans); |
845 | 0 | reply_nterror(req, NT_STATUS_INVALID_PARAMETER); |
846 | 0 | return; |
847 | 0 | } |
848 | | |
849 | | /**************************************************************************** |
850 | | Reply to a secondary SMBtrans. |
851 | | ****************************************************************************/ |
852 | | |
853 | | void reply_transs(struct smb_request *req) |
854 | 0 | { |
855 | 0 | connection_struct *conn = req->conn; |
856 | 0 | unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp; |
857 | 0 | struct trans_state *state; |
858 | |
|
859 | 0 | START_PROFILE(SMBtranss); |
860 | |
|
861 | 0 | show_msg((const char *)req->inbuf); |
862 | |
|
863 | 0 | if (req->wct < 8) { |
864 | 0 | reply_nterror(req, NT_STATUS_INVALID_PARAMETER); |
865 | 0 | END_PROFILE(SMBtranss); |
866 | 0 | return; |
867 | 0 | } |
868 | | |
869 | 0 | for (state = conn->pending_trans; state != NULL; |
870 | 0 | state = state->next) { |
871 | 0 | if (state->mid == req->mid) { |
872 | 0 | break; |
873 | 0 | } |
874 | 0 | } |
875 | |
|
876 | 0 | if ((state == NULL) || (state->cmd != SMBtrans)) { |
877 | 0 | reply_nterror(req, NT_STATUS_INVALID_PARAMETER); |
878 | 0 | END_PROFILE(SMBtranss); |
879 | 0 | return; |
880 | 0 | } |
881 | | |
882 | | /* Revise total_params and total_data in case they have changed |
883 | | * downwards */ |
884 | | |
885 | 0 | if (SVAL(req->vwv+0, 0) < state->total_param) |
886 | 0 | state->total_param = SVAL(req->vwv+0, 0); |
887 | 0 | if (SVAL(req->vwv+1, 0) < state->total_data) |
888 | 0 | state->total_data = SVAL(req->vwv+1, 0); |
889 | |
|
890 | 0 | pcnt = SVAL(req->vwv+2, 0); |
891 | 0 | poff = SVAL(req->vwv+3, 0); |
892 | 0 | pdisp = SVAL(req->vwv+4, 0); |
893 | |
|
894 | 0 | dcnt = SVAL(req->vwv+5, 0); |
895 | 0 | doff = SVAL(req->vwv+6, 0); |
896 | 0 | ddisp = SVAL(req->vwv+7, 0); |
897 | |
|
898 | 0 | state->received_param += pcnt; |
899 | 0 | state->received_data += dcnt; |
900 | |
|
901 | 0 | if ((state->received_data > state->total_data) || |
902 | 0 | (state->received_param > state->total_param)) |
903 | 0 | goto bad_param; |
904 | | |
905 | 0 | if (pcnt) { |
906 | 0 | if (smb_buffer_oob(state->total_param, pdisp, pcnt) |
907 | 0 | || smb_buffer_oob(smb_len(req->inbuf), poff, pcnt)) { |
908 | 0 | goto bad_param; |
909 | 0 | } |
910 | 0 | memcpy(state->param+pdisp,smb_base(req->inbuf)+poff,pcnt); |
911 | 0 | } |
912 | | |
913 | 0 | if (dcnt) { |
914 | 0 | if (smb_buffer_oob(state->total_data, ddisp, dcnt) |
915 | 0 | || smb_buffer_oob(smb_len(req->inbuf), doff, dcnt)) { |
916 | 0 | goto bad_param; |
917 | 0 | } |
918 | 0 | memcpy(state->data+ddisp, smb_base(req->inbuf)+doff,dcnt); |
919 | 0 | } |
920 | | |
921 | 0 | if ((state->received_param < state->total_param) || |
922 | 0 | (state->received_data < state->total_data)) { |
923 | 0 | END_PROFILE(SMBtranss); |
924 | 0 | return; |
925 | 0 | } |
926 | | |
927 | 0 | talloc_steal(talloc_tos(), state); |
928 | |
|
929 | 0 | handle_trans(conn, req, state); |
930 | |
|
931 | 0 | DLIST_REMOVE(conn->pending_trans, state); |
932 | 0 | SAFE_FREE(state->data); |
933 | 0 | SAFE_FREE(state->param); |
934 | 0 | TALLOC_FREE(state); |
935 | |
|
936 | 0 | END_PROFILE(SMBtranss); |
937 | 0 | return; |
938 | | |
939 | 0 | bad_param: |
940 | |
|
941 | 0 | DEBUG(0,("reply_transs: invalid trans parameters\n")); |
942 | 0 | DLIST_REMOVE(conn->pending_trans, state); |
943 | 0 | SAFE_FREE(state->data); |
944 | 0 | SAFE_FREE(state->param); |
945 | 0 | TALLOC_FREE(state); |
946 | 0 | reply_nterror(req, NT_STATUS_INVALID_PARAMETER); |
947 | 0 | END_PROFILE(SMBtranss); |
948 | 0 | return; |
949 | 0 | } |