Line data Source code
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_syntax() 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 14928 : 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 14928 : char *pathname_local = NULL;
71 14928 : char *p = NULL;
72 14928 : const char *hostname = NULL;
73 14928 : const char *servicename = NULL;
74 14928 : const char *reqpath = NULL;
75 14928 : bool my_hostname = false;
76 0 : NTSTATUS status;
77 :
78 14928 : DBG_DEBUG("path = |%s|\n", pathname);
79 :
80 14928 : pathname_local = talloc_strdup(talloc_tos(), pathname);
81 14928 : if (pathname_local == NULL) {
82 0 : 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 14928 : if (pathname_local[0] != '\\') {
96 2 : DBG_ERR("path %s doesn't start with \\\n",
97 : pathname_local);
98 2 : status = NT_STATUS_NOT_FOUND;
99 2 : goto out;
100 : }
101 :
102 : /* Now tokenize. */
103 : /* Parse out hostname. */
104 14926 : p = strchr(pathname_local + 1, '\\');
105 14926 : if (p == NULL) {
106 0 : DBG_ERR("can't parse hostname from path %s\n",
107 : pathname_local);
108 0 : status = NT_STATUS_NOT_FOUND;
109 0 : goto out;
110 : }
111 14926 : *p = '\0';
112 14926 : hostname = &pathname_local[1];
113 :
114 14926 : DBG_DEBUG("hostname: %s\n", hostname);
115 :
116 : /* Is this really our hostname ? */
117 14926 : my_hostname = is_myname_or_ipaddr(hostname);
118 14926 : if (!my_hostname) {
119 0 : DBG_ERR("Hostname %s is not ours.\n",
120 : hostname);
121 0 : status = NT_STATUS_NOT_FOUND;
122 0 : goto out;
123 : }
124 :
125 14926 : 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 14926 : p = strchr(servicename, '\\');
136 14926 : if (p != NULL) {
137 4450 : *p = '\0';
138 : }
139 :
140 14926 : DBG_DEBUG("servicename: %s\n", servicename);
141 :
142 14926 : if (p == NULL) {
143 : /* Client sent self referral "\server\share". */
144 10476 : reqpath = "";
145 : } else {
146 : /* Step past the '\0' we just replaced '\\' with. */
147 4450 : reqpath = p + 1;
148 : }
149 :
150 14926 : DBG_DEBUG("rest of the path: %s\n", reqpath);
151 :
152 14926 : if (_hostname != NULL) {
153 0 : *_hostname = talloc_strdup(ctx, hostname);
154 0 : if (*_hostname == NULL) {
155 0 : status = NT_STATUS_NO_MEMORY;
156 0 : goto out;
157 : }
158 : }
159 14926 : if (_servicename != NULL) {
160 14926 : *_servicename = talloc_strdup(ctx, servicename);
161 14926 : if (*_servicename == NULL) {
162 0 : status = NT_STATUS_NO_MEMORY;
163 0 : goto out;
164 : }
165 : }
166 14926 : if (_remaining_path != NULL) {
167 14926 : *_remaining_path = talloc_strdup(ctx, reqpath);
168 14926 : if (*_remaining_path == NULL) {
169 0 : status = NT_STATUS_NO_MEMORY;
170 0 : goto out;
171 : }
172 : }
173 :
174 14926 : status = NT_STATUS_OK;
175 14928 : out:
176 14928 : TALLOC_FREE(pathname_local);
177 14928 : 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 8406 : 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 115 : connection_struct *conn;
197 115 : char *connpath;
198 115 : const char *vfs_user;
199 115 : struct smbd_server_connection *sconn;
200 8406 : const char *servicename = lp_const_servicename(snum);
201 115 : bool ok;
202 :
203 8406 : sconn = talloc_zero(ctx, struct smbd_server_connection);
204 8406 : if (sconn == NULL) {
205 0 : return NT_STATUS_NO_MEMORY;
206 : }
207 :
208 8406 : sconn->ev_ctx = ev;
209 8406 : sconn->msg_ctx = msg;
210 :
211 8406 : conn = conn_new(sconn);
212 8406 : if (conn == NULL) {
213 0 : TALLOC_FREE(sconn);
214 0 : 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 8406 : talloc_steal(conn, sconn);
220 :
221 8406 : if (snum == -1 && servicename == NULL) {
222 1324 : servicename = "Unknown Service (snum == -1)";
223 : }
224 :
225 8406 : connpath = talloc_strdup(conn, path);
226 8406 : if (!connpath) {
227 0 : TALLOC_FREE(conn);
228 0 : return NT_STATUS_NO_MEMORY;
229 : }
230 8406 : connpath = talloc_string_sub(conn,
231 : connpath,
232 : "%S",
233 : servicename);
234 8406 : if (!connpath) {
235 0 : TALLOC_FREE(conn);
236 0 : return NT_STATUS_NO_MEMORY;
237 : }
238 :
239 : /* needed for smbd_vfs_init() */
240 :
241 8406 : conn->params->service = snum;
242 8406 : conn->cnum = TID_FIELD_INVALID;
243 :
244 8406 : SMB_ASSERT(session_info != NULL);
245 :
246 8406 : conn->session_info = copy_session_info(conn, session_info);
247 8406 : if (conn->session_info == NULL) {
248 0 : DBG_ERR("copy_serverinfo failed\n");
249 0 : TALLOC_FREE(conn);
250 0 : return NT_STATUS_NO_MEMORY;
251 : }
252 :
253 : /* unix_info could be NULL in session_info */
254 8406 : if (conn->session_info->unix_info != NULL) {
255 8406 : vfs_user = conn->session_info->unix_info->unix_name;
256 : } else {
257 0 : vfs_user = get_current_username();
258 : }
259 :
260 8406 : conn_setup_case_options(conn);
261 :
262 8406 : 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 8406 : share_access_check(conn->session_info->security_token,
271 : servicename,
272 : MAXIMUM_ALLOWED_ACCESS,
273 8406 : &conn->share_access);
274 :
275 8406 : if ((conn->share_access & FILE_WRITE_DATA) == 0) {
276 0 : if ((conn->share_access & FILE_READ_DATA) == 0) {
277 : /* No access, read or write. */
278 0 : DBG_WARNING("connection to %s "
279 : "denied due to security "
280 : "descriptor.\n",
281 : servicename);
282 0 : conn_free(conn);
283 0 : return NT_STATUS_ACCESS_DENIED;
284 : }
285 0 : conn->read_only = true;
286 : }
287 :
288 8406 : if (!smbd_vfs_init(conn)) {
289 0 : NTSTATUS status = map_nt_error_from_unix(errno);
290 0 : DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
291 0 : conn_free(conn);
292 0 : return status;
293 : }
294 :
295 : /* this must be the first filesystem operation that we do */
296 8406 : if (SMB_VFS_CONNECT(conn, servicename, vfs_user) < 0) {
297 0 : DEBUG(0,("VFS connect failed!\n"));
298 0 : conn_free(conn);
299 0 : return NT_STATUS_UNSUCCESSFUL;
300 : }
301 :
302 8406 : ok = canonicalize_connect_path(conn);
303 8406 : if (!ok) {
304 0 : DBG_ERR("Failed to canonicalize sharepath\n");
305 0 : conn_free(conn);
306 0 : return NT_STATUS_ACCESS_DENIED;
307 : }
308 :
309 8406 : conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
310 8406 : conn->tcon_done = true;
311 8406 : *pconn = talloc_move(ctx, &conn);
312 :
313 8406 : return NT_STATUS_OK;
314 : }
315 :
316 8382 : static int conn_struct_tos_destructor(struct conn_struct_tos *c)
317 : {
318 8382 : if (c->oldcwd_fname != NULL) {
319 4454 : vfs_ChDir(c->conn, c->oldcwd_fname);
320 4454 : TALLOC_FREE(c->oldcwd_fname);
321 : }
322 8382 : SMB_VFS_DISCONNECT(c->conn);
323 8382 : conn_free(c->conn);
324 8382 : 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 8394 : 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 8394 : struct conn_struct_tos *c = NULL;
344 8394 : struct tevent_context *ev = NULL;
345 115 : NTSTATUS status;
346 :
347 8394 : *_c = NULL;
348 :
349 8394 : c = talloc_zero(talloc_tos(), struct conn_struct_tos);
350 8394 : if (c == NULL) {
351 0 : return NT_STATUS_NO_MEMORY;
352 : }
353 :
354 8394 : ev = samba_tevent_context_init(c);
355 8394 : if (ev == NULL) {
356 0 : TALLOC_FREE(c);
357 0 : return NT_STATUS_NO_MEMORY;
358 : }
359 :
360 8394 : become_root();
361 8394 : status = create_conn_struct_as_root(c,
362 : ev,
363 : msg,
364 8394 : &c->conn,
365 : snum,
366 : path,
367 : session_info);
368 8394 : unbecome_root();
369 8394 : if (!NT_STATUS_IS_OK(status)) {
370 0 : TALLOC_FREE(c);
371 0 : return status;
372 : }
373 :
374 8394 : talloc_set_destructor(c, conn_struct_tos_destructor);
375 :
376 8394 : *_c = c;
377 8394 : 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 4466 : 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 4466 : struct conn_struct_tos *c = NULL;
396 4466 : struct smb_filename smb_fname_connectpath = {0};
397 0 : NTSTATUS status;
398 :
399 4466 : *_c = NULL;
400 :
401 4466 : status = create_conn_struct_tos(msg,
402 : snum,
403 : path,
404 : session_info,
405 : &c);
406 4466 : if (!NT_STATUS_IS_OK(status)) {
407 0 : 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 4466 : c->oldcwd_fname = vfs_GetWd(c, c->conn);
417 4466 : if (c->oldcwd_fname == NULL) {
418 0 : status = map_nt_error_from_unix(errno);
419 0 : DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
420 0 : TALLOC_FREE(c);
421 0 : return status;
422 : }
423 :
424 4466 : smb_fname_connectpath = (struct smb_filename) {
425 4466 : .base_name = c->conn->connectpath
426 : };
427 :
428 4466 : if (vfs_ChDir(c->conn, &smb_fname_connectpath) != 0) {
429 0 : status = map_nt_error_from_unix(errno);
430 0 : DBG_NOTICE("Can't ChDir to new conn path %s. "
431 : "Error was %s\n",
432 : c->conn->connectpath, strerror(errno));
433 0 : TALLOC_FREE(c->oldcwd_fname);
434 0 : TALLOC_FREE(c);
435 0 : return status;
436 : }
437 :
438 4466 : *_c = c;
439 4466 : 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 12 : 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 0 : NTSTATUS status;
462 :
463 12 : become_root();
464 12 : status = create_conn_struct_as_root(mem_ctx,
465 : ev,
466 : msg,
467 : c,
468 : snum,
469 : path,
470 : session_info);
471 12 : unbecome_root();
472 12 : return status;
473 : }
474 :
475 2192 : static void shuffle_strlist(char **list, int count)
476 : {
477 0 : int i;
478 0 : uint32_t r;
479 0 : char *tmp;
480 :
481 4292 : for (i = count; i > 1; i--) {
482 2100 : r = generate_random() % i;
483 :
484 2100 : tmp = list[i-1];
485 2100 : list[i-1] = list[r];
486 2100 : list[r] = tmp;
487 : }
488 2192 : }
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 4448 : 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 4448 : char *temp = NULL;
516 0 : char *prot;
517 4448 : char **alt_path = NULL;
518 4448 : size_t count = 0, i;
519 4448 : struct referral *reflist = NULL;
520 0 : char *saveptr;
521 :
522 4448 : temp = talloc_strdup(ctx, target);
523 4448 : if (!temp) {
524 0 : return false;
525 : }
526 4448 : prot = strtok_r(temp, ":", &saveptr);
527 4448 : if (!prot) {
528 0 : DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
529 0 : TALLOC_FREE(temp);
530 0 : return false;
531 : }
532 :
533 4448 : alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
534 4448 : if (!alt_path) {
535 0 : TALLOC_FREE(temp);
536 0 : return false;
537 : }
538 :
539 : /* parse out the alternate paths */
540 12886 : while((count<MAX_REFERRAL_COUNT) &&
541 12886 : ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
542 8438 : count++;
543 : }
544 :
545 : /* shuffle alternate paths */
546 4448 : if (shuffle_referrals) {
547 2192 : shuffle_strlist(alt_path, count);
548 : }
549 :
550 4448 : DBG_DEBUG("count=%zu\n", count);
551 :
552 4448 : if (count) {
553 4448 : reflist = talloc_zero_array(ctx,
554 : struct referral, count);
555 4448 : if(reflist == NULL) {
556 0 : TALLOC_FREE(temp);
557 0 : TALLOC_FREE(alt_path);
558 0 : return false;
559 : }
560 : } else {
561 0 : reflist = NULL;
562 : }
563 :
564 12886 : for(i=0;i<count;i++) {
565 0 : char *p;
566 :
567 : /* Canonicalize link target.
568 : * Replace all /'s in the path by a \ */
569 8438 : string_replace(alt_path[i], '/', '\\');
570 :
571 : /* Remove leading '\\'s */
572 8438 : p = alt_path[i];
573 8438 : while (*p && (*p == '\\')) {
574 0 : p++;
575 : }
576 :
577 8438 : reflist[i].alternate_path = talloc_asprintf(reflist,
578 : "\\%s",
579 : p);
580 8438 : if (!reflist[i].alternate_path) {
581 0 : TALLOC_FREE(temp);
582 0 : TALLOC_FREE(alt_path);
583 0 : TALLOC_FREE(reflist);
584 0 : return false;
585 : }
586 :
587 8438 : reflist[i].proximity = 0;
588 8438 : reflist[i].ttl = REFERRAL_TTL;
589 8438 : DBG_DEBUG("Created alt path: %s\n",
590 : reflist[i].alternate_path);
591 : }
592 :
593 4448 : if (ppreflist != NULL) {
594 4448 : *ppreflist = reflist;
595 : } else {
596 0 : TALLOC_FREE(reflist);
597 : }
598 4448 : if (prefcount != NULL) {
599 4448 : *prefcount = count;
600 : }
601 4448 : TALLOC_FREE(temp);
602 4448 : TALLOC_FREE(alt_path);
603 4448 : return true;
604 : }
605 :
606 : /**********************************************************************
607 : Returns true if the unix path is a valid msdfs symlink.
608 : **********************************************************************/
609 :
610 374 : bool is_msdfs_link(struct files_struct *dirfsp,
611 : struct smb_filename *atname)
612 : {
613 374 : NTSTATUS status = SMB_VFS_READ_DFS_PATHAT(dirfsp->conn,
614 : talloc_tos(),
615 : dirfsp,
616 : atname,
617 : NULL,
618 : NULL);
619 374 : 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 4448 : 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 0 : NTSTATUS status;
640 4448 : struct smb_filename *parent_smb_fname = NULL;
641 4448 : struct smb_filename *smb_fname_rel = NULL;
642 4448 : NTTIME twrp = 0;
643 4448 : char *local_pathname = NULL;
644 4448 : char *last_component = NULL;
645 4448 : char *atname = NULL;
646 4448 : size_t removed_components = 0;
647 4448 : bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
648 4448 : char *p = NULL;
649 4448 : char *canon_dfspath = NULL;
650 :
651 4448 : DBG_DEBUG("Conn path = %s reqpath = %s\n", conn->connectpath, reqpath);
652 :
653 4448 : local_pathname = talloc_strdup(ctx, reqpath);
654 4448 : if (local_pathname == NULL) {
655 0 : status = NT_STATUS_NO_MEMORY;
656 0 : goto out;
657 : }
658 :
659 : /* We know reqpath isn't a DFS path. */
660 4448 : ucf_flags &= ~UCF_DFS_PATHNAME;
661 :
662 4448 : if (ucf_flags & UCF_GMT_PATHNAME) {
663 0 : extract_snapshot_token(local_pathname, &twrp);
664 0 : 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 45500 : for (;;) {
679 49948 : TALLOC_CTX *frame = NULL;
680 49948 : struct files_struct *dirfsp = NULL;
681 49948 : struct smb_filename *smb_fname_walk = NULL;
682 :
683 49948 : 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 49948 : frame = talloc_stackframe();
692 49948 : 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 49948 : if (smb_fname_walk != NULL) {
701 4448 : parent_smb_fname = talloc_move(ctx, &smb_fname_walk);
702 : }
703 49948 : TALLOC_FREE(frame);
704 :
705 49948 : 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 4448 : break;
712 : }
713 :
714 : /* Step back one component and save it off as last_component. */
715 45500 : TALLOC_FREE(last_component);
716 45500 : p = strrchr(local_pathname, '/');
717 45500 : 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 3990 : last_component = talloc_strdup(ctx, local_pathname);
724 3990 : *local_pathname = '\0';
725 : } else {
726 41510 : last_component = talloc_strdup(ctx, p+1);
727 41510 : *p = '\0';
728 : }
729 45500 : if (last_component == NULL) {
730 0 : status = NT_STATUS_NO_MEMORY;
731 0 : goto out;
732 : }
733 : /* Integer wrap check. */
734 45500 : if (removed_components + 1 < removed_components) {
735 0 : status = NT_STATUS_INVALID_PARAMETER;
736 0 : goto out;
737 : }
738 45500 : removed_components++;
739 : }
740 :
741 4448 : if (!NT_STATUS_IS_OK(status)) {
742 0 : DBG_DEBUG("dfspath = %s. reqpath = %s. Error %s.\n",
743 : dfspath,
744 : reqpath,
745 : nt_errstr(status));
746 0 : goto out;
747 : }
748 :
749 4448 : if (parent_smb_fname->fsp == NULL) {
750 : /* Unable to open parent. */
751 0 : 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 0 : status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
757 0 : goto out;
758 : }
759 :
760 4448 : if (removed_components == 0) {
761 : /*
762 : * We never got NT_STATUS_PATH_NOT_COVERED.
763 : * There was no DFS redirect.
764 : */
765 0 : DBG_DEBUG("dfspath = %s. reqpath = %s. "
766 : "No removed components.\n",
767 : dfspath,
768 : reqpath);
769 0 : status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
770 0 : 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 4448 : 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 4448 : status = get_real_filename_at(parent_smb_fname->fsp,
787 : last_component,
788 : ctx,
789 : &atname);
790 :
791 4448 : if (!NT_STATUS_IS_OK(status)) {
792 0 : 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 0 : goto out;
800 : }
801 :
802 4448 : smb_fname_rel = synthetic_smb_fname(ctx,
803 : atname,
804 : NULL,
805 : NULL,
806 : twrp,
807 : posix ? SMB_FILENAME_POSIX_PATH : 0);
808 4448 : if (smb_fname_rel == NULL) {
809 0 : status = NT_STATUS_NO_MEMORY;
810 0 : goto out;
811 : }
812 :
813 : /* Get the referral to return. */
814 4448 : status = SMB_VFS_READ_DFS_PATHAT(conn,
815 : ctx,
816 : parent_smb_fname->fsp,
817 : smb_fname_rel,
818 : ppreflist,
819 : preferral_count);
820 4448 : if (!NT_STATUS_IS_OK(status)) {
821 0 : 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 0 : goto out;
829 : }
830 :
831 : /*
832 : * Now we must work out how much of the
833 : * given pathname we consumed.
834 : */
835 4448 : canon_dfspath = talloc_strdup(ctx, dfspath);
836 4448 : if (!canon_dfspath) {
837 0 : status = NT_STATUS_NO_MEMORY;
838 0 : goto out;
839 : }
840 : /* Canonicalize the raw dfspath. */
841 4448 : 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 4448 : trim_char(canon_dfspath, 0, '/');
848 :
849 4448 : DBG_DEBUG("Unconsumed path: %s\n", canon_dfspath);
850 :
851 45500 : while (removed_components > 0) {
852 41052 : p = strrchr(canon_dfspath, '/');
853 41052 : if (p != NULL) {
854 41052 : *p = '\0';
855 : }
856 41052 : removed_components--;
857 41052 : if (p == NULL && removed_components != 0) {
858 0 : DBG_ERR("Component mismatch. path = %s, "
859 : "%zu components left\n",
860 : canon_dfspath,
861 : removed_components);
862 0 : status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
863 0 : goto out;
864 : }
865 : }
866 4448 : *consumedcntp = strlen(canon_dfspath);
867 4448 : DBG_DEBUG("Path consumed: %s (%zu)\n", canon_dfspath, *consumedcntp);
868 4448 : status = NT_STATUS_OK;
869 :
870 4448 : out:
871 :
872 4448 : TALLOC_FREE(parent_smb_fname);
873 4448 : TALLOC_FREE(local_pathname);
874 4448 : TALLOC_FREE(last_component);
875 4448 : TALLOC_FREE(atname);
876 4448 : TALLOC_FREE(smb_fname_rel);
877 4448 : TALLOC_FREE(canon_dfspath);
878 4448 : return status;
879 : }
880 :
881 : /**********************************************************************
882 : Return a self referral.
883 : **********************************************************************/
884 :
885 1456 : 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 0 : struct referral *ref;
892 :
893 1456 : *self_referralp = True;
894 :
895 1456 : jucn->referral_count = 1;
896 1456 : if((ref = talloc_zero(ctx, struct referral)) == NULL) {
897 0 : return NT_STATUS_NO_MEMORY;
898 : }
899 :
900 1456 : ref->alternate_path = talloc_strdup(ctx, dfs_path);
901 1456 : if (!ref->alternate_path) {
902 0 : TALLOC_FREE(ref);
903 0 : return NT_STATUS_NO_MEMORY;
904 : }
905 1456 : ref->proximity = 0;
906 1456 : ref->ttl = REFERRAL_TTL;
907 1456 : jucn->referral_list = ref;
908 1456 : *consumedcntp = strlen(dfs_path);
909 1456 : 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 14926 : 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 14926 : TALLOC_CTX *frame = talloc_stackframe();
927 0 : const struct loadparm_substitution *lp_sub =
928 14926 : loadparm_s3_global_substitution();
929 14926 : struct conn_struct_tos *c = NULL;
930 14926 : struct connection_struct *conn = NULL;
931 14926 : char *servicename = NULL;
932 14926 : char *reqpath = NULL;
933 0 : int snum;
934 14926 : NTSTATUS status = NT_STATUS_NOT_FOUND;
935 :
936 14926 : *self_referralp = False;
937 :
938 14926 : status = parse_dfs_path_strict(
939 : frame,
940 : dfs_path,
941 : NULL, /* hostname */
942 : &servicename,
943 : &reqpath);
944 14926 : if (!NT_STATUS_IS_OK(status)) {
945 2 : TALLOC_FREE(frame);
946 2 : return status;
947 : }
948 :
949 : /* Path referrals are always non-POSIX. */
950 14924 : status = check_path_syntax(reqpath, false);
951 14924 : if (!NT_STATUS_IS_OK(status)) {
952 0 : TALLOC_FREE(frame);
953 0 : return status;
954 : }
955 :
956 14924 : jucn->service_name = talloc_strdup(ctx, servicename);
957 14924 : jucn->volume_name = talloc_strdup(ctx, reqpath);
958 14924 : if (!jucn->service_name || !jucn->volume_name) {
959 0 : TALLOC_FREE(frame);
960 0 : return NT_STATUS_NO_MEMORY;
961 : }
962 :
963 : /* Verify the share is a dfs root */
964 14924 : snum = lp_servicenumber(jucn->service_name);
965 14924 : if(snum < 0) {
966 32 : char *service_name = NULL;
967 32 : if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
968 30 : TALLOC_FREE(frame);
969 30 : return NT_STATUS_NOT_FOUND;
970 : }
971 2 : if (!service_name) {
972 0 : TALLOC_FREE(frame);
973 0 : return NT_STATUS_NO_MEMORY;
974 : }
975 2 : TALLOC_FREE(jucn->service_name);
976 2 : jucn->service_name = talloc_strdup(ctx, service_name);
977 2 : if (!jucn->service_name) {
978 0 : TALLOC_FREE(frame);
979 0 : return NT_STATUS_NO_MEMORY;
980 : }
981 : }
982 :
983 14894 : if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(talloc_tos(), lp_sub, snum) == '\0')) {
984 8990 : DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
985 : "a dfs root.\n",
986 : servicename, dfs_path));
987 8990 : TALLOC_FREE(frame);
988 8990 : 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 5904 : if (reqpath[0] == '\0') {
1000 0 : char *tmp;
1001 0 : struct referral *ref;
1002 0 : size_t refcount;
1003 :
1004 1456 : if (*lp_msdfs_proxy(talloc_tos(), lp_sub, snum) == '\0') {
1005 1456 : TALLOC_FREE(frame);
1006 1456 : 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 0 : tmp = talloc_asprintf(frame, "msdfs:%s",
1019 : lp_msdfs_proxy(frame, lp_sub, snum));
1020 0 : if (tmp == NULL) {
1021 0 : TALLOC_FREE(frame);
1022 0 : return NT_STATUS_NO_MEMORY;
1023 : }
1024 :
1025 0 : if (!parse_msdfs_symlink(ctx,
1026 0 : lp_msdfs_shuffle_referrals(snum),
1027 : tmp,
1028 : &ref,
1029 : &refcount)) {
1030 0 : TALLOC_FREE(frame);
1031 0 : return NT_STATUS_INVALID_PARAMETER;
1032 : }
1033 0 : jucn->referral_count = refcount;
1034 0 : jucn->referral_list = ref;
1035 0 : *consumedcntp = strlen(dfs_path);
1036 0 : TALLOC_FREE(frame);
1037 0 : return NT_STATUS_OK;
1038 : }
1039 :
1040 4448 : status = create_conn_struct_tos_cwd(global_messaging_context(),
1041 : snum,
1042 4448 : lp_path(frame, lp_sub, snum),
1043 : session_info,
1044 : &c);
1045 4448 : if (!NT_STATUS_IS_OK(status)) {
1046 0 : TALLOC_FREE(frame);
1047 0 : return status;
1048 : }
1049 4448 : 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 4448 : if (conn->sconn->remote_address == NULL) {
1058 8896 : conn->sconn->remote_address =
1059 4448 : tsocket_address_copy(remote_address, conn->sconn);
1060 4448 : if (conn->sconn->remote_address == NULL) {
1061 0 : TALLOC_FREE(frame);
1062 0 : return NT_STATUS_NO_MEMORY;
1063 : }
1064 : }
1065 4448 : if (conn->sconn->local_address == NULL) {
1066 8896 : conn->sconn->local_address =
1067 4448 : tsocket_address_copy(local_address, conn->sconn);
1068 4448 : if (conn->sconn->local_address == NULL) {
1069 0 : TALLOC_FREE(frame);
1070 0 : return NT_STATUS_NO_MEMORY;
1071 : }
1072 : }
1073 :
1074 4448 : 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 4448 : if (!NT_STATUS_IS_OK(status)) {
1084 0 : DBG_NOTICE("No valid referrals for path %s (%s)\n",
1085 : dfs_path,
1086 : nt_errstr(status));
1087 : }
1088 :
1089 4448 : TALLOC_FREE(frame);
1090 4448 : 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 14956 : 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 14956 : char *pdata = *ppdata;
1106 14956 : int reply_size = 0;
1107 0 : struct dfs_GetDFSReferral *r;
1108 14956 : DATA_BLOB blob = data_blob_null;
1109 0 : NTSTATUS status;
1110 0 : enum ndr_err_code ndr_err;
1111 :
1112 14956 : r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1113 14956 : if (r == NULL) {
1114 0 : *pstatus = NT_STATUS_NO_MEMORY;
1115 0 : return -1;
1116 : }
1117 :
1118 14956 : r->in.req.max_referral_level = max_referral_level;
1119 14956 : r->in.req.servername = talloc_strdup(r, dfs_path);
1120 14956 : if (r->in.req.servername == NULL) {
1121 0 : talloc_free(r);
1122 0 : *pstatus = NT_STATUS_NO_MEMORY;
1123 0 : return -1;
1124 : }
1125 :
1126 14956 : status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1127 14956 : if (!NT_STATUS_IS_OK(status)) {
1128 9026 : talloc_free(r);
1129 9026 : *pstatus = status;
1130 9026 : return -1;
1131 : }
1132 :
1133 5930 : ndr_err = ndr_push_struct_blob(&blob, r,
1134 5930 : r->out.resp,
1135 : (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1136 5930 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1137 0 : TALLOC_FREE(r);
1138 0 : *pstatus = NT_STATUS_INVALID_PARAMETER;
1139 0 : return -1;
1140 : }
1141 :
1142 5930 : pdata = (char *)SMB_REALLOC(pdata, blob.length);
1143 5930 : if(pdata == NULL) {
1144 0 : TALLOC_FREE(r);
1145 0 : DEBUG(0,("referral setup:"
1146 : "malloc failed for Realloc!\n"));
1147 0 : return -1;
1148 : }
1149 5930 : *ppdata = pdata;
1150 5930 : reply_size = blob.length;
1151 5930 : memcpy(pdata, blob.data, blob.length);
1152 5930 : TALLOC_FREE(r);
1153 :
1154 5930 : *pstatus = NT_STATUS_OK;
1155 5930 : 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 2 : bool create_junction(TALLOC_CTX *ctx,
1167 : const char *dfs_path,
1168 : struct junction_map *jucn)
1169 : {
1170 0 : const struct loadparm_substitution *lp_sub =
1171 2 : loadparm_s3_global_substitution();
1172 0 : int snum;
1173 2 : char *servicename = NULL;
1174 2 : char *reqpath = NULL;
1175 0 : NTSTATUS status;
1176 :
1177 2 : status = parse_dfs_path_strict(
1178 : ctx,
1179 : dfs_path,
1180 : NULL,
1181 : &servicename,
1182 : &reqpath);
1183 2 : if (!NT_STATUS_IS_OK(status)) {
1184 0 : return False;
1185 : }
1186 :
1187 : /* Check for a non-DFS share */
1188 2 : snum = lp_servicenumber(servicename);
1189 :
1190 2 : if(snum < 0 || !lp_msdfs_root(snum)) {
1191 0 : DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1192 : servicename));
1193 0 : return False;
1194 : }
1195 :
1196 : /* Junction create paths are always non-POSIX. */
1197 2 : status = check_path_syntax(reqpath, false);
1198 2 : if (!NT_STATUS_IS_OK(status)) {
1199 0 : return false;
1200 : }
1201 :
1202 2 : jucn->service_name = talloc_strdup(ctx, servicename);
1203 2 : jucn->volume_name = talloc_strdup(ctx, reqpath);
1204 2 : jucn->comment = lp_comment(ctx, lp_sub, snum);
1205 :
1206 2 : if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1207 0 : return False;
1208 : }
1209 2 : return True;
1210 : }
1211 :
1212 : /**********************************************************************
1213 : Forms a valid Unix pathname from the junction
1214 : **********************************************************************/
1215 :
1216 0 : 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 0 : const struct loadparm_substitution *lp_sub =
1222 0 : loadparm_s3_global_substitution();
1223 0 : struct conn_struct_tos *c = NULL;
1224 0 : int snum;
1225 0 : char *path_out = NULL;
1226 0 : NTSTATUS status;
1227 :
1228 0 : snum = lp_servicenumber(jucn->service_name);
1229 0 : if(snum < 0) {
1230 0 : return False;
1231 : }
1232 0 : status = create_conn_struct_tos_cwd(global_messaging_context(),
1233 : snum,
1234 0 : lp_path(talloc_tos(), lp_sub, snum),
1235 : session_info,
1236 : &c);
1237 0 : if (!NT_STATUS_IS_OK(status)) {
1238 0 : return False;
1239 : }
1240 :
1241 0 : path_out = talloc_asprintf(c,
1242 : "%s/%s",
1243 : lp_path(talloc_tos(), lp_sub, snum),
1244 0 : jucn->volume_name);
1245 0 : if (path_out == NULL) {
1246 0 : TALLOC_FREE(c);
1247 0 : return False;
1248 : }
1249 0 : *pp_path_out = path_out;
1250 0 : *conn_out = c->conn;
1251 0 : 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 0 : char *msdfs_link_string(TALLOC_CTX *ctx,
1260 : const struct referral *reflist,
1261 : size_t referral_count)
1262 : {
1263 0 : char *refpath = NULL;
1264 0 : bool insert_comma = false;
1265 0 : char *msdfs_link = NULL;
1266 0 : size_t i;
1267 :
1268 : /* Form the msdfs_link contents */
1269 0 : msdfs_link = talloc_strdup(ctx, "msdfs:");
1270 0 : if (msdfs_link == NULL) {
1271 0 : goto err;
1272 : }
1273 :
1274 0 : for( i= 0; i < referral_count; i++) {
1275 0 : refpath = talloc_strdup(ctx, reflist[i].alternate_path);
1276 :
1277 0 : if (refpath == NULL) {
1278 0 : goto err;
1279 : }
1280 :
1281 : /* Alternate paths always use Windows separators. */
1282 0 : trim_char(refpath, '\\', '\\');
1283 0 : if (*refpath == '\0') {
1284 0 : if (i == 0) {
1285 0 : insert_comma = false;
1286 : }
1287 0 : continue;
1288 : }
1289 0 : if (i > 0 && insert_comma) {
1290 0 : msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1291 : ",%s",
1292 : refpath);
1293 : } else {
1294 0 : msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1295 : "%s",
1296 : refpath);
1297 : }
1298 :
1299 0 : if (msdfs_link == NULL) {
1300 0 : goto err;
1301 : }
1302 :
1303 0 : if (!insert_comma) {
1304 0 : insert_comma = true;
1305 : }
1306 :
1307 0 : TALLOC_FREE(refpath);
1308 : }
1309 :
1310 0 : return msdfs_link;
1311 :
1312 0 : err:
1313 :
1314 0 : TALLOC_FREE(refpath);
1315 0 : TALLOC_FREE(msdfs_link);
1316 0 : return NULL;
1317 : }
1318 :
1319 0 : bool create_msdfs_link(const struct junction_map *jucn,
1320 : struct auth_session_info *session_info)
1321 : {
1322 0 : TALLOC_CTX *frame = talloc_stackframe();
1323 0 : char *path = NULL;
1324 0 : connection_struct *conn;
1325 0 : struct smb_filename *smb_fname = NULL;
1326 0 : struct smb_filename *parent_fname = NULL;
1327 0 : struct smb_filename *at_fname = NULL;
1328 0 : bool ok;
1329 0 : NTSTATUS status;
1330 0 : bool ret = false;
1331 :
1332 0 : ok = junction_to_local_path_tos(jucn, session_info, &path, &conn);
1333 0 : if (!ok) {
1334 0 : goto out;
1335 : }
1336 :
1337 0 : if (!CAN_WRITE(conn)) {
1338 0 : const struct loadparm_substitution *lp_sub =
1339 0 : loadparm_s3_global_substitution();
1340 0 : int snum = lp_servicenumber(jucn->service_name);
1341 :
1342 0 : DBG_WARNING("Can't create DFS entry on read-only share %s\n",
1343 : lp_servicename(frame, lp_sub, snum));
1344 0 : goto out;
1345 : }
1346 :
1347 0 : smb_fname = synthetic_smb_fname(frame,
1348 : path,
1349 : NULL,
1350 : NULL,
1351 : 0,
1352 : 0);
1353 0 : if (smb_fname == NULL) {
1354 0 : goto out;
1355 : }
1356 :
1357 0 : status = parent_pathref(frame,
1358 0 : conn->cwd_fsp,
1359 : smb_fname,
1360 : &parent_fname,
1361 : &at_fname);
1362 0 : if (!NT_STATUS_IS_OK(status)) {
1363 0 : goto out;
1364 : }
1365 :
1366 0 : status = SMB_VFS_CREATE_DFS_PATHAT(conn,
1367 : parent_fname->fsp,
1368 : at_fname,
1369 : jucn->referral_list,
1370 : jucn->referral_count);
1371 0 : if (!NT_STATUS_IS_OK(status)) {
1372 0 : if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1373 0 : int retval = SMB_VFS_UNLINKAT(conn,
1374 : parent_fname->fsp,
1375 : at_fname,
1376 : 0);
1377 0 : if (retval != 0) {
1378 0 : goto out;
1379 : }
1380 : }
1381 0 : status = SMB_VFS_CREATE_DFS_PATHAT(conn,
1382 : parent_fname->fsp,
1383 : at_fname,
1384 : jucn->referral_list,
1385 : jucn->referral_count);
1386 0 : if (!NT_STATUS_IS_OK(status)) {
1387 0 : DBG_WARNING("SMB_VFS_CREATE_DFS_PATHAT failed "
1388 : "%s - Error: %s\n",
1389 : path,
1390 : nt_errstr(status));
1391 0 : goto out;
1392 : }
1393 : }
1394 :
1395 0 : ret = true;
1396 :
1397 0 : out:
1398 0 : TALLOC_FREE(frame);
1399 0 : return ret;
1400 : }
1401 :
1402 0 : bool remove_msdfs_link(const struct junction_map *jucn,
1403 : struct auth_session_info *session_info)
1404 : {
1405 0 : TALLOC_CTX *frame = talloc_stackframe();
1406 0 : char *path = NULL;
1407 0 : connection_struct *conn;
1408 0 : bool ret = False;
1409 0 : struct smb_filename *smb_fname;
1410 0 : struct smb_filename *parent_fname = NULL;
1411 0 : struct smb_filename *at_fname = NULL;
1412 0 : NTSTATUS status;
1413 0 : bool ok;
1414 0 : int retval;
1415 :
1416 0 : ok = junction_to_local_path_tos(jucn, session_info, &path, &conn);
1417 0 : if (!ok) {
1418 0 : TALLOC_FREE(frame);
1419 0 : return false;
1420 : }
1421 :
1422 0 : if (!CAN_WRITE(conn)) {
1423 0 : const struct loadparm_substitution *lp_sub =
1424 0 : loadparm_s3_global_substitution();
1425 0 : int snum = lp_servicenumber(jucn->service_name);
1426 :
1427 0 : DBG_WARNING("Can't remove DFS entry on read-only share %s\n",
1428 : lp_servicename(frame, lp_sub, snum));
1429 0 : TALLOC_FREE(frame);
1430 0 : return false;
1431 : }
1432 :
1433 0 : smb_fname = synthetic_smb_fname(frame,
1434 : path,
1435 : NULL,
1436 : NULL,
1437 : 0,
1438 : 0);
1439 0 : if (smb_fname == NULL) {
1440 0 : TALLOC_FREE(frame);
1441 0 : errno = ENOMEM;
1442 0 : return false;
1443 : }
1444 :
1445 0 : status = parent_pathref(frame,
1446 0 : conn->cwd_fsp,
1447 : smb_fname,
1448 : &parent_fname,
1449 : &at_fname);
1450 0 : if (!NT_STATUS_IS_OK(status)) {
1451 0 : TALLOC_FREE(frame);
1452 0 : return false;
1453 : }
1454 :
1455 0 : retval = SMB_VFS_UNLINKAT(conn,
1456 : parent_fname->fsp,
1457 : at_fname,
1458 : 0);
1459 0 : if (retval == 0) {
1460 0 : ret = True;
1461 : }
1462 :
1463 0 : TALLOC_FREE(frame);
1464 0 : return ret;
1465 : }
1466 :
1467 : /*********************************************************************
1468 : Return the number of DFS links at the root of this share.
1469 : *********************************************************************/
1470 :
1471 0 : static size_t count_dfs_links(TALLOC_CTX *ctx,
1472 : struct auth_session_info *session_info,
1473 : int snum)
1474 : {
1475 0 : TALLOC_CTX *frame = talloc_stackframe();
1476 0 : const struct loadparm_substitution *lp_sub =
1477 0 : loadparm_s3_global_substitution();
1478 0 : size_t cnt = 0;
1479 0 : const char *dname = NULL;
1480 0 : char *talloced = NULL;
1481 0 : const char *connect_path = lp_path(frame, lp_sub, snum);
1482 0 : const char *msdfs_proxy = lp_msdfs_proxy(frame, lp_sub, snum);
1483 0 : struct conn_struct_tos *c = NULL;
1484 0 : connection_struct *conn = NULL;
1485 0 : NTSTATUS status;
1486 0 : struct smb_filename *smb_fname = NULL;
1487 0 : struct smb_Dir *dir_hnd = NULL;
1488 :
1489 0 : if(*connect_path == '\0') {
1490 0 : TALLOC_FREE(frame);
1491 0 : return 0;
1492 : }
1493 :
1494 : /*
1495 : * Fake up a connection struct for the VFS layer.
1496 : */
1497 :
1498 0 : status = create_conn_struct_tos_cwd(global_messaging_context(),
1499 : snum,
1500 : connect_path,
1501 : session_info,
1502 : &c);
1503 0 : if (!NT_STATUS_IS_OK(status)) {
1504 0 : DEBUG(3, ("create_conn_struct failed: %s\n",
1505 : nt_errstr(status)));
1506 0 : TALLOC_FREE(frame);
1507 0 : return 0;
1508 : }
1509 0 : conn = c->conn;
1510 :
1511 : /* Count a link for the msdfs root - convention */
1512 0 : cnt = 1;
1513 :
1514 : /* No more links if this is an msdfs proxy. */
1515 0 : if (*msdfs_proxy != '\0') {
1516 0 : goto out;
1517 : }
1518 :
1519 0 : smb_fname = synthetic_smb_fname(frame,
1520 : ".",
1521 : NULL,
1522 : NULL,
1523 : 0,
1524 : 0);
1525 0 : if (smb_fname == NULL) {
1526 0 : goto out;
1527 : }
1528 :
1529 : /* Now enumerate all dfs links */
1530 0 : status = OpenDir(frame,
1531 : conn,
1532 : smb_fname,
1533 : NULL,
1534 : 0,
1535 : &dir_hnd);
1536 0 : if (!NT_STATUS_IS_OK(status)) {
1537 0 : errno = map_errno_from_nt_status(status);
1538 0 : goto out;
1539 : }
1540 :
1541 0 : while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
1542 0 : struct smb_filename *smb_dname =
1543 0 : synthetic_smb_fname(frame,
1544 : dname,
1545 : NULL,
1546 : NULL,
1547 : 0,
1548 : 0);
1549 0 : if (smb_dname == NULL) {
1550 0 : goto out;
1551 : }
1552 0 : if (is_msdfs_link(dir_hnd_fetch_fsp(dir_hnd), smb_dname)) {
1553 0 : if (cnt + 1 < cnt) {
1554 0 : cnt = 0;
1555 0 : goto out;
1556 : }
1557 0 : cnt++;
1558 : }
1559 0 : TALLOC_FREE(talloced);
1560 0 : TALLOC_FREE(smb_dname);
1561 : }
1562 :
1563 0 : out:
1564 0 : TALLOC_FREE(frame);
1565 0 : return cnt;
1566 : }
1567 :
1568 : /*********************************************************************
1569 : *********************************************************************/
1570 :
1571 0 : static int form_junctions(TALLOC_CTX *ctx,
1572 : struct auth_session_info *session_info,
1573 : int snum,
1574 : struct junction_map *jucn,
1575 : size_t jn_remain)
1576 : {
1577 0 : TALLOC_CTX *frame = talloc_stackframe();
1578 0 : const struct loadparm_substitution *lp_sub =
1579 0 : loadparm_s3_global_substitution();
1580 0 : size_t cnt = 0;
1581 0 : const char *dname = NULL;
1582 0 : char *talloced = NULL;
1583 0 : const char *connect_path = lp_path(frame, lp_sub, snum);
1584 0 : char *service_name = lp_servicename(frame, lp_sub, snum);
1585 0 : const char *msdfs_proxy = lp_msdfs_proxy(frame, lp_sub, snum);
1586 0 : struct conn_struct_tos *c = NULL;
1587 0 : connection_struct *conn = NULL;
1588 0 : struct referral *ref = NULL;
1589 0 : struct smb_filename *smb_fname = NULL;
1590 0 : struct smb_Dir *dir_hnd = NULL;
1591 0 : NTSTATUS status;
1592 :
1593 0 : if (jn_remain == 0) {
1594 0 : TALLOC_FREE(frame);
1595 0 : return 0;
1596 : }
1597 :
1598 0 : if(*connect_path == '\0') {
1599 0 : TALLOC_FREE(frame);
1600 0 : return 0;
1601 : }
1602 :
1603 : /*
1604 : * Fake up a connection struct for the VFS layer.
1605 : */
1606 :
1607 0 : status = create_conn_struct_tos_cwd(global_messaging_context(),
1608 : snum,
1609 : connect_path,
1610 : session_info,
1611 : &c);
1612 0 : if (!NT_STATUS_IS_OK(status)) {
1613 0 : DEBUG(3, ("create_conn_struct failed: %s\n",
1614 : nt_errstr(status)));
1615 0 : TALLOC_FREE(frame);
1616 0 : return 0;
1617 : }
1618 0 : conn = c->conn;
1619 :
1620 : /* form a junction for the msdfs root - convention
1621 : DO NOT REMOVE THIS: NT clients will not work with us
1622 : if this is not present
1623 : */
1624 0 : jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1625 0 : jucn[cnt].volume_name = talloc_strdup(ctx, "");
1626 0 : if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1627 0 : goto out;
1628 : }
1629 0 : jucn[cnt].comment = "";
1630 0 : jucn[cnt].referral_count = 1;
1631 :
1632 0 : ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1633 0 : if (jucn[cnt].referral_list == NULL) {
1634 0 : goto out;
1635 : }
1636 :
1637 0 : ref->proximity = 0;
1638 0 : ref->ttl = REFERRAL_TTL;
1639 0 : if (*msdfs_proxy != '\0') {
1640 0 : ref->alternate_path = talloc_strdup(ctx,
1641 : msdfs_proxy);
1642 : } else {
1643 0 : ref->alternate_path = talloc_asprintf(ctx,
1644 : "\\\\%s\\%s",
1645 : get_local_machine_name(),
1646 : service_name);
1647 : }
1648 :
1649 0 : if (!ref->alternate_path) {
1650 0 : goto out;
1651 : }
1652 0 : cnt++;
1653 :
1654 : /* Don't enumerate if we're an msdfs proxy. */
1655 0 : if (*msdfs_proxy != '\0') {
1656 0 : goto out;
1657 : }
1658 :
1659 0 : smb_fname = synthetic_smb_fname(frame,
1660 : ".",
1661 : NULL,
1662 : NULL,
1663 : 0,
1664 : 0);
1665 0 : if (smb_fname == NULL) {
1666 0 : goto out;
1667 : }
1668 :
1669 : /* Now enumerate all dfs links */
1670 0 : status = OpenDir(frame,
1671 : conn,
1672 : smb_fname,
1673 : NULL,
1674 : 0,
1675 : &dir_hnd);
1676 0 : if (!NT_STATUS_IS_OK(status)) {
1677 0 : errno = map_errno_from_nt_status(status);
1678 0 : goto out;
1679 : }
1680 :
1681 0 : while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
1682 0 : struct smb_filename *smb_dname = NULL;
1683 :
1684 0 : if (cnt >= jn_remain) {
1685 0 : DEBUG(2, ("form_junctions: ran out of MSDFS "
1686 : "junction slots\n"));
1687 0 : TALLOC_FREE(talloced);
1688 0 : goto out;
1689 : }
1690 0 : smb_dname = synthetic_smb_fname(talloc_tos(),
1691 : dname,
1692 : NULL,
1693 : NULL,
1694 : 0,
1695 : 0);
1696 0 : if (smb_dname == NULL) {
1697 0 : TALLOC_FREE(talloced);
1698 0 : goto out;
1699 : }
1700 :
1701 0 : status = SMB_VFS_READ_DFS_PATHAT(conn,
1702 : ctx,
1703 : conn->cwd_fsp,
1704 : smb_dname,
1705 : &jucn[cnt].referral_list,
1706 : &jucn[cnt].referral_count);
1707 :
1708 0 : if (NT_STATUS_IS_OK(status)) {
1709 0 : jucn[cnt].service_name = talloc_strdup(ctx,
1710 : service_name);
1711 0 : jucn[cnt].volume_name = talloc_strdup(ctx, dname);
1712 0 : if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1713 0 : TALLOC_FREE(talloced);
1714 0 : goto out;
1715 : }
1716 0 : jucn[cnt].comment = "";
1717 0 : cnt++;
1718 : }
1719 0 : TALLOC_FREE(talloced);
1720 0 : TALLOC_FREE(smb_dname);
1721 : }
1722 :
1723 0 : out:
1724 0 : TALLOC_FREE(frame);
1725 0 : return cnt;
1726 : }
1727 :
1728 0 : struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx,
1729 : struct auth_session_info *session_info,
1730 : size_t *p_num_jn)
1731 : {
1732 0 : struct junction_map *jn = NULL;
1733 0 : int i=0;
1734 0 : size_t jn_count = 0;
1735 0 : int sharecount = 0;
1736 :
1737 0 : *p_num_jn = 0;
1738 0 : if(!lp_host_msdfs()) {
1739 0 : return NULL;
1740 : }
1741 :
1742 : /* Ensure all the usershares are loaded. */
1743 0 : become_root();
1744 0 : load_registry_shares();
1745 0 : sharecount = load_usershare_shares(NULL, connections_snum_used);
1746 0 : unbecome_root();
1747 :
1748 0 : for(i=0;i < sharecount;i++) {
1749 0 : if(lp_msdfs_root(i)) {
1750 0 : jn_count += count_dfs_links(ctx, session_info, i);
1751 : }
1752 : }
1753 0 : if (jn_count == 0) {
1754 0 : return NULL;
1755 : }
1756 0 : jn = talloc_array(ctx, struct junction_map, jn_count);
1757 0 : if (!jn) {
1758 0 : return NULL;
1759 : }
1760 0 : for(i=0; i < sharecount; i++) {
1761 0 : if (*p_num_jn >= jn_count) {
1762 0 : break;
1763 : }
1764 0 : if(lp_msdfs_root(i)) {
1765 0 : *p_num_jn += form_junctions(ctx,
1766 : session_info,
1767 : i,
1768 0 : &jn[*p_num_jn],
1769 0 : jn_count - *p_num_jn);
1770 : }
1771 : }
1772 0 : return jn;
1773 : }
|