1 | /*
|
---|
2 | Unix SMB/CIFS implementation.
|
---|
3 | raw dcerpc operations
|
---|
4 |
|
---|
5 | Copyright (C) Tim Potter 2003
|
---|
6 | Copyright (C) Andrew Tridgell 2003-2005
|
---|
7 | Copyright (C) Jelmer Vernooij 2004-2005
|
---|
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 | #include "includes.h"
|
---|
24 | #include "../lib/util/dlinklist.h"
|
---|
25 | #include "lib/events/events.h"
|
---|
26 | #include "librpc/rpc/dcerpc.h"
|
---|
27 | #include "librpc/rpc/dcerpc_proto.h"
|
---|
28 | #include "librpc/gen_ndr/ndr_misc.h"
|
---|
29 | #include "librpc/gen_ndr/ndr_dcerpc.h"
|
---|
30 | #include "libcli/composite/composite.h"
|
---|
31 | #include "auth/gensec/gensec.h"
|
---|
32 | #include "param/param.h"
|
---|
33 | #include "lib/util/tevent_ntstatus.h"
|
---|
34 | #include "librpc/rpc/rpc_common.h"
|
---|
35 |
|
---|
36 | enum rpc_request_state {
|
---|
37 | RPC_REQUEST_QUEUED,
|
---|
38 | RPC_REQUEST_PENDING,
|
---|
39 | RPC_REQUEST_DONE
|
---|
40 | };
|
---|
41 |
|
---|
42 | /*
|
---|
43 | handle for an async dcerpc request
|
---|
44 | */
|
---|
45 | struct rpc_request {
|
---|
46 | struct rpc_request *next, *prev;
|
---|
47 | struct dcerpc_pipe *p;
|
---|
48 | NTSTATUS status;
|
---|
49 | uint32_t call_id;
|
---|
50 | enum rpc_request_state state;
|
---|
51 | DATA_BLOB payload;
|
---|
52 | uint32_t flags;
|
---|
53 | uint32_t fault_code;
|
---|
54 |
|
---|
55 | /* this is used to distinguish bind and alter_context requests
|
---|
56 | from normal requests */
|
---|
57 | void (*recv_handler)(struct rpc_request *conn,
|
---|
58 | DATA_BLOB *blob, struct ncacn_packet *pkt);
|
---|
59 |
|
---|
60 | const struct GUID *object;
|
---|
61 | uint16_t opnum;
|
---|
62 | DATA_BLOB request_data;
|
---|
63 | bool ignore_timeout;
|
---|
64 |
|
---|
65 | /* use by the ndr level async recv call */
|
---|
66 | struct {
|
---|
67 | const struct ndr_interface_table *table;
|
---|
68 | uint32_t opnum;
|
---|
69 | void *struct_ptr;
|
---|
70 | TALLOC_CTX *mem_ctx;
|
---|
71 | } ndr;
|
---|
72 |
|
---|
73 | struct {
|
---|
74 | void (*callback)(struct rpc_request *);
|
---|
75 | void *private_data;
|
---|
76 | } async;
|
---|
77 | };
|
---|
78 |
|
---|
79 | _PUBLIC_ NTSTATUS dcerpc_init(struct loadparm_context *lp_ctx)
|
---|
80 | {
|
---|
81 | return gensec_init(lp_ctx);
|
---|
82 | }
|
---|
83 |
|
---|
84 | static void dcerpc_connection_dead(struct dcecli_connection *conn, NTSTATUS status);
|
---|
85 | static void dcerpc_ship_next_request(struct dcecli_connection *c);
|
---|
86 |
|
---|
87 | static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
|
---|
88 | const struct GUID *object,
|
---|
89 | uint16_t opnum,
|
---|
90 | DATA_BLOB *stub_data);
|
---|
91 | static NTSTATUS dcerpc_request_recv(struct rpc_request *req,
|
---|
92 | TALLOC_CTX *mem_ctx,
|
---|
93 | DATA_BLOB *stub_data);
|
---|
94 | static NTSTATUS dcerpc_ndr_validate_in(struct dcecli_connection *c,
|
---|
95 | TALLOC_CTX *mem_ctx,
|
---|
96 | DATA_BLOB blob,
|
---|
97 | size_t struct_size,
|
---|
98 | ndr_push_flags_fn_t ndr_push,
|
---|
99 | ndr_pull_flags_fn_t ndr_pull);
|
---|
100 | static NTSTATUS dcerpc_ndr_validate_out(struct dcecli_connection *c,
|
---|
101 | struct ndr_pull *pull_in,
|
---|
102 | void *struct_ptr,
|
---|
103 | size_t struct_size,
|
---|
104 | ndr_push_flags_fn_t ndr_push,
|
---|
105 | ndr_pull_flags_fn_t ndr_pull,
|
---|
106 | ndr_print_function_t ndr_print);
|
---|
107 |
|
---|
108 | /* destroy a dcerpc connection */
|
---|
109 | static int dcerpc_connection_destructor(struct dcecli_connection *conn)
|
---|
110 | {
|
---|
111 | if (conn->dead) {
|
---|
112 | conn->free_skipped = true;
|
---|
113 | return -1;
|
---|
114 | }
|
---|
115 | dcerpc_connection_dead(conn, NT_STATUS_LOCAL_DISCONNECT);
|
---|
116 | return 0;
|
---|
117 | }
|
---|
118 |
|
---|
119 |
|
---|
120 | /* initialise a dcerpc connection.
|
---|
121 | the event context is optional
|
---|
122 | */
|
---|
123 | static struct dcecli_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx,
|
---|
124 | struct tevent_context *ev)
|
---|
125 | {
|
---|
126 | struct dcecli_connection *c;
|
---|
127 |
|
---|
128 | c = talloc_zero(mem_ctx, struct dcecli_connection);
|
---|
129 | if (!c) {
|
---|
130 | return NULL;
|
---|
131 | }
|
---|
132 |
|
---|
133 | c->event_ctx = ev;
|
---|
134 |
|
---|
135 | if (c->event_ctx == NULL) {
|
---|
136 | talloc_free(c);
|
---|
137 | return NULL;
|
---|
138 | }
|
---|
139 |
|
---|
140 | c->call_id = 1;
|
---|
141 | c->security_state.auth_info = NULL;
|
---|
142 | c->security_state.session_key = dcerpc_generic_session_key;
|
---|
143 | c->security_state.generic_state = NULL;
|
---|
144 | c->binding_string = NULL;
|
---|
145 | c->flags = 0;
|
---|
146 | c->srv_max_xmit_frag = 0;
|
---|
147 | c->srv_max_recv_frag = 0;
|
---|
148 | c->pending = NULL;
|
---|
149 |
|
---|
150 | talloc_set_destructor(c, dcerpc_connection_destructor);
|
---|
151 |
|
---|
152 | return c;
|
---|
153 | }
|
---|
154 |
|
---|
155 | struct dcerpc_bh_state {
|
---|
156 | struct dcerpc_pipe *p;
|
---|
157 | };
|
---|
158 |
|
---|
159 | static bool dcerpc_bh_is_connected(struct dcerpc_binding_handle *h)
|
---|
160 | {
|
---|
161 | struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
|
---|
162 | struct dcerpc_bh_state);
|
---|
163 |
|
---|
164 | if (!hs->p) {
|
---|
165 | return false;
|
---|
166 | }
|
---|
167 |
|
---|
168 | return true;
|
---|
169 | }
|
---|
170 |
|
---|
171 | static uint32_t dcerpc_bh_set_timeout(struct dcerpc_binding_handle *h,
|
---|
172 | uint32_t timeout)
|
---|
173 | {
|
---|
174 | struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
|
---|
175 | struct dcerpc_bh_state);
|
---|
176 | uint32_t old;
|
---|
177 |
|
---|
178 | if (!hs->p) {
|
---|
179 | return DCERPC_REQUEST_TIMEOUT;
|
---|
180 | }
|
---|
181 |
|
---|
182 | old = hs->p->request_timeout;
|
---|
183 | hs->p->request_timeout = timeout;
|
---|
184 |
|
---|
185 | return old;
|
---|
186 | }
|
---|
187 |
|
---|
188 | struct dcerpc_bh_raw_call_state {
|
---|
189 | struct dcerpc_binding_handle *h;
|
---|
190 | DATA_BLOB in_data;
|
---|
191 | DATA_BLOB out_data;
|
---|
192 | uint32_t out_flags;
|
---|
193 | };
|
---|
194 |
|
---|
195 | static void dcerpc_bh_raw_call_done(struct rpc_request *subreq);
|
---|
196 |
|
---|
197 | static struct tevent_req *dcerpc_bh_raw_call_send(TALLOC_CTX *mem_ctx,
|
---|
198 | struct tevent_context *ev,
|
---|
199 | struct dcerpc_binding_handle *h,
|
---|
200 | const struct GUID *object,
|
---|
201 | uint32_t opnum,
|
---|
202 | uint32_t in_flags,
|
---|
203 | const uint8_t *in_data,
|
---|
204 | size_t in_length)
|
---|
205 | {
|
---|
206 | struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
|
---|
207 | struct dcerpc_bh_state);
|
---|
208 | struct tevent_req *req;
|
---|
209 | struct dcerpc_bh_raw_call_state *state;
|
---|
210 | bool ok;
|
---|
211 | struct rpc_request *subreq;
|
---|
212 |
|
---|
213 | req = tevent_req_create(mem_ctx, &state,
|
---|
214 | struct dcerpc_bh_raw_call_state);
|
---|
215 | if (req == NULL) {
|
---|
216 | return NULL;
|
---|
217 | }
|
---|
218 | state->h = h;
|
---|
219 | state->in_data.data = discard_const_p(uint8_t, in_data);
|
---|
220 | state->in_data.length = in_length;
|
---|
221 |
|
---|
222 | ok = dcerpc_bh_is_connected(h);
|
---|
223 | if (!ok) {
|
---|
224 | tevent_req_nterror(req, NT_STATUS_INVALID_CONNECTION);
|
---|
225 | return tevent_req_post(req, ev);
|
---|
226 | }
|
---|
227 |
|
---|
228 | subreq = dcerpc_request_send(hs->p,
|
---|
229 | object,
|
---|
230 | opnum,
|
---|
231 | &state->in_data);
|
---|
232 | if (tevent_req_nomem(subreq, req)) {
|
---|
233 | return tevent_req_post(req, ev);
|
---|
234 | }
|
---|
235 | subreq->async.callback = dcerpc_bh_raw_call_done;
|
---|
236 | subreq->async.private_data = req;
|
---|
237 |
|
---|
238 | return req;
|
---|
239 | }
|
---|
240 |
|
---|
241 | static void dcerpc_bh_raw_call_done(struct rpc_request *subreq)
|
---|
242 | {
|
---|
243 | struct tevent_req *req =
|
---|
244 | talloc_get_type_abort(subreq->async.private_data,
|
---|
245 | struct tevent_req);
|
---|
246 | struct dcerpc_bh_raw_call_state *state =
|
---|
247 | tevent_req_data(req,
|
---|
248 | struct dcerpc_bh_raw_call_state);
|
---|
249 | NTSTATUS status;
|
---|
250 | uint32_t fault_code;
|
---|
251 |
|
---|
252 | state->out_flags = 0;
|
---|
253 | if (subreq->flags & DCERPC_PULL_BIGENDIAN) {
|
---|
254 | state->out_flags |= LIBNDR_FLAG_BIGENDIAN;
|
---|
255 | }
|
---|
256 |
|
---|
257 | fault_code = subreq->fault_code;
|
---|
258 |
|
---|
259 | status = dcerpc_request_recv(subreq, state, &state->out_data);
|
---|
260 | if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
|
---|
261 | status = dcerpc_fault_to_nt_status(fault_code);
|
---|
262 | }
|
---|
263 | if (!NT_STATUS_IS_OK(status)) {
|
---|
264 | tevent_req_nterror(req, status);
|
---|
265 | return;
|
---|
266 | }
|
---|
267 |
|
---|
268 | tevent_req_done(req);
|
---|
269 | }
|
---|
270 |
|
---|
271 | static NTSTATUS dcerpc_bh_raw_call_recv(struct tevent_req *req,
|
---|
272 | TALLOC_CTX *mem_ctx,
|
---|
273 | uint8_t **out_data,
|
---|
274 | size_t *out_length,
|
---|
275 | uint32_t *out_flags)
|
---|
276 | {
|
---|
277 | struct dcerpc_bh_raw_call_state *state =
|
---|
278 | tevent_req_data(req,
|
---|
279 | struct dcerpc_bh_raw_call_state);
|
---|
280 | NTSTATUS status;
|
---|
281 |
|
---|
282 | if (tevent_req_is_nterror(req, &status)) {
|
---|
283 | tevent_req_received(req);
|
---|
284 | return status;
|
---|
285 | }
|
---|
286 |
|
---|
287 | *out_data = talloc_move(mem_ctx, &state->out_data.data);
|
---|
288 | *out_length = state->out_data.length;
|
---|
289 | *out_flags = state->out_flags;
|
---|
290 | tevent_req_received(req);
|
---|
291 | return NT_STATUS_OK;
|
---|
292 | }
|
---|
293 |
|
---|
294 | struct dcerpc_bh_disconnect_state {
|
---|
295 | uint8_t _dummy;
|
---|
296 | };
|
---|
297 |
|
---|
298 | static struct tevent_req *dcerpc_bh_disconnect_send(TALLOC_CTX *mem_ctx,
|
---|
299 | struct tevent_context *ev,
|
---|
300 | struct dcerpc_binding_handle *h)
|
---|
301 | {
|
---|
302 | struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
|
---|
303 | struct dcerpc_bh_state);
|
---|
304 | struct tevent_req *req;
|
---|
305 | struct dcerpc_bh_disconnect_state *state;
|
---|
306 | bool ok;
|
---|
307 |
|
---|
308 | req = tevent_req_create(mem_ctx, &state,
|
---|
309 | struct dcerpc_bh_disconnect_state);
|
---|
310 | if (req == NULL) {
|
---|
311 | return NULL;
|
---|
312 | }
|
---|
313 |
|
---|
314 | ok = dcerpc_bh_is_connected(h);
|
---|
315 | if (!ok) {
|
---|
316 | tevent_req_nterror(req, NT_STATUS_INVALID_CONNECTION);
|
---|
317 | return tevent_req_post(req, ev);
|
---|
318 | }
|
---|
319 |
|
---|
320 | /* TODO: do a real disconnect ... */
|
---|
321 | hs->p = NULL;
|
---|
322 |
|
---|
323 | tevent_req_done(req);
|
---|
324 | return tevent_req_post(req, ev);
|
---|
325 | }
|
---|
326 |
|
---|
327 | static NTSTATUS dcerpc_bh_disconnect_recv(struct tevent_req *req)
|
---|
328 | {
|
---|
329 | NTSTATUS status;
|
---|
330 |
|
---|
331 | if (tevent_req_is_nterror(req, &status)) {
|
---|
332 | tevent_req_received(req);
|
---|
333 | return status;
|
---|
334 | }
|
---|
335 |
|
---|
336 | tevent_req_received(req);
|
---|
337 | return NT_STATUS_OK;
|
---|
338 | }
|
---|
339 |
|
---|
340 | static bool dcerpc_bh_push_bigendian(struct dcerpc_binding_handle *h)
|
---|
341 | {
|
---|
342 | struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
|
---|
343 | struct dcerpc_bh_state);
|
---|
344 |
|
---|
345 | if (hs->p->conn->flags & DCERPC_PUSH_BIGENDIAN) {
|
---|
346 | return true;
|
---|
347 | }
|
---|
348 |
|
---|
349 | return false;
|
---|
350 | }
|
---|
351 |
|
---|
352 | static bool dcerpc_bh_ref_alloc(struct dcerpc_binding_handle *h)
|
---|
353 | {
|
---|
354 | struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
|
---|
355 | struct dcerpc_bh_state);
|
---|
356 |
|
---|
357 | if (hs->p->conn->flags & DCERPC_NDR_REF_ALLOC) {
|
---|
358 | return true;
|
---|
359 | }
|
---|
360 |
|
---|
361 | return false;
|
---|
362 | }
|
---|
363 |
|
---|
364 | static bool dcerpc_bh_use_ndr64(struct dcerpc_binding_handle *h)
|
---|
365 | {
|
---|
366 | struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
|
---|
367 | struct dcerpc_bh_state);
|
---|
368 |
|
---|
369 | if (hs->p->conn->flags & DCERPC_NDR64) {
|
---|
370 | return true;
|
---|
371 | }
|
---|
372 |
|
---|
373 | return false;
|
---|
374 | }
|
---|
375 |
|
---|
376 | static void dcerpc_bh_do_ndr_print(struct dcerpc_binding_handle *h,
|
---|
377 | int ndr_flags,
|
---|
378 | const void *_struct_ptr,
|
---|
379 | const struct ndr_interface_call *call)
|
---|
380 | {
|
---|
381 | struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
|
---|
382 | struct dcerpc_bh_state);
|
---|
383 | void *struct_ptr = discard_const(_struct_ptr);
|
---|
384 |
|
---|
385 | if (ndr_flags & NDR_IN) {
|
---|
386 | if (hs->p->conn->flags & DCERPC_DEBUG_PRINT_IN) {
|
---|
387 | ndr_print_function_debug(call->ndr_print,
|
---|
388 | call->name,
|
---|
389 | ndr_flags,
|
---|
390 | struct_ptr);
|
---|
391 | }
|
---|
392 | }
|
---|
393 | if (ndr_flags & NDR_OUT) {
|
---|
394 | if (hs->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
|
---|
395 | ndr_print_function_debug(call->ndr_print,
|
---|
396 | call->name,
|
---|
397 | ndr_flags,
|
---|
398 | struct_ptr);
|
---|
399 | }
|
---|
400 | }
|
---|
401 | }
|
---|
402 |
|
---|
403 | static void dcerpc_bh_ndr_push_failed(struct dcerpc_binding_handle *h,
|
---|
404 | NTSTATUS error,
|
---|
405 | const void *struct_ptr,
|
---|
406 | const struct ndr_interface_call *call)
|
---|
407 | {
|
---|
408 | DEBUG(2,("Unable to ndr_push structure for %s - %s\n",
|
---|
409 | call->name, nt_errstr(error)));
|
---|
410 | }
|
---|
411 |
|
---|
412 | static void dcerpc_bh_ndr_pull_failed(struct dcerpc_binding_handle *h,
|
---|
413 | NTSTATUS error,
|
---|
414 | const DATA_BLOB *blob,
|
---|
415 | const struct ndr_interface_call *call)
|
---|
416 | {
|
---|
417 | struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
|
---|
418 | struct dcerpc_bh_state);
|
---|
419 | const uint32_t num_examples = 20;
|
---|
420 | uint32_t i;
|
---|
421 |
|
---|
422 | DEBUG(2,("Unable to ndr_pull structure for %s - %s\n",
|
---|
423 | call->name, nt_errstr(error)));
|
---|
424 |
|
---|
425 | if (hs->p->conn->packet_log_dir == NULL) return;
|
---|
426 |
|
---|
427 | for (i=0;i<num_examples;i++) {
|
---|
428 | char *name=NULL;
|
---|
429 | asprintf(&name, "%s/rpclog/%s-out.%d",
|
---|
430 | hs->p->conn->packet_log_dir,
|
---|
431 | call->name, i);
|
---|
432 | if (name == NULL) {
|
---|
433 | return;
|
---|
434 | }
|
---|
435 | if (!file_exist(name)) {
|
---|
436 | if (file_save(name, blob->data, blob->length)) {
|
---|
437 | DEBUG(10,("Logged rpc packet to %s\n", name));
|
---|
438 | }
|
---|
439 | free(name);
|
---|
440 | break;
|
---|
441 | }
|
---|
442 | free(name);
|
---|
443 | }
|
---|
444 | }
|
---|
445 |
|
---|
446 | static NTSTATUS dcerpc_bh_ndr_validate_in(struct dcerpc_binding_handle *h,
|
---|
447 | TALLOC_CTX *mem_ctx,
|
---|
448 | const DATA_BLOB *blob,
|
---|
449 | const struct ndr_interface_call *call)
|
---|
450 | {
|
---|
451 | struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
|
---|
452 | struct dcerpc_bh_state);
|
---|
453 |
|
---|
454 | if (hs->p->conn->flags & DCERPC_DEBUG_VALIDATE_IN) {
|
---|
455 | NTSTATUS status;
|
---|
456 |
|
---|
457 | status = dcerpc_ndr_validate_in(hs->p->conn,
|
---|
458 | mem_ctx,
|
---|
459 | *blob,
|
---|
460 | call->struct_size,
|
---|
461 | call->ndr_push,
|
---|
462 | call->ndr_pull);
|
---|
463 | if (!NT_STATUS_IS_OK(status)) {
|
---|
464 | DEBUG(0,("Validation [in] failed for %s - %s\n",
|
---|
465 | call->name, nt_errstr(status)));
|
---|
466 | return status;
|
---|
467 | }
|
---|
468 | }
|
---|
469 |
|
---|
470 | DEBUG(10,("rpc request data:\n"));
|
---|
471 | dump_data(10, blob->data, blob->length);
|
---|
472 |
|
---|
473 | return NT_STATUS_OK;
|
---|
474 | }
|
---|
475 |
|
---|
476 | static NTSTATUS dcerpc_bh_ndr_validate_out(struct dcerpc_binding_handle *h,
|
---|
477 | struct ndr_pull *pull_in,
|
---|
478 | const void *_struct_ptr,
|
---|
479 | const struct ndr_interface_call *call)
|
---|
480 | {
|
---|
481 | struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
|
---|
482 | struct dcerpc_bh_state);
|
---|
483 | void *struct_ptr = discard_const(_struct_ptr);
|
---|
484 |
|
---|
485 | DEBUG(10,("rpc reply data:\n"));
|
---|
486 | dump_data(10, pull_in->data, pull_in->data_size);
|
---|
487 |
|
---|
488 | if (pull_in->offset != pull_in->data_size) {
|
---|
489 | DEBUG(0,("Warning! ignoring %u unread bytes at ofs:%u (0x%08X) for %s!\n",
|
---|
490 | pull_in->data_size - pull_in->offset,
|
---|
491 | pull_in->offset, pull_in->offset,
|
---|
492 | call->name));
|
---|
493 | /* we used to return NT_STATUS_INFO_LENGTH_MISMATCH here,
|
---|
494 | but it turns out that early versions of NT
|
---|
495 | (specifically NT3.1) add junk onto the end of rpc
|
---|
496 | packets, so if we want to interoperate at all with
|
---|
497 | those versions then we need to ignore this error */
|
---|
498 | }
|
---|
499 |
|
---|
500 | if (hs->p->conn->flags & DCERPC_DEBUG_VALIDATE_OUT) {
|
---|
501 | NTSTATUS status;
|
---|
502 |
|
---|
503 | status = dcerpc_ndr_validate_out(hs->p->conn,
|
---|
504 | pull_in,
|
---|
505 | struct_ptr,
|
---|
506 | call->struct_size,
|
---|
507 | call->ndr_push,
|
---|
508 | call->ndr_pull,
|
---|
509 | call->ndr_print);
|
---|
510 | if (!NT_STATUS_IS_OK(status)) {
|
---|
511 | DEBUG(2,("Validation [out] failed for %s - %s\n",
|
---|
512 | call->name, nt_errstr(status)));
|
---|
513 | return status;
|
---|
514 | }
|
---|
515 | }
|
---|
516 |
|
---|
517 | return NT_STATUS_OK;
|
---|
518 | }
|
---|
519 |
|
---|
520 | static const struct dcerpc_binding_handle_ops dcerpc_bh_ops = {
|
---|
521 | .name = "dcerpc",
|
---|
522 | .is_connected = dcerpc_bh_is_connected,
|
---|
523 | .set_timeout = dcerpc_bh_set_timeout,
|
---|
524 | .raw_call_send = dcerpc_bh_raw_call_send,
|
---|
525 | .raw_call_recv = dcerpc_bh_raw_call_recv,
|
---|
526 | .disconnect_send = dcerpc_bh_disconnect_send,
|
---|
527 | .disconnect_recv = dcerpc_bh_disconnect_recv,
|
---|
528 |
|
---|
529 | .push_bigendian = dcerpc_bh_push_bigendian,
|
---|
530 | .ref_alloc = dcerpc_bh_ref_alloc,
|
---|
531 | .use_ndr64 = dcerpc_bh_use_ndr64,
|
---|
532 | .do_ndr_print = dcerpc_bh_do_ndr_print,
|
---|
533 | .ndr_push_failed = dcerpc_bh_ndr_push_failed,
|
---|
534 | .ndr_pull_failed = dcerpc_bh_ndr_pull_failed,
|
---|
535 | .ndr_validate_in = dcerpc_bh_ndr_validate_in,
|
---|
536 | .ndr_validate_out = dcerpc_bh_ndr_validate_out,
|
---|
537 | };
|
---|
538 |
|
---|
539 | /* initialise a dcerpc pipe. */
|
---|
540 | struct dcerpc_binding_handle *dcerpc_pipe_binding_handle(struct dcerpc_pipe *p)
|
---|
541 | {
|
---|
542 | struct dcerpc_binding_handle *h;
|
---|
543 | struct dcerpc_bh_state *hs;
|
---|
544 |
|
---|
545 | h = dcerpc_binding_handle_create(p,
|
---|
546 | &dcerpc_bh_ops,
|
---|
547 | NULL,
|
---|
548 | NULL, /* TODO */
|
---|
549 | &hs,
|
---|
550 | struct dcerpc_bh_state,
|
---|
551 | __location__);
|
---|
552 | if (h == NULL) {
|
---|
553 | return NULL;
|
---|
554 | }
|
---|
555 | hs->p = p;
|
---|
556 |
|
---|
557 | dcerpc_binding_handle_set_sync_ev(h, p->conn->event_ctx);
|
---|
558 |
|
---|
559 | return h;
|
---|
560 | }
|
---|
561 |
|
---|
562 | /* initialise a dcerpc pipe. */
|
---|
563 | _PUBLIC_ struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev)
|
---|
564 | {
|
---|
565 | struct dcerpc_pipe *p;
|
---|
566 |
|
---|
567 | p = talloc_zero(mem_ctx, struct dcerpc_pipe);
|
---|
568 | if (!p) {
|
---|
569 | return NULL;
|
---|
570 | }
|
---|
571 |
|
---|
572 | p->conn = dcerpc_connection_init(p, ev);
|
---|
573 | if (p->conn == NULL) {
|
---|
574 | talloc_free(p);
|
---|
575 | return NULL;
|
---|
576 | }
|
---|
577 |
|
---|
578 | p->last_fault_code = 0;
|
---|
579 | p->context_id = 0;
|
---|
580 | p->request_timeout = DCERPC_REQUEST_TIMEOUT;
|
---|
581 | p->binding = NULL;
|
---|
582 |
|
---|
583 | ZERO_STRUCT(p->syntax);
|
---|
584 | ZERO_STRUCT(p->transfer_syntax);
|
---|
585 |
|
---|
586 | if (DEBUGLVL(100)) {
|
---|
587 | p->conn->flags |= DCERPC_DEBUG_PRINT_BOTH;
|
---|
588 | }
|
---|
589 |
|
---|
590 | p->binding_handle = dcerpc_pipe_binding_handle(p);
|
---|
591 | if (p->binding_handle == NULL) {
|
---|
592 | talloc_free(p);
|
---|
593 | return NULL;
|
---|
594 | }
|
---|
595 |
|
---|
596 | return p;
|
---|
597 | }
|
---|
598 |
|
---|
599 |
|
---|
600 | /*
|
---|
601 | choose the next call id to use
|
---|
602 | */
|
---|
603 | static uint32_t next_call_id(struct dcecli_connection *c)
|
---|
604 | {
|
---|
605 | c->call_id++;
|
---|
606 | if (c->call_id == 0) {
|
---|
607 | c->call_id++;
|
---|
608 | }
|
---|
609 | return c->call_id;
|
---|
610 | }
|
---|
611 |
|
---|
612 | /**
|
---|
613 | setup for a ndr pull, also setting up any flags from the binding string
|
---|
614 | */
|
---|
615 | static struct ndr_pull *ndr_pull_init_flags(struct dcecli_connection *c,
|
---|
616 | DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
|
---|
617 | {
|
---|
618 | struct ndr_pull *ndr = ndr_pull_init_blob(blob, mem_ctx);
|
---|
619 |
|
---|
620 | if (ndr == NULL) return ndr;
|
---|
621 |
|
---|
622 | if (c->flags & DCERPC_DEBUG_PAD_CHECK) {
|
---|
623 | ndr->flags |= LIBNDR_FLAG_PAD_CHECK;
|
---|
624 | }
|
---|
625 |
|
---|
626 | if (c->flags & DCERPC_NDR_REF_ALLOC) {
|
---|
627 | ndr->flags |= LIBNDR_FLAG_REF_ALLOC;
|
---|
628 | }
|
---|
629 |
|
---|
630 | if (c->flags & DCERPC_NDR64) {
|
---|
631 | ndr->flags |= LIBNDR_FLAG_NDR64;
|
---|
632 | }
|
---|
633 |
|
---|
634 | return ndr;
|
---|
635 | }
|
---|
636 |
|
---|
637 | /*
|
---|
638 | parse a data blob into a ncacn_packet structure. This handles both
|
---|
639 | input and output packets
|
---|
640 | */
|
---|
641 | static NTSTATUS ncacn_pull(struct dcecli_connection *c, DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
|
---|
642 | struct ncacn_packet *pkt)
|
---|
643 | {
|
---|
644 | struct ndr_pull *ndr;
|
---|
645 | enum ndr_err_code ndr_err;
|
---|
646 |
|
---|
647 | ndr = ndr_pull_init_flags(c, blob, mem_ctx);
|
---|
648 | if (!ndr) {
|
---|
649 | return NT_STATUS_NO_MEMORY;
|
---|
650 | }
|
---|
651 |
|
---|
652 | if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
|
---|
653 | ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
|
---|
654 | }
|
---|
655 |
|
---|
656 | ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
|
---|
657 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
658 | return ndr_map_error2ntstatus(ndr_err);
|
---|
659 | }
|
---|
660 |
|
---|
661 | return NT_STATUS_OK;
|
---|
662 | }
|
---|
663 |
|
---|
664 | /*
|
---|
665 | parse the authentication information on a dcerpc response packet
|
---|
666 | */
|
---|
667 | static NTSTATUS ncacn_pull_request_auth(struct dcecli_connection *c, TALLOC_CTX *mem_ctx,
|
---|
668 | DATA_BLOB *raw_packet,
|
---|
669 | struct ncacn_packet *pkt)
|
---|
670 | {
|
---|
671 | NTSTATUS status;
|
---|
672 | struct dcerpc_auth auth;
|
---|
673 | uint32_t auth_length;
|
---|
674 |
|
---|
675 | if (!c->security_state.auth_info ||
|
---|
676 | !c->security_state.generic_state) {
|
---|
677 | return NT_STATUS_OK;
|
---|
678 | }
|
---|
679 |
|
---|
680 | switch (c->security_state.auth_info->auth_level) {
|
---|
681 | case DCERPC_AUTH_LEVEL_PRIVACY:
|
---|
682 | case DCERPC_AUTH_LEVEL_INTEGRITY:
|
---|
683 | break;
|
---|
684 |
|
---|
685 | case DCERPC_AUTH_LEVEL_CONNECT:
|
---|
686 | if (pkt->auth_length != 0) {
|
---|
687 | break;
|
---|
688 | }
|
---|
689 | return NT_STATUS_OK;
|
---|
690 | case DCERPC_AUTH_LEVEL_NONE:
|
---|
691 | if (pkt->auth_length != 0) {
|
---|
692 | return NT_STATUS_INVALID_NETWORK_RESPONSE;
|
---|
693 | }
|
---|
694 | return NT_STATUS_OK;
|
---|
695 |
|
---|
696 | default:
|
---|
697 | return NT_STATUS_INVALID_LEVEL;
|
---|
698 | }
|
---|
699 |
|
---|
700 | status = dcerpc_pull_auth_trailer(pkt, mem_ctx,
|
---|
701 | &pkt->u.response.stub_and_verifier,
|
---|
702 | &auth, &auth_length, false);
|
---|
703 | NT_STATUS_NOT_OK_RETURN(status);
|
---|
704 |
|
---|
705 | pkt->u.response.stub_and_verifier.length -= auth_length;
|
---|
706 |
|
---|
707 | /* check signature or unseal the packet */
|
---|
708 | switch (c->security_state.auth_info->auth_level) {
|
---|
709 | case DCERPC_AUTH_LEVEL_PRIVACY:
|
---|
710 | status = gensec_unseal_packet(c->security_state.generic_state,
|
---|
711 | mem_ctx,
|
---|
712 | raw_packet->data + DCERPC_REQUEST_LENGTH,
|
---|
713 | pkt->u.response.stub_and_verifier.length,
|
---|
714 | raw_packet->data,
|
---|
715 | raw_packet->length - auth.credentials.length,
|
---|
716 | &auth.credentials);
|
---|
717 | memcpy(pkt->u.response.stub_and_verifier.data,
|
---|
718 | raw_packet->data + DCERPC_REQUEST_LENGTH,
|
---|
719 | pkt->u.response.stub_and_verifier.length);
|
---|
720 | break;
|
---|
721 |
|
---|
722 | case DCERPC_AUTH_LEVEL_INTEGRITY:
|
---|
723 | status = gensec_check_packet(c->security_state.generic_state,
|
---|
724 | mem_ctx,
|
---|
725 | pkt->u.response.stub_and_verifier.data,
|
---|
726 | pkt->u.response.stub_and_verifier.length,
|
---|
727 | raw_packet->data,
|
---|
728 | raw_packet->length - auth.credentials.length,
|
---|
729 | &auth.credentials);
|
---|
730 | break;
|
---|
731 |
|
---|
732 | case DCERPC_AUTH_LEVEL_CONNECT:
|
---|
733 | /* for now we ignore possible signatures here */
|
---|
734 | status = NT_STATUS_OK;
|
---|
735 | break;
|
---|
736 |
|
---|
737 | default:
|
---|
738 | status = NT_STATUS_INVALID_LEVEL;
|
---|
739 | break;
|
---|
740 | }
|
---|
741 |
|
---|
742 | /* remove the indicated amount of padding */
|
---|
743 | if (pkt->u.response.stub_and_verifier.length < auth.auth_pad_length) {
|
---|
744 | return NT_STATUS_INFO_LENGTH_MISMATCH;
|
---|
745 | }
|
---|
746 | pkt->u.response.stub_and_verifier.length -= auth.auth_pad_length;
|
---|
747 |
|
---|
748 | return status;
|
---|
749 | }
|
---|
750 |
|
---|
751 |
|
---|
752 | /*
|
---|
753 | push a dcerpc request packet into a blob, possibly signing it.
|
---|
754 | */
|
---|
755 | static NTSTATUS ncacn_push_request_sign(struct dcecli_connection *c,
|
---|
756 | DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
|
---|
757 | size_t sig_size,
|
---|
758 | struct ncacn_packet *pkt)
|
---|
759 | {
|
---|
760 | NTSTATUS status;
|
---|
761 | struct ndr_push *ndr;
|
---|
762 | DATA_BLOB creds2;
|
---|
763 | size_t payload_length;
|
---|
764 | enum ndr_err_code ndr_err;
|
---|
765 | size_t hdr_size = DCERPC_REQUEST_LENGTH;
|
---|
766 |
|
---|
767 | /* non-signed packets are simpler */
|
---|
768 | if (sig_size == 0) {
|
---|
769 | return ncacn_push_auth(blob, mem_ctx, pkt, NULL);
|
---|
770 | }
|
---|
771 |
|
---|
772 | switch (c->security_state.auth_info->auth_level) {
|
---|
773 | case DCERPC_AUTH_LEVEL_PRIVACY:
|
---|
774 | case DCERPC_AUTH_LEVEL_INTEGRITY:
|
---|
775 | break;
|
---|
776 |
|
---|
777 | case DCERPC_AUTH_LEVEL_CONNECT:
|
---|
778 | /* TODO: let the gensec mech decide if it wants to generate a signature */
|
---|
779 | return ncacn_push_auth(blob, mem_ctx, pkt, NULL);
|
---|
780 |
|
---|
781 | case DCERPC_AUTH_LEVEL_NONE:
|
---|
782 | return ncacn_push_auth(blob, mem_ctx, pkt, NULL);
|
---|
783 |
|
---|
784 | default:
|
---|
785 | return NT_STATUS_INVALID_LEVEL;
|
---|
786 | }
|
---|
787 |
|
---|
788 | ndr = ndr_push_init_ctx(mem_ctx);
|
---|
789 | if (!ndr) {
|
---|
790 | return NT_STATUS_NO_MEMORY;
|
---|
791 | }
|
---|
792 |
|
---|
793 | if (c->flags & DCERPC_PUSH_BIGENDIAN) {
|
---|
794 | ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
|
---|
795 | }
|
---|
796 |
|
---|
797 | if (c->flags & DCERPC_NDR64) {
|
---|
798 | ndr->flags |= LIBNDR_FLAG_NDR64;
|
---|
799 | }
|
---|
800 |
|
---|
801 | if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
|
---|
802 | ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
|
---|
803 | hdr_size += 16;
|
---|
804 | }
|
---|
805 |
|
---|
806 | ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
|
---|
807 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
808 | return ndr_map_error2ntstatus(ndr_err);
|
---|
809 | }
|
---|
810 |
|
---|
811 | /* pad to 16 byte multiple in the payload portion of the
|
---|
812 | packet. This matches what w2k3 does. Note that we can't use
|
---|
813 | ndr_push_align() as that is relative to the start of the
|
---|
814 | whole packet, whereas w2k8 wants it relative to the start
|
---|
815 | of the stub */
|
---|
816 | c->security_state.auth_info->auth_pad_length =
|
---|
817 | (16 - (pkt->u.request.stub_and_verifier.length & 15)) & 15;
|
---|
818 | ndr_err = ndr_push_zero(ndr, c->security_state.auth_info->auth_pad_length);
|
---|
819 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
820 | return ndr_map_error2ntstatus(ndr_err);
|
---|
821 | }
|
---|
822 |
|
---|
823 | payload_length = pkt->u.request.stub_and_verifier.length +
|
---|
824 | c->security_state.auth_info->auth_pad_length;
|
---|
825 |
|
---|
826 | /* we start without signature, it will appended later */
|
---|
827 | c->security_state.auth_info->credentials = data_blob(NULL,0);
|
---|
828 |
|
---|
829 | /* add the auth verifier */
|
---|
830 | ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, c->security_state.auth_info);
|
---|
831 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
832 | return ndr_map_error2ntstatus(ndr_err);
|
---|
833 | }
|
---|
834 |
|
---|
835 | /* extract the whole packet as a blob */
|
---|
836 | *blob = ndr_push_blob(ndr);
|
---|
837 |
|
---|
838 | /*
|
---|
839 | * Setup the frag and auth length in the packet buffer.
|
---|
840 | * This is needed if the GENSEC mech does AEAD signing
|
---|
841 | * of the packet headers. The signature itself will be
|
---|
842 | * appended later.
|
---|
843 | */
|
---|
844 | dcerpc_set_frag_length(blob, blob->length + sig_size);
|
---|
845 | dcerpc_set_auth_length(blob, sig_size);
|
---|
846 |
|
---|
847 | /* sign or seal the packet */
|
---|
848 | switch (c->security_state.auth_info->auth_level) {
|
---|
849 | case DCERPC_AUTH_LEVEL_PRIVACY:
|
---|
850 | status = gensec_seal_packet(c->security_state.generic_state,
|
---|
851 | mem_ctx,
|
---|
852 | blob->data + hdr_size,
|
---|
853 | payload_length,
|
---|
854 | blob->data,
|
---|
855 | blob->length,
|
---|
856 | &creds2);
|
---|
857 | if (!NT_STATUS_IS_OK(status)) {
|
---|
858 | return status;
|
---|
859 | }
|
---|
860 | break;
|
---|
861 |
|
---|
862 | case DCERPC_AUTH_LEVEL_INTEGRITY:
|
---|
863 | status = gensec_sign_packet(c->security_state.generic_state,
|
---|
864 | mem_ctx,
|
---|
865 | blob->data + hdr_size,
|
---|
866 | payload_length,
|
---|
867 | blob->data,
|
---|
868 | blob->length,
|
---|
869 | &creds2);
|
---|
870 | if (!NT_STATUS_IS_OK(status)) {
|
---|
871 | return status;
|
---|
872 | }
|
---|
873 | break;
|
---|
874 |
|
---|
875 | default:
|
---|
876 | status = NT_STATUS_INVALID_LEVEL;
|
---|
877 | break;
|
---|
878 | }
|
---|
879 |
|
---|
880 | if (creds2.length != sig_size) {
|
---|
881 | /* this means the sig_size estimate for the signature
|
---|
882 | was incorrect. We have to correct the packet
|
---|
883 | sizes. That means we could go over the max fragment
|
---|
884 | length */
|
---|
885 | DEBUG(3,("ncacn_push_request_sign: creds2.length[%u] != sig_size[%u] pad[%u] stub[%u]\n",
|
---|
886 | (unsigned) creds2.length,
|
---|
887 | (unsigned) sig_size,
|
---|
888 | (unsigned) c->security_state.auth_info->auth_pad_length,
|
---|
889 | (unsigned) pkt->u.request.stub_and_verifier.length));
|
---|
890 | dcerpc_set_frag_length(blob, blob->length + creds2.length);
|
---|
891 | dcerpc_set_auth_length(blob, creds2.length);
|
---|
892 | }
|
---|
893 |
|
---|
894 | if (!data_blob_append(mem_ctx, blob, creds2.data, creds2.length)) {
|
---|
895 | return NT_STATUS_NO_MEMORY;
|
---|
896 | }
|
---|
897 |
|
---|
898 | return NT_STATUS_OK;
|
---|
899 | }
|
---|
900 |
|
---|
901 |
|
---|
902 | /*
|
---|
903 | fill in the fixed values in a dcerpc header
|
---|
904 | */
|
---|
905 | static void init_ncacn_hdr(struct dcecli_connection *c, struct ncacn_packet *pkt)
|
---|
906 | {
|
---|
907 | pkt->rpc_vers = 5;
|
---|
908 | pkt->rpc_vers_minor = 0;
|
---|
909 | if (c->flags & DCERPC_PUSH_BIGENDIAN) {
|
---|
910 | pkt->drep[0] = 0;
|
---|
911 | } else {
|
---|
912 | pkt->drep[0] = DCERPC_DREP_LE;
|
---|
913 | }
|
---|
914 | pkt->drep[1] = 0;
|
---|
915 | pkt->drep[2] = 0;
|
---|
916 | pkt->drep[3] = 0;
|
---|
917 | }
|
---|
918 |
|
---|
919 | /*
|
---|
920 | map a bind nak reason to a NTSTATUS
|
---|
921 | */
|
---|
922 | static NTSTATUS dcerpc_map_reason(uint16_t reason)
|
---|
923 | {
|
---|
924 | switch (reason) {
|
---|
925 | case DCERPC_BIND_REASON_ASYNTAX:
|
---|
926 | return NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX;
|
---|
927 | case DCERPC_BIND_REASON_INVALID_AUTH_TYPE:
|
---|
928 | return NT_STATUS_INVALID_PARAMETER;
|
---|
929 | }
|
---|
930 | return NT_STATUS_UNSUCCESSFUL;
|
---|
931 | }
|
---|
932 |
|
---|
933 | /*
|
---|
934 | a bind or alter context has failed
|
---|
935 | */
|
---|
936 | static void dcerpc_composite_fail(struct rpc_request *req)
|
---|
937 | {
|
---|
938 | struct composite_context *c = talloc_get_type(req->async.private_data,
|
---|
939 | struct composite_context);
|
---|
940 | composite_error(c, req->status);
|
---|
941 | }
|
---|
942 |
|
---|
943 | /*
|
---|
944 | remove requests from the pending or queued queues
|
---|
945 | */
|
---|
946 | static int dcerpc_req_dequeue(struct rpc_request *req)
|
---|
947 | {
|
---|
948 | switch (req->state) {
|
---|
949 | case RPC_REQUEST_QUEUED:
|
---|
950 | DLIST_REMOVE(req->p->conn->request_queue, req);
|
---|
951 | break;
|
---|
952 | case RPC_REQUEST_PENDING:
|
---|
953 | DLIST_REMOVE(req->p->conn->pending, req);
|
---|
954 | break;
|
---|
955 | case RPC_REQUEST_DONE:
|
---|
956 | break;
|
---|
957 | }
|
---|
958 | return 0;
|
---|
959 | }
|
---|
960 |
|
---|
961 |
|
---|
962 | /*
|
---|
963 | mark the dcerpc connection dead. All outstanding requests get an error
|
---|
964 | */
|
---|
965 | static void dcerpc_connection_dead(struct dcecli_connection *conn, NTSTATUS status)
|
---|
966 | {
|
---|
967 | if (conn->dead) return;
|
---|
968 |
|
---|
969 | conn->dead = true;
|
---|
970 |
|
---|
971 | if (conn->transport.shutdown_pipe) {
|
---|
972 | conn->transport.shutdown_pipe(conn, status);
|
---|
973 | }
|
---|
974 |
|
---|
975 | /* all pending requests get the error */
|
---|
976 | while (conn->pending) {
|
---|
977 | struct rpc_request *req = conn->pending;
|
---|
978 | dcerpc_req_dequeue(req);
|
---|
979 | req->state = RPC_REQUEST_DONE;
|
---|
980 | req->status = status;
|
---|
981 | if (req->async.callback) {
|
---|
982 | req->async.callback(req);
|
---|
983 | }
|
---|
984 | }
|
---|
985 |
|
---|
986 | talloc_set_destructor(conn, NULL);
|
---|
987 | if (conn->free_skipped) {
|
---|
988 | talloc_free(conn);
|
---|
989 | }
|
---|
990 | }
|
---|
991 |
|
---|
992 | /*
|
---|
993 | forward declarations of the recv_data handlers for the types of
|
---|
994 | packets we need to handle
|
---|
995 | */
|
---|
996 | static void dcerpc_request_recv_data(struct dcecli_connection *c,
|
---|
997 | DATA_BLOB *raw_packet, struct ncacn_packet *pkt);
|
---|
998 |
|
---|
999 | /*
|
---|
1000 | receive a dcerpc reply from the transport. Here we work out what
|
---|
1001 | type of reply it is (normal request, bind or alter context) and
|
---|
1002 | dispatch to the appropriate handler
|
---|
1003 | */
|
---|
1004 | static void dcerpc_recv_data(struct dcecli_connection *conn, DATA_BLOB *blob, NTSTATUS status)
|
---|
1005 | {
|
---|
1006 | struct ncacn_packet pkt;
|
---|
1007 |
|
---|
1008 | if (NT_STATUS_IS_OK(status) && blob->length == 0) {
|
---|
1009 | status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
|
---|
1010 | }
|
---|
1011 |
|
---|
1012 | /* the transport may be telling us of a severe error, such as
|
---|
1013 | a dropped socket */
|
---|
1014 | if (!NT_STATUS_IS_OK(status)) {
|
---|
1015 | data_blob_free(blob);
|
---|
1016 | dcerpc_connection_dead(conn, status);
|
---|
1017 | return;
|
---|
1018 | }
|
---|
1019 |
|
---|
1020 | /* parse the basic packet to work out what type of response this is */
|
---|
1021 | status = ncacn_pull(conn, blob, blob->data, &pkt);
|
---|
1022 | if (!NT_STATUS_IS_OK(status)) {
|
---|
1023 | data_blob_free(blob);
|
---|
1024 | dcerpc_connection_dead(conn, status);
|
---|
1025 | }
|
---|
1026 |
|
---|
1027 | dcerpc_request_recv_data(conn, blob, &pkt);
|
---|
1028 | }
|
---|
1029 |
|
---|
1030 | /*
|
---|
1031 | Receive a bind reply from the transport
|
---|
1032 | */
|
---|
1033 | static void dcerpc_bind_recv_handler(struct rpc_request *req,
|
---|
1034 | DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
|
---|
1035 | {
|
---|
1036 | struct composite_context *c;
|
---|
1037 | struct dcecli_connection *conn;
|
---|
1038 |
|
---|
1039 | c = talloc_get_type(req->async.private_data, struct composite_context);
|
---|
1040 |
|
---|
1041 | if (pkt->ptype == DCERPC_PKT_BIND_NAK) {
|
---|
1042 | DEBUG(2,("dcerpc: bind_nak reason %d\n",
|
---|
1043 | pkt->u.bind_nak.reject_reason));
|
---|
1044 | composite_error(c, dcerpc_map_reason(pkt->u.bind_nak.
|
---|
1045 | reject_reason));
|
---|
1046 | return;
|
---|
1047 | }
|
---|
1048 |
|
---|
1049 | if ((pkt->ptype != DCERPC_PKT_BIND_ACK) ||
|
---|
1050 | (pkt->u.bind_ack.num_results == 0) ||
|
---|
1051 | (pkt->u.bind_ack.ctx_list[0].result != 0)) {
|
---|
1052 | req->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
|
---|
1053 | composite_error(c, NT_STATUS_NET_WRITE_FAULT);
|
---|
1054 | return;
|
---|
1055 | }
|
---|
1056 |
|
---|
1057 | conn = req->p->conn;
|
---|
1058 |
|
---|
1059 | conn->srv_max_xmit_frag = pkt->u.bind_ack.max_xmit_frag;
|
---|
1060 | conn->srv_max_recv_frag = pkt->u.bind_ack.max_recv_frag;
|
---|
1061 |
|
---|
1062 | if ((req->p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) &&
|
---|
1063 | (pkt->pfc_flags & DCERPC_PFC_FLAG_CONC_MPX)) {
|
---|
1064 | conn->flags |= DCERPC_CONCURRENT_MULTIPLEX;
|
---|
1065 | }
|
---|
1066 |
|
---|
1067 | if ((req->p->binding->flags & DCERPC_HEADER_SIGNING) &&
|
---|
1068 | (pkt->pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN)) {
|
---|
1069 | conn->flags |= DCERPC_HEADER_SIGNING;
|
---|
1070 | }
|
---|
1071 |
|
---|
1072 | /* the bind_ack might contain a reply set of credentials */
|
---|
1073 | if (conn->security_state.auth_info && pkt->u.bind_ack.auth_info.length) {
|
---|
1074 | NTSTATUS status;
|
---|
1075 | uint32_t auth_length;
|
---|
1076 | status = dcerpc_pull_auth_trailer(pkt, conn, &pkt->u.bind_ack.auth_info,
|
---|
1077 | conn->security_state.auth_info, &auth_length, true);
|
---|
1078 | if (!NT_STATUS_IS_OK(status)) {
|
---|
1079 | composite_error(c, status);
|
---|
1080 | return;
|
---|
1081 | }
|
---|
1082 | }
|
---|
1083 |
|
---|
1084 | req->p->assoc_group_id = pkt->u.bind_ack.assoc_group_id;
|
---|
1085 |
|
---|
1086 | composite_done(c);
|
---|
1087 | }
|
---|
1088 |
|
---|
1089 | /*
|
---|
1090 | handle timeouts of individual dcerpc requests
|
---|
1091 | */
|
---|
1092 | static void dcerpc_timeout_handler(struct tevent_context *ev, struct tevent_timer *te,
|
---|
1093 | struct timeval t, void *private_data)
|
---|
1094 | {
|
---|
1095 | struct rpc_request *req = talloc_get_type(private_data, struct rpc_request);
|
---|
1096 |
|
---|
1097 | if (req->ignore_timeout) {
|
---|
1098 | dcerpc_req_dequeue(req);
|
---|
1099 | req->state = RPC_REQUEST_DONE;
|
---|
1100 | req->status = NT_STATUS_IO_TIMEOUT;
|
---|
1101 | if (req->async.callback) {
|
---|
1102 | req->async.callback(req);
|
---|
1103 | }
|
---|
1104 | return;
|
---|
1105 | }
|
---|
1106 |
|
---|
1107 | dcerpc_connection_dead(req->p->conn, NT_STATUS_IO_TIMEOUT);
|
---|
1108 | }
|
---|
1109 |
|
---|
1110 | /*
|
---|
1111 | send a async dcerpc bind request
|
---|
1112 | */
|
---|
1113 | struct composite_context *dcerpc_bind_send(struct dcerpc_pipe *p,
|
---|
1114 | TALLOC_CTX *mem_ctx,
|
---|
1115 | const struct ndr_syntax_id *syntax,
|
---|
1116 | const struct ndr_syntax_id *transfer_syntax)
|
---|
1117 | {
|
---|
1118 | struct composite_context *c;
|
---|
1119 | struct ncacn_packet pkt;
|
---|
1120 | DATA_BLOB blob;
|
---|
1121 | struct rpc_request *req;
|
---|
1122 |
|
---|
1123 | c = composite_create(mem_ctx,p->conn->event_ctx);
|
---|
1124 | if (c == NULL) return NULL;
|
---|
1125 |
|
---|
1126 | c->private_data = p;
|
---|
1127 |
|
---|
1128 | p->syntax = *syntax;
|
---|
1129 | p->transfer_syntax = *transfer_syntax;
|
---|
1130 |
|
---|
1131 | init_ncacn_hdr(p->conn, &pkt);
|
---|
1132 |
|
---|
1133 | pkt.ptype = DCERPC_PKT_BIND;
|
---|
1134 | pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
|
---|
1135 | pkt.call_id = p->conn->call_id;
|
---|
1136 | pkt.auth_length = 0;
|
---|
1137 |
|
---|
1138 | if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
|
---|
1139 | pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
|
---|
1140 | }
|
---|
1141 |
|
---|
1142 | if (p->binding->flags & DCERPC_HEADER_SIGNING) {
|
---|
1143 | pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
|
---|
1144 | }
|
---|
1145 |
|
---|
1146 | pkt.u.bind.max_xmit_frag = 5840;
|
---|
1147 | pkt.u.bind.max_recv_frag = 5840;
|
---|
1148 | pkt.u.bind.assoc_group_id = p->binding->assoc_group_id;
|
---|
1149 | pkt.u.bind.num_contexts = 1;
|
---|
1150 | pkt.u.bind.ctx_list = talloc_array(mem_ctx, struct dcerpc_ctx_list, 1);
|
---|
1151 | if (composite_nomem(pkt.u.bind.ctx_list, c)) return c;
|
---|
1152 | pkt.u.bind.ctx_list[0].context_id = p->context_id;
|
---|
1153 | pkt.u.bind.ctx_list[0].num_transfer_syntaxes = 1;
|
---|
1154 | pkt.u.bind.ctx_list[0].abstract_syntax = p->syntax;
|
---|
1155 | pkt.u.bind.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
|
---|
1156 | pkt.u.bind.auth_info = data_blob(NULL, 0);
|
---|
1157 |
|
---|
1158 | /* construct the NDR form of the packet */
|
---|
1159 | c->status = ncacn_push_auth(&blob, c, &pkt,
|
---|
1160 | p->conn->security_state.auth_info);
|
---|
1161 | if (!composite_is_ok(c)) return c;
|
---|
1162 |
|
---|
1163 | p->conn->transport.recv_data = dcerpc_recv_data;
|
---|
1164 |
|
---|
1165 | /*
|
---|
1166 | * we allocate a dcerpc_request so we can be in the same
|
---|
1167 | * request queue as normal requests
|
---|
1168 | */
|
---|
1169 | req = talloc_zero(c, struct rpc_request);
|
---|
1170 | if (composite_nomem(req, c)) return c;
|
---|
1171 |
|
---|
1172 | req->state = RPC_REQUEST_PENDING;
|
---|
1173 | req->call_id = pkt.call_id;
|
---|
1174 | req->async.private_data = c;
|
---|
1175 | req->async.callback = dcerpc_composite_fail;
|
---|
1176 | req->p = p;
|
---|
1177 | req->recv_handler = dcerpc_bind_recv_handler;
|
---|
1178 | DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
|
---|
1179 | talloc_set_destructor(req, dcerpc_req_dequeue);
|
---|
1180 |
|
---|
1181 | c->status = p->conn->transport.send_request(p->conn, &blob,
|
---|
1182 | true);
|
---|
1183 | if (!composite_is_ok(c)) return c;
|
---|
1184 |
|
---|
1185 | event_add_timed(c->event_ctx, req,
|
---|
1186 | timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
|
---|
1187 | dcerpc_timeout_handler, req);
|
---|
1188 |
|
---|
1189 | return c;
|
---|
1190 | }
|
---|
1191 |
|
---|
1192 | /*
|
---|
1193 | recv side of async dcerpc bind request
|
---|
1194 | */
|
---|
1195 | NTSTATUS dcerpc_bind_recv(struct composite_context *ctx)
|
---|
1196 | {
|
---|
1197 | NTSTATUS result = composite_wait(ctx);
|
---|
1198 | talloc_free(ctx);
|
---|
1199 | return result;
|
---|
1200 | }
|
---|
1201 |
|
---|
1202 | /*
|
---|
1203 | perform a continued bind (and auth3)
|
---|
1204 | */
|
---|
1205 | NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p,
|
---|
1206 | TALLOC_CTX *mem_ctx)
|
---|
1207 | {
|
---|
1208 | struct ncacn_packet pkt;
|
---|
1209 | NTSTATUS status;
|
---|
1210 | DATA_BLOB blob;
|
---|
1211 |
|
---|
1212 | init_ncacn_hdr(p->conn, &pkt);
|
---|
1213 |
|
---|
1214 | pkt.ptype = DCERPC_PKT_AUTH3;
|
---|
1215 | pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
|
---|
1216 | pkt.call_id = next_call_id(p->conn);
|
---|
1217 | pkt.auth_length = 0;
|
---|
1218 | pkt.u.auth3.auth_info = data_blob(NULL, 0);
|
---|
1219 |
|
---|
1220 | if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
|
---|
1221 | pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
|
---|
1222 | }
|
---|
1223 |
|
---|
1224 | if (p->binding->flags & DCERPC_HEADER_SIGNING) {
|
---|
1225 | pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
|
---|
1226 | }
|
---|
1227 |
|
---|
1228 | /* construct the NDR form of the packet */
|
---|
1229 | status = ncacn_push_auth(&blob, mem_ctx,
|
---|
1230 | &pkt,
|
---|
1231 | p->conn->security_state.auth_info);
|
---|
1232 | if (!NT_STATUS_IS_OK(status)) {
|
---|
1233 | return status;
|
---|
1234 | }
|
---|
1235 |
|
---|
1236 | /* send it on its way */
|
---|
1237 | status = p->conn->transport.send_request(p->conn, &blob, false);
|
---|
1238 | if (!NT_STATUS_IS_OK(status)) {
|
---|
1239 | return status;
|
---|
1240 | }
|
---|
1241 |
|
---|
1242 | return NT_STATUS_OK;
|
---|
1243 | }
|
---|
1244 |
|
---|
1245 |
|
---|
1246 | /*
|
---|
1247 | process a fragment received from the transport layer during a
|
---|
1248 | request
|
---|
1249 |
|
---|
1250 | This function frees the data
|
---|
1251 | */
|
---|
1252 | static void dcerpc_request_recv_data(struct dcecli_connection *c,
|
---|
1253 | DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
|
---|
1254 | {
|
---|
1255 | struct rpc_request *req;
|
---|
1256 | unsigned int length;
|
---|
1257 | NTSTATUS status = NT_STATUS_OK;
|
---|
1258 |
|
---|
1259 | /*
|
---|
1260 | if this is an authenticated connection then parse and check
|
---|
1261 | the auth info. We have to do this before finding the
|
---|
1262 | matching packet, as the request structure might have been
|
---|
1263 | removed due to a timeout, but if it has been we still need
|
---|
1264 | to run the auth routines so that we don't get the sign/seal
|
---|
1265 | info out of step with the server
|
---|
1266 | */
|
---|
1267 | if (c->security_state.auth_info && c->security_state.generic_state &&
|
---|
1268 | pkt->ptype == DCERPC_PKT_RESPONSE) {
|
---|
1269 | status = ncacn_pull_request_auth(c, raw_packet->data, raw_packet, pkt);
|
---|
1270 | }
|
---|
1271 |
|
---|
1272 | /* find the matching request */
|
---|
1273 | for (req=c->pending;req;req=req->next) {
|
---|
1274 | if (pkt->call_id == req->call_id) break;
|
---|
1275 | }
|
---|
1276 |
|
---|
1277 | #if 0
|
---|
1278 | /* useful for testing certain vendors RPC servers */
|
---|
1279 | if (req == NULL && c->pending && pkt->call_id == 0) {
|
---|
1280 | DEBUG(0,("HACK FOR INCORRECT CALL ID\n"));
|
---|
1281 | req = c->pending;
|
---|
1282 | }
|
---|
1283 | #endif
|
---|
1284 |
|
---|
1285 | if (req == NULL) {
|
---|
1286 | DEBUG(2,("dcerpc_request: unmatched call_id %u in response packet\n", pkt->call_id));
|
---|
1287 | data_blob_free(raw_packet);
|
---|
1288 | return;
|
---|
1289 | }
|
---|
1290 |
|
---|
1291 | talloc_steal(req, raw_packet->data);
|
---|
1292 |
|
---|
1293 | if (req->recv_handler != NULL) {
|
---|
1294 | dcerpc_req_dequeue(req);
|
---|
1295 | req->state = RPC_REQUEST_DONE;
|
---|
1296 | req->recv_handler(req, raw_packet, pkt);
|
---|
1297 | return;
|
---|
1298 | }
|
---|
1299 |
|
---|
1300 | if (pkt->ptype == DCERPC_PKT_FAULT) {
|
---|
1301 | DEBUG(5,("rpc fault: %s\n", dcerpc_errstr(c, pkt->u.fault.status)));
|
---|
1302 | req->fault_code = pkt->u.fault.status;
|
---|
1303 | req->status = NT_STATUS_NET_WRITE_FAULT;
|
---|
1304 | goto req_done;
|
---|
1305 | }
|
---|
1306 |
|
---|
1307 | if (pkt->ptype != DCERPC_PKT_RESPONSE) {
|
---|
1308 | DEBUG(2,("Unexpected packet type %d in dcerpc response\n",
|
---|
1309 | (int)pkt->ptype));
|
---|
1310 | req->fault_code = DCERPC_FAULT_OTHER;
|
---|
1311 | req->status = NT_STATUS_NET_WRITE_FAULT;
|
---|
1312 | goto req_done;
|
---|
1313 | }
|
---|
1314 |
|
---|
1315 | /* now check the status from the auth routines, and if it failed then fail
|
---|
1316 | this request accordingly */
|
---|
1317 | if (!NT_STATUS_IS_OK(status)) {
|
---|
1318 | req->status = status;
|
---|
1319 | goto req_done;
|
---|
1320 | }
|
---|
1321 |
|
---|
1322 | length = pkt->u.response.stub_and_verifier.length;
|
---|
1323 |
|
---|
1324 | if (length > 0) {
|
---|
1325 | req->payload.data = talloc_realloc(req,
|
---|
1326 | req->payload.data,
|
---|
1327 | uint8_t,
|
---|
1328 | req->payload.length + length);
|
---|
1329 | if (!req->payload.data) {
|
---|
1330 | req->status = NT_STATUS_NO_MEMORY;
|
---|
1331 | goto req_done;
|
---|
1332 | }
|
---|
1333 | memcpy(req->payload.data+req->payload.length,
|
---|
1334 | pkt->u.response.stub_and_verifier.data, length);
|
---|
1335 | req->payload.length += length;
|
---|
1336 | }
|
---|
1337 |
|
---|
1338 | if (!(pkt->pfc_flags & DCERPC_PFC_FLAG_LAST)) {
|
---|
1339 | c->transport.send_read(c);
|
---|
1340 | return;
|
---|
1341 | }
|
---|
1342 |
|
---|
1343 | if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
|
---|
1344 | req->flags |= DCERPC_PULL_BIGENDIAN;
|
---|
1345 | } else {
|
---|
1346 | req->flags &= ~DCERPC_PULL_BIGENDIAN;
|
---|
1347 | }
|
---|
1348 |
|
---|
1349 |
|
---|
1350 | req_done:
|
---|
1351 | /* we've got the full payload */
|
---|
1352 | req->state = RPC_REQUEST_DONE;
|
---|
1353 | DLIST_REMOVE(c->pending, req);
|
---|
1354 |
|
---|
1355 | if (c->request_queue != NULL) {
|
---|
1356 | /* We have to look at shipping further requests before calling
|
---|
1357 | * the async function, that one might close the pipe */
|
---|
1358 | dcerpc_ship_next_request(c);
|
---|
1359 | }
|
---|
1360 |
|
---|
1361 | if (req->async.callback) {
|
---|
1362 | req->async.callback(req);
|
---|
1363 | }
|
---|
1364 | }
|
---|
1365 |
|
---|
1366 | /*
|
---|
1367 | perform the send side of a async dcerpc request
|
---|
1368 | */
|
---|
1369 | static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
|
---|
1370 | const struct GUID *object,
|
---|
1371 | uint16_t opnum,
|
---|
1372 | DATA_BLOB *stub_data)
|
---|
1373 | {
|
---|
1374 | struct rpc_request *req;
|
---|
1375 |
|
---|
1376 | p->conn->transport.recv_data = dcerpc_recv_data;
|
---|
1377 |
|
---|
1378 | req = talloc(p, struct rpc_request);
|
---|
1379 | if (req == NULL) {
|
---|
1380 | return NULL;
|
---|
1381 | }
|
---|
1382 |
|
---|
1383 | req->p = p;
|
---|
1384 | req->call_id = next_call_id(p->conn);
|
---|
1385 | req->status = NT_STATUS_OK;
|
---|
1386 | req->state = RPC_REQUEST_QUEUED;
|
---|
1387 | req->payload = data_blob(NULL, 0);
|
---|
1388 | req->flags = 0;
|
---|
1389 | req->fault_code = 0;
|
---|
1390 | req->ignore_timeout = false;
|
---|
1391 | req->async.callback = NULL;
|
---|
1392 | req->async.private_data = NULL;
|
---|
1393 | req->recv_handler = NULL;
|
---|
1394 |
|
---|
1395 | if (object != NULL) {
|
---|
1396 | req->object = (struct GUID *)talloc_memdup(req, (const void *)object, sizeof(*object));
|
---|
1397 | if (req->object == NULL) {
|
---|
1398 | talloc_free(req);
|
---|
1399 | return NULL;
|
---|
1400 | }
|
---|
1401 | } else {
|
---|
1402 | req->object = NULL;
|
---|
1403 | }
|
---|
1404 |
|
---|
1405 | req->opnum = opnum;
|
---|
1406 | req->request_data.length = stub_data->length;
|
---|
1407 | req->request_data.data = talloc_reference(req, stub_data->data);
|
---|
1408 | if (req->request_data.length && req->request_data.data == NULL) {
|
---|
1409 | return NULL;
|
---|
1410 | }
|
---|
1411 |
|
---|
1412 | DLIST_ADD_END(p->conn->request_queue, req, struct rpc_request *);
|
---|
1413 | talloc_set_destructor(req, dcerpc_req_dequeue);
|
---|
1414 |
|
---|
1415 | dcerpc_ship_next_request(p->conn);
|
---|
1416 |
|
---|
1417 | if (p->request_timeout) {
|
---|
1418 | event_add_timed(dcerpc_event_context(p), req,
|
---|
1419 | timeval_current_ofs(p->request_timeout, 0),
|
---|
1420 | dcerpc_timeout_handler, req);
|
---|
1421 | }
|
---|
1422 |
|
---|
1423 | return req;
|
---|
1424 | }
|
---|
1425 |
|
---|
1426 | /*
|
---|
1427 | Send a request using the transport
|
---|
1428 | */
|
---|
1429 |
|
---|
1430 | static void dcerpc_ship_next_request(struct dcecli_connection *c)
|
---|
1431 | {
|
---|
1432 | struct rpc_request *req;
|
---|
1433 | struct dcerpc_pipe *p;
|
---|
1434 | DATA_BLOB *stub_data;
|
---|
1435 | struct ncacn_packet pkt;
|
---|
1436 | DATA_BLOB blob;
|
---|
1437 | uint32_t remaining, chunk_size;
|
---|
1438 | bool first_packet = true;
|
---|
1439 | size_t sig_size = 0;
|
---|
1440 | bool need_async = false;
|
---|
1441 |
|
---|
1442 | req = c->request_queue;
|
---|
1443 | if (req == NULL) {
|
---|
1444 | return;
|
---|
1445 | }
|
---|
1446 |
|
---|
1447 | p = req->p;
|
---|
1448 | stub_data = &req->request_data;
|
---|
1449 |
|
---|
1450 | if (c->pending) {
|
---|
1451 | need_async = true;
|
---|
1452 | }
|
---|
1453 |
|
---|
1454 | DLIST_REMOVE(c->request_queue, req);
|
---|
1455 | DLIST_ADD(c->pending, req);
|
---|
1456 | req->state = RPC_REQUEST_PENDING;
|
---|
1457 |
|
---|
1458 | init_ncacn_hdr(p->conn, &pkt);
|
---|
1459 |
|
---|
1460 | remaining = stub_data->length;
|
---|
1461 |
|
---|
1462 | /* we can write a full max_recv_frag size, minus the dcerpc
|
---|
1463 | request header size */
|
---|
1464 | chunk_size = p->conn->srv_max_recv_frag;
|
---|
1465 | chunk_size -= DCERPC_REQUEST_LENGTH;
|
---|
1466 | if (c->security_state.auth_info &&
|
---|
1467 | c->security_state.generic_state) {
|
---|
1468 | sig_size = gensec_sig_size(c->security_state.generic_state,
|
---|
1469 | p->conn->srv_max_recv_frag);
|
---|
1470 | if (sig_size) {
|
---|
1471 | chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
|
---|
1472 | chunk_size -= sig_size;
|
---|
1473 | }
|
---|
1474 | }
|
---|
1475 | chunk_size -= (chunk_size % 16);
|
---|
1476 |
|
---|
1477 | pkt.ptype = DCERPC_PKT_REQUEST;
|
---|
1478 | pkt.call_id = req->call_id;
|
---|
1479 | pkt.auth_length = 0;
|
---|
1480 | pkt.pfc_flags = 0;
|
---|
1481 | pkt.u.request.alloc_hint = remaining;
|
---|
1482 | pkt.u.request.context_id = p->context_id;
|
---|
1483 | pkt.u.request.opnum = req->opnum;
|
---|
1484 |
|
---|
1485 | if (req->object) {
|
---|
1486 | pkt.u.request.object.object = *req->object;
|
---|
1487 | pkt.pfc_flags |= DCERPC_PFC_FLAG_OBJECT_UUID;
|
---|
1488 | chunk_size -= ndr_size_GUID(req->object,0);
|
---|
1489 | }
|
---|
1490 |
|
---|
1491 | /* we send a series of pdus without waiting for a reply */
|
---|
1492 | while (remaining > 0 || first_packet) {
|
---|
1493 | uint32_t chunk = MIN(chunk_size, remaining);
|
---|
1494 | bool last_frag = false;
|
---|
1495 | bool do_trans = false;
|
---|
1496 |
|
---|
1497 | first_packet = false;
|
---|
1498 | pkt.pfc_flags &= ~(DCERPC_PFC_FLAG_FIRST |DCERPC_PFC_FLAG_LAST);
|
---|
1499 |
|
---|
1500 | if (remaining == stub_data->length) {
|
---|
1501 | pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
|
---|
1502 | }
|
---|
1503 | if (chunk == remaining) {
|
---|
1504 | pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
|
---|
1505 | last_frag = true;
|
---|
1506 | }
|
---|
1507 |
|
---|
1508 | pkt.u.request.stub_and_verifier.data = stub_data->data +
|
---|
1509 | (stub_data->length - remaining);
|
---|
1510 | pkt.u.request.stub_and_verifier.length = chunk;
|
---|
1511 |
|
---|
1512 | req->status = ncacn_push_request_sign(p->conn, &blob, req, sig_size, &pkt);
|
---|
1513 | if (!NT_STATUS_IS_OK(req->status)) {
|
---|
1514 | req->state = RPC_REQUEST_DONE;
|
---|
1515 | DLIST_REMOVE(p->conn->pending, req);
|
---|
1516 | return;
|
---|
1517 | }
|
---|
1518 |
|
---|
1519 | if (last_frag && !need_async) {
|
---|
1520 | do_trans = true;
|
---|
1521 | }
|
---|
1522 |
|
---|
1523 | req->status = p->conn->transport.send_request(p->conn, &blob, do_trans);
|
---|
1524 | if (!NT_STATUS_IS_OK(req->status)) {
|
---|
1525 | req->state = RPC_REQUEST_DONE;
|
---|
1526 | DLIST_REMOVE(p->conn->pending, req);
|
---|
1527 | return;
|
---|
1528 | }
|
---|
1529 |
|
---|
1530 | if (last_frag && !do_trans) {
|
---|
1531 | req->status = p->conn->transport.send_read(p->conn);
|
---|
1532 | if (!NT_STATUS_IS_OK(req->status)) {
|
---|
1533 | req->state = RPC_REQUEST_DONE;
|
---|
1534 | DLIST_REMOVE(p->conn->pending, req);
|
---|
1535 | return;
|
---|
1536 | }
|
---|
1537 | }
|
---|
1538 |
|
---|
1539 | remaining -= chunk;
|
---|
1540 | }
|
---|
1541 | }
|
---|
1542 |
|
---|
1543 | /*
|
---|
1544 | return the event context for a dcerpc pipe
|
---|
1545 | used by callers who wish to operate asynchronously
|
---|
1546 | */
|
---|
1547 | _PUBLIC_ struct tevent_context *dcerpc_event_context(struct dcerpc_pipe *p)
|
---|
1548 | {
|
---|
1549 | return p->conn->event_ctx;
|
---|
1550 | }
|
---|
1551 |
|
---|
1552 |
|
---|
1553 |
|
---|
1554 | /*
|
---|
1555 | perform the receive side of a async dcerpc request
|
---|
1556 | */
|
---|
1557 | static NTSTATUS dcerpc_request_recv(struct rpc_request *req,
|
---|
1558 | TALLOC_CTX *mem_ctx,
|
---|
1559 | DATA_BLOB *stub_data)
|
---|
1560 | {
|
---|
1561 | NTSTATUS status;
|
---|
1562 |
|
---|
1563 | while (req->state != RPC_REQUEST_DONE) {
|
---|
1564 | struct tevent_context *ctx = dcerpc_event_context(req->p);
|
---|
1565 | if (event_loop_once(ctx) != 0) {
|
---|
1566 | return NT_STATUS_CONNECTION_DISCONNECTED;
|
---|
1567 | }
|
---|
1568 | }
|
---|
1569 | *stub_data = req->payload;
|
---|
1570 | status = req->status;
|
---|
1571 | if (stub_data->data) {
|
---|
1572 | stub_data->data = talloc_steal(mem_ctx, stub_data->data);
|
---|
1573 | }
|
---|
1574 | if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
|
---|
1575 | req->p->last_fault_code = req->fault_code;
|
---|
1576 | }
|
---|
1577 | talloc_unlink(talloc_parent(req), req);
|
---|
1578 | return status;
|
---|
1579 | }
|
---|
1580 |
|
---|
1581 | /*
|
---|
1582 | this is a paranoid NDR validator. For every packet we push onto the wire
|
---|
1583 | we pull it back again, then push it again. Then we compare the raw NDR data
|
---|
1584 | for that to the NDR we initially generated. If they don't match then we know
|
---|
1585 | we must have a bug in either the pull or push side of our code
|
---|
1586 | */
|
---|
1587 | static NTSTATUS dcerpc_ndr_validate_in(struct dcecli_connection *c,
|
---|
1588 | TALLOC_CTX *mem_ctx,
|
---|
1589 | DATA_BLOB blob,
|
---|
1590 | size_t struct_size,
|
---|
1591 | ndr_push_flags_fn_t ndr_push,
|
---|
1592 | ndr_pull_flags_fn_t ndr_pull)
|
---|
1593 | {
|
---|
1594 | void *st;
|
---|
1595 | struct ndr_pull *pull;
|
---|
1596 | struct ndr_push *push;
|
---|
1597 | DATA_BLOB blob2;
|
---|
1598 | enum ndr_err_code ndr_err;
|
---|
1599 |
|
---|
1600 | st = talloc_size(mem_ctx, struct_size);
|
---|
1601 | if (!st) {
|
---|
1602 | return NT_STATUS_NO_MEMORY;
|
---|
1603 | }
|
---|
1604 |
|
---|
1605 | pull = ndr_pull_init_flags(c, &blob, mem_ctx);
|
---|
1606 | if (!pull) {
|
---|
1607 | return NT_STATUS_NO_MEMORY;
|
---|
1608 | }
|
---|
1609 | pull->flags |= LIBNDR_FLAG_REF_ALLOC;
|
---|
1610 |
|
---|
1611 | if (c->flags & DCERPC_PUSH_BIGENDIAN) {
|
---|
1612 | pull->flags |= LIBNDR_FLAG_BIGENDIAN;
|
---|
1613 | }
|
---|
1614 |
|
---|
1615 | if (c->flags & DCERPC_NDR64) {
|
---|
1616 | pull->flags |= LIBNDR_FLAG_NDR64;
|
---|
1617 | }
|
---|
1618 |
|
---|
1619 | ndr_err = ndr_pull(pull, NDR_IN, st);
|
---|
1620 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
1621 | NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
|
---|
1622 | ndr_err = ndr_pull_error(pull, NDR_ERR_VALIDATE,
|
---|
1623 | "failed input validation pull - %s",
|
---|
1624 | nt_errstr(status));
|
---|
1625 | return ndr_map_error2ntstatus(ndr_err);
|
---|
1626 | }
|
---|
1627 |
|
---|
1628 | push = ndr_push_init_ctx(mem_ctx);
|
---|
1629 | if (!push) {
|
---|
1630 | return NT_STATUS_NO_MEMORY;
|
---|
1631 | }
|
---|
1632 |
|
---|
1633 | if (c->flags & DCERPC_PUSH_BIGENDIAN) {
|
---|
1634 | push->flags |= LIBNDR_FLAG_BIGENDIAN;
|
---|
1635 | }
|
---|
1636 |
|
---|
1637 | if (c->flags & DCERPC_NDR64) {
|
---|
1638 | push->flags |= LIBNDR_FLAG_NDR64;
|
---|
1639 | }
|
---|
1640 |
|
---|
1641 | ndr_err = ndr_push(push, NDR_IN, st);
|
---|
1642 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
1643 | NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
|
---|
1644 | ndr_err = ndr_pull_error(pull, NDR_ERR_VALIDATE,
|
---|
1645 | "failed input validation push - %s",
|
---|
1646 | nt_errstr(status));
|
---|
1647 | return ndr_map_error2ntstatus(ndr_err);
|
---|
1648 | }
|
---|
1649 |
|
---|
1650 | blob2 = ndr_push_blob(push);
|
---|
1651 |
|
---|
1652 | if (data_blob_cmp(&blob, &blob2) != 0) {
|
---|
1653 | DEBUG(3,("original:\n"));
|
---|
1654 | dump_data(3, blob.data, blob.length);
|
---|
1655 | DEBUG(3,("secondary:\n"));
|
---|
1656 | dump_data(3, blob2.data, blob2.length);
|
---|
1657 | ndr_err = ndr_pull_error(pull, NDR_ERR_VALIDATE,
|
---|
1658 | "failed input validation blobs doesn't match");
|
---|
1659 | return ndr_map_error2ntstatus(ndr_err);
|
---|
1660 | }
|
---|
1661 |
|
---|
1662 | return NT_STATUS_OK;
|
---|
1663 | }
|
---|
1664 |
|
---|
1665 | /*
|
---|
1666 | this is a paranoid NDR input validator. For every packet we pull
|
---|
1667 | from the wire we push it back again then pull and push it
|
---|
1668 | again. Then we compare the raw NDR data for that to the NDR we
|
---|
1669 | initially generated. If they don't match then we know we must have a
|
---|
1670 | bug in either the pull or push side of our code
|
---|
1671 | */
|
---|
1672 | static NTSTATUS dcerpc_ndr_validate_out(struct dcecli_connection *c,
|
---|
1673 | struct ndr_pull *pull_in,
|
---|
1674 | void *struct_ptr,
|
---|
1675 | size_t struct_size,
|
---|
1676 | ndr_push_flags_fn_t ndr_push,
|
---|
1677 | ndr_pull_flags_fn_t ndr_pull,
|
---|
1678 | ndr_print_function_t ndr_print)
|
---|
1679 | {
|
---|
1680 | void *st;
|
---|
1681 | struct ndr_pull *pull;
|
---|
1682 | struct ndr_push *push;
|
---|
1683 | DATA_BLOB blob, blob2;
|
---|
1684 | TALLOC_CTX *mem_ctx = pull_in;
|
---|
1685 | char *s1, *s2;
|
---|
1686 | enum ndr_err_code ndr_err;
|
---|
1687 |
|
---|
1688 | st = talloc_size(mem_ctx, struct_size);
|
---|
1689 | if (!st) {
|
---|
1690 | return NT_STATUS_NO_MEMORY;
|
---|
1691 | }
|
---|
1692 | memcpy(st, struct_ptr, struct_size);
|
---|
1693 |
|
---|
1694 | push = ndr_push_init_ctx(mem_ctx);
|
---|
1695 | if (!push) {
|
---|
1696 | return NT_STATUS_NO_MEMORY;
|
---|
1697 | }
|
---|
1698 |
|
---|
1699 | ndr_err = ndr_push(push, NDR_OUT, struct_ptr);
|
---|
1700 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
1701 | NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
|
---|
1702 | ndr_err = ndr_push_error(push, NDR_ERR_VALIDATE,
|
---|
1703 | "failed output validation push - %s",
|
---|
1704 | nt_errstr(status));
|
---|
1705 | return ndr_map_error2ntstatus(ndr_err);
|
---|
1706 | }
|
---|
1707 |
|
---|
1708 | blob = ndr_push_blob(push);
|
---|
1709 |
|
---|
1710 | pull = ndr_pull_init_flags(c, &blob, mem_ctx);
|
---|
1711 | if (!pull) {
|
---|
1712 | return NT_STATUS_NO_MEMORY;
|
---|
1713 | }
|
---|
1714 |
|
---|
1715 | pull->flags |= LIBNDR_FLAG_REF_ALLOC;
|
---|
1716 | ndr_err = ndr_pull(pull, NDR_OUT, st);
|
---|
1717 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
1718 | NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
|
---|
1719 | ndr_err = ndr_pull_error(pull, NDR_ERR_VALIDATE,
|
---|
1720 | "failed output validation pull - %s",
|
---|
1721 | nt_errstr(status));
|
---|
1722 | return ndr_map_error2ntstatus(ndr_err);
|
---|
1723 | }
|
---|
1724 |
|
---|
1725 | push = ndr_push_init_ctx(mem_ctx);
|
---|
1726 | if (!push) {
|
---|
1727 | return NT_STATUS_NO_MEMORY;
|
---|
1728 | }
|
---|
1729 |
|
---|
1730 | ndr_err = ndr_push(push, NDR_OUT, st);
|
---|
1731 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
1732 | NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
|
---|
1733 | ndr_err = ndr_push_error(push, NDR_ERR_VALIDATE,
|
---|
1734 | "failed output validation push2 - %s",
|
---|
1735 | nt_errstr(status));
|
---|
1736 | return ndr_map_error2ntstatus(ndr_err);
|
---|
1737 | }
|
---|
1738 |
|
---|
1739 | blob2 = ndr_push_blob(push);
|
---|
1740 |
|
---|
1741 | if (data_blob_cmp(&blob, &blob2) != 0) {
|
---|
1742 | DEBUG(3,("original:\n"));
|
---|
1743 | dump_data(3, blob.data, blob.length);
|
---|
1744 | DEBUG(3,("secondary:\n"));
|
---|
1745 | dump_data(3, blob2.data, blob2.length);
|
---|
1746 | ndr_err = ndr_push_error(push, NDR_ERR_VALIDATE,
|
---|
1747 | "failed output validation blobs doesn't match");
|
---|
1748 | return ndr_map_error2ntstatus(ndr_err);
|
---|
1749 | }
|
---|
1750 |
|
---|
1751 | /* this checks the printed forms of the two structures, which effectively
|
---|
1752 | tests all of the value() attributes */
|
---|
1753 | s1 = ndr_print_function_string(mem_ctx, ndr_print, "VALIDATE",
|
---|
1754 | NDR_OUT, struct_ptr);
|
---|
1755 | s2 = ndr_print_function_string(mem_ctx, ndr_print, "VALIDATE",
|
---|
1756 | NDR_OUT, st);
|
---|
1757 | if (strcmp(s1, s2) != 0) {
|
---|
1758 | #if 1
|
---|
1759 | DEBUG(3,("VALIDATE ERROR:\nWIRE:\n%s\n GEN:\n%s\n", s1, s2));
|
---|
1760 | #else
|
---|
1761 | /* this is sometimes useful */
|
---|
1762 | printf("VALIDATE ERROR\n");
|
---|
1763 | file_save("wire.dat", s1, strlen(s1));
|
---|
1764 | file_save("gen.dat", s2, strlen(s2));
|
---|
1765 | system("diff -u wire.dat gen.dat");
|
---|
1766 | #endif
|
---|
1767 | ndr_err = ndr_push_error(push, NDR_ERR_VALIDATE,
|
---|
1768 | "failed output validation strings doesn't match");
|
---|
1769 | return ndr_map_error2ntstatus(ndr_err);
|
---|
1770 | }
|
---|
1771 |
|
---|
1772 | return NT_STATUS_OK;
|
---|
1773 | }
|
---|
1774 |
|
---|
1775 | /*
|
---|
1776 | a useful function for retrieving the server name we connected to
|
---|
1777 | */
|
---|
1778 | _PUBLIC_ const char *dcerpc_server_name(struct dcerpc_pipe *p)
|
---|
1779 | {
|
---|
1780 | if (!p->conn->transport.target_hostname) {
|
---|
1781 | if (!p->conn->transport.peer_name) {
|
---|
1782 | return "";
|
---|
1783 | }
|
---|
1784 | return p->conn->transport.peer_name(p->conn);
|
---|
1785 | }
|
---|
1786 | return p->conn->transport.target_hostname(p->conn);
|
---|
1787 | }
|
---|
1788 |
|
---|
1789 |
|
---|
1790 | /*
|
---|
1791 | get the dcerpc auth_level for a open connection
|
---|
1792 | */
|
---|
1793 | uint32_t dcerpc_auth_level(struct dcecli_connection *c)
|
---|
1794 | {
|
---|
1795 | uint8_t auth_level;
|
---|
1796 |
|
---|
1797 | if (c->flags & DCERPC_SEAL) {
|
---|
1798 | auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
|
---|
1799 | } else if (c->flags & DCERPC_SIGN) {
|
---|
1800 | auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
|
---|
1801 | } else if (c->flags & DCERPC_CONNECT) {
|
---|
1802 | auth_level = DCERPC_AUTH_LEVEL_CONNECT;
|
---|
1803 | } else {
|
---|
1804 | auth_level = DCERPC_AUTH_LEVEL_NONE;
|
---|
1805 | }
|
---|
1806 | return auth_level;
|
---|
1807 | }
|
---|
1808 |
|
---|
1809 | /*
|
---|
1810 | Receive an alter reply from the transport
|
---|
1811 | */
|
---|
1812 | static void dcerpc_alter_recv_handler(struct rpc_request *req,
|
---|
1813 | DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
|
---|
1814 | {
|
---|
1815 | struct composite_context *c;
|
---|
1816 | struct dcerpc_pipe *recv_pipe;
|
---|
1817 |
|
---|
1818 | c = talloc_get_type(req->async.private_data, struct composite_context);
|
---|
1819 | recv_pipe = talloc_get_type(c->private_data, struct dcerpc_pipe);
|
---|
1820 |
|
---|
1821 | if (pkt->ptype == DCERPC_PKT_ALTER_RESP &&
|
---|
1822 | pkt->u.alter_resp.num_results == 1 &&
|
---|
1823 | pkt->u.alter_resp.ctx_list[0].result != 0) {
|
---|
1824 | DEBUG(2,("dcerpc: alter_resp failed - reason %d\n",
|
---|
1825 | pkt->u.alter_resp.ctx_list[0].reason));
|
---|
1826 | composite_error(c, dcerpc_map_reason(pkt->u.alter_resp.ctx_list[0].reason));
|
---|
1827 | return;
|
---|
1828 | }
|
---|
1829 |
|
---|
1830 | if (pkt->ptype == DCERPC_PKT_FAULT) {
|
---|
1831 | DEBUG(5,("rpc fault: %s\n", dcerpc_errstr(c, pkt->u.fault.status)));
|
---|
1832 | recv_pipe->last_fault_code = pkt->u.fault.status;
|
---|
1833 | composite_error(c, NT_STATUS_NET_WRITE_FAULT);
|
---|
1834 | return;
|
---|
1835 | }
|
---|
1836 |
|
---|
1837 | if (pkt->ptype != DCERPC_PKT_ALTER_RESP ||
|
---|
1838 | pkt->u.alter_resp.num_results == 0 ||
|
---|
1839 | pkt->u.alter_resp.ctx_list[0].result != 0) {
|
---|
1840 | recv_pipe->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
|
---|
1841 | composite_error(c, NT_STATUS_NET_WRITE_FAULT);
|
---|
1842 | return;
|
---|
1843 | }
|
---|
1844 |
|
---|
1845 | /* the alter_resp might contain a reply set of credentials */
|
---|
1846 | if (recv_pipe->conn->security_state.auth_info &&
|
---|
1847 | pkt->u.alter_resp.auth_info.length) {
|
---|
1848 | struct dcecli_connection *conn = recv_pipe->conn;
|
---|
1849 | NTSTATUS status;
|
---|
1850 | uint32_t auth_length;
|
---|
1851 | status = dcerpc_pull_auth_trailer(pkt, conn, &pkt->u.alter_resp.auth_info,
|
---|
1852 | conn->security_state.auth_info, &auth_length, true);
|
---|
1853 | if (!NT_STATUS_IS_OK(status)) {
|
---|
1854 | composite_error(c, status);
|
---|
1855 | return;
|
---|
1856 | }
|
---|
1857 | }
|
---|
1858 |
|
---|
1859 | composite_done(c);
|
---|
1860 | }
|
---|
1861 |
|
---|
1862 | /*
|
---|
1863 | send a dcerpc alter_context request
|
---|
1864 | */
|
---|
1865 | struct composite_context *dcerpc_alter_context_send(struct dcerpc_pipe *p,
|
---|
1866 | TALLOC_CTX *mem_ctx,
|
---|
1867 | const struct ndr_syntax_id *syntax,
|
---|
1868 | const struct ndr_syntax_id *transfer_syntax)
|
---|
1869 | {
|
---|
1870 | struct composite_context *c;
|
---|
1871 | struct ncacn_packet pkt;
|
---|
1872 | DATA_BLOB blob;
|
---|
1873 | struct rpc_request *req;
|
---|
1874 |
|
---|
1875 | c = composite_create(mem_ctx, p->conn->event_ctx);
|
---|
1876 | if (c == NULL) return NULL;
|
---|
1877 |
|
---|
1878 | c->private_data = p;
|
---|
1879 |
|
---|
1880 | p->syntax = *syntax;
|
---|
1881 | p->transfer_syntax = *transfer_syntax;
|
---|
1882 |
|
---|
1883 | init_ncacn_hdr(p->conn, &pkt);
|
---|
1884 |
|
---|
1885 | pkt.ptype = DCERPC_PKT_ALTER;
|
---|
1886 | pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
|
---|
1887 | pkt.call_id = p->conn->call_id;
|
---|
1888 | pkt.auth_length = 0;
|
---|
1889 |
|
---|
1890 | if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
|
---|
1891 | pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
|
---|
1892 | }
|
---|
1893 |
|
---|
1894 | if (p->binding->flags & DCERPC_HEADER_SIGNING) {
|
---|
1895 | pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
|
---|
1896 | }
|
---|
1897 |
|
---|
1898 | pkt.u.alter.max_xmit_frag = 5840;
|
---|
1899 | pkt.u.alter.max_recv_frag = 5840;
|
---|
1900 | pkt.u.alter.assoc_group_id = p->binding->assoc_group_id;
|
---|
1901 | pkt.u.alter.num_contexts = 1;
|
---|
1902 | pkt.u.alter.ctx_list = talloc_array(c, struct dcerpc_ctx_list, 1);
|
---|
1903 | if (composite_nomem(pkt.u.alter.ctx_list, c)) return c;
|
---|
1904 | pkt.u.alter.ctx_list[0].context_id = p->context_id;
|
---|
1905 | pkt.u.alter.ctx_list[0].num_transfer_syntaxes = 1;
|
---|
1906 | pkt.u.alter.ctx_list[0].abstract_syntax = p->syntax;
|
---|
1907 | pkt.u.alter.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
|
---|
1908 | pkt.u.alter.auth_info = data_blob(NULL, 0);
|
---|
1909 |
|
---|
1910 | /* construct the NDR form of the packet */
|
---|
1911 | c->status = ncacn_push_auth(&blob, mem_ctx, &pkt,
|
---|
1912 | p->conn->security_state.auth_info);
|
---|
1913 | if (!composite_is_ok(c)) return c;
|
---|
1914 |
|
---|
1915 | p->conn->transport.recv_data = dcerpc_recv_data;
|
---|
1916 |
|
---|
1917 | /*
|
---|
1918 | * we allocate a dcerpc_request so we can be in the same
|
---|
1919 | * request queue as normal requests
|
---|
1920 | */
|
---|
1921 | req = talloc_zero(c, struct rpc_request);
|
---|
1922 | if (composite_nomem(req, c)) return c;
|
---|
1923 |
|
---|
1924 | req->state = RPC_REQUEST_PENDING;
|
---|
1925 | req->call_id = pkt.call_id;
|
---|
1926 | req->async.private_data = c;
|
---|
1927 | req->async.callback = dcerpc_composite_fail;
|
---|
1928 | req->p = p;
|
---|
1929 | req->recv_handler = dcerpc_alter_recv_handler;
|
---|
1930 | DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
|
---|
1931 | talloc_set_destructor(req, dcerpc_req_dequeue);
|
---|
1932 |
|
---|
1933 | c->status = p->conn->transport.send_request(p->conn, &blob, true);
|
---|
1934 | if (!composite_is_ok(c)) return c;
|
---|
1935 |
|
---|
1936 | event_add_timed(c->event_ctx, req,
|
---|
1937 | timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
|
---|
1938 | dcerpc_timeout_handler, req);
|
---|
1939 |
|
---|
1940 | return c;
|
---|
1941 | }
|
---|
1942 |
|
---|
1943 | NTSTATUS dcerpc_alter_context_recv(struct composite_context *ctx)
|
---|
1944 | {
|
---|
1945 | NTSTATUS result = composite_wait(ctx);
|
---|
1946 | talloc_free(ctx);
|
---|
1947 | return result;
|
---|
1948 | }
|
---|
1949 |
|
---|
1950 | /*
|
---|
1951 | send a dcerpc alter_context request
|
---|
1952 | */
|
---|
1953 | _PUBLIC_ NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p,
|
---|
1954 | TALLOC_CTX *mem_ctx,
|
---|
1955 | const struct ndr_syntax_id *syntax,
|
---|
1956 | const struct ndr_syntax_id *transfer_syntax)
|
---|
1957 | {
|
---|
1958 | struct composite_context *creq;
|
---|
1959 | creq = dcerpc_alter_context_send(p, mem_ctx, syntax, transfer_syntax);
|
---|
1960 | return dcerpc_alter_context_recv(creq);
|
---|
1961 | }
|
---|
1962 |
|
---|