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

Last change on this file since 374 was 165, checked in by Paul Smedley, 16 years ago

Add 'missing' 3.0.34 diffs

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