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 This version does everything using pointers within one copy of the
40 pathname string, talloced on the struct dfs_path pointer (which
41 must be talloced). This may be too clever to live....
43 **********************************************************************/
45 static NTSTATUS parse_dfs_path(const char *pathname,
47 struct dfs_path *pdp, /* MUST BE TALLOCED */
48 bool *ppath_contains_wcard)
53 NTSTATUS status = NT_STATUS_OK;
59 * This is the only talloc we should need to do
60 * on the struct dfs_path. All the pointers inside
61 * it should point to offsets within this string.
64 pathname_local = talloc_strdup(pdp, pathname);
65 if (!pathname_local) {
66 return NT_STATUS_NO_MEMORY;
68 /* Get a pointer to the terminating '\0' */
69 eos_ptr = &pathname_local[strlen(pathname_local)];
70 p = temp = pathname_local;
72 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
74 sepchar = pdp->posix_path ? '/' : '\\';
76 if (*pathname != sepchar) {
77 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
80 * Possibly client sent a local path by mistake.
81 * Try and convert to a local path.
84 pdp->hostname = eos_ptr; /* "" */
85 pdp->servicename = eos_ptr; /* "" */
87 /* We've got no info about separators. */
88 pdp->posix_path = lp_posix_pathnames();
90 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
97 * Safe to use on talloc'ed string as it only shrinks.
98 * It also doesn't affect the eos_ptr.
100 trim_char(temp,sepchar,sepchar);
102 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
106 /* Parse out hostname. */
107 p = strchr_m(temp,sepchar);
109 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
112 * Possibly client sent a local path by mistake.
113 * Try and convert to a local path.
116 pdp->hostname = eos_ptr; /* "" */
117 pdp->servicename = eos_ptr; /* "" */
120 DEBUG(10,("parse_dfs_path: trying to convert %s "
126 pdp->hostname = temp;
128 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
130 /* Parse out servicename. */
132 p = strchr_m(temp,sepchar);
134 pdp->servicename = temp;
135 pdp->reqpath = eos_ptr; /* "" */
139 pdp->servicename = temp;
140 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
146 *ppath_contains_wcard = False;
150 /* Rest is reqpath. */
151 if (pdp->posix_path) {
152 status = check_path_syntax_posix(pdp->reqpath);
155 status = check_path_syntax_wcard(pdp->reqpath,
156 ppath_contains_wcard);
158 status = check_path_syntax(pdp->reqpath);
162 if (!NT_STATUS_IS_OK(status)) {
163 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
164 p, nt_errstr(status) ));
168 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
172 /********************************************************
173 Fake up a connection struct for the VFS layer.
174 Note this CHANGES CWD !!!! JRA.
175 *********************************************************/
177 static NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
178 connection_struct **pconn,
182 connection_struct *conn;
185 conn = TALLOC_ZERO_P(ctx, connection_struct);
187 return NT_STATUS_NO_MEMORY;
190 connpath = talloc_strdup(conn, path);
193 return NT_STATUS_NO_MEMORY;
195 connpath = talloc_string_sub(conn,
198 lp_servicename(snum));
201 return NT_STATUS_NO_MEMORY;
204 /* needed for smbd_vfs_init() */
206 if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
207 DEBUG(0, ("TALLOC failed\n"));
209 return NT_STATUS_NO_MEMORY;
212 conn->params->service = snum;
214 set_conn_connectpath(conn, connpath);
216 if (!smbd_vfs_init(conn)) {
217 NTSTATUS status = map_nt_error_from_unix(errno);
218 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
219 conn_free_internal(conn);
224 * Windows seems to insist on doing trans2getdfsreferral() calls on
225 * the IPC$ share as the anonymous user. If we try to chdir as that
226 * user we will fail.... WTF ? JRA.
229 if (vfs_ChDir(conn,conn->connectpath) != 0) {
230 NTSTATUS status = map_nt_error_from_unix(errno);
231 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
233 conn->connectpath, strerror(errno) ));
234 conn_free_internal(conn);
243 /**********************************************************************
244 Parse the contents of a symlink to verify if it is an msdfs referral
245 A valid referral is of the form:
247 msdfs:server1\share1,server2\share2
248 msdfs:server1\share1\pathname,server2\share2\pathname
249 msdfs:server1/share1,server2/share2
250 msdfs:server1/share1/pathname,server2/share2/pathname.
252 Note that the alternate paths returned here must be of the canonicalized
256 \server\share\path\to\file,
258 even in posix path mode. This is because we have no knowledge if the
259 server we're referring to understands posix paths.
260 **********************************************************************/
262 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
264 struct referral **preflist,
269 char **alt_path = NULL;
271 struct referral *reflist;
274 temp = talloc_strdup(ctx, target);
278 prot = strtok_r(temp, ":", &saveptr);
280 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
284 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
289 /* parse out the alternate paths */
290 while((count<MAX_REFERRAL_COUNT) &&
291 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
295 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
298 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
299 struct referral, count);
300 if(reflist == NULL) {
301 TALLOC_FREE(alt_path);
305 reflist = *preflist = NULL;
308 for(i=0;i<count;i++) {
311 /* Canonicalize link target.
312 * Replace all /'s in the path by a \ */
313 string_replace(alt_path[i], '/', '\\');
315 /* Remove leading '\\'s */
317 while (*p && (*p == '\\')) {
321 reflist[i].alternate_path = talloc_asprintf(ctx,
324 if (!reflist[i].alternate_path) {
328 reflist[i].proximity = 0;
329 reflist[i].ttl = REFERRAL_TTL;
330 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
331 reflist[i].alternate_path));
335 TALLOC_FREE(alt_path);
339 /**********************************************************************
340 Returns true if the unix path is a valid msdfs symlink and also
341 returns the target string from inside the link.
342 **********************************************************************/
344 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
345 connection_struct *conn,
347 char **pp_link_target,
348 SMB_STRUCT_STAT *sbufp)
351 int referral_len = 0;
352 char link_target_buf[7];
354 char *link_target = NULL;
356 if (pp_link_target) {
358 link_target = TALLOC_ARRAY(ctx, char, bufsize);
362 *pp_link_target = link_target;
364 bufsize = sizeof(link_target_buf);
365 link_target = link_target_buf;
372 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
373 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
378 if (!S_ISLNK(sbufp->st_mode)) {
379 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
384 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
385 if (referral_len == -1) {
386 DEBUG(0,("is_msdfs_link_read_target: Error reading "
387 "msdfs link %s: %s\n",
388 path, strerror(errno)));
391 link_target[referral_len] = '\0';
393 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
396 if (!strnequal(link_target, "msdfs:", 6)) {
403 if (link_target != link_target_buf) {
404 TALLOC_FREE(link_target);
409 /**********************************************************************
410 Returns true if the unix path is a valid msdfs symlink.
411 **********************************************************************/
413 bool is_msdfs_link(connection_struct *conn,
415 SMB_STRUCT_STAT *sbufp)
417 return is_msdfs_link_internal(talloc_tos(),
424 /*****************************************************************
425 Used by other functions to decide if a dfs path is remote,
426 and to get the list of referred locations for that remote path.
428 search_flag: For findfirsts, dfs links themselves are not
429 redirected, but paths beyond the links are. For normal smb calls,
430 even dfs links need to be redirected.
432 consumedcntp: how much of the dfs path is being redirected. the client
433 should try the remaining path on the redirected server.
435 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
436 link redirect are in targetpath.
437 *****************************************************************/
439 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
440 connection_struct *conn,
441 const char *dfspath, /* Incoming complete dfs path */
442 const struct dfs_path *pdp, /* Parsed out
443 server+share+extrapath. */
444 bool search_flag, /* Called from a findfirst ? */
446 char **pp_targetpath)
450 SMB_STRUCT_STAT sbuf;
452 char *localpath = NULL;
453 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
456 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
457 conn->connectpath, pdp->reqpath));
460 * Note the unix path conversion here we're doing we can
461 * throw away. We're looking for a symlink for a dfs
462 * resolution, if we don't find it we'll do another
463 * unix_convert later in the codepath.
464 * If we needed to remember what we'd resolved in
465 * dp->reqpath (as the original code did) we'd
466 * copy (localhost, dp->reqpath) on any code
467 * path below that returns True - but I don't
468 * think this is needed. JRA.
471 status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
473 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
474 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
478 /* Optimization - check if we can redirect the whole path. */
480 if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
482 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
483 "for dfs link %s.\n", dfspath));
487 DEBUG(6,("dfs_path_lookup: %s resolves to a "
488 "valid dfs link %s.\n", dfspath,
489 pp_targetpath ? *pp_targetpath : ""));
492 *consumedcntp = strlen(dfspath);
494 return NT_STATUS_PATH_NOT_COVERED;
497 /* Prepare to test only for '/' components in the given path,
498 * so if a Windows path replace all '\\' characters with '/'.
499 * For a POSIX DFS path we know all separators are already '/'. */
501 canon_dfspath = talloc_strdup(ctx, dfspath);
502 if (!canon_dfspath) {
503 return NT_STATUS_NO_MEMORY;
505 if (!pdp->posix_path) {
506 string_replace(canon_dfspath, '\\', '/');
510 * localpath comes out of unix_convert, so it has
511 * no trailing backslash. Make sure that canon_dfspath hasn't either.
512 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
515 trim_char(canon_dfspath,0,'/');
518 * Redirect if any component in the path is a link.
519 * We do this by walking backwards through the
520 * local path, chopping off the last component
521 * in both the local path and the canonicalized
522 * DFS path. If we hit a DFS link then we're done.
525 p = strrchr_m(localpath, '/');
527 q = strrchr_m(canon_dfspath, '/');
536 if (is_msdfs_link_internal(ctx, conn,
537 localpath, pp_targetpath, NULL)) {
538 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
539 "parent %s is dfs link\n", dfspath, localpath));
542 *consumedcntp = strlen(canon_dfspath);
543 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
549 return NT_STATUS_PATH_NOT_COVERED;
552 /* Step back on the filesystem. */
553 p = strrchr_m(localpath, '/');
556 /* And in the canonicalized dfs path. */
557 q = strrchr_m(canon_dfspath, '/');
564 /*****************************************************************
565 Decides if a dfs pathname should be redirected or not.
566 If not, the pathname is converted to a tcon-relative local unix path
568 search_wcard_flag: this flag performs 2 functions both related
569 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
572 This function can return NT_STATUS_OK, meaning use the returned path as-is
573 (mapped into a local path).
574 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
575 any other NT_STATUS error which is a genuine error to be
576 returned to the client.
577 *****************************************************************/
579 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
580 connection_struct *conn,
582 bool search_wcard_flag,
584 bool *ppath_contains_wcard)
587 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
590 return NT_STATUS_NO_MEMORY;
593 status = parse_dfs_path(path_in, search_wcard_flag, pdp,
594 ppath_contains_wcard);
595 if (!NT_STATUS_IS_OK(status)) {
600 if (pdp->reqpath[0] == '\0') {
602 *pp_path_out = talloc_strdup(ctx, "");
604 return NT_STATUS_NO_MEMORY;
606 DEBUG(5,("dfs_redirect: self-referral.\n"));
610 /* If dfs pathname for a non-dfs share, convert to tcon-relative
611 path and return OK */
613 if (!lp_msdfs_root(SNUM(conn))) {
614 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
617 return NT_STATUS_NO_MEMORY;
622 /* If it looked like a local path (zero hostname/servicename)
623 * just treat as a tcon-relative path. */
625 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
626 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
629 return NT_STATUS_NO_MEMORY;
634 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
635 || (strequal(pdp->servicename, HOMES_NAME)
636 && strequal(lp_servicename(SNUM(conn)),
637 get_current_username()) )) ) {
639 /* The given sharename doesn't match this connection. */
642 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
645 status = dfs_path_lookup(ctx, conn, path_in, pdp,
646 search_wcard_flag, NULL, NULL);
647 if (!NT_STATUS_IS_OK(status)) {
648 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
649 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
651 DEBUG(10,("dfs_redirect: dfs_path_lookup "
652 "failed for %s with %s\n",
653 path_in, nt_errstr(status) ));
658 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
660 /* Form non-dfs tcon-relative path */
661 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
664 return NT_STATUS_NO_MEMORY;
667 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
674 /**********************************************************************
675 Return a self referral.
676 **********************************************************************/
678 static NTSTATUS self_ref(TALLOC_CTX *ctx,
679 const char *dfs_path,
680 struct junction_map *jucn,
682 bool *self_referralp)
684 struct referral *ref;
686 *self_referralp = True;
688 jucn->referral_count = 1;
689 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
690 return NT_STATUS_NO_MEMORY;
693 ref->alternate_path = talloc_strdup(ctx, dfs_path);
694 if (!ref->alternate_path) {
695 return NT_STATUS_NO_MEMORY;
698 ref->ttl = REFERRAL_TTL;
699 jucn->referral_list = ref;
700 *consumedcntp = strlen(dfs_path);
704 /**********************************************************************
705 Gets valid referrals for a dfs path and fills up the
706 junction_map structure.
707 **********************************************************************/
709 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
710 const char *dfs_path,
711 struct junction_map *jucn,
713 bool *self_referralp)
715 struct connection_struct *conn;
716 char *targetpath = NULL;
718 NTSTATUS status = NT_STATUS_NOT_FOUND;
720 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
723 return NT_STATUS_NO_MEMORY;
726 *self_referralp = False;
728 status = parse_dfs_path(dfs_path, False, pdp, &dummy);
729 if (!NT_STATUS_IS_OK(status)) {
733 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
734 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
735 if (!jucn->service_name || !jucn->volume_name) {
737 return NT_STATUS_NO_MEMORY;
740 /* Verify the share is a dfs root */
741 snum = lp_servicenumber(jucn->service_name);
743 fstring service_name;
744 fstrcpy(service_name, jucn->service_name);
745 if ((snum = find_service(service_name)) < 0) {
746 return NT_STATUS_NOT_FOUND;
748 TALLOC_FREE(jucn->service_name);
749 jucn->service_name = talloc_strdup(ctx, service_name);
750 if (!jucn->service_name) {
752 return NT_STATUS_NO_MEMORY;
756 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
757 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
759 pdp->servicename, dfs_path));
761 return NT_STATUS_NOT_FOUND;
765 * Self referrals are tested with a anonymous IPC connection and
766 * a GET_DFS_REFERRAL call to \\server\share. (which means
767 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
768 * into the directory and will fail if it cannot (as the anonymous
769 * user). Cope with this.
772 if (pdp->reqpath[0] == '\0') {
774 struct referral *ref;
776 if (*lp_msdfs_proxy(snum) == '\0') {
786 * It's an msdfs proxy share. Redirect to
787 * the configured target share.
790 jucn->referral_count = 1;
791 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
793 return NT_STATUS_NO_MEMORY;
796 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
798 return NT_STATUS_NO_MEMORY;
801 trim_string(tmp, "\\", 0);
803 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
806 if (!ref->alternate_path) {
808 return NT_STATUS_NO_MEMORY;
811 if (pdp->reqpath[0] != '\0') {
812 ref->alternate_path = talloc_asprintf_append(
816 if (!ref->alternate_path) {
818 return NT_STATUS_NO_MEMORY;
822 ref->ttl = REFERRAL_TTL;
823 jucn->referral_list = ref;
824 *consumedcntp = strlen(dfs_path);
829 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum));
830 if (!NT_STATUS_IS_OK(status)) {
835 /* If this is a DFS path dfs_lookup should return
836 * NT_STATUS_PATH_NOT_COVERED. */
838 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
839 False, consumedcntp, &targetpath);
841 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
842 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
844 conn_free_internal(conn);
849 /* We know this is a valid dfs link. Parse the targetpath. */
850 if (!parse_msdfs_symlink(ctx, targetpath,
851 &jucn->referral_list,
852 &jucn->referral_count)) {
853 DEBUG(3,("get_referred_path: failed to parse symlink "
854 "target %s\n", targetpath ));
855 conn_free_internal(conn);
857 return NT_STATUS_NOT_FOUND;
860 conn_free_internal(conn);
865 static int setup_ver2_dfs_referral(const char *pathname,
867 struct junction_map *junction,
871 char* pdata = *ppdata;
873 smb_ucs2_t *uni_requestedpath = NULL;
874 int uni_reqpathoffset1,uni_reqpathoffset2;
876 int requestedpathlen=0;
881 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
883 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
884 &uni_requestedpath, pathname);
885 if (uni_requestedpath == NULL || requestedpathlen == 0) {
890 dump_data(0, (unsigned char *)uni_requestedpath,
894 DEBUG(10,("ref count = %u\n",junction->referral_count));
896 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
897 VERSION2_REFERRAL_SIZE * junction->referral_count;
899 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
901 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
903 reply_size = REFERRAL_HEADER_SIZE +
904 VERSION2_REFERRAL_SIZE*junction->referral_count +
905 2 * requestedpathlen;
906 DEBUG(10,("reply_size: %u\n",reply_size));
908 /* add up the unicode lengths of all the referral paths */
909 for(i=0;i<junction->referral_count;i++) {
910 DEBUG(10,("referral %u : %s\n",
912 junction->referral_list[i].alternate_path));
914 (strlen(junction->referral_list[i].alternate_path)+1)*2;
917 DEBUG(10,("reply_size = %u\n",reply_size));
918 /* add the unexplained 0x16 bytes */
921 pdata = (char *)SMB_REALLOC(pdata,reply_size);
923 DEBUG(0,("Realloc failed!\n"));
928 /* copy in the dfs requested paths.. required for offset calculations */
929 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
930 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
932 /* create the header */
933 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
934 /* number of referral in this pkt */
935 SSVAL(pdata,2,junction->referral_count);
937 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
939 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
943 /* add the referral elements */
944 for(i=0;i<junction->referral_count;i++) {
945 struct referral* ref = &junction->referral_list[i];
948 SSVAL(pdata,offset,2); /* version 2 */
949 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
951 SSVAL(pdata,offset+4,1);
953 SSVAL(pdata,offset+4,0);
956 /* ref_flags :use path_consumed bytes? */
957 SSVAL(pdata,offset+6,0);
958 SIVAL(pdata,offset+8,ref->proximity);
959 SIVAL(pdata,offset+12,ref->ttl);
961 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
962 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
963 /* copy referred path into current offset */
964 unilen = rpcstr_push(pdata+uni_curroffset,
966 reply_size - uni_curroffset,
969 SSVAL(pdata,offset+20,uni_curroffset-offset);
971 uni_curroffset += unilen;
972 offset += VERSION2_REFERRAL_SIZE;
974 /* add in the unexplained 22 (0x16) bytes at the end */
975 memset(pdata+uni_curroffset,'\0',0x16);
979 static int setup_ver3_dfs_referral(const char *pathname,
981 struct junction_map *junction,
985 char *pdata = *ppdata;
987 smb_ucs2_t *uni_reqpath = NULL;
988 int uni_reqpathoffset1, uni_reqpathoffset2;
995 DEBUG(10,("setting up version3 referral\n"));
997 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
998 if (uni_reqpath == NULL || reqpathlen == 0) {
1003 dump_data(0, (unsigned char *)uni_reqpath,
1007 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1008 VERSION3_REFERRAL_SIZE * junction->referral_count;
1009 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1010 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1012 for(i=0;i<junction->referral_count;i++) {
1013 DEBUG(10,("referral %u : %s\n",
1015 junction->referral_list[i].alternate_path));
1017 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1020 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1022 DEBUG(0,("version3 referral setup:"
1023 "malloc failed for Realloc!\n"));
1028 /* create the header */
1029 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
1030 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1032 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1034 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1037 /* copy in the reqpaths */
1038 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1039 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1042 for(i=0;i<junction->referral_count;i++) {
1043 struct referral* ref = &(junction->referral_list[i]);
1046 SSVAL(pdata,offset,3); /* version 3 */
1047 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1049 SSVAL(pdata,offset+4,1);
1051 SSVAL(pdata,offset+4,0);
1054 /* ref_flags :use path_consumed bytes? */
1055 SSVAL(pdata,offset+6,0);
1056 SIVAL(pdata,offset+8,ref->ttl);
1058 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1059 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1060 /* copy referred path into current offset */
1061 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1062 reply_size - uni_curroffset,
1063 STR_UNICODE | STR_TERMINATE);
1064 SSVAL(pdata,offset+16,uni_curroffset-offset);
1065 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1066 memset(pdata+offset+18,'\0',16);
1068 uni_curroffset += unilen;
1069 offset += VERSION3_REFERRAL_SIZE;
1074 /******************************************************************
1075 Set up the DFS referral for the dfs pathname. This call returns
1076 the amount of the path covered by this server, and where the
1077 client should be redirected to. This is the meat of the
1078 TRANS2_GET_DFS_REFERRAL call.
1079 ******************************************************************/
1081 int setup_dfs_referral(connection_struct *orig_conn,
1082 const char *dfs_path,
1083 int max_referral_level,
1084 char **ppdata, NTSTATUS *pstatus)
1086 struct junction_map *junction = NULL;
1087 int consumedcnt = 0;
1088 bool self_referral = False;
1090 char *pathnamep = NULL;
1091 char *local_dfs_path = NULL;
1094 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1095 *pstatus = NT_STATUS_NO_MEMORY;
1099 /* get the junction entry */
1101 talloc_destroy(ctx);
1102 *pstatus = NT_STATUS_NOT_FOUND;
1107 * Trim pathname sent by client so it begins with only one backslash.
1108 * Two backslashes confuse some dfs clients
1111 local_dfs_path = talloc_strdup(ctx,dfs_path);
1112 if (!local_dfs_path) {
1113 *pstatus = NT_STATUS_NO_MEMORY;
1114 talloc_destroy(ctx);
1117 pathnamep = local_dfs_path;
1118 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1119 IS_DIRECTORY_SEP(pathnamep[1])) {
1123 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1125 *pstatus = NT_STATUS_NO_MEMORY;
1126 talloc_destroy(ctx);
1130 /* The following call can change cwd. */
1131 *pstatus = get_referred_path(ctx, pathnamep, junction,
1132 &consumedcnt, &self_referral);
1133 if (!NT_STATUS_IS_OK(*pstatus)) {
1134 vfs_ChDir(orig_conn,orig_conn->connectpath);
1135 talloc_destroy(ctx);
1138 vfs_ChDir(orig_conn,orig_conn->connectpath);
1140 if (!self_referral) {
1141 pathnamep[consumedcnt] = '\0';
1143 if( DEBUGLVL( 3 ) ) {
1145 dbgtext("setup_dfs_referral: Path %s to "
1146 "alternate path(s):",
1148 for(i=0;i<junction->referral_count;i++)
1150 junction->referral_list[i].alternate_path);
1155 /* create the referral depeding on version */
1156 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1158 if (max_referral_level < 2) {
1159 max_referral_level = 2;
1161 if (max_referral_level > 3) {
1162 max_referral_level = 3;
1165 switch(max_referral_level) {
1167 reply_size = setup_ver2_dfs_referral(pathnamep,
1169 consumedcnt, self_referral);
1172 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1173 junction, consumedcnt, self_referral);
1176 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1178 max_referral_level));
1179 talloc_destroy(ctx);
1180 *pstatus = NT_STATUS_INVALID_LEVEL;
1185 DEBUGADD(0,("DFS Referral pdata:\n"));
1186 dump_data(0,(uint8 *)*ppdata,reply_size);
1189 talloc_destroy(ctx);
1190 *pstatus = NT_STATUS_OK;
1194 /**********************************************************************
1195 The following functions are called by the NETDFS RPC pipe functions
1196 **********************************************************************/
1198 /*********************************************************************
1199 Creates a junction structure from a DFS pathname
1200 **********************************************************************/
1202 bool create_junction(TALLOC_CTX *ctx,
1203 const char *dfs_path,
1204 struct junction_map *jucn)
1208 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1214 status = parse_dfs_path(dfs_path, False, pdp, &dummy);
1215 if (!NT_STATUS_IS_OK(status)) {
1219 /* check if path is dfs : validate first token */
1220 if (!is_myname_or_ipaddr(pdp->hostname)) {
1221 DEBUG(4,("create_junction: Invalid hostname %s "
1223 pdp->hostname, dfs_path));
1228 /* Check for a non-DFS share */
1229 snum = lp_servicenumber(pdp->servicename);
1231 if(snum < 0 || !lp_msdfs_root(snum)) {
1232 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1238 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1239 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1240 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1243 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1249 /**********************************************************************
1250 Forms a valid Unix pathname from the junction
1251 **********************************************************************/
1253 static bool junction_to_local_path(const struct junction_map *jucn,
1255 connection_struct **conn_out)
1259 snum = lp_servicenumber(jucn->service_name);
1263 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1265 lp_pathname(snum)))) {
1269 *pp_path_out = talloc_asprintf(conn_out,
1273 if (!*pp_path_out) {
1279 bool create_msdfs_link(const struct junction_map *jucn,
1283 char *msdfs_link = NULL;
1284 connection_struct *conn;
1286 bool insert_comma = False;
1289 if(!junction_to_local_path(jucn, &path, &conn)) {
1293 /* Form the msdfs_link contents */
1294 msdfs_link = talloc_strdup(conn, "msdfs:");
1298 for(i=0; i<jucn->referral_count; i++) {
1299 char *refpath = jucn->referral_list[i].alternate_path;
1301 /* Alternate paths always use Windows separators. */
1302 trim_char(refpath, '\\', '\\');
1303 if(*refpath == '\0') {
1305 insert_comma = False;
1309 if (i > 0 && insert_comma) {
1310 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1314 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1322 if (!insert_comma) {
1323 insert_comma = True;
1327 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1331 if(SMB_VFS_UNLINK(conn,path)!=0) {
1336 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1337 DEBUG(1,("create_msdfs_link: symlink failed "
1338 "%s -> %s\nError: %s\n",
1339 path, msdfs_link, strerror(errno)));
1347 conn_free_internal(conn);
1351 bool remove_msdfs_link(const struct junction_map *jucn)
1354 connection_struct *conn;
1357 if( junction_to_local_path(jucn, &path, &conn) ) {
1358 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1363 conn_free_internal(conn);
1367 /*********************************************************************
1368 Return the number of DFS links at the root of this share.
1369 *********************************************************************/
1371 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1374 SMB_STRUCT_DIR *dirp = NULL;
1376 const char *connect_path = lp_pathname(snum);
1377 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1378 connection_struct *conn;
1380 if(*connect_path == '\0') {
1385 * Fake up a connection struct for the VFS layer.
1388 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1389 &conn, snum, connect_path))) {
1393 /* Count a link for the msdfs root - convention */
1396 /* No more links if this is an msdfs proxy. */
1397 if (*msdfs_proxy != '\0') {
1401 /* Now enumerate all dfs links */
1402 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1407 while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1408 if (is_msdfs_link(conn,
1415 SMB_VFS_CLOSEDIR(conn,dirp);
1419 conn_free_internal(conn);
1423 /*********************************************************************
1424 *********************************************************************/
1426 static int form_junctions(TALLOC_CTX *ctx,
1428 struct junction_map *jucn,
1432 SMB_STRUCT_DIR *dirp = NULL;
1434 const char *connect_path = lp_pathname(snum);
1435 char *service_name = lp_servicename(snum);
1436 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1437 connection_struct *conn;
1438 struct referral *ref = NULL;
1440 if (jn_remain == 0) {
1444 if(*connect_path == '\0') {
1449 * Fake up a connection struct for the VFS layer.
1452 if (!NT_STATUS_IS_OK(create_conn_struct(ctx, &conn, snum, connect_path))) {
1456 /* form a junction for the msdfs root - convention
1457 DO NOT REMOVE THIS: NT clients will not work with us
1458 if this is not present
1460 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1461 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1462 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1465 jucn[cnt].referral_count = 1;
1467 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1468 if (jucn[cnt].referral_list == NULL) {
1473 ref->ttl = REFERRAL_TTL;
1474 if (*msdfs_proxy != '\0') {
1475 ref->alternate_path = talloc_strdup(ctx,
1478 ref->alternate_path = talloc_asprintf(ctx,
1480 get_local_machine_name(),
1484 if (!ref->alternate_path) {
1489 /* Don't enumerate if we're an msdfs proxy. */
1490 if (*msdfs_proxy != '\0') {
1494 /* Now enumerate all dfs links */
1495 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1500 while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1501 char *link_target = NULL;
1502 if (cnt >= jn_remain) {
1503 SMB_VFS_CLOSEDIR(conn,dirp);
1504 DEBUG(2, ("form_junctions: ran out of MSDFS "
1508 if (is_msdfs_link_internal(ctx,
1510 dname, &link_target,
1512 if (parse_msdfs_symlink(ctx,
1514 &jucn[cnt].referral_list,
1515 &jucn[cnt].referral_count)) {
1517 jucn[cnt].service_name = talloc_strdup(ctx,
1519 jucn[cnt].volume_name = talloc_strdup(ctx,
1521 if (!jucn[cnt].service_name ||
1522 !jucn[cnt].volume_name) {
1533 SMB_VFS_CLOSEDIR(conn,dirp);
1536 conn_free_internal(conn);
1540 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1542 struct junction_map *jn = NULL;
1544 size_t jn_count = 0;
1548 if(!lp_host_msdfs()) {
1552 /* Ensure all the usershares are loaded. */
1554 load_registry_shares();
1555 sharecount = load_usershare_shares();
1558 for(i=0;i < sharecount;i++) {
1559 if(lp_msdfs_root(i)) {
1560 jn_count += count_dfs_links(ctx, i);
1563 if (jn_count == 0) {
1566 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1570 for(i=0; i < sharecount; i++) {
1571 if (*p_num_jn >= jn_count) {
1574 if(lp_msdfs_root(i)) {
1575 *p_num_jn += form_junctions(ctx, i,
1577 jn_count - *p_num_jn);
1583 /******************************************************************************
1584 Core function to resolve a dfs pathname.
1585 ******************************************************************************/
1587 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1588 connection_struct *conn,
1590 const char *name_in,
1593 NTSTATUS status = NT_STATUS_OK;
1595 if (dfs_pathnames) {
1596 status = dfs_redirect(ctx,
1604 * Cheat and just return a copy of the in ptr.
1605 * Once srvstr_get_path() uses talloc it'll
1606 * be a talloced ptr anyway.
1608 *pp_name_out = CONST_DISCARD(char *,name_in);
1613 /******************************************************************************
1614 Core function to resolve a dfs pathname possibly containing a wildcard.
1615 This function is identical to the above except for the bool param to
1616 dfs_redirect but I need this to be separate so it's really clear when
1617 we're allowing wildcards and when we're not. JRA.
1618 ******************************************************************************/
1620 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1621 connection_struct *conn,
1623 const char *name_in,
1625 bool *ppath_contains_wcard)
1627 NTSTATUS status = NT_STATUS_OK;
1628 if (dfs_pathnames) {
1629 status = dfs_redirect(ctx,
1634 ppath_contains_wcard);
1637 * Cheat and just return a copy of the in ptr.
1638 * Once srvstr_get_path() uses talloc it'll
1639 * be a talloced ptr anyway.
1641 *pp_name_out = CONST_DISCARD(char *,name_in);