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 * localpath comes out of unix_convert, so it has
426 * no trailing backslash. Make sure that canon_dfspath hasn't either.
427 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
430 trim_char(canon_dfspath,0,'/');
433 * Redirect if any component in the path is a link.
434 * We do this by walking backwards through the
435 * local path, chopping off the last component
436 * in both the local path and the canonicalized
437 * DFS path. If we hit a DFS link then we're done.
440 p = strrchr_m(localpath, '/');
442 q = strrchr_m(canon_dfspath, '/');
451 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
452 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
453 "parent %s is dfs link\n", dfspath, localpath));
456 *consumedcntp = strlen(canon_dfspath);
457 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
458 "(%d)\n", canon_dfspath, *consumedcntp));
461 return NT_STATUS_PATH_NOT_COVERED;
464 /* Step back on the filesystem. */
465 p = strrchr_m(localpath, '/');
468 /* And in the canonicalized dfs path. */
469 q = strrchr_m(canon_dfspath, '/');
476 /*****************************************************************
477 Decides if a dfs pathname should be redirected or not.
478 If not, the pathname is converted to a tcon-relative local unix path
480 search_wcard_flag: this flag performs 2 functions bother related
481 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
484 This function can return NT_STATUS_OK, meaning use the returned path as-is
485 (mapped into a local path).
486 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
487 any other NT_STATUS error which is a genuine error to be
488 returned to the client.
489 *****************************************************************/
491 static NTSTATUS dfs_redirect( connection_struct *conn,
493 BOOL search_wcard_flag,
494 BOOL *ppath_contains_wcard)
500 status = parse_dfs_path(dfs_path, search_wcard_flag, &dp, ppath_contains_wcard);
501 if (!NT_STATUS_IS_OK(status)) {
505 if (dp.reqpath[0] == '\0') {
506 pstrcpy(dfs_path, dp.reqpath);
507 DEBUG(5,("dfs_redirect: self-referral.\n"));
511 /* If dfs pathname for a non-dfs share, convert to tcon-relative
512 path and return OK */
514 if (!lp_msdfs_root(SNUM(conn))) {
515 pstrcpy(dfs_path, dp.reqpath);
519 /* If it looked like a local path (zero hostname/servicename)
520 * just treat as a tcon-relative path. */
522 if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') {
523 pstrcpy(dfs_path, dp.reqpath);
527 if (!( strequal(dp.servicename, lp_servicename(SNUM(conn)))
528 || (strequal(dp.servicename, HOMES_NAME)
529 && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) {
531 /* The given sharename doesn't match this connection. */
533 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
536 status = dfs_path_lookup(conn, dfs_path, &dp,
537 search_wcard_flag, NULL, targetpath);
538 if (!NT_STATUS_IS_OK(status)) {
539 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
540 DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path));
542 DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
543 dfs_path, nt_errstr(status) ));
548 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path));
550 /* Form non-dfs tcon-relative path */
551 pstrcpy(dfs_path, dp.reqpath);
553 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path));
557 /**********************************************************************
558 Return a self referral.
559 **********************************************************************/
561 static NTSTATUS self_ref(TALLOC_CTX *ctx,
562 const char *dfs_path,
563 struct junction_map *jucn,
565 BOOL *self_referralp)
567 struct referral *ref;
569 *self_referralp = True;
571 jucn->referral_count = 1;
572 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
573 DEBUG(0,("self_ref: talloc failed for referral\n"));
574 return NT_STATUS_NO_MEMORY;
577 pstrcpy(ref->alternate_path,dfs_path);
579 ref->ttl = REFERRAL_TTL;
580 jucn->referral_list = ref;
581 *consumedcntp = strlen(dfs_path);
585 /**********************************************************************
586 Gets valid referrals for a dfs path and fills up the
587 junction_map structure.
588 **********************************************************************/
590 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
591 const char *dfs_path,
592 struct junction_map *jucn,
594 BOOL *self_referralp)
596 struct connection_struct conns;
597 struct connection_struct *conn = &conns;
602 NTSTATUS status = NT_STATUS_NOT_FOUND;
607 *self_referralp = False;
609 status = parse_dfs_path(dfs_path, False, &dp, &dummy);
610 if (!NT_STATUS_IS_OK(status)) {
614 /* Verify hostname in path */
615 if (!is_myname_or_ipaddr(dp.hostname)) {
616 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
617 dp.hostname, dfs_path));
618 return NT_STATUS_NOT_FOUND;
621 fstrcpy(jucn->service_name, dp.servicename);
622 pstrcpy(jucn->volume_name, dp.reqpath);
624 /* Verify the share is a dfs root */
625 snum = lp_servicenumber(jucn->service_name);
627 if ((snum = find_service(jucn->service_name)) < 0) {
628 return NT_STATUS_NOT_FOUND;
632 if (!lp_msdfs_root(snum)) {
633 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
634 dp.servicename, dfs_path));
635 return NT_STATUS_NOT_FOUND;
639 * Self referrals are tested with a anonymous IPC connection and
640 * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
641 * to an empty string). create_conn_struct cd's into the directory and will
642 * fail if it cannot (as the anonymous user). Cope with this.
645 if (dp.reqpath[0] == '\0') {
646 struct referral *ref;
648 if (*lp_msdfs_proxy(snum) == '\0') {
657 * It's an msdfs proxy share. Redirect to
658 * the configured target share.
661 jucn->referral_count = 1;
662 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
663 DEBUG(0, ("malloc failed for referral\n"));
664 return NT_STATUS_NO_MEMORY;
667 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
668 if (dp.reqpath[0] != '\0') {
669 pstrcat(ref->alternate_path, dp.reqpath);
672 ref->ttl = REFERRAL_TTL;
673 jucn->referral_list = ref;
674 *consumedcntp = strlen(dfs_path);
678 pstrcpy(conn_path, lp_pathname(snum));
679 status = create_conn_struct(conn, snum, conn_path);
680 if (!NT_STATUS_IS_OK(status)) {
684 /* If this is a DFS path dfs_lookup should return
685 * NT_STATUS_PATH_NOT_COVERED. */
687 status = dfs_path_lookup(conn, dfs_path, &dp,
688 False, consumedcntp, targetpath);
690 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
691 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
693 conn_free_internal(conn);
697 /* We know this is a valid dfs link. Parse the targetpath. */
698 if (!parse_msdfs_symlink(ctx, targetpath,
699 &jucn->referral_list,
700 &jucn->referral_count)) {
701 DEBUG(3,("get_referred_path: failed to parse symlink "
702 "target %s\n", targetpath ));
703 conn_free_internal(conn);
704 return NT_STATUS_NOT_FOUND;
707 conn_free_internal(conn);
711 static int setup_ver2_dfs_referral(const char *pathname,
713 struct junction_map *junction,
717 char* pdata = *ppdata;
719 unsigned char uni_requestedpath[1024];
720 int uni_reqpathoffset1,uni_reqpathoffset2;
722 int requestedpathlen=0;
727 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
729 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
733 dump_data(0, uni_requestedpath,requestedpathlen);
736 DEBUG(10,("ref count = %u\n",junction->referral_count));
738 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
739 VERSION2_REFERRAL_SIZE * junction->referral_count;
741 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
743 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
745 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
746 2 * requestedpathlen;
747 DEBUG(10,("reply_size: %u\n",reply_size));
749 /* add up the unicode lengths of all the referral paths */
750 for(i=0;i<junction->referral_count;i++) {
751 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
752 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
755 DEBUG(10,("reply_size = %u\n",reply_size));
756 /* add the unexplained 0x16 bytes */
759 pdata = (char *)SMB_REALLOC(pdata,reply_size);
761 DEBUG(0,("Realloc failed!\n"));
766 /* copy in the dfs requested paths.. required for offset calculations */
767 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
768 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
770 /* create the header */
771 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
772 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
774 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
776 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
780 /* add the referral elements */
781 for(i=0;i<junction->referral_count;i++) {
782 struct referral* ref = &junction->referral_list[i];
785 SSVAL(pdata,offset,2); /* version 2 */
786 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
788 SSVAL(pdata,offset+4,1);
790 SSVAL(pdata,offset+4,0);
792 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
793 SIVAL(pdata,offset+8,ref->proximity);
794 SIVAL(pdata,offset+12,ref->ttl);
796 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
797 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
798 /* copy referred path into current offset */
799 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
800 sizeof(pstring), STR_UNICODE);
802 SSVAL(pdata,offset+20,uni_curroffset-offset);
804 uni_curroffset += unilen;
805 offset += VERSION2_REFERRAL_SIZE;
807 /* add in the unexplained 22 (0x16) bytes at the end */
808 memset(pdata+uni_curroffset,'\0',0x16);
812 static int setup_ver3_dfs_referral(const char *pathname,
814 struct junction_map *junction,
818 char* pdata = *ppdata;
820 unsigned char uni_reqpath[1024];
821 int uni_reqpathoffset1, uni_reqpathoffset2;
828 DEBUG(10,("setting up version3 referral\n"));
830 reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
833 dump_data(0, uni_reqpath,reqpathlen);
836 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
837 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
838 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
840 for(i=0;i<junction->referral_count;i++) {
841 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
842 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
845 pdata = (char *)SMB_REALLOC(pdata,reply_size);
847 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
852 /* create the header */
853 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
854 SSVAL(pdata,2,junction->referral_count); /* number of referral */
856 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
858 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
861 /* copy in the reqpaths */
862 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
863 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
866 for(i=0;i<junction->referral_count;i++) {
867 struct referral* ref = &(junction->referral_list[i]);
870 SSVAL(pdata,offset,3); /* version 3 */
871 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
873 SSVAL(pdata,offset+4,1);
875 SSVAL(pdata,offset+4,0);
878 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
879 SIVAL(pdata,offset+8,ref->ttl);
881 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
882 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
883 /* copy referred path into current offset */
884 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
885 sizeof(pstring), STR_UNICODE | STR_TERMINATE);
886 SSVAL(pdata,offset+16,uni_curroffset-offset);
887 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
888 memset(pdata+offset+18,'\0',16);
890 uni_curroffset += unilen;
891 offset += VERSION3_REFERRAL_SIZE;
896 /******************************************************************
897 Set up the DFS referral for the dfs pathname. This call returns
898 the amount of the path covered by this server, and where the
899 client should be redirected to. This is the meat of the
900 TRANS2_GET_DFS_REFERRAL call.
901 ******************************************************************/
903 int setup_dfs_referral(connection_struct *orig_conn,
904 const char *dfs_path,
905 int max_referral_level,
906 char **ppdata, NTSTATUS *pstatus)
908 struct junction_map junction;
910 BOOL self_referral = False;
912 char *pathnamep = NULL;
913 pstring local_dfs_path;
916 if (!(ctx=talloc_init("setup_dfs_referral"))) {
917 *pstatus = NT_STATUS_NO_MEMORY;
921 ZERO_STRUCT(junction);
923 /* get the junction entry */
926 *pstatus = NT_STATUS_NOT_FOUND;
931 * Trim pathname sent by client so it begins with only one backslash.
932 * Two backslashes confuse some dfs clients
935 pstrcpy(local_dfs_path, dfs_path);
936 pathnamep = local_dfs_path;
937 while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) {
941 /* The following call can change cwd. */
942 *pstatus = get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral);
943 if (!NT_STATUS_IS_OK(*pstatus)) {
944 vfs_ChDir(orig_conn,orig_conn->connectpath);
948 vfs_ChDir(orig_conn,orig_conn->connectpath);
950 if (!self_referral) {
951 pathnamep[consumedcnt] = '\0';
953 if( DEBUGLVL( 3 ) ) {
955 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
956 for(i=0;i<junction.referral_count;i++)
957 dbgtext(" %s",junction.referral_list[i].alternate_path);
962 /* create the referral depeding on version */
963 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
965 if (max_referral_level < 2) {
966 max_referral_level = 2;
968 if (max_referral_level > 3) {
969 max_referral_level = 3;
972 switch(max_referral_level) {
974 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
975 consumedcnt, self_referral);
978 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
979 consumedcnt, self_referral);
982 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
984 *pstatus = NT_STATUS_INVALID_LEVEL;
989 DEBUGADD(0,("DFS Referral pdata:\n"));
990 dump_data(0,(uint8 *)*ppdata,reply_size);
994 *pstatus = NT_STATUS_OK;
998 /**********************************************************************
999 The following functions are called by the NETDFS RPC pipe functions
1000 **********************************************************************/
1002 /*********************************************************************
1003 Creates a junction structure from a DFS pathname
1004 **********************************************************************/
1006 BOOL create_junction(const char *dfs_path, struct junction_map *jucn)
1012 NTSTATUS status = parse_dfs_path(dfs_path, False, &dp, &dummy);
1014 if (!NT_STATUS_IS_OK(status)) {
1018 /* check if path is dfs : validate first token */
1019 if (!is_myname_or_ipaddr(dp.hostname)) {
1020 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
1021 dp.hostname, dfs_path));
1025 /* Check for a non-DFS share */
1026 snum = lp_servicenumber(dp.servicename);
1028 if(snum < 0 || !lp_msdfs_root(snum)) {
1029 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1034 fstrcpy(jucn->service_name,dp.servicename);
1035 pstrcpy(jucn->volume_name,dp.reqpath);
1036 pstrcpy(jucn->comment, lp_comment(snum));
1040 /**********************************************************************
1041 Forms a valid Unix pathname from the junction
1042 **********************************************************************/
1044 static BOOL junction_to_local_path(struct junction_map *jucn,
1047 connection_struct *conn_out)
1052 snum = lp_servicenumber(jucn->service_name);
1057 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
1058 safe_strcat(path, "/", max_pathlen-1);
1059 safe_strcat(path, jucn->volume_name, max_pathlen-1);
1061 pstrcpy(conn_path, lp_pathname(snum));
1062 if (!NT_STATUS_IS_OK(create_conn_struct(conn_out, snum, conn_path))) {
1069 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
1073 connection_struct conns;
1074 connection_struct *conn = &conns;
1076 BOOL insert_comma = False;
1081 if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
1085 /* Form the msdfs_link contents */
1086 pstrcpy(msdfs_link, "msdfs:");
1087 for(i=0; i<jucn->referral_count; i++) {
1088 char* refpath = jucn->referral_list[i].alternate_path;
1090 /* Alternate paths always use Windows separators. */
1091 trim_char(refpath, '\\', '\\');
1092 if(*refpath == '\0') {
1094 insert_comma = False;
1098 if (i > 0 && insert_comma) {
1099 pstrcat(msdfs_link, ",");
1102 pstrcat(msdfs_link, refpath);
1103 if (!insert_comma) {
1104 insert_comma = True;
1108 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1112 if(SMB_VFS_UNLINK(conn,path)!=0) {
1117 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1118 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
1119 path, msdfs_link, strerror(errno)));
1128 conn_free_internal(conn);
1132 BOOL remove_msdfs_link(struct junction_map *jucn)
1135 connection_struct conns;
1136 connection_struct *conn = &conns;
1141 if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1142 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1145 talloc_destroy( conn->mem_ctx );
1148 conn_free_internal(conn);
1152 static int form_junctions(TALLOC_CTX *ctx,
1154 struct junction_map *jucn,
1158 SMB_STRUCT_DIR *dirp;
1160 pstring connect_path;
1161 char *service_name = lp_servicename(snum);
1162 connection_struct conn;
1163 struct referral *ref = NULL;
1167 if (jn_remain <= 0) {
1171 pstrcpy(connect_path,lp_pathname(snum));
1173 if(*connect_path == '\0') {
1178 * Fake up a connection struct for the VFS layer.
1181 if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
1185 /* form a junction for the msdfs root - convention
1186 DO NOT REMOVE THIS: NT clients will not work with us
1187 if this is not present
1189 fstrcpy(jucn[cnt].service_name, service_name);
1190 jucn[cnt].volume_name[0] = '\0';
1191 jucn[cnt].referral_count = 1;
1193 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1194 if (jucn[cnt].referral_list == NULL) {
1195 DEBUG(0, ("talloc failed!\n"));
1200 ref->ttl = REFERRAL_TTL;
1201 if (*lp_msdfs_proxy(snum) != '\0') {
1202 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1207 pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
1208 get_local_machine_name(),
1212 /* Now enumerate all dfs links */
1213 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1218 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1219 pstring link_target;
1220 if (cnt >= jn_remain) {
1221 SMB_VFS_CLOSEDIR(&conn,dirp);
1222 DEBUG(2, ("ran out of MSDFS junction slots"));
1225 if (is_msdfs_link(&conn, dname, link_target, NULL)) {
1226 if (parse_msdfs_symlink(ctx,
1228 &jucn[cnt].referral_list,
1229 &jucn[cnt].referral_count)) {
1231 fstrcpy(jucn[cnt].service_name, service_name);
1232 pstrcpy(jucn[cnt].volume_name, dname);
1238 SMB_VFS_CLOSEDIR(&conn,dirp);
1242 conn_free_internal(&conn);
1246 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1252 if(!lp_host_msdfs()) {
1256 /* Ensure all the usershares are loaded. */
1258 load_registry_shares();
1259 sharecount = load_usershare_shares();
1262 for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1263 if(lp_msdfs_root(i)) {
1264 jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1270 /******************************************************************************
1271 Core function to resolve a dfs pathname.
1272 ******************************************************************************/
1274 NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name)
1276 NTSTATUS status = NT_STATUS_OK;
1278 if (dfs_pathnames) {
1279 status = dfs_redirect(conn, name, False, &dummy);
1284 /******************************************************************************
1285 Core function to resolve a dfs pathname possibly containing a wildcard.
1286 This function is identical to the above except for the BOOL param to
1287 dfs_redirect but I need this to be separate so it's really clear when
1288 we're allowing wildcards and when we're not. JRA.
1289 ******************************************************************************/
1291 NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard)
1293 NTSTATUS status = NT_STATUS_OK;
1294 if (dfs_pathnames) {
1295 status = dfs_redirect(conn, name, True, ppath_contains_wcard);