s3: libsmb: Ensure we disconnect the temporary SMB1 tcon pointer on failure to set...
[Samba.git] / source3 / libsmb / clidfs.c
blob040b957e6f8c6d19d68fe4554471b611b580cd0d
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
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.
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.
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/>.
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 #include "auth/credentials/credentials.h"
31 /********************************************************************
32 Important point.
34 DFS paths are *always* of the form \server\share\<pathname> (the \ characters
35 are not C escaped here).
37 - but if we're using POSIX paths then <pathname> may contain
38 '/' separators, not '\\' separators. So cope with '\\' or '/'
39 as a separator when looking at the pathname part.... JRA.
40 ********************************************************************/
42 /********************************************************************
43 Ensure a connection is encrypted.
44 ********************************************************************/
46 static NTSTATUS cli_cm_force_encryption_creds(struct cli_state *c,
47 struct cli_credentials *creds,
48 const char *sharename)
50 uint16_t major, minor;
51 uint32_t caplow, caphigh;
52 NTSTATUS status;
54 if (smbXcli_conn_protocol(c->conn) >= PROTOCOL_SMB2_02) {
55 status = smb2cli_session_encryption_on(c->smb2.session);
56 if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_SUPPORTED)) {
57 d_printf("Encryption required and "
58 "server doesn't support "
59 "SMB3 encryption - failing connect\n");
60 } else if (!NT_STATUS_IS_OK(status)) {
61 d_printf("Encryption required and "
62 "setup failed with error %s.\n",
63 nt_errstr(status));
65 return status;
68 if (!SERVER_HAS_UNIX_CIFS(c)) {
69 d_printf("Encryption required and "
70 "server that doesn't support "
71 "UNIX extensions - failing connect\n");
72 return NT_STATUS_NOT_SUPPORTED;
75 status = cli_unix_extensions_version(c, &major, &minor, &caplow,
76 &caphigh);
77 if (!NT_STATUS_IS_OK(status)) {
78 d_printf("Encryption required and "
79 "can't get UNIX CIFS extensions "
80 "version from server.\n");
81 return NT_STATUS_UNKNOWN_REVISION;
84 if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) {
85 d_printf("Encryption required and "
86 "share %s doesn't support "
87 "encryption.\n", sharename);
88 return NT_STATUS_UNSUPPORTED_COMPRESSION;
91 status = cli_smb1_setup_encryption(c, creds);
92 if (!NT_STATUS_IS_OK(status)) {
93 d_printf("Encryption required and "
94 "setup failed with error %s.\n",
95 nt_errstr(status));
96 return status;
99 return NT_STATUS_OK;
102 /********************************************************************
103 Return a connection to a server.
104 ********************************************************************/
106 static NTSTATUS do_connect(TALLOC_CTX *ctx,
107 const char *server,
108 const char *share,
109 struct cli_credentials *creds,
110 const struct sockaddr_storage *dest_ss,
111 int port,
112 int name_type,
113 struct cli_state **pcli)
115 struct cli_state *c = NULL;
116 char *servicename;
117 char *sharename;
118 char *newserver, *newshare;
119 NTSTATUS status;
120 int flags = 0;
121 enum protocol_types protocol = PROTOCOL_NONE;
122 enum smb_signing_setting signing_state =
123 cli_credentials_get_smb_signing(creds);
124 enum smb_encryption_setting encryption_state =
125 cli_credentials_get_smb_encryption(creds);
127 if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
128 signing_state = SMB_SIGNING_REQUIRED;
131 /* make a copy so we don't modify the global string 'service' */
132 servicename = talloc_strdup(ctx,share);
133 if (!servicename) {
134 return NT_STATUS_NO_MEMORY;
136 sharename = servicename;
137 if (*sharename == '\\') {
138 sharename += 2;
139 if (server == NULL) {
140 server = sharename;
142 sharename = strchr_m(sharename,'\\');
143 if (!sharename) {
144 return NT_STATUS_NO_MEMORY;
146 *sharename = 0;
147 sharename++;
149 if (server == NULL) {
150 return NT_STATUS_INVALID_PARAMETER;
153 status = cli_connect_nb(
154 server, dest_ss, port, name_type, NULL,
155 signing_state,
156 flags, &c);
158 if (!NT_STATUS_IS_OK(status)) {
159 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
160 DBG_ERR("NetBIOS support disabled, unable to connect");
163 DBG_WARNING("Connection to %s failed (Error %s)\n",
164 server,
165 nt_errstr(status));
166 return status;
169 DEBUG(4,(" session request ok\n"));
171 status = smbXcli_negprot(c->conn, c->timeout,
172 lp_client_min_protocol(),
173 lp_client_max_protocol());
175 if (!NT_STATUS_IS_OK(status)) {
176 d_printf("protocol negotiation failed: %s\n",
177 nt_errstr(status));
178 cli_shutdown(c);
179 return status;
181 protocol = smbXcli_conn_protocol(c->conn);
182 DEBUG(4,(" negotiated dialect[%s] against server[%s]\n",
183 smb_protocol_types_string(protocol),
184 smbXcli_conn_remote_name(c->conn)));
186 if (protocol >= PROTOCOL_SMB2_02) {
187 /* Ensure we ask for some initial credits. */
188 smb2cli_conn_set_max_credits(c->conn, DEFAULT_SMB2_MAX_CREDITS);
191 status = cli_session_setup_creds(c, creds);
192 if (!NT_STATUS_IS_OK(status)) {
193 /* If a password was not supplied then
194 * try again with a null username. */
195 if (encryption_state == SMB_ENCRYPTION_REQUIRED ||
196 smbXcli_conn_signing_mandatory(c->conn) ||
197 cli_credentials_authentication_requested(creds) ||
198 cli_credentials_is_anonymous(creds) ||
199 !NT_STATUS_IS_OK(status = cli_session_setup_anon(c)))
201 d_printf("session setup failed: %s\n",
202 nt_errstr(status));
203 if (NT_STATUS_EQUAL(status,
204 NT_STATUS_MORE_PROCESSING_REQUIRED))
205 d_printf("did you forget to run kinit?\n");
206 cli_shutdown(c);
207 return status;
209 d_printf("Anonymous login successful\n");
212 if (!NT_STATUS_IS_OK(status)) {
213 DEBUG(10,("cli_init_creds() failed: %s\n", nt_errstr(status)));
214 cli_shutdown(c);
215 return status;
218 DEBUG(4,(" session setup ok\n"));
220 /* here's the fun part....to support 'msdfs proxy' shares
221 (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL
222 here before trying to connect to the original share.
223 cli_check_msdfs_proxy() will fail if it is a normal share. */
225 if (smbXcli_conn_dfs_supported(c->conn) &&
226 cli_check_msdfs_proxy(ctx, c, sharename,
227 &newserver, &newshare,
228 creds)) {
229 cli_shutdown(c);
230 return do_connect(ctx, newserver,
231 newshare, creds,
232 NULL, port, name_type, pcli);
235 /* must be a normal share */
237 status = cli_tree_connect_creds(c, sharename, "?????", creds);
238 if (!NT_STATUS_IS_OK(status)) {
239 d_printf("tree connect failed: %s\n", nt_errstr(status));
240 cli_shutdown(c);
241 return status;
244 if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
245 status = cli_cm_force_encryption_creds(c,
246 creds,
247 sharename);
248 if (!NT_STATUS_IS_OK(status)) {
249 switch (encryption_state) {
250 case SMB_ENCRYPTION_DESIRED:
251 break;
252 case SMB_ENCRYPTION_REQUIRED:
253 default:
254 cli_shutdown(c);
255 return status;
260 DEBUG(4,(" tconx ok\n"));
261 *pcli = c;
262 return NT_STATUS_OK;
265 /********************************************************************
266 Add a new connection to the list.
267 referring_cli == NULL means a new initial connection.
268 ********************************************************************/
270 static NTSTATUS cli_cm_connect(TALLOC_CTX *ctx,
271 struct cli_state *referring_cli,
272 const char *server,
273 const char *share,
274 struct cli_credentials *creds,
275 const struct sockaddr_storage *dest_ss,
276 int port,
277 int name_type,
278 struct cli_state **pcli)
280 struct cli_state *cli = NULL;
281 NTSTATUS status;
283 status = do_connect(ctx, server, share,
284 creds,
285 dest_ss, port, name_type, &cli);
287 if (!NT_STATUS_IS_OK(status)) {
288 return status;
292 * This can't happen, this test is to satisfy static
293 * checkers (clang)
295 if (cli == NULL) {
296 return NT_STATUS_NO_MEMORY;
299 /* Enter into the list. */
300 if (referring_cli) {
301 DLIST_ADD_END(referring_cli, cli);
304 if (referring_cli && referring_cli->requested_posix_capabilities) {
305 uint16_t major, minor;
306 uint32_t caplow, caphigh;
307 status = cli_unix_extensions_version(cli, &major, &minor,
308 &caplow, &caphigh);
309 if (NT_STATUS_IS_OK(status)) {
310 cli_set_unix_extensions_capabilities(cli,
311 major, minor,
312 caplow, caphigh);
316 *pcli = cli;
317 return NT_STATUS_OK;
320 /********************************************************************
321 Return a connection to a server on a particular share.
322 ********************************************************************/
324 static struct cli_state *cli_cm_find(struct cli_state *cli,
325 const char *server,
326 const char *share)
328 struct cli_state *p;
330 if (cli == NULL) {
331 return NULL;
334 /* Search to the start of the list. */
335 for (p = cli; p; p = DLIST_PREV(p)) {
336 const char *remote_name =
337 smbXcli_conn_remote_name(p->conn);
339 if (strequal(server, remote_name) &&
340 strequal(share,p->share)) {
341 return p;
345 /* Search to the end of the list. */
346 for (p = cli->next; p; p = p->next) {
347 const char *remote_name =
348 smbXcli_conn_remote_name(p->conn);
350 if (strequal(server, remote_name) &&
351 strequal(share,p->share)) {
352 return p;
356 return NULL;
359 /****************************************************************************
360 Open a client connection to a \\server\share.
361 ****************************************************************************/
363 NTSTATUS cli_cm_open(TALLOC_CTX *ctx,
364 struct cli_state *referring_cli,
365 const char *server,
366 const char *share,
367 struct cli_credentials *creds,
368 const struct sockaddr_storage *dest_ss,
369 int port,
370 int name_type,
371 struct cli_state **pcli)
373 /* Try to reuse an existing connection in this list. */
374 struct cli_state *c = cli_cm_find(referring_cli, server, share);
375 NTSTATUS status;
377 if (c) {
378 *pcli = c;
379 return NT_STATUS_OK;
382 if (creds == NULL) {
383 /* Can't do a new connection
384 * without auth info. */
385 d_printf("cli_cm_open() Unable to open connection [\\%s\\%s] "
386 "without client credentials\n",
387 server, share );
388 return NT_STATUS_INVALID_PARAMETER;
391 status = cli_cm_connect(ctx,
392 referring_cli,
393 server,
394 share,
395 creds,
396 dest_ss,
397 port,
398 name_type,
399 &c);
400 if (!NT_STATUS_IS_OK(status)) {
401 return status;
403 *pcli = c;
404 return NT_STATUS_OK;
407 /****************************************************************************
408 ****************************************************************************/
410 void cli_cm_display(struct cli_state *cli)
412 int i;
414 for (i=0; cli; cli = cli->next,i++ ) {
415 d_printf("%d:\tserver=%s, share=%s\n",
416 i, smbXcli_conn_remote_name(cli->conn), cli->share);
420 /****************************************************************************
421 ****************************************************************************/
423 /****************************************************************************
424 ****************************************************************************/
426 #if 0
427 void cli_cm_set_credentials(struct user_auth_info *auth_info)
429 SAFE_FREE(cm_creds.username);
430 cm_creds.username = SMB_STRDUP(get_cmdline_auth_info_username(
431 auth_info));
433 if (get_cmdline_auth_info_got_pass(auth_info)) {
434 cm_set_password(get_cmdline_auth_info_password(auth_info));
437 cm_creds.use_kerberos = get_cmdline_auth_info_use_kerberos(auth_info);
438 cm_creds.fallback_after_kerberos = false;
439 cm_creds.signing_state = get_cmdline_auth_info_signing_state(auth_info);
441 #endif
443 /**********************************************************************
444 split a dfs path into the server, share name, and extrapath components
445 **********************************************************************/
447 static bool split_dfs_path(TALLOC_CTX *ctx,
448 const char *nodepath,
449 char **pp_server,
450 char **pp_share,
451 char **pp_extrapath)
453 char *p, *q;
454 char *path;
456 *pp_server = NULL;
457 *pp_share = NULL;
458 *pp_extrapath = NULL;
460 path = talloc_strdup(ctx, nodepath);
461 if (!path) {
462 goto fail;
465 if ( path[0] != '\\' ) {
466 goto fail;
469 p = strchr_m( path + 1, '\\' );
470 if ( !p ) {
471 goto fail;
474 *p = '\0';
475 p++;
477 /* Look for any extra/deep path */
478 q = strchr_m(p, '\\');
479 if (q != NULL) {
480 *q = '\0';
481 q++;
482 *pp_extrapath = talloc_strdup(ctx, q);
483 } else {
484 *pp_extrapath = talloc_strdup(ctx, "");
486 if (*pp_extrapath == NULL) {
487 goto fail;
490 *pp_share = talloc_strdup(ctx, p);
491 if (*pp_share == NULL) {
492 goto fail;
495 *pp_server = talloc_strdup(ctx, &path[1]);
496 if (*pp_server == NULL) {
497 goto fail;
500 TALLOC_FREE(path);
501 return true;
503 fail:
504 TALLOC_FREE(*pp_share);
505 TALLOC_FREE(*pp_extrapath);
506 TALLOC_FREE(path);
507 return false;
510 /****************************************************************************
511 Return the original path truncated at the directory component before
512 the first wildcard character. Trust the caller to provide a NULL
513 terminated string
514 ****************************************************************************/
516 static char *clean_path(TALLOC_CTX *ctx, const char *path)
518 size_t len;
519 char *p1, *p2, *p;
520 char *path_out;
522 /* No absolute paths. */
523 while (IS_DIRECTORY_SEP(*path)) {
524 path++;
527 path_out = talloc_strdup(ctx, path);
528 if (!path_out) {
529 return NULL;
532 p1 = strchr_m(path_out, '*');
533 p2 = strchr_m(path_out, '?');
535 if (p1 || p2) {
536 if (p1 && p2) {
537 p = MIN(p1,p2);
538 } else if (!p1) {
539 p = p2;
540 } else {
541 p = p1;
543 *p = '\0';
545 /* Now go back to the start of this component. */
546 p1 = strrchr_m(path_out, '/');
547 p2 = strrchr_m(path_out, '\\');
548 p = MAX(p1,p2);
549 if (p) {
550 *p = '\0';
554 /* Strip any trailing separator */
556 len = strlen(path_out);
557 if ( (len > 0) && IS_DIRECTORY_SEP(path_out[len-1])) {
558 path_out[len-1] = '\0';
561 return path_out;
564 /****************************************************************************
565 ****************************************************************************/
567 static char *cli_dfs_make_full_path(TALLOC_CTX *ctx,
568 struct cli_state *cli,
569 const char *dir)
571 char path_sep = '\\';
573 /* Ensure the extrapath doesn't start with a separator. */
574 while (IS_DIRECTORY_SEP(*dir)) {
575 dir++;
578 if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
579 path_sep = '/';
581 return talloc_asprintf(ctx, "%c%s%c%s%c%s",
582 path_sep,
583 smbXcli_conn_remote_name(cli->conn),
584 path_sep,
585 cli->share,
586 path_sep,
587 dir);
590 /********************************************************************
591 Get the dfs referral link.
592 ********************************************************************/
594 NTSTATUS cli_dfs_get_referral_ex(TALLOC_CTX *ctx,
595 struct cli_state *cli,
596 const char *path,
597 uint16_t max_referral_level,
598 struct client_dfs_referral **refs,
599 size_t *num_refs,
600 size_t *consumed)
602 unsigned int param_len = 0;
603 uint16_t recv_flags2;
604 uint8_t *param = NULL;
605 uint8_t *rdata = NULL;
606 char *p;
607 char *endp;
608 smb_ucs2_t *path_ucs;
609 char *consumed_path = NULL;
610 uint16_t consumed_ucs;
611 uint16_t num_referrals;
612 struct client_dfs_referral *referrals = NULL;
613 NTSTATUS status;
614 TALLOC_CTX *frame = talloc_stackframe();
616 *num_refs = 0;
617 *refs = NULL;
619 param = talloc_array(talloc_tos(), uint8_t, 2);
620 if (!param) {
621 status = NT_STATUS_NO_MEMORY;
622 goto out;
624 SSVAL(param, 0, max_referral_level);
626 param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn),
627 path, strlen(path)+1,
628 NULL);
629 if (!param) {
630 status = NT_STATUS_NO_MEMORY;
631 goto out;
633 param_len = talloc_get_size(param);
634 path_ucs = (smb_ucs2_t *)&param[2];
636 if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
637 DATA_BLOB in_input_buffer;
638 DATA_BLOB in_output_buffer = data_blob_null;
639 DATA_BLOB out_input_buffer = data_blob_null;
640 DATA_BLOB out_output_buffer = data_blob_null;
642 in_input_buffer.data = param;
643 in_input_buffer.length = param_len;
645 status = smb2cli_ioctl(cli->conn,
646 cli->timeout,
647 cli->smb2.session,
648 cli->smb2.tcon,
649 UINT64_MAX, /* in_fid_persistent */
650 UINT64_MAX, /* in_fid_volatile */
651 FSCTL_DFS_GET_REFERRALS,
652 0, /* in_max_input_length */
653 &in_input_buffer,
654 CLI_BUFFER_SIZE, /* in_max_output_length */
655 &in_output_buffer,
656 SMB2_IOCTL_FLAG_IS_FSCTL,
657 talloc_tos(),
658 &out_input_buffer,
659 &out_output_buffer);
660 if (!NT_STATUS_IS_OK(status)) {
661 goto out;
664 if (out_output_buffer.length < 4) {
665 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
666 goto out;
669 recv_flags2 = FLAGS2_UNICODE_STRINGS;
670 rdata = out_output_buffer.data;
671 endp = (char *)rdata + out_output_buffer.length;
672 } else {
673 unsigned int data_len = 0;
674 uint16_t setup[1];
676 SSVAL(setup, 0, TRANSACT2_GET_DFS_REFERRAL);
678 status = cli_trans(talloc_tos(), cli, SMBtrans2,
679 NULL, 0xffff, 0, 0,
680 setup, 1, 0,
681 param, param_len, 2,
682 NULL, 0, CLI_BUFFER_SIZE,
683 &recv_flags2,
684 NULL, 0, NULL, /* rsetup */
685 NULL, 0, NULL,
686 &rdata, 4, &data_len);
687 if (!NT_STATUS_IS_OK(status)) {
688 goto out;
691 endp = (char *)rdata + data_len;
694 consumed_ucs = SVAL(rdata, 0);
695 num_referrals = SVAL(rdata, 2);
697 /* consumed_ucs is the number of bytes
698 * of the UCS2 path consumed not counting any
699 * terminating null. We need to convert
700 * back to unix charset and count again
701 * to get the number of bytes consumed from
702 * the incoming path. */
704 errno = 0;
705 if (pull_string_talloc(talloc_tos(),
706 NULL,
708 &consumed_path,
709 path_ucs,
710 consumed_ucs,
711 STR_UNICODE) == 0) {
712 if (errno != 0) {
713 status = map_nt_error_from_unix(errno);
714 } else {
715 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
717 goto out;
719 if (consumed_path == NULL) {
720 status = map_nt_error_from_unix(errno);
721 goto out;
723 *consumed = strlen(consumed_path);
725 if (num_referrals != 0) {
726 uint16_t ref_version;
727 uint16_t ref_size;
728 int i;
729 uint16_t node_offset;
731 referrals = talloc_array(ctx, struct client_dfs_referral,
732 num_referrals);
734 if (!referrals) {
735 status = NT_STATUS_NO_MEMORY;
736 goto out;
738 /* start at the referrals array */
740 p = (char *)rdata+8;
741 for (i=0; i<num_referrals && p < endp; i++) {
742 if (p + 18 > endp) {
743 goto out;
745 ref_version = SVAL(p, 0);
746 ref_size = SVAL(p, 2);
747 node_offset = SVAL(p, 16);
749 if (ref_version != 3) {
750 p += ref_size;
751 continue;
754 referrals[i].proximity = SVAL(p, 8);
755 referrals[i].ttl = SVAL(p, 10);
757 if (p + node_offset > endp) {
758 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
759 goto out;
761 pull_string_talloc(referrals,
762 (const char *)rdata,
763 recv_flags2,
764 &referrals[i].dfspath,
765 p+node_offset,
766 PTR_DIFF(endp, p+node_offset),
767 STR_TERMINATE|STR_UNICODE);
769 if (!referrals[i].dfspath) {
770 status = map_nt_error_from_unix(errno);
771 goto out;
773 p += ref_size;
775 if (i < num_referrals) {
776 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
777 goto out;
781 *num_refs = num_referrals;
782 *refs = referrals;
784 out:
786 TALLOC_FREE(frame);
787 return status;
790 NTSTATUS cli_dfs_get_referral(TALLOC_CTX *ctx,
791 struct cli_state *cli,
792 const char *path,
793 struct client_dfs_referral **refs,
794 size_t *num_refs,
795 size_t *consumed)
797 return cli_dfs_get_referral_ex(ctx,
798 cli,
799 path,
801 refs, /* Max referral level we want */
802 num_refs,
803 consumed);
806 static bool cli_conn_have_dfs(struct cli_state *cli)
808 struct smbXcli_conn *conn = cli->conn;
809 struct smbXcli_tcon *tcon = NULL;
810 bool ok;
812 if (smbXcli_conn_protocol(conn) < PROTOCOL_SMB2_02) {
813 uint32_t capabilities = smb1cli_conn_capabilities(conn);
815 if ((capabilities & CAP_STATUS32) == 0) {
816 return false;
818 if ((capabilities & CAP_UNICODE) == 0) {
819 return false;
822 tcon = cli->smb1.tcon;
823 } else {
824 tcon = cli->smb2.tcon;
827 ok = smbXcli_tcon_is_dfs_share(tcon);
828 return ok;
831 /********************************************************************
832 ********************************************************************/
833 struct cli_dfs_path_split {
834 char *server;
835 char *share;
836 char *extrapath;
839 NTSTATUS cli_resolve_path(TALLOC_CTX *ctx,
840 const char *mountpt,
841 struct cli_credentials *creds,
842 struct cli_state *rootcli,
843 const char *path,
844 struct cli_state **targetcli,
845 char **pp_targetpath)
847 struct client_dfs_referral *refs = NULL;
848 size_t num_refs = 0;
849 size_t consumed = 0;
850 struct cli_state *cli_ipc = NULL;
851 char *dfs_path = NULL;
852 char *cleanpath = NULL;
853 char *extrapath = NULL;
854 int pathlen;
855 struct cli_state *newcli = NULL;
856 struct cli_state *ccli = NULL;
857 size_t count = 0;
858 char *newpath = NULL;
859 char *newmount = NULL;
860 char *ppath = NULL;
861 SMB_STRUCT_STAT sbuf;
862 uint32_t attributes;
863 NTSTATUS status;
864 struct smbXcli_tcon *target_tcon = NULL;
865 struct cli_dfs_path_split *dfs_refs = NULL;
866 bool ok;
868 if ( !rootcli || !path || !targetcli ) {
869 return NT_STATUS_INVALID_PARAMETER;
873 * Avoid more than one leading directory separator
875 while (IS_DIRECTORY_SEP(path[0]) && IS_DIRECTORY_SEP(path[1])) {
876 path++;
879 ok = cli_conn_have_dfs(rootcli);
880 if (!ok) {
881 *targetcli = rootcli;
882 *pp_targetpath = talloc_strdup(ctx, path);
883 if (!*pp_targetpath) {
884 return NT_STATUS_NO_MEMORY;
886 return NT_STATUS_OK;
889 *targetcli = NULL;
891 /* Send a trans2_query_path_info to check for a referral. */
893 cleanpath = clean_path(ctx, path);
894 if (!cleanpath) {
895 return NT_STATUS_NO_MEMORY;
898 dfs_path = cli_dfs_make_full_path(ctx, rootcli, cleanpath);
899 if (!dfs_path) {
900 return NT_STATUS_NO_MEMORY;
903 status = cli_qpathinfo_basic( rootcli, dfs_path, &sbuf, &attributes);
904 if (NT_STATUS_IS_OK(status)) {
905 /* This is an ordinary path, just return it. */
906 *targetcli = rootcli;
907 *pp_targetpath = talloc_strdup(ctx, path);
908 if (!*pp_targetpath) {
909 return NT_STATUS_NO_MEMORY;
911 goto done;
914 /* Special case where client asked for a path that does not exist */
916 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
917 *targetcli = rootcli;
918 *pp_targetpath = talloc_strdup(ctx, path);
919 if (!*pp_targetpath) {
920 return NT_STATUS_NO_MEMORY;
922 goto done;
925 /* We got an error, check for DFS referral. */
927 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
928 return status;
931 /* Check for the referral. */
933 status = cli_cm_open(ctx,
934 rootcli,
935 smbXcli_conn_remote_name(rootcli->conn),
936 "IPC$",
937 creds,
938 NULL, /* dest_ss not needed, we reuse the transport */
940 0x20,
941 &cli_ipc);
942 if (!NT_STATUS_IS_OK(status)) {
943 return status;
946 status = cli_dfs_get_referral(ctx, cli_ipc, dfs_path, &refs,
947 &num_refs, &consumed);
948 if (!NT_STATUS_IS_OK(status)) {
949 return status;
952 if (!num_refs || !refs[0].dfspath) {
953 return NT_STATUS_NOT_FOUND;
957 * Bug#10123 - DFS referal entries can be provided in a random order,
958 * so check the connection cache for each item to avoid unnecessary
959 * reconnections.
961 dfs_refs = talloc_array(ctx, struct cli_dfs_path_split, num_refs);
962 if (dfs_refs == NULL) {
963 return NT_STATUS_NO_MEMORY;
966 for (count = 0; count < num_refs; count++) {
967 if (!split_dfs_path(dfs_refs, refs[count].dfspath,
968 &dfs_refs[count].server,
969 &dfs_refs[count].share,
970 &dfs_refs[count].extrapath)) {
971 TALLOC_FREE(dfs_refs);
972 return NT_STATUS_NOT_FOUND;
975 ccli = cli_cm_find(rootcli, dfs_refs[count].server,
976 dfs_refs[count].share);
977 if (ccli != NULL) {
978 extrapath = dfs_refs[count].extrapath;
979 *targetcli = ccli;
980 break;
985 * If no cached connection was found, then connect to the first live
986 * referral server in the list.
988 for (count = 0; (ccli == NULL) && (count < num_refs); count++) {
989 /* Connect to the target server & share */
990 status = cli_cm_connect(ctx, rootcli,
991 dfs_refs[count].server,
992 dfs_refs[count].share,
993 creds,
994 NULL, /* dest_ss */
995 0, /* port */
996 0x20,
997 targetcli);
998 if (!NT_STATUS_IS_OK(status)) {
999 d_printf("Unable to follow dfs referral [\\%s\\%s]\n",
1000 dfs_refs[count].server,
1001 dfs_refs[count].share);
1002 continue;
1003 } else {
1004 extrapath = dfs_refs[count].extrapath;
1005 break;
1009 /* No available referral server for the connection */
1010 if (*targetcli == NULL) {
1011 TALLOC_FREE(dfs_refs);
1012 return status;
1015 /* Make sure to recreate the original string including any wildcards. */
1017 dfs_path = cli_dfs_make_full_path(ctx, rootcli, path);
1018 if (!dfs_path) {
1019 TALLOC_FREE(dfs_refs);
1020 return NT_STATUS_NO_MEMORY;
1022 pathlen = strlen(dfs_path);
1023 consumed = MIN(pathlen, consumed);
1024 *pp_targetpath = talloc_strdup(ctx, &dfs_path[consumed]);
1025 if (!*pp_targetpath) {
1026 TALLOC_FREE(dfs_refs);
1027 return NT_STATUS_NO_MEMORY;
1029 dfs_path[consumed] = '\0';
1032 * *pp_targetpath is now the unconsumed part of the path.
1033 * dfs_path is now the consumed part of the path
1034 * (in \server\share\path format).
1037 if (extrapath && strlen(extrapath) > 0) {
1038 /* EMC Celerra NAS version 5.6.50 (at least) doesn't appear to */
1039 /* put the trailing \ on the path, so to be safe we put one in if needed */
1040 if (extrapath[strlen(extrapath)-1] != '\\' && **pp_targetpath != '\\') {
1041 *pp_targetpath = talloc_asprintf(ctx,
1042 "%s\\%s",
1043 extrapath,
1044 *pp_targetpath);
1045 } else {
1046 *pp_targetpath = talloc_asprintf(ctx,
1047 "%s%s",
1048 extrapath,
1049 *pp_targetpath);
1051 if (!*pp_targetpath) {
1052 TALLOC_FREE(dfs_refs);
1053 return NT_STATUS_NO_MEMORY;
1057 /* parse out the consumed mount path */
1058 /* trim off the \server\share\ */
1060 ppath = dfs_path;
1062 if (*ppath != '\\') {
1063 d_printf("cli_resolve_path: "
1064 "dfs_path (%s) not in correct format.\n",
1065 dfs_path );
1066 TALLOC_FREE(dfs_refs);
1067 return NT_STATUS_NOT_FOUND;
1070 ppath++; /* Now pointing at start of server name. */
1072 if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) {
1073 TALLOC_FREE(dfs_refs);
1074 return NT_STATUS_NOT_FOUND;
1077 ppath++; /* Now pointing at start of share name. */
1079 if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) {
1080 TALLOC_FREE(dfs_refs);
1081 return NT_STATUS_NOT_FOUND;
1084 ppath++; /* Now pointing at path component. */
1086 newmount = talloc_asprintf(ctx, "%s\\%s", mountpt, ppath );
1087 if (!newmount) {
1088 TALLOC_FREE(dfs_refs);
1089 return NT_STATUS_NOT_FOUND;
1092 /* Check for another dfs referral, note that we are not
1093 checking for loops here. */
1095 if (!strequal(*pp_targetpath, "\\") && !strequal(*pp_targetpath, "/")) {
1096 status = cli_resolve_path(ctx,
1097 newmount,
1098 creds,
1099 *targetcli,
1100 *pp_targetpath,
1101 &newcli,
1102 &newpath);
1103 if (NT_STATUS_IS_OK(status)) {
1105 * When cli_resolve_path returns true here it's always
1106 * returning the complete path in newpath, so we're done
1107 * here.
1109 *targetcli = newcli;
1110 *pp_targetpath = newpath;
1111 TALLOC_FREE(dfs_refs);
1112 return status;
1116 done:
1118 if (smbXcli_conn_protocol((*targetcli)->conn) >= PROTOCOL_SMB2_02) {
1119 target_tcon = (*targetcli)->smb2.tcon;
1120 } else {
1121 target_tcon = (*targetcli)->smb1.tcon;
1124 /* If returning true ensure we return a dfs root full path. */
1125 if (smbXcli_tcon_is_dfs_share(target_tcon)) {
1126 dfs_path = talloc_strdup(ctx, *pp_targetpath);
1127 if (!dfs_path) {
1128 TALLOC_FREE(dfs_refs);
1129 return NT_STATUS_NO_MEMORY;
1131 *pp_targetpath = cli_dfs_make_full_path(ctx, *targetcli, dfs_path);
1132 if (*pp_targetpath == NULL) {
1133 TALLOC_FREE(dfs_refs);
1134 return NT_STATUS_NO_MEMORY;
1138 TALLOC_FREE(dfs_refs);
1139 return NT_STATUS_OK;
1142 /********************************************************************
1143 ********************************************************************/
1145 bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
1146 struct cli_state *cli,
1147 const char *sharename,
1148 char **pp_newserver,
1149 char **pp_newshare,
1150 struct cli_credentials *creds)
1152 struct client_dfs_referral *refs = NULL;
1153 size_t num_refs = 0;
1154 size_t consumed = 0;
1155 char *fullpath = NULL;
1156 bool res;
1157 struct smbXcli_tcon *orig_tcon = NULL;
1158 char *newextrapath = NULL;
1159 NTSTATUS status;
1160 const char *remote_name;
1161 enum smb_encryption_setting encryption_state =
1162 cli_credentials_get_smb_encryption(creds);
1164 if (!cli || !sharename) {
1165 return false;
1168 remote_name = smbXcli_conn_remote_name(cli->conn);
1170 /* special case. never check for a referral on the IPC$ share */
1172 if (strequal(sharename, "IPC$")) {
1173 return false;
1176 /* send a trans2_query_path_info to check for a referral */
1178 fullpath = talloc_asprintf(ctx, "\\%s\\%s", remote_name, sharename);
1179 if (!fullpath) {
1180 return false;
1183 /* Store tcon state. */
1184 if (cli_state_has_tcon(cli)) {
1185 orig_tcon = cli_state_save_tcon(cli);
1186 if (orig_tcon == NULL) {
1187 return false;
1191 /* check for the referral */
1193 if (!NT_STATUS_IS_OK(cli_tree_connect(cli, "IPC$", "IPC", NULL))) {
1194 cli_state_restore_tcon(cli, orig_tcon);
1195 return false;
1198 if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
1199 status = cli_cm_force_encryption_creds(cli, creds, "IPC$");
1200 if (!NT_STATUS_IS_OK(status)) {
1201 switch (encryption_state) {
1202 case SMB_ENCRYPTION_DESIRED:
1203 break;
1204 case SMB_ENCRYPTION_REQUIRED:
1205 default:
1207 * Failed to set up encryption.
1208 * Disconnect the temporary IPC$
1209 * tcon before restoring the original
1210 * tcon so we don't leak it.
1212 cli_tdis(cli);
1213 cli_state_restore_tcon(cli, orig_tcon);
1214 return false;
1219 status = cli_dfs_get_referral(ctx, cli, fullpath, &refs,
1220 &num_refs, &consumed);
1221 res = NT_STATUS_IS_OK(status);
1223 status = cli_tdis(cli);
1225 cli_state_restore_tcon(cli, orig_tcon);
1227 if (!NT_STATUS_IS_OK(status)) {
1228 return false;
1231 if (!res || !num_refs) {
1232 return false;
1235 if (!refs[0].dfspath) {
1236 return false;
1239 if (!split_dfs_path(ctx, refs[0].dfspath, pp_newserver,
1240 pp_newshare, &newextrapath)) {
1241 return false;
1244 /* check that this is not a self-referral */
1246 if (strequal(remote_name, *pp_newserver) &&
1247 strequal(sharename, *pp_newshare)) {
1248 return false;
1251 return true;