librpc/rpc: allow "assoc_group_id" in dcerpc_binding_[g|s]et_string()
[gd/samba-autobuild/.git] / librpc / rpc / binding.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    dcerpc utility functions
5
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
11
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.
16
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.
21
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/>.
24 */
25
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"
32
33 #undef strcasecmp
34 #undef strncasecmp
35
36 #define MAX_PROTSEQ             10
37
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 },
68         },
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 },
74         },
75 };
76
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},
93 };
94
95 static const struct ncacn_option *ncacn_option_by_name(const char *name)
96 {
97         size_t i;
98
99         for (i=0; i<ARRAY_SIZE(ncacn_options); i++) {
100                 int ret;
101
102                 ret = strcasecmp(ncacn_options[i].name, name);
103                 if (ret != 0) {
104                         continue;
105                 }
106
107                 return &ncacn_options[i];
108         }
109
110         return NULL;
111 }
112
113 const char *epm_floor_string(TALLOC_CTX *mem_ctx, struct epm_floor *epm_floor)
114 {
115         struct ndr_syntax_id syntax;
116         NTSTATUS status;
117
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;
124
125                                 if (GUID_equal(&syntax.uuid, &ndr_transfer_syntax_ndr.uuid)) {
126                                         return "NDR";
127                                 } 
128
129                                 if (GUID_equal(&syntax.uuid, &ndr_transfer_syntax_ndr64.uuid)) {
130                                         return "NDR64";
131                                 } 
132
133                                 uuidstr = GUID_string(mem_ctx, &syntax.uuid);
134
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));
139                         }
140
141                 case EPM_PROTOCOL_NCACN:
142                         return "RPC-C";
143
144                 case EPM_PROTOCOL_NCADG:
145                         return "RPC";
146
147                 case EPM_PROTOCOL_NCALRPC:
148                         return "NCALRPC";
149
150                 case EPM_PROTOCOL_DNET_NSP:
151                         return "DNET/NSP";
152
153                 case EPM_PROTOCOL_IP:
154                         return talloc_asprintf(mem_ctx, "IP:%s", epm_floor->rhs.ip.ipaddr);
155
156                 case EPM_PROTOCOL_NAMED_PIPE:
157                         return talloc_asprintf(mem_ctx, "NAMED-PIPE:%s", epm_floor->rhs.named_pipe.path);
158
159                 case EPM_PROTOCOL_SMB:
160                         return talloc_asprintf(mem_ctx, "SMB:%s", epm_floor->rhs.smb.unc);
161
162                 case EPM_PROTOCOL_UNIX_DS:
163                         return talloc_asprintf(mem_ctx, "Unix:%s", epm_floor->rhs.unix_ds.path);
164
165                 case EPM_PROTOCOL_NETBIOS:
166                         return talloc_asprintf(mem_ctx, "NetBIOS:%s", epm_floor->rhs.netbios.name);
167
168                 case EPM_PROTOCOL_NETBEUI:
169                         return "NETBeui";
170
171                 case EPM_PROTOCOL_SPX:
172                         return "SPX";
173
174                 case EPM_PROTOCOL_NB_IPX:
175                         return "NB_IPX";
176
177                 case EPM_PROTOCOL_HTTP:
178                         return talloc_asprintf(mem_ctx, "HTTP:%d", epm_floor->rhs.http.port);
179
180                 case EPM_PROTOCOL_TCP:
181                         return talloc_asprintf(mem_ctx, "TCP:%d", epm_floor->rhs.tcp.port);
182
183                 case EPM_PROTOCOL_UDP:
184                         return talloc_asprintf(mem_ctx, "UDP:%d", epm_floor->rhs.udp.port);
185
186                 default:
187                         return talloc_asprintf(mem_ctx, "UNK(%02x):", epm_floor->lhs.protocol);
188         }
189 }
190
191
192 /*
193   form a binding string from a binding structure
194 */
195 _PUBLIC_ char *dcerpc_binding_string(TALLOC_CTX *mem_ctx, const struct dcerpc_binding *b)
196 {
197         char *s = talloc_strdup(mem_ctx, "");
198         char *o = s;
199         int i;
200         const char *t_name = NULL;
201
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;
207                 }
208         }
209
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;
217                 }
218         }
219
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;
226                 }
227         }
228
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;
235                 }
236         }
237
238         if (!b->endpoint && !b->options && !b->flags) {
239                 return s;
240         }
241
242         o = s;
243         s = talloc_asprintf_append_buffer(s, "[");
244         if (s == NULL) {
245                 talloc_free(o);
246                 return NULL;
247         }
248
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;
255                 }
256         }
257
258         for (i=0;i<ARRAY_SIZE(ncacn_options);i++) {
259                 if (!(b->flags & ncacn_options[i].flag)) {
260                         continue;
261                 }
262
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;
268                 }
269         }
270
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;
277                 }
278         }
279
280         o = s;
281         s = talloc_asprintf_append_buffer(s, "]");
282         if (s == NULL) {
283                 talloc_free(o);
284                 return NULL;
285         }
286
287         return s;
288 }
289
290 /*
291   parse a binding string into a dcerpc_binding structure
292 */
293 _PUBLIC_ NTSTATUS dcerpc_parse_binding(TALLOC_CTX *mem_ctx, const char *_s, struct dcerpc_binding **b_out)
294 {
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;
302
303         b = talloc_zero(mem_ctx, struct dcerpc_binding);
304         if (!b) {
305                 return NT_STATUS_NO_MEMORY;
306         }
307
308         _t = talloc_strdup(b, _s);
309         if (_t == NULL) {
310                 talloc_free(b);
311                 return NT_STATUS_NO_MEMORY;
312         }
313
314         s = _t;
315
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;
323                 }
324                 options[strlen(options)-1] = 0;
325         }
326
327         p = strchr(s, '@');
328
329         if (p && PTR_DIFF(p, s) == 36) { /* 36 is the length of a UUID */
330                 *p = '\0';
331
332                 status = dcerpc_binding_set_string_option(b, "object", s);
333                 if (!NT_STATUS_IS_OK(status)) {
334                         talloc_free(b);
335                         return status;
336                 }
337
338                 s = p + 1;
339         }
340
341         p = strchr(s, ':');
342
343         if (p == NULL) {
344                 b->transport = NCA_UNKNOWN;
345         } else {
346                 *p = '\0';
347
348                 status = dcerpc_binding_set_string_option(b, "transport", s);
349                 if (!NT_STATUS_IS_OK(status)) {
350                         talloc_free(b);
351                         return status;
352                 }
353
354                 s = p + 1;
355         }
356
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;
362                 }
363
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;
368                 }
369         }
370
371         for (i=0; options != NULL; i++) {
372                 const char *name = options;
373                 const char *value = NULL;
374
375                 p = strchr(options, ',');
376                 if (p != NULL) {
377                         *p = '\0';
378                         options = p+1;
379                 } else {
380                         options = NULL;
381                 }
382
383                 p = strchr(name, '=');
384                 if (p != NULL) {
385                         *p = '\0';
386                         value = p + 1;
387                 }
388
389                 if (value == NULL) {
390                         /*
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.
395                          */
396                         const struct ncacn_option *no = NULL;
397
398                         value = name;
399
400                         no = ncacn_option_by_name(name);
401                         if (no == NULL) {
402                                 if (i > 0) {
403                                         /*
404                                          * we don't allow unknown options
405                                          */
406                                         return NT_STATUS_INVALID_PARAMETER_MIX;
407                                 }
408
409                                 /*
410                                  * This is the endpoint
411                                  */
412                                 name = "endpoint";
413                                 if (strlen(value) == 0) {
414                                         value = NULL;
415                                 }
416                         }
417                 }
418
419                 status = dcerpc_binding_set_string_option(b, name, value);
420                 if (!NT_STATUS_IS_OK(status)) {
421                         talloc_free(b);
422                         return status;
423                 }
424         }
425
426         talloc_free(_t);
427         *b_out = b;
428         return NT_STATUS_OK;
429 }
430
431 _PUBLIC_ struct GUID dcerpc_binding_get_object(const struct dcerpc_binding *b)
432 {
433         return b->object.uuid;
434 }
435
436 _PUBLIC_ NTSTATUS dcerpc_binding_set_object(struct dcerpc_binding *b,
437                                             struct GUID object)
438 {
439         char *tmp = discard_const_p(char, b->object_string);
440
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;
446         }
447
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;
452         }
453         talloc_free(tmp);
454
455         ZERO_STRUCT(b->object);
456         b->object.uuid = object;
457         return NT_STATUS_OK;
458 }
459
460 _PUBLIC_ enum dcerpc_transport_t dcerpc_binding_get_transport(const struct dcerpc_binding *b)
461 {
462         return b->transport;
463 }
464
465 _PUBLIC_ NTSTATUS dcerpc_binding_set_transport(struct dcerpc_binding *b,
466                                                enum dcerpc_transport_t transport)
467 {
468         char *tmp = discard_const_p(char, b->endpoint);
469
470         /*
471          * TODO: we may want to check the transport value is
472          * wellknown.
473          */
474         if (b->transport == transport) {
475                 return NT_STATUS_OK;
476         }
477
478         b->transport = transport;
479
480         /*
481          * This implicitly resets the endpoint
482          * as the endpoint is transport specific.
483          *
484          * TODO: in future we may reset more options
485          * here.
486          */
487         talloc_free(tmp);
488         b->endpoint = NULL;
489
490         return NT_STATUS_OK;
491 }
492
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)
496 {
497         enum dcerpc_AuthType auth_type;
498         enum dcerpc_AuthLevel auth_level;
499
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;
510         }
511
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;
522         }
523
524         if (_auth_type == NULL) {
525                 *_auth_type = auth_type;
526         }
527
528         if (_auth_level == NULL) {
529                 *_auth_level = auth_level;
530         }
531 }
532
533 _PUBLIC_ uint32_t dcerpc_binding_get_assoc_group_id(const struct dcerpc_binding *b)
534 {
535         return b->assoc_group_id;
536 }
537
538 _PUBLIC_ NTSTATUS dcerpc_binding_set_assoc_group_id(struct dcerpc_binding *b,
539                                                     uint32_t assoc_group_id)
540 {
541         b->assoc_group_id = assoc_group_id;
542         return NT_STATUS_OK;
543 }
544
545 _PUBLIC_ const char *dcerpc_binding_get_string_option(const struct dcerpc_binding *b,
546                                                       const char *name)
547 {
548         struct {
549                 const char *name;
550                 const char *value;
551 #define _SPECIAL(x) { .name = #x, .value = b->x, }
552         } specials[] = {
553                 { .name = "object", .value = b->object_string, },
554                 _SPECIAL(host),
555                 _SPECIAL(endpoint),
556                 _SPECIAL(target_hostname),
557                 _SPECIAL(target_principal),
558 #undef _SPECIAL
559         };
560         const struct ncacn_option *no = NULL;
561         size_t name_len = strlen(name);
562         size_t i;
563         int ret;
564
565         ret = strcmp(name, "transport");
566         if (ret == 0) {
567                 return derpc_transport_string_by_transport(b->transport);
568         }
569
570         ret = strcmp(name, "assoc_group_id");
571         if (ret == 0) {
572                 char *tmp = discard_const_p(char, b->assoc_group_string);
573
574                 if (b->assoc_group_id == 0) {
575                         return NULL;
576                 }
577
578                 snprintf(tmp, sizeof(b->assoc_group_string),
579                          "0x%08x", b->assoc_group_id);
580                 return (const char *)b->assoc_group_string;
581         }
582
583         for (i=0; i < ARRAY_SIZE(specials); i++) {
584                 ret = strcmp(specials[i].name, name);
585                 if (ret != 0) {
586                         continue;
587                 }
588
589                 return specials[i].value;
590         }
591
592         no = ncacn_option_by_name(name);
593         if (no != NULL) {
594                 if (b->flags & no->flag) {
595                         return no->name;
596                 }
597
598                 return NULL;
599         }
600
601         if (b->options == NULL) {
602                 return NULL;
603         }
604
605         for (i=0; b->options[i]; i++) {
606                 const char *o = b->options[i];
607                 const char *vs = NULL;
608
609                 ret = strncmp(name, o, name_len);
610                 if (ret != 0) {
611                         continue;
612                 }
613
614                 if (o[name_len] != '=') {
615                         continue;
616                 }
617
618                 vs = &o[name_len + 1];
619
620                 return vs;
621         }
622
623         return NULL;
624 }
625
626 _PUBLIC_ char *dcerpc_binding_copy_string_option(TALLOC_CTX *mem_ctx,
627                                                  const struct dcerpc_binding *b,
628                                                  const char *name)
629 {
630         const char *c = dcerpc_binding_get_string_option(b, name);
631         char *v;
632
633         if (c == NULL) {
634                 errno = ENOENT;
635                 return NULL;
636         }
637
638         v = talloc_strdup(mem_ctx, c);
639         if (v == NULL) {
640                 errno = ENOMEM;
641                 return NULL;
642         }
643
644         return v;
645 }
646
647 _PUBLIC_ NTSTATUS dcerpc_binding_set_string_option(struct dcerpc_binding *b,
648                                                    const char *name,
649                                                    const char *value)
650 {
651         struct {
652                 const char *name;
653                 const char **ptr;
654 #define _SPECIAL(x) { .name = #x, .ptr = &b->x, }
655         } specials[] = {
656                 _SPECIAL(host),
657                 _SPECIAL(endpoint),
658                 _SPECIAL(target_hostname),
659                 _SPECIAL(target_principal),
660 #undef _SPECIAL
661         };
662         const struct ncacn_option *no = NULL;
663         size_t name_len = strlen(name);
664         const char *opt = NULL;
665         char *tmp;
666         size_t i;
667         int ret;
668
669         /*
670          * Note: value == NULL, means delete it.
671          * value != NULL means add or reset.
672          */
673
674         ret = strcmp(name, "transport");
675         if (ret == 0) {
676                 enum dcerpc_transport_t t = dcerpc_transport_by_name(value);
677
678                 if (t == NCA_UNKNOWN && value != NULL) {
679                         return NT_STATUS_INVALID_PARAMETER_MIX;
680                 }
681
682                 return dcerpc_binding_set_transport(b, t);
683         }
684
685         ret = strcmp(name, "object");
686         if (ret == 0) {
687                 NTSTATUS status;
688                 struct GUID uuid = GUID_zero();
689
690                 if (value != NULL) {
691                         DATA_BLOB blob;
692                         blob = data_blob_string_const(value);
693                         if (blob.length != 36) {
694                                 return NT_STATUS_INVALID_PARAMETER_MIX;
695                         }
696
697                         status = GUID_from_data_blob(&blob, &uuid);
698                         if (!NT_STATUS_IS_OK(status)) {
699                                 return status;
700                         }
701                 }
702
703                 return dcerpc_binding_set_object(b, uuid);
704         }
705
706         ret = strcmp(name, "assoc_group_id");
707         if (ret == 0) {
708                 uint32_t assoc_group_id = 0;
709
710                 if (value != NULL) {
711                         char c;
712
713                         ret = sscanf(value, "0x%08x%c", &assoc_group_id, &c);
714                         if (ret != 1) {
715                                 return NT_STATUS_INVALID_PARAMETER_MIX;
716                         }
717                 }
718
719                 return dcerpc_binding_set_assoc_group_id(b, assoc_group_id);
720         }
721
722         for (i=0; i < ARRAY_SIZE(specials); i++) {
723                 ret = strcmp(specials[i].name, name);
724                 if (ret != 0) {
725                         continue;
726                 }
727
728                 tmp = discard_const_p(char, *specials[i].ptr);
729
730                 if (value == NULL) {
731                         talloc_free(tmp);
732                         *specials[i].ptr = NULL;
733                         return NT_STATUS_OK;
734                 }
735
736                 if (value[0] == '\0') {
737                         return NT_STATUS_INVALID_PARAMETER_MIX;
738                 }
739
740                 *specials[i].ptr = talloc_strdup(b, value);
741                 if (*specials[i].ptr == NULL) {
742                         *specials[i].ptr = tmp;
743                         return NT_STATUS_NO_MEMORY;
744                 }
745                 talloc_free(tmp);
746
747                 return NT_STATUS_OK;
748         }
749
750         no = ncacn_option_by_name(name);
751         if (no != NULL) {
752                 if (value == NULL) {
753                         b->flags &= ~no->flag;
754                         return NT_STATUS_OK;
755                 }
756
757                 ret = strcasecmp(no->name, value);
758                 if (ret != 0) {
759                         return NT_STATUS_INVALID_PARAMETER_MIX;
760                 }
761
762                 b->flags |= no->flag;
763                 return NT_STATUS_OK;
764         }
765
766         for (i=0; b->options && b->options[i]; i++) {
767                 const char *o = b->options[i];
768
769                 ret = strncmp(name, o, name_len);
770                 if (ret != 0) {
771                         continue;
772                 }
773
774                 if (o[name_len] != '=') {
775                         continue;
776                 }
777
778                 opt = o;
779                 break;
780         }
781
782         if (opt == NULL) {
783                 const char **n;
784
785                 if (value == NULL) {
786                         return NT_STATUS_OK;
787                 }
788
789                 n = talloc_realloc(b, b->options, const char *, i + 2);
790                 if (n == NULL) {
791                         return NT_STATUS_NO_MEMORY;
792                 }
793                 n[i] = NULL;
794                 n[i + 1] = NULL;
795                 b->options = n;
796         }
797
798         tmp = discard_const_p(char, opt);
799
800         if (value == NULL) {
801                 for (;b->options[i];i++) {
802                         b->options[i] = b->options[i+1];
803                 }
804                 talloc_free(tmp);
805                 return NT_STATUS_OK;
806         }
807
808         b->options[i] = talloc_asprintf(b->options, "%s=%s",
809                                         name, value);
810         if (b->options[i] == NULL) {
811                 b->options[i] = tmp;
812                 return NT_STATUS_NO_MEMORY;
813         }
814
815         return NT_STATUS_OK;
816 }
817
818 _PUBLIC_ uint32_t dcerpc_binding_get_flags(const struct dcerpc_binding *b)
819 {
820         return b->flags;
821 }
822
823 _PUBLIC_ NTSTATUS dcerpc_binding_set_flags(struct dcerpc_binding *b,
824                                            uint32_t additional,
825                                            uint32_t clear)
826 {
827         /*
828          * TODO: in future we may want to reject invalid combinations
829          */
830         b->flags &= ~clear;
831         b->flags |= additional;
832
833         return NT_STATUS_OK;
834 }
835
836 _PUBLIC_ NTSTATUS dcerpc_floor_get_lhs_data(const struct epm_floor *epm_floor,
837                                             struct ndr_syntax_id *syntax)
838 {
839         TALLOC_CTX *mem_ctx = talloc_init("floor_get_lhs_data");
840         struct ndr_pull *ndr;
841         enum ndr_err_code ndr_err;
842         uint16_t if_version=0;
843
844         ndr = ndr_pull_init_blob(&epm_floor->lhs.lhs_data, mem_ctx);
845         if (ndr == NULL) {
846                 talloc_free(mem_ctx);
847                 return NT_STATUS_NO_MEMORY;
848         }
849         ndr->flags |= LIBNDR_FLAG_NOALIGN;
850
851         ndr_err = ndr_pull_GUID(ndr, NDR_SCALARS | NDR_BUFFERS, &syntax->uuid);
852         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
853                 talloc_free(mem_ctx);
854                 return ndr_map_error2ntstatus(ndr_err);
855         }
856
857         ndr_err = ndr_pull_uint16(ndr, NDR_SCALARS, &if_version);
858         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
859                 talloc_free(mem_ctx);
860                 return ndr_map_error2ntstatus(ndr_err);
861         }
862
863         syntax->if_version = if_version;
864
865         talloc_free(mem_ctx);
866
867         return NT_STATUS_OK;
868 }
869
870 static DATA_BLOB dcerpc_floor_pack_lhs_data(TALLOC_CTX *mem_ctx, const struct ndr_syntax_id *syntax)
871 {
872         DATA_BLOB blob;
873         enum ndr_err_code ndr_err;
874         struct ndr_push *ndr;
875
876         ndr = ndr_push_init_ctx(mem_ctx);
877         if (ndr == NULL) {
878                 return data_blob_null;
879         }
880
881         ndr->flags |= LIBNDR_FLAG_NOALIGN;
882
883         ndr_err = ndr_push_GUID(ndr, NDR_SCALARS | NDR_BUFFERS, &syntax->uuid);
884         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
885                 return data_blob_null;
886         }
887         ndr_err = ndr_push_uint16(ndr, NDR_SCALARS, syntax->if_version);
888         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
889                 return data_blob_null;
890         }
891
892         blob = ndr_push_blob(ndr);
893         talloc_steal(mem_ctx, blob.data);
894         talloc_free(ndr);
895         return blob;
896 }
897
898 static bool dcerpc_floor_pack_rhs_if_version_data(
899         TALLOC_CTX *mem_ctx, const struct ndr_syntax_id *syntax,
900         DATA_BLOB *pblob)
901 {
902         DATA_BLOB blob;
903         struct ndr_push *ndr = ndr_push_init_ctx(mem_ctx);
904         enum ndr_err_code ndr_err;
905
906         if (ndr == NULL) {
907                 return false;
908         }
909
910         ndr->flags |= LIBNDR_FLAG_NOALIGN;
911
912         ndr_err = ndr_push_uint16(ndr, NDR_SCALARS, syntax->if_version >> 16);
913         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
914                 return false;
915         }
916
917         blob = ndr_push_blob(ndr);
918         talloc_steal(mem_ctx, blob.data);
919         talloc_free(ndr);
920         *pblob = blob;
921         return true;
922 }
923
924 char *dcerpc_floor_get_rhs_data(TALLOC_CTX *mem_ctx, struct epm_floor *epm_floor)
925 {
926         switch (epm_floor->lhs.protocol) {
927         case EPM_PROTOCOL_TCP:
928                 if (epm_floor->rhs.tcp.port == 0) return NULL;
929                 return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.tcp.port);
930
931         case EPM_PROTOCOL_UDP:
932                 if (epm_floor->rhs.udp.port == 0) return NULL;
933                 return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.udp.port);
934
935         case EPM_PROTOCOL_HTTP:
936                 if (epm_floor->rhs.http.port == 0) return NULL;
937                 return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.http.port);
938
939         case EPM_PROTOCOL_IP:
940                 return talloc_strdup(mem_ctx, epm_floor->rhs.ip.ipaddr);
941
942         case EPM_PROTOCOL_NCACN:
943                 return NULL;
944
945         case EPM_PROTOCOL_NCADG:
946                 return NULL;
947
948         case EPM_PROTOCOL_SMB:
949                 if (strlen(epm_floor->rhs.smb.unc) == 0) return NULL;
950                 return talloc_strdup(mem_ctx, epm_floor->rhs.smb.unc);
951
952         case EPM_PROTOCOL_NAMED_PIPE:
953                 if (strlen(epm_floor->rhs.named_pipe.path) == 0) return NULL;
954                 return talloc_strdup(mem_ctx, epm_floor->rhs.named_pipe.path);
955
956         case EPM_PROTOCOL_NETBIOS:
957                 if (strlen(epm_floor->rhs.netbios.name) == 0) return NULL;
958                 return talloc_strdup(mem_ctx, epm_floor->rhs.netbios.name);
959
960         case EPM_PROTOCOL_NCALRPC:
961                 return NULL;
962
963         case EPM_PROTOCOL_VINES_SPP:
964                 return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.vines_spp.port);
965
966         case EPM_PROTOCOL_VINES_IPC:
967                 return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.vines_ipc.port);
968
969         case EPM_PROTOCOL_STREETTALK:
970                 return talloc_strdup(mem_ctx, epm_floor->rhs.streettalk.streettalk);
971
972         case EPM_PROTOCOL_UNIX_DS:
973                 if (strlen(epm_floor->rhs.unix_ds.path) == 0) return NULL;
974                 return talloc_strdup(mem_ctx, epm_floor->rhs.unix_ds.path);
975
976         case EPM_PROTOCOL_NULL:
977                 return NULL;
978
979         default:
980                 DEBUG(0,("Unsupported lhs protocol %d\n", epm_floor->lhs.protocol));
981                 break;
982         }
983
984         return NULL;
985 }
986
987 static NTSTATUS dcerpc_floor_set_rhs_data(TALLOC_CTX *mem_ctx, 
988                                           struct epm_floor *epm_floor,  
989                                           const char *data)
990 {
991         if (data == NULL) {
992                 data = "";
993         }
994
995         switch (epm_floor->lhs.protocol) {
996         case EPM_PROTOCOL_TCP:
997                 epm_floor->rhs.tcp.port = atoi(data);
998                 return NT_STATUS_OK;
999
1000         case EPM_PROTOCOL_UDP:
1001                 epm_floor->rhs.udp.port = atoi(data);
1002                 return NT_STATUS_OK;
1003
1004         case EPM_PROTOCOL_HTTP:
1005                 epm_floor->rhs.http.port = atoi(data);
1006                 return NT_STATUS_OK;
1007
1008         case EPM_PROTOCOL_IP:
1009                 if (!is_ipaddress_v4(data)) {
1010                         data = "0.0.0.0";
1011                 }
1012                 epm_floor->rhs.ip.ipaddr = talloc_strdup(mem_ctx, data);
1013                 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.ip.ipaddr);
1014                 return NT_STATUS_OK;
1015
1016         case EPM_PROTOCOL_NCACN:
1017                 epm_floor->rhs.ncacn.minor_version = 0;
1018                 return NT_STATUS_OK;
1019
1020         case EPM_PROTOCOL_NCADG:
1021                 epm_floor->rhs.ncadg.minor_version = 0;
1022                 return NT_STATUS_OK;
1023
1024         case EPM_PROTOCOL_SMB:
1025                 epm_floor->rhs.smb.unc = talloc_strdup(mem_ctx, data);
1026                 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.smb.unc);
1027                 return NT_STATUS_OK;
1028
1029         case EPM_PROTOCOL_NAMED_PIPE:
1030                 epm_floor->rhs.named_pipe.path = talloc_strdup(mem_ctx, data);
1031                 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.named_pipe.path);
1032                 return NT_STATUS_OK;
1033
1034         case EPM_PROTOCOL_NETBIOS:
1035                 epm_floor->rhs.netbios.name = talloc_strdup(mem_ctx, data);
1036                 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.netbios.name);
1037                 return NT_STATUS_OK;
1038
1039         case EPM_PROTOCOL_NCALRPC:
1040                 return NT_STATUS_OK;
1041
1042         case EPM_PROTOCOL_VINES_SPP:
1043                 epm_floor->rhs.vines_spp.port = atoi(data);
1044                 return NT_STATUS_OK;
1045
1046         case EPM_PROTOCOL_VINES_IPC:
1047                 epm_floor->rhs.vines_ipc.port = atoi(data);
1048                 return NT_STATUS_OK;
1049
1050         case EPM_PROTOCOL_STREETTALK:
1051                 epm_floor->rhs.streettalk.streettalk = talloc_strdup(mem_ctx, data);
1052                 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.streettalk.streettalk);
1053                 return NT_STATUS_OK;
1054
1055         case EPM_PROTOCOL_UNIX_DS:
1056                 epm_floor->rhs.unix_ds.path = talloc_strdup(mem_ctx, data);
1057                 NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.unix_ds.path);
1058                 return NT_STATUS_OK;
1059
1060         case EPM_PROTOCOL_NULL:
1061                 return NT_STATUS_OK;
1062
1063         default:
1064                 DEBUG(0,("Unsupported lhs protocol %d\n", epm_floor->lhs.protocol));
1065                 break;
1066         }
1067
1068         return NT_STATUS_NOT_SUPPORTED;
1069 }
1070
1071 enum dcerpc_transport_t dcerpc_transport_by_endpoint_protocol(int prot)
1072 {
1073         int i;
1074
1075         /* Find a transport that has 'prot' as 4th protocol */
1076         for (i=0;i<ARRAY_SIZE(transports);i++) {
1077                 if (transports[i].num_protocols >= 2 && 
1078                         transports[i].protseq[1] == prot) {
1079                         return transports[i].transport;
1080                 }
1081         }
1082
1083         /* Unknown transport */
1084         return (unsigned int)-1;
1085 }
1086
1087 _PUBLIC_ enum dcerpc_transport_t dcerpc_transport_by_tower(const struct epm_tower *tower)
1088 {
1089         int i;
1090
1091         /* Find a transport that matches this tower */
1092         for (i=0;i<ARRAY_SIZE(transports);i++) {
1093                 int j;
1094                 if (transports[i].num_protocols != tower->num_floors - 2) {
1095                         continue; 
1096                 }
1097
1098                 for (j = 0; j < transports[i].num_protocols; j++) {
1099                         if (transports[i].protseq[j] != tower->floors[j+2].lhs.protocol) {
1100                                 break;
1101                         }
1102                 }
1103
1104                 if (j == transports[i].num_protocols) {
1105                         return transports[i].transport;
1106                 }
1107         }
1108
1109         /* Unknown transport */
1110         return (unsigned int)-1;
1111 }
1112
1113 _PUBLIC_ const char *derpc_transport_string_by_transport(enum dcerpc_transport_t t)
1114 {
1115         int i;
1116
1117         for (i=0; i<ARRAY_SIZE(transports); i++) {
1118                 if (t == transports[i].transport) {
1119                         return transports[i].name;
1120                 }
1121         }
1122         return NULL;
1123 }
1124
1125 _PUBLIC_ enum dcerpc_transport_t dcerpc_transport_by_name(const char *name)
1126 {
1127         size_t i;
1128
1129         if (name == NULL) {
1130                 return NCA_UNKNOWN;
1131         }
1132
1133         for (i=0; i<ARRAY_SIZE(transports);i++) {
1134                 if (strcasecmp(name, transports[i].name) == 0) {
1135                         return transports[i].transport;
1136                 }
1137         }
1138
1139         return NCA_UNKNOWN;
1140 }
1141
1142 _PUBLIC_ NTSTATUS dcerpc_binding_from_tower(TALLOC_CTX *mem_ctx,
1143                                             struct epm_tower *tower,
1144                                             struct dcerpc_binding **b_out)
1145 {
1146         NTSTATUS status;
1147         struct dcerpc_binding *binding;
1148
1149         /*
1150          * A tower needs to have at least 4 floors to carry useful
1151          * information. Floor 3 is the transport identifier which defines
1152          * how many floors are required at least.
1153          */
1154         if (tower->num_floors < 4) {
1155                 return NT_STATUS_INVALID_PARAMETER;
1156         }
1157
1158         binding = talloc_zero(mem_ctx, struct dcerpc_binding);
1159         NT_STATUS_HAVE_NO_MEMORY(binding);
1160
1161         ZERO_STRUCT(binding->object);
1162         binding->options = NULL;
1163         binding->host = NULL;
1164         binding->target_hostname = NULL;
1165         binding->flags = 0;
1166         binding->assoc_group_id = 0;
1167
1168         binding->transport = dcerpc_transport_by_tower(tower);
1169
1170         if (binding->transport == (unsigned int)-1) {
1171                 talloc_free(binding);
1172                 return NT_STATUS_NOT_SUPPORTED;
1173         }
1174
1175         /* Set object uuid */
1176         status = dcerpc_floor_get_lhs_data(&tower->floors[0], &binding->object);
1177
1178         if (!NT_STATUS_IS_OK(status)) {
1179                 DEBUG(1, ("Error pulling object uuid and version: %s", nt_errstr(status)));
1180                 talloc_free(binding);
1181                 return status;
1182         }
1183
1184         /* Ignore floor 1, it contains the NDR version info */
1185
1186         binding->options = NULL;
1187
1188         /* Set endpoint */
1189         errno = 0;
1190         if (tower->num_floors >= 4) {
1191                 binding->endpoint = dcerpc_floor_get_rhs_data(binding, &tower->floors[3]);
1192         }
1193         if (errno != 0) {
1194                 int saved_errno = errno;
1195                 talloc_free(binding);
1196                 return map_nt_error_from_unix_common(saved_errno);
1197         }
1198
1199         /* Set network address */
1200         errno = 0;
1201         if (tower->num_floors >= 5) {
1202                 binding->host = dcerpc_floor_get_rhs_data(binding, &tower->floors[4]);
1203         }
1204         if (errno != 0) {
1205                 int saved_errno = errno;
1206                 talloc_free(binding);
1207                 return map_nt_error_from_unix_common(saved_errno);
1208         }
1209         binding->target_hostname = binding->host;
1210
1211         *b_out = binding;
1212         return NT_STATUS_OK;
1213 }
1214
1215 _PUBLIC_ struct dcerpc_binding *dcerpc_binding_dup(TALLOC_CTX *mem_ctx,
1216                                                    const struct dcerpc_binding *b)
1217 {
1218         struct dcerpc_binding *n;
1219         uint32_t count;
1220
1221         n = talloc_zero(mem_ctx, struct dcerpc_binding);
1222         if (n == NULL) {
1223                 return NULL;
1224         }
1225
1226         n->transport = b->transport;
1227         n->object = b->object;
1228         n->flags = b->flags;
1229         n->assoc_group_id = b->assoc_group_id;
1230
1231         if (b->object_string != NULL) {
1232                 n->object_string = talloc_strdup(n, b->object_string);
1233                 if (n->object_string == NULL) {
1234                         talloc_free(n);
1235                         return NULL;
1236                 }
1237         }
1238         if (b->host != NULL) {
1239                 n->host = talloc_strdup(n, b->host);
1240                 if (n->host == NULL) {
1241                         talloc_free(n);
1242                         return NULL;
1243                 }
1244         }
1245
1246         if (b->target_hostname != NULL) {
1247                 n->target_hostname = talloc_strdup(n, b->target_hostname);
1248                 if (n->target_hostname == NULL) {
1249                         talloc_free(n);
1250                         return NULL;
1251                 }
1252         }
1253
1254         if (b->target_principal != NULL) {
1255                 n->target_principal = talloc_strdup(n, b->target_principal);
1256                 if (n->target_principal == NULL) {
1257                         talloc_free(n);
1258                         return NULL;
1259                 }
1260         }
1261
1262         if (b->endpoint != NULL) {
1263                 n->endpoint = talloc_strdup(n, b->endpoint);
1264                 if (n->endpoint == NULL) {
1265                         talloc_free(n);
1266                         return NULL;
1267                 }
1268         }
1269
1270         for (count = 0; b->options && b->options[count]; count++);
1271
1272         if (count > 0) {
1273                 uint32_t i;
1274
1275                 n->options = talloc_array(n, const char *, count + 1);
1276                 if (n->options == NULL) {
1277                         talloc_free(n);
1278                         return NULL;
1279                 }
1280
1281                 for (i = 0; i < count; i++) {
1282                         n->options[i] = talloc_strdup(n->options, b->options[i]);
1283                         if (n->options[i] == NULL) {
1284                                 talloc_free(n);
1285                                 return NULL;
1286                         }
1287                 }
1288                 n->options[count] = NULL;
1289         }
1290
1291         return n;
1292 }
1293
1294 _PUBLIC_ NTSTATUS dcerpc_binding_build_tower(TALLOC_CTX *mem_ctx,
1295                                              const struct dcerpc_binding *binding,
1296                                              struct epm_tower *tower)
1297 {
1298         const enum epm_protocol *protseq = NULL;
1299         int num_protocols = -1, i;
1300         NTSTATUS status;
1301
1302         /* Find transport */
1303         for (i=0;i<ARRAY_SIZE(transports);i++) {
1304                 if (transports[i].transport == binding->transport) {
1305                         protseq = transports[i].protseq;
1306                         num_protocols = transports[i].num_protocols;
1307                         break;
1308                 }
1309         }
1310
1311         if (num_protocols == -1) {
1312                 DEBUG(0, ("Unable to find transport with id '%d'\n", binding->transport));
1313                 return NT_STATUS_UNSUCCESSFUL;
1314         }
1315
1316         tower->num_floors = 2 + num_protocols;
1317         tower->floors = talloc_array(mem_ctx, struct epm_floor, tower->num_floors);
1318
1319         /* Floor 0 */
1320         tower->floors[0].lhs.protocol = EPM_PROTOCOL_UUID;
1321
1322         tower->floors[0].lhs.lhs_data = dcerpc_floor_pack_lhs_data(tower->floors, &binding->object);
1323
1324         if (!dcerpc_floor_pack_rhs_if_version_data(
1325                     tower->floors, &binding->object,
1326                     &tower->floors[0].rhs.uuid.unknown)) {
1327                 return NT_STATUS_NO_MEMORY;
1328         }
1329
1330         /* Floor 1 */
1331         tower->floors[1].lhs.protocol = EPM_PROTOCOL_UUID;
1332
1333         tower->floors[1].lhs.lhs_data = dcerpc_floor_pack_lhs_data(tower->floors, 
1334                                                                 &ndr_transfer_syntax_ndr);
1335
1336         tower->floors[1].rhs.uuid.unknown = data_blob_talloc_zero(tower->floors, 2);
1337
1338         /* Floor 2 to num_protocols */
1339         for (i = 0; i < num_protocols; i++) {
1340                 tower->floors[2 + i].lhs.protocol = protseq[i];
1341                 tower->floors[2 + i].lhs.lhs_data = data_blob_null;
1342                 ZERO_STRUCT(tower->floors[2 + i].rhs);
1343                 status = dcerpc_floor_set_rhs_data(tower->floors,
1344                                                    &tower->floors[2 + i],
1345                                                    NULL);
1346                 if (!NT_STATUS_IS_OK(status)) {
1347                         return status;
1348                 }
1349         }
1350
1351         /* The 4th floor contains the endpoint */
1352         if (num_protocols >= 2 && binding->endpoint) {
1353                 status = dcerpc_floor_set_rhs_data(tower->floors,
1354                                                    &tower->floors[3],
1355                                                    binding->endpoint);
1356                 if (!NT_STATUS_IS_OK(status)) {
1357                         return status;
1358                 }
1359         }
1360
1361         /* The 5th contains the network address */
1362         if (num_protocols >= 3 && binding->host) {
1363                 status = dcerpc_floor_set_rhs_data(tower->floors,
1364                                                    &tower->floors[4],
1365                                                    binding->host);
1366                 if (!NT_STATUS_IS_OK(status)) {
1367                         return status;
1368                 }
1369         }
1370
1371         return NT_STATUS_OK;
1372 }