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