s3: libsmb: Add a missing return statement in the timeout case.
[Samba.git] / source3 / libsmb / clidfs.c
blobd7dbf97f8fb877e62a8d1865cf041c8d9e9253ee
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"
30 #include "lib/param/param.h"
32 /********************************************************************
33 Important point.
35 DFS paths are *always* of the form \server\share\<pathname> (the \ characters
36 are not C escaped here).
38 - but if we're using POSIX paths then <pathname> may contain
39 '/' separators, not '\\' separators. So cope with '\\' or '/'
40 as a separator when looking at the pathname part.... JRA.
41 ********************************************************************/
43 /********************************************************************
44 Ensure a connection is encrypted.
45 ********************************************************************/
47 static NTSTATUS cli_cm_force_encryption_creds(struct cli_state *c,
48 struct cli_credentials *creds,
49 const char *sharename)
51 uint16_t major, minor;
52 uint32_t caplow, caphigh;
53 NTSTATUS status;
54 bool temp_ipc = false;
56 if (smbXcli_conn_protocol(c->conn) >= PROTOCOL_SMB2_02) {
57 status = smb2cli_session_encryption_on(c->smb2.session);
58 if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_SUPPORTED)) {
59 d_printf("Encryption required and "
60 "server doesn't support "
61 "SMB3 encryption - failing connect\n");
62 } else if (!NT_STATUS_IS_OK(status)) {
63 d_printf("Encryption required and "
64 "setup failed with error %s.\n",
65 nt_errstr(status));
67 return status;
70 if (!SERVER_HAS_UNIX_CIFS(c)) {
71 d_printf("Encryption required and "
72 "server that doesn't support "
73 "UNIX extensions - failing connect\n");
74 return NT_STATUS_NOT_SUPPORTED;
77 if (c->smb1.tcon == NULL) {
78 status = cli_tree_connect_creds(c, "IPC$", "IPC", creds);
79 if (!NT_STATUS_IS_OK(status)) {
80 d_printf("Encryption required and "
81 "can't connect to IPC$ to check "
82 "UNIX CIFS extensions.\n");
83 return NT_STATUS_UNKNOWN_REVISION;
85 temp_ipc = true;
88 status = cli_unix_extensions_version(c, &major, &minor, &caplow,
89 &caphigh);
90 if (!NT_STATUS_IS_OK(status)) {
91 d_printf("Encryption required and "
92 "can't get UNIX CIFS extensions "
93 "version from server.\n");
94 if (temp_ipc) {
95 cli_tdis(c);
97 return NT_STATUS_UNKNOWN_REVISION;
100 if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) {
101 d_printf("Encryption required and "
102 "share %s doesn't support "
103 "encryption.\n", sharename);
104 if (temp_ipc) {
105 cli_tdis(c);
107 return NT_STATUS_UNSUPPORTED_COMPRESSION;
110 status = cli_smb1_setup_encryption(c, creds);
111 if (!NT_STATUS_IS_OK(status)) {
112 d_printf("Encryption required and "
113 "setup failed with error %s.\n",
114 nt_errstr(status));
115 if (temp_ipc) {
116 cli_tdis(c);
118 return status;
121 if (temp_ipc) {
122 cli_tdis(c);
124 return NT_STATUS_OK;
127 /********************************************************************
128 Return a connection to a server.
129 ********************************************************************/
131 static NTSTATUS do_connect(TALLOC_CTX *ctx,
132 const char *server,
133 const char *share,
134 struct cli_credentials *creds,
135 const struct sockaddr_storage *dest_ss,
136 int port,
137 int name_type,
138 struct cli_state **pcli)
140 struct cli_state *c = NULL;
141 char *servicename;
142 char *sharename;
143 char *newserver, *newshare;
144 NTSTATUS status;
145 int flags = 0;
146 enum protocol_types protocol = PROTOCOL_NONE;
147 enum smb_signing_setting signing_state =
148 cli_credentials_get_smb_signing(creds);
149 enum smb_encryption_setting encryption_state =
150 cli_credentials_get_smb_encryption(creds);
152 if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
153 signing_state = SMB_SIGNING_REQUIRED;
156 /* make a copy so we don't modify the global string 'service' */
157 servicename = talloc_strdup(ctx,share);
158 if (!servicename) {
159 return NT_STATUS_NO_MEMORY;
161 sharename = servicename;
162 if (*sharename == '\\') {
163 sharename += 2;
164 if (server == NULL) {
165 server = sharename;
167 sharename = strchr_m(sharename,'\\');
168 if (!sharename) {
169 return NT_STATUS_NO_MEMORY;
171 *sharename = 0;
172 sharename++;
174 if (server == NULL) {
175 return NT_STATUS_INVALID_PARAMETER;
178 status = cli_connect_nb(
179 server, dest_ss, port, name_type, NULL,
180 signing_state,
181 flags, &c);
183 if (!NT_STATUS_IS_OK(status)) {
184 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
185 DBG_ERR("NetBIOS support disabled, unable to connect\n");
188 DBG_WARNING("Connection to %s failed (Error %s)\n",
189 server,
190 nt_errstr(status));
191 return status;
194 DEBUG(4,(" session request ok\n"));
196 status = smbXcli_negprot(c->conn, c->timeout,
197 lp_client_min_protocol(),
198 lp_client_max_protocol());
200 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
201 d_printf("Protocol negotiation (with timeout %d ms) timed out against server %s\n",
202 c->timeout,
203 smbXcli_conn_remote_name(c->conn));
204 cli_shutdown(c);
205 return status;
206 } else if (!NT_STATUS_IS_OK(status)) {
207 d_printf("Protocol negotiation to server %s (for a protocol between %s and %s) failed: %s\n",
208 smbXcli_conn_remote_name(c->conn),
209 lpcfg_get_smb_protocol(lp_client_min_protocol()),
210 lpcfg_get_smb_protocol(lp_client_max_protocol()),
211 nt_errstr(status));
212 cli_shutdown(c);
213 return status;
215 protocol = smbXcli_conn_protocol(c->conn);
216 DEBUG(4,(" negotiated dialect[%s] against server[%s]\n",
217 smb_protocol_types_string(protocol),
218 smbXcli_conn_remote_name(c->conn)));
220 if (protocol >= PROTOCOL_SMB2_02) {
221 /* Ensure we ask for some initial credits. */
222 smb2cli_conn_set_max_credits(c->conn, DEFAULT_SMB2_MAX_CREDITS);
225 status = cli_session_setup_creds(c, creds);
226 if (!NT_STATUS_IS_OK(status)) {
227 /* If a password was not supplied then
228 * try again with a null username. */
229 if (encryption_state == SMB_ENCRYPTION_REQUIRED ||
230 smbXcli_conn_signing_mandatory(c->conn) ||
231 cli_credentials_authentication_requested(creds) ||
232 cli_credentials_is_anonymous(creds) ||
233 !NT_STATUS_IS_OK(status = cli_session_setup_anon(c)))
235 d_printf("session setup failed: %s\n",
236 nt_errstr(status));
237 if (NT_STATUS_EQUAL(status,
238 NT_STATUS_MORE_PROCESSING_REQUIRED))
239 d_printf("did you forget to run kinit?\n");
240 cli_shutdown(c);
241 return status;
243 d_printf("Anonymous login successful\n");
246 if (!NT_STATUS_IS_OK(status)) {
247 DEBUG(10,("cli_init_creds() failed: %s\n", nt_errstr(status)));
248 cli_shutdown(c);
249 return status;
252 DEBUG(4,(" session setup ok\n"));
254 if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
255 status = cli_cm_force_encryption_creds(c,
256 creds,
257 sharename);
258 if (!NT_STATUS_IS_OK(status)) {
259 switch (encryption_state) {
260 case SMB_ENCRYPTION_DESIRED:
261 break;
262 case SMB_ENCRYPTION_REQUIRED:
263 default:
264 cli_shutdown(c);
265 return status;
270 /* here's the fun part....to support 'msdfs proxy' shares
271 (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL
272 here before trying to connect to the original share.
273 cli_check_msdfs_proxy() will fail if it is a normal share. */
275 if (smbXcli_conn_dfs_supported(c->conn) &&
276 cli_check_msdfs_proxy(ctx, c, sharename,
277 &newserver, &newshare,
278 creds)) {
279 cli_shutdown(c);
280 return do_connect(ctx, newserver,
281 newshare, creds,
282 NULL, port, name_type, pcli);
285 /* must be a normal share */
287 status = cli_tree_connect_creds(c, sharename, "?????", creds);
288 if (!NT_STATUS_IS_OK(status)) {
289 d_printf("tree connect failed: %s\n", nt_errstr(status));
290 cli_shutdown(c);
291 return status;
294 DEBUG(4,(" tconx ok\n"));
295 *pcli = c;
296 return NT_STATUS_OK;
299 /********************************************************************
300 Add a new connection to the list.
301 referring_cli == NULL means a new initial connection.
302 ********************************************************************/
304 static NTSTATUS cli_cm_connect(TALLOC_CTX *ctx,
305 struct cli_state *referring_cli,
306 const char *server,
307 const char *share,
308 struct cli_credentials *creds,
309 const struct sockaddr_storage *dest_ss,
310 int port,
311 int name_type,
312 struct cli_state **pcli)
314 struct cli_state *cli = NULL;
315 NTSTATUS status;
317 status = do_connect(ctx, server, share,
318 creds,
319 dest_ss, port, name_type, &cli);
321 if (!NT_STATUS_IS_OK(status)) {
322 return status;
326 * This can't happen, this test is to satisfy static
327 * checkers (clang)
329 if (cli == NULL) {
330 return NT_STATUS_NO_MEMORY;
333 /* Enter into the list. */
334 if (referring_cli) {
335 DLIST_ADD_END(referring_cli, cli);
338 if (referring_cli && referring_cli->requested_posix_capabilities) {
339 uint16_t major, minor;
340 uint32_t caplow, caphigh;
341 status = cli_unix_extensions_version(cli, &major, &minor,
342 &caplow, &caphigh);
343 if (NT_STATUS_IS_OK(status)) {
344 cli_set_unix_extensions_capabilities(cli,
345 major, minor,
346 caplow, caphigh);
350 *pcli = cli;
351 return NT_STATUS_OK;
354 /********************************************************************
355 Return a connection to a server on a particular share.
356 ********************************************************************/
358 static struct cli_state *cli_cm_find(struct cli_state *cli,
359 const char *server,
360 const char *share)
362 struct cli_state *p;
364 if (cli == NULL) {
365 return NULL;
368 /* Search to the start of the list. */
369 for (p = cli; p; p = DLIST_PREV(p)) {
370 const char *remote_name =
371 smbXcli_conn_remote_name(p->conn);
373 if (strequal(server, remote_name) &&
374 strequal(share,p->share)) {
375 return p;
379 /* Search to the end of the list. */
380 for (p = cli->next; p; p = p->next) {
381 const char *remote_name =
382 smbXcli_conn_remote_name(p->conn);
384 if (strequal(server, remote_name) &&
385 strequal(share,p->share)) {
386 return p;
390 return NULL;
393 /****************************************************************************
394 Open a client connection to a \\server\share.
395 ****************************************************************************/
397 NTSTATUS cli_cm_open(TALLOC_CTX *ctx,
398 struct cli_state *referring_cli,
399 const char *server,
400 const char *share,
401 struct cli_credentials *creds,
402 const struct sockaddr_storage *dest_ss,
403 int port,
404 int name_type,
405 struct cli_state **pcli)
407 /* Try to reuse an existing connection in this list. */
408 struct cli_state *c = cli_cm_find(referring_cli, server, share);
409 NTSTATUS status;
411 if (c) {
412 *pcli = c;
413 return NT_STATUS_OK;
416 if (creds == NULL) {
417 /* Can't do a new connection
418 * without auth info. */
419 d_printf("cli_cm_open() Unable to open connection [\\%s\\%s] "
420 "without client credentials\n",
421 server, share );
422 return NT_STATUS_INVALID_PARAMETER;
425 status = cli_cm_connect(ctx,
426 referring_cli,
427 server,
428 share,
429 creds,
430 dest_ss,
431 port,
432 name_type,
433 &c);
434 if (!NT_STATUS_IS_OK(status)) {
435 return status;
437 *pcli = c;
438 return NT_STATUS_OK;
441 /****************************************************************************
442 ****************************************************************************/
444 void cli_cm_display(struct cli_state *cli)
446 int i;
448 for (i=0; cli; cli = cli->next,i++ ) {
449 d_printf("%d:\tserver=%s, share=%s\n",
450 i, smbXcli_conn_remote_name(cli->conn), cli->share);
454 /**********************************************************************
455 split a dfs path into the server, share name, and extrapath components
456 **********************************************************************/
458 static bool split_dfs_path(TALLOC_CTX *ctx,
459 const char *nodepath,
460 char **pp_server,
461 char **pp_share,
462 char **pp_extrapath)
464 char *p, *q;
465 char *path;
467 *pp_server = NULL;
468 *pp_share = NULL;
469 *pp_extrapath = NULL;
471 path = talloc_strdup(ctx, nodepath);
472 if (!path) {
473 goto fail;
476 if ( path[0] != '\\' ) {
477 goto fail;
480 p = strchr_m( path + 1, '\\' );
481 if ( !p ) {
482 goto fail;
485 *p = '\0';
486 p++;
488 /* Look for any extra/deep path */
489 q = strchr_m(p, '\\');
490 if (q != NULL) {
491 *q = '\0';
492 q++;
493 *pp_extrapath = talloc_strdup(ctx, q);
494 } else {
495 *pp_extrapath = talloc_strdup(ctx, "");
497 if (*pp_extrapath == NULL) {
498 goto fail;
501 *pp_share = talloc_strdup(ctx, p);
502 if (*pp_share == NULL) {
503 goto fail;
506 *pp_server = talloc_strdup(ctx, &path[1]);
507 if (*pp_server == NULL) {
508 goto fail;
511 TALLOC_FREE(path);
512 return true;
514 fail:
515 TALLOC_FREE(*pp_share);
516 TALLOC_FREE(*pp_extrapath);
517 TALLOC_FREE(path);
518 return false;
521 /****************************************************************************
522 Return the original path truncated at the directory component before
523 the first wildcard character. Trust the caller to provide a NULL
524 terminated string
525 ****************************************************************************/
527 static char *clean_path(TALLOC_CTX *ctx, const char *path)
529 size_t len;
530 char *p1, *p2, *p;
531 char *path_out;
533 /* No absolute paths. */
534 while (IS_DIRECTORY_SEP(*path)) {
535 path++;
538 path_out = talloc_strdup(ctx, path);
539 if (!path_out) {
540 return NULL;
543 p1 = strchr_m(path_out, '*');
544 p2 = strchr_m(path_out, '?');
546 if (p1 || p2) {
547 if (p1 && p2) {
548 p = MIN(p1,p2);
549 } else if (!p1) {
550 p = p2;
551 } else {
552 p = p1;
554 *p = '\0';
556 /* Now go back to the start of this component. */
557 p1 = strrchr_m(path_out, '/');
558 p2 = strrchr_m(path_out, '\\');
559 p = MAX(p1,p2);
560 if (p) {
561 *p = '\0';
565 /* Strip any trailing separator */
567 len = strlen(path_out);
568 if ( (len > 0) && IS_DIRECTORY_SEP(path_out[len-1])) {
569 path_out[len-1] = '\0';
572 return path_out;
575 /****************************************************************************
576 ****************************************************************************/
578 static char *cli_dfs_make_full_path(TALLOC_CTX *ctx,
579 struct cli_state *cli,
580 const char *dir)
582 char path_sep = '\\';
584 /* Ensure the extrapath doesn't start with a separator. */
585 while (IS_DIRECTORY_SEP(*dir)) {
586 dir++;
589 if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
590 path_sep = '/';
592 return talloc_asprintf(ctx, "%c%s%c%s%c%s",
593 path_sep,
594 smbXcli_conn_remote_name(cli->conn),
595 path_sep,
596 cli->share,
597 path_sep,
598 dir);
601 /********************************************************************
602 Check if a path has already been converted to DFS.
603 ********************************************************************/
605 bool cli_dfs_is_already_full_path(struct cli_state *cli, const char *path)
607 const char *server = smbXcli_conn_remote_name(cli->conn);
608 size_t server_len = strlen(server);
609 bool found_server = false;
610 const char *share = cli->share;
611 size_t share_len = strlen(share);
612 bool found_share = false;
614 if (!IS_DIRECTORY_SEP(path[0])) {
615 return false;
617 path++;
618 found_server = (strncasecmp_m(path, server, server_len) == 0);
619 if (!found_server) {
620 return false;
622 path += server_len;
623 if (!IS_DIRECTORY_SEP(path[0])) {
624 return false;
626 path++;
627 found_share = (strncasecmp_m(path, share, share_len) == 0);
628 if (!found_share) {
629 return false;
631 path += share_len;
632 if (path[0] == '\0') {
633 return true;
635 if (IS_DIRECTORY_SEP(path[0])) {
636 return true;
638 return false;
641 /********************************************************************
642 Get the dfs referral link.
643 ********************************************************************/
645 NTSTATUS cli_dfs_get_referral_ex(TALLOC_CTX *ctx,
646 struct cli_state *cli,
647 const char *path,
648 uint16_t max_referral_level,
649 struct client_dfs_referral **refs,
650 size_t *num_refs,
651 size_t *consumed)
653 unsigned int param_len = 0;
654 uint16_t recv_flags2;
655 uint8_t *param = NULL;
656 uint8_t *rdata = NULL;
657 char *p;
658 char *endp;
659 smb_ucs2_t *path_ucs;
660 char *consumed_path = NULL;
661 uint16_t consumed_ucs;
662 uint16_t num_referrals;
663 struct client_dfs_referral *referrals = NULL;
664 NTSTATUS status;
665 TALLOC_CTX *frame = talloc_stackframe();
667 *num_refs = 0;
668 *refs = NULL;
670 param = talloc_array(talloc_tos(), uint8_t, 2);
671 if (!param) {
672 status = NT_STATUS_NO_MEMORY;
673 goto out;
675 SSVAL(param, 0, max_referral_level);
677 param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn),
678 path, strlen(path)+1,
679 NULL);
680 if (!param) {
681 status = NT_STATUS_NO_MEMORY;
682 goto out;
684 param_len = talloc_get_size(param);
685 path_ucs = (smb_ucs2_t *)&param[2];
687 if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
688 DATA_BLOB in_input_buffer;
689 DATA_BLOB in_output_buffer = data_blob_null;
690 DATA_BLOB out_input_buffer = data_blob_null;
691 DATA_BLOB out_output_buffer = data_blob_null;
693 in_input_buffer.data = param;
694 in_input_buffer.length = param_len;
696 status = smb2cli_ioctl(cli->conn,
697 cli->timeout,
698 cli->smb2.session,
699 cli->smb2.tcon,
700 UINT64_MAX, /* in_fid_persistent */
701 UINT64_MAX, /* in_fid_volatile */
702 FSCTL_DFS_GET_REFERRALS,
703 0, /* in_max_input_length */
704 &in_input_buffer,
705 CLI_BUFFER_SIZE, /* in_max_output_length */
706 &in_output_buffer,
707 SMB2_IOCTL_FLAG_IS_FSCTL,
708 talloc_tos(),
709 &out_input_buffer,
710 &out_output_buffer);
711 if (!NT_STATUS_IS_OK(status)) {
712 goto out;
715 if (out_output_buffer.length < 4) {
716 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
717 goto out;
720 recv_flags2 = FLAGS2_UNICODE_STRINGS;
721 rdata = out_output_buffer.data;
722 endp = (char *)rdata + out_output_buffer.length;
723 } else {
724 unsigned int data_len = 0;
725 uint16_t setup[1];
727 SSVAL(setup, 0, TRANSACT2_GET_DFS_REFERRAL);
729 status = cli_trans(talloc_tos(), cli, SMBtrans2,
730 NULL, 0xffff, 0, 0,
731 setup, 1, 0,
732 param, param_len, 2,
733 NULL, 0, CLI_BUFFER_SIZE,
734 &recv_flags2,
735 NULL, 0, NULL, /* rsetup */
736 NULL, 0, NULL,
737 &rdata, 4, &data_len);
738 if (!NT_STATUS_IS_OK(status)) {
739 goto out;
742 endp = (char *)rdata + data_len;
745 consumed_ucs = SVAL(rdata, 0);
746 num_referrals = SVAL(rdata, 2);
748 /* consumed_ucs is the number of bytes
749 * of the UCS2 path consumed not counting any
750 * terminating null. We need to convert
751 * back to unix charset and count again
752 * to get the number of bytes consumed from
753 * the incoming path. */
755 errno = 0;
756 if (pull_string_talloc(talloc_tos(),
757 NULL,
759 &consumed_path,
760 path_ucs,
761 consumed_ucs,
762 STR_UNICODE) == 0) {
763 if (errno != 0) {
764 status = map_nt_error_from_unix(errno);
765 } else {
766 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
768 goto out;
770 if (consumed_path == NULL) {
771 status = map_nt_error_from_unix(errno);
772 goto out;
774 *consumed = strlen(consumed_path);
776 if (num_referrals != 0) {
777 uint16_t ref_version;
778 uint16_t ref_size;
779 int i;
780 uint16_t node_offset;
782 referrals = talloc_array(ctx, struct client_dfs_referral,
783 num_referrals);
785 if (!referrals) {
786 status = NT_STATUS_NO_MEMORY;
787 goto out;
789 /* start at the referrals array */
791 p = (char *)rdata+8;
792 for (i=0; i<num_referrals && p < endp; i++) {
793 if (p + 18 > endp) {
794 goto out;
796 ref_version = SVAL(p, 0);
797 ref_size = SVAL(p, 2);
798 node_offset = SVAL(p, 16);
800 if (ref_version != 3) {
801 p += ref_size;
802 continue;
805 referrals[i].proximity = SVAL(p, 8);
806 referrals[i].ttl = SVAL(p, 10);
808 if (p + node_offset > endp) {
809 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
810 goto out;
812 pull_string_talloc(referrals,
813 (const char *)rdata,
814 recv_flags2,
815 &referrals[i].dfspath,
816 p+node_offset,
817 PTR_DIFF(endp, p+node_offset),
818 STR_TERMINATE|STR_UNICODE);
820 if (!referrals[i].dfspath) {
821 status = map_nt_error_from_unix(errno);
822 goto out;
824 p += ref_size;
826 if (i < num_referrals) {
827 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
828 goto out;
832 *num_refs = num_referrals;
833 *refs = referrals;
835 out:
837 TALLOC_FREE(frame);
838 return status;
841 NTSTATUS cli_dfs_get_referral(TALLOC_CTX *ctx,
842 struct cli_state *cli,
843 const char *path,
844 struct client_dfs_referral **refs,
845 size_t *num_refs,
846 size_t *consumed)
848 return cli_dfs_get_referral_ex(ctx,
849 cli,
850 path,
852 refs, /* Max referral level we want */
853 num_refs,
854 consumed);
857 static bool cli_conn_have_dfs(struct cli_state *cli)
859 struct smbXcli_conn *conn = cli->conn;
860 struct smbXcli_tcon *tcon = NULL;
861 bool ok;
863 if (smbXcli_conn_protocol(conn) < PROTOCOL_SMB2_02) {
864 uint32_t capabilities = smb1cli_conn_capabilities(conn);
866 if ((capabilities & CAP_STATUS32) == 0) {
867 return false;
869 if ((capabilities & CAP_UNICODE) == 0) {
870 return false;
873 tcon = cli->smb1.tcon;
874 } else {
875 tcon = cli->smb2.tcon;
878 ok = smbXcli_tcon_is_dfs_share(tcon);
879 return ok;
882 /********************************************************************
883 ********************************************************************/
884 struct cli_dfs_path_split {
885 char *server;
886 char *share;
887 char *extrapath;
890 NTSTATUS cli_resolve_path(TALLOC_CTX *ctx,
891 const char *mountpt,
892 struct cli_credentials *creds,
893 struct cli_state *rootcli,
894 const char *path,
895 struct cli_state **targetcli,
896 char **pp_targetpath)
898 struct client_dfs_referral *refs = NULL;
899 size_t num_refs = 0;
900 size_t consumed = 0;
901 struct cli_state *cli_ipc = NULL;
902 char *dfs_path = NULL;
903 char *cleanpath = NULL;
904 char *extrapath = NULL;
905 int pathlen;
906 struct cli_state *newcli = NULL;
907 struct cli_state *ccli = NULL;
908 size_t count = 0;
909 char *newpath = NULL;
910 char *newmount = NULL;
911 char *ppath = NULL;
912 SMB_STRUCT_STAT sbuf;
913 uint32_t attributes;
914 NTSTATUS status;
915 struct smbXcli_tcon *target_tcon = NULL;
916 struct cli_dfs_path_split *dfs_refs = NULL;
917 bool ok;
918 bool is_already_dfs = false;
920 if ( !rootcli || !path || !targetcli ) {
921 return NT_STATUS_INVALID_PARAMETER;
925 * Avoid more than one leading directory separator
927 while (IS_DIRECTORY_SEP(path[0]) && IS_DIRECTORY_SEP(path[1])) {
928 path++;
931 ok = cli_conn_have_dfs(rootcli);
932 if (!ok) {
933 *targetcli = rootcli;
934 *pp_targetpath = talloc_strdup(ctx, path);
935 if (!*pp_targetpath) {
936 return NT_STATUS_NO_MEMORY;
938 return NT_STATUS_OK;
941 *targetcli = NULL;
943 is_already_dfs = cli_dfs_is_already_full_path(rootcli, path);
944 if (is_already_dfs) {
945 const char *localpath = NULL;
947 * Given path is already converted to DFS.
948 * Convert to a local path so clean_path()
949 * can correctly strip any wildcards.
951 status = cli_dfs_target_check(ctx,
952 rootcli,
953 path,
954 &localpath);
955 if (!NT_STATUS_IS_OK(status)) {
956 return status;
958 path = localpath;
961 /* Send a trans2_query_path_info to check for a referral. */
963 cleanpath = clean_path(ctx, path);
964 if (!cleanpath) {
965 return NT_STATUS_NO_MEMORY;
968 dfs_path = cli_dfs_make_full_path(ctx, rootcli, cleanpath);
969 if (!dfs_path) {
970 return NT_STATUS_NO_MEMORY;
973 status = cli_qpathinfo_basic( rootcli, dfs_path, &sbuf, &attributes);
974 if (NT_STATUS_IS_OK(status)) {
975 /* This is an ordinary path, just return it. */
976 *targetcli = rootcli;
977 *pp_targetpath = talloc_strdup(ctx, path);
978 if (!*pp_targetpath) {
979 return NT_STATUS_NO_MEMORY;
981 goto done;
984 /* Special case where client asked for a path that does not exist */
986 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
987 *targetcli = rootcli;
988 *pp_targetpath = talloc_strdup(ctx, path);
989 if (!*pp_targetpath) {
990 return NT_STATUS_NO_MEMORY;
992 goto done;
995 /* We got an error, check for DFS referral. */
997 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
998 return status;
1001 /* Check for the referral. */
1003 status = cli_cm_open(ctx,
1004 rootcli,
1005 smbXcli_conn_remote_name(rootcli->conn),
1006 "IPC$",
1007 creds,
1008 NULL, /* dest_ss not needed, we reuse the transport */
1010 0x20,
1011 &cli_ipc);
1012 if (!NT_STATUS_IS_OK(status)) {
1013 return status;
1016 status = cli_dfs_get_referral(ctx, cli_ipc, dfs_path, &refs,
1017 &num_refs, &consumed);
1018 if (!NT_STATUS_IS_OK(status)) {
1019 return status;
1022 if (!num_refs || !refs[0].dfspath) {
1023 return NT_STATUS_NOT_FOUND;
1027 * Bug#10123 - DFS referral entries can be provided in a random order,
1028 * so check the connection cache for each item to avoid unnecessary
1029 * reconnections.
1031 dfs_refs = talloc_array(ctx, struct cli_dfs_path_split, num_refs);
1032 if (dfs_refs == NULL) {
1033 return NT_STATUS_NO_MEMORY;
1036 for (count = 0; count < num_refs; count++) {
1037 if (!split_dfs_path(dfs_refs, refs[count].dfspath,
1038 &dfs_refs[count].server,
1039 &dfs_refs[count].share,
1040 &dfs_refs[count].extrapath)) {
1041 TALLOC_FREE(dfs_refs);
1042 return NT_STATUS_NOT_FOUND;
1045 ccli = cli_cm_find(rootcli, dfs_refs[count].server,
1046 dfs_refs[count].share);
1047 if (ccli != NULL) {
1048 extrapath = dfs_refs[count].extrapath;
1049 *targetcli = ccli;
1050 break;
1055 * If no cached connection was found, then connect to the first live
1056 * referral server in the list.
1058 for (count = 0; (ccli == NULL) && (count < num_refs); count++) {
1059 /* Connect to the target server & share */
1060 status = cli_cm_connect(ctx, rootcli,
1061 dfs_refs[count].server,
1062 dfs_refs[count].share,
1063 creds,
1064 NULL, /* dest_ss */
1065 0, /* port */
1066 0x20,
1067 targetcli);
1068 if (!NT_STATUS_IS_OK(status)) {
1069 d_printf("Unable to follow dfs referral [\\%s\\%s]\n",
1070 dfs_refs[count].server,
1071 dfs_refs[count].share);
1072 continue;
1073 } else {
1074 extrapath = dfs_refs[count].extrapath;
1075 break;
1079 /* No available referral server for the connection */
1080 if (*targetcli == NULL) {
1081 TALLOC_FREE(dfs_refs);
1082 return status;
1085 /* Make sure to recreate the original string including any wildcards. */
1087 dfs_path = cli_dfs_make_full_path(ctx, rootcli, path);
1088 if (!dfs_path) {
1089 TALLOC_FREE(dfs_refs);
1090 return NT_STATUS_NO_MEMORY;
1092 pathlen = strlen(dfs_path);
1093 consumed = MIN(pathlen, consumed);
1094 *pp_targetpath = talloc_strdup(ctx, &dfs_path[consumed]);
1095 if (!*pp_targetpath) {
1096 TALLOC_FREE(dfs_refs);
1097 return NT_STATUS_NO_MEMORY;
1099 dfs_path[consumed] = '\0';
1102 * *pp_targetpath is now the unconsumed part of the path.
1103 * dfs_path is now the consumed part of the path
1104 * (in \server\share\path format).
1107 if (extrapath && strlen(extrapath) > 0) {
1108 /* EMC Celerra NAS version 5.6.50 (at least) doesn't appear to */
1109 /* put the trailing \ on the path, so to be safe we put one in if needed */
1110 if (extrapath[strlen(extrapath)-1] != '\\' && **pp_targetpath != '\\') {
1111 *pp_targetpath = talloc_asprintf(ctx,
1112 "%s\\%s",
1113 extrapath,
1114 *pp_targetpath);
1115 } else {
1116 *pp_targetpath = talloc_asprintf(ctx,
1117 "%s%s",
1118 extrapath,
1119 *pp_targetpath);
1121 if (!*pp_targetpath) {
1122 TALLOC_FREE(dfs_refs);
1123 return NT_STATUS_NO_MEMORY;
1127 /* parse out the consumed mount path */
1128 /* trim off the \server\share\ */
1130 ppath = dfs_path;
1132 if (*ppath != '\\') {
1133 d_printf("cli_resolve_path: "
1134 "dfs_path (%s) not in correct format.\n",
1135 dfs_path );
1136 TALLOC_FREE(dfs_refs);
1137 return NT_STATUS_NOT_FOUND;
1140 ppath++; /* Now pointing at start of server name. */
1142 if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) {
1143 TALLOC_FREE(dfs_refs);
1144 return NT_STATUS_NOT_FOUND;
1147 ppath++; /* Now pointing at start of share name. */
1149 if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) {
1150 TALLOC_FREE(dfs_refs);
1151 return NT_STATUS_NOT_FOUND;
1154 ppath++; /* Now pointing at path component. */
1156 newmount = talloc_asprintf(ctx, "%s\\%s", mountpt, ppath );
1157 if (!newmount) {
1158 TALLOC_FREE(dfs_refs);
1159 return NT_STATUS_NOT_FOUND;
1162 /* Check for another dfs referral, note that we are not
1163 checking for loops here. */
1165 if (!strequal(*pp_targetpath, "\\") && !strequal(*pp_targetpath, "/")) {
1166 status = cli_resolve_path(ctx,
1167 newmount,
1168 creds,
1169 *targetcli,
1170 *pp_targetpath,
1171 &newcli,
1172 &newpath);
1173 if (NT_STATUS_IS_OK(status)) {
1175 * When cli_resolve_path returns true here it's always
1176 * returning the complete path in newpath, so we're done
1177 * here.
1179 *targetcli = newcli;
1180 *pp_targetpath = newpath;
1181 TALLOC_FREE(dfs_refs);
1182 return status;
1186 done:
1188 if (smbXcli_conn_protocol((*targetcli)->conn) >= PROTOCOL_SMB2_02) {
1189 target_tcon = (*targetcli)->smb2.tcon;
1190 } else {
1191 target_tcon = (*targetcli)->smb1.tcon;
1194 /* If returning true ensure we return a dfs root full path. */
1195 if (smbXcli_tcon_is_dfs_share(target_tcon)) {
1196 dfs_path = talloc_strdup(ctx, *pp_targetpath);
1197 if (!dfs_path) {
1198 TALLOC_FREE(dfs_refs);
1199 return NT_STATUS_NO_MEMORY;
1201 *pp_targetpath = cli_dfs_make_full_path(ctx, *targetcli, dfs_path);
1202 if (*pp_targetpath == NULL) {
1203 TALLOC_FREE(dfs_refs);
1204 return NT_STATUS_NO_MEMORY;
1208 TALLOC_FREE(dfs_refs);
1209 return NT_STATUS_OK;
1212 /********************************************************************
1213 ********************************************************************/
1215 bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
1216 struct cli_state *cli,
1217 const char *sharename,
1218 char **pp_newserver,
1219 char **pp_newshare,
1220 struct cli_credentials *creds)
1222 struct client_dfs_referral *refs = NULL;
1223 size_t num_refs = 0;
1224 size_t consumed = 0;
1225 char *fullpath = NULL;
1226 bool res;
1227 struct smbXcli_tcon *orig_tcon = NULL;
1228 char *orig_share = NULL;
1229 char *newextrapath = NULL;
1230 NTSTATUS status;
1231 const char *remote_name;
1232 enum smb_encryption_setting encryption_state =
1233 cli_credentials_get_smb_encryption(creds);
1235 if (!cli || !sharename) {
1236 return false;
1239 remote_name = smbXcli_conn_remote_name(cli->conn);
1241 /* special case. never check for a referral on the IPC$ share */
1243 if (strequal(sharename, "IPC$")) {
1244 return false;
1247 /* send a trans2_query_path_info to check for a referral */
1249 fullpath = talloc_asprintf(ctx, "\\%s\\%s", remote_name, sharename);
1250 if (!fullpath) {
1251 return false;
1254 /* Store tcon state. */
1255 if (cli_state_has_tcon(cli)) {
1256 cli_state_save_tcon_share(cli, &orig_tcon, &orig_share);
1259 /* check for the referral */
1261 if (!NT_STATUS_IS_OK(cli_tree_connect(cli, "IPC$", "IPC", NULL))) {
1262 cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
1263 return false;
1266 if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
1267 status = cli_cm_force_encryption_creds(cli, creds, "IPC$");
1268 if (!NT_STATUS_IS_OK(status)) {
1269 switch (encryption_state) {
1270 case SMB_ENCRYPTION_DESIRED:
1271 break;
1272 case SMB_ENCRYPTION_REQUIRED:
1273 default:
1275 * Failed to set up encryption.
1276 * Disconnect the temporary IPC$
1277 * tcon before restoring the original
1278 * tcon so we don't leak it.
1280 cli_tdis(cli);
1281 cli_state_restore_tcon_share(cli,
1282 orig_tcon,
1283 orig_share);
1284 return false;
1289 status = cli_dfs_get_referral(ctx, cli, fullpath, &refs,
1290 &num_refs, &consumed);
1291 res = NT_STATUS_IS_OK(status);
1293 status = cli_tdis(cli);
1295 cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
1297 if (!NT_STATUS_IS_OK(status)) {
1298 return false;
1301 if (!res || !num_refs) {
1302 return false;
1305 if (!refs[0].dfspath) {
1306 return false;
1309 if (!split_dfs_path(ctx, refs[0].dfspath, pp_newserver,
1310 pp_newshare, &newextrapath)) {
1311 return false;
1314 /* check that this is not a self-referral */
1316 if (strequal(remote_name, *pp_newserver) &&
1317 strequal(sharename, *pp_newshare)) {
1318 return false;
1321 return true;
1324 /********************************************************************
1325 Windows and NetApp (and arguably the SMB1/2/3 specs) expect a non-DFS
1326 path for the targets of rename and hardlink. If we have been given
1327 a DFS path for these calls, convert it back into a local path by
1328 stripping off the DFS prefix.
1329 ********************************************************************/
1331 NTSTATUS cli_dfs_target_check(TALLOC_CTX *mem_ctx,
1332 struct cli_state *cli,
1333 const char *fname_dst,
1334 const char **fname_dst_out)
1336 char *dfs_prefix = NULL;
1337 size_t prefix_len = 0;
1338 struct smbXcli_tcon *tcon = NULL;
1340 if (!smbXcli_conn_dfs_supported(cli->conn)) {
1341 goto copy_fname_out;
1343 if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
1344 tcon = cli->smb2.tcon;
1345 } else {
1346 tcon = cli->smb1.tcon;
1348 if (!smbXcli_tcon_is_dfs_share(tcon)) {
1349 goto copy_fname_out;
1351 dfs_prefix = cli_dfs_make_full_path(mem_ctx, cli, "");
1352 if (dfs_prefix == NULL) {
1353 return NT_STATUS_NO_MEMORY;
1355 prefix_len = strlen(dfs_prefix);
1356 if (strncmp(fname_dst, dfs_prefix, prefix_len) != 0) {
1358 * Prefix doesn't match. Assume it was
1359 * already stripped or not added in the
1360 * first place.
1362 goto copy_fname_out;
1364 /* Return the trailing name after the prefix. */
1365 *fname_dst_out = &fname_dst[prefix_len];
1366 TALLOC_FREE(dfs_prefix);
1367 return NT_STATUS_OK;
1369 copy_fname_out:
1372 * No change to the destination name. Just
1373 * point it at the incoming destination name.
1375 *fname_dst_out = fname_dst;
1376 TALLOC_FREE(dfs_prefix);
1377 return NT_STATUS_OK;
1380 /********************************************************************
1381 Convert a pathname into a DFS path if it hasn't already been converted.
1382 Always returns a talloc'ed path, makes it easy to pass const paths in.
1383 ********************************************************************/
1385 char *smb1_dfs_share_path(TALLOC_CTX *ctx,
1386 struct cli_state *cli,
1387 const char *path)
1389 bool is_dfs = smbXcli_conn_dfs_supported(cli->conn) &&
1390 smbXcli_tcon_is_dfs_share(cli->smb1.tcon);
1391 bool is_already_dfs_path = false;
1392 bool posix = (cli->requested_posix_capabilities &
1393 CIFS_UNIX_POSIX_PATHNAMES_CAP);
1394 char sepchar = (posix ? '/' : '\\');
1396 if (!is_dfs) {
1397 return talloc_strdup(ctx, path);
1399 is_already_dfs_path = cli_dfs_is_already_full_path(cli, path);
1400 if (is_already_dfs_path) {
1401 return talloc_strdup(ctx, path);
1404 * We don't use cli_dfs_make_full_path() as,
1405 * when given a null path, cli_dfs_make_full_path
1406 * deliberately adds a trailing '\\' (this is by
1407 * design to check for an existing DFS prefix match).
1409 if (path[0] == '\0') {
1410 return talloc_asprintf(ctx,
1411 "%c%s%c%s",
1412 sepchar,
1413 smbXcli_conn_remote_name(cli->conn),
1414 sepchar,
1415 cli->share);
1417 while (*path == sepchar) {
1418 path++;
1420 return talloc_asprintf(ctx,
1421 "%c%s%c%s%c%s",
1422 sepchar,
1423 smbXcli_conn_remote_name(cli->conn),
1424 sepchar,
1425 cli->share,
1426 sepchar,
1427 path);