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)
53 NTSTATUS status = NT_STATUS_OK;
59 * This is the only talloc we should need to do
60 * on the struct dfs_path. All the pointers inside
61 * it should point to offsets within this string.
64 pathname_local = talloc_strdup(pdp, pathname);
65 if (!pathname_local) {
66 return NT_STATUS_NO_MEMORY;
68 /* Get a pointer to the terminating '\0' */
69 eos_ptr = &pathname_local[strlen(pathname_local)];
70 p = temp = pathname_local;
72 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
74 sepchar = pdp->posix_path ? '/' : '\\';
76 if (*pathname != sepchar) {
77 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
80 * Possibly client sent a local path by mistake.
81 * Try and convert to a local path.
84 pdp->hostname = eos_ptr; /* "" */
85 pdp->servicename = eos_ptr; /* "" */
87 /* We've got no info about separators. */
88 pdp->posix_path = lp_posix_pathnames();
90 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
97 * Safe to use on talloc'ed string as it only shrinks.
98 * It also doesn't affect the eos_ptr.
100 trim_char(temp,sepchar,sepchar);
102 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
106 /* Parse out hostname. */
107 p = strchr_m(temp,sepchar);
109 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
112 * Possibly client sent a local path by mistake.
113 * Try and convert to a local path.
116 pdp->hostname = eos_ptr; /* "" */
117 pdp->servicename = eos_ptr; /* "" */
120 DEBUG(10,("parse_dfs_path: trying to convert %s "
126 pdp->hostname = temp;
128 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
130 /* If we got a hostname, is it ours (or an IP address) ? */
131 if (!is_myname_or_ipaddr(pdp->hostname)) {
134 DEBUG(10,("parse_dfs_path: hostname %s isn't ours. "
135 "Try local path from path %s\n",
136 pdp->hostname, temp));
138 * Possibly client sent a local path by mistake.
139 * Try and convert to a local path.
142 pdp->hostname = eos_ptr; /* "" */
143 pdp->servicename = eos_ptr; /* "" */
146 DEBUG(10,("parse_dfs_path: trying to convert %s "
152 /* Parse out servicename. */
154 p = strchr_m(temp,sepchar);
156 pdp->servicename = temp;
157 pdp->reqpath = eos_ptr; /* "" */
161 pdp->servicename = temp;
162 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
168 *ppath_contains_wcard = False;
172 /* Rest is reqpath. */
173 if (pdp->posix_path) {
174 status = check_path_syntax_posix(pdp->reqpath);
177 status = check_path_syntax_wcard(pdp->reqpath,
178 ppath_contains_wcard);
180 status = check_path_syntax(pdp->reqpath);
184 if (!NT_STATUS_IS_OK(status)) {
185 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
186 p, nt_errstr(status) ));
190 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
194 /********************************************************
195 Fake up a connection struct for the VFS layer.
196 Note this CHANGES CWD !!!! JRA.
197 *********************************************************/
199 static NTSTATUS create_conn_struct(connection_struct *conn,
207 pstrcpy(connpath, path);
208 pstring_sub(connpath , "%S", lp_servicename(snum));
210 /* needed for smbd_vfs_init() */
212 if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) {
213 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
214 return NT_STATUS_NO_MEMORY;
217 if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx,
218 struct share_params))) {
219 DEBUG(0, ("TALLOC failed\n"));
220 return NT_STATUS_NO_MEMORY;
223 conn->params->service = snum;
225 set_conn_connectpath(conn, connpath);
227 if (!smbd_vfs_init(conn)) {
228 NTSTATUS status = map_nt_error_from_unix(errno);
229 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
230 conn_free_internal(conn);
235 * Windows seems to insist on doing trans2getdfsreferral() calls on
236 * the IPC$ share as the anonymous user. If we try to chdir as that
237 * user we will fail.... WTF ? JRA.
240 if (vfs_ChDir(conn,conn->connectpath) != 0) {
241 NTSTATUS status = map_nt_error_from_unix(errno);
242 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
244 conn->connectpath, strerror(errno) ));
245 conn_free_internal(conn);
252 /**********************************************************************
253 Parse the contents of a symlink to verify if it is an msdfs referral
254 A valid referral is of the form:
256 msdfs:server1\share1,server2\share2
257 msdfs:server1\share1\pathname,server2\share2\pathname
258 msdfs:server1/share1,server2/share2
259 msdfs:server1/share1/pathname,server2/share2/pathname.
261 Note that the alternate paths returned here must be of the canonicalized
265 \server\share\path\to\file,
267 even in posix path mode. This is because we have no knowledge if the
268 server we're referring to understands posix paths.
269 **********************************************************************/
271 static BOOL parse_msdfs_symlink(TALLOC_CTX *ctx,
273 struct referral **preflist,
278 char **alt_path = NULL;
280 struct referral *reflist;
282 temp = talloc_strdup(ctx, target);
286 prot = strtok(temp,":");
288 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
292 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
297 /* parse out the alternate paths */
298 while((count<MAX_REFERRAL_COUNT) &&
299 ((alt_path[count] = strtok(NULL,",")) != NULL)) {
303 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
306 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
307 struct referral, count);
308 if(reflist == NULL) {
309 TALLOC_FREE(alt_path);
313 reflist = *preflist = NULL;
316 for(i=0;i<count;i++) {
319 /* Canonicalize link target.
320 * Replace all /'s in the path by a \ */
321 string_replace(alt_path[i], '/', '\\');
323 /* Remove leading '\\'s */
325 while (*p && (*p == '\\')) {
329 reflist[i].alternate_path = talloc_asprintf(ctx,
332 if (!reflist[i].alternate_path) {
336 reflist[i].proximity = 0;
337 reflist[i].ttl = REFERRAL_TTL;
338 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
339 reflist[i].alternate_path));
343 TALLOC_FREE(alt_path);
347 /**********************************************************************
348 Returns true if the unix path is a valid msdfs symlink and also
349 returns the target string from inside the link.
350 **********************************************************************/
352 static BOOL is_msdfs_link_internal(TALLOC_CTX *ctx,
353 connection_struct *conn,
355 char **pp_link_target,
356 SMB_STRUCT_STAT *sbufp)
359 int referral_len = 0;
360 char link_target_buf[7];
362 char *link_target = NULL;
364 if (pp_link_target) {
366 link_target = TALLOC_ARRAY(ctx, char, bufsize);
370 *pp_link_target = link_target;
372 bufsize = sizeof(link_target_buf);
373 link_target = link_target_buf;
380 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
381 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
386 if (!S_ISLNK(sbufp->st_mode)) {
387 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
392 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
393 if (referral_len == -1) {
394 DEBUG(0,("is_msdfs_link_read_target: Error reading "
395 "msdfs link %s: %s\n",
396 path, strerror(errno)));
399 link_target[referral_len] = '\0';
401 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
404 if (!strnequal(link_target, "msdfs:", 6)) {
411 if (link_target != link_target_buf) {
412 TALLOC_FREE(link_target);
417 /**********************************************************************
418 Returns true if the unix path is a valid msdfs symlink.
419 **********************************************************************/
421 BOOL is_msdfs_link(connection_struct *conn,
423 SMB_STRUCT_STAT *sbufp)
425 return is_msdfs_link_internal(talloc_tos(),
432 /*****************************************************************
433 Used by other functions to decide if a dfs path is remote,
434 and to get the list of referred locations for that remote path.
436 search_flag: For findfirsts, dfs links themselves are not
437 redirected, but paths beyond the links are. For normal smb calls,
438 even dfs links need to be redirected.
440 consumedcntp: how much of the dfs path is being redirected. the client
441 should try the remaining path on the redirected server.
443 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
444 link redirect are in targetpath.
445 *****************************************************************/
447 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
448 connection_struct *conn,
449 const char *dfspath, /* Incoming complete dfs path */
450 const struct dfs_path *pdp, /* Parsed out
451 server+share+extrapath. */
452 BOOL search_flag, /* Called from a findfirst ? */
454 char **pp_targetpath)
458 SMB_STRUCT_STAT sbuf;
460 char *localpath = NULL;
461 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
464 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
465 conn->connectpath, pdp->reqpath));
468 * Note the unix path conversion here we're doing we can
469 * throw away. We're looking for a symlink for a dfs
470 * resolution, if we don't find it we'll do another
471 * unix_convert later in the codepath.
472 * If we needed to remember what we'd resolved in
473 * dp->reqpath (as the original code did) we'd
474 * pstrcpy(localhost, dp->reqpath) on any code
475 * path below that returns True - but I don't
476 * think this is needed. JRA.
479 status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
481 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
482 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
486 /* Optimization - check if we can redirect the whole path. */
488 if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
490 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
491 "for dfs link %s.\n", dfspath));
495 DEBUG(6,("dfs_path_lookup: %s resolves to a "
496 "valid dfs link %s.\n", dfspath,
497 pp_targetpath ? *pp_targetpath : ""));
500 *consumedcntp = strlen(dfspath);
502 return NT_STATUS_PATH_NOT_COVERED;
505 /* Prepare to test only for '/' components in the given path,
506 * so if a Windows path replace all '\\' characters with '/'.
507 * For a POSIX DFS path we know all separators are already '/'. */
509 canon_dfspath = talloc_strdup(ctx, dfspath);
510 if (!canon_dfspath) {
511 return NT_STATUS_NO_MEMORY;
513 if (!pdp->posix_path) {
514 string_replace(canon_dfspath, '\\', '/');
518 * localpath comes out of unix_convert, so it has
519 * no trailing backslash. Make sure that canon_dfspath hasn't either.
520 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
523 trim_char(canon_dfspath,0,'/');
526 * Redirect if any component in the path is a link.
527 * We do this by walking backwards through the
528 * local path, chopping off the last component
529 * in both the local path and the canonicalized
530 * DFS path. If we hit a DFS link then we're done.
533 p = strrchr_m(localpath, '/');
535 q = strrchr_m(canon_dfspath, '/');
544 if (is_msdfs_link_internal(ctx, conn,
545 localpath, pp_targetpath, NULL)) {
546 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
547 "parent %s is dfs link\n", dfspath, localpath));
550 *consumedcntp = strlen(canon_dfspath);
551 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
557 return NT_STATUS_PATH_NOT_COVERED;
560 /* Step back on the filesystem. */
561 p = strrchr_m(localpath, '/');
564 /* And in the canonicalized dfs path. */
565 q = strrchr_m(canon_dfspath, '/');
572 /*****************************************************************
573 Decides if a dfs pathname should be redirected or not.
574 If not, the pathname is converted to a tcon-relative local unix path
576 search_wcard_flag: this flag performs 2 functions both related
577 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
580 This function can return NT_STATUS_OK, meaning use the returned path as-is
581 (mapped into a local path).
582 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
583 any other NT_STATUS error which is a genuine error to be
584 returned to the client.
585 *****************************************************************/
587 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
588 connection_struct *conn,
590 BOOL search_wcard_flag,
592 BOOL *ppath_contains_wcard)
595 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
598 return NT_STATUS_NO_MEMORY;
601 status = parse_dfs_path(path_in, search_wcard_flag, pdp,
602 ppath_contains_wcard);
603 if (!NT_STATUS_IS_OK(status)) {
608 if (pdp->reqpath[0] == '\0') {
610 *pp_path_out = talloc_strdup(ctx, "");
612 return NT_STATUS_NO_MEMORY;
614 DEBUG(5,("dfs_redirect: self-referral.\n"));
618 /* If dfs pathname for a non-dfs share, convert to tcon-relative
619 path and return OK */
621 if (!lp_msdfs_root(SNUM(conn))) {
622 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
625 return NT_STATUS_NO_MEMORY;
630 /* If it looked like a local path (zero hostname/servicename)
631 * just treat as a tcon-relative path. */
633 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
634 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
637 return NT_STATUS_NO_MEMORY;
642 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
643 || (strequal(pdp->servicename, HOMES_NAME)
644 && strequal(lp_servicename(SNUM(conn)),
645 get_current_username()) )) ) {
647 /* The given sharename doesn't match this connection. */
650 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
653 status = dfs_path_lookup(ctx, conn, path_in, pdp,
654 search_wcard_flag, NULL, NULL);
655 if (!NT_STATUS_IS_OK(status)) {
656 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
657 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
659 DEBUG(10,("dfs_redirect: dfs_path_lookup "
660 "failed for %s with %s\n",
661 path_in, nt_errstr(status) ));
666 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
668 /* Form non-dfs tcon-relative path */
669 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
672 return NT_STATUS_NO_MEMORY;
675 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
682 /**********************************************************************
683 Return a self referral.
684 **********************************************************************/
686 static NTSTATUS self_ref(TALLOC_CTX *ctx,
687 const char *dfs_path,
688 struct junction_map *jucn,
690 BOOL *self_referralp)
692 struct referral *ref;
694 *self_referralp = True;
696 jucn->referral_count = 1;
697 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
698 return NT_STATUS_NO_MEMORY;
701 ref->alternate_path = talloc_strdup(ctx, dfs_path);
702 if (!ref->alternate_path) {
703 return NT_STATUS_NO_MEMORY;
706 ref->ttl = REFERRAL_TTL;
707 jucn->referral_list = ref;
708 *consumedcntp = strlen(dfs_path);
712 /**********************************************************************
713 Gets valid referrals for a dfs path and fills up the
714 junction_map structure.
715 **********************************************************************/
717 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
718 const char *dfs_path,
719 struct junction_map *jucn,
721 BOOL *self_referralp)
723 struct connection_struct conns;
724 struct connection_struct *conn = &conns;
725 char *targetpath = NULL;
727 NTSTATUS status = NT_STATUS_NOT_FOUND;
729 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
732 return NT_STATUS_NO_MEMORY;
736 *self_referralp = False;
738 status = parse_dfs_path(dfs_path, False, pdp, &dummy);
739 if (!NT_STATUS_IS_OK(status)) {
743 /* Verify hostname in path */
744 if (!is_myname_or_ipaddr(pdp->hostname)) {
745 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
746 pdp->hostname, dfs_path));
748 return NT_STATUS_NOT_FOUND;
751 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
752 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
753 if (!jucn->service_name || !jucn->volume_name) {
755 return NT_STATUS_NO_MEMORY;
758 /* Verify the share is a dfs root */
759 snum = lp_servicenumber(jucn->service_name);
761 fstring service_name;
762 fstrcpy(service_name, jucn->service_name);
763 if ((snum = find_service(service_name)) < 0) {
764 return NT_STATUS_NOT_FOUND;
766 TALLOC_FREE(jucn->service_name);
767 jucn->service_name = talloc_strdup(ctx, service_name);
768 if (!jucn->service_name) {
770 return NT_STATUS_NO_MEMORY;
774 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
775 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
777 pdp->servicename, dfs_path));
779 return NT_STATUS_NOT_FOUND;
783 * Self referrals are tested with a anonymous IPC connection and
784 * a GET_DFS_REFERRAL call to \\server\share. (which means
785 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
786 * into the directory and will fail if it cannot (as the anonymous
787 * user). Cope with this.
790 if (pdp->reqpath[0] == '\0') {
792 struct referral *ref;
794 if (*lp_msdfs_proxy(snum) == '\0') {
804 * It's an msdfs proxy share. Redirect to
805 * the configured target share.
808 jucn->referral_count = 1;
809 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
811 return NT_STATUS_NO_MEMORY;
814 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
816 return NT_STATUS_NO_MEMORY;
819 trim_string(tmp, "\\", 0);
821 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
824 if (!ref->alternate_path) {
826 return NT_STATUS_NO_MEMORY;
829 if (pdp->reqpath[0] != '\0') {
830 ref->alternate_path = talloc_asprintf_append(
834 if (!ref->alternate_path) {
836 return NT_STATUS_NO_MEMORY;
840 ref->ttl = REFERRAL_TTL;
841 jucn->referral_list = ref;
842 *consumedcntp = strlen(dfs_path);
847 status = create_conn_struct(conn, snum, lp_pathname(snum));
848 if (!NT_STATUS_IS_OK(status)) {
853 /* If this is a DFS path dfs_lookup should return
854 * NT_STATUS_PATH_NOT_COVERED. */
856 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
857 False, consumedcntp, &targetpath);
859 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
860 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
862 conn_free_internal(conn);
867 /* We know this is a valid dfs link. Parse the targetpath. */
868 if (!parse_msdfs_symlink(ctx, targetpath,
869 &jucn->referral_list,
870 &jucn->referral_count)) {
871 DEBUG(3,("get_referred_path: failed to parse symlink "
872 "target %s\n", targetpath ));
873 conn_free_internal(conn);
875 return NT_STATUS_NOT_FOUND;
878 conn_free_internal(conn);
883 static int setup_ver2_dfs_referral(const char *pathname,
885 struct junction_map *junction,
889 char* pdata = *ppdata;
891 unsigned char uni_requestedpath[sizeof(pstring)];
892 int uni_reqpathoffset1,uni_reqpathoffset2;
894 int requestedpathlen=0;
899 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
901 requestedpathlen = rpcstr_push(uni_requestedpath,
902 pathname, sizeof(pstring),
906 dump_data(0, uni_requestedpath,requestedpathlen);
909 DEBUG(10,("ref count = %u\n",junction->referral_count));
911 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
912 VERSION2_REFERRAL_SIZE * junction->referral_count;
914 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
916 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
918 reply_size = REFERRAL_HEADER_SIZE +
919 VERSION2_REFERRAL_SIZE*junction->referral_count +
920 2 * requestedpathlen;
921 DEBUG(10,("reply_size: %u\n",reply_size));
923 /* add up the unicode lengths of all the referral paths */
924 for(i=0;i<junction->referral_count;i++) {
925 DEBUG(10,("referral %u : %s\n",
927 junction->referral_list[i].alternate_path));
929 (strlen(junction->referral_list[i].alternate_path)+1)*2;
932 DEBUG(10,("reply_size = %u\n",reply_size));
933 /* add the unexplained 0x16 bytes */
936 pdata = (char *)SMB_REALLOC(pdata,reply_size);
938 DEBUG(0,("Realloc failed!\n"));
943 /* copy in the dfs requested paths.. required for offset calculations */
944 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
945 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
947 /* create the header */
948 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
949 /* number of referral in this pkt */
950 SSVAL(pdata,2,junction->referral_count);
952 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
954 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
958 /* add the referral elements */
959 for(i=0;i<junction->referral_count;i++) {
960 struct referral* ref = &junction->referral_list[i];
963 SSVAL(pdata,offset,2); /* version 2 */
964 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
966 SSVAL(pdata,offset+4,1);
968 SSVAL(pdata,offset+4,0);
971 /* ref_flags :use path_consumed bytes? */
972 SSVAL(pdata,offset+6,0);
973 SIVAL(pdata,offset+8,ref->proximity);
974 SIVAL(pdata,offset+12,ref->ttl);
976 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
977 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
978 /* copy referred path into current offset */
979 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
980 sizeof(pstring), STR_UNICODE);
982 SSVAL(pdata,offset+20,uni_curroffset-offset);
984 uni_curroffset += unilen;
985 offset += VERSION2_REFERRAL_SIZE;
987 /* add in the unexplained 22 (0x16) bytes at the end */
988 memset(pdata+uni_curroffset,'\0',0x16);
992 static int setup_ver3_dfs_referral(const char *pathname,
994 struct junction_map *junction,
998 char *pdata = *ppdata;
1000 unsigned char uni_reqpath[sizeof(pstring)];
1001 int uni_reqpathoffset1, uni_reqpathoffset2;
1008 DEBUG(10,("setting up version3 referral\n"));
1010 reqpathlen = rpcstr_push(uni_reqpath, pathname,
1011 sizeof(pstring), STR_TERMINATE);
1014 dump_data(0, uni_reqpath,reqpathlen);
1017 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1018 VERSION3_REFERRAL_SIZE * junction->referral_count;
1019 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1020 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1022 for(i=0;i<junction->referral_count;i++) {
1023 DEBUG(10,("referral %u : %s\n",
1025 junction->referral_list[i].alternate_path));
1027 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1030 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1032 DEBUG(0,("version3 referral setup:"
1033 "malloc failed for Realloc!\n"));
1038 /* create the header */
1039 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
1040 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1042 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1044 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1047 /* copy in the reqpaths */
1048 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1049 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1052 for(i=0;i<junction->referral_count;i++) {
1053 struct referral* ref = &(junction->referral_list[i]);
1056 SSVAL(pdata,offset,3); /* version 3 */
1057 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1059 SSVAL(pdata,offset+4,1);
1061 SSVAL(pdata,offset+4,0);
1064 /* ref_flags :use path_consumed bytes? */
1065 SSVAL(pdata,offset+6,0);
1066 SIVAL(pdata,offset+8,ref->ttl);
1068 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1069 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1070 /* copy referred path into current offset */
1071 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1073 STR_UNICODE | STR_TERMINATE);
1074 SSVAL(pdata,offset+16,uni_curroffset-offset);
1075 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1076 memset(pdata+offset+18,'\0',16);
1078 uni_curroffset += unilen;
1079 offset += VERSION3_REFERRAL_SIZE;
1084 /******************************************************************
1085 Set up the DFS referral for the dfs pathname. This call returns
1086 the amount of the path covered by this server, and where the
1087 client should be redirected to. This is the meat of the
1088 TRANS2_GET_DFS_REFERRAL call.
1089 ******************************************************************/
1091 int setup_dfs_referral(connection_struct *orig_conn,
1092 const char *dfs_path,
1093 int max_referral_level,
1094 char **ppdata, NTSTATUS *pstatus)
1096 struct junction_map *junction = NULL;
1097 int consumedcnt = 0;
1098 BOOL self_referral = False;
1100 char *pathnamep = NULL;
1101 char *local_dfs_path = NULL;
1104 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1105 *pstatus = NT_STATUS_NO_MEMORY;
1109 /* get the junction entry */
1111 talloc_destroy(ctx);
1112 *pstatus = NT_STATUS_NOT_FOUND;
1117 * Trim pathname sent by client so it begins with only one backslash.
1118 * Two backslashes confuse some dfs clients
1121 local_dfs_path = talloc_strdup(ctx,dfs_path);
1122 if (!local_dfs_path) {
1123 *pstatus = NT_STATUS_NO_MEMORY;
1124 talloc_destroy(ctx);
1127 pathnamep = local_dfs_path;
1128 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1129 IS_DIRECTORY_SEP(pathnamep[1])) {
1133 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1135 *pstatus = NT_STATUS_NO_MEMORY;
1136 talloc_destroy(ctx);
1140 /* The following call can change cwd. */
1141 *pstatus = get_referred_path(ctx, pathnamep, junction,
1142 &consumedcnt, &self_referral);
1143 if (!NT_STATUS_IS_OK(*pstatus)) {
1144 vfs_ChDir(orig_conn,orig_conn->connectpath);
1145 talloc_destroy(ctx);
1148 vfs_ChDir(orig_conn,orig_conn->connectpath);
1150 if (!self_referral) {
1151 pathnamep[consumedcnt] = '\0';
1153 if( DEBUGLVL( 3 ) ) {
1155 dbgtext("setup_dfs_referral: Path %s to "
1156 "alternate path(s):",
1158 for(i=0;i<junction->referral_count;i++)
1160 junction->referral_list[i].alternate_path);
1165 /* create the referral depeding on version */
1166 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1168 if (max_referral_level < 2) {
1169 max_referral_level = 2;
1171 if (max_referral_level > 3) {
1172 max_referral_level = 3;
1175 switch(max_referral_level) {
1177 reply_size = setup_ver2_dfs_referral(pathnamep,
1179 consumedcnt, self_referral);
1182 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1183 junction, consumedcnt, self_referral);
1186 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1188 max_referral_level));
1189 talloc_destroy(ctx);
1190 *pstatus = NT_STATUS_INVALID_LEVEL;
1195 DEBUGADD(0,("DFS Referral pdata:\n"));
1196 dump_data(0,(uint8 *)*ppdata,reply_size);
1199 talloc_destroy(ctx);
1200 *pstatus = NT_STATUS_OK;
1204 /**********************************************************************
1205 The following functions are called by the NETDFS RPC pipe functions
1206 **********************************************************************/
1208 /*********************************************************************
1209 Creates a junction structure from a DFS pathname
1210 **********************************************************************/
1212 BOOL create_junction(TALLOC_CTX *ctx,
1213 const char *dfs_path,
1214 struct junction_map *jucn)
1218 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1224 status = parse_dfs_path(dfs_path, False, pdp, &dummy);
1225 if (!NT_STATUS_IS_OK(status)) {
1229 /* check if path is dfs : validate first token */
1230 if (!is_myname_or_ipaddr(pdp->hostname)) {
1231 DEBUG(4,("create_junction: Invalid hostname %s "
1233 pdp->hostname, dfs_path));
1238 /* Check for a non-DFS share */
1239 snum = lp_servicenumber(pdp->servicename);
1241 if(snum < 0 || !lp_msdfs_root(snum)) {
1242 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1248 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1249 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1250 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1253 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1259 /**********************************************************************
1260 Forms a valid Unix pathname from the junction
1261 **********************************************************************/
1263 static BOOL junction_to_local_path(const struct junction_map *jucn,
1265 connection_struct *conn_out)
1269 snum = lp_servicenumber(jucn->service_name);
1273 if (!NT_STATUS_IS_OK(create_conn_struct(conn_out, snum,
1274 lp_pathname(snum)))) {
1278 *pp_path_out = talloc_asprintf(conn_out->mem_ctx,
1282 if (!*pp_path_out) {
1288 BOOL create_msdfs_link(const struct junction_map *jucn,
1292 char *msdfs_link = NULL;
1293 connection_struct conns;
1294 connection_struct *conn = &conns;
1296 BOOL insert_comma = False;
1301 if(!junction_to_local_path(jucn, &path, conn)) {
1305 /* Form the msdfs_link contents */
1306 msdfs_link = talloc_strdup(conn->mem_ctx, "msdfs:");
1310 for(i=0; i<jucn->referral_count; i++) {
1311 char *refpath = jucn->referral_list[i].alternate_path;
1313 /* Alternate paths always use Windows separators. */
1314 trim_char(refpath, '\\', '\\');
1315 if(*refpath == '\0') {
1317 insert_comma = False;
1321 if (i > 0 && insert_comma) {
1322 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1326 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1334 if (!insert_comma) {
1335 insert_comma = True;
1339 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1343 if(SMB_VFS_UNLINK(conn,path)!=0) {
1348 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1349 DEBUG(1,("create_msdfs_link: symlink failed "
1350 "%s -> %s\nError: %s\n",
1351 path, msdfs_link, strerror(errno)));
1359 conn_free_internal(conn);
1363 BOOL remove_msdfs_link(const struct junction_map *jucn)
1366 connection_struct conns;
1367 connection_struct *conn = &conns;
1372 if( junction_to_local_path(jucn, &path, conn) ) {
1373 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1378 conn_free_internal(conn);
1382 /*********************************************************************
1383 Return the number of DFS links at the root of this share.
1384 *********************************************************************/
1386 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1389 SMB_STRUCT_DIR *dirp = NULL;
1391 const char *connect_path = lp_pathname(snum);
1392 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1393 connection_struct conn;
1397 if(*connect_path == '\0') {
1402 * Fake up a connection struct for the VFS layer.
1405 if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
1409 /* Count a link for the msdfs root - convention */
1412 /* No more links if this is an msdfs proxy. */
1413 if (*msdfs_proxy != '\0') {
1417 /* Now enumerate all dfs links */
1418 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1423 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1424 if (is_msdfs_link(&conn,
1431 SMB_VFS_CLOSEDIR(&conn,dirp);
1435 conn_free_internal(&conn);
1439 /*********************************************************************
1440 *********************************************************************/
1442 static int form_junctions(TALLOC_CTX *ctx,
1444 struct junction_map *jucn,
1448 SMB_STRUCT_DIR *dirp = NULL;
1450 const char *connect_path = lp_pathname(snum);
1451 char *service_name = lp_servicename(snum);
1452 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1453 connection_struct conn;
1454 struct referral *ref = NULL;
1458 if (jn_remain == 0) {
1462 if(*connect_path == '\0') {
1467 * Fake up a connection struct for the VFS layer.
1470 if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
1474 /* form a junction for the msdfs root - convention
1475 DO NOT REMOVE THIS: NT clients will not work with us
1476 if this is not present
1478 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1479 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1480 if (!jucn[cnt].service_name || jucn[cnt].volume_name) {
1483 jucn[cnt].referral_count = 1;
1485 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1486 if (jucn[cnt].referral_list == NULL) {
1491 ref->ttl = REFERRAL_TTL;
1492 if (*msdfs_proxy != '\0') {
1493 ref->alternate_path = talloc_strdup(ctx,
1496 ref->alternate_path = talloc_asprintf(ctx,
1498 get_local_machine_name(),
1502 if (!ref->alternate_path) {
1507 /* Don't enumerate if we're an msdfs proxy. */
1508 if (*msdfs_proxy != '\0') {
1512 /* Now enumerate all dfs links */
1513 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1518 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1519 char *link_target = NULL;
1520 if (cnt >= jn_remain) {
1521 SMB_VFS_CLOSEDIR(&conn,dirp);
1522 DEBUG(2, ("form_junctions: ran out of MSDFS "
1526 if (is_msdfs_link_internal(ctx,
1528 dname, &link_target,
1530 if (parse_msdfs_symlink(ctx,
1532 &jucn[cnt].referral_list,
1533 &jucn[cnt].referral_count)) {
1535 jucn[cnt].service_name = talloc_strdup(ctx,
1537 jucn[cnt].volume_name = talloc_strdup(ctx,
1539 if (!jucn[cnt].service_name ||
1540 !jucn[cnt].volume_name) {
1551 SMB_VFS_CLOSEDIR(&conn,dirp);
1554 conn_free_internal(&conn);
1558 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1560 struct junction_map *jn = NULL;
1562 size_t jn_count = 0;
1566 if(!lp_host_msdfs()) {
1570 /* Ensure all the usershares are loaded. */
1572 load_registry_shares();
1573 sharecount = load_usershare_shares();
1576 for(i=0;i < sharecount;i++) {
1577 if(lp_msdfs_root(i)) {
1578 jn_count += count_dfs_links(ctx, i);
1581 if (jn_count == 0) {
1584 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1588 for(i=0; i < sharecount; i++) {
1589 if (*p_num_jn >= jn_count) {
1592 if(lp_msdfs_root(i)) {
1593 *p_num_jn += form_junctions(ctx, i,
1595 jn_count - *p_num_jn);
1601 /******************************************************************************
1602 Core function to resolve a dfs pathname.
1603 ******************************************************************************/
1605 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1606 connection_struct *conn,
1608 const char *name_in,
1611 NTSTATUS status = NT_STATUS_OK;
1613 if (dfs_pathnames) {
1614 status = dfs_redirect(ctx,
1622 * Cheat and just return a copy of the in ptr.
1623 * Once srvstr_get_path() uses talloc it'll
1624 * be a talloced ptr anyway.
1626 *pp_name_out = CONST_DISCARD(char *,name_in);
1631 /******************************************************************************
1632 Core function to resolve a dfs pathname possibly containing a wildcard.
1633 This function is identical to the above except for the BOOL param to
1634 dfs_redirect but I need this to be separate so it's really clear when
1635 we're allowing wildcards and when we're not. JRA.
1636 ******************************************************************************/
1638 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1639 connection_struct *conn,
1641 const char *name_in,
1643 BOOL *ppath_contains_wcard)
1645 NTSTATUS status = NT_STATUS_OK;
1646 if (dfs_pathnames) {
1647 status = dfs_redirect(ctx,
1652 ppath_contains_wcard);
1655 * Cheat and just return a copy of the in ptr.
1656 * Once srvstr_get_path() uses talloc it'll
1657 * be a talloced ptr anyway.
1659 *pp_name_out = CONST_DISCARD(char *,name_in);