root/source3/libsmb/clidfs.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cli_cm_force_encryption
  2. do_connect
  3. cli_set_mntpoint
  4. cli_cm_connect
  5. cli_cm_find
  6. cli_cm_open
  7. cli_cm_display
  8. cli_cm_set_credentials
  9. split_dfs_path
  10. clean_path
  11. cli_dfs_make_full_path
  12. cli_dfs_check_error
  13. cli_dfs_get_referral
  14. cli_resolve_path
  15. cli_check_msdfs_proxy

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

/* [<][>][^][v][top][bottom][index][help] */