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