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