libgpo: apply some const.
[kai/samba-autobuild/.git] / source3 / libsmb / clidfs.c
1 /*
2    Unix SMB/CIFS implementation.
3    client connect/disconnect routines
4    Copyright (C) Andrew Tridgell                  1994-1998
5    Copyright (C) Gerald (Jerry) Carter            2004
6    Copyright (C) Jeremy Allison                   2007-2009
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "libsmb/libsmb.h"
24 #include "libsmb/clirap.h"
25 #include "msdfs.h"
26 #include "trans2.h"
27 #include "libsmb/nmblib.h"
28 #include "../libcli/smb/smbXcli_base.h"
29
30 /********************************************************************
31  Important point.
32
33  DFS paths are *always* of the form \server\share\<pathname> (the \ characters
34  are not C escaped here).
35
36  - but if we're using POSIX paths then <pathname> may contain
37    '/' separators, not '\\' separators. So cope with '\\' or '/'
38    as a separator when looking at the pathname part.... JRA.
39 ********************************************************************/
40
41 /********************************************************************
42  Ensure a connection is encrypted.
43 ********************************************************************/
44
45 NTSTATUS cli_cm_force_encryption_creds(struct cli_state *c,
46                                        struct cli_credentials *creds,
47                                        const char *sharename)
48 {
49         uint16_t major, minor;
50         uint32_t caplow, caphigh;
51         NTSTATUS status;
52
53         if (smbXcli_conn_protocol(c->conn) >= PROTOCOL_SMB2_02) {
54                 status = smb2cli_session_encryption_on(c->smb2.session);
55                 if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_SUPPORTED)) {
56                         d_printf("Encryption required and "
57                                 "server doesn't support "
58                                 "SMB3 encryption - failing connect\n");
59                 } else if (!NT_STATUS_IS_OK(status)) {
60                         d_printf("Encryption required and "
61                                 "setup failed with error %s.\n",
62                                 nt_errstr(status));
63                 }
64                 return status;
65         }
66
67         if (!SERVER_HAS_UNIX_CIFS(c)) {
68                 d_printf("Encryption required and "
69                         "server that doesn't support "
70                         "UNIX extensions - failing connect\n");
71                 return NT_STATUS_NOT_SUPPORTED;
72         }
73
74         status = cli_unix_extensions_version(c, &major, &minor, &caplow,
75                                              &caphigh);
76         if (!NT_STATUS_IS_OK(status)) {
77                 d_printf("Encryption required and "
78                         "can't get UNIX CIFS extensions "
79                         "version from server.\n");
80                 return NT_STATUS_UNKNOWN_REVISION;
81         }
82
83         if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) {
84                 d_printf("Encryption required and "
85                         "share %s doesn't support "
86                         "encryption.\n", sharename);
87                 return NT_STATUS_UNSUPPORTED_COMPRESSION;
88         }
89
90         status = cli_smb1_setup_encryption(c, creds);
91         if (!NT_STATUS_IS_OK(status)) {
92                 d_printf("Encryption required and "
93                         "setup failed with error %s.\n",
94                         nt_errstr(status));
95                 return status;
96         }
97
98         return NT_STATUS_OK;
99 }
100
101 NTSTATUS cli_cm_force_encryption(struct cli_state *c,
102                         const char *username,
103                         const char *password,
104                         const char *domain,
105                         const char *sharename)
106 {
107         struct cli_credentials *creds = NULL;
108         NTSTATUS status;
109
110         creds = cli_session_creds_init(c,
111                                        username,
112                                        domain,
113                                        NULL, /* default realm */
114                                        password,
115                                        c->use_kerberos,
116                                        c->fallback_after_kerberos,
117                                        c->use_ccache,
118                                        c->pw_nt_hash);
119         if (creds == NULL) {
120                 return NT_STATUS_NO_MEMORY;
121         }
122
123         status = cli_cm_force_encryption_creds(c, creds, sharename);
124         /* gensec currently references the creds so we can't free them here */
125         talloc_unlink(c, creds);
126         return status;
127 }
128
129 /********************************************************************
130  Return a connection to a server.
131 ********************************************************************/
132
133 static NTSTATUS do_connect(TALLOC_CTX *ctx,
134                                         const char *server,
135                                         const char *share,
136                                         const struct user_auth_info *auth_info,
137                                         bool show_sessetup,
138                                         bool force_encrypt,
139                                         int max_protocol,
140                                         int port,
141                                         int name_type,
142                                         struct cli_state **pcli)
143 {
144         struct cli_state *c = NULL;
145         char *servicename;
146         char *sharename;
147         char *newserver, *newshare;
148         const char *username;
149         const char *password;
150         const char *domain;
151         NTSTATUS status;
152         int flags = 0;
153         int signing_state = get_cmdline_auth_info_signing_state(auth_info);
154         struct cli_credentials *creds = NULL;
155
156         if (force_encrypt) {
157                 signing_state = SMB_SIGNING_REQUIRED;
158         }
159
160         /* make a copy so we don't modify the global string 'service' */
161         servicename = talloc_strdup(ctx,share);
162         if (!servicename) {
163                 return NT_STATUS_NO_MEMORY;
164         }
165         sharename = servicename;
166         if (*sharename == '\\') {
167                 sharename += 2;
168                 if (server == NULL) {
169                         server = sharename;
170                 }
171                 sharename = strchr_m(sharename,'\\');
172                 if (!sharename) {
173                         return NT_STATUS_NO_MEMORY;
174                 }
175                 *sharename = 0;
176                 sharename++;
177         }
178         if (server == NULL) {
179                 return NT_STATUS_INVALID_PARAMETER;
180         }
181
182         if (get_cmdline_auth_info_use_kerberos(auth_info)) {
183                 flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
184         }
185         if (get_cmdline_auth_info_fallback_after_kerberos(auth_info)) {
186                 flags |= CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
187         }
188         if (get_cmdline_auth_info_use_ccache(auth_info)) {
189                 flags |= CLI_FULL_CONNECTION_USE_CCACHE;
190         }
191         if (get_cmdline_auth_info_use_pw_nt_hash(auth_info)) {
192                 flags |= CLI_FULL_CONNECTION_USE_NT_HASH;
193         }
194
195         status = cli_connect_nb(
196                 server, NULL, port, name_type, NULL,
197                 signing_state,
198                 flags, &c);
199
200         if (!NT_STATUS_IS_OK(status)) {
201                 d_printf("Connection to %s failed (Error %s)\n",
202                                 server,
203                                 nt_errstr(status));
204                 return status;
205         }
206
207         if (max_protocol == 0) {
208                 max_protocol = PROTOCOL_NT1;
209         }
210         DEBUG(4,(" session request ok\n"));
211
212         status = smbXcli_negprot(c->conn, c->timeout,
213                                  lp_client_min_protocol(),
214                                  max_protocol);
215
216         if (!NT_STATUS_IS_OK(status)) {
217                 d_printf("protocol negotiation failed: %s\n",
218                          nt_errstr(status));
219                 cli_shutdown(c);
220                 return status;
221         }
222
223         if (smbXcli_conn_protocol(c->conn) >= PROTOCOL_SMB2_02) {
224                 /* Ensure we ask for some initial credits. */
225                 smb2cli_conn_set_max_credits(c->conn, DEFAULT_SMB2_MAX_CREDITS);
226         }
227
228         username = get_cmdline_auth_info_username(auth_info);
229         password = get_cmdline_auth_info_password(auth_info);
230         domain = get_cmdline_auth_info_domain(auth_info);
231         if ((domain == NULL) || (domain[0] == '\0')) {
232                 domain = lp_workgroup();
233         }
234
235         creds = get_cmdline_auth_info_creds(auth_info);
236
237         status = cli_session_setup_creds(c, creds);
238         if (!NT_STATUS_IS_OK(status)) {
239                 /* If a password was not supplied then
240                  * try again with a null username. */
241                 if (password[0] || !username[0] ||
242                         get_cmdline_auth_info_use_kerberos(auth_info) ||
243                         !NT_STATUS_IS_OK(status = cli_session_setup_anon(c)))
244                 {
245                         d_printf("session setup failed: %s\n",
246                                  nt_errstr(status));
247                         if (NT_STATUS_EQUAL(status,
248                                             NT_STATUS_MORE_PROCESSING_REQUIRED))
249                                 d_printf("did you forget to run kinit?\n");
250                         cli_shutdown(c);
251                         return status;
252                 }
253                 d_printf("Anonymous login successful\n");
254         }
255
256         if (!NT_STATUS_IS_OK(status)) {
257                 DEBUG(10,("cli_init_creds() failed: %s\n", nt_errstr(status)));
258                 cli_shutdown(c);
259                 return status;
260         }
261
262         if ( show_sessetup ) {
263                 if (*c->server_domain) {
264                         DEBUG(0,("Domain=[%s] OS=[%s] Server=[%s]\n",
265                                 c->server_domain,c->server_os,c->server_type));
266                 } else if (*c->server_os || *c->server_type) {
267                         DEBUG(0,("OS=[%s] Server=[%s]\n",
268                                  c->server_os,c->server_type));
269                 }
270         }
271         DEBUG(4,(" session setup ok\n"));
272
273         /* here's the fun part....to support 'msdfs proxy' shares
274            (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL
275            here before trying to connect to the original share.
276            cli_check_msdfs_proxy() will fail if it is a normal share. */
277
278         if (smbXcli_conn_dfs_supported(c->conn) &&
279                         cli_check_msdfs_proxy(ctx, c, sharename,
280                                 &newserver, &newshare,
281                                 force_encrypt, creds)) {
282                 cli_shutdown(c);
283                 return do_connect(ctx, newserver,
284                                 newshare, auth_info, false,
285                                 force_encrypt, max_protocol,
286                                 port, name_type, pcli);
287         }
288
289         /* must be a normal share */
290
291         status = cli_tree_connect_creds(c, sharename, "?????", creds);
292         if (!NT_STATUS_IS_OK(status)) {
293                 d_printf("tree connect failed: %s\n", nt_errstr(status));
294                 cli_shutdown(c);
295                 return status;
296         }
297
298         if (force_encrypt) {
299                 status = cli_cm_force_encryption_creds(c,
300                                                        creds,
301                                                        sharename);
302                 if (!NT_STATUS_IS_OK(status)) {
303                         cli_shutdown(c);
304                         return status;
305                 }
306         }
307
308         DEBUG(4,(" tconx ok\n"));
309         *pcli = c;
310         return NT_STATUS_OK;
311 }
312
313 /****************************************************************************
314 ****************************************************************************/
315
316 static void cli_set_mntpoint(struct cli_state *cli, const char *mnt)
317 {
318         TALLOC_CTX *frame = talloc_stackframe();
319         char *name = clean_name(frame, mnt);
320         if (!name) {
321                 TALLOC_FREE(frame);
322                 return;
323         }
324         TALLOC_FREE(cli->dfs_mountpoint);
325         cli->dfs_mountpoint = talloc_strdup(cli, name);
326         TALLOC_FREE(frame);
327 }
328
329 /********************************************************************
330  Add a new connection to the list.
331  referring_cli == NULL means a new initial connection.
332 ********************************************************************/
333
334 static NTSTATUS cli_cm_connect(TALLOC_CTX *ctx,
335                                struct cli_state *referring_cli,
336                                const char *server,
337                                const char *share,
338                                const struct user_auth_info *auth_info,
339                                bool show_hdr,
340                                bool force_encrypt,
341                                int max_protocol,
342                                int port,
343                                int name_type,
344                                struct cli_state **pcli)
345 {
346         struct cli_state *cli;
347         NTSTATUS status;
348
349         status = do_connect(ctx, server, share,
350                                 auth_info,
351                                 show_hdr, force_encrypt, max_protocol,
352                                 port, name_type, &cli);
353
354         if (!NT_STATUS_IS_OK(status)) {
355                 return status;
356         }
357
358         /* Enter into the list. */
359         if (referring_cli) {
360                 DLIST_ADD_END(referring_cli, cli);
361         }
362
363         if (referring_cli && referring_cli->requested_posix_capabilities) {
364                 uint16_t major, minor;
365                 uint32_t caplow, caphigh;
366                 status = cli_unix_extensions_version(cli, &major, &minor,
367                                                      &caplow, &caphigh);
368                 if (NT_STATUS_IS_OK(status)) {
369                         cli_set_unix_extensions_capabilities(cli,
370                                         major, minor,
371                                         caplow, caphigh);
372                 }
373         }
374
375         *pcli = cli;
376         return NT_STATUS_OK;
377 }
378
379 /********************************************************************
380  Return a connection to a server on a particular share.
381 ********************************************************************/
382
383 static struct cli_state *cli_cm_find(struct cli_state *cli,
384                                 const char *server,
385                                 const char *share)
386 {
387         struct cli_state *p;
388
389         if (cli == NULL) {
390                 return NULL;
391         }
392
393         /* Search to the start of the list. */
394         for (p = cli; p; p = DLIST_PREV(p)) {
395                 const char *remote_name =
396                         smbXcli_conn_remote_name(p->conn);
397
398                 if (strequal(server, remote_name) &&
399                                 strequal(share,p->share)) {
400                         return p;
401                 }
402         }
403
404         /* Search to the end of the list. */
405         for (p = cli->next; p; p = p->next) {
406                 const char *remote_name =
407                         smbXcli_conn_remote_name(p->conn);
408
409                 if (strequal(server, remote_name) &&
410                                 strequal(share,p->share)) {
411                         return p;
412                 }
413         }
414
415         return NULL;
416 }
417
418 /****************************************************************************
419  Open a client connection to a \\server\share.
420 ****************************************************************************/
421
422 NTSTATUS cli_cm_open(TALLOC_CTX *ctx,
423                                 struct cli_state *referring_cli,
424                                 const char *server,
425                                 const char *share,
426                                 const struct user_auth_info *auth_info,
427                                 bool show_hdr,
428                                 bool force_encrypt,
429                                 int max_protocol,
430                                 int port,
431                                 int name_type,
432                                 struct cli_state **pcli)
433 {
434         /* Try to reuse an existing connection in this list. */
435         struct cli_state *c = cli_cm_find(referring_cli, server, share);
436         NTSTATUS status;
437
438         if (c) {
439                 *pcli = c;
440                 return NT_STATUS_OK;
441         }
442
443         if (auth_info == NULL) {
444                 /* Can't do a new connection
445                  * without auth info. */
446                 d_printf("cli_cm_open() Unable to open connection [\\%s\\%s] "
447                         "without auth info\n",
448                         server, share );
449                 return NT_STATUS_INVALID_PARAMETER;
450         }
451
452         status = cli_cm_connect(ctx,
453                                 referring_cli,
454                                 server,
455                                 share,
456                                 auth_info,
457                                 show_hdr,
458                                 force_encrypt,
459                                 max_protocol,
460                                 port,
461                                 name_type,
462                                 &c);
463         if (!NT_STATUS_IS_OK(status)) {
464                 return status;
465         }
466         *pcli = c;
467         return NT_STATUS_OK;
468 }
469
470 /****************************************************************************
471 ****************************************************************************/
472
473 void cli_cm_display(struct cli_state *cli)
474 {
475         int i;
476
477         for (i=0; cli; cli = cli->next,i++ ) {
478                 d_printf("%d:\tserver=%s, share=%s\n",
479                         i, smbXcli_conn_remote_name(cli->conn), cli->share);
480         }
481 }
482
483 /****************************************************************************
484 ****************************************************************************/
485
486 /****************************************************************************
487 ****************************************************************************/
488
489 #if 0
490 void cli_cm_set_credentials(struct user_auth_info *auth_info)
491 {
492         SAFE_FREE(cm_creds.username);
493         cm_creds.username = SMB_STRDUP(get_cmdline_auth_info_username(
494                                                auth_info));
495
496         if (get_cmdline_auth_info_got_pass(auth_info)) {
497                 cm_set_password(get_cmdline_auth_info_password(auth_info));
498         }
499
500         cm_creds.use_kerberos = get_cmdline_auth_info_use_kerberos(auth_info);
501         cm_creds.fallback_after_kerberos = false;
502         cm_creds.signing_state = get_cmdline_auth_info_signing_state(auth_info);
503 }
504 #endif
505
506 /**********************************************************************
507  split a dfs path into the server, share name, and extrapath components
508 **********************************************************************/
509
510 static bool split_dfs_path(TALLOC_CTX *ctx,
511                                 const char *nodepath,
512                                 char **pp_server,
513                                 char **pp_share,
514                                 char **pp_extrapath)
515 {
516         char *p, *q;
517         char *path;
518
519         *pp_server = NULL;
520         *pp_share = NULL;
521         *pp_extrapath = NULL;
522
523         path = talloc_strdup(ctx, nodepath);
524         if (!path) {
525                 goto fail;
526         }
527
528         if ( path[0] != '\\' ) {
529                 goto fail;
530         }
531
532         p = strchr_m( path + 1, '\\' );
533         if ( !p ) {
534                 goto fail;
535         }
536
537         *p = '\0';
538         p++;
539
540         /* Look for any extra/deep path */
541         q = strchr_m(p, '\\');
542         if (q != NULL) {
543                 *q = '\0';
544                 q++;
545                 *pp_extrapath = talloc_strdup(ctx, q);
546         } else {
547                 *pp_extrapath = talloc_strdup(ctx, "");
548         }
549         if (*pp_extrapath == NULL) {
550                 goto fail;
551         }
552
553         *pp_share = talloc_strdup(ctx, p);
554         if (*pp_share == NULL) {
555                 goto fail;
556         }
557
558         *pp_server = talloc_strdup(ctx, &path[1]);
559         if (*pp_server == NULL) {
560                 goto fail;
561         }
562
563         TALLOC_FREE(path);
564         return true;
565
566 fail:
567         TALLOC_FREE(*pp_share);
568         TALLOC_FREE(*pp_extrapath);
569         TALLOC_FREE(path);
570         return false;
571 }
572
573 /****************************************************************************
574  Return the original path truncated at the directory component before
575  the first wildcard character. Trust the caller to provide a NULL
576  terminated string
577 ****************************************************************************/
578
579 static char *clean_path(TALLOC_CTX *ctx, const char *path)
580 {
581         size_t len;
582         char *p1, *p2, *p;
583         char *path_out;
584
585         /* No absolute paths. */
586         while (IS_DIRECTORY_SEP(*path)) {
587                 path++;
588         }
589
590         path_out = talloc_strdup(ctx, path);
591         if (!path_out) {
592                 return NULL;
593         }
594
595         p1 = strchr_m(path_out, '*');
596         p2 = strchr_m(path_out, '?');
597
598         if (p1 || p2) {
599                 if (p1 && p2) {
600                         p = MIN(p1,p2);
601                 } else if (!p1) {
602                         p = p2;
603                 } else {
604                         p = p1;
605                 }
606                 *p = '\0';
607
608                 /* Now go back to the start of this component. */
609                 p1 = strrchr_m(path_out, '/');
610                 p2 = strrchr_m(path_out, '\\');
611                 p = MAX(p1,p2);
612                 if (p) {
613                         *p = '\0';
614                 }
615         }
616
617         /* Strip any trailing separator */
618
619         len = strlen(path_out);
620         if ( (len > 0) && IS_DIRECTORY_SEP(path_out[len-1])) {
621                 path_out[len-1] = '\0';
622         }
623
624         return path_out;
625 }
626
627 /****************************************************************************
628 ****************************************************************************/
629
630 static char *cli_dfs_make_full_path(TALLOC_CTX *ctx,
631                                         struct cli_state *cli,
632                                         const char *dir)
633 {
634         char path_sep = '\\';
635
636         /* Ensure the extrapath doesn't start with a separator. */
637         while (IS_DIRECTORY_SEP(*dir)) {
638                 dir++;
639         }
640
641         if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
642                 path_sep = '/';
643         }
644         return talloc_asprintf(ctx, "%c%s%c%s%c%s",
645                         path_sep,
646                         smbXcli_conn_remote_name(cli->conn),
647                         path_sep,
648                         cli->share,
649                         path_sep,
650                         dir);
651 }
652
653 /********************************************************************
654  check for dfs referral
655 ********************************************************************/
656
657 static bool cli_dfs_check_error(struct cli_state *cli, NTSTATUS expected,
658                                 NTSTATUS status)
659 {
660         /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */
661
662         if (!(smbXcli_conn_use_unicode(cli->conn))) {
663                 return false;
664         }
665         if (!(smb1cli_conn_capabilities(cli->conn) & CAP_STATUS32)) {
666                 return false;
667         }
668         if (NT_STATUS_EQUAL(status, expected)) {
669                 return true;
670         }
671         return false;
672 }
673
674 /********************************************************************
675  Get the dfs referral link.
676 ********************************************************************/
677
678 NTSTATUS cli_dfs_get_referral(TALLOC_CTX *ctx,
679                         struct cli_state *cli,
680                         const char *path,
681                         struct client_dfs_referral **refs,
682                         size_t *num_refs,
683                         size_t *consumed)
684 {
685         unsigned int param_len = 0;
686         uint16_t recv_flags2;
687         uint8_t *param = NULL;
688         uint8_t *rdata = NULL;
689         char *p;
690         char *endp;
691         smb_ucs2_t *path_ucs;
692         char *consumed_path = NULL;
693         uint16_t consumed_ucs;
694         uint16_t num_referrals;
695         struct client_dfs_referral *referrals = NULL;
696         NTSTATUS status;
697         TALLOC_CTX *frame = talloc_stackframe();
698
699         *num_refs = 0;
700         *refs = NULL;
701
702         param = talloc_array(talloc_tos(), uint8_t, 2);
703         if (!param) {
704                 status = NT_STATUS_NO_MEMORY;
705                 goto out;
706         }
707         SSVAL(param, 0, 0x03);  /* max referral level */
708
709         param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn),
710                                       path, strlen(path)+1,
711                                       NULL);
712         if (!param) {
713                 status = NT_STATUS_NO_MEMORY;
714                 goto out;
715         }
716         param_len = talloc_get_size(param);
717         path_ucs = (smb_ucs2_t *)&param[2];
718
719         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
720                 DATA_BLOB in_input_buffer;
721                 DATA_BLOB in_output_buffer = data_blob_null;
722                 DATA_BLOB out_input_buffer = data_blob_null;
723                 DATA_BLOB out_output_buffer = data_blob_null;
724
725                 in_input_buffer.data = param;
726                 in_input_buffer.length = param_len;
727
728                 status = smb2cli_ioctl(cli->conn,
729                                        cli->timeout,
730                                        cli->smb2.session,
731                                        cli->smb2.tcon,
732                                        UINT64_MAX, /* in_fid_persistent */
733                                        UINT64_MAX, /* in_fid_volatile */
734                                        FSCTL_DFS_GET_REFERRALS,
735                                        0, /* in_max_input_length */
736                                        &in_input_buffer,
737                                        CLI_BUFFER_SIZE, /* in_max_output_length */
738                                        &in_output_buffer,
739                                        SMB2_IOCTL_FLAG_IS_FSCTL,
740                                        talloc_tos(),
741                                        &out_input_buffer,
742                                        &out_output_buffer);
743                 if (!NT_STATUS_IS_OK(status)) {
744                         goto out;
745                 }
746
747                 if (out_output_buffer.length < 4) {
748                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
749                         goto out;
750                 }
751
752                 recv_flags2 = FLAGS2_UNICODE_STRINGS;
753                 rdata = out_output_buffer.data;
754                 endp = (char *)rdata + out_output_buffer.length;
755         } else {
756                 unsigned int data_len = 0;
757                 uint16_t setup[1];
758
759                 SSVAL(setup, 0, TRANSACT2_GET_DFS_REFERRAL);
760
761                 status = cli_trans(talloc_tos(), cli, SMBtrans2,
762                                    NULL, 0xffff, 0, 0,
763                                    setup, 1, 0,
764                                    param, param_len, 2,
765                                    NULL, 0, CLI_BUFFER_SIZE,
766                                    &recv_flags2,
767                                    NULL, 0, NULL, /* rsetup */
768                                    NULL, 0, NULL,
769                                    &rdata, 4, &data_len);
770                 if (!NT_STATUS_IS_OK(status)) {
771                         goto out;
772                 }
773
774                 endp = (char *)rdata + data_len;
775         }
776
777         consumed_ucs  = SVAL(rdata, 0);
778         num_referrals = SVAL(rdata, 2);
779
780         /* consumed_ucs is the number of bytes
781          * of the UCS2 path consumed not counting any
782          * terminating null. We need to convert
783          * back to unix charset and count again
784          * to get the number of bytes consumed from
785          * the incoming path. */
786
787         errno = 0;
788         if (pull_string_talloc(talloc_tos(),
789                         NULL,
790                         0,
791                         &consumed_path,
792                         path_ucs,
793                         consumed_ucs,
794                         STR_UNICODE) == 0) {
795                 if (errno != 0) {
796                         status = map_nt_error_from_unix(errno);
797                 } else {
798                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
799                 }
800                 goto out;
801         }
802         if (consumed_path == NULL) {
803                 status = map_nt_error_from_unix(errno);
804                 goto out;
805         }
806         *consumed = strlen(consumed_path);
807
808         if (num_referrals != 0) {
809                 uint16_t ref_version;
810                 uint16_t ref_size;
811                 int i;
812                 uint16_t node_offset;
813
814                 referrals = talloc_array(ctx, struct client_dfs_referral,
815                                          num_referrals);
816
817                 if (!referrals) {
818                         status = NT_STATUS_NO_MEMORY;
819                         goto out;
820                 }
821                 /* start at the referrals array */
822
823                 p = (char *)rdata+8;
824                 for (i=0; i<num_referrals && p < endp; i++) {
825                         if (p + 18 > endp) {
826                                 goto out;
827                         }
828                         ref_version = SVAL(p, 0);
829                         ref_size    = SVAL(p, 2);
830                         node_offset = SVAL(p, 16);
831
832                         if (ref_version != 3) {
833                                 p += ref_size;
834                                 continue;
835                         }
836
837                         referrals[i].proximity = SVAL(p, 8);
838                         referrals[i].ttl       = SVAL(p, 10);
839
840                         if (p + node_offset > endp) {
841                                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
842                                 goto out;
843                         }
844                         clistr_pull_talloc(referrals,
845                                            (const char *)rdata,
846                                            recv_flags2,
847                                            &referrals[i].dfspath,
848                                            p+node_offset,
849                                            PTR_DIFF(endp, p+node_offset),
850                                            STR_TERMINATE|STR_UNICODE);
851
852                         if (!referrals[i].dfspath) {
853                                 status = map_nt_error_from_unix(errno);
854                                 goto out;
855                         }
856                         p += ref_size;
857                 }
858                 if (i < num_referrals) {
859                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
860                         goto out;
861                 }
862         }
863
864         *num_refs = num_referrals;
865         *refs = referrals;
866
867   out:
868
869         TALLOC_FREE(frame);
870         return status;
871 }
872
873 /********************************************************************
874 ********************************************************************/
875 struct cli_dfs_path_split {
876         char *server;
877         char *share;
878         char *extrapath;
879 };
880
881 NTSTATUS cli_resolve_path(TALLOC_CTX *ctx,
882                           const char *mountpt,
883                           const struct user_auth_info *dfs_auth_info,
884                           struct cli_state *rootcli,
885                           const char *path,
886                           struct cli_state **targetcli,
887                           char **pp_targetpath)
888 {
889         struct client_dfs_referral *refs = NULL;
890         size_t num_refs = 0;
891         size_t consumed = 0;
892         struct cli_state *cli_ipc = NULL;
893         char *dfs_path = NULL;
894         char *cleanpath = NULL;
895         char *extrapath = NULL;
896         int pathlen;
897         struct cli_state *newcli = NULL;
898         struct cli_state *ccli = NULL;
899         int count = 0;
900         char *newpath = NULL;
901         char *newmount = NULL;
902         char *ppath = NULL;
903         SMB_STRUCT_STAT sbuf;
904         uint32_t attributes;
905         NTSTATUS status;
906         struct smbXcli_tcon *root_tcon = NULL;
907         struct smbXcli_tcon *target_tcon = NULL;
908         struct cli_dfs_path_split *dfs_refs = NULL;
909
910         if ( !rootcli || !path || !targetcli ) {
911                 return NT_STATUS_INVALID_PARAMETER;
912         }
913
914         /* Don't do anything if this is not a DFS root. */
915
916         if (smbXcli_conn_protocol(rootcli->conn) >= PROTOCOL_SMB2_02) {
917                 root_tcon = rootcli->smb2.tcon;
918         } else {
919                 root_tcon = rootcli->smb1.tcon;
920         }
921
922         if (!smbXcli_tcon_is_dfs_share(root_tcon)) {
923                 *targetcli = rootcli;
924                 *pp_targetpath = talloc_strdup(ctx, path);
925                 if (!*pp_targetpath) {
926                         return NT_STATUS_NO_MEMORY;
927                 }
928                 return NT_STATUS_OK;
929         }
930
931         *targetcli = NULL;
932
933         /* Send a trans2_query_path_info to check for a referral. */
934
935         cleanpath = clean_path(ctx, path);
936         if (!cleanpath) {
937                 return NT_STATUS_NO_MEMORY;
938         }
939
940         dfs_path = cli_dfs_make_full_path(ctx, rootcli, cleanpath);
941         if (!dfs_path) {
942                 return NT_STATUS_NO_MEMORY;
943         }
944
945         status = cli_qpathinfo_basic( rootcli, dfs_path, &sbuf, &attributes);
946         if (NT_STATUS_IS_OK(status)) {
947                 /* This is an ordinary path, just return it. */
948                 *targetcli = rootcli;
949                 *pp_targetpath = talloc_strdup(ctx, path);
950                 if (!*pp_targetpath) {
951                         return NT_STATUS_NO_MEMORY;
952                 }
953                 goto done;
954         }
955
956         /* Special case where client asked for a path that does not exist */
957
958         if (cli_dfs_check_error(rootcli, NT_STATUS_OBJECT_NAME_NOT_FOUND,
959                                 status)) {
960                 *targetcli = rootcli;
961                 *pp_targetpath = talloc_strdup(ctx, path);
962                 if (!*pp_targetpath) {
963                         return NT_STATUS_NO_MEMORY;
964                 }
965                 goto done;
966         }
967
968         /* We got an error, check for DFS referral. */
969
970         if (!cli_dfs_check_error(rootcli, NT_STATUS_PATH_NOT_COVERED,
971                                  status)) {
972                 return status;
973         }
974
975         /* Check for the referral. */
976
977         status = cli_cm_open(ctx,
978                              rootcli,
979                              smbXcli_conn_remote_name(rootcli->conn),
980                              "IPC$",
981                              dfs_auth_info,
982                              false,
983                              smb1cli_conn_encryption_on(rootcli->conn),
984                              smbXcli_conn_protocol(rootcli->conn),
985                              0,
986                              0x20,
987                              &cli_ipc);
988         if (!NT_STATUS_IS_OK(status)) {
989                 return status;
990         }
991
992         status = cli_dfs_get_referral(ctx, cli_ipc, dfs_path, &refs,
993                                       &num_refs, &consumed);
994         if (!NT_STATUS_IS_OK(status)) {
995                 return status;
996         }
997
998         if (!num_refs || !refs[0].dfspath) {
999                 return NT_STATUS_NOT_FOUND;
1000         }
1001
1002         /*
1003          * Bug#10123 - DFS referal entries can be provided in a random order,
1004          * so check the connection cache for each item to avoid unnecessary
1005          * reconnections.
1006          */
1007         dfs_refs = talloc_array(ctx, struct cli_dfs_path_split, num_refs);
1008         if (dfs_refs == NULL) {
1009                 return NT_STATUS_NO_MEMORY;
1010         }
1011
1012         for (count = 0; count < num_refs; count++) {
1013                 if (!split_dfs_path(dfs_refs, refs[count].dfspath,
1014                                     &dfs_refs[count].server,
1015                                     &dfs_refs[count].share,
1016                                     &dfs_refs[count].extrapath)) {
1017                         TALLOC_FREE(dfs_refs);
1018                         return NT_STATUS_NOT_FOUND;
1019                 }
1020
1021                 ccli = cli_cm_find(rootcli, dfs_refs[count].server,
1022                                    dfs_refs[count].share);
1023                 if (ccli != NULL) {
1024                         extrapath = dfs_refs[count].extrapath;
1025                         *targetcli = ccli;
1026                         break;
1027                 }
1028         }
1029
1030         /*
1031          * If no cached connection was found, then connect to the first live
1032          * referral server in the list.
1033          */
1034         for (count = 0; (ccli == NULL) && (count < num_refs); count++) {
1035                 /* Connect to the target server & share */
1036                 status = cli_cm_connect(ctx, rootcli,
1037                                 dfs_refs[count].server,
1038                                 dfs_refs[count].share,
1039                                 dfs_auth_info,
1040                                 false,
1041                                 smb1cli_conn_encryption_on(rootcli->conn),
1042                                 smbXcli_conn_protocol(rootcli->conn),
1043                                 0,
1044                                 0x20,
1045                                 targetcli);
1046                 if (!NT_STATUS_IS_OK(status)) {
1047                         d_printf("Unable to follow dfs referral [\\%s\\%s]\n",
1048                                  dfs_refs[count].server,
1049                                  dfs_refs[count].share);
1050                         continue;
1051                 } else {
1052                         extrapath = dfs_refs[count].extrapath;
1053                         break;
1054                 }
1055         }
1056
1057         /* No available referral server for the connection */
1058         if (*targetcli == NULL) {
1059                 TALLOC_FREE(dfs_refs);
1060                 return status;
1061         }
1062
1063         /* Make sure to recreate the original string including any wildcards. */
1064
1065         dfs_path = cli_dfs_make_full_path(ctx, rootcli, path);
1066         if (!dfs_path) {
1067                 TALLOC_FREE(dfs_refs);
1068                 return NT_STATUS_NO_MEMORY;
1069         }
1070         pathlen = strlen(dfs_path);
1071         consumed = MIN(pathlen, consumed);
1072         *pp_targetpath = talloc_strdup(ctx, &dfs_path[consumed]);
1073         if (!*pp_targetpath) {
1074                 TALLOC_FREE(dfs_refs);
1075                 return NT_STATUS_NO_MEMORY;
1076         }
1077         dfs_path[consumed] = '\0';
1078
1079         /*
1080          * *pp_targetpath is now the unconsumed part of the path.
1081          * dfs_path is now the consumed part of the path
1082          * (in \server\share\path format).
1083          */
1084
1085         if (extrapath && strlen(extrapath) > 0) {
1086                 /* EMC Celerra NAS version 5.6.50 (at least) doesn't appear to */
1087                 /* put the trailing \ on the path, so to be save we put one in if needed */
1088                 if (extrapath[strlen(extrapath)-1] != '\\' && **pp_targetpath != '\\') {
1089                         *pp_targetpath = talloc_asprintf(ctx,
1090                                                   "%s\\%s",
1091                                                   extrapath,
1092                                                   *pp_targetpath);
1093                 } else {
1094                         *pp_targetpath = talloc_asprintf(ctx,
1095                                                   "%s%s",
1096                                                   extrapath,
1097                                                   *pp_targetpath);
1098                 }
1099                 if (!*pp_targetpath) {
1100                         TALLOC_FREE(dfs_refs);
1101                         return NT_STATUS_NO_MEMORY;
1102                 }
1103         }
1104
1105         /* parse out the consumed mount path */
1106         /* trim off the \server\share\ */
1107
1108         ppath = dfs_path;
1109
1110         if (*ppath != '\\') {
1111                 d_printf("cli_resolve_path: "
1112                         "dfs_path (%s) not in correct format.\n",
1113                         dfs_path );
1114                 TALLOC_FREE(dfs_refs);
1115                 return NT_STATUS_NOT_FOUND;
1116         }
1117
1118         ppath++; /* Now pointing at start of server name. */
1119
1120         if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) {
1121                 TALLOC_FREE(dfs_refs);
1122                 return NT_STATUS_NOT_FOUND;
1123         }
1124
1125         ppath++; /* Now pointing at start of share name. */
1126
1127         if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) {
1128                 TALLOC_FREE(dfs_refs);
1129                 return NT_STATUS_NOT_FOUND;
1130         }
1131
1132         ppath++; /* Now pointing at path component. */
1133
1134         newmount = talloc_asprintf(ctx, "%s\\%s", mountpt, ppath );
1135         if (!newmount) {
1136                 TALLOC_FREE(dfs_refs);
1137                 return NT_STATUS_NOT_FOUND;
1138         }
1139
1140         cli_set_mntpoint(*targetcli, newmount);
1141
1142         /* Check for another dfs referral, note that we are not
1143            checking for loops here. */
1144
1145         if (!strequal(*pp_targetpath, "\\") && !strequal(*pp_targetpath, "/")) {
1146                 status = cli_resolve_path(ctx,
1147                                           newmount,
1148                                           dfs_auth_info,
1149                                           *targetcli,
1150                                           *pp_targetpath,
1151                                           &newcli,
1152                                           &newpath);
1153                 if (NT_STATUS_IS_OK(status)) {
1154                         /*
1155                          * When cli_resolve_path returns true here it's always
1156                          * returning the complete path in newpath, so we're done
1157                          * here.
1158                          */
1159                         *targetcli = newcli;
1160                         *pp_targetpath = newpath;
1161                         TALLOC_FREE(dfs_refs);
1162                         return status;
1163                 }
1164         }
1165
1166   done:
1167
1168         if (smbXcli_conn_protocol((*targetcli)->conn) >= PROTOCOL_SMB2_02) {
1169                 target_tcon = (*targetcli)->smb2.tcon;
1170         } else {
1171                 target_tcon = (*targetcli)->smb1.tcon;
1172         }
1173
1174         /* If returning true ensure we return a dfs root full path. */
1175         if (smbXcli_tcon_is_dfs_share(target_tcon)) {
1176                 dfs_path = talloc_strdup(ctx, *pp_targetpath);
1177                 if (!dfs_path) {
1178                         TALLOC_FREE(dfs_refs);
1179                         return NT_STATUS_NO_MEMORY;
1180                 }
1181                 *pp_targetpath = cli_dfs_make_full_path(ctx, *targetcli, dfs_path);
1182                 if (*pp_targetpath == NULL) {
1183                         TALLOC_FREE(dfs_refs);
1184                         return NT_STATUS_NO_MEMORY;
1185                 }
1186         }
1187
1188         TALLOC_FREE(dfs_refs);
1189         return NT_STATUS_OK;
1190 }
1191
1192 /********************************************************************
1193 ********************************************************************/
1194
1195 bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
1196                                 struct cli_state *cli,
1197                                 const char *sharename,
1198                                 char **pp_newserver,
1199                                 char **pp_newshare,
1200                                 bool force_encrypt,
1201                                 struct cli_credentials *creds)
1202 {
1203         struct client_dfs_referral *refs = NULL;
1204         size_t num_refs = 0;
1205         size_t consumed = 0;
1206         char *fullpath = NULL;
1207         bool res;
1208         uint16_t cnum;
1209         char *newextrapath = NULL;
1210         NTSTATUS status;
1211         const char *remote_name;
1212
1213         if (!cli || !sharename) {
1214                 return false;
1215         }
1216
1217         remote_name = smbXcli_conn_remote_name(cli->conn);
1218         cnum = cli_state_get_tid(cli);
1219
1220         /* special case.  never check for a referral on the IPC$ share */
1221
1222         if (strequal(sharename, "IPC$")) {
1223                 return false;
1224         }
1225
1226         /* send a trans2_query_path_info to check for a referral */
1227
1228         fullpath = talloc_asprintf(ctx, "\\%s\\%s", remote_name, sharename);
1229         if (!fullpath) {
1230                 return false;
1231         }
1232
1233         /* check for the referral */
1234
1235         if (!NT_STATUS_IS_OK(cli_tree_connect(cli, "IPC$", "IPC", NULL))) {
1236                 return false;
1237         }
1238
1239         if (force_encrypt) {
1240                 status = cli_cm_force_encryption_creds(cli, creds, "IPC$");
1241                 if (!NT_STATUS_IS_OK(status)) {
1242                         return false;
1243                 }
1244         }
1245
1246         status = cli_dfs_get_referral(ctx, cli, fullpath, &refs,
1247                                       &num_refs, &consumed);
1248         res = NT_STATUS_IS_OK(status);
1249
1250         status = cli_tdis(cli);
1251         if (!NT_STATUS_IS_OK(status)) {
1252                 return false;
1253         }
1254
1255         cli_state_set_tid(cli, cnum);
1256
1257         if (!res || !num_refs) {
1258                 return false;
1259         }
1260
1261         if (!refs[0].dfspath) {
1262                 return false;
1263         }
1264
1265         if (!split_dfs_path(ctx, refs[0].dfspath, pp_newserver,
1266                             pp_newshare, &newextrapath)) {
1267                 return false;
1268         }
1269
1270         /* check that this is not a self-referral */
1271
1272         if (strequal(remote_name, *pp_newserver) &&
1273                         strequal(sharename, *pp_newshare)) {
1274                 return false;
1275         }
1276
1277         return true;
1278 }