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) && !NT_STATUS_EQUAL(status,
393 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
397 /* Optimization - check if we can redirect the whole path. */
399 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
401 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
402 "for dfs link %s.\n", dfspath));
406 DEBUG(6,("dfs_path_lookup: %s resolves to a "
407 "valid dfs link %s.\n", dfspath, targetpath));
410 *consumedcntp = strlen(dfspath);
412 return NT_STATUS_PATH_NOT_COVERED;
415 /* Prepare to test only for '/' components in the given path,
416 * so if a Windows path replace all '\\' characters with '/'.
417 * For a POSIX DFS path we know all separators are already '/'. */
419 pstrcpy(canon_dfspath, dfspath);
420 if (!pdp->posix_path) {
421 string_replace(canon_dfspath, '\\', '/');
425 * Redirect if any component in the path is a link.
426 * We do this by walking backwards through the
427 * local path, chopping off the last component
428 * in both the local path and the canonicalized
429 * DFS path. If we hit a DFS link then we're done.
432 p = strrchr_m(localpath, '/');
434 q = strrchr_m(canon_dfspath, '/');
443 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
444 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
445 "parent %s is dfs link\n", dfspath, localpath));
448 *consumedcntp = strlen(canon_dfspath);
449 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
450 "(%d)\n", canon_dfspath, *consumedcntp));
453 return NT_STATUS_PATH_NOT_COVERED;
456 /* Step back on the filesystem. */
457 p = strrchr_m(localpath, '/');
460 /* And in the canonicalized dfs path. */
461 q = strrchr_m(canon_dfspath, '/');
468 /*****************************************************************
469 Decides if a dfs pathname should be redirected or not.
470 If not, the pathname is converted to a tcon-relative local unix path
472 search_wcard_flag: this flag performs 2 functions bother related
473 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
476 This function can return NT_STATUS_OK, meaning use the returned path as-is
477 (mapped into a local path).
478 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
479 any other NT_STATUS error which is a genuine error to be
480 returned to the client.
481 *****************************************************************/
483 static NTSTATUS dfs_redirect( connection_struct *conn,
485 BOOL search_wcard_flag,
486 BOOL *ppath_contains_wcard)
492 status = parse_dfs_path(dfs_path, search_wcard_flag, &dp, ppath_contains_wcard);
493 if (!NT_STATUS_IS_OK(status)) {
497 if (dp.reqpath[0] == '\0') {
498 pstrcpy(dfs_path, dp.reqpath);
499 DEBUG(5,("dfs_redirect: self-referral.\n"));
503 /* If dfs pathname for a non-dfs share, convert to tcon-relative
504 path and return OK */
506 if (!lp_msdfs_root(SNUM(conn))) {
507 pstrcpy(dfs_path, dp.reqpath);
511 /* If it looked like a local path (zero hostname/servicename)
512 * just treat as a tcon-relative path. */
514 if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') {
515 pstrcpy(dfs_path, dp.reqpath);
519 if (!( strequal(dp.servicename, lp_servicename(SNUM(conn)))
520 || (strequal(dp.servicename, HOMES_NAME)
521 && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) {
523 /* The given sharename doesn't match this connection. */
525 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
528 status = dfs_path_lookup(conn, dfs_path, &dp,
529 search_wcard_flag, NULL, targetpath);
530 if (!NT_STATUS_IS_OK(status)) {
531 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
532 DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path));
534 DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
535 dfs_path, nt_errstr(status) ));
540 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path));
542 /* Form non-dfs tcon-relative path */
543 pstrcpy(dfs_path, dp.reqpath);
545 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path));
549 /**********************************************************************
550 Return a self referral.
551 **********************************************************************/
553 static NTSTATUS self_ref(TALLOC_CTX *ctx,
554 const char *dfs_path,
555 struct junction_map *jucn,
557 BOOL *self_referralp)
559 struct referral *ref;
561 *self_referralp = True;
563 jucn->referral_count = 1;
564 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
565 DEBUG(0,("self_ref: talloc failed for referral\n"));
566 return NT_STATUS_NO_MEMORY;
569 pstrcpy(ref->alternate_path,dfs_path);
571 ref->ttl = REFERRAL_TTL;
572 jucn->referral_list = ref;
573 *consumedcntp = strlen(dfs_path);
577 /**********************************************************************
578 Gets valid referrals for a dfs path and fills up the
579 junction_map structure.
580 **********************************************************************/
582 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
583 const char *dfs_path,
584 struct junction_map *jucn,
586 BOOL *self_referralp)
588 struct connection_struct conns;
589 struct connection_struct *conn = &conns;
594 NTSTATUS status = NT_STATUS_NOT_FOUND;
599 *self_referralp = False;
601 status = parse_dfs_path(dfs_path, False, &dp, &dummy);
602 if (!NT_STATUS_IS_OK(status)) {
606 /* Verify hostname in path */
607 if (!is_myname_or_ipaddr(dp.hostname)) {
608 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
609 dp.hostname, dfs_path));
610 return NT_STATUS_NOT_FOUND;
613 fstrcpy(jucn->service_name, dp.servicename);
614 pstrcpy(jucn->volume_name, dp.reqpath);
616 /* Verify the share is a dfs root */
617 snum = lp_servicenumber(jucn->service_name);
619 if ((snum = find_service(jucn->service_name)) < 0) {
620 return NT_STATUS_NOT_FOUND;
624 if (!lp_msdfs_root(snum)) {
625 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
626 dp.servicename, dfs_path));
627 return NT_STATUS_NOT_FOUND;
631 * Self referrals are tested with a anonymous IPC connection and
632 * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
633 * to an empty string). create_conn_struct cd's into the directory and will
634 * fail if it cannot (as the anonymous user). Cope with this.
637 if (dp.reqpath[0] == '\0') {
638 struct referral *ref;
640 if (*lp_msdfs_proxy(snum) == '\0') {
649 * It's an msdfs proxy share. Redirect to
650 * the configured target share.
653 jucn->referral_count = 1;
654 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
655 DEBUG(0, ("malloc failed for referral\n"));
656 return NT_STATUS_NO_MEMORY;
659 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
660 if (dp.reqpath[0] != '\0') {
661 pstrcat(ref->alternate_path, dp.reqpath);
664 ref->ttl = REFERRAL_TTL;
665 jucn->referral_list = ref;
666 *consumedcntp = strlen(dfs_path);
670 pstrcpy(conn_path, lp_pathname(snum));
671 status = create_conn_struct(conn, snum, conn_path);
672 if (!NT_STATUS_IS_OK(status)) {
676 /* If this is a DFS path dfs_lookup should return
677 * NT_STATUS_PATH_NOT_COVERED. */
679 status = dfs_path_lookup(conn, dfs_path, &dp,
680 False, consumedcntp, targetpath);
682 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
683 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
685 conn_free_internal(conn);
689 /* We know this is a valid dfs link. Parse the targetpath. */
690 if (!parse_msdfs_symlink(ctx, targetpath,
691 &jucn->referral_list,
692 &jucn->referral_count)) {
693 DEBUG(3,("get_referred_path: failed to parse symlink "
694 "target %s\n", targetpath ));
695 conn_free_internal(conn);
696 return NT_STATUS_NOT_FOUND;
699 conn_free_internal(conn);
703 static int setup_ver2_dfs_referral(const char *pathname,
705 struct junction_map *junction,
709 char* pdata = *ppdata;
711 unsigned char uni_requestedpath[1024];
712 int uni_reqpathoffset1,uni_reqpathoffset2;
714 int requestedpathlen=0;
719 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
721 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
725 dump_data(0, uni_requestedpath,requestedpathlen);
728 DEBUG(10,("ref count = %u\n",junction->referral_count));
730 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
731 VERSION2_REFERRAL_SIZE * junction->referral_count;
733 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
735 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
737 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
738 2 * requestedpathlen;
739 DEBUG(10,("reply_size: %u\n",reply_size));
741 /* add up the unicode lengths of all the referral paths */
742 for(i=0;i<junction->referral_count;i++) {
743 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
744 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
747 DEBUG(10,("reply_size = %u\n",reply_size));
748 /* add the unexplained 0x16 bytes */
751 pdata = (char *)SMB_REALLOC(pdata,reply_size);
753 DEBUG(0,("Realloc failed!\n"));
758 /* copy in the dfs requested paths.. required for offset calculations */
759 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
760 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
762 /* create the header */
763 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
764 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
766 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
768 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
772 /* add the referral elements */
773 for(i=0;i<junction->referral_count;i++) {
774 struct referral* ref = &junction->referral_list[i];
777 SSVAL(pdata,offset,2); /* version 2 */
778 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
780 SSVAL(pdata,offset+4,1);
782 SSVAL(pdata,offset+4,0);
784 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
785 SIVAL(pdata,offset+8,ref->proximity);
786 SIVAL(pdata,offset+12,ref->ttl);
788 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
789 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
790 /* copy referred path into current offset */
791 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
792 sizeof(pstring), STR_UNICODE);
794 SSVAL(pdata,offset+20,uni_curroffset-offset);
796 uni_curroffset += unilen;
797 offset += VERSION2_REFERRAL_SIZE;
799 /* add in the unexplained 22 (0x16) bytes at the end */
800 memset(pdata+uni_curroffset,'\0',0x16);
804 static int setup_ver3_dfs_referral(const char *pathname,
806 struct junction_map *junction,
810 char* pdata = *ppdata;
812 unsigned char uni_reqpath[1024];
813 int uni_reqpathoffset1, uni_reqpathoffset2;
820 DEBUG(10,("setting up version3 referral\n"));
822 reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
825 dump_data(0, uni_reqpath,reqpathlen);
828 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
829 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
830 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
832 for(i=0;i<junction->referral_count;i++) {
833 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
834 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
837 pdata = (char *)SMB_REALLOC(pdata,reply_size);
839 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
844 /* create the header */
845 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
846 SSVAL(pdata,2,junction->referral_count); /* number of referral */
848 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
850 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
853 /* copy in the reqpaths */
854 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
855 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
858 for(i=0;i<junction->referral_count;i++) {
859 struct referral* ref = &(junction->referral_list[i]);
862 SSVAL(pdata,offset,3); /* version 3 */
863 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
865 SSVAL(pdata,offset+4,1);
867 SSVAL(pdata,offset+4,0);
870 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
871 SIVAL(pdata,offset+8,ref->ttl);
873 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
874 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
875 /* copy referred path into current offset */
876 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
877 sizeof(pstring), STR_UNICODE | STR_TERMINATE);
878 SSVAL(pdata,offset+16,uni_curroffset-offset);
879 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
880 memset(pdata+offset+18,'\0',16);
882 uni_curroffset += unilen;
883 offset += VERSION3_REFERRAL_SIZE;
888 /******************************************************************
889 Set up the DFS referral for the dfs pathname. This call returns
890 the amount of the path covered by this server, and where the
891 client should be redirected to. This is the meat of the
892 TRANS2_GET_DFS_REFERRAL call.
893 ******************************************************************/
895 int setup_dfs_referral(connection_struct *orig_conn,
896 const char *dfs_path,
897 int max_referral_level,
898 char **ppdata, NTSTATUS *pstatus)
900 struct junction_map junction;
902 BOOL self_referral = False;
904 char *pathnamep = NULL;
905 pstring local_dfs_path;
908 if (!(ctx=talloc_init("setup_dfs_referral"))) {
909 *pstatus = NT_STATUS_NO_MEMORY;
913 ZERO_STRUCT(junction);
915 /* get the junction entry */
918 *pstatus = NT_STATUS_NOT_FOUND;
923 * Trim pathname sent by client so it begins with only one backslash.
924 * Two backslashes confuse some dfs clients
927 pstrcpy(local_dfs_path, dfs_path);
928 pathnamep = local_dfs_path;
929 while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) {
933 /* The following call can change cwd. */
934 *pstatus = get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral);
935 if (!NT_STATUS_IS_OK(*pstatus)) {
936 vfs_ChDir(orig_conn,orig_conn->connectpath);
940 vfs_ChDir(orig_conn,orig_conn->connectpath);
942 if (!self_referral) {
943 pathnamep[consumedcnt] = '\0';
945 if( DEBUGLVL( 3 ) ) {
947 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
948 for(i=0;i<junction.referral_count;i++)
949 dbgtext(" %s",junction.referral_list[i].alternate_path);
954 /* create the referral depeding on version */
955 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
957 if (max_referral_level < 2) {
958 max_referral_level = 2;
960 if (max_referral_level > 3) {
961 max_referral_level = 3;
964 switch(max_referral_level) {
966 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
967 consumedcnt, self_referral);
970 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
971 consumedcnt, self_referral);
974 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
976 *pstatus = NT_STATUS_INVALID_LEVEL;
981 DEBUGADD(0,("DFS Referral pdata:\n"));
982 dump_data(0,(uint8 *)*ppdata,reply_size);
986 *pstatus = NT_STATUS_OK;
990 /**********************************************************************
991 The following functions are called by the NETDFS RPC pipe functions
992 **********************************************************************/
994 /*********************************************************************
995 Creates a junction structure from a DFS pathname
996 **********************************************************************/
998 BOOL create_junction(const char *dfs_path, struct junction_map *jucn)
1004 NTSTATUS status = parse_dfs_path(dfs_path, False, &dp, &dummy);
1006 if (!NT_STATUS_IS_OK(status)) {
1010 /* check if path is dfs : validate first token */
1011 if (!is_myname_or_ipaddr(dp.hostname)) {
1012 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
1013 dp.hostname, dfs_path));
1017 /* Check for a non-DFS share */
1018 snum = lp_servicenumber(dp.servicename);
1020 if(snum < 0 || !lp_msdfs_root(snum)) {
1021 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1026 fstrcpy(jucn->service_name,dp.servicename);
1027 pstrcpy(jucn->volume_name,dp.reqpath);
1028 pstrcpy(jucn->comment, lp_comment(snum));
1032 /**********************************************************************
1033 Forms a valid Unix pathname from the junction
1034 **********************************************************************/
1036 static BOOL junction_to_local_path(struct junction_map *jucn,
1039 connection_struct *conn_out)
1044 snum = lp_servicenumber(jucn->service_name);
1049 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
1050 safe_strcat(path, "/", max_pathlen-1);
1051 safe_strcat(path, jucn->volume_name, max_pathlen-1);
1053 pstrcpy(conn_path, lp_pathname(snum));
1054 if (!NT_STATUS_IS_OK(create_conn_struct(conn_out, snum, conn_path))) {
1061 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
1065 connection_struct conns;
1066 connection_struct *conn = &conns;
1068 BOOL insert_comma = False;
1073 if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
1077 /* Form the msdfs_link contents */
1078 pstrcpy(msdfs_link, "msdfs:");
1079 for(i=0; i<jucn->referral_count; i++) {
1080 char* refpath = jucn->referral_list[i].alternate_path;
1082 /* Alternate paths always use Windows separators. */
1083 trim_char(refpath, '\\', '\\');
1084 if(*refpath == '\0') {
1086 insert_comma = False;
1090 if (i > 0 && insert_comma) {
1091 pstrcat(msdfs_link, ",");
1094 pstrcat(msdfs_link, refpath);
1095 if (!insert_comma) {
1096 insert_comma = True;
1100 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1104 if(SMB_VFS_UNLINK(conn,path)!=0) {
1109 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1110 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
1111 path, msdfs_link, strerror(errno)));
1120 conn_free_internal(conn);
1124 BOOL remove_msdfs_link(struct junction_map *jucn)
1127 connection_struct conns;
1128 connection_struct *conn = &conns;
1133 if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1134 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1137 talloc_destroy( conn->mem_ctx );
1140 conn_free_internal(conn);
1144 static int form_junctions(TALLOC_CTX *ctx,
1146 struct junction_map *jucn,
1150 SMB_STRUCT_DIR *dirp;
1152 pstring connect_path;
1153 char *service_name = lp_servicename(snum);
1154 connection_struct conn;
1155 struct referral *ref = NULL;
1159 if (jn_remain <= 0) {
1163 pstrcpy(connect_path,lp_pathname(snum));
1165 if(*connect_path == '\0') {
1170 * Fake up a connection struct for the VFS layer.
1173 if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
1177 /* form a junction for the msdfs root - convention
1178 DO NOT REMOVE THIS: NT clients will not work with us
1179 if this is not present
1181 fstrcpy(jucn[cnt].service_name, service_name);
1182 jucn[cnt].volume_name[0] = '\0';
1183 jucn[cnt].referral_count = 1;
1185 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1186 if (jucn[cnt].referral_list == NULL) {
1187 DEBUG(0, ("talloc failed!\n"));
1192 ref->ttl = REFERRAL_TTL;
1193 if (*lp_msdfs_proxy(snum) != '\0') {
1194 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1199 pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
1200 get_local_machine_name(),
1204 /* Now enumerate all dfs links */
1205 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1210 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1211 pstring link_target;
1212 if (cnt >= jn_remain) {
1213 SMB_VFS_CLOSEDIR(&conn,dirp);
1214 DEBUG(2, ("ran out of MSDFS junction slots"));
1217 if (is_msdfs_link(&conn, dname, link_target, NULL)) {
1218 if (parse_msdfs_symlink(ctx,
1220 &jucn[cnt].referral_list,
1221 &jucn[cnt].referral_count)) {
1223 fstrcpy(jucn[cnt].service_name, service_name);
1224 pstrcpy(jucn[cnt].volume_name, dname);
1230 SMB_VFS_CLOSEDIR(&conn,dirp);
1234 conn_free_internal(&conn);
1238 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1244 if(!lp_host_msdfs()) {
1248 /* Ensure all the usershares are loaded. */
1250 load_registry_shares();
1251 sharecount = load_usershare_shares();
1254 for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1255 if(lp_msdfs_root(i)) {
1256 jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1262 /******************************************************************************
1263 Core function to resolve a dfs pathname.
1264 ******************************************************************************/
1266 NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name)
1268 NTSTATUS status = NT_STATUS_OK;
1270 if (dfs_pathnames) {
1271 status = dfs_redirect(conn, name, False, &dummy);
1276 /******************************************************************************
1277 Core function to resolve a dfs pathname possibly containing a wildcard.
1278 This function is identical to the above except for the BOOL param to
1279 dfs_redirect but I need this to be separate so it's really clear when
1280 we're allowing wildcards and when we're not. JRA.
1281 ******************************************************************************/
1283 NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard)
1285 NTSTATUS status = NT_STATUS_OK;
1286 if (dfs_pathnames) {
1287 status = dfs_redirect(conn, name, True, ppath_contains_wcard);