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