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)
54 NTSTATUS status = NT_STATUS_OK;
60 * This is the only talloc we should need to do
61 * on the struct dfs_path. All the pointers inside
62 * it should point to offsets within this string.
65 pathname_local = talloc_strdup(pdp, pathname);
66 if (!pathname_local) {
67 return NT_STATUS_NO_MEMORY;
69 /* Get a pointer to the terminating '\0' */
70 eos_ptr = &pathname_local[strlen(pathname_local)];
71 p = temp = pathname_local;
73 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
75 sepchar = pdp->posix_path ? '/' : '\\';
77 if (*pathname != sepchar) {
78 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
81 * Possibly client sent a local path by mistake.
82 * Try and convert to a local path.
85 pdp->hostname = eos_ptr; /* "" */
86 pdp->servicename = eos_ptr; /* "" */
88 /* We've got no info about separators. */
89 pdp->posix_path = lp_posix_pathnames();
91 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
98 * Safe to use on talloc'ed string as it only shrinks.
99 * It also doesn't affect the eos_ptr.
101 trim_char(temp,sepchar,sepchar);
103 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
107 /* Parse out hostname. */
108 p = strchr_m(temp,sepchar);
110 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
113 * Possibly client sent a local path by mistake.
114 * Try and convert to a local path.
117 pdp->hostname = eos_ptr; /* "" */
118 pdp->servicename = eos_ptr; /* "" */
121 DEBUG(10,("parse_dfs_path: trying to convert %s "
127 pdp->hostname = temp;
129 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
131 /* Parse out servicename. */
133 p = strchr_m(servicename,sepchar);
138 /* Is this really our servicename ? */
139 if (NULL == conn_find_byname(servicename)) {
140 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
144 * Possibly client sent a local path by mistake.
145 * Try and convert to a local path.
148 pdp->hostname = eos_ptr; /* "" */
149 pdp->servicename = eos_ptr; /* "" */
151 /* Repair the path - replace the sepchar's
154 *servicename = sepchar;
160 DEBUG(10,("parse_dfs_path: trying to convert %s "
166 pdp->servicename = servicename;
169 /* Client sent self referral \server\share. */
170 pdp->reqpath = eos_ptr; /* "" */
174 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
180 *ppath_contains_wcard = False;
184 /* Rest is reqpath. */
185 if (pdp->posix_path) {
186 status = check_path_syntax_posix(pdp->reqpath);
189 status = check_path_syntax_wcard(pdp->reqpath,
190 ppath_contains_wcard);
192 status = check_path_syntax(pdp->reqpath);
196 if (!NT_STATUS_IS_OK(status)) {
197 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
198 p, nt_errstr(status) ));
202 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
206 /********************************************************
207 Fake up a connection struct for the VFS layer.
208 Note this CHANGES CWD !!!! JRA.
209 *********************************************************/
211 static NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
212 connection_struct **pconn,
216 connection_struct *conn;
219 conn = TALLOC_ZERO_P(ctx, connection_struct);
221 return NT_STATUS_NO_MEMORY;
224 connpath = talloc_strdup(conn, path);
227 return NT_STATUS_NO_MEMORY;
229 connpath = talloc_string_sub(conn,
232 lp_servicename(snum));
235 return NT_STATUS_NO_MEMORY;
238 /* needed for smbd_vfs_init() */
240 if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
241 DEBUG(0, ("TALLOC failed\n"));
243 return NT_STATUS_NO_MEMORY;
246 conn->params->service = snum;
248 set_conn_connectpath(conn, connpath);
250 if (!smbd_vfs_init(conn)) {
251 NTSTATUS status = map_nt_error_from_unix(errno);
252 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
253 conn_free_internal(conn);
258 * Windows seems to insist on doing trans2getdfsreferral() calls on
259 * the IPC$ share as the anonymous user. If we try to chdir as that
260 * user we will fail.... WTF ? JRA.
263 if (vfs_ChDir(conn,conn->connectpath) != 0) {
264 NTSTATUS status = map_nt_error_from_unix(errno);
265 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
267 conn->connectpath, strerror(errno) ));
268 conn_free_internal(conn);
277 /**********************************************************************
278 Parse the contents of a symlink to verify if it is an msdfs referral
279 A valid referral is of the form:
281 msdfs:server1\share1,server2\share2
282 msdfs:server1\share1\pathname,server2\share2\pathname
283 msdfs:server1/share1,server2/share2
284 msdfs:server1/share1/pathname,server2/share2/pathname.
286 Note that the alternate paths returned here must be of the canonicalized
290 \server\share\path\to\file,
292 even in posix path mode. This is because we have no knowledge if the
293 server we're referring to understands posix paths.
294 **********************************************************************/
296 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
298 struct referral **preflist,
303 char **alt_path = NULL;
305 struct referral *reflist;
308 temp = talloc_strdup(ctx, target);
312 prot = strtok_r(temp, ":", &saveptr);
314 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
318 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
323 /* parse out the alternate paths */
324 while((count<MAX_REFERRAL_COUNT) &&
325 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
329 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
332 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
333 struct referral, count);
334 if(reflist == NULL) {
335 TALLOC_FREE(alt_path);
339 reflist = *preflist = NULL;
342 for(i=0;i<count;i++) {
345 /* Canonicalize link target.
346 * Replace all /'s in the path by a \ */
347 string_replace(alt_path[i], '/', '\\');
349 /* Remove leading '\\'s */
351 while (*p && (*p == '\\')) {
355 reflist[i].alternate_path = talloc_asprintf(ctx,
358 if (!reflist[i].alternate_path) {
362 reflist[i].proximity = 0;
363 reflist[i].ttl = REFERRAL_TTL;
364 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
365 reflist[i].alternate_path));
369 TALLOC_FREE(alt_path);
373 /**********************************************************************
374 Returns true if the unix path is a valid msdfs symlink and also
375 returns the target string from inside the link.
376 **********************************************************************/
378 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
379 connection_struct *conn,
381 char **pp_link_target,
382 SMB_STRUCT_STAT *sbufp)
385 int referral_len = 0;
386 char link_target_buf[7];
388 char *link_target = NULL;
390 if (pp_link_target) {
392 link_target = TALLOC_ARRAY(ctx, char, bufsize);
396 *pp_link_target = link_target;
398 bufsize = sizeof(link_target_buf);
399 link_target = link_target_buf;
406 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
407 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
412 if (!S_ISLNK(sbufp->st_mode)) {
413 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
418 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
419 if (referral_len == -1) {
420 DEBUG(0,("is_msdfs_link_read_target: Error reading "
421 "msdfs link %s: %s\n",
422 path, strerror(errno)));
425 link_target[referral_len] = '\0';
427 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
430 if (!strnequal(link_target, "msdfs:", 6)) {
437 if (link_target != link_target_buf) {
438 TALLOC_FREE(link_target);
443 /**********************************************************************
444 Returns true if the unix path is a valid msdfs symlink.
445 **********************************************************************/
447 bool is_msdfs_link(connection_struct *conn,
449 SMB_STRUCT_STAT *sbufp)
451 return is_msdfs_link_internal(talloc_tos(),
458 /*****************************************************************
459 Used by other functions to decide if a dfs path is remote,
460 and to get the list of referred locations for that remote path.
462 search_flag: For findfirsts, dfs links themselves are not
463 redirected, but paths beyond the links are. For normal smb calls,
464 even dfs links need to be redirected.
466 consumedcntp: how much of the dfs path is being redirected. the client
467 should try the remaining path on the redirected server.
469 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
470 link redirect are in targetpath.
471 *****************************************************************/
473 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
474 connection_struct *conn,
475 const char *dfspath, /* Incoming complete dfs path */
476 const struct dfs_path *pdp, /* Parsed out
477 server+share+extrapath. */
478 bool search_flag, /* Called from a findfirst ? */
480 char **pp_targetpath)
484 SMB_STRUCT_STAT sbuf;
486 char *localpath = NULL;
487 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
490 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
491 conn->connectpath, pdp->reqpath));
494 * Note the unix path conversion here we're doing we can
495 * throw away. We're looking for a symlink for a dfs
496 * resolution, if we don't find it we'll do another
497 * unix_convert later in the codepath.
498 * If we needed to remember what we'd resolved in
499 * dp->reqpath (as the original code did) we'd
500 * copy (localhost, dp->reqpath) on any code
501 * path below that returns True - but I don't
502 * think this is needed. JRA.
505 status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
507 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
508 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
512 /* Optimization - check if we can redirect the whole path. */
514 if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
516 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
517 "for dfs link %s.\n", dfspath));
521 DEBUG(6,("dfs_path_lookup: %s resolves to a "
522 "valid dfs link %s.\n", dfspath,
523 pp_targetpath ? *pp_targetpath : ""));
526 *consumedcntp = strlen(dfspath);
528 return NT_STATUS_PATH_NOT_COVERED;
531 /* Prepare to test only for '/' components in the given path,
532 * so if a Windows path replace all '\\' characters with '/'.
533 * For a POSIX DFS path we know all separators are already '/'. */
535 canon_dfspath = talloc_strdup(ctx, dfspath);
536 if (!canon_dfspath) {
537 return NT_STATUS_NO_MEMORY;
539 if (!pdp->posix_path) {
540 string_replace(canon_dfspath, '\\', '/');
544 * localpath comes out of unix_convert, so it has
545 * no trailing backslash. Make sure that canon_dfspath hasn't either.
546 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
549 trim_char(canon_dfspath,0,'/');
552 * Redirect if any component in the path is a link.
553 * We do this by walking backwards through the
554 * local path, chopping off the last component
555 * in both the local path and the canonicalized
556 * DFS path. If we hit a DFS link then we're done.
559 p = strrchr_m(localpath, '/');
561 q = strrchr_m(canon_dfspath, '/');
570 if (is_msdfs_link_internal(ctx, conn,
571 localpath, pp_targetpath, NULL)) {
572 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
573 "parent %s is dfs link\n", dfspath, localpath));
576 *consumedcntp = strlen(canon_dfspath);
577 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
583 return NT_STATUS_PATH_NOT_COVERED;
586 /* Step back on the filesystem. */
587 p = strrchr_m(localpath, '/');
590 /* And in the canonicalized dfs path. */
591 q = strrchr_m(canon_dfspath, '/');
598 /*****************************************************************
599 Decides if a dfs pathname should be redirected or not.
600 If not, the pathname is converted to a tcon-relative local unix path
602 search_wcard_flag: this flag performs 2 functions both related
603 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
606 This function can return NT_STATUS_OK, meaning use the returned path as-is
607 (mapped into a local path).
608 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
609 any other NT_STATUS error which is a genuine error to be
610 returned to the client.
611 *****************************************************************/
613 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
614 connection_struct *conn,
616 bool search_wcard_flag,
618 bool *ppath_contains_wcard)
621 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
624 return NT_STATUS_NO_MEMORY;
627 status = parse_dfs_path(path_in, search_wcard_flag, pdp,
628 ppath_contains_wcard);
629 if (!NT_STATUS_IS_OK(status)) {
634 if (pdp->reqpath[0] == '\0') {
636 *pp_path_out = talloc_strdup(ctx, "");
638 return NT_STATUS_NO_MEMORY;
640 DEBUG(5,("dfs_redirect: self-referral.\n"));
644 /* If dfs pathname for a non-dfs share, convert to tcon-relative
645 path and return OK */
647 if (!lp_msdfs_root(SNUM(conn))) {
648 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
651 return NT_STATUS_NO_MEMORY;
656 /* If it looked like a local path (zero hostname/servicename)
657 * just treat as a tcon-relative path. */
659 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
660 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
663 return NT_STATUS_NO_MEMORY;
668 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
669 || (strequal(pdp->servicename, HOMES_NAME)
670 && strequal(lp_servicename(SNUM(conn)),
671 get_current_username()) )) ) {
673 /* The given sharename doesn't match this connection. */
676 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
679 status = dfs_path_lookup(ctx, conn, path_in, pdp,
680 search_wcard_flag, NULL, NULL);
681 if (!NT_STATUS_IS_OK(status)) {
682 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
683 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
685 DEBUG(10,("dfs_redirect: dfs_path_lookup "
686 "failed for %s with %s\n",
687 path_in, nt_errstr(status) ));
692 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
694 /* Form non-dfs tcon-relative path */
695 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
698 return NT_STATUS_NO_MEMORY;
701 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
708 /**********************************************************************
709 Return a self referral.
710 **********************************************************************/
712 static NTSTATUS self_ref(TALLOC_CTX *ctx,
713 const char *dfs_path,
714 struct junction_map *jucn,
716 bool *self_referralp)
718 struct referral *ref;
720 *self_referralp = True;
722 jucn->referral_count = 1;
723 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
724 return NT_STATUS_NO_MEMORY;
727 ref->alternate_path = talloc_strdup(ctx, dfs_path);
728 if (!ref->alternate_path) {
729 return NT_STATUS_NO_MEMORY;
732 ref->ttl = REFERRAL_TTL;
733 jucn->referral_list = ref;
734 *consumedcntp = strlen(dfs_path);
738 /**********************************************************************
739 Gets valid referrals for a dfs path and fills up the
740 junction_map structure.
741 **********************************************************************/
743 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
744 const char *dfs_path,
745 struct junction_map *jucn,
747 bool *self_referralp)
749 struct connection_struct *conn;
750 char *targetpath = NULL;
752 NTSTATUS status = NT_STATUS_NOT_FOUND;
754 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
757 return NT_STATUS_NO_MEMORY;
760 *self_referralp = False;
762 status = parse_dfs_path(dfs_path, False, pdp, &dummy);
763 if (!NT_STATUS_IS_OK(status)) {
767 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
768 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
769 if (!jucn->service_name || !jucn->volume_name) {
771 return NT_STATUS_NO_MEMORY;
774 /* Verify the share is a dfs root */
775 snum = lp_servicenumber(jucn->service_name);
777 fstring service_name;
778 fstrcpy(service_name, jucn->service_name);
779 if ((snum = find_service(service_name)) < 0) {
780 return NT_STATUS_NOT_FOUND;
782 TALLOC_FREE(jucn->service_name);
783 jucn->service_name = talloc_strdup(ctx, service_name);
784 if (!jucn->service_name) {
786 return NT_STATUS_NO_MEMORY;
790 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
791 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
793 pdp->servicename, dfs_path));
795 return NT_STATUS_NOT_FOUND;
799 * Self referrals are tested with a anonymous IPC connection and
800 * a GET_DFS_REFERRAL call to \\server\share. (which means
801 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
802 * into the directory and will fail if it cannot (as the anonymous
803 * user). Cope with this.
806 if (pdp->reqpath[0] == '\0') {
808 struct referral *ref;
810 if (*lp_msdfs_proxy(snum) == '\0') {
820 * It's an msdfs proxy share. Redirect to
821 * the configured target share.
824 jucn->referral_count = 1;
825 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
827 return NT_STATUS_NO_MEMORY;
830 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
832 return NT_STATUS_NO_MEMORY;
835 trim_string(tmp, "\\", 0);
837 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
840 if (!ref->alternate_path) {
842 return NT_STATUS_NO_MEMORY;
845 if (pdp->reqpath[0] != '\0') {
846 ref->alternate_path = talloc_asprintf_append(
850 if (!ref->alternate_path) {
852 return NT_STATUS_NO_MEMORY;
856 ref->ttl = REFERRAL_TTL;
857 jucn->referral_list = ref;
858 *consumedcntp = strlen(dfs_path);
863 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum));
864 if (!NT_STATUS_IS_OK(status)) {
869 /* If this is a DFS path dfs_lookup should return
870 * NT_STATUS_PATH_NOT_COVERED. */
872 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
873 False, consumedcntp, &targetpath);
875 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
876 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
878 conn_free_internal(conn);
883 /* We know this is a valid dfs link. Parse the targetpath. */
884 if (!parse_msdfs_symlink(ctx, targetpath,
885 &jucn->referral_list,
886 &jucn->referral_count)) {
887 DEBUG(3,("get_referred_path: failed to parse symlink "
888 "target %s\n", targetpath ));
889 conn_free_internal(conn);
891 return NT_STATUS_NOT_FOUND;
894 conn_free_internal(conn);
899 static int setup_ver2_dfs_referral(const char *pathname,
901 struct junction_map *junction,
905 char* pdata = *ppdata;
907 smb_ucs2_t *uni_requestedpath = NULL;
908 int uni_reqpathoffset1,uni_reqpathoffset2;
910 int requestedpathlen=0;
915 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
917 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
918 &uni_requestedpath, pathname);
919 if (uni_requestedpath == NULL || requestedpathlen == 0) {
924 dump_data(0, (unsigned char *)uni_requestedpath,
928 DEBUG(10,("ref count = %u\n",junction->referral_count));
930 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
931 VERSION2_REFERRAL_SIZE * junction->referral_count;
933 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
935 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
937 reply_size = REFERRAL_HEADER_SIZE +
938 VERSION2_REFERRAL_SIZE*junction->referral_count +
939 2 * requestedpathlen;
940 DEBUG(10,("reply_size: %u\n",reply_size));
942 /* add up the unicode lengths of all the referral paths */
943 for(i=0;i<junction->referral_count;i++) {
944 DEBUG(10,("referral %u : %s\n",
946 junction->referral_list[i].alternate_path));
948 (strlen(junction->referral_list[i].alternate_path)+1)*2;
951 DEBUG(10,("reply_size = %u\n",reply_size));
952 /* add the unexplained 0x16 bytes */
955 pdata = (char *)SMB_REALLOC(pdata,reply_size);
957 DEBUG(0,("Realloc failed!\n"));
962 /* copy in the dfs requested paths.. required for offset calculations */
963 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
964 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
966 /* create the header */
967 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
968 /* number of referral in this pkt */
969 SSVAL(pdata,2,junction->referral_count);
971 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
973 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
977 /* add the referral elements */
978 for(i=0;i<junction->referral_count;i++) {
979 struct referral* ref = &junction->referral_list[i];
982 SSVAL(pdata,offset,2); /* version 2 */
983 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
985 SSVAL(pdata,offset+4,1);
987 SSVAL(pdata,offset+4,0);
990 /* ref_flags :use path_consumed bytes? */
991 SSVAL(pdata,offset+6,0);
992 SIVAL(pdata,offset+8,ref->proximity);
993 SIVAL(pdata,offset+12,ref->ttl);
995 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
996 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
997 /* copy referred path into current offset */
998 unilen = rpcstr_push(pdata+uni_curroffset,
1000 reply_size - uni_curroffset,
1003 SSVAL(pdata,offset+20,uni_curroffset-offset);
1005 uni_curroffset += unilen;
1006 offset += VERSION2_REFERRAL_SIZE;
1008 /* add in the unexplained 22 (0x16) bytes at the end */
1009 memset(pdata+uni_curroffset,'\0',0x16);
1013 static int setup_ver3_dfs_referral(const char *pathname,
1015 struct junction_map *junction,
1019 char *pdata = *ppdata;
1021 smb_ucs2_t *uni_reqpath = NULL;
1022 int uni_reqpathoffset1, uni_reqpathoffset2;
1029 DEBUG(10,("setting up version3 referral\n"));
1031 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1032 if (uni_reqpath == NULL || reqpathlen == 0) {
1037 dump_data(0, (unsigned char *)uni_reqpath,
1041 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1042 VERSION3_REFERRAL_SIZE * junction->referral_count;
1043 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1044 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1046 for(i=0;i<junction->referral_count;i++) {
1047 DEBUG(10,("referral %u : %s\n",
1049 junction->referral_list[i].alternate_path));
1051 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1054 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1056 DEBUG(0,("version3 referral setup:"
1057 "malloc failed for Realloc!\n"));
1062 /* create the header */
1063 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
1064 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1066 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1068 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1071 /* copy in the reqpaths */
1072 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1073 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1076 for(i=0;i<junction->referral_count;i++) {
1077 struct referral* ref = &(junction->referral_list[i]);
1080 SSVAL(pdata,offset,3); /* version 3 */
1081 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1083 SSVAL(pdata,offset+4,1);
1085 SSVAL(pdata,offset+4,0);
1088 /* ref_flags :use path_consumed bytes? */
1089 SSVAL(pdata,offset+6,0);
1090 SIVAL(pdata,offset+8,ref->ttl);
1092 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1093 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1094 /* copy referred path into current offset */
1095 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1096 reply_size - uni_curroffset,
1097 STR_UNICODE | STR_TERMINATE);
1098 SSVAL(pdata,offset+16,uni_curroffset-offset);
1099 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1100 memset(pdata+offset+18,'\0',16);
1102 uni_curroffset += unilen;
1103 offset += VERSION3_REFERRAL_SIZE;
1108 /******************************************************************
1109 Set up the DFS referral for the dfs pathname. This call returns
1110 the amount of the path covered by this server, and where the
1111 client should be redirected to. This is the meat of the
1112 TRANS2_GET_DFS_REFERRAL call.
1113 ******************************************************************/
1115 int setup_dfs_referral(connection_struct *orig_conn,
1116 const char *dfs_path,
1117 int max_referral_level,
1118 char **ppdata, NTSTATUS *pstatus)
1120 struct junction_map *junction = NULL;
1121 int consumedcnt = 0;
1122 bool self_referral = False;
1124 char *pathnamep = NULL;
1125 char *local_dfs_path = NULL;
1128 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1129 *pstatus = NT_STATUS_NO_MEMORY;
1133 /* get the junction entry */
1135 talloc_destroy(ctx);
1136 *pstatus = NT_STATUS_NOT_FOUND;
1141 * Trim pathname sent by client so it begins with only one backslash.
1142 * Two backslashes confuse some dfs clients
1145 local_dfs_path = talloc_strdup(ctx,dfs_path);
1146 if (!local_dfs_path) {
1147 *pstatus = NT_STATUS_NO_MEMORY;
1148 talloc_destroy(ctx);
1151 pathnamep = local_dfs_path;
1152 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1153 IS_DIRECTORY_SEP(pathnamep[1])) {
1157 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1159 *pstatus = NT_STATUS_NO_MEMORY;
1160 talloc_destroy(ctx);
1164 /* The following call can change cwd. */
1165 *pstatus = get_referred_path(ctx, pathnamep, junction,
1166 &consumedcnt, &self_referral);
1167 if (!NT_STATUS_IS_OK(*pstatus)) {
1168 vfs_ChDir(orig_conn,orig_conn->connectpath);
1169 talloc_destroy(ctx);
1172 vfs_ChDir(orig_conn,orig_conn->connectpath);
1174 if (!self_referral) {
1175 pathnamep[consumedcnt] = '\0';
1177 if( DEBUGLVL( 3 ) ) {
1179 dbgtext("setup_dfs_referral: Path %s to "
1180 "alternate path(s):",
1182 for(i=0;i<junction->referral_count;i++)
1184 junction->referral_list[i].alternate_path);
1189 /* create the referral depeding on version */
1190 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1192 if (max_referral_level < 2) {
1193 max_referral_level = 2;
1195 if (max_referral_level > 3) {
1196 max_referral_level = 3;
1199 switch(max_referral_level) {
1201 reply_size = setup_ver2_dfs_referral(pathnamep,
1203 consumedcnt, self_referral);
1206 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1207 junction, consumedcnt, self_referral);
1210 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1212 max_referral_level));
1213 talloc_destroy(ctx);
1214 *pstatus = NT_STATUS_INVALID_LEVEL;
1219 DEBUGADD(0,("DFS Referral pdata:\n"));
1220 dump_data(0,(uint8 *)*ppdata,reply_size);
1223 talloc_destroy(ctx);
1224 *pstatus = NT_STATUS_OK;
1228 /**********************************************************************
1229 The following functions are called by the NETDFS RPC pipe functions
1230 **********************************************************************/
1232 /*********************************************************************
1233 Creates a junction structure from a DFS pathname
1234 **********************************************************************/
1236 bool create_junction(TALLOC_CTX *ctx,
1237 const char *dfs_path,
1238 struct junction_map *jucn)
1242 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1248 status = parse_dfs_path(dfs_path, False, pdp, &dummy);
1249 if (!NT_STATUS_IS_OK(status)) {
1253 /* check if path is dfs : validate first token */
1254 if (!is_myname_or_ipaddr(pdp->hostname)) {
1255 DEBUG(4,("create_junction: Invalid hostname %s "
1257 pdp->hostname, dfs_path));
1262 /* Check for a non-DFS share */
1263 snum = lp_servicenumber(pdp->servicename);
1265 if(snum < 0 || !lp_msdfs_root(snum)) {
1266 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1272 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1273 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1274 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1277 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1283 /**********************************************************************
1284 Forms a valid Unix pathname from the junction
1285 **********************************************************************/
1287 static bool junction_to_local_path(const struct junction_map *jucn,
1289 connection_struct **conn_out)
1293 snum = lp_servicenumber(jucn->service_name);
1297 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1299 lp_pathname(snum)))) {
1303 *pp_path_out = talloc_asprintf(conn_out,
1307 if (!*pp_path_out) {
1313 bool create_msdfs_link(const struct junction_map *jucn,
1317 char *msdfs_link = NULL;
1318 connection_struct *conn;
1320 bool insert_comma = False;
1323 if(!junction_to_local_path(jucn, &path, &conn)) {
1327 /* Form the msdfs_link contents */
1328 msdfs_link = talloc_strdup(conn, "msdfs:");
1332 for(i=0; i<jucn->referral_count; i++) {
1333 char *refpath = jucn->referral_list[i].alternate_path;
1335 /* Alternate paths always use Windows separators. */
1336 trim_char(refpath, '\\', '\\');
1337 if(*refpath == '\0') {
1339 insert_comma = False;
1343 if (i > 0 && insert_comma) {
1344 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1348 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1356 if (!insert_comma) {
1357 insert_comma = True;
1361 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1365 if(SMB_VFS_UNLINK(conn,path)!=0) {
1370 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1371 DEBUG(1,("create_msdfs_link: symlink failed "
1372 "%s -> %s\nError: %s\n",
1373 path, msdfs_link, strerror(errno)));
1381 conn_free_internal(conn);
1385 bool remove_msdfs_link(const struct junction_map *jucn)
1388 connection_struct *conn;
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;
1414 if(*connect_path == '\0') {
1419 * Fake up a connection struct for the VFS layer.
1422 if (!NT_STATUS_IS_OK(create_conn_struct(talloc_tos(),
1423 &conn, snum, connect_path))) {
1427 /* Count a link for the msdfs root - convention */
1430 /* No more links if this is an msdfs proxy. */
1431 if (*msdfs_proxy != '\0') {
1435 /* Now enumerate all dfs links */
1436 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1441 while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1442 if (is_msdfs_link(conn,
1449 SMB_VFS_CLOSEDIR(conn,dirp);
1453 conn_free_internal(conn);
1457 /*********************************************************************
1458 *********************************************************************/
1460 static int form_junctions(TALLOC_CTX *ctx,
1462 struct junction_map *jucn,
1466 SMB_STRUCT_DIR *dirp = NULL;
1468 const char *connect_path = lp_pathname(snum);
1469 char *service_name = lp_servicename(snum);
1470 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1471 connection_struct *conn;
1472 struct referral *ref = NULL;
1474 if (jn_remain == 0) {
1478 if(*connect_path == '\0') {
1483 * Fake up a connection struct for the VFS layer.
1486 if (!NT_STATUS_IS_OK(create_conn_struct(ctx, &conn, snum, connect_path))) {
1490 /* form a junction for the msdfs root - convention
1491 DO NOT REMOVE THIS: NT clients will not work with us
1492 if this is not present
1494 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1495 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1496 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1499 jucn[cnt].referral_count = 1;
1501 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1502 if (jucn[cnt].referral_list == NULL) {
1507 ref->ttl = REFERRAL_TTL;
1508 if (*msdfs_proxy != '\0') {
1509 ref->alternate_path = talloc_strdup(ctx,
1512 ref->alternate_path = talloc_asprintf(ctx,
1514 get_local_machine_name(),
1518 if (!ref->alternate_path) {
1523 /* Don't enumerate if we're an msdfs proxy. */
1524 if (*msdfs_proxy != '\0') {
1528 /* Now enumerate all dfs links */
1529 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1534 while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1535 char *link_target = NULL;
1536 if (cnt >= jn_remain) {
1537 SMB_VFS_CLOSEDIR(conn,dirp);
1538 DEBUG(2, ("form_junctions: ran out of MSDFS "
1542 if (is_msdfs_link_internal(ctx,
1544 dname, &link_target,
1546 if (parse_msdfs_symlink(ctx,
1548 &jucn[cnt].referral_list,
1549 &jucn[cnt].referral_count)) {
1551 jucn[cnt].service_name = talloc_strdup(ctx,
1553 jucn[cnt].volume_name = talloc_strdup(ctx,
1555 if (!jucn[cnt].service_name ||
1556 !jucn[cnt].volume_name) {
1567 SMB_VFS_CLOSEDIR(conn,dirp);
1570 conn_free_internal(conn);
1574 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1576 struct junction_map *jn = NULL;
1578 size_t jn_count = 0;
1582 if(!lp_host_msdfs()) {
1586 /* Ensure all the usershares are loaded. */
1588 load_registry_shares();
1589 sharecount = load_usershare_shares();
1592 for(i=0;i < sharecount;i++) {
1593 if(lp_msdfs_root(i)) {
1594 jn_count += count_dfs_links(ctx, i);
1597 if (jn_count == 0) {
1600 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1604 for(i=0; i < sharecount; i++) {
1605 if (*p_num_jn >= jn_count) {
1608 if(lp_msdfs_root(i)) {
1609 *p_num_jn += form_junctions(ctx, i,
1611 jn_count - *p_num_jn);
1617 /******************************************************************************
1618 Core function to resolve a dfs pathname.
1619 ******************************************************************************/
1621 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1622 connection_struct *conn,
1624 const char *name_in,
1627 NTSTATUS status = NT_STATUS_OK;
1629 if (dfs_pathnames) {
1630 status = dfs_redirect(ctx,
1638 * Cheat and just return a copy of the in ptr.
1639 * Once srvstr_get_path() uses talloc it'll
1640 * be a talloced ptr anyway.
1642 *pp_name_out = CONST_DISCARD(char *,name_in);
1647 /******************************************************************************
1648 Core function to resolve a dfs pathname possibly containing a wildcard.
1649 This function is identical to the above except for the bool param to
1650 dfs_redirect but I need this to be separate so it's really clear when
1651 we're allowing wildcards and when we're not. JRA.
1652 ******************************************************************************/
1654 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1655 connection_struct *conn,
1657 const char *name_in,
1659 bool *ppath_contains_wcard)
1661 NTSTATUS status = NT_STATUS_OK;
1662 if (dfs_pathnames) {
1663 status = dfs_redirect(ctx,
1668 ppath_contains_wcard);
1671 * Cheat and just return a copy of the in ptr.
1672 * Once srvstr_get_path() uses talloc it'll
1673 * be a talloced ptr anyway.
1675 *pp_name_out = CONST_DISCARD(char *,name_in);