r5672: Use switch_type() and the token storage mechanism for unions:
[samba.git] / source4 / lib / dcom / common / 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
29 #define DCOM_NEGOTIATED_PROTOCOLS { EPM_PROTOCOL_TCP, EPM_PROTOCOL_SMB, EPM_PROTOCOL_NCALRPC }
30
31 static NTSTATUS dcerpc_binding_from_STRINGBINDING(TALLOC_CTX *mem_ctx, struct dcerpc_binding *b, struct STRINGBINDING *bd)
32 {
33         char *host, *endpoint;
34
35         ZERO_STRUCTP(b);
36         
37         b->transport = dcerpc_transport_by_endpoint_protocol(bd->wTowerId);
38
39         if (b->transport == -1) {
40                 DEBUG(1, ("Can't find transport match endpoint protocol %d\n", bd->wTowerId));
41                 return NT_STATUS_NOT_SUPPORTED;
42         }
43
44         host = talloc_strdup(mem_ctx, bd->NetworkAddr);
45         endpoint = strchr(host, '[');
46
47         if (endpoint) {
48                 *endpoint = '\0';
49                 endpoint++;
50
51                 endpoint[strlen(endpoint)-1] = '\0';
52         }
53
54         b->host = host;
55         b->endpoint = endpoint;
56
57         return NT_STATUS_OK;
58 }
59
60 static NTSTATUS dcom_connect_host(struct dcom_context *ctx, struct dcerpc_pipe **p, const char *server)
61 {
62         struct dcerpc_binding bd;
63         enum dcerpc_transport_t available_transports[] = { NCACN_IP_TCP, NCACN_NP };
64         int i;
65         NTSTATUS status;
66         TALLOC_CTX *mem_ctx = talloc_init("dcom_connect");
67
68         /* Allow server name to contain a binding string */
69         if (NT_STATUS_IS_OK(dcerpc_parse_binding(mem_ctx, server, &bd))) {
70                 status = dcerpc_pipe_connect_b(p, &bd, 
71                                         DCERPC_IREMOTEACTIVATION_UUID, 
72                                         DCERPC_IREMOTEACTIVATION_VERSION, 
73                                         ctx->domain, ctx->user, ctx->password);
74
75                 talloc_free(mem_ctx);
76                 return status;
77         }
78         talloc_free(mem_ctx);
79
80         ZERO_STRUCT(bd);
81         bd.host = server;
82         
83         if (server == NULL) { 
84                 bd.transport = NCALRPC; 
85                 return dcerpc_pipe_connect_b(p, &bd, 
86                                         DCERPC_IREMOTEACTIVATION_UUID, 
87                                         DCERPC_IREMOTEACTIVATION_VERSION, 
88                                         ctx->domain, ctx->user, ctx->password);
89         }
90
91         for (i = 0; i < ARRAY_SIZE(available_transports); i++)
92         {
93                 bd.transport = available_transports[i];
94                 
95                 status = dcerpc_pipe_connect_b(p, &bd, 
96                                                 DCERPC_IREMOTEACTIVATION_UUID, 
97                                                 DCERPC_IREMOTEACTIVATION_VERSION, 
98                                                 ctx->domain, ctx->user, ctx->password);
99
100                 if (NT_STATUS_IS_OK(status)) {
101                         return status;
102                 }
103         }
104         
105         return status;
106 }
107
108 WERROR dcom_init(struct dcom_context **ctx, const char *domain, const char *user, const char *pass)
109 {
110         *ctx = talloc(NULL, struct dcom_context);
111         (*ctx)->oxids = NULL;
112         (*ctx)->domain = talloc_strdup(*ctx, domain);
113         (*ctx)->user = talloc_strdup(*ctx, user);
114         (*ctx)->password = talloc_strdup(*ctx, pass);
115         (*ctx)->dcerpc_flags = 0;
116         
117         return WERR_OK;
118 }
119
120 struct dcom_object_exporter *oxid_mapping_by_oxid (struct dcom_context *ctx, uint64_t oxid)
121 {
122         struct dcom_object_exporter *m;
123         
124         for (m = ctx->oxids;m;m = m->next) {
125                 if (m->oxid     == oxid) {
126                         break;
127                 }
128         }
129
130         /* Add oxid mapping if we couldn't find one */
131         if (!m) {
132                 m = talloc_zero(ctx, struct dcom_object_exporter);
133                 m->oxid = oxid;
134                 DLIST_ADD(ctx->oxids, m);
135         }
136
137         return m;
138 }
139
140 WERROR dcom_ping(struct dcom_context *ctx)
141 {
142         /* FIXME: If OID's waiting in queue, do a ComplexPing call */
143         /* FIXME: otherwise, do a SimplePing call */
144         return WERR_OK;
145 }
146
147 static WERROR dcom_create_object(struct dcom_context *ctx, struct GUID *clsid, const char *server, int num_ifaces, struct GUID *iid, struct dcom_interface_p ***ip, WERROR *results)
148 {
149         uint16_t protseq[] = DCOM_NEGOTIATED_PROTOCOLS;
150         struct dcerpc_pipe *p;
151         struct dcom_object_exporter *m;
152         NTSTATUS status;
153         struct RemoteActivation r;
154         struct DUALSTRINGARRAY dualstring;
155         int i;
156
157         if (!server) {
158                 return com_create_object(ctx, clsid, num_ifaces, iid, ip, results);
159         }
160
161         status = dcom_connect_host(ctx, &p, server);
162         if (NT_STATUS_IS_ERR(status)) {
163                 DEBUG(1, ("Unable to connect to %s - %s\n", server, nt_errstr(status)));
164                 return ntstatus_to_werror(status);
165         }
166
167         ZERO_STRUCT(r.in);
168         r.in.this.version.MajorVersion = COM_MAJOR_VERSION;
169         r.in.this.version.MinorVersion = COM_MINOR_VERSION;
170         r.in.this.cid = GUID_random();
171         r.in.Clsid = *clsid;
172         r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
173         r.in.num_protseqs = ARRAY_SIZE(protseq);
174         r.in.protseq = protseq;
175         r.in.Interfaces = num_ifaces;
176         r.in.pIIDs = iid;
177         r.out.ifaces = talloc_array(ctx, struct pMInterfacePointer, num_ifaces);
178         r.out.pdsaOxidBindings = &dualstring;
179         
180         status = dcerpc_RemoteActivation(p, ctx, &r);
181         if(NT_STATUS_IS_ERR(status)) {
182                 DEBUG(1, ("Error while running RemoteActivation %s\n", nt_errstr(status)));
183                 return ntstatus_to_werror(status);
184         }
185
186         if(!W_ERROR_IS_OK(r.out.result)) {
187                 return r.out.result; 
188         }
189         
190         if(!W_ERROR_IS_OK(r.out.hr)) { 
191                 return r.out.hr; 
192         }
193
194         *ip = talloc_array(ctx, struct dcom_interface_p *, num_ifaces);
195         for (i = 0; i < num_ifaces; i++) {
196                 results[i] = r.out.results[i];
197                 (*ip)[i] = NULL;
198                 if (W_ERROR_IS_OK(results[i])) {
199                         status = dcom_ifacep_from_OBJREF(ctx, &(*ip)[i], &r.out.ifaces[i].p->obj);
200                         if (NT_STATUS_IS_OK(status)) {
201                                 (*ip)[i]->private_references = 1;
202                         } else {
203                                 results[i] = ntstatus_to_werror(status);
204                         }
205                 }
206         }
207
208         /* Add the OXID data for the returned oxid */
209         m = oxid_mapping_by_oxid(ctx, r.out.pOxid);
210         m->bindings = *r.out.pdsaOxidBindings;
211         
212         return WERR_OK;
213 }
214
215 WERROR dcom_get_class_object(struct dcom_context *ctx, struct GUID *clsid, const char *server, struct GUID *iid, struct dcom_interface_p **ip)
216 {
217         struct dcom_object_exporter *m;
218         struct RemoteActivation r;
219         struct dcerpc_pipe *p;
220         struct DUALSTRINGARRAY dualstring;
221         NTSTATUS status;
222         struct pMInterfacePointer pm;
223         uint16_t protseq[] = DCOM_NEGOTIATED_PROTOCOLS;
224
225         if (!server) {
226                 return com_get_class_object(ctx, clsid, iid, ip);
227         }
228
229         status = dcom_connect_host(ctx, &p, server);
230         if (NT_STATUS_IS_ERR(status)) {
231                 DEBUG(1, ("Unable to connect to %s - %s\n", server, nt_errstr(status)));
232                 return ntstatus_to_werror(status);
233         }
234
235         ZERO_STRUCT(r.in);
236         r.in.this.version.MajorVersion = COM_MAJOR_VERSION;
237         r.in.this.version.MinorVersion = COM_MINOR_VERSION;
238         r.in.this.cid = GUID_random();
239         r.in.Clsid = *clsid;
240         r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
241         r.in.num_protseqs = ARRAY_SIZE(protseq);
242         r.in.protseq = protseq;
243         r.in.Interfaces = 1;
244         r.in.pIIDs = iid;
245         r.in.Mode = MODE_GET_CLASS_OBJECT;
246         r.out.ifaces = &pm;
247         r.out.pdsaOxidBindings = &dualstring;
248
249         status = dcerpc_RemoteActivation(p, ctx, &r);
250         if(NT_STATUS_IS_ERR(status)) {
251                 DEBUG(1, ("Error while running RemoteActivation - %s\n", nt_errstr(status)));
252                 return ntstatus_to_werror(status);
253         }
254
255         if(!W_ERROR_IS_OK(r.out.result)) { return r.out.result; }
256         if(!W_ERROR_IS_OK(r.out.hr)) { return r.out.hr; }
257         if(!W_ERROR_IS_OK(r.out.results[0])) { return r.out.results[0]; }
258         
259         /* Set up the interface data */
260         dcom_ifacep_from_OBJREF(ctx, ip, &pm.p->obj);
261         (*ip)->private_references = 1;
262         
263         /* Add the OXID data for the returned oxid */
264         m = oxid_mapping_by_oxid(ctx, r.out.pOxid);
265         m->bindings = *r.out.pdsaOxidBindings;
266
267         return WERR_OK;
268 }
269
270 NTSTATUS dcom_get_pipe (struct dcom_interface_p *iface, struct dcerpc_pipe **pp)
271 {
272         struct dcerpc_binding binding;
273         struct GUID iid;
274         uint64_t oxid;
275         NTSTATUS status;
276         int i;
277         struct dcerpc_pipe *p;
278         TALLOC_CTX *tmp_ctx;
279         const char *uuid;
280
281         tmp_ctx = talloc_new(NULL);
282
283         p = iface->ox->pipe;
284         
285         oxid = iface->ox->oxid;
286         iid = iface->interface->iid;
287
288         uuid = GUID_string(tmp_ctx, &iid);
289         
290         if (p) {
291                 if (!GUID_equal(&p->syntax.uuid, &iid)) {
292                         struct dcerpc_pipe *p2;
293                         iface->ox->pipe->syntax.uuid = iid;
294                         status = dcerpc_secondary_context(p, &p2, uuid, 0);
295                         if (NT_STATUS_IS_OK(status)) {
296                                 p = p2;
297                         }
298                 } else {
299                         p = talloc_reference(NULL, p);
300                 }
301                 *pp = p;
302                 talloc_free(tmp_ctx);
303                 return status;
304         }
305
306         i = 0;
307         do {
308                 status = dcerpc_binding_from_STRINGBINDING(iface->ctx, &binding, 
309                                                            iface->ox->bindings.stringbindings[i]);
310                 if (!NT_STATUS_IS_OK(status)) {
311                         DEBUG(1, ("Error parsing string binding"));
312                 } else {
313                         binding.flags = iface->ctx->dcerpc_flags;
314                         status = dcerpc_pipe_connect_b(&p, &binding, 
315                                                        uuid, 0.0, 
316                                                        iface->ctx->domain, iface->ctx->user, 
317                                                        iface->ctx->password);
318                 }
319
320                 i++;
321         } while (NT_STATUS_IS_ERR(status) && iface->ox->bindings.stringbindings[i]);
322
323         if (NT_STATUS_IS_ERR(status)) {
324                 DEBUG(0, ("Unable to connect to remote host - %s\n", nt_errstr(status)));
325                 talloc_free(tmp_ctx);
326                 return status;
327         }
328
329         DEBUG(2, ("Successfully connected to OXID %llx\n", oxid));
330         
331         *pp = p;
332         talloc_free(tmp_ctx);
333
334         return NT_STATUS_OK;
335 }
336
337 struct dcom_object *dcom_object_by_oid(struct dcom_object_exporter *ox, uint64_t oid)
338 {
339         struct dcom_object *o;
340
341         for (o = ox->objects; o; o = o->next) {
342                 if (o->oid == oid) {
343                         break;
344                 }
345         }
346
347         if (o == NULL) {
348                 o = talloc_zero(ox, struct dcom_object);
349                 o->oid = oid;
350                 DLIST_ADD(ox->objects, o);
351         }
352         
353         return o;
354 }
355
356
357 NTSTATUS dcom_OBJREF_from_ifacep(struct dcom_context *ctx, struct OBJREF *o, struct dcom_interface_p *_p)
358 {
359         return NT_STATUS_NOT_IMPLEMENTED;       
360 }
361
362 NTSTATUS dcom_ifacep_from_OBJREF(struct dcom_context *ctx, struct dcom_interface_p **_p, struct OBJREF *o)
363 {
364         struct dcom_interface_p *p = talloc(ctx, struct dcom_interface_p);
365
366         p->ctx = ctx;   
367         p->interface = dcom_interface_by_iid(&o->iid);
368         if (!p->interface) {
369                 DEBUG(0, ("Unable to find interface with IID %s\n", GUID_string(ctx, &o->iid)));
370                 return NT_STATUS_NOT_SUPPORTED;
371         }
372
373         p->private_references = 0;
374         p->objref_flags = o->flags;
375         
376         switch(p->objref_flags) {
377         case OBJREF_NULL: 
378                 p->object = NULL; 
379                 p->ox = NULL;
380                 p->vtable = dcom_proxy_vtable_by_iid(&p->interface->iid);
381                 ZERO_STRUCT(p->ipid);
382                 *_p = p;
383                 return NT_STATUS_OK;
384                 
385         case OBJREF_STANDARD:
386                 p->ox = oxid_mapping_by_oxid(ctx, o->u_objref.u_standard.std.oxid);
387                 p->ipid = o->u_objref.u_standard.std.ipid;
388                 p->object = dcom_object_by_oid(p->ox, o->u_objref.u_standard.std.oid);
389                 p->ox->resolver_address = o->u_objref.u_standard.saResAddr;
390                 p->vtable = dcom_proxy_vtable_by_iid(&p->interface->iid);
391                 *_p = p;
392                 return NT_STATUS_OK;
393                 
394         case OBJREF_HANDLER:
395                 p->ox = oxid_mapping_by_oxid(ctx, o->u_objref.u_handler.std.oxid );
396                 p->ipid = o->u_objref.u_handler.std.ipid;
397                 p->object = dcom_object_by_oid(p->ox, o->u_objref.u_standard.std.oid);
398                 p->ox->resolver_address = o->u_objref.u_handler.saResAddr;
399 /*FIXME         p->vtable = dcom_vtable_by_clsid(&o->u_objref.u_handler.clsid);*/
400                 /* FIXME: Do the custom unmarshaling call */
401         
402                 *_p = p;
403                 return NT_STATUS_OK;
404                 
405         case OBJREF_CUSTOM:
406                 {
407                         p->vtable = NULL;
408                 
409                 /* FIXME: Do the actual custom unmarshaling call */
410                 p->ox = NULL;
411                 p->object = NULL;
412                 ZERO_STRUCT(p->ipid);
413                 *_p = p;
414                 return NT_STATUS_NOT_SUPPORTED;
415                 }
416         }
417
418         return NT_STATUS_NOT_SUPPORTED;
419
420         
421 #if 0
422         struct dcom_oxid_mapping *m;
423         /* Add OXID mapping if none present yet */
424         if (!m) {
425                 struct dcerpc_pipe *po;
426                 struct ResolveOxid r;
427                 uint16_t protseq[] = DCOM_NEGOTIATED_PROTOCOLS;
428
429                 DEBUG(3, ("No binding data present yet, resolving OXID %llu\n", p->ox->oxid));
430
431                 m = talloc_zero(p->ctx, struct dcom_oxid_mapping);
432                 m->oxid = oxid; 
433
434                 i = 0;
435                 do {
436                         status = dcerpc_binding_from_STRINGBINDING(p->ctx, &binding, p->client.objref->u_objref.u_standard.saResAddr.stringbindings[i]);
437
438                         if (NT_STATUS_IS_OK(status)) {
439                                 binding.flags = iface->ctx->dcerpc_flags;
440                                 status = dcerpc_pipe_connect_b(&po, &binding, DCERPC_IOXIDRESOLVER_UUID, DCERPC_IOXIDRESOLVER_VERSION, iface->ctx->domain, iface->ctx->user, iface->ctx->password);
441                         } else {
442                                 DEBUG(1, ("Error parsing string binding - %s", nt_errstr(status)));
443                         }
444
445                         i++;
446                 } while (!NT_STATUS_IS_OK(status) && iface->client.objref->u_objref.u_standard.saResAddr.stringbindings[i]);
447
448                 if (NT_STATUS_IS_ERR(status)) {
449                         DEBUG(1, ("Error while connecting to OXID Resolver : %s\n", nt_errstr(status)));
450                         return status;
451                 }
452
453                 r.in.pOxid = oxid;
454                 r.in.cRequestedProtseqs = ARRAY_SIZE(protseq);
455                 r.in.arRequestedProtseqs = protseq;
456                 r.out.ppdsaOxidBindings = &m->bindings;
457
458                 status = dcerpc_ResolveOxid(po, iface->ctx, &r);
459                 if (NT_STATUS_IS_ERR(status)) {
460                         DEBUG(1, ("Error while resolving OXID: %s\n", nt_errstr(status)));
461                         return status;
462                 }
463
464                 dcerpc_pipe_close(po);
465
466                 DLIST_ADD(iface->ctx->oxids, m);
467         }
468 #endif
469
470         return NT_STATUS_NOT_SUPPORTED; 
471 }
472
473 uint64_t dcom_get_current_oxid(void)
474 {
475         return getpid();
476 }