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