root/source3/smbd/msdfs.c

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

DEFINITIONS

This source file includes following definitions.
  1. parse_dfs_path
  2. create_conn_struct
  3. parse_msdfs_symlink
  4. is_msdfs_link_internal
  5. is_msdfs_link
  6. dfs_path_lookup
  7. dfs_redirect
  8. self_ref
  9. get_referred_path
  10. setup_ver2_dfs_referral
  11. setup_ver3_dfs_referral
  12. setup_dfs_referral
  13. create_junction
  14. junction_to_local_path
  15. create_msdfs_link
  16. remove_msdfs_link
  17. count_dfs_links
  18. form_junctions
  19. enum_msdfs_links
  20. resolve_dfspath
  21. resolve_dfspath_wcard

   1 /*
   2    Unix SMB/Netbios implementation.
   3    Version 3.0
   4    MSDFS services for Samba
   5    Copyright (C) Shirish Kalele 2000
   6    Copyright (C) Jeremy Allison 2007
   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 
  23 #define DBGC_CLASS DBGC_MSDFS
  24 #include "includes.h"
  25 #include "smbd/globals.h"
  26 
  27 /**********************************************************************
  28  Parse a DFS pathname of the form \hostname\service\reqpath
  29  into the dfs_path structure.
  30  If POSIX pathnames is true, the pathname may also be of the
  31  form /hostname/service/reqpath.
  32  We cope with either here.
  33 
  34  Unfortunately, due to broken clients who might set the
  35  SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
  36  send a local path, we have to cope with that too....
  37 
  38  If conn != NULL then ensure the provided service is
  39  the one pointed to by the connection.
  40 
  41  This version does everything using pointers within one copy of the
  42  pathname string, talloced on the struct dfs_path pointer (which
  43  must be talloced). This may be too clever to live....
  44  JRA.
  45 **********************************************************************/
  46 
  47 static NTSTATUS parse_dfs_path(connection_struct *conn,
     /* [<][>][^][v][top][bottom][index][help] */
  48                                 const char *pathname,
  49                                 bool allow_wcards,
  50                                 struct dfs_path *pdp, /* MUST BE TALLOCED */
  51                                 bool *ppath_contains_wcard)
  52 {
  53         char *pathname_local;
  54         char *p,*temp;
  55         char *servicename;
  56         char *eos_ptr;
  57         NTSTATUS status = NT_STATUS_OK;
  58         char sepchar;
  59 
  60         ZERO_STRUCTP(pdp);
  61 
  62         /*
  63          * This is the only talloc we should need to do
  64          * on the struct dfs_path. All the pointers inside
  65          * it should point to offsets within this string.
  66          */
  67 
  68         pathname_local = talloc_strdup(pdp, pathname);
  69         if (!pathname_local) {
  70                 return NT_STATUS_NO_MEMORY;
  71         }
  72         /* Get a pointer to the terminating '\0' */
  73         eos_ptr = &pathname_local[strlen(pathname_local)];
  74         p = temp = pathname_local;
  75 
  76         pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
  77 
  78         sepchar = pdp->posix_path ? '/' : '\\';
  79 
  80         if (*pathname != sepchar) {
  81                 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
  82                         pathname, sepchar ));
  83                 /*
  84                  * Possibly client sent a local path by mistake.
  85                  * Try and convert to a local path.
  86                  */
  87 
  88                 pdp->hostname = eos_ptr; /* "" */
  89                 pdp->servicename = eos_ptr; /* "" */
  90 
  91                 /* We've got no info about separators. */
  92                 pdp->posix_path = lp_posix_pathnames();
  93                 p = temp;
  94                 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
  95                         "local path\n",
  96                         temp));
  97                 goto local_path;
  98         }
  99 
 100         /*
 101          * Safe to use on talloc'ed string as it only shrinks.
 102          * It also doesn't affect the eos_ptr.
 103          */
 104         trim_char(temp,sepchar,sepchar);
 105 
 106         DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
 107                 temp, sepchar));
 108 
 109         /* Now tokenize. */
 110         /* Parse out hostname. */
 111         p = strchr_m(temp,sepchar);
 112         if(p == NULL) {
 113                 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
 114                         temp));
 115                 /*
 116                  * Possibly client sent a local path by mistake.
 117                  * Try and convert to a local path.
 118                  */
 119 
 120                 pdp->hostname = eos_ptr; /* "" */
 121                 pdp->servicename = eos_ptr; /* "" */
 122 
 123                 p = temp;
 124                 DEBUG(10,("parse_dfs_path: trying to convert %s "
 125                         "to a local path\n",
 126                         temp));
 127                 goto local_path;
 128         }
 129         *p = '\0';
 130         pdp->hostname = temp;
 131 
 132         DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
 133 
 134         /* Parse out servicename. */
 135         servicename = p+1;
 136         p = strchr_m(servicename,sepchar);
 137         if (p) {
 138                 *p = '\0';
 139         }
 140 
 141         /* Is this really our servicename ? */
 142         if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
 143                         || (strequal(servicename, HOMES_NAME)
 144                         && strequal(lp_servicename(SNUM(conn)),
 145                                 get_current_username()) )) ) {
 146                 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
 147                         servicename));
 148 
 149                 /*
 150                  * Possibly client sent a local path by mistake.
 151                  * Try and convert to a local path.
 152                  */
 153 
 154                 pdp->hostname = eos_ptr; /* "" */
 155                 pdp->servicename = eos_ptr; /* "" */
 156 
 157                 /* Repair the path - replace the sepchar's
 158                    we nulled out */
 159                 servicename--;
 160                 *servicename = sepchar;
 161                 if (p) {
 162                         *p = sepchar;
 163                 }
 164 
 165                 p = temp;
 166                 DEBUG(10,("parse_dfs_path: trying to convert %s "
 167                         "to a local path\n",
 168                         temp));
 169                 goto local_path;
 170         }
 171 
 172         pdp->servicename = servicename;
 173 
 174         DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
 175 
 176         if(p == NULL) {
 177                 /* Client sent self referral \server\share. */
 178                 pdp->reqpath = eos_ptr; /* "" */
 179                 return NT_STATUS_OK;
 180         }
 181 
 182         p++;
 183 
 184   local_path:
 185 
 186         *ppath_contains_wcard = False;
 187 
 188         pdp->reqpath = p;
 189 
 190         /* Rest is reqpath. */
 191         if (pdp->posix_path) {
 192                 status = check_path_syntax_posix(pdp->reqpath);
 193         } else {
 194                 if (allow_wcards) {
 195                         status = check_path_syntax_wcard(pdp->reqpath,
 196                                         ppath_contains_wcard);
 197                 } else {
 198                         status = check_path_syntax(pdp->reqpath);
 199                 }
 200         }
 201 
 202         if (!NT_STATUS_IS_OK(status)) {
 203                 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
 204                         p, nt_errstr(status) ));
 205                 return status;
 206         }
 207 
 208         DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
 209         return NT_STATUS_OK;
 210 }
 211 
 212 /********************************************************
 213  Fake up a connection struct for the VFS layer.
 214  Note this CHANGES CWD !!!! JRA.
 215 *********************************************************/
 216 
 217 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 218                                 connection_struct **pconn,
 219                                 int snum,
 220                                 const char *path,
 221                                 struct auth_serversupplied_info *server_info,
 222                                 char **poldcwd)
 223 {
 224         connection_struct *conn;
 225         char *connpath;
 226         char *oldcwd;
 227 
 228         conn = TALLOC_ZERO_P(ctx, connection_struct);
 229         if (conn == NULL) {
 230                 return NT_STATUS_NO_MEMORY;
 231         }
 232 
 233         connpath = talloc_strdup(conn, path);
 234         if (!connpath) {
 235                 TALLOC_FREE(conn);
 236                 return NT_STATUS_NO_MEMORY;
 237         }
 238         connpath = talloc_string_sub(conn,
 239                                 connpath,
 240                                 "%S",
 241                                 lp_servicename(snum));
 242         if (!connpath) {
 243                 TALLOC_FREE(conn);
 244                 return NT_STATUS_NO_MEMORY;
 245         }
 246 
 247         /* needed for smbd_vfs_init() */
 248 
 249         if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
 250                 DEBUG(0, ("TALLOC failed\n"));
 251                 TALLOC_FREE(conn);
 252                 return NT_STATUS_NO_MEMORY;
 253         }
 254 
 255         conn->params->service = snum;
 256 
 257         if (server_info != NULL) {
 258                 conn->server_info = copy_serverinfo(conn, server_info);
 259                 if (conn->server_info == NULL) {
 260                         DEBUG(0, ("copy_serverinfo failed\n"));
 261                         TALLOC_FREE(conn);
 262                         return NT_STATUS_NO_MEMORY;
 263                 }
 264         }
 265 
 266         set_conn_connectpath(conn, connpath);
 267 
 268         if (!smbd_vfs_init(conn)) {
 269                 NTSTATUS status = map_nt_error_from_unix(errno);
 270                 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
 271                 conn_free_internal(conn);
 272                 return status;
 273         }
 274 
 275         conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn);
 276 
 277         /*
 278          * Windows seems to insist on doing trans2getdfsreferral() calls on
 279          * the IPC$ share as the anonymous user. If we try to chdir as that
 280          * user we will fail.... WTF ? JRA.
 281          */
 282 
 283         oldcwd = vfs_GetWd(ctx, conn);
 284         if (oldcwd == NULL) {
 285                 NTSTATUS status = map_nt_error_from_unix(errno);
 286                 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
 287                 conn_free_internal(conn);
 288                 return status;
 289         }
 290 
 291         if (vfs_ChDir(conn,conn->connectpath) != 0) {
 292                 NTSTATUS status = map_nt_error_from_unix(errno);
 293                 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
 294                         "Error was %s\n",
 295                         conn->connectpath, strerror(errno) ));
 296                 conn_free_internal(conn);
 297                 return status;
 298         }
 299 
 300         *pconn = conn;
 301         *poldcwd = oldcwd;
 302 
 303         return NT_STATUS_OK;
 304 }
 305 
 306 /**********************************************************************
 307  Parse the contents of a symlink to verify if it is an msdfs referral
 308  A valid referral is of the form:
 309 
 310  msdfs:server1\share1,server2\share2
 311  msdfs:server1\share1\pathname,server2\share2\pathname
 312  msdfs:server1/share1,server2/share2
 313  msdfs:server1/share1/pathname,server2/share2/pathname.
 314 
 315  Note that the alternate paths returned here must be of the canonicalized
 316  form:
 317 
 318  \server\share or
 319  \server\share\path\to\file,
 320 
 321  even in posix path mode. This is because we have no knowledge if the
 322  server we're referring to understands posix paths.
 323  **********************************************************************/
 324 
 325 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 326                                 const char *target,
 327                                 struct referral **preflist,
 328                                 int *refcount)
 329 {
 330         char *temp = NULL;
 331         char *prot;
 332         char **alt_path = NULL;
 333         int count = 0, i;
 334         struct referral *reflist;
 335         char *saveptr;
 336 
 337         temp = talloc_strdup(ctx, target);
 338         if (!temp) {
 339                 return False;
 340         }
 341         prot = strtok_r(temp, ":", &saveptr);
 342         if (!prot) {
 343                 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
 344                 return False;
 345         }
 346 
 347         alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
 348         if (!alt_path) {
 349                 return False;
 350         }
 351 
 352         /* parse out the alternate paths */
 353         while((count<MAX_REFERRAL_COUNT) &&
 354               ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
 355                 count++;
 356         }
 357 
 358         DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
 359 
 360         if (count) {
 361                 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
 362                                 struct referral, count);
 363                 if(reflist == NULL) {
 364                         TALLOC_FREE(alt_path);
 365                         return False;
 366                 }
 367         } else {
 368                 reflist = *preflist = NULL;
 369         }
 370 
 371         for(i=0;i<count;i++) {
 372                 char *p;
 373 
 374                 /* Canonicalize link target.
 375                  * Replace all /'s in the path by a \ */
 376                 string_replace(alt_path[i], '/', '\\');
 377 
 378                 /* Remove leading '\\'s */
 379                 p = alt_path[i];
 380                 while (*p && (*p == '\\')) {
 381                         p++;
 382                 }
 383 
 384                 reflist[i].alternate_path = talloc_asprintf(ctx,
 385                                 "\\%s",
 386                                 p);
 387                 if (!reflist[i].alternate_path) {
 388                         return False;
 389                 }
 390 
 391                 reflist[i].proximity = 0;
 392                 reflist[i].ttl = REFERRAL_TTL;
 393                 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
 394                                         reflist[i].alternate_path));
 395         }
 396 
 397         *refcount = count;
 398 
 399         TALLOC_FREE(alt_path);
 400         return True;
 401 }
 402 
 403 /**********************************************************************
 404  Returns true if the unix path is a valid msdfs symlink and also
 405  returns the target string from inside the link.
 406 **********************************************************************/
 407 
 408 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 409                         connection_struct *conn,
 410                         const char *path,
 411                         char **pp_link_target,
 412                         SMB_STRUCT_STAT *sbufp)
 413 {
 414         SMB_STRUCT_STAT st;
 415         int referral_len = 0;
 416 #if defined(HAVE_BROKEN_READLINK)
 417         char link_target_buf[PATH_MAX];
 418 #else
 419         char link_target_buf[7];
 420 #endif
 421         size_t bufsize = 0;
 422         char *link_target = NULL;
 423 
 424         if (pp_link_target) {
 425                 bufsize = 1024;
 426                 link_target = TALLOC_ARRAY(ctx, char, bufsize);
 427                 if (!link_target) {
 428                         return False;
 429                 }
 430                 *pp_link_target = link_target;
 431         } else {
 432                 bufsize = sizeof(link_target_buf);
 433                 link_target = link_target_buf;
 434         }
 435 
 436         if (sbufp == NULL) {
 437                 sbufp = &st;
 438         }
 439 
 440         if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
 441                 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
 442                         path));
 443                 goto err;
 444         }
 445 
 446         if (!S_ISLNK(sbufp->st_mode)) {
 447                 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
 448                                         path));
 449                 goto err;
 450         }
 451 
 452         referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
 453         if (referral_len == -1) {
 454                 DEBUG(0,("is_msdfs_link_read_target: Error reading "
 455                         "msdfs link %s: %s\n",
 456                         path, strerror(errno)));
 457                 goto err;
 458         }
 459         link_target[referral_len] = '\0';
 460 
 461         DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
 462                                 link_target));
 463 
 464         if (!strnequal(link_target, "msdfs:", 6)) {
 465                 goto err;
 466         }
 467         return True;
 468 
 469   err:
 470 
 471         if (link_target != link_target_buf) {
 472                 TALLOC_FREE(link_target);
 473         }
 474         return False;
 475 }
 476 
 477 /**********************************************************************
 478  Returns true if the unix path is a valid msdfs symlink.
 479 **********************************************************************/
 480 
 481 bool is_msdfs_link(connection_struct *conn,
     /* [<][>][^][v][top][bottom][index][help] */
 482                 const char *path,
 483                 SMB_STRUCT_STAT *sbufp)
 484 {
 485         return is_msdfs_link_internal(talloc_tos(),
 486                                         conn,
 487                                         path,
 488                                         NULL,
 489                                         sbufp);
 490 }
 491 
 492 /*****************************************************************
 493  Used by other functions to decide if a dfs path is remote,
 494  and to get the list of referred locations for that remote path.
 495 
 496  search_flag: For findfirsts, dfs links themselves are not
 497  redirected, but paths beyond the links are. For normal smb calls,
 498  even dfs links need to be redirected.
 499 
 500  consumedcntp: how much of the dfs path is being redirected. the client
 501  should try the remaining path on the redirected server.
 502 
 503  If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
 504  link redirect are in targetpath.
 505 *****************************************************************/
 506 
 507 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 508                 connection_struct *conn,
 509                 const char *dfspath, /* Incoming complete dfs path */
 510                 const struct dfs_path *pdp, /* Parsed out
 511                                                server+share+extrapath. */
 512                 bool search_flag, /* Called from a findfirst ? */
 513                 int *consumedcntp,
 514                 char **pp_targetpath)
 515 {
 516         char *p = NULL;
 517         char *q = NULL;
 518         SMB_STRUCT_STAT sbuf;
 519         NTSTATUS status;
 520         char *localpath = NULL;
 521         char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
 522                                   components). */
 523 
 524         DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
 525                 conn->connectpath, pdp->reqpath));
 526 
 527         /*
 528          * Note the unix path conversion here we're doing we can
 529          * throw away. We're looking for a symlink for a dfs
 530          * resolution, if we don't find it we'll do another
 531          * unix_convert later in the codepath.
 532          * If we needed to remember what we'd resolved in
 533          * dp->reqpath (as the original code did) we'd
 534          * copy (localhost, dp->reqpath) on any code
 535          * path below that returns True - but I don't
 536          * think this is needed. JRA.
 537          */
 538 
 539         status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
 540                         NULL, &sbuf);
 541         if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
 542                                         NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
 543                 return status;
 544         }
 545 
 546         /* Optimization - check if we can redirect the whole path. */
 547 
 548         if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
 549                 if (search_flag) {
 550                         DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
 551                                  "for dfs link %s.\n", dfspath));
 552                         return NT_STATUS_OK;
 553                 }
 554 
 555                 DEBUG(6,("dfs_path_lookup: %s resolves to a "
 556                         "valid dfs link %s.\n", dfspath,
 557                         pp_targetpath ? *pp_targetpath : ""));
 558 
 559                 if (consumedcntp) {
 560                         *consumedcntp = strlen(dfspath);
 561                 }
 562                 return NT_STATUS_PATH_NOT_COVERED;
 563         }
 564 
 565         /* Prepare to test only for '/' components in the given path,
 566          * so if a Windows path replace all '\\' characters with '/'.
 567          * For a POSIX DFS path we know all separators are already '/'. */
 568 
 569         canon_dfspath = talloc_strdup(ctx, dfspath);
 570         if (!canon_dfspath) {
 571                 return NT_STATUS_NO_MEMORY;
 572         }
 573         if (!pdp->posix_path) {
 574                 string_replace(canon_dfspath, '\\', '/');
 575         }
 576 
 577         /*
 578          * localpath comes out of unix_convert, so it has
 579          * no trailing backslash. Make sure that canon_dfspath hasn't either.
 580          * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
 581          */
 582 
 583         trim_char(canon_dfspath,0,'/');
 584 
 585         /*
 586          * Redirect if any component in the path is a link.
 587          * We do this by walking backwards through the
 588          * local path, chopping off the last component
 589          * in both the local path and the canonicalized
 590          * DFS path. If we hit a DFS link then we're done.
 591          */
 592 
 593         p = strrchr_m(localpath, '/');
 594         if (consumedcntp) {
 595                 q = strrchr_m(canon_dfspath, '/');
 596         }
 597 
 598         while (p) {
 599                 *p = '\0';
 600                 if (q) {
 601                         *q = '\0';
 602                 }
 603 
 604                 if (is_msdfs_link_internal(ctx, conn,
 605                                         localpath, pp_targetpath, NULL)) {
 606                         DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
 607                                 "parent %s is dfs link\n", dfspath, localpath));
 608 
 609                         if (consumedcntp) {
 610                                 *consumedcntp = strlen(canon_dfspath);
 611                                 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
 612                                         "(%d)\n",
 613                                         canon_dfspath,
 614                                         *consumedcntp));
 615                         }
 616 
 617                         return NT_STATUS_PATH_NOT_COVERED;
 618                 }
 619 
 620                 /* Step back on the filesystem. */
 621                 p = strrchr_m(localpath, '/');
 622 
 623                 if (consumedcntp) {
 624                         /* And in the canonicalized dfs path. */
 625                         q = strrchr_m(canon_dfspath, '/');
 626                 }
 627         }
 628 
 629         return NT_STATUS_OK;
 630 }
 631 
 632 /*****************************************************************
 633  Decides if a dfs pathname should be redirected or not.
 634  If not, the pathname is converted to a tcon-relative local unix path
 635 
 636  search_wcard_flag: this flag performs 2 functions both related
 637  to searches.  See resolve_dfs_path() and parse_dfs_path_XX()
 638  for details.
 639 
 640  This function can return NT_STATUS_OK, meaning use the returned path as-is
 641  (mapped into a local path).
 642  or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
 643  any other NT_STATUS error which is a genuine error to be
 644  returned to the client.
 645 *****************************************************************/
 646 
 647 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 648                         connection_struct *conn,
 649                         const char *path_in,
 650                         bool search_wcard_flag,
 651                         char **pp_path_out,
 652                         bool *ppath_contains_wcard)
 653 {
 654         NTSTATUS status;
 655         struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
 656 
 657         if (!pdp) {
 658                 return NT_STATUS_NO_MEMORY;
 659         }
 660 
 661         status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
 662                         ppath_contains_wcard);
 663         if (!NT_STATUS_IS_OK(status)) {
 664                 TALLOC_FREE(pdp);
 665                 return status;
 666         }
 667 
 668         if (pdp->reqpath[0] == '\0') {
 669                 TALLOC_FREE(pdp);
 670                 *pp_path_out = talloc_strdup(ctx, "");
 671                 if (!*pp_path_out) {
 672                         return NT_STATUS_NO_MEMORY;
 673                 }
 674                 DEBUG(5,("dfs_redirect: self-referral.\n"));
 675                 return NT_STATUS_OK;
 676         }
 677 
 678         /* If dfs pathname for a non-dfs share, convert to tcon-relative
 679            path and return OK */
 680 
 681         if (!lp_msdfs_root(SNUM(conn))) {
 682                 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
 683                 TALLOC_FREE(pdp);
 684                 if (!*pp_path_out) {
 685                         return NT_STATUS_NO_MEMORY;
 686                 }
 687                 return NT_STATUS_OK;
 688         }
 689 
 690         /* If it looked like a local path (zero hostname/servicename)
 691          * just treat as a tcon-relative path. */
 692 
 693         if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
 694                 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
 695                 TALLOC_FREE(pdp);
 696                 if (!*pp_path_out) {
 697                         return NT_STATUS_NO_MEMORY;
 698                 }
 699                 return NT_STATUS_OK;
 700         }
 701 
 702         if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
 703                         || (strequal(pdp->servicename, HOMES_NAME)
 704                         && strequal(lp_servicename(SNUM(conn)),
 705                                 conn->server_info->sanitized_username) )) ) {
 706 
 707                 /* The given sharename doesn't match this connection. */
 708                 TALLOC_FREE(pdp);
 709 
 710                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
 711         }
 712 
 713         status = dfs_path_lookup(ctx, conn, path_in, pdp,
 714                         search_wcard_flag, NULL, NULL);
 715         if (!NT_STATUS_IS_OK(status)) {
 716                 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
 717                         DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
 718                 } else {
 719                         DEBUG(10,("dfs_redirect: dfs_path_lookup "
 720                                 "failed for %s with %s\n",
 721                                 path_in, nt_errstr(status) ));
 722                 }
 723                 return status;
 724         }
 725 
 726         DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
 727 
 728         /* Form non-dfs tcon-relative path */
 729         *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
 730         TALLOC_FREE(pdp);
 731         if (!*pp_path_out) {
 732                 return NT_STATUS_NO_MEMORY;
 733         }
 734 
 735         DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
 736                                 path_in,
 737                                 *pp_path_out));
 738 
 739         return NT_STATUS_OK;
 740 }
 741 
 742 /**********************************************************************
 743  Return a self referral.
 744 **********************************************************************/
 745 
 746 static NTSTATUS self_ref(TALLOC_CTX *ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 747                         const char *dfs_path,
 748                         struct junction_map *jucn,
 749                         int *consumedcntp,
 750                         bool *self_referralp)
 751 {
 752         struct referral *ref;
 753 
 754         *self_referralp = True;
 755 
 756         jucn->referral_count = 1;
 757         if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
 758                 return NT_STATUS_NO_MEMORY;
 759         }
 760 
 761         ref->alternate_path = talloc_strdup(ctx, dfs_path);
 762         if (!ref->alternate_path) {
 763                 return NT_STATUS_NO_MEMORY;
 764         }
 765         ref->proximity = 0;
 766         ref->ttl = REFERRAL_TTL;
 767         jucn->referral_list = ref;
 768         *consumedcntp = strlen(dfs_path);
 769         return NT_STATUS_OK;
 770 }
 771 
 772 /**********************************************************************
 773  Gets valid referrals for a dfs path and fills up the
 774  junction_map structure.
 775 **********************************************************************/
 776 
 777 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 778                         const char *dfs_path,
 779                         struct junction_map *jucn,
 780                         int *consumedcntp,
 781                         bool *self_referralp)
 782 {
 783         struct connection_struct *conn;
 784         char *targetpath = NULL;
 785         int snum;
 786         NTSTATUS status = NT_STATUS_NOT_FOUND;
 787         bool dummy;
 788         struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
 789         char *oldpath;
 790 
 791         if (!pdp) {
 792                 return NT_STATUS_NO_MEMORY;
 793         }
 794 
 795         *self_referralp = False;
 796 
 797         status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
 798         if (!NT_STATUS_IS_OK(status)) {
 799                 return status;
 800         }
 801 
 802         jucn->service_name = talloc_strdup(ctx, pdp->servicename);
 803         jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
 804         if (!jucn->service_name || !jucn->volume_name) {
 805                 TALLOC_FREE(pdp);
 806                 return NT_STATUS_NO_MEMORY;
 807         }
 808 
 809         /* Verify the share is a dfs root */
 810         snum = lp_servicenumber(jucn->service_name);
 811         if(snum < 0) {
 812                 fstring service_name;
 813                 fstrcpy(service_name, jucn->service_name);
 814                 if ((snum = find_service(service_name)) < 0) {
 815                         return NT_STATUS_NOT_FOUND;
 816                 }
 817                 TALLOC_FREE(jucn->service_name);
 818                 jucn->service_name = talloc_strdup(ctx, service_name);
 819                 if (!jucn->service_name) {
 820                         TALLOC_FREE(pdp);
 821                         return NT_STATUS_NO_MEMORY;
 822                 }
 823         }
 824 
 825         if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
 826                 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
 827                         "a dfs root.\n",
 828                         pdp->servicename, dfs_path));
 829                 TALLOC_FREE(pdp);
 830                 return NT_STATUS_NOT_FOUND;
 831         }
 832 
 833         /*
 834          * Self referrals are tested with a anonymous IPC connection and
 835          * a GET_DFS_REFERRAL call to \\server\share. (which means
 836          * dp.reqpath[0] points to an empty string). create_conn_struct cd's
 837          * into the directory and will fail if it cannot (as the anonymous
 838          * user). Cope with this.
 839          */
 840 
 841         if (pdp->reqpath[0] == '\0') {
 842                 char *tmp;
 843                 struct referral *ref;
 844 
 845                 if (*lp_msdfs_proxy(snum) == '\0') {
 846                         TALLOC_FREE(pdp);
 847                         return self_ref(ctx,
 848                                         dfs_path,
 849                                         jucn,
 850                                         consumedcntp,
 851                                         self_referralp);
 852                 }
 853 
 854                 /*
 855                  * It's an msdfs proxy share. Redirect to
 856                  * the configured target share.
 857                  */
 858 
 859                 jucn->referral_count = 1;
 860                 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
 861                         TALLOC_FREE(pdp);
 862                         return NT_STATUS_NO_MEMORY;
 863                 }
 864 
 865                 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
 866                         TALLOC_FREE(pdp);
 867                         return NT_STATUS_NO_MEMORY;
 868                 }
 869 
 870                 trim_string(tmp, "\\", 0);
 871 
 872                 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
 873                 TALLOC_FREE(tmp);
 874 
 875                 if (!ref->alternate_path) {
 876                         TALLOC_FREE(pdp);
 877                         return NT_STATUS_NO_MEMORY;
 878                 }
 879 
 880                 if (pdp->reqpath[0] != '\0') {
 881                         ref->alternate_path = talloc_asprintf_append(
 882                                         ref->alternate_path,
 883                                         "%s",
 884                                         pdp->reqpath);
 885                         if (!ref->alternate_path) {
 886                                 TALLOC_FREE(pdp);
 887                                 return NT_STATUS_NO_MEMORY;
 888                         }
 889                 }
 890                 ref->proximity = 0;
 891                 ref->ttl = REFERRAL_TTL;
 892                 jucn->referral_list = ref;
 893                 *consumedcntp = strlen(dfs_path);
 894                 TALLOC_FREE(pdp);
 895                 return NT_STATUS_OK;
 896         }
 897 
 898         status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
 899                                     NULL, &oldpath);
 900         if (!NT_STATUS_IS_OK(status)) {
 901                 TALLOC_FREE(pdp);
 902                 return status;
 903         }
 904 
 905         /* If this is a DFS path dfs_lookup should return
 906          * NT_STATUS_PATH_NOT_COVERED. */
 907 
 908         status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
 909                         False, consumedcntp, &targetpath);
 910 
 911         if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
 912                 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
 913                         dfs_path));
 914                 vfs_ChDir(conn, oldpath);
 915                 conn_free_internal(conn);
 916                 TALLOC_FREE(pdp);
 917                 return status;
 918         }
 919 
 920         /* We know this is a valid dfs link. Parse the targetpath. */
 921         if (!parse_msdfs_symlink(ctx, targetpath,
 922                                 &jucn->referral_list,
 923                                 &jucn->referral_count)) {
 924                 DEBUG(3,("get_referred_path: failed to parse symlink "
 925                         "target %s\n", targetpath ));
 926                 vfs_ChDir(conn, oldpath);
 927                 conn_free_internal(conn);
 928                 TALLOC_FREE(pdp);
 929                 return NT_STATUS_NOT_FOUND;
 930         }
 931 
 932         vfs_ChDir(conn, oldpath);
 933         conn_free_internal(conn);
 934         TALLOC_FREE(pdp);
 935         return NT_STATUS_OK;
 936 }
 937 
 938 static int setup_ver2_dfs_referral(const char *pathname,
     /* [<][>][^][v][top][bottom][index][help] */
 939                                 char **ppdata,
 940                                 struct junction_map *junction,
 941                                 bool self_referral)
 942 {
 943         char* pdata = *ppdata;
 944 
 945         smb_ucs2_t *uni_requestedpath = NULL;
 946         int uni_reqpathoffset1,uni_reqpathoffset2;
 947         int uni_curroffset;
 948         int requestedpathlen=0;
 949         int offset;
 950         int reply_size = 0;
 951         int i=0;
 952 
 953         DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
 954 
 955         requestedpathlen = rpcstr_push_talloc(talloc_tos(),
 956                                         &uni_requestedpath, pathname);
 957         if (uni_requestedpath == NULL || requestedpathlen == 0) {
 958                 return -1;
 959         }
 960 
 961         if (DEBUGLVL(10)) {
 962                 dump_data(0, (unsigned char *)uni_requestedpath,
 963                         requestedpathlen);
 964         }
 965 
 966         DEBUG(10,("ref count = %u\n",junction->referral_count));
 967 
 968         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
 969                         VERSION2_REFERRAL_SIZE * junction->referral_count;
 970 
 971         uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
 972 
 973         uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
 974 
 975         reply_size = REFERRAL_HEADER_SIZE +
 976                         VERSION2_REFERRAL_SIZE*junction->referral_count +
 977                         2 * requestedpathlen;
 978         DEBUG(10,("reply_size: %u\n",reply_size));
 979 
 980         /* add up the unicode lengths of all the referral paths */
 981         for(i=0;i<junction->referral_count;i++) {
 982                 DEBUG(10,("referral %u : %s\n",
 983                         i,
 984                         junction->referral_list[i].alternate_path));
 985                 reply_size +=
 986                         (strlen(junction->referral_list[i].alternate_path)+1)*2;
 987         }
 988 
 989         DEBUG(10,("reply_size = %u\n",reply_size));
 990         /* add the unexplained 0x16 bytes */
 991         reply_size += 0x16;
 992 
 993         pdata = (char *)SMB_REALLOC(pdata,reply_size);
 994         if(pdata == NULL) {
 995                 DEBUG(0,("Realloc failed!\n"));
 996                 return -1;
 997         }
 998         *ppdata = pdata;
 999 
1000         /* copy in the dfs requested paths.. required for offset calculations */
1001         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1002         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1003 
1004         /* create the header */
1005         SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1006                                                 2 byte null */
1007         /* number of referral in this pkt */
1008         SSVAL(pdata,2,junction->referral_count);
1009         if(self_referral) {
1010                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1011         } else {
1012                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1013         }
1014 
1015         offset = 8;
1016         /* add the referral elements */
1017         for(i=0;i<junction->referral_count;i++) {
1018                 struct referral* ref = &junction->referral_list[i];
1019                 int unilen;
1020 
1021                 SSVAL(pdata,offset,2); /* version 2 */
1022                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1023                 if(self_referral) {
1024                         SSVAL(pdata,offset+4,1);
1025                 } else {
1026                         SSVAL(pdata,offset+4,0);
1027                 }
1028 
1029                 /* ref_flags :use path_consumed bytes? */
1030                 SSVAL(pdata,offset+6,0);
1031                 SIVAL(pdata,offset+8,ref->proximity);
1032                 SIVAL(pdata,offset+12,ref->ttl);
1033 
1034                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1035                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1036                 /* copy referred path into current offset */
1037                 unilen = rpcstr_push(pdata+uni_curroffset,
1038                                         ref->alternate_path,
1039                                         reply_size - uni_curroffset,
1040                                         STR_UNICODE);
1041 
1042                 SSVAL(pdata,offset+20,uni_curroffset-offset);
1043 
1044                 uni_curroffset += unilen;
1045                 offset += VERSION2_REFERRAL_SIZE;
1046         }
1047         /* add in the unexplained 22 (0x16) bytes at the end */
1048         memset(pdata+uni_curroffset,'\0',0x16);
1049         return reply_size;
1050 }
1051 
1052 static int setup_ver3_dfs_referral(const char *pathname,
     /* [<][>][^][v][top][bottom][index][help] */
1053                                 char **ppdata,
1054                                 struct junction_map *junction,
1055                                 bool self_referral)
1056 {
1057         char *pdata = *ppdata;
1058 
1059         smb_ucs2_t *uni_reqpath = NULL;
1060         int uni_reqpathoffset1, uni_reqpathoffset2;
1061         int uni_curroffset;
1062         int reply_size = 0;
1063 
1064         int reqpathlen = 0;
1065         int offset,i=0;
1066 
1067         DEBUG(10,("setting up version3 referral\n"));
1068 
1069         reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1070         if (uni_reqpath == NULL || reqpathlen == 0) {
1071                 return -1;
1072         }
1073 
1074         if (DEBUGLVL(10)) {
1075                 dump_data(0, (unsigned char *)uni_reqpath,
1076                         reqpathlen);
1077         }
1078 
1079         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1080                         VERSION3_REFERRAL_SIZE * junction->referral_count;
1081         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1082         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1083 
1084         for(i=0;i<junction->referral_count;i++) {
1085                 DEBUG(10,("referral %u : %s\n",
1086                         i,
1087                         junction->referral_list[i].alternate_path));
1088                 reply_size +=
1089                         (strlen(junction->referral_list[i].alternate_path)+1)*2;
1090         }
1091 
1092         pdata = (char *)SMB_REALLOC(pdata,reply_size);
1093         if(pdata == NULL) {
1094                 DEBUG(0,("version3 referral setup:"
1095                         "malloc failed for Realloc!\n"));
1096                 return -1;
1097         }
1098         *ppdata = pdata;
1099 
1100         /* create the header */
1101         SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1102                                           2 byte null */
1103         SSVAL(pdata,2,junction->referral_count); /* number of referral */
1104         if(self_referral) {
1105                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1106         } else {
1107                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1108         }
1109 
1110         /* copy in the reqpaths */
1111         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1112         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1113 
1114         offset = 8;
1115         for(i=0;i<junction->referral_count;i++) {
1116                 struct referral* ref = &(junction->referral_list[i]);
1117                 int unilen;
1118 
1119                 SSVAL(pdata,offset,3); /* version 3 */
1120                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1121                 if(self_referral) {
1122                         SSVAL(pdata,offset+4,1);
1123                 } else {
1124                         SSVAL(pdata,offset+4,0);
1125                 }
1126 
1127                 /* ref_flags :use path_consumed bytes? */
1128                 SSVAL(pdata,offset+6,0);
1129                 SIVAL(pdata,offset+8,ref->ttl);
1130 
1131                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1132                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1133                 /* copy referred path into current offset */
1134                 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1135                                         reply_size - uni_curroffset,
1136                                         STR_UNICODE | STR_TERMINATE);
1137                 SSVAL(pdata,offset+16,uni_curroffset-offset);
1138                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1139                 memset(pdata+offset+18,'\0',16);
1140 
1141                 uni_curroffset += unilen;
1142                 offset += VERSION3_REFERRAL_SIZE;
1143         }
1144         return reply_size;
1145 }
1146 
1147 /******************************************************************
1148  Set up the DFS referral for the dfs pathname. This call returns
1149  the amount of the path covered by this server, and where the
1150  client should be redirected to. This is the meat of the
1151  TRANS2_GET_DFS_REFERRAL call.
1152 ******************************************************************/
1153 
1154 int setup_dfs_referral(connection_struct *orig_conn,
     /* [<][>][^][v][top][bottom][index][help] */
1155                         const char *dfs_path,
1156                         int max_referral_level,
1157                         char **ppdata, NTSTATUS *pstatus)
1158 {
1159         struct junction_map *junction = NULL;
1160         int consumedcnt = 0;
1161         bool self_referral = False;
1162         int reply_size = 0;
1163         char *pathnamep = NULL;
1164         char *local_dfs_path = NULL;
1165         TALLOC_CTX *ctx;
1166 
1167         if (!(ctx=talloc_init("setup_dfs_referral"))) {
1168                 *pstatus = NT_STATUS_NO_MEMORY;
1169                 return -1;
1170         }
1171 
1172         /* get the junction entry */
1173         if (!dfs_path) {
1174                 talloc_destroy(ctx);
1175                 *pstatus = NT_STATUS_NOT_FOUND;
1176                 return -1;
1177         }
1178 
1179         /*
1180          * Trim pathname sent by client so it begins with only one backslash.
1181          * Two backslashes confuse some dfs clients
1182          */
1183 
1184         local_dfs_path = talloc_strdup(ctx,dfs_path);
1185         if (!local_dfs_path) {
1186                 *pstatus = NT_STATUS_NO_MEMORY;
1187                 talloc_destroy(ctx);
1188                 return -1;
1189         }
1190         pathnamep = local_dfs_path;
1191         while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1192                         IS_DIRECTORY_SEP(pathnamep[1])) {
1193                 pathnamep++;
1194         }
1195 
1196         junction = TALLOC_ZERO_P(ctx, struct junction_map);
1197         if (!junction) {
1198                 *pstatus = NT_STATUS_NO_MEMORY;
1199                 talloc_destroy(ctx);
1200                 return -1;
1201         }
1202 
1203         /* The following call can change cwd. */
1204         *pstatus = get_referred_path(ctx, pathnamep, junction,
1205                         &consumedcnt, &self_referral);
1206         if (!NT_STATUS_IS_OK(*pstatus)) {
1207                 vfs_ChDir(orig_conn,orig_conn->connectpath);
1208                 talloc_destroy(ctx);
1209                 return -1;
1210         }
1211         vfs_ChDir(orig_conn,orig_conn->connectpath);
1212 
1213         if (!self_referral) {
1214                 pathnamep[consumedcnt] = '\0';
1215 
1216                 if( DEBUGLVL( 3 ) ) {
1217                         int i=0;
1218                         dbgtext("setup_dfs_referral: Path %s to "
1219                                 "alternate path(s):",
1220                                 pathnamep);
1221                         for(i=0;i<junction->referral_count;i++)
1222                                 dbgtext(" %s",
1223                                 junction->referral_list[i].alternate_path);
1224                         dbgtext(".\n");
1225                 }
1226         }
1227 
1228         /* create the referral depeding on version */
1229         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1230 
1231         if (max_referral_level < 2) {
1232                 max_referral_level = 2;
1233         }
1234         if (max_referral_level > 3) {
1235                 max_referral_level = 3;
1236         }
1237 
1238         switch(max_referral_level) {
1239         case 2:
1240                 reply_size = setup_ver2_dfs_referral(pathnamep,
1241                                         ppdata, junction,
1242                                         self_referral);
1243                 break;
1244         case 3:
1245                 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1246                                         junction, self_referral);
1247                 break;
1248         default:
1249                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1250                         "version: %d\n",
1251                         max_referral_level));
1252                 talloc_destroy(ctx);
1253                 *pstatus = NT_STATUS_INVALID_LEVEL;
1254                 return -1;
1255         }
1256 
1257         if (DEBUGLVL(10)) {
1258                 DEBUGADD(0,("DFS Referral pdata:\n"));
1259                 dump_data(0,(uint8 *)*ppdata,reply_size);
1260         }
1261 
1262         talloc_destroy(ctx);
1263         *pstatus = NT_STATUS_OK;
1264         return reply_size;
1265 }
1266 
1267 /**********************************************************************
1268  The following functions are called by the NETDFS RPC pipe functions
1269  **********************************************************************/
1270 
1271 /*********************************************************************
1272  Creates a junction structure from a DFS pathname
1273 **********************************************************************/
1274 
1275 bool create_junction(TALLOC_CTX *ctx,
     /* [<][>][^][v][top][bottom][index][help] */
1276                 const char *dfs_path,
1277                 struct junction_map *jucn)
1278 {
1279         int snum;
1280         bool dummy;
1281         struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1282         NTSTATUS status;
1283 
1284         if (!pdp) {
1285                 return False;
1286         }
1287         status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1288         if (!NT_STATUS_IS_OK(status)) {
1289                 return False;
1290         }
1291 
1292         /* check if path is dfs : validate first token */
1293         if (!is_myname_or_ipaddr(pdp->hostname)) {
1294                 DEBUG(4,("create_junction: Invalid hostname %s "
1295                         "in dfs path %s\n",
1296                         pdp->hostname, dfs_path));
1297                 TALLOC_FREE(pdp);
1298                 return False;
1299         }
1300 
1301         /* Check for a non-DFS share */
1302         snum = lp_servicenumber(pdp->servicename);
1303 
1304         if(snum < 0 || !lp_msdfs_root(snum)) {
1305                 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1306                         pdp->servicename));
1307                 TALLOC_FREE(pdp);
1308                 return False;
1309         }
1310 
1311         jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1312         jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1313         jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1314 
1315         TALLOC_FREE(pdp);
1316         if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1317                 return False;
1318         }
1319         return True;
1320 }
1321 
1322 /**********************************************************************
1323  Forms a valid Unix pathname from the junction
1324  **********************************************************************/
1325 
1326 static bool junction_to_local_path(const struct junction_map *jucn,
     /* [<][>][^][v][top][bottom][index][help] */
1327                                    char **pp_path_out,
1328                                    connection_struct **conn_out,
1329                                    char **oldpath)
1330 {
1331         int snum;
1332         NTSTATUS status;
1333 
1334         snum = lp_servicenumber(jucn->service_name);
1335         if(snum < 0) {
1336                 return False;
1337         }
1338         status = create_conn_struct(talloc_tos(), conn_out, snum,
1339                                     lp_pathname(snum), NULL, oldpath);
1340         if (!NT_STATUS_IS_OK(status)) {
1341                 return False;
1342         }
1343 
1344         *pp_path_out = talloc_asprintf(*conn_out,
1345                         "%s/%s",
1346                         lp_pathname(snum),
1347                         jucn->volume_name);
1348         if (!*pp_path_out) {
1349                 vfs_ChDir(*conn_out, *oldpath);
1350                 conn_free_internal(*conn_out);
1351                 return False;
1352         }
1353         return True;
1354 }
1355 
1356 bool create_msdfs_link(const struct junction_map *jucn)
     /* [<][>][^][v][top][bottom][index][help] */
