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