r3168: Use generic function for looking up endpoints
[kai/samba-autobuild/.git] / source4 / librpc / rpc / dcerpc_util.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    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25
26
27 /*
28   find the pipe name for a local IDL interface
29 */
30 const char *idl_pipe_name(const char *uuid, uint32_t if_version)
31 {
32         int i;
33         for (i=0;dcerpc_pipes[i];i++) {
34                 if (strcasecmp(dcerpc_pipes[i]->uuid, uuid) == 0 &&
35                     dcerpc_pipes[i]->if_version == if_version) {
36                         return dcerpc_pipes[i]->name;
37                 }
38         }
39         return "UNKNOWN";
40 }
41
42 /*
43   find the number of calls defined by local IDL
44 */
45 int idl_num_calls(const char *uuid, uint32_t if_version)
46 {
47         int i;
48         for (i=0;dcerpc_pipes[i];i++) {
49                 if (strcasecmp(dcerpc_pipes[i]->uuid, uuid) == 0 &&
50                     dcerpc_pipes[i]->if_version == if_version) {
51                         return dcerpc_pipes[i]->num_calls;
52                 }
53         }
54         return -1;
55 }
56
57
58 /*
59   find a dcerpc interface by name
60 */
61 const struct dcerpc_interface_table *idl_iface_by_name(const char *name)
62 {
63         int i;
64         for (i=0;dcerpc_pipes[i];i++) {
65                 if (strcasecmp(dcerpc_pipes[i]->name, name) == 0) {
66                         return dcerpc_pipes[i];
67                 }
68         }
69         return NULL;
70 }
71
72 /*
73   find a dcerpc interface by uuid
74 */
75 const struct dcerpc_interface_table *idl_iface_by_uuid(const char *uuid)
76 {
77         int i;
78         for (i=0;dcerpc_pipes[i];i++) {
79                 if (strcasecmp(dcerpc_pipes[i]->uuid, uuid) == 0) {
80                         return dcerpc_pipes[i];
81                 }
82         }
83         return NULL;
84 }
85
86
87
88 /* 
89    push a dcerpc_packet into a blob, potentially with auth info
90 */
91 NTSTATUS dcerpc_push_auth(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
92                           struct dcerpc_packet *pkt,
93                           struct dcerpc_auth *auth_info)
94 {
95         NTSTATUS status;
96         struct ndr_push *ndr;
97
98         ndr = ndr_push_init_ctx(mem_ctx);
99         if (!ndr) {
100                 return NT_STATUS_NO_MEMORY;
101         }
102
103         if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
104                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
105         }
106
107         if (auth_info) {
108                 pkt->auth_length = auth_info->credentials.length;
109         } else {
110                 pkt->auth_length = 0;
111         }
112
113         status = ndr_push_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
114         if (!NT_STATUS_IS_OK(status)) {
115                 return status;
116         }
117
118         if (auth_info) {
119                 status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, auth_info);
120         }
121
122         *blob = ndr_push_blob(ndr);
123
124         /* fill in the frag length */
125         dcerpc_set_frag_length(blob, blob->length);
126
127         return NT_STATUS_OK;
128 }
129
130 #define MAX_PROTSEQ             10
131
132 static const struct {
133         const char *name;
134         enum dcerpc_transport_t transport;
135         int num_protocols;
136         enum epm_protocols protseq[MAX_PROTSEQ];
137 } transports[] = {
138         { "ncacn_np",     NCACN_NP, 3, 
139                 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_SMB, EPM_PROTOCOL_NETBIOS }},
140         { "ncacn_ip_tcp", NCACN_IP_TCP, 3, 
141                 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_TCP, EPM_PROTOCOL_IP } }, 
142         { "ncacn_http", NCACN_HTTP, 3, 
143                 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_HTTP, EPM_PROTOCOL_IP } }, 
144         { "ncadg_ip_udp", NCACN_IP_UDP, 3, 
145                 { EPM_PROTOCOL_NCADG, EPM_PROTOCOL_UDP, EPM_PROTOCOL_IP } },
146         { "ncalrpc", NCALRPC, 2, 
147                 { EPM_PROTOCOL_NCALRPC, EPM_PROTOCOL_PIPE } },
148         { "ncacn_unix_stream", NCACN_UNIX_STREAM, 2, 
149                 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_UNIX_DS } },
150         { "ncadg_unix_dgram", NCADG_UNIX_DGRAM, 2, 
151                 { EPM_PROTOCOL_NCADG, EPM_PROTOCOL_UNIX_DS } },
152         { "ncacn_at_dsp", NCACN_AT_DSP, 3, 
153                 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_APPLETALK, EPM_PROTOCOL_DSP } },
154         { "ncadg_at_ddp", NCADG_AT_DDP, 3, 
155                 { EPM_PROTOCOL_NCADG, EPM_PROTOCOL_APPLETALK, EPM_PROTOCOL_DDP } },
156         { "ncacn_vns_ssp", NCACN_VNS_SPP, 3, 
157                 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_STREETTALK, EPM_PROTOCOL_VINES_SPP } },
158         { "ncacn_vns_ipc", NCACN_VNS_IPC, 3, 
159                 { EPM_PROTOCOL_NCACN, EPM_PROTOCOL_STREETTALK, EPM_PROTOCOL_VINES_IPC }, },
160 };
161
162 static const struct {
163         const char *name;
164         uint32_t flag;
165 } ncacn_options[] = {
166         {"sign", DCERPC_SIGN},
167         {"seal", DCERPC_SEAL},
168         {"connect", DCERPC_CONNECT},
169         {"validate", DCERPC_DEBUG_VALIDATE_BOTH},
170         {"print", DCERPC_DEBUG_PRINT_BOTH},
171         {"padcheck", DCERPC_DEBUG_PAD_CHECK},
172         {"bigendian", DCERPC_PUSH_BIGENDIAN}
173 };
174
175
176
177 /*
178   form a binding string from a binding structure
179 */
180 const char *dcerpc_binding_string(TALLOC_CTX *mem_ctx, const struct dcerpc_binding *b)
181 {
182         char *s = NULL;
183         int i;
184         const char *t_name=NULL;
185
186         for (i=0;i<ARRAY_SIZE(transports);i++) {
187                 if (transports[i].transport == b->transport) {
188                         t_name = transports[i].name;
189                 }
190         }
191         if (!t_name) {
192                 return NULL;
193         }
194
195         if (!uuid_all_zero(&b->object)) { 
196                 s = talloc_asprintf(mem_ctx, "%s@", GUID_string(mem_ctx, &b->object));
197         }
198
199         s = talloc_asprintf_append(s, "%s:", t_name);
200         if (!s) return NULL;
201
202         if (b->host) {
203                 s = talloc_asprintf_append(s, "%s", b->host);
204         }
205
206         if (!b->endpoint && !b->options && !b->flags) {
207                 return s;
208         }
209
210         s = talloc_asprintf_append(s, "[");
211
212         if (b->endpoint) {
213                 s = talloc_asprintf_append(s, "%s", b->endpoint);
214         }
215
216         /* this is a *really* inefficent way of dealing with strings,
217            but this is rarely called and the strings are always short,
218            so I don't care */
219         for (i=0;b->options && b->options[i];i++) {
220                 s = talloc_asprintf_append(s, ",%s", b->options[i]);
221                 if (!s) return NULL;
222         }
223         for (i=0;i<ARRAY_SIZE(ncacn_options);i++) {
224                 if (b->flags & ncacn_options[i].flag) {
225                         s = talloc_asprintf_append(s, ",%s", ncacn_options[i].name);
226                         if (!s) return NULL;
227                 }
228         }
229
230         s = talloc_asprintf_append(s, "]");
231
232         return s;
233 }
234
235 /*
236   parse a binding string into a dcerpc_binding structure
237 */
238 NTSTATUS dcerpc_parse_binding(TALLOC_CTX *mem_ctx, const char *s, struct dcerpc_binding *b)
239 {
240         char *options, *type;
241         char *p;
242         int i, j, comma_count;
243
244         p = strchr(s, '@');
245
246         if (p && PTR_DIFF(p, s) == 36) { /* 36 is the length of a UUID */
247                 NTSTATUS status;
248
249                 status = GUID_from_string(s, &b->object);
250
251                 if (NT_STATUS_IS_ERR(status)) {
252                         DEBUG(0, ("Failed parsing UUID\n"));
253                         return status;
254                 }
255
256                 s = p + 1;
257         } else {
258                 ZERO_STRUCT(b->object);
259         }
260
261         b->object_version = 0;
262
263         p = strchr(s, ':');
264         if (!p) {
265                 return NT_STATUS_INVALID_PARAMETER;
266         }
267
268         type = talloc_strndup(mem_ctx, s, PTR_DIFF(p, s));
269         if (!type) {
270                 return NT_STATUS_NO_MEMORY;
271         }
272
273         for (i=0;i<ARRAY_SIZE(transports);i++) {
274                 if (strcasecmp(type, transports[i].name) == 0) {
275                         b->transport = transports[i].transport;
276                         break;
277                 }
278         }
279         if (i==ARRAY_SIZE(transports)) {
280                 DEBUG(0,("Unknown dcerpc transport '%s'\n", type));
281                 return NT_STATUS_INVALID_PARAMETER;
282         }
283         
284         s = p+1;
285
286         p = strchr(s, '[');
287         if (p) {
288                 b->host = talloc_strndup(mem_ctx, s, PTR_DIFF(p, s));
289                 options = talloc_strdup(mem_ctx, p+1);
290                 if (options[strlen(options)-1] != ']') {
291                         return NT_STATUS_INVALID_PARAMETER;
292                 }
293                 options[strlen(options)-1] = 0;
294         } else {
295                 b->host = talloc_strdup(mem_ctx, s);
296                 options = NULL;
297         }
298
299         if (!b->host) {
300                 return NT_STATUS_NO_MEMORY;
301         }
302
303         b->options = NULL;
304         b->flags = 0;
305         b->endpoint = NULL;
306
307         if (!options) {
308                 return NT_STATUS_OK;
309         }
310
311         comma_count = count_chars(options, ',');
312
313         b->options = talloc_array_p(mem_ctx, const char *, comma_count+2);
314         if (!b->options) {
315                 return NT_STATUS_NO_MEMORY;
316         }
317
318         for (i=0; (p = strchr(options, ',')); i++) {
319                 b->options[i] = talloc_strndup(mem_ctx, options, PTR_DIFF(p, options));
320                 if (!b->options[i]) {
321                         return NT_STATUS_NO_MEMORY;
322                 }
323                 options = p+1;
324         }
325         b->options[i] = options;
326         b->options[i+1] = NULL;
327
328         /* Endpoint is first option */
329         b->endpoint = b->options[0];
330         if (strlen(b->endpoint) == 0) b->endpoint = NULL;
331
332         for (i=0;b->options[i];i++) {
333                 b->options[i] = b->options[i+1];
334         }
335
336         /* some options are pre-parsed for convenience */
337         for (i=0;b->options[i];i++) {
338                 for (j=0;j<ARRAY_SIZE(ncacn_options);j++) {
339                         if (strcasecmp(ncacn_options[j].name, b->options[i]) == 0) {
340                                 int k;
341                                 b->flags |= ncacn_options[j].flag;
342                                 for (k=i;b->options[k];k++) {
343                                         b->options[k] = b->options[k+1];
344                                 }
345                                 i--;
346                                 break;
347                         }
348                 }
349         }
350
351         if (b->options[0] == NULL)
352                 b->options = NULL;
353         
354         return NT_STATUS_OK;
355 }
356
357 static const char *floor_get_rhs_data(TALLOC_CTX *mem_ctx, struct epm_floor *floor)
358 {
359         switch (floor->lhs.protocol) {
360         case EPM_PROTOCOL_TCP:
361                 if (floor->rhs.tcp.port == 0) return NULL;
362                 return talloc_asprintf(mem_ctx, "%d", floor->rhs.tcp.port);
363                 
364         case EPM_PROTOCOL_UDP:
365                 if (floor->rhs.udp.port == 0) return NULL;
366                 return talloc_asprintf(mem_ctx, "%d", floor->rhs.udp.port);
367
368         case EPM_PROTOCOL_HTTP:
369                 if (floor->rhs.http.port == 0) return NULL;
370                 return talloc_asprintf(mem_ctx, "%d", floor->rhs.http.port);
371
372         case EPM_PROTOCOL_IP:
373                 if (floor->rhs.ip.address == 0) {
374                         return NULL; 
375                 }
376
377                 {
378                 struct in_addr in;
379                         in.s_addr = htonl(floor->rhs.ip.address);
380             return talloc_strdup(mem_ctx, inet_ntoa(in));
381                 }
382
383         case EPM_PROTOCOL_NCACN:
384                 return NULL;
385
386         case EPM_PROTOCOL_NCADG:
387                 return NULL;
388
389         case EPM_PROTOCOL_SMB:
390                 if (strlen(floor->rhs.smb.unc) == 0) return NULL;
391                 return talloc_strdup(mem_ctx, floor->rhs.smb.unc);
392
393         case EPM_PROTOCOL_PIPE:
394                 if (strlen(floor->rhs.pipe.path) == 0) return NULL;
395                 return talloc_strdup(mem_ctx, floor->rhs.pipe.path);
396
397         case EPM_PROTOCOL_NETBIOS:
398                 if (strlen(floor->rhs.netbios.name) == 0) return NULL;
399                 return talloc_strdup(mem_ctx, floor->rhs.netbios.name);
400
401         case EPM_PROTOCOL_NCALRPC:
402                 return NULL;
403                 
404         case EPM_PROTOCOL_VINES_SPP:
405                 return talloc_asprintf(mem_ctx, "%d", floor->rhs.vines_spp.port);
406                 
407         case EPM_PROTOCOL_VINES_IPC:
408                 return talloc_asprintf(mem_ctx, "%d", floor->rhs.vines_ipc.port);
409                 
410         case EPM_PROTOCOL_STREETTALK:
411                 return talloc_strdup(mem_ctx, floor->rhs.streettalk.streettalk);
412                 
413         case EPM_PROTOCOL_UNIX_DS:
414                 if (strlen(floor->rhs.unix_ds.path) == 0) return NULL;
415                 return talloc_strdup(mem_ctx, floor->rhs.unix_ds.path);
416                 
417         case EPM_PROTOCOL_NULL:
418                 return NULL;
419         }
420
421         return NULL;
422 }
423
424 static NTSTATUS floor_set_rhs_data(TALLOC_CTX *mem_ctx, struct epm_floor *floor,  const char *data)
425 {
426         switch (floor->lhs.protocol) {
427         case EPM_PROTOCOL_TCP:
428                 floor->rhs.tcp.port = atoi(data);
429                 return NT_STATUS_OK;
430                 
431         case EPM_PROTOCOL_UDP:
432                 floor->rhs.udp.port = atoi(data);
433                 return NT_STATUS_OK;
434
435         case EPM_PROTOCOL_HTTP:
436                 floor->rhs.http.port = atoi(data);
437                 return NT_STATUS_OK;
438
439         case EPM_PROTOCOL_IP:
440                 floor->rhs.ip.address = 0;
441
442                 /* Only try to put in a IPv4 address. Windows 2003 just returns 
443                  * 0.0.0.0 for IPv6 addresses */
444                 if (strlen(data) > 0) {
445                     struct addrinfo hints, *res;
446
447                         memset(&hints, 0, sizeof(struct addrinfo));
448
449                         hints.ai_family = AF_INET;
450                         hints.ai_protocol = PF_INET;
451
452                         if (getaddrinfo(data, NULL, &hints, &res) < 0) {
453                                 return NT_STATUS_BAD_NETWORK_NAME;
454                         }
455
456                         floor->rhs.ip.address = ntohl(((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr);
457
458                         freeaddrinfo(res);
459                 }
460                 return NT_STATUS_OK;
461
462         case EPM_PROTOCOL_NCACN:
463                 floor->rhs.ncacn.minor_version = 0;
464                 return NT_STATUS_OK;
465
466         case EPM_PROTOCOL_NCADG:
467                 floor->rhs.ncadg.minor_version = 0;
468                 return NT_STATUS_OK;
469
470         case EPM_PROTOCOL_SMB:
471                 floor->rhs.smb.unc = talloc_strdup(mem_ctx, data);
472                 if (!floor->rhs.smb.unc) {
473                         return NT_STATUS_NO_MEMORY;
474                 }
475                 return NT_STATUS_OK;
476
477         case EPM_PROTOCOL_PIPE:
478                 floor->rhs.pipe.path = talloc_strdup(mem_ctx, data);
479                 if (!floor->rhs.pipe.path) {
480                         return NT_STATUS_NO_MEMORY;
481                 }
482                 return NT_STATUS_OK;
483
484         case EPM_PROTOCOL_NETBIOS:
485                 floor->rhs.netbios.name = talloc_strdup(mem_ctx, data);
486                 if (!floor->rhs.netbios.name) {
487                         return NT_STATUS_NO_MEMORY;
488                 }
489                 return NT_STATUS_OK;
490
491         case EPM_PROTOCOL_NCALRPC:
492                 return NT_STATUS_OK;
493                 
494         case EPM_PROTOCOL_VINES_SPP:
495                 floor->rhs.vines_spp.port = atoi(data);
496                 return NT_STATUS_OK;
497                 
498         case EPM_PROTOCOL_VINES_IPC:
499                 floor->rhs.vines_ipc.port = atoi(data);
500                 return NT_STATUS_OK;
501                 
502         case EPM_PROTOCOL_STREETTALK:
503                 floor->rhs.streettalk.streettalk = talloc_strdup(mem_ctx, data);
504                 if (!floor->rhs.streettalk.streettalk) {
505                         return NT_STATUS_NO_MEMORY;
506                 }
507                 return NT_STATUS_OK;
508                 
509         case EPM_PROTOCOL_UNIX_DS:
510                 floor->rhs.unix_ds.path = talloc_strdup(mem_ctx, data);
511                 if (!floor->rhs.unix_ds.path) {
512                         return NT_STATUS_NO_MEMORY;
513                 }
514                 return NT_STATUS_OK;
515                 
516         case EPM_PROTOCOL_NULL:
517                 return NT_STATUS_OK;
518         }
519
520         return NT_STATUS_NOT_SUPPORTED;
521 }
522
523 enum dcerpc_transport_t dcerpc_transport_by_tower(struct epm_tower *tower)
524 {
525         int i;
526
527         /* Find a transport that matches this tower */
528         for (i=0;i<ARRAY_SIZE(transports);i++) {
529                 int j;
530                 if (transports[i].num_protocols != tower->num_floors - 2) {
531                         continue; 
532                 }
533
534                 for (j = 0; j < transports[i].num_protocols; j++) {
535                         if (transports[i].protseq[j] != tower->floors[j+2].lhs.protocol) {
536                                 break;
537                         }
538                 }
539
540                 if (j == transports[i].num_protocols) {
541                         return transports[i].transport;
542                 }
543         }
544         
545         /* Unknown transport */
546         return -1;
547 }
548
549 NTSTATUS dcerpc_binding_from_tower(TALLOC_CTX *mem_ctx, struct epm_tower *tower, struct dcerpc_binding *binding)
550 {
551         ZERO_STRUCT(binding->object);
552         binding->options = NULL;
553         binding->host = NULL;
554         binding->flags = 0;
555
556         binding->transport = dcerpc_transport_by_tower(tower);
557
558         if (binding->transport == -1) {
559                 return NT_STATUS_NOT_SUPPORTED;
560         }
561
562         if (tower->num_floors < 1) {
563                 return NT_STATUS_OK;
564         }
565
566         /* Set object uuid */
567         binding->object = tower->floors[0].lhs.info.uuid.uuid;
568         binding->object_version = tower->floors[0].lhs.info.uuid.version;
569
570         /* Ignore floor 1, it contains the NDR version info */
571         
572         binding->options = NULL;
573
574         /* Set endpoint */
575         if (tower->num_floors >= 4) {
576                 binding->endpoint = floor_get_rhs_data(mem_ctx, &tower->floors[3]);
577         } else {
578                 binding->endpoint = NULL;
579         }
580
581         /* Set network address */
582         if (tower->num_floors >= 5) {
583                 binding->host = floor_get_rhs_data(mem_ctx, &tower->floors[4]);
584         }
585         return NT_STATUS_OK;
586 }
587
588 NTSTATUS dcerpc_binding_build_tower(TALLOC_CTX *mem_ctx, struct dcerpc_binding *binding, struct epm_tower *tower)
589 {
590         const enum epm_protocols *protseq;
591         int num_protocols = -1, i;
592         NTSTATUS status;
593         
594         /* Find transport */
595         for (i=0;i<ARRAY_SIZE(transports);i++) {
596                 if (transports[i].transport == binding->transport) {
597                         protseq = transports[i].protseq;
598                         num_protocols = transports[i].num_protocols;
599                         break;
600                 }
601         }
602
603         if (num_protocols == -1) {
604                 DEBUG(0, ("Unable to find transport with id '%d'\n", binding->transport));
605                 return NT_STATUS_UNSUCCESSFUL;
606         }
607
608         tower->num_floors = 2 + num_protocols;
609         tower->floors = talloc_array_p(mem_ctx, struct epm_floor, tower->num_floors);
610
611         /* Floor 0 */
612         tower->floors[0].lhs.protocol = EPM_PROTOCOL_UUID;
613         tower->floors[0].lhs.info.uuid.uuid = binding->object;
614         tower->floors[0].lhs.info.uuid.version = binding->object_version;
615         tower->floors[0].rhs.uuid.unknown = 0;
616         
617         /* Floor 1 */
618         tower->floors[1].lhs.protocol = EPM_PROTOCOL_UUID;
619         tower->floors[1].lhs.info.uuid.version = NDR_GUID_VERSION;
620         tower->floors[1].rhs.uuid.unknown = 0;
621         status = GUID_from_string(NDR_GUID, &tower->floors[1].lhs.info.uuid.uuid);
622         if (NT_STATUS_IS_ERR(status)) {
623                 return status;
624         }
625
626         /* Floor 2 to num_protocols */
627         for (i = 0; i < num_protocols; i++) {
628                 tower->floors[2 + i].lhs.protocol = protseq[i];
629                 tower->floors[2 + i].lhs.info.lhs_data = data_blob_talloc(mem_ctx, NULL, 0);
630                 ZERO_STRUCT(tower->floors[2 + i].rhs);
631                 floor_set_rhs_data(mem_ctx, &tower->floors[2 + i], "");
632         }
633
634         /* The 4th floor contains the endpoint */
635         if (num_protocols >= 2 && binding->endpoint) {
636                 status = floor_set_rhs_data(mem_ctx, &tower->floors[3], binding->endpoint);
637                 if (NT_STATUS_IS_ERR(status)) {
638                         return status;
639                 }
640         }
641         
642         /* The 5th contains the network address */
643         if (num_protocols >= 3 && binding->host) {
644                 status = floor_set_rhs_data(mem_ctx, &tower->floors[4], binding->host);
645                 if (NT_STATUS_IS_ERR(status)) {
646                         return status;
647                 }
648         }
649
650         return NT_STATUS_OK;
651 }
652
653 NTSTATUS dcerpc_epm_map_binding(TALLOC_CTX *mem_ctx, struct dcerpc_binding *binding,
654                                  const char *uuid, uint_t version)
655 {
656         struct dcerpc_pipe *p;
657         NTSTATUS status;
658         struct epm_Map r;
659         struct policy_handle handle;
660         struct GUID guid;
661         struct epm_twr_t twr, *twr_r;
662         struct dcerpc_binding epmapper_binding;
663
664
665         if (!strcmp(uuid, DCERPC_EPMAPPER_UUID)) {
666                 switch(binding->transport) {
667                         case NCACN_IP_TCP: binding->endpoint = "135"/*FIXME*/; return NT_STATUS_OK;
668                         case NCALRPC: binding->endpoint = EPMAPPER_IDENTIFIER; return NT_STATUS_OK;
669                         default: return NT_STATUS_NOT_SUPPORTED;
670                 }
671         }
672         
673
674         ZERO_STRUCT(epmapper_binding);
675         epmapper_binding.transport = binding->transport;
676         epmapper_binding.host = binding->host;
677         epmapper_binding.options = NULL;
678         epmapper_binding.flags = 0;
679         epmapper_binding.endpoint = NULL;
680         
681         status = dcerpc_pipe_connect_b(&p,
682                                         &epmapper_binding,
683                                    DCERPC_EPMAPPER_UUID,
684                                    DCERPC_EPMAPPER_VERSION,
685                                    NULL, NULL, NULL);
686
687         if (!NT_STATUS_IS_OK(status)) {
688                 return status;
689         }
690
691         ZERO_STRUCT(handle);
692         ZERO_STRUCT(guid);
693
694         status = GUID_from_string(uuid, &binding->object);
695         if (NT_STATUS_IS_ERR(status)) {
696                 return status;
697         }
698
699         binding->object_version = version;
700
701         status = dcerpc_binding_build_tower(p, binding, &twr.tower);
702         if (NT_STATUS_IS_ERR(status)) {
703                 return status;
704         }
705
706         /* with some nice pretty paper around it of course */
707         r.in.object = &guid;
708         r.in.map_tower = &twr;
709         r.in.entry_handle = &handle;
710         r.in.max_towers = 1;
711         r.out.entry_handle = &handle;
712
713         status = dcerpc_epm_Map(p, p, &r);
714         if (!NT_STATUS_IS_OK(status)) {
715                 dcerpc_pipe_close(p);
716                 return status;
717         }
718         if (r.out.result != 0 || r.out.num_towers != 1) {
719                 dcerpc_pipe_close(p);
720                 return NT_STATUS_PORT_UNREACHABLE;
721         }
722
723         twr_r = r.out.towers[0].twr;
724         if (!twr_r) {
725                 dcerpc_pipe_close(p);
726                 return NT_STATUS_PORT_UNREACHABLE;
727         }
728
729         if (twr_r->tower.num_floors != twr.tower.num_floors ||
730             twr_r->tower.floors[3].lhs.protocol != twr.tower.floors[3].lhs.protocol) {
731                 dcerpc_pipe_close(p);
732                 return NT_STATUS_PORT_UNREACHABLE;
733         }
734
735         binding->endpoint = floor_get_rhs_data(mem_ctx, &twr_r->tower.floors[3]);
736
737         dcerpc_pipe_close(p);
738
739         return NT_STATUS_OK;
740 }
741
742
743 /* open a rpc connection to a rpc pipe on SMB using the binding
744    structure to determine the endpoint and options */
745 static NTSTATUS dcerpc_pipe_connect_ncacn_np(struct dcerpc_pipe **p, 
746                                              struct dcerpc_binding *binding,
747                                              const char *pipe_uuid, 
748                                              uint32_t pipe_version,
749                                              const char *domain,
750                                              const char *username,
751                                              const char *password)
752 {
753         NTSTATUS status;
754         BOOL retry;
755         struct smbcli_state *cli;
756         const char *pipe_name;
757         TALLOC_CTX *mem_ctx = talloc_init("dcerpc_pipe_connect_ncacn_np");
758         
759         if (!binding->endpoint) {
760                 const struct dcerpc_interface_table *table = idl_iface_by_uuid(pipe_uuid);
761                 struct dcerpc_binding default_binding;
762                 int i;
763
764                 if (!table) {
765                         DEBUG(0,("Unknown interface endpoint '%s'\n", pipe_uuid));
766                         talloc_destroy(mem_ctx);
767                         return NT_STATUS_INVALID_PARAMETER;
768                 }
769
770                 /* Find one of the default pipes for this interface */
771                 for (i = 0; i < table->endpoints->count; i++) {
772                         status = dcerpc_parse_binding(mem_ctx, table->endpoints->names[i], &default_binding);
773
774                         if (NT_STATUS_IS_OK(status) && default_binding.transport == NCACN_NP) {
775                                 pipe_name = default_binding.endpoint;   
776                                 break;
777                                 
778                         }
779                 }
780         } else {
781                 pipe_name = binding->endpoint;
782         }
783
784         if (!strncasecmp(pipe_name, "/pipe/", 6) || 
785                 !strncasecmp(pipe_name, "\\pipe\\", 6)) {
786                 pipe_name+=6;
787         }
788
789         if (pipe_name[0] != '\\') {
790                 pipe_name = talloc_asprintf(mem_ctx, "\\%s", pipe_name);
791         }
792         
793         if (!username || !username[0]) {
794                 status = smbcli_full_connection(NULL, &cli, lp_netbios_name(),
795                                              binding->host, NULL, 
796                                              "ipc$", "?????", 
797                                              "", "", NULL, 0, &retry);
798         } else {
799                 status = smbcli_full_connection(NULL, &cli, lp_netbios_name(),
800                                              binding->host, NULL, 
801                                              "ipc$", "?????", 
802                                              username, domain,
803                                              password, 0, &retry);
804         }
805         if (!NT_STATUS_IS_OK(status)) {
806                 DEBUG(0,("Failed to connect to %s - %s\n", binding->host, nt_errstr(status)));
807                 talloc_destroy(mem_ctx);
808                 return status;
809         }
810
811         status = dcerpc_pipe_open_smb(p, cli->tree, pipe_name);
812         if (!NT_STATUS_IS_OK(status)) {
813                 DEBUG(0,("Failed to open pipe %s - %s\n", pipe_name, nt_errstr(status)));
814                 smbcli_tdis(cli);
815                 smbcli_shutdown(cli);
816                 talloc_destroy(mem_ctx);
817         return status;
818     }   
819
820         talloc_destroy(mem_ctx);
821         
822         /* this ensures that the reference count is decremented so
823            a pipe close will really close the link */
824         talloc_steal(*p, cli);
825
826         (*p)->flags = binding->flags;
827
828         /* remember the binding string for possible secondary connections */
829         (*p)->binding_string = dcerpc_binding_string((*p), binding);
830
831         if (username && username[0] && (binding->flags & DCERPC_SCHANNEL_ANY)) {
832                 status = dcerpc_bind_auth_schannel(*p, pipe_uuid, pipe_version, 
833                                                    domain, username, password);
834         } else if (username && username[0] &&
835                    (binding->flags & (DCERPC_CONNECT|DCERPC_SIGN|DCERPC_SEAL))) {
836                 status = dcerpc_bind_auth_ntlm(*p, pipe_uuid, pipe_version, domain, username, password);
837         } else {    
838                 status = dcerpc_bind_auth_none(*p, pipe_uuid, pipe_version);
839
840         }
841
842         if (!NT_STATUS_IS_OK(status)) {
843                 DEBUG(0,("Failed to bind to uuid %s - %s\n", pipe_uuid, nt_errstr(status)));
844                 dcerpc_pipe_close(*p);
845                 *p = NULL;
846                 return status;
847         }
848
849         return NT_STATUS_OK;
850 }
851
852 /* open a rpc connection to a rpc pipe on SMP using the binding
853    structure to determine the endpoint and options */
854 static NTSTATUS dcerpc_pipe_connect_ncalrpc(struct dcerpc_pipe **p, 
855                                                  struct dcerpc_binding *binding,
856                                                  const char *pipe_uuid, 
857                                                  uint32_t pipe_version,
858                                                  const char *domain,
859                                                  const char *username,
860                                                  const char *password)
861 {
862         NTSTATUS status;
863         TALLOC_CTX *mem_ctx = talloc_init("dcerpc_pipe_connect_ncalrpc");
864
865         /* Look up identifier using the epmapper */
866         if (!binding->endpoint) {
867                 status = dcerpc_epm_map_binding(mem_ctx, binding, pipe_uuid, pipe_version);
868                 if (!NT_STATUS_IS_OK(status)) {
869                         DEBUG(0,("Failed to map DCERPC/TCP NCALRPC identifier for '%s' - %s\n", 
870                                  pipe_uuid, nt_errstr(status)));
871                         talloc_destroy(mem_ctx);
872                         return status;
873                 }
874                 DEBUG(1,("Mapped to DCERPC/TCP identifier %s\n", binding->endpoint));
875         }
876
877         status = dcerpc_pipe_open_pipe(p, binding->endpoint);
878
879         if (!NT_STATUS_IS_OK(status)) {
880                 DEBUG(0,("Failed to open ncalrpc pipe '%s'\n", binding->endpoint));
881                 talloc_destroy(mem_ctx);
882                 return status;
883     }
884
885         (*p)->flags = binding->flags;
886
887         /* remember the binding string for possible secondary connections */
888         (*p)->binding_string = dcerpc_binding_string((*p), binding);
889
890         if (username && username[0] && (binding->flags & DCERPC_SCHANNEL_ANY)) {
891                 status = dcerpc_bind_auth_schannel(*p, pipe_uuid, pipe_version, 
892                                                    domain, username, password);
893         } else if (username && username[0]) {
894                 status = dcerpc_bind_auth_ntlm(*p, pipe_uuid, pipe_version, domain, username, password);
895         } else {    
896                 status = dcerpc_bind_auth_none(*p, pipe_uuid, pipe_version);
897         }
898
899         if (!NT_STATUS_IS_OK(status)) {
900                 DEBUG(0,("Failed to bind to uuid %s - %s\n", 
901                          pipe_uuid, nt_errstr(status)));
902                 dcerpc_pipe_close(*p);
903                 *p = NULL;
904                 talloc_destroy(mem_ctx);
905                 return status;
906         }
907  
908         talloc_destroy(mem_ctx);
909     return status;
910 }
911
912
913
914 /* open a rpc connection to a rpc pipe on SMP using the binding
915    structure to determine the endpoint and options */
916 static NTSTATUS dcerpc_pipe_connect_ncacn_unix_stream(struct dcerpc_pipe **p, 
917                                                  struct dcerpc_binding *binding,
918                                                  const char *pipe_uuid, 
919                                                  uint32_t pipe_version,
920                                                  const char *domain,
921                                                  const char *username,
922                                                  const char *password)
923 {
924         NTSTATUS status;
925
926         if (!binding->endpoint) {
927                 DEBUG(0, ("Path to unix socket not specified\n"));
928                 return NT_STATUS_INVALID_PARAMETER;
929         }
930
931         status = dcerpc_pipe_open_unix_stream(p, binding->endpoint);
932         if (!NT_STATUS_IS_OK(status)) {
933                 DEBUG(0,("Failed to open unix socket %s\n", binding->endpoint));
934                 return status;
935     }
936
937         (*p)->flags = binding->flags;
938
939         /* remember the binding string for possible secondary connections */
940         (*p)->binding_string = dcerpc_binding_string((*p), binding);
941
942         if (username && username[0] && (binding->flags & DCERPC_SCHANNEL_ANY)) {
943                 status = dcerpc_bind_auth_schannel(*p, pipe_uuid, pipe_version, 
944                                                    domain, username, password);
945         } else if (username && username[0]) {
946                 status = dcerpc_bind_auth_ntlm(*p, pipe_uuid, pipe_version, domain, username, password);
947         } else {    
948                 status = dcerpc_bind_auth_none(*p, pipe_uuid, pipe_version);
949         }
950
951         if (!NT_STATUS_IS_OK(status)) {
952                 DEBUG(0,("Failed to bind to uuid %s - %s\n", 
953                          pipe_uuid, nt_errstr(status)));
954                 dcerpc_pipe_close(*p);
955                 *p = NULL;
956                 return status;
957         }
958  
959     return status;
960 }
961
962 /* open a rpc connection to a rpc pipe on SMP using the binding
963    structure to determine the endpoint and options */
964 static NTSTATUS dcerpc_pipe_connect_ncacn_ip_tcp(struct dcerpc_pipe **p, 
965                                                  struct dcerpc_binding *binding,
966                                                  const char *pipe_uuid, 
967                                                  uint32_t pipe_version,
968                                                  const char *domain,
969                                                  const char *username,
970                                                  const char *password)
971 {
972         NTSTATUS status;
973         uint32_t port = 0;
974         TALLOC_CTX *mem_ctx = talloc_init("connect_ncacn_ip_tcp");
975
976         if (!binding->endpoint) {
977                 status = dcerpc_epm_map_binding(mem_ctx, binding, 
978                                                  pipe_uuid, pipe_version);
979                 if (!NT_STATUS_IS_OK(status)) {
980                         DEBUG(0,("Failed to map DCERPC/TCP port for '%s' - %s\n", 
981                                  pipe_uuid, nt_errstr(status)));
982                         return status;
983                 }
984                 DEBUG(1,("Mapped to DCERPC/TCP port %s\n", binding->endpoint));
985         }
986
987         port = atoi(binding->endpoint);
988
989         status = dcerpc_pipe_open_tcp(p, binding->host, port, AF_UNSPEC);
990         if (!NT_STATUS_IS_OK(status)) {
991                 DEBUG(0,("Failed to connect to %s:%d\n", binding->host, port));
992                 return status;
993         }
994
995         (*p)->flags = binding->flags;
996
997         /* remember the binding string for possible secondary connections */
998         (*p)->binding_string = dcerpc_binding_string((*p), binding);
999
1000         if (username && username[0] && (binding->flags & DCERPC_SCHANNEL_ANY)) {
1001                 status = dcerpc_bind_auth_schannel(*p, pipe_uuid, pipe_version, 
1002                                                    domain, username, password);
1003         } else if (username && username[0]) {
1004                 status = dcerpc_bind_auth_ntlm(*p, pipe_uuid, pipe_version, domain, username, password);
1005         } else {    
1006                 status = dcerpc_bind_auth_none(*p, pipe_uuid, pipe_version);
1007         }
1008
1009         if (!NT_STATUS_IS_OK(status)) {
1010                 DEBUG(0,("Failed to bind to uuid %s - %s\n", 
1011                          pipe_uuid, nt_errstr(status)));
1012                 dcerpc_pipe_close(*p);
1013                 *p = NULL;
1014                 return status;
1015         }
1016  
1017         return status;
1018 }
1019
1020
1021 /* open a rpc connection to a rpc pipe, using the specified 
1022    binding structure to determine the endpoint and options */
1023 NTSTATUS dcerpc_pipe_connect_b(struct dcerpc_pipe **p, 
1024                                struct dcerpc_binding *binding,
1025                                const char *pipe_uuid, 
1026                                uint32_t pipe_version,
1027                                const char *domain,
1028                                const char *username,
1029                                const char *password)
1030 {
1031         NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
1032
1033         switch (binding->transport) {
1034         case NCACN_NP:
1035                 status = dcerpc_pipe_connect_ncacn_np(p, binding, pipe_uuid, pipe_version,
1036                                                       domain, username, password);
1037                 break;
1038         case NCACN_IP_TCP:
1039                 status = dcerpc_pipe_connect_ncacn_ip_tcp(p, binding, pipe_uuid, pipe_version,
1040                                                           domain, username, password);
1041                 break;
1042         case NCACN_UNIX_STREAM:
1043                 status = dcerpc_pipe_connect_ncacn_unix_stream(p, binding, pipe_uuid, pipe_version, domain, username, password);
1044                 break;
1045         case NCALRPC:
1046                 status = dcerpc_pipe_connect_ncalrpc(p, binding, pipe_uuid, pipe_version, domain, username, password);
1047                 break;
1048         default:
1049                 return NT_STATUS_NOT_SUPPORTED;
1050         }
1051
1052         return status;
1053 }
1054
1055
1056 /* open a rpc connection to a rpc pipe, using the specified string
1057    binding to determine the endpoint and options */
1058 NTSTATUS dcerpc_pipe_connect(struct dcerpc_pipe **p, 
1059                              const char *binding,
1060                              const char *pipe_uuid, 
1061                              uint32_t pipe_version,
1062                              const char *domain,
1063                              const char *username,
1064                              const char *password)
1065 {
1066         struct dcerpc_binding b;
1067         NTSTATUS status;
1068         TALLOC_CTX *mem_ctx;
1069
1070         mem_ctx = talloc_init("dcerpc_pipe_connect");
1071         if (!mem_ctx) return NT_STATUS_NO_MEMORY;
1072
1073         status = dcerpc_parse_binding(mem_ctx, binding, &b);
1074         if (!NT_STATUS_IS_OK(status)) {
1075                 DEBUG(0,("Failed to parse dcerpc binding '%s'\n", binding));
1076                 talloc_destroy(mem_ctx);
1077                 return status;
1078         }
1079
1080         DEBUG(3,("Using binding %s\n", dcerpc_binding_string(mem_ctx, &b)));
1081
1082         status = dcerpc_pipe_connect_b(p, &b, pipe_uuid, pipe_version, domain, username, password);
1083
1084         talloc_destroy(mem_ctx);
1085         return status;
1086 }
1087
1088
1089 /*
1090   create a secondary dcerpc connection from a primary connection
1091
1092   if the primary is a SMB connection then the secondary connection
1093   will be on the same SMB connection, but use a new fnum
1094 */
1095 NTSTATUS dcerpc_secondary_connection(struct dcerpc_pipe *p, struct dcerpc_pipe **p2,
1096                                      const char *pipe_name,
1097                                      const char *pipe_uuid,
1098                                      uint32_t pipe_version)
1099 {
1100         struct smbcli_tree *tree;
1101         NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
1102         struct dcerpc_binding b;
1103
1104         switch (p->transport.transport) {
1105         case NCACN_NP:
1106                 tree = dcerpc_smb_tree(p);
1107                 if (!tree) {
1108                         return NT_STATUS_INVALID_PARAMETER;
1109                 }
1110
1111                 status = dcerpc_pipe_open_smb(p2, tree, pipe_name);
1112                 break;
1113
1114         case NCACN_IP_TCP:
1115                 status = dcerpc_parse_binding(p, p->binding_string, &b);
1116                 if (!NT_STATUS_IS_OK(status)) {
1117                         return status;
1118                 }
1119                 b.flags &= ~DCERPC_AUTH_OPTIONS;
1120                 status = dcerpc_pipe_connect_ncacn_ip_tcp(p2, &b, pipe_uuid,
1121                                                           pipe_version, NULL, 
1122                                                           NULL, NULL);
1123                 break;
1124         default:
1125                 return NT_STATUS_NOT_SUPPORTED;
1126         }
1127
1128         if (!NT_STATUS_IS_OK(status)) {
1129                 return status;
1130         }
1131
1132         (*p2)->flags = p->flags;
1133
1134         status = dcerpc_bind_auth_none(*p2, pipe_uuid, pipe_version);
1135         if (!NT_STATUS_IS_OK(status)) {
1136                 return status;
1137         }
1138
1139         return NT_STATUS_OK;
1140 }
1141
1142 NTSTATUS dcerpc_generic_session_key(struct dcerpc_pipe *p,
1143                                     DATA_BLOB *session_key)
1144 {
1145         /* this took quite a few CPU cycles to find ... */
1146         session_key->data = discard_const_p(unsigned char, "SystemLibraryDTC");
1147         session_key->length = 16;
1148         return NT_STATUS_OK;
1149 }
1150
1151 /*
1152   fetch the user session key - may be default (above) or the SMB session key
1153 */
1154 NTSTATUS dcerpc_fetch_session_key(struct dcerpc_pipe *p,
1155                                   DATA_BLOB *session_key)
1156 {
1157         return p->security_state.session_key(p, session_key);
1158 }
1159
1160
1161 /*
1162   log a rpc packet in a format suitable for ndrdump. This is especially useful
1163   for sealed packets, where ethereal cannot easily see the contents
1164
1165   this triggers on a debug level of >= 10
1166 */
1167 void dcerpc_log_packet(const struct dcerpc_interface_table *ndr,
1168                        uint32_t opnum, uint32_t flags, DATA_BLOB *pkt)
1169 {
1170         const int num_examples = 20;
1171         int i;
1172
1173         if (DEBUGLEVEL < 10) return;
1174
1175         for (i=0;i<num_examples;i++) {
1176                 char *name=NULL;
1177                 asprintf(&name, "%s/rpclog/%s-%u.%d.%s", 
1178                          lp_lockdir(), ndr->name, opnum, i,
1179                          (flags&NDR_IN)?"in":"out");
1180                 if (name == NULL) {
1181                         return;
1182                 }
1183                 if (!file_exist(name, NULL)) {
1184                         if (file_save(name, pkt->data, pkt->length)) {
1185                                 DEBUG(10,("Logged rpc packet to %s\n", name));
1186                         }
1187                         free(name);
1188                         break;
1189                 }
1190                 free(name);
1191         }
1192 }
1193