source: branches/samba-3.5.x/source3/smbd/msdfs.c@ 717

Last change on this file since 717 was 596, checked in by Herwig Bauernfeind, 14 years ago

Samba 3.5: Update trunk to 3.5.8

File size: 44.9 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 struct auth_serversupplied_info *server_info,
799 const char *dfs_path,
800 struct junction_map *jucn,
801 int *consumedcntp,
802 bool *self_referralp)
803{
804 struct connection_struct *conn;
805 char *targetpath = NULL;
806 int snum;
807 NTSTATUS status = NT_STATUS_NOT_FOUND;
808 bool dummy;
809 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
810 char *oldpath;
811
812 if (!pdp) {
813 return NT_STATUS_NO_MEMORY;
814 }
815
816 *self_referralp = False;
817
818 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
819 if (!NT_STATUS_IS_OK(status)) {
820 return status;
821 }
822
823 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
824 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
825 if (!jucn->service_name || !jucn->volume_name) {
826 TALLOC_FREE(pdp);
827 return NT_STATUS_NO_MEMORY;
828 }
829
830 /* Verify the share is a dfs root */
831 snum = lp_servicenumber(jucn->service_name);
832 if(snum < 0) {
833 fstring service_name;
834 fstrcpy(service_name, jucn->service_name);
835 if ((snum = find_service(service_name)) < 0) {
836 return NT_STATUS_NOT_FOUND;
837 }
838 TALLOC_FREE(jucn->service_name);
839 jucn->service_name = talloc_strdup(ctx, service_name);
840 if (!jucn->service_name) {
841 TALLOC_FREE(pdp);
842 return NT_STATUS_NO_MEMORY;
843 }
844 }
845
846 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
847 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
848 "a dfs root.\n",
849 pdp->servicename, dfs_path));
850 TALLOC_FREE(pdp);
851 return NT_STATUS_NOT_FOUND;
852 }
853
854 /*
855 * Self referrals are tested with a anonymous IPC connection and
856 * a GET_DFS_REFERRAL call to \\server\share. (which means
857 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
858 * into the directory and will fail if it cannot (as the anonymous
859 * user). Cope with this.
860 */
861
862 if (pdp->reqpath[0] == '\0') {
863 char *tmp;
864 struct referral *ref;
865
866 if (*lp_msdfs_proxy(snum) == '\0') {
867 TALLOC_FREE(pdp);
868 return self_ref(ctx,
869 dfs_path,
870 jucn,
871 consumedcntp,
872 self_referralp);
873 }
874
875 /*
876 * It's an msdfs proxy share. Redirect to
877 * the configured target share.
878 */
879
880 jucn->referral_count = 1;
881 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
882 TALLOC_FREE(pdp);
883 return NT_STATUS_NO_MEMORY;
884 }
885
886 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
887 TALLOC_FREE(pdp);
888 return NT_STATUS_NO_MEMORY;
889 }
890
891 trim_string(tmp, "\\", 0);
892
893 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
894 TALLOC_FREE(tmp);
895
896 if (!ref->alternate_path) {
897 TALLOC_FREE(pdp);
898 return NT_STATUS_NO_MEMORY;
899 }
900
901 if (pdp->reqpath[0] != '\0') {
902 ref->alternate_path = talloc_asprintf_append(
903 ref->alternate_path,
904 "%s",
905 pdp->reqpath);
906 if (!ref->alternate_path) {
907 TALLOC_FREE(pdp);
908 return NT_STATUS_NO_MEMORY;
909 }
910 }
911 ref->proximity = 0;
912 ref->ttl = REFERRAL_TTL;
913 jucn->referral_list = ref;
914 *consumedcntp = strlen(dfs_path);
915 TALLOC_FREE(pdp);
916 return NT_STATUS_OK;
917 }
918
919 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
920 server_info, &oldpath);
921 if (!NT_STATUS_IS_OK(status)) {
922 TALLOC_FREE(pdp);
923 return status;
924 }
925
926 /* If this is a DFS path dfs_lookup should return
927 * NT_STATUS_PATH_NOT_COVERED. */
928
929 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
930 False, consumedcntp, &targetpath);
931
932 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
933 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
934 dfs_path));
935 vfs_ChDir(conn, oldpath);
936 conn_free(conn);
937 TALLOC_FREE(pdp);
938 return status;
939 }
940
941 /* We know this is a valid dfs link. Parse the targetpath. */
942 if (!parse_msdfs_symlink(ctx, targetpath,
943 &jucn->referral_list,
944 &jucn->referral_count)) {
945 DEBUG(3,("get_referred_path: failed to parse symlink "
946 "target %s\n", targetpath ));
947 vfs_ChDir(conn, oldpath);
948 conn_free(conn);
949 TALLOC_FREE(pdp);
950 return NT_STATUS_NOT_FOUND;
951 }
952
953 vfs_ChDir(conn, oldpath);
954 conn_free(conn);
955 TALLOC_FREE(pdp);
956 return NT_STATUS_OK;
957}
958
959static int setup_ver2_dfs_referral(const char *pathname,
960 char **ppdata,
961 struct junction_map *junction,
962 bool self_referral)
963{
964 char* pdata = *ppdata;
965
966 smb_ucs2_t *uni_requestedpath = NULL;
967 int uni_reqpathoffset1,uni_reqpathoffset2;
968 int uni_curroffset;
969 int requestedpathlen=0;
970 int offset;
971 int reply_size = 0;
972 int i=0;
973
974 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
975
976 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
977 &uni_requestedpath, pathname);
978 if (uni_requestedpath == NULL || requestedpathlen == 0) {
979 return -1;
980 }
981
982 if (DEBUGLVL(10)) {
983 dump_data(0, (unsigned char *)uni_requestedpath,
984 requestedpathlen);
985 }
986
987 DEBUG(10,("ref count = %u\n",junction->referral_count));
988
989 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
990 VERSION2_REFERRAL_SIZE * junction->referral_count;
991
992 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
993
994 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
995
996 reply_size = REFERRAL_HEADER_SIZE +
997 VERSION2_REFERRAL_SIZE*junction->referral_count +
998 2 * requestedpathlen;
999 DEBUG(10,("reply_size: %u\n",reply_size));
1000
1001 /* add up the unicode lengths of all the referral paths */
1002 for(i=0;i<junction->referral_count;i++) {
1003 DEBUG(10,("referral %u : %s\n",
1004 i,
1005 junction->referral_list[i].alternate_path));
1006 reply_size +=
1007 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1008 }
1009
1010 DEBUG(10,("reply_size = %u\n",reply_size));
1011 /* add the unexplained 0x16 bytes */
1012 reply_size += 0x16;
1013
1014 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1015 if(pdata == NULL) {
1016 DEBUG(0,("Realloc failed!\n"));
1017 return -1;
1018 }
1019 *ppdata = pdata;
1020
1021 /* copy in the dfs requested paths.. required for offset calculations */
1022 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1023 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1024
1025 /* create the header */
1026 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1027 2 byte null */
1028 /* number of referral in this pkt */
1029 SSVAL(pdata,2,junction->referral_count);
1030 if(self_referral) {
1031 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1032 } else {
1033 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1034 }
1035
1036 offset = 8;
1037 /* add the referral elements */
1038 for(i=0;i<junction->referral_count;i++) {
1039 struct referral* ref = &junction->referral_list[i];
1040 int unilen;
1041
1042 SSVAL(pdata,offset,2); /* version 2 */
1043 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1044 if(self_referral) {
1045 SSVAL(pdata,offset+4,1);
1046 } else {
1047 SSVAL(pdata,offset+4,0);
1048 }
1049
1050 /* ref_flags :use path_consumed bytes? */
1051 SSVAL(pdata,offset+6,0);
1052 SIVAL(pdata,offset+8,ref->proximity);
1053 SIVAL(pdata,offset+12,ref->ttl);
1054
1055 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1056 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1057 /* copy referred path into current offset */
1058 unilen = rpcstr_push(pdata+uni_curroffset,
1059 ref->alternate_path,
1060 reply_size - uni_curroffset,
1061 STR_UNICODE);
1062
1063 SSVAL(pdata,offset+20,uni_curroffset-offset);
1064
1065 uni_curroffset += unilen;
1066 offset += VERSION2_REFERRAL_SIZE;
1067 }
1068 /* add in the unexplained 22 (0x16) bytes at the end */
1069 memset(pdata+uni_curroffset,'\0',0x16);
1070 return reply_size;
1071}
1072
1073static int setup_ver3_dfs_referral(const char *pathname,
1074 char **ppdata,
1075 struct junction_map *junction,
1076 bool self_referral)
1077{
1078 char *pdata = *ppdata;
1079
1080 smb_ucs2_t *uni_reqpath = NULL;
1081 int uni_reqpathoffset1, uni_reqpathoffset2;
1082 int uni_curroffset;
1083 int reply_size = 0;
1084
1085 int reqpathlen = 0;
1086 int offset,i=0;
1087
1088 DEBUG(10,("setting up version3 referral\n"));
1089
1090 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1091 if (uni_reqpath == NULL || reqpathlen == 0) {
1092 return -1;
1093 }
1094
1095 if (DEBUGLVL(10)) {
1096 dump_data(0, (unsigned char *)uni_reqpath,
1097 reqpathlen);
1098 }
1099
1100 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1101 VERSION3_REFERRAL_SIZE * junction->referral_count;
1102 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1103 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1104
1105 for(i=0;i<junction->referral_count;i++) {
1106 DEBUG(10,("referral %u : %s\n",
1107 i,
1108 junction->referral_list[i].alternate_path));
1109 reply_size +=
1110 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1111 }
1112
1113 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1114 if(pdata == NULL) {
1115 DEBUG(0,("version3 referral setup:"
1116 "malloc failed for Realloc!\n"));
1117 return -1;
1118 }
1119 *ppdata = pdata;
1120
1121 /* create the header */
1122 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1123 2 byte null */
1124 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1125 if(self_referral) {
1126 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1127 } else {
1128 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1129 }
1130
1131 /* copy in the reqpaths */
1132 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1133 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1134
1135 offset = 8;
1136 for(i=0;i<junction->referral_count;i++) {
1137 struct referral* ref = &(junction->referral_list[i]);
1138 int unilen;
1139
1140 SSVAL(pdata,offset,3); /* version 3 */
1141 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1142 if(self_referral) {
1143 SSVAL(pdata,offset+4,1);
1144 } else {
1145 SSVAL(pdata,offset+4,0);
1146 }
1147
1148 /* ref_flags :use path_consumed bytes? */
1149 SSVAL(pdata,offset+6,0);
1150 SIVAL(pdata,offset+8,ref->ttl);
1151
1152 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1153 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1154 /* copy referred path into current offset */
1155 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1156 reply_size - uni_curroffset,
1157 STR_UNICODE | STR_TERMINATE);
1158 SSVAL(pdata,offset+16,uni_curroffset-offset);
1159 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1160 memset(pdata+offset+18,'\0',16);
1161
1162 uni_curroffset += unilen;
1163 offset += VERSION3_REFERRAL_SIZE;
1164 }
1165 return reply_size;
1166}
1167
1168/******************************************************************
1169 Set up the DFS referral for the dfs pathname. This call returns
1170 the amount of the path covered by this server, and where the
1171 client should be redirected to. This is the meat of the
1172 TRANS2_GET_DFS_REFERRAL call.
1173******************************************************************/
1174
1175int setup_dfs_referral(connection_struct *orig_conn,
1176 const char *dfs_path,
1177 int max_referral_level,
1178 char **ppdata, NTSTATUS *pstatus)
1179{
1180 struct junction_map *junction = NULL;
1181 int consumedcnt = 0;
1182 bool self_referral = False;
1183 int reply_size = 0;
1184 char *pathnamep = NULL;
1185 char *local_dfs_path = NULL;
1186 TALLOC_CTX *ctx;
1187
1188 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1189 *pstatus = NT_STATUS_NO_MEMORY;
1190 return -1;
1191 }
1192
1193 /* get the junction entry */
1194 if (!dfs_path) {
1195 talloc_destroy(ctx);
1196 *pstatus = NT_STATUS_NOT_FOUND;
1197 return -1;
1198 }
1199
1200 /*
1201 * Trim pathname sent by client so it begins with only one backslash.
1202 * Two backslashes confuse some dfs clients
1203 */
1204
1205 local_dfs_path = talloc_strdup(ctx,dfs_path);
1206 if (!local_dfs_path) {
1207 *pstatus = NT_STATUS_NO_MEMORY;
1208 talloc_destroy(ctx);
1209 return -1;
1210 }
1211 pathnamep = local_dfs_path;
1212 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1213 IS_DIRECTORY_SEP(pathnamep[1])) {
1214 pathnamep++;
1215 }
1216
1217 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1218 if (!junction) {
1219 *pstatus = NT_STATUS_NO_MEMORY;
1220 talloc_destroy(ctx);
1221 return -1;
1222 }
1223
1224 /* The following call can change cwd. */
1225 *pstatus = get_referred_path(ctx, orig_conn->server_info,
1226 pathnamep, junction,
1227 &consumedcnt, &self_referral);
1228 if (!NT_STATUS_IS_OK(*pstatus)) {
1229 vfs_ChDir(orig_conn,orig_conn->connectpath);
1230 talloc_destroy(ctx);
1231 return -1;
1232 }
1233 vfs_ChDir(orig_conn,orig_conn->connectpath);
1234
1235 if (!self_referral) {
1236 pathnamep[consumedcnt] = '\0';
1237
1238 if( DEBUGLVL( 3 ) ) {
1239 int i=0;
1240 dbgtext("setup_dfs_referral: Path %s to "
1241 "alternate path(s):",
1242 pathnamep);
1243 for(i=0;i<junction->referral_count;i++)
1244 dbgtext(" %s",
1245 junction->referral_list[i].alternate_path);
1246 dbgtext(".\n");
1247 }
1248 }
1249
1250 /* create the referral depeding on version */
1251 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1252
1253 if (max_referral_level < 2) {
1254 max_referral_level = 2;
1255 }
1256 if (max_referral_level > 3) {
1257 max_referral_level = 3;
1258 }
1259
1260 switch(max_referral_level) {
1261 case 2:
1262 reply_size = setup_ver2_dfs_referral(pathnamep,
1263 ppdata, junction,
1264 self_referral);
1265 break;
1266 case 3:
1267 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1268 junction, self_referral);
1269 break;
1270 default:
1271 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1272 "version: %d\n",
1273 max_referral_level));
1274 talloc_destroy(ctx);
1275 *pstatus = NT_STATUS_INVALID_LEVEL;
1276 return -1;
1277 }
1278
1279 if (DEBUGLVL(10)) {
1280 DEBUGADD(0,("DFS Referral pdata:\n"));
1281 dump_data(0,(uint8 *)*ppdata,reply_size);
1282 }
1283
1284 talloc_destroy(ctx);
1285 *pstatus = NT_STATUS_OK;
1286 return reply_size;
1287}
1288
1289/**********************************************************************
1290 The following functions are called by the NETDFS RPC pipe functions
1291 **********************************************************************/
1292
1293/*********************************************************************
1294 Creates a junction structure from a DFS pathname
1295**********************************************************************/
1296
1297bool create_junction(TALLOC_CTX *ctx,
1298 const char *dfs_path,
1299 struct junction_map *jucn)
1300{
1301 int snum;
1302 bool dummy;
1303 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1304 NTSTATUS status;
1305
1306 if (!pdp) {
1307 return False;
1308 }
1309 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1310 if (!NT_STATUS_IS_OK(status)) {
1311 return False;
1312 }
1313
1314 /* check if path is dfs : validate first token */
1315 if (!is_myname_or_ipaddr(pdp->hostname)) {
1316 DEBUG(4,("create_junction: Invalid hostname %s "
1317 "in dfs path %s\n",
1318 pdp->hostname, dfs_path));
1319 TALLOC_FREE(pdp);
1320 return False;
1321 }
1322
1323 /* Check for a non-DFS share */
1324 snum = lp_servicenumber(pdp->servicename);
1325
1326 if(snum < 0 || !lp_msdfs_root(snum)) {
1327 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1328 pdp->servicename));
1329 TALLOC_FREE(pdp);
1330 return False;
1331 }
1332
1333 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1334 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1335 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1336
1337 TALLOC_FREE(pdp);
1338 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1339 return False;
1340 }
1341 return True;
1342}
1343
1344/**********************************************************************
1345 Forms a valid Unix pathname from the junction
1346 **********************************************************************/
1347
1348static bool junction_to_local_path(const struct junction_map *jucn,
1349 char **pp_path_out,
1350 connection_struct **conn_out,
1351 char **oldpath)
1352{
1353 int snum;
1354 NTSTATUS status;
1355
1356 snum = lp_servicenumber(jucn->service_name);
1357 if(snum < 0) {
1358 return False;
1359 }
1360 status = create_conn_struct(talloc_tos(), conn_out, snum,
1361 lp_pathname(snum), NULL, oldpath);
1362 if (!NT_STATUS_IS_OK(status)) {
1363 return False;
1364 }
1365
1366 *pp_path_out = talloc_asprintf(*conn_out,
1367 "%s/%s",
1368 lp_pathname(snum),
1369 jucn->volume_name);
1370 if (!*pp_path_out) {
1371 vfs_ChDir(*conn_out, *oldpath);
1372 conn_free(*conn_out);
1373 return False;
1374 }
1375 return True;
1376}
1377
1378bool create_msdfs_link(const struct junction_map *jucn)
1379{
1380 char *path = NULL;
1381 char *cwd;
1382 char *msdfs_link = NULL;
1383 connection_struct *conn;
1384 int i=0;
1385 bool insert_comma = False;
1386 bool ret = False;
1387
1388 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1389 return False;
1390 }
1391
1392 /* Form the msdfs_link contents */
1393 msdfs_link = talloc_strdup(conn, "msdfs:");
1394 if (!msdfs_link) {
1395 goto out;
1396 }
1397 for(i=0; i<jucn->referral_count; i++) {
1398 char *refpath = jucn->referral_list[i].alternate_path;
1399
1400 /* Alternate paths always use Windows separators. */
1401 trim_char(refpath, '\\', '\\');
1402 if(*refpath == '\0') {
1403 if (i == 0) {
1404 insert_comma = False;
1405 }
1406 continue;
1407 }
1408 if (i > 0 && insert_comma) {
1409 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1410 ",%s",
1411 refpath);
1412 } else {
1413 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1414 "%s",
1415 refpath);
1416 }
1417
1418 if (!msdfs_link) {
1419 goto out;
1420 }
1421 if (!insert_comma) {
1422 insert_comma = True;
1423 }
1424 }
1425
1426 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1427 path, msdfs_link));
1428
1429 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1430 if (errno == EEXIST) {
1431 struct smb_filename *smb_fname = NULL;
1432 NTSTATUS status;
1433
1434 status = create_synthetic_smb_fname(talloc_tos(), path,
1435 NULL, NULL,
1436 &smb_fname);
1437 if (!NT_STATUS_IS_OK(status)) {
1438 errno = map_errno_from_nt_status(status);
1439 goto out;
1440 }
1441
1442 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1443 TALLOC_FREE(smb_fname);
1444 goto out;
1445 }
1446 TALLOC_FREE(smb_fname);
1447 }
1448 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1449 DEBUG(1,("create_msdfs_link: symlink failed "
1450 "%s -> %s\nError: %s\n",
1451 path, msdfs_link, strerror(errno)));
1452 goto out;
1453 }
1454 }
1455
1456 ret = True;
1457
1458out:
1459 vfs_ChDir(conn, cwd);
1460 conn_free(conn);
1461 return ret;
1462}
1463
1464bool remove_msdfs_link(const struct junction_map *jucn)
1465{
1466 char *path = NULL;
1467 char *cwd;
1468 connection_struct *conn;
1469 bool ret = False;
1470 struct smb_filename *smb_fname = NULL;
1471 NTSTATUS status;
1472
1473 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1474 return false;
1475 }
1476
1477 status = create_synthetic_smb_fname(talloc_tos(), path,
1478 NULL, NULL,
1479 &smb_fname);
1480 if (!NT_STATUS_IS_OK(status)) {
1481 errno = map_errno_from_nt_status(status);
1482 return false;
1483 }
1484
1485 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1486 ret = True;
1487 }
1488
1489 TALLOC_FREE(smb_fname);
1490 vfs_ChDir(conn, cwd);
1491 conn_free(conn);
1492 return ret;
1493}
1494
1495/*********************************************************************
1496 Return the number of DFS links at the root of this share.
1497*********************************************************************/
1498
1499static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1500{
1501 size_t cnt = 0;
1502 SMB_STRUCT_DIR *dirp = NULL;
1503 const char *dname = NULL;
1504 char *talloced = NULL;
1505 const char *connect_path = lp_pathname(snum);
1506 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1507 connection_struct *conn;
1508 NTSTATUS status;
1509 char *cwd;
1510
1511 if(*connect_path == '\0') {
1512 return 0;
1513 }
1514
1515 /*
1516 * Fake up a connection struct for the VFS layer.
1517 */
1518
1519 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1520 NULL, &cwd);
1521 if (!NT_STATUS_IS_OK(status)) {
1522 DEBUG(3, ("create_conn_struct failed: %s\n",
1523 nt_errstr(status)));
1524 return 0;
1525 }
1526
1527 /* Count a link for the msdfs root - convention */
1528 cnt = 1;
1529
1530 /* No more links if this is an msdfs proxy. */
1531 if (*msdfs_proxy != '\0') {
1532 goto out;
1533 }
1534
1535 /* Now enumerate all dfs links */
1536 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1537 if(!dirp) {
1538 goto out;
1539 }
1540
1541 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1542 != NULL) {
1543 if (is_msdfs_link(conn,
1544 dname,
1545 NULL)) {
1546 cnt++;
1547 }
1548 TALLOC_FREE(talloced);
1549 }
1550
1551 SMB_VFS_CLOSEDIR(conn,dirp);
1552
1553out:
1554 vfs_ChDir(conn, cwd);
1555 conn_free(conn);
1556 return cnt;
1557}
1558
1559/*********************************************************************
1560*********************************************************************/
1561
1562static int form_junctions(TALLOC_CTX *ctx,
1563 int snum,
1564 struct junction_map *jucn,
1565 size_t jn_remain)
1566{
1567 size_t cnt = 0;
1568 SMB_STRUCT_DIR *dirp = NULL;
1569 const char *dname = NULL;
1570 char *talloced = NULL;
1571 const char *connect_path = lp_pathname(snum);
1572 char *service_name = lp_servicename(snum);
1573 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1574 connection_struct *conn;
1575 struct referral *ref = NULL;
1576 char *cwd;
1577 NTSTATUS status;
1578
1579 if (jn_remain == 0) {
1580 return 0;
1581 }
1582
1583 if(*connect_path == '\0') {
1584 return 0;
1585 }
1586
1587 /*
1588 * Fake up a connection struct for the VFS layer.
1589 */
1590
1591 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1592 &cwd);
1593 if (!NT_STATUS_IS_OK(status)) {
1594 DEBUG(3, ("create_conn_struct failed: %s\n",
1595 nt_errstr(status)));
1596 return 0;
1597 }
1598
1599 /* form a junction for the msdfs root - convention
1600 DO NOT REMOVE THIS: NT clients will not work with us
1601 if this is not present
1602 */
1603 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1604 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1605 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1606 goto out;
1607 }
1608 jucn[cnt].comment = "";
1609 jucn[cnt].referral_count = 1;
1610
1611 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1612 if (jucn[cnt].referral_list == NULL) {
1613 goto out;
1614 }
1615
1616 ref->proximity = 0;
1617 ref->ttl = REFERRAL_TTL;
1618 if (*msdfs_proxy != '\0') {
1619 ref->alternate_path = talloc_strdup(ctx,
1620 msdfs_proxy);
1621 } else {
1622 ref->alternate_path = talloc_asprintf(ctx,
1623 "\\\\%s\\%s",
1624 get_local_machine_name(),
1625 service_name);
1626 }
1627
1628 if (!ref->alternate_path) {
1629 goto out;
1630 }
1631 cnt++;
1632
1633 /* Don't enumerate if we're an msdfs proxy. */
1634 if (*msdfs_proxy != '\0') {
1635 goto out;
1636 }
1637
1638 /* Now enumerate all dfs links */
1639 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1640 if(!dirp) {
1641 goto out;
1642 }
1643
1644 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1645 != NULL) {
1646 char *link_target = NULL;
1647 if (cnt >= jn_remain) {
1648 DEBUG(2, ("form_junctions: ran out of MSDFS "
1649 "junction slots"));
1650 TALLOC_FREE(talloced);
1651 goto out;
1652 }
1653 if (is_msdfs_link_internal(ctx,
1654 conn,
1655 dname, &link_target,
1656 NULL)) {
1657 if (parse_msdfs_symlink(ctx,
1658 link_target,
1659 &jucn[cnt].referral_list,
1660 &jucn[cnt].referral_count)) {
1661
1662 jucn[cnt].service_name = talloc_strdup(ctx,
1663 service_name);
1664 jucn[cnt].volume_name = talloc_strdup(ctx,
1665 dname);
1666 if (!jucn[cnt].service_name ||
1667 !jucn[cnt].volume_name) {
1668 TALLOC_FREE(talloced);
1669 goto out;
1670 }
1671 jucn[cnt].comment = "";
1672 cnt++;
1673 }
1674 TALLOC_FREE(link_target);
1675 }
1676 TALLOC_FREE(talloced);
1677 }
1678
1679out:
1680
1681 if (dirp) {
1682 SMB_VFS_CLOSEDIR(conn,dirp);
1683 }
1684
1685 vfs_ChDir(conn, cwd);
1686 conn_free(conn);
1687 return cnt;
1688}
1689
1690struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1691{
1692 struct junction_map *jn = NULL;
1693 int i=0;
1694 size_t jn_count = 0;
1695 int sharecount = 0;
1696
1697 *p_num_jn = 0;
1698 if(!lp_host_msdfs()) {
1699 return NULL;
1700 }
1701
1702 /* Ensure all the usershares are loaded. */
1703 become_root();
1704 load_registry_shares();
1705 sharecount = load_usershare_shares();
1706 unbecome_root();
1707
1708 for(i=0;i < sharecount;i++) {
1709 if(lp_msdfs_root(i)) {
1710 jn_count += count_dfs_links(ctx, i);
1711 }
1712 }
1713 if (jn_count == 0) {
1714 return NULL;
1715 }
1716 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1717 if (!jn) {
1718 return NULL;
1719 }
1720 for(i=0; i < sharecount; i++) {
1721 if (*p_num_jn >= jn_count) {
1722 break;
1723 }
1724 if(lp_msdfs_root(i)) {
1725 *p_num_jn += form_junctions(ctx, i,
1726 &jn[*p_num_jn],
1727 jn_count - *p_num_jn);
1728 }
1729 }
1730 return jn;
1731}
1732
1733/******************************************************************************
1734 Core function to resolve a dfs pathname possibly containing a wildcard. If
1735 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1736 detected during dfs resolution.
1737******************************************************************************/
1738
1739NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1740 connection_struct *conn,
1741 bool dfs_pathnames,
1742 const char *name_in,
1743 bool allow_wcards,
1744 char **pp_name_out,
1745 bool *ppath_contains_wcard)
1746{
1747 bool path_contains_wcard;
1748 NTSTATUS status = NT_STATUS_OK;
1749
1750 if (dfs_pathnames) {
1751 status = dfs_redirect(ctx,
1752 conn,
1753 name_in,
1754 allow_wcards,
1755 pp_name_out,
1756 &path_contains_wcard);
1757
1758 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1759 *ppath_contains_wcard = path_contains_wcard;
1760 }
1761 } else {
1762 /*
1763 * Cheat and just return a copy of the in ptr.
1764 * Once srvstr_get_path() uses talloc it'll
1765 * be a talloced ptr anyway.
1766 */
1767 *pp_name_out = CONST_DISCARD(char *,name_in);
1768 }
1769 return status;
1770}
Note: See TracBrowser for help on using the repository browser.