2 Unix SMB/CIFS implementation.
4 dcerpc utility functions
6 Copyright (C) Andrew Tridgell 2003
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 this ndr_size_* stuff should really be auto-generated ....
29 static size_t ndr_size_epm_floor(struct epm_floor *fl)
32 if (fl->lhs.protocol == EPM_PROTOCOL_UUID) {
35 ret += fl->lhs.info.lhs_data.length;
37 ret += fl->rhs.rhs_data.length;
41 size_t ndr_size_epm_towers(struct epm_towers *towers)
45 for (i=0;i<towers->num_floors;i++) {
46 ret += ndr_size_epm_floor(&towers->floors[i]);
52 work out what TCP port to use for a given interface on a given host
54 NTSTATUS dcerpc_epm_map_tcp_port(const char *server,
55 const char *uuid, uint_t version,
58 struct dcerpc_pipe *p;
61 struct policy_handle handle;
63 struct epm_twr_t twr, *twr_r;
65 if (strcasecmp(uuid, DCERPC_EPMAPPER_UUID) == 0 ||
66 strcasecmp(uuid, DCERPC_MGMT_UUID) == 0) {
67 /* don't lookup epmapper via epmapper! */
68 *port = EPMAPPER_PORT;
72 status = dcerpc_pipe_open_tcp(&p, server, EPMAPPER_PORT);
73 if (!NT_STATUS_IS_OK(status)) {
77 /* we can use the pipes memory context here as we will have a short
79 status = dcerpc_bind_byuuid(p, p->mem_ctx,
81 DCERPC_EPMAPPER_VERSION);
82 if (!NT_STATUS_IS_OK(status)) {
90 twr.towers.num_floors = 5;
91 twr.towers.floors = talloc(p->mem_ctx, sizeof(twr.towers.floors[0]) * 5);
93 /* what I'd like for christmas ... */
95 /* an RPC interface ... */
96 twr.towers.floors[0].lhs.protocol = EPM_PROTOCOL_UUID;
97 GUID_from_string(uuid, &twr.towers.floors[0].lhs.info.uuid.uuid);
98 twr.towers.floors[0].lhs.info.uuid.version = version;
99 twr.towers.floors[0].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2);
101 /* encoded with NDR ... */
102 twr.towers.floors[1].lhs.protocol = EPM_PROTOCOL_UUID;
103 GUID_from_string(NDR_GUID, &twr.towers.floors[1].lhs.info.uuid.uuid);
104 twr.towers.floors[1].lhs.info.uuid.version = NDR_GUID_VERSION;
105 twr.towers.floors[1].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2);
107 /* on an RPC connection ... */
108 twr.towers.floors[2].lhs.protocol = EPM_PROTOCOL_RPC_C;
109 twr.towers.floors[2].lhs.info.lhs_data = data_blob(NULL, 0);
110 twr.towers.floors[2].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2);
112 /* on a TCP port ... */
113 twr.towers.floors[3].lhs.protocol = EPM_PROTOCOL_TCP;
114 twr.towers.floors[3].lhs.info.lhs_data = data_blob(NULL, 0);
115 twr.towers.floors[3].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2);
117 /* on an IP link ... */
118 twr.towers.floors[4].lhs.protocol = EPM_PROTOCOL_IP;
119 twr.towers.floors[4].lhs.info.lhs_data = data_blob(NULL, 0);
120 twr.towers.floors[4].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 4);
122 /* with some nice pretty paper around it of course */
124 r.in.map_tower = &twr;
125 r.in.entry_handle = &handle;
127 r.out.entry_handle = &handle;
129 status = dcerpc_epm_Map(p, p->mem_ctx, &r);
130 if (!NT_STATUS_IS_OK(status)) {
131 dcerpc_pipe_close(p);
134 if (r.out.status != 0 || r.out.num_towers != 1) {
135 dcerpc_pipe_close(p);
136 return NT_STATUS_PORT_UNREACHABLE;
139 twr_r = r.out.towers[0].twr;
141 dcerpc_pipe_close(p);
142 return NT_STATUS_PORT_UNREACHABLE;
145 if (twr_r->towers.num_floors != 5 ||
146 twr_r->towers.floors[3].lhs.protocol != twr.towers.floors[3].lhs.protocol ||
147 twr_r->towers.floors[3].rhs.rhs_data.length != 2) {
148 dcerpc_pipe_close(p);
149 return NT_STATUS_PORT_UNREACHABLE;
152 *port = RSVAL(twr_r->towers.floors[3].rhs.rhs_data.data, 0);
154 dcerpc_pipe_close(p);
161 find the pipe name for a local IDL interface
163 const char *idl_pipe_name(const char *uuid, uint32_t if_version)
166 for (i=0;dcerpc_pipes[i];i++) {
167 if (strcasecmp(dcerpc_pipes[i]->uuid, uuid) == 0 &&
168 dcerpc_pipes[i]->if_version == if_version) {
169 return dcerpc_pipes[i]->name;
176 find the number of calls defined by local IDL
178 int idl_num_calls(const char *uuid, uint32_t if_version)
181 for (i=0;dcerpc_pipes[i];i++) {
182 if (strcasecmp(dcerpc_pipes[i]->uuid, uuid) == 0 &&
183 dcerpc_pipes[i]->if_version == if_version) {
184 return dcerpc_pipes[i]->num_calls;
192 find a dcerpc interface by name
194 const struct dcerpc_interface_table *idl_iface_by_name(const char *name)
197 for (i=0;dcerpc_pipes[i];i++) {
198 if (strcasecmp(dcerpc_pipes[i]->name, name) == 0) {
199 return dcerpc_pipes[i];
206 find a dcerpc interface by uuid
208 const struct dcerpc_interface_table *idl_iface_by_uuid(const char *uuid)
211 for (i=0;dcerpc_pipes[i];i++) {
212 if (strcasecmp(dcerpc_pipes[i]->uuid, uuid) == 0) {
213 return dcerpc_pipes[i];
222 push a dcerpc_packet into a blob, potentially with auth info
224 NTSTATUS dcerpc_push_auth(DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
225 struct dcerpc_packet *pkt,
226 struct dcerpc_auth *auth_info)
229 struct ndr_push *ndr;
231 ndr = ndr_push_init_ctx(mem_ctx);
233 return NT_STATUS_NO_MEMORY;
236 if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
237 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
241 pkt->auth_length = auth_info->credentials.length;
243 pkt->auth_length = 0;
246 status = ndr_push_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
247 if (!NT_STATUS_IS_OK(status)) {
252 status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, auth_info);
255 *blob = ndr_push_blob(ndr);
257 /* fill in the frag length */
258 dcerpc_set_frag_length(blob, blob->length);
264 static const struct {
266 enum dcerpc_transport_t transport;
267 } ncacn_transports[] = {
268 {"ncacn_np", NCACN_NP},
269 {"ncacn_ip_tcp", NCACN_IP_TCP}
272 static const struct {
275 } ncacn_options[] = {
276 {"sign", DCERPC_SIGN},
277 {"seal", DCERPC_SEAL},
278 {"validate", DCERPC_DEBUG_VALIDATE_BOTH},
279 {"print", DCERPC_DEBUG_PRINT_BOTH},
280 {"bigendian", DCERPC_PUSH_BIGENDIAN}
284 form a binding string from a binding structure
286 const char *dcerpc_binding_string(TALLOC_CTX *mem_ctx, const struct dcerpc_binding *b)
290 const char *t_name=NULL;
292 for (i=0;i<ARRAY_SIZE(ncacn_transports);i++) {
293 if (ncacn_transports[i].transport == b->transport) {
294 t_name = ncacn_transports[i].name;
301 s = talloc_asprintf(mem_ctx, "%s:%s:[", t_name, b->host);
304 /* this is a *really* inefficent way of dealing with strings,
305 but this is rarely called and the strings are always short,
307 for (i=0;b->options && b->options[i];i++) {
308 s = talloc_asprintf(mem_ctx, "%s%s,", s, b->options[i]);
311 for (i=0;i<ARRAY_SIZE(ncacn_options);i++) {
312 if (b->flags & ncacn_options[i].flag) {
313 s = talloc_asprintf(mem_ctx, "%s%s,", s, ncacn_options[i].name);
317 if (s[strlen(s)-1] == ',') {
320 s = talloc_asprintf(mem_ctx, "%s]", s);
326 parse a binding string into a dcerpc_binding structure
328 NTSTATUS dcerpc_parse_binding(TALLOC_CTX *mem_ctx, const char *s, struct dcerpc_binding *b)
330 char *part1, *part2, *part3;
332 int i, j, comma_count;
336 return NT_STATUS_INVALID_PARAMETER;
338 part1 = talloc_strndup(mem_ctx, s, PTR_DIFF(p, s));
340 return NT_STATUS_NO_MEMORY;
348 part2 = talloc_strndup(mem_ctx, s, PTR_DIFF(p, s));
349 part3 = talloc_strdup(mem_ctx, p+1);
350 if (part3[strlen(part3)-1] != ']') {
351 return NT_STATUS_INVALID_PARAMETER;
353 part3[strlen(part3)-1] = 0;
355 part2 = talloc_strdup(mem_ctx, s);
359 part2 = talloc_strndup(mem_ctx, s, PTR_DIFF(p, s));
360 part3 = talloc_strdup(mem_ctx, p+1);
363 return NT_STATUS_NO_MEMORY;
366 for (i=0;i<ARRAY_SIZE(ncacn_transports);i++) {
367 if (strcasecmp(part1, ncacn_transports[i].name) == 0) {
368 b->transport = ncacn_transports[i].transport;
372 if (i==ARRAY_SIZE(ncacn_transports)) {
373 DEBUG(0,("Unknown dcerpc transport '%s'\n", part1));
374 return NT_STATUS_INVALID_PARAMETER;
385 /* the [] brackets are optional */
386 if (*part3 == '[' && part3[strlen(part3)-1] == ']') {
388 part3[strlen(part3)-1] = 0;
391 comma_count = count_chars(part3, ',');
392 b->options = talloc_array_p(mem_ctx, const char *, comma_count+2);
394 return NT_STATUS_NO_MEMORY;
397 for (i=0; (p = strchr(part3, ',')); i++) {
398 b->options[i] = talloc_strndup(mem_ctx, part3, PTR_DIFF(p, part3));
399 if (!b->options[i]) {
400 return NT_STATUS_NO_MEMORY;
404 b->options[i] = part3;
405 b->options[i+1] = NULL;
407 /* some options are pre-parsed for convenience */
408 for (i=0;b->options[i];i++) {
409 for (j=0;j<ARRAY_SIZE(ncacn_options);j++) {
410 if (strcasecmp(ncacn_options[j].name, b->options[i]) == 0) {
412 b->flags |= ncacn_options[j].flag;
413 for (k=i;b->options[k];k++) {
414 b->options[k] = b->options[k+1];
426 /* open a rpc connection to a rpc pipe on SMB using the binding
427 structure to determine the endpoint and options */
428 static NTSTATUS dcerpc_pipe_connect_ncacn_np(struct dcerpc_pipe **p,
429 struct dcerpc_binding *binding,
430 const char *pipe_uuid,
431 uint32_t pipe_version,
433 const char *username,
434 const char *password)
438 struct cli_state *cli;
439 const char *pipe_name;
441 if (!binding->options || !binding->options[0]) {
442 const struct dcerpc_interface_table *table = idl_iface_by_uuid(pipe_uuid);
444 DEBUG(0,("Unknown interface endpoint '%s'\n", pipe_uuid));
445 return NT_STATUS_INVALID_PARAMETER;
447 /* only try the first endpoint for now */
448 pipe_name = table->endpoints->names[0];
450 pipe_name = binding->options[0];
453 if (strncasecmp(pipe_name, "\\pipe\\", 6) == 0) {
456 if (strncasecmp(pipe_name, "/pipe/", 6) == 0) {
460 if ((binding->flags & DCERPC_SCHANNEL_ANY) || !username || !username[0]) {
461 status = cli_full_connection(&cli, lp_netbios_name(),
464 "", "", NULL, 0, &retry);
466 status = cli_full_connection(&cli, lp_netbios_name(),
470 password, 0, &retry);
472 if (!NT_STATUS_IS_OK(status)) {
473 DEBUG(0,("Failed to connect to %s - %s\n", binding->host, nt_errstr(status)));
477 status = dcerpc_pipe_open_smb(p, cli->tree, pipe_name);
478 if (!NT_STATUS_IS_OK(status)) {
479 DEBUG(0,("Failed to open pipe %s - %s\n", pipe_name, nt_errstr(status)));
485 /* this ensures that the reference count is decremented so
486 a pipe close will really close the link */
487 cli_tree_close(cli->tree);
489 (*p)->flags = binding->flags;
491 /* remember the binding string for possible secondary connections */
492 (*p)->binding_string = dcerpc_binding_string((*p)->mem_ctx, binding);
494 if (username && username[0] && (binding->flags & DCERPC_SCHANNEL_ANY)) {
495 status = dcerpc_bind_auth_schannel(*p, pipe_uuid, pipe_version,
496 domain, username, password);
497 } else if (username && username[0] && (binding->flags & (DCERPC_SIGN | DCERPC_SEAL))) {
498 status = dcerpc_bind_auth_ntlm(*p, pipe_uuid, pipe_version, domain, username, password);
500 status = dcerpc_bind_auth_none(*p, pipe_uuid, pipe_version);
503 if (!NT_STATUS_IS_OK(status)) {
504 DEBUG(0,("Failed to bind to uuid %s - %s\n", pipe_uuid, nt_errstr(status)));
505 dcerpc_pipe_close(*p);
513 /* open a rpc connection to a rpc pipe on SMP using the binding
514 structure to determine the endpoint and options */
515 static NTSTATUS dcerpc_pipe_connect_ncacn_ip_tcp(struct dcerpc_pipe **p,
516 struct dcerpc_binding *binding,
517 const char *pipe_uuid,
518 uint32_t pipe_version,
520 const char *username,
521 const char *password)
526 if (binding->options && binding->options[0]) {
527 port = atoi(binding->options[0]);
531 status = dcerpc_epm_map_tcp_port(binding->host,
532 pipe_uuid, pipe_version,
534 if (!NT_STATUS_IS_OK(status)) {
535 DEBUG(0,("Failed to map DCERPC/TCP port for '%s' - %s\n",
536 pipe_uuid, nt_errstr(status)));
539 DEBUG(1,("Mapped to DCERPC/TCP port %u\n", port));
542 status = dcerpc_pipe_open_tcp(p, binding->host, port);
543 if (!NT_STATUS_IS_OK(status)) {
544 DEBUG(0,("Failed to connect to %s:%d\n", binding->host, port));
548 /* it doesn't seem to work to do a null NTLMSSP session without either sign
549 or seal, so force signing if we are doing ntlmssp */
550 if (username && username[0] && !(binding->flags & (DCERPC_SIGN|DCERPC_SEAL))) {
551 binding->flags |= DCERPC_SIGN;
554 (*p)->flags = binding->flags;
556 /* remember the binding string for possible secondary connections */
557 (*p)->binding_string = dcerpc_binding_string((*p)->mem_ctx, binding);
559 if (username && username[0] && (binding->flags & DCERPC_SCHANNEL_ANY)) {
560 status = dcerpc_bind_auth_schannel(*p, pipe_uuid, pipe_version,
561 domain, username, password);
562 } else if (username && username[0] && (binding->flags & (DCERPC_SIGN | DCERPC_SEAL))) {
563 status = dcerpc_bind_auth_ntlm(*p, pipe_uuid, pipe_version, domain, username, password);
565 status = dcerpc_bind_auth_none(*p, pipe_uuid, pipe_version);
568 if (!NT_STATUS_IS_OK(status)) {
569 DEBUG(0,("Failed to bind to uuid %s - %s\n",
570 pipe_uuid, nt_errstr(status)));
571 dcerpc_pipe_close(*p);
579 /* open a rpc connection to a rpc pipe, using the specified
580 binding structure to determine the endpoint and options */
581 NTSTATUS dcerpc_pipe_connect_b(struct dcerpc_pipe **p,
582 struct dcerpc_binding *binding,
583 const char *pipe_uuid,
584 uint32_t pipe_version,
586 const char *username,
587 const char *password)
589 NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
591 switch (binding->transport) {
593 status = dcerpc_pipe_connect_ncacn_np(p, binding, pipe_uuid, pipe_version,
594 domain, username, password);
597 status = dcerpc_pipe_connect_ncacn_ip_tcp(p, binding, pipe_uuid, pipe_version,
598 domain, username, password);
606 /* open a rpc connection to a rpc pipe, using the specified string
607 binding to determine the endpoint and options */
608 NTSTATUS dcerpc_pipe_connect(struct dcerpc_pipe **p,
610 const char *pipe_uuid,
611 uint32_t pipe_version,
613 const char *username,
614 const char *password)
616 struct dcerpc_binding b;
620 mem_ctx = talloc_init("dcerpc_pipe_connect");
621 if (!mem_ctx) return NT_STATUS_NO_MEMORY;
623 status = dcerpc_parse_binding(mem_ctx, binding, &b);
624 if (!NT_STATUS_IS_OK(status)) {
625 DEBUG(0,("Failed to parse dcerpc binding '%s'\n", binding));
626 talloc_destroy(mem_ctx);
630 DEBUG(3,("Using binding %s\n", dcerpc_binding_string(mem_ctx, &b)));
632 status = dcerpc_pipe_connect_b(p, &b, pipe_uuid, pipe_version, domain, username, password);
634 talloc_destroy(mem_ctx);
640 create a secondary dcerpc connection from a primary connection
642 if the primary is a SMB connection then the secondary connection
643 will be on the same SMB connection, but use a new fnum
645 NTSTATUS dcerpc_secondary_connection(struct dcerpc_pipe *p, struct dcerpc_pipe **p2,
646 const char *pipe_name,
647 const char *pipe_uuid,
648 uint32_t pipe_version)
650 struct cli_tree *tree;
651 NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
652 struct dcerpc_binding b;
654 switch (p->transport.transport) {
656 tree = dcerpc_smb_tree(p);
658 return NT_STATUS_INVALID_PARAMETER;
661 status = dcerpc_pipe_open_smb(p2, tree, pipe_name);
665 status = dcerpc_parse_binding(p->mem_ctx, p->binding_string, &b);
666 if (!NT_STATUS_IS_OK(status)) {
669 b.flags &= ~DCERPC_AUTH_OPTIONS;
670 status = dcerpc_pipe_connect_ncacn_ip_tcp(p2, &b, pipe_uuid,
676 if (!NT_STATUS_IS_OK(status)) {
680 (*p2)->flags = p->flags;
682 status = dcerpc_bind_auth_none(*p2, pipe_uuid, pipe_version);
683 if (!NT_STATUS_IS_OK(status)) {
692 fetch the user session key for the underlying transport. Currently
693 only works for the ncacn_np transport
695 NTSTATUS dcerpc_fetch_session_key(struct dcerpc_pipe *p,
696 DATA_BLOB *session_key)
698 struct cli_tree *tree;
700 if (p->security_state.ops) {
701 return p->security_state.ops->session_key(&p->security_state, session_key);
704 tree = dcerpc_smb_tree(p);
706 if (tree->session->user_session_key.data) {
707 *session_key = tree->session->user_session_key;
712 return NT_STATUS_NO_USER_SESSION_KEY;
717 log a rpc packet in a format suitable for ndrdump. This is especially useful
718 for sealed packets, where ethereal cannot easily see the contents
720 this triggers on a debug level of >= 10
722 void dcerpc_log_packet(const struct dcerpc_interface_table *ndr,
723 uint32_t opnum, uint32_t flags, DATA_BLOB *pkt)
725 const int num_examples = 20;
728 if (DEBUGLEVEL < 10) return;
730 for (i=0;i<num_examples;i++) {
732 asprintf(&name, "%s/rpclog/%s-%u.%d.%s",
733 lp_lockdir(), ndr->name, opnum, i,
734 (flags&NDR_IN)?"in":"out");
738 if (!file_exist(name, NULL)) {
739 if (file_save(name, pkt->data, pkt->length)) {
740 DEBUG(10,("Logged rpc packet to %s\n", name));