2 Unix SMB/CIFS implementation.
3 Main DCOM functionality
4 Copyright (C) 2004 Jelmer Vernooij <jelmer@samba.org>
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.
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.
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.
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"
28 #define DCOM_NEGOTIATED_PROTOCOLS { EPM_PROTOCOL_TCP, EPM_PROTOCOL_SMB, EPM_PROTOCOL_NCALRPC }
30 struct dcom_oxid_mapping {
31 struct dcom_oxid_mapping *prev, *next;
32 struct DUALSTRINGARRAY bindings;
34 struct dcerpc_pipe *pipe;
37 static NTSTATUS dcerpc_binding_from_STRINGBINDING(TALLOC_CTX *mem_ctx, struct dcerpc_binding *b, struct STRINGBINDING *bd)
39 char *host, *endpoint;
43 b->transport = dcerpc_transport_by_endpoint_protocol(bd->wTowerId);
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;
50 host = talloc_strdup(mem_ctx, bd->NetworkAddr);
51 endpoint = strchr(host, '[');
57 endpoint[strlen(endpoint)-1] = '\0';
61 b->endpoint = endpoint;
66 static NTSTATUS dcom_connect_host(struct dcom_context *ctx, struct dcerpc_pipe **p, const char *server)
68 struct dcerpc_binding bd;
69 enum dcerpc_transport_t available_transports[] = { NCACN_IP_TCP, NCACN_NP };
72 TALLOC_CTX *mem_ctx = talloc_init("dcom_connect");
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);
81 talloc_destroy(mem_ctx);
84 talloc_destroy(mem_ctx);
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);
97 for (i = 0; i < ARRAY_SIZE(available_transports); i++)
99 bd.transport = available_transports[i];
101 status = dcerpc_pipe_connect_b(p, &bd,
102 DCERPC_IREMOTEACTIVATION_UUID,
103 DCERPC_IREMOTEACTIVATION_VERSION,
104 ctx->domain, ctx->user, ctx->password);
106 if (NT_STATUS_IS_OK(status)) {
114 NTSTATUS dcerpc_IUnknown_AddRef(struct dcom_interface *p, TALLOC_CTX *mem_ctx, struct IUnknown_AddRef *rr)
117 struct REMINTERFACEREF ref;
119 /* This is rather inefficient, but we'll patch it up later */
120 r.in.cInterfaceRefs = 1;
121 r.in.InterfaceRefs = &ref;
123 return dcerpc_RemAddRef(p, mem_ctx, &r);
126 NTSTATUS dcerpc_IUnknown_Release(struct dcom_interface *p, TALLOC_CTX *mem_ctx, struct IUnknown_Release *rr)
129 struct REMINTERFACEREF ref;
131 p->private_references--;
133 /* Only do the remote version of this call when all local references have
135 if (p->private_references == 0) {
137 r.in.cInterfaceRefs = 1;
138 r.in.InterfaceRefs = &ref;
140 status = dcerpc_RemRelease(p, mem_ctx, &r);
142 if (NT_STATUS_IS_OK(status)) {
152 NTSTATUS dcerpc_IUnknown_QueryInterface(struct dcom_interface *o, TALLOC_CTX *mem_ctx, struct IUnknown_QueryInterface *rr)
154 /* FIXME: Ask local server for interface pointer. Local server can then
155 * call RemQueryInterface if necessary */
156 return NT_STATUS_NOT_SUPPORTED;
159 WERROR dcom_init(struct dcom_context **ctx, const char *domain, const char *user, const char *pass)
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);
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)
172 struct dcom_oxid_mapping *m;
173 struct RemoteActivation r;
175 struct dcerpc_pipe *p;
177 uint16 protseq[] = DCOM_NEGOTIATED_PROTOCOLS;
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);
186 r.in.this.version.MajorVersion = 5;
187 r.in.this.version.MinorVersion = 1;
188 uuid_generate_random(&r.in.this.cid);
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;
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;
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);
205 if(!W_ERROR_IS_OK(r.out.result)) {
209 if(!W_ERROR_IS_OK(r.out.hr)) {
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;
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);
230 WERROR dcom_get_class_object(struct dcom_context *ctx, struct GUID *clsid, const char *server, struct GUID *iid, struct dcom_interface *ip)
232 struct dcom_oxid_mapping *m;
233 struct RemoteActivation r;
234 struct dcerpc_pipe *p;
236 struct pMInterfacePointer pm;
237 uint16 protseq[] = DCOM_NEGOTIATED_PROTOCOLS;
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);
246 r.in.this.version.MajorVersion = 5;
247 r.in.this.version.MinorVersion = 1;
248 uuid_generate_random(&r.in.this.cid);
250 r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
251 r.in.num_protseqs = ARRAY_SIZE(protseq);
252 r.in.protseq = protseq;
255 r.in.Mode = MODE_GET_CLASS_OBJECT;
257 m = talloc_zero_p(ctx, struct dcom_oxid_mapping);
258 r.out.pdsaOxidBindings = &m->bindings;
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);
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]; }
270 /* Set up the interface data */
271 ip->private_references = 1;
273 ip->objref = &pm.p->obj;
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);
284 static struct dcom_oxid_mapping *oxid_mapping_by_oxid (struct dcom_context *ctx, HYPER_T oxid)
286 struct dcom_oxid_mapping *m;
288 for (m = ctx->oxids;m;m = m->next) {
289 if (m->oxid == oxid) {
297 NTSTATUS dcom_get_pipe (struct dcom_interface *iface, struct dcerpc_pipe **p)
299 struct dcom_oxid_mapping *m;
300 struct dcerpc_binding binding;
305 SMB_ASSERT(iface->objref->signature == OBJREF_SIGNATURE);
307 if (iface->objref->flags & OBJREF_HANDLER) {
308 DEBUG(0, ("dcom_get_pipe: OBJREF_HANDLER not supported!\n"));
309 return NT_STATUS_NOT_SUPPORTED;
312 if (iface->objref->flags & OBJREF_CUSTOM) {
313 DEBUG(0, ("dcom_get_pipe: OBJREF_CUSTOM not supported!\n"));
314 return NT_STATUS_NOT_SUPPORTED;
317 DEBUG(1, ("DCOM: Connecting to %s\n", GUID_string(NULL, &iface->objref->iid)));
319 oxid = iface->objref->u_objref.u_standard.std.oxid;
320 iid = iface->objref->iid;
322 m = oxid_mapping_by_oxid(iface->ctx, oxid);
324 /* Add OXID mapping if none present yet */
326 struct dcerpc_pipe *po;
327 struct ResolveOxid r;
328 uint16 protseq[] = DCOM_NEGOTIATED_PROTOCOLS;
330 DEBUG(3, ("No binding data present yet, resolving OXID %llu\n", oxid));
332 m = talloc_zero_p(iface->ctx, struct dcom_oxid_mapping);
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]);
338 if (NT_STATUS_IS_ERR(status)) {
339 DEBUG(1, ("Error parsing string binding"));
343 status = dcerpc_pipe_connect_b(&po, &binding, DCERPC_IOXIDRESOLVER_UUID, DCERPC_IOXIDRESOLVER_VERSION, iface->ctx->domain, iface->ctx->user, iface->ctx->password);
345 if (NT_STATUS_IS_ERR(status)) {
346 DEBUG(1, ("Error while connecting to OXID Resolver : %s\n", nt_errstr(status)));
351 r.in.cRequestedProtseqs = ARRAY_SIZE(protseq);
352 r.in.arRequestedProtseqs = protseq;
353 r.out.ppdsaOxidBindings = &m->bindings;
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)));
361 dcerpc_pipe_close(po);
363 DLIST_ADD(iface->ctx->oxids, m);
368 /* FIXME: Switch to correct IID using an alter context call */
372 /* FIXME: Check other string bindings as well, not just 0 */
373 status = dcerpc_binding_from_STRINGBINDING(iface->ctx, &binding, m->bindings.stringbindings[0]);
375 if (NT_STATUS_IS_ERR(status)) {
376 DEBUG(1, ("Error parsing string binding"));
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);
382 if (NT_STATUS_IS_ERR(status)) {
386 DEBUG(2, ("Successfully connected to OXID %llx\n", oxid));