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