Fix bug #5326 - OS/2 servers give strange "high word" replies for print jobs.
[Samba.git] / source / libsmb / clidfs.c
blob971cde12527cb8716892aff5842d73b068fd866b
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
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
24 /********************************************************************
25 Important point.
27 DFS paths are *always* of the form \server\share\<pathname> (the \ characters
28 are not C escaped here).
30 - but if we're using POSIX paths then <pathname> may contain
31 '/' separators, not '\\' separators. So cope with '\\' or '/'
32 as a separator when looking at the pathname part.... JRA.
33 ********************************************************************/
35 struct client_connection {
36 struct client_connection *prev, *next;
37 struct cli_state *cli;
38 char *mount;
41 /* global state....globals reek! */
42 int max_protocol = PROTOCOL_NT1;
44 static struct cm_cred_struct {
45 char *username;
46 char *password;
47 bool got_pass;
48 bool use_kerberos;
49 int signing_state;
50 } cm_creds;
52 static void cm_set_password(const char *newpass);
54 static int port;
55 static int name_type = 0x20;
56 static bool have_ip;
57 static struct sockaddr_storage dest_ss;
59 static struct client_connection *connections;
61 static bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
62 struct cli_state *cli,
63 const char *sharename,
64 char **pp_newserver,
65 char **pp_newshare,
66 bool force_encrypt,
67 const char *username,
68 const char *password,
69 const char *domain);
71 /********************************************************************
72 Ensure a connection is encrypted.
73 ********************************************************************/
75 NTSTATUS cli_cm_force_encryption(struct cli_state *c,
76 const char *username,
77 const char *password,
78 const char *domain,
79 const char *sharename)
81 NTSTATUS status = cli_force_encryption(c,
82 username,
83 password,
84 domain);
86 if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_SUPPORTED)) {
87 d_printf("Encryption required and "
88 "server that doesn't support "
89 "UNIX extensions - failing connect\n");
90 } else if (NT_STATUS_EQUAL(status,NT_STATUS_UNKNOWN_REVISION)) {
91 d_printf("Encryption required and "
92 "can't get UNIX CIFS extensions "
93 "version from server.\n");
94 } else if (NT_STATUS_EQUAL(status,NT_STATUS_UNSUPPORTED_COMPRESSION)) {
95 d_printf("Encryption required and "
96 "share %s doesn't support "
97 "encryption.\n", sharename);
98 } else if (!NT_STATUS_IS_OK(status)) {
99 d_printf("Encryption required and "
100 "setup failed with error %s.\n",
101 nt_errstr(status));
104 return status;
107 /********************************************************************
108 Return a connection to a server.
109 ********************************************************************/
111 static struct cli_state *do_connect(TALLOC_CTX *ctx,
112 const char *server,
113 const char *share,
114 bool show_sessetup,
115 bool force_encrypt)
117 struct cli_state *c = NULL;
118 struct nmb_name called, calling;
119 const char *server_n;
120 struct sockaddr_storage ss;
121 char *servicename;
122 char *sharename;
123 char *newserver, *newshare;
124 const char *username;
125 const char *password;
126 NTSTATUS status;
128 /* make a copy so we don't modify the global string 'service' */
129 servicename = talloc_strdup(ctx,share);
130 if (!servicename) {
131 return NULL;
133 sharename = servicename;
134 if (*sharename == '\\') {
135 server = sharename+2;
136 sharename = strchr_m(server,'\\');
137 if (!sharename) {
138 return NULL;
140 *sharename = 0;
141 sharename++;
144 server_n = server;
146 zero_addr(&ss);
148 make_nmb_name(&calling, global_myname(), 0x0);
149 make_nmb_name(&called , server, name_type);
151 again:
152 zero_addr(&ss);
153 if (have_ip)
154 ss = dest_ss;
156 /* have to open a new connection */
157 if (!(c=cli_initialise()) || (cli_set_port(c, port) != port)) {
158 d_printf("Connection to %s failed\n", server_n);
159 if (c) {
160 cli_shutdown(c);
162 return NULL;
164 status = cli_connect(c, server_n, &ss);
165 if (!NT_STATUS_IS_OK(status)) {
166 d_printf("Connection to %s failed (Error %s)\n",
167 server_n,
168 nt_errstr(status));
169 cli_shutdown(c);
170 return NULL;
173 c->protocol = max_protocol;
174 c->use_kerberos = cm_creds.use_kerberos;
175 cli_setup_signing_state(c, cm_creds.signing_state);
177 if (!cli_session_request(c, &calling, &called)) {
178 char *p;
179 d_printf("session request to %s failed (%s)\n",
180 called.name, cli_errstr(c));
181 cli_shutdown(c);
182 c = NULL;
183 if ((p=strchr_m(called.name, '.'))) {
184 *p = 0;
185 goto again;
187 if (strcmp(called.name, "*SMBSERVER")) {
188 make_nmb_name(&called , "*SMBSERVER", 0x20);
189 goto again;
191 return NULL;
194 DEBUG(4,(" session request ok\n"));
196 if (!cli_negprot(c)) {
197 d_printf("protocol negotiation failed\n");
198 cli_shutdown(c);
199 return NULL;
202 if (!cm_creds.got_pass) {
203 char *pass = getpass("Password: ");
204 if (pass) {
205 cm_set_password(pass);
209 username = cm_creds.username ? cm_creds.username : "";
210 password = cm_creds.password ? cm_creds.password : "";
212 if (!NT_STATUS_IS_OK(cli_session_setup(c, username,
213 password, strlen(password),
214 password, strlen(password),
215 lp_workgroup()))) {
216 /* If a password was not supplied then
217 * try again with a null username. */
218 if (password[0] || !username[0] || cm_creds.use_kerberos ||
219 !NT_STATUS_IS_OK(cli_session_setup(c, "",
220 "", 0,
221 "", 0,
222 lp_workgroup()))) {
223 d_printf("session setup failed: %s\n", cli_errstr(c));
224 if (NT_STATUS_V(cli_nt_error(c)) ==
225 NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED))
226 d_printf("did you forget to run kinit?\n");
227 cli_shutdown(c);
228 return NULL;
230 d_printf("Anonymous login successful\n");
233 if ( show_sessetup ) {
234 if (*c->server_domain) {
235 DEBUG(0,("Domain=[%s] OS=[%s] Server=[%s]\n",
236 c->server_domain,c->server_os,c->server_type));
237 } else if (*c->server_os || *c->server_type) {
238 DEBUG(0,("OS=[%s] Server=[%s]\n",
239 c->server_os,c->server_type));
242 DEBUG(4,(" session setup ok\n"));
244 /* here's the fun part....to support 'msdfs proxy' shares
245 (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL
246 here before trying to connect to the original share.
247 check_dfs_proxy() will fail if it is a normal share. */
249 if ((c->capabilities & CAP_DFS) &&
250 cli_check_msdfs_proxy(ctx, c, sharename,
251 &newserver, &newshare,
252 force_encrypt,
253 username,
254 password,
255 lp_workgroup())) {
256 cli_shutdown(c);
257 return do_connect(ctx, newserver,
258 newshare, false, force_encrypt);
261 /* must be a normal share */
263 if (!cli_send_tconX(c, sharename, "?????",
264 password, strlen(password)+1)) {
265 d_printf("tree connect failed: %s\n", cli_errstr(c));
266 cli_shutdown(c);
267 return NULL;
270 if (force_encrypt) {
271 status = cli_cm_force_encryption(c,
272 username,
273 password,
274 lp_workgroup(),
275 sharename);
276 if (!NT_STATUS_IS_OK(status)) {
277 cli_shutdown(c);
278 return NULL;
282 DEBUG(4,(" tconx ok\n"));
283 return c;
286 /****************************************************************************
287 ****************************************************************************/
289 static void cli_cm_set_mntpoint(struct cli_state *c, const char *mnt)
291 struct client_connection *p;
292 int i;
294 for (p=connections,i=0; p; p=p->next,i++) {
295 if (strequal(p->cli->desthost, c->desthost) &&
296 strequal(p->cli->share, c->share)) {
297 break;
301 if (p) {
302 char *name = clean_name(NULL, p->mount);
303 if (!name) {
304 return;
306 p->mount = talloc_strdup(p, name);
307 TALLOC_FREE(name);
311 /****************************************************************************
312 ****************************************************************************/
314 const char *cli_cm_get_mntpoint(struct cli_state *c)
316 struct client_connection *p;
317 int i;
319 for (p=connections,i=0; p; p=p->next,i++) {
320 if (strequal(p->cli->desthost, c->desthost) &&
321 strequal(p->cli->share, c->share)) {
322 break;
326 if (p) {
327 return p->mount;
329 return NULL;
332 /********************************************************************
333 Add a new connection to the list
334 ********************************************************************/
336 static struct cli_state *cli_cm_connect(TALLOC_CTX *ctx,
337 struct cli_state *referring_cli,
338 const char *server,
339 const char *share,
340 bool show_hdr,
341 bool force_encrypt)
343 struct client_connection *node;
345 /* NB This must be the null context here... JRA. */
346 node = TALLOC_ZERO_ARRAY(NULL, struct client_connection, 1);
347 if (!node) {
348 return NULL;
351 node->cli = do_connect(ctx, server, share, show_hdr, force_encrypt);
353 if ( !node->cli ) {
354 TALLOC_FREE( node );
355 return NULL;
358 DLIST_ADD( connections, node );
360 cli_cm_set_mntpoint(node->cli, "");
362 if (referring_cli && referring_cli->posix_capabilities) {
363 uint16 major, minor;
364 uint32 caplow, caphigh;
365 if (cli_unix_extensions_version(node->cli, &major,
366 &minor, &caplow, &caphigh)) {
367 cli_set_unix_extensions_capabilities(node->cli,
368 major, minor,
369 caplow, caphigh);
373 return node->cli;
376 /********************************************************************
377 Return a connection to a server.
378 ********************************************************************/
380 static struct cli_state *cli_cm_find(const char *server, const char *share)
382 struct client_connection *p;
384 for (p=connections; p; p=p->next) {
385 if ( strequal(server, p->cli->desthost) &&
386 strequal(share,p->cli->share)) {
387 return p->cli;
391 return NULL;
394 /****************************************************************************
395 Open a client connection to a \\server\share. Set's the current *cli
396 global variable as a side-effect (but only if the connection is successful).
397 ****************************************************************************/
399 struct cli_state *cli_cm_open(TALLOC_CTX *ctx,
400 struct cli_state *referring_cli,
401 const char *server,
402 const char *share,
403 bool show_hdr,
404 bool force_encrypt)
406 struct cli_state *c;
408 /* try to reuse an existing connection */
410 c = cli_cm_find(server, share);
411 if (!c) {
412 c = cli_cm_connect(ctx, referring_cli,
413 server, share, show_hdr, force_encrypt);
416 return c;
419 /****************************************************************************
420 ****************************************************************************/
422 void cli_cm_shutdown(void)
424 struct client_connection *p, *x;
426 for (p=connections; p;) {
427 cli_shutdown(p->cli);
428 x = p;
429 p = p->next;
431 TALLOC_FREE(x);
434 connections = NULL;
435 return;
438 /****************************************************************************
439 ****************************************************************************/
441 void cli_cm_display(void)
443 struct client_connection *p;
444 int i;
446 for ( p=connections,i=0; p; p=p->next,i++ ) {
447 d_printf("%d:\tserver=%s, share=%s\n",
448 i, p->cli->desthost, p->cli->share );
452 /****************************************************************************
453 ****************************************************************************/
455 static void cm_set_password(const char *newpass)
457 SAFE_FREE(cm_creds.password);
458 cm_creds.password = SMB_STRDUP(newpass);
459 if (cm_creds.password) {
460 cm_creds.got_pass = true;
464 void cli_cm_set_credentials(void)
466 SAFE_FREE(cm_creds.username);
467 cm_creds.username = SMB_STRDUP(get_cmdline_auth_info_username());
469 if (get_cmdline_auth_info_got_pass()) {
470 cm_set_password(get_cmdline_auth_info_password());
473 cm_creds.use_kerberos = get_cmdline_auth_info_use_kerberos();
474 cm_creds.signing_state = get_cmdline_auth_info_signing_state();
477 /****************************************************************************
478 ****************************************************************************/
480 void cli_cm_set_port(int port_number)
482 port = port_number;
485 /****************************************************************************
486 ****************************************************************************/
488 void cli_cm_set_dest_name_type(int type)
490 name_type = type;
493 /****************************************************************************
494 ****************************************************************************/
496 void cli_cm_set_dest_ss(struct sockaddr_storage *pss)
498 dest_ss = *pss;
499 have_ip = true;
502 /**********************************************************************
503 split a dfs path into the server, share name, and extrapath components
504 **********************************************************************/
506 static void split_dfs_path(TALLOC_CTX *ctx,
507 const char *nodepath,
508 char **pp_server,
509 char **pp_share,
510 char **pp_extrapath)
512 char *p, *q;
513 char *path;
515 *pp_server = NULL;
516 *pp_share = NULL;
517 *pp_extrapath = NULL;
519 path = talloc_strdup(ctx, nodepath);
520 if (!path) {
521 return;
524 if ( path[0] != '\\' ) {
525 return;
528 p = strchr_m( path + 1, '\\' );
529 if ( !p ) {
530 return;
533 *p = '\0';
534 p++;
536 /* Look for any extra/deep path */
537 q = strchr_m(p, '\\');
538 if (q != NULL) {
539 *q = '\0';
540 q++;
541 *pp_extrapath = talloc_strdup(ctx, q);
542 } else {
543 *pp_extrapath = talloc_strdup(ctx, "");
546 *pp_share = talloc_strdup(ctx, p);
547 *pp_server = talloc_strdup(ctx, &path[1]);
550 /****************************************************************************
551 Return the original path truncated at the directory component before
552 the first wildcard character. Trust the caller to provide a NULL
553 terminated string
554 ****************************************************************************/
556 static char *clean_path(TALLOC_CTX *ctx, const char *path)
558 size_t len;
559 char *p1, *p2, *p;
560 char *path_out;
562 /* No absolute paths. */
563 while (IS_DIRECTORY_SEP(*path)) {
564 path++;
567 path_out = talloc_strdup(ctx, path);
568 if (!path_out) {
569 return NULL;
572 p1 = strchr_m(path_out, '*');
573 p2 = strchr_m(path_out, '?');
575 if (p1 || p2) {
576 if (p1 && p2) {
577 p = MIN(p1,p2);
578 } else if (!p1) {
579 p = p2;
580 } else {
581 p = p1;
583 *p = '\0';
585 /* Now go back to the start of this component. */
586 p1 = strrchr_m(path_out, '/');
587 p2 = strrchr_m(path_out, '\\');
588 p = MAX(p1,p2);
589 if (p) {
590 *p = '\0';
594 /* Strip any trailing separator */
596 len = strlen(path_out);
597 if ( (len > 0) && IS_DIRECTORY_SEP(path_out[len-1])) {
598 path_out[len-1] = '\0';
601 return path_out;
604 /****************************************************************************
605 ****************************************************************************/
607 static char *cli_dfs_make_full_path(TALLOC_CTX *ctx,
608 struct cli_state *cli,
609 const char *dir)
611 /* Ensure the extrapath doesn't start with a separator. */
612 while (IS_DIRECTORY_SEP(*dir)) {
613 dir++;
616 return talloc_asprintf(ctx, "\\%s\\%s\\%s",
617 cli->desthost, cli->share, dir);
620 /********************************************************************
621 check for dfs referral
622 ********************************************************************/
624 static bool cli_dfs_check_error( struct cli_state *cli, NTSTATUS status )
626 uint32 flgs2 = SVAL(cli->inbuf,smb_flg2);
628 /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */
630 if (!((flgs2&FLAGS2_32_BIT_ERROR_CODES) &&
631 (flgs2&FLAGS2_UNICODE_STRINGS)))
632 return false;
634 if (NT_STATUS_EQUAL(status, NT_STATUS(IVAL(cli->inbuf,smb_rcls))))
635 return true;
637 return false;
640 /********************************************************************
641 Get the dfs referral link.
642 ********************************************************************/
644 bool cli_dfs_get_referral(TALLOC_CTX *ctx,
645 struct cli_state *cli,
646 const char *path,
647 CLIENT_DFS_REFERRAL**refs,
648 size_t *num_refs,
649 uint16 *consumed)
651 unsigned int data_len = 0;
652 unsigned int param_len = 0;
653 uint16 setup = TRANSACT2_GET_DFS_REFERRAL;
654 char *param;
655 char *rparam=NULL, *rdata=NULL;
656 char *p;
657 char *endp;
658 size_t pathlen = 2*(strlen(path)+1);
659 uint16 num_referrals;
660 CLIENT_DFS_REFERRAL *referrals = NULL;
661 bool ret = false;
663 *num_refs = 0;
664 *refs = NULL;
666 param = SMB_MALLOC_ARRAY(char, 2+pathlen+2);
667 if (!param) {
668 return false;
670 SSVAL(param, 0, 0x03); /* max referral level */
671 p = &param[2];
673 p += clistr_push(cli, p, path, pathlen, STR_TERMINATE);
674 param_len = PTR_DIFF(p, param);
676 if (!cli_send_trans(cli, SMBtrans2,
677 NULL, /* name */
678 -1, 0, /* fid, flags */
679 &setup, 1, 0, /* setup, length, max */
680 param, param_len, 2, /* param, length, max */
681 NULL, 0, cli->max_xmit /* data, length, max */
682 )) {
683 SAFE_FREE(param);
684 return false;
687 SAFE_FREE(param);
689 if (!cli_receive_trans(cli, SMBtrans2,
690 &rparam, &param_len,
691 &rdata, &data_len)) {
692 return false;
695 if (data_len < 4) {
696 goto out;
699 endp = rdata + data_len;
701 *consumed = SVAL(rdata, 0);
702 num_referrals = SVAL(rdata, 2);
704 if (num_referrals != 0) {
705 uint16 ref_version;
706 uint16 ref_size;
707 int i;
708 uint16 node_offset;
710 referrals = TALLOC_ARRAY(ctx, CLIENT_DFS_REFERRAL,
711 num_referrals);
713 if (!referrals) {
714 goto out;
716 /* start at the referrals array */
718 p = rdata+8;
719 for (i=0; i<num_referrals && p < endp; i++) {
720 if (p + 18 > endp) {
721 goto out;
723 ref_version = SVAL(p, 0);
724 ref_size = SVAL(p, 2);
725 node_offset = SVAL(p, 16);
727 if (ref_version != 3) {
728 p += ref_size;
729 continue;
732 referrals[i].proximity = SVAL(p, 8);
733 referrals[i].ttl = SVAL(p, 10);
735 if (p + node_offset > endp) {
736 goto out;
738 clistr_pull_talloc(ctx, cli, &referrals[i].dfspath,
739 p+node_offset, -1,
740 STR_TERMINATE|STR_UNICODE );
742 if (!referrals[i].dfspath) {
743 goto out;
745 p += ref_size;
747 if (i < num_referrals) {
748 goto out;
752 ret = true;
754 *num_refs = num_referrals;
755 *refs = referrals;
757 out:
759 SAFE_FREE(rdata);
760 SAFE_FREE(rparam);
761 return ret;
764 /********************************************************************
765 ********************************************************************/
767 bool cli_resolve_path(TALLOC_CTX *ctx,
768 const char *mountpt,
769 struct cli_state *rootcli,
770 const char *path,
771 struct cli_state **targetcli,
772 char **pp_targetpath)
774 CLIENT_DFS_REFERRAL *refs = NULL;
775 size_t num_refs = 0;
776 uint16 consumed;
777 struct cli_state *cli_ipc = NULL;
778 char *dfs_path = NULL;
779 char *cleanpath = NULL;
780 char *extrapath = NULL;
781 int pathlen;
782 char *server = NULL;
783 char *share = NULL;
784 struct cli_state *newcli = NULL;
785 char *newpath = NULL;
786 char *newmount = NULL;
787 char *ppath = NULL;
788 SMB_STRUCT_STAT sbuf;
789 uint32 attributes;
791 if ( !rootcli || !path || !targetcli ) {
792 return false;
795 /* Don't do anything if this is not a DFS root. */
797 if ( !rootcli->dfsroot) {
798 *targetcli = rootcli;
799 *pp_targetpath = talloc_strdup(ctx, path);
800 if (!*pp_targetpath) {
801 return false;
803 return true;
806 *targetcli = NULL;
808 /* Send a trans2_query_path_info to check for a referral. */
810 cleanpath = clean_path(ctx, path);
811 if (!cleanpath) {
812 return false;
815 dfs_path = cli_dfs_make_full_path(ctx, rootcli, cleanpath);
816 if (!dfs_path) {
817 return false;
820 if (cli_qpathinfo_basic( rootcli, dfs_path, &sbuf, &attributes)) {
821 /* This is an ordinary path, just return it. */
822 *targetcli = rootcli;
823 *pp_targetpath = talloc_strdup(ctx, path);
824 if (!*pp_targetpath) {
825 return false;
827 goto done;
830 /* Special case where client asked for a path that does not exist */
832 if (cli_dfs_check_error(rootcli, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
833 *targetcli = rootcli;
834 *pp_targetpath = talloc_strdup(ctx, path);
835 if (!*pp_targetpath) {
836 return false;
838 goto done;
841 /* We got an error, check for DFS referral. */
843 if (!cli_dfs_check_error(rootcli, NT_STATUS_PATH_NOT_COVERED)) {
844 return false;
847 /* Check for the referral. */
849 if (!(cli_ipc = cli_cm_open(ctx, rootcli,
850 rootcli->desthost,
851 "IPC$", false,
852 (rootcli->trans_enc_state != NULL)))) {
853 return false;
856 if (!cli_dfs_get_referral(ctx, cli_ipc, dfs_path, &refs,
857 &num_refs, &consumed) || !num_refs) {
858 return false;
861 /* Just store the first referral for now. */
863 if (!refs[0].dfspath) {
864 return false;
866 split_dfs_path(ctx, refs[0].dfspath, &server, &share, &extrapath );
868 if (!server || !share) {
869 return false;
872 /* Make sure to recreate the original string including any wildcards. */
874 dfs_path = cli_dfs_make_full_path(ctx, rootcli, path);
875 if (!dfs_path) {
876 return false;
878 pathlen = strlen(dfs_path)*2;
879 consumed = MIN(pathlen, consumed);
880 *pp_targetpath = talloc_strdup(ctx, &dfs_path[consumed/2]);
881 if (!*pp_targetpath) {
882 return false;
884 dfs_path[consumed/2] = '\0';
887 * *pp_targetpath is now the unconsumed part of the path.
888 * dfs_path is now the consumed part of the path
889 * (in \server\share\path format).
892 /* Open the connection to the target server & share */
893 if ((*targetcli = cli_cm_open(ctx, rootcli,
894 server,
895 share,
896 false,
897 (rootcli->trans_enc_state != NULL))) == NULL) {
898 d_printf("Unable to follow dfs referral [\\%s\\%s]\n",
899 server, share );
900 return false;
903 if (extrapath && strlen(extrapath) > 0) {
904 *pp_targetpath = talloc_asprintf(ctx,
905 "%s%s",
906 extrapath,
907 *pp_targetpath);
908 if (!*pp_targetpath) {
909 return false;
913 /* parse out the consumed mount path */
914 /* trim off the \server\share\ */
916 ppath = dfs_path;
918 if (*ppath != '\\') {
919 d_printf("cli_resolve_path: "
920 "dfs_path (%s) not in correct format.\n",
921 dfs_path );
922 return false;
925 ppath++; /* Now pointing at start of server name. */
927 if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) {
928 return false;
931 ppath++; /* Now pointing at start of share name. */
933 if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) {
934 return false;
937 ppath++; /* Now pointing at path component. */
939 newmount = talloc_asprintf(ctx, "%s\\%s", mountpt, ppath );
940 if (!newmount) {
941 return false;
944 cli_cm_set_mntpoint(*targetcli, newmount);
946 /* Check for another dfs referral, note that we are not
947 checking for loops here. */
949 if (!strequal(*pp_targetpath, "\\") && !strequal(*pp_targetpath, "/")) {
950 if (cli_resolve_path(ctx,
951 newmount,
952 *targetcli,
953 *pp_targetpath,
954 &newcli,
955 &newpath)) {
957 * When cli_resolve_path returns true here it's always
958 * returning the complete path in newpath, so we're done
959 * here.
961 *targetcli = newcli;
962 *pp_targetpath = newpath;
963 return true;
967 done:
969 /* If returning true ensure we return a dfs root full path. */
970 if ((*targetcli)->dfsroot) {
971 dfs_path = talloc_strdup(ctx, *pp_targetpath);
972 if (!dfs_path) {
973 return false;
975 *pp_targetpath = cli_dfs_make_full_path(ctx, *targetcli, dfs_path);
978 return true;
981 /********************************************************************
982 ********************************************************************/
984 static bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
985 struct cli_state *cli,
986 const char *sharename,
987 char **pp_newserver,
988 char **pp_newshare,
989 bool force_encrypt,
990 const char *username,
991 const char *password,
992 const char *domain)
994 CLIENT_DFS_REFERRAL *refs = NULL;
995 size_t num_refs = 0;
996 uint16 consumed;
997 char *fullpath = NULL;
998 bool res;
999 uint16 cnum;
1000 char *newextrapath = NULL;
1002 if (!cli || !sharename) {
1003 return false;
1006 cnum = cli->cnum;
1008 /* special case. never check for a referral on the IPC$ share */
1010 if (strequal(sharename, "IPC$")) {
1011 return false;
1014 /* send a trans2_query_path_info to check for a referral */
1016 fullpath = talloc_asprintf(ctx, "\\%s\\%s", cli->desthost, sharename );
1017 if (!fullpath) {
1018 return false;
1021 /* check for the referral */
1023 if (!cli_send_tconX(cli, "IPC$", "IPC", NULL, 0)) {
1024 return false;
1027 if (force_encrypt) {
1028 NTSTATUS status = cli_cm_force_encryption(cli,
1029 username,
1030 password,
1031 lp_workgroup(),
1032 "IPC$");
1033 if (!NT_STATUS_IS_OK(status)) {
1034 return false;
1038 res = cli_dfs_get_referral(ctx, cli, fullpath, &refs, &num_refs, &consumed);
1040 if (!cli_tdis(cli)) {
1041 return false;
1044 cli->cnum = cnum;
1046 if (!res || !num_refs) {
1047 return false;
1050 if (!refs[0].dfspath) {
1051 return false;
1054 split_dfs_path(ctx, refs[0].dfspath, pp_newserver,
1055 pp_newshare, &newextrapath );
1057 if ((*pp_newserver == NULL) || (*pp_newshare == NULL)) {
1058 return false;
1061 /* check that this is not a self-referral */
1063 if (strequal(cli->desthost, *pp_newserver) &&
1064 strequal(sharename, *pp_newshare)) {
1065 return false;
1068 return true;