source: trunk/server/source3/smbd/msdfs.c@ 537

Last change on this file since 537 was 480, checked in by Silvan Scherrer, 15 years ago

Samba Server 3.5: trunk update to 3.5.4

File size: 44.8 KB
Line 
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
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
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.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21*/
22
23#define DBGC_CLASS DBGC_MSDFS
24#include "includes.h"
25#include "smbd/globals.h"
26
27/**********************************************************************
28 Parse a DFS pathname of the form \hostname\service\reqpath
29 into the dfs_path structure.
30 If POSIX pathnames is true, the pathname may also be of the
31 form /hostname/service/reqpath.
32 We cope with either here.
33
34 Unfortunately, due to broken clients who might set the
35 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
36 send a local path, we have to cope with that too....
37
38 If conn != NULL then ensure the provided service is
39 the one pointed to by the connection.
40
41 This version does everything using pointers within one copy of the
42 pathname string, talloced on the struct dfs_path pointer (which
43 must be talloced). This may be too clever to live....
44 JRA.
45**********************************************************************/
46
47static NTSTATUS parse_dfs_path(connection_struct *conn,
48 const char *pathname,
49 bool allow_wcards,
50 struct dfs_path *pdp, /* MUST BE TALLOCED */
51 bool *ppath_contains_wcard)
52{
53 char *pathname_local;
54 char *p,*temp;
55 char *servicename;
56 char *eos_ptr;
57 NTSTATUS status = NT_STATUS_OK;
58 char sepchar;
59
60 ZERO_STRUCTP(pdp);
61
62 /*
63 * This is the only talloc we should need to do
64 * on the struct dfs_path. All the pointers inside
65 * it should point to offsets within this string.
66 */
67
68 pathname_local = talloc_strdup(pdp, pathname);
69 if (!pathname_local) {
70 return NT_STATUS_NO_MEMORY;
71 }
72 /* Get a pointer to the terminating '\0' */
73 eos_ptr = &pathname_local[strlen(pathname_local)];
74 p = temp = pathname_local;
75
76 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
77
78 sepchar = pdp->posix_path ? '/' : '\\';
79
80 if (*pathname != sepchar) {
81 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
82 pathname, sepchar ));
83 /*
84 * Possibly client sent a local path by mistake.
85 * Try and convert to a local path.
86 */
87
88 pdp->hostname = eos_ptr; /* "" */
89 pdp->servicename = eos_ptr; /* "" */
90
91 /* We've got no info about separators. */
92 pdp->posix_path = lp_posix_pathnames();
93 p = temp;
94 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
95 "local path\n",
96 temp));
97 goto local_path;
98 }
99
100 /*
101 * Safe to use on talloc'ed string as it only shrinks.
102 * It also doesn't affect the eos_ptr.
103 */
104 trim_char(temp,sepchar,sepchar);
105
106 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
107 temp, sepchar));
108
109 /* Now tokenize. */
110 /* Parse out hostname. */
111 p = strchr_m(temp,sepchar);
112 if(p == NULL) {
113 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
114 temp));
115 /*
116 * Possibly client sent a local path by mistake.
117 * Try and convert to a local path.
118 */
119
120 pdp->hostname = eos_ptr; /* "" */
121 pdp->servicename = eos_ptr; /* "" */
122
123 p = temp;
124 DEBUG(10,("parse_dfs_path: trying to convert %s "
125 "to a local path\n",
126 temp));
127 goto local_path;
128 }
129 *p = '\0';
130 pdp->hostname = temp;
131
132 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
133
134 /* Parse out servicename. */
135 servicename = p+1;
136 p = strchr_m(servicename,sepchar);
137 if (p) {
138 *p = '\0';
139 }
140
141 /* Is this really our servicename ? */
142 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
143 || (strequal(servicename, HOMES_NAME)
144 && strequal(lp_servicename(SNUM(conn)),
145 get_current_username()) )) ) {
146 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
147 servicename));
148
149 /*
150 * Possibly client sent a local path by mistake.
151 * Try and convert to a local path.
152 */
153
154 pdp->hostname = eos_ptr; /* "" */
155 pdp->servicename = eos_ptr; /* "" */
156
157 /* Repair the path - replace the sepchar's
158 we nulled out */
159 servicename--;
160 *servicename = sepchar;
161 if (p) {
162 *p = sepchar;
163 }
164
165 p = temp;
166 DEBUG(10,("parse_dfs_path: trying to convert %s "
167 "to a local path\n",
168 temp));
169 goto local_path;
170 }
171
172 pdp->servicename = servicename;
173
174 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
175
176 if(p == NULL) {
177 /* Client sent self referral \server\share. */
178 pdp->reqpath = eos_ptr; /* "" */
179 return NT_STATUS_OK;
180 }
181
182 p++;
183
184 local_path:
185
186 *ppath_contains_wcard = False;
187
188 pdp->reqpath = p;
189
190 /* Rest is reqpath. */
191 if (pdp->posix_path) {
192 status = check_path_syntax_posix(pdp->reqpath);
193 } else {
194 if (allow_wcards) {
195 status = check_path_syntax_wcard(pdp->reqpath,
196 ppath_contains_wcard);
197 } else {
198 status = check_path_syntax(pdp->reqpath);
199 }
200 }
201
202 if (!NT_STATUS_IS_OK(status)) {
203 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
204 p, nt_errstr(status) ));
205 return status;
206 }
207
208 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
209 return NT_STATUS_OK;
210}
211
212/********************************************************
213 Fake up a connection struct for the VFS layer.
214 Note this CHANGES CWD !!!! JRA.
215*********************************************************/
216
217NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
218 connection_struct **pconn,
219 int snum,
220 const char *path,
221 struct auth_serversupplied_info *server_info,
222 char **poldcwd)
223{
224 connection_struct *conn;
225 char *connpath;
226 char *oldcwd;
227
228 conn = TALLOC_ZERO_P(ctx, connection_struct);
229 if (conn == NULL) {
230 return NT_STATUS_NO_MEMORY;
231 }
232
233 connpath = talloc_strdup(conn, path);
234 if (!connpath) {
235 TALLOC_FREE(conn);
236 return NT_STATUS_NO_MEMORY;
237 }
238 connpath = talloc_string_sub(conn,
239 connpath,
240 "%S",
241 lp_servicename(snum));
242 if (!connpath) {
243 TALLOC_FREE(conn);
244 return NT_STATUS_NO_MEMORY;
245 }
246
247 /* needed for smbd_vfs_init() */
248
249 if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
250 DEBUG(0, ("TALLOC failed\n"));
251 TALLOC_FREE(conn);
252 return NT_STATUS_NO_MEMORY;
253 }
254
255 conn->params->service = snum;
256
257 if (server_info != NULL) {
258 conn->server_info = copy_serverinfo(conn, server_info);
259 if (conn->server_info == NULL) {
260 DEBUG(0, ("copy_serverinfo failed\n"));
261 TALLOC_FREE(conn);
262 return NT_STATUS_NO_MEMORY;
263 }
264 }
265
266 set_conn_connectpath(conn, connpath);
267
268 if (!smbd_vfs_init(conn)) {
269 NTSTATUS status = map_nt_error_from_unix(errno);
270 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
271 conn_free(conn);
272 return status;
273 }
274
275 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
276
277 /*
278 * Windows seems to insist on doing trans2getdfsreferral() calls on
279 * the IPC$ share as the anonymous user. If we try to chdir as that
280 * user we will fail.... WTF ? JRA.
281 */
282
283 oldcwd = vfs_GetWd(ctx, conn);
284 if (oldcwd == NULL) {
285 NTSTATUS status = map_nt_error_from_unix(errno);
286 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
287 conn_free(conn);
288 return status;
289 }
290
291 if (vfs_ChDir(conn,conn->connectpath) != 0) {
292 NTSTATUS status = map_nt_error_from_unix(errno);
293 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
294 "Error was %s\n",
295 conn->connectpath, strerror(errno) ));
296 conn_free(conn);
297 return status;
298 }
299
300 *pconn = conn;
301 *poldcwd = oldcwd;
302
303 return NT_STATUS_OK;
304}
305
306/**********************************************************************
307 Parse the contents of a symlink to verify if it is an msdfs referral
308 A valid referral is of the form:
309
310 msdfs:server1\share1,server2\share2
311 msdfs:server1\share1\pathname,server2\share2\pathname
312 msdfs:server1/share1,server2/share2
313 msdfs:server1/share1/pathname,server2/share2/pathname.
314
315 Note that the alternate paths returned here must be of the canonicalized
316 form:
317
318 \server\share or
319 \server\share\path\to\file,
320
321 even in posix path mode. This is because we have no knowledge if the
322 server we're referring to understands posix paths.
323 **********************************************************************/
324
325static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
326 const char *target,
327 struct referral **preflist,
328 int *refcount)
329{
330 char *temp = NULL;
331 char *prot;
332 char **alt_path = NULL;
333 int count = 0, i;
334 struct referral *reflist;
335 char *saveptr;
336
337 temp = talloc_strdup(ctx, target);
338 if (!temp) {
339 return False;
340 }
341 prot = strtok_r(temp, ":", &saveptr);
342 if (!prot) {
343 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
344 return False;
345 }
346
347 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
348 if (!alt_path) {
349 return False;
350 }
351
352 /* parse out the alternate paths */
353 while((count<MAX_REFERRAL_COUNT) &&
354 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
355 count++;
356 }
357
358 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
359
360 if (count) {
361 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
362 struct referral, count);
363 if(reflist == NULL) {
364 TALLOC_FREE(alt_path);
365 return False;
366 }
367 } else {
368 reflist = *preflist = NULL;
369 }
370
371 for(i=0;i<count;i++) {
372 char *p;
373
374 /* Canonicalize link target.
375 * Replace all /'s in the path by a \ */
376 string_replace(alt_path[i], '/', '\\');
377
378 /* Remove leading '\\'s */
379 p = alt_path[i];
380 while (*p && (*p == '\\')) {
381 p++;
382 }
383
384 reflist[i].alternate_path = talloc_asprintf(ctx,
385 "\\%s",
386 p);
387 if (!reflist[i].alternate_path) {
388 return False;
389 }
390
391 reflist[i].proximity = 0;
392 reflist[i].ttl = REFERRAL_TTL;
393 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
394 reflist[i].alternate_path));
395 }
396
397 *refcount = count;
398
399 TALLOC_FREE(alt_path);
400 return True;
401}
402
403/**********************************************************************
404 Returns true if the unix path is a valid msdfs symlink and also
405 returns the target string from inside the link.
406**********************************************************************/
407
408static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
409 connection_struct *conn,
410 const char *path,
411 char **pp_link_target,
412 SMB_STRUCT_STAT *sbufp)
413{
414 int referral_len = 0;
415#if defined(HAVE_BROKEN_READLINK)
416 char link_target_buf[PATH_MAX];
417#else
418 char link_target_buf[7];
419#endif
420 size_t bufsize = 0;
421 char *link_target = NULL;
422 struct smb_filename smb_fname;
423
424 if (pp_link_target) {
425 bufsize = 1024;
426 link_target = TALLOC_ARRAY(ctx, char, bufsize);
427 if (!link_target) {
428 return False;
429 }
430 *pp_link_target = link_target;
431 } else {
432 bufsize = sizeof(link_target_buf);
433 link_target = link_target_buf;
434 }
435
436 ZERO_STRUCT(smb_fname);
437 smb_fname.base_name = discard_const_p(char, path);
438
439 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
440 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
441 path));
442 goto err;
443 }
444 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
445 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
446 path));
447 goto err;
448 }
449 if (sbufp != NULL) {
450 *sbufp = smb_fname.st;
451 }
452
453 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
454 if (referral_len == -1) {
455 DEBUG(0,("is_msdfs_link_read_target: Error reading "
456 "msdfs link %s: %s\n",
457 path, strerror(errno)));
458 goto err;
459 }
460 link_target[referral_len] = '\0';
461
462 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
463 link_target));
464
465 if (!strnequal(link_target, "msdfs:", 6)) {
466 goto err;
467 }
468 return True;
469
470 err:
471
472 if (link_target != link_target_buf) {
473 TALLOC_FREE(link_target);
474 }
475 return False;
476}
477
478/**********************************************************************
479 Returns true if the unix path is a valid msdfs symlink.
480**********************************************************************/
481
482bool is_msdfs_link(connection_struct *conn,
483 const char *path,
484 SMB_STRUCT_STAT *sbufp)
485{
486 return is_msdfs_link_internal(talloc_tos(),
487 conn,
488 path,
489 NULL,
490 sbufp);
491}
492
493/*****************************************************************
494 Used by other functions to decide if a dfs path is remote,
495 and to get the list of referred locations for that remote path.
496
497 search_flag: For findfirsts, dfs links themselves are not
498 redirected, but paths beyond the links are. For normal smb calls,
499 even dfs links need to be redirected.
500
501 consumedcntp: how much of the dfs path is being redirected. the client
502 should try the remaining path on the redirected server.
503
504 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
505 link redirect are in targetpath.
506*****************************************************************/
507
508static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
509 connection_struct *conn,
510 const char *dfspath, /* Incoming complete dfs path */
511 const struct dfs_path *pdp, /* Parsed out
512 server+share+extrapath. */
513 bool search_flag, /* Called from a findfirst ? */
514 int *consumedcntp,
515 char **pp_targetpath)
516{
517 char *p = NULL;
518 char *q = NULL;
519 NTSTATUS status;
520 struct smb_filename *smb_fname = NULL;
521 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
522 components). */
523
524 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
525 conn->connectpath, pdp->reqpath));
526
527 /*
528 * Note the unix path conversion here we're doing we can
529 * throw away. We're looking for a symlink for a dfs
530 * resolution, if we don't find it we'll do another
531 * unix_convert later in the codepath.
532 * If we needed to remember what we'd resolved in
533 * dp->reqpath (as the original code did) we'd
534 * copy (localhost, dp->reqpath) on any code
535 * path below that returns True - but I don't
536 * think this is needed. JRA.
537 */
538
539 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
540 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
541
542 if (!NT_STATUS_IS_OK(status)) {
543 if (!NT_STATUS_EQUAL(status,
544 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
545 return status;
546 }
547
548 /* Create an smb_fname to use below. */
549 status = create_synthetic_smb_fname(ctx, pdp->reqpath, NULL,
550 NULL, &smb_fname);
551 if (!NT_STATUS_IS_OK(status)) {
552 return status;
553 }
554 }
555
556 /* Optimization - check if we can redirect the whole path. */
557
558 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
559 pp_targetpath, NULL)) {
560 if (search_flag) {
561 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
562 "for dfs link %s.\n", dfspath));
563 status = NT_STATUS_OK;
564 goto out;
565 }
566
567 DEBUG(6,("dfs_path_lookup: %s resolves to a "
568 "valid dfs link %s.\n", dfspath,
569 pp_targetpath ? *pp_targetpath : ""));
570
571 if (consumedcntp) {
572 *consumedcntp = strlen(dfspath);
573 }
574 status = NT_STATUS_PATH_NOT_COVERED;
575 goto out;
576 }
577
578 /* Prepare to test only for '/' components in the given path,
579 * so if a Windows path replace all '\\' characters with '/'.
580 * For a POSIX DFS path we know all separators are already '/'. */
581
582 canon_dfspath = talloc_strdup(ctx, dfspath);
583 if (!canon_dfspath) {
584 status = NT_STATUS_NO_MEMORY;
585 goto out;
586 }
587 if (!pdp->posix_path) {
588 string_replace(canon_dfspath, '\\', '/');
589 }
590
591 /*
592 * localpath comes out of unix_convert, so it has
593 * no trailing backslash. Make sure that canon_dfspath hasn't either.
594 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
595 */
596
597 trim_char(canon_dfspath,0,'/');
598
599 /*
600 * Redirect if any component in the path is a link.
601 * We do this by walking backwards through the
602 * local path, chopping off the last component
603 * in both the local path and the canonicalized
604 * DFS path. If we hit a DFS link then we're done.
605 */
606
607 p = strrchr_m(smb_fname->base_name, '/');
608 if (consumedcntp) {
609 q = strrchr_m(canon_dfspath, '/');
610 }
611
612 while (p) {
613 *p = '\0';
614 if (q) {
615 *q = '\0';
616 }
617
618 if (is_msdfs_link_internal(ctx, conn,
619 smb_fname->base_name, pp_targetpath,
620 NULL)) {
621 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
622 "parent %s is dfs link\n", dfspath,
623 smb_fname_str_dbg(smb_fname)));
624
625 if (consumedcntp) {
626 *consumedcntp = strlen(canon_dfspath);
627 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
628 "(%d)\n",
629 canon_dfspath,
630 *consumedcntp));
631 }
632
633 status = NT_STATUS_PATH_NOT_COVERED;
634 goto out;
635 }
636
637 /* Step back on the filesystem. */
638 p = strrchr_m(smb_fname->base_name, '/');
639
640 if (consumedcntp) {
641 /* And in the canonicalized dfs path. */
642 q = strrchr_m(canon_dfspath, '/');
643 }
644 }
645
646 status = NT_STATUS_OK;
647 out:
648 TALLOC_FREE(smb_fname);
649 return status;
650}
651
652/*****************************************************************
653 Decides if a dfs pathname should be redirected or not.
654 If not, the pathname is converted to a tcon-relative local unix path
655
656 search_wcard_flag: this flag performs 2 functions both related
657 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
658 for details.
659
660 This function can return NT_STATUS_OK, meaning use the returned path as-is
661 (mapped into a local path).
662 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
663 any other NT_STATUS error which is a genuine error to be
664 returned to the client.
665*****************************************************************/
666
667static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
668 connection_struct *conn,
669 const char *path_in,
670 bool search_wcard_flag,
671 char **pp_path_out,
672 bool *ppath_contains_wcard)
673{
674 NTSTATUS status;
675 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
676
677 if (!pdp) {
678 return NT_STATUS_NO_MEMORY;
679 }
680
681 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
682 ppath_contains_wcard);
683 if (!NT_STATUS_IS_OK(status)) {
684 TALLOC_FREE(pdp);
685 return status;
686 }
687
688 if (pdp->reqpath[0] == '\0') {
689 TALLOC_FREE(pdp);
690 *pp_path_out = talloc_strdup(ctx, "");
691 if (!*pp_path_out) {
692 return NT_STATUS_NO_MEMORY;
693 }
694 DEBUG(5,("dfs_redirect: self-referral.\n"));
695 return NT_STATUS_OK;
696 }
697
698 /* If dfs pathname for a non-dfs share, convert to tcon-relative
699 path and return OK */
700
701 if (!lp_msdfs_root(SNUM(conn))) {
702 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
703 TALLOC_FREE(pdp);
704 if (!*pp_path_out) {
705 return NT_STATUS_NO_MEMORY;
706 }
707 return NT_STATUS_OK;
708 }
709
710 /* If it looked like a local path (zero hostname/servicename)
711 * just treat as a tcon-relative path. */
712
713 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
714 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
715 TALLOC_FREE(pdp);
716 if (!*pp_path_out) {
717 return NT_STATUS_NO_MEMORY;
718 }
719 return NT_STATUS_OK;
720 }
721
722 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
723 || (strequal(pdp->servicename, HOMES_NAME)
724 && strequal(lp_servicename(SNUM(conn)),
725 conn->server_info->sanitized_username) )) ) {
726
727 /* The given sharename doesn't match this connection. */
728 TALLOC_FREE(pdp);
729
730 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
731 }
732
733 status = dfs_path_lookup(ctx, conn, path_in, pdp,
734 search_wcard_flag, NULL, NULL);
735 if (!NT_STATUS_IS_OK(status)) {
736 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
737 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
738 } else {
739 DEBUG(10,("dfs_redirect: dfs_path_lookup "
740 "failed for %s with %s\n",
741 path_in, nt_errstr(status) ));
742 }
743 return status;
744 }
745
746 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
747
748 /* Form non-dfs tcon-relative path */
749 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
750 TALLOC_FREE(pdp);
751 if (!*pp_path_out) {
752 return NT_STATUS_NO_MEMORY;
753 }
754
755 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
756 path_in,
757 *pp_path_out));
758
759 return NT_STATUS_OK;
760}
761
762/**********************************************************************
763 Return a self referral.
764**********************************************************************/
765
766static NTSTATUS self_ref(TALLOC_CTX *ctx,
767 const char *dfs_path,
768 struct junction_map *jucn,
769 int *consumedcntp,
770 bool *self_referralp)
771{
772 struct referral *ref;
773
774 *self_referralp = True;
775
776 jucn->referral_count = 1;
777 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
778 return NT_STATUS_NO_MEMORY;
779 }
780
781 ref->alternate_path = talloc_strdup(ctx, dfs_path);
782 if (!ref->alternate_path) {
783 return NT_STATUS_NO_MEMORY;
784 }
785 ref->proximity = 0;
786 ref->ttl = REFERRAL_TTL;
787 jucn->referral_list = ref;
788 *consumedcntp = strlen(dfs_path);
789 return NT_STATUS_OK;
790}
791
792/**********************************************************************
793 Gets valid referrals for a dfs path and fills up the
794 junction_map structure.
795**********************************************************************/
796
797NTSTATUS get_referred_path(TALLOC_CTX *ctx,
798 const char *dfs_path,
799 struct junction_map *jucn,
800 int *consumedcntp,
801 bool *self_referralp)
802{
803 struct connection_struct *conn;
804 char *targetpath = NULL;
805 int snum;
806 NTSTATUS status = NT_STATUS_NOT_FOUND;
807 bool dummy;
808 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
809 char *oldpath;
810
811 if (!pdp) {
812 return NT_STATUS_NO_MEMORY;
813 }
814
815 *self_referralp = False;
816
817 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
818 if (!NT_STATUS_IS_OK(status)) {
819 return status;
820 }
821
822 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
823 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
824 if (!jucn->service_name || !jucn->volume_name) {
825 TALLOC_FREE(pdp);
826 return NT_STATUS_NO_MEMORY;
827 }
828
829 /* Verify the share is a dfs root */
830 snum = lp_servicenumber(jucn->service_name);
831 if(snum < 0) {
832 fstring service_name;
833 fstrcpy(service_name, jucn->service_name);
834 if ((snum = find_service(service_name)) < 0) {
835 return NT_STATUS_NOT_FOUND;
836 }
837 TALLOC_FREE(jucn->service_name);
838 jucn->service_name = talloc_strdup(ctx, service_name);
839 if (!jucn->service_name) {
840 TALLOC_FREE(pdp);
841 return NT_STATUS_NO_MEMORY;
842 }
843 }
844
845 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
846 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
847 "a dfs root.\n",
848 pdp->servicename, dfs_path));
849 TALLOC_FREE(pdp);
850 return NT_STATUS_NOT_FOUND;
851 }
852
853 /*
854 * Self referrals are tested with a anonymous IPC connection and
855 * a GET_DFS_REFERRAL call to \\server\share. (which means
856 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
857 * into the directory and will fail if it cannot (as the anonymous
858 * user). Cope with this.
859 */
860
861 if (pdp->reqpath[0] == '\0') {
862 char *tmp;
863 struct referral *ref;
864
865 if (*lp_msdfs_proxy(snum) == '\0') {
866 TALLOC_FREE(pdp);
867 return self_ref(ctx,
868 dfs_path,
869 jucn,
870 consumedcntp,
871 self_referralp);
872 }
873
874 /*
875 * It's an msdfs proxy share. Redirect to
876 * the configured target share.
877 */
878
879 jucn->referral_count = 1;
880 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
881 TALLOC_FREE(pdp);
882 return NT_STATUS_NO_MEMORY;
883 }
884
885 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
886 TALLOC_FREE(pdp);
887 return NT_STATUS_NO_MEMORY;
888 }
889
890 trim_string(tmp, "\\", 0);
891
892 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
893 TALLOC_FREE(tmp);
894
895 if (!ref->alternate_path) {
896 TALLOC_FREE(pdp);
897 return NT_STATUS_NO_MEMORY;
898 }
899
900 if (pdp->reqpath[0] != '\0') {
901 ref->alternate_path = talloc_asprintf_append(
902 ref->alternate_path,
903 "%s",
904 pdp->reqpath);
905 if (!ref->alternate_path) {
906 TALLOC_FREE(pdp);
907 return NT_STATUS_NO_MEMORY;
908 }
909 }
910 ref->proximity = 0;
911 ref->ttl = REFERRAL_TTL;
912 jucn->referral_list = ref;
913 *consumedcntp = strlen(dfs_path);
914 TALLOC_FREE(pdp);
915 return NT_STATUS_OK;
916 }
917
918 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
919 NULL, &oldpath);
920 if (!NT_STATUS_IS_OK(status)) {
921 TALLOC_FREE(pdp);
922 return status;
923 }
924
925 /* If this is a DFS path dfs_lookup should return
926 * NT_STATUS_PATH_NOT_COVERED. */
927
928 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
929 False, consumedcntp, &targetpath);
930
931 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
932 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
933 dfs_path));
934 vfs_ChDir(conn, oldpath);
935 conn_free(conn);
936 TALLOC_FREE(pdp);
937 return status;
938 }
939
940 /* We know this is a valid dfs link. Parse the targetpath. */
941 if (!parse_msdfs_symlink(ctx, targetpath,
942 &jucn->referral_list,
943 &jucn->referral_count)) {
944 DEBUG(3,("get_referred_path: failed to parse symlink "
945 "target %s\n", targetpath ));
946 vfs_ChDir(conn, oldpath);
947 conn_free(conn);
948 TALLOC_FREE(pdp);
949 return NT_STATUS_NOT_FOUND;
950 }
951
952 vfs_ChDir(conn, oldpath);
953 conn_free(conn);
954 TALLOC_FREE(pdp);
955 return NT_STATUS_OK;
956}
957
958static int setup_ver2_dfs_referral(const char *pathname,
959 char **ppdata,
960 struct junction_map *junction,
961 bool self_referral)
962{
963 char* pdata = *ppdata;
964
965 smb_ucs2_t *uni_requestedpath = NULL;
966 int uni_reqpathoffset1,uni_reqpathoffset2;
967 int uni_curroffset;
968 int requestedpathlen=0;
969 int offset;
970 int reply_size = 0;
971 int i=0;
972
973 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
974
975 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
976 &uni_requestedpath, pathname);
977 if (uni_requestedpath == NULL || requestedpathlen == 0) {
978 return -1;
979 }
980
981 if (DEBUGLVL(10)) {
982 dump_data(0, (unsigned char *)uni_requestedpath,
983 requestedpathlen);
984 }
985
986 DEBUG(10,("ref count = %u\n",junction->referral_count));
987
988 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
989 VERSION2_REFERRAL_SIZE * junction->referral_count;
990
991 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
992
993 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
994
995 reply_size = REFERRAL_HEADER_SIZE +
996 VERSION2_REFERRAL_SIZE*junction->referral_count +
997 2 * requestedpathlen;
998 DEBUG(10,("reply_size: %u\n",reply_size));
999
1000 /* add up the unicode lengths of all the referral paths */
1001 for(i=0;i<junction->referral_count;i++) {
1002 DEBUG(10,("referral %u : %s\n",
1003 i,
1004 junction->referral_list[i].alternate_path));
1005 reply_size +=
1006 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1007 }
1008
1009 DEBUG(10,("reply_size = %u\n",reply_size));
1010 /* add the unexplained 0x16 bytes */
1011 reply_size += 0x16;
1012
1013 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1014 if(pdata == NULL) {
1015 DEBUG(0,("Realloc failed!\n"));
1016 return -1;
1017 }
1018 *ppdata = pdata;
1019
1020 /* copy in the dfs requested paths.. required for offset calculations */
1021 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1022 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1023
1024 /* create the header */
1025 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1026 2 byte null */
1027 /* number of referral in this pkt */
1028 SSVAL(pdata,2,junction->referral_count);
1029 if(self_referral) {
1030 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1031 } else {
1032 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1033 }
1034
1035 offset = 8;
1036 /* add the referral elements */
1037 for(i=0;i<junction->referral_count;i++) {
1038 struct referral* ref = &junction->referral_list[i];
1039 int unilen;
1040
1041 SSVAL(pdata,offset,2); /* version 2 */
1042 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1043 if(self_referral) {
1044 SSVAL(pdata,offset+4,1);
1045 } else {
1046 SSVAL(pdata,offset+4,0);
1047 }
1048
1049 /* ref_flags :use path_consumed bytes? */
1050 SSVAL(pdata,offset+6,0);
1051 SIVAL(pdata,offset+8,ref->proximity);
1052 SIVAL(pdata,offset+12,ref->ttl);
1053
1054 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1055 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1056 /* copy referred path into current offset */
1057 unilen = rpcstr_push(pdata+uni_curroffset,
1058 ref->alternate_path,
1059 reply_size - uni_curroffset,
1060 STR_UNICODE);
1061
1062 SSVAL(pdata,offset+20,uni_curroffset-offset);
1063
1064 uni_curroffset += unilen;
1065 offset += VERSION2_REFERRAL_SIZE;
1066 }
1067 /* add in the unexplained 22 (0x16) bytes at the end */
1068 memset(pdata+uni_curroffset,'\0',0x16);
1069 return reply_size;
1070}
1071
1072static int setup_ver3_dfs_referral(const char *pathname,
1073 char **ppdata,
1074 struct junction_map *junction,
1075 bool self_referral)
1076{
1077 char *pdata = *ppdata;
1078
1079 smb_ucs2_t *uni_reqpath = NULL;
1080 int uni_reqpathoffset1, uni_reqpathoffset2;
1081 int uni_curroffset;
1082 int reply_size = 0;
1083
1084 int reqpathlen = 0;
1085 int offset,i=0;
1086
1087 DEBUG(10,("setting up version3 referral\n"));
1088
1089 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1090 if (uni_reqpath == NULL || reqpathlen == 0) {
1091 return -1;
1092 }
1093
1094 if (DEBUGLVL(10)) {
1095 dump_data(0, (unsigned char *)uni_reqpath,
1096 reqpathlen);
1097 }
1098
1099 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1100 VERSION3_REFERRAL_SIZE * junction->referral_count;
1101 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1102 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1103
1104 for(i=0;i<junction->referral_count;i++) {
1105 DEBUG(10,("referral %u : %s\n",
1106 i,
1107 junction->referral_list[i].alternate_path));
1108 reply_size +=
1109 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1110 }
1111
1112 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1113 if(pdata == NULL) {
1114 DEBUG(0,("version3 referral setup:"
1115 "malloc failed for Realloc!\n"));
1116 return -1;
1117 }
1118 *ppdata = pdata;
1119
1120 /* create the header */
1121 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1122 2 byte null */
1123 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1124 if(self_referral) {
1125 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1126 } else {
1127 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1128 }
1129
1130 /* copy in the reqpaths */
1131 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1132 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1133
1134 offset = 8;
1135 for(i=0;i<junction->referral_count;i++) {
1136 struct referral* ref = &(junction->referral_list[i]);
1137 int unilen;
1138
1139 SSVAL(pdata,offset,3); /* version 3 */
1140 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1141 if(self_referral) {
1142 SSVAL(pdata,offset+4,1);
1143 } else {
1144 SSVAL(pdata,offset+4,0);
1145 }
1146
1147 /* ref_flags :use path_consumed bytes? */
1148 SSVAL(pdata,offset+6,0);
1149 SIVAL(pdata,offset+8,ref->ttl);
1150
1151 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1152 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1153 /* copy referred path into current offset */
1154 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1155 reply_size - uni_curroffset,
1156 STR_UNICODE | STR_TERMINATE);
1157 SSVAL(pdata,offset+16,uni_curroffset-offset);
1158 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1159 memset(pdata+offset+18,'\0',16);
1160
1161 uni_curroffset += unilen;
1162 offset += VERSION3_REFERRAL_SIZE;
1163 }
1164 return reply_size;
1165}
1166
1167/******************************************************************
1168 Set up the DFS referral for the dfs pathname. This call returns
1169 the amount of the path covered by this server, and where the
1170 client should be redirected to. This is the meat of the
1171 TRANS2_GET_DFS_REFERRAL call.
1172******************************************************************/
1173
1174int setup_dfs_referral(connection_struct *orig_conn,
1175 const char *dfs_path,
1176 int max_referral_level,
1177 char **ppdata, NTSTATUS *pstatus)
1178{
1179 struct junction_map *junction = NULL;
1180 int consumedcnt = 0;
1181 bool self_referral = False;
1182 int reply_size = 0;
1183 char *pathnamep = NULL;
1184 char *local_dfs_path = NULL;
1185 TALLOC_CTX *ctx;
1186
1187 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1188 *pstatus = NT_STATUS_NO_MEMORY;
1189 return -1;
1190 }
1191
1192 /* get the junction entry */
1193 if (!dfs_path) {
1194 talloc_destroy(ctx);
1195 *pstatus = NT_STATUS_NOT_FOUND;
1196 return -1;
1197 }
1198
1199 /*
1200 * Trim pathname sent by client so it begins with only one backslash.
1201 * Two backslashes confuse some dfs clients
1202 */
1203
1204 local_dfs_path = talloc_strdup(ctx,dfs_path);
1205 if (!local_dfs_path) {
1206 *pstatus = NT_STATUS_NO_MEMORY;
1207 talloc_destroy(ctx);
1208 return -1;
1209 }
1210 pathnamep = local_dfs_path;
1211 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1212 IS_DIRECTORY_SEP(pathnamep[1])) {
1213 pathnamep++;
1214 }
1215
1216 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1217 if (!junction) {
1218 *pstatus = NT_STATUS_NO_MEMORY;
1219 talloc_destroy(ctx);
1220 return -1;
1221 }
1222
1223 /* The following call can change cwd. */
1224 *pstatus = get_referred_path(ctx, pathnamep, junction,
1225 &consumedcnt, &self_referral);
1226 if (!NT_STATUS_IS_OK(*pstatus)) {
1227 vfs_ChDir(orig_conn,orig_conn->connectpath);
1228 talloc_destroy(ctx);
1229 return -1;
1230 }
1231 vfs_ChDir(orig_conn,orig_conn->connectpath);
1232
1233 if (!self_referral) {
1234 pathnamep[consumedcnt] = '\0';
1235
1236 if( DEBUGLVL( 3 ) ) {
1237 int i=0;
1238 dbgtext("setup_dfs_referral: Path %s to "
1239 "alternate path(s):",
1240 pathnamep);
1241 for(i=0;i<junction->referral_count;i++)
1242 dbgtext(" %s",
1243 junction->referral_list[i].alternate_path);
1244 dbgtext(".\n");
1245 }
1246 }
1247
1248 /* create the referral depeding on version */
1249 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1250
1251 if (max_referral_level < 2) {
1252 max_referral_level = 2;
1253 }
1254 if (max_referral_level > 3) {
1255 max_referral_level = 3;
1256 }
1257
1258 switch(max_referral_level) {
1259 case 2:
1260 reply_size = setup_ver2_dfs_referral(pathnamep,
1261 ppdata, junction,
1262 self_referral);
1263 break;
1264 case 3:
1265 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1266 junction, self_referral);
1267 break;
1268 default:
1269 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1270 "version: %d\n",
1271 max_referral_level));
1272 talloc_destroy(ctx);
1273 *pstatus = NT_STATUS_INVALID_LEVEL;
1274 return -1;
1275 }
1276
1277 if (DEBUGLVL(10)) {
1278 DEBUGADD(0,("DFS Referral pdata:\n"));
1279 dump_data(0,(uint8 *)*ppdata,reply_size);
1280 }
1281
1282 talloc_destroy(ctx);
1283 *pstatus = NT_STATUS_OK;
1284 return reply_size;
1285}
1286
1287/**********************************************************************
1288 The following functions are called by the NETDFS RPC pipe functions
1289 **********************************************************************/
1290
1291/*********************************************************************
1292 Creates a junction structure from a DFS pathname
1293**********************************************************************/
1294
1295bool create_junction(TALLOC_CTX *ctx,
1296 const char *dfs_path,
1297 struct junction_map *jucn)
1298{
1299 int snum;
1300 bool dummy;
1301 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1302 NTSTATUS status;
1303
1304 if (!pdp) {
1305 return False;
1306 }
1307 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1308 if (!NT_STATUS_IS_OK(status)) {
1309 return False;
1310 }
1311
1312 /* check if path is dfs : validate first token */
1313 if (!is_myname_or_ipaddr(pdp->hostname)) {
1314 DEBUG(4,("create_junction: Invalid hostname %s "
1315 "in dfs path %s\n",
1316 pdp->hostname, dfs_path));
1317 TALLOC_FREE(pdp);
1318 return False;
1319 }
1320
1321 /* Check for a non-DFS share */
1322 snum = lp_servicenumber(pdp->servicename);
1323
1324 if(snum < 0 || !lp_msdfs_root(snum)) {
1325 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1326 pdp->servicename));
1327 TALLOC_FREE(pdp);
1328 return False;
1329 }
1330
1331 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1332 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1333 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1334
1335 TALLOC_FREE(pdp);
1336 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1337 return False;
1338 }
1339 return True;
1340}
1341
1342/**********************************************************************
1343 Forms a valid Unix pathname from the junction
1344 **********************************************************************/
1345
1346static bool junction_to_local_path(const struct junction_map *jucn,
1347 char **pp_path_out,
1348 connection_struct **conn_out,
1349 char **oldpath)
1350{
1351 int snum;
1352 NTSTATUS status;
1353
1354 snum = lp_servicenumber(jucn->service_name);
1355 if(snum < 0) {
1356 return False;
1357 }
1358 status = create_conn_struct(talloc_tos(), conn_out, snum,
1359 lp_pathname(snum), NULL, oldpath);
1360 if (!NT_STATUS_IS_OK(status)) {
1361 return False;
1362 }
1363
1364 *pp_path_out = talloc_asprintf(*conn_out,
1365 "%s/%s",
1366 lp_pathname(snum),
1367 jucn->volume_name);
1368 if (!*pp_path_out) {
1369 vfs_ChDir(*conn_out, *oldpath);
1370 conn_free(*conn_out);
1371 return False;
1372 }
1373 return True;
1374}
1375
1376bool create_msdfs_link(const struct junction_map *jucn)
1377{
1378 char *path = NULL;
1379 char *cwd;
1380 char *msdfs_link = NULL;
1381 connection_struct *conn;
1382 int i=0;
1383 bool insert_comma = False;
1384 bool ret = False;
1385
1386 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1387 return False;
1388 }
1389
1390 /* Form the msdfs_link contents */
1391 msdfs_link = talloc_strdup(conn, "msdfs:");
1392 if (!msdfs_link) {
1393 goto out;
1394 }
1395 for(i=0; i<jucn->referral_count; i++) {
1396 char *refpath = jucn->referral_list[i].alternate_path;
1397
1398 /* Alternate paths always use Windows separators. */
1399 trim_char(refpath, '\\', '\\');
1400 if(*refpath == '\0') {
1401 if (i == 0) {
1402 insert_comma = False;
1403 }
1404 continue;
1405 }
1406 if (i > 0 && insert_comma) {
1407 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1408 ",%s",
1409 refpath);
1410 } else {
1411 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1412 "%s",
1413 refpath);
1414 }
1415
1416 if (!msdfs_link) {
1417 goto out;
1418 }
1419 if (!insert_comma) {
1420 insert_comma = True;
1421 }
1422 }
1423
1424 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1425 path, msdfs_link));
1426
1427 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1428 if (errno == EEXIST) {
1429 struct smb_filename *smb_fname = NULL;
1430 NTSTATUS status;
1431
1432 status = create_synthetic_smb_fname(talloc_tos(), path,
1433 NULL, NULL,
1434 &smb_fname);
1435 if (!NT_STATUS_IS_OK(status)) {
1436 errno = map_errno_from_nt_status(status);
1437 goto out;
1438 }
1439
1440 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1441 TALLOC_FREE(smb_fname);
1442 goto out;
1443 }
1444 TALLOC_FREE(smb_fname);
1445 }
1446 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1447 DEBUG(1,("create_msdfs_link: symlink failed "
1448 "%s -> %s\nError: %s\n",
1449 path, msdfs_link, strerror(errno)));
1450 goto out;
1451 }
1452 }
1453
1454 ret = True;
1455
1456out:
1457 vfs_ChDir(conn, cwd);
1458 conn_free(conn);
1459 return ret;
1460}
1461
1462bool remove_msdfs_link(const struct junction_map *jucn)
1463{
1464 char *path = NULL;
1465 char *cwd;
1466 connection_struct *conn;
1467 bool ret = False;
1468 struct smb_filename *smb_fname = NULL;
1469 NTSTATUS status;
1470
1471 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1472 return false;
1473 }
1474
1475 status = create_synthetic_smb_fname(talloc_tos(), path,
1476 NULL, NULL,
1477 &smb_fname);
1478 if (!NT_STATUS_IS_OK(status)) {
1479 errno = map_errno_from_nt_status(status);
1480 return false;
1481 }
1482
1483 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1484 ret = True;
1485 }
1486
1487 TALLOC_FREE(smb_fname);
1488 vfs_ChDir(conn, cwd);
1489 conn_free(conn);
1490 return ret;
1491}
1492
1493/*********************************************************************
1494 Return the number of DFS links at the root of this share.
1495*********************************************************************/
1496
1497static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1498{
1499 size_t cnt = 0;
1500 SMB_STRUCT_DIR *dirp = NULL;
1501 const char *dname = NULL;
1502 char *talloced = NULL;
1503 const char *connect_path = lp_pathname(snum);
1504 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1505 connection_struct *conn;
1506 NTSTATUS status;
1507 char *cwd;
1508
1509 if(*connect_path == '\0') {
1510 return 0;
1511 }
1512
1513 /*
1514 * Fake up a connection struct for the VFS layer.
1515 */
1516
1517 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1518 NULL, &cwd);
1519 if (!NT_STATUS_IS_OK(status)) {
1520 DEBUG(3, ("create_conn_struct failed: %s\n",
1521 nt_errstr(status)));
1522 return 0;
1523 }
1524
1525 /* Count a link for the msdfs root - convention */
1526 cnt = 1;
1527
1528 /* No more links if this is an msdfs proxy. */
1529 if (*msdfs_proxy != '\0') {
1530 goto out;
1531 }
1532
1533 /* Now enumerate all dfs links */
1534 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1535 if(!dirp) {
1536 goto out;
1537 }
1538
1539 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1540 != NULL) {
1541 if (is_msdfs_link(conn,
1542 dname,
1543 NULL)) {
1544 cnt++;
1545 }
1546 TALLOC_FREE(talloced);
1547 }
1548
1549 SMB_VFS_CLOSEDIR(conn,dirp);
1550
1551out:
1552 vfs_ChDir(conn, cwd);
1553 conn_free(conn);
1554 return cnt;
1555}
1556
1557/*********************************************************************
1558*********************************************************************/
1559
1560static int form_junctions(TALLOC_CTX *ctx,
1561 int snum,
1562 struct junction_map *jucn,
1563 size_t jn_remain)
1564{
1565 size_t cnt = 0;
1566 SMB_STRUCT_DIR *dirp = NULL;
1567 const char *dname = NULL;
1568 char *talloced = NULL;
1569 const char *connect_path = lp_pathname(snum);
1570 char *service_name = lp_servicename(snum);
1571 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1572 connection_struct *conn;
1573 struct referral *ref = NULL;
1574 char *cwd;
1575 NTSTATUS status;
1576
1577 if (jn_remain == 0) {
1578 return 0;
1579 }
1580
1581 if(*connect_path == '\0') {
1582 return 0;
1583 }
1584
1585 /*
1586 * Fake up a connection struct for the VFS layer.
1587 */
1588
1589 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1590 &cwd);
1591 if (!NT_STATUS_IS_OK(status)) {
1592 DEBUG(3, ("create_conn_struct failed: %s\n",
1593 nt_errstr(status)));
1594 return 0;
1595 }
1596
1597 /* form a junction for the msdfs root - convention
1598 DO NOT REMOVE THIS: NT clients will not work with us
1599 if this is not present
1600 */
1601 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1602 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1603 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1604 goto out;
1605 }
1606 jucn[cnt].comment = "";
1607 jucn[cnt].referral_count = 1;
1608
1609 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1610 if (jucn[cnt].referral_list == NULL) {
1611 goto out;
1612 }
1613
1614 ref->proximity = 0;
1615 ref->ttl = REFERRAL_TTL;
1616 if (*msdfs_proxy != '\0') {
1617 ref->alternate_path = talloc_strdup(ctx,
1618 msdfs_proxy);
1619 } else {
1620 ref->alternate_path = talloc_asprintf(ctx,
1621 "\\\\%s\\%s",
1622 get_local_machine_name(),
1623 service_name);
1624 }
1625
1626 if (!ref->alternate_path) {
1627 goto out;
1628 }
1629 cnt++;
1630
1631 /* Don't enumerate if we're an msdfs proxy. */
1632 if (*msdfs_proxy != '\0') {
1633 goto out;
1634 }
1635
1636 /* Now enumerate all dfs links */
1637 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1638 if(!dirp) {
1639 goto out;
1640 }
1641
1642 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1643 != NULL) {
1644 char *link_target = NULL;
1645 if (cnt >= jn_remain) {
1646 DEBUG(2, ("form_junctions: ran out of MSDFS "
1647 "junction slots"));
1648 TALLOC_FREE(talloced);
1649 goto out;
1650 }
1651 if (is_msdfs_link_internal(ctx,
1652 conn,
1653 dname, &link_target,
1654 NULL)) {
1655 if (parse_msdfs_symlink(ctx,
1656 link_target,
1657 &jucn[cnt].referral_list,
1658 &jucn[cnt].referral_count)) {
1659
1660 jucn[cnt].service_name = talloc_strdup(ctx,
1661 service_name);
1662 jucn[cnt].volume_name = talloc_strdup(ctx,
1663 dname);
1664 if (!jucn[cnt].service_name ||
1665 !jucn[cnt].volume_name) {
1666 TALLOC_FREE(talloced);
1667 goto out;
1668 }
1669 jucn[cnt].comment = "";
1670 cnt++;
1671 }
1672 TALLOC_FREE(link_target);
1673 }
1674 TALLOC_FREE(talloced);
1675 }
1676
1677out:
1678
1679 if (dirp) {
1680 SMB_VFS_CLOSEDIR(conn,dirp);
1681 }
1682
1683 vfs_ChDir(conn, cwd);
1684 conn_free(conn);
1685 return cnt;
1686}
1687
1688struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1689{
1690 struct junction_map *jn = NULL;
1691 int i=0;
1692 size_t jn_count = 0;
1693 int sharecount = 0;
1694
1695 *p_num_jn = 0;
1696 if(!lp_host_msdfs()) {
1697 return NULL;
1698 }
1699
1700 /* Ensure all the usershares are loaded. */
1701 become_root();
1702 load_registry_shares();
1703 sharecount = load_usershare_shares();
1704 unbecome_root();
1705
1706 for(i=0;i < sharecount;i++) {
1707 if(lp_msdfs_root(i)) {
1708 jn_count += count_dfs_links(ctx, i);
1709 }
1710 }
1711 if (jn_count == 0) {
1712 return NULL;
1713 }
1714 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1715 if (!jn) {
1716 return NULL;
1717 }
1718 for(i=0; i < sharecount; i++) {
1719 if (*p_num_jn >= jn_count) {
1720 break;
1721 }
1722 if(lp_msdfs_root(i)) {
1723 *p_num_jn += form_junctions(ctx, i,
1724 &jn[*p_num_jn],
1725 jn_count - *p_num_jn);
1726 }
1727 }
1728 return jn;
1729}
1730
1731/******************************************************************************
1732 Core function to resolve a dfs pathname possibly containing a wildcard. If
1733 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1734 detected during dfs resolution.
1735******************************************************************************/
1736
1737NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1738 connection_struct *conn,
1739 bool dfs_pathnames,
1740 const char *name_in,
1741 bool allow_wcards,
1742 char **pp_name_out,
1743 bool *ppath_contains_wcard)
1744{
1745 bool path_contains_wcard;
1746 NTSTATUS status = NT_STATUS_OK;
1747
1748 if (dfs_pathnames) {
1749 status = dfs_redirect(ctx,
1750 conn,
1751 name_in,
1752 allow_wcards,
1753 pp_name_out,
1754 &path_contains_wcard);
1755
1756 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1757 *ppath_contains_wcard = path_contains_wcard;
1758 }
1759 } else {
1760 /*
1761 * Cheat and just return a copy of the in ptr.
1762 * Once srvstr_get_path() uses talloc it'll
1763 * be a talloced ptr anyway.
1764 */
1765 *pp_name_out = CONST_DISCARD(char *,name_in);
1766 }
1767 return status;
1768}
Note: See TracBrowser for help on using the repository browser.