[GLUE] Rsync SAMBA_3_0 SVN r25598 in order to create the v3-0-test branch.
[Samba.git] / source / smbd / msdfs.c
blob74acf3d37e30977b1fb2c87832b163949cbd0bd1
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.0
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 2 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, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #define DBGC_CLASS DBGC_MSDFS
25 #include "includes.h"
27 extern uint32 global_client_caps;
29 /**********************************************************************
30 Parse a DFS pathname of the form \hostname\service\reqpath
31 into the dfs_path structure.
32 If POSIX pathnames is true, the pathname may also be of the
33 form /hostname/service/reqpath.
34 We cope with either here.
36 Unfortunately, due to broken clients who might set the
37 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
38 send a local path, we have to cope with that too....
40 JRA.
41 **********************************************************************/
43 static NTSTATUS parse_dfs_path(const char *pathname,
44 BOOL allow_wcards,
45 struct dfs_path *pdp,
46 BOOL *ppath_contains_wcard)
48 pstring pathname_local;
49 char *p,*temp;
50 NTSTATUS status = NT_STATUS_OK;
51 char sepchar;
53 ZERO_STRUCTP(pdp);
55 pstrcpy(pathname_local,pathname);
56 p = temp = pathname_local;
58 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
60 sepchar = pdp->posix_path ? '/' : '\\';
62 if (*pathname != sepchar) {
63 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
64 pathname, sepchar ));
66 * Possibly client sent a local path by mistake.
67 * Try and convert to a local path.
70 pdp->hostname[0] = '\0';
71 pdp->servicename[0] = '\0';
73 /* We've got no info about separators. */
74 pdp->posix_path = lp_posix_pathnames();
75 p = temp;
76 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
77 temp));
78 goto local_path;
81 trim_char(temp,sepchar,sepchar);
83 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
84 temp, sepchar));
86 /* Now tokenize. */
87 /* Parse out hostname. */
88 p = strchr_m(temp,sepchar);
89 if(p == NULL) {
90 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
91 temp));
93 * Possibly client sent a local path by mistake.
94 * Try and convert to a local path.
97 pdp->hostname[0] = '\0';
98 pdp->servicename[0] = '\0';
100 p = temp;
101 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
102 temp));
103 goto local_path;
105 *p = '\0';
106 fstrcpy(pdp->hostname,temp);
107 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
109 /* If we got a hostname, is it ours (or an IP address) ? */
110 if (!is_myname_or_ipaddr(pdp->hostname)) {
111 /* Repair path. */
112 *p = sepchar;
113 DEBUG(10,("parse_dfs_path: hostname %s isn't ours. Try local path from path %s\n",
114 pdp->hostname, temp));
116 * Possibly client sent a local path by mistake.
117 * Try and convert to a local path.
120 pdp->hostname[0] = '\0';
121 pdp->servicename[0] = '\0';
123 p = temp;
124 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
125 temp));
126 goto local_path;
129 /* Parse out servicename. */
130 temp = p+1;
131 p = strchr_m(temp,sepchar);
132 if(p == NULL) {
133 fstrcpy(pdp->servicename,temp);
134 pdp->reqpath[0] = '\0';
135 return NT_STATUS_OK;
137 *p = '\0';
138 fstrcpy(pdp->servicename,temp);
139 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
141 p++;
143 local_path:
145 *ppath_contains_wcard = False;
147 /* Rest is reqpath. */
148 if (pdp->posix_path) {
149 status = check_path_syntax_posix(pdp->reqpath, p);
150 } else {
151 if (allow_wcards) {
152 status = check_path_syntax_wcard(pdp->reqpath, p, ppath_contains_wcard);
153 } else {
154 status = check_path_syntax(pdp->reqpath, p);
158 if (!NT_STATUS_IS_OK(status)) {
159 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
160 p, nt_errstr(status) ));
161 return status;
164 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
165 return NT_STATUS_OK;
168 /********************************************************
169 Fake up a connection struct for the VFS layer.
170 Note this CHANGES CWD !!!! JRA.
171 *********************************************************/
173 static NTSTATUS create_conn_struct(connection_struct *conn, int snum, const char *path)
175 pstring connpath;
177 ZERO_STRUCTP(conn);
179 pstrcpy(connpath, path);
180 pstring_sub(connpath , "%S", lp_servicename(snum));
182 /* needed for smbd_vfs_init() */
184 if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) {
185 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
186 return NT_STATUS_NO_MEMORY;
189 if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx, struct share_params))) {
190 DEBUG(0, ("TALLOC failed\n"));
191 return NT_STATUS_NO_MEMORY;
194 conn->params->service = snum;
196 set_conn_connectpath(conn, connpath);
198 if (!smbd_vfs_init(conn)) {
199 NTSTATUS status = map_nt_error_from_unix(errno);
200 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
201 conn_free_internal(conn);
202 return status;
206 * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
207 * share as the anonymous user. If we try to chdir as that user we will
208 * fail.... WTF ? JRA.
211 if (vfs_ChDir(conn,conn->connectpath) != 0) {
212 NTSTATUS status = map_nt_error_from_unix(errno);
213 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
214 conn->connectpath, strerror(errno) ));
215 conn_free_internal(conn);
216 return status;
219 return NT_STATUS_OK;
222 /**********************************************************************
223 Parse the contents of a symlink to verify if it is an msdfs referral
224 A valid referral is of the form:
226 msdfs:server1\share1,server2\share2
227 msdfs:server1\share1\pathname,server2\share2\pathname
228 msdfs:server1/share1,server2/share2
229 msdfs:server1/share1/pathname,server2/share2/pathname.
231 Note that the alternate paths returned here must be of the canonicalized
232 form:
234 \server\share or
235 \server\share\path\to\file,
237 even in posix path mode. This is because we have no knowledge if the
238 server we're referring to understands posix paths.
239 **********************************************************************/
241 static BOOL parse_msdfs_symlink(TALLOC_CTX *ctx,
242 char *target,
243 struct referral **preflist,
244 int *refcount)
246 pstring temp;
247 char *prot;
248 char *alt_path[MAX_REFERRAL_COUNT];
249 int count = 0, i;
250 struct referral *reflist;
252 pstrcpy(temp,target);
253 prot = strtok(temp,":");
254 if (!prot) {
255 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
256 return False;
259 /* parse out the alternate paths */
260 while((count<MAX_REFERRAL_COUNT) &&
261 ((alt_path[count] = strtok(NULL,",")) != NULL)) {
262 count++;
265 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
267 if (count) {
268 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx, struct referral, count);
269 if(reflist == NULL) {
270 DEBUG(0,("parse_msdfs_symlink: talloc failed!\n"));
271 return False;
273 } else {
274 reflist = *preflist = NULL;
277 for(i=0;i<count;i++) {
278 char *p;
280 /* Canonicalize link target. Replace all /'s in the path by a \ */
281 string_replace(alt_path[i], '/', '\\');
283 /* Remove leading '\\'s */
284 p = alt_path[i];
285 while (*p && (*p == '\\')) {
286 p++;
289 pstrcpy(reflist[i].alternate_path, "\\");
290 pstrcat(reflist[i].alternate_path, p);
292 reflist[i].proximity = 0;
293 reflist[i].ttl = REFERRAL_TTL;
294 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n", reflist[i].alternate_path));
295 *refcount += 1;
298 return True;
301 /**********************************************************************
302 Returns true if the unix path is a valid msdfs symlink and also
303 returns the target string from inside the link.
304 **********************************************************************/
306 BOOL is_msdfs_link(connection_struct *conn,
307 const char *path,
308 pstring link_target,
309 SMB_STRUCT_STAT *sbufp)
311 SMB_STRUCT_STAT st;
312 int referral_len = 0;
314 if (sbufp == NULL) {
315 sbufp = &st;
318 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
319 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
320 return False;
323 if (!S_ISLNK(sbufp->st_mode)) {
324 DEBUG(5,("is_msdfs_link: %s is not a link.\n",path));
325 return False;
328 /* open the link and read it */
329 referral_len = SMB_VFS_READLINK(conn, path, link_target, sizeof(pstring)-1);
330 if (referral_len == -1) {
331 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n",
332 path, strerror(errno)));
333 return False;
335 link_target[referral_len] = '\0';
337 DEBUG(5,("is_msdfs_link: %s -> %s\n",path, link_target));
339 if (!strnequal(link_target, "msdfs:", 6)) {
340 return False;
342 return True;
345 /*****************************************************************
346 Used by other functions to decide if a dfs path is remote,
347 and to get the list of referred locations for that remote path.
349 search_flag: For findfirsts, dfs links themselves are not
350 redirected, but paths beyond the links are. For normal smb calls,
351 even dfs links need to be redirected.
353 consumedcntp: how much of the dfs path is being redirected. the client
354 should try the remaining path on the redirected server.
356 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
357 link redirect are in targetpath.
358 *****************************************************************/
360 static NTSTATUS dfs_path_lookup(connection_struct *conn,
361 const char *dfspath, /* Incoming complete dfs path */
362 const struct dfs_path *pdp, /* Parsed out server+share+extrapath. */
363 BOOL search_flag, /* Called from a findfirst ? */
364 int *consumedcntp,
365 pstring targetpath)
367 char *p = NULL;
368 char *q = NULL;
369 SMB_STRUCT_STAT sbuf;
370 NTSTATUS status;
371 pstring localpath;
372 pstring canon_dfspath; /* Canonicalized dfs path. (only '/' components). */
374 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
375 conn->connectpath, pdp->reqpath));
378 * Note the unix path conversion here we're doing we can
379 * throw away. We're looking for a symlink for a dfs
380 * resolution, if we don't find it we'll do another
381 * unix_convert later in the codepath.
382 * If we needed to remember what we'd resolved in
383 * dp->reqpath (as the original code did) we'd
384 * pstrcpy(localhost, dp->reqpath) on any code
385 * path below that returns True - but I don't
386 * think this is needed. JRA.
389 pstrcpy(localpath, pdp->reqpath);
390 status = unix_convert(conn, localpath, search_flag, NULL, &sbuf);
391 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
392 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
393 return status;
396 /* Optimization - check if we can redirect the whole path. */
398 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
399 if (search_flag) {
400 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
401 "for dfs link %s.\n", dfspath));
402 return NT_STATUS_OK;
405 DEBUG(6,("dfs_path_lookup: %s resolves to a "
406 "valid dfs link %s.\n", dfspath, targetpath));
408 if (consumedcntp) {
409 *consumedcntp = strlen(dfspath);
411 return NT_STATUS_PATH_NOT_COVERED;
414 /* Prepare to test only for '/' components in the given path,
415 * so if a Windows path replace all '\\' characters with '/'.
416 * For a POSIX DFS path we know all separators are already '/'. */
418 pstrcpy(canon_dfspath, dfspath);
419 if (!pdp->posix_path) {
420 string_replace(canon_dfspath, '\\', '/');
424 * localpath comes out of unix_convert, so it has
425 * no trailing backslash. Make sure that canon_dfspath hasn't either.
426 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
429 trim_char(canon_dfspath,0,'/');
432 * Redirect if any component in the path is a link.
433 * We do this by walking backwards through the
434 * local path, chopping off the last component
435 * in both the local path and the canonicalized
436 * DFS path. If we hit a DFS link then we're done.
439 p = strrchr_m(localpath, '/');
440 if (consumedcntp) {
441 q = strrchr_m(canon_dfspath, '/');
444 while (p) {
445 *p = '\0';
446 if (q) {
447 *q = '\0';
450 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
451 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
452 "parent %s is dfs link\n", dfspath, localpath));
454 if (consumedcntp) {
455 *consumedcntp = strlen(canon_dfspath);
456 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
457 "(%d)\n", canon_dfspath, *consumedcntp));
460 return NT_STATUS_PATH_NOT_COVERED;
463 /* Step back on the filesystem. */
464 p = strrchr_m(localpath, '/');
466 if (consumedcntp) {
467 /* And in the canonicalized dfs path. */
468 q = strrchr_m(canon_dfspath, '/');
472 return NT_STATUS_OK;
475 /*****************************************************************
476 Decides if a dfs pathname should be redirected or not.
477 If not, the pathname is converted to a tcon-relative local unix path
479 search_wcard_flag: this flag performs 2 functions bother related
480 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
481 for details.
483 This function can return NT_STATUS_OK, meaning use the returned path as-is
484 (mapped into a local path).
485 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
486 any other NT_STATUS error which is a genuine error to be
487 returned to the client.
488 *****************************************************************/
490 static NTSTATUS dfs_redirect( connection_struct *conn,
491 pstring dfs_path,
492 BOOL search_wcard_flag,
493 BOOL *ppath_contains_wcard)
495 NTSTATUS status;
496 struct dfs_path dp;
497 pstring targetpath;
499 status = parse_dfs_path(dfs_path, search_wcard_flag, &dp, ppath_contains_wcard);
500 if (!NT_STATUS_IS_OK(status)) {
501 return status;
504 if (dp.reqpath[0] == '\0') {
505 pstrcpy(dfs_path, dp.reqpath);
506 DEBUG(5,("dfs_redirect: self-referral.\n"));
507 return NT_STATUS_OK;
510 /* If dfs pathname for a non-dfs share, convert to tcon-relative
511 path and return OK */
513 if (!lp_msdfs_root(SNUM(conn))) {
514 pstrcpy(dfs_path, dp.reqpath);
515 return NT_STATUS_OK;
518 /* If it looked like a local path (zero hostname/servicename)
519 * just treat as a tcon-relative path. */
521 if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') {
522 pstrcpy(dfs_path, dp.reqpath);
523 return NT_STATUS_OK;
526 if (!( strequal(dp.servicename, lp_servicename(SNUM(conn)))
527 || (strequal(dp.servicename, HOMES_NAME)
528 && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) {
530 /* The given sharename doesn't match this connection. */
532 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
535 status = dfs_path_lookup(conn, dfs_path, &dp,
536 search_wcard_flag, NULL, targetpath);
537 if (!NT_STATUS_IS_OK(status)) {
538 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
539 DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path));
540 } else {
541 DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
542 dfs_path, nt_errstr(status) ));
544 return status;
547 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path));
549 /* Form non-dfs tcon-relative path */
550 pstrcpy(dfs_path, dp.reqpath);
552 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path));
553 return NT_STATUS_OK;
556 /**********************************************************************
557 Return a self referral.
558 **********************************************************************/
560 static NTSTATUS self_ref(TALLOC_CTX *ctx,
561 const char *dfs_path,
562 struct junction_map *jucn,
563 int *consumedcntp,
564 BOOL *self_referralp)
566 struct referral *ref;
568 *self_referralp = True;
570 jucn->referral_count = 1;
571 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
572 DEBUG(0,("self_ref: talloc failed for referral\n"));
573 return NT_STATUS_NO_MEMORY;
576 pstrcpy(ref->alternate_path,dfs_path);
577 ref->proximity = 0;
578 ref->ttl = REFERRAL_TTL;
579 jucn->referral_list = ref;
580 *consumedcntp = strlen(dfs_path);
581 return NT_STATUS_OK;
584 /**********************************************************************
585 Gets valid referrals for a dfs path and fills up the
586 junction_map structure.
587 **********************************************************************/
589 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
590 const char *dfs_path,
591 struct junction_map *jucn,
592 int *consumedcntp,
593 BOOL *self_referralp)
595 struct connection_struct conns;
596 struct connection_struct *conn = &conns;
597 struct dfs_path dp;
598 pstring conn_path;
599 pstring targetpath;
600 int snum;
601 NTSTATUS status = NT_STATUS_NOT_FOUND;
602 BOOL dummy;
604 ZERO_STRUCT(conns);
606 *self_referralp = False;
608 status = parse_dfs_path(dfs_path, False, &dp, &dummy);
609 if (!NT_STATUS_IS_OK(status)) {
610 return status;
613 /* Verify hostname in path */
614 if (!is_myname_or_ipaddr(dp.hostname)) {
615 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
616 dp.hostname, dfs_path));
617 return NT_STATUS_NOT_FOUND;
620 fstrcpy(jucn->service_name, dp.servicename);
621 pstrcpy(jucn->volume_name, dp.reqpath);
623 /* Verify the share is a dfs root */
624 snum = lp_servicenumber(jucn->service_name);
625 if(snum < 0) {
626 if ((snum = find_service(jucn->service_name)) < 0) {
627 return NT_STATUS_NOT_FOUND;
631 if (!lp_msdfs_root(snum)) {
632 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
633 dp.servicename, dfs_path));
634 return NT_STATUS_NOT_FOUND;
638 * Self referrals are tested with a anonymous IPC connection and
639 * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
640 * to an empty string). create_conn_struct cd's into the directory and will
641 * fail if it cannot (as the anonymous user). Cope with this.
644 if (dp.reqpath[0] == '\0') {
645 struct referral *ref;
647 if (*lp_msdfs_proxy(snum) == '\0') {
648 return self_ref(ctx,
649 dfs_path,
650 jucn,
651 consumedcntp,
652 self_referralp);
656 * It's an msdfs proxy share. Redirect to
657 * the configured target share.
660 jucn->referral_count = 1;
661 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
662 DEBUG(0, ("malloc failed for referral\n"));
663 return NT_STATUS_NO_MEMORY;
666 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
667 if (dp.reqpath[0] != '\0') {
668 pstrcat(ref->alternate_path, dp.reqpath);
670 ref->proximity = 0;
671 ref->ttl = REFERRAL_TTL;
672 jucn->referral_list = ref;
673 *consumedcntp = strlen(dfs_path);
674 return NT_STATUS_OK;
677 pstrcpy(conn_path, lp_pathname(snum));
678 status = create_conn_struct(conn, snum, conn_path);
679 if (!NT_STATUS_IS_OK(status)) {
680 return status;
683 /* If this is a DFS path dfs_lookup should return
684 * NT_STATUS_PATH_NOT_COVERED. */
686 status = dfs_path_lookup(conn, dfs_path, &dp,
687 False, consumedcntp, targetpath);
689 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
690 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
691 dfs_path));
692 conn_free_internal(conn);
693 return status;
696 /* We know this is a valid dfs link. Parse the targetpath. */
697 if (!parse_msdfs_symlink(ctx, targetpath,
698 &jucn->referral_list,
699 &jucn->referral_count)) {
700 DEBUG(3,("get_referred_path: failed to parse symlink "
701 "target %s\n", targetpath ));
702 conn_free_internal(conn);
703 return NT_STATUS_NOT_FOUND;
706 conn_free_internal(conn);
707 return NT_STATUS_OK;
710 static int setup_ver2_dfs_referral(const char *pathname,
711 char **ppdata,
712 struct junction_map *junction,
713 int consumedcnt,
714 BOOL self_referral)
716 char* pdata = *ppdata;
718 unsigned char uni_requestedpath[1024];
719 int uni_reqpathoffset1,uni_reqpathoffset2;
720 int uni_curroffset;
721 int requestedpathlen=0;
722 int offset;
723 int reply_size = 0;
724 int i=0;
726 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
728 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
729 STR_TERMINATE);
731 if (DEBUGLVL(10)) {
732 dump_data(0, (const char *) uni_requestedpath,requestedpathlen);
735 DEBUG(10,("ref count = %u\n",junction->referral_count));
737 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
738 VERSION2_REFERRAL_SIZE * junction->referral_count;
740 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
742 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
744 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
745 2 * requestedpathlen;
746 DEBUG(10,("reply_size: %u\n",reply_size));
748 /* add up the unicode lengths of all the referral paths */
749 for(i=0;i<junction->referral_count;i++) {
750 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
751 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
754 DEBUG(10,("reply_size = %u\n",reply_size));
755 /* add the unexplained 0x16 bytes */
756 reply_size += 0x16;
758 pdata = (char *)SMB_REALLOC(pdata,reply_size);
759 if(pdata == NULL) {
760 DEBUG(0,("Realloc failed!\n"));
761 return -1;
763 *ppdata = pdata;
765 /* copy in the dfs requested paths.. required for offset calculations */
766 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
767 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
769 /* create the header */
770 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
771 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
772 if(self_referral) {
773 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
774 } else {
775 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
778 offset = 8;
779 /* add the referral elements */
780 for(i=0;i<junction->referral_count;i++) {
781 struct referral* ref = &junction->referral_list[i];
782 int unilen;
784 SSVAL(pdata,offset,2); /* version 2 */
785 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
786 if(self_referral) {
787 SSVAL(pdata,offset+4,1);
788 } else {
789 SSVAL(pdata,offset+4,0);
791 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
792 SIVAL(pdata,offset+8,ref->proximity);
793 SIVAL(pdata,offset+12,ref->ttl);
795 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
796 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
797 /* copy referred path into current offset */
798 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
799 sizeof(pstring), STR_UNICODE);
801 SSVAL(pdata,offset+20,uni_curroffset-offset);
803 uni_curroffset += unilen;
804 offset += VERSION2_REFERRAL_SIZE;
806 /* add in the unexplained 22 (0x16) bytes at the end */
807 memset(pdata+uni_curroffset,'\0',0x16);
808 return reply_size;
811 static int setup_ver3_dfs_referral(const char *pathname,
812 char **ppdata,
813 struct junction_map *junction,
814 int consumedcnt,
815 BOOL self_referral)
817 char* pdata = *ppdata;
819 unsigned char uni_reqpath[1024];
820 int uni_reqpathoffset1, uni_reqpathoffset2;
821 int uni_curroffset;
822 int reply_size = 0;
824 int reqpathlen = 0;
825 int offset,i=0;
827 DEBUG(10,("setting up version3 referral\n"));
829 reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
831 if (DEBUGLVL(10)) {
832 dump_data(0, (char *) uni_reqpath,reqpathlen);
835 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
836 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
837 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
839 for(i=0;i<junction->referral_count;i++) {
840 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
841 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
844 pdata = (char *)SMB_REALLOC(pdata,reply_size);
845 if(pdata == NULL) {
846 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
847 return -1;
849 *ppdata = pdata;
851 /* create the header */
852 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
853 SSVAL(pdata,2,junction->referral_count); /* number of referral */
854 if(self_referral) {
855 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
856 } else {
857 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
860 /* copy in the reqpaths */
861 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
862 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
864 offset = 8;
865 for(i=0;i<junction->referral_count;i++) {
866 struct referral* ref = &(junction->referral_list[i]);
867 int unilen;
869 SSVAL(pdata,offset,3); /* version 3 */
870 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
871 if(self_referral) {
872 SSVAL(pdata,offset+4,1);
873 } else {
874 SSVAL(pdata,offset+4,0);
877 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
878 SIVAL(pdata,offset+8,ref->ttl);
880 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
881 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
882 /* copy referred path into current offset */
883 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
884 sizeof(pstring), STR_UNICODE | STR_TERMINATE);
885 SSVAL(pdata,offset+16,uni_curroffset-offset);
886 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
887 memset(pdata+offset+18,'\0',16);
889 uni_curroffset += unilen;
890 offset += VERSION3_REFERRAL_SIZE;
892 return reply_size;
895 /******************************************************************
896 Set up the DFS referral for the dfs pathname. This call returns
897 the amount of the path covered by this server, and where the
898 client should be redirected to. This is the meat of the
899 TRANS2_GET_DFS_REFERRAL call.
900 ******************************************************************/
902 int setup_dfs_referral(connection_struct *orig_conn,
903 const char *dfs_path,
904 int max_referral_level,
905 char **ppdata, NTSTATUS *pstatus)
907 struct junction_map junction;
908 int consumedcnt = 0;
909 BOOL self_referral = False;
910 int reply_size = 0;
911 char *pathnamep = NULL;
912 pstring local_dfs_path;
913 TALLOC_CTX *ctx;
915 if (!(ctx=talloc_init("setup_dfs_referral"))) {
916 *pstatus = NT_STATUS_NO_MEMORY;
917 return -1;
920 ZERO_STRUCT(junction);
922 /* get the junction entry */
923 if (!dfs_path) {
924 talloc_destroy(ctx);
925 *pstatus = NT_STATUS_NOT_FOUND;
926 return -1;
930 * Trim pathname sent by client so it begins with only one backslash.
931 * Two backslashes confuse some dfs clients
934 pstrcpy(local_dfs_path, dfs_path);
935 pathnamep = local_dfs_path;
936 while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) {
937 pathnamep++;
940 /* The following call can change cwd. */
941 *pstatus = get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral);
942 if (!NT_STATUS_IS_OK(*pstatus)) {
943 vfs_ChDir(orig_conn,orig_conn->connectpath);
944 talloc_destroy(ctx);
945 return -1;
947 vfs_ChDir(orig_conn,orig_conn->connectpath);
949 if (!self_referral) {
950 pathnamep[consumedcnt] = '\0';
952 if( DEBUGLVL( 3 ) ) {
953 int i=0;
954 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
955 for(i=0;i<junction.referral_count;i++)
956 dbgtext(" %s",junction.referral_list[i].alternate_path);
957 dbgtext(".\n");
961 /* create the referral depeding on version */
962 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
964 if (max_referral_level < 2) {
965 max_referral_level = 2;
967 if (max_referral_level > 3) {
968 max_referral_level = 3;
971 switch(max_referral_level) {
972 case 2:
973 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
974 consumedcnt, self_referral);
975 break;
976 case 3:
977 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
978 consumedcnt, self_referral);
979 break;
980 default:
981 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
982 talloc_destroy(ctx);
983 *pstatus = NT_STATUS_INVALID_LEVEL;
984 return -1;
987 if (DEBUGLVL(10)) {
988 DEBUGADD(0,("DFS Referral pdata:\n"));
989 dump_data(0,*ppdata,reply_size);
992 talloc_destroy(ctx);
993 *pstatus = NT_STATUS_OK;
994 return reply_size;
997 /**********************************************************************
998 The following functions are called by the NETDFS RPC pipe functions
999 **********************************************************************/
1001 /*********************************************************************
1002 Creates a junction structure from a DFS pathname
1003 **********************************************************************/
1005 BOOL create_junction(const char *dfs_path, struct junction_map *jucn)
1007 int snum;
1008 BOOL dummy;
1009 struct dfs_path dp;
1011 NTSTATUS status = parse_dfs_path(dfs_path, False, &dp, &dummy);
1013 if (!NT_STATUS_IS_OK(status)) {
1014 return False;
1017 /* check if path is dfs : validate first token */
1018 if (!is_myname_or_ipaddr(dp.hostname)) {
1019 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
1020 dp.hostname, dfs_path));
1021 return False;
1024 /* Check for a non-DFS share */
1025 snum = lp_servicenumber(dp.servicename);
1027 if(snum < 0 || !lp_msdfs_root(snum)) {
1028 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1029 dp.servicename));
1030 return False;
1033 fstrcpy(jucn->service_name,dp.servicename);
1034 pstrcpy(jucn->volume_name,dp.reqpath);
1035 pstrcpy(jucn->comment, lp_comment(snum));
1036 return True;
1039 /**********************************************************************
1040 Forms a valid Unix pathname from the junction
1041 **********************************************************************/
1043 static BOOL junction_to_local_path(struct junction_map *jucn,
1044 char *path,
1045 int max_pathlen,
1046 connection_struct *conn_out)
1048 int snum;
1049 pstring conn_path;
1051 snum = lp_servicenumber(jucn->service_name);
1052 if(snum < 0) {
1053 return False;
1056 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
1057 safe_strcat(path, "/", max_pathlen-1);
1058 safe_strcat(path, jucn->volume_name, max_pathlen-1);
1060 pstrcpy(conn_path, lp_pathname(snum));
1061 if (!NT_STATUS_IS_OK(create_conn_struct(conn_out, snum, conn_path))) {
1062 return False;
1065 return True;
1068 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
1070 pstring path;
1071 pstring msdfs_link;
1072 connection_struct conns;
1073 connection_struct *conn = &conns;
1074 int i=0;
1075 BOOL insert_comma = False;
1076 BOOL ret = False;
1078 ZERO_STRUCT(conns);
1080 if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
1081 return False;
1084 /* Form the msdfs_link contents */
1085 pstrcpy(msdfs_link, "msdfs:");
1086 for(i=0; i<jucn->referral_count; i++) {
1087 char* refpath = jucn->referral_list[i].alternate_path;
1089 /* Alternate paths always use Windows separators. */
1090 trim_char(refpath, '\\', '\\');
1091 if(*refpath == '\0') {
1092 if (i == 0) {
1093 insert_comma = False;
1095 continue;
1097 if (i > 0 && insert_comma) {
1098 pstrcat(msdfs_link, ",");
1101 pstrcat(msdfs_link, refpath);
1102 if (!insert_comma) {
1103 insert_comma = True;
1107 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1108 path, msdfs_link));
1110 if(exists) {
1111 if(SMB_VFS_UNLINK(conn,path)!=0) {
1112 goto out;
1116 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1117 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
1118 path, msdfs_link, strerror(errno)));
1119 goto out;
1123 ret = True;
1125 out:
1127 conn_free_internal(conn);
1128 return ret;
1131 BOOL remove_msdfs_link(struct junction_map *jucn)
1133 pstring path;
1134 connection_struct conns;
1135 connection_struct *conn = &conns;
1136 BOOL ret = False;
1138 ZERO_STRUCT(conns);
1140 if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1141 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1142 ret = True;
1144 talloc_destroy( conn->mem_ctx );
1147 conn_free_internal(conn);
1148 return ret;
1151 static int form_junctions(TALLOC_CTX *ctx,
1152 int snum,
1153 struct junction_map *jucn,
1154 int jn_remain)
1156 int cnt = 0;
1157 SMB_STRUCT_DIR *dirp;
1158 char *dname;
1159 pstring connect_path;
1160 char *service_name = lp_servicename(snum);
1161 connection_struct conn;
1162 struct referral *ref = NULL;
1164 ZERO_STRUCT(conn);
1166 if (jn_remain <= 0) {
1167 return 0;
1170 pstrcpy(connect_path,lp_pathname(snum));
1172 if(*connect_path == '\0') {
1173 return 0;
1177 * Fake up a connection struct for the VFS layer.
1180 if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
1181 return 0;
1184 /* form a junction for the msdfs root - convention
1185 DO NOT REMOVE THIS: NT clients will not work with us
1186 if this is not present
1188 fstrcpy(jucn[cnt].service_name, service_name);
1189 jucn[cnt].volume_name[0] = '\0';
1190 jucn[cnt].referral_count = 1;
1192 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1193 if (jucn[cnt].referral_list == NULL) {
1194 DEBUG(0, ("talloc failed!\n"));
1195 goto out;
1198 ref->proximity = 0;
1199 ref->ttl = REFERRAL_TTL;
1200 if (*lp_msdfs_proxy(snum) != '\0') {
1201 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1202 cnt++;
1203 goto out;
1206 pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
1207 get_local_machine_name(),
1208 service_name);
1209 cnt++;
1211 /* Now enumerate all dfs links */
1212 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1213 if(!dirp) {
1214 goto out;
1217 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1218 pstring link_target;
1219 if (cnt >= jn_remain) {
1220 SMB_VFS_CLOSEDIR(&conn,dirp);
1221 DEBUG(2, ("ran out of MSDFS junction slots"));
1222 goto out;
1224 if (is_msdfs_link(&conn, dname, link_target, NULL)) {
1225 if (parse_msdfs_symlink(ctx,
1226 link_target,
1227 &jucn[cnt].referral_list,
1228 &jucn[cnt].referral_count)) {
1230 fstrcpy(jucn[cnt].service_name, service_name);
1231 pstrcpy(jucn[cnt].volume_name, dname);
1232 cnt++;
1237 SMB_VFS_CLOSEDIR(&conn,dirp);
1239 out:
1241 conn_free_internal(&conn);
1242 return cnt;
1245 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1247 int i=0;
1248 int sharecount = 0;
1249 int jn_count = 0;
1251 if(!lp_host_msdfs()) {
1252 return 0;
1255 /* Ensure all the usershares are loaded. */
1256 become_root();
1257 sharecount = load_usershare_shares();
1258 unbecome_root();
1260 for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1261 if(lp_msdfs_root(i)) {
1262 jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1265 return jn_count;
1268 /******************************************************************************
1269 Core function to resolve a dfs pathname.
1270 ******************************************************************************/
1272 NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name)
1274 NTSTATUS status = NT_STATUS_OK;
1275 BOOL dummy;
1276 if (dfs_pathnames) {
1277 status = dfs_redirect(conn, name, False, &dummy);
1279 return status;
1282 /******************************************************************************
1283 Core function to resolve a dfs pathname possibly containing a wildcard.
1284 This function is identical to the above except for the BOOL param to
1285 dfs_redirect but I need this to be separate so it's really clear when
1286 we're allowing wildcards and when we're not. JRA.
1287 ******************************************************************************/
1289 NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard)
1291 NTSTATUS status = NT_STATUS_OK;
1292 if (dfs_pathnames) {
1293 status = dfs_redirect(conn, name, True, ppath_contains_wcard);
1295 return status;