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
)) {
395 /* Optimization - check if we can redirect the whole path. */
397 if (is_msdfs_link(conn
, localpath
, targetpath
, NULL
)) {
399 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
400 "for dfs link %s.\n", dfspath
));
404 DEBUG(6,("dfs_path_lookup: %s resolves to a "
405 "valid dfs link %s.\n", dfspath
, targetpath
));
408 *consumedcntp
= strlen(dfspath
);
410 return NT_STATUS_PATH_NOT_COVERED
;
413 /* Prepare to test only for '/' components in the given path,
414 * so if a Windows path replace all '\\' characters with '/'.
415 * For a POSIX DFS path we know all separators are already '/'. */
417 pstrcpy(canon_dfspath
, dfspath
);
418 if (!pdp
->posix_path
) {
419 string_replace(canon_dfspath
, '\\', '/');
423 * Redirect if any component in the path is a link.
424 * We do this by walking backwards through the
425 * local path, chopping off the last component
426 * in both the local path and the canonicalized
427 * DFS path. If we hit a DFS link then we're done.
430 p
= strrchr_m(localpath
, '/');
432 q
= strrchr_m(canon_dfspath
, '/');
441 if (is_msdfs_link(conn
, localpath
, targetpath
, NULL
)) {
442 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
443 "parent %s is dfs link\n", dfspath
, localpath
));
446 *consumedcntp
= strlen(canon_dfspath
);
447 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
448 "(%d)\n", canon_dfspath
, *consumedcntp
));
451 return NT_STATUS_PATH_NOT_COVERED
;
454 /* Step back on the filesystem. */
455 p
= strrchr_m(localpath
, '/');
458 /* And in the canonicalized dfs path. */
459 q
= strrchr_m(canon_dfspath
, '/');
466 /*****************************************************************
467 Decides if a dfs pathname should be redirected or not.
468 If not, the pathname is converted to a tcon-relative local unix path
470 search_wcard_flag: this flag performs 2 functions bother related
471 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
474 This function can return NT_STATUS_OK, meaning use the returned path as-is
475 (mapped into a local path).
476 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
477 any other NT_STATUS error which is a genuine error to be
478 returned to the client.
479 *****************************************************************/
481 static NTSTATUS
dfs_redirect( connection_struct
*conn
,
483 BOOL search_wcard_flag
,
484 BOOL
*ppath_contains_wcard
)
490 status
= parse_dfs_path(dfs_path
, search_wcard_flag
, &dp
, ppath_contains_wcard
);
491 if (!NT_STATUS_IS_OK(status
)) {
495 if (dp
.reqpath
[0] == '\0') {
496 pstrcpy(dfs_path
, dp
.reqpath
);
497 DEBUG(5,("dfs_redirect: self-referral.\n"));
501 /* If dfs pathname for a non-dfs share, convert to tcon-relative
502 path and return OK */
504 if (!lp_msdfs_root(SNUM(conn
))) {
505 pstrcpy(dfs_path
, dp
.reqpath
);
509 /* If it looked like a local path (zero hostname/servicename)
510 * just treat as a tcon-relative path. */
512 if (dp
.hostname
[0] == '\0' && dp
.servicename
[0] == '\0') {
513 pstrcpy(dfs_path
, dp
.reqpath
);
517 if (!( strequal(dp
.servicename
, lp_servicename(SNUM(conn
)))
518 || (strequal(dp
.servicename
, HOMES_NAME
)
519 && strequal(lp_servicename(SNUM(conn
)), get_current_username()) )) ) {
521 /* The given sharename doesn't match this connection. */
523 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
526 status
= dfs_path_lookup(conn
, dfs_path
, &dp
,
527 search_wcard_flag
, NULL
, targetpath
);
528 if (!NT_STATUS_IS_OK(status
)) {
529 if (NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
530 DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path
));
532 DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
533 dfs_path
, nt_errstr(status
) ));
538 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path
));
540 /* Form non-dfs tcon-relative path */
541 pstrcpy(dfs_path
, dp
.reqpath
);
543 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path
));
547 /**********************************************************************
548 Return a self referral.
549 **********************************************************************/
551 static NTSTATUS
self_ref(TALLOC_CTX
*ctx
,
552 const char *dfs_path
,
553 struct junction_map
*jucn
,
555 BOOL
*self_referralp
)
557 struct referral
*ref
;
559 *self_referralp
= True
;
561 jucn
->referral_count
= 1;
562 if((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
563 DEBUG(0,("self_ref: talloc failed for referral\n"));
564 return NT_STATUS_NO_MEMORY
;
567 pstrcpy(ref
->alternate_path
,dfs_path
);
569 ref
->ttl
= REFERRAL_TTL
;
570 jucn
->referral_list
= ref
;
571 *consumedcntp
= strlen(dfs_path
);
575 /**********************************************************************
576 Gets valid referrals for a dfs path and fills up the
577 junction_map structure.
578 **********************************************************************/
580 NTSTATUS
get_referred_path(TALLOC_CTX
*ctx
,
581 const char *dfs_path
,
582 struct junction_map
*jucn
,
584 BOOL
*self_referralp
)
586 struct connection_struct conns
;
587 struct connection_struct
*conn
= &conns
;
592 NTSTATUS status
= NT_STATUS_NOT_FOUND
;
597 *self_referralp
= False
;
599 status
= parse_dfs_path(dfs_path
, False
, &dp
, &dummy
);
600 if (!NT_STATUS_IS_OK(status
)) {
604 /* Verify hostname in path */
605 if (!is_myname_or_ipaddr(dp
.hostname
)) {
606 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
607 dp
.hostname
, dfs_path
));
608 return NT_STATUS_NOT_FOUND
;
611 fstrcpy(jucn
->service_name
, dp
.servicename
);
612 pstrcpy(jucn
->volume_name
, dp
.reqpath
);
614 /* Verify the share is a dfs root */
615 snum
= lp_servicenumber(jucn
->service_name
);
617 if ((snum
= find_service(jucn
->service_name
)) < 0) {
618 return NT_STATUS_NOT_FOUND
;
622 if (!lp_msdfs_root(snum
)) {
623 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
624 dp
.servicename
, dfs_path
));
625 return NT_STATUS_NOT_FOUND
;
629 * Self referrals are tested with a anonymous IPC connection and
630 * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
631 * to an empty string). create_conn_struct cd's into the directory and will
632 * fail if it cannot (as the anonymous user). Cope with this.
635 if (dp
.reqpath
[0] == '\0') {
636 struct referral
*ref
;
638 if (*lp_msdfs_proxy(snum
) == '\0') {
647 * It's an msdfs proxy share. Redirect to
648 * the configured target share.
651 jucn
->referral_count
= 1;
652 if ((ref
= TALLOC_ZERO_P(ctx
, struct referral
)) == NULL
) {
653 DEBUG(0, ("malloc failed for referral\n"));
654 return NT_STATUS_NO_MEMORY
;
657 pstrcpy(ref
->alternate_path
, lp_msdfs_proxy(snum
));
658 if (dp
.reqpath
[0] != '\0') {
659 pstrcat(ref
->alternate_path
, dp
.reqpath
);
662 ref
->ttl
= REFERRAL_TTL
;
663 jucn
->referral_list
= ref
;
664 *consumedcntp
= strlen(dfs_path
);
668 pstrcpy(conn_path
, lp_pathname(snum
));
669 status
= create_conn_struct(conn
, snum
, conn_path
);
670 if (!NT_STATUS_IS_OK(status
)) {
674 /* If this is a DFS path dfs_lookup should return
675 * NT_STATUS_PATH_NOT_COVERED. */
677 status
= dfs_path_lookup(conn
, dfs_path
, &dp
,
678 False
, consumedcntp
, targetpath
);
680 if (!NT_STATUS_EQUAL(status
, NT_STATUS_PATH_NOT_COVERED
)) {
681 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
683 conn_free_internal(conn
);
687 /* We know this is a valid dfs link. Parse the targetpath. */
688 if (!parse_msdfs_symlink(ctx
, targetpath
,
689 &jucn
->referral_list
,
690 &jucn
->referral_count
)) {
691 DEBUG(3,("get_referred_path: failed to parse symlink "
692 "target %s\n", targetpath
));
693 conn_free_internal(conn
);
694 return NT_STATUS_NOT_FOUND
;
697 conn_free_internal(conn
);
701 static int setup_ver2_dfs_referral(const char *pathname
,
703 struct junction_map
*junction
,
707 char* pdata
= *ppdata
;
709 unsigned char uni_requestedpath
[1024];
710 int uni_reqpathoffset1
,uni_reqpathoffset2
;
712 int requestedpathlen
=0;
717 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
719 requestedpathlen
= rpcstr_push(uni_requestedpath
, pathname
, sizeof(pstring
),
723 dump_data(0, uni_requestedpath
,requestedpathlen
);
726 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
728 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
729 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
731 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
733 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
735 reply_size
= REFERRAL_HEADER_SIZE
+ VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
736 2 * requestedpathlen
;
737 DEBUG(10,("reply_size: %u\n",reply_size
));
739 /* add up the unicode lengths of all the referral paths */
740 for(i
=0;i
<junction
->referral_count
;i
++) {
741 DEBUG(10,("referral %u : %s\n",i
,junction
->referral_list
[i
].alternate_path
));
742 reply_size
+= (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
745 DEBUG(10,("reply_size = %u\n",reply_size
));
746 /* add the unexplained 0x16 bytes */
749 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
751 DEBUG(0,("Realloc failed!\n"));
756 /* copy in the dfs requested paths.. required for offset calculations */
757 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
758 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
760 /* create the header */
761 SSVAL(pdata
,0,consumedcnt
* 2); /* path consumed */
762 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral in this pkt */
764 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
766 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
770 /* add the referral elements */
771 for(i
=0;i
<junction
->referral_count
;i
++) {
772 struct referral
* ref
= &junction
->referral_list
[i
];
775 SSVAL(pdata
,offset
,2); /* version 2 */
776 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
778 SSVAL(pdata
,offset
+4,1);
780 SSVAL(pdata
,offset
+4,0);
782 SSVAL(pdata
,offset
+6,0); /* ref_flags :use path_consumed bytes? */
783 SIVAL(pdata
,offset
+8,ref
->proximity
);
784 SIVAL(pdata
,offset
+12,ref
->ttl
);
786 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
787 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
788 /* copy referred path into current offset */
789 unilen
= rpcstr_push(pdata
+uni_curroffset
, ref
->alternate_path
,
790 sizeof(pstring
), STR_UNICODE
);
792 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
794 uni_curroffset
+= unilen
;
795 offset
+= VERSION2_REFERRAL_SIZE
;
797 /* add in the unexplained 22 (0x16) bytes at the end */
798 memset(pdata
+uni_curroffset
,'\0',0x16);
802 static int setup_ver3_dfs_referral(const char *pathname
,
804 struct junction_map
*junction
,
808 char* pdata
= *ppdata
;
810 unsigned char uni_reqpath
[1024];
811 int uni_reqpathoffset1
, uni_reqpathoffset2
;
818 DEBUG(10,("setting up version3 referral\n"));
820 reqpathlen
= rpcstr_push(uni_reqpath
, pathname
, sizeof(pstring
), STR_TERMINATE
);
823 dump_data(0, uni_reqpath
,reqpathlen
);
826 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+ VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
827 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
828 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
830 for(i
=0;i
<junction
->referral_count
;i
++) {
831 DEBUG(10,("referral %u : %s\n",i
,junction
->referral_list
[i
].alternate_path
));
832 reply_size
+= (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
835 pdata
= (char *)SMB_REALLOC(pdata
,reply_size
);
837 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
842 /* create the header */
843 SSVAL(pdata
,0,consumedcnt
* 2); /* path consumed */
844 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral */
846 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
848 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
851 /* copy in the reqpaths */
852 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
853 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
856 for(i
=0;i
<junction
->referral_count
;i
++) {
857 struct referral
* ref
= &(junction
->referral_list
[i
]);
860 SSVAL(pdata
,offset
,3); /* version 3 */
861 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
863 SSVAL(pdata
,offset
+4,1);
865 SSVAL(pdata
,offset
+4,0);
868 SSVAL(pdata
,offset
+6,0); /* ref_flags :use path_consumed bytes? */
869 SIVAL(pdata
,offset
+8,ref
->ttl
);
871 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
872 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
873 /* copy referred path into current offset */
874 unilen
= rpcstr_push(pdata
+uni_curroffset
,ref
->alternate_path
,
875 sizeof(pstring
), STR_UNICODE
| STR_TERMINATE
);
876 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
877 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
878 memset(pdata
+offset
+18,'\0',16);
880 uni_curroffset
+= unilen
;
881 offset
+= VERSION3_REFERRAL_SIZE
;
886 /******************************************************************
887 Set up the DFS referral for the dfs pathname. This call returns
888 the amount of the path covered by this server, and where the
889 client should be redirected to. This is the meat of the
890 TRANS2_GET_DFS_REFERRAL call.
891 ******************************************************************/
893 int setup_dfs_referral(connection_struct
*orig_conn
,
894 const char *dfs_path
,
895 int max_referral_level
,
896 char **ppdata
, NTSTATUS
*pstatus
)
898 struct junction_map junction
;
900 BOOL self_referral
= False
;
902 char *pathnamep
= NULL
;
903 pstring local_dfs_path
;
906 if (!(ctx
=talloc_init("setup_dfs_referral"))) {
907 *pstatus
= NT_STATUS_NO_MEMORY
;
911 ZERO_STRUCT(junction
);
913 /* get the junction entry */
916 *pstatus
= NT_STATUS_NOT_FOUND
;
921 * Trim pathname sent by client so it begins with only one backslash.
922 * Two backslashes confuse some dfs clients
925 pstrcpy(local_dfs_path
, dfs_path
);
926 pathnamep
= local_dfs_path
;
927 while (IS_DIRECTORY_SEP(pathnamep
[0]) && IS_DIRECTORY_SEP(pathnamep
[1])) {
931 /* The following call can change cwd. */
932 *pstatus
= get_referred_path(ctx
, pathnamep
, &junction
, &consumedcnt
, &self_referral
);
933 if (!NT_STATUS_IS_OK(*pstatus
)) {
934 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
938 vfs_ChDir(orig_conn
,orig_conn
->connectpath
);
940 if (!self_referral
) {
941 pathnamep
[consumedcnt
] = '\0';
943 if( DEBUGLVL( 3 ) ) {
945 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep
);
946 for(i
=0;i
<junction
.referral_count
;i
++)
947 dbgtext(" %s",junction
.referral_list
[i
].alternate_path
);
952 /* create the referral depeding on version */
953 DEBUG(10,("max_referral_level :%d\n",max_referral_level
));
955 if (max_referral_level
< 2) {
956 max_referral_level
= 2;
958 if (max_referral_level
> 3) {
959 max_referral_level
= 3;
962 switch(max_referral_level
) {
964 reply_size
= setup_ver2_dfs_referral(pathnamep
, ppdata
, &junction
,
965 consumedcnt
, self_referral
);
968 reply_size
= setup_ver3_dfs_referral(pathnamep
, ppdata
, &junction
,
969 consumedcnt
, self_referral
);
972 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level
));
974 *pstatus
= NT_STATUS_INVALID_LEVEL
;
979 DEBUGADD(0,("DFS Referral pdata:\n"));
980 dump_data(0,(uint8
*)*ppdata
,reply_size
);
984 *pstatus
= NT_STATUS_OK
;
988 /**********************************************************************
989 The following functions are called by the NETDFS RPC pipe functions
990 **********************************************************************/
992 /*********************************************************************
993 Creates a junction structure from a DFS pathname
994 **********************************************************************/
996 BOOL
create_junction(const char *dfs_path
, struct junction_map
*jucn
)
1002 NTSTATUS status
= parse_dfs_path(dfs_path
, False
, &dp
, &dummy
);
1004 if (!NT_STATUS_IS_OK(status
)) {
1008 /* check if path is dfs : validate first token */
1009 if (!is_myname_or_ipaddr(dp
.hostname
)) {
1010 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
1011 dp
.hostname
, dfs_path
));
1015 /* Check for a non-DFS share */
1016 snum
= lp_servicenumber(dp
.servicename
);
1018 if(snum
< 0 || !lp_msdfs_root(snum
)) {
1019 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1024 fstrcpy(jucn
->service_name
,dp
.servicename
);
1025 pstrcpy(jucn
->volume_name
,dp
.reqpath
);
1026 pstrcpy(jucn
->comment
, lp_comment(snum
));
1030 /**********************************************************************
1031 Forms a valid Unix pathname from the junction
1032 **********************************************************************/
1034 static BOOL
junction_to_local_path(struct junction_map
*jucn
,
1037 connection_struct
*conn_out
)
1042 snum
= lp_servicenumber(jucn
->service_name
);
1047 safe_strcpy(path
, lp_pathname(snum
), max_pathlen
-1);
1048 safe_strcat(path
, "/", max_pathlen
-1);
1049 safe_strcat(path
, jucn
->volume_name
, max_pathlen
-1);
1051 pstrcpy(conn_path
, lp_pathname(snum
));
1052 if (!NT_STATUS_IS_OK(create_conn_struct(conn_out
, snum
, conn_path
))) {
1059 BOOL
create_msdfs_link(struct junction_map
*jucn
, BOOL exists
)
1063 connection_struct conns
;
1064 connection_struct
*conn
= &conns
;
1066 BOOL insert_comma
= False
;
1071 if(!junction_to_local_path(jucn
, path
, sizeof(path
), conn
)) {
1075 /* Form the msdfs_link contents */
1076 pstrcpy(msdfs_link
, "msdfs:");
1077 for(i
=0; i
<jucn
->referral_count
; i
++) {
1078 char* refpath
= jucn
->referral_list
[i
].alternate_path
;
1080 /* Alternate paths always use Windows separators. */
1081 trim_char(refpath
, '\\', '\\');
1082 if(*refpath
== '\0') {
1084 insert_comma
= False
;
1088 if (i
> 0 && insert_comma
) {
1089 pstrcat(msdfs_link
, ",");
1092 pstrcat(msdfs_link
, refpath
);
1093 if (!insert_comma
) {
1094 insert_comma
= True
;
1098 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1102 if(SMB_VFS_UNLINK(conn
,path
)!=0) {
1107 if(SMB_VFS_SYMLINK(conn
, msdfs_link
, path
) < 0) {
1108 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
1109 path
, msdfs_link
, strerror(errno
)));
1118 conn_free_internal(conn
);
1122 BOOL
remove_msdfs_link(struct junction_map
*jucn
)
1125 connection_struct conns
;
1126 connection_struct
*conn
= &conns
;
1131 if( junction_to_local_path(jucn
, path
, sizeof(path
), conn
) ) {
1132 if( SMB_VFS_UNLINK(conn
, path
) == 0 ) {
1135 talloc_destroy( conn
->mem_ctx
);
1138 conn_free_internal(conn
);
1142 static int form_junctions(TALLOC_CTX
*ctx
,
1144 struct junction_map
*jucn
,
1148 SMB_STRUCT_DIR
*dirp
;
1150 pstring connect_path
;
1151 char *service_name
= lp_servicename(snum
);
1152 connection_struct conn
;
1153 struct referral
*ref
= NULL
;
1157 if (jn_remain
<= 0) {
1161 pstrcpy(connect_path
,lp_pathname(snum
));
1163 if(*connect_path
== '\0') {
1168 * Fake up a connection struct for the VFS layer.
1171 if (!NT_STATUS_IS_OK(create_conn_struct(&conn
, snum
, connect_path
))) {
1175 /* form a junction for the msdfs root - convention
1176 DO NOT REMOVE THIS: NT clients will not work with us
1177 if this is not present
1179 fstrcpy(jucn
[cnt
].service_name
, service_name
);
1180 jucn
[cnt
].volume_name
[0] = '\0';
1181 jucn
[cnt
].referral_count
= 1;
1183 ref
= jucn
[cnt
].referral_list
= TALLOC_ZERO_P(ctx
, struct referral
);
1184 if (jucn
[cnt
].referral_list
== NULL
) {
1185 DEBUG(0, ("talloc failed!\n"));
1190 ref
->ttl
= REFERRAL_TTL
;
1191 if (*lp_msdfs_proxy(snum
) != '\0') {
1192 pstrcpy(ref
->alternate_path
, lp_msdfs_proxy(snum
));
1197 pstr_sprintf(ref
->alternate_path
, "\\\\%s\\%s",
1198 get_local_machine_name(),
1202 /* Now enumerate all dfs links */
1203 dirp
= SMB_VFS_OPENDIR(&conn
, ".", NULL
, 0);
1208 while ((dname
= vfs_readdirname(&conn
, dirp
)) != NULL
) {
1209 pstring link_target
;
1210 if (cnt
>= jn_remain
) {
1211 SMB_VFS_CLOSEDIR(&conn
,dirp
);
1212 DEBUG(2, ("ran out of MSDFS junction slots"));
1215 if (is_msdfs_link(&conn
, dname
, link_target
, NULL
)) {
1216 if (parse_msdfs_symlink(ctx
,
1218 &jucn
[cnt
].referral_list
,
1219 &jucn
[cnt
].referral_count
)) {
1221 fstrcpy(jucn
[cnt
].service_name
, service_name
);
1222 pstrcpy(jucn
[cnt
].volume_name
, dname
);
1228 SMB_VFS_CLOSEDIR(&conn
,dirp
);
1232 conn_free_internal(&conn
);
1236 int enum_msdfs_links(TALLOC_CTX
*ctx
, struct junction_map
*jucn
, int jn_max
)
1242 if(!lp_host_msdfs()) {
1246 /* Ensure all the usershares are loaded. */
1248 load_registry_shares();
1249 sharecount
= load_usershare_shares();
1252 for(i
=0;i
< sharecount
&& (jn_max
- jn_count
) > 0;i
++) {
1253 if(lp_msdfs_root(i
)) {
1254 jn_count
+= form_junctions(ctx
, i
,jucn
,jn_max
- jn_count
);
1260 /******************************************************************************
1261 Core function to resolve a dfs pathname.
1262 ******************************************************************************/
1264 NTSTATUS
resolve_dfspath(connection_struct
*conn
, BOOL dfs_pathnames
, pstring name
)
1266 NTSTATUS status
= NT_STATUS_OK
;
1268 if (dfs_pathnames
) {
1269 status
= dfs_redirect(conn
, name
, False
, &dummy
);
1274 /******************************************************************************
1275 Core function to resolve a dfs pathname possibly containing a wildcard.
1276 This function is identical to the above except for the BOOL param to
1277 dfs_redirect but I need this to be separate so it's really clear when
1278 we're allowing wildcards and when we're not. JRA.
1279 ******************************************************************************/
1281 NTSTATUS
resolve_dfspath_wcard(connection_struct
*conn
, BOOL dfs_pathnames
, pstring name
, BOOL
*ppath_contains_wcard
)
1283 NTSTATUS status
= NT_STATUS_OK
;
1284 if (dfs_pathnames
) {
1285 status
= dfs_redirect(conn
, name
, True
, ppath_contains_wcard
);