2 Unix SMB/Netbios implementation.
4 MSDFS services for Samba
5 Copyright (C) Shirish Kalele 2000
6 Copyright (C) Jeremy Allison 2007
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #define DBGC_CLASS DBGC_MSDFS
26 extern uint32 global_client_caps;
28 /**********************************************************************
29 Parse a DFS pathname of the form \hostname\service\reqpath
30 into the dfs_path structure.
31 If POSIX pathnames is true, the pathname may also be of the
32 form /hostname/service/reqpath.
33 We cope with either here.
35 Unfortunately, due to broken clients who might set the
36 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
37 send a local path, we have to cope with that too....
39 This version does everything using pointers within one copy of the
40 pathname string, talloced on the struct dfs_path pointer (which
41 must be talloced). This may be too clever to live....
43 **********************************************************************/
45 static NTSTATUS parse_dfs_path(const char *pathname,
47 struct dfs_path *pdp, /* MUST BE TALLOCED */
48 bool *ppath_contains_wcard)
54 NTSTATUS status = NT_STATUS_OK;
60 * This is the only talloc we should need to do
61 * on the struct dfs_path. All the pointers inside
62 * it should point to offsets within this string.
65 pathname_local = talloc_strdup(pdp, pathname);
66 if (!pathname_local) {
67 return NT_STATUS_NO_MEMORY;
69 /* Get a pointer to the terminating '\0' */
70 eos_ptr = &pathname_local[strlen(pathname_local)];
71 p = temp = pathname_local;
73 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
75 sepchar = pdp->posix_path ? '/' : '\\';
77 if (*pathname != sepchar) {
78 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
81 * Possibly client sent a local path by mistake.
82 * Try and convert to a local path.
85 pdp->hostname = eos_ptr; /* "" */
86 pdp->servicename = eos_ptr; /* "" */
88 /* We've got no info about separators. */
89 pdp->posix_path = lp_posix_pathnames();
91 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
98 * Safe to use on talloc'ed string as it only shrinks.
99 * It also doesn't affect the eos_ptr.
101 trim_char(temp,sepchar,sepchar);
103 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
107 /* Parse out hostname. */
108 p = strchr_m(temp,sepchar);
110 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
113 * Possibly client sent a local path by mistake.
114 * Try and convert to a local path.
117 pdp->hostname = eos_ptr; /* "" */
118 pdp->servicename = eos_ptr; /* "" */
121 DEBUG(10,("parse_dfs_path: trying to convert %s "
127 pdp->hostname = temp;
129 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
131 /* Parse out servicename. */
133 p = strchr_m(servicename,sepchar);
138 /* Is this really our servicename ? */
139 if (NULL == conn_find_byname(servicename)) {
140 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
144 * Possibly client sent a local path by mistake.
145 * Try and convert to a local path.
148 pdp->hostname = eos_ptr; /* "" */
149 pdp->servicename = eos_ptr; /* "" */
151 /* Repair the path - replace the sepchar's
154 *servicename = sepchar;
160 DEBUG(10,("parse_dfs_path: trying to convert %s "
166 pdp->servicename = servicename;
169 /* Client sent self referral \server\share. */
170 pdp->reqpath = eos_ptr; /* "" */
174 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
180 *ppath_contains_wcard = False;
184 /* Rest is reqpath. */
185 if (pdp->posix_path) {
186 status = check_path_syntax_posix(pdp->reqpath);
189 status = check_path_syntax_wcard(pdp->reqpath,
190 ppath_contains_wcard);
192 status = check_path_syntax(pdp->reqpath);
196 if (!NT_STATUS_IS_OK(status)) {
197 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
198 p, nt_errstr(status) ));
202 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
206 /********************************************************
207 Fake up a connection struct for the VFS layer.
208 Note this CHANGES CWD !!!! JRA.
209 *********************************************************/
211 static NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
212 connection_struct *conn,
220 connpath = talloc_strdup(ctx, path);
222 return NT_STATUS_NO_MEMORY;
224 connpath = talloc_string_sub(ctx,
227 lp_servicename(snum));
229 return NT_STATUS_NO_MEMORY;
232 /* needed for smbd_vfs_init() */
234 if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) {
235 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
236 return NT_STATUS_NO_MEMORY;
239 if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx,
240 struct share_params))) {
241 DEBUG(0, ("TALLOC failed\n"));
242 return NT_STATUS_NO_MEMORY;
245 conn->params->service = snum;
247 set_conn_connectpath(conn, connpath);
249 if (!smbd_vfs_init(conn)) {
250 NTSTATUS status = map_nt_error_from_unix(errno);
251 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
252 conn_free_internal(conn);
257 * Windows seems to insist on doing trans2getdfsreferral() calls on
258 * the IPC$ share as the anonymous user. If we try to chdir as that
259 * user we will fail.... WTF ? JRA.
262 if (vfs_ChDir(conn,conn->connectpath) != 0) {
263 NTSTATUS status = map_nt_error_from_unix(errno);
264 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
266 conn->connectpath, strerror(errno) ));
267 conn_free_internal(conn);
274 /**********************************************************************
275 Parse the contents of a symlink to verify if it is an msdfs referral
276 A valid referral is of the form:
278 msdfs:server1\share1,server2\share2
279 msdfs:server1\share1\pathname,server2\share2\pathname
280 msdfs:server1/share1,server2/share2
281 msdfs:server1/share1/pathname,server2/share2/pathname.
283 Note that the alternate paths returned here must be of the canonicalized
287 \server\share\path\to\file,
289 even in posix path mode. This is because we have no knowledge if the
290 server we're referring to understands posix paths.
291 **********************************************************************/
293 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
295 struct referral **preflist,
300 char **alt_path = NULL;
302 struct referral *reflist;
305 temp = talloc_strdup(ctx, target);
309 prot = strtok_r(temp, ":", &saveptr);
311 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
315 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
320 /* parse out the alternate paths */
321 while((count<MAX_REFERRAL_COUNT) &&
322 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
326 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
329 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
330 struct referral, count);
331 if(reflist == NULL) {
332 TALLOC_FREE(alt_path);
336 reflist = *preflist = NULL;
339 for(i=0;i<count;i++) {
342 /* Canonicalize link target.
343 * Replace all /'s in the path by a \ */
344 string_replace(alt_path[i], '/', '\\');
346 /* Remove leading '\\'s */
348 while (*p && (*p == '\\')) {
352 reflist[i].alternate_path = talloc_asprintf(ctx,
355 if (!reflist[i].alternate_path) {
359 reflist[i].proximity = 0;
360 reflist[i].ttl = REFERRAL_TTL;
361 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
362 reflist[i].alternate_path));
366 TALLOC_FREE(alt_path);
370 /**********************************************************************
371 Returns true if the unix path is a valid msdfs symlink and also
372 returns the target string from inside the link.
373 **********************************************************************/
375 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
376 connection_struct *conn,
378 char **pp_link_target,
379 SMB_STRUCT_STAT *sbufp)
382 int referral_len = 0;
383 char link_target_buf[7];
385 char *link_target = NULL;
387 if (pp_link_target) {
389 link_target = TALLOC_ARRAY(ctx, char, bufsize);
393 *pp_link_target = link_target;
395 bufsize = sizeof(link_target_buf);
396 link_target = link_target_buf;
403 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
404 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
409 if (!S_ISLNK(sbufp->st_mode)) {
410 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
415 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
416 if (referral_len == -1) {
417 DEBUG(0,("is_msdfs_link_read_target: Error reading "
418 "msdfs link %s: %s\n",
419 path, strerror(errno)));
422 link_target[referral_len] = '\0';
424 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
427 if (!strnequal(link_target, "msdfs:", 6)) {
434 if (link_target != link_target_buf) {
435 TALLOC_FREE(link_target);
440 /**********************************************************************
441 Returns true if the unix path is a valid msdfs symlink.
442 **********************************************************************/
444 bool is_msdfs_link(connection_struct *conn,
446 SMB_STRUCT_STAT *sbufp)
448 return is_msdfs_link_internal(talloc_tos(),
455 /*****************************************************************
456 Used by other functions to decide if a dfs path is remote,
457 and to get the list of referred locations for that remote path.
459 search_flag: For findfirsts, dfs links themselves are not
460 redirected, but paths beyond the links are. For normal smb calls,
461 even dfs links need to be redirected.
463 consumedcntp: how much of the dfs path is being redirected. the client
464 should try the remaining path on the redirected server.
466 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
467 link redirect are in targetpath.
468 *****************************************************************/
470 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
471 connection_struct *conn,
472 const char *dfspath, /* Incoming complete dfs path */
473 const struct dfs_path *pdp, /* Parsed out
474 server+share+extrapath. */
475 bool search_flag, /* Called from a findfirst ? */
477 char **pp_targetpath)
481 SMB_STRUCT_STAT sbuf;
483 char *localpath = NULL;
484 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
487 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
488 conn->connectpath, pdp->reqpath));
491 * Note the unix path conversion here we're doing we can
492 * throw away. We're looking for a symlink for a dfs
493 * resolution, if we don't find it we'll do another
494 * unix_convert later in the codepath.
495 * If we needed to remember what we'd resolved in
496 * dp->reqpath (as the original code did) we'd
497 * copy (localhost, dp->reqpath) on any code
498 * path below that returns True - but I don't
499 * think this is needed. JRA.
502 status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
504 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
505 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
509 /* Optimization - check if we can redirect the whole path. */
511 if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
513 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
514 "for dfs link %s.\n", dfspath));
518 DEBUG(6,("dfs_path_lookup: %s resolves to a "
519 "valid dfs link %s.\n", dfspath,
520 pp_targetpath ? *pp_targetpath : ""));
523 *consumedcntp = strlen(dfspath);
525 return NT_STATUS_PATH_NOT_COVERED;
528 /* Prepare to test only for '/' components in the given path,
529 * so if a Windows path replace all '\\' characters with '/'.
530 * For a POSIX DFS path we know all separators are already '/'. */
532 canon_dfspath = talloc_strdup(ctx, dfspath);
533 if (!canon_dfspath) {
534 return NT_STATUS_NO_MEMORY;
536 if (!pdp->posix_path) {
537 string_replace(canon_dfspath, '\\', '/');
541 * localpath comes out of unix_convert, so it has
542 * no trailing backslash. Make sure that canon_dfspath hasn't either.
543 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
546 trim_char(canon_dfspath,0,'/');
549 * Redirect if any component in the path is a link.
550 * We do this by walking backwards through the
551 * local path, chopping off the last component
552 * in both the local path and the canonicalized
553 * DFS path. If we hit a DFS link then we're done.
556 p = strrchr_m(localpath, '/');
558 q = strrchr_m(canon_dfspath, '/');
567 if (is_msdfs_link_internal(ctx, conn,
568 localpath, pp_targetpath, NULL)) {
569 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
570 "parent %s is dfs link\n", dfspath, localpath));
573 *consumedcntp = strlen(canon_dfspath);
574 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
580 return NT_STATUS_PATH_NOT_COVERED;
583 /* Step back on the filesystem. */
584 p = strrchr_m(localpath, '/');
587 /* And in the canonicalized dfs path. */
588 q = strrchr_m(canon_dfspath, '/');
595 /*****************************************************************
596 Decides if a dfs pathname should be redirected or not.
597 If not, the pathname is converted to a tcon-relative local unix path
599 search_wcard_flag: this flag performs 2 functions both related
600 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
603 This function can return NT_STATUS_OK, meaning use the returned path as-is
604 (mapped into a local path).
605 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
606 any other NT_STATUS error which is a genuine error to be
607 returned to the client.
608 *****************************************************************/
610 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
611 connection_struct *conn,
613 bool search_wcard_flag,
615 bool *ppath_contains_wcard)
618 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
621 return NT_STATUS_NO_MEMORY;
624 status = parse_dfs_path(path_in, search_wcard_flag, pdp,
625 ppath_contains_wcard);
626 if (!NT_STATUS_IS_OK(status)) {
631 if (pdp->reqpath[0] == '\0') {
633 *pp_path_out = talloc_strdup(ctx, "");
635 return NT_STATUS_NO_MEMORY;
637 DEBUG(5,("dfs_redirect: self-referral.\n"));
641 /* If dfs pathname for a non-dfs share, convert to tcon-relative
642 path and return OK */
644 if (!lp_msdfs_root(SNUM(conn))) {
645 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
648 return NT_STATUS_NO_MEMORY;
653 /* If it looked like a local path (zero hostname/servicename)
654 * just treat as a tcon-relative path. */
656 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
657 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
660 return NT_STATUS_NO_MEMORY;
665 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
666 || (strequal(pdp->servicename, HOMES_NAME)
667 && strequal(lp_servicename(SNUM(conn)),
668 get_current_username()) )) ) {
670 /* The given sharename doesn't match this connection. */
673 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
676 status = dfs_path_lookup(ctx, conn, path_in, pdp,
677 search_wcard_flag, NULL, NULL);
678 if (!NT_STATUS_IS_OK(status)) {
679 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
680 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
682 DEBUG(10,("dfs_redirect: dfs_path_lookup "
683 "failed for %s with %s\n",
684 path_in, nt_errstr(status) ));
689 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
691 /* Form non-dfs tcon-relative path */
692 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
695 return NT_STATUS_NO_MEMORY;
698 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
705 /**********************************************************************
706 Return a self referral.
707 **********************************************************************/
709 static NTSTATUS self_ref(TALLOC_CTX *ctx,
710 const char *dfs_path,
711 struct junction_map *jucn,
713 bool *self_referralp)
715 struct referral *ref;
717 *self_referralp = True;
719 jucn->referral_count = 1;
720 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
721 return NT_STATUS_NO_MEMORY;
724 ref->alternate_path = talloc_strdup(ctx, dfs_path);
725 if (!ref->alternate_path) {
726 return NT_STATUS_NO_MEMORY;
729 ref->ttl = REFERRAL_TTL;
730 jucn->referral_list = ref;
731 *consumedcntp = strlen(dfs_path);
735 /**********************************************************************
736 Gets valid referrals for a dfs path and fills up the
737 junction_map structure.
738 **********************************************************************/
740 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
741 const char *dfs_path,
742 struct junction_map *jucn,
744 bool *self_referralp)
746 struct connection_struct conns;
747 struct connection_struct *conn = &conns;
748 char *targetpath = NULL;
750 NTSTATUS status = NT_STATUS_NOT_FOUND;
752 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
755 return NT_STATUS_NO_MEMORY;
759 *self_referralp = False;
761 status = parse_dfs_path(dfs_path, False, pdp, &dummy);
762 if (!NT_STATUS_IS_OK(status)) {
766 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
767 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
768 if (!jucn->service_name || !jucn->volume_name) {
770 return NT_STATUS_NO_MEMORY;
773 /* Verify the share is a dfs root */
774 snum = lp_servicenumber(jucn->service_name);
776 fstring service_name;
777 fstrcpy(service_name, jucn->service_name);
778 if ((snum = find_service(service_name)) < 0) {
779 return NT_STATUS_NOT_FOUND;
781 TALLOC_FREE(jucn->service_name);
782 jucn->service_name = talloc_strdup(ctx, service_name);
783 if (!jucn->service_name) {
785 return NT_STATUS_NO_MEMORY;
789 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
790 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
792 pdp->servicename, dfs_path));
794 return NT_STATUS_NOT_FOUND;
798 * Self referrals are tested with a anonymous IPC connection and
799 * a GET_DFS_REFERRAL call to \\server\share. (which means
800 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
801 * into the directory and will fail if it cannot (as the anonymous
802 * user). Cope with this.
805 if (pdp->reqpath[0] == '\0') {
807 struct referral *ref;
809 if (*lp_msdfs_proxy(snum) == '\0') {
819 * It's an msdfs proxy share. Redirect to
820 * the configured target share.
823 jucn->referral_count = 1;
824 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
826 return NT_STATUS_NO_MEMORY;
829 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
831 return NT_STATUS_NO_MEMORY;
834 trim_string(tmp, "\\", 0);
836 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
839 if (!ref->alternate_path) {
841 return NT_STATUS_NO_MEMORY;
844 if (pdp->reqpath[0] != '\0') {
845 ref->alternate_path = talloc_asprintf_append(
849 if (!ref->alternate_path) {
851 return NT_STATUS_NO_MEMORY;
855 ref->ttl = REFERRAL_TTL;
856 jucn->referral_list = ref;
857 *consumedcntp = strlen(dfs_path);
862 status = create_conn_struct(ctx, conn, snum, lp_pathname(snum));
863 if (!NT_STATUS_IS_OK(status)) {
868 /* If this is a DFS path dfs_lookup should return
869 * NT_STATUS_PATH_NOT_COVERED. */
871 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
872 False, consumedcntp, &targetpath);
874 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
875 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
877 conn_free_internal(conn);
882 /* We know this is a valid dfs link. Parse the targetpath. */
883 if (!parse_msdfs_symlink(ctx, targetpath,
884 &jucn->referral_list,
885 &jucn->referral_count)) {
886 DEBUG(3,("get_referred_path: failed to parse symlink "
887 "target %s\n", targetpath ));
888 conn_free_internal(conn);
890 return NT_STATUS_NOT_FOUND;
893 conn_free_internal(conn);
898 static int setup_ver2_dfs_referral(const char *pathname,
900 struct junction_map *junction,
904 char* pdata = *ppdata;
906 smb_ucs2_t *uni_requestedpath = NULL;
907 int uni_reqpathoffset1,uni_reqpathoffset2;
909 int requestedpathlen=0;
914 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
916 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
917 &uni_requestedpath, pathname);
918 if (uni_requestedpath == NULL || requestedpathlen == 0) {
923 dump_data(0, (unsigned char *)uni_requestedpath,
927 DEBUG(10,("ref count = %u\n",junction->referral_count));
929 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
930 VERSION2_REFERRAL_SIZE * junction->referral_count;
932 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
934 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
936 reply_size = REFERRAL_HEADER_SIZE +
937 VERSION2_REFERRAL_SIZE*junction->referral_count +
938 2 * requestedpathlen;
939 DEBUG(10,("reply_size: %u\n",reply_size));
941 /* add up the unicode lengths of all the referral paths */
942 for(i=0;i<junction->referral_count;i++) {
943 DEBUG(10,("referral %u : %s\n",
945 junction->referral_list[i].alternate_path));
947 (strlen(junction->referral_list[i].alternate_path)+1)*2;
950 DEBUG(10,("reply_size = %u\n",reply_size));
951 /* add the unexplained 0x16 bytes */
954 pdata = (char *)SMB_REALLOC(pdata,reply_size);
956 DEBUG(0,("Realloc failed!\n"));
961 /* copy in the dfs requested paths.. required for offset calculations */
962 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
963 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
965 /* create the header */
966 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
967 /* number of referral in this pkt */
968 SSVAL(pdata,2,junction->referral_count);
970 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
972 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
976 /* add the referral elements */
977 for(i=0;i<junction->referral_count;i++) {
978 struct referral* ref = &junction->referral_list[i];
981 SSVAL(pdata,offset,2); /* version 2 */
982 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
984 SSVAL(pdata,offset+4,1);
986 SSVAL(pdata,offset+4,0);
989 /* ref_flags :use path_consumed bytes? */
990 SSVAL(pdata,offset+6,0);
991 SIVAL(pdata,offset+8,ref->proximity);
992 SIVAL(pdata,offset+12,ref->ttl);
994 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
995 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
996 /* copy referred path into current offset */
997 unilen = rpcstr_push(pdata+uni_curroffset,
999 reply_size - uni_curroffset,
1002 SSVAL(pdata,offset+20,uni_curroffset-offset);
1004 uni_curroffset += unilen;
1005 offset += VERSION2_REFERRAL_SIZE;
1007 /* add in the unexplained 22 (0x16) bytes at the end */
1008 memset(pdata+uni_curroffset,'\0',0x16);
1012 static int setup_ver3_dfs_referral(const char *pathname,
1014 struct junction_map *junction,
1018 char *pdata = *ppdata;
1020 smb_ucs2_t *uni_reqpath = NULL;
1021 int uni_reqpathoffset1, uni_reqpathoffset2;
1028 DEBUG(10,("setting up version3 referral\n"));
1030 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1031 if (uni_reqpath == NULL || reqpathlen == 0) {
1036 dump_data(0, (unsigned char *)uni_reqpath,
1040 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1041 VERSION3_REFERRAL_SIZE * junction->referral_count;
1042 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1043 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1045 for(i=0;i<junction->referral_count;i++) {
1046 DEBUG(10,("referral %u : %s\n",
1048 junction->referral_list[i].alternate_path));
1050 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1053 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1055 DEBUG(0,("version3 referral setup:"
1056 "malloc failed for Realloc!\n"));
1061 /* create the header */
1062 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
1063 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1065 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1067 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1070 /* copy in the reqpaths */
1071 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1072 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1075 for(i=0;i<junction->referral_count;i++) {
1076 struct referral* ref = &(junction->referral_list[i]);
1079 SSVAL(pdata,offset,3); /* version 3 */
1080 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1082 SSVAL(pdata,offset+4,1);
1084 SSVAL(pdata,offset+4,0);
1087 /* ref_flags :use path_consumed bytes? */
1088 SSVAL(pdata,offset+6,0);
1089 SIVAL(pdata,offset+8,ref->ttl);
1091 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1092 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1093 /* copy referred path into current offset */
1094 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1095 reply_size - uni_curroffset,
1096 STR_UNICODE | STR_TERMINATE);
1097 SSVAL(pdata,offset+16,uni_curroffset-offset);
1098 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1099 memset(pdata+offset+18,'\0',16);
1101 uni_curroffset += unilen;
1102 offset += VERSION3_REFERRAL_SIZE;
1107 /******************************************************************
1108 Set up the DFS referral for the dfs pathname. This call returns
1109 the amount of the path covered by this server, and where the
1110 client should be redirected to. This is the meat of the
1111 TRANS2_GET_DFS_REFERRAL call.
1112 ******************************************************************/
1114 int setup_dfs_referral(connection_struct *orig_conn,
1115 const char *dfs_path,
1116 int max_referral_level,
1117 char **ppdata, NTSTATUS *pstatus)
1119 struct junction_map *junction = NULL;
1120 int consumedcnt = 0;
1121 bool self_referral = False;
1123 char *pathnamep = NULL;
1124 char *local_dfs_path = NULL;
1127 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1128 *pstatus = NT_STATUS_NO_MEMORY;
1132 /* get the junction entry */
1134 talloc_destroy(ctx);
1135 *pstatus = NT_STATUS_NOT_FOUND;
1140 * Trim pathname sent by client so it begins with only one backslash.
1141 * Two backslashes confuse some dfs clients
1144 local_dfs_path = talloc_strdup(ctx,dfs_path);
1145 if (!local_dfs_path) {
1146 *pstatus = NT_STATUS_NO_MEMORY;
1147 talloc_destroy(ctx);
1150 pathnamep = local_dfs_path;
1151 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1152 IS_DIRECTORY_SEP(pathnamep[1])) {
1156 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1158 *pstatus = NT_STATUS_NO_MEMORY;
1159 talloc_destroy(ctx);
1163 /* The following call can change cwd. */
1164 *pstatus = get_referred_path(ctx, pathnamep, junction,
1165 &consumedcnt, &self_referral);
1166 if (!NT_STATUS_IS_OK(*pstatus)) {
1167 vfs_ChDir(orig_conn,orig_conn->connectpath);
1168 talloc_destroy(ctx);
1171 vfs_ChDir(orig_conn,orig_conn->connectpath);
1173 if (!self_referral) {
1174 pathnamep[consumedcnt] = '\0';
1176 if( DEBUGLVL( 3 ) ) {
1178 dbgtext("setup_dfs_referral: Path %s to "
1179 "alternate path(s):",
1181 for(i=0;i<junction->referral_count;i++)
1183 junction->referral_list[i].alternate_path);
1188 /* create the referral depeding on version */
1189 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1191 if (max_referral_level < 2) {
1192 max_referral_level = 2;
1194 if (max_referral_level > 3) {
1195 max_referral_level = 3;
1198 switch(max_referral_level) {
1200 reply_size = setup_ver2_dfs_referral(pathnamep,
1202 consumedcnt, self_referral);
1205 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1206 junction, consumedcnt, self_referral);
1209 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1211 max_referral_level));
1212 talloc_destroy(ctx);
1213 *pstatus = NT_STATUS_INVALID_LEVEL;
1218 DEBUGADD(0,("DFS Referral pdata:\n"));
1219 dump_data(0,(uint8 *)*ppdata,reply_size);
1222 talloc_destroy(ctx);
1223 *pstatus = NT_STATUS_OK;
1227 /**********************************************************************
1228 The following functions are called by the NETDFS RPC pipe functions
1229 **********************************************************************/
1231 /*********************************************************************
1232 Creates a junction structure from a DFS pathname
1233 **********************************************************************/
1235 bool create_junction(TALLOC_CTX *ctx,
1236 const char *dfs_path,
1237 struct junction_map *jucn)
1241 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1247 status = parse_dfs_path(dfs_path, False, pdp, &dummy);
1248 if (!NT_STATUS_IS_OK(status)) {
1252 /* check if path is dfs : validate first token */
1253 if (!is_myname_or_ipaddr(pdp->hostname)) {
1254 DEBUG(4,("create_junction: Invalid hostname %s "
1256 pdp->hostname, dfs_path));
1261 /* Check for a non-DFS share */
1262 snum = lp_servicenumber(pdp->servicename);
1264 if(snum < 0 || !lp_msdfs_root(snum)) {
1265 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1271 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1272 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1273 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1276 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1282 /**********************************************************************
1283 Forms a valid Unix pathname from the junction
1284 **********************************************************************/
1286 static bool junction_to_local_path(const struct junction_map *jucn,
1288 connection_struct *conn_out)
1292 snum = lp_servicenumber(jucn->service_name);
1296 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1298 lp_pathname(snum)))) {
1302 *pp_path_out = talloc_asprintf(conn_out->mem_ctx,
1306 if (!*pp_path_out) {
1312 bool create_msdfs_link(const struct junction_map *jucn,
1316 char *msdfs_link = NULL;
1317 connection_struct conns;
1318 connection_struct *conn = &conns;
1320 bool insert_comma = False;
1325 if(!junction_to_local_path(jucn, &path, conn)) {
1329 /* Form the msdfs_link contents */
1330 msdfs_link = talloc_strdup(conn->mem_ctx, "msdfs:");
1334 for(i=0; i<jucn->referral_count; i++) {
1335 char *refpath = jucn->referral_list[i].alternate_path;
1337 /* Alternate paths always use Windows separators. */
1338 trim_char(refpath, '\\', '\\');
1339 if(*refpath == '\0') {
1341 insert_comma = False;
1345 if (i > 0 && insert_comma) {
1346 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1350 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1358 if (!insert_comma) {
1359 insert_comma = True;
1363 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1367 if(SMB_VFS_UNLINK(conn,path)!=0) {
1372 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1373 DEBUG(1,("create_msdfs_link: symlink failed "
1374 "%s -> %s\nError: %s\n",
1375 path, msdfs_link, strerror(errno)));
1383 conn_free_internal(conn);
1387 bool remove_msdfs_link(const struct junction_map *jucn)
1390 connection_struct conns;
1391 connection_struct *conn = &conns;
1396 if( junction_to_local_path(jucn, &path, conn) ) {
1397 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1402 conn_free_internal(conn);
1406 /*********************************************************************
1407 Return the number of DFS links at the root of this share.
1408 *********************************************************************/
1410 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1413 SMB_STRUCT_DIR *dirp = NULL;
1415 const char *connect_path = lp_pathname(snum);
1416 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1417 connection_struct conn;
1421 if(*connect_path == '\0') {
1426 * Fake up a connection struct for the VFS layer.
1429 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1430 &conn, snum, connect_path))) {
1434 /* Count a link for the msdfs root - convention */
1437 /* No more links if this is an msdfs proxy. */
1438 if (*msdfs_proxy != '\0') {
1442 /* Now enumerate all dfs links */
1443 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1448 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1449 if (is_msdfs_link(&conn,
1456 SMB_VFS_CLOSEDIR(&conn,dirp);
1460 conn_free_internal(&conn);
1464 /*********************************************************************
1465 *********************************************************************/
1467 static int form_junctions(TALLOC_CTX *ctx,
1469 struct junction_map *jucn,
1473 SMB_STRUCT_DIR *dirp = NULL;
1475 const char *connect_path = lp_pathname(snum);
1476 char *service_name = lp_servicename(snum);
1477 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1478 connection_struct conn;
1479 struct referral *ref = NULL;
1483 if (jn_remain == 0) {
1487 if(*connect_path == '\0') {
1492 * Fake up a connection struct for the VFS layer.
1495 if (!NT_STATUS_IS_OK(create_conn_struct(ctx, &conn, snum, connect_path))) {
1499 /* form a junction for the msdfs root - convention
1500 DO NOT REMOVE THIS: NT clients will not work with us
1501 if this is not present
1503 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1504 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1505 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1508 jucn[cnt].referral_count = 1;
1510 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1511 if (jucn[cnt].referral_list == NULL) {
1516 ref->ttl = REFERRAL_TTL;
1517 if (*msdfs_proxy != '\0') {
1518 ref->alternate_path = talloc_strdup(ctx,
1521 ref->alternate_path = talloc_asprintf(ctx,
1523 get_local_machine_name(),
1527 if (!ref->alternate_path) {
1532 /* Don't enumerate if we're an msdfs proxy. */
1533 if (*msdfs_proxy != '\0') {
1537 /* Now enumerate all dfs links */
1538 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1543 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1544 char *link_target = NULL;
1545 if (cnt >= jn_remain) {
1546 SMB_VFS_CLOSEDIR(&conn,dirp);
1547 DEBUG(2, ("form_junctions: ran out of MSDFS "
1551 if (is_msdfs_link_internal(ctx,
1553 dname, &link_target,
1555 if (parse_msdfs_symlink(ctx,
1557 &jucn[cnt].referral_list,
1558 &jucn[cnt].referral_count)) {
1560 jucn[cnt].service_name = talloc_strdup(ctx,
1562 jucn[cnt].volume_name = talloc_strdup(ctx,
1564 if (!jucn[cnt].service_name ||
1565 !jucn[cnt].volume_name) {
1576 SMB_VFS_CLOSEDIR(&conn,dirp);
1579 conn_free_internal(&conn);
1583 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1585 struct junction_map *jn = NULL;
1587 size_t jn_count = 0;
1591 if(!lp_host_msdfs()) {
1595 /* Ensure all the usershares are loaded. */
1597 load_registry_shares();
1598 sharecount = load_usershare_shares();
1601 for(i=0;i < sharecount;i++) {
1602 if(lp_msdfs_root(i)) {
1603 jn_count += count_dfs_links(ctx, i);
1606 if (jn_count == 0) {
1609 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1613 for(i=0; i < sharecount; i++) {
1614 if (*p_num_jn >= jn_count) {
1617 if(lp_msdfs_root(i)) {
1618 *p_num_jn += form_junctions(ctx, i,
1620 jn_count - *p_num_jn);
1626 /******************************************************************************
1627 Core function to resolve a dfs pathname.
1628 ******************************************************************************/
1630 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1631 connection_struct *conn,
1633 const char *name_in,
1636 NTSTATUS status = NT_STATUS_OK;
1638 if (dfs_pathnames) {
1639 status = dfs_redirect(ctx,
1647 * Cheat and just return a copy of the in ptr.
1648 * Once srvstr_get_path() uses talloc it'll
1649 * be a talloced ptr anyway.
1651 *pp_name_out = CONST_DISCARD(char *,name_in);
1656 /******************************************************************************
1657 Core function to resolve a dfs pathname possibly containing a wildcard.
1658 This function is identical to the above except for the bool param to
1659 dfs_redirect but I need this to be separate so it's really clear when
1660 we're allowing wildcards and when we're not. JRA.
1661 ******************************************************************************/
1663 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1664 connection_struct *conn,
1666 const char *name_in,
1668 bool *ppath_contains_wcard)
1670 NTSTATUS status = NT_STATUS_OK;
1671 if (dfs_pathnames) {
1672 status = dfs_redirect(ctx,
1677 ppath_contains_wcard);
1680 * Cheat and just return a copy of the in ptr.
1681 * Once srvstr_get_path() uses talloc it'll
1682 * be a talloced ptr anyway.
1684 *pp_name_out = CONST_DISCARD(char *,name_in);