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 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20*/ 21 22#define NO_SYSLOG 23 24#include "includes.h" 25 26 27struct client_connection { 28 struct client_connection *prev, *next; 29 struct cli_state *cli; 30 pstring mount; 31}; 32 33/* global state....globals reek! */ 34 35static pstring username; 36static pstring password; 37static BOOL use_kerberos; 38static BOOL got_pass; 39static int signing_state; 40int max_protocol = PROTOCOL_NT1; 41 42static int port; 43static int name_type = 0x20; 44static BOOL have_ip; 45static struct in_addr dest_ip; 46 47static struct client_connection *connections; 48 49/******************************************************************** 50 Return a connection to a server. 51********************************************************************/ 52 53static struct cli_state *do_connect( const char *server, const char *share, 54 BOOL show_sessetup ) 55{ 56 struct cli_state *c; 57 struct nmb_name called, calling; 58 const char *server_n; 59 struct in_addr ip; 60 pstring servicename; 61 char *sharename; 62 63 /* make a copy so we don't modify the global string 'service' */ 64 pstrcpy(servicename, share); 65 sharename = servicename; 66 if (*sharename == '\\') { 67 server = sharename+2; 68 sharename = strchr_m(server,'\\'); 69 if (!sharename) return NULL; 70 *sharename = 0; 71 sharename++; 72 } 73 74 server_n = server; 75 76 zero_ip(&ip); 77 78 make_nmb_name(&calling, global_myname(), 0x0); 79 make_nmb_name(&called , server, name_type); 80 81 again: 82 zero_ip(&ip); 83 if (have_ip) 84 ip = dest_ip; 85 86 /* have to open a new connection */ 87 if (!(c=cli_initialise(NULL)) || (cli_set_port(c, port) != port) || 88 !cli_connect(c, server_n, &ip)) { 89 d_printf("Connection to %s failed\n", server_n); 90 return NULL; 91 } 92 93 c->protocol = max_protocol; 94 c->use_kerberos = use_kerberos; 95 cli_setup_signing_state(c, signing_state); 96 97 98 if (!cli_session_request(c, &calling, &called)) { 99 char *p; 100 d_printf("session request to %s failed (%s)\n", 101 called.name, cli_errstr(c)); 102 cli_shutdown(c); 103 if ((p=strchr_m(called.name, '.'))) { 104 *p = 0; 105 goto again; 106 } 107 if (strcmp(called.name, "*SMBSERVER")) { 108 make_nmb_name(&called , "*SMBSERVER", 0x20); 109 goto again; 110 } 111 return NULL; 112 } 113 114 DEBUG(4,(" session request ok\n")); 115 116 if (!cli_negprot(c)) { 117 d_printf("protocol negotiation failed\n"); 118 cli_shutdown(c); 119 return NULL; 120 } 121 122 if (!got_pass) { 123 char *pass = getpass("Password: "); 124 if (pass) { 125 pstrcpy(password, pass); 126 got_pass = 1; 127 } 128 } 129 130 if (!cli_session_setup(c, username, 131 password, strlen(password), 132 password, strlen(password), 133 lp_workgroup())) { 134 /* if a password was not supplied then try again with a null username */ 135 if (password[0] || !username[0] || use_kerberos || 136 !cli_session_setup(c, "", "", 0, "", 0, lp_workgroup())) { 137 d_printf("session setup failed: %s\n", cli_errstr(c)); 138 if (NT_STATUS_V(cli_nt_error(c)) == 139 NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED)) 140 d_printf("did you forget to run kinit?\n"); 141 cli_shutdown(c); 142 return NULL; 143 } 144 d_printf("Anonymous login successful\n"); 145 } 146 147 if ( show_sessetup ) { 148 if (*c->server_domain) { 149 DEBUG(0,("Domain=[%s] OS=[%s] Server=[%s]\n", 150 c->server_domain,c->server_os,c->server_type)); 151 } else if (*c->server_os || *c->server_type){ 152 DEBUG(0,("OS=[%s] Server=[%s]\n", 153 c->server_os,c->server_type)); 154 } 155 } 156 DEBUG(4,(" session setup ok\n")); 157 158 if (!cli_send_tconX(c, sharename, "?????", 159 password, strlen(password)+1)) { 160 d_printf("tree connect failed: %s\n", cli_errstr(c)); 161 cli_shutdown(c); 162 return NULL; 163 } 164 165 DEBUG(4,(" tconx ok\n")); 166 167 return c; 168} 169 170/**************************************************************************** 171****************************************************************************/ 172 173static void cli_cm_set_mntpoint( struct cli_state *c, const char *mnt ) 174{ 175 struct client_connection *p; 176 int i; 177 178 for ( p=connections,i=0; p; p=p->next,i++ ) { 179 if ( strequal(p->cli->desthost, c->desthost) && strequal(p->cli->share, c->share) ) 180 break; 181 } 182 183 if ( p ) { 184 pstrcpy( p->mount, mnt ); 185 dos_clean_name( p->mount ); 186 } 187} 188 189/**************************************************************************** 190****************************************************************************/ 191 192const char * cli_cm_get_mntpoint( struct cli_state *c ) 193{ 194 struct client_connection *p; 195 int i; 196 197 for ( p=connections,i=0; p; p=p->next,i++ ) { 198 if ( strequal(p->cli->desthost, c->desthost) && strequal(p->cli->share, c->share) ) 199 break; 200 } 201 202 if ( p ) 203 return p->mount; 204 205 return NULL; 206} 207 208/******************************************************************** 209 Add a new connection to the list 210********************************************************************/ 211 212static struct cli_state* cli_cm_connect( const char *server, const char *share, 213 BOOL show_hdr ) 214{ 215 struct client_connection *node; 216 217 node = SMB_XMALLOC_P( struct client_connection ); 218 219 node->cli = do_connect( server, share, show_hdr ); 220 221 if ( !node->cli ) { 222 SAFE_FREE( node ); 223 return NULL; 224 } 225 226 DLIST_ADD( connections, node ); 227 228 cli_cm_set_mntpoint( node->cli, "" ); 229 230 return node->cli; 231 232} 233 234/******************************************************************** 235 Return a connection to a server. 236********************************************************************/ 237 238static struct cli_state* cli_cm_find( const char *server, const char *share ) 239{ 240 struct client_connection *p; 241 242 for ( p=connections; p; p=p->next ) { 243 if ( strequal(server, p->cli->desthost) && strequal(share,p->cli->share) ) 244 return p->cli; 245 } 246 247 return NULL; 248} 249 250/**************************************************************************** 251 open a client connection to a \\server\share. Set's the current *cli 252 global variable as a side-effect (but only if the connection is successful). 253****************************************************************************/ 254 255struct cli_state* cli_cm_open( const char *server, const char *share, BOOL show_hdr ) 256{ 257 struct cli_state *c; 258 259 /* try to reuse an existing connection */ 260 261 c = cli_cm_find( server, share ); 262 263 if ( !c ) 264 c = cli_cm_connect( server, share, show_hdr ); 265 266 return c; 267} 268 269/**************************************************************************** 270****************************************************************************/ 271 272void cli_cm_shutdown( void ) 273{ 274 275 struct client_connection *p, *x; 276 277 for ( p=connections; p; ) { 278 cli_shutdown( p->cli ); 279 x = p; 280 p = p->next; 281 282 SAFE_FREE( x ); 283 } 284 285 connections = NULL; 286 287 return; 288} 289 290/**************************************************************************** 291****************************************************************************/ 292 293void cli_cm_display(void) 294{ 295 struct client_connection *p; 296 int i; 297 298 for ( p=connections,i=0; p; p=p->next,i++ ) { 299 d_printf("%d:\tserver=%s, share=%s\n", 300 i, p->cli->desthost, p->cli->share ); 301 } 302} 303 304/**************************************************************************** 305****************************************************************************/ 306 307void cli_cm_set_credentials( struct user_auth_info *user ) 308{ 309 pstrcpy( username, user->username ); 310 311 if ( user->got_pass ) { 312 pstrcpy( password, user->password ); 313 got_pass = True; 314 } 315 316 use_kerberos = user->use_kerberos; 317 signing_state = user->signing_state; 318} 319 320/**************************************************************************** 321****************************************************************************/ 322 323void cli_cm_set_port( int port_number ) 324{ 325 port = port_number; 326} 327 328/**************************************************************************** 329****************************************************************************/ 330 331void cli_cm_set_dest_name_type( int type ) 332{ 333 name_type = type; 334} 335 336/**************************************************************************** 337****************************************************************************/ 338 339void cli_cm_set_dest_ip(struct in_addr ip ) 340{ 341 dest_ip = ip; 342 have_ip = True; 343} 344 345/******************************************************************** 346 split a dfs path into the server and share name components 347********************************************************************/ 348 349static void split_dfs_path( const char *nodepath, fstring server, fstring share ) 350{ 351 char *p; 352 pstring path; 353 354 pstrcpy( path, nodepath ); 355 356 if ( path[0] != '\\' ) 357 return; 358 359 p = strrchr_m( path, '\\' ); 360 361 if ( !p ) 362 return; 363 364 *p = '\0'; 365 p++; 366 367 fstrcpy( share, p ); 368 fstrcpy( server, &path[1] ); 369} 370 371/**************************************************************************** 372 return the original path truncated at the first wildcard character 373 (also strips trailing \'s). Trust the caller to provide a NULL 374 terminated string 375****************************************************************************/ 376 377static void clean_path( pstring clean, const char *path ) 378{ 379 int len; 380 char *p; 381 pstring newpath; 382 383 pstrcpy( newpath, path ); 384 p = newpath; 385 386 while ( p ) { 387 /* first check for '*' */ 388 389 p = strrchr_m( newpath, '*' ); 390 if ( p ) { 391 *p = '\0'; 392 p = newpath; 393 continue; 394 } 395 396 /* first check for '?' */ 397 398 p = strrchr_m( newpath, '?' ); 399 if ( p ) { 400 *p = '\0'; 401 p = newpath; 402 } 403 } 404 405 /* strip a trailing backslash */ 406 407 len = strlen( newpath ); 408 if ( newpath[len-1] == '\\' ) 409 newpath[len-1] = '\0'; 410 411 pstrcpy( clean, newpath ); 412} 413 414/**************************************************************************** 415****************************************************************************/ 416 417static BOOL make_full_path( pstring path, const char *server, const char *share, 418 const char *dir ) 419{ 420 pstring servicename; 421 char *sharename; 422 const char *directory; 423 424 425 /* make a copy so we don't modify the global string 'service' */ 426 427 pstrcpy(servicename, share); 428 sharename = servicename; 429 430 if (*sharename == '\\') { 431 432 server = sharename+2; 433 sharename = strchr_m(server,'\\'); 434 435 if (!sharename) 436 return False; 437 438 *sharename = 0; 439 sharename++; 440 } 441 442 directory = dir; 443 if ( *directory == '\\' ) 444 directory++; 445 446 pstr_sprintf( path, "\\%s\\%s\\%s", server, sharename, directory ); 447 448 return True; 449} 450 451/******************************************************************** 452 check for dfs referral 453********************************************************************/ 454 455static BOOL cli_dfs_check_error( struct cli_state *cli ) 456{ 457 uint32 flgs2 = SVAL(cli->inbuf,smb_flg2); 458 459 /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */ 460 461 if ( !( (flgs2&FLAGS2_32_BIT_ERROR_CODES) && (flgs2&FLAGS2_UNICODE_STRINGS) ) ) 462 return False; 463 464 if ( NT_STATUS_EQUAL( NT_STATUS_PATH_NOT_COVERED, NT_STATUS(IVAL(cli->inbuf,smb_rcls)) ) ) 465 return True; 466 467 return False; 468} 469 470/******************************************************************** 471 get the dfs referral link 472********************************************************************/ 473 474BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, 475 CLIENT_DFS_REFERRAL**refs, size_t *num_refs, 476 uint16 *consumed) 477{ 478 unsigned int data_len = 0; 479 unsigned int param_len = 0; 480 uint16 setup = TRANSACT2_GET_DFS_REFERRAL; 481 char param[sizeof(pstring)+2]; 482 pstring data; 483 char *rparam=NULL, *rdata=NULL; 484 char *p; 485 size_t pathlen = 2*(strlen(path)+1); 486 uint16 num_referrals; 487 CLIENT_DFS_REFERRAL *referrals = NULL; 488 489 memset(param, 0, sizeof(param)); 490 SSVAL(param, 0, 0x03); /* max referral level */ 491 p = ¶m[2]; 492 493 p += clistr_push(cli, p, path, MIN(pathlen, sizeof(param)-2), STR_TERMINATE); 494 param_len = PTR_DIFF(p, param); 495 496 if (!cli_send_trans(cli, SMBtrans2, 497 NULL, /* name */ 498 -1, 0, /* fid, flags */ 499 &setup, 1, 0, /* setup, length, max */ 500 param, param_len, 2, /* param, length, max */ 501 (char *)&data, data_len, cli->max_xmit /* data, length, max */ 502 )) { 503 return False; 504 } 505 506 if (!cli_receive_trans(cli, SMBtrans2, 507 &rparam, ¶m_len, 508 &rdata, &data_len)) { 509 return False; 510 } 511 512 *consumed = SVAL( rdata, 0 ); 513 num_referrals = SVAL( rdata, 2 ); 514 515 if ( num_referrals != 0 ) { 516 uint16 ref_version; 517 uint16 ref_size; 518 int i; 519 uint16 node_offset; 520 521 522 referrals = SMB_XMALLOC_ARRAY( CLIENT_DFS_REFERRAL, num_referrals ); 523 524 /* start at the referrals array */ 525 526 p = rdata+8; 527 for ( i=0; i<num_referrals; i++ ) { 528 ref_version = SVAL( p, 0 ); 529 ref_size = SVAL( p, 2 ); 530 node_offset = SVAL( p, 16 ); 531 532 if ( ref_version != 3 ) { 533 p += ref_size; 534 continue; 535 } 536 537 referrals[i].proximity = SVAL( p, 8 ); 538 referrals[i].ttl = SVAL( p, 10 ); 539 540 clistr_pull( cli, referrals[i].dfspath, p+node_offset, 541 sizeof(referrals[i].dfspath), -1, STR_TERMINATE|STR_UNICODE ); 542 543 p += ref_size; 544 } 545 546 } 547 548 *num_refs = num_referrals; 549 *refs = referrals; 550 551 SAFE_FREE(rdata); 552 SAFE_FREE(rparam); 553 554 return True; 555} 556 557/******************************************************************** 558********************************************************************/ 559 560BOOL cli_resolve_path( const char *mountpt, struct cli_state *rootcli, const char *path, 561 struct cli_state **targetcli, pstring targetpath ) 562{ 563 CLIENT_DFS_REFERRAL *refs = NULL; 564 size_t num_refs; 565 uint16 consumed; 566 struct cli_state *cli_ipc; 567 pstring fullpath, cleanpath; 568 int pathlen; 569 fstring server, share; 570 struct cli_state *newcli; 571 pstring newpath; 572 pstring newmount; 573 char *ppath; 574 575 SMB_STRUCT_STAT sbuf; 576 uint32 attributes; 577 578 *targetcli = NULL; 579 580 if ( !rootcli || !path || !targetcli ) 581 return False; 582 583 /* send a trans2_query_path_info to check for a referral */ 584 585 clean_path( cleanpath, path ); 586 make_full_path( fullpath, rootcli->desthost, rootcli->share, cleanpath ); 587 588 /* don't bother continuing if this is not a dfs root */ 589 590 if ( !rootcli->dfsroot || cli_qpathinfo_basic( rootcli, cleanpath, &sbuf, &attributes ) ) { 591 *targetcli = rootcli; 592 pstrcpy( targetpath, path ); 593 return True; 594 } 595 596 /* we got an error, check for DFS referral */ 597 598 if ( !cli_dfs_check_error(rootcli) ) 599 return False; 600 601 /* check for the referral */ 602 603 if ( !(cli_ipc = cli_cm_open( rootcli->desthost, "IPC$", False )) ) 604 return False; 605 606 if ( !cli_dfs_get_referral(cli_ipc, fullpath, &refs, &num_refs, &consumed) 607 || !num_refs ) 608 { 609 return False; 610 } 611 612 /* just store the first referral for now 613 Make sure to recreate the original string including any wildcards */ 614 615 make_full_path( fullpath, rootcli->desthost, rootcli->share, path ); 616 pathlen = strlen( fullpath )*2; 617 consumed = MIN(pathlen, consumed ); 618 pstrcpy( targetpath, &fullpath[consumed/2] ); 619 620 split_dfs_path( refs[0].dfspath, server, share ); 621 SAFE_FREE( refs ); 622 623 /* open the connection to the target path */ 624 625 if ( (*targetcli = cli_cm_open(server, share, False)) == NULL ) { 626 d_printf("Unable to follow dfs referral [//%s/%s]\n", 627 server, share ); 628 629 return False; 630 } 631 632 /* parse out the consumed mount path */ 633 /* trim off the \server\share\ */ 634 635 fullpath[consumed/2] = '\0'; 636 dos_clean_name( fullpath ); 637 ppath = strchr_m( fullpath, '\\' ); 638 ppath = strchr_m( ppath+1, '\\' ); 639 ppath = strchr_m( ppath+1, '\\' ); 640 ppath++; 641 642 pstr_sprintf( newmount, "%s\\%s", mountpt, ppath ); 643 cli_cm_set_mntpoint( *targetcli, newmount ); 644 645 /* check for another dfs referral, note that we are not 646 checking for loops here */ 647 648 if ( !strequal( targetpath, "\\" ) ) { 649 if ( cli_resolve_path( newmount, *targetcli, targetpath, &newcli, newpath ) ) { 650 *targetcli = newcli; 651 pstrcpy( targetpath, newpath ); 652 } 653 } 654 655 return True; 656} 657