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