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

Last change on this file since 732 was 732, checked in by Silvan Scherrer, 13 years ago

Samba Server 3.5: update branche to 3.5.12

File size: 44.5 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
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 */
533
534 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
535 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
536
537 if (!NT_STATUS_IS_OK(status)) {
538 if (!NT_STATUS_EQUAL(status,
539 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
540 return status;
541 }
542 if (smb_fname == NULL || smb_fname->base_name == NULL) {
543 return status;
544 }
545 }
546
547 /* Optimization - check if we can redirect the whole path. */
548
549 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
550 pp_targetpath, NULL)) {
551 if (search_flag) {
552 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
553 "for dfs link %s.\n", dfspath));
554 status = NT_STATUS_OK;
555 goto out;
556 }
557
558 DEBUG(6,("dfs_path_lookup: %s resolves to a "
559 "valid dfs link %s.\n", dfspath,
560 pp_targetpath ? *pp_targetpath : ""));
561
562 if (consumedcntp) {
563 *consumedcntp = strlen(dfspath);
564 }
565 status = NT_STATUS_PATH_NOT_COVERED;
566 goto out;
567 }
568
569 /* Prepare to test only for '/' components in the given path,
570 * so if a Windows path replace all '\\' characters with '/'.
571 * For a POSIX DFS path we know all separators are already '/'. */
572
573 canon_dfspath = talloc_strdup(ctx, dfspath);
574 if (!canon_dfspath) {
575 status = NT_STATUS_NO_MEMORY;
576 goto out;
577 }
578 if (!pdp->posix_path) {
579 string_replace(canon_dfspath, '\\', '/');
580 }
581
582 /*
583 * localpath comes out of unix_convert, so it has
584 * no trailing backslash. Make sure that canon_dfspath hasn't either.
585 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
586 */
587
588 trim_char(canon_dfspath,0,'/');
589
590 /*
591 * Redirect if any component in the path is a link.
592 * We do this by walking backwards through the
593 * local path, chopping off the last component
594 * in both the local path and the canonicalized
595 * DFS path. If we hit a DFS link then we're done.
596 */
597
598 p = strrchr_m(smb_fname->base_name, '/');
599 if (consumedcntp) {
600 q = strrchr_m(canon_dfspath, '/');
601 }
602
603 while (p) {
604 *p = '\0';
605 if (q) {
606 *q = '\0';
607 }
608
609 if (is_msdfs_link_internal(ctx, conn,
610 smb_fname->base_name, pp_targetpath,
611 NULL)) {
612 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
613 "parent %s is dfs link\n", dfspath,
614 smb_fname_str_dbg(smb_fname)));
615
616 if (consumedcntp) {
617 *consumedcntp = strlen(canon_dfspath);
618 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
619 "(%d)\n",
620 canon_dfspath,
621 *consumedcntp));
622 }
623
624 status = NT_STATUS_PATH_NOT_COVERED;
625 goto out;
626 }
627
628 /* Step back on the filesystem. */
629 p = strrchr_m(smb_fname->base_name, '/');
630
631 if (consumedcntp) {
632 /* And in the canonicalized dfs path. */
633 q = strrchr_m(canon_dfspath, '/');
634 }
635 }
636
637 status = NT_STATUS_OK;
638 out:
639 TALLOC_FREE(smb_fname);
640 return status;
641}
642
643/*****************************************************************
644 Decides if a dfs pathname should be redirected or not.
645 If not, the pathname is converted to a tcon-relative local unix path
646
647 search_wcard_flag: this flag performs 2 functions both related
648 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
649 for details.
650
651 This function can return NT_STATUS_OK, meaning use the returned path as-is
652 (mapped into a local path).
653 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
654 any other NT_STATUS error which is a genuine error to be
655 returned to the client.
656*****************************************************************/
657
658static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
659 connection_struct *conn,
660 const char *path_in,
661 bool search_wcard_flag,
662 char **pp_path_out,
663 bool *ppath_contains_wcard)
664{
665 NTSTATUS status;
666 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
667
668 if (!pdp) {
669 return NT_STATUS_NO_MEMORY;
670 }
671
672 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
673 ppath_contains_wcard);
674 if (!NT_STATUS_IS_OK(status)) {
675 TALLOC_FREE(pdp);
676 return status;
677 }
678
679 if (pdp->reqpath[0] == '\0') {
680 TALLOC_FREE(pdp);
681 *pp_path_out = talloc_strdup(ctx, "");
682 if (!*pp_path_out) {
683 return NT_STATUS_NO_MEMORY;
684 }
685 DEBUG(5,("dfs_redirect: self-referral.\n"));
686 return NT_STATUS_OK;
687 }
688
689 /* If dfs pathname for a non-dfs share, convert to tcon-relative
690 path and return OK */
691
692 if (!lp_msdfs_root(SNUM(conn))) {
693 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
694 TALLOC_FREE(pdp);
695 if (!*pp_path_out) {
696 return NT_STATUS_NO_MEMORY;
697 }
698 return NT_STATUS_OK;
699 }
700
701 /* If it looked like a local path (zero hostname/servicename)
702 * just treat as a tcon-relative path. */
703
704 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
705 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
706 TALLOC_FREE(pdp);
707 if (!*pp_path_out) {
708 return NT_STATUS_NO_MEMORY;
709 }
710 return NT_STATUS_OK;
711 }
712
713 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
714 || (strequal(pdp->servicename, HOMES_NAME)
715 && strequal(lp_servicename(SNUM(conn)),
716 conn->server_info->sanitized_username) )) ) {
717
718 /* The given sharename doesn't match this connection. */
719 TALLOC_FREE(pdp);
720
721 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
722 }
723
724 status = dfs_path_lookup(ctx, conn, path_in, pdp,
725 search_wcard_flag, NULL, NULL);
726 if (!NT_STATUS_IS_OK(status)) {
727 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
728 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
729 } else {
730 DEBUG(10,("dfs_redirect: dfs_path_lookup "
731 "failed for %s with %s\n",
732 path_in, nt_errstr(status) ));
733 }
734 return status;
735 }
736
737 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
738
739 /* Form non-dfs tcon-relative path */
740 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
741 TALLOC_FREE(pdp);
742 if (!*pp_path_out) {
743 return NT_STATUS_NO_MEMORY;
744 }
745
746 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
747 path_in,
748 *pp_path_out));
749
750 return NT_STATUS_OK;
751}
752
753/**********************************************************************
754 Return a self referral.
755**********************************************************************/
756
757static NTSTATUS self_ref(TALLOC_CTX *ctx,
758 const char *dfs_path,
759 struct junction_map *jucn,
760 int *consumedcntp,
761 bool *self_referralp)
762{
763 struct referral *ref;
764
765 *self_referralp = True;
766
767 jucn->referral_count = 1;
768 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
769 return NT_STATUS_NO_MEMORY;
770 }
771
772 ref->alternate_path = talloc_strdup(ctx, dfs_path);
773 if (!ref->alternate_path) {
774 return NT_STATUS_NO_MEMORY;
775 }
776 ref->proximity = 0;
777 ref->ttl = REFERRAL_TTL;
778 jucn->referral_list = ref;
779 *consumedcntp = strlen(dfs_path);
780 return NT_STATUS_OK;
781}
782
783/**********************************************************************
784 Gets valid referrals for a dfs path and fills up the
785 junction_map structure.
786**********************************************************************/
787
788NTSTATUS get_referred_path(TALLOC_CTX *ctx,
789 struct auth_serversupplied_info *server_info,
790 const char *dfs_path,
791 struct junction_map *jucn,
792 int *consumedcntp,
793 bool *self_referralp)
794{
795 struct connection_struct *conn;
796 char *targetpath = NULL;
797 int snum;
798 NTSTATUS status = NT_STATUS_NOT_FOUND;
799 bool dummy;
800 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
801 char *oldpath;
802
803 if (!pdp) {
804 return NT_STATUS_NO_MEMORY;
805 }
806
807 *self_referralp = False;
808
809 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
810 if (!NT_STATUS_IS_OK(status)) {
811 return status;
812 }
813
814 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
815 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
816 if (!jucn->service_name || !jucn->volume_name) {
817 TALLOC_FREE(pdp);
818 return NT_STATUS_NO_MEMORY;
819 }
820
821 /* Verify the share is a dfs root */
822 snum = lp_servicenumber(jucn->service_name);
823 if(snum < 0) {
824 fstring service_name;
825 fstrcpy(service_name, jucn->service_name);
826 if ((snum = find_service(service_name)) < 0) {
827 return NT_STATUS_NOT_FOUND;
828 }
829 TALLOC_FREE(jucn->service_name);
830 jucn->service_name = talloc_strdup(ctx, service_name);
831 if (!jucn->service_name) {
832 TALLOC_FREE(pdp);
833 return NT_STATUS_NO_MEMORY;
834 }
835 }
836
837 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
838 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
839 "a dfs root.\n",
840 pdp->servicename, dfs_path));
841 TALLOC_FREE(pdp);
842 return NT_STATUS_NOT_FOUND;
843 }
844
845 /*
846 * Self referrals are tested with a anonymous IPC connection and
847 * a GET_DFS_REFERRAL call to \\server\share. (which means
848 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
849 * into the directory and will fail if it cannot (as the anonymous
850 * user). Cope with this.
851 */
852
853 if (pdp->reqpath[0] == '\0') {
854 char *tmp;
855 struct referral *ref;
856
857 if (*lp_msdfs_proxy(snum) == '\0') {
858 TALLOC_FREE(pdp);
859 return self_ref(ctx,
860 dfs_path,
861 jucn,
862 consumedcntp,
863 self_referralp);
864 }
865
866 /*
867 * It's an msdfs proxy share. Redirect to
868 * the configured target share.
869 */
870
871 jucn->referral_count = 1;
872 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
873 TALLOC_FREE(pdp);
874 return NT_STATUS_NO_MEMORY;
875 }
876
877 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
878 TALLOC_FREE(pdp);
879 return NT_STATUS_NO_MEMORY;
880 }
881
882 trim_string(tmp, "\\", 0);
883
884 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
885 TALLOC_FREE(tmp);
886
887 if (!ref->alternate_path) {
888 TALLOC_FREE(pdp);
889 return NT_STATUS_NO_MEMORY;
890 }
891
892 if (pdp->reqpath[0] != '\0') {
893 ref->alternate_path = talloc_asprintf_append(
894 ref->alternate_path,
895 "%s",
896 pdp->reqpath);
897 if (!ref->alternate_path) {
898 TALLOC_FREE(pdp);
899 return NT_STATUS_NO_MEMORY;
900 }
901 }
902 ref->proximity = 0;
903 ref->ttl = REFERRAL_TTL;
904 jucn->referral_list = ref;
905 *consumedcntp = strlen(dfs_path);
906 TALLOC_FREE(pdp);
907 return NT_STATUS_OK;
908 }
909
910 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
911 server_info, &oldpath);
912 if (!NT_STATUS_IS_OK(status)) {
913 TALLOC_FREE(pdp);
914 return status;
915 }
916
917 /* If this is a DFS path dfs_lookup should return
918 * NT_STATUS_PATH_NOT_COVERED. */
919
920 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
921 False, consumedcntp, &targetpath);
922
923 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
924 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
925 dfs_path));
926 vfs_ChDir(conn, oldpath);
927 conn_free(conn);
928 TALLOC_FREE(pdp);
929 return status;
930 }
931
932 /* We know this is a valid dfs link. Parse the targetpath. */
933 if (!parse_msdfs_symlink(ctx, targetpath,
934 &jucn->referral_list,
935 &jucn->referral_count)) {
936 DEBUG(3,("get_referred_path: failed to parse symlink "
937 "target %s\n", targetpath ));
938 vfs_ChDir(conn, oldpath);
939 conn_free(conn);
940 TALLOC_FREE(pdp);
941 return NT_STATUS_NOT_FOUND;
942 }
943
944 vfs_ChDir(conn, oldpath);
945 conn_free(conn);
946 TALLOC_FREE(pdp);
947 return NT_STATUS_OK;
948}
949
950static int setup_ver2_dfs_referral(const char *pathname,
951 char **ppdata,
952 struct junction_map *junction,
953 bool self_referral)
954{
955 char* pdata = *ppdata;
956
957 smb_ucs2_t *uni_requestedpath = NULL;
958 int uni_reqpathoffset1,uni_reqpathoffset2;
959 int uni_curroffset;
960 int requestedpathlen=0;
961 int offset;
962 int reply_size = 0;
963 int i=0;
964
965 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
966
967 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
968 &uni_requestedpath, pathname);
969 if (uni_requestedpath == NULL || requestedpathlen == 0) {
970 return -1;
971 }
972
973 if (DEBUGLVL(10)) {
974 dump_data(0, (unsigned char *)uni_requestedpath,
975 requestedpathlen);
976 }
977
978 DEBUG(10,("ref count = %u\n",junction->referral_count));
979
980 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
981 VERSION2_REFERRAL_SIZE * junction->referral_count;
982
983 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
984
985 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
986
987 reply_size = REFERRAL_HEADER_SIZE +
988 VERSION2_REFERRAL_SIZE*junction->referral_count +
989 2 * requestedpathlen;
990 DEBUG(10,("reply_size: %u\n",reply_size));
991
992 /* add up the unicode lengths of all the referral paths */
993 for(i=0;i<junction->referral_count;i++) {
994 DEBUG(10,("referral %u : %s\n",
995 i,
996 junction->referral_list[i].alternate_path));
997 reply_size +=
998 (strlen(junction->referral_list[i].alternate_path)+1)*2;
999 }
1000
1001 DEBUG(10,("reply_size = %u\n",reply_size));
1002 /* add the unexplained 0x16 bytes */
1003 reply_size += 0x16;
1004
1005 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1006 if(pdata == NULL) {
1007 DEBUG(0,("Realloc failed!\n"));
1008 return -1;
1009 }
1010 *ppdata = pdata;
1011
1012 /* copy in the dfs requested paths.. required for offset calculations */
1013 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1014 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1015
1016 /* create the header */
1017 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1018 2 byte null */
1019 /* number of referral in this pkt */
1020 SSVAL(pdata,2,junction->referral_count);
1021 if(self_referral) {
1022 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1023 } else {
1024 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1025 }
1026
1027 offset = 8;
1028 /* add the referral elements */
1029 for(i=0;i<junction->referral_count;i++) {
1030 struct referral* ref = &junction->referral_list[i];
1031 int unilen;
1032
1033 SSVAL(pdata,offset,2); /* version 2 */
1034 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1035 if(self_referral) {
1036 SSVAL(pdata,offset+4,1);
1037 } else {
1038 SSVAL(pdata,offset+4,0);
1039 }
1040
1041 /* ref_flags :use path_consumed bytes? */
1042 SSVAL(pdata,offset+6,0);
1043 SIVAL(pdata,offset+8,ref->proximity);
1044 SIVAL(pdata,offset+12,ref->ttl);
1045
1046 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1047 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1048 /* copy referred path into current offset */
1049 unilen = rpcstr_push(pdata+uni_curroffset,
1050 ref->alternate_path,
1051 reply_size - uni_curroffset,
1052 STR_UNICODE);
1053
1054 SSVAL(pdata,offset+20,uni_curroffset-offset);
1055
1056 uni_curroffset += unilen;
1057 offset += VERSION2_REFERRAL_SIZE;
1058 }
1059 /* add in the unexplained 22 (0x16) bytes at the end */
1060 memset(pdata+uni_curroffset,'\0',0x16);
1061 return reply_size;
1062}
1063
1064static int setup_ver3_dfs_referral(const char *pathname,
1065 char **ppdata,
1066 struct junction_map *junction,
1067 bool self_referral)
1068{
1069 char *pdata = *ppdata;
1070
1071 smb_ucs2_t *uni_reqpath = NULL;
1072 int uni_reqpathoffset1, uni_reqpathoffset2;
1073 int uni_curroffset;
1074 int reply_size = 0;
1075
1076 int reqpathlen = 0;
1077 int offset,i=0;
1078
1079 DEBUG(10,("setting up version3 referral\n"));
1080
1081 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1082 if (uni_reqpath == NULL || reqpathlen == 0) {
1083 return -1;
1084 }
1085
1086 if (DEBUGLVL(10)) {
1087 dump_data(0, (unsigned char *)uni_reqpath,
1088 reqpathlen);
1089 }
1090
1091 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1092 VERSION3_REFERRAL_SIZE * junction->referral_count;
1093 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1094 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1095
1096 for(i=0;i<junction->referral_count;i++) {
1097 DEBUG(10,("referral %u : %s\n",
1098 i,
1099 junction->referral_list[i].alternate_path));
1100 reply_size +=
1101 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1102 }
1103
1104 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1105 if(pdata == NULL) {
1106 DEBUG(0,("version3 referral setup:"
1107 "malloc failed for Realloc!\n"));
1108 return -1;
1109 }
1110 *ppdata = pdata;
1111
1112 /* create the header */
1113 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1114 2 byte null */
1115 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1116 if(self_referral) {
1117 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1118 } else {
1119 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1120 }
1121
1122 /* copy in the reqpaths */
1123 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1124 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1125
1126 offset = 8;
1127 for(i=0;i<junction->referral_count;i++) {
1128 struct referral* ref = &(junction->referral_list[i]);
1129 int unilen;
1130
1131 SSVAL(pdata,offset,3); /* version 3 */
1132 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1133 if(self_referral) {
1134 SSVAL(pdata,offset+4,1);
1135 } else {
1136 SSVAL(pdata,offset+4,0);
1137 }
1138
1139 /* ref_flags :use path_consumed bytes? */
1140 SSVAL(pdata,offset+6,0);
1141 SIVAL(pdata,offset+8,ref->ttl);
1142
1143 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1144 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1145 /* copy referred path into current offset */
1146 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1147 reply_size - uni_curroffset,
1148 STR_UNICODE | STR_TERMINATE);
1149 SSVAL(pdata,offset+16,uni_curroffset-offset);
1150 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1151 memset(pdata+offset+18,'\0',16);
1152
1153 uni_curroffset += unilen;
1154 offset += VERSION3_REFERRAL_SIZE;
1155 }
1156 return reply_size;
1157}
1158
1159/******************************************************************
1160 Set up the DFS referral for the dfs pathname. This call returns
1161 the amount of the path covered by this server, and where the
1162 client should be redirected to. This is the meat of the
1163 TRANS2_GET_DFS_REFERRAL call.
1164******************************************************************/
1165
1166int setup_dfs_referral(connection_struct *orig_conn,
1167 const char *dfs_path,
1168 int max_referral_level,
1169 char **ppdata, NTSTATUS *pstatus)
1170{
1171 struct junction_map *junction = NULL;
1172 int consumedcnt = 0;
1173 bool self_referral = False;
1174 int reply_size = 0;
1175 char *pathnamep = NULL;
1176 char *local_dfs_path = NULL;
1177 TALLOC_CTX *ctx;
1178
1179 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1180 *pstatus = NT_STATUS_NO_MEMORY;
1181 return -1;
1182 }
1183
1184 /* get the junction entry */
1185 if (!dfs_path) {
1186 talloc_destroy(ctx);
1187 *pstatus = NT_STATUS_NOT_FOUND;
1188 return -1;
1189 }
1190
1191 /*
1192 * Trim pathname sent by client so it begins with only one backslash.
1193 * Two backslashes confuse some dfs clients
1194 */
1195
1196 local_dfs_path = talloc_strdup(ctx,dfs_path);
1197 if (!local_dfs_path) {
1198 *pstatus = NT_STATUS_NO_MEMORY;
1199 talloc_destroy(ctx);
1200 return -1;
1201 }
1202 pathnamep = local_dfs_path;
1203 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1204 IS_DIRECTORY_SEP(pathnamep[1])) {
1205 pathnamep++;
1206 }
1207
1208 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1209 if (!junction) {
1210 *pstatus = NT_STATUS_NO_MEMORY;
1211 talloc_destroy(ctx);
1212 return -1;
1213 }
1214
1215 /* The following call can change cwd. */
1216 *pstatus = get_referred_path(ctx, orig_conn->server_info,
1217 pathnamep, junction,
1218 &consumedcnt, &self_referral);
1219 if (!NT_STATUS_IS_OK(*pstatus)) {
1220 vfs_ChDir(orig_conn,orig_conn->connectpath);
1221 talloc_destroy(ctx);
1222 return -1;
1223 }
1224 vfs_ChDir(orig_conn,orig_conn->connectpath);
1225
1226 if (!self_referral) {
1227 pathnamep[consumedcnt] = '\0';
1228
1229 if( DEBUGLVL( 3 ) ) {
1230 int i=0;
1231 dbgtext("setup_dfs_referral: Path %s to "
1232 "alternate path(s):",
1233 pathnamep);
1234 for(i=0;i<junction->referral_count;i++)
1235 dbgtext(" %s",
1236 junction->referral_list[i].alternate_path);
1237 dbgtext(".\n");
1238 }
1239 }
1240
1241 /* create the referral depeding on version */
1242 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1243
1244 if (max_referral_level < 2) {
1245 max_referral_level = 2;
1246 }
1247 if (max_referral_level > 3) {
1248 max_referral_level = 3;
1249 }
1250
1251 switch(max_referral_level) {
1252 case 2:
1253 reply_size = setup_ver2_dfs_referral(pathnamep,
1254 ppdata, junction,
1255 self_referral);
1256 break;
1257 case 3:
1258 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1259 junction, self_referral);
1260 break;
1261 default:
1262 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1263 "version: %d\n",
1264 max_referral_level));
1265 talloc_destroy(ctx);
1266 *pstatus = NT_STATUS_INVALID_LEVEL;
1267 return -1;
1268 }
1269
1270 if (DEBUGLVL(10)) {
1271 DEBUGADD(0,("DFS Referral pdata:\n"));
1272 dump_data(0,(uint8 *)*ppdata,reply_size);
1273 }
1274
1275 talloc_destroy(ctx);
1276 *pstatus = NT_STATUS_OK;
1277 return reply_size;
1278}
1279
1280/**********************************************************************
1281 The following functions are called by the NETDFS RPC pipe functions
1282 **********************************************************************/
1283
1284/*********************************************************************
1285 Creates a junction structure from a DFS pathname
1286**********************************************************************/
1287
1288bool create_junction(TALLOC_CTX *ctx,
1289 const char *dfs_path,
1290 struct junction_map *jucn)
1291{
1292 int snum;
1293 bool dummy;
1294 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1295 NTSTATUS status;
1296
1297 if (!pdp) {
1298 return False;
1299 }
1300 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1301 if (!NT_STATUS_IS_OK(status)) {
1302 return False;
1303 }
1304
1305 /* check if path is dfs : validate first token */
1306 if (!is_myname_or_ipaddr(pdp->hostname)) {
1307 DEBUG(4,("create_junction: Invalid hostname %s "
1308 "in dfs path %s\n",
1309 pdp->hostname, dfs_path));
1310 TALLOC_FREE(pdp);
1311 return False;
1312 }
1313
1314 /* Check for a non-DFS share */
1315 snum = lp_servicenumber(pdp->servicename);
1316
1317 if(snum < 0 || !lp_msdfs_root(snum)) {
1318 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1319 pdp->servicename));
1320 TALLOC_FREE(pdp);
1321 return False;
1322 }
1323
1324 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1325 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1326 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1327
1328 TALLOC_FREE(pdp);
1329 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1330 return False;
1331 }
1332 return True;
1333}
1334
1335/**********************************************************************
1336 Forms a valid Unix pathname from the junction
1337 **********************************************************************/
1338
1339static bool junction_to_local_path(const struct junction_map *jucn,
1340 char **pp_path_out,
1341 connection_struct **conn_out,
1342 char **oldpath)
1343{
1344 int snum;
1345 NTSTATUS status;
1346
1347 snum = lp_servicenumber(jucn->service_name);
1348 if(snum < 0) {
1349 return False;
1350 }
1351 status = create_conn_struct(talloc_tos(), conn_out, snum,
1352 lp_pathname(snum), NULL, oldpath);
1353 if (!NT_STATUS_IS_OK(status)) {
1354 return False;
1355 }
1356
1357 *pp_path_out = talloc_asprintf(*conn_out,
1358 "%s/%s",
1359 lp_pathname(snum),
1360 jucn->volume_name);
1361 if (!*pp_path_out) {
1362 vfs_ChDir(*conn_out, *oldpath);
1363 conn_free(*conn_out);
1364 return False;
1365 }
1366 return True;
1367}
1368
1369bool create_msdfs_link(const struct junction_map *jucn)
1370{
1371 char *path = NULL;
1372 char *cwd;
1373 char *msdfs_link = NULL;
1374 connection_struct *conn;
1375 int i=0;
1376 bool insert_comma = False;
1377 bool ret = False;
1378
1379 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1380 return False;
1381 }
1382
1383 /* Form the msdfs_link contents */
1384 msdfs_link = talloc_strdup(conn, "msdfs:");
1385 if (!msdfs_link) {
1386 goto out;
1387 }
1388 for(i=0; i<jucn->referral_count; i++) {
1389 char *refpath = jucn->referral_list[i].alternate_path;
1390
1391 /* Alternate paths always use Windows separators. */
1392 trim_char(refpath, '\\', '\\');
1393 if(*refpath == '\0') {
1394 if (i == 0) {
1395 insert_comma = False;
1396 }
1397 continue;
1398 }
1399 if (i > 0 && insert_comma) {
1400 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1401 ",%s",
1402 refpath);
1403 } else {
1404 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1405 "%s",
1406 refpath);
1407 }
1408
1409 if (!msdfs_link) {
1410 goto out;
1411 }
1412 if (!insert_comma) {
1413 insert_comma = True;
1414 }
1415 }
1416
1417 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1418 path, msdfs_link));
1419
1420 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1421 if (errno == EEXIST) {
1422 struct smb_filename *smb_fname = NULL;
1423 NTSTATUS status;
1424
1425 status = create_synthetic_smb_fname(talloc_tos(), path,
1426 NULL, NULL,
1427 &smb_fname);
1428 if (!NT_STATUS_IS_OK(status)) {
1429 errno = map_errno_from_nt_status(status);
1430 goto out;
1431 }
1432
1433 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1434 TALLOC_FREE(smb_fname);
1435 goto out;
1436 }
1437 TALLOC_FREE(smb_fname);
1438 }
1439 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1440 DEBUG(1,("create_msdfs_link: symlink failed "
1441 "%s -> %s\nError: %s\n",
1442 path, msdfs_link, strerror(errno)));
1443 goto out;
1444 }
1445 }
1446
1447 ret = True;
1448
1449out:
1450 vfs_ChDir(conn, cwd);
1451 conn_free(conn);
1452 return ret;
1453}
1454
1455bool remove_msdfs_link(const struct junction_map *jucn)
1456{
1457 char *path = NULL;
1458 char *cwd;
1459 connection_struct *conn;
1460 bool ret = False;
1461 struct smb_filename *smb_fname = NULL;
1462 NTSTATUS status;
1463
1464 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1465 return false;
1466 }
1467
1468 status = create_synthetic_smb_fname(talloc_tos(), path,
1469 NULL, NULL,
1470 &smb_fname);
1471 if (!NT_STATUS_IS_OK(status)) {
1472 errno = map_errno_from_nt_status(status);
1473 return false;
1474 }
1475
1476 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1477 ret = True;
1478 }
1479
1480 TALLOC_FREE(smb_fname);
1481 vfs_ChDir(conn, cwd);
1482 conn_free(conn);
1483 return ret;
1484}
1485
1486/*********************************************************************
1487 Return the number of DFS links at the root of this share.
1488*********************************************************************/
1489
1490static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1491{
1492 size_t cnt = 0;
1493 SMB_STRUCT_DIR *dirp = NULL;
1494 const char *dname = NULL;
1495 char *talloced = NULL;
1496 const char *connect_path = lp_pathname(snum);
1497 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1498 connection_struct *conn;
1499 NTSTATUS status;
1500 char *cwd;
1501
1502 if(*connect_path == '\0') {
1503 return 0;
1504 }
1505
1506 /*
1507 * Fake up a connection struct for the VFS layer.
1508 */
1509
1510 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1511 NULL, &cwd);
1512 if (!NT_STATUS_IS_OK(status)) {
1513 DEBUG(3, ("create_conn_struct failed: %s\n",
1514 nt_errstr(status)));
1515 return 0;
1516 }
1517
1518 /* Count a link for the msdfs root - convention */
1519 cnt = 1;
1520
1521 /* No more links if this is an msdfs proxy. */
1522 if (*msdfs_proxy != '\0') {
1523 goto out;
1524 }
1525
1526 /* Now enumerate all dfs links */
1527 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1528 if(!dirp) {
1529 goto out;
1530 }
1531
1532 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1533 != NULL) {
1534 if (is_msdfs_link(conn,
1535 dname,
1536 NULL)) {
1537 cnt++;
1538 }
1539 TALLOC_FREE(talloced);
1540 }
1541
1542 SMB_VFS_CLOSEDIR(conn,dirp);
1543
1544out:
1545 vfs_ChDir(conn, cwd);
1546 conn_free(conn);
1547 return cnt;
1548}
1549
1550/*********************************************************************
1551*********************************************************************/
1552
1553static int form_junctions(TALLOC_CTX *ctx,
1554 int snum,
1555 struct junction_map *jucn,
1556 size_t jn_remain)
1557{
1558 size_t cnt = 0;
1559 SMB_STRUCT_DIR *dirp = NULL;
1560 const char *dname = NULL;
1561 char *talloced = NULL;
1562 const char *connect_path = lp_pathname(snum);
1563 char *service_name = lp_servicename(snum);
1564 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1565 connection_struct *conn;
1566 struct referral *ref = NULL;
1567 char *cwd;
1568 NTSTATUS status;
1569
1570 if (jn_remain == 0) {
1571 return 0;
1572 }
1573
1574 if(*connect_path == '\0') {
1575 return 0;
1576 }
1577
1578 /*
1579 * Fake up a connection struct for the VFS layer.
1580 */
1581
1582 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1583 &cwd);
1584 if (!NT_STATUS_IS_OK(status)) {
1585 DEBUG(3, ("create_conn_struct failed: %s\n",
1586 nt_errstr(status)));
1587 return 0;
1588 }
1589
1590 /* form a junction for the msdfs root - convention
1591 DO NOT REMOVE THIS: NT clients will not work with us
1592 if this is not present
1593 */
1594 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1595 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1596 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1597 goto out;
1598 }
1599 jucn[cnt].comment = "";
1600 jucn[cnt].referral_count = 1;
1601
1602 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1603 if (jucn[cnt].referral_list == NULL) {
1604 goto out;
1605 }
1606
1607 ref->proximity = 0;
1608 ref->ttl = REFERRAL_TTL;
1609 if (*msdfs_proxy != '\0') {
1610 ref->alternate_path = talloc_strdup(ctx,
1611 msdfs_proxy);
1612 } else {
1613 ref->alternate_path = talloc_asprintf(ctx,
1614 "\\\\%s\\%s",
1615 get_local_machine_name(),
1616 service_name);
1617 }
1618
1619 if (!ref->alternate_path) {
1620 goto out;
1621 }
1622 cnt++;
1623
1624 /* Don't enumerate if we're an msdfs proxy. */
1625 if (*msdfs_proxy != '\0') {
1626 goto out;
1627 }
1628
1629 /* Now enumerate all dfs links */
1630 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1631 if(!dirp) {
1632 goto out;
1633 }
1634
1635 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1636 != NULL) {
1637 char *link_target = NULL;
1638 if (cnt >= jn_remain) {
1639 DEBUG(2, ("form_junctions: ran out of MSDFS "
1640 "junction slots"));
1641 TALLOC_FREE(talloced);
1642 goto out;
1643 }
1644 if (is_msdfs_link_internal(ctx,
1645 conn,
1646 dname, &link_target,
1647 NULL)) {
1648 if (parse_msdfs_symlink(ctx,
1649 link_target,
1650 &jucn[cnt].referral_list,
1651 &jucn[cnt].referral_count)) {
1652
1653 jucn[cnt].service_name = talloc_strdup(ctx,
1654 service_name);
1655 jucn[cnt].volume_name = talloc_strdup(ctx,
1656 dname);
1657 if (!jucn[cnt].service_name ||
1658 !jucn[cnt].volume_name) {
1659 TALLOC_FREE(talloced);
1660 goto out;
1661 }
1662 jucn[cnt].comment = "";
1663 cnt++;
1664 }
1665 TALLOC_FREE(link_target);
1666 }
1667 TALLOC_FREE(talloced);
1668 }
1669
1670out:
1671
1672 if (dirp) {
1673 SMB_VFS_CLOSEDIR(conn,dirp);
1674 }
1675
1676 vfs_ChDir(conn, cwd);
1677 conn_free(conn);
1678 return cnt;
1679}
1680
1681struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1682{
1683 struct junction_map *jn = NULL;
1684 int i=0;
1685 size_t jn_count = 0;
1686 int sharecount = 0;
1687
1688 *p_num_jn = 0;
1689 if(!lp_host_msdfs()) {
1690 return NULL;
1691 }
1692
1693 /* Ensure all the usershares are loaded. */
1694 become_root();
1695 load_registry_shares();
1696 sharecount = load_usershare_shares();
1697 unbecome_root();
1698
1699 for(i=0;i < sharecount;i++) {
1700 if(lp_msdfs_root(i)) {
1701 jn_count += count_dfs_links(ctx, i);
1702 }
1703 }
1704 if (jn_count == 0) {
1705 return NULL;
1706 }
1707 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1708 if (!jn) {
1709 return NULL;
1710 }
1711 for(i=0; i < sharecount; i++) {
1712 if (*p_num_jn >= jn_count) {
1713 break;
1714 }
1715 if(lp_msdfs_root(i)) {
1716 *p_num_jn += form_junctions(ctx, i,
1717 &jn[*p_num_jn],
1718 jn_count - *p_num_jn);
1719 }
1720 }
1721 return jn;
1722}
1723
1724/******************************************************************************
1725 Core function to resolve a dfs pathname possibly containing a wildcard. If
1726 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1727 detected during dfs resolution.
1728******************************************************************************/
1729
1730NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1731 connection_struct *conn,
1732 bool dfs_pathnames,
1733 const char *name_in,
1734 bool allow_wcards,
1735 char **pp_name_out,
1736 bool *ppath_contains_wcard)
1737{
1738 bool path_contains_wcard;
1739 NTSTATUS status = NT_STATUS_OK;
1740
1741 if (dfs_pathnames) {
1742 status = dfs_redirect(ctx,
1743 conn,
1744 name_in,
1745 allow_wcards,
1746 pp_name_out,
1747 &path_contains_wcard);
1748
1749 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1750 *ppath_contains_wcard = path_contains_wcard;
1751 }
1752 } else {
1753 /*
1754 * Cheat and just return a copy of the in ptr.
1755 * Once srvstr_get_path() uses talloc it'll
1756 * be a talloced ptr anyway.
1757 */
1758 *pp_name_out = CONST_DISCARD(char *,name_in);
1759 }
1760 return status;
1761}
Note: See TracBrowser for help on using the repository browser.