2 Unix SMB/Netbios implementation.
4 MSDFS services for Samba
5 Copyright (C) Shirish Kalele 2000
6 Copyright (C) Jeremy Allison 2007
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #define DBGC_CLASS DBGC_MSDFS
26 extern uint32 global_client_caps;
28 /**********************************************************************
29 Parse a DFS pathname of the form \hostname\service\reqpath
30 into the dfs_path structure.
31 If POSIX pathnames is true, the pathname may also be of the
32 form /hostname/service/reqpath.
33 We cope with either here.
35 Unfortunately, due to broken clients who might set the
36 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
37 send a local path, we have to cope with that too....
39 If conn != NULL then ensure the provided service is
40 the one pointed to by the connection.
42 This version does everything using pointers within one copy of the
43 pathname string, talloced on the struct dfs_path pointer (which
44 must be talloced). This may be too clever to live....
46 **********************************************************************/
48 static NTSTATUS parse_dfs_path(connection_struct *conn,
51 struct dfs_path *pdp, /* MUST BE TALLOCED */
52 bool *ppath_contains_wcard)
58 NTSTATUS status = NT_STATUS_OK;
64 * This is the only talloc we should need to do
65 * on the struct dfs_path. All the pointers inside
66 * it should point to offsets within this string.
69 pathname_local = talloc_strdup(pdp, pathname);
70 if (!pathname_local) {
71 return NT_STATUS_NO_MEMORY;
73 /* Get a pointer to the terminating '\0' */
74 eos_ptr = &pathname_local[strlen(pathname_local)];
75 p = temp = pathname_local;
77 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
79 sepchar = pdp->posix_path ? '/' : '\\';
81 if (*pathname != sepchar) {
82 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
85 * Possibly client sent a local path by mistake.
86 * Try and convert to a local path.
89 pdp->hostname = eos_ptr; /* "" */
90 pdp->servicename = eos_ptr; /* "" */
92 /* We've got no info about separators. */
93 pdp->posix_path = lp_posix_pathnames();
95 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
102 * Safe to use on talloc'ed string as it only shrinks.
103 * It also doesn't affect the eos_ptr.
105 trim_char(temp,sepchar,sepchar);
107 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
111 /* Parse out hostname. */
112 p = strchr_m(temp,sepchar);
114 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
117 * Possibly client sent a local path by mistake.
118 * Try and convert to a local path.
121 pdp->hostname = eos_ptr; /* "" */
122 pdp->servicename = eos_ptr; /* "" */
125 DEBUG(10,("parse_dfs_path: trying to convert %s "
131 pdp->hostname = temp;
133 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
135 /* Parse out servicename. */
137 p = strchr_m(servicename,sepchar);
142 /* Is this really our servicename ? */
143 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
144 || (strequal(servicename, HOMES_NAME)
145 && strequal(lp_servicename(SNUM(conn)),
146 get_current_username()) )) ) {
147 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
151 * Possibly client sent a local path by mistake.
152 * Try and convert to a local path.
155 pdp->hostname = eos_ptr; /* "" */
156 pdp->servicename = eos_ptr; /* "" */
158 /* Repair the path - replace the sepchar's
161 *servicename = sepchar;
167 DEBUG(10,("parse_dfs_path: trying to convert %s "
173 pdp->servicename = servicename;
175 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
178 /* Client sent self referral \server\share. */
179 pdp->reqpath = eos_ptr; /* "" */
187 *ppath_contains_wcard = False;
191 /* Rest is reqpath. */
192 if (pdp->posix_path) {
193 status = check_path_syntax_posix(pdp->reqpath);
196 status = check_path_syntax_wcard(pdp->reqpath,
197 ppath_contains_wcard);
199 status = check_path_syntax(pdp->reqpath);
203 if (!NT_STATUS_IS_OK(status)) {
204 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
205 p, nt_errstr(status) ));
209 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
213 /********************************************************
214 Fake up a connection struct for the VFS layer.
215 Note this CHANGES CWD !!!! JRA.
216 *********************************************************/
218 static NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
219 connection_struct **pconn,
223 connection_struct *conn;
226 conn = TALLOC_ZERO_P(ctx, connection_struct);
228 return NT_STATUS_NO_MEMORY;
231 connpath = talloc_strdup(conn, path);
234 return NT_STATUS_NO_MEMORY;
236 connpath = talloc_string_sub(conn,
239 lp_servicename(snum));
242 return NT_STATUS_NO_MEMORY;
245 /* needed for smbd_vfs_init() */
247 if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
248 DEBUG(0, ("TALLOC failed\n"));
250 return NT_STATUS_NO_MEMORY;
253 conn->params->service = snum;
255 set_conn_connectpath(conn, connpath);
257 if (!smbd_vfs_init(conn)) {
258 NTSTATUS status = map_nt_error_from_unix(errno);
259 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
260 conn_free_internal(conn);
265 * Windows seems to insist on doing trans2getdfsreferral() calls on
266 * the IPC$ share as the anonymous user. If we try to chdir as that
267 * user we will fail.... WTF ? JRA.
270 if (vfs_ChDir(conn,conn->connectpath) != 0) {
271 NTSTATUS status = map_nt_error_from_unix(errno);
272 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
274 conn->connectpath, strerror(errno) ));
275 conn_free_internal(conn);
284 /**********************************************************************
285 Parse the contents of a symlink to verify if it is an msdfs referral
286 A valid referral is of the form:
288 msdfs:server1\share1,server2\share2
289 msdfs:server1\share1\pathname,server2\share2\pathname
290 msdfs:server1/share1,server2/share2
291 msdfs:server1/share1/pathname,server2/share2/pathname.
293 Note that the alternate paths returned here must be of the canonicalized
297 \server\share\path\to\file,
299 even in posix path mode. This is because we have no knowledge if the
300 server we're referring to understands posix paths.
301 **********************************************************************/
303 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
305 struct referral **preflist,
310 char **alt_path = NULL;
312 struct referral *reflist;
315 temp = talloc_strdup(ctx, target);
319 prot = strtok_r(temp, ":", &saveptr);
321 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
325 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
330 /* parse out the alternate paths */
331 while((count<MAX_REFERRAL_COUNT) &&
332 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
336 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
339 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
340 struct referral, count);
341 if(reflist == NULL) {
342 TALLOC_FREE(alt_path);
346 reflist = *preflist = NULL;
349 for(i=0;i<count;i++) {
352 /* Canonicalize link target.
353 * Replace all /'s in the path by a \ */
354 string_replace(alt_path[i], '/', '\\');
356 /* Remove leading '\\'s */
358 while (*p && (*p == '\\')) {
362 reflist[i].alternate_path = talloc_asprintf(ctx,
365 if (!reflist[i].alternate_path) {
369 reflist[i].proximity = 0;
370 reflist[i].ttl = REFERRAL_TTL;
371 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
372 reflist[i].alternate_path));
377 TALLOC_FREE(alt_path);
381 /**********************************************************************
382 Returns true if the unix path is a valid msdfs symlink and also
383 returns the target string from inside the link.
384 **********************************************************************/
386 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
387 connection_struct *conn,
389 char **pp_link_target,
390 SMB_STRUCT_STAT *sbufp)
393 int referral_len = 0;
394 char link_target_buf[7];
396 char *link_target = NULL;
398 if (pp_link_target) {
400 link_target = TALLOC_ARRAY(ctx, char, bufsize);
404 *pp_link_target = link_target;
406 bufsize = sizeof(link_target_buf);
407 link_target = link_target_buf;
414 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
415 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
420 if (!S_ISLNK(sbufp->st_mode)) {
421 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
426 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
427 if (referral_len == -1) {
428 DEBUG(0,("is_msdfs_link_read_target: Error reading "
429 "msdfs link %s: %s\n",
430 path, strerror(errno)));
433 link_target[referral_len] = '\0';
435 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
438 if (!strnequal(link_target, "msdfs:", 6)) {
445 if (link_target != link_target_buf) {
446 TALLOC_FREE(link_target);
451 /**********************************************************************
452 Returns true if the unix path is a valid msdfs symlink.
453 **********************************************************************/
455 bool is_msdfs_link(connection_struct *conn,
457 SMB_STRUCT_STAT *sbufp)
459 return is_msdfs_link_internal(talloc_tos(),
466 /*****************************************************************
467 Used by other functions to decide if a dfs path is remote,
468 and to get the list of referred locations for that remote path.
470 search_flag: For findfirsts, dfs links themselves are not
471 redirected, but paths beyond the links are. For normal smb calls,
472 even dfs links need to be redirected.
474 consumedcntp: how much of the dfs path is being redirected. the client
475 should try the remaining path on the redirected server.
477 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
478 link redirect are in targetpath.
479 *****************************************************************/
481 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
482 connection_struct *conn,
483 const char *dfspath, /* Incoming complete dfs path */
484 const struct dfs_path *pdp, /* Parsed out
485 server+share+extrapath. */
486 bool search_flag, /* Called from a findfirst ? */
488 char **pp_targetpath)
492 SMB_STRUCT_STAT sbuf;
494 char *localpath = NULL;
495 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
498 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
499 conn->connectpath, pdp->reqpath));
502 * Note the unix path conversion here we're doing we can
503 * throw away. We're looking for a symlink for a dfs
504 * resolution, if we don't find it we'll do another
505 * unix_convert later in the codepath.
506 * If we needed to remember what we'd resolved in
507 * dp->reqpath (as the original code did) we'd
508 * copy (localhost, dp->reqpath) on any code
509 * path below that returns True - but I don't
510 * think this is needed. JRA.
513 status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
515 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
516 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
520 /* Optimization - check if we can redirect the whole path. */
522 if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
524 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
525 "for dfs link %s.\n", dfspath));
529 DEBUG(6,("dfs_path_lookup: %s resolves to a "
530 "valid dfs link %s.\n", dfspath,
531 pp_targetpath ? *pp_targetpath : ""));
534 *consumedcntp = strlen(dfspath);
536 return NT_STATUS_PATH_NOT_COVERED;
539 /* Prepare to test only for '/' components in the given path,
540 * so if a Windows path replace all '\\' characters with '/'.
541 * For a POSIX DFS path we know all separators are already '/'. */
543 canon_dfspath = talloc_strdup(ctx, dfspath);
544 if (!canon_dfspath) {
545 return NT_STATUS_NO_MEMORY;
547 if (!pdp->posix_path) {
548 string_replace(canon_dfspath, '\\', '/');
552 * localpath comes out of unix_convert, so it has
553 * no trailing backslash. Make sure that canon_dfspath hasn't either.
554 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
557 trim_char(canon_dfspath,0,'/');
560 * Redirect if any component in the path is a link.
561 * We do this by walking backwards through the
562 * local path, chopping off the last component
563 * in both the local path and the canonicalized
564 * DFS path. If we hit a DFS link then we're done.
567 p = strrchr_m(localpath, '/');
569 q = strrchr_m(canon_dfspath, '/');
578 if (is_msdfs_link_internal(ctx, conn,
579 localpath, pp_targetpath, NULL)) {
580 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
581 "parent %s is dfs link\n", dfspath, localpath));
584 *consumedcntp = strlen(canon_dfspath);
585 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
591 return NT_STATUS_PATH_NOT_COVERED;
594 /* Step back on the filesystem. */
595 p = strrchr_m(localpath, '/');
598 /* And in the canonicalized dfs path. */
599 q = strrchr_m(canon_dfspath, '/');
606 /*****************************************************************
607 Decides if a dfs pathname should be redirected or not.
608 If not, the pathname is converted to a tcon-relative local unix path
610 search_wcard_flag: this flag performs 2 functions both related
611 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
614 This function can return NT_STATUS_OK, meaning use the returned path as-is
615 (mapped into a local path).
616 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
617 any other NT_STATUS error which is a genuine error to be
618 returned to the client.
619 *****************************************************************/
621 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
622 connection_struct *conn,
624 bool search_wcard_flag,
626 bool *ppath_contains_wcard)
629 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
632 return NT_STATUS_NO_MEMORY;
635 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
636 ppath_contains_wcard);
637 if (!NT_STATUS_IS_OK(status)) {
642 if (pdp->reqpath[0] == '\0') {
644 *pp_path_out = talloc_strdup(ctx, "");
646 return NT_STATUS_NO_MEMORY;
648 DEBUG(5,("dfs_redirect: self-referral.\n"));
652 /* If dfs pathname for a non-dfs share, convert to tcon-relative
653 path and return OK */
655 if (!lp_msdfs_root(SNUM(conn))) {
656 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
659 return NT_STATUS_NO_MEMORY;
664 /* If it looked like a local path (zero hostname/servicename)
665 * just treat as a tcon-relative path. */
667 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
668 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
671 return NT_STATUS_NO_MEMORY;
676 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
677 || (strequal(pdp->servicename, HOMES_NAME)
678 && strequal(lp_servicename(SNUM(conn)),
679 conn->server_info->sanitized_username) )) ) {
681 /* The given sharename doesn't match this connection. */
684 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
687 status = dfs_path_lookup(ctx, conn, path_in, pdp,
688 search_wcard_flag, NULL, NULL);
689 if (!NT_STATUS_IS_OK(status)) {
690 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
691 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
693 DEBUG(10,("dfs_redirect: dfs_path_lookup "
694 "failed for %s with %s\n",
695 path_in, nt_errstr(status) ));
700 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
702 /* Form non-dfs tcon-relative path */
703 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
706 return NT_STATUS_NO_MEMORY;
709 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
716 /**********************************************************************
717 Return a self referral.
718 **********************************************************************/
720 static NTSTATUS self_ref(TALLOC_CTX *ctx,
721 const char *dfs_path,
722 struct junction_map *jucn,
724 bool *self_referralp)
726 struct referral *ref;
728 *self_referralp = True;
730 jucn->referral_count = 1;
731 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
732 return NT_STATUS_NO_MEMORY;
735 ref->alternate_path = talloc_strdup(ctx, dfs_path);
736 if (!ref->alternate_path) {
737 return NT_STATUS_NO_MEMORY;
740 ref->ttl = REFERRAL_TTL;
741 jucn->referral_list = ref;
742 *consumedcntp = strlen(dfs_path);
746 /**********************************************************************
747 Gets valid referrals for a dfs path and fills up the
748 junction_map structure.
749 **********************************************************************/
751 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
752 const char *dfs_path,
753 struct junction_map *jucn,
755 bool *self_referralp)
757 struct connection_struct *conn;
758 char *targetpath = NULL;
760 NTSTATUS status = NT_STATUS_NOT_FOUND;
762 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
765 return NT_STATUS_NO_MEMORY;
768 *self_referralp = False;
770 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
771 if (!NT_STATUS_IS_OK(status)) {
775 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
776 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
777 if (!jucn->service_name || !jucn->volume_name) {
779 return NT_STATUS_NO_MEMORY;
782 /* Verify the share is a dfs root */
783 snum = lp_servicenumber(jucn->service_name);
785 fstring service_name;
786 fstrcpy(service_name, jucn->service_name);
787 if ((snum = find_service(service_name)) < 0) {
788 return NT_STATUS_NOT_FOUND;
790 TALLOC_FREE(jucn->service_name);
791 jucn->service_name = talloc_strdup(ctx, service_name);
792 if (!jucn->service_name) {
794 return NT_STATUS_NO_MEMORY;
798 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
799 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
801 pdp->servicename, dfs_path));
803 return NT_STATUS_NOT_FOUND;
807 * Self referrals are tested with a anonymous IPC connection and
808 * a GET_DFS_REFERRAL call to \\server\share. (which means
809 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
810 * into the directory and will fail if it cannot (as the anonymous
811 * user). Cope with this.
814 if (pdp->reqpath[0] == '\0') {
816 struct referral *ref;
818 if (*lp_msdfs_proxy(snum) == '\0') {
828 * It's an msdfs proxy share. Redirect to
829 * the configured target share.
832 jucn->referral_count = 1;
833 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
835 return NT_STATUS_NO_MEMORY;
838 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
840 return NT_STATUS_NO_MEMORY;
843 trim_string(tmp, "\\", 0);
845 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
848 if (!ref->alternate_path) {
850 return NT_STATUS_NO_MEMORY;
853 if (pdp->reqpath[0] != '\0') {
854 ref->alternate_path = talloc_asprintf_append(
858 if (!ref->alternate_path) {
860 return NT_STATUS_NO_MEMORY;
864 ref->ttl = REFERRAL_TTL;
865 jucn->referral_list = ref;
866 *consumedcntp = strlen(dfs_path);
871 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum));
872 if (!NT_STATUS_IS_OK(status)) {
877 /* If this is a DFS path dfs_lookup should return
878 * NT_STATUS_PATH_NOT_COVERED. */
880 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
881 False, consumedcntp, &targetpath);
883 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
884 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
886 conn_free_internal(conn);
891 /* We know this is a valid dfs link. Parse the targetpath. */
892 if (!parse_msdfs_symlink(ctx, targetpath,
893 &jucn->referral_list,
894 &jucn->referral_count)) {
895 DEBUG(3,("get_referred_path: failed to parse symlink "
896 "target %s\n", targetpath ));
897 conn_free_internal(conn);
899 return NT_STATUS_NOT_FOUND;
902 conn_free_internal(conn);
907 static int setup_ver2_dfs_referral(const char *pathname,
909 struct junction_map *junction,
913 char* pdata = *ppdata;
915 smb_ucs2_t *uni_requestedpath = NULL;
916 int uni_reqpathoffset1,uni_reqpathoffset2;
918 int requestedpathlen=0;
923 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
925 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
926 &uni_requestedpath, pathname);
927 if (uni_requestedpath == NULL || requestedpathlen == 0) {
932 dump_data(0, (unsigned char *)uni_requestedpath,
936 DEBUG(10,("ref count = %u\n",junction->referral_count));
938 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
939 VERSION2_REFERRAL_SIZE * junction->referral_count;
941 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
943 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
945 reply_size = REFERRAL_HEADER_SIZE +
946 VERSION2_REFERRAL_SIZE*junction->referral_count +
947 2 * requestedpathlen;
948 DEBUG(10,("reply_size: %u\n",reply_size));
950 /* add up the unicode lengths of all the referral paths */
951 for(i=0;i<junction->referral_count;i++) {
952 DEBUG(10,("referral %u : %s\n",
954 junction->referral_list[i].alternate_path));
956 (strlen(junction->referral_list[i].alternate_path)+1)*2;
959 DEBUG(10,("reply_size = %u\n",reply_size));
960 /* add the unexplained 0x16 bytes */
963 pdata = (char *)SMB_REALLOC(pdata,reply_size);
965 DEBUG(0,("Realloc failed!\n"));
970 /* copy in the dfs requested paths.. required for offset calculations */
971 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
972 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
974 /* create the header */
975 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
976 /* number of referral in this pkt */
977 SSVAL(pdata,2,junction->referral_count);
979 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
981 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
985 /* add the referral elements */
986 for(i=0;i<junction->referral_count;i++) {
987 struct referral* ref = &junction->referral_list[i];
990 SSVAL(pdata,offset,2); /* version 2 */
991 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
993 SSVAL(pdata,offset+4,1);
995 SSVAL(pdata,offset+4,0);
998 /* ref_flags :use path_consumed bytes? */
999 SSVAL(pdata,offset+6,0);
1000 SIVAL(pdata,offset+8,ref->proximity);
1001 SIVAL(pdata,offset+12,ref->ttl);
1003 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1004 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1005 /* copy referred path into current offset */
1006 unilen = rpcstr_push(pdata+uni_curroffset,
1007 ref->alternate_path,
1008 reply_size - uni_curroffset,
1011 SSVAL(pdata,offset+20,uni_curroffset-offset);
1013 uni_curroffset += unilen;
1014 offset += VERSION2_REFERRAL_SIZE;
1016 /* add in the unexplained 22 (0x16) bytes at the end */
1017 memset(pdata+uni_curroffset,'\0',0x16);
1021 static int setup_ver3_dfs_referral(const char *pathname,
1023 struct junction_map *junction,
1027 char *pdata = *ppdata;
1029 smb_ucs2_t *uni_reqpath = NULL;
1030 int uni_reqpathoffset1, uni_reqpathoffset2;
1037 DEBUG(10,("setting up version3 referral\n"));
1039 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1040 if (uni_reqpath == NULL || reqpathlen == 0) {
1045 dump_data(0, (unsigned char *)uni_reqpath,
1049 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1050 VERSION3_REFERRAL_SIZE * junction->referral_count;
1051 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1052 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1054 for(i=0;i<junction->referral_count;i++) {
1055 DEBUG(10,("referral %u : %s\n",
1057 junction->referral_list[i].alternate_path));
1059 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1062 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1064 DEBUG(0,("version3 referral setup:"
1065 "malloc failed for Realloc!\n"));
1070 /* create the header */
1071 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
1072 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1074 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1076 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1079 /* copy in the reqpaths */
1080 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1081 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1084 for(i=0;i<junction->referral_count;i++) {
1085 struct referral* ref = &(junction->referral_list[i]);
1088 SSVAL(pdata,offset,3); /* version 3 */
1089 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1091 SSVAL(pdata,offset+4,1);
1093 SSVAL(pdata,offset+4,0);
1096 /* ref_flags :use path_consumed bytes? */
1097 SSVAL(pdata,offset+6,0);
1098 SIVAL(pdata,offset+8,ref->ttl);
1100 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1101 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1102 /* copy referred path into current offset */
1103 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1104 reply_size - uni_curroffset,
1105 STR_UNICODE | STR_TERMINATE);
1106 SSVAL(pdata,offset+16,uni_curroffset-offset);
1107 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1108 memset(pdata+offset+18,'\0',16);
1110 uni_curroffset += unilen;
1111 offset += VERSION3_REFERRAL_SIZE;
1116 /******************************************************************
1117 Set up the DFS referral for the dfs pathname. This call returns
1118 the amount of the path covered by this server, and where the
1119 client should be redirected to. This is the meat of the
1120 TRANS2_GET_DFS_REFERRAL call.
1121 ******************************************************************/
1123 int setup_dfs_referral(connection_struct *orig_conn,
1124 const char *dfs_path,
1125 int max_referral_level,
1126 char **ppdata, NTSTATUS *pstatus)
1128 struct junction_map *junction = NULL;
1129 int consumedcnt = 0;
1130 bool self_referral = False;
1132 char *pathnamep = NULL;
1133 char *local_dfs_path = NULL;
1136 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1137 *pstatus = NT_STATUS_NO_MEMORY;
1141 /* get the junction entry */
1143 talloc_destroy(ctx);
1144 *pstatus = NT_STATUS_NOT_FOUND;
1149 * Trim pathname sent by client so it begins with only one backslash.
1150 * Two backslashes confuse some dfs clients
1153 local_dfs_path = talloc_strdup(ctx,dfs_path);
1154 if (!local_dfs_path) {
1155 *pstatus = NT_STATUS_NO_MEMORY;
1156 talloc_destroy(ctx);
1159 pathnamep = local_dfs_path;
1160 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1161 IS_DIRECTORY_SEP(pathnamep[1])) {
1165 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1167 *pstatus = NT_STATUS_NO_MEMORY;
1168 talloc_destroy(ctx);
1172 /* The following call can change cwd. */
1173 *pstatus = get_referred_path(ctx, pathnamep, junction,
1174 &consumedcnt, &self_referral);
1175 if (!NT_STATUS_IS_OK(*pstatus)) {
1176 vfs_ChDir(orig_conn,orig_conn->connectpath);
1177 talloc_destroy(ctx);
1180 vfs_ChDir(orig_conn,orig_conn->connectpath);
1182 if (!self_referral) {
1183 pathnamep[consumedcnt] = '\0';
1185 if( DEBUGLVL( 3 ) ) {
1187 dbgtext("setup_dfs_referral: Path %s to "
1188 "alternate path(s):",
1190 for(i=0;i<junction->referral_count;i++)
1192 junction->referral_list[i].alternate_path);
1197 /* create the referral depeding on version */
1198 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1200 if (max_referral_level < 2) {
1201 max_referral_level = 2;
1203 if (max_referral_level > 3) {
1204 max_referral_level = 3;
1207 switch(max_referral_level) {
1209 reply_size = setup_ver2_dfs_referral(pathnamep,
1211 consumedcnt, self_referral);
1214 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1215 junction, consumedcnt, self_referral);
1218 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1220 max_referral_level));
1221 talloc_destroy(ctx);
1222 *pstatus = NT_STATUS_INVALID_LEVEL;
1227 DEBUGADD(0,("DFS Referral pdata:\n"));
1228 dump_data(0,(uint8 *)*ppdata,reply_size);
1231 talloc_destroy(ctx);
1232 *pstatus = NT_STATUS_OK;
1236 /**********************************************************************
1237 The following functions are called by the NETDFS RPC pipe functions
1238 **********************************************************************/
1240 /*********************************************************************
1241 Creates a junction structure from a DFS pathname
1242 **********************************************************************/
1244 bool create_junction(TALLOC_CTX *ctx,
1245 const char *dfs_path,
1246 struct junction_map *jucn)
1250 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1256 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1257 if (!NT_STATUS_IS_OK(status)) {
1261 /* check if path is dfs : validate first token */
1262 if (!is_myname_or_ipaddr(pdp->hostname)) {
1263 DEBUG(4,("create_junction: Invalid hostname %s "
1265 pdp->hostname, dfs_path));
1270 /* Check for a non-DFS share */
1271 snum = lp_servicenumber(pdp->servicename);
1273 if(snum < 0 || !lp_msdfs_root(snum)) {
1274 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1280 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1281 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1282 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1285 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1291 /**********************************************************************
1292 Forms a valid Unix pathname from the junction
1293 **********************************************************************/
1295 static bool junction_to_local_path(const struct junction_map *jucn,
1297 connection_struct **conn_out)
1301 snum = lp_servicenumber(jucn->service_name);
1305 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1307 lp_pathname(snum)))) {
1311 *pp_path_out = talloc_asprintf(conn_out,
1315 if (!*pp_path_out) {
1321 bool create_msdfs_link(const struct junction_map *jucn,
1325 char *msdfs_link = NULL;
1326 connection_struct *conn;
1328 bool insert_comma = False;
1331 if(!junction_to_local_path(jucn, &path, &conn)) {
1335 /* Form the msdfs_link contents */
1336 msdfs_link = talloc_strdup(conn, "msdfs:");
1340 for(i=0; i<jucn->referral_count; i++) {
1341 char *refpath = jucn->referral_list[i].alternate_path;
1343 /* Alternate paths always use Windows separators. */
1344 trim_char(refpath, '\\', '\\');
1345 if(*refpath == '\0') {
1347 insert_comma = False;
1351 if (i > 0 && insert_comma) {
1352 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1356 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1364 if (!insert_comma) {
1365 insert_comma = True;
1369 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1373 if(SMB_VFS_UNLINK(conn,path)!=0) {
1378 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1379 DEBUG(1,("create_msdfs_link: symlink failed "
1380 "%s -> %s\nError: %s\n",
1381 path, msdfs_link, strerror(errno)));
1389 conn_free_internal(conn);
1393 bool remove_msdfs_link(const struct junction_map *jucn)
1396 connection_struct *conn;
1399 if( junction_to_local_path(jucn, &path, &conn) ) {
1400 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1405 conn_free_internal(conn);
1409 /*********************************************************************
1410 Return the number of DFS links at the root of this share.
1411 *********************************************************************/
1413 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1416 SMB_STRUCT_DIR *dirp = NULL;
1418 const char *connect_path = lp_pathname(snum);
1419 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1420 connection_struct *conn;
1422 if(*connect_path == '\0') {
1427 * Fake up a connection struct for the VFS layer.
1430 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1431 &conn, snum, connect_path))) {
1435 /* Count a link for the msdfs root - convention */
1438 /* No more links if this is an msdfs proxy. */
1439 if (*msdfs_proxy != '\0') {
1443 /* Now enumerate all dfs links */
1444 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1449 while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1450 if (is_msdfs_link(conn,
1457 SMB_VFS_CLOSEDIR(conn,dirp);
1461 conn_free_internal(conn);
1465 /*********************************************************************
1466 *********************************************************************/
1468 static int form_junctions(TALLOC_CTX *ctx,
1470 struct junction_map *jucn,
1474 SMB_STRUCT_DIR *dirp = NULL;
1476 const char *connect_path = lp_pathname(snum);
1477 char *service_name = lp_servicename(snum);
1478 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1479 connection_struct *conn;
1480 struct referral *ref = NULL;
1482 if (jn_remain == 0) {
1486 if(*connect_path == '\0') {
1491 * Fake up a connection struct for the VFS layer.
1494 if (!NT_STATUS_IS_OK(create_conn_struct(ctx, &conn, snum, connect_path))) {
1498 /* form a junction for the msdfs root - convention
1499 DO NOT REMOVE THIS: NT clients will not work with us
1500 if this is not present
1502 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1503 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1504 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1507 jucn[cnt].comment = "";
1508 jucn[cnt].referral_count = 1;
1510 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1511 if (jucn[cnt].referral_list == NULL) {
1516 ref->ttl = REFERRAL_TTL;
1517 if (*msdfs_proxy != '\0') {
1518 ref->alternate_path = talloc_strdup(ctx,
1521 ref->alternate_path = talloc_asprintf(ctx,
1523 get_local_machine_name(),
1527 if (!ref->alternate_path) {
1532 /* Don't enumerate if we're an msdfs proxy. */
1533 if (*msdfs_proxy != '\0') {
1537 /* Now enumerate all dfs links */
1538 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1543 while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1544 char *link_target = NULL;
1545 if (cnt >= jn_remain) {
1546 DEBUG(2, ("form_junctions: ran out of MSDFS "
1550 if (is_msdfs_link_internal(ctx,
1552 dname, &link_target,
1554 if (parse_msdfs_symlink(ctx,
1556 &jucn[cnt].referral_list,
1557 &jucn[cnt].referral_count)) {
1559 jucn[cnt].service_name = talloc_strdup(ctx,
1561 jucn[cnt].volume_name = talloc_strdup(ctx,
1563 if (!jucn[cnt].service_name ||
1564 !jucn[cnt].volume_name) {
1567 jucn[cnt].comment = "";
1576 SMB_VFS_CLOSEDIR(conn,dirp);
1579 conn_free_internal(conn);
1583 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1585 struct junction_map *jn = NULL;
1587 size_t jn_count = 0;
1591 if(!lp_host_msdfs()) {
1595 /* Ensure all the usershares are loaded. */
1597 load_registry_shares();
1598 sharecount = load_usershare_shares();
1601 for(i=0;i < sharecount;i++) {
1602 if(lp_msdfs_root(i)) {
1603 jn_count += count_dfs_links(ctx, i);
1606 if (jn_count == 0) {
1609 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1613 for(i=0; i < sharecount; i++) {
1614 if (*p_num_jn >= jn_count) {
1617 if(lp_msdfs_root(i)) {
1618 *p_num_jn += form_junctions(ctx, i,
1620 jn_count - *p_num_jn);
1626 /******************************************************************************
1627 Core function to resolve a dfs pathname.
1628 ******************************************************************************/
1630 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1631 connection_struct *conn,
1633 const char *name_in,
1636 NTSTATUS status = NT_STATUS_OK;
1638 if (dfs_pathnames) {
1639 status = dfs_redirect(ctx,
1647 * Cheat and just return a copy of the in ptr.
1648 * Once srvstr_get_path() uses talloc it'll
1649 * be a talloced ptr anyway.
1651 *pp_name_out = CONST_DISCARD(char *,name_in);
1656 /******************************************************************************
1657 Core function to resolve a dfs pathname possibly containing a wildcard.
1658 This function is identical to the above except for the bool param to
1659 dfs_redirect but I need this to be separate so it's really clear when
1660 we're allowing wildcards and when we're not. JRA.
1661 ******************************************************************************/
1663 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1664 connection_struct *conn,
1666 const char *name_in,
1668 bool *ppath_contains_wcard)
1670 NTSTATUS status = NT_STATUS_OK;
1671 if (dfs_pathnames) {
1672 status = dfs_redirect(ctx,
1677 ppath_contains_wcard);
1680 * Cheat and just return a copy of the in ptr.
1681 * Once srvstr_get_path() uses talloc it'll
1682 * be a talloced ptr anyway.
1684 *pp_name_out = CONST_DISCARD(char *,name_in);