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/>.
23 #include "libsmb/libsmb.h"
24 #include "libsmb/clirap.h"
27 #include "libsmb/nmblib.h"
28 #include "../libcli/smb/smbXcli_base.h"
29 #include "auth/credentials/credentials.h"
31 /********************************************************************
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
;
53 bool temp_ipc
= false;
55 if (smbXcli_conn_protocol(c
->conn
) >= PROTOCOL_SMB2_02
) {
56 status
= smb2cli_session_encryption_on(c
->smb2
.session
);
57 if (NT_STATUS_EQUAL(status
,NT_STATUS_NOT_SUPPORTED
)) {
58 d_printf("Encryption required and "
59 "server doesn't support "
60 "SMB3 encryption - failing connect\n");
61 } else if (!NT_STATUS_IS_OK(status
)) {
62 d_printf("Encryption required and "
63 "setup failed with error %s.\n",
69 if (!SERVER_HAS_UNIX_CIFS(c
)) {
70 d_printf("Encryption required and "
71 "server that doesn't support "
72 "UNIX extensions - failing connect\n");
73 return NT_STATUS_NOT_SUPPORTED
;
76 if (c
->smb1
.tcon
== NULL
) {
77 status
= cli_tree_connect_creds(c
, "IPC$", "IPC", creds
);
78 if (!NT_STATUS_IS_OK(status
)) {
79 d_printf("Encryption required and "
80 "can't connect to IPC$ to check "
81 "UNIX CIFS extensions.\n");
82 return NT_STATUS_UNKNOWN_REVISION
;
87 status
= cli_unix_extensions_version(c
, &major
, &minor
, &caplow
,
89 if (!NT_STATUS_IS_OK(status
)) {
90 d_printf("Encryption required and "
91 "can't get UNIX CIFS extensions "
92 "version from server.\n");
96 return NT_STATUS_UNKNOWN_REVISION
;
99 if (!(caplow
& CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP
)) {
100 d_printf("Encryption required and "
101 "share %s doesn't support "
102 "encryption.\n", sharename
);
106 return NT_STATUS_UNSUPPORTED_COMPRESSION
;
109 status
= cli_smb1_setup_encryption(c
, creds
);
110 if (!NT_STATUS_IS_OK(status
)) {
111 d_printf("Encryption required and "
112 "setup failed with error %s.\n",
126 /********************************************************************
127 Return a connection to a server.
128 ********************************************************************/
130 static NTSTATUS
do_connect(TALLOC_CTX
*ctx
,
133 struct cli_credentials
*creds
,
134 const struct sockaddr_storage
*dest_ss
,
137 struct cli_state
**pcli
)
139 struct cli_state
*c
= NULL
;
142 char *newserver
, *newshare
;
145 enum protocol_types protocol
= PROTOCOL_NONE
;
146 enum smb_signing_setting signing_state
=
147 cli_credentials_get_smb_signing(creds
);
148 enum smb_encryption_setting encryption_state
=
149 cli_credentials_get_smb_encryption(creds
);
151 if (encryption_state
>= SMB_ENCRYPTION_DESIRED
) {
152 signing_state
= SMB_SIGNING_REQUIRED
;
155 /* make a copy so we don't modify the global string 'service' */
156 servicename
= talloc_strdup(ctx
,share
);
158 return NT_STATUS_NO_MEMORY
;
160 sharename
= servicename
;
161 if (*sharename
== '\\') {
163 if (server
== NULL
) {
166 sharename
= strchr_m(sharename
,'\\');
168 return NT_STATUS_NO_MEMORY
;
173 if (server
== NULL
) {
174 return NT_STATUS_INVALID_PARAMETER
;
177 status
= cli_connect_nb(
178 server
, dest_ss
, port
, name_type
, NULL
,
182 if (!NT_STATUS_IS_OK(status
)) {
183 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_SUPPORTED
)) {
184 DBG_ERR("NetBIOS support disabled, unable to connect");
187 DBG_WARNING("Connection to %s failed (Error %s)\n",
193 DEBUG(4,(" session request ok\n"));
195 status
= smbXcli_negprot(c
->conn
, c
->timeout
,
196 lp_client_min_protocol(),
197 lp_client_max_protocol());
199 if (!NT_STATUS_IS_OK(status
)) {
200 d_printf("protocol negotiation failed: %s\n",
205 protocol
= smbXcli_conn_protocol(c
->conn
);
206 DEBUG(4,(" negotiated dialect[%s] against server[%s]\n",
207 smb_protocol_types_string(protocol
),
208 smbXcli_conn_remote_name(c
->conn
)));
210 if (protocol
>= PROTOCOL_SMB2_02
) {
211 /* Ensure we ask for some initial credits. */
212 smb2cli_conn_set_max_credits(c
->conn
, DEFAULT_SMB2_MAX_CREDITS
);
215 status
= cli_session_setup_creds(c
, creds
);
216 if (!NT_STATUS_IS_OK(status
)) {
217 /* If a password was not supplied then
218 * try again with a null username. */
219 if (encryption_state
== SMB_ENCRYPTION_REQUIRED
||
220 smbXcli_conn_signing_mandatory(c
->conn
) ||
221 cli_credentials_authentication_requested(creds
) ||
222 cli_credentials_is_anonymous(creds
) ||
223 !NT_STATUS_IS_OK(status
= cli_session_setup_anon(c
)))
225 d_printf("session setup failed: %s\n",
227 if (NT_STATUS_EQUAL(status
,
228 NT_STATUS_MORE_PROCESSING_REQUIRED
))
229 d_printf("did you forget to run kinit?\n");
233 d_printf("Anonymous login successful\n");
236 if (!NT_STATUS_IS_OK(status
)) {
237 DEBUG(10,("cli_init_creds() failed: %s\n", nt_errstr(status
)));
242 DEBUG(4,(" session setup ok\n"));
244 if (encryption_state
>= SMB_ENCRYPTION_DESIRED
) {
245 status
= cli_cm_force_encryption_creds(c
,
248 if (!NT_STATUS_IS_OK(status
)) {
249 switch (encryption_state
) {
250 case SMB_ENCRYPTION_DESIRED
:
252 case SMB_ENCRYPTION_REQUIRED
:
260 /* here's the fun part....to support 'msdfs proxy' shares
261 (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL
262 here before trying to connect to the original share.
263 cli_check_msdfs_proxy() will fail if it is a normal share. */
265 if (smbXcli_conn_dfs_supported(c
->conn
) &&
266 cli_check_msdfs_proxy(ctx
, c
, sharename
,
267 &newserver
, &newshare
,
270 return do_connect(ctx
, newserver
,
272 NULL
, port
, name_type
, pcli
);
275 /* must be a normal share */
277 status
= cli_tree_connect_creds(c
, sharename
, "?????", creds
);
278 if (!NT_STATUS_IS_OK(status
)) {
279 d_printf("tree connect failed: %s\n", nt_errstr(status
));
284 DEBUG(4,(" tconx ok\n"));
289 /********************************************************************
290 Add a new connection to the list.
291 referring_cli == NULL means a new initial connection.
292 ********************************************************************/
294 static NTSTATUS
cli_cm_connect(TALLOC_CTX
*ctx
,
295 struct cli_state
*referring_cli
,
298 struct cli_credentials
*creds
,
299 const struct sockaddr_storage
*dest_ss
,
302 struct cli_state
**pcli
)
304 struct cli_state
*cli
= NULL
;
307 status
= do_connect(ctx
, server
, share
,
309 dest_ss
, port
, name_type
, &cli
);
311 if (!NT_STATUS_IS_OK(status
)) {
316 * This can't happen, this test is to satisfy static
320 return NT_STATUS_NO_MEMORY
;
323 /* Enter into the list. */
325 DLIST_ADD_END(referring_cli
, cli
);
328 if (referring_cli
&& referring_cli
->requested_posix_capabilities
) {
329 uint16_t major
, minor
;
330 uint32_t caplow
, caphigh
;
331 status
= cli_unix_extensions_version(cli
, &major
, &minor
,
333 if (NT_STATUS_IS_OK(status
)) {
334 cli_set_unix_extensions_capabilities(cli
,
344 /********************************************************************
345 Return a connection to a server on a particular share.
346 ********************************************************************/
348 static struct cli_state
*cli_cm_find(struct cli_state
*cli
,
358 /* Search to the start of the list. */
359 for (p
= cli
; p
; p
= DLIST_PREV(p
)) {
360 const char *remote_name
=
361 smbXcli_conn_remote_name(p
->conn
);
363 if (strequal(server
, remote_name
) &&
364 strequal(share
,p
->share
)) {
369 /* Search to the end of the list. */
370 for (p
= cli
->next
; p
; p
= p
->next
) {
371 const char *remote_name
=
372 smbXcli_conn_remote_name(p
->conn
);
374 if (strequal(server
, remote_name
) &&
375 strequal(share
,p
->share
)) {
383 /****************************************************************************
384 Open a client connection to a \\server\share.
385 ****************************************************************************/
387 NTSTATUS
cli_cm_open(TALLOC_CTX
*ctx
,
388 struct cli_state
*referring_cli
,
391 struct cli_credentials
*creds
,
392 const struct sockaddr_storage
*dest_ss
,
395 struct cli_state
**pcli
)
397 /* Try to reuse an existing connection in this list. */
398 struct cli_state
*c
= cli_cm_find(referring_cli
, server
, share
);
407 /* Can't do a new connection
408 * without auth info. */
409 d_printf("cli_cm_open() Unable to open connection [\\%s\\%s] "
410 "without client credentials\n",
412 return NT_STATUS_INVALID_PARAMETER
;
415 status
= cli_cm_connect(ctx
,
424 if (!NT_STATUS_IS_OK(status
)) {
431 /****************************************************************************
432 ****************************************************************************/
434 void cli_cm_display(struct cli_state
*cli
)
438 for (i
=0; cli
; cli
= cli
->next
,i
++ ) {
439 d_printf("%d:\tserver=%s, share=%s\n",
440 i
, smbXcli_conn_remote_name(cli
->conn
), cli
->share
);
444 /**********************************************************************
445 split a dfs path into the server, share name, and extrapath components
446 **********************************************************************/
448 static bool split_dfs_path(TALLOC_CTX
*ctx
,
449 const char *nodepath
,
459 *pp_extrapath
= NULL
;
461 path
= talloc_strdup(ctx
, nodepath
);
466 if ( path
[0] != '\\' ) {
470 p
= strchr_m( path
+ 1, '\\' );
478 /* Look for any extra/deep path */
479 q
= strchr_m(p
, '\\');
483 *pp_extrapath
= talloc_strdup(ctx
, q
);
485 *pp_extrapath
= talloc_strdup(ctx
, "");
487 if (*pp_extrapath
== NULL
) {
491 *pp_share
= talloc_strdup(ctx
, p
);
492 if (*pp_share
== NULL
) {
496 *pp_server
= talloc_strdup(ctx
, &path
[1]);
497 if (*pp_server
== NULL
) {
505 TALLOC_FREE(*pp_share
);
506 TALLOC_FREE(*pp_extrapath
);
511 /****************************************************************************
512 Return the original path truncated at the directory component before
513 the first wildcard character. Trust the caller to provide a NULL
515 ****************************************************************************/
517 static char *clean_path(TALLOC_CTX
*ctx
, const char *path
)
523 /* No absolute paths. */
524 while (IS_DIRECTORY_SEP(*path
)) {
528 path_out
= talloc_strdup(ctx
, path
);
533 p1
= strchr_m(path_out
, '*');
534 p2
= strchr_m(path_out
, '?');
546 /* Now go back to the start of this component. */
547 p1
= strrchr_m(path_out
, '/');
548 p2
= strrchr_m(path_out
, '\\');
555 /* Strip any trailing separator */
557 len
= strlen(path_out
);
558 if ( (len
> 0) && IS_DIRECTORY_SEP(path_out
[len
-1])) {
559 path_out
[len
-1] = '\0';
565 /****************************************************************************
566 ****************************************************************************/
568 static char *cli_dfs_make_full_path(TALLOC_CTX
*ctx
,
569 struct cli_state
*cli
,
572 char path_sep
= '\\';
574 /* Ensure the extrapath doesn't start with a separator. */
575 while (IS_DIRECTORY_SEP(*dir
)) {
579 if (cli
->requested_posix_capabilities
& CIFS_UNIX_POSIX_PATHNAMES_CAP
) {
582 return talloc_asprintf(ctx
, "%c%s%c%s%c%s",
584 smbXcli_conn_remote_name(cli
->conn
),
591 /********************************************************************
592 Get the dfs referral link.
593 ********************************************************************/
595 NTSTATUS
cli_dfs_get_referral_ex(TALLOC_CTX
*ctx
,
596 struct cli_state
*cli
,
598 uint16_t max_referral_level
,
599 struct client_dfs_referral
**refs
,
603 unsigned int param_len
= 0;
604 uint16_t recv_flags2
;
605 uint8_t *param
= NULL
;
606 uint8_t *rdata
= NULL
;
609 smb_ucs2_t
*path_ucs
;
610 char *consumed_path
= NULL
;
611 uint16_t consumed_ucs
;
612 uint16_t num_referrals
;
613 struct client_dfs_referral
*referrals
= NULL
;
615 TALLOC_CTX
*frame
= talloc_stackframe();
620 param
= talloc_array(talloc_tos(), uint8_t, 2);
622 status
= NT_STATUS_NO_MEMORY
;
625 SSVAL(param
, 0, max_referral_level
);
627 param
= trans2_bytes_push_str(param
, smbXcli_conn_use_unicode(cli
->conn
),
628 path
, strlen(path
)+1,
631 status
= NT_STATUS_NO_MEMORY
;
634 param_len
= talloc_get_size(param
);
635 path_ucs
= (smb_ucs2_t
*)¶m
[2];
637 if (smbXcli_conn_protocol(cli
->conn
) >= PROTOCOL_SMB2_02
) {
638 DATA_BLOB in_input_buffer
;
639 DATA_BLOB in_output_buffer
= data_blob_null
;
640 DATA_BLOB out_input_buffer
= data_blob_null
;
641 DATA_BLOB out_output_buffer
= data_blob_null
;
643 in_input_buffer
.data
= param
;
644 in_input_buffer
.length
= param_len
;
646 status
= smb2cli_ioctl(cli
->conn
,
650 UINT64_MAX
, /* in_fid_persistent */
651 UINT64_MAX
, /* in_fid_volatile */
652 FSCTL_DFS_GET_REFERRALS
,
653 0, /* in_max_input_length */
655 CLI_BUFFER_SIZE
, /* in_max_output_length */
657 SMB2_IOCTL_FLAG_IS_FSCTL
,
661 if (!NT_STATUS_IS_OK(status
)) {
665 if (out_output_buffer
.length
< 4) {
666 status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
670 recv_flags2
= FLAGS2_UNICODE_STRINGS
;
671 rdata
= out_output_buffer
.data
;
672 endp
= (char *)rdata
+ out_output_buffer
.length
;
674 unsigned int data_len
= 0;
677 SSVAL(setup
, 0, TRANSACT2_GET_DFS_REFERRAL
);
679 status
= cli_trans(talloc_tos(), cli
, SMBtrans2
,
683 NULL
, 0, CLI_BUFFER_SIZE
,
685 NULL
, 0, NULL
, /* rsetup */
687 &rdata
, 4, &data_len
);
688 if (!NT_STATUS_IS_OK(status
)) {
692 endp
= (char *)rdata
+ data_len
;
695 consumed_ucs
= SVAL(rdata
, 0);
696 num_referrals
= SVAL(rdata
, 2);
698 /* consumed_ucs is the number of bytes
699 * of the UCS2 path consumed not counting any
700 * terminating null. We need to convert
701 * back to unix charset and count again
702 * to get the number of bytes consumed from
703 * the incoming path. */
706 if (pull_string_talloc(talloc_tos(),
714 status
= map_nt_error_from_unix(errno
);
716 status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
720 if (consumed_path
== NULL
) {
721 status
= map_nt_error_from_unix(errno
);
724 *consumed
= strlen(consumed_path
);
726 if (num_referrals
!= 0) {
727 uint16_t ref_version
;
730 uint16_t node_offset
;
732 referrals
= talloc_array(ctx
, struct client_dfs_referral
,
736 status
= NT_STATUS_NO_MEMORY
;
739 /* start at the referrals array */
742 for (i
=0; i
<num_referrals
&& p
< endp
; i
++) {
746 ref_version
= SVAL(p
, 0);
747 ref_size
= SVAL(p
, 2);
748 node_offset
= SVAL(p
, 16);
750 if (ref_version
!= 3) {
755 referrals
[i
].proximity
= SVAL(p
, 8);
756 referrals
[i
].ttl
= SVAL(p
, 10);
758 if (p
+ node_offset
> endp
) {
759 status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
762 pull_string_talloc(referrals
,
765 &referrals
[i
].dfspath
,
767 PTR_DIFF(endp
, p
+node_offset
),
768 STR_TERMINATE
|STR_UNICODE
);
770 if (!referrals
[i
].dfspath
) {
771 status
= map_nt_error_from_unix(errno
);
776 if (i
< num_referrals
) {
777 status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
782 *num_refs
= num_referrals
;
791 NTSTATUS
cli_dfs_get_referral(TALLOC_CTX
*ctx
,
792 struct cli_state
*cli
,
794 struct client_dfs_referral
**refs
,
798 return cli_dfs_get_referral_ex(ctx
,
802 refs
, /* Max referral level we want */
807 static bool cli_conn_have_dfs(struct cli_state
*cli
)
809 struct smbXcli_conn
*conn
= cli
->conn
;
810 struct smbXcli_tcon
*tcon
= NULL
;
813 if (smbXcli_conn_protocol(conn
) < PROTOCOL_SMB2_02
) {
814 uint32_t capabilities
= smb1cli_conn_capabilities(conn
);
816 if ((capabilities
& CAP_STATUS32
) == 0) {
819 if ((capabilities
& CAP_UNICODE
) == 0) {
823 tcon
= cli
->smb1
.tcon
;
825 tcon
= cli
->smb2
.tcon
;
828 ok
= smbXcli_tcon_is_dfs_share(tcon
);
832 /********************************************************************
833 ********************************************************************/
834 struct cli_dfs_path_split
{
840 NTSTATUS
cli_resolve_path(TALLOC_CTX
*ctx
,
842 struct cli_credentials
*creds
,
843 struct cli_state
*rootcli
,
845 struct cli_state
**targetcli
,
846 char **pp_targetpath
)
848 struct client_dfs_referral
*refs
= NULL
;
851 struct cli_state
*cli_ipc
= NULL
;
852 char *dfs_path
= NULL
;
853 char *cleanpath
= NULL
;
854 char *extrapath
= NULL
;
856 struct cli_state
*newcli
= NULL
;
857 struct cli_state
*ccli
= NULL
;
859 char *newpath
= NULL
;
860 char *newmount
= NULL
;
862 SMB_STRUCT_STAT sbuf
;
865 struct smbXcli_tcon
*target_tcon
= NULL
;
866 struct cli_dfs_path_split
*dfs_refs
= NULL
;
869 if ( !rootcli
|| !path
|| !targetcli
) {
870 return NT_STATUS_INVALID_PARAMETER
;
874 * Avoid more than one leading directory separator
876 while (IS_DIRECTORY_SEP(path
[0]) && IS_DIRECTORY_SEP(path
[1])) {
880 ok
= cli_conn_have_dfs(rootcli
);
882 *targetcli
= rootcli
;
883 *pp_targetpath
= talloc_strdup(ctx
, path
);
884 if (!*pp_targetpath
) {
885 return NT_STATUS_NO_MEMORY
;
892 /* Send a trans2_query_path_info to check for a referral. */
894 cleanpath
= clean_path(ctx
, path
);
896 return NT_STATUS_NO_MEMORY
;
899 dfs_path
= cli_dfs_make_full_path(ctx
, rootcli
, cleanpath
);
901 return NT_STATUS_NO_MEMORY
;
904 status
= cli_qpathinfo_basic( rootcli
, dfs_path
, &sbuf
, &attributes
);
905 if (NT_STATUS_IS_OK(status
)) {
906 /* This is an ordinary path, just return it. */
907 *targetcli
= rootcli
;
908 *pp_targetpath
= talloc_strdup(ctx
, path
);
909 if (!*pp_targetpath
) {
910 return NT_STATUS_NO_MEMORY
;
915 /* Special case where client asked for a path that does not exist */
917 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
918 *targetcli
= rootcli
;
919 *pp_targetpath
= talloc_strdup(ctx
, path
);
920 if (!*pp_targetpath
) {
921 return NT_STATUS_NO_MEMORY
;
926 /* We got an error, check for DFS referral. */
928 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
932 /* Check for the referral. */
934 status
= cli_cm_open(ctx
,
936 smbXcli_conn_remote_name(rootcli
->conn
),
939 NULL
, /* dest_ss not needed, we reuse the transport */
943 if (!NT_STATUS_IS_OK(status
)) {
947 status
= cli_dfs_get_referral(ctx
, cli_ipc
, dfs_path
, &refs
,
948 &num_refs
, &consumed
);
949 if (!NT_STATUS_IS_OK(status
)) {
953 if (!num_refs
|| !refs
[0].dfspath
) {
954 return NT_STATUS_NOT_FOUND
;
958 * Bug#10123 - DFS referal entries can be provided in a random order,
959 * so check the connection cache for each item to avoid unnecessary
962 dfs_refs
= talloc_array(ctx
, struct cli_dfs_path_split
, num_refs
);
963 if (dfs_refs
== NULL
) {
964 return NT_STATUS_NO_MEMORY
;
967 for (count
= 0; count
< num_refs
; count
++) {
968 if (!split_dfs_path(dfs_refs
, refs
[count
].dfspath
,
969 &dfs_refs
[count
].server
,
970 &dfs_refs
[count
].share
,
971 &dfs_refs
[count
].extrapath
)) {
972 TALLOC_FREE(dfs_refs
);
973 return NT_STATUS_NOT_FOUND
;
976 ccli
= cli_cm_find(rootcli
, dfs_refs
[count
].server
,
977 dfs_refs
[count
].share
);
979 extrapath
= dfs_refs
[count
].extrapath
;
986 * If no cached connection was found, then connect to the first live
987 * referral server in the list.
989 for (count
= 0; (ccli
== NULL
) && (count
< num_refs
); count
++) {
990 /* Connect to the target server & share */
991 status
= cli_cm_connect(ctx
, rootcli
,
992 dfs_refs
[count
].server
,
993 dfs_refs
[count
].share
,
999 if (!NT_STATUS_IS_OK(status
)) {
1000 d_printf("Unable to follow dfs referral [\\%s\\%s]\n",
1001 dfs_refs
[count
].server
,
1002 dfs_refs
[count
].share
);
1005 extrapath
= dfs_refs
[count
].extrapath
;
1010 /* No available referral server for the connection */
1011 if (*targetcli
== NULL
) {
1012 TALLOC_FREE(dfs_refs
);
1016 /* Make sure to recreate the original string including any wildcards. */
1018 dfs_path
= cli_dfs_make_full_path(ctx
, rootcli
, path
);
1020 TALLOC_FREE(dfs_refs
);
1021 return NT_STATUS_NO_MEMORY
;
1023 pathlen
= strlen(dfs_path
);
1024 consumed
= MIN(pathlen
, consumed
);
1025 *pp_targetpath
= talloc_strdup(ctx
, &dfs_path
[consumed
]);
1026 if (!*pp_targetpath
) {
1027 TALLOC_FREE(dfs_refs
);
1028 return NT_STATUS_NO_MEMORY
;
1030 dfs_path
[consumed
] = '\0';
1033 * *pp_targetpath is now the unconsumed part of the path.
1034 * dfs_path is now the consumed part of the path
1035 * (in \server\share\path format).
1038 if (extrapath
&& strlen(extrapath
) > 0) {
1039 /* EMC Celerra NAS version 5.6.50 (at least) doesn't appear to */
1040 /* put the trailing \ on the path, so to be safe we put one in if needed */
1041 if (extrapath
[strlen(extrapath
)-1] != '\\' && **pp_targetpath
!= '\\') {
1042 *pp_targetpath
= talloc_asprintf(ctx
,
1047 *pp_targetpath
= talloc_asprintf(ctx
,
1052 if (!*pp_targetpath
) {
1053 TALLOC_FREE(dfs_refs
);
1054 return NT_STATUS_NO_MEMORY
;
1058 /* parse out the consumed mount path */
1059 /* trim off the \server\share\ */
1063 if (*ppath
!= '\\') {
1064 d_printf("cli_resolve_path: "
1065 "dfs_path (%s) not in correct format.\n",
1067 TALLOC_FREE(dfs_refs
);
1068 return NT_STATUS_NOT_FOUND
;
1071 ppath
++; /* Now pointing at start of server name. */
1073 if ((ppath
= strchr_m( dfs_path
, '\\' )) == NULL
) {
1074 TALLOC_FREE(dfs_refs
);
1075 return NT_STATUS_NOT_FOUND
;
1078 ppath
++; /* Now pointing at start of share name. */
1080 if ((ppath
= strchr_m( ppath
+1, '\\' )) == NULL
) {
1081 TALLOC_FREE(dfs_refs
);
1082 return NT_STATUS_NOT_FOUND
;
1085 ppath
++; /* Now pointing at path component. */
1087 newmount
= talloc_asprintf(ctx
, "%s\\%s", mountpt
, ppath
);
1089 TALLOC_FREE(dfs_refs
);
1090 return NT_STATUS_NOT_FOUND
;
1093 /* Check for another dfs referral, note that we are not
1094 checking for loops here. */
1096 if (!strequal(*pp_targetpath
, "\\") && !strequal(*pp_targetpath
, "/")) {
1097 status
= cli_resolve_path(ctx
,
1104 if (NT_STATUS_IS_OK(status
)) {
1106 * When cli_resolve_path returns true here it's always
1107 * returning the complete path in newpath, so we're done
1110 *targetcli
= newcli
;
1111 *pp_targetpath
= newpath
;
1112 TALLOC_FREE(dfs_refs
);
1119 if (smbXcli_conn_protocol((*targetcli
)->conn
) >= PROTOCOL_SMB2_02
) {
1120 target_tcon
= (*targetcli
)->smb2
.tcon
;
1122 target_tcon
= (*targetcli
)->smb1
.tcon
;
1125 /* If returning true ensure we return a dfs root full path. */
1126 if (smbXcli_tcon_is_dfs_share(target_tcon
)) {
1127 dfs_path
= talloc_strdup(ctx
, *pp_targetpath
);
1129 TALLOC_FREE(dfs_refs
);
1130 return NT_STATUS_NO_MEMORY
;
1132 *pp_targetpath
= cli_dfs_make_full_path(ctx
, *targetcli
, dfs_path
);
1133 if (*pp_targetpath
== NULL
) {
1134 TALLOC_FREE(dfs_refs
);
1135 return NT_STATUS_NO_MEMORY
;
1139 TALLOC_FREE(dfs_refs
);
1140 return NT_STATUS_OK
;
1143 /********************************************************************
1144 ********************************************************************/
1146 bool cli_check_msdfs_proxy(TALLOC_CTX
*ctx
,
1147 struct cli_state
*cli
,
1148 const char *sharename
,
1149 char **pp_newserver
,
1151 struct cli_credentials
*creds
)
1153 struct client_dfs_referral
*refs
= NULL
;
1154 size_t num_refs
= 0;
1155 size_t consumed
= 0;
1156 char *fullpath
= NULL
;
1158 struct smbXcli_tcon
*orig_tcon
= NULL
;
1159 char *newextrapath
= NULL
;
1161 const char *remote_name
;
1162 enum smb_encryption_setting encryption_state
=
1163 cli_credentials_get_smb_encryption(creds
);
1165 if (!cli
|| !sharename
) {
1169 remote_name
= smbXcli_conn_remote_name(cli
->conn
);
1171 /* special case. never check for a referral on the IPC$ share */
1173 if (strequal(sharename
, "IPC$")) {
1177 /* send a trans2_query_path_info to check for a referral */
1179 fullpath
= talloc_asprintf(ctx
, "\\%s\\%s", remote_name
, sharename
);
1184 /* Store tcon state. */
1185 if (cli_state_has_tcon(cli
)) {
1186 orig_tcon
= cli_state_save_tcon(cli
);
1187 if (orig_tcon
== NULL
) {
1192 /* check for the referral */
1194 if (!NT_STATUS_IS_OK(cli_tree_connect(cli
, "IPC$", "IPC", NULL
))) {
1195 cli_state_restore_tcon(cli
, orig_tcon
);
1199 if (encryption_state
>= SMB_ENCRYPTION_DESIRED
) {
1200 status
= cli_cm_force_encryption_creds(cli
, creds
, "IPC$");
1201 if (!NT_STATUS_IS_OK(status
)) {
1202 switch (encryption_state
) {
1203 case SMB_ENCRYPTION_DESIRED
:
1205 case SMB_ENCRYPTION_REQUIRED
:
1208 * Failed to set up encryption.
1209 * Disconnect the temporary IPC$
1210 * tcon before restoring the original
1211 * tcon so we don't leak it.
1214 cli_state_restore_tcon(cli
, orig_tcon
);
1220 status
= cli_dfs_get_referral(ctx
, cli
, fullpath
, &refs
,
1221 &num_refs
, &consumed
);
1222 res
= NT_STATUS_IS_OK(status
);
1224 status
= cli_tdis(cli
);
1226 cli_state_restore_tcon(cli
, orig_tcon
);
1228 if (!NT_STATUS_IS_OK(status
)) {
1232 if (!res
|| !num_refs
) {
1236 if (!refs
[0].dfspath
) {
1240 if (!split_dfs_path(ctx
, refs
[0].dfspath
, pp_newserver
,
1241 pp_newshare
, &newextrapath
)) {
1245 /* check that this is not a self-referral */
1247 if (strequal(remote_name
, *pp_newserver
) &&
1248 strequal(sharename
, *pp_newshare
)) {
1255 /********************************************************************
1256 Windows and NetApp (and arguably the SMB1/2/3 specs) expect a non-DFS
1257 path for the targets of rename and hardlink. If we have been given
1258 a DFS path for these calls, convert it back into a local path by
1259 stripping off the DFS prefix.
1260 ********************************************************************/
1262 NTSTATUS
cli_dfs_target_check(TALLOC_CTX
*mem_ctx
,
1263 struct cli_state
*cli
,
1264 const char *fname_src
,
1265 const char *fname_dst
,
1266 const char **fname_dst_out
)
1268 char *dfs_prefix
= NULL
;
1269 size_t prefix_len
= 0;
1270 struct smbXcli_tcon
*tcon
= NULL
;
1272 if (!smbXcli_conn_dfs_supported(cli
->conn
)) {
1273 goto copy_fname_out
;
1275 if (smbXcli_conn_protocol(cli
->conn
) >= PROTOCOL_SMB2_02
) {
1276 tcon
= cli
->smb2
.tcon
;
1278 tcon
= cli
->smb1
.tcon
;
1280 if (!smbXcli_tcon_is_dfs_share(tcon
)) {
1281 goto copy_fname_out
;
1283 dfs_prefix
= cli_dfs_make_full_path(mem_ctx
, cli
, "");
1284 if (dfs_prefix
== NULL
) {
1285 return NT_STATUS_NO_MEMORY
;
1287 prefix_len
= strlen(dfs_prefix
);
1288 if (strncmp(fname_dst
, dfs_prefix
, prefix_len
) != 0) {
1290 * Prefix doesn't match. Assume it was
1291 * already stripped or not added in the
1294 goto copy_fname_out
;
1296 /* Return the trailing name after the prefix. */
1297 *fname_dst_out
= &fname_dst
[prefix_len
];
1298 TALLOC_FREE(dfs_prefix
);
1299 return NT_STATUS_OK
;
1304 * No change to the destination name. Just
1305 * point it at the incoming destination name.
1307 *fname_dst_out
= fname_dst
;
1308 TALLOC_FREE(dfs_prefix
);
1309 return NT_STATUS_OK
;