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 /* If we got a hostname, is it ours (or an IP address) ? */
131 if (!is_myname_or_ipaddr(pdp->hostname)) {
134 DEBUG(10,("parse_dfs_path: hostname %s isn't ours. "
135 "Try local path from path %s\n",
136 pdp->hostname, temp));
138 * Possibly client sent a local path by mistake.
139 * Try and convert to a local path.
142 pdp->hostname = eos_ptr; /* "" */
143 pdp->servicename = eos_ptr; /* "" */
146 DEBUG(10,("parse_dfs_path: trying to convert %s "
152 /* Parse out servicename. */
154 p = strchr_m(temp,sepchar);
156 pdp->servicename = temp;
157 pdp->reqpath = eos_ptr; /* "" */
161 pdp->servicename = temp;
162 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
168 *ppath_contains_wcard = False;
172 /* Rest is reqpath. */
173 if (pdp->posix_path) {
174 status = check_path_syntax_posix(pdp->reqpath);
177 status = check_path_syntax_wcard(pdp->reqpath,
178 ppath_contains_wcard);
180 status = check_path_syntax(pdp->reqpath);
184 if (!NT_STATUS_IS_OK(status)) {
185 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
186 p, nt_errstr(status) ));
190 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
194 /********************************************************
195 Fake up a connection struct for the VFS layer.
196 Note this CHANGES CWD !!!! JRA.
197 *********************************************************/
199 static NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
200 connection_struct *conn,
208 connpath = talloc_strdup(ctx, path);
210 return NT_STATUS_NO_MEMORY;
212 connpath = talloc_string_sub(ctx,
215 lp_servicename(snum));
217 return NT_STATUS_NO_MEMORY;
220 /* needed for smbd_vfs_init() */
222 if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) {
223 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
224 return NT_STATUS_NO_MEMORY;
227 if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx,
228 struct share_params))) {
229 DEBUG(0, ("TALLOC failed\n"));
230 return NT_STATUS_NO_MEMORY;
233 conn->params->service = snum;
235 set_conn_connectpath(conn, connpath);
237 if (!smbd_vfs_init(conn)) {
238 NTSTATUS status = map_nt_error_from_unix(errno);
239 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
240 conn_free_internal(conn);
245 * Windows seems to insist on doing trans2getdfsreferral() calls on
246 * the IPC$ share as the anonymous user. If we try to chdir as that
247 * user we will fail.... WTF ? JRA.
250 if (vfs_ChDir(conn,conn->connectpath) != 0) {
251 NTSTATUS status = map_nt_error_from_unix(errno);
252 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
254 conn->connectpath, strerror(errno) ));
255 conn_free_internal(conn);
262 /**********************************************************************
263 Parse the contents of a symlink to verify if it is an msdfs referral
264 A valid referral is of the form:
266 msdfs:server1\share1,server2\share2
267 msdfs:server1\share1\pathname,server2\share2\pathname
268 msdfs:server1/share1,server2/share2
269 msdfs:server1/share1/pathname,server2/share2/pathname.
271 Note that the alternate paths returned here must be of the canonicalized
275 \server\share\path\to\file,
277 even in posix path mode. This is because we have no knowledge if the
278 server we're referring to understands posix paths.
279 **********************************************************************/
281 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
283 struct referral **preflist,
288 char **alt_path = NULL;
290 struct referral *reflist;
292 temp = talloc_strdup(ctx, target);
296 prot = strtok(temp,":");
298 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
302 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
307 /* parse out the alternate paths */
308 while((count<MAX_REFERRAL_COUNT) &&
309 ((alt_path[count] = strtok(NULL,",")) != NULL)) {
313 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
316 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
317 struct referral, count);
318 if(reflist == NULL) {
319 TALLOC_FREE(alt_path);
323 reflist = *preflist = NULL;
326 for(i=0;i<count;i++) {
329 /* Canonicalize link target.
330 * Replace all /'s in the path by a \ */
331 string_replace(alt_path[i], '/', '\\');
333 /* Remove leading '\\'s */
335 while (*p && (*p == '\\')) {
339 reflist[i].alternate_path = talloc_asprintf(ctx,
342 if (!reflist[i].alternate_path) {
346 reflist[i].proximity = 0;
347 reflist[i].ttl = REFERRAL_TTL;
348 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
349 reflist[i].alternate_path));
353 TALLOC_FREE(alt_path);
357 /**********************************************************************
358 Returns true if the unix path is a valid msdfs symlink and also
359 returns the target string from inside the link.
360 **********************************************************************/
362 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
363 connection_struct *conn,
365 char **pp_link_target,
366 SMB_STRUCT_STAT *sbufp)
369 int referral_len = 0;
370 char link_target_buf[7];
372 char *link_target = NULL;
374 if (pp_link_target) {
376 link_target = TALLOC_ARRAY(ctx, char, bufsize);
380 *pp_link_target = link_target;
382 bufsize = sizeof(link_target_buf);
383 link_target = link_target_buf;
390 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
391 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
396 if (!S_ISLNK(sbufp->st_mode)) {
397 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
402 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
403 if (referral_len == -1) {
404 DEBUG(0,("is_msdfs_link_read_target: Error reading "
405 "msdfs link %s: %s\n",
406 path, strerror(errno)));
409 link_target[referral_len] = '\0';
411 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
414 if (!strnequal(link_target, "msdfs:", 6)) {
421 if (link_target != link_target_buf) {
422 TALLOC_FREE(link_target);
427 /**********************************************************************
428 Returns true if the unix path is a valid msdfs symlink.
429 **********************************************************************/
431 bool is_msdfs_link(connection_struct *conn,
433 SMB_STRUCT_STAT *sbufp)
435 return is_msdfs_link_internal(talloc_tos(),
442 /*****************************************************************
443 Used by other functions to decide if a dfs path is remote,
444 and to get the list of referred locations for that remote path.
446 search_flag: For findfirsts, dfs links themselves are not
447 redirected, but paths beyond the links are. For normal smb calls,
448 even dfs links need to be redirected.
450 consumedcntp: how much of the dfs path is being redirected. the client
451 should try the remaining path on the redirected server.
453 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
454 link redirect are in targetpath.
455 *****************************************************************/
457 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
458 connection_struct *conn,
459 const char *dfspath, /* Incoming complete dfs path */
460 const struct dfs_path *pdp, /* Parsed out
461 server+share+extrapath. */
462 bool search_flag, /* Called from a findfirst ? */
464 char **pp_targetpath)
468 SMB_STRUCT_STAT sbuf;
470 char *localpath = NULL;
471 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
474 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
475 conn->connectpath, pdp->reqpath));
478 * Note the unix path conversion here we're doing we can
479 * throw away. We're looking for a symlink for a dfs
480 * resolution, if we don't find it we'll do another
481 * unix_convert later in the codepath.
482 * If we needed to remember what we'd resolved in
483 * dp->reqpath (as the original code did) we'd
484 * copy (localhost, dp->reqpath) on any code
485 * path below that returns True - but I don't
486 * think this is needed. JRA.
489 status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
491 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
492 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
496 /* Optimization - check if we can redirect the whole path. */
498 if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
500 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
501 "for dfs link %s.\n", dfspath));
505 DEBUG(6,("dfs_path_lookup: %s resolves to a "
506 "valid dfs link %s.\n", dfspath,
507 pp_targetpath ? *pp_targetpath : ""));
510 *consumedcntp = strlen(dfspath);
512 return NT_STATUS_PATH_NOT_COVERED;
515 /* Prepare to test only for '/' components in the given path,
516 * so if a Windows path replace all '\\' characters with '/'.
517 * For a POSIX DFS path we know all separators are already '/'. */
519 canon_dfspath = talloc_strdup(ctx, dfspath);
520 if (!canon_dfspath) {
521 return NT_STATUS_NO_MEMORY;
523 if (!pdp->posix_path) {
524 string_replace(canon_dfspath, '\\', '/');
528 * localpath comes out of unix_convert, so it has
529 * no trailing backslash. Make sure that canon_dfspath hasn't either.
530 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
533 trim_char(canon_dfspath,0,'/');
536 * Redirect if any component in the path is a link.
537 * We do this by walking backwards through the
538 * local path, chopping off the last component
539 * in both the local path and the canonicalized
540 * DFS path. If we hit a DFS link then we're done.
543 p = strrchr_m(localpath, '/');
545 q = strrchr_m(canon_dfspath, '/');
554 if (is_msdfs_link_internal(ctx, conn,
555 localpath, pp_targetpath, NULL)) {
556 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
557 "parent %s is dfs link\n", dfspath, localpath));
560 *consumedcntp = strlen(canon_dfspath);
561 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
567 return NT_STATUS_PATH_NOT_COVERED;
570 /* Step back on the filesystem. */
571 p = strrchr_m(localpath, '/');
574 /* And in the canonicalized dfs path. */
575 q = strrchr_m(canon_dfspath, '/');
582 /*****************************************************************
583 Decides if a dfs pathname should be redirected or not.
584 If not, the pathname is converted to a tcon-relative local unix path
586 search_wcard_flag: this flag performs 2 functions both related
587 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
590 This function can return NT_STATUS_OK, meaning use the returned path as-is
591 (mapped into a local path).
592 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
593 any other NT_STATUS error which is a genuine error to be
594 returned to the client.
595 *****************************************************************/
597 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
598 connection_struct *conn,
600 bool search_wcard_flag,
602 bool *ppath_contains_wcard)
605 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
608 return NT_STATUS_NO_MEMORY;
611 status = parse_dfs_path(path_in, search_wcard_flag, pdp,
612 ppath_contains_wcard);
613 if (!NT_STATUS_IS_OK(status)) {
618 if (pdp->reqpath[0] == '\0') {
620 *pp_path_out = talloc_strdup(ctx, "");
622 return NT_STATUS_NO_MEMORY;
624 DEBUG(5,("dfs_redirect: self-referral.\n"));
628 /* If dfs pathname for a non-dfs share, convert to tcon-relative
629 path and return OK */
631 if (!lp_msdfs_root(SNUM(conn))) {
632 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
635 return NT_STATUS_NO_MEMORY;
640 /* If it looked like a local path (zero hostname/servicename)
641 * just treat as a tcon-relative path. */
643 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
644 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
647 return NT_STATUS_NO_MEMORY;
652 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
653 || (strequal(pdp->servicename, HOMES_NAME)
654 && strequal(lp_servicename(SNUM(conn)),
655 get_current_username()) )) ) {
657 /* The given sharename doesn't match this connection. */
660 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
663 status = dfs_path_lookup(ctx, conn, path_in, pdp,
664 search_wcard_flag, NULL, NULL);
665 if (!NT_STATUS_IS_OK(status)) {
666 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
667 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
669 DEBUG(10,("dfs_redirect: dfs_path_lookup "
670 "failed for %s with %s\n",
671 path_in, nt_errstr(status) ));
676 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
678 /* Form non-dfs tcon-relative path */
679 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
682 return NT_STATUS_NO_MEMORY;
685 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
692 /**********************************************************************
693 Return a self referral.
694 **********************************************************************/
696 static NTSTATUS self_ref(TALLOC_CTX *ctx,
697 const char *dfs_path,
698 struct junction_map *jucn,
700 bool *self_referralp)
702 struct referral *ref;
704 *self_referralp = True;
706 jucn->referral_count = 1;
707 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
708 return NT_STATUS_NO_MEMORY;
711 ref->alternate_path = talloc_strdup(ctx, dfs_path);
712 if (!ref->alternate_path) {
713 return NT_STATUS_NO_MEMORY;
716 ref->ttl = REFERRAL_TTL;
717 jucn->referral_list = ref;
718 *consumedcntp = strlen(dfs_path);
722 /**********************************************************************
723 Gets valid referrals for a dfs path and fills up the
724 junction_map structure.
725 **********************************************************************/
727 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
728 const char *dfs_path,
729 struct junction_map *jucn,
731 bool *self_referralp)
733 struct connection_struct conns;
734 struct connection_struct *conn = &conns;
735 char *targetpath = NULL;
737 NTSTATUS status = NT_STATUS_NOT_FOUND;
739 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
742 return NT_STATUS_NO_MEMORY;
746 *self_referralp = False;
748 status = parse_dfs_path(dfs_path, False, pdp, &dummy);
749 if (!NT_STATUS_IS_OK(status)) {
753 /* Verify hostname in path */
754 if (!is_myname_or_ipaddr(pdp->hostname)) {
755 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
756 pdp->hostname, dfs_path));
758 return NT_STATUS_NOT_FOUND;
761 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
762 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
763 if (!jucn->service_name || !jucn->volume_name) {
765 return NT_STATUS_NO_MEMORY;
768 /* Verify the share is a dfs root */
769 snum = lp_servicenumber(jucn->service_name);
771 fstring service_name;
772 fstrcpy(service_name, jucn->service_name);
773 if ((snum = find_service(service_name)) < 0) {
774 return NT_STATUS_NOT_FOUND;
776 TALLOC_FREE(jucn->service_name);
777 jucn->service_name = talloc_strdup(ctx, service_name);
778 if (!jucn->service_name) {
780 return NT_STATUS_NO_MEMORY;
784 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
785 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
787 pdp->servicename, dfs_path));
789 return NT_STATUS_NOT_FOUND;
793 * Self referrals are tested with a anonymous IPC connection and
794 * a GET_DFS_REFERRAL call to \\server\share. (which means
795 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
796 * into the directory and will fail if it cannot (as the anonymous
797 * user). Cope with this.
800 if (pdp->reqpath[0] == '\0') {
802 struct referral *ref;
804 if (*lp_msdfs_proxy(snum) == '\0') {
814 * It's an msdfs proxy share. Redirect to
815 * the configured target share.
818 jucn->referral_count = 1;
819 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
821 return NT_STATUS_NO_MEMORY;
824 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
826 return NT_STATUS_NO_MEMORY;
829 trim_string(tmp, "\\", 0);
831 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
834 if (!ref->alternate_path) {
836 return NT_STATUS_NO_MEMORY;
839 if (pdp->reqpath[0] != '\0') {
840 ref->alternate_path = talloc_asprintf_append(
844 if (!ref->alternate_path) {
846 return NT_STATUS_NO_MEMORY;
850 ref->ttl = REFERRAL_TTL;
851 jucn->referral_list = ref;
852 *consumedcntp = strlen(dfs_path);
857 status = create_conn_struct(ctx, conn, snum, lp_pathname(snum));
858 if (!NT_STATUS_IS_OK(status)) {
863 /* If this is a DFS path dfs_lookup should return
864 * NT_STATUS_PATH_NOT_COVERED. */
866 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
867 False, consumedcntp, &targetpath);
869 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
870 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
872 conn_free_internal(conn);
877 /* We know this is a valid dfs link. Parse the targetpath. */
878 if (!parse_msdfs_symlink(ctx, targetpath,
879 &jucn->referral_list,
880 &jucn->referral_count)) {
881 DEBUG(3,("get_referred_path: failed to parse symlink "
882 "target %s\n", targetpath ));
883 conn_free_internal(conn);
885 return NT_STATUS_NOT_FOUND;
888 conn_free_internal(conn);
893 static int setup_ver2_dfs_referral(const char *pathname,
895 struct junction_map *junction,
899 char* pdata = *ppdata;
901 smb_ucs2_t *uni_requestedpath = NULL;
902 int uni_reqpathoffset1,uni_reqpathoffset2;
904 int requestedpathlen=0;
909 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
911 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
912 &uni_requestedpath, pathname);
913 if (uni_requestedpath == NULL || requestedpathlen == 0) {
918 dump_data(0, (unsigned char *)uni_requestedpath,
922 DEBUG(10,("ref count = %u\n",junction->referral_count));
924 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
925 VERSION2_REFERRAL_SIZE * junction->referral_count;
927 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
929 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
931 reply_size = REFERRAL_HEADER_SIZE +
932 VERSION2_REFERRAL_SIZE*junction->referral_count +
933 2 * requestedpathlen;
934 DEBUG(10,("reply_size: %u\n",reply_size));
936 /* add up the unicode lengths of all the referral paths */
937 for(i=0;i<junction->referral_count;i++) {
938 DEBUG(10,("referral %u : %s\n",
940 junction->referral_list[i].alternate_path));
942 (strlen(junction->referral_list[i].alternate_path)+1)*2;
945 DEBUG(10,("reply_size = %u\n",reply_size));
946 /* add the unexplained 0x16 bytes */
949 pdata = (char *)SMB_REALLOC(pdata,reply_size);
951 DEBUG(0,("Realloc failed!\n"));
956 /* copy in the dfs requested paths.. required for offset calculations */
957 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
958 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
960 /* create the header */
961 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
962 /* number of referral in this pkt */
963 SSVAL(pdata,2,junction->referral_count);
965 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
967 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
971 /* add the referral elements */
972 for(i=0;i<junction->referral_count;i++) {
973 struct referral* ref = &junction->referral_list[i];
976 SSVAL(pdata,offset,2); /* version 2 */
977 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
979 SSVAL(pdata,offset+4,1);
981 SSVAL(pdata,offset+4,0);
984 /* ref_flags :use path_consumed bytes? */
985 SSVAL(pdata,offset+6,0);
986 SIVAL(pdata,offset+8,ref->proximity);
987 SIVAL(pdata,offset+12,ref->ttl);
989 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
990 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
991 /* copy referred path into current offset */
992 unilen = rpcstr_push(pdata+uni_curroffset,
994 reply_size - uni_curroffset,
997 SSVAL(pdata,offset+20,uni_curroffset-offset);
999 uni_curroffset += unilen;
1000 offset += VERSION2_REFERRAL_SIZE;
1002 /* add in the unexplained 22 (0x16) bytes at the end */
1003 memset(pdata+uni_curroffset,'\0',0x16);
1007 static int setup_ver3_dfs_referral(const char *pathname,
1009 struct junction_map *junction,
1013 char *pdata = *ppdata;
1015 smb_ucs2_t *uni_reqpath = NULL;
1016 int uni_reqpathoffset1, uni_reqpathoffset2;
1023 DEBUG(10,("setting up version3 referral\n"));
1025 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1026 if (uni_reqpath == NULL || reqpathlen == 0) {
1031 dump_data(0, (unsigned char *)uni_reqpath,
1035 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1036 VERSION3_REFERRAL_SIZE * junction->referral_count;
1037 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1038 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1040 for(i=0;i<junction->referral_count;i++) {
1041 DEBUG(10,("referral %u : %s\n",
1043 junction->referral_list[i].alternate_path));
1045 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1048 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1050 DEBUG(0,("version3 referral setup:"
1051 "malloc failed for Realloc!\n"));
1056 /* create the header */
1057 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
1058 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1060 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1062 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1065 /* copy in the reqpaths */
1066 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1067 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1070 for(i=0;i<junction->referral_count;i++) {
1071 struct referral* ref = &(junction->referral_list[i]);
1074 SSVAL(pdata,offset,3); /* version 3 */
1075 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1077 SSVAL(pdata,offset+4,1);
1079 SSVAL(pdata,offset+4,0);
1082 /* ref_flags :use path_consumed bytes? */
1083 SSVAL(pdata,offset+6,0);
1084 SIVAL(pdata,offset+8,ref->ttl);
1086 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1087 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1088 /* copy referred path into current offset */
1089 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1090 reply_size - uni_curroffset,
1091 STR_UNICODE | STR_TERMINATE);
1092 SSVAL(pdata,offset+16,uni_curroffset-offset);
1093 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1094 memset(pdata+offset+18,'\0',16);
1096 uni_curroffset += unilen;
1097 offset += VERSION3_REFERRAL_SIZE;
1102 /******************************************************************
1103 Set up the DFS referral for the dfs pathname. This call returns
1104 the amount of the path covered by this server, and where the
1105 client should be redirected to. This is the meat of the
1106 TRANS2_GET_DFS_REFERRAL call.
1107 ******************************************************************/
1109 int setup_dfs_referral(connection_struct *orig_conn,
1110 const char *dfs_path,
1111 int max_referral_level,
1112 char **ppdata, NTSTATUS *pstatus)
1114 struct junction_map *junction = NULL;
1115 int consumedcnt = 0;
1116 bool self_referral = False;
1118 char *pathnamep = NULL;
1119 char *local_dfs_path = NULL;
1122 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1123 *pstatus = NT_STATUS_NO_MEMORY;
1127 /* get the junction entry */
1129 talloc_destroy(ctx);
1130 *pstatus = NT_STATUS_NOT_FOUND;
1135 * Trim pathname sent by client so it begins with only one backslash.
1136 * Two backslashes confuse some dfs clients
1139 local_dfs_path = talloc_strdup(ctx,dfs_path);
1140 if (!local_dfs_path) {
1141 *pstatus = NT_STATUS_NO_MEMORY;
1142 talloc_destroy(ctx);
1145 pathnamep = local_dfs_path;
1146 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1147 IS_DIRECTORY_SEP(pathnamep[1])) {
1151 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1153 *pstatus = NT_STATUS_NO_MEMORY;
1154 talloc_destroy(ctx);
1158 /* The following call can change cwd. */
1159 *pstatus = get_referred_path(ctx, pathnamep, junction,
1160 &consumedcnt, &self_referral);
1161 if (!NT_STATUS_IS_OK(*pstatus)) {
1162 vfs_ChDir(orig_conn,orig_conn->connectpath);
1163 talloc_destroy(ctx);
1166 vfs_ChDir(orig_conn,orig_conn->connectpath);
1168 if (!self_referral) {
1169 pathnamep[consumedcnt] = '\0';
1171 if( DEBUGLVL( 3 ) ) {
1173 dbgtext("setup_dfs_referral: Path %s to "
1174 "alternate path(s):",
1176 for(i=0;i<junction->referral_count;i++)
1178 junction->referral_list[i].alternate_path);
1183 /* create the referral depeding on version */
1184 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1186 if (max_referral_level < 2) {
1187 max_referral_level = 2;
1189 if (max_referral_level > 3) {
1190 max_referral_level = 3;
1193 switch(max_referral_level) {
1195 reply_size = setup_ver2_dfs_referral(pathnamep,
1197 consumedcnt, self_referral);
1200 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1201 junction, consumedcnt, self_referral);
1204 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1206 max_referral_level));
1207 talloc_destroy(ctx);
1208 *pstatus = NT_STATUS_INVALID_LEVEL;
1213 DEBUGADD(0,("DFS Referral pdata:\n"));
1214 dump_data(0,(uint8 *)*ppdata,reply_size);
1217 talloc_destroy(ctx);
1218 *pstatus = NT_STATUS_OK;
1222 /**********************************************************************
1223 The following functions are called by the NETDFS RPC pipe functions
1224 **********************************************************************/
1226 /*********************************************************************
1227 Creates a junction structure from a DFS pathname
1228 **********************************************************************/
1230 bool create_junction(TALLOC_CTX *ctx,
1231 const char *dfs_path,
1232 struct junction_map *jucn)
1236 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1242 status = parse_dfs_path(dfs_path, False, pdp, &dummy);
1243 if (!NT_STATUS_IS_OK(status)) {
1247 /* check if path is dfs : validate first token */
1248 if (!is_myname_or_ipaddr(pdp->hostname)) {
1249 DEBUG(4,("create_junction: Invalid hostname %s "
1251 pdp->hostname, dfs_path));
1256 /* Check for a non-DFS share */
1257 snum = lp_servicenumber(pdp->servicename);
1259 if(snum < 0 || !lp_msdfs_root(snum)) {
1260 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1266 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1267 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1268 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1271 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1277 /**********************************************************************
1278 Forms a valid Unix pathname from the junction
1279 **********************************************************************/
1281 static bool junction_to_local_path(const struct junction_map *jucn,
1283 connection_struct *conn_out)
1287 snum = lp_servicenumber(jucn->service_name);
1291 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1293 lp_pathname(snum)))) {
1297 *pp_path_out = talloc_asprintf(conn_out->mem_ctx,
1301 if (!*pp_path_out) {
1307 bool create_msdfs_link(const struct junction_map *jucn,
1311 char *msdfs_link = NULL;
1312 connection_struct conns;
1313 connection_struct *conn = &conns;
1315 bool insert_comma = False;
1320 if(!junction_to_local_path(jucn, &path, conn)) {
1324 /* Form the msdfs_link contents */
1325 msdfs_link = talloc_strdup(conn->mem_ctx, "msdfs:");
1329 for(i=0; i<jucn->referral_count; i++) {
1330 char *refpath = jucn->referral_list[i].alternate_path;
1332 /* Alternate paths always use Windows separators. */
1333 trim_char(refpath, '\\', '\\');
1334 if(*refpath == '\0') {
1336 insert_comma = False;
1340 if (i > 0 && insert_comma) {
1341 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1345 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1353 if (!insert_comma) {
1354 insert_comma = True;
1358 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1362 if(SMB_VFS_UNLINK(conn,path)!=0) {
1367 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1368 DEBUG(1,("create_msdfs_link: symlink failed "
1369 "%s -> %s\nError: %s\n",
1370 path, msdfs_link, strerror(errno)));
1378 conn_free_internal(conn);
1382 bool remove_msdfs_link(const struct junction_map *jucn)
1385 connection_struct conns;
1386 connection_struct *conn = &conns;
1391 if( junction_to_local_path(jucn, &path, conn) ) {
1392 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1397 conn_free_internal(conn);
1401 /*********************************************************************
1402 Return the number of DFS links at the root of this share.
1403 *********************************************************************/
1405 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1408 SMB_STRUCT_DIR *dirp = NULL;
1410 const char *connect_path = lp_pathname(snum);
1411 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1412 connection_struct conn;
1416 if(*connect_path == '\0') {
1421 * Fake up a connection struct for the VFS layer.
1424 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1425 &conn, snum, connect_path))) {
1429 /* Count a link for the msdfs root - convention */
1432 /* No more links if this is an msdfs proxy. */
1433 if (*msdfs_proxy != '\0') {
1437 /* Now enumerate all dfs links */
1438 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1443 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1444 if (is_msdfs_link(&conn,
1451 SMB_VFS_CLOSEDIR(&conn,dirp);
1455 conn_free_internal(&conn);
1459 /*********************************************************************
1460 *********************************************************************/
1462 static int form_junctions(TALLOC_CTX *ctx,
1464 struct junction_map *jucn,
1468 SMB_STRUCT_DIR *dirp = NULL;
1470 const char *connect_path = lp_pathname(snum);
1471 char *service_name = lp_servicename(snum);
1472 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1473 connection_struct conn;
1474 struct referral *ref = NULL;
1478 if (jn_remain == 0) {
1482 if(*connect_path == '\0') {
1487 * Fake up a connection struct for the VFS layer.
1490 if (!NT_STATUS_IS_OK(create_conn_struct(ctx, &conn, snum, connect_path))) {
1494 /* form a junction for the msdfs root - convention
1495 DO NOT REMOVE THIS: NT clients will not work with us
1496 if this is not present
1498 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1499 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1500 if (!jucn[cnt].service_name || jucn[cnt].volume_name) {
1503 jucn[cnt].referral_count = 1;
1505 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1506 if (jucn[cnt].referral_list == NULL) {
1511 ref->ttl = REFERRAL_TTL;
1512 if (*msdfs_proxy != '\0') {
1513 ref->alternate_path = talloc_strdup(ctx,
1516 ref->alternate_path = talloc_asprintf(ctx,
1518 get_local_machine_name(),
1522 if (!ref->alternate_path) {
1527 /* Don't enumerate if we're an msdfs proxy. */
1528 if (*msdfs_proxy != '\0') {
1532 /* Now enumerate all dfs links */
1533 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1538 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1539 char *link_target = NULL;
1540 if (cnt >= jn_remain) {
1541 SMB_VFS_CLOSEDIR(&conn,dirp);
1542 DEBUG(2, ("form_junctions: ran out of MSDFS "
1546 if (is_msdfs_link_internal(ctx,
1548 dname, &link_target,
1550 if (parse_msdfs_symlink(ctx,
1552 &jucn[cnt].referral_list,
1553 &jucn[cnt].referral_count)) {
1555 jucn[cnt].service_name = talloc_strdup(ctx,
1557 jucn[cnt].volume_name = talloc_strdup(ctx,
1559 if (!jucn[cnt].service_name ||
1560 !jucn[cnt].volume_name) {
1571 SMB_VFS_CLOSEDIR(&conn,dirp);
1574 conn_free_internal(&conn);
1578 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1580 struct junction_map *jn = NULL;
1582 size_t jn_count = 0;
1586 if(!lp_host_msdfs()) {
1590 /* Ensure all the usershares are loaded. */
1592 load_registry_shares();
1593 sharecount = load_usershare_shares();
1596 for(i=0;i < sharecount;i++) {
1597 if(lp_msdfs_root(i)) {
1598 jn_count += count_dfs_links(ctx, i);
1601 if (jn_count == 0) {
1604 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1608 for(i=0; i < sharecount; i++) {
1609 if (*p_num_jn >= jn_count) {
1612 if(lp_msdfs_root(i)) {
1613 *p_num_jn += form_junctions(ctx, i,
1615 jn_count - *p_num_jn);
1621 /******************************************************************************
1622 Core function to resolve a dfs pathname.
1623 ******************************************************************************/
1625 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1626 connection_struct *conn,
1628 const char *name_in,
1631 NTSTATUS status = NT_STATUS_OK;
1633 if (dfs_pathnames) {
1634 status = dfs_redirect(ctx,
1642 * Cheat and just return a copy of the in ptr.
1643 * Once srvstr_get_path() uses talloc it'll
1644 * be a talloced ptr anyway.
1646 *pp_name_out = CONST_DISCARD(char *,name_in);
1651 /******************************************************************************
1652 Core function to resolve a dfs pathname possibly containing a wildcard.
1653 This function is identical to the above except for the bool param to
1654 dfs_redirect but I need this to be separate so it's really clear when
1655 we're allowing wildcards and when we're not. JRA.
1656 ******************************************************************************/
1658 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1659 connection_struct *conn,
1661 const char *name_in,
1663 bool *ppath_contains_wcard)
1665 NTSTATUS status = NT_STATUS_OK;
1666 if (dfs_pathnames) {
1667 status = dfs_redirect(ctx,
1672 ppath_contains_wcard);
1675 * Cheat and just return a copy of the in ptr.
1676 * Once srvstr_get_path() uses talloc it'll
1677 * be a talloced ptr anyway.
1679 *pp_name_out = CONST_DISCARD(char *,name_in);