librpc/rpc: add dcerpc_binding_[g|s]et_abstract_syntax()
[Samba.git] / librpc / rpc / binding.c
blob2b584b573268eaea9015ecc7a71784cd3899f2b0
1 /*
2 Unix SMB/CIFS implementation.
4 dcerpc utility functions
6 Copyright (C) Andrew Tridgell 2003
7 Copyright (C) Jelmer Vernooij 2004
8 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
9 Copyright (C) Rafal Szczesniak 2006
10 Copyright (C) Stefan Metzmacher 2014
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "../../lib/util/util_net.h"
28 #include "librpc/gen_ndr/ndr_epmapper.h"
29 #include "librpc/gen_ndr/ndr_misc.h"
30 #include "librpc/rpc/dcerpc.h"
31 #include "rpc_common.h"
33 #undef strcasecmp
34 #undef strncasecmp
36 #define MAX_PROTSEQ 10
38 static const struct {
39 const char *name;
40 enum dcerpc_transport_t transport;
41 int num_protocols;
42 enum epm_protocol protseq[MAX_PROTSEQ];
43 } transports[] = {
44 { "ncacn_np", NCACN_NP, 3,
45 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_SMB, EPM_PROTOCOL_NETBIOS }},
46 { "ncacn_ip_tcp", NCACN_IP_TCP, 3,
47 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_TCP, EPM_PROTOCOL_IP } },
48 { "ncacn_http", NCACN_HTTP, 3,
49 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_HTTP, EPM_PROTOCOL_IP } },
50 { "ncadg_ip_udp", NCACN_IP_UDP, 3,
51 { EPM_PROTOCOL_NCADG, EPM_PROTOCOL_UDP, EPM_PROTOCOL_IP } },
52 { "ncalrpc", NCALRPC, 2,
53 { EPM_PROTOCOL_NCALRPC, EPM_PROTOCOL_NAMED_PIPE } },
54 { "ncacn_unix_stream", NCACN_UNIX_STREAM, 2,
55 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_UNIX_DS } },
56 { "ncadg_unix_dgram", NCADG_UNIX_DGRAM, 2,
57 { EPM_PROTOCOL_NCADG, EPM_PROTOCOL_UNIX_DS } },
58 { "ncacn_at_dsp", NCACN_AT_DSP, 3,
59 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_APPLETALK, EPM_PROTOCOL_DSP } },
60 { "ncadg_at_ddp", NCADG_AT_DDP, 3,
61 { EPM_PROTOCOL_NCADG, EPM_PROTOCOL_APPLETALK, EPM_PROTOCOL_DDP } },
62 { "ncacn_vns_ssp", NCACN_VNS_SPP, 3,
63 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_STREETTALK, EPM_PROTOCOL_VINES_SPP } },
64 { "ncacn_vns_ipc", NCACN_VNS_IPC, 3,
65 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_STREETTALK, EPM_PROTOCOL_VINES_IPC }, },
66 { "ncadg_ipx", NCADG_IPX, 2,
67 { EPM_PROTOCOL_NCADG, EPM_PROTOCOL_IPX },
69 { "ncacn_spx", NCACN_SPX, 3,
70 /* I guess some MS programmer confused the identifier for
71 * EPM_PROTOCOL_UUID (0x0D or 13) with the one for
72 * EPM_PROTOCOL_SPX (0x13) here. -- jelmer*/
73 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_NCALRPC, EPM_PROTOCOL_UUID },
77 static const struct ncacn_option {
78 const char *name;
79 uint32_t flag;
80 } ncacn_options[] = {
81 {"sign", DCERPC_SIGN},
82 {"seal", DCERPC_SEAL},
83 {"connect", DCERPC_CONNECT},
84 {"spnego", DCERPC_AUTH_SPNEGO},
85 {"ntlm", DCERPC_AUTH_NTLM},
86 {"krb5", DCERPC_AUTH_KRB5},
87 {"validate", DCERPC_DEBUG_VALIDATE_BOTH},
88 {"print", DCERPC_DEBUG_PRINT_BOTH},
89 {"padcheck", DCERPC_DEBUG_PAD_CHECK},
90 {"bigendian", DCERPC_PUSH_BIGENDIAN},
91 {"smb2", DCERPC_SMB2},
92 {"ndr64", DCERPC_NDR64},
95 static const struct ncacn_option *ncacn_option_by_name(const char *name)
97 size_t i;
99 for (i=0; i<ARRAY_SIZE(ncacn_options); i++) {
100 int ret;
102 ret = strcasecmp(ncacn_options[i].name, name);
103 if (ret != 0) {
104 continue;
107 return &ncacn_options[i];
110 return NULL;
113 const char *epm_floor_string(TALLOC_CTX *mem_ctx, struct epm_floor *epm_floor)
115 struct ndr_syntax_id syntax;
116 NTSTATUS status;
118 switch(epm_floor->lhs.protocol) {
119 case EPM_PROTOCOL_UUID:
120 status = dcerpc_floor_get_lhs_data(epm_floor, &syntax);
121 if (NT_STATUS_IS_OK(status)) {
122 /* lhs is used: UUID */
123 char *uuidstr;
125 if (GUID_equal(&syntax.uuid, &ndr_transfer_syntax_ndr.uuid)) {
126 return "NDR";
129 if (GUID_equal(&syntax.uuid, &ndr_transfer_syntax_ndr64.uuid)) {
130 return "NDR64";
133 uuidstr = GUID_string(mem_ctx, &syntax.uuid);
135 return talloc_asprintf(mem_ctx, " uuid %s/0x%02x", uuidstr, syntax.if_version);
136 } else { /* IPX */
137 return talloc_asprintf(mem_ctx, "IPX:%s",
138 data_blob_hex_string_upper(mem_ctx, &epm_floor->rhs.uuid.unknown));
141 case EPM_PROTOCOL_NCACN:
142 return "RPC-C";
144 case EPM_PROTOCOL_NCADG:
145 return "RPC";
147 case EPM_PROTOCOL_NCALRPC:
148 return "NCALRPC";
150 case EPM_PROTOCOL_DNET_NSP:
151 return "DNET/NSP";
153 case EPM_PROTOCOL_IP:
154 return talloc_asprintf(mem_ctx, "IP:%s", epm_floor->rhs.ip.ipaddr);
156 case EPM_PROTOCOL_NAMED_PIPE:
157 return talloc_asprintf(mem_ctx, "NAMED-PIPE:%s", epm_floor->rhs.named_pipe.path);
159 case EPM_PROTOCOL_SMB:
160 return talloc_asprintf(mem_ctx, "SMB:%s", epm_floor->rhs.smb.unc);
162 case EPM_PROTOCOL_UNIX_DS:
163 return talloc_asprintf(mem_ctx, "Unix:%s", epm_floor->rhs.unix_ds.path);
165 case EPM_PROTOCOL_NETBIOS:
166 return talloc_asprintf(mem_ctx, "NetBIOS:%s", epm_floor->rhs.netbios.name);
168 case EPM_PROTOCOL_NETBEUI:
169 return "NETBeui";
171 case EPM_PROTOCOL_SPX:
172 return "SPX";
174 case EPM_PROTOCOL_NB_IPX:
175 return "NB_IPX";
177 case EPM_PROTOCOL_HTTP:
178 return talloc_asprintf(mem_ctx, "HTTP:%d", epm_floor->rhs.http.port);
180 case EPM_PROTOCOL_TCP:
181 return talloc_asprintf(mem_ctx, "TCP:%d", epm_floor->rhs.tcp.port);
183 case EPM_PROTOCOL_UDP:
184 return talloc_asprintf(mem_ctx, "UDP:%d", epm_floor->rhs.udp.port);
186 default:
187 return talloc_asprintf(mem_ctx, "UNK(%02x):", epm_floor->lhs.protocol);
193 form a binding string from a binding structure
195 _PUBLIC_ char *dcerpc_binding_string(TALLOC_CTX *mem_ctx, const struct dcerpc_binding *b)
197 char *s = talloc_strdup(mem_ctx, "");
198 char *o = s;
199 int i;
200 const char *t_name = NULL;
202 if (b->transport != NCA_UNKNOWN) {
203 t_name = derpc_transport_string_by_transport(b->transport);
204 if (!t_name) {
205 talloc_free(o);
206 return NULL;
210 if (!GUID_all_zero(&b->object.uuid)) {
211 o = s;
212 s = talloc_asprintf_append_buffer(s, "%s@",
213 GUID_string(mem_ctx, &b->object.uuid));
214 if (s == NULL) {
215 talloc_free(o);
216 return NULL;
220 if (t_name != NULL) {
221 o = s;
222 s = talloc_asprintf_append_buffer(s, "%s:", t_name);
223 if (s == NULL) {
224 talloc_free(o);
225 return NULL;
229 if (b->host) {
230 o = s;
231 s = talloc_asprintf_append_buffer(s, "%s", b->host);
232 if (s == NULL) {
233 talloc_free(o);
234 return NULL;
238 if (!b->endpoint && !b->options && !b->flags) {
239 return s;
242 o = s;
243 s = talloc_asprintf_append_buffer(s, "[");
244 if (s == NULL) {
245 talloc_free(o);
246 return NULL;
249 if (b->endpoint) {
250 o = s;
251 s = talloc_asprintf_append_buffer(s, "%s", b->endpoint);
252 if (s == NULL) {
253 talloc_free(o);
254 return NULL;
258 for (i=0;i<ARRAY_SIZE(ncacn_options);i++) {
259 if (!(b->flags & ncacn_options[i].flag)) {
260 continue;
263 o = s;
264 s = talloc_asprintf_append_buffer(s, ",%s", ncacn_options[i].name);
265 if (s == NULL) {
266 talloc_free(o);
267 return NULL;
271 for (i=0;b->options && b->options[i];i++) {
272 o = s;
273 s = talloc_asprintf_append_buffer(s, ",%s", b->options[i]);
274 if (s == NULL) {
275 talloc_free(o);
276 return NULL;
280 o = s;
281 s = talloc_asprintf_append_buffer(s, "]");
282 if (s == NULL) {
283 talloc_free(o);
284 return NULL;
287 return s;
291 parse a binding string into a dcerpc_binding structure
293 _PUBLIC_ NTSTATUS dcerpc_parse_binding(TALLOC_CTX *mem_ctx, const char *_s, struct dcerpc_binding **b_out)
295 char *_t;
296 struct dcerpc_binding *b;
297 char *s;
298 char *options = NULL;
299 char *p;
300 size_t i;
301 NTSTATUS status;
303 b = talloc_zero(mem_ctx, struct dcerpc_binding);
304 if (!b) {
305 return NT_STATUS_NO_MEMORY;
308 _t = talloc_strdup(b, _s);
309 if (_t == NULL) {
310 talloc_free(b);
311 return NT_STATUS_NO_MEMORY;
314 s = _t;
316 p = strchr(s, '[');
317 if (p) {
318 *p = '\0';
319 options = p + 1;
320 if (options[strlen(options)-1] != ']') {
321 talloc_free(b);
322 return NT_STATUS_INVALID_PARAMETER_MIX;
324 options[strlen(options)-1] = 0;
327 p = strchr(s, '@');
329 if (p && PTR_DIFF(p, s) == 36) { /* 36 is the length of a UUID */
330 *p = '\0';
332 status = dcerpc_binding_set_string_option(b, "object", s);
333 if (!NT_STATUS_IS_OK(status)) {
334 talloc_free(b);
335 return status;
338 s = p + 1;
341 p = strchr(s, ':');
343 if (p == NULL) {
344 b->transport = NCA_UNKNOWN;
345 } else {
346 *p = '\0';
348 status = dcerpc_binding_set_string_option(b, "transport", s);
349 if (!NT_STATUS_IS_OK(status)) {
350 talloc_free(b);
351 return status;
354 s = p + 1;
357 if (strlen(s) > 0) {
358 status = dcerpc_binding_set_string_option(b, "host", s);
359 if (!NT_STATUS_IS_OK(status)) {
360 talloc_free(b);
361 return status;
364 b->target_hostname = talloc_strdup(b, b->host);
365 if (b->target_hostname == NULL) {
366 talloc_free(b);
367 return NT_STATUS_NO_MEMORY;
371 for (i=0; options != NULL; i++) {
372 const char *name = options;
373 const char *value = NULL;
375 p = strchr(options, ',');
376 if (p != NULL) {
377 *p = '\0';
378 options = p+1;
379 } else {
380 options = NULL;
383 p = strchr(name, '=');
384 if (p != NULL) {
385 *p = '\0';
386 value = p + 1;
389 if (value == NULL) {
391 * If it's not a key=value pair
392 * it might be a ncacn_option
393 * or if it's the first option
394 * it's the endpoint.
396 const struct ncacn_option *no = NULL;
398 value = name;
400 no = ncacn_option_by_name(name);
401 if (no == NULL) {
402 if (i > 0) {
404 * we don't allow unknown options
406 return NT_STATUS_INVALID_PARAMETER_MIX;
410 * This is the endpoint
412 name = "endpoint";
413 if (strlen(value) == 0) {
414 value = NULL;
419 status = dcerpc_binding_set_string_option(b, name, value);
420 if (!NT_STATUS_IS_OK(status)) {
421 talloc_free(b);
422 return status;
426 talloc_free(_t);
427 *b_out = b;
428 return NT_STATUS_OK;
431 _PUBLIC_ struct GUID dcerpc_binding_get_object(const struct dcerpc_binding *b)
433 return b->object.uuid;
436 _PUBLIC_ NTSTATUS dcerpc_binding_set_object(struct dcerpc_binding *b,
437 struct GUID object)
439 char *tmp = discard_const_p(char, b->object_string);
441 if (GUID_all_zero(&object)) {
442 talloc_free(tmp);
443 b->object_string = NULL;
444 ZERO_STRUCT(b->object);
445 return NT_STATUS_OK;
448 b->object_string = GUID_string(b, &object);
449 if (b->object_string == NULL) {
450 b->object_string = tmp;
451 return NT_STATUS_NO_MEMORY;
453 talloc_free(tmp);
455 ZERO_STRUCT(b->object);
456 b->object.uuid = object;
457 return NT_STATUS_OK;
460 _PUBLIC_ enum dcerpc_transport_t dcerpc_binding_get_transport(const struct dcerpc_binding *b)
462 return b->transport;
465 _PUBLIC_ NTSTATUS dcerpc_binding_set_transport(struct dcerpc_binding *b,
466 enum dcerpc_transport_t transport)
468 char *tmp = discard_const_p(char, b->endpoint);
471 * TODO: we may want to check the transport value is
472 * wellknown.
474 if (b->transport == transport) {
475 return NT_STATUS_OK;
478 b->transport = transport;
481 * This implicitly resets the endpoint
482 * as the endpoint is transport specific.
484 * TODO: in future we may reset more options
485 * here.
487 talloc_free(tmp);
488 b->endpoint = NULL;
490 return NT_STATUS_OK;
493 _PUBLIC_ void dcerpc_binding_get_auth_info(const struct dcerpc_binding *b,
494 enum dcerpc_AuthType *_auth_type,
495 enum dcerpc_AuthLevel *_auth_level)
497 enum dcerpc_AuthType auth_type;
498 enum dcerpc_AuthLevel auth_level;
500 if (b->flags & DCERPC_AUTH_SPNEGO) {
501 auth_type = DCERPC_AUTH_TYPE_SPNEGO;
502 } else if (b->flags & DCERPC_AUTH_KRB5) {
503 auth_type = DCERPC_AUTH_TYPE_KRB5;
504 } else if (b->flags & DCERPC_SCHANNEL) {
505 auth_type = DCERPC_AUTH_TYPE_SCHANNEL;
506 } else if (b->flags & DCERPC_AUTH_NTLM) {
507 auth_type = DCERPC_AUTH_TYPE_NTLMSSP;
508 } else {
509 auth_type = DCERPC_AUTH_TYPE_NONE;
512 if (b->flags & DCERPC_SEAL) {
513 auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
514 } else if (b->flags & DCERPC_SIGN) {
515 auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
516 } else if (b->flags & DCERPC_CONNECT) {
517 auth_level = DCERPC_AUTH_LEVEL_CONNECT;
518 } else if (auth_type != DCERPC_AUTH_TYPE_NONE) {
519 auth_level = DCERPC_AUTH_LEVEL_CONNECT;
520 } else {
521 auth_level = DCERPC_AUTH_LEVEL_NONE;
524 if (_auth_type == NULL) {
525 *_auth_type = auth_type;
528 if (_auth_level == NULL) {
529 *_auth_level = auth_level;
533 _PUBLIC_ uint32_t dcerpc_binding_get_assoc_group_id(const struct dcerpc_binding *b)
535 return b->assoc_group_id;
538 _PUBLIC_ NTSTATUS dcerpc_binding_set_assoc_group_id(struct dcerpc_binding *b,
539 uint32_t assoc_group_id)
541 b->assoc_group_id = assoc_group_id;
542 return NT_STATUS_OK;
545 _PUBLIC_ struct ndr_syntax_id dcerpc_binding_get_abstract_syntax(const struct dcerpc_binding *b)
548 * For now we just use object, until all callers are fixed.
550 return b->object;
553 _PUBLIC_ NTSTATUS dcerpc_binding_set_abstract_syntax(struct dcerpc_binding *b,
554 const struct ndr_syntax_id *syntax)
556 NTSTATUS status;
557 struct GUID object;
560 * For now we just use object, until all callers are fixed.
563 if (syntax != NULL) {
564 object = syntax->uuid;
565 } else {
566 object = GUID_zero();
570 * This sets also the string
572 status = dcerpc_binding_set_object(b, object);
573 if (!NT_STATUS_IS_OK(status)) {
574 return status;
577 if (syntax != NULL) {
579 * Here we need to reset the whole ndr_syntax_id
580 * structure including the .if_version
582 b->object = *syntax;
585 return NT_STATUS_OK;
588 _PUBLIC_ const char *dcerpc_binding_get_string_option(const struct dcerpc_binding *b,
589 const char *name)
591 struct {
592 const char *name;
593 const char *value;
594 #define _SPECIAL(x) { .name = #x, .value = b->x, }
595 } specials[] = {
596 { .name = "object", .value = b->object_string, },
597 _SPECIAL(host),
598 _SPECIAL(endpoint),
599 _SPECIAL(target_hostname),
600 _SPECIAL(target_principal),
601 #undef _SPECIAL
603 const struct ncacn_option *no = NULL;
604 size_t name_len = strlen(name);
605 size_t i;
606 int ret;
608 ret = strcmp(name, "transport");
609 if (ret == 0) {
610 return derpc_transport_string_by_transport(b->transport);
613 ret = strcmp(name, "assoc_group_id");
614 if (ret == 0) {
615 char *tmp = discard_const_p(char, b->assoc_group_string);
617 if (b->assoc_group_id == 0) {
618 return NULL;
621 snprintf(tmp, sizeof(b->assoc_group_string),
622 "0x%08x", b->assoc_group_id);
623 return (const char *)b->assoc_group_string;
626 for (i=0; i < ARRAY_SIZE(specials); i++) {
627 ret = strcmp(specials[i].name, name);
628 if (ret != 0) {
629 continue;
632 return specials[i].value;
635 no = ncacn_option_by_name(name);
636 if (no != NULL) {
637 if (b->flags & no->flag) {
638 return no->name;
641 return NULL;
644 if (b->options == NULL) {
645 return NULL;
648 for (i=0; b->options[i]; i++) {
649 const char *o = b->options[i];
650 const char *vs = NULL;
652 ret = strncmp(name, o, name_len);
653 if (ret != 0) {
654 continue;
657 if (o[name_len] != '=') {
658 continue;
661 vs = &o[name_len + 1];
663 return vs;
666 return NULL;
669 _PUBLIC_ char *dcerpc_binding_copy_string_option(TALLOC_CTX *mem_ctx,
670 const struct dcerpc_binding *b,
671 const char *name)
673 const char *c = dcerpc_binding_get_string_option(b, name);
674 char *v;
676 if (c == NULL) {
677 errno = ENOENT;
678 return NULL;
681 v = talloc_strdup(mem_ctx, c);
682 if (v == NULL) {
683 errno = ENOMEM;
684 return NULL;
687 return v;
690 _PUBLIC_ NTSTATUS dcerpc_binding_set_string_option(struct dcerpc_binding *b,
691 const char *name,
692 const char *value)
694 struct {
695 const char *name;
696 const char **ptr;
697 #define _SPECIAL(x) { .name = #x, .ptr = &b->x, }
698 } specials[] = {
699 _SPECIAL(host),
700 _SPECIAL(endpoint),
701 _SPECIAL(target_hostname),
702 _SPECIAL(target_principal),
703 #undef _SPECIAL
705 const struct ncacn_option *no = NULL;
706 size_t name_len = strlen(name);
707 const char *opt = NULL;
708 char *tmp;
709 size_t i;
710 int ret;
713 * Note: value == NULL, means delete it.
714 * value != NULL means add or reset.
717 ret = strcmp(name, "transport");
718 if (ret == 0) {
719 enum dcerpc_transport_t t = dcerpc_transport_by_name(value);
721 if (t == NCA_UNKNOWN && value != NULL) {
722 return NT_STATUS_INVALID_PARAMETER_MIX;
725 return dcerpc_binding_set_transport(b, t);
728 ret = strcmp(name, "object");
729 if (ret == 0) {
730 NTSTATUS status;
731 struct GUID uuid = GUID_zero();
733 if (value != NULL) {
734 DATA_BLOB blob;
735 blob = data_blob_string_const(value);
736 if (blob.length != 36) {
737 return NT_STATUS_INVALID_PARAMETER_MIX;
740 status = GUID_from_data_blob(&blob, &uuid);
741 if (!NT_STATUS_IS_OK(status)) {
742 return status;
746 return dcerpc_binding_set_object(b, uuid);
749 ret = strcmp(name, "assoc_group_id");
750 if (ret == 0) {
751 uint32_t assoc_group_id = 0;
753 if (value != NULL) {
754 char c;
756 ret = sscanf(value, "0x%08x%c", &assoc_group_id, &c);
757 if (ret != 1) {
758 return NT_STATUS_INVALID_PARAMETER_MIX;
762 return dcerpc_binding_set_assoc_group_id(b, assoc_group_id);
765 for (i=0; i < ARRAY_SIZE(specials); i++) {
766 ret = strcmp(specials[i].name, name);
767 if (ret != 0) {
768 continue;
771 tmp = discard_const_p(char, *specials[i].ptr);
773 if (value == NULL) {
774 talloc_free(tmp);
775 *specials[i].ptr = NULL;
776 return NT_STATUS_OK;
779 if (value[0] == '\0') {
780 return NT_STATUS_INVALID_PARAMETER_MIX;
783 *specials[i].ptr = talloc_strdup(b, value);
784 if (*specials[i].ptr == NULL) {
785 *specials[i].ptr = tmp;
786 return NT_STATUS_NO_MEMORY;
788 talloc_free(tmp);
790 return NT_STATUS_OK;
793 no = ncacn_option_by_name(name);
794 if (no != NULL) {
795 if (value == NULL) {
796 b->flags &= ~no->flag;
797 return NT_STATUS_OK;
800 ret = strcasecmp(no->name, value);
801 if (ret != 0) {
802 return NT_STATUS_INVALID_PARAMETER_MIX;
805 b->flags |= no->flag;
806 return NT_STATUS_OK;
809 for (i=0; b->options && b->options[i]; i++) {
810 const char *o = b->options[i];
812 ret = strncmp(name, o, name_len);
813 if (ret != 0) {
814 continue;
817 if (o[name_len] != '=') {
818 continue;
821 opt = o;
822 break;
825 if (opt == NULL) {
826 const char **n;
828 if (value == NULL) {
829 return NT_STATUS_OK;
832 n = talloc_realloc(b, b->options, const char *, i + 2);
833 if (n == NULL) {
834 return NT_STATUS_NO_MEMORY;
836 n[i] = NULL;
837 n[i + 1] = NULL;
838 b->options = n;
841 tmp = discard_const_p(char, opt);
843 if (value == NULL) {
844 for (;b->options[i];i++) {
845 b->options[i] = b->options[i+1];
847 talloc_free(tmp);
848 return NT_STATUS_OK;
851 b->options[i] = talloc_asprintf(b->options, "%s=%s",
852 name, value);
853 if (b->options[i] == NULL) {
854 b->options[i] = tmp;
855 return NT_STATUS_NO_MEMORY;
858 return NT_STATUS_OK;
861 _PUBLIC_ uint32_t dcerpc_binding_get_flags(const struct dcerpc_binding *b)
863 return b->flags;
866 _PUBLIC_ NTSTATUS dcerpc_binding_set_flags(struct dcerpc_binding *b,
867 uint32_t additional,
868 uint32_t clear)
871 * TODO: in future we may want to reject invalid combinations
873 b->flags &= ~clear;
874 b->flags |= additional;
876 return NT_STATUS_OK;
879 _PUBLIC_ NTSTATUS dcerpc_floor_get_lhs_data(const struct epm_floor *epm_floor,
880 struct ndr_syntax_id *syntax)
882 TALLOC_CTX *mem_ctx = talloc_init("floor_get_lhs_data");
883 struct ndr_pull *ndr;
884 enum ndr_err_code ndr_err;
885 uint16_t if_version=0;
887 ndr = ndr_pull_init_blob(&epm_floor->lhs.lhs_data, mem_ctx);
888 if (ndr == NULL) {
889 talloc_free(mem_ctx);
890 return NT_STATUS_NO_MEMORY;
892 ndr->flags |= LIBNDR_FLAG_NOALIGN;
894 ndr_err = ndr_pull_GUID(ndr, NDR_SCALARS | NDR_BUFFERS, &syntax->uuid);
895 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
896 talloc_free(mem_ctx);
897 return ndr_map_error2ntstatus(ndr_err);
900 ndr_err = ndr_pull_uint16(ndr, NDR_SCALARS, &if_version);
901 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
902 talloc_free(mem_ctx);
903 return ndr_map_error2ntstatus(ndr_err);
906 syntax->if_version = if_version;
908 talloc_free(mem_ctx);
910 return NT_STATUS_OK;
913 static DATA_BLOB dcerpc_floor_pack_lhs_data(TALLOC_CTX *mem_ctx, const struct ndr_syntax_id *syntax)
915 DATA_BLOB blob;
916 enum ndr_err_code ndr_err;
917 struct ndr_push *ndr;
919 ndr = ndr_push_init_ctx(mem_ctx);
920 if (ndr == NULL) {
921 return data_blob_null;
924 ndr->flags |= LIBNDR_FLAG_NOALIGN;
926 ndr_err = ndr_push_GUID(ndr, NDR_SCALARS | NDR_BUFFERS, &syntax->uuid);
927 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
928 return data_blob_null;
930 ndr_err = ndr_push_uint16(ndr, NDR_SCALARS, syntax->if_version);
931 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
932 return data_blob_null;
935 blob = ndr_push_blob(ndr);
936 talloc_steal(mem_ctx, blob.data);
937 talloc_free(ndr);
938 return blob;
941 static bool dcerpc_floor_pack_rhs_if_version_data(
942 TALLOC_CTX *mem_ctx, const struct ndr_syntax_id *syntax,
943 DATA_BLOB *pblob)
945 DATA_BLOB blob;
946 struct ndr_push *ndr = ndr_push_init_ctx(mem_ctx);
947 enum ndr_err_code ndr_err;
949 if (ndr == NULL) {
950 return false;
953 ndr->flags |= LIBNDR_FLAG_NOALIGN;
955 ndr_err = ndr_push_uint16(ndr, NDR_SCALARS, syntax->if_version >> 16);
956 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
957 return false;
960 blob = ndr_push_blob(ndr);
961 talloc_steal(mem_ctx, blob.data);
962 talloc_free(ndr);
963 *pblob = blob;
964 return true;
967 char *dcerpc_floor_get_rhs_data(TALLOC_CTX *mem_ctx, struct epm_floor *epm_floor)
969 switch (epm_floor->lhs.protocol) {
970 case EPM_PROTOCOL_TCP:
971 if (epm_floor->rhs.tcp.port == 0) return NULL;
972 return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.tcp.port);
974 case EPM_PROTOCOL_UDP:
975 if (epm_floor->rhs.udp.port == 0) return NULL;
976 return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.udp.port);
978 case EPM_PROTOCOL_HTTP:
979 if (epm_floor->rhs.http.port == 0) return NULL;
980 return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.http.port);
982 case EPM_PROTOCOL_IP:
983 return talloc_strdup(mem_ctx, epm_floor->rhs.ip.ipaddr);
985 case EPM_PROTOCOL_NCACN:
986 return NULL;
988 case EPM_PROTOCOL_NCADG:
989 return NULL;
991 case EPM_PROTOCOL_SMB:
992 if (strlen(epm_floor->rhs.smb.unc) == 0) return NULL;
993 return talloc_strdup(mem_ctx, epm_floor->rhs.smb.unc);
995 case EPM_PROTOCOL_NAMED_PIPE:
996 if (strlen(epm_floor->rhs.named_pipe.path) == 0) return NULL;
997 return talloc_strdup(mem_ctx, epm_floor->rhs.named_pipe.path);
999 case EPM_PROTOCOL_NETBIOS:
1000 if (strlen(epm_floor->rhs.netbios.name) == 0) return NULL;
1001 return talloc_strdup(mem_ctx, epm_floor->rhs.netbios.name);
1003 case EPM_PROTOCOL_NCALRPC:
1004 return NULL;
1006 case EPM_PROTOCOL_VINES_SPP:
1007 return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.vines_spp.port);
1009 case EPM_PROTOCOL_VINES_IPC:
1010 return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.vines_ipc.port);
1012 case EPM_PROTOCOL_STREETTALK:
1013 return talloc_strdup(mem_ctx, epm_floor->rhs.streettalk.streettalk);
1015 case EPM_PROTOCOL_UNIX_DS:
1016 if (strlen(epm_floor->rhs.unix_ds.path) == 0) return NULL;
1017 return talloc_strdup(mem_ctx, epm_floor->rhs.unix_ds.path);
1019 case EPM_PROTOCOL_NULL:
1020 return NULL;
1022 default:
1023 DEBUG(0,("Unsupported lhs protocol %d\n", epm_floor->lhs.protocol));
1024 break;
1027 return NULL;
1030 static NTSTATUS dcerpc_floor_set_rhs_data(TALLOC_CTX *mem_ctx,
1031 struct epm_floor *epm_floor,
1032 const char *data)
1034 if (data == NULL) {
1035 data = "";
1038 switch (epm_floor->lhs.protocol) {
1039 case EPM_PROTOCOL_TCP:
1040 epm_floor->rhs.tcp.port = atoi(data);
1041 return NT_STATUS_OK;
1043 case EPM_PROTOCOL_UDP:
1044 epm_floor->rhs.udp.port = atoi(data);
1045 return NT_STATUS_OK;
1047 case EPM_PROTOCOL_HTTP:
1048 epm_floor->rhs.http.port = atoi(data);
1049 return NT_STATUS_OK;
1051 case EPM_PROTOCOL_IP:
1052 if (!is_ipaddress_v4(data)) {
1053 data = "0.0.0.0";
1055 epm_floor->rhs.ip.ipaddr = talloc_strdup(mem_ctx, data);
1056 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.ip.ipaddr);
1057 return NT_STATUS_OK;
1059 case EPM_PROTOCOL_NCACN:
1060 epm_floor->rhs.ncacn.minor_version = 0;
1061 return NT_STATUS_OK;
1063 case EPM_PROTOCOL_NCADG:
1064 epm_floor->rhs.ncadg.minor_version = 0;
1065 return NT_STATUS_OK;
1067 case EPM_PROTOCOL_SMB:
1068 epm_floor->rhs.smb.unc = talloc_strdup(mem_ctx, data);
1069 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.smb.unc);
1070 return NT_STATUS_OK;
1072 case EPM_PROTOCOL_NAMED_PIPE:
1073 epm_floor->rhs.named_pipe.path = talloc_strdup(mem_ctx, data);
1074 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.named_pipe.path);
1075 return NT_STATUS_OK;
1077 case EPM_PROTOCOL_NETBIOS:
1078 epm_floor->rhs.netbios.name = talloc_strdup(mem_ctx, data);
1079 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.netbios.name);
1080 return NT_STATUS_OK;
1082 case EPM_PROTOCOL_NCALRPC:
1083 return NT_STATUS_OK;
1085 case EPM_PROTOCOL_VINES_SPP:
1086 epm_floor->rhs.vines_spp.port = atoi(data);
1087 return NT_STATUS_OK;
1089 case EPM_PROTOCOL_VINES_IPC:
1090 epm_floor->rhs.vines_ipc.port = atoi(data);
1091 return NT_STATUS_OK;
1093 case EPM_PROTOCOL_STREETTALK:
1094 epm_floor->rhs.streettalk.streettalk = talloc_strdup(mem_ctx, data);
1095 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.streettalk.streettalk);
1096 return NT_STATUS_OK;
1098 case EPM_PROTOCOL_UNIX_DS:
1099 epm_floor->rhs.unix_ds.path = talloc_strdup(mem_ctx, data);
1100 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.unix_ds.path);
1101 return NT_STATUS_OK;
1103 case EPM_PROTOCOL_NULL:
1104 return NT_STATUS_OK;
1106 default:
1107 DEBUG(0,("Unsupported lhs protocol %d\n", epm_floor->lhs.protocol));
1108 break;
1111 return NT_STATUS_NOT_SUPPORTED;
1114 enum dcerpc_transport_t dcerpc_transport_by_endpoint_protocol(int prot)
1116 int i;
1118 /* Find a transport that has 'prot' as 4th protocol */
1119 for (i=0;i<ARRAY_SIZE(transports);i++) {
1120 if (transports[i].num_protocols >= 2 &&
1121 transports[i].protseq[1] == prot) {
1122 return transports[i].transport;
1126 /* Unknown transport */
1127 return (unsigned int)-1;
1130 _PUBLIC_ enum dcerpc_transport_t dcerpc_transport_by_tower(const struct epm_tower *tower)
1132 int i;
1134 /* Find a transport that matches this tower */
1135 for (i=0;i<ARRAY_SIZE(transports);i++) {
1136 int j;
1137 if (transports[i].num_protocols != tower->num_floors - 2) {
1138 continue;
1141 for (j = 0; j < transports[i].num_protocols; j++) {
1142 if (transports[i].protseq[j] != tower->floors[j+2].lhs.protocol) {
1143 break;
1147 if (j == transports[i].num_protocols) {
1148 return transports[i].transport;
1152 /* Unknown transport */
1153 return (unsigned int)-1;
1156 _PUBLIC_ const char *derpc_transport_string_by_transport(enum dcerpc_transport_t t)
1158 int i;
1160 for (i=0; i<ARRAY_SIZE(transports); i++) {
1161 if (t == transports[i].transport) {
1162 return transports[i].name;
1165 return NULL;
1168 _PUBLIC_ enum dcerpc_transport_t dcerpc_transport_by_name(const char *name)
1170 size_t i;
1172 if (name == NULL) {
1173 return NCA_UNKNOWN;
1176 for (i=0; i<ARRAY_SIZE(transports);i++) {
1177 if (strcasecmp(name, transports[i].name) == 0) {
1178 return transports[i].transport;
1182 return NCA_UNKNOWN;
1185 _PUBLIC_ NTSTATUS dcerpc_binding_from_tower(TALLOC_CTX *mem_ctx,
1186 struct epm_tower *tower,
1187 struct dcerpc_binding **b_out)
1189 NTSTATUS status;
1190 struct dcerpc_binding *binding;
1191 struct ndr_syntax_id abstract_syntax;
1194 * A tower needs to have at least 4 floors to carry useful
1195 * information. Floor 3 is the transport identifier which defines
1196 * how many floors are required at least.
1198 if (tower->num_floors < 4) {
1199 return NT_STATUS_INVALID_PARAMETER;
1202 binding = talloc_zero(mem_ctx, struct dcerpc_binding);
1203 NT_STATUS_HAVE_NO_MEMORY(binding);
1205 ZERO_STRUCT(binding->object);
1206 binding->options = NULL;
1207 binding->host = NULL;
1208 binding->target_hostname = NULL;
1209 binding->flags = 0;
1210 binding->assoc_group_id = 0;
1212 binding->transport = dcerpc_transport_by_tower(tower);
1214 if (binding->transport == (unsigned int)-1) {
1215 talloc_free(binding);
1216 return NT_STATUS_NOT_SUPPORTED;
1219 /* Set abstract syntax */
1220 status = dcerpc_floor_get_lhs_data(&tower->floors[0], &abstract_syntax);
1221 if (!NT_STATUS_IS_OK(status)) {
1222 DEBUG(1, ("Error pulling abstract syntax: %s", nt_errstr(status)));
1223 return status;
1226 status = dcerpc_binding_set_abstract_syntax(binding, &abstract_syntax);
1227 if (!NT_STATUS_IS_OK(status)) {
1228 talloc_free(binding);
1229 DEBUG(1, ("Error set object uuid and version: %s", nt_errstr(status)));
1230 return status;
1233 /* Ignore floor 1, it contains the NDR version info */
1235 binding->options = NULL;
1237 /* Set endpoint */
1238 errno = 0;
1239 if (tower->num_floors >= 4) {
1240 binding->endpoint = dcerpc_floor_get_rhs_data(binding, &tower->floors[3]);
1242 if (errno != 0) {
1243 int saved_errno = errno;
1244 talloc_free(binding);
1245 return map_nt_error_from_unix_common(saved_errno);
1248 /* Set network address */
1249 errno = 0;
1250 if (tower->num_floors >= 5) {
1251 binding->host = dcerpc_floor_get_rhs_data(binding, &tower->floors[4]);
1253 if (errno != 0) {
1254 int saved_errno = errno;
1255 talloc_free(binding);
1256 return map_nt_error_from_unix_common(saved_errno);
1258 binding->target_hostname = binding->host;
1260 *b_out = binding;
1261 return NT_STATUS_OK;
1264 _PUBLIC_ struct dcerpc_binding *dcerpc_binding_dup(TALLOC_CTX *mem_ctx,
1265 const struct dcerpc_binding *b)
1267 struct dcerpc_binding *n;
1268 uint32_t count;
1270 n = talloc_zero(mem_ctx, struct dcerpc_binding);
1271 if (n == NULL) {
1272 return NULL;
1275 n->transport = b->transport;
1276 n->object = b->object;
1277 n->flags = b->flags;
1278 n->assoc_group_id = b->assoc_group_id;
1280 if (b->object_string != NULL) {
1281 n->object_string = talloc_strdup(n, b->object_string);
1282 if (n->object_string == NULL) {
1283 talloc_free(n);
1284 return NULL;
1287 if (b->host != NULL) {
1288 n->host = talloc_strdup(n, b->host);
1289 if (n->host == NULL) {
1290 talloc_free(n);
1291 return NULL;
1295 if (b->target_hostname != NULL) {
1296 n->target_hostname = talloc_strdup(n, b->target_hostname);
1297 if (n->target_hostname == NULL) {
1298 talloc_free(n);
1299 return NULL;
1303 if (b->target_principal != NULL) {
1304 n->target_principal = talloc_strdup(n, b->target_principal);
1305 if (n->target_principal == NULL) {
1306 talloc_free(n);
1307 return NULL;
1311 if (b->endpoint != NULL) {
1312 n->endpoint = talloc_strdup(n, b->endpoint);
1313 if (n->endpoint == NULL) {
1314 talloc_free(n);
1315 return NULL;
1319 for (count = 0; b->options && b->options[count]; count++);
1321 if (count > 0) {
1322 uint32_t i;
1324 n->options = talloc_array(n, const char *, count + 1);
1325 if (n->options == NULL) {
1326 talloc_free(n);
1327 return NULL;
1330 for (i = 0; i < count; i++) {
1331 n->options[i] = talloc_strdup(n->options, b->options[i]);
1332 if (n->options[i] == NULL) {
1333 talloc_free(n);
1334 return NULL;
1337 n->options[count] = NULL;
1340 return n;
1343 _PUBLIC_ NTSTATUS dcerpc_binding_build_tower(TALLOC_CTX *mem_ctx,
1344 const struct dcerpc_binding *binding,
1345 struct epm_tower *tower)
1347 const enum epm_protocol *protseq = NULL;
1348 int num_protocols = -1, i;
1349 struct ndr_syntax_id abstract_syntax;
1350 NTSTATUS status;
1352 /* Find transport */
1353 for (i=0;i<ARRAY_SIZE(transports);i++) {
1354 if (transports[i].transport == binding->transport) {
1355 protseq = transports[i].protseq;
1356 num_protocols = transports[i].num_protocols;
1357 break;
1361 if (num_protocols == -1) {
1362 DEBUG(0, ("Unable to find transport with id '%d'\n", binding->transport));
1363 return NT_STATUS_UNSUCCESSFUL;
1366 tower->num_floors = 2 + num_protocols;
1367 tower->floors = talloc_array(mem_ctx, struct epm_floor, tower->num_floors);
1369 /* Floor 0 */
1370 tower->floors[0].lhs.protocol = EPM_PROTOCOL_UUID;
1372 abstract_syntax = dcerpc_binding_get_abstract_syntax(binding);
1373 tower->floors[0].lhs.lhs_data = dcerpc_floor_pack_lhs_data(tower->floors,
1374 &abstract_syntax);
1376 if (!dcerpc_floor_pack_rhs_if_version_data(
1377 tower->floors, &abstract_syntax,
1378 &tower->floors[0].rhs.uuid.unknown)) {
1379 return NT_STATUS_NO_MEMORY;
1382 /* Floor 1 */
1383 tower->floors[1].lhs.protocol = EPM_PROTOCOL_UUID;
1385 tower->floors[1].lhs.lhs_data = dcerpc_floor_pack_lhs_data(tower->floors,
1386 &ndr_transfer_syntax_ndr);
1388 tower->floors[1].rhs.uuid.unknown = data_blob_talloc_zero(tower->floors, 2);
1390 /* Floor 2 to num_protocols */
1391 for (i = 0; i < num_protocols; i++) {
1392 tower->floors[2 + i].lhs.protocol = protseq[i];
1393 tower->floors[2 + i].lhs.lhs_data = data_blob_null;
1394 ZERO_STRUCT(tower->floors[2 + i].rhs);
1395 status = dcerpc_floor_set_rhs_data(tower->floors,
1396 &tower->floors[2 + i],
1397 NULL);
1398 if (!NT_STATUS_IS_OK(status)) {
1399 return status;
1403 /* The 4th floor contains the endpoint */
1404 if (num_protocols >= 2 && binding->endpoint) {
1405 status = dcerpc_floor_set_rhs_data(tower->floors,
1406 &tower->floors[3],
1407 binding->endpoint);
1408 if (!NT_STATUS_IS_OK(status)) {
1409 return status;
1413 /* The 5th contains the network address */
1414 if (num_protocols >= 3 && binding->host) {
1415 status = dcerpc_floor_set_rhs_data(tower->floors,
1416 &tower->floors[4],
1417 binding->host);
1418 if (!NT_STATUS_IS_OK(status)) {
1419 return status;
1423 return NT_STATUS_OK;