s3: smbd: Remove now unused dfs_filename_convert().
[samba.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 "../auth/auth_util.h"
32 #include "lib/param/loadparm.h"
33 #include "libcli/security/security.h"
34 #include "librpc/gen_ndr/ndr_dfsblobs.h"
35 #include "lib/tsocket/tsocket.h"
36 #include "lib/global_contexts.h"
37 #include "source3/lib/substitute.h"
38
39 /**********************************************************************
40  Parse a DFS pathname of the form(s)
41
42  \hostname\service                      - self referral
43  \hostname\service\remainingpath        - Windows referral path
44
45  FIXME! Should we also parse:
46  \hostname\service/remainingpath        - POSIX referral path
47  as currently nothing uses this ?
48
49  into the dfs_path components. Strict form.
50
51  Checks DFS path starts with separator.
52  Checks hostname is ours.
53  Ensures servicename (share) is sent, and
54      if so, terminates the name or is followed by
55      \pathname.
56
57  If returned, remainingpath is untouched. Caller must call
58  check_path_syntaxXXX() on it.
59
60  Called by all non-fileserver processing (DFS RPC, FSCTL_DFS_GET_REFERRALS)
61  etc. Errors out on any inconsistency in the path.
62 **********************************************************************/
63
64 static NTSTATUS parse_dfs_path_strict(TALLOC_CTX *ctx,
65                                 const char *pathname,
66                                 char **_hostname,
67                                 char **_servicename,
68                                 char **_remaining_path)
69 {
70         char *pathname_local = NULL;
71         char *p = NULL;
72         const char *hostname = NULL;
73         const char *servicename = NULL;
74         const char *reqpath = NULL;
75         bool my_hostname = false;
76         NTSTATUS status;
77
78         DBG_DEBUG("path = |%s|\n", pathname);
79
80         pathname_local = talloc_strdup(talloc_tos(), pathname);
81         if (pathname_local == NULL) {
82                 return NT_STATUS_NO_MEMORY;
83         }
84         /*
85          * parse_dfs_path_strict() is called from
86          * get_referred_path() and create_junction()
87          * which use Windows DFS paths of \server\share.
88          */
89
90         /*
91          * Strict DFS paths *must* start with the
92          * path separator '\\'.
93          */
94
95         if (pathname_local[0] != '\\') {
96                 DBG_ERR("path %s doesn't start with \\\n",
97                         pathname_local);
98                 status = NT_STATUS_NOT_FOUND;
99                 goto out;
100         }
101
102         /* Now tokenize. */
103         /* Parse out hostname. */
104         p = strchr(pathname_local + 1, '\\');
105         if (p == NULL) {
106                 DBG_ERR("can't parse hostname from path %s\n",
107                         pathname_local);
108                 status = NT_STATUS_NOT_FOUND;
109                 goto out;
110         }
111         *p = '\0';
112         hostname = &pathname_local[1];
113
114         DBG_DEBUG("hostname: %s\n", hostname);
115
116         /* Is this really our hostname ? */
117         my_hostname = is_myname_or_ipaddr(hostname);
118         if (!my_hostname) {
119                 DBG_ERR("Hostname %s is not ours.\n",
120                         hostname);
121                 status = NT_STATUS_NOT_FOUND;
122                 goto out;
123         }
124
125         servicename = p + 1;
126
127         /*
128          * Find the end of servicename by looking for
129          * a directory separator character. The character
130          * should be '\\' for a Windows path.
131          * If there is no separator, then this is a self-referral
132          * of "\server\share".
133          */
134
135         p = strchr(servicename, '\\');
136         if (p != NULL) {
137                 *p = '\0';
138         }
139
140         DBG_DEBUG("servicename: %s\n", servicename);
141
142         if (p == NULL) {
143                 /* Client sent self referral "\server\share". */
144                 reqpath = "";
145         } else {
146                 /* Step past the '\0' we just replaced '\\' with. */
147                 reqpath = p + 1;
148         }
149
150         DBG_DEBUG("rest of the path: %s\n", reqpath);
151
152         if (_hostname != NULL) {
153                 *_hostname = talloc_strdup(ctx, hostname);
154                 if (*_hostname == NULL) {
155                         status = NT_STATUS_NO_MEMORY;
156                         goto out;
157                 }
158         }
159         if (_servicename != NULL) {
160                 *_servicename = talloc_strdup(ctx, servicename);
161                 if (*_servicename == NULL) {
162                         status = NT_STATUS_NO_MEMORY;
163                         goto out;
164                 }
165         }
166         if (_remaining_path != NULL) {
167                 *_remaining_path = talloc_strdup(ctx, reqpath);
168                 if (*_remaining_path == NULL) {
169                         status = NT_STATUS_NO_MEMORY;
170                         goto out;
171                 }
172         }
173
174         status = NT_STATUS_OK;
175 out:
176         TALLOC_FREE(pathname_local);
177         return status;
178 }
179
180 /********************************************************
181  Fake up a connection struct for the VFS layer, for use in
182  applications (such as the python bindings), that do not want the
183  global working directory changed under them.
184
185  SMB_VFS_CONNECT requires root privileges.
186 *********************************************************/
187
188 static NTSTATUS create_conn_struct_as_root(TALLOC_CTX *ctx,
189                             struct tevent_context *ev,
190                             struct messaging_context *msg,
191                             connection_struct **pconn,
192                             int snum,
193                             const char *path,
194                             const struct auth_session_info *session_info)
195 {
196         connection_struct *conn;
197         char *connpath;
198         const char *vfs_user;
199         struct smbd_server_connection *sconn;
200         const char *servicename = lp_const_servicename(snum);
201         bool ok;
202
203         sconn = talloc_zero(ctx, struct smbd_server_connection);
204         if (sconn == NULL) {
205                 return NT_STATUS_NO_MEMORY;
206         }
207
208         sconn->ev_ctx = ev;
209         sconn->msg_ctx = msg;
210
211         conn = conn_new(sconn);
212         if (conn == NULL) {
213                 TALLOC_FREE(sconn);
214                 return NT_STATUS_NO_MEMORY;
215         }
216
217         /* Now we have conn, we need to make sconn a child of conn,
218          * for a proper talloc tree */
219         talloc_steal(conn, sconn);
220
221         if (snum == -1 && servicename == NULL) {
222                 servicename = "Unknown Service (snum == -1)";
223         }
224
225         connpath = talloc_strdup(conn, path);
226         if (!connpath) {
227                 TALLOC_FREE(conn);
228                 return NT_STATUS_NO_MEMORY;
229         }
230         connpath = talloc_string_sub(conn,
231                                      connpath,
232                                      "%S",
233                                      servicename);
234         if (!connpath) {
235                 TALLOC_FREE(conn);
236                 return NT_STATUS_NO_MEMORY;
237         }
238
239         /* needed for smbd_vfs_init() */
240
241         conn->params->service = snum;
242         conn->cnum = TID_FIELD_INVALID;
243
244         SMB_ASSERT(session_info != NULL);
245
246         conn->session_info = copy_session_info(conn, session_info);
247         if (conn->session_info == NULL) {
248                 DBG_ERR("copy_serverinfo failed\n");
249                 TALLOC_FREE(conn);
250                 return NT_STATUS_NO_MEMORY;
251         }
252
253         /* unix_info could be NULL in session_info */
254         if (conn->session_info->unix_info != NULL) {
255                 vfs_user = conn->session_info->unix_info->unix_name;
256         } else {
257                 vfs_user = get_current_username();
258         }
259
260         conn_setup_case_options(conn);
261
262         set_conn_connectpath(conn, connpath);
263
264         /*
265          * New code to check if there's a share security descriptor
266          * added from NT server manager. This is done after the
267          * smb.conf checks are done as we need a uid and token. JRA.
268          *
269          */
270         share_access_check(conn->session_info->security_token,
271                            servicename,
272                            MAXIMUM_ALLOWED_ACCESS,
273                            &conn->share_access);
274
275         if ((conn->share_access & FILE_WRITE_DATA) == 0) {
276                 if ((conn->share_access & FILE_READ_DATA) == 0) {
277                         /* No access, read or write. */
278                         DBG_WARNING("connection to %s "
279                                     "denied due to security "
280                                     "descriptor.\n",
281                                     servicename);
282                         conn_free(conn);
283                         return NT_STATUS_ACCESS_DENIED;
284                 }
285                 conn->read_only = true;
286         }
287
288         if (!smbd_vfs_init(conn)) {
289                 NTSTATUS status = map_nt_error_from_unix(errno);
290                 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
291                 conn_free(conn);
292                 return status;
293         }
294
295         /* this must be the first filesystem operation that we do */
296         if (SMB_VFS_CONNECT(conn, servicename, vfs_user) < 0) {
297                 DEBUG(0,("VFS connect failed!\n"));
298                 conn_free(conn);
299                 return NT_STATUS_UNSUCCESSFUL;
300         }
301
302         ok = canonicalize_connect_path(conn);
303         if (!ok) {
304                 DBG_ERR("Failed to canonicalize sharepath\n");
305                 conn_free(conn);
306                 return NT_STATUS_ACCESS_DENIED;
307         }
308
309         conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
310         conn->tcon_done = true;
311         *pconn = talloc_move(ctx, &conn);
312
313         return NT_STATUS_OK;
314 }
315
316 static int conn_struct_tos_destructor(struct conn_struct_tos *c)
317 {
318         if (c->oldcwd_fname != NULL) {
319                 vfs_ChDir(c->conn, c->oldcwd_fname);
320                 TALLOC_FREE(c->oldcwd_fname);
321         }
322         SMB_VFS_DISCONNECT(c->conn);
323         conn_free(c->conn);
324         return 0;
325 }
326
327 /********************************************************
328  Fake up a connection struct for the VFS layer, for use in
329  applications (such as the python bindings), that do not want the
330  global working directory changed under them.
331
332  SMB_VFS_CONNECT requires root privileges.
333  This temporary uses become_root() and unbecome_root().
334
335  But further impersonation has to be cone by the caller.
336 *********************************************************/
337 NTSTATUS create_conn_struct_tos(struct messaging_context *msg,
338                                 int snum,
339                                 const char *path,
340                                 const struct auth_session_info *session_info,
341                                 struct conn_struct_tos **_c)
342 {
343         struct conn_struct_tos *c = NULL;
344         struct tevent_context *ev = NULL;
345         NTSTATUS status;
346
347         *_c = NULL;
348
349         c = talloc_zero(talloc_tos(), struct conn_struct_tos);
350         if (c == NULL) {
351                 return NT_STATUS_NO_MEMORY;
352         }
353
354         ev = samba_tevent_context_init(c);
355         if (ev == NULL) {
356                 TALLOC_FREE(c);
357                 return NT_STATUS_NO_MEMORY;
358         }
359
360         become_root();
361         status = create_conn_struct_as_root(c,
362                                             ev,
363                                             msg,
364                                             &c->conn,
365                                             snum,
366                                             path,
367                                             session_info);
368         unbecome_root();
369         if (!NT_STATUS_IS_OK(status)) {
370                 TALLOC_FREE(c);
371                 return status;
372         }
373
374         talloc_set_destructor(c, conn_struct_tos_destructor);
375
376         *_c = c;
377         return NT_STATUS_OK;
378 }
379
380 /********************************************************
381  Fake up a connection struct for the VFS layer.
382  Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
383
384  See also the comment for create_conn_struct_tos() above!
385
386  The CWD change is reverted by the destructor of
387  conn_struct_tos when the current talloc_tos() is destroyed.
388 *********************************************************/
389 NTSTATUS create_conn_struct_tos_cwd(struct messaging_context *msg,
390                                     int snum,
391                                     const char *path,
392                                     const struct auth_session_info *session_info,
393                                     struct conn_struct_tos **_c)
394 {
395         struct conn_struct_tos *c = NULL;
396         struct smb_filename smb_fname_connectpath = {0};
397         NTSTATUS status;
398
399         *_c = NULL;
400
401         status = create_conn_struct_tos(msg,
402                                         snum,
403                                         path,
404                                         session_info,
405                                         &c);
406         if (!NT_STATUS_IS_OK(status)) {
407                 return status;
408         }
409
410         /*
411          * Windows seems to insist on doing trans2getdfsreferral() calls on
412          * the IPC$ share as the anonymous user. If we try to chdir as that
413          * user we will fail.... WTF ? JRA.
414          */
415
416         c->oldcwd_fname = vfs_GetWd(c, c->conn);
417         if (c->oldcwd_fname == NULL) {
418                 status = map_nt_error_from_unix(errno);
419                 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
420                 TALLOC_FREE(c);
421                 return status;
422         }
423
424         smb_fname_connectpath = (struct smb_filename) {
425                 .base_name = c->conn->connectpath
426         };
427
428         if (vfs_ChDir(c->conn, &smb_fname_connectpath) != 0) {
429                 status = map_nt_error_from_unix(errno);
430                 DBG_NOTICE("Can't ChDir to new conn path %s. "
431                            "Error was %s\n",
432                            c->conn->connectpath, strerror(errno));
433                 TALLOC_FREE(c->oldcwd_fname);
434                 TALLOC_FREE(c);
435                 return status;
436         }
437
438         *_c = c;
439         return NT_STATUS_OK;
440 }
441
442 /********************************************************
443  Fake up a connection struct for the VFS layer.
444  This takes an TALLOC_CTX and tevent_context from the
445  caller and the resulting connection_struct is stable
446  across the lifetime of mem_ctx and ev.
447
448  Note: this performs a vfs connect and changes cwd.
449
450  See also the comment for create_conn_struct_tos() above!
451 *********************************************************/
452
453 NTSTATUS create_conn_struct_cwd(TALLOC_CTX *mem_ctx,
454                                 struct tevent_context *ev,
455                                 struct messaging_context *msg,
456                                 const struct auth_session_info *session_info,
457                                 int snum,
458                                 const char *path,
459                                 struct connection_struct **c)
460 {
461         NTSTATUS status;
462
463         become_root();
464         status = create_conn_struct_as_root(mem_ctx,
465                                             ev,
466                                             msg,
467                                             c,
468                                             snum,
469                                             path,
470                                             session_info);
471         unbecome_root();
472         return status;
473 }
474
475 static void shuffle_strlist(char **list, int count)
476 {
477         int i;
478         uint32_t r;
479         char *tmp;
480
481         for (i = count; i > 1; i--) {
482                 r = generate_random() % i;
483
484                 tmp = list[i-1];
485                 list[i-1] = list[r];
486                 list[r] = tmp;
487         }
488 }
489
490 /**********************************************************************
491  Parse the contents of a symlink to verify if it is an msdfs referral
492  A valid referral is of the form:
493
494  msdfs:server1\share1,server2\share2
495  msdfs:server1\share1\pathname,server2\share2\pathname
496  msdfs:server1/share1,server2/share2
497  msdfs:server1/share1/pathname,server2/share2/pathname.
498
499  Note that the alternate paths returned here must be of the canonicalized
500  form:
501
502  \server\share or
503  \server\share\path\to\file,
504
505  even in posix path mode. This is because we have no knowledge if the
506  server we're referring to understands posix paths.
507  **********************************************************************/
508
509 bool parse_msdfs_symlink(TALLOC_CTX *ctx,
510                         bool shuffle_referrals,
511                         const char *target,
512                         struct referral **ppreflist,
513                         size_t *prefcount)
514 {
515         char *temp = NULL;
516         char *prot;
517         char **alt_path = NULL;
518         size_t count = 0, i;
519         struct referral *reflist = NULL;
520         char *saveptr;
521
522         temp = talloc_strdup(ctx, target);
523         if (!temp) {
524                 return false;
525         }
526         prot = strtok_r(temp, ":", &saveptr);
527         if (!prot) {
528                 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
529                 TALLOC_FREE(temp);
530                 return false;
531         }
532
533         alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
534         if (!alt_path) {
535                 TALLOC_FREE(temp);
536                 return false;
537         }
538
539         /* parse out the alternate paths */
540         while((count<MAX_REFERRAL_COUNT) &&
541               ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
542                 count++;
543         }
544
545         /* shuffle alternate paths */
546         if (shuffle_referrals) {
547                 shuffle_strlist(alt_path, count);
548         }
549
550         DBG_DEBUG("count=%zu\n", count);
551
552         if (count) {
553                 reflist = talloc_zero_array(ctx,
554                                 struct referral, count);
555                 if(reflist == NULL) {
556                         TALLOC_FREE(temp);
557                         TALLOC_FREE(alt_path);
558                         return false;
559                 }
560         } else {
561                 reflist = NULL;
562         }
563
564         for(i=0;i<count;i++) {
565                 char *p;
566
567                 /* Canonicalize link target.
568                  * Replace all /'s in the path by a \ */
569                 string_replace(alt_path[i], '/', '\\');
570
571                 /* Remove leading '\\'s */
572                 p = alt_path[i];
573                 while (*p && (*p == '\\')) {
574                         p++;
575                 }
576
577                 reflist[i].alternate_path = talloc_asprintf(reflist,
578                                 "\\%s",
579                                 p);
580                 if (!reflist[i].alternate_path) {
581                         TALLOC_FREE(temp);
582                         TALLOC_FREE(alt_path);
583                         TALLOC_FREE(reflist);
584                         return false;
585                 }
586
587                 reflist[i].proximity = 0;
588                 reflist[i].ttl = REFERRAL_TTL;
589                 DBG_DEBUG("Created alt path: %s\n",
590                         reflist[i].alternate_path);
591         }
592
593         if (ppreflist != NULL) {
594                 *ppreflist = reflist;
595         } else {
596                 TALLOC_FREE(reflist);
597         }
598         if (prefcount != NULL) {
599                 *prefcount = count;
600         }
601         TALLOC_FREE(temp);
602         TALLOC_FREE(alt_path);
603         return true;
604 }
605
606 /**********************************************************************
607  Returns true if the unix path is a valid msdfs symlink.
608 **********************************************************************/
609
610 bool is_msdfs_link(struct files_struct *dirfsp,
611                    struct smb_filename *atname)
612 {
613         NTSTATUS status = SMB_VFS_READ_DFS_PATHAT(dirfsp->conn,
614                                         talloc_tos(),
615                                         dirfsp,
616                                         atname,
617                                         NULL,
618                                         NULL);
619         return (NT_STATUS_IS_OK(status));
620 }
621
622 /*****************************************************************
623  Used by other functions to decide if a dfs path is remote,
624  and to get the list of referred locations for that remote path.
625
626  consumedcntp: how much of the dfs path is being redirected. the client
627  should try the remaining path on the redirected server.
628 *****************************************************************/
629
630 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
631                 connection_struct *conn,
632                 const char *dfspath, /* Incoming complete dfs path */
633                 const char *reqpath, /* Parsed out remaining path. */
634                 uint32_t ucf_flags,
635                 size_t *consumedcntp,
636                 struct referral **ppreflist,
637                 size_t *preferral_count)
638 {
639         NTSTATUS status;
640         struct smb_filename *parent_smb_fname = NULL;
641         struct smb_filename *smb_fname_rel = NULL;
642         NTTIME twrp = 0;
643         char *local_pathname = NULL;
644         char *last_component = NULL;
645         char *atname = NULL;
646         size_t removed_components = 0;
647         bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
648         char *p = NULL;
649         char *canon_dfspath = NULL;
650
651         DBG_DEBUG("Conn path = %s reqpath = %s\n", conn->connectpath, reqpath);
652
653         local_pathname = talloc_strdup(ctx, reqpath);
654         if (local_pathname == NULL) {
655                 status = NT_STATUS_NO_MEMORY;
656                 goto out;
657         }
658
659         /* We know reqpath isn't a DFS path. */
660         ucf_flags &= ~UCF_DFS_PATHNAME;
661
662         if (ucf_flags & UCF_GMT_PATHNAME) {
663                 extract_snapshot_token(local_pathname, &twrp);
664                 ucf_flags &= ~UCF_GMT_PATHNAME;
665         }
666
667         /*
668          * We should have been given a DFS path to resolve.
669          * This should return NT_STATUS_PATH_NOT_COVERED.
670          *
671          * Do a pathname walk, stripping off components
672          * until we get NT_STATUS_OK instead of
673          * NT_STATUS_PATH_NOT_COVERED.
674          *
675          * Fail on any other error.
676          */
677
678         for (;;) {
679                 TALLOC_CTX *frame = NULL;
680                 struct files_struct *dirfsp = NULL;
681                 struct smb_filename *smb_fname_walk = NULL;
682
683                 TALLOC_FREE(parent_smb_fname);
684
685                 /*
686                  * Use a local stackframe as filename_convert_dirfsp()
687                  * opens handles on the last two components in the path.
688                  * Allow these to be freed as we step back through
689                  * the local_pathname.
690                  */
691                 frame = talloc_stackframe();
692                 status = filename_convert_dirfsp(frame,
693                                                  conn,
694                                                  local_pathname,
695                                                  ucf_flags,
696                                                  twrp,
697                                                  &dirfsp,
698                                                  &smb_fname_walk);
699                 /* If we got a name, save it. */
700                 if (smb_fname_walk != NULL) {
701                         parent_smb_fname = talloc_move(ctx, &smb_fname_walk);
702                 }
703                 TALLOC_FREE(frame);
704
705                 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
706                         /*
707                          * For any other status than NT_STATUS_PATH_NOT_COVERED
708                          * (including NT_STATUS_OK) we exit the walk.
709                          * If it's an error we catch it outside the loop.
710                          */
711                         break;
712                 }
713
714                 /* Step back one component and save it off as last_component. */
715                 TALLOC_FREE(last_component);
716                 p = strrchr(local_pathname, '/');
717                 if (p == NULL) {
718                         /*
719                          * We removed all components.
720                          * Go around once more to make
721                          * sure we can open the root '\0'.
722                          */
723                         last_component = talloc_strdup(ctx, local_pathname);
724                         *local_pathname = '\0';
725                 } else {
726                         last_component = talloc_strdup(ctx, p+1);
727                         *p = '\0';
728                 }
729                 if (last_component == NULL) {
730                         status = NT_STATUS_NO_MEMORY;
731                         goto out;
732                 }
733                 /* Integer wrap check. */
734                 if (removed_components + 1 < removed_components) {
735                         status = NT_STATUS_INVALID_PARAMETER;
736                         goto out;
737                 }
738                 removed_components++;
739         }
740
741         if (!NT_STATUS_IS_OK(status)) {
742                 DBG_DEBUG("dfspath = %s. reqpath = %s. Error %s.\n",
743                         dfspath,
744                         reqpath,
745                         nt_errstr(status));
746                 goto out;
747         }
748
749         if (parent_smb_fname->fsp == NULL) {
750                 /* Unable to open parent. */
751                 DBG_DEBUG("dfspath = %s. reqpath = %s. "
752                           "Unable to open parent directory (%s).\n",
753                         dfspath,
754                         reqpath,
755                         smb_fname_str_dbg(parent_smb_fname));
756                 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
757                 goto out;
758         }
759
760         if (removed_components == 0) {
761                 /*
762                  * We never got NT_STATUS_PATH_NOT_COVERED.
763                  * There was no DFS redirect.
764                  */
765                 DBG_DEBUG("dfspath = %s. reqpath = %s. "
766                         "No removed components.\n",
767                         dfspath,
768                         reqpath);
769                 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
770                 goto out;
771         }
772
773         /*
774          * One of the removed_components was the MSDFS link
775          * at the end. We need to count this in the resolved
776          * path below, so remove one from removed_components.
777          */
778         removed_components--;
779
780         /*
781          * Now parent_smb_fname->fsp is the parent directory dirfsp,
782          * last_component is the untranslated MS-DFS link name.
783          * Search for it in the parent directory to get the real
784          * filename on disk.
785          */
786         status = get_real_filename_at(parent_smb_fname->fsp,
787                                       last_component,
788                                       ctx,
789                                       &atname);
790
791         if (!NT_STATUS_IS_OK(status)) {
792                 DBG_DEBUG("dfspath = %s. reqpath = %s "
793                         "get_real_filename_at(%s, %s) error (%s)\n",
794                         dfspath,
795                         reqpath,
796                         smb_fname_str_dbg(parent_smb_fname),
797                         last_component,
798                         nt_errstr(status));
799                 goto out;
800         }
801
802         smb_fname_rel = synthetic_smb_fname(ctx,
803                                 atname,
804                                 NULL,
805                                 NULL,
806                                 twrp,
807                                 posix ? SMB_FILENAME_POSIX_PATH : 0);
808         if (smb_fname_rel == NULL) {
809                 status = NT_STATUS_NO_MEMORY;
810                 goto out;
811         }
812
813         /* Get the referral to return. */
814         status = SMB_VFS_READ_DFS_PATHAT(conn,
815                                          ctx,
816                                          parent_smb_fname->fsp,
817                                          smb_fname_rel,
818                                          ppreflist,
819                                          preferral_count);
820         if (!NT_STATUS_IS_OK(status)) {
821                 DBG_DEBUG("dfspath = %s. reqpath = %s. "
822                         "SMB_VFS_READ_DFS_PATHAT(%s, %s) error (%s)\n",
823                         dfspath,
824                         reqpath,
825                         smb_fname_str_dbg(parent_smb_fname),
826                         smb_fname_str_dbg(smb_fname_rel),
827                         nt_errstr(status));
828                 goto out;
829         }
830
831         /*
832          * Now we must work out how much of the
833          * given pathname we consumed.
834          */
835         canon_dfspath = talloc_strdup(ctx, dfspath);
836         if (!canon_dfspath) {
837                 status = NT_STATUS_NO_MEMORY;
838                 goto out;
839         }
840         /* Canonicalize the raw dfspath. */
841         string_replace(canon_dfspath, '\\', '/');
842
843         /*
844          * reqpath comes out of parse_dfs_path(), so it has
845          * no trailing backslash. Make sure that canon_dfspath hasn't either.
846          */
847         trim_char(canon_dfspath, 0, '/');
848
849         DBG_DEBUG("Unconsumed path: %s\n", canon_dfspath);
850
851         while (removed_components > 0) {
852                 p = strrchr(canon_dfspath, '/');
853                 if (p != NULL) {
854                         *p = '\0';
855                 }
856                 removed_components--;
857                 if (p == NULL && removed_components != 0) {
858                         DBG_ERR("Component mismatch. path = %s, "
859                                 "%zu components left\n",
860                                 canon_dfspath,
861                                 removed_components);
862                         status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
863                         goto out;
864                 }
865         }
866         *consumedcntp = strlen(canon_dfspath);
867         DBG_DEBUG("Path consumed: %s (%zu)\n", canon_dfspath, *consumedcntp);
868         status = NT_STATUS_OK;
869
870   out:
871
872         TALLOC_FREE(parent_smb_fname);
873         TALLOC_FREE(local_pathname);
874         TALLOC_FREE(last_component);
875         TALLOC_FREE(atname);
876         TALLOC_FREE(smb_fname_rel);
877         TALLOC_FREE(canon_dfspath);
878         return status;
879 }
880
881 /**********************************************************************
882  Return a self referral.
883 **********************************************************************/
884
885 static NTSTATUS self_ref(TALLOC_CTX *ctx,
886                         const char *dfs_path,
887                         struct junction_map *jucn,
888                         size_t *consumedcntp,
889                         bool *self_referralp)
890 {
891         struct referral *ref;
892
893         *self_referralp = True;
894
895         jucn->referral_count = 1;
896         if((ref = talloc_zero(ctx, struct referral)) == NULL) {
897                 return NT_STATUS_NO_MEMORY;
898         }
899
900         ref->alternate_path = talloc_strdup(ctx, dfs_path);
901         if (!ref->alternate_path) {
902                 TALLOC_FREE(ref);
903                 return NT_STATUS_NO_MEMORY;
904         }
905         ref->proximity = 0;
906         ref->ttl = REFERRAL_TTL;
907         jucn->referral_list = ref;
908         *consumedcntp = strlen(dfs_path);
909         return NT_STATUS_OK;
910 }
911
912 /**********************************************************************
913  Gets valid referrals for a dfs path and fills up the
914  junction_map structure.
915 **********************************************************************/
916
917 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
918                            struct auth_session_info *session_info,
919                            const char *dfs_path,
920                            const struct tsocket_address *remote_address,
921                            const struct tsocket_address *local_address,
922                            struct junction_map *jucn,
923                            size_t *consumedcntp,
924                            bool *self_referralp)
925 {
926         TALLOC_CTX *frame = talloc_stackframe();
927         const struct loadparm_substitution *lp_sub =
928                 loadparm_s3_global_substitution();
929         struct conn_struct_tos *c = NULL;
930         struct connection_struct *conn = NULL;
931         char *servicename = NULL;
932         char *reqpath = NULL;
933         int snum;
934         NTSTATUS status = NT_STATUS_NOT_FOUND;
935
936         *self_referralp = False;
937
938         status = parse_dfs_path_strict(
939                                 frame,
940                                 dfs_path,
941                                 NULL, /* hostname */
942                                 &servicename,
943                                 &reqpath);
944         if (!NT_STATUS_IS_OK(status)) {
945                 TALLOC_FREE(frame);
946                 return status;
947         }
948
949         /* Path referrals are always non-POSIX. */
950         status = check_path_syntax(reqpath);
951         if (!NT_STATUS_IS_OK(status)) {
952                 TALLOC_FREE(frame);
953                 return status;
954         }
955
956         jucn->service_name = talloc_strdup(ctx, servicename);
957         jucn->volume_name = talloc_strdup(ctx, reqpath);
958         if (!jucn->service_name || !jucn->volume_name) {
959                 TALLOC_FREE(frame);
960                 return NT_STATUS_NO_MEMORY;
961         }
962
963         /* Verify the share is a dfs root */
964         snum = lp_servicenumber(jucn->service_name);
965         if(snum < 0) {
966                 char *service_name = NULL;
967                 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
968                         TALLOC_FREE(frame);
969                         return NT_STATUS_NOT_FOUND;
970                 }
971                 if (!service_name) {
972                         TALLOC_FREE(frame);
973                         return NT_STATUS_NO_MEMORY;
974                 }
975                 TALLOC_FREE(jucn->service_name);
976                 jucn->service_name = talloc_strdup(ctx, service_name);
977                 if (!jucn->service_name) {
978                         TALLOC_FREE(frame);
979                         return NT_STATUS_NO_MEMORY;
980                 }
981         }
982
983         if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(talloc_tos(), lp_sub, snum) == '\0')) {
984                 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
985                         "a dfs root.\n",
986                         servicename, dfs_path));
987                 TALLOC_FREE(frame);
988                 return NT_STATUS_NOT_FOUND;
989         }
990
991         /*
992          * Self referrals are tested with a anonymous IPC connection and
993          * a GET_DFS_REFERRAL call to \\server\share. (which means
994          * dp.reqpath[0] points to an empty string). create_conn_struct cd's
995          * into the directory and will fail if it cannot (as the anonymous
996          * user). Cope with this.
997          */
998
999         if (reqpath[0] == '\0') {
1000                 char *tmp;
1001                 struct referral *ref;
1002                 size_t refcount;
1003
1004                 if (*lp_msdfs_proxy(talloc_tos(), lp_sub, snum) == '\0') {
1005                         TALLOC_FREE(frame);
1006                         return self_ref(ctx,
1007                                         dfs_path,
1008                                         jucn,
1009                                         consumedcntp,
1010                                         self_referralp);
1011                 }
1012
1013                 /*
1014                  * It's an msdfs proxy share. Redirect to
1015                  * the configured target share.
1016                  */
1017
1018                 tmp = talloc_asprintf(frame, "msdfs:%s",
1019                                       lp_msdfs_proxy(frame, lp_sub, snum));
1020                 if (tmp == NULL) {
1021                         TALLOC_FREE(frame);
1022                         return NT_STATUS_NO_MEMORY;
1023                 }
1024
1025                 if (!parse_msdfs_symlink(ctx,
1026                                 lp_msdfs_shuffle_referrals(snum),
1027                                 tmp,
1028                                 &ref,
1029                                 &refcount)) {
1030                         TALLOC_FREE(frame);
1031                         return NT_STATUS_INVALID_PARAMETER;
1032                 }
1033                 jucn->referral_count = refcount;
1034                 jucn->referral_list = ref;
1035                 *consumedcntp = strlen(dfs_path);
1036                 TALLOC_FREE(frame);
1037                 return NT_STATUS_OK;
1038         }
1039
1040         status = create_conn_struct_tos_cwd(global_messaging_context(),
1041                                             snum,
1042                                             lp_path(frame, lp_sub, snum),
1043                                             session_info,
1044                                             &c);
1045         if (!NT_STATUS_IS_OK(status)) {
1046                 TALLOC_FREE(frame);
1047                 return status;
1048         }
1049         conn = c->conn;
1050
1051         /*
1052          * TODO
1053          *
1054          * The remote and local address should be passed down to
1055          * create_conn_struct_cwd.
1056          */
1057         if (conn->sconn->remote_address == NULL) {
1058                 conn->sconn->remote_address =
1059                         tsocket_address_copy(remote_address, conn->sconn);
1060                 if (conn->sconn->remote_address == NULL) {
1061                         TALLOC_FREE(frame);
1062                         return NT_STATUS_NO_MEMORY;
1063                 }
1064         }
1065         if (conn->sconn->local_address == NULL) {
1066                 conn->sconn->local_address =
1067                         tsocket_address_copy(local_address, conn->sconn);
1068                 if (conn->sconn->local_address == NULL) {
1069                         TALLOC_FREE(frame);
1070                         return NT_STATUS_NO_MEMORY;
1071                 }
1072         }
1073
1074         status = dfs_path_lookup(ctx,
1075                                 conn,
1076                                 dfs_path,
1077                                 reqpath,
1078                                 0, /* ucf_flags */
1079                                 consumedcntp,
1080                                 &jucn->referral_list,
1081                                 &jucn->referral_count);
1082
1083         if (!NT_STATUS_IS_OK(status)) {
1084                 DBG_NOTICE("No valid referrals for path %s (%s)\n",
1085                         dfs_path,
1086                         nt_errstr(status));
1087         }
1088
1089         TALLOC_FREE(frame);
1090         return status;
1091 }
1092
1093 /******************************************************************
1094  Set up the DFS referral for the dfs pathname. This call returns
1095  the amount of the path covered by this server, and where the
1096  client should be redirected to. This is the meat of the
1097  TRANS2_GET_DFS_REFERRAL call.
1098 ******************************************************************/
1099
1100 int setup_dfs_referral(connection_struct *orig_conn,
1101                         const char *dfs_path,
1102                         int max_referral_level,
1103                         char **ppdata, NTSTATUS *pstatus)
1104 {
1105         char *pdata = *ppdata;
1106         int reply_size = 0;
1107         struct dfs_GetDFSReferral *r;
1108         DATA_BLOB blob = data_blob_null;
1109         NTSTATUS status;
1110         enum ndr_err_code ndr_err;
1111
1112         r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1113         if (r == NULL) {
1114                 *pstatus = NT_STATUS_NO_MEMORY;
1115                 return -1;
1116         }
1117
1118         r->in.req.max_referral_level = max_referral_level;
1119         r->in.req.servername = talloc_strdup(r, dfs_path);
1120         if (r->in.req.servername == NULL) {
1121                 talloc_free(r);
1122                 *pstatus = NT_STATUS_NO_MEMORY;
1123                 return -1;
1124         }
1125
1126         status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1127         if (!NT_STATUS_IS_OK(status)) {
1128                 talloc_free(r);
1129                 *pstatus = status;
1130                 return -1;
1131         }
1132
1133         ndr_err = ndr_push_struct_blob(&blob, r,
1134                                 r->out.resp,
1135                                 (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1136         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1137                 TALLOC_FREE(r);
1138                 *pstatus = NT_STATUS_INVALID_PARAMETER;
1139                 return -1;
1140         }
1141
1142         pdata = (char *)SMB_REALLOC(pdata, blob.length);
1143         if(pdata == NULL) {
1144                 TALLOC_FREE(r);
1145                 DEBUG(0,("referral setup:"
1146                          "malloc failed for Realloc!\n"));
1147                 return -1;
1148         }
1149         *ppdata = pdata;
1150         reply_size = blob.length;
1151         memcpy(pdata, blob.data, blob.length);
1152         TALLOC_FREE(r);
1153
1154         *pstatus = NT_STATUS_OK;
1155         return reply_size;
1156 }
1157
1158 /**********************************************************************
1159  The following functions are called by the NETDFS RPC pipe functions
1160  **********************************************************************/
1161
1162 /*********************************************************************
1163  Creates a junction structure from a DFS pathname
1164 **********************************************************************/
1165
1166 bool create_junction(TALLOC_CTX *ctx,
1167                 const char *dfs_path,
1168                 struct junction_map *jucn)
1169 {
1170         const struct loadparm_substitution *lp_sub =
1171                 loadparm_s3_global_substitution();
1172         int snum;
1173         char *servicename = NULL;
1174         char *reqpath = NULL;
1175         NTSTATUS status;
1176
1177         status = parse_dfs_path_strict(
1178                                 ctx,
1179                                 dfs_path,
1180                                 NULL,
1181                                 &servicename,
1182                                 &reqpath);
1183         if (!NT_STATUS_IS_OK(status)) {
1184                 return False;
1185         }
1186
1187         /* Check for a non-DFS share */
1188         snum = lp_servicenumber(servicename);
1189
1190         if(snum < 0 || !lp_msdfs_root(snum)) {
1191                 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1192                         servicename));
1193                 return False;
1194         }
1195
1196         /* Junction create paths are always non-POSIX. */
1197         status = check_path_syntax(reqpath);
1198         if (!NT_STATUS_IS_OK(status)) {
1199                 return false;
1200         }
1201
1202         jucn->service_name = talloc_strdup(ctx, servicename);
1203         jucn->volume_name = talloc_strdup(ctx, reqpath);
1204         jucn->comment = lp_comment(ctx, lp_sub, snum);
1205
1206         if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1207                 return False;
1208         }
1209         return True;
1210 }
1211
1212 /**********************************************************************
1213  Forms a valid Unix pathname from the junction
1214  **********************************************************************/
1215
1216 static bool junction_to_local_path_tos(const struct junction_map *jucn,
1217                                        struct auth_session_info *session_info,
1218                                        char **pp_path_out,
1219                                        connection_struct **conn_out)
1220 {
1221         const struct loadparm_substitution *lp_sub =
1222                 loadparm_s3_global_substitution();
1223         struct conn_struct_tos *c = NULL;
1224         int snum;
1225         char *path_out = NULL;
1226         NTSTATUS status;
1227
1228         snum = lp_servicenumber(jucn->service_name);
1229         if(snum < 0) {
1230                 return False;
1231         }
1232         status = create_conn_struct_tos_cwd(global_messaging_context(),
1233                                             snum,
1234                                             lp_path(talloc_tos(), lp_sub, snum),
1235                                             session_info,
1236                                             &c);
1237         if (!NT_STATUS_IS_OK(status)) {
1238                 return False;
1239         }
1240
1241         path_out = talloc_asprintf(c,
1242                         "%s/%s",
1243                         lp_path(talloc_tos(), lp_sub, snum),
1244                         jucn->volume_name);
1245         if (path_out == NULL) {
1246                 TALLOC_FREE(c);
1247                 return False;
1248         }
1249         *pp_path_out = path_out;
1250         *conn_out = c->conn;
1251         return True;
1252 }
1253
1254 /*
1255  * Create a msdfs string in Samba format we can store
1256  * in a filesystem object (currently a symlink).
1257  */
1258
1259 char *msdfs_link_string(TALLOC_CTX *ctx,
1260                         const struct referral *reflist,
1261                         size_t referral_count)
1262 {
1263         char *refpath = NULL;
1264         bool insert_comma = false;
1265         char *msdfs_link = NULL;
1266         size_t i;
1267
1268         /* Form the msdfs_link contents */
1269         msdfs_link = talloc_strdup(ctx, "msdfs:");
1270         if (msdfs_link == NULL) {
1271                 goto err;
1272         }
1273
1274         for( i= 0; i < referral_count; i++) {
1275                 refpath = talloc_strdup(ctx, reflist[i].alternate_path);
1276
1277                 if (refpath == NULL) {
1278                         goto err;
1279                 }
1280
1281                 /* Alternate paths always use Windows separators. */
1282                 trim_char(refpath, '\\', '\\');
1283                 if (*refpath == '\0') {
1284                         if (i == 0) {
1285                                 insert_comma = false;
1286                         }
1287                         continue;
1288                 }
1289                 if (i > 0 && insert_comma) {
1290                         msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1291                                         ",%s",
1292                                         refpath);
1293                 } else {
1294                         msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1295                                         "%s",
1296                                         refpath);
1297                 }
1298
1299                 if (msdfs_link == NULL) {
1300                         goto err;
1301                 }
1302
1303                 if (!insert_comma) {
1304                         insert_comma = true;
1305                 }
1306
1307                 TALLOC_FREE(refpath);
1308         }
1309
1310         return msdfs_link;
1311
1312   err:
1313
1314         TALLOC_FREE(refpath);
1315         TALLOC_FREE(msdfs_link);
1316         return NULL;
1317 }
1318
1319 bool create_msdfs_link(const struct junction_map *jucn,
1320                        struct auth_session_info *session_info)
1321 {
1322         TALLOC_CTX *frame = talloc_stackframe();
1323         char *path = NULL;
1324         connection_struct *conn;
1325         struct smb_filename *smb_fname = NULL;
1326         struct smb_filename *parent_fname = NULL;
1327         struct smb_filename *at_fname = NULL;
1328         bool ok;
1329         NTSTATUS status;
1330         bool ret = false;
1331
1332         ok = junction_to_local_path_tos(jucn, session_info, &path, &conn);
1333         if (!ok) {
1334                 goto out;
1335         }
1336
1337         if (!CAN_WRITE(conn)) {
1338                 const struct loadparm_substitution *lp_sub =
1339                         loadparm_s3_global_substitution();
1340                 int snum = lp_servicenumber(jucn->service_name);
1341
1342                 DBG_WARNING("Can't create DFS entry on read-only share %s\n",
1343                         lp_servicename(frame, lp_sub, snum));
1344                 goto out;
1345         }
1346
1347         smb_fname = synthetic_smb_fname(frame,
1348                                 path,
1349                                 NULL,
1350                                 NULL,
1351                                 0,
1352                                 0);
1353         if (smb_fname == NULL) {
1354                 goto out;
1355         }
1356
1357         status = parent_pathref(frame,
1358                                 conn->cwd_fsp,
1359                                 smb_fname,
1360                                 &parent_fname,
1361                                 &at_fname);
1362         if (!NT_STATUS_IS_OK(status)) {
1363                 goto out;
1364         }
1365
1366         status = SMB_VFS_CREATE_DFS_PATHAT(conn,
1367                                 parent_fname->fsp,
1368                                 at_fname,
1369                                 jucn->referral_list,
1370                                 jucn->referral_count);
1371         if (!NT_STATUS_IS_OK(status)) {
1372                 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1373                         int retval = SMB_VFS_UNLINKAT(conn,
1374                                                 parent_fname->fsp,
1375                                                 at_fname,
1376                                                 0);
1377                         if (retval != 0) {
1378                                 goto out;
1379                         }
1380                 }
1381                 status = SMB_VFS_CREATE_DFS_PATHAT(conn,
1382                                 parent_fname->fsp,
1383                                 at_fname,
1384                                 jucn->referral_list,
1385                                 jucn->referral_count);
1386                 if (!NT_STATUS_IS_OK(status)) {
1387                         DBG_WARNING("SMB_VFS_CREATE_DFS_PATHAT failed "
1388                                 "%s - Error: %s\n",
1389                                 path,
1390                                 nt_errstr(status));
1391                         goto out;
1392                 }
1393         }
1394
1395         ret = true;
1396
1397 out:
1398         TALLOC_FREE(frame);
1399         return ret;
1400 }
1401
1402 bool remove_msdfs_link(const struct junction_map *jucn,
1403                        struct auth_session_info *session_info)
1404 {
1405         TALLOC_CTX *frame = talloc_stackframe();
1406         char *path = NULL;
1407         connection_struct *conn;
1408         bool ret = False;
1409         struct smb_filename *smb_fname;
1410         struct smb_filename *parent_fname = NULL;
1411         struct smb_filename *at_fname = NULL;
1412         NTSTATUS status;
1413         bool ok;
1414         int retval;
1415
1416         ok = junction_to_local_path_tos(jucn, session_info, &path, &conn);
1417         if (!ok) {
1418                 TALLOC_FREE(frame);
1419                 return false;
1420         }
1421
1422         if (!CAN_WRITE(conn)) {
1423                 const struct loadparm_substitution *lp_sub =
1424                         loadparm_s3_global_substitution();
1425                 int snum = lp_servicenumber(jucn->service_name);
1426
1427                 DBG_WARNING("Can't remove DFS entry on read-only share %s\n",
1428                         lp_servicename(frame, lp_sub, snum));
1429                 TALLOC_FREE(frame);
1430                 return false;
1431         }
1432
1433         smb_fname = synthetic_smb_fname(frame,
1434                                         path,
1435                                         NULL,
1436                                         NULL,
1437                                         0,
1438                                         0);
1439         if (smb_fname == NULL) {
1440                 TALLOC_FREE(frame);
1441                 errno = ENOMEM;
1442                 return false;
1443         }
1444
1445         status = parent_pathref(frame,
1446                                 conn->cwd_fsp,
1447                                 smb_fname,
1448                                 &parent_fname,
1449                                 &at_fname);
1450         if (!NT_STATUS_IS_OK(status)) {
1451                 TALLOC_FREE(frame);
1452                 return false;
1453         }
1454
1455         retval = SMB_VFS_UNLINKAT(conn,
1456                         parent_fname->fsp,
1457                         at_fname,
1458                         0);
1459         if (retval == 0) {
1460                 ret = True;
1461         }
1462
1463         TALLOC_FREE(frame);
1464         return ret;
1465 }
1466
1467 /*********************************************************************
1468  Return the number of DFS links at the root of this share.
1469 *********************************************************************/
1470
1471 static size_t count_dfs_links(TALLOC_CTX *ctx,
1472                               struct auth_session_info *session_info,
1473                               int snum)
1474 {
1475         TALLOC_CTX *frame = talloc_stackframe();
1476         const struct loadparm_substitution *lp_sub =
1477                 loadparm_s3_global_substitution();
1478         size_t cnt = 0;
1479         const char *dname = NULL;
1480         char *talloced = NULL;
1481         const char *connect_path = lp_path(frame, lp_sub, snum);
1482         const char *msdfs_proxy = lp_msdfs_proxy(frame, lp_sub, snum);
1483         struct conn_struct_tos *c = NULL;
1484         connection_struct *conn = NULL;
1485         NTSTATUS status;
1486         struct smb_filename *smb_fname = NULL;
1487         struct smb_Dir *dir_hnd = NULL;
1488         long offset = 0;
1489
1490         if(*connect_path == '\0') {
1491                 TALLOC_FREE(frame);
1492                 return 0;
1493         }
1494
1495         /*
1496          * Fake up a connection struct for the VFS layer.
1497          */
1498
1499         status = create_conn_struct_tos_cwd(global_messaging_context(),
1500                                             snum,
1501                                             connect_path,
1502                                             session_info,
1503                                             &c);
1504         if (!NT_STATUS_IS_OK(status)) {
1505                 DEBUG(3, ("create_conn_struct failed: %s\n",
1506                           nt_errstr(status)));
1507                 TALLOC_FREE(frame);
1508                 return 0;
1509         }
1510         conn = c->conn;
1511
1512         /* Count a link for the msdfs root - convention */
1513         cnt = 1;
1514
1515         /* No more links if this is an msdfs proxy. */
1516         if (*msdfs_proxy != '\0') {
1517                 goto out;
1518         }
1519
1520         smb_fname = synthetic_smb_fname(frame,
1521                                         ".",
1522                                         NULL,
1523                                         NULL,
1524                                         0,
1525                                         0);
1526         if (smb_fname == NULL) {
1527                 goto out;
1528         }
1529
1530         /* Now enumerate all dfs links */
1531         status = OpenDir(frame,
1532                          conn,
1533                          smb_fname,
1534                          NULL,
1535                          0,
1536                          &dir_hnd);
1537         if (!NT_STATUS_IS_OK(status)) {
1538                 errno = map_errno_from_nt_status(status);
1539                 goto out;
1540         }
1541
1542         while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
1543                != NULL)
1544         {
1545                 struct smb_filename *smb_dname =
1546                         synthetic_smb_fname(frame,
1547                                         dname,
1548                                         NULL,
1549                                         NULL,
1550                                         0,
1551                                         0);
1552                 if (smb_dname == NULL) {
1553                         goto out;
1554                 }
1555                 if (is_msdfs_link(dir_hnd_fetch_fsp(dir_hnd), smb_dname)) {
1556                         if (cnt + 1 < cnt) {
1557                                 cnt = 0;
1558                                 goto out;
1559                         }
1560                         cnt++;
1561                 }
1562                 TALLOC_FREE(talloced);
1563                 TALLOC_FREE(smb_dname);
1564         }
1565
1566 out:
1567         TALLOC_FREE(frame);
1568         return cnt;
1569 }
1570
1571 /*********************************************************************
1572 *********************************************************************/
1573
1574 static int form_junctions(TALLOC_CTX *ctx,
1575                           struct auth_session_info *session_info,
1576                                 int snum,
1577                                 struct junction_map *jucn,
1578                                 size_t jn_remain)
1579 {
1580         TALLOC_CTX *frame = talloc_stackframe();
1581         const struct loadparm_substitution *lp_sub =
1582                 loadparm_s3_global_substitution();
1583         size_t cnt = 0;
1584         const char *dname = NULL;
1585         char *talloced = NULL;
1586         const char *connect_path = lp_path(frame, lp_sub, snum);
1587         char *service_name = lp_servicename(frame, lp_sub, snum);
1588         const char *msdfs_proxy = lp_msdfs_proxy(frame, lp_sub, snum);
1589         struct conn_struct_tos *c = NULL;
1590         connection_struct *conn = NULL;
1591         struct referral *ref = NULL;
1592         struct smb_filename *smb_fname = NULL;
1593         struct smb_Dir *dir_hnd = NULL;
1594         long offset = 0;
1595         NTSTATUS status;
1596
1597         if (jn_remain == 0) {
1598                 TALLOC_FREE(frame);
1599                 return 0;
1600         }
1601
1602         if(*connect_path == '\0') {
1603                 TALLOC_FREE(frame);
1604                 return 0;
1605         }
1606
1607         /*
1608          * Fake up a connection struct for the VFS layer.
1609          */
1610
1611         status = create_conn_struct_tos_cwd(global_messaging_context(),
1612                                             snum,
1613                                             connect_path,
1614                                             session_info,
1615                                             &c);
1616         if (!NT_STATUS_IS_OK(status)) {
1617                 DEBUG(3, ("create_conn_struct failed: %s\n",
1618                           nt_errstr(status)));
1619                 TALLOC_FREE(frame);
1620                 return 0;
1621         }
1622         conn = c->conn;
1623
1624         /* form a junction for the msdfs root - convention
1625            DO NOT REMOVE THIS: NT clients will not work with us
1626            if this is not present
1627         */
1628         jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1629         jucn[cnt].volume_name = talloc_strdup(ctx, "");
1630         if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1631                 goto out;
1632         }
1633         jucn[cnt].comment = "";
1634         jucn[cnt].referral_count = 1;
1635
1636         ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1637         if (jucn[cnt].referral_list == NULL) {
1638                 goto out;
1639         }
1640
1641         ref->proximity = 0;
1642         ref->ttl = REFERRAL_TTL;
1643         if (*msdfs_proxy != '\0') {
1644                 ref->alternate_path = talloc_strdup(ctx,
1645                                                 msdfs_proxy);
1646         } else {
1647                 ref->alternate_path = talloc_asprintf(ctx,
1648                         "\\\\%s\\%s",
1649                         get_local_machine_name(),
1650                         service_name);
1651         }
1652
1653         if (!ref->alternate_path) {
1654                 goto out;
1655         }
1656         cnt++;
1657
1658         /* Don't enumerate if we're an msdfs proxy. */
1659         if (*msdfs_proxy != '\0') {
1660                 goto out;
1661         }
1662
1663         smb_fname = synthetic_smb_fname(frame,
1664                                         ".",
1665                                         NULL,
1666                                         NULL,
1667                                         0,
1668                                         0);
1669         if (smb_fname == NULL) {
1670                 goto out;
1671         }
1672
1673         /* Now enumerate all dfs links */
1674         status = OpenDir(frame,
1675                          conn,
1676                          smb_fname,
1677                          NULL,
1678                          0,
1679                          &dir_hnd);
1680         if (!NT_STATUS_IS_OK(status)) {
1681                 errno = map_errno_from_nt_status(status);
1682                 goto out;
1683         }
1684
1685         while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
1686                != NULL)
1687         {
1688                 struct smb_filename *smb_dname = NULL;
1689
1690                 if (cnt >= jn_remain) {
1691                         DEBUG(2, ("form_junctions: ran out of MSDFS "
1692                                 "junction slots"));
1693                         TALLOC_FREE(talloced);
1694                         goto out;
1695                 }
1696                 smb_dname = synthetic_smb_fname(talloc_tos(),
1697                                 dname,
1698                                 NULL,
1699                                 NULL,
1700                                 0,
1701                                 0);
1702                 if (smb_dname == NULL) {
1703                         TALLOC_FREE(talloced);
1704                         goto out;
1705                 }
1706
1707                 status = SMB_VFS_READ_DFS_PATHAT(conn,
1708                                 ctx,
1709                                 conn->cwd_fsp,
1710                                 smb_dname,
1711                                 &jucn[cnt].referral_list,
1712                                 &jucn[cnt].referral_count);
1713
1714                 if (NT_STATUS_IS_OK(status)) {
1715                         jucn[cnt].service_name = talloc_strdup(ctx,
1716                                                         service_name);
1717                         jucn[cnt].volume_name = talloc_strdup(ctx, dname);
1718                         if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1719                                 TALLOC_FREE(talloced);
1720                                 goto out;
1721                         }
1722                         jucn[cnt].comment = "";
1723                         cnt++;
1724                 }
1725                 TALLOC_FREE(talloced);
1726                 TALLOC_FREE(smb_dname);
1727         }
1728
1729 out:
1730         TALLOC_FREE(frame);
1731         return cnt;
1732 }
1733
1734 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx,
1735                                       struct auth_session_info *session_info,
1736                                       size_t *p_num_jn)
1737 {
1738         struct junction_map *jn = NULL;
1739         int i=0;
1740         size_t jn_count = 0;
1741         int sharecount = 0;
1742
1743         *p_num_jn = 0;
1744         if(!lp_host_msdfs()) {
1745                 return NULL;
1746         }
1747
1748         /* Ensure all the usershares are loaded. */
1749         become_root();
1750         load_registry_shares();
1751         sharecount = load_usershare_shares(NULL, connections_snum_used);
1752         unbecome_root();
1753
1754         for(i=0;i < sharecount;i++) {
1755                 if(lp_msdfs_root(i)) {
1756                         jn_count += count_dfs_links(ctx, session_info, i);
1757                 }
1758         }
1759         if (jn_count == 0) {
1760                 return NULL;
1761         }
1762         jn = talloc_array(ctx,  struct junction_map, jn_count);
1763         if (!jn) {
1764                 return NULL;
1765         }
1766         for(i=0; i < sharecount; i++) {
1767                 if (*p_num_jn >= jn_count) {
1768                         break;
1769                 }
1770                 if(lp_msdfs_root(i)) {
1771                         *p_num_jn += form_junctions(ctx,
1772                                         session_info,
1773                                         i,
1774                                         &jn[*p_num_jn],
1775                                         jn_count - *p_num_jn);
1776                 }
1777         }
1778         return jn;
1779 }