1357 {
1358         char *path = NULL;
1359         char *cwd;
1360         char *msdfs_link = NULL;
1361         connection_struct *conn;
1362         int i=0;
1363         bool insert_comma = False;
1364         bool ret = False;
1365 
1366         if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1367                 return False;
1368         }
1369 
1370         /* Form the msdfs_link contents */
1371         msdfs_link = talloc_strdup(conn, "msdfs:");
1372         if (!msdfs_link) {
1373                 goto out;
1374         }
1375         for(i=0; i<jucn->referral_count; i++) {
1376                 char *refpath = jucn->referral_list[i].alternate_path;
1377 
1378                 /* Alternate paths always use Windows separators. */
1379                 trim_char(refpath, '\\', '\\');
1380                 if(*refpath == '\0') {
1381                         if (i == 0) {
1382                                 insert_comma = False;
1383                         }
1384                         continue;
1385                 }
1386                 if (i > 0 && insert_comma) {
1387                         msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1388                                         ",%s",
1389                                         refpath);
1390                 } else {
1391                         msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1392                                         "%s",
1393                                         refpath);
1394                 }
1395 
1396                 if (!msdfs_link) {
1397                         goto out;
1398                 }
1399                 if (!insert_comma) {
1400                         insert_comma = True;
1401                 }
1402         }
1403 
1404         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1405                 path, msdfs_link));
1406 
1407         if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1408                 if (errno == EEXIST) {
1409                         if(SMB_VFS_UNLINK(conn,path)!=0) {
1410                                 goto out;
1411                         }
1412                 }
1413                 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1414                         DEBUG(1,("create_msdfs_link: symlink failed "
1415                                  "%s -> %s\nError: %s\n",
1416                                  path, msdfs_link, strerror(errno)));
1417                         goto out;
1418                 }
1419         }
1420 
1421         ret = True;
1422 
1423 out:
1424         vfs_ChDir(conn, cwd);
1425         conn_free_internal(conn);
1426         return ret;
1427 }
1428 
1429 bool remove_msdfs_link(const struct junction_map *jucn)
     /* [<][>][^][v][top][bottom][index][help] */
