Fix bug #10097 - MacOSX 10.9 will not follow path-based DFS referrals handed out...
[Samba.git] / source3 / smbd / msdfs.c
blobccbd89cb00f1f51cb588f98dacc5dad3a1accd97
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
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
25 #include "system/filesys.h"
26 #include "smbd/smbd.h"
27 #include "smbd/globals.h"
28 #include "msdfs.h"
29 #include "auth.h"
30 #include "lib/param/loadparm.h"
31 #include "libcli/security/security.h"
32 #include "librpc/gen_ndr/ndr_dfsblobs.h"
34 /**********************************************************************
35 Parse a DFS pathname of the form \hostname\service\reqpath
36 into the dfs_path structure.
37 If POSIX pathnames is true, the pathname may also be of the
38 form /hostname/service/reqpath.
39 We cope with either here.
41 Unfortunately, due to broken clients who might set the
42 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
43 send a local path, we have to cope with that too....
45 If conn != NULL then ensure the provided service is
46 the one pointed to by the connection.
48 This version does everything using pointers within one copy of the
49 pathname string, talloced on the struct dfs_path pointer (which
50 must be talloced). This may be too clever to live....
51 JRA.
52 **********************************************************************/
54 static NTSTATUS parse_dfs_path(connection_struct *conn,
55 const char *pathname,
56 bool allow_wcards,
57 bool allow_broken_path,
58 struct dfs_path *pdp, /* MUST BE TALLOCED */
59 bool *ppath_contains_wcard)
61 char *pathname_local;
62 char *p,*temp;
63 char *servicename;
64 char *eos_ptr;
65 NTSTATUS status = NT_STATUS_OK;
66 char sepchar;
68 ZERO_STRUCTP(pdp);
71 * This is the only talloc we should need to do
72 * on the struct dfs_path. All the pointers inside
73 * it should point to offsets within this string.
76 pathname_local = talloc_strdup(pdp, pathname);
77 if (!pathname_local) {
78 return NT_STATUS_NO_MEMORY;
80 /* Get a pointer to the terminating '\0' */
81 eos_ptr = &pathname_local[strlen(pathname_local)];
82 p = temp = pathname_local;
84 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
86 sepchar = pdp->posix_path ? '/' : '\\';
88 if (allow_broken_path && (*pathname != sepchar)) {
89 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
90 pathname, sepchar ));
92 * Possibly client sent a local path by mistake.
93 * Try and convert to a local path.
96 pdp->hostname = eos_ptr; /* "" */
97 pdp->servicename = eos_ptr; /* "" */
99 /* We've got no info about separators. */
100 pdp->posix_path = lp_posix_pathnames();
101 p = temp;
102 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
103 "local path\n",
104 temp));
105 goto local_path;
109 * Safe to use on talloc'ed string as it only shrinks.
110 * It also doesn't affect the eos_ptr.
112 trim_char(temp,sepchar,sepchar);
114 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
115 temp, sepchar));
117 /* Now tokenize. */
118 /* Parse out hostname. */
119 p = strchr_m(temp,sepchar);
120 if(p == NULL) {
121 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
122 temp));
124 * Possibly client sent a local path by mistake.
125 * Try and convert to a local path.
128 pdp->hostname = eos_ptr; /* "" */
129 pdp->servicename = eos_ptr; /* "" */
131 p = temp;
132 DEBUG(10,("parse_dfs_path: trying to convert %s "
133 "to a local path\n",
134 temp));
135 goto local_path;
137 *p = '\0';
138 pdp->hostname = temp;
140 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
142 /* Parse out servicename. */
143 servicename = p+1;
144 p = strchr_m(servicename,sepchar);
145 if (p) {
146 *p = '\0';
149 /* Is this really our servicename ? */
150 if (conn && !( strequal(servicename, lp_servicename(talloc_tos(), SNUM(conn)))
151 || (strequal(servicename, HOMES_NAME)
152 && strequal(lp_servicename(talloc_tos(), SNUM(conn)),
153 get_current_username()) )) ) {
154 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
155 servicename));
158 * Possibly client sent a local path by mistake.
159 * Try and convert to a local path.
162 pdp->hostname = eos_ptr; /* "" */
163 pdp->servicename = eos_ptr; /* "" */
165 /* Repair the path - replace the sepchar's
166 we nulled out */
167 servicename--;
168 *servicename = sepchar;
169 if (p) {
170 *p = sepchar;
173 p = temp;
174 DEBUG(10,("parse_dfs_path: trying to convert %s "
175 "to a local path\n",
176 temp));
177 goto local_path;
180 pdp->servicename = servicename;
182 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
184 if(p == NULL) {
185 /* Client sent self referral \server\share. */
186 pdp->reqpath = eos_ptr; /* "" */
187 return NT_STATUS_OK;
190 p++;
192 local_path:
194 *ppath_contains_wcard = False;
196 pdp->reqpath = p;
198 /* Rest is reqpath. */
199 if (pdp->posix_path) {
200 status = check_path_syntax_posix(pdp->reqpath);
201 } else {
202 if (allow_wcards) {
203 status = check_path_syntax_wcard(pdp->reqpath,
204 ppath_contains_wcard);
205 } else {
206 status = check_path_syntax(pdp->reqpath);
210 if (!NT_STATUS_IS_OK(status)) {
211 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
212 p, nt_errstr(status) ));
213 return status;
216 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
217 return NT_STATUS_OK;
220 /********************************************************
221 Fake up a connection struct for the VFS layer.
222 Note: this performs a vfs connect and CHANGES CWD !!!! JRA.
223 *********************************************************/
225 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
226 struct tevent_context *ev,
227 struct messaging_context *msg,
228 connection_struct **pconn,
229 int snum,
230 const char *path,
231 const struct auth_session_info *session_info,
232 char **poldcwd)
234 connection_struct *conn;
235 char *connpath;
236 char *oldcwd;
237 const char *vfs_user;
239 conn = talloc_zero(ctx, connection_struct);
240 if (conn == NULL) {
241 return NT_STATUS_NO_MEMORY;
244 connpath = talloc_strdup(conn, path);
245 if (!connpath) {
246 TALLOC_FREE(conn);
247 return NT_STATUS_NO_MEMORY;
249 connpath = talloc_string_sub(conn,
250 connpath,
251 "%S",
252 lp_servicename(talloc_tos(), snum));
253 if (!connpath) {
254 TALLOC_FREE(conn);
255 return NT_STATUS_NO_MEMORY;
258 conn->sconn = talloc_zero(conn, struct smbd_server_connection);
259 if (conn->sconn == NULL) {
260 TALLOC_FREE(conn);
261 return NT_STATUS_NO_MEMORY;
264 conn->sconn->ev_ctx = ev;
265 conn->sconn->msg_ctx = msg;
266 conn->sconn->sock = -1;
267 conn->sconn->smb1.echo_handler.trusted_fd = -1;
268 conn->sconn->smb1.echo_handler.socket_lock_fd = -1;
270 /* needed for smbd_vfs_init() */
272 if (!(conn->params = talloc_zero(conn, struct share_params))) {
273 DEBUG(0, ("TALLOC failed\n"));
274 TALLOC_FREE(conn);
275 return NT_STATUS_NO_MEMORY;
278 conn->params->service = snum;
279 conn->cnum = TID_FIELD_INVALID;
281 DLIST_ADD(conn->sconn->connections, conn);
282 conn->sconn->num_connections++;
284 if (session_info != NULL) {
285 conn->session_info = copy_session_info(conn, session_info);
286 if (conn->session_info == NULL) {
287 DEBUG(0, ("copy_serverinfo failed\n"));
288 TALLOC_FREE(conn);
289 return NT_STATUS_NO_MEMORY;
291 vfs_user = conn->session_info->unix_info->unix_name;
292 } else {
293 /* use current authenticated user in absence of session_info */
294 vfs_user = get_current_username();
297 set_conn_connectpath(conn, connpath);
300 * New code to check if there's a share security descripter
301 * added from NT server manager. This is done after the
302 * smb.conf checks are done as we need a uid and token. JRA.
305 if (conn->session_info) {
306 share_access_check(conn->session_info->security_token,
307 lp_servicename(talloc_tos(), snum),
308 MAXIMUM_ALLOWED_ACCESS,
309 &conn->share_access);
311 if ((conn->share_access & FILE_WRITE_DATA) == 0) {
312 if ((conn->share_access & FILE_READ_DATA) == 0) {
313 /* No access, read or write. */
314 DEBUG(0,("create_conn_struct: connection to %s "
315 "denied due to security "
316 "descriptor.\n",
317 lp_servicename(talloc_tos(), snum)));
318 conn_free(conn);
319 return NT_STATUS_ACCESS_DENIED;
320 } else {
321 conn->read_only = true;
324 } else {
325 conn->share_access = 0;
326 conn->read_only = true;
329 if (!smbd_vfs_init(conn)) {
330 NTSTATUS status = map_nt_error_from_unix(errno);
331 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
332 conn_free(conn);
333 return status;
336 /* this must be the first filesystem operation that we do */
337 if (SMB_VFS_CONNECT(conn, lp_servicename(talloc_tos(), snum), vfs_user) < 0) {
338 DEBUG(0,("VFS connect failed!\n"));
339 conn_free(conn);
340 return NT_STATUS_UNSUCCESSFUL;
343 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
346 * Windows seems to insist on doing trans2getdfsreferral() calls on
347 * the IPC$ share as the anonymous user. If we try to chdir as that
348 * user we will fail.... WTF ? JRA.
351 oldcwd = vfs_GetWd(ctx, conn);
352 if (oldcwd == NULL) {
353 NTSTATUS status = map_nt_error_from_unix(errno);
354 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
355 conn_free(conn);
356 return status;
359 if (vfs_ChDir(conn,conn->connectpath) != 0) {
360 NTSTATUS status = map_nt_error_from_unix(errno);
361 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
362 "Error was %s\n",
363 conn->connectpath, strerror(errno) ));
364 conn_free(conn);
365 return status;
368 *pconn = conn;
369 *poldcwd = oldcwd;
371 return NT_STATUS_OK;
374 /**********************************************************************
375 Parse the contents of a symlink to verify if it is an msdfs referral
376 A valid referral is of the form:
378 msdfs:server1\share1,server2\share2
379 msdfs:server1\share1\pathname,server2\share2\pathname
380 msdfs:server1/share1,server2/share2
381 msdfs:server1/share1/pathname,server2/share2/pathname.
383 Note that the alternate paths returned here must be of the canonicalized
384 form:
386 \server\share or
387 \server\share\path\to\file,
389 even in posix path mode. This is because we have no knowledge if the
390 server we're referring to understands posix paths.
391 **********************************************************************/
393 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
394 const char *target,
395 struct referral **preflist,
396 int *refcount)
398 char *temp = NULL;
399 char *prot;
400 char **alt_path = NULL;
401 int count = 0, i;
402 struct referral *reflist;
403 char *saveptr;
405 temp = talloc_strdup(ctx, target);
406 if (!temp) {
407 return False;
409 prot = strtok_r(temp, ":", &saveptr);
410 if (!prot) {
411 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
412 return False;
415 alt_path = talloc_array(ctx, char *, MAX_REFERRAL_COUNT);
416 if (!alt_path) {
417 return False;
420 /* parse out the alternate paths */
421 while((count<MAX_REFERRAL_COUNT) &&
422 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
423 count++;
426 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
428 if (count) {
429 reflist = *preflist = talloc_zero_array(ctx,
430 struct referral, count);
431 if(reflist == NULL) {
432 TALLOC_FREE(alt_path);
433 return False;
435 } else {
436 reflist = *preflist = NULL;
439 for(i=0;i<count;i++) {
440 char *p;
442 /* Canonicalize link target.
443 * Replace all /'s in the path by a \ */
444 string_replace(alt_path[i], '/', '\\');
446 /* Remove leading '\\'s */
447 p = alt_path[i];
448 while (*p && (*p == '\\')) {
449 p++;
452 reflist[i].alternate_path = talloc_asprintf(ctx,
453 "\\%s",
455 if (!reflist[i].alternate_path) {
456 return False;
459 reflist[i].proximity = 0;
460 reflist[i].ttl = REFERRAL_TTL;
461 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
462 reflist[i].alternate_path));
465 *refcount = count;
467 TALLOC_FREE(alt_path);
468 return True;
471 /**********************************************************************
472 Returns true if the unix path is a valid msdfs symlink and also
473 returns the target string from inside the link.
474 **********************************************************************/
476 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
477 connection_struct *conn,
478 const char *path,
479 char **pp_link_target,
480 SMB_STRUCT_STAT *sbufp)
482 int referral_len = 0;
483 #if defined(HAVE_BROKEN_READLINK)
484 char link_target_buf[PATH_MAX];
485 #else
486 char link_target_buf[7];
487 #endif
488 size_t bufsize = 0;
489 char *link_target = NULL;
490 struct smb_filename smb_fname;
492 if (pp_link_target) {
493 bufsize = 1024;
494 link_target = talloc_array(ctx, char, bufsize);
495 if (!link_target) {
496 return False;
498 *pp_link_target = link_target;
499 } else {
500 bufsize = sizeof(link_target_buf);
501 link_target = link_target_buf;
504 ZERO_STRUCT(smb_fname);
505 smb_fname.base_name = discard_const_p(char, path);
507 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
508 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
509 path));
510 goto err;
512 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
513 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
514 path));
515 goto err;
517 if (sbufp != NULL) {
518 *sbufp = smb_fname.st;
521 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
522 if (referral_len == -1) {
523 DEBUG(0,("is_msdfs_link_read_target: Error reading "
524 "msdfs link %s: %s\n",
525 path, strerror(errno)));
526 goto err;
528 link_target[referral_len] = '\0';
530 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
531 link_target));
533 if (!strnequal(link_target, "msdfs:", 6)) {
534 goto err;
536 return True;
538 err:
540 if (link_target != link_target_buf) {
541 TALLOC_FREE(link_target);
543 return False;
546 /**********************************************************************
547 Returns true if the unix path is a valid msdfs symlink.
548 **********************************************************************/
550 bool is_msdfs_link(connection_struct *conn,
551 const char *path,
552 SMB_STRUCT_STAT *sbufp)
554 return is_msdfs_link_internal(talloc_tos(),
555 conn,
556 path,
557 NULL,
558 sbufp);
561 /*****************************************************************
562 Used by other functions to decide if a dfs path is remote,
563 and to get the list of referred locations for that remote path.
565 search_flag: For findfirsts, dfs links themselves are not
566 redirected, but paths beyond the links are. For normal smb calls,
567 even dfs links need to be redirected.
569 consumedcntp: how much of the dfs path is being redirected. the client
570 should try the remaining path on the redirected server.
572 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
573 link redirect are in targetpath.
574 *****************************************************************/
576 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
577 connection_struct *conn,
578 const char *dfspath, /* Incoming complete dfs path */
579 const struct dfs_path *pdp, /* Parsed out
580 server+share+extrapath. */
581 bool search_flag, /* Called from a findfirst ? */
582 int *consumedcntp,
583 char **pp_targetpath)
585 char *p = NULL;
586 char *q = NULL;
587 NTSTATUS status;
588 struct smb_filename *smb_fname = NULL;
589 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
590 components). */
592 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
593 conn->connectpath, pdp->reqpath));
596 * Note the unix path conversion here we're doing we
597 * throw away. We're looking for a symlink for a dfs
598 * resolution, if we don't find it we'll do another
599 * unix_convert later in the codepath.
602 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
603 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
605 if (!NT_STATUS_IS_OK(status)) {
606 if (!NT_STATUS_EQUAL(status,
607 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
608 return status;
610 if (smb_fname == NULL || smb_fname->base_name == NULL) {
611 return status;
615 /* Optimization - check if we can redirect the whole path. */
617 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
618 pp_targetpath, NULL)) {
619 if (search_flag) {
620 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
621 "for dfs link %s.\n", dfspath));
622 status = NT_STATUS_OK;
623 goto out;
626 DEBUG(6,("dfs_path_lookup: %s resolves to a "
627 "valid dfs link %s.\n", dfspath,
628 pp_targetpath ? *pp_targetpath : ""));
630 if (consumedcntp) {
631 *consumedcntp = strlen(dfspath);
633 status = NT_STATUS_PATH_NOT_COVERED;
634 goto out;
637 /* Prepare to test only for '/' components in the given path,
638 * so if a Windows path replace all '\\' characters with '/'.
639 * For a POSIX DFS path we know all separators are already '/'. */
641 canon_dfspath = talloc_strdup(ctx, dfspath);
642 if (!canon_dfspath) {
643 status = NT_STATUS_NO_MEMORY;
644 goto out;
646 if (!pdp->posix_path) {
647 string_replace(canon_dfspath, '\\', '/');
651 * localpath comes out of unix_convert, so it has
652 * no trailing backslash. Make sure that canon_dfspath hasn't either.
653 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
656 trim_char(canon_dfspath,0,'/');
659 * Redirect if any component in the path is a link.
660 * We do this by walking backwards through the
661 * local path, chopping off the last component
662 * in both the local path and the canonicalized
663 * DFS path. If we hit a DFS link then we're done.
666 p = strrchr_m(smb_fname->base_name, '/');
667 if (consumedcntp) {
668 q = strrchr_m(canon_dfspath, '/');
671 while (p) {
672 *p = '\0';
673 if (q) {
674 *q = '\0';
677 if (is_msdfs_link_internal(ctx, conn,
678 smb_fname->base_name, pp_targetpath,
679 NULL)) {
680 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
681 "parent %s is dfs link\n", dfspath,
682 smb_fname_str_dbg(smb_fname)));
684 if (consumedcntp) {
685 *consumedcntp = strlen(canon_dfspath);
686 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
687 "(%d)\n",
688 canon_dfspath,
689 *consumedcntp));
692 status = NT_STATUS_PATH_NOT_COVERED;
693 goto out;
696 /* Step back on the filesystem. */
697 p = strrchr_m(smb_fname->base_name, '/');
699 if (consumedcntp) {
700 /* And in the canonicalized dfs path. */
701 q = strrchr_m(canon_dfspath, '/');
705 status = NT_STATUS_OK;
706 out:
707 TALLOC_FREE(smb_fname);
708 return status;
711 /*****************************************************************
712 Decides if a dfs pathname should be redirected or not.
713 If not, the pathname is converted to a tcon-relative local unix path
715 search_wcard_flag: this flag performs 2 functions both related
716 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
717 for details.
719 This function can return NT_STATUS_OK, meaning use the returned path as-is
720 (mapped into a local path).
721 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
722 any other NT_STATUS error which is a genuine error to be
723 returned to the client.
724 *****************************************************************/
726 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
727 connection_struct *conn,
728 const char *path_in,
729 bool search_wcard_flag,
730 bool allow_broken_path,
731 char **pp_path_out,
732 bool *ppath_contains_wcard)
734 NTSTATUS status;
735 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
737 if (!pdp) {
738 return NT_STATUS_NO_MEMORY;
741 status = parse_dfs_path(conn, path_in, search_wcard_flag,
742 allow_broken_path, pdp,
743 ppath_contains_wcard);
744 if (!NT_STATUS_IS_OK(status)) {
745 TALLOC_FREE(pdp);
746 return status;
749 if (pdp->reqpath[0] == '\0') {
750 TALLOC_FREE(pdp);
751 *pp_path_out = talloc_strdup(ctx, "");
752 if (!*pp_path_out) {
753 return NT_STATUS_NO_MEMORY;
755 DEBUG(5,("dfs_redirect: self-referral.\n"));
756 return NT_STATUS_OK;
759 /* If dfs pathname for a non-dfs share, convert to tcon-relative
760 path and return OK */
762 if (!lp_msdfs_root(SNUM(conn))) {
763 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
764 TALLOC_FREE(pdp);
765 if (!*pp_path_out) {
766 return NT_STATUS_NO_MEMORY;
768 return NT_STATUS_OK;
771 /* If it looked like a local path (zero hostname/servicename)
772 * just treat as a tcon-relative path. */
774 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
775 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
776 TALLOC_FREE(pdp);
777 if (!*pp_path_out) {
778 return NT_STATUS_NO_MEMORY;
780 return NT_STATUS_OK;
783 if (!( strequal(pdp->servicename, lp_servicename(talloc_tos(), SNUM(conn)))
784 || (strequal(pdp->servicename, HOMES_NAME)
785 && strequal(lp_servicename(talloc_tos(), SNUM(conn)),
786 conn->session_info->unix_info->sanitized_username) )) ) {
788 /* The given sharename doesn't match this connection. */
789 TALLOC_FREE(pdp);
791 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
794 status = dfs_path_lookup(ctx, conn, path_in, pdp,
795 search_wcard_flag, NULL, NULL);
796 if (!NT_STATUS_IS_OK(status)) {
797 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
798 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
799 } else {
800 DEBUG(10,("dfs_redirect: dfs_path_lookup "
801 "failed for %s with %s\n",
802 path_in, nt_errstr(status) ));
804 return status;
807 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
809 /* Form non-dfs tcon-relative path */
810 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
811 TALLOC_FREE(pdp);
812 if (!*pp_path_out) {
813 return NT_STATUS_NO_MEMORY;
816 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
817 path_in,
818 *pp_path_out));
820 return NT_STATUS_OK;
823 /**********************************************************************
824 Return a self referral.
825 **********************************************************************/
827 static NTSTATUS self_ref(TALLOC_CTX *ctx,
828 const char *dfs_path,
829 struct junction_map *jucn,
830 int *consumedcntp,
831 bool *self_referralp)
833 struct referral *ref;
835 *self_referralp = True;
837 jucn->referral_count = 1;
838 if((ref = talloc_zero(ctx, struct referral)) == NULL) {
839 return NT_STATUS_NO_MEMORY;
842 ref->alternate_path = talloc_strdup(ctx, dfs_path);
843 if (!ref->alternate_path) {
844 TALLOC_FREE(ref);
845 return NT_STATUS_NO_MEMORY;
847 ref->proximity = 0;
848 ref->ttl = REFERRAL_TTL;
849 jucn->referral_list = ref;
850 *consumedcntp = strlen(dfs_path);
851 return NT_STATUS_OK;
854 /**********************************************************************
855 Gets valid referrals for a dfs path and fills up the
856 junction_map structure.
857 **********************************************************************/
859 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
860 const char *dfs_path,
861 bool allow_broken_path,
862 struct junction_map *jucn,
863 int *consumedcntp,
864 bool *self_referralp)
866 struct connection_struct *conn;
867 char *targetpath = NULL;
868 int snum;
869 NTSTATUS status = NT_STATUS_NOT_FOUND;
870 bool dummy;
871 struct dfs_path *pdp = talloc(ctx, struct dfs_path);
872 char *oldpath;
874 if (!pdp) {
875 return NT_STATUS_NO_MEMORY;
878 *self_referralp = False;
880 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
881 pdp, &dummy);
882 if (!NT_STATUS_IS_OK(status)) {
883 return status;
886 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
887 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
888 if (!jucn->service_name || !jucn->volume_name) {
889 TALLOC_FREE(pdp);
890 return NT_STATUS_NO_MEMORY;
893 /* Verify the share is a dfs root */
894 snum = lp_servicenumber(jucn->service_name);
895 if(snum < 0) {
896 char *service_name = NULL;
897 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
898 return NT_STATUS_NOT_FOUND;
900 if (!service_name) {
901 return NT_STATUS_NO_MEMORY;
903 TALLOC_FREE(jucn->service_name);
904 jucn->service_name = talloc_strdup(ctx, service_name);
905 if (!jucn->service_name) {
906 TALLOC_FREE(pdp);
907 return NT_STATUS_NO_MEMORY;
911 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(talloc_tos(), snum) == '\0')) {
912 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
913 "a dfs root.\n",
914 pdp->servicename, dfs_path));
915 TALLOC_FREE(pdp);
916 return NT_STATUS_NOT_FOUND;
920 * Self referrals are tested with a anonymous IPC connection and
921 * a GET_DFS_REFERRAL call to \\server\share. (which means
922 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
923 * into the directory and will fail if it cannot (as the anonymous
924 * user). Cope with this.
927 if (pdp->reqpath[0] == '\0') {
928 char *tmp;
929 struct referral *ref;
931 if (*lp_msdfs_proxy(talloc_tos(), snum) == '\0') {
932 TALLOC_FREE(pdp);
933 return self_ref(ctx,
934 dfs_path,
935 jucn,
936 consumedcntp,
937 self_referralp);
941 * It's an msdfs proxy share. Redirect to
942 * the configured target share.
945 jucn->referral_count = 1;
946 if ((ref = talloc_zero(ctx, struct referral)) == NULL) {
947 TALLOC_FREE(pdp);
948 return NT_STATUS_NO_MEMORY;
951 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(talloc_tos(), snum)))) {
952 TALLOC_FREE(pdp);
953 return NT_STATUS_NO_MEMORY;
956 trim_string(tmp, "\\", 0);
958 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
959 TALLOC_FREE(tmp);
961 if (!ref->alternate_path) {
962 TALLOC_FREE(pdp);
963 return NT_STATUS_NO_MEMORY;
966 if (pdp->reqpath[0] != '\0') {
967 ref->alternate_path = talloc_asprintf_append(
968 ref->alternate_path,
969 "%s",
970 pdp->reqpath);
971 if (!ref->alternate_path) {
972 TALLOC_FREE(pdp);
973 return NT_STATUS_NO_MEMORY;
976 ref->proximity = 0;
977 ref->ttl = REFERRAL_TTL;
978 jucn->referral_list = ref;
979 *consumedcntp = strlen(dfs_path);
980 TALLOC_FREE(pdp);
981 return NT_STATUS_OK;
984 status = create_conn_struct(ctx,
985 server_event_context(),
986 server_messaging_context(),
987 &conn, snum,
988 lp_pathname(talloc_tos(), snum), NULL, &oldpath);
989 if (!NT_STATUS_IS_OK(status)) {
990 TALLOC_FREE(pdp);
991 return status;
994 /* If this is a DFS path dfs_lookup should return
995 * NT_STATUS_PATH_NOT_COVERED. */
997 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
998 False, consumedcntp, &targetpath);
1000 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
1001 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
1002 dfs_path));
1003 if (NT_STATUS_IS_OK(status)) {
1005 * We are in an error path here (we
1006 * know it's not a DFS path), but
1007 * dfs_path_lookup() can return
1008 * NT_STATUS_OK. Ensure we always
1009 * return a valid error code.
1011 * #9588 - ACLs are not inherited to directories
1012 * for DFS shares.
1014 status = NT_STATUS_NOT_FOUND;
1016 goto err_exit;
1019 /* We know this is a valid dfs link. Parse the targetpath. */
1020 if (!parse_msdfs_symlink(ctx, targetpath,
1021 &jucn->referral_list,
1022 &jucn->referral_count)) {
1023 DEBUG(3,("get_referred_path: failed to parse symlink "
1024 "target %s\n", targetpath ));
1025 status = NT_STATUS_NOT_FOUND;
1026 goto err_exit;
1029 status = NT_STATUS_OK;
1030 err_exit:
1031 vfs_ChDir(conn, oldpath);
1032 SMB_VFS_DISCONNECT(conn);
1033 conn_free(conn);
1034 TALLOC_FREE(pdp);
1035 return status;
1038 /******************************************************************
1039 Set up the DFS referral for the dfs pathname. This call returns
1040 the amount of the path covered by this server, and where the
1041 client should be redirected to. This is the meat of the
1042 TRANS2_GET_DFS_REFERRAL call.
1043 ******************************************************************/
1045 int setup_dfs_referral(connection_struct *orig_conn,
1046 const char *dfs_path,
1047 int max_referral_level,
1048 char **ppdata, NTSTATUS *pstatus)
1050 char *pdata = *ppdata;
1051 int reply_size = 0;
1052 struct dfs_GetDFSReferral *r;
1053 DATA_BLOB blob = data_blob_null;
1054 NTSTATUS status;
1055 enum ndr_err_code ndr_err;
1057 r = talloc_zero(talloc_tos(), struct dfs_GetDFSReferral);
1058 if (r == NULL) {
1059 *pstatus = NT_STATUS_NO_MEMORY;
1060 return -1;
1063 r->in.req.max_referral_level = max_referral_level;
1064 r->in.req.servername = talloc_strdup(r, dfs_path);
1065 if (r->in.req.servername == NULL) {
1066 talloc_free(r);
1067 *pstatus = NT_STATUS_NO_MEMORY;
1068 return -1;
1071 status = SMB_VFS_GET_DFS_REFERRALS(orig_conn, r);
1072 if (!NT_STATUS_IS_OK(status)) {
1073 talloc_free(r);
1074 *pstatus = status;
1075 return -1;
1078 ndr_err = ndr_push_struct_blob(&blob, r,
1079 r->out.resp,
1080 (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
1081 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1082 TALLOC_FREE(r);
1083 *pstatus = NT_STATUS_INVALID_PARAMETER;
1084 return -1;
1087 pdata = (char *)SMB_REALLOC(pdata, blob.length);
1088 if(pdata == NULL) {
1089 TALLOC_FREE(r);
1090 DEBUG(0,("referral setup:"
1091 "malloc failed for Realloc!\n"));
1092 return -1;
1094 *ppdata = pdata;
1095 reply_size = blob.length;
1096 memcpy(pdata, blob.data, blob.length);
1097 TALLOC_FREE(r);
1099 *pstatus = NT_STATUS_OK;
1100 return reply_size;
1103 /**********************************************************************
1104 The following functions are called by the NETDFS RPC pipe functions
1105 **********************************************************************/
1107 /*********************************************************************
1108 Creates a junction structure from a DFS pathname
1109 **********************************************************************/
1111 bool create_junction(TALLOC_CTX *ctx,
1112 const char *dfs_path,
1113 bool allow_broken_path,
1114 struct junction_map *jucn)
1116 int snum;
1117 bool dummy;
1118 struct dfs_path *pdp = talloc(ctx,struct dfs_path);
1119 NTSTATUS status;
1121 if (!pdp) {
1122 return False;
1124 status = parse_dfs_path(NULL, dfs_path, False, allow_broken_path,
1125 pdp, &dummy);
1126 if (!NT_STATUS_IS_OK(status)) {
1127 return False;
1130 /* check if path is dfs : validate first token */
1131 if (!is_myname_or_ipaddr(pdp->hostname)) {
1132 DEBUG(4,("create_junction: Invalid hostname %s "
1133 "in dfs path %s\n",
1134 pdp->hostname, dfs_path));
1135 TALLOC_FREE(pdp);
1136 return False;
1139 /* Check for a non-DFS share */
1140 snum = lp_servicenumber(pdp->servicename);
1142 if(snum < 0 || !lp_msdfs_root(snum)) {
1143 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1144 pdp->servicename));
1145 TALLOC_FREE(pdp);
1146 return False;
1149 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1150 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1151 jucn->comment = lp_comment(ctx, snum);
1153 TALLOC_FREE(pdp);
1154 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1155 return False;
1157 return True;
1160 /**********************************************************************
1161 Forms a valid Unix pathname from the junction
1162 **********************************************************************/
1164 static bool junction_to_local_path(const struct junction_map *jucn,
1165 char **pp_path_out,
1166 connection_struct **conn_out,
1167 char **oldpath)
1169 int snum;
1170 NTSTATUS status;
1172 snum = lp_servicenumber(jucn->service_name);
1173 if(snum < 0) {
1174 return False;
1176 status = create_conn_struct(talloc_tos(),
1177 server_event_context(),
1178 server_messaging_context(),
1179 conn_out,
1180 snum, lp_pathname(talloc_tos(), snum), NULL, oldpath);
1181 if (!NT_STATUS_IS_OK(status)) {
1182 return False;
1185 *pp_path_out = talloc_asprintf(*conn_out,
1186 "%s/%s",
1187 lp_pathname(talloc_tos(), snum),
1188 jucn->volume_name);
1189 if (!*pp_path_out) {
1190 vfs_ChDir(*conn_out, *oldpath);
1191 SMB_VFS_DISCONNECT(*conn_out);
1192 conn_free(*conn_out);
1193 return False;
1195 return True;
1198 bool create_msdfs_link(const struct junction_map *jucn)
1200 char *path = NULL;
1201 char *cwd;
1202 char *msdfs_link = NULL;
1203 connection_struct *conn;
1204 int i=0;
1205 bool insert_comma = False;
1206 bool ret = False;
1208 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1209 return False;
1212 /* Form the msdfs_link contents */
1213 msdfs_link = talloc_strdup(conn, "msdfs:");
1214 if (!msdfs_link) {
1215 goto out;
1217 for(i=0; i<jucn->referral_count; i++) {
1218 char *refpath = jucn->referral_list[i].alternate_path;
1220 /* Alternate paths always use Windows separators. */
1221 trim_char(refpath, '\\', '\\');
1222 if(*refpath == '\0') {
1223 if (i == 0) {
1224 insert_comma = False;
1226 continue;
1228 if (i > 0 && insert_comma) {
1229 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1230 ",%s",
1231 refpath);
1232 } else {
1233 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1234 "%s",
1235 refpath);
1238 if (!msdfs_link) {
1239 goto out;
1241 if (!insert_comma) {
1242 insert_comma = True;
1246 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1247 path, msdfs_link));
1249 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1250 if (errno == EEXIST) {
1251 struct smb_filename *smb_fname = NULL;
1252 NTSTATUS status;
1254 status = create_synthetic_smb_fname(talloc_tos(), path,
1255 NULL, NULL,
1256 &smb_fname);
1257 if (!NT_STATUS_IS_OK(status)) {
1258 errno = map_errno_from_nt_status(status);
1259 goto out;
1262 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1263 TALLOC_FREE(smb_fname);
1264 goto out;
1266 TALLOC_FREE(smb_fname);
1268 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1269 DEBUG(1,("create_msdfs_link: symlink failed "
1270 "%s -> %s\nError: %s\n",
1271 path, msdfs_link, strerror(errno)));
1272 goto out;
1276 ret = True;
1278 out:
1279 vfs_ChDir(conn, cwd);
1280 SMB_VFS_DISCONNECT(conn);
1281 conn_free(conn);
1282 return ret;
1285 bool remove_msdfs_link(const struct junction_map *jucn)
1287 char *path = NULL;
1288 char *cwd;
1289 connection_struct *conn;
1290 bool ret = False;
1291 struct smb_filename *smb_fname = NULL;
1292 NTSTATUS status;
1294 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1295 return false;
1298 status = create_synthetic_smb_fname(talloc_tos(), path,
1299 NULL, NULL,
1300 &smb_fname);
1301 if (!NT_STATUS_IS_OK(status)) {
1302 errno = map_errno_from_nt_status(status);
1303 return false;
1306 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1307 ret = True;
1310 TALLOC_FREE(smb_fname);
1311 vfs_ChDir(conn, cwd);
1312 SMB_VFS_DISCONNECT(conn);
1313 conn_free(conn);
1314 return ret;
1317 /*********************************************************************
1318 Return the number of DFS links at the root of this share.
1319 *********************************************************************/
1321 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1323 size_t cnt = 0;
1324 DIR *dirp = NULL;
1325 const char *dname = NULL;
1326 char *talloced = NULL;
1327 const char *connect_path = lp_pathname(talloc_tos(), snum);
1328 const char *msdfs_proxy = lp_msdfs_proxy(talloc_tos(), snum);
1329 connection_struct *conn;
1330 NTSTATUS status;
1331 char *cwd;
1333 if(*connect_path == '\0') {
1334 return 0;
1338 * Fake up a connection struct for the VFS layer.
1341 status = create_conn_struct(talloc_tos(),
1342 server_event_context(),
1343 server_messaging_context(),
1344 &conn,
1345 snum, connect_path, NULL, &cwd);
1346 if (!NT_STATUS_IS_OK(status)) {
1347 DEBUG(3, ("create_conn_struct failed: %s\n",
1348 nt_errstr(status)));
1349 return 0;
1352 /* Count a link for the msdfs root - convention */
1353 cnt = 1;
1355 /* No more links if this is an msdfs proxy. */
1356 if (*msdfs_proxy != '\0') {
1357 goto out;
1360 /* Now enumerate all dfs links */
1361 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1362 if(!dirp) {
1363 goto out;
1366 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1367 != NULL) {
1368 if (is_msdfs_link(conn,
1369 dname,
1370 NULL)) {
1371 cnt++;
1373 TALLOC_FREE(talloced);
1376 SMB_VFS_CLOSEDIR(conn,dirp);
1378 out:
1379 vfs_ChDir(conn, cwd);
1380 SMB_VFS_DISCONNECT(conn);
1381 conn_free(conn);
1382 return cnt;
1385 /*********************************************************************
1386 *********************************************************************/
1388 static int form_junctions(TALLOC_CTX *ctx,
1389 int snum,
1390 struct junction_map *jucn,
1391 size_t jn_remain)
1393 size_t cnt = 0;
1394 DIR *dirp = NULL;
1395 const char *dname = NULL;
1396 char *talloced = NULL;
1397 const char *connect_path = lp_pathname(talloc_tos(), snum);
1398 char *service_name = lp_servicename(talloc_tos(), snum);
1399 const char *msdfs_proxy = lp_msdfs_proxy(talloc_tos(), snum);
1400 connection_struct *conn;
1401 struct referral *ref = NULL;
1402 char *cwd;
1403 NTSTATUS status;
1405 if (jn_remain == 0) {
1406 return 0;
1409 if(*connect_path == '\0') {
1410 return 0;
1414 * Fake up a connection struct for the VFS layer.
1417 status = create_conn_struct(ctx,
1418 server_event_context(),
1419 server_messaging_context(),
1420 &conn, snum, connect_path, NULL,
1421 &cwd);
1422 if (!NT_STATUS_IS_OK(status)) {
1423 DEBUG(3, ("create_conn_struct failed: %s\n",
1424 nt_errstr(status)));
1425 return 0;
1428 /* form a junction for the msdfs root - convention
1429 DO NOT REMOVE THIS: NT clients will not work with us
1430 if this is not present
1432 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1433 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1434 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1435 goto out;
1437 jucn[cnt].comment = "";
1438 jucn[cnt].referral_count = 1;
1440 ref = jucn[cnt].referral_list = talloc_zero(ctx, struct referral);
1441 if (jucn[cnt].referral_list == NULL) {
1442 goto out;
1445 ref->proximity = 0;
1446 ref->ttl = REFERRAL_TTL;
1447 if (*msdfs_proxy != '\0') {
1448 ref->alternate_path = talloc_strdup(ctx,
1449 msdfs_proxy);
1450 } else {
1451 ref->alternate_path = talloc_asprintf(ctx,
1452 "\\\\%s\\%s",
1453 get_local_machine_name(),
1454 service_name);
1457 if (!ref->alternate_path) {
1458 goto out;
1460 cnt++;
1462 /* Don't enumerate if we're an msdfs proxy. */
1463 if (*msdfs_proxy != '\0') {
1464 goto out;
1467 /* Now enumerate all dfs links */
1468 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1469 if(!dirp) {
1470 goto out;
1473 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1474 != NULL) {
1475 char *link_target = NULL;
1476 if (cnt >= jn_remain) {
1477 DEBUG(2, ("form_junctions: ran out of MSDFS "
1478 "junction slots"));
1479 TALLOC_FREE(talloced);
1480 goto out;
1482 if (is_msdfs_link_internal(ctx,
1483 conn,
1484 dname, &link_target,
1485 NULL)) {
1486 if (parse_msdfs_symlink(ctx,
1487 link_target,
1488 &jucn[cnt].referral_list,
1489 &jucn[cnt].referral_count)) {
1491 jucn[cnt].service_name = talloc_strdup(ctx,
1492 service_name);
1493 jucn[cnt].volume_name = talloc_strdup(ctx,
1494 dname);
1495 if (!jucn[cnt].service_name ||
1496 !jucn[cnt].volume_name) {
1497 TALLOC_FREE(talloced);
1498 goto out;
1500 jucn[cnt].comment = "";
1501 cnt++;
1503 TALLOC_FREE(link_target);
1505 TALLOC_FREE(talloced);
1508 out:
1510 if (dirp) {
1511 SMB_VFS_CLOSEDIR(conn,dirp);
1514 vfs_ChDir(conn, cwd);
1515 conn_free(conn);
1516 return cnt;
1519 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1521 struct junction_map *jn = NULL;
1522 int i=0;
1523 size_t jn_count = 0;
1524 int sharecount = 0;
1526 *p_num_jn = 0;
1527 if(!lp_host_msdfs()) {
1528 return NULL;
1531 /* Ensure all the usershares are loaded. */
1532 become_root();
1533 load_registry_shares();
1534 sharecount = load_usershare_shares(NULL, connections_snum_used);
1535 unbecome_root();
1537 for(i=0;i < sharecount;i++) {
1538 if(lp_msdfs_root(i)) {
1539 jn_count += count_dfs_links(ctx, i);
1542 if (jn_count == 0) {
1543 return NULL;
1545 jn = talloc_array(ctx, struct junction_map, jn_count);
1546 if (!jn) {
1547 return NULL;
1549 for(i=0; i < sharecount; i++) {
1550 if (*p_num_jn >= jn_count) {
1551 break;
1553 if(lp_msdfs_root(i)) {
1554 *p_num_jn += form_junctions(ctx, i,
1555 &jn[*p_num_jn],
1556 jn_count - *p_num_jn);
1559 return jn;
1562 /******************************************************************************
1563 Core function to resolve a dfs pathname possibly containing a wildcard. If
1564 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1565 detected during dfs resolution.
1566 ******************************************************************************/
1568 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1569 connection_struct *conn,
1570 bool dfs_pathnames,
1571 const char *name_in,
1572 bool allow_wcards,
1573 bool allow_broken_path,
1574 char **pp_name_out,
1575 bool *ppath_contains_wcard)
1577 bool path_contains_wcard;
1578 NTSTATUS status = NT_STATUS_OK;
1580 if (dfs_pathnames) {
1581 status = dfs_redirect(ctx,
1582 conn,
1583 name_in,
1584 allow_wcards,
1585 allow_broken_path,
1586 pp_name_out,
1587 &path_contains_wcard);
1589 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1590 *ppath_contains_wcard = path_contains_wcard;
1592 } else {
1594 * Cheat and just return a copy of the in ptr.
1595 * Once srvstr_get_path() uses talloc it'll
1596 * be a talloced ptr anyway.
1598 *pp_name_out = discard_const_p(char, name_in);
1600 return status;