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));
376 TALLOC_FREE(alt_path);
380 /**********************************************************************
381 Returns true if the unix path is a valid msdfs symlink and also
382 returns the target string from inside the link.
383 **********************************************************************/
385 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
386 connection_struct *conn,
388 char **pp_link_target,
389 SMB_STRUCT_STAT *sbufp)
392 int referral_len = 0;
393 char link_target_buf[7];
395 char *link_target = NULL;
397 if (pp_link_target) {
399 link_target = TALLOC_ARRAY(ctx, char, bufsize);
403 *pp_link_target = link_target;
405 bufsize = sizeof(link_target_buf);
406 link_target = link_target_buf;
413 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
414 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
419 if (!S_ISLNK(sbufp->st_mode)) {
420 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
425 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
426 if (referral_len == -1) {
427 DEBUG(0,("is_msdfs_link_read_target: Error reading "
428 "msdfs link %s: %s\n",
429 path, strerror(errno)));
432 link_target[referral_len] = '\0';
434 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
437 if (!strnequal(link_target, "msdfs:", 6)) {
444 if (link_target != link_target_buf) {
445 TALLOC_FREE(link_target);
450 /**********************************************************************
451 Returns true if the unix path is a valid msdfs symlink.
452 **********************************************************************/
454 bool is_msdfs_link(connection_struct *conn,
456 SMB_STRUCT_STAT *sbufp)
458 return is_msdfs_link_internal(talloc_tos(),
465 /*****************************************************************
466 Used by other functions to decide if a dfs path is remote,
467 and to get the list of referred locations for that remote path.
469 search_flag: For findfirsts, dfs links themselves are not
470 redirected, but paths beyond the links are. For normal smb calls,
471 even dfs links need to be redirected.
473 consumedcntp: how much of the dfs path is being redirected. the client
474 should try the remaining path on the redirected server.
476 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
477 link redirect are in targetpath.
478 *****************************************************************/
480 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
481 connection_struct *conn,
482 const char *dfspath, /* Incoming complete dfs path */
483 const struct dfs_path *pdp, /* Parsed out
484 server+share+extrapath. */
485 bool search_flag, /* Called from a findfirst ? */
487 char **pp_targetpath)
491 SMB_STRUCT_STAT sbuf;
493 char *localpath = NULL;
494 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
497 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
498 conn->connectpath, pdp->reqpath));
501 * Note the unix path conversion here we're doing we can
502 * throw away. We're looking for a symlink for a dfs
503 * resolution, if we don't find it we'll do another
504 * unix_convert later in the codepath.
505 * If we needed to remember what we'd resolved in
506 * dp->reqpath (as the original code did) we'd
507 * copy (localhost, dp->reqpath) on any code
508 * path below that returns True - but I don't
509 * think this is needed. JRA.
512 status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
514 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
515 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
519 /* Optimization - check if we can redirect the whole path. */
521 if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
523 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
524 "for dfs link %s.\n", dfspath));
528 DEBUG(6,("dfs_path_lookup: %s resolves to a "
529 "valid dfs link %s.\n", dfspath,
530 pp_targetpath ? *pp_targetpath : ""));
533 *consumedcntp = strlen(dfspath);
535 return NT_STATUS_PATH_NOT_COVERED;
538 /* Prepare to test only for '/' components in the given path,
539 * so if a Windows path replace all '\\' characters with '/'.
540 * For a POSIX DFS path we know all separators are already '/'. */
542 canon_dfspath = talloc_strdup(ctx, dfspath);
543 if (!canon_dfspath) {
544 return NT_STATUS_NO_MEMORY;
546 if (!pdp->posix_path) {
547 string_replace(canon_dfspath, '\\', '/');
551 * localpath comes out of unix_convert, so it has
552 * no trailing backslash. Make sure that canon_dfspath hasn't either.
553 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
556 trim_char(canon_dfspath,0,'/');
559 * Redirect if any component in the path is a link.
560 * We do this by walking backwards through the
561 * local path, chopping off the last component
562 * in both the local path and the canonicalized
563 * DFS path. If we hit a DFS link then we're done.
566 p = strrchr_m(localpath, '/');
568 q = strrchr_m(canon_dfspath, '/');
577 if (is_msdfs_link_internal(ctx, conn,
578 localpath, pp_targetpath, NULL)) {
579 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
580 "parent %s is dfs link\n", dfspath, localpath));
583 *consumedcntp = strlen(canon_dfspath);
584 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
590 return NT_STATUS_PATH_NOT_COVERED;
593 /* Step back on the filesystem. */
594 p = strrchr_m(localpath, '/');
597 /* And in the canonicalized dfs path. */
598 q = strrchr_m(canon_dfspath, '/');
605 /*****************************************************************
606 Decides if a dfs pathname should be redirected or not.
607 If not, the pathname is converted to a tcon-relative local unix path
609 search_wcard_flag: this flag performs 2 functions both related
610 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
613 This function can return NT_STATUS_OK, meaning use the returned path as-is
614 (mapped into a local path).
615 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
616 any other NT_STATUS error which is a genuine error to be
617 returned to the client.
618 *****************************************************************/
620 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
621 connection_struct *conn,
623 bool search_wcard_flag,
625 bool *ppath_contains_wcard)
628 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
631 return NT_STATUS_NO_MEMORY;
634 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
635 ppath_contains_wcard);
636 if (!NT_STATUS_IS_OK(status)) {
641 if (pdp->reqpath[0] == '\0') {
643 *pp_path_out = talloc_strdup(ctx, "");
645 return NT_STATUS_NO_MEMORY;
647 DEBUG(5,("dfs_redirect: self-referral.\n"));
651 /* If dfs pathname for a non-dfs share, convert to tcon-relative
652 path and return OK */
654 if (!lp_msdfs_root(SNUM(conn))) {
655 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
658 return NT_STATUS_NO_MEMORY;
663 /* If it looked like a local path (zero hostname/servicename)
664 * just treat as a tcon-relative path. */
666 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
667 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
670 return NT_STATUS_NO_MEMORY;
675 status = dfs_path_lookup(ctx, conn, path_in, pdp,
676 search_wcard_flag, NULL, NULL);
677 if (!NT_STATUS_IS_OK(status)) {
678 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
679 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
681 DEBUG(10,("dfs_redirect: dfs_path_lookup "
682 "failed for %s with %s\n",
683 path_in, nt_errstr(status) ));
688 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
690 /* Form non-dfs tcon-relative path */
691 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
694 return NT_STATUS_NO_MEMORY;
697 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
704 /**********************************************************************
705 Return a self referral.
706 **********************************************************************/
708 static NTSTATUS self_ref(TALLOC_CTX *ctx,
709 const char *dfs_path,
710 struct junction_map *jucn,
712 bool *self_referralp)
714 struct referral *ref;
716 *self_referralp = True;
718 jucn->referral_count = 1;
719 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
720 return NT_STATUS_NO_MEMORY;
723 ref->alternate_path = talloc_strdup(ctx, dfs_path);
724 if (!ref->alternate_path) {
725 return NT_STATUS_NO_MEMORY;
728 ref->ttl = REFERRAL_TTL;
729 jucn->referral_list = ref;
730 *consumedcntp = strlen(dfs_path);
734 /**********************************************************************
735 Gets valid referrals for a dfs path and fills up the
736 junction_map structure.
737 **********************************************************************/
739 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
740 const char *dfs_path,
741 struct junction_map *jucn,
743 bool *self_referralp)
745 struct connection_struct *conn;
746 char *targetpath = NULL;
748 NTSTATUS status = NT_STATUS_NOT_FOUND;
750 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
753 return NT_STATUS_NO_MEMORY;
756 *self_referralp = False;
758 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
759 if (!NT_STATUS_IS_OK(status)) {
763 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
764 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
765 if (!jucn->service_name || !jucn->volume_name) {
767 return NT_STATUS_NO_MEMORY;
770 /* Verify the share is a dfs root */
771 snum = lp_servicenumber(jucn->service_name);
773 fstring service_name;
774 fstrcpy(service_name, jucn->service_name);
775 if ((snum = find_service(service_name)) < 0) {
776 return NT_STATUS_NOT_FOUND;
778 TALLOC_FREE(jucn->service_name);
779 jucn->service_name = talloc_strdup(ctx, service_name);
780 if (!jucn->service_name) {
782 return NT_STATUS_NO_MEMORY;
786 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
787 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
789 pdp->servicename, dfs_path));
791 return NT_STATUS_NOT_FOUND;
795 * Self referrals are tested with a anonymous IPC connection and
796 * a GET_DFS_REFERRAL call to \\server\share. (which means
797 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
798 * into the directory and will fail if it cannot (as the anonymous
799 * user). Cope with this.
802 if (pdp->reqpath[0] == '\0') {
804 struct referral *ref;
806 if (*lp_msdfs_proxy(snum) == '\0') {
816 * It's an msdfs proxy share. Redirect to
817 * the configured target share.
820 jucn->referral_count = 1;
821 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
823 return NT_STATUS_NO_MEMORY;
826 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
828 return NT_STATUS_NO_MEMORY;
831 trim_string(tmp, "\\", 0);
833 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
836 if (!ref->alternate_path) {
838 return NT_STATUS_NO_MEMORY;
841 if (pdp->reqpath[0] != '\0') {
842 ref->alternate_path = talloc_asprintf_append(
846 if (!ref->alternate_path) {
848 return NT_STATUS_NO_MEMORY;
852 ref->ttl = REFERRAL_TTL;
853 jucn->referral_list = ref;
854 *consumedcntp = strlen(dfs_path);
859 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum));
860 if (!NT_STATUS_IS_OK(status)) {
865 /* If this is a DFS path dfs_lookup should return
866 * NT_STATUS_PATH_NOT_COVERED. */
868 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
869 False, consumedcntp, &targetpath);
871 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
872 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
874 conn_free_internal(conn);
879 /* We know this is a valid dfs link. Parse the targetpath. */
880 if (!parse_msdfs_symlink(ctx, targetpath,
881 &jucn->referral_list,
882 &jucn->referral_count)) {
883 DEBUG(3,("get_referred_path: failed to parse symlink "
884 "target %s\n", targetpath ));
885 conn_free_internal(conn);
887 return NT_STATUS_NOT_FOUND;
890 conn_free_internal(conn);
895 static int setup_ver2_dfs_referral(const char *pathname,
897 struct junction_map *junction,
901 char* pdata = *ppdata;
903 smb_ucs2_t *uni_requestedpath = NULL;
904 int uni_reqpathoffset1,uni_reqpathoffset2;
906 int requestedpathlen=0;
911 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
913 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
914 &uni_requestedpath, pathname);
915 if (uni_requestedpath == NULL || requestedpathlen == 0) {
920 dump_data(0, (unsigned char *)uni_requestedpath,
924 DEBUG(10,("ref count = %u\n",junction->referral_count));
926 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
927 VERSION2_REFERRAL_SIZE * junction->referral_count;
929 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
931 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
933 reply_size = REFERRAL_HEADER_SIZE +
934 VERSION2_REFERRAL_SIZE*junction->referral_count +
935 2 * requestedpathlen;
936 DEBUG(10,("reply_size: %u\n",reply_size));
938 /* add up the unicode lengths of all the referral paths */
939 for(i=0;i<junction->referral_count;i++) {
940 DEBUG(10,("referral %u : %s\n",
942 junction->referral_list[i].alternate_path));
944 (strlen(junction->referral_list[i].alternate_path)+1)*2;
947 DEBUG(10,("reply_size = %u\n",reply_size));
948 /* add the unexplained 0x16 bytes */
951 pdata = (char *)SMB_REALLOC(pdata,reply_size);
953 DEBUG(0,("Realloc failed!\n"));
958 /* copy in the dfs requested paths.. required for offset calculations */
959 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
960 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
962 /* create the header */
963 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
964 /* number of referral in this pkt */
965 SSVAL(pdata,2,junction->referral_count);
967 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
969 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
973 /* add the referral elements */
974 for(i=0;i<junction->referral_count;i++) {
975 struct referral* ref = &junction->referral_list[i];
978 SSVAL(pdata,offset,2); /* version 2 */
979 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
981 SSVAL(pdata,offset+4,1);
983 SSVAL(pdata,offset+4,0);
986 /* ref_flags :use path_consumed bytes? */
987 SSVAL(pdata,offset+6,0);
988 SIVAL(pdata,offset+8,ref->proximity);
989 SIVAL(pdata,offset+12,ref->ttl);
991 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
992 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
993 /* copy referred path into current offset */
994 unilen = rpcstr_push(pdata+uni_curroffset,
996 reply_size - uni_curroffset,
999 SSVAL(pdata,offset+20,uni_curroffset-offset);
1001 uni_curroffset += unilen;
1002 offset += VERSION2_REFERRAL_SIZE;
1004 /* add in the unexplained 22 (0x16) bytes at the end */
1005 memset(pdata+uni_curroffset,'\0',0x16);
1009 static int setup_ver3_dfs_referral(const char *pathname,
1011 struct junction_map *junction,
1015 char *pdata = *ppdata;
1017 smb_ucs2_t *uni_reqpath = NULL;
1018 int uni_reqpathoffset1, uni_reqpathoffset2;
1025 DEBUG(10,("setting up version3 referral\n"));
1027 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1028 if (uni_reqpath == NULL || reqpathlen == 0) {
1033 dump_data(0, (unsigned char *)uni_reqpath,
1037 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1038 VERSION3_REFERRAL_SIZE * junction->referral_count;
1039 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1040 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1042 for(i=0;i<junction->referral_count;i++) {
1043 DEBUG(10,("referral %u : %s\n",
1045 junction->referral_list[i].alternate_path));
1047 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1050 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1052 DEBUG(0,("version3 referral setup:"
1053 "malloc failed for Realloc!\n"));
1058 /* create the header */
1059 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
1060 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1062 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1064 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1067 /* copy in the reqpaths */
1068 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1069 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1072 for(i=0;i<junction->referral_count;i++) {
1073 struct referral* ref = &(junction->referral_list[i]);
1076 SSVAL(pdata,offset,3); /* version 3 */
1077 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1079 SSVAL(pdata,offset+4,1);
1081 SSVAL(pdata,offset+4,0);
1084 /* ref_flags :use path_consumed bytes? */
1085 SSVAL(pdata,offset+6,0);
1086 SIVAL(pdata,offset+8,ref->ttl);
1088 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1089 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1090 /* copy referred path into current offset */
1091 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1092 reply_size - uni_curroffset,
1093 STR_UNICODE | STR_TERMINATE);
1094 SSVAL(pdata,offset+16,uni_curroffset-offset);
1095 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1096 memset(pdata+offset+18,'\0',16);
1098 uni_curroffset += unilen;
1099 offset += VERSION3_REFERRAL_SIZE;
1104 /******************************************************************
1105 Set up the DFS referral for the dfs pathname. This call returns
1106 the amount of the path covered by this server, and where the
1107 client should be redirected to. This is the meat of the
1108 TRANS2_GET_DFS_REFERRAL call.
1109 ******************************************************************/
1111 int setup_dfs_referral(connection_struct *orig_conn,
1112 const char *dfs_path,
1113 int max_referral_level,
1114 char **ppdata, NTSTATUS *pstatus)
1116 struct junction_map *junction = NULL;
1117 int consumedcnt = 0;
1118 bool self_referral = False;
1120 char *pathnamep = NULL;
1121 char *local_dfs_path = NULL;
1124 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1125 *pstatus = NT_STATUS_NO_MEMORY;
1129 /* get the junction entry */
1131 talloc_destroy(ctx);
1132 *pstatus = NT_STATUS_NOT_FOUND;
1137 * Trim pathname sent by client so it begins with only one backslash.
1138 * Two backslashes confuse some dfs clients
1141 local_dfs_path = talloc_strdup(ctx,dfs_path);
1142 if (!local_dfs_path) {
1143 *pstatus = NT_STATUS_NO_MEMORY;
1144 talloc_destroy(ctx);
1147 pathnamep = local_dfs_path;
1148 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1149 IS_DIRECTORY_SEP(pathnamep[1])) {
1153 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1155 *pstatus = NT_STATUS_NO_MEMORY;
1156 talloc_destroy(ctx);
1160 /* The following call can change cwd. */
1161 *pstatus = get_referred_path(ctx, pathnamep, junction,
1162 &consumedcnt, &self_referral);
1163 if (!NT_STATUS_IS_OK(*pstatus)) {
1164 vfs_ChDir(orig_conn,orig_conn->connectpath);
1165 talloc_destroy(ctx);
1168 vfs_ChDir(orig_conn,orig_conn->connectpath);
1170 if (!self_referral) {
1171 pathnamep[consumedcnt] = '\0';
1173 if( DEBUGLVL( 3 ) ) {
1175 dbgtext("setup_dfs_referral: Path %s to "
1176 "alternate path(s):",
1178 for(i=0;i<junction->referral_count;i++)
1180 junction->referral_list[i].alternate_path);
1185 /* create the referral depeding on version */
1186 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1188 if (max_referral_level < 2) {
1189 max_referral_level = 2;
1191 if (max_referral_level > 3) {
1192 max_referral_level = 3;
1195 switch(max_referral_level) {
1197 reply_size = setup_ver2_dfs_referral(pathnamep,
1199 consumedcnt, self_referral);
1202 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1203 junction, consumedcnt, self_referral);
1206 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1208 max_referral_level));
1209 talloc_destroy(ctx);
1210 *pstatus = NT_STATUS_INVALID_LEVEL;
1215 DEBUGADD(0,("DFS Referral pdata:\n"));
1216 dump_data(0,(uint8 *)*ppdata,reply_size);
1219 talloc_destroy(ctx);
1220 *pstatus = NT_STATUS_OK;
1224 /**********************************************************************
1225 The following functions are called by the NETDFS RPC pipe functions
1226 **********************************************************************/
1228 /*********************************************************************
1229 Creates a junction structure from a DFS pathname
1230 **********************************************************************/
1232 bool create_junction(TALLOC_CTX *ctx,
1233 const char *dfs_path,
1234 struct junction_map *jucn)
1238 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1244 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1245 if (!NT_STATUS_IS_OK(status)) {
1249 /* check if path is dfs : validate first token */
1250 if (!is_myname_or_ipaddr(pdp->hostname)) {
1251 DEBUG(4,("create_junction: Invalid hostname %s "
1253 pdp->hostname, dfs_path));
1258 /* Check for a non-DFS share */
1259 snum = lp_servicenumber(pdp->servicename);
1261 if(snum < 0 || !lp_msdfs_root(snum)) {
1262 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1268 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1269 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1270 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1273 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1279 /**********************************************************************
1280 Forms a valid Unix pathname from the junction
1281 **********************************************************************/
1283 static bool junction_to_local_path(const struct junction_map *jucn,
1285 connection_struct **conn_out)
1289 snum = lp_servicenumber(jucn->service_name);
1293 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1295 lp_pathname(snum)))) {
1299 *pp_path_out = talloc_asprintf(conn_out,
1303 if (!*pp_path_out) {
1309 bool create_msdfs_link(const struct junction_map *jucn,
1313 char *msdfs_link = NULL;
1314 connection_struct *conn;
1316 bool insert_comma = False;
1319 if(!junction_to_local_path(jucn, &path, &conn)) {
1323 /* Form the msdfs_link contents */
1324 msdfs_link = talloc_strdup(conn, "msdfs:");
1328 for(i=0; i<jucn->referral_count; i++) {
1329 char *refpath = jucn->referral_list[i].alternate_path;
1331 /* Alternate paths always use Windows separators. */
1332 trim_char(refpath, '\\', '\\');
1333 if(*refpath == '\0') {
1335 insert_comma = False;
1339 if (i > 0 && insert_comma) {
1340 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1344 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1352 if (!insert_comma) {
1353 insert_comma = True;
1357 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1361 if(SMB_VFS_UNLINK(conn,path)!=0) {
1366 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1367 DEBUG(1,("create_msdfs_link: symlink failed "
1368 "%s -> %s\nError: %s\n",
1369 path, msdfs_link, strerror(errno)));
1377 conn_free_internal(conn);
1381 bool remove_msdfs_link(const struct junction_map *jucn)
1384 connection_struct *conn;
1387 if( junction_to_local_path(jucn, &path, &conn) ) {
1388 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1393 conn_free_internal(conn);
1397 /*********************************************************************
1398 Return the number of DFS links at the root of this share.
1399 *********************************************************************/
1401 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1404 SMB_STRUCT_DIR *dirp = NULL;
1406 const char *connect_path = lp_pathname(snum);
1407 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1408 connection_struct *conn;
1410 if(*connect_path == '\0') {
1415 * Fake up a connection struct for the VFS layer.
1418 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1419 &conn, snum, connect_path))) {
1423 /* Count a link for the msdfs root - convention */
1426 /* No more links if this is an msdfs proxy. */
1427 if (*msdfs_proxy != '\0') {
1431 /* Now enumerate all dfs links */
1432 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1437 while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1438 if (is_msdfs_link(conn,
1445 SMB_VFS_CLOSEDIR(conn,dirp);
1449 conn_free_internal(conn);
1453 /*********************************************************************
1454 *********************************************************************/
1456 static int form_junctions(TALLOC_CTX *ctx,
1458 struct junction_map *jucn,
1462 SMB_STRUCT_DIR *dirp = NULL;
1464 const char *connect_path = lp_pathname(snum);
1465 char *service_name = lp_servicename(snum);
1466 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1467 connection_struct *conn;
1468 struct referral *ref = NULL;
1470 if (jn_remain == 0) {
1474 if(*connect_path == '\0') {
1479 * Fake up a connection struct for the VFS layer.
1482 if (!NT_STATUS_IS_OK(create_conn_struct(ctx, &conn, snum, connect_path))) {
1486 /* form a junction for the msdfs root - convention
1487 DO NOT REMOVE THIS: NT clients will not work with us
1488 if this is not present
1490 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1491 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1492 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1495 jucn[cnt].referral_count = 1;
1497 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1498 if (jucn[cnt].referral_list == NULL) {
1503 ref->ttl = REFERRAL_TTL;
1504 if (*msdfs_proxy != '\0') {
1505 ref->alternate_path = talloc_strdup(ctx,
1508 ref->alternate_path = talloc_asprintf(ctx,
1510 get_local_machine_name(),
1514 if (!ref->alternate_path) {
1519 /* Don't enumerate if we're an msdfs proxy. */
1520 if (*msdfs_proxy != '\0') {
1524 /* Now enumerate all dfs links */
1525 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1530 while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1531 char *link_target = NULL;
1532 if (cnt >= jn_remain) {
1533 SMB_VFS_CLOSEDIR(conn,dirp);
1534 DEBUG(2, ("form_junctions: ran out of MSDFS "
1538 if (is_msdfs_link_internal(ctx,
1540 dname, &link_target,
1542 if (parse_msdfs_symlink(ctx,
1544 &jucn[cnt].referral_list,
1545 &jucn[cnt].referral_count)) {
1547 jucn[cnt].service_name = talloc_strdup(ctx,
1549 jucn[cnt].volume_name = talloc_strdup(ctx,
1551 if (!jucn[cnt].service_name ||
1552 !jucn[cnt].volume_name) {
1563 SMB_VFS_CLOSEDIR(conn,dirp);
1566 conn_free_internal(conn);
1570 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1572 struct junction_map *jn = NULL;
1574 size_t jn_count = 0;
1578 if(!lp_host_msdfs()) {
1582 /* Ensure all the usershares are loaded. */
1584 load_registry_shares();
1585 sharecount = load_usershare_shares();
1588 for(i=0;i < sharecount;i++) {
1589 if(lp_msdfs_root(i)) {
1590 jn_count += count_dfs_links(ctx, i);
1593 if (jn_count == 0) {
1596 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1600 for(i=0; i < sharecount; i++) {
1601 if (*p_num_jn >= jn_count) {
1604 if(lp_msdfs_root(i)) {
1605 *p_num_jn += form_junctions(ctx, i,
1607 jn_count - *p_num_jn);
1613 /******************************************************************************
1614 Core function to resolve a dfs pathname.
1615 ******************************************************************************/
1617 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1618 connection_struct *conn,
1620 const char *name_in,
1623 NTSTATUS status = NT_STATUS_OK;
1625 if (dfs_pathnames) {
1626 status = dfs_redirect(ctx,
1634 * Cheat and just return a copy of the in ptr.
1635 * Once srvstr_get_path() uses talloc it'll
1636 * be a talloced ptr anyway.
1638 *pp_name_out = CONST_DISCARD(char *,name_in);
1643 /******************************************************************************
1644 Core function to resolve a dfs pathname possibly containing a wildcard.
1645 This function is identical to the above except for the bool param to
1646 dfs_redirect but I need this to be separate so it's really clear when
1647 we're allowing wildcards and when we're not. JRA.
1648 ******************************************************************************/
1650 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1651 connection_struct *conn,
1653 const char *name_in,
1655 bool *ppath_contains_wcard)
1657 NTSTATUS status = NT_STATUS_OK;
1658 if (dfs_pathnames) {
1659 status = dfs_redirect(ctx,
1664 ppath_contains_wcard);
1667 * Cheat and just return a copy of the in ptr.
1668 * Once srvstr_get_path() uses talloc it'll
1669 * be a talloced ptr anyway.
1671 *pp_name_out = CONST_DISCARD(char *,name_in);