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