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,
273 struct client_connection *node;
275 /* NB This must be the null context here... JRA. */
276 node = TALLOC_ZERO_ARRAY(NULL, struct client_connection, 1);
281 node->cli = do_connect(ctx, server, share, show_hdr);
288 DLIST_ADD( connections, node );
290 cli_cm_set_mntpoint(node->cli, "");
296 /********************************************************************
297 Return a connection to a server.
298 ********************************************************************/
300 static struct cli_state *cli_cm_find(const char *server, const char *share)
302 struct client_connection *p;
304 for (p=connections; p; p=p->next) {
305 if ( strequal(server, p->cli->desthost) &&
306 strequal(share,p->cli->share)) {
314 /****************************************************************************
315 Open a client connection to a \\server\share. Set's the current *cli
316 global variable as a side-effect (but only if the connection is successful).
317 ****************************************************************************/
319 struct cli_state *cli_cm_open(TALLOC_CTX *ctx,
326 /* try to reuse an existing connection */
328 c = cli_cm_find(server, share);
330 c = cli_cm_connect(ctx, server, share, show_hdr);
336 /****************************************************************************
337 ****************************************************************************/
339 void cli_cm_shutdown(void)
341 struct client_connection *p, *x;
343 for (p=connections; p;) {
344 cli_shutdown(p->cli);
355 /****************************************************************************
356 ****************************************************************************/
358 void cli_cm_display(void)
360 struct client_connection *p;
363 for ( p=connections,i=0; p; p=p->next,i++ ) {
364 d_printf("%d:\tserver=%s, share=%s\n",
365 i, p->cli->desthost, p->cli->share );
369 /****************************************************************************
370 ****************************************************************************/
372 static void cm_set_password(const char *newpass)
374 SAFE_FREE(cm_creds.password);
375 cm_creds.password = SMB_STRDUP(newpass);
376 if (cm_creds.password) {
377 cm_creds.got_pass = true;
381 void cli_cm_set_credentials(struct user_auth_info *user)
383 SAFE_FREE(cm_creds.username);
384 cm_creds.username = SMB_STRDUP(user->username);
386 if (user->got_pass) {
387 cm_set_password(user->password);
390 cm_creds.use_kerberos = user->use_kerberos;
391 cm_creds.signing_state = user->signing_state;
394 /****************************************************************************
395 ****************************************************************************/
397 void cli_cm_set_port(int port_number)
402 /****************************************************************************
403 ****************************************************************************/
405 void cli_cm_set_dest_name_type(int type)
410 /****************************************************************************
411 ****************************************************************************/
413 void cli_cm_set_dest_ss(struct sockaddr_storage *pss)
419 /**********************************************************************
420 split a dfs path into the server, share name, and extrapath components
421 **********************************************************************/
423 static void split_dfs_path(TALLOC_CTX *ctx,
424 const char *nodepath,
434 *pp_extrapath = NULL;
436 path = talloc_strdup(ctx, nodepath);
441 if ( path[0] != '\\' ) {
445 p = strchr_m( path + 1, '\\' );
453 /* Look for any extra/deep path */
454 q = strchr_m(p, '\\');
458 *pp_extrapath = talloc_strdup(ctx, q);
460 *pp_extrapath = talloc_strdup(ctx, "");
463 *pp_share = talloc_strdup(ctx, p);
464 *pp_server = talloc_strdup(ctx, &path[1]);
467 /****************************************************************************
468 Return the original path truncated at the directory component before
469 the first wildcard character. Trust the caller to provide a NULL
471 ****************************************************************************/
473 static char *clean_path(TALLOC_CTX *ctx, const char *path)
479 /* No absolute paths. */
480 while (IS_DIRECTORY_SEP(*path)) {
484 path_out = talloc_strdup(ctx, path);
489 p1 = strchr_m(path_out, '*');
490 p2 = strchr_m(path_out, '?');
502 /* Now go back to the start of this component. */
503 p1 = strrchr_m(path_out, '/');
504 p2 = strrchr_m(path_out, '\\');
511 /* Strip any trailing separator */
513 len = strlen(path_out);
514 if ( (len > 0) && IS_DIRECTORY_SEP(path_out[len-1])) {
515 path_out[len-1] = '\0';
521 /****************************************************************************
522 ****************************************************************************/
524 static char *cli_dfs_make_full_path(TALLOC_CTX *ctx,
525 struct cli_state *cli,
528 /* Ensure the extrapath doesn't start with a separator. */
529 while (IS_DIRECTORY_SEP(*dir)) {
533 return talloc_asprintf(ctx, "\\%s\\%s\\%s",
534 cli->desthost, cli->share, dir);
537 /********************************************************************
538 check for dfs referral
539 ********************************************************************/
541 static bool cli_dfs_check_error( struct cli_state *cli, NTSTATUS status )
543 uint32 flgs2 = SVAL(cli->inbuf,smb_flg2);
545 /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */
547 if (!((flgs2&FLAGS2_32_BIT_ERROR_CODES) &&
548 (flgs2&FLAGS2_UNICODE_STRINGS)))
551 if (NT_STATUS_EQUAL(status, NT_STATUS(IVAL(cli->inbuf,smb_rcls))))
557 /********************************************************************
558 Get the dfs referral link.
559 ********************************************************************/
561 bool cli_dfs_get_referral(struct cli_state *cli,
563 CLIENT_DFS_REFERRAL**refs,
567 unsigned int data_len = 0;
568 unsigned int param_len = 0;
569 uint16 setup = TRANSACT2_GET_DFS_REFERRAL;
571 char *rparam=NULL, *rdata=NULL;
573 size_t pathlen = 2*(strlen(path)+1);
574 uint16 num_referrals;
575 CLIENT_DFS_REFERRAL *referrals = NULL;
577 memset(param, 0, sizeof(param));
578 SSVAL(param, 0, 0x03); /* max referral level */
581 p += clistr_push(cli, p, path, MIN(pathlen, sizeof(param)-2),
583 param_len = PTR_DIFF(p, param);
585 if (!cli_send_trans(cli, SMBtrans2,
587 -1, 0, /* fid, flags */
588 &setup, 1, 0, /* setup, length, max */
589 param, param_len, 2, /* param, length, max */
590 NULL, 0, cli->max_xmit /* data, length, max */
595 if (!cli_receive_trans(cli, SMBtrans2,
597 &rdata, &data_len)) {
601 *consumed = SVAL( rdata, 0 );
602 num_referrals = SVAL( rdata, 2 );
604 if ( num_referrals != 0 ) {
610 referrals = SMB_XMALLOC_ARRAY( CLIENT_DFS_REFERRAL,
613 /* start at the referrals array */
616 for ( i=0; i<num_referrals; i++ ) {
617 ref_version = SVAL( p, 0 );
618 ref_size = SVAL( p, 2 );
619 node_offset = SVAL( p, 16 );
621 if ( ref_version != 3 ) {
626 referrals[i].proximity = SVAL( p, 8 );
627 referrals[i].ttl = SVAL( p, 10 );
629 clistr_pull( cli, referrals[i].dfspath, p+node_offset,
630 sizeof(referrals[i].dfspath), -1,
631 STR_TERMINATE|STR_UNICODE );
637 *num_refs = num_referrals;
647 /********************************************************************
648 ********************************************************************/
650 bool cli_resolve_path(TALLOC_CTX *ctx,
652 struct cli_state *rootcli,
654 struct cli_state **targetcli,
655 char **pp_targetpath)
657 CLIENT_DFS_REFERRAL *refs = NULL;
660 struct cli_state *cli_ipc = NULL;
661 char *dfs_path = NULL;
662 char *cleanpath = NULL;
663 char *extrapath = NULL;
667 struct cli_state *newcli = NULL;
668 char *newpath = NULL;
669 char *newmount = NULL;
671 SMB_STRUCT_STAT sbuf;
674 if ( !rootcli || !path || !targetcli ) {
678 /* Don't do anything if this is not a DFS root. */
680 if ( !rootcli->dfsroot) {
681 *targetcli = rootcli;
682 *pp_targetpath = talloc_strdup(ctx, path);
683 if (!*pp_targetpath) {
691 /* Send a trans2_query_path_info to check for a referral. */
693 cleanpath = clean_path(ctx, path);
698 dfs_path = cli_dfs_make_full_path(ctx, rootcli, cleanpath);
703 if (cli_qpathinfo_basic( rootcli, dfs_path, &sbuf, &attributes)) {
704 /* This is an ordinary path, just return it. */
705 *targetcli = rootcli;
706 *pp_targetpath = talloc_strdup(ctx, path);
707 if (!*pp_targetpath) {
713 /* Special case where client asked for a path that does not exist */
715 if (cli_dfs_check_error(rootcli, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
716 *targetcli = rootcli;
717 *pp_targetpath = talloc_strdup(ctx, path);
718 if (!*pp_targetpath) {
724 /* We got an error, check for DFS referral. */
726 if (!cli_dfs_check_error(rootcli, NT_STATUS_PATH_NOT_COVERED)) {
730 /* Check for the referral. */
732 if (!(cli_ipc = cli_cm_open(ctx, rootcli->desthost, "IPC$", false))) {
736 if (!cli_dfs_get_referral(cli_ipc, dfs_path, &refs,
737 &num_refs, &consumed) || !num_refs) {
741 /* Just store the first referral for now. */
743 split_dfs_path(ctx, refs[0].dfspath, &server, &share, &extrapath );
746 if (!server || !share) {
750 /* Make sure to recreate the original string including any wildcards. */
752 dfs_path = cli_dfs_make_full_path(ctx, rootcli, path);
756 pathlen = strlen(dfs_path)*2;
757 consumed = MIN(pathlen, consumed);
758 *pp_targetpath = talloc_strdup(ctx, &dfs_path[consumed/2]);
759 if (!*pp_targetpath) {
762 dfs_path[consumed/2] = '\0';
765 * *pp_targetpath is now the unconsumed part of the path.
766 * dfs_path is now the consumed part of the path
767 * (in \server\share\path format).
770 /* Open the connection to the target server & share */
771 if ((*targetcli = cli_cm_open(ctx, server, share, false)) == NULL) {
772 d_printf("Unable to follow dfs referral [\\%s\\%s]\n",
777 if (extrapath && strlen(extrapath) > 0) {
778 *pp_targetpath = talloc_asprintf(ctx,
782 if (!*pp_targetpath) {
787 /* parse out the consumed mount path */
788 /* trim off the \server\share\ */
792 if (*ppath != '\\') {
793 d_printf("cli_resolve_path: "
794 "dfs_path (%s) not in correct format.\n",
799 ppath++; /* Now pointing at start of server name. */
801 if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) {
805 ppath++; /* Now pointing at start of share name. */
807 if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) {
811 ppath++; /* Now pointing at path component. */
813 newmount = talloc_asprintf(ctx, "%s\\%s", mountpt, ppath );
818 cli_cm_set_mntpoint(*targetcli, newmount);
820 /* Check for another dfs referral, note that we are not
821 checking for loops here. */
823 if (!strequal(*pp_targetpath, "\\") && !strequal(*pp_targetpath, "/")) {
824 if (cli_resolve_path(ctx,
831 * When cli_resolve_path returns true here it's always
832 * returning the complete path in newpath, so we're done
836 *pp_targetpath = newpath;
843 /* If returning true ensure we return a dfs root full path. */
844 if ((*targetcli)->dfsroot) {
845 dfs_path = talloc_strdup(ctx, *pp_targetpath);
849 *pp_targetpath = cli_dfs_make_full_path(ctx, *targetcli, dfs_path);
855 /********************************************************************
856 Temporary hack - remove when pstring is dead. JRA.
857 ********************************************************************/
859 bool cli_resolve_path_pstring( const char *mountpt,
860 struct cli_state *rootcli,
862 struct cli_state **targetcli,
866 TALLOC_CTX *ctx = talloc_stackframe();
867 bool ret = cli_resolve_path(ctx,
874 pstrcpy(targetpath, tpath);
879 /********************************************************************
880 ********************************************************************/
882 bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
883 struct cli_state *cli,
884 const char *sharename,
888 CLIENT_DFS_REFERRAL *refs = NULL;
891 char *fullpath = NULL;
894 char *newextrapath = NULL;
896 if (!cli || !sharename) {
902 /* special case. never check for a referral on the IPC$ share */
904 if (strequal(sharename, "IPC$")) {
908 /* send a trans2_query_path_info to check for a referral */
910 fullpath = talloc_asprintf(ctx, "\\%s\\%s", cli->desthost, sharename );
915 /* check for the referral */
917 if (!cli_send_tconX(cli, "IPC$", "IPC", NULL, 0)) {
921 res = cli_dfs_get_referral(cli, fullpath, &refs, &num_refs, &consumed);
923 if (!cli_tdis(cli)) {
930 if (!res || !num_refs) {
935 split_dfs_path(ctx, refs[0].dfspath, pp_newserver,
936 pp_newshare, &newextrapath );
940 if (!pp_newserver || !pp_newshare) {
944 /* check that this is not a self-referral */
946 if (strequal(cli->desthost, *pp_newserver) &&
947 strequal(sharename, *pp_newshare)) {