d69a8f6a32148472071852ad45738e825723a5b6
[jelmer/samba4-debian.git] / source / lib / com / dcom / main.c
1 /*
2    Unix SMB/CIFS implementation.
3    Main DCOM functionality
4    Copyright (C) 2004 Jelmer Vernooij <jelmer@samba.org>
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "librpc/gen_ndr/ndr_epmapper.h"
24 #include "librpc/gen_ndr/ndr_remact.h"
25 #include "librpc/gen_ndr/com_dcom.h"
26 #include "lib/com/dcom/dcom.h"
27 #include "librpc/rpc/dcerpc_table.h"
28
29 #define DCOM_NEGOTIATED_PROTOCOLS { EPM_PROTOCOL_TCP, EPM_PROTOCOL_SMB, EPM_PROTOCOL_NCALRPC }
30
31 struct dcom_client_context *dcom_client_init(struct com_context *ctx, struct cli_credentials *credentials)
32 {
33         ctx->dcom = talloc(ctx, struct dcom_client_context);
34         ctx->dcom->credentials = credentials;
35
36         return ctx->dcom;
37 }
38
39 static NTSTATUS dcerpc_binding_from_STRINGBINDING(TALLOC_CTX *mem_ctx, struct dcerpc_binding **b_out, struct STRINGBINDING *bd)
40 {
41         char *host, *endpoint;
42         struct dcerpc_binding *b;
43
44         b = talloc_zero(mem_ctx, struct dcerpc_binding);
45         if (!b) {
46                 return NT_STATUS_NO_MEMORY;
47         }
48         
49         b->transport = dcerpc_transport_by_endpoint_protocol(bd->wTowerId);
50
51         if (b->transport == -1) {
52                 DEBUG(1, ("Can't find transport match endpoint protocol %d\n", bd->wTowerId));
53                 return NT_STATUS_NOT_SUPPORTED;
54         }
55
56         host = talloc_strdup(b, bd->NetworkAddr);
57         endpoint = strchr(host, '[');
58
59         if (endpoint) {
60                 *endpoint = '\0';
61                 endpoint++;
62
63                 endpoint[strlen(endpoint)-1] = '\0';
64         }
65
66         b->host = host;
67         b->endpoint = talloc_strdup(b, endpoint);
68
69         *b_out = b;
70         return NT_STATUS_OK;
71 }
72
73 static NTSTATUS dcom_connect_host(struct com_context *ctx, struct dcerpc_pipe **p, const char *server)
74 {
75         struct dcerpc_binding *bd;
76         const char * available_transports[] = { "ncacn_ip_tcp", "ncacn_np" };
77         int i;
78         NTSTATUS status;
79         TALLOC_CTX *mem_ctx = talloc_init("dcom_connect");
80
81         if (server == NULL) { 
82                 return dcerpc_pipe_connect(ctx, p, "ncalrpc", 
83                                                                    &dcerpc_table_IRemoteActivation,
84                                                                    ctx->dcom->credentials, ctx->event_ctx);
85         }
86
87         /* Allow server name to contain a binding string */
88         if (NT_STATUS_IS_OK(dcerpc_parse_binding(mem_ctx, server, &bd))) {
89                 status = dcerpc_pipe_connect_b(ctx, p, bd, 
90                                                                            &dcerpc_table_IRemoteActivation,
91                                                                    ctx->dcom->credentials, ctx->event_ctx);
92
93                 talloc_free(mem_ctx);
94                 return status;
95         }
96
97         for (i = 0; i < ARRAY_SIZE(available_transports); i++)
98         {
99                 char *binding = talloc_asprintf(mem_ctx, "%s:%s", available_transports[i], server);
100                 if (!binding) {
101                         talloc_free(mem_ctx);
102                         return NT_STATUS_NO_MEMORY;
103                 }
104                 
105                 status = dcerpc_pipe_connect(ctx, p, binding, 
106                                                                          &dcerpc_table_IRemoteActivation,
107                                              ctx->dcom->credentials, ctx->event_ctx);
108
109                 if (NT_STATUS_IS_OK(status)) {
110                         talloc_free(mem_ctx);
111                         return status;
112                 }
113         }
114         
115         talloc_free(mem_ctx);
116         return status;
117 }
118
119 struct dcom_object_exporter *object_exporter_by_oxid(struct com_context *ctx, uint64_t oxid)
120 {
121         struct dcom_object_exporter *ox;
122         for (ox = ctx->dcom->object_exporters; ox; ox = ox->next) {
123                 if (ox->oxid == oxid) {
124                         return ox;
125                 }
126         }
127
128         return NULL; 
129 }
130
131 struct dcom_object_exporter *object_exporter_by_ip(struct com_context *ctx, struct IUnknown *ip)
132 {
133         return NULL; /* FIXME */
134 }
135
136 WERROR dcom_create_object(struct com_context *ctx, struct GUID *clsid, const char *server, int num_ifaces, struct GUID *iid, struct IUnknown ***ip, WERROR *results)
137 {
138         uint16_t protseq[] = DCOM_NEGOTIATED_PROTOCOLS;
139         struct dcerpc_pipe *p;
140         struct dcom_object_exporter *m;
141         NTSTATUS status;
142         struct RemoteActivation r;
143         struct DUALSTRINGARRAY dualstring;
144         int i;
145
146         status = dcom_connect_host(ctx, &p, server);
147         if (NT_STATUS_IS_ERR(status)) {
148                 DEBUG(1, ("Unable to connect to %s - %s\n", server, nt_errstr(status)));
149                 return ntstatus_to_werror(status);
150         }
151
152         ZERO_STRUCT(r.in);
153         r.in.this.version.MajorVersion = COM_MAJOR_VERSION;
154         r.in.this.version.MinorVersion = COM_MINOR_VERSION;
155         r.in.this.cid = GUID_random();
156         r.in.Clsid = *clsid;
157         r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
158         r.in.num_protseqs = ARRAY_SIZE(protseq);
159         r.in.protseq = protseq;
160         r.in.Interfaces = num_ifaces;
161         r.in.pIIDs = iid;
162         r.out.ifaces = talloc_array(ctx, struct MInterfacePointer *, num_ifaces);
163         r.out.pdsaOxidBindings = &dualstring;
164         
165         status = dcerpc_RemoteActivation(p, ctx, &r);
166         if(NT_STATUS_IS_ERR(status)) {
167                 DEBUG(1, ("Error while running RemoteActivation %s\n", nt_errstr(status)));
168                 return ntstatus_to_werror(status);
169         }
170
171         if(!W_ERROR_IS_OK(r.out.result)) {
172                 return r.out.result; 
173         }
174         
175         if(!W_ERROR_IS_OK(r.out.hr)) { 
176                 return r.out.hr; 
177         }
178
179         *ip = talloc_array(ctx, struct IUnknown *, num_ifaces);
180         for (i = 0; i < num_ifaces; i++) {
181                 results[i] = r.out.results[i];
182                 (*ip)[i] = NULL;
183                 if (W_ERROR_IS_OK(results[i])) {
184                         status = dcom_IUnknown_from_OBJREF(ctx, &(*ip)[i], &r.out.ifaces[i]->obj);
185                         if (!NT_STATUS_IS_OK(status)) {
186                                 results[i] = ntstatus_to_werror(status);
187                         }
188                 }
189         }
190
191         /* Add the OXID data for the returned oxid */
192         m = object_exporter_by_oxid(ctx, r.out.pOxid);
193         m->bindings = *r.out.pdsaOxidBindings;
194         
195         return WERR_OK;
196 }
197
198 WERROR dcom_get_class_object(struct com_context *ctx, struct GUID *clsid, const char *server, struct GUID *iid, struct IUnknown **ip)
199 {
200         struct dcom_object_exporter *m;
201         struct RemoteActivation r;
202         struct dcerpc_pipe *p;
203         struct DUALSTRINGARRAY dualstring;
204         NTSTATUS status;
205         struct MInterfacePointer pm;
206         struct MInterfacePointer *ifaces[1];
207         uint16_t protseq[] = DCOM_NEGOTIATED_PROTOCOLS;
208
209         if (!server) {
210                 return com_get_class_object(ctx, clsid, iid, ip);
211         }
212
213         status = dcom_connect_host(ctx, &p, server);
214         if (NT_STATUS_IS_ERR(status)) {
215                 DEBUG(1, ("Unable to connect to %s - %s\n", server, nt_errstr(status)));
216                 return ntstatus_to_werror(status);
217         }
218
219         ZERO_STRUCT(r.in);
220         r.in.this.version.MajorVersion = COM_MAJOR_VERSION;
221         r.in.this.version.MinorVersion = COM_MINOR_VERSION;
222         r.in.this.cid = GUID_random();
223         r.in.Clsid = *clsid;
224         r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
225         r.in.num_protseqs = ARRAY_SIZE(protseq);
226         r.in.protseq = protseq;
227         r.in.Interfaces = 1;
228         r.in.pIIDs = iid;
229         r.in.Mode = MODE_GET_CLASS_OBJECT;
230         r.out.ifaces = ifaces;
231         ifaces[0] = &pm;
232         r.out.pdsaOxidBindings = &dualstring;
233
234         status = dcerpc_RemoteActivation(p, ctx, &r);
235         if(NT_STATUS_IS_ERR(status)) {
236                 DEBUG(1, ("Error while running RemoteActivation - %s\n", nt_errstr(status)));
237                 return ntstatus_to_werror(status);
238         }
239
240         if(!W_ERROR_IS_OK(r.out.result)) { return r.out.result; }
241         if(!W_ERROR_IS_OK(r.out.hr)) { return r.out.hr; }
242         if(!W_ERROR_IS_OK(r.out.results[0])) { return r.out.results[0]; }
243         
244         /* Set up the interface data */
245         dcom_IUnknown_from_OBJREF(ctx, ip, &pm.obj);
246         
247         /* Add the OXID data for the returned oxid */
248         m = object_exporter_by_oxid(ctx, r.out.pOxid);
249         m->bindings = *r.out.pdsaOxidBindings;
250
251         return WERR_OK;
252 }
253
254 NTSTATUS dcom_get_pipe(struct IUnknown *iface, struct dcerpc_pipe **pp)
255 {
256         struct dcerpc_binding *binding;
257         struct GUID iid;
258         uint64_t oxid;
259         NTSTATUS status;
260         int i;
261         struct dcerpc_pipe *p;
262         TALLOC_CTX *tmp_ctx;
263         struct dcom_object_exporter *ox;
264
265         ox = object_exporter_by_ip(iface->ctx, iface);
266
267         tmp_ctx = talloc_new(NULL);
268
269         p = ox->pipe;
270         
271         iid = iface->vtable->iid;
272
273         if (p) {
274                 if (!GUID_equal(&p->syntax.uuid, &iid)) {
275                         struct dcerpc_pipe *p2;
276                         ox->pipe->syntax.uuid = iid;
277
278                         /* interface will always be present, so 
279                          * idl_iface_by_uuid can't return NULL */
280                         status = dcerpc_secondary_context(p, &p2, idl_iface_by_uuid(&iid));
281
282                         if (NT_STATUS_IS_OK(status)) {
283                                 p = p2;
284                         }
285                 } else {
286                         p = talloc_reference(NULL, p);
287                 }
288                 *pp = p;
289                 talloc_free(tmp_ctx);
290                 return status;
291         }
292
293         i = 0;
294         do {
295                 status = dcerpc_binding_from_STRINGBINDING(iface->ctx, &binding, 
296                                                            ox->bindings.stringbindings[i]);
297                 if (!NT_STATUS_IS_OK(status)) {
298                         DEBUG(1, ("Error parsing string binding"));
299                 } else {
300                         status = dcerpc_pipe_connect_b(NULL, &p, binding, 
301                                                        idl_iface_by_uuid(&iid),
302                                                        iface->ctx->dcom->credentials,
303                                                            iface->ctx->event_ctx);
304                 }
305                 talloc_free(binding);
306                 i++;
307         } while (!NT_STATUS_IS_OK(status) && ox->bindings.stringbindings[i]);
308
309         if (NT_STATUS_IS_ERR(status)) {
310                 DEBUG(0, ("Unable to connect to remote host - %s\n", nt_errstr(status)));
311                 talloc_free(tmp_ctx);
312                 return status;
313         }
314
315         DEBUG(2, ("Successfully connected to OXID %llx\n", (long long)oxid));
316         
317         *pp = p;
318         talloc_free(tmp_ctx);
319
320         return NT_STATUS_OK;
321 }
322
323 NTSTATUS dcom_OBJREF_from_IUnknown(struct OBJREF *o, struct IUnknown *p)
324 {
325         /* FIXME: Cache generated objref objects? */
326         ZERO_STRUCTP(o);
327         
328         o->signature = OBJREF_SIGNATURE;
329         
330         if (!p) {
331                 o->flags = OBJREF_NULL;
332         } else {
333                 o->iid = p->vtable->iid;
334                 /* 
335                 OBJREF_STANDARD
336                 OBJREF_CUSTOM
337                 OBJREF_HANDLER
338                 */
339         }
340
341         return NT_STATUS_NOT_IMPLEMENTED;       
342 }
343
344 NTSTATUS dcom_IUnknown_from_OBJREF(struct com_context *ctx, struct IUnknown **_p, struct OBJREF *o)
345 {
346         struct IUnknown *p;
347         struct dcom_object_exporter *ox;
348
349         switch(o->flags) {
350         case OBJREF_NULL: 
351                 *_p = NULL;
352                 return NT_STATUS_OK;
353                 
354         case OBJREF_STANDARD:
355                 p = talloc(ctx, struct IUnknown);
356                 p->ctx = ctx;   
357                 p->vtable = dcom_proxy_vtable_by_iid(&o->iid);
358                 if (!p->vtable) {
359                         DEBUG(0, ("Unable to find proxy class for interface with IID %s\n", GUID_string(ctx, &o->iid)));
360                         return NT_STATUS_NOT_SUPPORTED;
361                 }
362
363                 ox = object_exporter_by_oxid(ctx, o->u_objref.u_standard.std.oxid);
364                 /* FIXME: Add object to list of objects to ping */
365                 *_p = p;
366                 return NT_STATUS_OK;
367                 
368         case OBJREF_HANDLER:
369                 p = talloc(ctx, struct IUnknown);
370                 p->ctx = ctx;   
371                 ox = object_exporter_by_oxid(ctx, o->u_objref.u_handler.std.oxid );
372                 /* FIXME: Add object to list of objects to ping */
373 /*FIXME         p->vtable = dcom_vtable_by_clsid(&o->u_objref.u_handler.clsid);*/
374                 /* FIXME: Do the custom unmarshaling call */
375         
376                 *_p = p;
377                 return NT_STATUS_OK;
378                 
379         case OBJREF_CUSTOM:
380                 p = talloc(ctx, struct IUnknown);
381                 p->ctx = ctx;   
382                 p->vtable = NULL;
383                 /* FIXME: Do the actual custom unmarshaling call */
384                 *_p = p;
385                 return NT_STATUS_NOT_SUPPORTED;
386         }
387
388         return NT_STATUS_NOT_SUPPORTED;
389 }
390
391 uint64_t dcom_get_current_oxid(void)
392 {
393         return getpid();
394 }