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