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