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