1430 {
1431         char *path = NULL;
1432         char *cwd;
1433         connection_struct *conn;
1434         bool ret = False;
1435 
1436         if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1437                 return false;
1438         }
1439 
1440         if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1441                 ret = True;
1442         }
1443 
1444         vfs_ChDir(conn, cwd);
1445         conn_free_internal(conn);
1446         return ret;
1447 }
1448 
1449 /*********************************************************************
1450  Return the number of DFS links at the root of this share.
1451 *********************************************************************/
1452 
1453 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
     /* [<][>][^][v][top][bottom][index][help] */
1454 {
1455         size_t cnt = 0;
1456         SMB_STRUCT_DIR *dirp = NULL;
1457         char *dname = NULL;
1458         const char *connect_path = lp_pathname(snum);
1459         const char *msdfs_proxy = lp_msdfs_proxy(snum);
1460         connection_struct *conn;
1461         NTSTATUS status;
1462         char *cwd;
1463 
1464         if(*connect_path == '\0') {
1465                 return 0;
1466         }
1467 
1468         /*
1469          * Fake up a connection struct for the VFS layer.
1470          */
1471 
1472         status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1473                                     NULL, &cwd);
1474         if (!NT_STATUS_IS_OK(status)) {
1475                 DEBUG(3, ("create_conn_struct failed: %s\n",
1476                           nt_errstr(status)));
1477                 return 0;
1478         }
1479 
1480         /* Count a link for the msdfs root - convention */
1481         cnt = 1;
1482 
1483         /* No more links if this is an msdfs proxy. */
1484         if (*msdfs_proxy != '\0') {
1485                 goto out;
1486         }
1487 
1488         /* Now enumerate all dfs links */
1489         dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1490         if(!dirp) {
1491                 goto out;
1492         }
1493 
1494         while ((dname = vfs_readdirname(conn, dirp, NULL)) != NULL) {
1495                 if (is_msdfs_link(conn,
1496                                 dname,
1497                                 NULL)) {
1498                         cnt++;
1499                 }
1500         }
1501 
1502         SMB_VFS_CLOSEDIR(conn,dirp);
1503 
1504 out:
1505         vfs_ChDir(conn, cwd);
1506         conn_free_internal(conn);
1507         return cnt;
1508 }
1509 
1510 /*********************************************************************
1511 *********************************************************************/
1512 
1513 static int form_junctions(TALLOC_CTX *ctx,
     /* [<][>][^][v][top][bottom][index][help] */
1514                                 int snum,
1515                                 struct junction_map *jucn,
1516                                 size_t jn_remain)
1517 {
1518         size_t cnt = 0;
1519         SMB_STRUCT_DIR *dirp = NULL;
1520         char *dname = NULL;
1521         const char *connect_path = lp_pathname(snum);
1522         char *service_name = lp_servicename(snum);
1523         const char *msdfs_proxy = lp_msdfs_proxy(snum);
1524         connection_struct *conn;
1525         struct referral *ref = NULL;
1526         char *cwd;
1527         NTSTATUS status;
1528 
1529         if (jn_remain == 0) {
1530                 return 0;
1531         }
1532 
1533         if(*connect_path == '\0') {
1534                 return 0;
1535         }
1536 
1537         /*
1538          * Fake up a connection struct for the VFS layer.
1539          */
1540 
1541         status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1542                                     &cwd);
1543         if (!NT_STATUS_IS_OK(status)) {
1544                 DEBUG(3, ("create_conn_struct failed: %s\n",
1545                           nt_errstr(status)));
1546                 return 0;
1547         }
1548 
1549         /* form a junction for the msdfs root - convention
1550            DO NOT REMOVE THIS: NT clients will not work with us
1551            if this is not present
1552         */
1553         jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1554         jucn[cnt].volume_name = talloc_strdup(ctx, "");
1555         if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1556                 goto out;
1557         }
1558         jucn[cnt].comment = "";
1559         jucn[cnt].referral_count = 1;
1560 
1561         ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1562         if (jucn[cnt].referral_list == NULL) {
1563                 goto out;
1564         }
1565 
1566         ref->proximity = 0;
1567         ref->ttl = REFERRAL_TTL;
1568         if (*msdfs_proxy != '\0') {
1569                 ref->alternate_path = talloc_strdup(ctx,
1570                                                 msdfs_proxy);
1571         } else {
1572                 ref->alternate_path = talloc_asprintf(ctx,
1573                         "\\\\%s\\%s",
1574                         get_local_machine_name(),
1575                         service_name);
1576         }
1577 
1578         if (!ref->alternate_path) {
1579                 goto out;
1580         }
1581         cnt++;
1582 
1583         /* Don't enumerate if we're an msdfs proxy. */
1584         if (*msdfs_proxy != '\0') {
1585                 goto out;
1586         }
1587 
1588         /* Now enumerate all dfs links */
1589         dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1590         if(!dirp) {
1591                 goto out;
1592         }
1593 
1594         while ((dname = vfs_readdirname(conn, dirp, NULL)) != NULL) {
1595                 char *link_target = NULL;
1596                 if (cnt >= jn_remain) {
1597                         DEBUG(2, ("form_junctions: ran out of MSDFS "
1598                                 "junction slots"));
1599                         goto out;
1600                 }
1601                 if (is_msdfs_link_internal(ctx,
1602                                         conn,
1603                                         dname, &link_target,
1604                                         NULL)) {
1605                         if (parse_msdfs_symlink(ctx,
1606                                         link_target,
1607                                         &jucn[cnt].referral_list,
1608                                         &jucn[cnt].referral_count)) {
1609 
1610                                 jucn[cnt].service_name = talloc_strdup(ctx,
1611                                                                 service_name);
1612                                 jucn[cnt].volume_name = talloc_strdup(ctx,
1613                                                                 dname);
1614                                 if (!jucn[cnt].service_name ||
1615                                                 !jucn[cnt].volume_name) {
1616                                         goto out;
1617                                 }
1618                                 jucn[cnt].comment = "";
1619                                 cnt++;
1620                         }
1621                         TALLOC_FREE(link_target);
1622                 }
1623         }
1624 
1625 out:
1626 
1627         if (dirp) {
1628                 SMB_VFS_CLOSEDIR(conn,dirp);
1629         }
1630 
1631         vfs_ChDir(conn, cwd);
1632         conn_free_internal(conn);
1633         return cnt;
1634 }
1635 
1636 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
     /* [<][>][^][v][top][bottom][index][help] */
