source: branches/samba-3.0/source/smbd/msdfs.c@ 104

Last change on this file since 104 was 62, checked in by Paul Smedley, 18 years ago

Update source to 3.0.25c level

File size: 35.2 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 2 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, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22*/
23
24#define DBGC_CLASS DBGC_MSDFS
25#include "includes.h"
26
27extern uint32 global_client_caps;
28
29/**********************************************************************
30 Parse a DFS pathname of the form \hostname\service\reqpath
31 into the dfs_path structure.
32 If POSIX pathnames is true, the pathname may also be of the
33 form /hostname/service/reqpath.
34 We cope with either here.
35
36 Unfortunately, due to broken clients who might set the
37 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
38 send a local path, we have to cope with that too....
39
40 JRA.
41**********************************************************************/
42
43static NTSTATUS parse_dfs_path(const char *pathname,
44 BOOL allow_wcards,
45 struct dfs_path *pdp,
46 BOOL *ppath_contains_wcard)
47{
48 pstring pathname_local;
49 char *p,*temp;
50 NTSTATUS status = NT_STATUS_OK;
51 char sepchar;
52
53 ZERO_STRUCTP(pdp);
54
55 pstrcpy(pathname_local,pathname);
56 p = temp = pathname_local;
57
58 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
59
60 sepchar = pdp->posix_path ? '/' : '\\';
61
62 if (*pathname != sepchar) {
63 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
64 pathname, sepchar ));
65 /*
66 * Possibly client sent a local path by mistake.
67 * Try and convert to a local path.
68 */
69
70 pdp->hostname[0] = '\0';
71 pdp->servicename[0] = '\0';
72
73 /* We've got no info about separators. */
74 pdp->posix_path = lp_posix_pathnames();
75 p = temp;
76 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
77 temp));
78 goto local_path;
79 }
80
81 trim_char(temp,sepchar,sepchar);
82
83 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
84 temp, sepchar));
85
86 /* Now tokenize. */
87 /* Parse out hostname. */
88 p = strchr_m(temp,sepchar);
89 if(p == NULL) {
90 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
91 temp));
92 /*
93 * Possibly client sent a local path by mistake.
94 * Try and convert to a local path.
95 */
96
97 pdp->hostname[0] = '\0';
98 pdp->servicename[0] = '\0';
99
100 p = temp;
101 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
102 temp));
103 goto local_path;
104 }
105 *p = '\0';
106 fstrcpy(pdp->hostname,temp);
107 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
108
109 /* If we got a hostname, is it ours (or an IP address) ? */
110 if (!is_myname_or_ipaddr(pdp->hostname)) {
111 /* Repair path. */
112 *p = sepchar;
113 DEBUG(10,("parse_dfs_path: hostname %s isn't ours. Try local path from path %s\n",
114 pdp->hostname, temp));
115 /*
116 * Possibly client sent a local path by mistake.
117 * Try and convert to a local path.
118 */
119
120 pdp->hostname[0] = '\0';
121 pdp->servicename[0] = '\0';
122
123 p = temp;
124 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
125 temp));
126 goto local_path;
127 }
128
129 /* Parse out servicename. */
130 temp = p+1;
131 p = strchr_m(temp,sepchar);
132 if(p == NULL) {
133 fstrcpy(pdp->servicename,temp);
134 pdp->reqpath[0] = '\0';
135 return NT_STATUS_OK;
136 }
137 *p = '\0';
138 fstrcpy(pdp->servicename,temp);
139 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
140
141 p++;
142
143 local_path:
144
145 *ppath_contains_wcard = False;
146
147 /* Rest is reqpath. */
148 if (pdp->posix_path) {
149 status = check_path_syntax_posix(pdp->reqpath, p);
150 } else {
151 if (allow_wcards) {
152 status = check_path_syntax_wcard(pdp->reqpath, p, ppath_contains_wcard);
153 } else {
154 status = check_path_syntax(pdp->reqpath, p);
155 }
156 }
157
158 if (!NT_STATUS_IS_OK(status)) {
159 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
160 p, nt_errstr(status) ));
161 return status;
162 }
163
164 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
165 return NT_STATUS_OK;
166}
167
168/********************************************************
169 Fake up a connection struct for the VFS layer.
170 Note this CHANGES CWD !!!! JRA.
171*********************************************************/
172
173static NTSTATUS create_conn_struct(connection_struct *conn, int snum, const char *path)
174{
175 pstring connpath;
176
177 ZERO_STRUCTP(conn);
178
179 pstrcpy(connpath, path);
180 pstring_sub(connpath , "%S", lp_servicename(snum));
181
182 /* needed for smbd_vfs_init() */
183
184 if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) {
185 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
186 return NT_STATUS_NO_MEMORY;
187 }
188
189 if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx, struct share_params))) {
190 DEBUG(0, ("TALLOC failed\n"));
191 return NT_STATUS_NO_MEMORY;
192 }
193
194 conn->params->service = snum;
195
196 set_conn_connectpath(conn, connpath);
197
198 if (!smbd_vfs_init(conn)) {
199 NTSTATUS status = map_nt_error_from_unix(errno);
200 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
201 conn_free_internal(conn);
202 return status;
203 }
204
205 /*
206 * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
207 * share as the anonymous user. If we try to chdir as that user we will
208 * fail.... WTF ? JRA.
209 */
210
211 if (vfs_ChDir(conn,conn->connectpath) != 0) {
212 NTSTATUS status = map_nt_error_from_unix(errno);
213 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
214 conn->connectpath, strerror(errno) ));
215 conn_free_internal(conn);
216 return status;
217 }
218
219 return NT_STATUS_OK;
220}
221
222/**********************************************************************
223 Parse the contents of a symlink to verify if it is an msdfs referral
224 A valid referral is of the form:
225
226 msdfs:server1\share1,server2\share2
227 msdfs:server1\share1\pathname,server2\share2\pathname
228 msdfs:server1/share1,server2/share2
229 msdfs:server1/share1/pathname,server2/share2/pathname.
230
231 Note that the alternate paths returned here must be of the canonicalized
232 form:
233
234 \server\share or
235 \server\share\path\to\file,
236
237 even in posix path mode. This is because we have no knowledge if the
238 server we're referring to understands posix paths.
239 **********************************************************************/
240
241static BOOL parse_msdfs_symlink(TALLOC_CTX *ctx,
242 char *target,
243 struct referral **preflist,
244 int *refcount)
245{
246 pstring temp;
247 char *prot;
248 char *alt_path[MAX_REFERRAL_COUNT];
249 int count = 0, i;
250 struct referral *reflist;
251
252 pstrcpy(temp,target);
253 prot = strtok(temp,":");
254 if (!prot) {
255 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
256 return False;
257 }
258
259 /* parse out the alternate paths */
260 while((count<MAX_REFERRAL_COUNT) &&
261 ((alt_path[count] = strtok(NULL,",")) != NULL)) {
262 count++;
263 }
264
265 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
266
267 if (count) {
268 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx, struct referral, count);
269 if(reflist == NULL) {
270 DEBUG(0,("parse_msdfs_symlink: talloc failed!\n"));
271 return False;
272 }
273 } else {
274 reflist = *preflist = NULL;
275 }
276
277 for(i=0;i<count;i++) {
278 char *p;
279
280 /* Canonicalize link target. Replace all /'s in the path by a \ */
281 string_replace(alt_path[i], '/', '\\');
282
283 /* Remove leading '\\'s */
284 p = alt_path[i];
285 while (*p && (*p == '\\')) {
286 p++;
287 }
288
289 pstrcpy(reflist[i].alternate_path, "\\");
290 pstrcat(reflist[i].alternate_path, p);
291
292 reflist[i].proximity = 0;
293 reflist[i].ttl = REFERRAL_TTL;
294 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n", reflist[i].alternate_path));
295 *refcount += 1;
296 }
297
298 return True;
299}
300
301/**********************************************************************
302 Returns true if the unix path is a valid msdfs symlink and also
303 returns the target string from inside the link.
304**********************************************************************/
305
306BOOL is_msdfs_link(connection_struct *conn,
307 const char *path,
308 pstring link_target,
309 SMB_STRUCT_STAT *sbufp)
310{
311 SMB_STRUCT_STAT st;
312 int referral_len = 0;
313
314 if (sbufp == NULL) {
315 sbufp = &st;
316 }
317
318 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
319 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
320 return False;
321 }
322
323 if (!S_ISLNK(sbufp->st_mode)) {
324 DEBUG(5,("is_msdfs_link: %s is not a link.\n",path));
325 return False;
326 }
327
328 /* open the link and read it */
329 referral_len = SMB_VFS_READLINK(conn, path, link_target, sizeof(pstring)-1);
330 if (referral_len == -1) {
331 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n",
332 path, strerror(errno)));
333 return False;
334 }
335 link_target[referral_len] = '\0';
336
337 DEBUG(5,("is_msdfs_link: %s -> %s\n",path, link_target));
338
339 if (!strnequal(link_target, "msdfs:", 6)) {
340 return False;
341 }
342 return True;
343}
344
345/*****************************************************************
346 Used by other functions to decide if a dfs path is remote,
347 and to get the list of referred locations for that remote path.
348
349 search_flag: For findfirsts, dfs links themselves are not
350 redirected, but paths beyond the links are. For normal smb calls,
351 even dfs links need to be redirected.
352
353 consumedcntp: how much of the dfs path is being redirected. the client
354 should try the remaining path on the redirected server.
355
356 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
357 link redirect are in targetpath.
358*****************************************************************/
359
360static NTSTATUS dfs_path_lookup(connection_struct *conn,
361 const char *dfspath, /* Incoming complete dfs path */
362 const struct dfs_path *pdp, /* Parsed out server+share+extrapath. */
363 BOOL search_flag, /* Called from a findfirst ? */
364 int *consumedcntp,
365 pstring targetpath)
366{
367 char *p = NULL;
368 char *q = NULL;
369 SMB_STRUCT_STAT sbuf;
370 NTSTATUS status;
371 pstring localpath;
372 pstring canon_dfspath; /* Canonicalized dfs path. (only '/' components). */
373
374 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
375 conn->connectpath, pdp->reqpath));
376
377 /*
378 * Note the unix path conversion here we're doing we can
379 * throw away. We're looking for a symlink for a dfs
380 * resolution, if we don't find it we'll do another
381 * unix_convert later in the codepath.
382 * If we needed to remember what we'd resolved in
383 * dp->reqpath (as the original code did) we'd
384 * pstrcpy(localhost, dp->reqpath) on any code
385 * path below that returns True - but I don't
386 * think this is needed. JRA.
387 */
388
389 pstrcpy(localpath, pdp->reqpath);
390 status = unix_convert(conn, localpath, search_flag, NULL, &sbuf);
391 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
392 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
393 return status;
394 }
395
396 /* Optimization - check if we can redirect the whole path. */
397
398 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
399 if (search_flag) {
400 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
401 "for dfs link %s.\n", dfspath));
402 return NT_STATUS_OK;
403 }
404
405 DEBUG(6,("dfs_path_lookup: %s resolves to a "
406 "valid dfs link %s.\n", dfspath, targetpath));
407
408 if (consumedcntp) {
409 *consumedcntp = strlen(dfspath);
410 }
411 return NT_STATUS_PATH_NOT_COVERED;
412 }
413
414 /* Prepare to test only for '/' components in the given path,
415 * so if a Windows path replace all '\\' characters with '/'.
416 * For a POSIX DFS path we know all separators are already '/'. */
417
418 pstrcpy(canon_dfspath, dfspath);
419 if (!pdp->posix_path) {
420 string_replace(canon_dfspath, '\\', '/');
421 }
422
423 /*
424 * localpath comes out of unix_convert, so it has
425 * no trailing backslash. Make sure that canon_dfspath hasn't either.
426 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
427 */
428
429 trim_char(canon_dfspath,0,'/');
430
431 /*
432 * Redirect if any component in the path is a link.
433 * We do this by walking backwards through the
434 * local path, chopping off the last component
435 * in both the local path and the canonicalized
436 * DFS path. If we hit a DFS link then we're done.
437 */
438
439 p = strrchr_m(localpath, '/');
440 if (consumedcntp) {
441 q = strrchr_m(canon_dfspath, '/');
442 }
443
444 while (p) {
445 *p = '\0';
446 if (q) {
447 *q = '\0';
448 }
449
450 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
451 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
452 "parent %s is dfs link\n", dfspath, localpath));
453
454 if (consumedcntp) {
455 *consumedcntp = strlen(canon_dfspath);
456 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
457 "(%d)\n", canon_dfspath, *consumedcntp));
458 }
459
460 return NT_STATUS_PATH_NOT_COVERED;
461 }
462
463 /* Step back on the filesystem. */
464 p = strrchr_m(localpath, '/');
465
466 if (consumedcntp) {
467 /* And in the canonicalized dfs path. */
468 q = strrchr_m(canon_dfspath, '/');
469 }
470 }
471
472 return NT_STATUS_OK;
473}
474
475/*****************************************************************
476 Decides if a dfs pathname should be redirected or not.
477 If not, the pathname is converted to a tcon-relative local unix path
478
479 search_wcard_flag: this flag performs 2 functions bother related
480 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
481 for details.
482
483 This function can return NT_STATUS_OK, meaning use the returned path as-is
484 (mapped into a local path).
485 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
486 any other NT_STATUS error which is a genuine error to be
487 returned to the client.
488*****************************************************************/
489
490static NTSTATUS dfs_redirect( connection_struct *conn,
491 pstring dfs_path,
492 BOOL search_wcard_flag,
493 BOOL *ppath_contains_wcard)
494{
495 NTSTATUS status;
496 struct dfs_path dp;
497 pstring targetpath;
498
499 status = parse_dfs_path(dfs_path, search_wcard_flag, &dp, ppath_contains_wcard);
500 if (!NT_STATUS_IS_OK(status)) {
501 return status;
502 }
503
504 if (dp.reqpath[0] == '\0') {
505 pstrcpy(dfs_path, dp.reqpath);
506 DEBUG(5,("dfs_redirect: self-referral.\n"));
507 return NT_STATUS_OK;
508 }
509
510 /* If dfs pathname for a non-dfs share, convert to tcon-relative
511 path and return OK */
512
513 if (!lp_msdfs_root(SNUM(conn))) {
514 pstrcpy(dfs_path, dp.reqpath);
515 return NT_STATUS_OK;
516 }
517
518 /* If it looked like a local path (zero hostname/servicename)
519 * just treat as a tcon-relative path. */
520
521 if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') {
522 pstrcpy(dfs_path, dp.reqpath);
523 return NT_STATUS_OK;
524 }
525
526 if (!( strequal(dp.servicename, lp_servicename(SNUM(conn)))
527 || (strequal(dp.servicename, HOMES_NAME)
528 && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) {
529
530 /* The given sharename doesn't match this connection. */
531
532 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
533 }
534
535 status = dfs_path_lookup(conn, dfs_path, &dp,
536 search_wcard_flag, NULL, targetpath);
537 if (!NT_STATUS_IS_OK(status)) {
538 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
539 DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path));
540 } else {
541 DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
542 dfs_path, nt_errstr(status) ));
543 }
544 return status;
545 }
546
547 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path));
548
549 /* Form non-dfs tcon-relative path */
550 pstrcpy(dfs_path, dp.reqpath);
551
552 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path));
553 return NT_STATUS_OK;
554}
555
556/**********************************************************************
557 Return a self referral.
558**********************************************************************/
559
560static NTSTATUS self_ref(TALLOC_CTX *ctx,
561 const char *dfs_path,
562 struct junction_map *jucn,
563 int *consumedcntp,
564 BOOL *self_referralp)
565{
566 struct referral *ref;
567
568 *self_referralp = True;
569
570 jucn->referral_count = 1;
571 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
572 DEBUG(0,("self_ref: talloc failed for referral\n"));
573 return NT_STATUS_NO_MEMORY;
574 }
575
576 pstrcpy(ref->alternate_path,dfs_path);
577 ref->proximity = 0;
578 ref->ttl = REFERRAL_TTL;
579 jucn->referral_list = ref;
580 *consumedcntp = strlen(dfs_path);
581 return NT_STATUS_OK;
582}
583
584/**********************************************************************
585 Gets valid referrals for a dfs path and fills up the
586 junction_map structure.
587**********************************************************************/
588
589NTSTATUS get_referred_path(TALLOC_CTX *ctx,
590 const char *dfs_path,
591 struct junction_map *jucn,
592 int *consumedcntp,
593 BOOL *self_referralp)
594{
595 struct connection_struct conns;
596 struct connection_struct *conn = &conns;
597 struct dfs_path dp;
598 pstring conn_path;
599 pstring targetpath;
600 int snum;
601 NTSTATUS status = NT_STATUS_NOT_FOUND;
602 BOOL dummy;
603
604 ZERO_STRUCT(conns);
605
606 *self_referralp = False;
607
608 status = parse_dfs_path(dfs_path, False, &dp, &dummy);
609 if (!NT_STATUS_IS_OK(status)) {
610 return status;
611 }
612
613 /* Verify hostname in path */
614 if (!is_myname_or_ipaddr(dp.hostname)) {
615 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
616 dp.hostname, dfs_path));
617 return NT_STATUS_NOT_FOUND;
618 }
619
620 fstrcpy(jucn->service_name, dp.servicename);
621 pstrcpy(jucn->volume_name, dp.reqpath);
622
623 /* Verify the share is a dfs root */
624 snum = lp_servicenumber(jucn->service_name);
625 if(snum < 0) {
626 if ((snum = find_service(jucn->service_name)) < 0) {
627 return NT_STATUS_NOT_FOUND;
628 }
629 }
630
631 if (!lp_msdfs_root(snum)) {
632 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
633 dp.servicename, dfs_path));
634 return NT_STATUS_NOT_FOUND;
635 }
636
637 /*
638 * Self referrals are tested with a anonymous IPC connection and
639 * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
640 * to an empty string). create_conn_struct cd's into the directory and will
641 * fail if it cannot (as the anonymous user). Cope with this.
642 */
643
644 if (dp.reqpath[0] == '\0') {
645 struct referral *ref;
646
647 if (*lp_msdfs_proxy(snum) == '\0') {
648 return self_ref(ctx,
649 dfs_path,
650 jucn,
651 consumedcntp,
652 self_referralp);
653 }
654
655 /*
656 * It's an msdfs proxy share. Redirect to
657 * the configured target share.
658 */
659
660 jucn->referral_count = 1;
661 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
662 DEBUG(0, ("malloc failed for referral\n"));
663 return NT_STATUS_NO_MEMORY;
664 }
665
666 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
667 if (dp.reqpath[0] != '\0') {
668 pstrcat(ref->alternate_path, dp.reqpath);
669 }
670 ref->proximity = 0;
671 ref->ttl = REFERRAL_TTL;
672 jucn->referral_list = ref;
673 *consumedcntp = strlen(dfs_path);
674 return NT_STATUS_OK;
675 }
676
677 pstrcpy(conn_path, lp_pathname(snum));
678 status = create_conn_struct(conn, snum, conn_path);
679 if (!NT_STATUS_IS_OK(status)) {
680 return status;
681 }
682
683 /* If this is a DFS path dfs_lookup should return
684 * NT_STATUS_PATH_NOT_COVERED. */
685
686 status = dfs_path_lookup(conn, dfs_path, &dp,
687 False, consumedcntp, targetpath);
688
689 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
690 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
691 dfs_path));
692 conn_free_internal(conn);
693 return status;
694 }
695
696 /* We know this is a valid dfs link. Parse the targetpath. */
697 if (!parse_msdfs_symlink(ctx, targetpath,
698 &jucn->referral_list,
699 &jucn->referral_count)) {
700 DEBUG(3,("get_referred_path: failed to parse symlink "
701 "target %s\n", targetpath ));
702 conn_free_internal(conn);
703 return NT_STATUS_NOT_FOUND;
704 }
705
706 conn_free_internal(conn);
707 return NT_STATUS_OK;
708}
709
710static int setup_ver2_dfs_referral(const char *pathname,
711 char **ppdata,
712 struct junction_map *junction,
713 int consumedcnt,
714 BOOL self_referral)
715{
716 char* pdata = *ppdata;
717
718 unsigned char uni_requestedpath[1024];
719 int uni_reqpathoffset1,uni_reqpathoffset2;
720 int uni_curroffset;
721 int requestedpathlen=0;
722 int offset;
723 int reply_size = 0;
724 int i=0;
725
726 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
727
728 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
729 STR_TERMINATE);
730
731 if (DEBUGLVL(10)) {
732 dump_data(0, (const char *) uni_requestedpath,requestedpathlen);
733 }
734
735 DEBUG(10,("ref count = %u\n",junction->referral_count));
736
737 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
738 VERSION2_REFERRAL_SIZE * junction->referral_count;
739
740 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
741
742 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
743
744 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
745 2 * requestedpathlen;
746 DEBUG(10,("reply_size: %u\n",reply_size));
747
748 /* add up the unicode lengths of all the referral paths */
749 for(i=0;i<junction->referral_count;i++) {
750 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
751 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
752 }
753
754 DEBUG(10,("reply_size = %u\n",reply_size));
755 /* add the unexplained 0x16 bytes */
756 reply_size += 0x16;
757
758 pdata = (char *)SMB_REALLOC(pdata,reply_size);
759 if(pdata == NULL) {
760 DEBUG(0,("Realloc failed!\n"));
761 return -1;
762 }
763 *ppdata = pdata;
764
765 /* copy in the dfs requested paths.. required for offset calculations */
766 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
767 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
768
769 /* create the header */
770 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
771 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
772 if(self_referral) {
773 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
774 } else {
775 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
776 }
777
778 offset = 8;
779 /* add the referral elements */
780 for(i=0;i<junction->referral_count;i++) {
781 struct referral* ref = &junction->referral_list[i];
782 int unilen;
783
784 SSVAL(pdata,offset,2); /* version 2 */
785 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
786 if(self_referral) {
787 SSVAL(pdata,offset+4,1);
788 } else {
789 SSVAL(pdata,offset+4,0);
790 }
791 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
792 SIVAL(pdata,offset+8,ref->proximity);
793 SIVAL(pdata,offset+12,ref->ttl);
794
795 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
796 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
797 /* copy referred path into current offset */
798 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
799 sizeof(pstring), STR_UNICODE);
800
801 SSVAL(pdata,offset+20,uni_curroffset-offset);
802
803 uni_curroffset += unilen;
804 offset += VERSION2_REFERRAL_SIZE;
805 }
806 /* add in the unexplained 22 (0x16) bytes at the end */
807 memset(pdata+uni_curroffset,'\0',0x16);
808 return reply_size;
809}
810
811static int setup_ver3_dfs_referral(const char *pathname,
812 char **ppdata,
813 struct junction_map *junction,
814 int consumedcnt,
815 BOOL self_referral)
816{
817 char* pdata = *ppdata;
818
819 unsigned char uni_reqpath[1024];
820 int uni_reqpathoffset1, uni_reqpathoffset2;
821 int uni_curroffset;
822 int reply_size = 0;
823
824 int reqpathlen = 0;
825 int offset,i=0;
826
827 DEBUG(10,("setting up version3 referral\n"));
828
829 reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
830
831 if (DEBUGLVL(10)) {
832 dump_data(0, (char *) uni_reqpath,reqpathlen);
833 }
834
835 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
836 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
837 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
838
839 for(i=0;i<junction->referral_count;i++) {
840 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
841 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
842 }
843
844 pdata = (char *)SMB_REALLOC(pdata,reply_size);
845 if(pdata == NULL) {
846 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
847 return -1;
848 }
849 *ppdata = pdata;
850
851 /* create the header */
852 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
853 SSVAL(pdata,2,junction->referral_count); /* number of referral */
854 if(self_referral) {
855 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
856 } else {
857 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
858 }
859
860 /* copy in the reqpaths */
861 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
862 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
863
864 offset = 8;
865 for(i=0;i<junction->referral_count;i++) {
866 struct referral* ref = &(junction->referral_list[i]);
867 int unilen;
868
869 SSVAL(pdata,offset,3); /* version 3 */
870 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
871 if(self_referral) {
872 SSVAL(pdata,offset+4,1);
873 } else {
874 SSVAL(pdata,offset+4,0);
875 }
876
877 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
878 SIVAL(pdata,offset+8,ref->ttl);
879
880 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
881 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
882 /* copy referred path into current offset */
883 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
884 sizeof(pstring), STR_UNICODE | STR_TERMINATE);
885 SSVAL(pdata,offset+16,uni_curroffset-offset);
886 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
887 memset(pdata+offset+18,'\0',16);
888
889 uni_curroffset += unilen;
890 offset += VERSION3_REFERRAL_SIZE;
891 }
892 return reply_size;
893}
894
895/******************************************************************
896 Set up the DFS referral for the dfs pathname. This call returns
897 the amount of the path covered by this server, and where the
898 client should be redirected to. This is the meat of the
899 TRANS2_GET_DFS_REFERRAL call.
900******************************************************************/
901
902int setup_dfs_referral(connection_struct *orig_conn,
903 const char *dfs_path,
904 int max_referral_level,
905 char **ppdata, NTSTATUS *pstatus)
906{
907 struct junction_map junction;
908 int consumedcnt = 0;
909 BOOL self_referral = False;
910 int reply_size = 0;
911 char *pathnamep = NULL;
912 pstring local_dfs_path;
913 TALLOC_CTX *ctx;
914
915 if (!(ctx=talloc_init("setup_dfs_referral"))) {
916 *pstatus = NT_STATUS_NO_MEMORY;
917 return -1;
918 }
919
920 ZERO_STRUCT(junction);
921
922 /* get the junction entry */
923 if (!dfs_path) {
924 talloc_destroy(ctx);
925 *pstatus = NT_STATUS_NOT_FOUND;
926 return -1;
927 }
928
929 /*
930 * Trim pathname sent by client so it begins with only one backslash.
931 * Two backslashes confuse some dfs clients
932 */
933
934 pstrcpy(local_dfs_path, dfs_path);
935 pathnamep = local_dfs_path;
936 while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) {
937 pathnamep++;
938 }
939
940 /* The following call can change cwd. */
941 *pstatus = get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral);
942 if (!NT_STATUS_IS_OK(*pstatus)) {
943 vfs_ChDir(orig_conn,orig_conn->connectpath);
944 talloc_destroy(ctx);
945 return -1;
946 }
947 vfs_ChDir(orig_conn,orig_conn->connectpath);
948
949 if (!self_referral) {
950 pathnamep[consumedcnt] = '\0';
951
952 if( DEBUGLVL( 3 ) ) {
953 int i=0;
954 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
955 for(i=0;i<junction.referral_count;i++)
956 dbgtext(" %s",junction.referral_list[i].alternate_path);
957 dbgtext(".\n");
958 }
959 }
960
961 /* create the referral depeding on version */
962 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
963
964 if (max_referral_level < 2) {
965 max_referral_level = 2;
966 }
967 if (max_referral_level > 3) {
968 max_referral_level = 3;
969 }
970
971 switch(max_referral_level) {
972 case 2:
973 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
974 consumedcnt, self_referral);
975 break;
976 case 3:
977 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
978 consumedcnt, self_referral);
979 break;
980 default:
981 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
982 talloc_destroy(ctx);
983 *pstatus = NT_STATUS_INVALID_LEVEL;
984 return -1;
985 }
986
987 if (DEBUGLVL(10)) {
988 DEBUGADD(0,("DFS Referral pdata:\n"));
989 dump_data(0,*ppdata,reply_size);
990 }
991
992 talloc_destroy(ctx);
993 *pstatus = NT_STATUS_OK;
994 return reply_size;
995}
996
997/**********************************************************************
998 The following functions are called by the NETDFS RPC pipe functions
999 **********************************************************************/
1000
1001/*********************************************************************
1002 Creates a junction structure from a DFS pathname
1003**********************************************************************/
1004
1005BOOL create_junction(const char *dfs_path, struct junction_map *jucn)
1006{
1007 int snum;
1008 BOOL dummy;
1009 struct dfs_path dp;
1010
1011 NTSTATUS status = parse_dfs_path(dfs_path, False, &dp, &dummy);
1012
1013 if (!NT_STATUS_IS_OK(status)) {
1014 return False;
1015 }
1016
1017 /* check if path is dfs : validate first token */
1018 if (!is_myname_or_ipaddr(dp.hostname)) {
1019 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
1020 dp.hostname, dfs_path));
1021 return False;
1022 }
1023
1024 /* Check for a non-DFS share */
1025 snum = lp_servicenumber(dp.servicename);
1026
1027 if(snum < 0 || !lp_msdfs_root(snum)) {
1028 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1029 dp.servicename));
1030 return False;
1031 }
1032
1033 fstrcpy(jucn->service_name,dp.servicename);
1034 pstrcpy(jucn->volume_name,dp.reqpath);
1035 pstrcpy(jucn->comment, lp_comment(snum));
1036 return True;
1037}
1038
1039/**********************************************************************
1040 Forms a valid Unix pathname from the junction
1041 **********************************************************************/
1042
1043static BOOL junction_to_local_path(struct junction_map *jucn,
1044 char *path,
1045 int max_pathlen,
1046 connection_struct *conn_out)
1047{
1048 int snum;
1049 pstring conn_path;
1050
1051 snum = lp_servicenumber(jucn->service_name);
1052 if(snum < 0) {
1053 return False;
1054 }
1055
1056 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
1057 safe_strcat(path, "/", max_pathlen-1);
1058 safe_strcat(path, jucn->volume_name, max_pathlen-1);
1059
1060 pstrcpy(conn_path, lp_pathname(snum));
1061 if (!NT_STATUS_IS_OK(create_conn_struct(conn_out, snum, conn_path))) {
1062 return False;
1063 }
1064
1065 return True;
1066}
1067
1068BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
1069{
1070 pstring path;
1071 pstring msdfs_link;
1072 connection_struct conns;
1073 connection_struct *conn = &conns;
1074 int i=0;
1075 BOOL insert_comma = False;
1076 BOOL ret = False;
1077
1078 ZERO_STRUCT(conns);
1079
1080 if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
1081 return False;
1082 }
1083
1084 /* Form the msdfs_link contents */
1085 pstrcpy(msdfs_link, "msdfs:");
1086 for(i=0; i<jucn->referral_count; i++) {
1087 char* refpath = jucn->referral_list[i].alternate_path;
1088
1089 /* Alternate paths always use Windows separators. */
1090 trim_char(refpath, '\\', '\\');
1091 if(*refpath == '\0') {
1092 if (i == 0) {
1093 insert_comma = False;
1094 }
1095 continue;
1096 }
1097 if (i > 0 && insert_comma) {
1098 pstrcat(msdfs_link, ",");
1099 }
1100
1101 pstrcat(msdfs_link, refpath);
1102 if (!insert_comma) {
1103 insert_comma = True;
1104 }
1105 }
1106
1107 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1108 path, msdfs_link));
1109
1110 if(exists) {
1111 if(SMB_VFS_UNLINK(conn,path)!=0) {
1112 goto out;
1113 }
1114 }
1115
1116 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1117 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
1118 path, msdfs_link, strerror(errno)));
1119 goto out;
1120 }
1121
1122
1123 ret = True;
1124
1125out:
1126
1127 conn_free_internal(conn);
1128 return ret;
1129}
1130
1131BOOL remove_msdfs_link(struct junction_map *jucn)
1132{
1133 pstring path;
1134 connection_struct conns;
1135 connection_struct *conn = &conns;
1136 BOOL ret = False;
1137
1138 ZERO_STRUCT(conns);
1139
1140 if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1141 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1142 ret = True;
1143 }
1144 talloc_destroy( conn->mem_ctx );
1145 }
1146
1147 conn_free_internal(conn);
1148 return ret;
1149}
1150
1151static int form_junctions(TALLOC_CTX *ctx,
1152 int snum,
1153 struct junction_map *jucn,
1154 int jn_remain)
1155{
1156 int cnt = 0;
1157 SMB_STRUCT_DIR *dirp;
1158 char *dname;
1159 pstring connect_path;
1160 char *service_name = lp_servicename(snum);
1161 connection_struct conn;
1162 struct referral *ref = NULL;
1163
1164 ZERO_STRUCT(conn);
1165
1166 if (jn_remain <= 0) {
1167 return 0;
1168 }
1169
1170 pstrcpy(connect_path,lp_pathname(snum));
1171
1172 if(*connect_path == '\0') {
1173 return 0;
1174 }
1175
1176 /*
1177 * Fake up a connection struct for the VFS layer.
1178 */
1179
1180 if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
1181 return 0;
1182 }
1183
1184 /* form a junction for the msdfs root - convention
1185 DO NOT REMOVE THIS: NT clients will not work with us
1186 if this is not present
1187 */
1188 fstrcpy(jucn[cnt].service_name, service_name);
1189 jucn[cnt].volume_name[0] = '\0';
1190 jucn[cnt].referral_count = 1;
1191
1192 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1193 if (jucn[cnt].referral_list == NULL) {
1194 DEBUG(0, ("talloc failed!\n"));
1195 goto out;
1196 }
1197
1198 ref->proximity = 0;
1199 ref->ttl = REFERRAL_TTL;
1200 if (*lp_msdfs_proxy(snum) != '\0') {
1201 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1202 cnt++;
1203 goto out;
1204 }
1205
1206 pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
1207 get_local_machine_name(),
1208 service_name);
1209 cnt++;
1210
1211 /* Now enumerate all dfs links */
1212 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1213 if(!dirp) {
1214 goto out;
1215 }
1216
1217 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1218 pstring link_target;
1219 if (cnt >= jn_remain) {
1220 SMB_VFS_CLOSEDIR(&conn,dirp);
1221 DEBUG(2, ("ran out of MSDFS junction slots"));
1222 goto out;
1223 }
1224 if (is_msdfs_link(&conn, dname, link_target, NULL)) {
1225 if (parse_msdfs_symlink(ctx,
1226 link_target,
1227 &jucn[cnt].referral_list,
1228 &jucn[cnt].referral_count)) {
1229
1230 fstrcpy(jucn[cnt].service_name, service_name);
1231 pstrcpy(jucn[cnt].volume_name, dname);
1232 cnt++;
1233 }
1234 }
1235 }
1236
1237 SMB_VFS_CLOSEDIR(&conn,dirp);
1238
1239out:
1240
1241 conn_free_internal(&conn);
1242 return cnt;
1243}
1244
1245int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1246{
1247 int i=0;
1248 int sharecount = 0;
1249 int jn_count = 0;
1250
1251 if(!lp_host_msdfs()) {
1252 return 0;
1253 }
1254
1255 /* Ensure all the usershares are loaded. */
1256 become_root();
1257 sharecount = load_usershare_shares();
1258 unbecome_root();
1259
1260 for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1261 if(lp_msdfs_root(i)) {
1262 jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1263 }
1264 }
1265 return jn_count;
1266}
1267
1268/******************************************************************************
1269 Core function to resolve a dfs pathname.
1270******************************************************************************/
1271
1272NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name)
1273{
1274 NTSTATUS status = NT_STATUS_OK;
1275 BOOL dummy;
1276 if (dfs_pathnames) {
1277 status = dfs_redirect(conn, name, False, &dummy);
1278 }
1279 return status;
1280}
1281
1282/******************************************************************************
1283 Core function to resolve a dfs pathname possibly containing a wildcard.
1284 This function is identical to the above except for the BOOL param to
1285 dfs_redirect but I need this to be separate so it's really clear when
1286 we're allowing wildcards and when we're not. JRA.
1287******************************************************************************/
1288
1289NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard)
1290{
1291 NTSTATUS status = NT_STATUS_OK;
1292 if (dfs_pathnames) {
1293 status = dfs_redirect(conn, name, True, ppath_contains_wcard);
1294 }
1295 return status;
1296}
Note: See TracBrowser for help on using the repository browser.