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