1637 {
1638         struct junction_map *jn = NULL;
1639         int i=0;
1640         size_t jn_count = 0;
1641         int sharecount = 0;
1642 
1643         *p_num_jn = 0;
1644         if(!lp_host_msdfs()) {
1645                 return NULL;
1646         }
1647 
1648         /* Ensure all the usershares are loaded. */
1649         become_root();
1650         load_registry_shares();
1651         sharecount = load_usershare_shares();
1652         unbecome_root();
1653 
1654         for(i=0;i < sharecount;i++) {
1655                 if(lp_msdfs_root(i)) {
1656                         jn_count += count_dfs_links(ctx, i);
1657                 }
1658         }
1659         if (jn_count == 0) {
1660                 return NULL;
1661         }
1662         jn = TALLOC_ARRAY(ctx,  struct junction_map, jn_count);
1663         if (!jn) {
1664                 return NULL;
1665         }
1666         for(i=0; i < sharecount; i++) {
1667                 if (*p_num_jn >= jn_count) {
1668                         break;
1669                 }
1670                 if(lp_msdfs_root(i)) {
1671                         *p_num_jn += form_junctions(ctx, i,
1672                                         &jn[*p_num_jn],
1673                                         jn_count - *p_num_jn);
1674                 }
1675         }
1676         return jn;
1677 }
1678 
1679 /******************************************************************************
1680  Core function to resolve a dfs pathname.
1681 ******************************************************************************/
1682 
1683 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
     /* [<][>][^][v][top][bottom][index][help] */
