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....
40 **********************************************************************/
42 static NTSTATUS parse_dfs_path(const char *pathname,
45 BOOL *ppath_contains_wcard)
47 pstring pathname_local;
49 NTSTATUS status = NT_STATUS_OK;
54 pstrcpy(pathname_local,pathname);
55 p = temp = pathname_local;
57 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
59 sepchar = pdp->posix_path ? '/' : '\\';
61 if (*pathname != sepchar) {
62 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
65 * Possibly client sent a local path by mistake.
66 * Try and convert to a local path.
69 pdp->hostname[0] = '\0';
70 pdp->servicename[0] = '\0';
72 /* We've got no info about separators. */
73 pdp->posix_path = lp_posix_pathnames();
75 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
80 trim_char(temp,sepchar,sepchar);
82 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
86 /* Parse out hostname. */
87 p = strchr_m(temp,sepchar);
89 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
92 * Possibly client sent a local path by mistake.
93 * Try and convert to a local path.
96 pdp->hostname[0] = '\0';
97 pdp->servicename[0] = '\0';
100 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
105 fstrcpy(pdp->hostname,temp);
106 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
108 /* If we got a hostname, is it ours (or an IP address) ? */
109 if (!is_myname_or_ipaddr(pdp->hostname)) {
112 DEBUG(10,("parse_dfs_path: hostname %s isn't ours. Try local path from path %s\n",
113 pdp->hostname, temp));
115 * Possibly client sent a local path by mistake.
116 * Try and convert to a local path.
119 pdp->hostname[0] = '\0';
120 pdp->servicename[0] = '\0';
123 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
128 /* Parse out servicename. */
130 p = strchr_m(temp,sepchar);
132 fstrcpy(pdp->servicename,temp);
133 pdp->reqpath[0] = '\0';
137 fstrcpy(pdp->servicename,temp);
138 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
144 *ppath_contains_wcard = False;
146 pstrcpy(pdp->reqpath, p);
148 /* Rest is reqpath. */
149 if (pdp->posix_path) {
150 status = check_path_syntax_posix(pdp->reqpath);
153 status = check_path_syntax_wcard(pdp->reqpath, ppath_contains_wcard);
155 status = check_path_syntax(pdp->reqpath);
159 if (!NT_STATUS_IS_OK(status)) {
160 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
161 p, nt_errstr(status) ));
165 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
169 /********************************************************
170 Fake up a connection struct for the VFS layer.
171 Note this CHANGES CWD !!!! JRA.
172 *********************************************************/
174 static NTSTATUS create_conn_struct(connection_struct *conn, int snum, const char *path)
180 pstrcpy(connpath, path);
181 pstring_sub(connpath , "%S", lp_servicename(snum));
183 /* needed for smbd_vfs_init() */
185 if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) {
186 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
187 return NT_STATUS_NO_MEMORY;
190 if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx, struct share_params))) {
191 DEBUG(0, ("TALLOC failed\n"));
192 return NT_STATUS_NO_MEMORY;
195 conn->params->service = snum;
197 set_conn_connectpath(conn, connpath);
199 if (!smbd_vfs_init(conn)) {
200 NTSTATUS status = map_nt_error_from_unix(errno);
201 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
202 conn_free_internal(conn);
207 * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
208 * share as the anonymous user. If we try to chdir as that user we will
209 * fail.... WTF ? JRA.
212 if (vfs_ChDir(conn,conn->connectpath) != 0) {
213 NTSTATUS status = map_nt_error_from_unix(errno);
214 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
215 conn->connectpath, strerror(errno) ));
216 conn_free_internal(conn);
223 /**********************************************************************
224 Parse the contents of a symlink to verify if it is an msdfs referral
225 A valid referral is of the form:
227 msdfs:server1\share1,server2\share2
228 msdfs:server1\share1\pathname,server2\share2\pathname
229 msdfs:server1/share1,server2/share2
230 msdfs:server1/share1/pathname,server2/share2/pathname.
232 Note that the alternate paths returned here must be of the canonicalized
236 \server\share\path\to\file,
238 even in posix path mode. This is because we have no knowledge if the
239 server we're referring to understands posix paths.
240 **********************************************************************/
242 static BOOL parse_msdfs_symlink(TALLOC_CTX *ctx,
244 struct referral **preflist,
249 char *alt_path[MAX_REFERRAL_COUNT];
251 struct referral *reflist;
253 pstrcpy(temp,target);
254 prot = strtok(temp,":");
256 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
260 /* parse out the alternate paths */
261 while((count<MAX_REFERRAL_COUNT) &&
262 ((alt_path[count] = strtok(NULL,",")) != NULL)) {
266 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
269 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx, struct referral, count);
270 if(reflist == NULL) {
271 DEBUG(0,("parse_msdfs_symlink: talloc failed!\n"));
275 reflist = *preflist = NULL;
278 for(i=0;i<count;i++) {
281 /* Canonicalize link target. Replace all /'s in the path by a \ */
282 string_replace(alt_path[i], '/', '\\');
284 /* Remove leading '\\'s */
286 while (*p && (*p == '\\')) {
290 pstrcpy(reflist[i].alternate_path, "\\");
291 pstrcat(reflist[i].alternate_path, p);
293 reflist[i].proximity = 0;
294 reflist[i].ttl = REFERRAL_TTL;
295 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n", reflist[i].alternate_path));
302 /**********************************************************************
303 Returns true if the unix path is a valid msdfs symlink and also
304 returns the target string from inside the link.
305 **********************************************************************/
307 BOOL is_msdfs_link(connection_struct *conn,
310 SMB_STRUCT_STAT *sbufp)
313 int referral_len = 0;
319 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
320 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
324 if (!S_ISLNK(sbufp->st_mode)) {
325 DEBUG(5,("is_msdfs_link: %s is not a link.\n",path));
329 /* open the link and read it */
330 referral_len = SMB_VFS_READLINK(conn, path, link_target, sizeof(pstring)-1);
331 if (referral_len == -1) {
332 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n",
333 path, strerror(errno)));
336 link_target[referral_len] = '\0';
338 DEBUG(5,("is_msdfs_link: %s -> %s\n",path, link_target));
340 if (!strnequal(link_target, "msdfs:", 6)) {
346 /*****************************************************************
347 Used by other functions to decide if a dfs path is remote,
348 and to get the list of referred locations for that remote path.
350 search_flag: For findfirsts, dfs links themselves are not
351 redirected, but paths beyond the links are. For normal smb calls,
352 even dfs links need to be redirected.
354 consumedcntp: how much of the dfs path is being redirected. the client
355 should try the remaining path on the redirected server.
357 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
358 link redirect are in targetpath.
359 *****************************************************************/
361 static NTSTATUS dfs_path_lookup(connection_struct *conn,
362 const char *dfspath, /* Incoming complete dfs path */
363 const struct dfs_path *pdp, /* Parsed out server+share+extrapath. */
364 BOOL search_flag, /* Called from a findfirst ? */
370 SMB_STRUCT_STAT sbuf;
373 pstring canon_dfspath; /* Canonicalized dfs path. (only '/' components). */
375 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
376 conn->connectpath, pdp->reqpath));
379 * Note the unix path conversion here we're doing we can
380 * throw away. We're looking for a symlink for a dfs
381 * resolution, if we don't find it we'll do another
382 * unix_convert later in the codepath.
383 * If we needed to remember what we'd resolved in
384 * dp->reqpath (as the original code did) we'd
385 * pstrcpy(localhost, dp->reqpath) on any code
386 * path below that returns True - but I don't
387 * think this is needed. JRA.
390 pstrcpy(localpath, pdp->reqpath);
391 status = unix_convert(conn, localpath, search_flag, NULL, &sbuf);
392 if (!NT_STATUS_IS_OK(status)) {
396 /* Optimization - check if we can redirect the whole path. */
398 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
400 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
401 "for dfs link %s.\n", dfspath));
405 DEBUG(6,("dfs_path_lookup: %s resolves to a "
406 "valid dfs link %s.\n", dfspath, targetpath));
409 *consumedcntp = strlen(dfspath);
411 return NT_STATUS_PATH_NOT_COVERED;
414 /* Prepare to test only for '/' components in the given path,
415 * so if a Windows path replace all '\\' characters with '/'.
416 * For a POSIX DFS path we know all separators are already '/'. */
418 pstrcpy(canon_dfspath, dfspath);
419 if (!pdp->posix_path) {
420 string_replace(canon_dfspath, '\\', '/');
424 * Redirect if any component in the path is a link.
425 * We do this by walking backwards through the
426 * local path, chopping off the last component
427 * in both the local path and the canonicalized
428 * DFS path. If we hit a DFS link then we're done.
431 p = strrchr_m(localpath, '/');
433 q = strrchr_m(canon_dfspath, '/');
442 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
443 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
444 "parent %s is dfs link\n", dfspath, localpath));
447 *consumedcntp = strlen(canon_dfspath);
448 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
449 "(%d)\n", canon_dfspath, *consumedcntp));
452 return NT_STATUS_PATH_NOT_COVERED;
455 /* Step back on the filesystem. */
456 p = strrchr_m(localpath, '/');
459 /* And in the canonicalized dfs path. */
460 q = strrchr_m(canon_dfspath, '/');
467 /*****************************************************************
468 Decides if a dfs pathname should be redirected or not.
469 If not, the pathname is converted to a tcon-relative local unix path
471 search_wcard_flag: this flag performs 2 functions bother related
472 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
475 This function can return NT_STATUS_OK, meaning use the returned path as-is
476 (mapped into a local path).
477 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
478 any other NT_STATUS error which is a genuine error to be
479 returned to the client.
480 *****************************************************************/
482 static NTSTATUS dfs_redirect( connection_struct *conn,
484 BOOL search_wcard_flag,
485 BOOL *ppath_contains_wcard)
491 status = parse_dfs_path(dfs_path, search_wcard_flag, &dp, ppath_contains_wcard);
492 if (!NT_STATUS_IS_OK(status)) {
496 if (dp.reqpath[0] == '\0') {
497 pstrcpy(dfs_path, dp.reqpath);
498 DEBUG(5,("dfs_redirect: self-referral.\n"));
502 /* If dfs pathname for a non-dfs share, convert to tcon-relative
503 path and return OK */
505 if (!lp_msdfs_root(SNUM(conn))) {
506 pstrcpy(dfs_path, dp.reqpath);
510 /* If it looked like a local path (zero hostname/servicename)
511 * just treat as a tcon-relative path. */
513 if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') {
514 pstrcpy(dfs_path, dp.reqpath);
518 if (!( strequal(dp.servicename, lp_servicename(SNUM(conn)))
519 || (strequal(dp.servicename, HOMES_NAME)
520 && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) {
522 /* The given sharename doesn't match this connection. */
524 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
527 status = dfs_path_lookup(conn, dfs_path, &dp,
528 search_wcard_flag, NULL, targetpath);
529 if (!NT_STATUS_IS_OK(status)) {
530 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
531 DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path));
533 DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
534 dfs_path, nt_errstr(status) ));
539 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path));
541 /* Form non-dfs tcon-relative path */
542 pstrcpy(dfs_path, dp.reqpath);
544 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path));
548 /**********************************************************************
549 Return a self referral.
550 **********************************************************************/
552 static NTSTATUS self_ref(TALLOC_CTX *ctx,
553 const char *dfs_path,
554 struct junction_map *jucn,
556 BOOL *self_referralp)
558 struct referral *ref;
560 *self_referralp = True;
562 jucn->referral_count = 1;
563 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
564 DEBUG(0,("self_ref: talloc failed for referral\n"));
565 return NT_STATUS_NO_MEMORY;
568 pstrcpy(ref->alternate_path,dfs_path);
570 ref->ttl = REFERRAL_TTL;
571 jucn->referral_list = ref;
572 *consumedcntp = strlen(dfs_path);
576 /**********************************************************************
577 Gets valid referrals for a dfs path and fills up the
578 junction_map structure.
579 **********************************************************************/
581 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
582 const char *dfs_path,
583 struct junction_map *jucn,
585 BOOL *self_referralp)
587 struct connection_struct conns;
588 struct connection_struct *conn = &conns;
593 NTSTATUS status = NT_STATUS_NOT_FOUND;
598 *self_referralp = False;
600 status = parse_dfs_path(dfs_path, False, &dp, &dummy);
601 if (!NT_STATUS_IS_OK(status)) {
605 /* Verify hostname in path */
606 if (!is_myname_or_ipaddr(dp.hostname)) {
607 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
608 dp.hostname, dfs_path));
609 return NT_STATUS_NOT_FOUND;
612 fstrcpy(jucn->service_name, dp.servicename);
613 pstrcpy(jucn->volume_name, dp.reqpath);
615 /* Verify the share is a dfs root */
616 snum = lp_servicenumber(jucn->service_name);
618 if ((snum = find_service(jucn->service_name)) < 0) {
619 return NT_STATUS_NOT_FOUND;
623 if (!lp_msdfs_root(snum)) {
624 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
625 dp.servicename, dfs_path));
626 return NT_STATUS_NOT_FOUND;
630 * Self referrals are tested with a anonymous IPC connection and
631 * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
632 * to an empty string). create_conn_struct cd's into the directory and will
633 * fail if it cannot (as the anonymous user). Cope with this.
636 if (dp.reqpath[0] == '\0') {
637 struct referral *ref;
639 if (*lp_msdfs_proxy(snum) == '\0') {
648 * It's an msdfs proxy share. Redirect to
649 * the configured target share.
652 jucn->referral_count = 1;
653 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
654 DEBUG(0, ("malloc failed for referral\n"));
655 return NT_STATUS_NO_MEMORY;
658 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
659 if (dp.reqpath[0] != '\0') {
660 pstrcat(ref->alternate_path, dp.reqpath);
663 ref->ttl = REFERRAL_TTL;
664 jucn->referral_list = ref;
665 *consumedcntp = strlen(dfs_path);
669 pstrcpy(conn_path, lp_pathname(snum));
670 status = create_conn_struct(conn, snum, conn_path);
671 if (!NT_STATUS_IS_OK(status)) {
675 /* If this is a DFS path dfs_lookup should return
676 * NT_STATUS_PATH_NOT_COVERED. */
678 status = dfs_path_lookup(conn, dfs_path, &dp,
679 False, consumedcntp, targetpath);
681 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
682 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
684 conn_free_internal(conn);
688 /* We know this is a valid dfs link. Parse the targetpath. */
689 if (!parse_msdfs_symlink(ctx, targetpath,
690 &jucn->referral_list,
691 &jucn->referral_count)) {
692 DEBUG(3,("get_referred_path: failed to parse symlink "
693 "target %s\n", targetpath ));
694 conn_free_internal(conn);
695 return NT_STATUS_NOT_FOUND;
698 conn_free_internal(conn);
702 static int setup_ver2_dfs_referral(const char *pathname,
704 struct junction_map *junction,
708 char* pdata = *ppdata;
710 unsigned char uni_requestedpath[1024];
711 int uni_reqpathoffset1,uni_reqpathoffset2;
713 int requestedpathlen=0;
718 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
720 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
724 dump_data(0, uni_requestedpath,requestedpathlen);
727 DEBUG(10,("ref count = %u\n",junction->referral_count));
729 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
730 VERSION2_REFERRAL_SIZE * junction->referral_count;
732 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
734 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
736 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
737 2 * requestedpathlen;
738 DEBUG(10,("reply_size: %u\n",reply_size));
740 /* add up the unicode lengths of all the referral paths */
741 for(i=0;i<junction->referral_count;i++) {
742 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
743 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
746 DEBUG(10,("reply_size = %u\n",reply_size));
747 /* add the unexplained 0x16 bytes */
750 pdata = (char *)SMB_REALLOC(pdata,reply_size);
752 DEBUG(0,("Realloc failed!\n"));
757 /* copy in the dfs requested paths.. required for offset calculations */
758 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
759 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
761 /* create the header */
762 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
763 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
765 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
767 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
771 /* add the referral elements */
772 for(i=0;i<junction->referral_count;i++) {
773 struct referral* ref = &junction->referral_list[i];
776 SSVAL(pdata,offset,2); /* version 2 */
777 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
779 SSVAL(pdata,offset+4,1);
781 SSVAL(pdata,offset+4,0);
783 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
784 SIVAL(pdata,offset+8,ref->proximity);
785 SIVAL(pdata,offset+12,ref->ttl);
787 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
788 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
789 /* copy referred path into current offset */
790 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
791 sizeof(pstring), STR_UNICODE);
793 SSVAL(pdata,offset+20,uni_curroffset-offset);
795 uni_curroffset += unilen;
796 offset += VERSION2_REFERRAL_SIZE;
798 /* add in the unexplained 22 (0x16) bytes at the end */
799 memset(pdata+uni_curroffset,'\0',0x16);
803 static int setup_ver3_dfs_referral(const char *pathname,
805 struct junction_map *junction,
809 char* pdata = *ppdata;
811 unsigned char uni_reqpath[1024];
812 int uni_reqpathoffset1, uni_reqpathoffset2;
819 DEBUG(10,("setting up version3 referral\n"));
821 reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
824 dump_data(0, uni_reqpath,reqpathlen);
827 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
828 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
829 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
831 for(i=0;i<junction->referral_count;i++) {
832 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
833 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
836 pdata = (char *)SMB_REALLOC(pdata,reply_size);
838 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
843 /* create the header */
844 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
845 SSVAL(pdata,2,junction->referral_count); /* number of referral */
847 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
849 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
852 /* copy in the reqpaths */
853 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
854 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
857 for(i=0;i<junction->referral_count;i++) {
858 struct referral* ref = &(junction->referral_list[i]);
861 SSVAL(pdata,offset,3); /* version 3 */
862 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
864 SSVAL(pdata,offset+4,1);
866 SSVAL(pdata,offset+4,0);
869 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
870 SIVAL(pdata,offset+8,ref->ttl);
872 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
873 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
874 /* copy referred path into current offset */
875 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
876 sizeof(pstring), STR_UNICODE | STR_TERMINATE);
877 SSVAL(pdata,offset+16,uni_curroffset-offset);
878 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
879 memset(pdata+offset+18,'\0',16);
881 uni_curroffset += unilen;
882 offset += VERSION3_REFERRAL_SIZE;
887 /******************************************************************
888 Set up the DFS referral for the dfs pathname. This call returns
889 the amount of the path covered by this server, and where the
890 client should be redirected to. This is the meat of the
891 TRANS2_GET_DFS_REFERRAL call.
892 ******************************************************************/
894 int setup_dfs_referral(connection_struct *orig_conn,
895 const char *dfs_path,
896 int max_referral_level,
897 char **ppdata, NTSTATUS *pstatus)
899 struct junction_map junction;
901 BOOL self_referral = False;
903 char *pathnamep = NULL;
904 pstring local_dfs_path;
907 if (!(ctx=talloc_init("setup_dfs_referral"))) {
908 *pstatus = NT_STATUS_NO_MEMORY;
912 ZERO_STRUCT(junction);
914 /* get the junction entry */
917 *pstatus = NT_STATUS_NOT_FOUND;
922 * Trim pathname sent by client so it begins with only one backslash.
923 * Two backslashes confuse some dfs clients
926 pstrcpy(local_dfs_path, dfs_path);
927 pathnamep = local_dfs_path;
928 while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) {
932 /* The following call can change cwd. */
933 *pstatus = get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral);
934 if (!NT_STATUS_IS_OK(*pstatus)) {
935 vfs_ChDir(orig_conn,orig_conn->connectpath);
939 vfs_ChDir(orig_conn,orig_conn->connectpath);
941 if (!self_referral) {
942 pathnamep[consumedcnt] = '\0';
944 if( DEBUGLVL( 3 ) ) {
946 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
947 for(i=0;i<junction.referral_count;i++)
948 dbgtext(" %s",junction.referral_list[i].alternate_path);
953 /* create the referral depeding on version */
954 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
956 if (max_referral_level < 2) {
957 max_referral_level = 2;
959 if (max_referral_level > 3) {
960 max_referral_level = 3;
963 switch(max_referral_level) {
965 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
966 consumedcnt, self_referral);
969 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
970 consumedcnt, self_referral);
973 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
975 *pstatus = NT_STATUS_INVALID_LEVEL;
980 DEBUGADD(0,("DFS Referral pdata:\n"));
981 dump_data(0,(uint8 *)*ppdata,reply_size);
985 *pstatus = NT_STATUS_OK;
989 /**********************************************************************
990 The following functions are called by the NETDFS RPC pipe functions
991 **********************************************************************/
993 /*********************************************************************
994 Creates a junction structure from a DFS pathname
995 **********************************************************************/
997 BOOL create_junction(const char *dfs_path, struct junction_map *jucn)
1003 NTSTATUS status = parse_dfs_path(dfs_path, False, &dp, &dummy);
1005 if (!NT_STATUS_IS_OK(status)) {
1009 /* check if path is dfs : validate first token */
1010 if (!is_myname_or_ipaddr(dp.hostname)) {
1011 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
1012 dp.hostname, dfs_path));
1016 /* Check for a non-DFS share */
1017 snum = lp_servicenumber(dp.servicename);
1019 if(snum < 0 || !lp_msdfs_root(snum)) {
1020 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1025 fstrcpy(jucn->service_name,dp.servicename);
1026 pstrcpy(jucn->volume_name,dp.reqpath);
1027 pstrcpy(jucn->comment, lp_comment(snum));
1031 /**********************************************************************
1032 Forms a valid Unix pathname from the junction
1033 **********************************************************************/
1035 static BOOL junction_to_local_path(struct junction_map *jucn,
1038 connection_struct *conn_out)
1043 snum = lp_servicenumber(jucn->service_name);
1048 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
1049 safe_strcat(path, "/", max_pathlen-1);
1050 safe_strcat(path, jucn->volume_name, max_pathlen-1);
1052 pstrcpy(conn_path, lp_pathname(snum));
1053 if (!NT_STATUS_IS_OK(create_conn_struct(conn_out, snum, conn_path))) {
1060 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
1064 connection_struct conns;
1065 connection_struct *conn = &conns;
1067 BOOL insert_comma = False;
1072 if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
1076 /* Form the msdfs_link contents */
1077 pstrcpy(msdfs_link, "msdfs:");
1078 for(i=0; i<jucn->referral_count; i++) {
1079 char* refpath = jucn->referral_list[i].alternate_path;
1081 /* Alternate paths always use Windows separators. */
1082 trim_char(refpath, '\\', '\\');
1083 if(*refpath == '\0') {
1085 insert_comma = False;
1089 if (i > 0 && insert_comma) {
1090 pstrcat(msdfs_link, ",");
1093 pstrcat(msdfs_link, refpath);
1094 if (!insert_comma) {
1095 insert_comma = True;
1099 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1103 if(SMB_VFS_UNLINK(conn,path)!=0) {
1108 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1109 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
1110 path, msdfs_link, strerror(errno)));
1119 conn_free_internal(conn);
1123 BOOL remove_msdfs_link(struct junction_map *jucn)
1126 connection_struct conns;
1127 connection_struct *conn = &conns;
1132 if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1133 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1136 talloc_destroy( conn->mem_ctx );
1139 conn_free_internal(conn);
1143 static int form_junctions(TALLOC_CTX *ctx,
1145 struct junction_map *jucn,
1149 SMB_STRUCT_DIR *dirp;
1151 pstring connect_path;
1152 char *service_name = lp_servicename(snum);
1153 connection_struct conn;
1154 struct referral *ref = NULL;
1158 if (jn_remain <= 0) {
1162 pstrcpy(connect_path,lp_pathname(snum));
1164 if(*connect_path == '\0') {
1169 * Fake up a connection struct for the VFS layer.
1172 if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
1176 /* form a junction for the msdfs root - convention
1177 DO NOT REMOVE THIS: NT clients will not work with us
1178 if this is not present
1180 fstrcpy(jucn[cnt].service_name, service_name);
1181 jucn[cnt].volume_name[0] = '\0';
1182 jucn[cnt].referral_count = 1;
1184 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1185 if (jucn[cnt].referral_list == NULL) {
1186 DEBUG(0, ("talloc failed!\n"));
1191 ref->ttl = REFERRAL_TTL;
1192 if (*lp_msdfs_proxy(snum) != '\0') {
1193 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1198 pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
1199 get_local_machine_name(),
1203 /* Now enumerate all dfs links */
1204 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1209 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1210 pstring link_target;
1211 if (cnt >= jn_remain) {
1212 SMB_VFS_CLOSEDIR(&conn,dirp);
1213 DEBUG(2, ("ran out of MSDFS junction slots"));
1216 if (is_msdfs_link(&conn, dname, link_target, NULL)) {
1217 if (parse_msdfs_symlink(ctx,
1219 &jucn[cnt].referral_list,
1220 &jucn[cnt].referral_count)) {
1222 fstrcpy(jucn[cnt].service_name, service_name);
1223 pstrcpy(jucn[cnt].volume_name, dname);
1229 SMB_VFS_CLOSEDIR(&conn,dirp);
1233 conn_free_internal(&conn);
1237 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1243 if(!lp_host_msdfs()) {
1247 /* Ensure all the usershares are loaded. */
1249 load_registry_shares();
1250 sharecount = load_usershare_shares();
1253 for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1254 if(lp_msdfs_root(i)) {
1255 jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1261 /******************************************************************************
1262 Core function to resolve a dfs pathname.
1263 ******************************************************************************/
1265 NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name)
1267 NTSTATUS status = NT_STATUS_OK;
1269 if (dfs_pathnames) {
1270 status = dfs_redirect(conn, name, False, &dummy);
1275 /******************************************************************************
1276 Core function to resolve a dfs pathname possibly containing a wildcard.
1277 This function is identical to the above except for the BOOL param to
1278 dfs_redirect but I need this to be separate so it's really clear when
1279 we're allowing wildcards and when we're not. JRA.
1280 ******************************************************************************/
1282 NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard)
1284 NTSTATUS status = NT_STATUS_OK;
1285 if (dfs_pathnames) {
1286 status = dfs_redirect(conn, name, True, ppath_contains_wcard);