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 BOOL 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"));
189 if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx, struct share_params))) {
190 DEBUG(0, ("TALLOC failed\n"));
194 conn->params->service = snum;
196 set_conn_connectpath(conn, connpath);
198 if (!smbd_vfs_init(conn)) {
199 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
200 conn_free_internal(conn);
205 * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
206 * share as the anonymous user. If we try to chdir as that user we will
207 * fail.... WTF ? JRA.
210 if (vfs_ChDir(conn,conn->connectpath) != 0) {
211 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
212 conn->connectpath, strerror(errno) ));
213 conn_free_internal(conn);
220 /**********************************************************************
221 Parse the contents of a symlink to verify if it is an msdfs referral
222 A valid referral is of the form:
224 msdfs:server1\share1,server2\share2
225 msdfs:server1\share1\pathname,server2\share2\pathname
226 msdfs:server1/share1,server2/share2
227 msdfs:server1/share1/pathname,server2/share2/pathname.
229 Note that the alternate paths returned here must be of the canonicalized
233 \server\share\path\to\file,
235 even in posix path mode. This is because we have no knowledge if the
236 server we're referring to understands posix paths.
237 **********************************************************************/
239 static BOOL parse_msdfs_symlink(TALLOC_CTX *ctx,
241 struct referral **preflist,
246 char *alt_path[MAX_REFERRAL_COUNT];
248 struct referral *reflist;
250 pstrcpy(temp,target);
251 prot = strtok(temp,":");
253 /* parse out the alternate paths */
254 while((count<MAX_REFERRAL_COUNT) &&
255 ((alt_path[count] = strtok(NULL,",")) != NULL)) {
259 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
261 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx, struct referral, count);
262 if(reflist == NULL) {
263 DEBUG(0,("parse_msdfs_symlink: talloc failed!\n"));
267 for(i=0;i<count;i++) {
270 /* Canonicalize link target. Replace all /'s in the path by a \ */
271 string_replace(alt_path[i], '/', '\\');
273 /* Remove leading '\\'s */
275 while (*p && (*p == '\\')) {
279 pstrcpy(reflist[i].alternate_path, "\\");
280 pstrcat(reflist[i].alternate_path, p);
282 reflist[i].proximity = 0;
283 reflist[i].ttl = REFERRAL_TTL;
284 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n", reflist[i].alternate_path));
291 /**********************************************************************
292 Returns true if the unix path is a valid msdfs symlink and also
293 returns the target string from inside the link.
294 **********************************************************************/
296 BOOL is_msdfs_link(connection_struct *conn,
299 SMB_STRUCT_STAT *sbufp)
302 int referral_len = 0;
308 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
309 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
313 if (!S_ISLNK(sbufp->st_mode)) {
314 DEBUG(5,("is_msdfs_link: %s is not a link.\n",path));
318 /* open the link and read it */
319 referral_len = SMB_VFS_READLINK(conn, path, link_target, sizeof(pstring)-1);
320 if (referral_len == -1) {
321 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n",
322 path, strerror(errno)));
325 link_target[referral_len] = '\0';
327 DEBUG(5,("is_msdfs_link: %s -> %s\n",path, link_target));
329 if (!strnequal(link_target, "msdfs:", 6)) {
335 /*****************************************************************
336 Used by other functions to decide if a dfs path is remote,
337 and to get the list of referred locations for that remote path.
339 search_flag: For findfirsts, dfs links themselves are not
340 redirected, but paths beyond the links are. For normal smb calls,
341 even dfs links need to be redirected.
343 consumedcntp: how much of the dfs path is being redirected. the client
344 should try the remaining path on the redirected server.
346 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
347 link redirect are in targetpath.
348 *****************************************************************/
350 static NTSTATUS dfs_path_lookup(connection_struct *conn,
351 const char *dfspath, /* Incoming complete dfs path */
352 const struct dfs_path *pdp, /* Parsed out server+share+extrapath. */
353 BOOL search_flag, /* Called from a findfirst ? */
359 SMB_STRUCT_STAT sbuf;
362 pstring canon_dfspath; /* Canonicalized dfs path. (only '/' components). */
364 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
365 conn->connectpath, pdp->reqpath));
368 * Note the unix path conversion here we're doing we can
369 * throw away. We're looking for a symlink for a dfs
370 * resolution, if we don't find it we'll do another
371 * unix_convert later in the codepath.
372 * If we needed to remember what we'd resolved in
373 * dp->reqpath (as the original code did) we'd
374 * pstrcpy(localhost, dp->reqpath) on any code
375 * path below that returns True - but I don't
376 * think this is needed. JRA.
379 pstrcpy(localpath, pdp->reqpath);
380 status = unix_convert(conn, localpath, search_flag, NULL, &sbuf);
381 if (!NT_STATUS_IS_OK(status)) {
385 /* Optimization - check if we can redirect the whole path. */
387 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
389 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
390 "for dfs link %s.\n", dfspath));
394 DEBUG(6,("dfs_path_lookup: %s resolves to a "
395 "valid dfs link %s.\n", dfspath, targetpath));
398 *consumedcntp = strlen(dfspath);
400 return NT_STATUS_PATH_NOT_COVERED;
403 /* Prepare to test only for '/' components in the given path,
404 * so if a Windows path replace all '\\' characters with '/'.
405 * For a POSIX DFS path we know all separators are already '/'. */
407 pstrcpy(canon_dfspath, dfspath);
408 if (!pdp->posix_path) {
409 string_replace(canon_dfspath, '\\', '/');
413 * Redirect if any component in the path is a link.
414 * We do this by walking backwards through the
415 * local path, chopping off the last component
416 * in both the local path and the canonicalized
417 * DFS path. If we hit a DFS link then we're done.
420 p = strrchr_m(localpath, '/');
422 q = strrchr_m(canon_dfspath, '/');
431 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
432 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
433 "parent %s is dfs link\n", dfspath, localpath));
436 *consumedcntp = strlen(canon_dfspath);
437 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
438 "(%d)\n", canon_dfspath, *consumedcntp));
441 return NT_STATUS_PATH_NOT_COVERED;
444 /* Step back on the filesystem. */
445 p = strrchr_m(localpath, '/');
448 /* And in the canonicalized dfs path. */
449 q = strrchr_m(canon_dfspath, '/');
456 /*****************************************************************
457 Decides if a dfs pathname should be redirected or not.
458 If not, the pathname is converted to a tcon-relative local unix path
460 search_wcard_flag: this flag performs 2 functions bother related
461 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
464 This function can return NT_STATUS_OK, meaning use the returned path as-is
465 (mapped into a local path).
466 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
467 any other NT_STATUS error which is a genuine error to be
468 returned to the client.
469 *****************************************************************/
471 static NTSTATUS dfs_redirect( connection_struct *conn,
473 BOOL search_wcard_flag,
474 BOOL *ppath_contains_wcard)
480 status = parse_dfs_path(dfs_path, search_wcard_flag, &dp, ppath_contains_wcard);
481 if (!NT_STATUS_IS_OK(status)) {
485 if (dp.reqpath[0] == '\0') {
486 pstrcpy(dfs_path, dp.reqpath);
487 DEBUG(5,("dfs_redirect: self-referral.\n"));
491 /* If dfs pathname for a non-dfs share, convert to tcon-relative
492 path and return OK */
494 if (!lp_msdfs_root(SNUM(conn))) {
495 pstrcpy(dfs_path, dp.reqpath);
499 /* If it looked like a local path (zero hostname/servicename)
500 * just treat as a tcon-relative path. */
502 if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') {
503 pstrcpy(dfs_path, dp.reqpath);
507 if (!( strequal(dp.servicename, lp_servicename(SNUM(conn)))
508 || (strequal(dp.servicename, HOMES_NAME)
509 && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) {
511 /* The given sharename doesn't match this connection. */
513 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
516 status = dfs_path_lookup(conn, dfs_path, &dp,
517 search_wcard_flag, NULL, targetpath);
518 if (!NT_STATUS_IS_OK(status)) {
519 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
520 DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path));
522 DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
523 dfs_path, nt_errstr(status) ));
528 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path));
530 /* Form non-dfs tcon-relative path */
531 pstrcpy(dfs_path, dp.reqpath);
533 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path));
537 /**********************************************************************
538 Return a self referral.
539 **********************************************************************/
541 static BOOL self_ref(TALLOC_CTX *ctx,
542 const char *dfs_path,
543 struct junction_map *jucn,
545 BOOL *self_referralp)
547 struct referral *ref;
549 *self_referralp = True;
551 jucn->referral_count = 1;
552 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
553 DEBUG(0,("self_ref: talloc failed for referral\n"));
557 pstrcpy(ref->alternate_path,dfs_path);
559 ref->ttl = REFERRAL_TTL;
560 jucn->referral_list = ref;
561 *consumedcntp = strlen(dfs_path);
565 /**********************************************************************
566 Gets valid referrals for a dfs path and fills up the
567 junction_map structure.
568 **********************************************************************/
570 BOOL get_referred_path(TALLOC_CTX *ctx,
571 const char *dfs_path,
572 struct junction_map *jucn,
574 BOOL *self_referralp)
576 struct connection_struct conns;
577 struct connection_struct *conn = &conns;
588 *self_referralp = False;
590 status = parse_dfs_path(dfs_path, False, &dp, &dummy);
591 if (!NT_STATUS_IS_OK(status)) {
595 /* Verify hostname in path */
596 if (!is_myname_or_ipaddr(dp.hostname)) {
597 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
598 dp.hostname, dfs_path));
602 fstrcpy(jucn->service_name, dp.servicename);
603 pstrcpy(jucn->volume_name, dp.reqpath);
605 /* Verify the share is a dfs root */
606 snum = lp_servicenumber(jucn->service_name);
608 if ((snum = find_service(jucn->service_name)) < 0) {
613 if (!lp_msdfs_root(snum)) {
614 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
615 dp.servicename, dfs_path));
620 * Self referrals are tested with a anonymous IPC connection and
621 * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
622 * to an empty string). create_conn_struct cd's into the directory and will
623 * fail if it cannot (as the anonymous user). Cope with this.
626 if (dp.reqpath[0] == '\0') {
627 struct referral *ref;
629 if (*lp_msdfs_proxy(snum) == '\0') {
638 * It's an msdfs proxy share. Redirect to
639 * the configured target share.
642 jucn->referral_count = 1;
643 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
644 DEBUG(0, ("malloc failed for referral\n"));
648 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
649 if (dp.reqpath[0] != '\0') {
650 pstrcat(ref->alternate_path, dp.reqpath);
653 ref->ttl = REFERRAL_TTL;
654 jucn->referral_list = ref;
655 *consumedcntp = strlen(dfs_path);
660 pstrcpy(conn_path, lp_pathname(snum));
661 if (!create_conn_struct(conn, snum, conn_path)) {
665 /* If this is a DFS path dfs_lookup should return
666 * NT_STATUS_PATH_NOT_COVERED. */
668 status = dfs_path_lookup(conn, dfs_path, &dp,
669 False, consumedcntp, targetpath);
671 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
672 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
677 /* We know this is a valid dfs link. Parse the targetpath. */
678 if (!parse_msdfs_symlink(ctx, targetpath,
679 &jucn->referral_list,
680 &jucn->referral_count)) {
681 DEBUG(3,("get_referred_path: failed to parse symlink "
682 "target %s\n", targetpath ));
690 conn_free_internal(conn);
694 static int setup_ver2_dfs_referral(const char *pathname,
696 struct junction_map *junction,
700 char* pdata = *ppdata;
702 unsigned char uni_requestedpath[1024];
703 int uni_reqpathoffset1,uni_reqpathoffset2;
705 int requestedpathlen=0;
710 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
712 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
716 dump_data(0, (const char *) uni_requestedpath,requestedpathlen);
719 DEBUG(10,("ref count = %u\n",junction->referral_count));
721 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
722 VERSION2_REFERRAL_SIZE * junction->referral_count;
724 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
726 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
728 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
729 2 * requestedpathlen;
730 DEBUG(10,("reply_size: %u\n",reply_size));
732 /* add up the unicode lengths of all the referral paths */
733 for(i=0;i<junction->referral_count;i++) {
734 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
735 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
738 DEBUG(10,("reply_size = %u\n",reply_size));
739 /* add the unexplained 0x16 bytes */
742 pdata = (char *)SMB_REALLOC(pdata,reply_size);
744 DEBUG(0,("Realloc failed!\n"));
749 /* copy in the dfs requested paths.. required for offset calculations */
750 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
751 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
753 /* create the header */
754 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
755 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
757 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
759 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
763 /* add the referral elements */
764 for(i=0;i<junction->referral_count;i++) {
765 struct referral* ref = &junction->referral_list[i];
768 SSVAL(pdata,offset,2); /* version 2 */
769 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
771 SSVAL(pdata,offset+4,1);
773 SSVAL(pdata,offset+4,0);
775 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
776 SIVAL(pdata,offset+8,ref->proximity);
777 SIVAL(pdata,offset+12,ref->ttl);
779 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
780 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
781 /* copy referred path into current offset */
782 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
783 sizeof(pstring), STR_UNICODE);
785 SSVAL(pdata,offset+20,uni_curroffset-offset);
787 uni_curroffset += unilen;
788 offset += VERSION2_REFERRAL_SIZE;
790 /* add in the unexplained 22 (0x16) bytes at the end */
791 memset(pdata+uni_curroffset,'\0',0x16);
795 static int setup_ver3_dfs_referral(const char *pathname,
797 struct junction_map *junction,
801 char* pdata = *ppdata;
803 unsigned char uni_reqpath[1024];
804 int uni_reqpathoffset1, uni_reqpathoffset2;
811 DEBUG(10,("setting up version3 referral\n"));
813 reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
816 dump_data(0, (char *) uni_reqpath,reqpathlen);
819 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
820 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
821 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
823 for(i=0;i<junction->referral_count;i++) {
824 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
825 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
828 pdata = (char *)SMB_REALLOC(pdata,reply_size);
830 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
835 /* create the header */
836 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
837 SSVAL(pdata,2,junction->referral_count); /* number of referral */
839 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
841 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
844 /* copy in the reqpaths */
845 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
846 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
849 for(i=0;i<junction->referral_count;i++) {
850 struct referral* ref = &(junction->referral_list[i]);
853 SSVAL(pdata,offset,3); /* version 3 */
854 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
856 SSVAL(pdata,offset+4,1);
858 SSVAL(pdata,offset+4,0);
861 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
862 SIVAL(pdata,offset+8,ref->ttl);
864 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
865 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
866 /* copy referred path into current offset */
867 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
868 sizeof(pstring), STR_UNICODE | STR_TERMINATE);
869 SSVAL(pdata,offset+16,uni_curroffset-offset);
870 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
871 memset(pdata+offset+18,'\0',16);
873 uni_curroffset += unilen;
874 offset += VERSION3_REFERRAL_SIZE;
879 /******************************************************************
880 Set up the DFS referral for the dfs pathname. This call returns
881 the amount of the path covered by this server, and where the
882 client should be redirected to. This is the meat of the
883 TRANS2_GET_DFS_REFERRAL call.
884 ******************************************************************/
886 int setup_dfs_referral(connection_struct *orig_conn,
887 const char *dfs_path,
888 int max_referral_level,
891 struct junction_map junction;
893 BOOL self_referral = False;
895 char *pathnamep = NULL;
896 pstring local_dfs_path;
899 if (!(ctx=talloc_init("setup_dfs_referral"))) {
903 ZERO_STRUCT(junction);
905 /* get the junction entry */
912 * Trim pathname sent by client so it begins with only one backslash.
913 * Two backslashes confuse some dfs clients
916 pstrcpy(local_dfs_path, dfs_path);
917 pathnamep = local_dfs_path;
918 while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) {
922 /* The following call can change cwd. */
923 if (!get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral)) {
924 vfs_ChDir(orig_conn,orig_conn->connectpath);
928 vfs_ChDir(orig_conn,orig_conn->connectpath);
930 if (!self_referral) {
931 pathnamep[consumedcnt] = '\0';
933 if( DEBUGLVL( 3 ) ) {
935 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
936 for(i=0;i<junction.referral_count;i++)
937 dbgtext(" %s",junction.referral_list[i].alternate_path);
942 /* create the referral depeding on version */
943 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
945 if (max_referral_level < 2) {
946 max_referral_level = 2;
948 if (max_referral_level > 3) {
949 max_referral_level = 3;
952 switch(max_referral_level) {
954 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
955 consumedcnt, self_referral);
958 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
959 consumedcnt, self_referral);
962 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
968 DEBUGADD(0,("DFS Referral pdata:\n"));
969 dump_data(0,*ppdata,reply_size);
976 /**********************************************************************
977 The following functions are called by the NETDFS RPC pipe functions
978 **********************************************************************/
980 /*********************************************************************
981 Creates a junction structure from a DFS pathname
982 **********************************************************************/
984 BOOL create_junction(const char *dfs_path, struct junction_map *jucn)
990 NTSTATUS status = parse_dfs_path(dfs_path, False, &dp, &dummy);
992 if (!NT_STATUS_IS_OK(status)) {
996 /* check if path is dfs : validate first token */
997 if (!is_myname_or_ipaddr(dp.hostname)) {
998 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
999 dp.hostname, dfs_path));
1003 /* Check for a non-DFS share */
1004 snum = lp_servicenumber(dp.servicename);
1006 if(snum < 0 || !lp_msdfs_root(snum)) {
1007 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1012 fstrcpy(jucn->service_name,dp.servicename);
1013 pstrcpy(jucn->volume_name,dp.reqpath);
1014 pstrcpy(jucn->comment, lp_comment(snum));
1018 /**********************************************************************
1019 Forms a valid Unix pathname from the junction
1020 **********************************************************************/
1022 static BOOL junction_to_local_path(struct junction_map *jucn,
1025 connection_struct *conn_out)
1030 snum = lp_servicenumber(jucn->service_name);
1035 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
1036 safe_strcat(path, "/", max_pathlen-1);
1037 safe_strcat(path, jucn->volume_name, max_pathlen-1);
1039 pstrcpy(conn_path, lp_pathname(snum));
1040 if (!create_conn_struct(conn_out, snum, conn_path)) {
1047 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
1051 connection_struct conns;
1052 connection_struct *conn = &conns;
1054 BOOL insert_comma = False;
1059 if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
1063 /* Form the msdfs_link contents */
1064 pstrcpy(msdfs_link, "msdfs:");
1065 for(i=0; i<jucn->referral_count; i++) {
1066 char* refpath = jucn->referral_list[i].alternate_path;
1068 /* Alternate paths always use Windows separators. */
1069 trim_char(refpath, '\\', '\\');
1070 if(*refpath == '\0') {
1072 insert_comma = False;
1076 if (i > 0 && insert_comma) {
1077 pstrcat(msdfs_link, ",");
1080 pstrcat(msdfs_link, refpath);
1081 if (!insert_comma) {
1082 insert_comma = True;
1086 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1090 if(SMB_VFS_UNLINK(conn,path)!=0) {
1095 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1096 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
1097 path, msdfs_link, strerror(errno)));
1106 conn_free_internal(conn);
1110 BOOL remove_msdfs_link(struct junction_map *jucn)
1113 connection_struct conns;
1114 connection_struct *conn = &conns;
1119 if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1120 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1123 talloc_destroy( conn->mem_ctx );
1126 conn_free_internal(conn);
1130 static int form_junctions(TALLOC_CTX *ctx,
1132 struct junction_map *jucn,
1136 SMB_STRUCT_DIR *dirp;
1138 pstring connect_path;
1139 char *service_name = lp_servicename(snum);
1140 connection_struct conn;
1141 struct referral *ref = NULL;
1145 if (jn_remain <= 0) {
1149 pstrcpy(connect_path,lp_pathname(snum));
1151 if(*connect_path == '\0') {
1156 * Fake up a connection struct for the VFS layer.
1159 if (!create_conn_struct(&conn, snum, connect_path)) {
1163 /* form a junction for the msdfs root - convention
1164 DO NOT REMOVE THIS: NT clients will not work with us
1165 if this is not present
1167 fstrcpy(jucn[cnt].service_name, service_name);
1168 jucn[cnt].volume_name[0] = '\0';
1169 jucn[cnt].referral_count = 1;
1171 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1172 if (jucn[cnt].referral_list == NULL) {
1173 DEBUG(0, ("talloc failed!\n"));
1178 ref->ttl = REFERRAL_TTL;
1179 if (*lp_msdfs_proxy(snum) != '\0') {
1180 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1185 pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
1186 get_local_machine_name(),
1190 /* Now enumerate all dfs links */
1191 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1196 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1197 pstring link_target;
1198 if (cnt >= jn_remain) {
1199 SMB_VFS_CLOSEDIR(&conn,dirp);
1200 DEBUG(2, ("ran out of MSDFS junction slots"));
1203 if (is_msdfs_link(&conn, dname, link_target, NULL)) {
1204 if (parse_msdfs_symlink(ctx,
1206 &jucn[cnt].referral_list,
1207 &jucn[cnt].referral_count)) {
1209 fstrcpy(jucn[cnt].service_name, service_name);
1210 pstrcpy(jucn[cnt].volume_name, dname);
1216 SMB_VFS_CLOSEDIR(&conn,dirp);
1220 conn_free_internal(&conn);
1224 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1230 if(!lp_host_msdfs()) {
1234 /* Ensure all the usershares are loaded. */
1236 load_registry_shares();
1237 sharecount = load_usershare_shares();
1240 for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1241 if(lp_msdfs_root(i)) {
1242 jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1248 /******************************************************************************
1249 Core function to resolve a dfs pathname.
1250 ******************************************************************************/
1252 NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name)
1254 NTSTATUS status = NT_STATUS_OK;
1256 if (dfs_pathnames && lp_host_msdfs() && lp_msdfs_root(SNUM(conn))) {
1257 status = dfs_redirect(conn, name, False, &dummy);
1262 /******************************************************************************
1263 Core function to resolve a dfs pathname possibly containing a wildcard.
1264 This function is identical to the above except for the BOOL param to
1265 dfs_redirect but I need this to be separate so it's really clear when
1266 we're allowing wildcards and when we're not. JRA.
1267 ******************************************************************************/
1269 NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard)
1271 NTSTATUS status = NT_STATUS_OK;
1272 if (dfs_pathnames && lp_host_msdfs() && lp_msdfs_root(SNUM(conn))) {
1273 status = dfs_redirect(conn, name, True, ppath_contains_wcard);