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/>.
24 /********************************************************************
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;
41 /* global state....globals reek! */
42 int max_protocol = PROTOCOL_NT1;
44 static struct cm_cred_struct {
52 static void cm_set_password(const char *newpass);
55 static int name_type = 0x20;
57 static struct sockaddr_storage dest_ss;
59 static struct client_connection *connections;
61 /********************************************************************
62 Return a connection to a server.
63 ********************************************************************/
65 static struct cli_state *do_connect(TALLOC_CTX *ctx,
70 struct cli_state *c = NULL;
71 struct nmb_name called, calling;
73 struct sockaddr_storage ss;
76 char *newserver, *newshare;
81 /* make a copy so we don't modify the global string 'service' */
82 servicename = talloc_strdup(ctx,share);
86 sharename = servicename;
87 if (*sharename == '\\') {
89 sharename = strchr_m(server,'\\');
101 make_nmb_name(&calling, global_myname(), 0x0);
102 make_nmb_name(&called , server, name_type);
109 /* have to open a new connection */
110 if (!(c=cli_initialise()) || (cli_set_port(c, port) != port)) {
111 d_printf("Connection to %s failed\n", server_n);
114 status = cli_connect(c, server_n, &ss);
115 if (!NT_STATUS_IS_OK(status)) {
116 d_printf("Connection to %s failed (Error %s)\n",
122 c->protocol = max_protocol;
123 c->use_kerberos = cm_creds.use_kerberos;
124 cli_setup_signing_state(c, cm_creds.signing_state);
126 if (!cli_session_request(c, &calling, &called)) {
128 d_printf("session request to %s failed (%s)\n",
129 called.name, cli_errstr(c));
132 if ((p=strchr_m(called.name, '.'))) {
136 if (strcmp(called.name, "*SMBSERVER")) {
137 make_nmb_name(&called , "*SMBSERVER", 0x20);
143 DEBUG(4,(" session request ok\n"));
145 if (!cli_negprot(c)) {
146 d_printf("protocol negotiation failed\n");
151 if (!cm_creds.got_pass) {
152 char *pass = getpass("Password: ");
154 cm_set_password(pass);
158 username = cm_creds.username ? cm_creds.username : "";
159 password = cm_creds.password ? cm_creds.password : "";
161 if (!NT_STATUS_IS_OK(cli_session_setup(c, username,
162 password, strlen(password),
163 password, strlen(password),
165 /* If a password was not supplied then
166 * try again with a null username. */
167 if (password[0] || !username[0] || cm_creds.use_kerberos ||
168 !NT_STATUS_IS_OK(cli_session_setup(c, "",
172 d_printf("session setup failed: %s\n", cli_errstr(c));
173 if (NT_STATUS_V(cli_nt_error(c)) ==
174 NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED))
175 d_printf("did you forget to run kinit?\n");
179 d_printf("Anonymous login successful\n");
182 if ( show_sessetup ) {
183 if (*c->server_domain) {
184 DEBUG(0,("Domain=[%s] OS=[%s] Server=[%s]\n",
185 c->server_domain,c->server_os,c->server_type));
186 } else if (*c->server_os || *c->server_type) {
187 DEBUG(0,("OS=[%s] Server=[%s]\n",
188 c->server_os,c->server_type));
191 DEBUG(4,(" session setup ok\n"));
193 /* here's the fun part....to support 'msdfs proxy' shares
194 (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL
195 here before trying to connect to the original share.
196 check_dfs_proxy() will fail if it is a normal share. */
198 if ((c->capabilities & CAP_DFS) &&
199 cli_check_msdfs_proxy(ctx, c, sharename,
200 &newserver, &newshare)) {
202 return do_connect(ctx, newserver, newshare, false);
205 /* must be a normal share */
207 if (!cli_send_tconX(c, sharename, "?????",
208 password, strlen(password)+1)) {
209 d_printf("tree connect failed: %s\n", cli_errstr(c));
214 DEBUG(4,(" tconx ok\n"));
218 /****************************************************************************
219 ****************************************************************************/
221 static void cli_cm_set_mntpoint(struct cli_state *c, const char *mnt)
223 struct client_connection *p;
226 for (p=connections,i=0; p; p=p->next,i++) {
227 if (strequal(p->cli->desthost, c->desthost) &&
228 strequal(p->cli->share, c->share)) {
234 char *name = clean_name(NULL, p->mount);
238 p->mount = talloc_strdup(p, name);
243 /****************************************************************************
244 ****************************************************************************/
246 const char *cli_cm_get_mntpoint(struct cli_state *c)
248 struct client_connection *p;
251 for (p=connections,i=0; p; p=p->next,i++) {
252 if (strequal(p->cli->desthost, c->desthost) &&
253 strequal(p->cli->share, c->share)) {
264 /********************************************************************
265 Add a new connection to the list
266 ********************************************************************/
268 static struct cli_state *cli_cm_connect(TALLOC_CTX *ctx,
269 struct cli_state *referring_cli,
274 struct client_connection *node;
276 /* NB This must be the null context here... JRA. */
277 node = TALLOC_ZERO_ARRAY(NULL, struct client_connection, 1);
282 node->cli = do_connect(ctx, server, share, show_hdr);
289 DLIST_ADD( connections, node );
291 cli_cm_set_mntpoint(node->cli, "");
293 if (referring_cli && referring_cli->posix_capabilities) {
295 uint32 caplow, caphigh;
296 if (cli_unix_extensions_version(node->cli, &major,
297 &minor, &caplow, &caphigh)) {
298 cli_set_unix_extensions_capabilities(node->cli,
307 /********************************************************************
308 Return a connection to a server.
309 ********************************************************************/
311 static struct cli_state *cli_cm_find(const char *server, const char *share)
313 struct client_connection *p;
315 for (p=connections; p; p=p->next) {
316 if ( strequal(server, p->cli->desthost) &&
317 strequal(share,p->cli->share)) {
325 /****************************************************************************
326 Open a client connection to a \\server\share. Set's the current *cli
327 global variable as a side-effect (but only if the connection is successful).
328 ****************************************************************************/
330 struct cli_state *cli_cm_open(TALLOC_CTX *ctx,
331 struct cli_state *referring_cli,
338 /* try to reuse an existing connection */
340 c = cli_cm_find(server, share);
342 c = cli_cm_connect(ctx, referring_cli, server, share, show_hdr);
348 /****************************************************************************
349 ****************************************************************************/
351 void cli_cm_shutdown(void)
353 struct client_connection *p, *x;
355 for (p=connections; p;) {
356 cli_shutdown(p->cli);
367 /****************************************************************************
368 ****************************************************************************/
370 void cli_cm_display(void)
372 struct client_connection *p;
375 for ( p=connections,i=0; p; p=p->next,i++ ) {
376 d_printf("%d:\tserver=%s, share=%s\n",
377 i, p->cli->desthost, p->cli->share );
381 /****************************************************************************
382 ****************************************************************************/
384 static void cm_set_password(const char *newpass)
386 SAFE_FREE(cm_creds.password);
387 cm_creds.password = SMB_STRDUP(newpass);
388 if (cm_creds.password) {
389 cm_creds.got_pass = true;
393 void cli_cm_set_credentials(void)
395 SAFE_FREE(cm_creds.username);
396 cm_creds.username = SMB_STRDUP(get_cmdline_auth_info_username());
398 if (get_cmdline_auth_info_got_pass()) {
399 cm_set_password(get_cmdline_auth_info_password());
402 cm_creds.use_kerberos = get_cmdline_auth_info_use_kerberos();
403 cm_creds.signing_state = get_cmdline_auth_info_signing_state();
406 /****************************************************************************
407 ****************************************************************************/
409 void cli_cm_set_port(int port_number)
414 /****************************************************************************
415 ****************************************************************************/
417 void cli_cm_set_dest_name_type(int type)
422 /****************************************************************************
423 ****************************************************************************/
425 void cli_cm_set_dest_ss(struct sockaddr_storage *pss)
431 /**********************************************************************
432 split a dfs path into the server, share name, and extrapath components
433 **********************************************************************/
435 static void split_dfs_path(TALLOC_CTX *ctx,
436 const char *nodepath,
446 *pp_extrapath = NULL;
448 path = talloc_strdup(ctx, nodepath);
453 if ( path[0] != '\\' ) {
457 p = strchr_m( path + 1, '\\' );
465 /* Look for any extra/deep path */
466 q = strchr_m(p, '\\');
470 *pp_extrapath = talloc_strdup(ctx, q);
472 *pp_extrapath = talloc_strdup(ctx, "");
475 *pp_share = talloc_strdup(ctx, p);
476 *pp_server = talloc_strdup(ctx, &path[1]);
479 /****************************************************************************
480 Return the original path truncated at the directory component before
481 the first wildcard character. Trust the caller to provide a NULL
483 ****************************************************************************/
485 static char *clean_path(TALLOC_CTX *ctx, const char *path)
491 /* No absolute paths. */
492 while (IS_DIRECTORY_SEP(*path)) {
496 path_out = talloc_strdup(ctx, path);
501 p1 = strchr_m(path_out, '*');
502 p2 = strchr_m(path_out, '?');
514 /* Now go back to the start of this component. */
515 p1 = strrchr_m(path_out, '/');
516 p2 = strrchr_m(path_out, '\\');
523 /* Strip any trailing separator */
525 len = strlen(path_out);
526 if ( (len > 0) && IS_DIRECTORY_SEP(path_out[len-1])) {
527 path_out[len-1] = '\0';
533 /****************************************************************************
534 ****************************************************************************/
536 static char *cli_dfs_make_full_path(TALLOC_CTX *ctx,
537 struct cli_state *cli,
540 /* Ensure the extrapath doesn't start with a separator. */
541 while (IS_DIRECTORY_SEP(*dir)) {
545 return talloc_asprintf(ctx, "\\%s\\%s\\%s",
546 cli->desthost, cli->share, dir);
549 /********************************************************************
550 check for dfs referral
551 ********************************************************************/
553 static bool cli_dfs_check_error( struct cli_state *cli, NTSTATUS status )
555 uint32 flgs2 = SVAL(cli->inbuf,smb_flg2);
557 /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */
559 if (!((flgs2&FLAGS2_32_BIT_ERROR_CODES) &&
560 (flgs2&FLAGS2_UNICODE_STRINGS)))
563 if (NT_STATUS_EQUAL(status, NT_STATUS(IVAL(cli->inbuf,smb_rcls))))
569 /********************************************************************
570 Get the dfs referral link.
571 ********************************************************************/
573 bool cli_dfs_get_referral(struct cli_state *cli,
575 CLIENT_DFS_REFERRAL**refs,
579 unsigned int data_len = 0;
580 unsigned int param_len = 0;
581 uint16 setup = TRANSACT2_GET_DFS_REFERRAL;
583 char *rparam=NULL, *rdata=NULL;
585 size_t pathlen = 2*(strlen(path)+1);
586 uint16 num_referrals;
587 CLIENT_DFS_REFERRAL *referrals = NULL;
589 memset(param, 0, sizeof(param));
590 SSVAL(param, 0, 0x03); /* max referral level */
593 p += clistr_push(cli, p, path, MIN(pathlen, sizeof(param)-2),
595 param_len = PTR_DIFF(p, param);
597 if (!cli_send_trans(cli, SMBtrans2,
599 -1, 0, /* fid, flags */
600 &setup, 1, 0, /* setup, length, max */
601 param, param_len, 2, /* param, length, max */
602 NULL, 0, cli->max_xmit /* data, length, max */
607 if (!cli_receive_trans(cli, SMBtrans2,
609 &rdata, &data_len)) {
613 *consumed = SVAL( rdata, 0 );
614 num_referrals = SVAL( rdata, 2 );
616 if ( num_referrals != 0 ) {
622 referrals = SMB_XMALLOC_ARRAY( CLIENT_DFS_REFERRAL,
625 /* start at the referrals array */
628 for ( i=0; i<num_referrals; i++ ) {
629 ref_version = SVAL( p, 0 );
630 ref_size = SVAL( p, 2 );
631 node_offset = SVAL( p, 16 );
633 if ( ref_version != 3 ) {
638 referrals[i].proximity = SVAL( p, 8 );
639 referrals[i].ttl = SVAL( p, 10 );
641 clistr_pull( cli, referrals[i].dfspath, p+node_offset,
642 sizeof(referrals[i].dfspath), -1,
643 STR_TERMINATE|STR_UNICODE );
649 *num_refs = num_referrals;
659 /********************************************************************
660 ********************************************************************/
662 bool cli_resolve_path(TALLOC_CTX *ctx,
664 struct cli_state *rootcli,
666 struct cli_state **targetcli,
667 char **pp_targetpath)
669 CLIENT_DFS_REFERRAL *refs = NULL;
672 struct cli_state *cli_ipc = NULL;
673 char *dfs_path = NULL;
674 char *cleanpath = NULL;
675 char *extrapath = NULL;
679 struct cli_state *newcli = NULL;
680 char *newpath = NULL;
681 char *newmount = NULL;
683 SMB_STRUCT_STAT sbuf;
686 if ( !rootcli || !path || !targetcli ) {
690 /* Don't do anything if this is not a DFS root. */
692 if ( !rootcli->dfsroot) {
693 *targetcli = rootcli;
694 *pp_targetpath = talloc_strdup(ctx, path);
695 if (!*pp_targetpath) {
703 /* Send a trans2_query_path_info to check for a referral. */
705 cleanpath = clean_path(ctx, path);
710 dfs_path = cli_dfs_make_full_path(ctx, rootcli, cleanpath);
715 if (cli_qpathinfo_basic( rootcli, dfs_path, &sbuf, &attributes)) {
716 /* This is an ordinary path, just return it. */
717 *targetcli = rootcli;
718 *pp_targetpath = talloc_strdup(ctx, path);
719 if (!*pp_targetpath) {
725 /* Special case where client asked for a path that does not exist */
727 if (cli_dfs_check_error(rootcli, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
728 *targetcli = rootcli;
729 *pp_targetpath = talloc_strdup(ctx, path);
730 if (!*pp_targetpath) {
736 /* We got an error, check for DFS referral. */
738 if (!cli_dfs_check_error(rootcli, NT_STATUS_PATH_NOT_COVERED)) {
742 /* Check for the referral. */
744 if (!(cli_ipc = cli_cm_open(ctx, rootcli,
745 rootcli->desthost, "IPC$", false))) {
749 if (!cli_dfs_get_referral(cli_ipc, dfs_path, &refs,
750 &num_refs, &consumed) || !num_refs) {
754 /* Just store the first referral for now. */
756 split_dfs_path(ctx, refs[0].dfspath, &server, &share, &extrapath );
759 if (!server || !share) {
763 /* Make sure to recreate the original string including any wildcards. */
765 dfs_path = cli_dfs_make_full_path(ctx, rootcli, path);
769 pathlen = strlen(dfs_path)*2;
770 consumed = MIN(pathlen, consumed);
771 *pp_targetpath = talloc_strdup(ctx, &dfs_path[consumed/2]);
772 if (!*pp_targetpath) {
775 dfs_path[consumed/2] = '\0';
778 * *pp_targetpath is now the unconsumed part of the path.
779 * dfs_path is now the consumed part of the path
780 * (in \server\share\path format).
783 /* Open the connection to the target server & share */
784 if ((*targetcli = cli_cm_open(ctx, rootcli,
785 server, share, false)) == NULL) {
786 d_printf("Unable to follow dfs referral [\\%s\\%s]\n",
791 if (extrapath && strlen(extrapath) > 0) {
792 *pp_targetpath = talloc_asprintf(ctx,
796 if (!*pp_targetpath) {
801 /* parse out the consumed mount path */
802 /* trim off the \server\share\ */
806 if (*ppath != '\\') {
807 d_printf("cli_resolve_path: "
808 "dfs_path (%s) not in correct format.\n",
813 ppath++; /* Now pointing at start of server name. */
815 if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) {
819 ppath++; /* Now pointing at start of share name. */
821 if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) {
825 ppath++; /* Now pointing at path component. */
827 newmount = talloc_asprintf(ctx, "%s\\%s", mountpt, ppath );
832 cli_cm_set_mntpoint(*targetcli, newmount);
834 /* Check for another dfs referral, note that we are not
835 checking for loops here. */
837 if (!strequal(*pp_targetpath, "\\") && !strequal(*pp_targetpath, "/")) {
838 if (cli_resolve_path(ctx,
845 * When cli_resolve_path returns true here it's always
846 * returning the complete path in newpath, so we're done
850 *pp_targetpath = newpath;
857 /* If returning true ensure we return a dfs root full path. */
858 if ((*targetcli)->dfsroot) {
859 dfs_path = talloc_strdup(ctx, *pp_targetpath);
863 *pp_targetpath = cli_dfs_make_full_path(ctx, *targetcli, dfs_path);
869 /********************************************************************
870 ********************************************************************/
872 bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
873 struct cli_state *cli,
874 const char *sharename,
878 CLIENT_DFS_REFERRAL *refs = NULL;
881 char *fullpath = NULL;
884 char *newextrapath = NULL;
886 if (!cli || !sharename) {
892 /* special case. never check for a referral on the IPC$ share */
894 if (strequal(sharename, "IPC$")) {
898 /* send a trans2_query_path_info to check for a referral */
900 fullpath = talloc_asprintf(ctx, "\\%s\\%s", cli->desthost, sharename );
905 /* check for the referral */
907 if (!cli_send_tconX(cli, "IPC$", "IPC", NULL, 0)) {
911 res = cli_dfs_get_referral(cli, fullpath, &refs, &num_refs, &consumed);
913 if (!cli_tdis(cli)) {
920 if (!res || !num_refs) {
925 split_dfs_path(ctx, refs[0].dfspath, pp_newserver,
926 pp_newshare, &newextrapath );
930 if (!pp_newserver || !pp_newshare) {
934 /* check that this is not a self-referral */
936 if (strequal(cli->desthost, *pp_newserver) &&
937 strequal(sharename, *pp_newshare)) {