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