r3606: More DCOM fixes:
[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 "dlinklist.h"
23 #include "librpc/gen_ndr/ndr_epmapper.h"
24 #include "librpc/gen_ndr/ndr_remact.h"
25 #include "librpc/gen_ndr/ndr_oxidresolver.h"
26 #include "librpc/gen_ndr/ndr_dcom.h"
27
28 #define DCOM_NEGOTIATED_PROTOCOLS { EPM_PROTOCOL_TCP, EPM_PROTOCOL_SMB, EPM_PROTOCOL_NCALRPC }
29
30 struct dcom_oxid_mapping {
31         struct dcom_oxid_mapping *prev, *next;  
32         struct DUALSTRINGARRAY bindings;
33         HYPER_T oxid;
34         struct dcerpc_pipe *pipe;
35 };
36
37 static NTSTATUS dcerpc_binding_from_STRINGBINDING(TALLOC_CTX *mem_ctx, struct dcerpc_binding *b, struct STRINGBINDING *bd)
38 {
39         char *host, *endpoint;
40
41         ZERO_STRUCTP(b);
42         
43         b->transport = dcerpc_transport_by_endpoint_protocol(bd->wTowerId);
44
45         if (b->transport == -1) {
46                 DEBUG(1, ("Can't find transport match endpoint protocol %d\n", bd->wTowerId));
47                 return NT_STATUS_NOT_SUPPORTED;
48         }
49
50         host = talloc_strdup(mem_ctx, bd->NetworkAddr);
51         endpoint = strchr(host, '[');
52
53         if (endpoint) {
54                 *endpoint = '\0';
55                 endpoint++;
56
57                 endpoint[strlen(endpoint)-1] = '\0';
58         }
59
60         b->host = host;
61         b->endpoint = endpoint;
62
63         return NT_STATUS_OK;
64 }
65
66 static NTSTATUS dcom_connect_host(struct dcom_context *ctx, struct dcerpc_pipe **p, const char *server)
67 {
68         struct dcerpc_binding bd;
69         enum dcerpc_transport_t available_transports[] = { NCACN_IP_TCP, NCACN_NP };
70         int i;
71         NTSTATUS status;
72         TALLOC_CTX *mem_ctx = talloc_init("dcom_connect");
73
74         /* Allow server name to contain a binding string */
75         if (NT_STATUS_IS_OK(dcerpc_parse_binding(mem_ctx, server, &bd))) {
76                 status = dcerpc_pipe_connect_b(p, &bd, 
77                                         DCERPC_IREMOTEACTIVATION_UUID, 
78                                         DCERPC_IREMOTEACTIVATION_VERSION, 
79                                         ctx->domain, ctx->user, ctx->password);
80
81                 talloc_destroy(mem_ctx);
82                 return status;
83         }
84         talloc_destroy(mem_ctx);
85
86         ZERO_STRUCT(bd);
87         bd.host = server;
88         
89         if (server == NULL) { 
90                 bd.transport = NCALRPC; 
91                 return dcerpc_pipe_connect_b(p, &bd, 
92                                         DCERPC_IREMOTEACTIVATION_UUID, 
93                                         DCERPC_IREMOTEACTIVATION_VERSION, 
94                                         ctx->domain, ctx->user, ctx->password);
95         }
96
97         for (i = 0; i < ARRAY_SIZE(available_transports); i++)
98         {
99                 bd.transport = available_transports[i];
100                 
101                 status = dcerpc_pipe_connect_b(p, &bd, 
102                                                 DCERPC_IREMOTEACTIVATION_UUID, 
103                                                 DCERPC_IREMOTEACTIVATION_VERSION, 
104                                                 ctx->domain, ctx->user, ctx->password);
105
106                 if (NT_STATUS_IS_OK(status)) {
107                         return status;
108                 }
109         }
110         
111         return status;
112 }
113
114 NTSTATUS dcerpc_IUnknown_AddRef(struct dcom_interface *p, TALLOC_CTX *mem_ctx, struct IUnknown_AddRef *rr) 
115 {
116         struct RemAddRef r;
117         struct REMINTERFACEREF ref;
118         
119         /* This is rather inefficient, but we'll patch it up later */
120         r.in.cInterfaceRefs = 1;
121         r.in.InterfaceRefs = &ref;
122
123         return dcerpc_RemAddRef(p, mem_ctx, &r);
124 }
125
126 NTSTATUS dcerpc_IUnknown_Release(struct dcom_interface *p, TALLOC_CTX *mem_ctx, struct IUnknown_Release *rr)
127 {
128         struct RemRelease r;
129         struct REMINTERFACEREF ref;
130
131         p->private_references--;
132
133         /* Only do the remote version of this call when all local references have 
134          * been released */
135         if (p->private_references == 0) {
136                 NTSTATUS status;
137                 r.in.cInterfaceRefs = 1;
138                 r.in.InterfaceRefs = &ref;
139
140                 status = dcerpc_RemRelease(p, mem_ctx, &r);
141                 
142                 if (NT_STATUS_IS_OK(status)) {
143                         talloc_destroy(p);      
144                 }
145
146                 return status;
147         }
148
149         return NT_STATUS_OK;
150 }
151
152 NTSTATUS dcerpc_IUnknown_QueryInterface(struct dcom_interface *o, TALLOC_CTX *mem_ctx, struct IUnknown_QueryInterface *rr)
153 {
154         /* FIXME: Ask local server for interface pointer. Local server can then 
155          * call RemQueryInterface if necessary */
156         return NT_STATUS_NOT_SUPPORTED;
157 }
158
159 WERROR dcom_init(struct dcom_context **ctx, const char *domain, const char *user, const char *pass)
160 {
161         *ctx = talloc_p(NULL, struct dcom_context);
162         (*ctx)->oxids = NULL;
163         (*ctx)->domain = talloc_strdup(*ctx, domain);
164         (*ctx)->user = talloc_strdup(*ctx, user);
165         (*ctx)->password = talloc_strdup(*ctx, pass);
166         
167         return WERR_OK;
168 }
169
170 WERROR dcom_create_object(struct dcom_context *ctx, struct GUID *clsid, const char *server, int num_ifaces, struct GUID *iid, struct dcom_interface **ip, WERROR *results)
171 {
172         struct dcom_oxid_mapping *m;
173         struct RemoteActivation r;
174         int i;
175         struct dcerpc_pipe *p;
176         NTSTATUS status;
177         uint16 protseq[] = DCOM_NEGOTIATED_PROTOCOLS;
178
179         status = dcom_connect_host(ctx, &p, server);
180         if (NT_STATUS_IS_ERR(status)) {
181                 DEBUG(1, ("Unable to connect to %s - %s\n", server, nt_errstr(status)));
182                 return ntstatus_to_werror(status);
183         }
184
185         ZERO_STRUCT(r.in);
186         r.in.this.version.MajorVersion = 5;
187         r.in.this.version.MinorVersion = 1;
188         uuid_generate_random(&r.in.this.cid);
189         r.in.Clsid = *clsid;
190         r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
191         r.in.num_protseqs = ARRAY_SIZE(protseq);
192         r.in.protseq = protseq;
193         r.in.Interfaces = num_ifaces;
194         r.in.pIIDs = iid;
195         r.out.ifaces = talloc_array_p(ctx, struct pMInterfacePointer, num_ifaces);
196         m = talloc_zero_p(ctx, struct dcom_oxid_mapping);
197         r.out.pdsaOxidBindings = &m->bindings;
198         
199         status = dcerpc_RemoteActivation(p, ctx, &r);
200         if(NT_STATUS_IS_ERR(status)) {
201                 DEBUG(1, ("Error while running RemoteActivation %s\n", nt_errstr(status)));
202                 return ntstatus_to_werror(status);
203         }
204
205         if(!W_ERROR_IS_OK(r.out.result)) {
206                 return r.out.result; 
207         }
208         
209         if(!W_ERROR_IS_OK(r.out.hr)) { 
210                 return r.out.hr; 
211         }
212
213         *ip = talloc_array_p(ctx, struct dcom_interface, num_ifaces);
214         for (i = 0; i < num_ifaces; i++) {
215                 results[i] = r.out.results[i];
216                 (*ip)[i].private_references = 1;
217                 (*ip)[i].objref = &r.out.ifaces[i].p->obj;
218                 (*ip)[i].pipe = NULL;
219                 (*ip)[i].ctx = ctx;
220         }
221
222         /* Add the OXID data for the returned oxid */
223         m->oxid = r.out.pOxid;
224         m->bindings = *r.out.pdsaOxidBindings;
225         DLIST_ADD(ctx->oxids, m);
226
227         return WERR_OK;
228 }
229
230 WERROR dcom_get_class_object(struct dcom_context *ctx, struct GUID *clsid, const char *server, struct GUID *iid, struct dcom_interface *ip)
231 {
232         struct dcom_oxid_mapping *m;
233         struct RemoteActivation r;
234         struct dcerpc_pipe *p;
235         NTSTATUS status;
236         struct pMInterfacePointer pm;
237         uint16 protseq[] = DCOM_NEGOTIATED_PROTOCOLS;
238
239         status = dcom_connect_host(ctx, &p, server);
240         if (NT_STATUS_IS_ERR(status)) {
241                 DEBUG(1, ("Unable to connect to %s - %s\n", server, nt_errstr(status)));
242                 return ntstatus_to_werror(status);
243         }
244
245         ZERO_STRUCT(r.in);
246         r.in.this.version.MajorVersion = 5;
247         r.in.this.version.MinorVersion = 1;
248         uuid_generate_random(&r.in.this.cid);
249         r.in.Clsid = *clsid;
250         r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
251         r.in.num_protseqs = ARRAY_SIZE(protseq);
252         r.in.protseq = protseq;
253         r.in.Interfaces = 1;
254         r.in.pIIDs = iid;
255         r.in.Mode = MODE_GET_CLASS_OBJECT;
256         r.out.ifaces = &pm;
257         m = talloc_zero_p(ctx, struct dcom_oxid_mapping);
258         r.out.pdsaOxidBindings = &m->bindings;
259
260         status = dcerpc_RemoteActivation(p, ctx, &r);
261         if(NT_STATUS_IS_ERR(status)) {
262                 DEBUG(1, ("Error while running RemoteActivation - %s\n", nt_errstr(status)));
263                 return ntstatus_to_werror(status);
264         }
265
266         if(!W_ERROR_IS_OK(r.out.result)) { return r.out.result; }
267         if(!W_ERROR_IS_OK(r.out.hr)) { return r.out.hr; }
268         if(!W_ERROR_IS_OK(r.out.results[0])) { return r.out.results[0]; }
269         
270         /* Set up the interface data */
271         ip->private_references = 1;
272         ip->pipe = NULL;
273         ip->objref = &pm.p->obj;
274         ip->ctx = ctx;
275         
276         /* Add the OXID data for the returned oxid */
277         m->oxid = r.out.pOxid;
278         m->bindings = *r.out.pdsaOxidBindings;
279         DLIST_ADD(ctx->oxids, m);
280
281         return WERR_OK;
282 }
283
284 static struct dcom_oxid_mapping *oxid_mapping_by_oxid (struct dcom_context *ctx, HYPER_T oxid)
285 {
286         struct dcom_oxid_mapping *m;
287         
288         for (m = ctx->oxids;m;m = m->next) {
289                 if (m->oxid     == oxid) {
290                         return m;
291                 }
292         }
293
294         return NULL;
295 }
296
297 NTSTATUS dcom_get_pipe (struct dcom_interface *iface, struct dcerpc_pipe **p)
298 {
299         struct dcom_oxid_mapping *m;
300         struct dcerpc_binding binding;
301         struct GUID iid;
302         HYPER_T oxid;
303         NTSTATUS status;
304         
305         SMB_ASSERT(iface->objref->signature == OBJREF_SIGNATURE);
306
307         if (iface->objref->flags & OBJREF_HANDLER) {
308                 DEBUG(0, ("dcom_get_pipe: OBJREF_HANDLER not supported!\n"));
309                 return NT_STATUS_NOT_SUPPORTED;
310         }
311
312         if (iface->objref->flags & OBJREF_CUSTOM) {
313                 DEBUG(0, ("dcom_get_pipe: OBJREF_CUSTOM not supported!\n"));
314                 return NT_STATUS_NOT_SUPPORTED;
315         }
316
317         DEBUG(1, ("DCOM: Connecting to %s\n", GUID_string(NULL, &iface->objref->iid)));
318
319         oxid = iface->objref->u_objref.u_standard.std.oxid;
320         iid = iface->objref->iid;
321
322         m = oxid_mapping_by_oxid(iface->ctx, oxid);
323
324         /* Add OXID mapping if none present yet */
325         if (!m) {
326                 struct dcerpc_pipe *po;
327                 struct ResolveOxid r;
328                 uint16 protseq[] = DCOM_NEGOTIATED_PROTOCOLS;
329
330                 DEBUG(3, ("No binding data present yet, resolving OXID %llu\n", oxid));
331
332                 m = talloc_zero_p(iface->ctx, struct dcom_oxid_mapping);
333                 m->oxid = oxid; 
334
335                 /* FIXME: Check other string bindings as well, not just 0 */
336                 status = dcerpc_binding_from_STRINGBINDING(iface->ctx, &binding, iface->objref->u_objref.u_standard.saResAddr.stringbindings[0]);
337
338                 if (NT_STATUS_IS_ERR(status)) {
339                         DEBUG(1, ("Error parsing string binding"));
340                         return status;
341                 }
342
343                 status = dcerpc_pipe_connect_b(&po, &binding, DCERPC_IOXIDRESOLVER_UUID, DCERPC_IOXIDRESOLVER_VERSION, iface->ctx->domain, iface->ctx->user, iface->ctx->password);
344
345                 if (NT_STATUS_IS_ERR(status)) {
346                         DEBUG(1, ("Error while connecting to OXID Resolver : %s\n", nt_errstr(status)));
347                         return status;
348                 }
349
350                 r.in.pOxid = oxid;
351                 r.in.cRequestedProtseqs = ARRAY_SIZE(protseq);
352                 r.in.arRequestedProtseqs = protseq;
353                 r.out.ppdsaOxidBindings = &m->bindings;
354
355                 status = dcerpc_ResolveOxid(po, iface->ctx, &r);
356                 if (NT_STATUS_IS_ERR(status)) {
357                         DEBUG(1, ("Error while resolving OXID: %s\n", nt_errstr(status)));
358                         return status;
359                 }
360
361                 dcerpc_pipe_close(po);
362
363                 DLIST_ADD(iface->ctx->oxids, m);
364         }
365
366         if (m->pipe) {
367                 *p = m->pipe;
368                 /* FIXME: Switch to correct IID using an alter context call */
369                 return NT_STATUS_OK;
370         }
371
372         /* FIXME: Check other string bindings as well, not just 0 */
373         status = dcerpc_binding_from_STRINGBINDING(iface->ctx, &binding, m->bindings.stringbindings[0]);
374
375         if (NT_STATUS_IS_ERR(status)) {
376                 DEBUG(1, ("Error parsing string binding"));
377                 return status;
378         }
379
380         status = dcerpc_pipe_connect_b(&m->pipe, &binding, GUID_string(iface->ctx, &iid) , 0.0, iface->ctx->domain, iface->ctx->user, iface->ctx->password);
381
382         if (NT_STATUS_IS_ERR(status)) {
383                 return status;
384         }
385
386         DEBUG(2, ("Successfully connected to OXID %llx\n", oxid));
387         
388         *p = m->pipe;
389         return NT_STATUS_OK;
390 }