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 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
676 || (strequal(pdp->servicename, HOMES_NAME)
677 && strequal(lp_servicename(SNUM(conn)),
678 conn->server_info->sanitized_username) )) ) {
680 /* The given sharename doesn't match this connection. */
683 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
686 status = dfs_path_lookup(ctx, conn, path_in, pdp,
687 search_wcard_flag, NULL, NULL);
688 if (!NT_STATUS_IS_OK(status)) {
689 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
690 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
692 DEBUG(10,("dfs_redirect: dfs_path_lookup "
693 "failed for %s with %s\n",
694 path_in, nt_errstr(status) ));
699 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
701 /* Form non-dfs tcon-relative path */
702 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
705 return NT_STATUS_NO_MEMORY;
708 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
715 /**********************************************************************
716 Return a self referral.
717 **********************************************************************/
719 static NTSTATUS self_ref(TALLOC_CTX *ctx,
720 const char *dfs_path,
721 struct junction_map *jucn,
723 bool *self_referralp)
725 struct referral *ref;
727 *self_referralp = True;
729 jucn->referral_count = 1;
730 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
731 return NT_STATUS_NO_MEMORY;
734 ref->alternate_path = talloc_strdup(ctx, dfs_path);
735 if (!ref->alternate_path) {
736 return NT_STATUS_NO_MEMORY;
739 ref->ttl = REFERRAL_TTL;
740 jucn->referral_list = ref;
741 *consumedcntp = strlen(dfs_path);
745 /**********************************************************************
746 Gets valid referrals for a dfs path and fills up the
747 junction_map structure.
748 **********************************************************************/
750 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
751 const char *dfs_path,
752 struct junction_map *jucn,
754 bool *self_referralp)
756 struct connection_struct *conn;
757 char *targetpath = NULL;
759 NTSTATUS status = NT_STATUS_NOT_FOUND;
761 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
764 return NT_STATUS_NO_MEMORY;
767 *self_referralp = False;
769 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
770 if (!NT_STATUS_IS_OK(status)) {
774 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
775 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
776 if (!jucn->service_name || !jucn->volume_name) {
778 return NT_STATUS_NO_MEMORY;
781 /* Verify the share is a dfs root */
782 snum = lp_servicenumber(jucn->service_name);
784 fstring service_name;
785 fstrcpy(service_name, jucn->service_name);
786 if ((snum = find_service(service_name)) < 0) {
787 return NT_STATUS_NOT_FOUND;
789 TALLOC_FREE(jucn->service_name);
790 jucn->service_name = talloc_strdup(ctx, service_name);
791 if (!jucn->service_name) {
793 return NT_STATUS_NO_MEMORY;
797 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
798 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
800 pdp->servicename, dfs_path));
802 return NT_STATUS_NOT_FOUND;
806 * Self referrals are tested with a anonymous IPC connection and
807 * a GET_DFS_REFERRAL call to \\server\share. (which means
808 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
809 * into the directory and will fail if it cannot (as the anonymous
810 * user). Cope with this.
813 if (pdp->reqpath[0] == '\0') {
815 struct referral *ref;
817 if (*lp_msdfs_proxy(snum) == '\0') {
827 * It's an msdfs proxy share. Redirect to
828 * the configured target share.
831 jucn->referral_count = 1;
832 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
834 return NT_STATUS_NO_MEMORY;
837 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
839 return NT_STATUS_NO_MEMORY;
842 trim_string(tmp, "\\", 0);
844 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
847 if (!ref->alternate_path) {
849 return NT_STATUS_NO_MEMORY;
852 if (pdp->reqpath[0] != '\0') {
853 ref->alternate_path = talloc_asprintf_append(
857 if (!ref->alternate_path) {
859 return NT_STATUS_NO_MEMORY;
863 ref->ttl = REFERRAL_TTL;
864 jucn->referral_list = ref;
865 *consumedcntp = strlen(dfs_path);
870 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum));
871 if (!NT_STATUS_IS_OK(status)) {
876 /* If this is a DFS path dfs_lookup should return
877 * NT_STATUS_PATH_NOT_COVERED. */
879 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
880 False, consumedcntp, &targetpath);
882 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
883 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
885 conn_free_internal(conn);
890 /* We know this is a valid dfs link. Parse the targetpath. */
891 if (!parse_msdfs_symlink(ctx, targetpath,
892 &jucn->referral_list,
893 &jucn->referral_count)) {
894 DEBUG(3,("get_referred_path: failed to parse symlink "
895 "target %s\n", targetpath ));
896 conn_free_internal(conn);
898 return NT_STATUS_NOT_FOUND;
901 conn_free_internal(conn);
906 static int setup_ver2_dfs_referral(const char *pathname,
908 struct junction_map *junction,
912 char* pdata = *ppdata;
914 smb_ucs2_t *uni_requestedpath = NULL;
915 int uni_reqpathoffset1,uni_reqpathoffset2;
917 int requestedpathlen=0;
922 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
924 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
925 &uni_requestedpath, pathname);
926 if (uni_requestedpath == NULL || requestedpathlen == 0) {
931 dump_data(0, (unsigned char *)uni_requestedpath,
935 DEBUG(10,("ref count = %u\n",junction->referral_count));
937 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
938 VERSION2_REFERRAL_SIZE * junction->referral_count;
940 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
942 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
944 reply_size = REFERRAL_HEADER_SIZE +
945 VERSION2_REFERRAL_SIZE*junction->referral_count +
946 2 * requestedpathlen;
947 DEBUG(10,("reply_size: %u\n",reply_size));
949 /* add up the unicode lengths of all the referral paths */
950 for(i=0;i<junction->referral_count;i++) {
951 DEBUG(10,("referral %u : %s\n",
953 junction->referral_list[i].alternate_path));
955 (strlen(junction->referral_list[i].alternate_path)+1)*2;
958 DEBUG(10,("reply_size = %u\n",reply_size));
959 /* add the unexplained 0x16 bytes */
962 pdata = (char *)SMB_REALLOC(pdata,reply_size);
964 DEBUG(0,("Realloc failed!\n"));
969 /* copy in the dfs requested paths.. required for offset calculations */
970 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
971 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
973 /* create the header */
974 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
975 /* number of referral in this pkt */
976 SSVAL(pdata,2,junction->referral_count);
978 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
980 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
984 /* add the referral elements */
985 for(i=0;i<junction->referral_count;i++) {
986 struct referral* ref = &junction->referral_list[i];
989 SSVAL(pdata,offset,2); /* version 2 */
990 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
992 SSVAL(pdata,offset+4,1);
994 SSVAL(pdata,offset+4,0);
997 /* ref_flags :use path_consumed bytes? */
998 SSVAL(pdata,offset+6,0);
999 SIVAL(pdata,offset+8,ref->proximity);
1000 SIVAL(pdata,offset+12,ref->ttl);
1002 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1003 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1004 /* copy referred path into current offset */
1005 unilen = rpcstr_push(pdata+uni_curroffset,
1006 ref->alternate_path,
1007 reply_size - uni_curroffset,
1010 SSVAL(pdata,offset+20,uni_curroffset-offset);
1012 uni_curroffset += unilen;
1013 offset += VERSION2_REFERRAL_SIZE;
1015 /* add in the unexplained 22 (0x16) bytes at the end */
1016 memset(pdata+uni_curroffset,'\0',0x16);
1020 static int setup_ver3_dfs_referral(const char *pathname,
1022 struct junction_map *junction,
1026 char *pdata = *ppdata;
1028 smb_ucs2_t *uni_reqpath = NULL;
1029 int uni_reqpathoffset1, uni_reqpathoffset2;
1036 DEBUG(10,("setting up version3 referral\n"));
1038 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1039 if (uni_reqpath == NULL || reqpathlen == 0) {
1044 dump_data(0, (unsigned char *)uni_reqpath,
1048 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1049 VERSION3_REFERRAL_SIZE * junction->referral_count;
1050 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1051 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1053 for(i=0;i<junction->referral_count;i++) {
1054 DEBUG(10,("referral %u : %s\n",
1056 junction->referral_list[i].alternate_path));
1058 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1061 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1063 DEBUG(0,("version3 referral setup:"
1064 "malloc failed for Realloc!\n"));
1069 /* create the header */
1070 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
1071 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1073 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1075 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1078 /* copy in the reqpaths */
1079 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1080 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1083 for(i=0;i<junction->referral_count;i++) {
1084 struct referral* ref = &(junction->referral_list[i]);
1087 SSVAL(pdata,offset,3); /* version 3 */
1088 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1090 SSVAL(pdata,offset+4,1);
1092 SSVAL(pdata,offset+4,0);
1095 /* ref_flags :use path_consumed bytes? */
1096 SSVAL(pdata,offset+6,0);
1097 SIVAL(pdata,offset+8,ref->ttl);
1099 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1100 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1101 /* copy referred path into current offset */
1102 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1103 reply_size - uni_curroffset,
1104 STR_UNICODE | STR_TERMINATE);
1105 SSVAL(pdata,offset+16,uni_curroffset-offset);
1106 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1107 memset(pdata+offset+18,'\0',16);
1109 uni_curroffset += unilen;
1110 offset += VERSION3_REFERRAL_SIZE;
1115 /******************************************************************
1116 Set up the DFS referral for the dfs pathname. This call returns
1117 the amount of the path covered by this server, and where the
1118 client should be redirected to. This is the meat of the
1119 TRANS2_GET_DFS_REFERRAL call.
1120 ******************************************************************/
1122 int setup_dfs_referral(connection_struct *orig_conn,
1123 const char *dfs_path,
1124 int max_referral_level,
1125 char **ppdata, NTSTATUS *pstatus)
1127 struct junction_map *junction = NULL;
1128 int consumedcnt = 0;
1129 bool self_referral = False;
1131 char *pathnamep = NULL;
1132 char *local_dfs_path = NULL;
1135 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1136 *pstatus = NT_STATUS_NO_MEMORY;
1140 /* get the junction entry */
1142 talloc_destroy(ctx);
1143 *pstatus = NT_STATUS_NOT_FOUND;
1148 * Trim pathname sent by client so it begins with only one backslash.
1149 * Two backslashes confuse some dfs clients
1152 local_dfs_path = talloc_strdup(ctx,dfs_path);
1153 if (!local_dfs_path) {
1154 *pstatus = NT_STATUS_NO_MEMORY;
1155 talloc_destroy(ctx);
1158 pathnamep = local_dfs_path;
1159 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1160 IS_DIRECTORY_SEP(pathnamep[1])) {
1164 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1166 *pstatus = NT_STATUS_NO_MEMORY;
1167 talloc_destroy(ctx);
1171 /* The following call can change cwd. */
1172 *pstatus = get_referred_path(ctx, pathnamep, junction,
1173 &consumedcnt, &self_referral);
1174 if (!NT_STATUS_IS_OK(*pstatus)) {
1175 vfs_ChDir(orig_conn,orig_conn->connectpath);
1176 talloc_destroy(ctx);
1179 vfs_ChDir(orig_conn,orig_conn->connectpath);
1181 if (!self_referral) {
1182 pathnamep[consumedcnt] = '\0';
1184 if( DEBUGLVL( 3 ) ) {
1186 dbgtext("setup_dfs_referral: Path %s to "
1187 "alternate path(s):",
1189 for(i=0;i<junction->referral_count;i++)
1191 junction->referral_list[i].alternate_path);
1196 /* create the referral depeding on version */
1197 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1199 if (max_referral_level < 2) {
1200 max_referral_level = 2;
1202 if (max_referral_level > 3) {
1203 max_referral_level = 3;
1206 switch(max_referral_level) {
1208 reply_size = setup_ver2_dfs_referral(pathnamep,
1210 consumedcnt, self_referral);
1213 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1214 junction, consumedcnt, self_referral);
1217 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1219 max_referral_level));
1220 talloc_destroy(ctx);
1221 *pstatus = NT_STATUS_INVALID_LEVEL;
1226 DEBUGADD(0,("DFS Referral pdata:\n"));
1227 dump_data(0,(uint8 *)*ppdata,reply_size);
1230 talloc_destroy(ctx);
1231 *pstatus = NT_STATUS_OK;
1235 /**********************************************************************
1236 The following functions are called by the NETDFS RPC pipe functions
1237 **********************************************************************/
1239 /*********************************************************************
1240 Creates a junction structure from a DFS pathname
1241 **********************************************************************/
1243 bool create_junction(TALLOC_CTX *ctx,
1244 const char *dfs_path,
1245 struct junction_map *jucn)
1249 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1255 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1256 if (!NT_STATUS_IS_OK(status)) {
1260 /* check if path is dfs : validate first token */
1261 if (!is_myname_or_ipaddr(pdp->hostname)) {
1262 DEBUG(4,("create_junction: Invalid hostname %s "
1264 pdp->hostname, dfs_path));
1269 /* Check for a non-DFS share */
1270 snum = lp_servicenumber(pdp->servicename);
1272 if(snum < 0 || !lp_msdfs_root(snum)) {
1273 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1279 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1280 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1281 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1284 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1290 /**********************************************************************
1291 Forms a valid Unix pathname from the junction
1292 **********************************************************************/
1294 static bool junction_to_local_path(const struct junction_map *jucn,
1296 connection_struct **conn_out)
1300 snum = lp_servicenumber(jucn->service_name);
1304 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1306 lp_pathname(snum)))) {
1310 *pp_path_out = talloc_asprintf(conn_out,
1314 if (!*pp_path_out) {
1320 bool create_msdfs_link(const struct junction_map *jucn,
1324 char *msdfs_link = NULL;
1325 connection_struct *conn;
1327 bool insert_comma = False;
1330 if(!junction_to_local_path(jucn, &path, &conn)) {
1334 /* Form the msdfs_link contents */
1335 msdfs_link = talloc_strdup(conn, "msdfs:");
1339 for(i=0; i<jucn->referral_count; i++) {
1340 char *refpath = jucn->referral_list[i].alternate_path;
1342 /* Alternate paths always use Windows separators. */
1343 trim_char(refpath, '\\', '\\');
1344 if(*refpath == '\0') {
1346 insert_comma = False;
1350 if (i > 0 && insert_comma) {
1351 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1355 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1363 if (!insert_comma) {
1364 insert_comma = True;
1368 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1372 if(SMB_VFS_UNLINK(conn,path)!=0) {
1377 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1378 DEBUG(1,("create_msdfs_link: symlink failed "
1379 "%s -> %s\nError: %s\n",
1380 path, msdfs_link, strerror(errno)));
1388 conn_free_internal(conn);
1392 bool remove_msdfs_link(const struct junction_map *jucn)
1395 connection_struct *conn;
1398 if( junction_to_local_path(jucn, &path, &conn) ) {
1399 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1404 conn_free_internal(conn);
1408 /*********************************************************************
1409 Return the number of DFS links at the root of this share.
1410 *********************************************************************/
1412 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1415 SMB_STRUCT_DIR *dirp = NULL;
1417 const char *connect_path = lp_pathname(snum);
1418 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1419 connection_struct *conn;
1421 if(*connect_path == '\0') {
1426 * Fake up a connection struct for the VFS layer.
1429 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1430 &conn, snum, connect_path))) {
1434 /* Count a link for the msdfs root - convention */
1437 /* No more links if this is an msdfs proxy. */
1438 if (*msdfs_proxy != '\0') {
1442 /* Now enumerate all dfs links */
1443 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1448 while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1449 if (is_msdfs_link(conn,
1456 SMB_VFS_CLOSEDIR(conn,dirp);
1460 conn_free_internal(conn);
1464 /*********************************************************************
1465 *********************************************************************/
1467 static int form_junctions(TALLOC_CTX *ctx,
1469 struct junction_map *jucn,
1473 SMB_STRUCT_DIR *dirp = NULL;
1475 const char *connect_path = lp_pathname(snum);
1476 char *service_name = lp_servicename(snum);
1477 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1478 connection_struct *conn;
1479 struct referral *ref = NULL;
1481 if (jn_remain == 0) {
1485 if(*connect_path == '\0') {
1490 * Fake up a connection struct for the VFS layer.
1493 if (!NT_STATUS_IS_OK(create_conn_struct(ctx, &conn, snum, connect_path))) {
1497 /* form a junction for the msdfs root - convention
1498 DO NOT REMOVE THIS: NT clients will not work with us
1499 if this is not present
1501 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1502 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1503 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1506 jucn[cnt].referral_count = 1;
1508 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1509 if (jucn[cnt].referral_list == NULL) {
1514 ref->ttl = REFERRAL_TTL;
1515 if (*msdfs_proxy != '\0') {
1516 ref->alternate_path = talloc_strdup(ctx,
1519 ref->alternate_path = talloc_asprintf(ctx,
1521 get_local_machine_name(),
1525 if (!ref->alternate_path) {
1530 /* Don't enumerate if we're an msdfs proxy. */
1531 if (*msdfs_proxy != '\0') {
1535 /* Now enumerate all dfs links */
1536 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1541 while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1542 char *link_target = NULL;
1543 if (cnt >= jn_remain) {
1544 DEBUG(2, ("form_junctions: ran out of MSDFS "
1548 if (is_msdfs_link_internal(ctx,
1550 dname, &link_target,
1552 if (parse_msdfs_symlink(ctx,
1554 &jucn[cnt].referral_list,
1555 &jucn[cnt].referral_count)) {
1557 jucn[cnt].service_name = talloc_strdup(ctx,
1559 jucn[cnt].volume_name = talloc_strdup(ctx,
1561 if (!jucn[cnt].service_name ||
1562 !jucn[cnt].volume_name) {
1573 SMB_VFS_CLOSEDIR(conn,dirp);
1576 conn_free_internal(conn);
1580 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1582 struct junction_map *jn = NULL;
1584 size_t jn_count = 0;
1588 if(!lp_host_msdfs()) {
1592 /* Ensure all the usershares are loaded. */
1594 load_registry_shares();
1595 sharecount = load_usershare_shares();
1598 for(i=0;i < sharecount;i++) {
1599 if(lp_msdfs_root(i)) {
1600 jn_count += count_dfs_links(ctx, i);
1603 if (jn_count == 0) {
1606 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1610 for(i=0; i < sharecount; i++) {
1611 if (*p_num_jn >= jn_count) {
1614 if(lp_msdfs_root(i)) {
1615 *p_num_jn += form_junctions(ctx, i,
1617 jn_count - *p_num_jn);
1623 /******************************************************************************
1624 Core function to resolve a dfs pathname.
1625 ******************************************************************************/
1627 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1628 connection_struct *conn,
1630 const char *name_in,
1633 NTSTATUS status = NT_STATUS_OK;
1635 if (dfs_pathnames) {
1636 status = dfs_redirect(ctx,
1644 * Cheat and just return a copy of the in ptr.
1645 * Once srvstr_get_path() uses talloc it'll
1646 * be a talloced ptr anyway.
1648 *pp_name_out = CONST_DISCARD(char *,name_in);
1653 /******************************************************************************
1654 Core function to resolve a dfs pathname possibly containing a wildcard.
1655 This function is identical to the above except for the bool param to
1656 dfs_redirect but I need this to be separate so it's really clear when
1657 we're allowing wildcards and when we're not. JRA.
1658 ******************************************************************************/
1660 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1661 connection_struct *conn,
1663 const char *name_in,
1665 bool *ppath_contains_wcard)
1667 NTSTATUS status = NT_STATUS_OK;
1668 if (dfs_pathnames) {
1669 status = dfs_redirect(ctx,
1674 ppath_contains_wcard);
1677 * Cheat and just return a copy of the in ptr.
1678 * Once srvstr_get_path() uses talloc it'll
1679 * be a talloced ptr anyway.
1681 *pp_name_out = CONST_DISCARD(char *,name_in);