r23510: Tidy calls to smb_panic by removing trailing newlines. Print the
[Samba.git] / source3 / smbd / msdfs.c
blob9f203bfd1909df3b6ad0a12eceff6d1d1da569dc
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)) {
392 return status;
395 /* Optimization - check if we can redirect the whole path. */
397 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
398 if (search_flag) {
399 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
400 "for dfs link %s.\n", dfspath));
401 return NT_STATUS_OK;
404 DEBUG(6,("dfs_path_lookup: %s resolves to a "
405 "valid dfs link %s.\n", dfspath, targetpath));
407 if (consumedcntp) {
408 *consumedcntp = strlen(dfspath);
410 return NT_STATUS_PATH_NOT_COVERED;
413 /* Prepare to test only for '/' components in the given path,
414 * so if a Windows path replace all '\\' characters with '/'.
415 * For a POSIX DFS path we know all separators are already '/'. */
417 pstrcpy(canon_dfspath, dfspath);
418 if (!pdp->posix_path) {
419 string_replace(canon_dfspath, '\\', '/');
423 * Redirect if any component in the path is a link.
424 * We do this by walking backwards through the
425 * local path, chopping off the last component
426 * in both the local path and the canonicalized
427 * DFS path. If we hit a DFS link then we're done.
430 p = strrchr_m(localpath, '/');
431 if (consumedcntp) {
432 q = strrchr_m(canon_dfspath, '/');
435 while (p) {
436 *p = '\0';
437 if (q) {
438 *q = '\0';
441 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
442 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
443 "parent %s is dfs link\n", dfspath, localpath));
445 if (consumedcntp) {
446 *consumedcntp = strlen(canon_dfspath);
447 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
448 "(%d)\n", canon_dfspath, *consumedcntp));
451 return NT_STATUS_PATH_NOT_COVERED;
454 /* Step back on the filesystem. */
455 p = strrchr_m(localpath, '/');
457 if (consumedcntp) {
458 /* And in the canonicalized dfs path. */
459 q = strrchr_m(canon_dfspath, '/');
463 return NT_STATUS_OK;
466 /*****************************************************************
467 Decides if a dfs pathname should be redirected or not.
468 If not, the pathname is converted to a tcon-relative local unix path
470 search_wcard_flag: this flag performs 2 functions bother related
471 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
472 for details.
474 This function can return NT_STATUS_OK, meaning use the returned path as-is
475 (mapped into a local path).
476 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
477 any other NT_STATUS error which is a genuine error to be
478 returned to the client.
479 *****************************************************************/
481 static NTSTATUS dfs_redirect( connection_struct *conn,
482 pstring dfs_path,
483 BOOL search_wcard_flag,
484 BOOL *ppath_contains_wcard)
486 NTSTATUS status;
487 struct dfs_path dp;
488 pstring targetpath;
490 status = parse_dfs_path(dfs_path, search_wcard_flag, &dp, ppath_contains_wcard);
491 if (!NT_STATUS_IS_OK(status)) {
492 return status;
495 if (dp.reqpath[0] == '\0') {
496 pstrcpy(dfs_path, dp.reqpath);
497 DEBUG(5,("dfs_redirect: self-referral.\n"));
498 return NT_STATUS_OK;
501 /* If dfs pathname for a non-dfs share, convert to tcon-relative
502 path and return OK */
504 if (!lp_msdfs_root(SNUM(conn))) {
505 pstrcpy(dfs_path, dp.reqpath);
506 return NT_STATUS_OK;
509 /* If it looked like a local path (zero hostname/servicename)
510 * just treat as a tcon-relative path. */
512 if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') {
513 pstrcpy(dfs_path, dp.reqpath);
514 return NT_STATUS_OK;
517 if (!( strequal(dp.servicename, lp_servicename(SNUM(conn)))
518 || (strequal(dp.servicename, HOMES_NAME)
519 && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) {
521 /* The given sharename doesn't match this connection. */
523 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
526 status = dfs_path_lookup(conn, dfs_path, &dp,
527 search_wcard_flag, NULL, targetpath);
528 if (!NT_STATUS_IS_OK(status)) {
529 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
530 DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path));
531 } else {
532 DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
533 dfs_path, nt_errstr(status) ));
535 return status;
538 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path));
540 /* Form non-dfs tcon-relative path */
541 pstrcpy(dfs_path, dp.reqpath);
543 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path));
544 return NT_STATUS_OK;
547 /**********************************************************************
548 Return a self referral.
549 **********************************************************************/
551 static NTSTATUS self_ref(TALLOC_CTX *ctx,
552 const char *dfs_path,
553 struct junction_map *jucn,
554 int *consumedcntp,
555 BOOL *self_referralp)
557 struct referral *ref;
559 *self_referralp = True;
561 jucn->referral_count = 1;
562 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
563 DEBUG(0,("self_ref: talloc failed for referral\n"));
564 return NT_STATUS_NO_MEMORY;
567 pstrcpy(ref->alternate_path,dfs_path);
568 ref->proximity = 0;
569 ref->ttl = REFERRAL_TTL;
570 jucn->referral_list = ref;
571 *consumedcntp = strlen(dfs_path);
572 return NT_STATUS_OK;
575 /**********************************************************************
576 Gets valid referrals for a dfs path and fills up the
577 junction_map structure.
578 **********************************************************************/
580 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
581 const char *dfs_path,
582 struct junction_map *jucn,
583 int *consumedcntp,
584 BOOL *self_referralp)
586 struct connection_struct conns;
587 struct connection_struct *conn = &conns;
588 struct dfs_path dp;
589 pstring conn_path;
590 pstring targetpath;
591 int snum;
592 NTSTATUS status = NT_STATUS_NOT_FOUND;
593 BOOL dummy;
595 ZERO_STRUCT(conns);
597 *self_referralp = False;
599 status = parse_dfs_path(dfs_path, False, &dp, &dummy);
600 if (!NT_STATUS_IS_OK(status)) {
601 return status;
604 /* Verify hostname in path */
605 if (!is_myname_or_ipaddr(dp.hostname)) {
606 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
607 dp.hostname, dfs_path));
608 return NT_STATUS_NOT_FOUND;
611 fstrcpy(jucn->service_name, dp.servicename);
612 pstrcpy(jucn->volume_name, dp.reqpath);
614 /* Verify the share is a dfs root */
615 snum = lp_servicenumber(jucn->service_name);
616 if(snum < 0) {
617 if ((snum = find_service(jucn->service_name)) < 0) {
618 return NT_STATUS_NOT_FOUND;
622 if (!lp_msdfs_root(snum)) {
623 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
624 dp.servicename, dfs_path));
625 return NT_STATUS_NOT_FOUND;
629 * Self referrals are tested with a anonymous IPC connection and
630 * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
631 * to an empty string). create_conn_struct cd's into the directory and will
632 * fail if it cannot (as the anonymous user). Cope with this.
635 if (dp.reqpath[0] == '\0') {
636 struct referral *ref;
638 if (*lp_msdfs_proxy(snum) == '\0') {
639 return self_ref(ctx,
640 dfs_path,
641 jucn,
642 consumedcntp,
643 self_referralp);
647 * It's an msdfs proxy share. Redirect to
648 * the configured target share.
651 jucn->referral_count = 1;
652 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
653 DEBUG(0, ("malloc failed for referral\n"));
654 return NT_STATUS_NO_MEMORY;
657 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
658 if (dp.reqpath[0] != '\0') {
659 pstrcat(ref->alternate_path, dp.reqpath);
661 ref->proximity = 0;
662 ref->ttl = REFERRAL_TTL;
663 jucn->referral_list = ref;
664 *consumedcntp = strlen(dfs_path);
665 return NT_STATUS_OK;
668 pstrcpy(conn_path, lp_pathname(snum));
669 status = create_conn_struct(conn, snum, conn_path);
670 if (!NT_STATUS_IS_OK(status)) {
671 return status;
674 /* If this is a DFS path dfs_lookup should return
675 * NT_STATUS_PATH_NOT_COVERED. */
677 status = dfs_path_lookup(conn, dfs_path, &dp,
678 False, consumedcntp, targetpath);
680 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
681 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
682 dfs_path));
683 conn_free_internal(conn);
684 return status;
687 /* We know this is a valid dfs link. Parse the targetpath. */
688 if (!parse_msdfs_symlink(ctx, targetpath,
689 &jucn->referral_list,
690 &jucn->referral_count)) {
691 DEBUG(3,("get_referred_path: failed to parse symlink "
692 "target %s\n", targetpath ));
693 conn_free_internal(conn);
694 return NT_STATUS_NOT_FOUND;
697 conn_free_internal(conn);
698 return NT_STATUS_OK;
701 static int setup_ver2_dfs_referral(const char *pathname,
702 char **ppdata,
703 struct junction_map *junction,
704 int consumedcnt,
705 BOOL self_referral)
707 char* pdata = *ppdata;
709 unsigned char uni_requestedpath[1024];
710 int uni_reqpathoffset1,uni_reqpathoffset2;
711 int uni_curroffset;
712 int requestedpathlen=0;
713 int offset;
714 int reply_size = 0;
715 int i=0;
717 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
719 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
720 STR_TERMINATE);
722 if (DEBUGLVL(10)) {
723 dump_data(0, uni_requestedpath,requestedpathlen);
726 DEBUG(10,("ref count = %u\n",junction->referral_count));
728 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
729 VERSION2_REFERRAL_SIZE * junction->referral_count;
731 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
733 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
735 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
736 2 * requestedpathlen;
737 DEBUG(10,("reply_size: %u\n",reply_size));
739 /* add up the unicode lengths of all the referral paths */
740 for(i=0;i<junction->referral_count;i++) {
741 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
742 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
745 DEBUG(10,("reply_size = %u\n",reply_size));
746 /* add the unexplained 0x16 bytes */
747 reply_size += 0x16;
749 pdata = (char *)SMB_REALLOC(pdata,reply_size);
750 if(pdata == NULL) {
751 DEBUG(0,("Realloc failed!\n"));
752 return -1;
754 *ppdata = pdata;
756 /* copy in the dfs requested paths.. required for offset calculations */
757 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
758 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
760 /* create the header */
761 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
762 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
763 if(self_referral) {
764 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
765 } else {
766 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
769 offset = 8;
770 /* add the referral elements */
771 for(i=0;i<junction->referral_count;i++) {
772 struct referral* ref = &junction->referral_list[i];
773 int unilen;
775 SSVAL(pdata,offset,2); /* version 2 */
776 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
777 if(self_referral) {
778 SSVAL(pdata,offset+4,1);
779 } else {
780 SSVAL(pdata,offset+4,0);
782 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
783 SIVAL(pdata,offset+8,ref->proximity);
784 SIVAL(pdata,offset+12,ref->ttl);
786 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
787 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
788 /* copy referred path into current offset */
789 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
790 sizeof(pstring), STR_UNICODE);
792 SSVAL(pdata,offset+20,uni_curroffset-offset);
794 uni_curroffset += unilen;
795 offset += VERSION2_REFERRAL_SIZE;
797 /* add in the unexplained 22 (0x16) bytes at the end */
798 memset(pdata+uni_curroffset,'\0',0x16);
799 return reply_size;
802 static int setup_ver3_dfs_referral(const char *pathname,
803 char **ppdata,
804 struct junction_map *junction,
805 int consumedcnt,
806 BOOL self_referral)
808 char* pdata = *ppdata;
810 unsigned char uni_reqpath[1024];
811 int uni_reqpathoffset1, uni_reqpathoffset2;
812 int uni_curroffset;
813 int reply_size = 0;
815 int reqpathlen = 0;
816 int offset,i=0;
818 DEBUG(10,("setting up version3 referral\n"));
820 reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
822 if (DEBUGLVL(10)) {
823 dump_data(0, uni_reqpath,reqpathlen);
826 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
827 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
828 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
830 for(i=0;i<junction->referral_count;i++) {
831 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
832 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
835 pdata = (char *)SMB_REALLOC(pdata,reply_size);
836 if(pdata == NULL) {
837 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
838 return -1;
840 *ppdata = pdata;
842 /* create the header */
843 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
844 SSVAL(pdata,2,junction->referral_count); /* number of referral */
845 if(self_referral) {
846 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
847 } else {
848 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
851 /* copy in the reqpaths */
852 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
853 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
855 offset = 8;
856 for(i=0;i<junction->referral_count;i++) {
857 struct referral* ref = &(junction->referral_list[i]);
858 int unilen;
860 SSVAL(pdata,offset,3); /* version 3 */
861 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
862 if(self_referral) {
863 SSVAL(pdata,offset+4,1);
864 } else {
865 SSVAL(pdata,offset+4,0);
868 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
869 SIVAL(pdata,offset+8,ref->ttl);
871 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
872 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
873 /* copy referred path into current offset */
874 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
875 sizeof(pstring), STR_UNICODE | STR_TERMINATE);
876 SSVAL(pdata,offset+16,uni_curroffset-offset);
877 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
878 memset(pdata+offset+18,'\0',16);
880 uni_curroffset += unilen;
881 offset += VERSION3_REFERRAL_SIZE;
883 return reply_size;
886 /******************************************************************
887 Set up the DFS referral for the dfs pathname. This call returns
888 the amount of the path covered by this server, and where the
889 client should be redirected to. This is the meat of the
890 TRANS2_GET_DFS_REFERRAL call.
891 ******************************************************************/
893 int setup_dfs_referral(connection_struct *orig_conn,
894 const char *dfs_path,
895 int max_referral_level,
896 char **ppdata, NTSTATUS *pstatus)
898 struct junction_map junction;
899 int consumedcnt = 0;
900 BOOL self_referral = False;
901 int reply_size = 0;
902 char *pathnamep = NULL;
903 pstring local_dfs_path;
904 TALLOC_CTX *ctx;
906 if (!(ctx=talloc_init("setup_dfs_referral"))) {
907 *pstatus = NT_STATUS_NO_MEMORY;
908 return -1;
911 ZERO_STRUCT(junction);
913 /* get the junction entry */
914 if (!dfs_path) {
915 talloc_destroy(ctx);
916 *pstatus = NT_STATUS_NOT_FOUND;
917 return -1;
921 * Trim pathname sent by client so it begins with only one backslash.
922 * Two backslashes confuse some dfs clients
925 pstrcpy(local_dfs_path, dfs_path);
926 pathnamep = local_dfs_path;
927 while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) {
928 pathnamep++;
931 /* The following call can change cwd. */
932 *pstatus = get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral);
933 if (!NT_STATUS_IS_OK(*pstatus)) {
934 vfs_ChDir(orig_conn,orig_conn->connectpath);
935 talloc_destroy(ctx);
936 return -1;
938 vfs_ChDir(orig_conn,orig_conn->connectpath);
940 if (!self_referral) {
941 pathnamep[consumedcnt] = '\0';
943 if( DEBUGLVL( 3 ) ) {
944 int i=0;
945 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
946 for(i=0;i<junction.referral_count;i++)
947 dbgtext(" %s",junction.referral_list[i].alternate_path);
948 dbgtext(".\n");
952 /* create the referral depeding on version */
953 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
955 if (max_referral_level < 2) {
956 max_referral_level = 2;
958 if (max_referral_level > 3) {
959 max_referral_level = 3;
962 switch(max_referral_level) {
963 case 2:
964 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
965 consumedcnt, self_referral);
966 break;
967 case 3:
968 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
969 consumedcnt, self_referral);
970 break;
971 default:
972 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
973 talloc_destroy(ctx);
974 *pstatus = NT_STATUS_INVALID_LEVEL;
975 return -1;
978 if (DEBUGLVL(10)) {
979 DEBUGADD(0,("DFS Referral pdata:\n"));
980 dump_data(0,(uint8 *)*ppdata,reply_size);
983 talloc_destroy(ctx);
984 *pstatus = NT_STATUS_OK;
985 return reply_size;
988 /**********************************************************************
989 The following functions are called by the NETDFS RPC pipe functions
990 **********************************************************************/
992 /*********************************************************************
993 Creates a junction structure from a DFS pathname
994 **********************************************************************/
996 BOOL create_junction(const char *dfs_path, struct junction_map *jucn)
998 int snum;
999 BOOL dummy;
1000 struct dfs_path dp;
1002 NTSTATUS status = parse_dfs_path(dfs_path, False, &dp, &dummy);
1004 if (!NT_STATUS_IS_OK(status)) {
1005 return False;
1008 /* check if path is dfs : validate first token */
1009 if (!is_myname_or_ipaddr(dp.hostname)) {
1010 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
1011 dp.hostname, dfs_path));
1012 return False;
1015 /* Check for a non-DFS share */
1016 snum = lp_servicenumber(dp.servicename);
1018 if(snum < 0 || !lp_msdfs_root(snum)) {
1019 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1020 dp.servicename));
1021 return False;
1024 fstrcpy(jucn->service_name,dp.servicename);
1025 pstrcpy(jucn->volume_name,dp.reqpath);
1026 pstrcpy(jucn->comment, lp_comment(snum));
1027 return True;
1030 /**********************************************************************
1031 Forms a valid Unix pathname from the junction
1032 **********************************************************************/
1034 static BOOL junction_to_local_path(struct junction_map *jucn,
1035 char *path,
1036 int max_pathlen,
1037 connection_struct *conn_out)
1039 int snum;
1040 pstring conn_path;
1042 snum = lp_servicenumber(jucn->service_name);
1043 if(snum < 0) {
1044 return False;
1047 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
1048 safe_strcat(path, "/", max_pathlen-1);
1049 safe_strcat(path, jucn->volume_name, max_pathlen-1);
1051 pstrcpy(conn_path, lp_pathname(snum));
1052 if (!NT_STATUS_IS_OK(create_conn_struct(conn_out, snum, conn_path))) {
1053 return False;
1056 return True;
1059 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
1061 pstring path;
1062 pstring msdfs_link;
1063 connection_struct conns;
1064 connection_struct *conn = &conns;
1065 int i=0;
1066 BOOL insert_comma = False;
1067 BOOL ret = False;
1069 ZERO_STRUCT(conns);
1071 if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
1072 return False;
1075 /* Form the msdfs_link contents */
1076 pstrcpy(msdfs_link, "msdfs:");
1077 for(i=0; i<jucn->referral_count; i++) {
1078 char* refpath = jucn->referral_list[i].alternate_path;
1080 /* Alternate paths always use Windows separators. */
1081 trim_char(refpath, '\\', '\\');
1082 if(*refpath == '\0') {
1083 if (i == 0) {
1084 insert_comma = False;
1086 continue;
1088 if (i > 0 && insert_comma) {
1089 pstrcat(msdfs_link, ",");
1092 pstrcat(msdfs_link, refpath);
1093 if (!insert_comma) {
1094 insert_comma = True;
1098 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1099 path, msdfs_link));
1101 if(exists) {
1102 if(SMB_VFS_UNLINK(conn,path)!=0) {
1103 goto out;
1107 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1108 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
1109 path, msdfs_link, strerror(errno)));
1110 goto out;
1114 ret = True;
1116 out:
1118 conn_free_internal(conn);
1119 return ret;
1122 BOOL remove_msdfs_link(struct junction_map *jucn)
1124 pstring path;
1125 connection_struct conns;
1126 connection_struct *conn = &conns;
1127 BOOL ret = False;
1129 ZERO_STRUCT(conns);
1131 if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1132 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1133 ret = True;
1135 talloc_destroy( conn->mem_ctx );
1138 conn_free_internal(conn);
1139 return ret;
1142 static int form_junctions(TALLOC_CTX *ctx,
1143 int snum,
1144 struct junction_map *jucn,
1145 int jn_remain)
1147 int cnt = 0;
1148 SMB_STRUCT_DIR *dirp;
1149 char *dname;
1150 pstring connect_path;
1151 char *service_name = lp_servicename(snum);
1152 connection_struct conn;
1153 struct referral *ref = NULL;
1155 ZERO_STRUCT(conn);
1157 if (jn_remain <= 0) {
1158 return 0;
1161 pstrcpy(connect_path,lp_pathname(snum));
1163 if(*connect_path == '\0') {
1164 return 0;
1168 * Fake up a connection struct for the VFS layer.
1171 if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
1172 return 0;
1175 /* form a junction for the msdfs root - convention
1176 DO NOT REMOVE THIS: NT clients will not work with us
1177 if this is not present
1179 fstrcpy(jucn[cnt].service_name, service_name);
1180 jucn[cnt].volume_name[0] = '\0';
1181 jucn[cnt].referral_count = 1;
1183 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1184 if (jucn[cnt].referral_list == NULL) {
1185 DEBUG(0, ("talloc failed!\n"));
1186 goto out;
1189 ref->proximity = 0;
1190 ref->ttl = REFERRAL_TTL;
1191 if (*lp_msdfs_proxy(snum) != '\0') {
1192 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1193 cnt++;
1194 goto out;
1197 pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
1198 get_local_machine_name(),
1199 service_name);
1200 cnt++;
1202 /* Now enumerate all dfs links */
1203 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1204 if(!dirp) {
1205 goto out;
1208 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1209 pstring link_target;
1210 if (cnt >= jn_remain) {
1211 SMB_VFS_CLOSEDIR(&conn,dirp);
1212 DEBUG(2, ("ran out of MSDFS junction slots"));
1213 goto out;
1215 if (is_msdfs_link(&conn, dname, link_target, NULL)) {
1216 if (parse_msdfs_symlink(ctx,
1217 link_target,
1218 &jucn[cnt].referral_list,
1219 &jucn[cnt].referral_count)) {
1221 fstrcpy(jucn[cnt].service_name, service_name);
1222 pstrcpy(jucn[cnt].volume_name, dname);
1223 cnt++;
1228 SMB_VFS_CLOSEDIR(&conn,dirp);
1230 out:
1232 conn_free_internal(&conn);
1233 return cnt;
1236 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1238 int i=0;
1239 int sharecount = 0;
1240 int jn_count = 0;
1242 if(!lp_host_msdfs()) {
1243 return 0;
1246 /* Ensure all the usershares are loaded. */
1247 become_root();
1248 load_registry_shares();
1249 sharecount = load_usershare_shares();
1250 unbecome_root();
1252 for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1253 if(lp_msdfs_root(i)) {
1254 jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1257 return jn_count;
1260 /******************************************************************************
1261 Core function to resolve a dfs pathname.
1262 ******************************************************************************/
1264 NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name)
1266 NTSTATUS status = NT_STATUS_OK;
1267 BOOL dummy;
1268 if (dfs_pathnames) {
1269 status = dfs_redirect(conn, name, False, &dummy);
1271 return status;
1274 /******************************************************************************
1275 Core function to resolve a dfs pathname possibly containing a wildcard.
1276 This function is identical to the above except for the BOOL param to
1277 dfs_redirect but I need this to be separate so it's really clear when
1278 we're allowing wildcards and when we're not. JRA.
1279 ******************************************************************************/
1281 NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard)
1283 NTSTATUS status = NT_STATUS_OK;
1284 if (dfs_pathnames) {
1285 status = dfs_redirect(conn, name, True, ppath_contains_wcard);
1287 return status;