source: trunk/server/source3/libsmb/clidfs.c

Last change on this file was 920, checked in by Silvan Scherrer, 9 years ago

Samba Server: apply latest security patches to trunk

File size: 25.6 KB
Line 
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
29/********************************************************************
30 Important point.
31
32 DFS paths are *always* of the form \server\share\<pathname> (the \ characters
33 are not C escaped here).
34
35 - but if we're using POSIX paths then <pathname> may contain
36 '/' separators, not '\\' separators. So cope with '\\' or '/'
37 as a separator when looking at the pathname part.... JRA.
38********************************************************************/
39
40/********************************************************************
41 Ensure a connection is encrypted.
42********************************************************************/
43
44NTSTATUS cli_cm_force_encryption(struct cli_state *c,
45 const char *username,
46 const char *password,
47 const char *domain,
48 const char *sharename)
49{
50 NTSTATUS status = cli_force_encryption(c,
51 username,
52 password,
53 domain);
54
55 if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_SUPPORTED)) {
56 d_printf("Encryption required and "
57 "server that doesn't support "
58 "UNIX extensions - failing connect\n");
59 } else if (NT_STATUS_EQUAL(status,NT_STATUS_UNKNOWN_REVISION)) {
60 d_printf("Encryption required and "
61 "can't get UNIX CIFS extensions "
62 "version from server.\n");
63 } else if (NT_STATUS_EQUAL(status,NT_STATUS_UNSUPPORTED_COMPRESSION)) {
64 d_printf("Encryption required and "
65 "share %s doesn't support "
66 "encryption.\n", sharename);
67 } else if (!NT_STATUS_IS_OK(status)) {
68 d_printf("Encryption required and "
69 "setup failed with error %s.\n",
70 nt_errstr(status));
71 }
72
73 return status;
74}
75
76/********************************************************************
77 Return a connection to a server.
78********************************************************************/
79
80static struct cli_state *do_connect(TALLOC_CTX *ctx,
81 const char *server,
82 const char *share,
83 const struct user_auth_info *auth_info,
84 bool show_sessetup,
85 bool force_encrypt,
86 int max_protocol,
87 int port,
88 int name_type)
89{
90 struct cli_state *c = NULL;
91 struct nmb_name called, calling;
92 const char *called_str;
93 const char *server_n;
94 struct sockaddr_storage ss;
95 char *servicename;
96 char *sharename;
97 char *newserver, *newshare;
98 const char *username;
99 const char *password;
100 NTSTATUS status;
101 int signing_state = get_cmdline_auth_info_signing_state(auth_info);
102
103 if (force_encrypt) {
104 signing_state = Required;
105 }
106
107 /* make a copy so we don't modify the global string 'service' */
108 servicename = talloc_strdup(ctx,share);
109 if (!servicename) {
110 return NULL;
111 }
112 sharename = servicename;
113 if (*sharename == '\\') {
114 sharename += 2;
115 called_str = sharename;
116 if (server == NULL) {
117 server = sharename;
118 }
119 sharename = strchr_m(sharename,'\\');
120 if (!sharename) {
121 return NULL;
122 }
123 *sharename = 0;
124 sharename++;
125 } else {
126 called_str = server;
127 }
128
129 server_n = server;
130
131 zero_sockaddr(&ss);
132
133 make_nmb_name(&calling, global_myname(), 0x0);
134 make_nmb_name(&called , called_str, name_type);
135
136 again:
137 zero_sockaddr(&ss);
138
139 /* have to open a new connection */
140 c = cli_initialise_ex(signing_state);
141 if (c == NULL) {
142 d_printf("Connection to %s failed\n", server_n);
143 return NULL;
144 }
145 if (port) {
146 cli_set_port(c, port);
147 }
148
149 status = cli_connect(c, server_n, &ss);
150 if (!NT_STATUS_IS_OK(status)) {
151 d_printf("Connection to %s failed (Error %s)\n",
152 server_n,
153 nt_errstr(status));
154 cli_shutdown(c);
155 return NULL;
156 }
157
158 if (max_protocol == 0) {
159 max_protocol = PROTOCOL_NT1;
160 }
161 c->protocol = max_protocol;
162 c->use_kerberos = get_cmdline_auth_info_use_kerberos(auth_info);
163 c->fallback_after_kerberos =
164 get_cmdline_auth_info_fallback_after_kerberos(auth_info);
165 c->use_ccache = get_cmdline_auth_info_use_ccache(auth_info);
166
167 if (!cli_session_request(c, &calling, &called)) {
168 char *p;
169 d_printf("session request to %s failed (%s)\n",
170 called.name, cli_errstr(c));
171 cli_shutdown(c);
172 c = NULL;
173 if ((p=strchr_m(called.name, '.'))) {
174 *p = 0;
175 goto again;
176 }
177 if (strcmp(called.name, "*SMBSERVER")) {
178 make_nmb_name(&called , "*SMBSERVER", 0x20);
179 goto again;
180 }
181 return NULL;
182 }
183
184 DEBUG(4,(" session request ok\n"));
185
186 status = cli_negprot(c);
187
188 if (!NT_STATUS_IS_OK(status)) {
189 d_printf("protocol negotiation failed: %s\n",
190 nt_errstr(status));
191 cli_shutdown(c);
192 return NULL;
193 }
194
195 username = get_cmdline_auth_info_username(auth_info);
196 password = get_cmdline_auth_info_password(auth_info);
197
198 if (!NT_STATUS_IS_OK(cli_session_setup(c, username,
199 password, strlen(password),
200 password, strlen(password),
201 lp_workgroup()))) {
202 /* If a password was not supplied then
203 * try again with a null username. */
204 if (password[0] || !username[0] ||
205 get_cmdline_auth_info_use_kerberos(auth_info) ||
206 !NT_STATUS_IS_OK(cli_session_setup(c, "",
207 "", 0,
208 "", 0,
209 lp_workgroup()))) {
210 d_printf("session setup failed: %s\n", cli_errstr(c));
211 if (NT_STATUS_V(cli_nt_error(c)) ==
212 NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED))
213 d_printf("did you forget to run kinit?\n");
214 cli_shutdown(c);
215 return NULL;
216 }
217 d_printf("Anonymous login successful\n");
218 status = cli_init_creds(c, "", lp_workgroup(), "");
219 } else {
220 status = cli_init_creds(c, username, lp_workgroup(), password);
221 }
222
223 if (!NT_STATUS_IS_OK(status)) {
224 DEBUG(10,("cli_init_creds() failed: %s\n", nt_errstr(status)));
225 cli_shutdown(c);
226 return NULL;
227 }
228
229 if ( show_sessetup ) {
230 if (*c->server_domain) {
231 DEBUG(0,("Domain=[%s] OS=[%s] Server=[%s]\n",
232 c->server_domain,c->server_os,c->server_type));
233 } else if (*c->server_os || *c->server_type) {
234 DEBUG(0,("OS=[%s] Server=[%s]\n",
235 c->server_os,c->server_type));
236 }
237 }
238 DEBUG(4,(" session setup ok\n"));
239
240 /* here's the fun part....to support 'msdfs proxy' shares
241 (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL
242 here before trying to connect to the original share.
243 cli_check_msdfs_proxy() will fail if it is a normal share. */
244
245 if ((c->capabilities & CAP_DFS) &&
246 cli_check_msdfs_proxy(ctx, c, sharename,
247 &newserver, &newshare,
248 force_encrypt,
249 username,
250 password,
251 lp_workgroup())) {
252 cli_shutdown(c);
253 return do_connect(ctx, newserver,
254 newshare, auth_info, false,
255 force_encrypt, max_protocol,
256 port, name_type);
257 }
258
259 /* must be a normal share */
260
261 status = cli_tcon_andx(c, sharename, "?????",
262 password, strlen(password)+1);
263 if (!NT_STATUS_IS_OK(status)) {
264 d_printf("tree connect failed: %s\n", nt_errstr(status));
265 cli_shutdown(c);
266 return NULL;
267 }
268
269 if (force_encrypt) {
270 status = cli_cm_force_encryption(c,
271 username,
272 password,
273 lp_workgroup(),
274 sharename);
275 if (!NT_STATUS_IS_OK(status)) {
276 cli_shutdown(c);
277 return NULL;
278 }
279 }
280
281 DEBUG(4,(" tconx ok\n"));
282 return c;
283}
284
285/****************************************************************************
286****************************************************************************/
287
288static void cli_set_mntpoint(struct cli_state *cli, const char *mnt)
289{
290 char *name = clean_name(NULL, mnt);
291 if (!name) {
292 return;
293 }
294 TALLOC_FREE(cli->dfs_mountpoint);
295 cli->dfs_mountpoint = talloc_strdup(cli, name);
296 TALLOC_FREE(name);
297}
298
299/********************************************************************
300 Add a new connection to the list.
301 referring_cli == NULL means a new initial connection.
302********************************************************************/
303
304static struct cli_state *cli_cm_connect(TALLOC_CTX *ctx,
305 struct cli_state *referring_cli,
306 const char *server,
307 const char *share,
308 const struct user_auth_info *auth_info,
309 bool show_hdr,
310 bool force_encrypt,
311 int max_protocol,
312 int port,
313 int name_type)
314{
315 struct cli_state *cli;
316
317 cli = do_connect(ctx, server, share,
318 auth_info,
319 show_hdr, force_encrypt, max_protocol,
320 port, name_type);
321
322 if (!cli ) {
323 return NULL;
324 }
325
326 /* Enter into the list. */
327 if (referring_cli) {
328 DLIST_ADD_END(referring_cli, cli, struct cli_state *);
329 }
330
331 if (referring_cli && referring_cli->requested_posix_capabilities) {
332 uint16 major, minor;
333 uint32 caplow, caphigh;
334 NTSTATUS status;
335 status = cli_unix_extensions_version(cli, &major, &minor,
336 &caplow, &caphigh);
337 if (NT_STATUS_IS_OK(status)) {
338 cli_set_unix_extensions_capabilities(cli,
339 major, minor,
340 caplow, caphigh);
341 }
342 }
343
344 return cli;
345}
346
347/********************************************************************
348 Return a connection to a server on a particular share.
349********************************************************************/
350
351static struct cli_state *cli_cm_find(struct cli_state *cli,
352 const char *server,
353 const char *share)
354{
355 struct cli_state *p;
356
357 if (cli == NULL) {
358 return NULL;
359 }
360
361 /* Search to the start of the list. */
362 for (p = cli; p; p = DLIST_PREV(p)) {
363 if (strequal(server, p->desthost) &&
364 strequal(share,p->share)) {
365 return p;
366 }
367 }
368
369 /* Search to the end of the list. */
370 for (p = cli->next; p; p = p->next) {
371 if (strequal(server, p->desthost) &&
372 strequal(share,p->share)) {
373 return p;
374 }
375 }
376
377 return NULL;
378}
379
380/****************************************************************************
381 Open a client connection to a \\server\share.
382****************************************************************************/
383
384struct cli_state *cli_cm_open(TALLOC_CTX *ctx,
385 struct cli_state *referring_cli,
386 const char *server,
387 const char *share,
388 const struct user_auth_info *auth_info,
389 bool show_hdr,
390 bool force_encrypt,
391 int max_protocol,
392 int port,
393 int name_type)
394{
395 /* Try to reuse an existing connection in this list. */
396 struct cli_state *c = cli_cm_find(referring_cli, server, share);
397
398 if (c) {
399 return c;
400 }
401
402 if (auth_info == NULL) {
403 /* Can't do a new connection
404 * without auth info. */
405 d_printf("cli_cm_open() Unable to open connection [\\%s\\%s] "
406 "without auth info\n",
407 server, share );
408 return NULL;
409 }
410
411 return cli_cm_connect(ctx,
412 referring_cli,
413 server,
414 share,
415 auth_info,
416 show_hdr,
417 force_encrypt,
418 max_protocol,
419 port,
420 name_type);
421}
422
423/****************************************************************************
424****************************************************************************/
425
426void cli_cm_display(const struct cli_state *cli)
427{
428 int i;
429
430 for (i=0; cli; cli = cli->next,i++ ) {
431 d_printf("%d:\tserver=%s, share=%s\n",
432 i, cli->desthost, cli->share );
433 }
434}
435
436/****************************************************************************
437****************************************************************************/
438
439/****************************************************************************
440****************************************************************************/
441
442#if 0
443void cli_cm_set_credentials(struct user_auth_info *auth_info)
444{
445 SAFE_FREE(cm_creds.username);
446 cm_creds.username = SMB_STRDUP(get_cmdline_auth_info_username(
447 auth_info));
448
449 if (get_cmdline_auth_info_got_pass(auth_info)) {
450 cm_set_password(get_cmdline_auth_info_password(auth_info));
451 }
452
453 cm_creds.use_kerberos = get_cmdline_auth_info_use_kerberos(auth_info);
454 cm_creds.fallback_after_kerberos = false;
455 cm_creds.signing_state = get_cmdline_auth_info_signing_state(auth_info);
456}
457#endif
458
459/**********************************************************************
460 split a dfs path into the server, share name, and extrapath components
461**********************************************************************/
462
463static bool split_dfs_path(TALLOC_CTX *ctx,
464 const char *nodepath,
465 char **pp_server,
466 char **pp_share,
467 char **pp_extrapath)
468{
469 char *p, *q;
470 char *path;
471
472 *pp_server = NULL;
473 *pp_share = NULL;
474 *pp_extrapath = NULL;
475
476 path = talloc_strdup(ctx, nodepath);
477 if (!path) {
478 goto fail;
479 }
480
481 if ( path[0] != '\\' ) {
482 goto fail;
483 }
484
485 p = strchr_m( path + 1, '\\' );
486 if ( !p ) {
487 goto fail;
488 }
489
490 *p = '\0';
491 p++;
492
493 /* Look for any extra/deep path */
494 q = strchr_m(p, '\\');
495 if (q != NULL) {
496 *q = '\0';
497 q++;
498 *pp_extrapath = talloc_strdup(ctx, q);
499 } else {
500 *pp_extrapath = talloc_strdup(ctx, "");
501 }
502 if (*pp_extrapath == NULL) {
503 goto fail;
504 }
505
506 *pp_share = talloc_strdup(ctx, p);
507 if (*pp_share == NULL) {
508 goto fail;
509 }
510
511 *pp_server = talloc_strdup(ctx, &path[1]);
512 if (*pp_server == NULL) {
513 goto fail;
514 }
515
516 TALLOC_FREE(path);
517 return true;
518
519fail:
520 TALLOC_FREE(*pp_share);
521 TALLOC_FREE(*pp_extrapath);
522 TALLOC_FREE(path);
523 return false;
524}
525
526/****************************************************************************
527 Return the original path truncated at the directory component before
528 the first wildcard character. Trust the caller to provide a NULL
529 terminated string
530****************************************************************************/
531
532static char *clean_path(TALLOC_CTX *ctx, const char *path)
533{
534 size_t len;
535 char *p1, *p2, *p;
536 char *path_out;
537
538 /* No absolute paths. */
539 while (IS_DIRECTORY_SEP(*path)) {
540 path++;
541 }
542
543 path_out = talloc_strdup(ctx, path);
544 if (!path_out) {
545 return NULL;
546 }
547
548 p1 = strchr_m(path_out, '*');
549 p2 = strchr_m(path_out, '?');
550
551 if (p1 || p2) {
552 if (p1 && p2) {
553 p = MIN(p1,p2);
554 } else if (!p1) {
555 p = p2;
556 } else {
557 p = p1;
558 }
559 *p = '\0';
560
561 /* Now go back to the start of this component. */
562 p1 = strrchr_m(path_out, '/');
563 p2 = strrchr_m(path_out, '\\');
564 p = MAX(p1,p2);
565 if (p) {
566 *p = '\0';
567 }
568 }
569
570 /* Strip any trailing separator */
571
572 len = strlen(path_out);
573 if ( (len > 0) && IS_DIRECTORY_SEP(path_out[len-1])) {
574 path_out[len-1] = '\0';
575 }
576
577 return path_out;
578}
579
580/****************************************************************************
581****************************************************************************/
582
583static char *cli_dfs_make_full_path(TALLOC_CTX *ctx,
584 struct cli_state *cli,
585 const char *dir)
586{
587 char path_sep = '\\';
588
589 /* Ensure the extrapath doesn't start with a separator. */
590 while (IS_DIRECTORY_SEP(*dir)) {
591 dir++;
592 }
593
594 if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
595 path_sep = '/';
596 }
597 return talloc_asprintf(ctx, "%c%s%c%s%c%s",
598 path_sep,
599 cli->desthost,
600 path_sep,
601 cli->share,
602 path_sep,
603 dir);
604}
605
606/********************************************************************
607 check for dfs referral
608********************************************************************/
609
610static bool cli_dfs_check_error(struct cli_state *cli, NTSTATUS expected,
611 NTSTATUS status)
612{
613 /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */
614
615 if (!(cli->capabilities & CAP_UNICODE)) {
616 return false;
617 }
618 if (!(cli->capabilities & CAP_STATUS32)) {
619 return false;
620 }
621 if (NT_STATUS_EQUAL(status, expected)) {
622 return true;
623 }
624 return false;
625}
626
627/********************************************************************
628 Get the dfs referral link.
629********************************************************************/
630
631NTSTATUS cli_dfs_get_referral(TALLOC_CTX *ctx,
632 struct cli_state *cli,
633 const char *path,
634 struct client_dfs_referral **refs,
635 size_t *num_refs,
636 size_t *consumed)
637{
638 unsigned int data_len = 0;
639 unsigned int param_len = 0;
640 uint16 setup[1];
641 uint8_t *param = NULL;
642 uint8_t *rdata = NULL;
643 char *p;
644 char *endp;
645 size_t pathlen = 2*(strlen(path)+1);
646 smb_ucs2_t *path_ucs;
647 char *consumed_path = NULL;
648 uint16_t consumed_ucs;
649 uint16 num_referrals;
650 struct client_dfs_referral *referrals = NULL;
651 NTSTATUS status;
652
653 *num_refs = 0;
654 *refs = NULL;
655
656 SSVAL(setup, 0, TRANSACT2_GET_DFS_REFERRAL);
657
658 param = SMB_MALLOC_ARRAY(uint8_t, 2+pathlen+2);
659 if (!param) {
660 status = NT_STATUS_NO_MEMORY;
661 goto out;
662 }
663 SSVAL(param, 0, 0x03); /* max referral level */
664 p = (char *)(&param[2]);
665
666 path_ucs = (smb_ucs2_t *)p;
667 p += clistr_push(cli, p, path, pathlen, STR_TERMINATE);
668 param_len = PTR_DIFF(p, param);
669
670 status = cli_trans(talloc_tos(), cli, SMBtrans2,
671 NULL, 0xffff, 0, 0,
672 setup, 1, 0,
673 param, param_len, 2,
674 NULL, 0, cli->max_xmit,
675 NULL,
676 NULL, 0, NULL, /* rsetup */
677 NULL, 0, NULL,
678 &rdata, 4, &data_len);
679 if (!NT_STATUS_IS_OK(status)) {
680 goto out;
681 }
682 if (data_len < 4) {
683 goto out;
684 }
685
686 endp = (char *)rdata + data_len;
687
688 consumed_ucs = SVAL(rdata, 0);
689 num_referrals = SVAL(rdata, 2);
690
691 /* consumed_ucs is the number of bytes
692 * of the UCS2 path consumed not counting any
693 * terminating null. We need to convert
694 * back to unix charset and count again
695 * to get the number of bytes consumed from
696 * the incoming path. */
697
698 if (pull_string_talloc(talloc_tos(),
699 NULL,
700 0,
701 &consumed_path,
702 path_ucs,
703 consumed_ucs,
704 STR_UNICODE) == 0) {
705 goto out;
706 }
707 if (consumed_path == NULL) {
708 goto out;
709 }
710 *consumed = strlen(consumed_path);
711
712 if (num_referrals != 0) {
713 uint16 ref_version;
714 uint16 ref_size;
715 int i;
716 uint16 node_offset;
717
718 referrals = talloc_array(ctx, struct client_dfs_referral,
719 num_referrals);
720
721 if (!referrals) {
722 goto out;
723 }
724 /* start at the referrals array */
725
726 p = (char *)rdata+8;
727 for (i=0; i<num_referrals && p < endp; i++) {
728 if (p + 18 > endp) {
729 goto out;
730 }
731 ref_version = SVAL(p, 0);
732 ref_size = SVAL(p, 2);
733 node_offset = SVAL(p, 16);
734
735 if (ref_version != 3) {
736 p += ref_size;
737 continue;
738 }
739
740 referrals[i].proximity = SVAL(p, 8);
741 referrals[i].ttl = SVAL(p, 10);
742
743 if (p + node_offset > endp) {
744 goto out;
745 }
746 clistr_pull_talloc(ctx, cli->inbuf,
747 SVAL(cli->inbuf, smb_flg2),
748 &referrals[i].dfspath,
749 p+node_offset, -1,
750 STR_TERMINATE|STR_UNICODE);
751
752 if (!referrals[i].dfspath) {
753 goto out;
754 }
755 p += ref_size;
756 }
757 if (i < num_referrals) {
758 goto out;
759 }
760 }
761
762 *num_refs = num_referrals;
763 *refs = referrals;
764
765 out:
766
767 TALLOC_FREE(consumed_path);
768 SAFE_FREE(param);
769 TALLOC_FREE(rdata);
770 return status;
771}
772
773/********************************************************************
774********************************************************************/
775
776bool cli_resolve_path(TALLOC_CTX *ctx,
777 const char *mountpt,
778 const struct user_auth_info *dfs_auth_info,
779 struct cli_state *rootcli,
780 const char *path,
781 struct cli_state **targetcli,
782 char **pp_targetpath)
783{
784 struct client_dfs_referral *refs = NULL;
785 size_t num_refs = 0;
786 size_t consumed = 0;
787 struct cli_state *cli_ipc = NULL;
788 char *dfs_path = NULL;
789 char *cleanpath = NULL;
790 char *extrapath = NULL;
791 int pathlen;
792 char *server = NULL;
793 char *share = NULL;
794 struct cli_state *newcli = NULL;
795 char *newpath = NULL;
796 char *newmount = NULL;
797 char *ppath = NULL;
798 SMB_STRUCT_STAT sbuf;
799 uint32 attributes;
800 NTSTATUS status;
801
802 if ( !rootcli || !path || !targetcli ) {
803 return false;
804 }
805
806 /* Don't do anything if this is not a DFS root. */
807
808 if ( !rootcli->dfsroot) {
809 *targetcli = rootcli;
810 *pp_targetpath = talloc_strdup(ctx, path);
811 if (!*pp_targetpath) {
812 return false;
813 }
814 return true;
815 }
816
817 *targetcli = NULL;
818
819 /* Send a trans2_query_path_info to check for a referral. */
820
821 cleanpath = clean_path(ctx, path);
822 if (!cleanpath) {
823 return false;
824 }
825
826 dfs_path = cli_dfs_make_full_path(ctx, rootcli, cleanpath);
827 if (!dfs_path) {
828 return false;
829 }
830
831 status = cli_qpathinfo_basic( rootcli, dfs_path, &sbuf, &attributes);
832 if (NT_STATUS_IS_OK(status)) {
833 /* This is an ordinary path, just return it. */
834 *targetcli = rootcli;
835 *pp_targetpath = talloc_strdup(ctx, path);
836 if (!*pp_targetpath) {
837 return false;
838 }
839 goto done;
840 }
841
842 /* Special case where client asked for a path that does not exist */
843
844 if (cli_dfs_check_error(rootcli, NT_STATUS_OBJECT_NAME_NOT_FOUND,
845 status)) {
846 *targetcli = rootcli;
847 *pp_targetpath = talloc_strdup(ctx, path);
848 if (!*pp_targetpath) {
849 return false;
850 }
851 goto done;
852 }
853
854 /* We got an error, check for DFS referral. */
855
856 if (!cli_dfs_check_error(rootcli, NT_STATUS_PATH_NOT_COVERED,
857 status)) {
858 return false;
859 }
860
861 /* Check for the referral. */
862
863 if (!(cli_ipc = cli_cm_open(ctx,
864 rootcli,
865 rootcli->desthost,
866 "IPC$",
867 dfs_auth_info,
868 false,
869 (rootcli->trans_enc_state != NULL),
870 rootcli->protocol,
871 0,
872 0x20))) {
873 return false;
874 }
875
876 status = cli_dfs_get_referral(ctx, cli_ipc, dfs_path, &refs,
877 &num_refs, &consumed);
878 if (!NT_STATUS_IS_OK(status) || !num_refs) {
879 return false;
880 }
881
882 /* Just store the first referral for now. */
883
884 if (!refs[0].dfspath) {
885 return false;
886 }
887 if (!split_dfs_path(ctx, refs[0].dfspath, &server, &share,
888 &extrapath)) {
889 return false;
890 }
891
892 /* Make sure to recreate the original string including any wildcards. */
893
894 dfs_path = cli_dfs_make_full_path(ctx, rootcli, path);
895 if (!dfs_path) {
896 return false;
897 }
898 pathlen = strlen(dfs_path);
899 consumed = MIN(pathlen, consumed);
900 *pp_targetpath = talloc_strdup(ctx, &dfs_path[consumed]);
901 if (!*pp_targetpath) {
902 return false;
903 }
904 dfs_path[consumed] = '\0';
905
906 /*
907 * *pp_targetpath is now the unconsumed part of the path.
908 * dfs_path is now the consumed part of the path
909 * (in \server\share\path format).
910 */
911
912 /* Open the connection to the target server & share */
913 if ((*targetcli = cli_cm_open(ctx, rootcli,
914 server,
915 share,
916 dfs_auth_info,
917 false,
918 (rootcli->trans_enc_state != NULL),
919 rootcli->protocol,
920 0,
921 0x20)) == NULL) {
922 d_printf("Unable to follow dfs referral [\\%s\\%s]\n",
923 server, share );
924 return false;
925 }
926
927 if (extrapath && strlen(extrapath) > 0) {
928 /* EMC Celerra NAS version 5.6.50 (at least) doesn't appear to */
929 /* put the trailing \ on the path, so to be save we put one in if needed */
930 if (extrapath[strlen(extrapath)-1] != '\\' && **pp_targetpath != '\\') {
931 *pp_targetpath = talloc_asprintf(ctx,
932 "%s\\%s",
933 extrapath,
934 *pp_targetpath);
935 } else {
936 *pp_targetpath = talloc_asprintf(ctx,
937 "%s%s",
938 extrapath,
939 *pp_targetpath);
940 }
941 if (!*pp_targetpath) {
942 return false;
943 }
944 }
945
946 /* parse out the consumed mount path */
947 /* trim off the \server\share\ */
948
949 ppath = dfs_path;
950
951 if (*ppath != '\\') {
952 d_printf("cli_resolve_path: "
953 "dfs_path (%s) not in correct format.\n",
954 dfs_path );
955 return false;
956 }
957
958 ppath++; /* Now pointing at start of server name. */
959
960 if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) {
961 return false;
962 }
963
964 ppath++; /* Now pointing at start of share name. */
965
966 if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) {
967 return false;
968 }
969
970 ppath++; /* Now pointing at path component. */
971
972 newmount = talloc_asprintf(ctx, "%s\\%s", mountpt, ppath );
973 if (!newmount) {
974 return false;
975 }
976
977 cli_set_mntpoint(*targetcli, newmount);
978
979 /* Check for another dfs referral, note that we are not
980 checking for loops here. */
981
982 if (!strequal(*pp_targetpath, "\\") && !strequal(*pp_targetpath, "/")) {
983 if (cli_resolve_path(ctx,
984 newmount,
985 dfs_auth_info,
986 *targetcli,
987 *pp_targetpath,
988 &newcli,
989 &newpath)) {
990 /*
991 * When cli_resolve_path returns true here it's always
992 * returning the complete path in newpath, so we're done
993 * here.
994 */
995 *targetcli = newcli;
996 *pp_targetpath = newpath;
997 return true;
998 }
999 }
1000
1001 done:
1002
1003 /* If returning true ensure we return a dfs root full path. */
1004 if ((*targetcli)->dfsroot) {
1005 dfs_path = talloc_strdup(ctx, *pp_targetpath);
1006 if (!dfs_path) {
1007 return false;
1008 }
1009 *pp_targetpath = cli_dfs_make_full_path(ctx, *targetcli, dfs_path);
1010 }
1011
1012 return true;
1013}
1014
1015/********************************************************************
1016********************************************************************/
1017
1018bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
1019 struct cli_state *cli,
1020 const char *sharename,
1021 char **pp_newserver,
1022 char **pp_newshare,
1023 bool force_encrypt,
1024 const char *username,
1025 const char *password,
1026 const char *domain)
1027{
1028 struct client_dfs_referral *refs = NULL;
1029 size_t num_refs = 0;
1030 size_t consumed = 0;
1031 char *fullpath = NULL;
1032 bool res;
1033 uint16 cnum;
1034 char *newextrapath = NULL;
1035 NTSTATUS status;
1036
1037 if (!cli || !sharename) {
1038 return false;
1039 }
1040
1041 cnum = cli->cnum;
1042
1043 /* special case. never check for a referral on the IPC$ share */
1044
1045 if (strequal(sharename, "IPC$")) {
1046 return false;
1047 }
1048
1049 /* send a trans2_query_path_info to check for a referral */
1050
1051 fullpath = talloc_asprintf(ctx, "\\%s\\%s", cli->desthost, sharename );
1052 if (!fullpath) {
1053 return false;
1054 }
1055
1056 /* check for the referral */
1057
1058 if (!NT_STATUS_IS_OK(cli_tcon_andx(cli, "IPC$", "IPC", NULL, 0))) {
1059 return false;
1060 }
1061
1062 if (force_encrypt) {
1063 status = cli_cm_force_encryption(cli,
1064 username,
1065 password,
1066 lp_workgroup(),
1067 "IPC$");
1068 if (!NT_STATUS_IS_OK(status)) {
1069 return false;
1070 }
1071 }
1072
1073 status = cli_dfs_get_referral(ctx, cli, fullpath, &refs,
1074 &num_refs, &consumed);
1075 res = NT_STATUS_IS_OK(status);
1076
1077 status = cli_tdis(cli);
1078 if (!NT_STATUS_IS_OK(status)) {
1079 return false;
1080 }
1081
1082 cli->cnum = cnum;
1083
1084 if (!res || !num_refs) {
1085 return false;
1086 }
1087
1088 if (!refs[0].dfspath) {
1089 return false;
1090 }
1091
1092 if (!split_dfs_path(ctx, refs[0].dfspath, pp_newserver,
1093 pp_newshare, &newextrapath)) {
1094 return false;
1095 }
1096
1097 /* check that this is not a self-referral */
1098
1099 if (strequal(cli->desthost, *pp_newserver) &&
1100 strequal(sharename, *pp_newshare)) {
1101 return false;
1102 }
1103
1104 return true;
1105}
Note: See TracBrowser for help on using the repository browser.