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 2 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, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #define DBGC_CLASS DBGC_MSDFS
27 extern uint32 global_client_caps
;
29 /**********************************************************************
30 Parse a DFS pathname of the form \hostname\service\reqpath
31 into the dfs_path structure.
32 If POSIX pathnames is true, the pathname may also be of the
33 form /hostname/service/reqpath.
34 We cope with either here.
36 Unfortunately, due to broken clients who might set the
37 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
38 send a local path, we have to cope with that too....
41 **********************************************************************/
43 static NTSTATUS
parse_dfs_path(const char *pathname
,
46 BOOL
*ppath_contains_wcard
)
48 pstring pathname_local
;
50 NTSTATUS status
= NT_STATUS_OK
;
55 pstrcpy(pathname_local
,pathname
);
56 p
= temp
= pathname_local
;
58 pdp
->posix_path
= (lp_posix_pathnames() && *pathname
== '/');
60 sepchar
= pdp
->posix_path
? '/' : '\\';
62 if (*pathname
!= sepchar
) {
63 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
66 * Possibly client sent a local path by mistake.
67 * Try and convert to a local path.
70 pdp
->hostname
[0] = '\0';
71 pdp
->servicename
[0] = '\0';
73 /* We've got no info about separators. */
74 pdp
->posix_path
= lp_posix_pathnames();
76 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
81 trim_char(temp
,sepchar
,sepchar
);
83 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
87 /* Parse out hostname. */
88 p
= strchr_m(temp
,sepchar
);
90 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
93 * Possibly client sent a local path by mistake.
94 * Try and convert to a local path.
97 pdp
->hostname
[0] = '\0';
98 pdp
->servicename
[0] = '\0';
101 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
106 fstrcpy(pdp
->hostname
,temp
);
107 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp
->hostname
));
109 /* If we got a hostname, is it ours (or an IP address) ? */
110 if (!is_myname_or_ipaddr(pdp
->hostname
)) {
113 DEBUG(10,("parse_dfs_path: hostname %s isn't ours. Try local path from path %s\n",
114 pdp
->hostname
, temp
));
116 * Possibly client sent a local path by mistake.
117 * Try and convert to a local path.
120 pdp
->hostname
[0] = '\0';
121 pdp
->servicename
[0] = '\0';
124 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
129 /* Parse out servicename. */
131 p
= strchr_m(temp
,sepchar
);
133 fstrcpy(pdp
->servicename
,temp
);
134 pdp
->reqpath
[0] = '\0';
138 fstrcpy(pdp
->servicename
,temp
);
139 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp
->servicename
));
145 *ppath_contains_wcard
= False
;
147 /* Rest is reqpath. */
148 if (pdp
->posix_path
) {
149 status
= check_path_syntax_posix(pdp
->reqpath
, p
);
152 status
= check_path_syntax_wcard(pdp
->reqpath
, p
, ppath_contains_wcard
);
154 status
= check_path_syntax(pdp
->reqpath
, p
);
158 if (!NT_STATUS_IS_OK(status
)) {
159 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
160 p
, nt_errstr(status
) ));
164 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp
->reqpath
));
168 /********************************************************
169 Fake up a connection struct for the VFS layer.
170 Note this CHANGES CWD !!!! JRA.
171 *********************************************************/
173 static NTSTATUS
create_conn_struct(connection_struct
*conn
, int snum
, const char *path
)
179 pstrcpy(connpath
, path
);
180 pstring_sub(connpath
, "%S", lp_servicename(snum
));
182 /* needed for smbd_vfs_init() */
184 if ((conn
->mem_ctx
=talloc_init("connection_struct")) == NULL
) {
185 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
186 return NT_STATUS_NO_MEMORY
;
189 if (!(conn
->params
= TALLOC_ZERO_P(conn
->mem_ctx
, struct share_params
))) {
190 DEBUG(0, ("TALLOC failed\n"));
191 return NT_STATUS_NO_MEMORY
;
194 conn
->params
->service
= snum
;
196 set_conn_connectpath(conn
, connpath
);
198 if (!smbd_vfs_init(conn
)) {
199 NTSTATUS status
= map_nt_error_from_unix(errno
);
200 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
201 conn_free_internal(conn
);
206 * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
207 * share as the anonymous user. If we try to chdir as that user we will
208 * fail.... WTF ? JRA.
211 if (vfs_ChDir(conn
,conn
->connectpath
) != 0) {
212 NTSTATUS status
= map_nt_error_from_unix(errno
);
213 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
214 conn
->connectpath
, strerror(errno
) ));
215 conn_free_internal(conn
);
222 /**********************************************************************
223 Parse the contents of a symlink to verify if it is an msdfs referral
224 A valid referral is of the form:
226 msdfs:server1\share1,server2\share2
227 msdfs:server1\share1\pathname,server2\share2\pathname
228 msdfs:server1/share1,server2/share2
229 msdfs:server1/share1/pathname,server2/share2/pathname.
231 Note that the alternate paths returned here must be of the canonicalized
235 \server\share\path\to\file,
237 even in posix path mode. This is because we have no knowledge if the
238 server we're referring to understands posix paths.
239 **********************************************************************/
241 static BOOL
parse_msdfs_symlink(TALLOC_CTX
*ctx
,
243 struct referral
**preflist
,
248 char *alt_path
[MAX_REFERRAL_COUNT
];
250 struct referral
*reflist
;
252 pstrcpy(temp
,target
);
253 prot
= strtok(temp
,":");
255 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
259 /* parse out the alternate paths */
260 while((count
<MAX_REFERRAL_COUNT
) &&
261 ((alt_path
[count
] = strtok(NULL
,",")) != NULL
)) {
265 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count
));
268 reflist
= *preflist
= TALLOC_ZERO_ARRAY(ctx
, struct referral
, count
);
269 if(reflist
== NULL
) {
270 DEBUG(0,("parse_msdfs_symlink: talloc failed!\n"));
274 reflist
= *preflist
= NULL
;
277 for(i
=0;i
<count
;i
++) {
280 /* Canonicalize link target. Replace all /'s in the path by a \ */
281 string_replace(alt_path
[i
], '/', '\\');
283 /* Remove leading '\\'s */
285 while (*p
&& (*p
== '\\')) {
289 pstrcpy(reflist
[i
].alternate_path
, "\\");
290 pstrcat(reflist
[i
].alternate_path
, p
);
292 reflist
[i
].proximity
= 0;
293 reflist
[i
].ttl
= REFERRAL_TTL
;
294 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n", reflist
[i
].alternate_path
));
301 /**********************************************************************
302 Returns true if the unix path is a valid msdfs symlink and also
303 returns the target string from inside the link.
304 **********************************************************************/
306 BOOL
is_msdfs_link(connection_struct
*conn
,
309 SMB_STRUCT_STAT
*sbufp
)
312 int referral_len
= 0;
318 if (SMB_VFS_LSTAT(conn
, path
, sbufp
) != 0) {
319 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path
));
323 if (!S_ISLNK(sbufp
->st_mode
)) {
324 DEBUG(5,("is_msdfs_link: %s is not a link.\n",path
));
328 /* open the link and read it */
329 referral_len
= SMB_VFS_READLINK(conn
, path
, link_target
, sizeof(pstring
)-1);
330 if (referral_len
== -1) {
331 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n",
332 path
, strerror(errno
)));
335 link_target
[referral_len
] = '\0';
337 DEBUG(5,("is_msdfs_link: %s -> %s\n",path
, link_target
));
339 if (!strnequal(link_target
, "msdfs:", 6)) {
345 /*****************************************************************
346 Used by other functions to decide if a dfs path is remote,
347 and to get the list of referred locations for that remote path.
349 search_flag: For findfirsts, dfs links themselves are not
350 redirected, but paths beyond the links are. For normal smb calls,
351 even dfs links need to be redirected.
353 consumedcntp: how much of the dfs path is being redirected. the client
354 should try the remaining path on the redirected server.
356 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
357 link redirect are in targetpath.
358 *****************************************************************/
360 static NTSTATUS
dfs_path_lookup(connection_struct
*conn
,
361 const char *dfspath
, /* Incoming complete dfs path */
362 const struct dfs_path
*pdp
, /* Parsed out server+share+extrapath. */
363 BOOL search_flag
, /* Called from a findfirst ? */
369 SMB_STRUCT_STAT sbuf
;
372 pstring canon_dfspath
; /* Canonicalized dfs path. (only '/' components). */
374 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
375 conn
->connectpath
, pdp
->reqpath
));
378 * Note the unix path conversion here we're doing we can
379 * throw away. We're looking for a symlink for a dfs
380 * resolution, if we don't find it we'll do another
381 * unix_convert later in the codepath.
382 * If we needed to remember what we'd resolved in
383 * dp->reqpath (as the original code did) we'd
384 * pstrcpy(localhost, dp->reqpath) on any code
385 * path below that returns True - but I don't
386 * think this is needed. JRA.
389 pstrcpy(localpath
, pdp
->reqpath
);
390 status
= unix_convert(conn
, localpath
, search_flag
, NULL
, &sbuf
);
391 if (!NT_STATUS_IS_OK(status
) && !NT_STATUS_EQUAL(status
,
392 NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
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 * localpath comes out of unix_convert, so it has
425 * no trailing backslash. Make sure that canon_dfspath hasn't either.
426 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
429 trim_char(canon_dfspath
,0,'/');
432 * Redirect if any component in the path is a link.
433 * We do this by walking backwards through the
434 * local path, chopping off the last component
435 * in both the local path and the canonicalized
436 * DFS path. If we hit a DFS link then we're done.
439 p
= strrchr_m(localpath
, '/');
441 q
= strrchr_m(canon_dfspath
, '/');
450 if (is_msdfs_link(conn
, localpath
, targetpath
, NULL
)) {
451 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
452 "parent %s is dfs link\n", dfspath
, localpath
));
455 *consumedcntp
= strlen(canon_dfspath
);
456 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
457 "(%d)\n", canon_dfspath
, *consumedcntp
));
460 return NT_STATUS_PATH_NOT_COVERED
;
463 /* Step back on the filesystem. */
464 p
= strrchr_m(localpath
, '/');
467 /* And in the canonicalized dfs path. */
468 q
= strrchr_m(canon_dfspath
, '/');
475 /*****************************************************************
476 Decides if a dfs pathname should be redirected or not.
477 If not, the pathname is converted to a tcon-relative local unix path
479 search_wcard_flag: this flag performs 2 functions bother related
480 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
483 This function can return NT_STATUS_OK, meaning use the returned path as-is
484 (mapped into a local path).
485 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
486 any other NT_STATUS error which is a genuine error to be
487 returned to the client.
488 *****************************************************************/
490 static NTSTATUS
dfs_redirect( connection_struct
*conn
,
492 BOOL search_wcard_flag
,
493 BOOL
*ppath_contains_wcard
)
499 status
= parse_dfs_path(dfs_path
, search_wcard_flag
, &dp
, ppath_contains_wcard
);
500 if (!NT_STATUS_IS_OK(status
)) {
504 if (dp
.reqpath
[0] == '\0') {
505 pstrcpy(dfs_path
, dp
.reqpath
);
506 DEBUG(5,("dfs_redirect: self-referral.\n"));
510 /* If dfs pathname for a non-dfs share, convert to tcon-relative
511 path and return OK */
513 if (!lp_msdfs_root(SNUM(conn
))) {
514 pstrcpy(dfs_path
, dp
.reqpath
);
518 /* If it looked like a local path (zero hostname/servicename)
519 * just treat as a tcon-relative path. */
521 if (dp
.hostname
[0] == '\0' && dp
.servicename
[0] == '\0') {
522 pstrcpy(dfs_path
, dp
.reqpath
);
526 if (!( strequal(dp
.servicename
, lp_servicename(SNUM(conn
)))
527 || (strequal(dp
.servicename
, HOMES_NAME
)
528 && strequal(lp_servicename(SNUM(conn
)), get_current_username()) )) ) {
530 /* The given sharename doesn't match this connection. */
532 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
535 status
= dfs_path_lookup(conn
, dfs_path
, &dp
,
536 search_wcard_flag
, NULL
, targetpath
);
537 if (!NT_STATUS_IS_OK(status
)) {
538 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
539 DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path
));
541 DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
542 dfs_path
, nt_errstr(status
) ));
547 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path
));
549 /* Form non-dfs tcon-relative path */
550 pstrcpy(dfs_path
, dp
.reqpath
);
552 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path
));
556 /**********************************************************************
557 Return a self referral.
558 **********************************************************************/
560 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
561 const char *dfs_path
,
562 struct junction_map
*jucn
,
564 BOOL
*self_referralp
)
566 struct referral
*ref
;
568 *self_referralp
= True
;
570 jucn
->referral_count
= 1;
571 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
572 DEBUG(0,("self_ref: talloc failed for referral\n"));
573 return NT_STATUS_NO_MEMORY
;
576 pstrcpy(ref
->alternate_path
,dfs_path
);
578 ref
->ttl
= REFERRAL_TTL
;
579 jucn
->referral_list
= ref
;
580 *consumedcntp
= strlen(dfs_path
);
584 /**********************************************************************
585 Gets valid referrals for a dfs path and fills up the
586 junction_map structure.
587 **********************************************************************/
589 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
590 const char *dfs_path
,
591 struct junction_map
*jucn
,
593 BOOL
*self_referralp
)
595 struct connection_struct conns
;
596 struct connection_struct
*conn
= &conns
;
601 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
606 *self_referralp
= False
;
608 status
= parse_dfs_path(dfs_path
, False
, &dp
, &dummy
);
609 if (!NT_STATUS_IS_OK(status
)) {
613 /* Verify hostname in path */
614 if (!is_myname_or_ipaddr(dp
.hostname
)) {
615 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
616 dp
.hostname
, dfs_path
));
617 return NT_STATUS_NOT_FOUND
;
620 fstrcpy(jucn
->service_name
, dp
.servicename
);
621 pstrcpy(jucn
->volume_name
, dp
.reqpath
);
623 /* Verify the share is a dfs root */
624 snum
= lp_servicenumber(jucn
->service_name
);
626 if ((snum
= find_service(jucn
->service_name
)) < 0) {
627 return NT_STATUS_NOT_FOUND
;
631 if (!lp_msdfs_root(snum
)) {
632 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
633 dp
.servicename
, dfs_path
));
634 return NT_STATUS_NOT_FOUND
;
638 * Self referrals are tested with a anonymous IPC connection and
639 * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
640 * to an empty string). create_conn_struct cd's into the directory and will
641 * fail if it cannot (as the anonymous user). Cope with this.
644 if (dp
.reqpath
[0] == '\0') {
645 struct referral
*ref
;
647 if (*lp_msdfs_proxy(snum
) == '\0') {
656 * It's an msdfs proxy share. Redirect to
657 * the configured target share.
660 jucn
->referral_count
= 1;
661 if ((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
662 DEBUG(0, ("malloc failed for referral\n"));
663 return NT_STATUS_NO_MEMORY
;
666 pstrcpy(ref
->alternate_path
, lp_msdfs_proxy(snum
));
667 if (dp
.reqpath
[0] != '\0') {
668 pstrcat(ref
->alternate_path
, dp
.reqpath
);
671 ref
->ttl
= REFERRAL_TTL
;
672 jucn
->referral_list
= ref
;
673 *consumedcntp
= strlen(dfs_path
);
677 pstrcpy(conn_path
, lp_pathname(snum
));
678 status
= create_conn_struct(conn
, snum
, conn_path
);
679 if (!NT_STATUS_IS_OK(status
)) {
683 /* If this is a DFS path dfs_lookup should return
684 * NT_STATUS_PATH_NOT_COVERED. */
686 status
= dfs_path_lookup(conn
, dfs_path
, &dp
,
687 False
, consumedcntp
, targetpath
);
689 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
690 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
692 conn_free_internal(conn
);
696 /* We know this is a valid dfs link. Parse the targetpath. */
697 if (!parse_msdfs_symlink(ctx
, targetpath
,
698 &jucn
->referral_list
,
699 &jucn
->referral_count
)) {
700 DEBUG(3,("get_referred_path: failed to parse symlink "
701 "target %s\n", targetpath
));
702 conn_free_internal(conn
);
703 return NT_STATUS_NOT_FOUND
;
706 conn_free_internal(conn
);
710 static int setup_ver2_dfs_referral(const char *pathname
,
712 struct junction_map
*junction
,
716 char* pdata
= *ppdata
;
718 unsigned char uni_requestedpath
[1024];
719 int uni_reqpathoffset1
,uni_reqpathoffset2
;
721 int requestedpathlen
=0;
726 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
728 requestedpathlen
= rpcstr_push(uni_requestedpath
, pathname
, sizeof(pstring
),
732 dump_data(0, (const char *) uni_requestedpath
,requestedpathlen
);
735 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
737 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
738 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
740 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
742 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
744 reply_size
= REFERRAL_HEADER_SIZE
+ VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
745 2 * requestedpathlen
;
746 DEBUG(10,("reply_size: %u\n",reply_size
));
748 /* add up the unicode lengths of all the referral paths */
749 for(i
=0;i
<junction
->referral_count
;i
++) {
750 DEBUG(10,("referral %u : %s\n",i
,junction
->referral_list
[i
].alternate_path
));
751 reply_size
+= (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
754 DEBUG(10,("reply_size = %u\n",reply_size
));
755 /* add the unexplained 0x16 bytes */
758 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
760 DEBUG(0,("Realloc failed!\n"));
765 /* copy in the dfs requested paths.. required for offset calculations */
766 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
767 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
769 /* create the header */
770 SSVAL(pdata
,0,consumedcnt
* 2); /* path consumed */
771 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral in this pkt */
773 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
775 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
779 /* add the referral elements */
780 for(i
=0;i
<junction
->referral_count
;i
++) {
781 struct referral
* ref
= &junction
->referral_list
[i
];
784 SSVAL(pdata
,offset
,2); /* version 2 */
785 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
787 SSVAL(pdata
,offset
+4,1);
789 SSVAL(pdata
,offset
+4,0);
791 SSVAL(pdata
,offset
+6,0); /* ref_flags :use path_consumed bytes? */
792 SIVAL(pdata
,offset
+8,ref
->proximity
);
793 SIVAL(pdata
,offset
+12,ref
->ttl
);
795 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
796 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
797 /* copy referred path into current offset */
798 unilen
= rpcstr_push(pdata
+uni_curroffset
, ref
->alternate_path
,
799 sizeof(pstring
), STR_UNICODE
);
801 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
803 uni_curroffset
+= unilen
;
804 offset
+= VERSION2_REFERRAL_SIZE
;
806 /* add in the unexplained 22 (0x16) bytes at the end */
807 memset(pdata
+uni_curroffset
,'\0',0x16);
811 static int setup_ver3_dfs_referral(const char *pathname
,
813 struct junction_map
*junction
,
817 char* pdata
= *ppdata
;
819 unsigned char uni_reqpath
[1024];
820 int uni_reqpathoffset1
, uni_reqpathoffset2
;
827 DEBUG(10,("setting up version3 referral\n"));
829 reqpathlen
= rpcstr_push(uni_reqpath
, pathname
, sizeof(pstring
), STR_TERMINATE
);
832 dump_data(0, (char *) uni_reqpath
,reqpathlen
);
835 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+ VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
836 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
837 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
839 for(i
=0;i
<junction
->referral_count
;i
++) {
840 DEBUG(10,("referral %u : %s\n",i
,junction
->referral_list
[i
].alternate_path
));
841 reply_size
+= (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
844 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
846 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
851 /* create the header */
852 SSVAL(pdata
,0,consumedcnt
* 2); /* path consumed */
853 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral */
855 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
857 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
860 /* copy in the reqpaths */
861 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
862 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
865 for(i
=0;i
<junction
->referral_count
;i
++) {
866 struct referral
* ref
= &(junction
->referral_list
[i
]);
869 SSVAL(pdata
,offset
,3); /* version 3 */
870 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
872 SSVAL(pdata
,offset
+4,1);
874 SSVAL(pdata
,offset
+4,0);
877 SSVAL(pdata
,offset
+6,0); /* ref_flags :use path_consumed bytes? */
878 SIVAL(pdata
,offset
+8,ref
->ttl
);
880 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
881 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
882 /* copy referred path into current offset */
883 unilen
= rpcstr_push(pdata
+uni_curroffset
,ref
->alternate_path
,
884 sizeof(pstring
), STR_UNICODE
| STR_TERMINATE
);
885 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
886 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
887 memset(pdata
+offset
+18,'\0',16);
889 uni_curroffset
+= unilen
;
890 offset
+= VERSION3_REFERRAL_SIZE
;
895 /******************************************************************
896 Set up the DFS referral for the dfs pathname. This call returns
897 the amount of the path covered by this server, and where the
898 client should be redirected to. This is the meat of the
899 TRANS2_GET_DFS_REFERRAL call.
900 ******************************************************************/
902 int setup_dfs_referral(connection_struct
*orig_conn
,
903 const char *dfs_path
,
904 int max_referral_level
,
905 char **ppdata
, NTSTATUS
*pstatus
)
907 struct junction_map junction
;
909 BOOL self_referral
= False
;
911 char *pathnamep
= NULL
;
912 pstring local_dfs_path
;
915 if (!(ctx
=talloc_init("setup_dfs_referral"))) {
916 *pstatus
= NT_STATUS_NO_MEMORY
;
920 ZERO_STRUCT(junction
);
922 /* get the junction entry */
925 *pstatus
= NT_STATUS_NOT_FOUND
;
930 * Trim pathname sent by client so it begins with only one backslash.
931 * Two backslashes confuse some dfs clients
934 pstrcpy(local_dfs_path
, dfs_path
);
935 pathnamep
= local_dfs_path
;
936 while (IS_DIRECTORY_SEP(pathnamep
[0]) && IS_DIRECTORY_SEP(pathnamep
[1])) {
940 /* The following call can change cwd. */
941 *pstatus
= get_referred_path(ctx
, pathnamep
, &junction
, &consumedcnt
, &self_referral
);
942 if (!NT_STATUS_IS_OK(*pstatus
)) {
943 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
947 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
949 if (!self_referral
) {
950 pathnamep
[consumedcnt
] = '\0';
952 if( DEBUGLVL( 3 ) ) {
954 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep
);
955 for(i
=0;i
<junction
.referral_count
;i
++)
956 dbgtext(" %s",junction
.referral_list
[i
].alternate_path
);
961 /* create the referral depeding on version */
962 DEBUG(10,("max_referral_level :%d\n",max_referral_level
));
964 if (max_referral_level
< 2) {
965 max_referral_level
= 2;
967 if (max_referral_level
> 3) {
968 max_referral_level
= 3;
971 switch(max_referral_level
) {
973 reply_size
= setup_ver2_dfs_referral(pathnamep
, ppdata
, &junction
,
974 consumedcnt
, self_referral
);
977 reply_size
= setup_ver3_dfs_referral(pathnamep
, ppdata
, &junction
,
978 consumedcnt
, self_referral
);
981 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level
));
983 *pstatus
= NT_STATUS_INVALID_LEVEL
;
988 DEBUGADD(0,("DFS Referral pdata:\n"));
989 dump_data(0,*ppdata
,reply_size
);
993 *pstatus
= NT_STATUS_OK
;
997 /**********************************************************************
998 The following functions are called by the NETDFS RPC pipe functions
999 **********************************************************************/
1001 /*********************************************************************
1002 Creates a junction structure from a DFS pathname
1003 **********************************************************************/
1005 BOOL
create_junction(const char *dfs_path
, struct junction_map
*jucn
)
1011 NTSTATUS status
= parse_dfs_path(dfs_path
, False
, &dp
, &dummy
);
1013 if (!NT_STATUS_IS_OK(status
)) {
1017 /* check if path is dfs : validate first token */
1018 if (!is_myname_or_ipaddr(dp
.hostname
)) {
1019 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
1020 dp
.hostname
, dfs_path
));
1024 /* Check for a non-DFS share */
1025 snum
= lp_servicenumber(dp
.servicename
);
1027 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1028 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1033 fstrcpy(jucn
->service_name
,dp
.servicename
);
1034 pstrcpy(jucn
->volume_name
,dp
.reqpath
);
1035 pstrcpy(jucn
->comment
, lp_comment(snum
));
1039 /**********************************************************************
1040 Forms a valid Unix pathname from the junction
1041 **********************************************************************/
1043 static BOOL
junction_to_local_path(struct junction_map
*jucn
,
1046 connection_struct
*conn_out
)
1051 snum
= lp_servicenumber(jucn
->service_name
);
1056 safe_strcpy(path
, lp_pathname(snum
), max_pathlen
-1);
1057 safe_strcat(path
, "/", max_pathlen
-1);
1058 safe_strcat(path
, jucn
->volume_name
, max_pathlen
-1);
1060 pstrcpy(conn_path
, lp_pathname(snum
));
1061 if (!NT_STATUS_IS_OK(create_conn_struct(conn_out
, snum
, conn_path
))) {
1068 BOOL
create_msdfs_link(struct junction_map
*jucn
, BOOL exists
)
1072 connection_struct conns
;
1073 connection_struct
*conn
= &conns
;
1075 BOOL insert_comma
= False
;
1080 if(!junction_to_local_path(jucn
, path
, sizeof(path
), conn
)) {
1084 /* Form the msdfs_link contents */
1085 pstrcpy(msdfs_link
, "msdfs:");
1086 for(i
=0; i
<jucn
->referral_count
; i
++) {
1087 char* refpath
= jucn
->referral_list
[i
].alternate_path
;
1089 /* Alternate paths always use Windows separators. */
1090 trim_char(refpath
, '\\', '\\');
1091 if(*refpath
== '\0') {
1093 insert_comma
= False
;
1097 if (i
> 0 && insert_comma
) {
1098 pstrcat(msdfs_link
, ",");
1101 pstrcat(msdfs_link
, refpath
);
1102 if (!insert_comma
) {
1103 insert_comma
= True
;
1107 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1111 if(SMB_VFS_UNLINK(conn
,path
)!=0) {
1116 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1117 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
1118 path
, msdfs_link
, strerror(errno
)));
1127 conn_free_internal(conn
);
1131 BOOL
remove_msdfs_link(struct junction_map
*jucn
)
1134 connection_struct conns
;
1135 connection_struct
*conn
= &conns
;
1140 if( junction_to_local_path(jucn
, path
, sizeof(path
), conn
) ) {
1141 if( SMB_VFS_UNLINK(conn
, path
) == 0 ) {
1144 talloc_destroy( conn
->mem_ctx
);
1147 conn_free_internal(conn
);
1151 static int form_junctions(TALLOC_CTX
*ctx
,
1153 struct junction_map
*jucn
,
1157 SMB_STRUCT_DIR
*dirp
;
1159 pstring connect_path
;
1160 char *service_name
= lp_servicename(snum
);
1161 connection_struct conn
;
1162 struct referral
*ref
= NULL
;
1166 if (jn_remain
<= 0) {
1170 pstrcpy(connect_path
,lp_pathname(snum
));
1172 if(*connect_path
== '\0') {
1177 * Fake up a connection struct for the VFS layer.
1180 if (!NT_STATUS_IS_OK(create_conn_struct(&conn
, snum
, connect_path
))) {
1184 /* form a junction for the msdfs root - convention
1185 DO NOT REMOVE THIS: NT clients will not work with us
1186 if this is not present
1188 fstrcpy(jucn
[cnt
].service_name
, service_name
);
1189 jucn
[cnt
].volume_name
[0] = '\0';
1190 jucn
[cnt
].referral_count
= 1;
1192 ref
= jucn
[cnt
].referral_list
= TALLOC_ZERO_P(ctx
, struct referral
);
1193 if (jucn
[cnt
].referral_list
== NULL
) {
1194 DEBUG(0, ("talloc failed!\n"));
1199 ref
->ttl
= REFERRAL_TTL
;
1200 if (*lp_msdfs_proxy(snum
) != '\0') {
1201 pstrcpy(ref
->alternate_path
, lp_msdfs_proxy(snum
));
1206 pstr_sprintf(ref
->alternate_path
, "\\\\%s\\%s",
1207 get_local_machine_name(),
1211 /* Now enumerate all dfs links */
1212 dirp
= SMB_VFS_OPENDIR(&conn
, ".", NULL
, 0);
1217 while ((dname
= vfs_readdirname(&conn
, dirp
)) != NULL
) {
1218 pstring link_target
;
1219 if (cnt
>= jn_remain
) {
1220 SMB_VFS_CLOSEDIR(&conn
,dirp
);
1221 DEBUG(2, ("ran out of MSDFS junction slots"));
1224 if (is_msdfs_link(&conn
, dname
, link_target
, NULL
)) {
1225 if (parse_msdfs_symlink(ctx
,
1227 &jucn
[cnt
].referral_list
,
1228 &jucn
[cnt
].referral_count
)) {
1230 fstrcpy(jucn
[cnt
].service_name
, service_name
);
1231 pstrcpy(jucn
[cnt
].volume_name
, dname
);
1237 SMB_VFS_CLOSEDIR(&conn
,dirp
);
1241 conn_free_internal(&conn
);
1245 int enum_msdfs_links(TALLOC_CTX
*ctx
, struct junction_map
*jucn
, int jn_max
)
1251 if(!lp_host_msdfs()) {
1255 /* Ensure all the usershares are loaded. */
1257 sharecount
= load_usershare_shares();
1260 for(i
=0;i
< sharecount
&& (jn_max
- jn_count
) > 0;i
++) {
1261 if(lp_msdfs_root(i
)) {
1262 jn_count
+= form_junctions(ctx
, i
,jucn
,jn_max
- jn_count
);
1268 /******************************************************************************
1269 Core function to resolve a dfs pathname.
1270 ******************************************************************************/
1272 NTSTATUS
resolve_dfspath(connection_struct
*conn
, BOOL dfs_pathnames
, pstring name
)
1274 NTSTATUS status
= NT_STATUS_OK
;
1276 if (dfs_pathnames
) {
1277 status
= dfs_redirect(conn
, name
, False
, &dummy
);
1282 /******************************************************************************
1283 Core function to resolve a dfs pathname possibly containing a wildcard.
1284 This function is identical to the above except for the BOOL param to
1285 dfs_redirect but I need this to be separate so it's really clear when
1286 we're allowing wildcards and when we're not. JRA.
1287 ******************************************************************************/
1289 NTSTATUS
resolve_dfspath_wcard(connection_struct
*conn
, BOOL dfs_pathnames
, pstring name
, BOOL
*ppath_contains_wcard
)
1291 NTSTATUS status
= NT_STATUS_OK
;
1292 if (dfs_pathnames
) {
1293 status
= dfs_redirect(conn
, name
, True
, ppath_contains_wcard
);