Updated with sepcifics of how we determine sendfile.
[Samba.git] / source / smbd / msdfs.c
blob982d0ae59050f776bfc2573adf12ace7b9ba0d56
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 /* Parse out servicename. */
110 temp = p+1;
111 p = strchr_m(temp,sepchar);
112 if(p == NULL) {
113 fstrcpy(pdp->servicename,temp);
114 pdp->reqpath[0] = '\0';
115 return NT_STATUS_OK;
117 *p = '\0';
118 fstrcpy(pdp->servicename,temp);
119 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
121 p++;
123 local_path:
125 *ppath_contains_wcard = False;
127 /* Rest is reqpath. */
128 if (pdp->posix_path) {
129 status = check_path_syntax_posix(pdp->reqpath, p);
130 } else {
131 if (allow_wcards) {
132 status = check_path_syntax_wcard(pdp->reqpath, p, ppath_contains_wcard);
133 } else {
134 status = check_path_syntax(pdp->reqpath, p);
138 if (!NT_STATUS_IS_OK(status)) {
139 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
140 p, nt_errstr(status) ));
141 return status;
144 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
145 return NT_STATUS_OK;
148 /********************************************************
149 Fake up a connection struct for the VFS layer.
150 Note this CHANGES CWD !!!! JRA.
151 *********************************************************/
153 static NTSTATUS create_conn_struct(connection_struct *conn, int snum, const char *path)
155 pstring connpath;
157 ZERO_STRUCTP(conn);
159 pstrcpy(connpath, path);
160 pstring_sub(connpath , "%S", lp_servicename(snum));
162 /* needed for smbd_vfs_init() */
164 if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) {
165 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
166 return NT_STATUS_NO_MEMORY;
169 if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx, struct share_params))) {
170 DEBUG(0, ("TALLOC failed\n"));
171 return NT_STATUS_NO_MEMORY;
174 conn->params->service = snum;
176 set_conn_connectpath(conn, connpath);
178 if (!smbd_vfs_init(conn)) {
179 NTSTATUS status = map_nt_error_from_unix(errno);
180 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
181 conn_free_internal(conn);
182 return status;
186 * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
187 * share as the anonymous user. If we try to chdir as that user we will
188 * fail.... WTF ? JRA.
191 if (vfs_ChDir(conn,conn->connectpath) != 0) {
192 NTSTATUS status = map_nt_error_from_unix(errno);
193 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
194 conn->connectpath, strerror(errno) ));
195 conn_free_internal(conn);
196 return status;
199 return NT_STATUS_OK;
202 /**********************************************************************
203 Parse the contents of a symlink to verify if it is an msdfs referral
204 A valid referral is of the form:
206 msdfs:server1\share1,server2\share2
207 msdfs:server1\share1\pathname,server2\share2\pathname
208 msdfs:server1/share1,server2/share2
209 msdfs:server1/share1/pathname,server2/share2/pathname.
211 Note that the alternate paths returned here must be of the canonicalized
212 form:
214 \server\share or
215 \server\share\path\to\file,
217 even in posix path mode. This is because we have no knowledge if the
218 server we're referring to understands posix paths.
219 **********************************************************************/
221 static BOOL parse_msdfs_symlink(TALLOC_CTX *ctx,
222 char *target,
223 struct referral **preflist,
224 int *refcount)
226 pstring temp;
227 char *prot;
228 char *alt_path[MAX_REFERRAL_COUNT];
229 int count = 0, i;
230 struct referral *reflist;
232 pstrcpy(temp,target);
233 prot = strtok(temp,":");
234 if (!prot) {
235 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
236 return False;
239 /* parse out the alternate paths */
240 while((count<MAX_REFERRAL_COUNT) &&
241 ((alt_path[count] = strtok(NULL,",")) != NULL)) {
242 count++;
245 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
247 if (count) {
248 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx, struct referral, count);
249 if(reflist == NULL) {
250 DEBUG(0,("parse_msdfs_symlink: talloc failed!\n"));
251 return False;
253 } else {
254 reflist = *preflist = NULL;
257 for(i=0;i<count;i++) {
258 char *p;
260 /* Canonicalize link target. Replace all /'s in the path by a \ */
261 string_replace(alt_path[i], '/', '\\');
263 /* Remove leading '\\'s */
264 p = alt_path[i];
265 while (*p && (*p == '\\')) {
266 p++;
269 pstrcpy(reflist[i].alternate_path, "\\");
270 pstrcat(reflist[i].alternate_path, p);
272 reflist[i].proximity = 0;
273 reflist[i].ttl = REFERRAL_TTL;
274 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n", reflist[i].alternate_path));
275 *refcount += 1;
278 return True;
281 /**********************************************************************
282 Returns true if the unix path is a valid msdfs symlink and also
283 returns the target string from inside the link.
284 **********************************************************************/
286 BOOL is_msdfs_link(connection_struct *conn,
287 const char *path,
288 pstring link_target,
289 SMB_STRUCT_STAT *sbufp)
291 SMB_STRUCT_STAT st;
292 int referral_len = 0;
294 if (sbufp == NULL) {
295 sbufp = &st;
298 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
299 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
300 return False;
303 if (!S_ISLNK(sbufp->st_mode)) {
304 DEBUG(5,("is_msdfs_link: %s is not a link.\n",path));
305 return False;
308 /* open the link and read it */
309 referral_len = SMB_VFS_READLINK(conn, path, link_target, sizeof(pstring)-1);
310 if (referral_len == -1) {
311 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n",
312 path, strerror(errno)));
313 return False;
315 link_target[referral_len] = '\0';
317 DEBUG(5,("is_msdfs_link: %s -> %s\n",path, link_target));
319 if (!strnequal(link_target, "msdfs:", 6)) {
320 return False;
322 return True;
325 /*****************************************************************
326 Used by other functions to decide if a dfs path is remote,
327 and to get the list of referred locations for that remote path.
329 search_flag: For findfirsts, dfs links themselves are not
330 redirected, but paths beyond the links are. For normal smb calls,
331 even dfs links need to be redirected.
333 consumedcntp: how much of the dfs path is being redirected. the client
334 should try the remaining path on the redirected server.
336 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
337 link redirect are in targetpath.
338 *****************************************************************/
340 static NTSTATUS dfs_path_lookup(connection_struct *conn,
341 const char *dfspath, /* Incoming complete dfs path */
342 const struct dfs_path *pdp, /* Parsed out server+share+extrapath. */
343 BOOL search_flag, /* Called from a findfirst ? */
344 int *consumedcntp,
345 pstring targetpath)
347 char *p = NULL;
348 char *q = NULL;
349 SMB_STRUCT_STAT sbuf;
350 NTSTATUS status;
351 pstring localpath;
352 pstring canon_dfspath; /* Canonicalized dfs path. (only '/' components). */
354 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
355 conn->connectpath, pdp->reqpath));
358 * Note the unix path conversion here we're doing we can
359 * throw away. We're looking for a symlink for a dfs
360 * resolution, if we don't find it we'll do another
361 * unix_convert later in the codepath.
362 * If we needed to remember what we'd resolved in
363 * dp->reqpath (as the original code did) we'd
364 * pstrcpy(localhost, dp->reqpath) on any code
365 * path below that returns True - but I don't
366 * think this is needed. JRA.
369 pstrcpy(localpath, pdp->reqpath);
370 status = unix_convert(conn, localpath, search_flag, NULL, &sbuf);
371 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
372 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
373 return status;
376 /* Optimization - check if we can redirect the whole path. */
378 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
379 if (search_flag) {
380 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
381 "for dfs link %s.\n", dfspath));
382 return NT_STATUS_OK;
385 DEBUG(6,("dfs_path_lookup: %s resolves to a "
386 "valid dfs link %s.\n", dfspath, targetpath));
388 if (consumedcntp) {
389 *consumedcntp = strlen(dfspath);
391 return NT_STATUS_PATH_NOT_COVERED;
394 /* Prepare to test only for '/' components in the given path,
395 * so if a Windows path replace all '\\' characters with '/'.
396 * For a POSIX DFS path we know all separators are already '/'. */
398 pstrcpy(canon_dfspath, dfspath);
399 if (!pdp->posix_path) {
400 string_replace(canon_dfspath, '\\', '/');
404 * localpath comes out of unix_convert, so it has
405 * no trailing backslash. Make sure that canon_dfspath hasn't either.
406 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
409 trim_char(canon_dfspath,0,'/');
412 * Redirect if any component in the path is a link.
413 * We do this by walking backwards through the
414 * local path, chopping off the last component
415 * in both the local path and the canonicalized
416 * DFS path. If we hit a DFS link then we're done.
419 p = strrchr_m(localpath, '/');
420 if (consumedcntp) {
421 q = strrchr_m(canon_dfspath, '/');
424 while (p) {
425 *p = '\0';
426 if (q) {
427 *q = '\0';
430 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
431 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
432 "parent %s is dfs link\n", dfspath, localpath));
434 if (consumedcntp) {
435 *consumedcntp = strlen(canon_dfspath);
436 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
437 "(%d)\n", canon_dfspath, *consumedcntp));
440 return NT_STATUS_PATH_NOT_COVERED;
443 /* Step back on the filesystem. */
444 p = strrchr_m(localpath, '/');
446 if (consumedcntp) {
447 /* And in the canonicalized dfs path. */
448 q = strrchr_m(canon_dfspath, '/');
452 return NT_STATUS_OK;
455 /*****************************************************************
456 Decides if a dfs pathname should be redirected or not.
457 If not, the pathname is converted to a tcon-relative local unix path
459 search_wcard_flag: this flag performs 2 functions bother related
460 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
461 for details.
463 This function can return NT_STATUS_OK, meaning use the returned path as-is
464 (mapped into a local path).
465 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
466 any other NT_STATUS error which is a genuine error to be
467 returned to the client.
468 *****************************************************************/
470 static NTSTATUS dfs_redirect( connection_struct *conn,
471 pstring dfs_path,
472 BOOL search_wcard_flag,
473 BOOL *ppath_contains_wcard)
475 NTSTATUS status;
476 struct dfs_path dp;
477 pstring targetpath;
479 status = parse_dfs_path(dfs_path, search_wcard_flag, &dp, ppath_contains_wcard);
480 if (!NT_STATUS_IS_OK(status)) {
481 return status;
484 if (dp.reqpath[0] == '\0') {
485 pstrcpy(dfs_path, dp.reqpath);
486 DEBUG(5,("dfs_redirect: self-referral.\n"));
487 return NT_STATUS_OK;
490 /* If dfs pathname for a non-dfs share, convert to tcon-relative
491 path and return OK */
493 if (!lp_msdfs_root(SNUM(conn))) {
494 pstrcpy(dfs_path, dp.reqpath);
495 return NT_STATUS_OK;
498 /* If it looked like a local path (zero hostname/servicename)
499 * just treat as a tcon-relative path. */
501 if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') {
502 pstrcpy(dfs_path, dp.reqpath);
503 return NT_STATUS_OK;
506 if (!( strequal(dp.servicename, lp_servicename(SNUM(conn)))
507 || (strequal(dp.servicename, HOMES_NAME)
508 && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) {
510 /* The given sharename doesn't match this connection. */
512 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
515 status = dfs_path_lookup(conn, dfs_path, &dp,
516 search_wcard_flag, NULL, targetpath);
517 if (!NT_STATUS_IS_OK(status)) {
518 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
519 DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path));
520 } else {
521 DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
522 dfs_path, nt_errstr(status) ));
524 return status;
527 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path));
529 /* Form non-dfs tcon-relative path */
530 pstrcpy(dfs_path, dp.reqpath);
532 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path));
533 return NT_STATUS_OK;
536 /**********************************************************************
537 Return a self referral.
538 **********************************************************************/
540 static NTSTATUS self_ref(TALLOC_CTX *ctx,
541 const char *dfs_path,
542 struct junction_map *jucn,
543 int *consumedcntp,
544 BOOL *self_referralp)
546 struct referral *ref;
548 *self_referralp = True;
550 jucn->referral_count = 1;
551 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
552 DEBUG(0,("self_ref: talloc failed for referral\n"));
553 return NT_STATUS_NO_MEMORY;
556 pstrcpy(ref->alternate_path,dfs_path);
557 ref->proximity = 0;
558 ref->ttl = REFERRAL_TTL;
559 jucn->referral_list = ref;
560 *consumedcntp = strlen(dfs_path);
561 return NT_STATUS_OK;
564 /**********************************************************************
565 Gets valid referrals for a dfs path and fills up the
566 junction_map structure.
567 **********************************************************************/
569 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
570 const char *dfs_path,
571 struct junction_map *jucn,
572 int *consumedcntp,
573 BOOL *self_referralp)
575 struct connection_struct conns;
576 struct connection_struct *conn = &conns;
577 struct dfs_path dp;
578 pstring conn_path;
579 pstring targetpath;
580 int snum;
581 NTSTATUS status = NT_STATUS_NOT_FOUND;
582 BOOL dummy;
584 ZERO_STRUCT(conns);
586 *self_referralp = False;
588 status = parse_dfs_path(dfs_path, False, &dp, &dummy);
589 if (!NT_STATUS_IS_OK(status)) {
590 return status;
593 fstrcpy(jucn->service_name, dp.servicename);
594 pstrcpy(jucn->volume_name, dp.reqpath);
596 /* Verify the share is a dfs root */
597 snum = lp_servicenumber(jucn->service_name);
598 if(snum < 0) {
599 if ((snum = find_service(jucn->service_name)) < 0) {
600 return NT_STATUS_NOT_FOUND;
604 if (!lp_msdfs_root(snum)) {
605 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
606 dp.servicename, dfs_path));
607 return NT_STATUS_NOT_FOUND;
611 * Self referrals are tested with a anonymous IPC connection and
612 * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
613 * to an empty string). create_conn_struct cd's into the directory and will
614 * fail if it cannot (as the anonymous user). Cope with this.
617 if (dp.reqpath[0] == '\0') {
618 struct referral *ref;
620 if (*lp_msdfs_proxy(snum) == '\0') {
621 return self_ref(ctx,
622 dfs_path,
623 jucn,
624 consumedcntp,
625 self_referralp);
629 * It's an msdfs proxy share. Redirect to
630 * the configured target share.
633 jucn->referral_count = 1;
634 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
635 DEBUG(0, ("malloc failed for referral\n"));
636 return NT_STATUS_NO_MEMORY;
639 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
640 if (dp.reqpath[0] != '\0') {
641 pstrcat(ref->alternate_path, dp.reqpath);
643 ref->proximity = 0;
644 ref->ttl = REFERRAL_TTL;
645 jucn->referral_list = ref;
646 *consumedcntp = strlen(dfs_path);
647 return NT_STATUS_OK;
650 pstrcpy(conn_path, lp_pathname(snum));
651 status = create_conn_struct(conn, snum, conn_path);
652 if (!NT_STATUS_IS_OK(status)) {
653 return status;
656 /* If this is a DFS path dfs_lookup should return
657 * NT_STATUS_PATH_NOT_COVERED. */
659 status = dfs_path_lookup(conn, dfs_path, &dp,
660 False, consumedcntp, targetpath);
662 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
663 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
664 dfs_path));
665 conn_free_internal(conn);
666 return status;
669 /* We know this is a valid dfs link. Parse the targetpath. */
670 if (!parse_msdfs_symlink(ctx, targetpath,
671 &jucn->referral_list,
672 &jucn->referral_count)) {
673 DEBUG(3,("get_referred_path: failed to parse symlink "
674 "target %s\n", targetpath ));
675 conn_free_internal(conn);
676 return NT_STATUS_NOT_FOUND;
679 conn_free_internal(conn);
680 return NT_STATUS_OK;
683 static int setup_ver2_dfs_referral(const char *pathname,
684 char **ppdata,
685 struct junction_map *junction,
686 int consumedcnt,
687 BOOL self_referral)
689 char* pdata = *ppdata;
691 unsigned char uni_requestedpath[1024];
692 int uni_reqpathoffset1,uni_reqpathoffset2;
693 int uni_curroffset;
694 int requestedpathlen=0;
695 int offset;
696 int reply_size = 0;
697 int i=0;
699 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
701 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
702 STR_TERMINATE);
704 if (DEBUGLVL(10)) {
705 dump_data(0, (const char *) uni_requestedpath,requestedpathlen);
708 DEBUG(10,("ref count = %u\n",junction->referral_count));
710 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
711 VERSION2_REFERRAL_SIZE * junction->referral_count;
713 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
715 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
717 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
718 2 * requestedpathlen;
719 DEBUG(10,("reply_size: %u\n",reply_size));
721 /* add up the unicode lengths of all the referral paths */
722 for(i=0;i<junction->referral_count;i++) {
723 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
724 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
727 DEBUG(10,("reply_size = %u\n",reply_size));
728 /* add the unexplained 0x16 bytes */
729 reply_size += 0x16;
731 pdata = (char *)SMB_REALLOC(pdata,reply_size);
732 if(pdata == NULL) {
733 DEBUG(0,("Realloc failed!\n"));
734 return -1;
736 *ppdata = pdata;
738 /* copy in the dfs requested paths.. required for offset calculations */
739 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
740 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
742 /* create the header */
743 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
744 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
745 if(self_referral) {
746 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
747 } else {
748 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
751 offset = 8;
752 /* add the referral elements */
753 for(i=0;i<junction->referral_count;i++) {
754 struct referral* ref = &junction->referral_list[i];
755 int unilen;
757 SSVAL(pdata,offset,2); /* version 2 */
758 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
759 if(self_referral) {
760 SSVAL(pdata,offset+4,1);
761 } else {
762 SSVAL(pdata,offset+4,0);
764 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
765 SIVAL(pdata,offset+8,ref->proximity);
766 SIVAL(pdata,offset+12,ref->ttl);
768 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
769 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
770 /* copy referred path into current offset */
771 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
772 sizeof(pstring), STR_UNICODE);
774 SSVAL(pdata,offset+20,uni_curroffset-offset);
776 uni_curroffset += unilen;
777 offset += VERSION2_REFERRAL_SIZE;
779 /* add in the unexplained 22 (0x16) bytes at the end */
780 memset(pdata+uni_curroffset,'\0',0x16);
781 return reply_size;
784 static int setup_ver3_dfs_referral(const char *pathname,
785 char **ppdata,
786 struct junction_map *junction,
787 int consumedcnt,
788 BOOL self_referral)
790 char* pdata = *ppdata;
792 unsigned char uni_reqpath[1024];
793 int uni_reqpathoffset1, uni_reqpathoffset2;
794 int uni_curroffset;
795 int reply_size = 0;
797 int reqpathlen = 0;
798 int offset,i=0;
800 DEBUG(10,("setting up version3 referral\n"));
802 reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
804 if (DEBUGLVL(10)) {
805 dump_data(0, (char *) uni_reqpath,reqpathlen);
808 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
809 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
810 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
812 for(i=0;i<junction->referral_count;i++) {
813 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
814 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
817 pdata = (char *)SMB_REALLOC(pdata,reply_size);
818 if(pdata == NULL) {
819 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
820 return -1;
822 *ppdata = pdata;
824 /* create the header */
825 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
826 SSVAL(pdata,2,junction->referral_count); /* number of referral */
827 if(self_referral) {
828 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
829 } else {
830 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
833 /* copy in the reqpaths */
834 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
835 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
837 offset = 8;
838 for(i=0;i<junction->referral_count;i++) {
839 struct referral* ref = &(junction->referral_list[i]);
840 int unilen;
842 SSVAL(pdata,offset,3); /* version 3 */
843 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
844 if(self_referral) {
845 SSVAL(pdata,offset+4,1);
846 } else {
847 SSVAL(pdata,offset+4,0);
850 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
851 SIVAL(pdata,offset+8,ref->ttl);
853 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
854 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
855 /* copy referred path into current offset */
856 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
857 sizeof(pstring), STR_UNICODE | STR_TERMINATE);
858 SSVAL(pdata,offset+16,uni_curroffset-offset);
859 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
860 memset(pdata+offset+18,'\0',16);
862 uni_curroffset += unilen;
863 offset += VERSION3_REFERRAL_SIZE;
865 return reply_size;
868 /******************************************************************
869 Set up the DFS referral for the dfs pathname. This call returns
870 the amount of the path covered by this server, and where the
871 client should be redirected to. This is the meat of the
872 TRANS2_GET_DFS_REFERRAL call.
873 ******************************************************************/
875 int setup_dfs_referral(connection_struct *orig_conn,
876 const char *dfs_path,
877 int max_referral_level,
878 char **ppdata, NTSTATUS *pstatus)
880 struct junction_map junction;
881 int consumedcnt = 0;
882 BOOL self_referral = False;
883 int reply_size = 0;
884 char *pathnamep = NULL;
885 pstring local_dfs_path;
886 TALLOC_CTX *ctx;
888 if (!(ctx=talloc_init("setup_dfs_referral"))) {
889 *pstatus = NT_STATUS_NO_MEMORY;
890 return -1;
893 ZERO_STRUCT(junction);
895 /* get the junction entry */
896 if (!dfs_path) {
897 talloc_destroy(ctx);
898 *pstatus = NT_STATUS_NOT_FOUND;
899 return -1;
903 * Trim pathname sent by client so it begins with only one backslash.
904 * Two backslashes confuse some dfs clients
907 pstrcpy(local_dfs_path, dfs_path);
908 pathnamep = local_dfs_path;
909 while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) {
910 pathnamep++;
913 /* The following call can change cwd. */
914 *pstatus = get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral);
915 if (!NT_STATUS_IS_OK(*pstatus)) {
916 vfs_ChDir(orig_conn,orig_conn->connectpath);
917 talloc_destroy(ctx);
918 return -1;
920 vfs_ChDir(orig_conn,orig_conn->connectpath);
922 if (!self_referral) {
923 pathnamep[consumedcnt] = '\0';
925 if( DEBUGLVL( 3 ) ) {
926 int i=0;
927 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
928 for(i=0;i<junction.referral_count;i++)
929 dbgtext(" %s",junction.referral_list[i].alternate_path);
930 dbgtext(".\n");
934 /* create the referral depeding on version */
935 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
937 if (max_referral_level < 2) {
938 max_referral_level = 2;
940 if (max_referral_level > 3) {
941 max_referral_level = 3;
944 switch(max_referral_level) {
945 case 2:
946 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
947 consumedcnt, self_referral);
948 break;
949 case 3:
950 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
951 consumedcnt, self_referral);
952 break;
953 default:
954 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
955 talloc_destroy(ctx);
956 *pstatus = NT_STATUS_INVALID_LEVEL;
957 return -1;
960 if (DEBUGLVL(10)) {
961 DEBUGADD(0,("DFS Referral pdata:\n"));
962 dump_data(0,*ppdata,reply_size);
965 talloc_destroy(ctx);
966 *pstatus = NT_STATUS_OK;
967 return reply_size;
970 /**********************************************************************
971 The following functions are called by the NETDFS RPC pipe functions
972 **********************************************************************/
974 /*********************************************************************
975 Creates a junction structure from a DFS pathname
976 **********************************************************************/
978 BOOL create_junction(const char *dfs_path, struct junction_map *jucn)
980 int snum;
981 BOOL dummy;
982 struct dfs_path dp;
984 NTSTATUS status = parse_dfs_path(dfs_path, False, &dp, &dummy);
986 if (!NT_STATUS_IS_OK(status)) {
987 return False;
990 /* check if path is dfs : validate first token */
991 if (!is_myname_or_ipaddr(dp.hostname)) {
992 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
993 dp.hostname, dfs_path));
994 return False;
997 /* Check for a non-DFS share */
998 snum = lp_servicenumber(dp.servicename);
1000 if(snum < 0 || !lp_msdfs_root(snum)) {
1001 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1002 dp.servicename));
1003 return False;
1006 fstrcpy(jucn->service_name,dp.servicename);
1007 pstrcpy(jucn->volume_name,dp.reqpath);
1008 pstrcpy(jucn->comment, lp_comment(snum));
1009 return True;
1012 /**********************************************************************
1013 Forms a valid Unix pathname from the junction
1014 **********************************************************************/
1016 static BOOL junction_to_local_path(struct junction_map *jucn,
1017 char *path,
1018 int max_pathlen,
1019 connection_struct *conn_out)
1021 int snum;
1022 pstring conn_path;
1024 snum = lp_servicenumber(jucn->service_name);
1025 if(snum < 0) {
1026 return False;
1029 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
1030 safe_strcat(path, "/", max_pathlen-1);
1031 safe_strcat(path, jucn->volume_name, max_pathlen-1);
1033 pstrcpy(conn_path, lp_pathname(snum));
1034 if (!NT_STATUS_IS_OK(create_conn_struct(conn_out, snum, conn_path))) {
1035 return False;
1038 return True;
1041 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
1043 pstring path;
1044 pstring msdfs_link;
1045 connection_struct conns;
1046 connection_struct *conn = &conns;
1047 int i=0;
1048 BOOL insert_comma = False;
1049 BOOL ret = False;
1051 ZERO_STRUCT(conns);
1053 if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
1054 return False;
1057 /* Form the msdfs_link contents */
1058 pstrcpy(msdfs_link, "msdfs:");
1059 for(i=0; i<jucn->referral_count; i++) {
1060 char* refpath = jucn->referral_list[i].alternate_path;
1062 /* Alternate paths always use Windows separators. */
1063 trim_char(refpath, '\\', '\\');
1064 if(*refpath == '\0') {
1065 if (i == 0) {
1066 insert_comma = False;
1068 continue;
1070 if (i > 0 && insert_comma) {
1071 pstrcat(msdfs_link, ",");
1074 pstrcat(msdfs_link, refpath);
1075 if (!insert_comma) {
1076 insert_comma = True;
1080 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1081 path, msdfs_link));
1083 if(exists) {
1084 if(SMB_VFS_UNLINK(conn,path)!=0) {
1085 goto out;
1089 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1090 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
1091 path, msdfs_link, strerror(errno)));
1092 goto out;
1096 ret = True;
1098 out:
1100 conn_free_internal(conn);
1101 return ret;
1104 BOOL remove_msdfs_link(struct junction_map *jucn)
1106 pstring path;
1107 connection_struct conns;
1108 connection_struct *conn = &conns;
1109 BOOL ret = False;
1111 ZERO_STRUCT(conns);
1113 if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1114 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1115 ret = True;
1117 talloc_destroy( conn->mem_ctx );
1120 conn_free_internal(conn);
1121 return ret;
1124 static int form_junctions(TALLOC_CTX *ctx,
1125 int snum,
1126 struct junction_map *jucn,
1127 int jn_remain)
1129 int cnt = 0;
1130 SMB_STRUCT_DIR *dirp;
1131 char *dname;
1132 pstring connect_path;
1133 char *service_name = lp_servicename(snum);
1134 connection_struct conn;
1135 struct referral *ref = NULL;
1137 ZERO_STRUCT(conn);
1139 if (jn_remain <= 0) {
1140 return 0;
1143 pstrcpy(connect_path,lp_pathname(snum));
1145 if(*connect_path == '\0') {
1146 return 0;
1150 * Fake up a connection struct for the VFS layer.
1153 if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
1154 return 0;
1157 /* form a junction for the msdfs root - convention
1158 DO NOT REMOVE THIS: NT clients will not work with us
1159 if this is not present
1161 fstrcpy(jucn[cnt].service_name, service_name);
1162 jucn[cnt].volume_name[0] = '\0';
1163 jucn[cnt].referral_count = 1;
1165 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1166 if (jucn[cnt].referral_list == NULL) {
1167 DEBUG(0, ("talloc failed!\n"));
1168 goto out;
1171 ref->proximity = 0;
1172 ref->ttl = REFERRAL_TTL;
1173 if (*lp_msdfs_proxy(snum) != '\0') {
1174 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1175 cnt++;
1176 goto out;
1179 pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
1180 get_local_machine_name(),
1181 service_name);
1182 cnt++;
1184 /* Now enumerate all dfs links */
1185 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1186 if(!dirp) {
1187 goto out;
1190 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1191 pstring link_target;
1192 if (cnt >= jn_remain) {
1193 SMB_VFS_CLOSEDIR(&conn,dirp);
1194 DEBUG(2, ("ran out of MSDFS junction slots"));
1195 goto out;
1197 if (is_msdfs_link(&conn, dname, link_target, NULL)) {
1198 if (parse_msdfs_symlink(ctx,
1199 link_target,
1200 &jucn[cnt].referral_list,
1201 &jucn[cnt].referral_count)) {
1203 fstrcpy(jucn[cnt].service_name, service_name);
1204 pstrcpy(jucn[cnt].volume_name, dname);
1205 cnt++;
1210 SMB_VFS_CLOSEDIR(&conn,dirp);
1212 out:
1214 conn_free_internal(&conn);
1215 return cnt;
1218 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1220 int i=0;
1221 int sharecount = 0;
1222 int jn_count = 0;
1224 if(!lp_host_msdfs()) {
1225 return 0;
1228 /* Ensure all the usershares are loaded. */
1229 become_root();
1230 sharecount = load_usershare_shares();
1231 unbecome_root();
1233 for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1234 if(lp_msdfs_root(i)) {
1235 jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1238 return jn_count;
1241 /******************************************************************************
1242 Core function to resolve a dfs pathname.
1243 ******************************************************************************/
1245 NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name)
1247 NTSTATUS status = NT_STATUS_OK;
1248 BOOL dummy;
1249 if (dfs_pathnames) {
1250 status = dfs_redirect(conn, name, False, &dummy);
1252 return status;
1255 /******************************************************************************
1256 Core function to resolve a dfs pathname possibly containing a wildcard.
1257 This function is identical to the above except for the BOOL param to
1258 dfs_redirect but I need this to be separate so it's really clear when
1259 we're allowing wildcards and when we're not. JRA.
1260 ******************************************************************************/
1262 NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard)
1264 NTSTATUS status = NT_STATUS_OK;
1265 if (dfs_pathnames) {
1266 status = dfs_redirect(conn, name, True, ppath_contains_wcard);
1268 return status;