1 /*
2 Unix SMB/CIFS implementation.
3 Main DCOM functionality
4 Copyright (C) 2004 Jelmer Vernooij <jelmer@samba.org>
5 Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 #include "system/filesys.h"
24 #include "librpc/gen_ndr/epmapper.h"
25 #include "librpc/gen_ndr/ndr_remact_c.h"
26 #include "librpc/gen_ndr/com_dcom.h"
27 #include "librpc/gen_ndr/dcom.h"
28 #include "librpc/gen_ndr/ndr_oxidresolver.h"
29 #include "librpc/gen_ndr/ndr_oxidresolver_c.h"
30 #include "librpc/gen_ndr/ndr_remact.h"
31 #include "librpc/gen_ndr/ndr_remact_c.h"
32 #include "lib/com/dcom/dcom.h"
33 #include "librpc/rpc/dcerpc_table.h"
34 #include "lib/util/dlinklist.h"
35 #include "auth/credentials/credentials.h"
36 #include "libcli/composite/composite.h"
37
38 #include <ctype.h>
39
40 #define DCOM_NEGOTIATED_PROTOCOLS { EPM_PROTOCOL_TCP, EPM_PROTOCOL_SMB, EPM_PROTOCOL_NCALRPC }
41
42 /*
43 * Structure used to keep track of the state of an asynchronous DCOM object
44 * activation request.
45 */
46 struct dcom_activation_state
47 {
48 struct composite_context *parent_ctx; /* parent composite context */
49 struct com_context *com_ctx; /* the COM context */
50 struct GUID clsid; /* the requested class ID */
51 struct GUID iid; /* the requester interface ID */
52 int num_ifaces; /* the requested # of interfaces */
53 const char *server; /* the server binding string */
54 struct dcerpc_binding *binding; /* the resolved server binding */
55 struct COMVERSION negotiated_version; /* the COM version negotiated */
56
57 struct IUnknown **ip; /* receives requested interfaces */
58 WERROR coresult; /* receives the COM result */
59 };
60
dcerpc_binding_from_STRINGBINDING(TALLOC_CTX * mem_ctx,struct dcerpc_binding ** b_out,struct STRINGBINDING * bd)61 static NTSTATUS dcerpc_binding_from_STRINGBINDING(TALLOC_CTX *mem_ctx,
62 struct dcerpc_binding **b_out, struct STRINGBINDING *bd)
63 {
64 char *host, *endpoint;
65 struct dcerpc_binding *b;
66
67 b = talloc_zero(mem_ctx, struct dcerpc_binding);
68 if (!b)
69 {
70 return NT_STATUS_NO_MEMORY;
71 }
72
73 b->transport = dcerpc_transport_by_endpoint_protocol(bd->wTowerId);
74
75 if (b->transport == -1)
76 {
77 DEBUG(1, ("Can't find transport match endpoint protocol %d\n", bd->wTowerId));
78 talloc_free(b);
79 return NT_STATUS_NOT_SUPPORTED;
80 }
81
82 host = talloc_strdup(b, bd->NetworkAddr);
83 endpoint = strchr(host, '[');
84
85 if (endpoint)
86 {
87 *endpoint = '\0';
88 endpoint++;
89
90 endpoint[strlen(endpoint) - 1] = '\0';
91 }
92
93 b->host = host;
94 b->endpoint = talloc_strdup(b, endpoint);
95
96 *b_out = b;
97 return NT_STATUS_OK;
98 }
99
dcom_get_server_credentials(struct com_context * ctx,const char * server)100 struct cli_credentials *dcom_get_server_credentials(struct com_context *ctx,
101 const char *server)
102 {
103 struct dcom_server_credentials *c;
104 struct cli_credentials *d;
105
106 d = NULL;
107 for (c = ctx->dcom->credentials; c; c = c->next)
108 {
109 if (c->server == NULL)
110 {
111 d = c->credentials;
112 continue;
113 }
114 if (server && !strcmp(c->server, server))
115 return c->credentials;
116 }
117 return d;
118 }
119
dcom_set_server_credentials(struct com_context * ctx,const char * server,struct cli_credentials * credentials)120 void dcom_set_server_credentials(struct com_context *ctx, const char *server,
121 struct cli_credentials *credentials)
122 {
123 struct dcom_server_credentials *c;
124
125 for (c = ctx->dcom->credentials; c; c = c->next)
126 {
127 if ((server == NULL && c->server == NULL) || (server && c->server
128 && !strcmp(c->server, server)))
129 {
130 if (c->credentials && c->credentials != credentials)
131 {
132 talloc_unlink(c, c->credentials);
133 c->credentials = credentials;
134 if (talloc_find_parent_bytype(c->credentials, struct dcom_server_credentials))
135 (void) talloc_reference(c, c->credentials);
136 else
137 talloc_steal(c, c->credentials);
138 }
139 return;
140 }
141 }
142 c = talloc(ctx->event_ctx, struct dcom_server_credentials);
143 c->server = talloc_strdup(c, server);
144 c->credentials = credentials;
145 if (talloc_find_parent_bytype(c->credentials, struct dcom_server_credentials))
146 (void) talloc_reference(c, c->credentials);
147 else
148 talloc_steal(c, c->credentials);
149 DLIST_ADD(ctx->dcom->credentials, c);
150 }
151
dcom_update_credentials_for_aliases(struct com_context * ctx,const char * server,struct DUALSTRINGARRAY * pds)152 void dcom_update_credentials_for_aliases(struct com_context *ctx,
153 const char *server, struct DUALSTRINGARRAY *pds)
154 {
155 struct cli_credentials *cc;
156 struct dcerpc_binding *b;
157 uint32_t i;
158 NTSTATUS status;
159
160 cc = dcom_get_server_credentials(ctx, server);
161 for (i = 0; pds->stringbindings[i]; ++i)
162 {
163 if (pds->stringbindings[i]->wTowerId != EPM_PROTOCOL_TCP)
164 continue;
165 status = dcerpc_binding_from_STRINGBINDING(ctx, &b,
166 pds->stringbindings[i]);
167 if (!NT_STATUS_IS_OK(status))
168 continue;
169 dcom_set_server_credentials(ctx, b->host, cc);
170 talloc_free(b);
171 }
172 }
173
dcom_client_init(struct com_context * ctx,struct cli_credentials * credentials)174 struct dcom_client_context *dcom_client_init(struct com_context *ctx,
175 struct cli_credentials *credentials)
176 {
177 ctx->dcom = talloc_zero(ctx, struct dcom_client_context);
178 if (!credentials)
179 {
180 credentials = cli_credentials_init(ctx);
181 cli_credentials_set_conf(credentials);
182 cli_credentials_parse_string(credentials, "%", CRED_SPECIFIED);
183 }
184 dcom_set_server_credentials(ctx, NULL, credentials);
185 return ctx->dcom;
186 }
187
dcom_update_server(struct dcom_object_exporter * m,const char * server)188 static void dcom_update_server(struct dcom_object_exporter *m,
189 const char *server)
190 {
191 char *c = strchr(server, '[');
192
193 if (m->host)
194 talloc_free(m->host);
195
196 m->host = c ? talloc_strndup(m, server, c - server)
197 : talloc_strdup(m, server);
198 }
199
200 /*
201 * Complete a remote activation request by receiving the final results and
202 * updating the updating the local object exporter as needed.
203 */
remote_activation_complete(struct rpc_request * rpc_req)204 static void remote_activation_complete(struct rpc_request *rpc_req)
205 {
206 struct composite_context *c = NULL;
207 struct RemoteActivation* r = NULL;
208 struct dcom_activation_state *s = NULL;
209 struct dcom_object_exporter *m = NULL;
210 struct IUnknown *ru_template = NULL;
211 WERROR result;
212 int i;
213
214 /* retrieve the parent composite context */
215 c = talloc_get_type(rpc_req->async.private, struct composite_context);
216 if (!NT_STATUS_IS_OK(rpc_req->status))
217 {
218 composite_error(c, rpc_req->status);
219 return;
220 }
221
222 /* retrieve pointers to the RemoteActivation output and our state data */
223 r = talloc_get_type(rpc_req->ndr.struct_ptr, struct RemoteActivation);
224 s = talloc_get_type(c->private_data, struct dcom_activation_state);
225
226 /* receive the results of the RPC request and display if requested */
227 c->status = dcerpc_ndr_request_recv(rpc_req);
228 if (!composite_is_ok(c)) return;
229
230 if (DEBUGLVL(9))
231 NDR_PRINT_OUT_DEBUG(RemoteActivation, r);
232
233 /* determine if the remote activation request was successful */
234 result = *(r->out.hr);
235 if (!W_ERROR_IS_OK(result))
236 {
237 composite_error(c, werror_to_ntstatus(result));
238 return;
239 }
240
241 m = object_exporter_update_oxid(s->com_ctx,
242 *(r->out.pOxid), *(r->out.pdsaOxidBindings));
243
244 /* build the return interface pointers from the results. */
245 s->ip = talloc_array(c, struct IUnknown *, s->num_ifaces);
246 if (composite_nomem(s->ip, c)) return;
247 for (i = 0; i < s->num_ifaces; i++)
248 {
249 s->ip[i] = NULL;
250
251 /*
252 * determine if each individual interface was successfully returned; in
253 * some cases the overall request may be successful but certain
254 * interfaces may not be available.
255 */
256 if (W_ERROR_IS_OK(r->out.results[i]))
257 {
258 NTSTATUS status = dcom_IUnknown_from_OBJREF(s->com_ctx,
259 &(s->ip[i]), &(r->out.ifaces[i]->obj));
260 if (!NT_STATUS_IS_OK(status))
261 {
262 r->out.results[i] = ntstatus_to_werror(status);
263 }
264 else if (!ru_template)
265 {
266 ru_template = s->ip[i];
267 }
268 }
269 }
270
271 /*
272 * do something with the object exporter?! this whole section of code
273 * desperately needs a rewrite and clarity.
274 */
275 /* TODO:avg check when exactly oxid should be updated,its lifetime etc */
276 if (m->rem_unknown && memcmp(
277 &m->rem_unknown->obj.u_objref.u_standard.std.ipid,
278 r->out.ipidRemUnknown, sizeof(*(r->out.ipidRemUnknown))))
279 {
280 talloc_free(m->rem_unknown);
281 m->rem_unknown = NULL;
282 }
283 if (!m->rem_unknown)
284 {
285 if (!ru_template)
286 {
287 DEBUG(1,("dcom_activate: Cannot Create IRemUnknown - template interface not available\n"));
288 result = WERR_GENERAL_FAILURE;
289 // TODO: notice how this failure doesn't get sent back to the caller?
290 }
291 m->rem_unknown = talloc_zero(m, struct IRemUnknown);
292 memcpy(m->rem_unknown, ru_template, sizeof(struct IUnknown));
293 GUID_from_string(COM_IREMUNKNOWN_UUID, &m->rem_unknown->obj.iid);
294 m->rem_unknown->obj.u_objref.u_standard.std.ipid
295 = *(r->out.ipidRemUnknown);
296 m->rem_unknown->vtable
297 = (struct IRemUnknown_vtable *) dcom_proxy_vtable_by_iid(
298 &m->rem_unknown->obj.iid);
299 /* TODO:avg copy stringbindigs?? */
300 }
301
302 dcom_update_credentials_for_aliases(s->com_ctx, s->server,
303 *(r->out.pdsaOxidBindings));
304 dcom_update_server(m, s->server);
305
306 composite_done(c);
307 }
308
309 /*
310 * Continue the remote activation request following a connect by issuing the
311 * RemoteActivation call after all the preliminary negotiation has been
312 * completed.
313 */
remote_activation_continue(struct composite_context * ctx)314 static void remote_activation_continue(struct composite_context *ctx)
315 {
316 static uint16_t protseq[] = DCOM_NEGOTIATED_PROTOCOLS;
317
318 struct composite_context *c = NULL;
319 struct dcom_activation_state *s = NULL;
320 struct dcerpc_pipe *p = NULL;
321 struct RemoteActivation *r = NULL;
322 struct rpc_request *rpc_req = NULL;
323
324 /* retrieve the parent composite context */
325 c = talloc_get_type(ctx->async.private_data, struct composite_context);
326 if (!composite_is_ok(c)) return;
327
328 /* retrieve the activation state data */
329 s = talloc_get_type(c->private_data, struct dcom_activation_state);
330
331 /*
332 * complete the pipe connect and receive a pointer to the dcerpc_pipe
333 * structure
334 */
335 c->status = dcerpc_pipe_connect_b_recv(ctx, c, &p);
336 if (!composite_is_ok(c)) return;
337
338 /*
339 * Prepare arguments for the RemoteActivation call. Refer to 3.1.4.1.1.2
340 * "Issuing the Activation Request" in [MS-DCOM] for more information.
341 */
342 r = talloc_zero(c, struct RemoteActivation);
343 if (composite_nomem(r, c)) return;
344
345 r->in.this.version = s->negotiated_version;
346 r->in.this.cid = GUID_random(); /* generate a new causality identifier */
347 r->in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
348 r->in.Clsid = s->clsid;
349 r->in.Mode = 0; /* we do not require an interface to the class factory */
350 r->in.protseq = protseq;
351 r->in.num_protseqs = ARRAY_SIZE(protseq);
352 r->in.Interfaces = s->num_ifaces;
353 r->in.pIIDs = &s->iid;
354
355 r->out.that = talloc_zero(r, struct ORPCTHAT);
356 if (composite_nomem(r->out.that, c)) return;
357
358 r->out.pOxid = talloc_zero(r, uint64_t);
359 if (composite_nomem(r->out.pOxid, c)) return;
360
361 r->out.pdsaOxidBindings = talloc_zero(r, struct DUALSTRINGARRAY *);
362 if (composite_nomem(r->out.pdsaOxidBindings, c)) return;
363
364 r->out.ipidRemUnknown = talloc_zero(r, struct GUID);
365 if (composite_nomem(r->out.ipidRemUnknown, c)) return;
366
367 r->out.AuthnHint = talloc_zero(r, uint32_t);
368 if (composite_nomem(r->out.AuthnHint, c)) return;
369
370 r->out.ServerVersion = talloc_zero(r, struct COMVERSION);
371 if (composite_nomem(r->out.ServerVersion, c)) return;
372
373 r->out.hr = talloc_zero(r, WERROR);
374 if (composite_nomem(r->out.hr, c)) return;
375
376 r->out.ifaces = talloc_array(r, struct MInterfacePointer *, s->num_ifaces);
377 if (composite_nomem(r->out.ifaces, c)) return;
378
379 r->out.results = &s->coresult;
380
381 /*
382 * Send the asynchronous RemoteActivation request and setup the
383 * RPC continuation callback.
384 */
385 rpc_req = dcerpc_RemoteActivation_send(p, c, r);
386 if (composite_nomem(rpc_req, c)) return;
387
388 composite_continue_rpc(c, rpc_req, remote_activation_complete, c);
389 }
390
391 /**
392 * Continue the RPC IOXIDResolver:ServerAlive call by receiving the
393 * response and processing the results.
394 */
determine_rpc_binding_continue2(struct rpc_request * rpc_ctx)395 static void determine_rpc_binding_continue2(struct rpc_request *rpc_ctx)
396 {
397 struct composite_context *c = NULL;
398 struct dcom_activation_state *s = NULL;
399 struct ServerAlive* r = NULL;
400 struct cli_credentials *creds = NULL;
401 struct composite_context *pipe_conn_req = NULL;
402 NTSTATUS status;
403
404 /* retrieve the parent composite context */
405 c = talloc_get_type(rpc_ctx->async.private, struct composite_context);
406
407 /* retrieve a pointer to the results structure and our state data */
408 s = talloc_get_type(c->private_data, struct dcom_activation_state);
409 r = talloc_get_type(rpc_ctx->ndr.struct_ptr, struct ServerAlive);
410
411 /* retrieve the results of the RPC request */
412 c->status = dcerpc_ndr_request_recv(rpc_ctx);
413 DEBUG(3, ("dcerpc_ndr_request_recv returned %s\n", nt_errstr(c->status)));
414 if (!composite_is_ok(c)) return;
415
416 status = werror_to_ntstatus(r->out.result);
417 DEBUG(3, ("IObjectExporter::ServerAlive returned %s\n", nt_errstr(status)));
418 if (!W_ERROR_IS_OK(r->out.result))
419 {
420 composite_error(c, status);
421 }
422 if (!composite_is_ok(c)) return;
423
424 // lowest common denominator here; ideally we'd just call ServerAlive2
425 // and do better negotiation on our end...
426 s->negotiated_version.MajorVersion = COM_MAJOR_VERSION;
427 s->negotiated_version.MinorVersion = COM_MINOR_VERSION;
428
429 /*
430 * Proceed with the activating request using the same binding that was
431 * successful for the ServerAlive call and try to retrieve a pipe to
432 * the IRemoteActivation interface.
433 */
434 creds = dcom_get_server_credentials(s->com_ctx, s->server);
435 pipe_conn_req = dcerpc_pipe_connect_b_send(c,
436 s->binding, &dcerpc_table_IRemoteActivation, creds, c->event_ctx);
437
438 composite_continue(c, pipe_conn_req, remote_activation_continue, c);
439 }
440
441 /*
442 * Continue the RPC connect after a successful socket open to the server by
443 * receiving the results and then attempting to send a
444 * IOXIDResolver::ServerAlive call if the connect was successful.
445 */
determine_rpc_binding_continue(struct composite_context * ctx)446 static void determine_rpc_binding_continue(struct composite_context *ctx)
447 {
448 struct composite_context *c = NULL;
449 struct dcerpc_pipe* p = NULL;
450 struct ServerAlive* r = NULL;
451 struct rpc_request* rpc_ctx = NULL;
452
453 /* retrieve the parent composite context */
454 c = talloc_get_type(ctx->async.private_data, struct composite_context);
455
456 /*
457 * complete the pipe connect and receive a pointer to the dcerpc_pipe
458 * structure. a failure here is most likely that no RPC server is listening
459 * on the specified host & port.
460 */
461 c->status = dcerpc_pipe_connect_b_recv(ctx, c, &p);
462 if (!composite_is_ok(c)) return;
463
464 /* prepare arguments for the ServerAlive call and send it off */
465 r = talloc_zero(c, struct ServerAlive);
466 if (composite_nomem(r, c)) return;
467
468 rpc_ctx = dcerpc_ServerAlive_send(p, c, r);
469 if (composite_nomem(rpc_ctx, c)) return;
470
471 composite_continue_rpc(c, rpc_ctx, determine_rpc_binding_continue2, c);
472 }
473
474 /*
475 * 3.1.4.1.1.1 Determining RPC Binding Information for Activation
476 * The client MUST call the IObjectExporter::ServerAlive2 method using the first
477 * RPC protocol sequence listed in section 3.2.2.3, unless the client COMVERSION
478 * is less than 5.6, in which case the client MUST call the
479 * IObjectExporter::ServerAlive method. The client MUST specify the RPC endpoint
480 * information to contain the remote server name on which the application wants
481 * to activate the object and the well-known endpoint of the object resolver.
482 *
483 * In our case, we're just assuming the lowest common denominator and acting
484 * like client version 5.1, so we just call ServerAlive instead of ServerAlive2.
485 */
dcom_determine_rpc_binding(struct composite_context * parent_ctx,const char * server,struct dcom_activation_state * activation_state,void * private_data)486 static struct composite_context *dcom_determine_rpc_binding(
487 struct composite_context *parent_ctx, const char* server,
488 struct dcom_activation_state *activation_state, void *private_data)
489 {
490 struct composite_context *c = NULL;
491 struct cli_credentials* creds = NULL;
492 struct composite_context *pipe_conn_req = NULL;
493 NTSTATUS status;
494
495 /* composite context allocation and setup */
496 c = composite_create(parent_ctx, parent_ctx->event_ctx);
497 if (c == NULL) return NULL;
498 c->private_data = activation_state;
499
500 /*
501 * determine if the caller specified a server binding, and if not use
502 * the default
503 */
504 status = dcerpc_parse_binding(c, server, &activation_state->binding);
505 if (!NT_STATUS_IS_OK(status))
506 {
507 /* build a binding string using NCACN_IP_TCP */
508 char *bindstr = talloc_asprintf(c, "ncacn_ip_tcp:%s", server);
509 if (composite_nomem(bindstr, c)) return c;
510
511 status = dcerpc_parse_binding(c, bindstr, &activation_state->binding);
512 if (!NT_STATUS_IS_OK(status))
513 {
514 DEBUG(0, ("Failed to parse dcerpc binding '%s'\n", bindstr));
515 composite_error(c, status);
516 return c;
517 }
518 }
519 else if (activation_state->binding->transport != NCACN_IP_TCP)
520 {
521 DEBUG(0, ("Invalid transport specified, only NCACN_IP_TCP "
522 "supported for DCOM object activation\n"));
523 composite_error(c, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED);
524 return c;
525 }
526
527 DEBUG(3, ("Using binding %s\n", dcerpc_binding_string(c,
528 activation_state->binding)));
529
530 /*
531 * create an empty set of credentials since an
532 * IOXIDResolver:ServerAlive call should never have any authentication
533 * specified
534 */
535 creds = talloc_zero(c, struct cli_credentials);
536 if (composite_nomem(creds, c)) return c;
537 creds->username = "";
538 creds->password = "";
539
540 /*
541 * start connecting to a rpc pipe after binding structure
542 * is established
543 */
544 pipe_conn_req = dcerpc_pipe_connect_b_send(c,
545 activation_state->binding, &dcerpc_table_IOXIDResolver, creds,
546 parent_ctx->event_ctx);
547 if (composite_nomem(pipe_conn_req, c)) return c;
548
549 composite_continue(c, pipe_conn_req, determine_rpc_binding_continue, c);
550 return c;
551 }
552
553 /*
554 * Complete an activation request and continue the composite chain.
555 */
complete_activation(struct composite_context * ctx)556 static void complete_activation(struct composite_context *ctx)
557 {
558 struct composite_context *c = NULL;
559 struct dcom_activation_state *s = NULL;
560
561 c = talloc_get_type(ctx->async.private_data, struct composite_context);
562 c->status = ctx->status;
563 if (!composite_is_ok(c)) return;
564
565 s = talloc_get_type(ctx->private_data, struct dcom_activation_state);
566
567 DEBUG(3, ("Negotiated COM version: %d.%d using binding %s\n",
568 s->negotiated_version.MajorVersion,
569 s->negotiated_version.MinorVersion,
570 dcerpc_binding_string(c, s->binding)));
571
572 composite_done(c);
573 }
574
575 /*
576 * Begin an asynchronous DCOM object activation request.
577 *
578 * parent_ctx the parent memory context for the request
579 * clsid the class ID requested
580 * server the binding string or server name
581 * num_ifaces the number of interfaces requested
582 * iid the interface ID requested
583 * com_ctx the COM context to use
584 */
dcom_activate_send(TALLOC_CTX * parent_ctx,const struct GUID * clsid,const char * server,int num_ifaces,const struct GUID * iid,struct com_context * com_ctx)585 struct composite_context *dcom_activate_send(TALLOC_CTX *parent_ctx,
586 const struct GUID *clsid, const char *server, int num_ifaces,
587 const struct GUID *iid, struct com_context *com_ctx)
588 {
589 struct composite_context *c = NULL;
590 struct dcom_activation_state *s = NULL;
591 struct composite_context *binding_ctx = NULL;
592
593 /* composite context allocation and setup */
594 c = composite_create(parent_ctx, com_ctx->event_ctx);
595 if (c == NULL) return NULL;
596
597 s = talloc_zero(c, struct dcom_activation_state);
598 if (composite_nomem(s, c)) return c;
599
600 c->private_data = s;
601 s->parent_ctx = parent_ctx;
602 s->com_ctx = com_ctx;
603 s->server = server;
604 s->clsid = *clsid;
605 s->iid = *iid;
606 s->num_ifaces = num_ifaces;
607
608 /*
609 * Begin the DCOM object activation by first attempting to determine the
610 * correct RPC binding to use and what COM version should be used. This
611 * requires sending a non-authenticated IOXIDResolver:ServerAlive RPC call
612 * to the requested server and handling the results. See section 3.1.4.1.1
613 * of [MS-DCOM] for further information.
614 */
615 binding_ctx = dcom_determine_rpc_binding(c, server, s, c);
616 if (composite_nomem(binding_ctx, c)) return c;
617
618 composite_continue(c, binding_ctx, complete_activation, c);
619 return c;
620 }
621
622 /*
623 * Complete an asynchronous DCOM activation request by receiving the results
624 * and providing the IUknown interface pointers back to the caller.
625 */
dcom_activate_recv(struct composite_context * c,TALLOC_CTX * parent_ctx,struct IUnknown *** interfaces)626 NTSTATUS dcom_activate_recv(struct composite_context *c,
627 TALLOC_CTX *parent_ctx, struct IUnknown ***interfaces)
628 {
629 NTSTATUS status = composite_wait(c);
630 if (NT_STATUS_IS_OK(status))
631 {
632 struct dcom_activation_state *s = talloc_get_type(c->private_data,
633 struct dcom_activation_state);
634
635 talloc_steal(parent_ctx, s->ip);
636 *interfaces = s->ip;
637
638 status = werror_to_ntstatus(s->coresult);
639 }
640
641 talloc_free(c);
642
643 return status;
644 }
645
646 /*
647 * Synchronous DCOM activation request.
648 */
dcom_activate(struct com_context * com_ctx,TALLOC_CTX * parent_ctx,const char * server,struct GUID * clsid,struct GUID * iid,int num_ifaces,struct IUnknown *** interfaces)649 NTSTATUS dcom_activate(struct com_context *com_ctx, TALLOC_CTX *parent_ctx,
650 const char* server, struct GUID *clsid, struct GUID *iid,
651 int num_ifaces, struct IUnknown ***interfaces)
652 {
653 struct composite_context *c = dcom_activate_send(parent_ctx, clsid, server,
654 num_ifaces, iid, com_ctx);
655 return dcom_activate_recv(c, parent_ctx, interfaces);
656 }
657
object_exporter_by_oxid(struct com_context * ctx,uint64_t oxid)658 struct dcom_object_exporter *object_exporter_by_oxid(struct com_context *ctx,
659 uint64_t oxid)
660 {
661 struct dcom_object_exporter *ox;
662 for (ox = ctx->dcom->object_exporters; ox; ox = ox->next)
663 {
664 if (ox->oxid == oxid)
665 {
666 return ox;
667 }
668 }
669
670 return NULL;
671 }
672
object_exporter_update_oxid(struct com_context * ctx,uint64_t oxid,struct DUALSTRINGARRAY * bindings)673 struct dcom_object_exporter *object_exporter_update_oxid(
674 struct com_context *ctx, uint64_t oxid,
675 struct DUALSTRINGARRAY *bindings)
676 {
677 struct dcom_object_exporter *ox;
678 ox = object_exporter_by_oxid(ctx, oxid);
679 if (!ox)
680 {
681 ox = talloc_zero(ctx, struct dcom_object_exporter);
682 DLIST_ADD(ctx->dcom->object_exporters, ox);
683 ox->oxid = oxid;
684 }
685 else
686 {
687 talloc_free(ox->bindings);
688 }
689 ox->bindings = bindings;
690 talloc_steal(ox, bindings);
691 return ox;
692 }
693
694 struct query_interface_state
695 {
696 struct dcom_object_exporter *ox;
697 struct REMQIRESULT *rqir;
698 uint16_t cIids;
699 struct GUID *iids;
700 struct IUnknown **ips;
701 WERROR result;
702 };
703
object_exporter_by_ip(struct com_context * ctx,struct IUnknown * ip)704 struct dcom_object_exporter *object_exporter_by_ip(struct com_context *ctx,
705 struct IUnknown *ip)
706 {
707 return object_exporter_by_oxid(ctx, ip->obj.u_objref.u_standard.std.oxid);
708 }
709
query_interface_continue(struct composite_context * ctx)710 static void query_interface_continue(struct composite_context *ctx)
711 {
712 struct composite_context *c = NULL;
713 struct query_interface_state *s = NULL;
714 WERROR result;
715
716 /* retrieve the parent composite context */
717 c = talloc_get_type(ctx->async.private_data, struct composite_context);
718
719 s = talloc_get_type(c->private_data, struct query_interface_state);
720
721 result = IRemUnknown_RemQueryInterface_recv(ctx, &s->rqir);
722 if (W_ERROR_IS_OK(result))
723 {
724 int i;
725 struct IUnknown ru;
726 NTSTATUS status;
727
728 s->ips = talloc_array(c, struct IUnknown *, s->cIids);
729 if (composite_nomem(s->ips, c)) return;
730
731 ru = *(struct IUnknown *) s->ox->rem_unknown;
732 for (i = 0; i < s->cIids; i++)
733 {
734 s->ips[i] = NULL;
735 if (W_ERROR_IS_OK(s->rqir[i].hResult))
736 {
737 ru.obj.iid = s->iids[i];
738 ru.obj.u_objref.u_standard.std = s->rqir[i].std;
739 status = dcom_IUnknown_from_OBJREF(s->ox->rem_unknown->ctx,
740 &(s->ips[i]), &ru.obj);
741 if (!NT_STATUS_IS_OK(status))
742 {
743 /*
744 * TODO: the old code used an array of status result values
745 * separately from the overall status. Do we really need
746 * such a distinction?
747 */
748 result = ntstatus_to_werror(status);
749 }
750 }
751 }
752 }
753
754 s->result = result;
755 c->status = werror_to_ntstatus(result);
756 composite_done(c);
757 }
758
dcom_query_interface_recv(struct composite_context * c,TALLOC_CTX * parent_ctx,struct IUnknown *** interfaces)759 NTSTATUS dcom_query_interface_recv(struct composite_context *c,
760 TALLOC_CTX *parent_ctx, struct IUnknown ***interfaces)
761 {
762 NTSTATUS status = composite_wait(c);
763 if (NT_STATUS_IS_OK(status))
764 {
765 struct query_interface_state *s = NULL;
766
767 s = talloc_get_type(c->private_data, struct query_interface_state);
768
769 talloc_steal(parent_ctx, s->ips);
770 *interfaces = s->ips;
771
772 status = werror_to_ntstatus(s->result);
773 }
774
775 talloc_free(c);
776
777 return status;
778 }
779
dcom_query_interface_send(struct IUnknown * d,TALLOC_CTX * parent_ctx,uint32_t cRefs,uint16_t cIids,struct GUID * iids)780 struct composite_context *dcom_query_interface_send(struct IUnknown *d,
781 TALLOC_CTX *parent_ctx, uint32_t cRefs, uint16_t cIids,
782 struct GUID *iids)
783 {
784 struct composite_context *c;
785 struct composite_context *new_ctx;
786 struct query_interface_state *s;
787
788 c = composite_create(parent_ctx, d->ctx->event_ctx);
789 if (c == NULL) return NULL;
790
791 s = talloc_zero(c, struct query_interface_state);
792 if (composite_nomem(s, c)) return c;
793 c->private_data = s;
794
795 s->ox = object_exporter_by_ip(d->ctx, d);
796 s->cIids = cIids;
797 s->iids = talloc_memdup(s, iids, sizeof(struct GUID) * cIids);
798 if (composite_nomem(s->iids, c)) return c;
799
800 new_ctx = IRemUnknown_RemQueryInterface_send(s->ox->rem_unknown, c,
801 &IUnknown_ipid(d), cRefs, cIids, s->iids);
802 if (composite_nomem(new_ctx, c)) return c;
803
804 composite_continue(c, new_ctx, query_interface_continue, c);
805 return c;
806 }
807
dcom_query_interface(struct IUnknown * d,TALLOC_CTX * parent_ctx,uint32_t cRefs,uint16_t cIids,struct GUID * iids,struct IUnknown *** interfaces)808 NTSTATUS dcom_query_interface(struct IUnknown *d, TALLOC_CTX *parent_ctx,
809 uint32_t cRefs, uint16_t cIids, struct GUID *iids,
810 struct IUnknown ***interfaces)
811 {
812 struct composite_context *c = dcom_query_interface_send(d, parent_ctx,
813 cRefs, cIids, iids);
814 return dcom_query_interface_recv(c, parent_ctx, interfaces);
815 }
816
817 /*
818 * Returns true if the provided string is an numeric IP address binding string.
819 */
is_ip_binding(const char * s)820 static int is_ip_binding(const char* s)
821 {
822 while (*s && (*s != '['))
823 {
824 if (isdigit(*s) || *s == '.')
825 ++s;
826 else
827 return 0;
828 }
829 return 1;
830 }
831
832 /*
833 * Attempt to find a STRINGBINDING that is similar to the specified host name.
834 */
find_similar_binding(struct STRINGBINDING ** sb,const char * host)835 static int find_similar_binding(struct STRINGBINDING **sb, const char *host)
836 {
837 const size_t host_len = strlen(host);
838 int i;
839
840 for (i = 0; sb[i] != NULL; ++i)
841 {
842 /* only look @ TCP/IP bindings */
843 if (sb[i]->wTowerId == EPM_PROTOCOL_TCP)
844 {
845 const char *s = strchr(sb[i]->NetworkAddr, '[');
846 if (s != NULL)
847 {
848 /*
849 * see if the hostnames match, but use the shorter of the two
850 * names in case the user provided a FQDN but the STRINGBINDING
851 * is only the hostname (the common case)
852 */
853 const size_t addr_len = s - sb[i]->NetworkAddr;
854 const size_t n = (addr_len < host_len) ? addr_len : host_len;
855 if (strncasecmp(host, sb[i]->NetworkAddr, n) == 0) break;
856 }
857 }
858 }
859
860 return i;
861 }
862
dcom_OBJREF_from_IUnknown(struct OBJREF * o,struct IUnknown * p)863 NTSTATUS dcom_OBJREF_from_IUnknown(struct OBJREF *o, struct IUnknown *p)
864 {
865 /* FIXME: Cache generated objref objects? */
866 ZERO_STRUCTP(o);
867
868 if (!p)
869 {
870 o->signature = OBJREF_SIGNATURE;
871 o->flags = OBJREF_NULL;
872 }
873 else
874 {
875 *o = p->obj;
876 switch (o->flags)
877 {
878 case OBJREF_CUSTOM:
879 {
880 marshal_fn marshal;
881
882 marshal = dcom_marshal_by_clsid(&o->u_objref.u_custom.clsid);
883 if (marshal)
884 {
885 return marshal(p, o);
886 }
887 else
888 {
889 return NT_STATUS_NOT_SUPPORTED;
890 }
891 }
892 }
893 }
894
895 return NT_STATUS_OK;
896 }
897
dcom_IUnknown_from_OBJREF(struct com_context * ctx,struct IUnknown ** _p,struct OBJREF * o)898 NTSTATUS dcom_IUnknown_from_OBJREF(struct com_context *ctx,
899 struct IUnknown **_p, struct OBJREF *o)
900 {
901 struct IUnknown *p;
902 struct dcom_object_exporter *ox;
903 unmarshal_fn unmarshal;
904
905 switch (o->flags)
906 {
907 case OBJREF_NULL:
908 *_p = NULL;
909 return NT_STATUS_OK;
910
911 case OBJREF_STANDARD:
912 p = talloc_zero(ctx, struct IUnknown);
913 p->ctx = ctx;
914 p->obj = *o;
915 p->vtable = dcom_proxy_vtable_by_iid(&o->iid);
916
917 if (!p->vtable)
918 {
919 DEBUG(0, ("Unable to find proxy class for interface with IID %s\n", GUID_string(ctx, &o->iid)));
920 return NT_STATUS_NOT_SUPPORTED;
921 }
922
923 p->vtable->Release_send = dcom_release_send;
924
925 ox = object_exporter_by_oxid(ctx, o->u_objref.u_standard.std.oxid);
926 /* FIXME: Add object to list of objects to ping */
927 *_p = p;
928 return NT_STATUS_OK;
929
930 case OBJREF_HANDLER:
931 p = talloc_zero(ctx, struct IUnknown);
932 p->ctx = ctx;
933 p->obj = *o;
934 ox = object_exporter_by_oxid(ctx, o->u_objref.u_handler.std.oxid);
935 /* FIXME: Add object to list of objects to ping */
936 /*FIXME p->vtable = dcom_vtable_by_clsid(&o->u_objref.u_handler.clsid);*/
937 /* FIXME: Do the custom unmarshaling call */
938
939 *_p = p;
940 return NT_STATUS_NOT_SUPPORTED;
941
942 case OBJREF_CUSTOM:
943 p = talloc_zero(ctx, struct IUnknown);
944 p->ctx = ctx;
945 p->vtable = NULL;
946 p->obj = *o;
947 unmarshal = dcom_unmarshal_by_clsid(&o->u_objref.u_custom.clsid);
948 *_p = p;
949 if (unmarshal)
950 {
951 return unmarshal(o, _p);
952 }
953 else
954 {
955 return NT_STATUS_NOT_SUPPORTED;
956 }
957 }
958
959 return NT_STATUS_NOT_SUPPORTED;
960 }
961
dcom_get_current_oxid(void)962 uint64_t dcom_get_current_oxid(void)
963 {
964 return getpid();
965 }
966
967 /*
968 * State structure used for asynchronous dcom_get_pipe calls.
969 */
970 struct dcom_get_pipe_state
971 {
972 struct IUnknown *iface; /* the requested interface */
973 struct dcom_object_exporter *ox; /* the object exporter for it */
974 struct dcerpc_pipe *p; /* the final pipe */
975
976 int similar_binding_index;
977 int current_binding_index;
978 int current_binding_offset;
979 };
980
981 /*
982 * Complete the alter_context PDU request for an existing pipe so it can be
983 * used again for the requested interface.
984 */
reuse_existing_pipe_continue(struct composite_context * ctx)985 static void reuse_existing_pipe_continue(struct composite_context *ctx)
986 {
987 struct composite_context *c = NULL;
988
989 c = talloc_get_type(ctx->async.private_data, struct composite_context);
990
991 /* nothing to do here except pass along the status of the alter context */
992 c->status = dcerpc_alter_context_recv(ctx);
993 if (!composite_is_ok(c)) return;
994
995 composite_done(c);
996 }
997
998 /*
999 * Reuse an existing pipe already on the object exporter. If the pipe isn't
1000 * presently bound to the GUID for the requested interface then an alter_context
1001 * PDU request is sent off, otherwise the pipe is used as-is.
1002 */
reuse_existing_pipe(struct composite_context * c)1003 static void reuse_existing_pipe(struct composite_context *c)
1004 {
1005 struct composite_context *new_ctx = NULL;
1006 struct dcom_get_pipe_state *s = NULL;
1007 struct dcerpc_pipe *p = NULL;
1008
1009 s = talloc_get_type(c->private_data, struct dcom_get_pipe_state);
1010 p = s->p = s->ox->pipe;
1011
1012 if (!GUID_equal(&p->syntax.uuid, &s->iface->vtable->iid))
1013 {
1014 s->ox->pipe->syntax.uuid = s->iface->vtable->iid;
1015
1016 new_ctx = dcerpc_alter_context_send(p, c,
1017 &idl_iface_by_uuid(&s->iface->vtable->iid)->syntax_id,
1018 &p->transfer_syntax);
1019 if (composite_nomem(new_ctx, c)) return;
1020
1021 composite_continue(c, new_ctx, reuse_existing_pipe_continue, c);
1022 }
1023 else
1024 {
1025 c->status = NT_STATUS_OK;
1026 composite_done(c);
1027 }
1028 }
1029
1030 /* forward reference */
1031 static void try_next_binding(struct composite_context *c,
1032 struct dcom_get_pipe_state *s);
1033
1034 /*
1035 * Continues a new pipe binding request by determining if the specific binding
1036 * attempt was successful.
1037 */
bind_new_pipe_continue(struct composite_context * ctx)1038 static void bind_new_pipe_continue(struct composite_context *ctx)
1039 {
1040 struct composite_context *c = NULL;
1041 struct dcom_get_pipe_state *s = NULL;
1042 struct dcerpc_pipe *p = NULL;
1043 const struct STRINGBINDING *sb = NULL;
1044 NTSTATUS status;
1045
1046 c = talloc_get_type(ctx->async.private_data, struct composite_context);
1047 s = talloc_get_type(c->private_data, struct dcom_get_pipe_state);
1048 sb = s->ox->bindings->stringbindings[s->current_binding_offset];
1049
1050 status = dcerpc_pipe_connect_b_recv(ctx, c, &p);
1051 if (!NT_STATUS_IS_OK(status))
1052 {
1053 DEBUG(9, ("Unable to bind to %s: %s\n", sb->NetworkAddr,
1054 nt_errstr(status)));
1055 try_next_binding(c, s);
1056 }
1057 else
1058 {
1059 DEBUG(9, ("bind_new_pipe_continue: successfully bound to %s\n",
1060 dcerpc_binding_string(c, p->binding)));
1061 s->p = p;
1062 composite_done(c);
1063 }
1064 }
1065
1066 /*
1067 * Try to bind to the next available STRINGBINDING until we finally run out of
1068 * them.
1069 */
try_next_binding(struct composite_context * c,struct dcom_get_pipe_state * s)1070 static void try_next_binding(struct composite_context *c,
1071 struct dcom_get_pipe_state *s)
1072 {
1073 struct STRINGBINDING **bindings = s->ox->bindings->stringbindings;
1074 struct STRINGBINDING *sb = NULL;
1075 int try_index = -1;
1076
1077 /* repeat our loop until we reach the end OR we find a binding to try */
1078 while (bindings[s->current_binding_index++] != NULL)
1079 {
1080 /* wrap the offset around if we've reached the end of the bindings */
1081 if (bindings[++s->current_binding_offset] == NULL)
1082 s->current_binding_offset = 0;
1083
1084 sb = bindings[s->current_binding_offset];
1085
1086 /* let's try the similar binding as soon as we see it */
1087 if (s->current_binding_offset == s->similar_binding_index)
1088 {
1089 try_index = s->current_binding_offset;
1090 break;
1091 }
1092
1093 /*
1094 * otherwise, if we've got a non-TCP/IP binding let's just skip it
1095 */
1096 if (sb->wTowerId != EPM_PROTOCOL_TCP)
1097 {
1098 DEBUG(3, ("dcom_get_pipe: Skipping binding %s\n", sb->NetworkAddr));
1099 continue;
1100 }
1101
1102 /* hey, a binding to try! */
1103 try_index = s->current_binding_offset;
1104 break;
1105 }
1106
1107 if (try_index != -1)
1108 {
1109 struct composite_context *new_ctx = NULL;
1110 struct dcerpc_binding *binding = NULL;
1111 NTSTATUS status;
1112
1113 DEBUG(9, ("dcom_get_pipe: Trying binding %s\n", sb->NetworkAddr));
1114 status = dcerpc_binding_from_STRINGBINDING(s->iface->ctx, &binding, sb);
1115 if (!NT_STATUS_IS_OK(status))
1116 {
1117 DEBUG(1, ("Error parsing string binding %s: %s\n", sb->NetworkAddr,
1118 nt_errstr(status)));
1119 try_next_binding(c, s);
1120 }
1121 else
1122 {
1123 binding->flags |= DCERPC_AUTH_NTLM | DCERPC_SIGN;
1124 if (DEBUGLVL(9)) binding->flags |= DCERPC_DEBUG_PRINT_BOTH;
1125
1126 new_ctx = dcerpc_pipe_connect_b_send(c, binding,
1127 idl_iface_by_uuid(&s->iface->obj.iid),
1128 dcom_get_server_credentials(s->iface->ctx, binding->host),
1129 s->iface->ctx->event_ctx);
1130
1131 if (!composite_nomem(new_ctx, c))
1132 composite_continue(c, new_ctx, bind_new_pipe_continue, c);
1133 }
1134 }
1135 else
1136 {
1137 /* no more bindings left and we never connected, so error time */
1138 /* NOTE: this should effectively never happen */
1139 composite_error(c, NT_STATUS_INVALID_ADDRESS);
1140 }
1141 }
1142
1143 /*
1144 * Allocate a new DCE/RPC pipe by finding the best binding that matches our
1145 * previous connection attempts.
1146 */
bind_new_pipe(struct composite_context * c)1147 static void bind_new_pipe(struct composite_context *c)
1148 {
1149 struct dcom_get_pipe_state *s = NULL;
1150 struct dcerpc_binding *binding = NULL;
1151 const char *host = NULL;
1152 int similar_index = -1;
1153 NTSTATUS status;
1154
1155 s = talloc_get_type(c->private_data, struct dcom_get_pipe_state);
1156
1157 /*
1158 * First, try and find a similar STRINGBINDING by comparing the specified
1159 * hostname with that the server has provided us. We parse the hostname as
1160 * a binding string first to make sure we have a consistent hostname format
1161 * without any binding prefix.
1162 */
1163 host = s->ox->host;
1164 status = dcerpc_parse_binding(c, host, &binding);
1165 if (NT_STATUS_IS_OK(status))
1166 {
1167 host = talloc_strdup(c, binding->host);
1168 talloc_free(binding);
1169 }
1170 similar_index = find_similar_binding(s->ox->bindings->stringbindings, host);
1171 DEBUG(1, (__location__": dcom_get_pipe: host=%s, similar=%s\n", host,
1172 s->ox->bindings->stringbindings[similar_index] ? s->ox->bindings->stringbindings[similar_index]->NetworkAddr : "None"));
1173
1174 /*
1175 * Second, set the initial values for the binding offset and index and begin
1176 * the search for a binding that answers. The current_binding_index
1177 * represents an index from 0 until we hit the maximum string binding, but
1178 * the current_binding_offset starts at the similar_index first and then
1179 * wraps as needed. This approach gives us one pass through the
1180 * stringbindings but starts where we think is best.
1181 */
1182 s->similar_binding_index = similar_index;
1183 s->current_binding_offset = similar_index - 1;
1184 s->current_binding_index = 0;
1185
1186 try_next_binding(c, s);
1187 }
1188
1189 /*
1190 * Asynchronously request a DCERPC pipe for the specified interface pointer.
1191 */
dcom_get_pipe_send(struct IUnknown * d,TALLOC_CTX * parent_ctx)1192 struct composite_context *dcom_get_pipe_send(struct IUnknown *d,
1193 TALLOC_CTX *parent_ctx)
1194 {
1195 struct composite_context *c = NULL;
1196 struct dcom_get_pipe_state *s = NULL;
1197
1198 /* create a new composite to use for this call sequence */
1199 c = composite_create(0, d->ctx->event_ctx);
1200 if (c == NULL) return NULL;
1201
1202 s = talloc_zero(c, struct dcom_get_pipe_state);
1203 if (composite_nomem(s, c)) return c;
1204 c->private_data = s;
1205 s->iface = d;
1206
1207 /* get the local object exporter for this IUnknown */
1208 s->ox = object_exporter_by_oxid(s->iface->ctx,
1209 s->iface->obj.u_objref.u_standard.std.oxid);
1210 if (s->ox == NULL)
1211 {
1212 DEBUG(0, ("dcom_get_pipe: OXID not found\n"));
1213 composite_error(c, NT_STATUS_NOT_SUPPORTED);
1214 return c;
1215 }
1216
1217 /* release the existing pipe if it is invalid */
1218 if (s->ox->pipe != NULL && s->ox->pipe->last_fault_code)
1219 {
1220 DEBUG(1, ("dcom_get_pipe: pipe's last_fault_code was %08x, freeing\n",
1221 s->ox->pipe->last_fault_code));
1222 talloc_free(s->ox->pipe);
1223 s->ox->pipe = NULL;
1224 }
1225
1226 /* if the object exporter has a valid pipe then reuse it, otherwise... */
1227 if (s->ox->pipe != NULL)
1228 {
1229 reuse_existing_pipe(c);
1230 }
1231 else
1232 {
1233 bind_new_pipe(c);
1234 }
1235
1236 return c;
1237 }
1238
1239 /*
1240 * Receive the results of an asynchronous request to get a DCERPC pipe.
1241 */
dcom_get_pipe_recv(struct composite_context * c,TALLOC_CTX * parent_ctx,struct dcerpc_pipe ** pp)1242 NTSTATUS dcom_get_pipe_recv(struct composite_context *c,
1243 TALLOC_CTX *parent_ctx, struct dcerpc_pipe **pp)
1244 {
1245 struct dcom_get_pipe_state *s = NULL;
1246 NTSTATUS status;
1247
1248 *pp = NULL;
1249
1250 status = composite_wait(c);
1251 if (NT_STATUS_IS_OK(status))
1252 {
1253 s = talloc_get_type(c->private_data, struct dcom_get_pipe_state);
1254
1255 /*
1256 * let the caller own it, but also let the object exporter own a
1257 * reference as well
1258 */
1259 talloc_steal(parent_ctx, s->p);
1260 *pp = s->p;
1261
1262 talloc_reference(s->ox, s->p);
1263 s->ox->pipe = s->p;
1264 }
1265
1266 talloc_free(c);
1267 return status;
1268 }
1269
1270 /* FIXME:avg put IUnknown_Release_out into header */
1271 struct IUnknown_Release_out
1272 {
1273 uint32_t result;
1274 };
1275
dcom_release_continue(struct composite_context * cr)1276 void dcom_release_continue(struct composite_context *cr)
1277 {
1278 struct composite_context *c;
1279 struct IUnknown *d;
1280 struct IUnknown_Release_out *out;
1281 WERROR r;
1282
1283 c = talloc_get_type(cr->async.private_data, struct composite_context);
1284 d = c->private_data;
1285 r = IRemUnknown_RemRelease_recv(cr);
1286 talloc_free(d);
1287 out = talloc_zero(c, struct IUnknown_Release_out);
1288 out->result = W_ERROR_V(r);
1289 c->private_data = out;
1290 composite_done(c);
1291 }
1292
dcom_release_send(struct IUnknown * d,TALLOC_CTX * mem_ctx)1293 struct composite_context *dcom_release_send(struct IUnknown *d,
1294 TALLOC_CTX *mem_ctx)
1295 {
1296 struct composite_context *c, *cr;
1297 struct REMINTERFACEREF iref;
1298 struct dcom_object_exporter *ox;
1299
1300 c = composite_create(d->ctx, d->ctx->event_ctx);
1301 if (c == NULL)
1302 return NULL;
1303 c->private_data = d;
1304
1305 ox = object_exporter_by_ip(d->ctx, d);
1306 iref.ipid = IUnknown_ipid(d);
1307 iref.cPublicRefs = 5;
1308 iref.cPrivateRefs = 0;
1309 cr = IRemUnknown_RemRelease_send(ox->rem_unknown, mem_ctx, 1, &iref);
1310
1311 composite_continue(c, cr, dcom_release_continue, c);
1312 return c;
1313 }
1314
dcom_release_recv(struct composite_context * c)1315 uint32_t dcom_release_recv(struct composite_context *c)
1316 {
1317 NTSTATUS status;
1318 WERROR r;
1319
1320 status = composite_wait(c);
1321 if (!NT_STATUS_IS_OK(status))
1322 r = ntstatus_to_werror(status);
1323 else
1324 W_ERROR_V(r) = ((struct IUnknown_Release_out *) c->private_data)->result;
1325 talloc_free(c);
1326 return W_ERROR_IS_OK(r) ? 0 : W_ERROR_V(r);
1327 }
1328
dcom_release(void * interface,TALLOC_CTX * mem_ctx)1329 uint32_t dcom_release(void *interface, TALLOC_CTX *mem_ctx)
1330 {
1331 struct composite_context *c;
1332
1333 c = dcom_release_send(interface, mem_ctx);
1334 return dcom_release_recv(c);
1335 }
1336
dcom_proxy_async_call_recv_pipe_send_rpc(struct composite_context * c_pipe)1337 void dcom_proxy_async_call_recv_pipe_send_rpc(struct composite_context *c_pipe)
1338 {
1339 struct composite_context *c;
1340 struct dcom_proxy_async_call_state *s;
1341 struct dcerpc_pipe *p;
1342 struct rpc_request *req;
1343 NTSTATUS status;
1344
1345 c = c_pipe->async.private_data;
1346 s = talloc_get_type(c->private_data, struct dcom_proxy_async_call_state);
1347
1348 status = dcom_get_pipe_recv(c_pipe, c, &p);
1349 if (!NT_STATUS_IS_OK(status))
1350 {
1351 composite_error(c, NT_STATUS_RPC_NT_CALL_FAILED);
1352 return;
1353 }
1354
1355 req = dcerpc_ndr_request_send(p, &s->d->obj.u_objref.u_standard.std.ipid,
1356 s->table, s->opnum, s, s->r);
1357 composite_continue_rpc(c, req, s->continuation, c);
1358 }
1359
1360