1684                         connection_struct *conn,
1685                         bool dfs_pathnames,
1686                         const char *name_in,
1687                         char **pp_name_out)
1688 {
1689         NTSTATUS status = NT_STATUS_OK;
1690         bool dummy;
1691         if (dfs_pathnames) {
1692                 status = dfs_redirect(ctx,
1693                                         conn,
1694                                         name_in,
1695                                         False,
1696                                         pp_name_out,
1697                                         &dummy);
1698         } else {
1699                 /*
1700                  * Cheat and just return a copy of the in ptr.
1701                  * Once srvstr_get_path() uses talloc it'll
1702                  * be a talloced ptr anyway.
1703                  */
1704                 *pp_name_out = CONST_DISCARD(char *,name_in);
1705         }
1706         return status;
1707 }
1708 
1709 /******************************************************************************
1710  Core function to resolve a dfs pathname possibly containing a wildcard.
1711  This function is identical to the above except for the bool param to
1712  dfs_redirect but I need this to be separate so it's really clear when
1713  we're allowing wildcards and when we're not. JRA.
1714 ******************************************************************************/
1715 
1716 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
     /* [<][>][^][v][top][bottom][index][help] */
1717                                 connection_struct *conn,
1718                                 bool dfs_pathnames,
1719                                 const char *name_in,
1720                                 char **pp_name_out,
1721                                 bool *ppath_contains_wcard)
1722 {
1723         NTSTATUS status = NT_STATUS_OK;
1724         if (dfs_pathnames) {
1725                 status = dfs_redirect(ctx,
1726                                         conn,
1727                                         name_in,
1728                                         True,
1729                                         pp_name_out,
1730                                         ppath_contains_wcard);
1731         } else {
1732                 /*
1733                  * Cheat and just return a copy of the in ptr.
1734                  * Once srvstr_get_path() uses talloc it'll
1735                  * be a talloced ptr anyway.
1736                  */
1737                 *pp_name_out = CONST_DISCARD(char *,name_in);
1738         }
1739         return status;
1740 }

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