From a398754c9bb1639f762979765de6c540c714b5cb Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Mon, 20 Mar 2017 11:32:19 -0700
Subject: [PATCH 01/15] CVE-2017-2619: s3/smbd: re-open directory after
 dptr_CloseDir()

dptr_CloseDir() will close and invalidate the fsp's file descriptor, we
have to reopen it.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=12496

Signed-off-by: Ralph Bohme <slow@samba.org>
Signed-off-by: Jeremy Allison <jra@samba.org>
---
 source3/smbd/open.c      |  2 +-
 source3/smbd/proto.h     |  2 ++
 source3/smbd/smb2_find.c | 17 +++++++++++++++++
 3 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 441b8cd4362..35eee0a1485 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -197,7 +197,7 @@ static NTSTATUS check_base_file_access(struct connection_struct *conn,
  fd support routines - attempt to do a dos_open.
 ****************************************************************************/
 
-static NTSTATUS fd_open(struct connection_struct *conn,
+NTSTATUS fd_open(struct connection_struct *conn,
 		    files_struct *fsp,
 		    int flags,
 		    mode_t mode)
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index f5fad2bbb50..594edfa1e98 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -603,6 +603,8 @@ NTSTATUS smb1_file_se_access_check(connection_struct *conn,
 				const struct security_token *token,
 				uint32_t access_desired,
 				uint32_t *access_granted);
+NTSTATUS fd_open(struct connection_struct *conn, files_struct *fsp,
+		int flags, mode_t mode);
 NTSTATUS fd_close(files_struct *fsp);
 void change_file_owner_to_parent(connection_struct *conn,
 				 const char *inherit_from_dir,
diff --git a/source3/smbd/smb2_find.c b/source3/smbd/smb2_find.c
index 6fe6545c128..9dd3176497b 100644
--- a/source3/smbd/smb2_find.c
+++ b/source3/smbd/smb2_find.c
@@ -24,6 +24,7 @@
 #include "../libcli/smb/smb_common.h"
 #include "trans2.h"
 #include "../lib/util/tevent_ntstatus.h"
+#include "system/filesys.h"
 
 static struct tevent_req *smbd_smb2_find_send(TALLOC_CTX *mem_ctx,
 					      struct tevent_context *ev,
@@ -301,7 +302,23 @@ static struct tevent_req *smbd_smb2_find_send(TALLOC_CTX *mem_ctx,
 	}
 
 	if (in_flags & SMB2_CONTINUE_FLAG_REOPEN) {
+		int flags;
+
 		dptr_CloseDir(fsp);
+
+		/*
+		 * dptr_CloseDir() will close and invalidate the fsp's file
+		 * descriptor, we have to reopen it.
+		 */
+
+		flags = O_RDONLY;
+#ifdef O_DIRECTORY
+		flags |= O_DIRECTORY;
+#endif
+		status = fd_open(conn, fsp, flags, 0);
+		if (tevent_req_nterror(req, status)) {
+			return tevent_req_post(req, ev);
+		}
 	}
 
 	wcard_has_wild = ms_has_wild(in_file_name);
-- 
2.13.5


From a35fa98b99aa60132eb2c083d6393c28905e2045 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Tue, 28 Feb 2017 09:24:07 -0800
Subject: [PATCH 02/15] s3: vfs: dirsort doesn't handle opendir of "."
 correctly.

Needs to store $cwd path for correct sorting.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12499

Signed-off-by: Jeremy Allison <jra@samba.org>
---
 source3/modules/vfs_dirsort.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/source3/modules/vfs_dirsort.c b/source3/modules/vfs_dirsort.c
index 66582e67890..dbcf0b16ed3 100644
--- a/source3/modules/vfs_dirsort.c
+++ b/source3/modules/vfs_dirsort.c
@@ -153,6 +153,10 @@ static SMB_STRUCT_DIR *dirsort_opendir(vfs_handle_struct *handle,
 		return NULL;
 	}
 
+	if (ISDOT(data->smb_fname->base_name)) {
+		data->smb_fname->base_name = vfs_GetWd(data, handle->conn);
+	}
+
 	/* Open the underlying directory and count the number of entries */
 	data->source_directory = SMB_VFS_NEXT_OPENDIR(handle, fname, mask,
 						      attr);
-- 
2.13.5


From 23d2849d724a0f5bdf51dc7d7db438ed9fb4c2a9 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Mon, 13 Mar 2017 13:44:42 -0700
Subject: [PATCH 03/15] s3: VFS: vfs_streams_xattr.c: Make streams_xattr_open()
 store the same path as streams_xattr_recheck().

If the open is changing directories, fsp->fsp_name->base_name
will be the full path from the share root, whilst
smb_fname will be relative to the $cwd.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12546

Back-ported from a24ba3e4083200ec9885363efc5769f43183fb6b

Signed-off-by: Jeremy Allison <jra@samba.org>
---
 source3/modules/vfs_streams_xattr.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
index 731c813f4d7..be46f8dc1e6 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -511,8 +511,15 @@ static int streams_xattr_open(vfs_handle_struct *handle,
 
         sio->xattr_name = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
 					xattr_name);
+	/*
+	 * sio->base needs to be a copy of fsp->fsp_name->base_name,
+	 * making it identical to streams_xattr_recheck(). If the
+	 * open is changing directories, fsp->fsp_name->base_name
+	 * will be the full path from the share root, whilst
+	 * smb_fname will be relative to the $cwd.
+	 */
         sio->base = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
-				  smb_fname->base_name);
+				  fsp->fsp_name->base_name);
 	sio->fsp_name_ptr = fsp->fsp_name;
 	sio->handle = handle;
 	sio->fsp = fsp;
-- 
2.13.5


From 91935aaf77c70e3e2436af1d6e4a538d29fd4276 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Mon, 13 Mar 2017 13:54:04 -0700
Subject: [PATCH 04/15] vfs_streams_xattr: use fsp, not base_fsp

The base_fsp's fd is always -1 as it's closed after being openend in
create_file_unixpath().

Additionally in streams_xattr_open force using of SMB_VFS_FSETXATTR() by
sticking the just created fd into the fsp (and removing it afterwards).

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12591

Back-ported from 021189e32ba507832b5e821e5cda8a2889225955.

Signed-off-by: Jeremy Allison <jra@samba.org>
---
 source3/modules/vfs_streams_xattr.c | 205 +++++++++++++++++-------------------
 1 file changed, 99 insertions(+), 106 deletions(-)

diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
index be46f8dc1e6..a4ab84bba71 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -229,7 +229,7 @@ static int streams_xattr_fstat(vfs_handle_struct *handle, files_struct *fsp,
 		return -1;
 	}
 
-	sbuf->st_ex_size = get_xattr_size(handle->conn, fsp->base_fsp,
+	sbuf->st_ex_size = get_xattr_size(handle->conn, fsp,
 					io->base, io->xattr_name);
 	if (sbuf->st_ex_size == -1) {
 		return -1;
@@ -364,6 +364,7 @@ static int streams_xattr_open(vfs_handle_struct *handle,
 	char *xattr_name = NULL;
 	int baseflags;
 	int hostfd = -1;
+	int ret;
 
 	DEBUG(10, ("streams_xattr_open called for %s\n",
 		   smb_fname_str_dbg(smb_fname)));
@@ -375,133 +376,125 @@ static int streams_xattr_open(vfs_handle_struct *handle,
 	/* If the default stream is requested, just open the base file. */
 	if (is_ntfs_default_stream_smb_fname(smb_fname)) {
 		char *tmp_stream_name;
-		int ret;
 
 		tmp_stream_name = smb_fname->stream_name;
 		smb_fname->stream_name = NULL;
 
 		ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
 
-		smb_fname->stream_name = tmp_stream_name;
-
-		return ret;
-	}
+			smb_fname->stream_name = tmp_stream_name;
 
-	status = streams_xattr_get_name(talloc_tos(), smb_fname->stream_name,
-					&xattr_name);
-	if (!NT_STATUS_IS_OK(status)) {
-		errno = map_errno_from_nt_status(status);
-		goto fail;
-	}
+			return ret;
+		}
 
-	/* Create an smb_filename with stream_name == NULL. */
-	status = create_synthetic_smb_fname(talloc_tos(),
-					    smb_fname->base_name,
-					    NULL, NULL,
-					    &smb_fname_base);
-	if (!NT_STATUS_IS_OK(status)) {
-		errno = map_errno_from_nt_status(status);
-		goto fail;
-	}
+		status = streams_xattr_get_name(talloc_tos(), smb_fname->stream_name,
+						&xattr_name);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto fail;
+		}
 
-	/*
-	 * We use baseflags to turn off nasty side-effects when opening the
-	 * underlying file.
-         */
-        baseflags = flags;
-        baseflags &= ~O_TRUNC;
-        baseflags &= ~O_EXCL;
-        baseflags &= ~O_CREAT;
+		/* Create an smb_filename with stream_name == NULL. */
+		status = create_synthetic_smb_fname(talloc_tos(),
+						    smb_fname->base_name,
+						    NULL, NULL,
+						    &smb_fname_base);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto fail;
+		}
 
-        hostfd = SMB_VFS_OPEN(handle->conn, smb_fname_base, fsp,
-			      baseflags, mode);
+		/*
+		 * We use baseflags to turn off nasty side-effects when opening the
+		 * underlying file.
+		 */
+		baseflags = flags;
+		baseflags &= ~O_TRUNC;
+		baseflags &= ~O_EXCL;
+		baseflags &= ~O_CREAT;
 
-	TALLOC_FREE(smb_fname_base);
+		hostfd = SMB_VFS_OPEN(handle->conn, smb_fname_base, fsp,
+				      baseflags, mode);
 
-        /* It is legit to open a stream on a directory, but the base
-         * fd has to be read-only.
-         */
-        if ((hostfd == -1) && (errno == EISDIR)) {
-                baseflags &= ~O_ACCMODE;
-                baseflags |= O_RDONLY;
-                hostfd = SMB_VFS_OPEN(handle->conn, smb_fname, fsp, baseflags,
-				      mode);
-        }
+		TALLOC_FREE(smb_fname_base);
 
-        if (hostfd == -1) {
-		goto fail;
-        }
+		/* It is legit to open a stream on a directory, but the base
+		 * fd has to be read-only.
+		 */
+		if ((hostfd == -1) && (errno == EISDIR)) {
+			baseflags &= ~O_ACCMODE;
+			baseflags |= O_RDONLY;
+			hostfd = SMB_VFS_OPEN(handle->conn, smb_fname, fsp, baseflags,
+					      mode);
+		}
 
-	status = get_ea_value(talloc_tos(), handle->conn, NULL,
-			      smb_fname->base_name, xattr_name, &ea);
+		if (hostfd == -1) {
+			goto fail;
+		}
 
-	DEBUG(10, ("get_ea_value returned %s\n", nt_errstr(status)));
+		status = get_ea_value(talloc_tos(), handle->conn, NULL,
+				      smb_fname->base_name, xattr_name, &ea);
 
-	if (!NT_STATUS_IS_OK(status)
-	    && !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
-		/*
-		 * The base file is not there. This is an error even if we got
-		 * O_CREAT, the higher levels should have created the base
-		 * file for us.
-		 */
-		DEBUG(10, ("streams_xattr_open: base file %s not around, "
-			   "returning ENOENT\n", smb_fname->base_name));
-		errno = ENOENT;
-		goto fail;
-	}
+		DEBUG(10, ("get_ea_value returned %s\n", nt_errstr(status)));
 
-	if (!NT_STATUS_IS_OK(status)) {
-		/*
-		 * The attribute does not exist
-		 */
+		if (!NT_STATUS_IS_OK(status)
+		    && !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+			/*
+			 * The base file is not there. This is an error even if we got
+			 * O_CREAT, the higher levels should have created the base
+			 * file for us.
+			 */
+			DEBUG(10, ("streams_xattr_open: base file %s not around, "
+				   "returning ENOENT\n", smb_fname->base_name));
+			errno = ENOENT;
+			goto fail;
+		}
 
-                if (flags & O_CREAT) {
+		if (!NT_STATUS_IS_OK(status)) {
 			/*
-			 * Darn, xattrs need at least 1 byte
+			 * The attribute does not exist
 			 */
-                        char null = '\0';
 
-			DEBUG(10, ("creating attribute %s on file %s\n",
-				   xattr_name, smb_fname->base_name));
+			if (flags & O_CREAT) {
+				/*
+				 * Darn, xattrs need at least 1 byte
+				 */
+				char null = '\0';
+
+				DEBUG(10, ("creating attribute %s on file %s\n",
+					   xattr_name, smb_fname->base_name));
+
+				fsp->fh->fd = hostfd;
+				ret = SMB_VFS_FSETXATTR(fsp, xattr_name,
+						&null, sizeof(null),
+						flags & O_EXCL ? XATTR_CREATE : 0);
+				fsp->fh->fd = -1;
+				if (ret != 0) {
+					goto fail;
+				}
+			}
+		}
 
+		if (flags & O_TRUNC) {
+			char null = '\0';
 			if (fsp->base_fsp->fh->fd != -1) {
-                        	if (SMB_VFS_FSETXATTR(
-					fsp->base_fsp, xattr_name,
-					&null, sizeof(null),
-					flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
+				if (SMB_VFS_FSETXATTR(
+						fsp->base_fsp, xattr_name,
+						&null, sizeof(null),
+						flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
 					goto fail;
 				}
 			} else {
-	                        if (SMB_VFS_SETXATTR(
-					handle->conn, smb_fname->base_name,
-					xattr_name, &null, sizeof(null),
-					flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
+				if (SMB_VFS_SETXATTR(
+						handle->conn, smb_fname->base_name,
+						xattr_name, &null, sizeof(null),
+						flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
 					goto fail;
 				}
 			}
 		}
-	}
-
-	if (flags & O_TRUNC) {
-		char null = '\0';
-		if (fsp->base_fsp->fh->fd != -1) {
-			if (SMB_VFS_FSETXATTR(
-					fsp->base_fsp, xattr_name,
-					&null, sizeof(null),
-					flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
-				goto fail;
-			}
-		} else {
-			if (SMB_VFS_SETXATTR(
-					handle->conn, smb_fname->base_name,
-					xattr_name, &null, sizeof(null),
-					flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
-				goto fail;
-			}
-		}
-	}
 
-        sio = (struct stream_io *)VFS_ADD_FSP_EXTENSION(handle, fsp,
+		sio = (struct stream_io *)VFS_ADD_FSP_EXTENSION(handle, fsp,
 							struct stream_io,
 							NULL);
         if (sio == NULL) {
@@ -868,7 +861,7 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle,
 		return -1;
 	}
 
-	status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
+	status = get_ea_value(talloc_tos(), handle->conn, fsp,
 			      sio->base, sio->xattr_name, &ea);
 	if (!NT_STATUS_IS_OK(status)) {
 		return -1;
@@ -892,13 +885,13 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle,
 
         memcpy(ea.value.data + offset, data, n);
 
-	if (fsp->base_fsp->fh->fd != -1) {
-		ret = SMB_VFS_FSETXATTR(fsp->base_fsp,
+	if (fsp->fh->fd != -1) {
+		ret = SMB_VFS_FSETXATTR(fsp,
 				sio->xattr_name,
 				ea.value.data, ea.value.length, 0);
 	} else {
 		ret = SMB_VFS_SETXATTR(fsp->conn,
-				       fsp->base_fsp->fsp_name->base_name,
+				       fsp->fsp_name->base_name,
 				sio->xattr_name,
 				ea.value.data, ea.value.length, 0);
 	}
@@ -932,7 +925,7 @@ static ssize_t streams_xattr_pread(vfs_handle_struct *handle,
 		return -1;
 	}
 
-	status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
+	status = get_ea_value(talloc_tos(), handle->conn, fsp,
 			      sio->base, sio->xattr_name, &ea);
 	if (!NT_STATUS_IS_OK(status)) {
 		return -1;
@@ -977,7 +970,7 @@ static int streams_xattr_ftruncate(struct vfs_handle_struct *handle,
 		return -1;
 	}
 
-	status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
+	status = get_ea_value(talloc_tos(), handle->conn, fsp,
 			      sio->base, sio->xattr_name, &ea);
 	if (!NT_STATUS_IS_OK(status)) {
 		return -1;
@@ -1002,13 +995,13 @@ static int streams_xattr_ftruncate(struct vfs_handle_struct *handle,
 	ea.value.length = offset + 1;
 	ea.value.data[offset] = 0;
 
-	if (fsp->base_fsp->fh->fd != -1) {
-		ret = SMB_VFS_FSETXATTR(fsp->base_fsp,
+	if (fsp->fh->fd != -1) {
+		ret = SMB_VFS_FSETXATTR(fsp,
 				sio->xattr_name,
 				ea.value.data, ea.value.length, 0);
 	} else {
 		ret = SMB_VFS_SETXATTR(fsp->conn,
-				       fsp->base_fsp->fsp_name->base_name,
+			        fsp->fsp_name->base_name,
 				sio->xattr_name,
 				ea.value.data, ea.value.length, 0);
 	}
-- 
2.13.5


From 3f3c731faaa59f4d3ce7e49c12795c40e048d29f Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Mon, 19 Dec 2016 11:55:56 -0800
Subject: [PATCH 05/15] s3: smbd: Create wrapper function for OpenDir in
 preparation for making robust.

CVE-2017-2619

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496

Signed-off-by: Jeremy Allison <jra@samba.org>
---
 source3/smbd/dir.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index 18ecf066824..ebe2641f813 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -1367,7 +1367,8 @@ static int smb_Dir_destructor(struct smb_Dir *dirp)
  Open a directory.
 ********************************************************************/
 
-struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn,
+static struct smb_Dir *OpenDir_internal(TALLOC_CTX *mem_ctx,
+			connection_struct *conn,
 			const char *name,
 			const char *mask,
 			uint32 attr)
@@ -1407,6 +1408,18 @@ struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn,
 	return NULL;
 }
 
+struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn,
+			const char *name,
+			const char *mask,
+			uint32_t attr)
+{
+	return OpenDir_internal(mem_ctx,
+				conn,
+				name,
+				mask,
+				attr);
+}
+
 /*******************************************************************
  Open a directory from an fsp.
 ********************************************************************/
-- 
2.13.5


From 7efeb067c1586e0f1cfbb775b1efcb3b92005140 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Mon, 19 Dec 2016 16:25:26 -0800
Subject: [PATCH 06/15] s3: smbd: Opendir_internal() early return if
 SMB_VFS_OPENDIR failed.

CVE-2017-2619

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496

Signed-off-by: Jeremy Allison <jra@samba.org>
---
 source3/smbd/dir.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index ebe2641f813..65327dd0dd1 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -1380,6 +1380,13 @@ static struct smb_Dir *OpenDir_internal(TALLOC_CTX *mem_ctx,
 		return NULL;
 	}
 
+	dirp->dir = SMB_VFS_OPENDIR(conn, name, mask, attr);
+	if (!dirp->dir) {
+		DEBUG(5,("OpenDir: Can't open %s. %s\n", name,
+			 strerror(errno) ));
+		goto fail;
+	}
+
 	dirp->conn = conn;
 	dirp->name_cache_size = lp_directory_name_cache_size(SNUM(conn));
 
@@ -1394,13 +1401,6 @@ static struct smb_Dir *OpenDir_internal(TALLOC_CTX *mem_ctx,
 	}
 	talloc_set_destructor(dirp, smb_Dir_destructor);
 
-	dirp->dir = SMB_VFS_OPENDIR(conn, dirp->dir_path, mask, attr);
-	if (!dirp->dir) {
-		DEBUG(5,("OpenDir: Can't open %s. %s\n", dirp->dir_path,
-			 strerror(errno) ));
-		goto fail;
-	}
-
 	return dirp;
 
   fail:
-- 
2.13.5


From 49d22a0c51ef1f78f0488a7c35131887704e987b Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Mon, 19 Dec 2016 16:35:00 -0800
Subject: [PATCH 07/15] s3: smbd: Create and use open_dir_safely(). Use from
 OpenDir().

Hardens OpenDir against TOC/TOU races.

CVE-2017-2619

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496

Signed-off-by: Jeremy Allison <jra@samba.org>
---
 source3/smbd/dir.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 59 insertions(+), 7 deletions(-)

diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index 65327dd0dd1..2d168c3ba9f 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -1390,12 +1390,6 @@ static struct smb_Dir *OpenDir_internal(TALLOC_CTX *mem_ctx,
 	dirp->conn = conn;
 	dirp->name_cache_size = lp_directory_name_cache_size(SNUM(conn));
 
-	dirp->dir_path = talloc_strdup(dirp, name);
-	if (!dirp->dir_path) {
-		errno = ENOMEM;
-		goto fail;
-	}
-
 	if (sconn && !sconn->using_smb2) {
 		sconn->searches.dirhandles_open++;
 	}
@@ -1408,12 +1402,70 @@ static struct smb_Dir *OpenDir_internal(TALLOC_CTX *mem_ctx,
 	return NULL;
 }
 
+/****************************************************************************
+ Open a directory handle by pathname, ensuring it's under the share path.
+****************************************************************************/
+
+static struct smb_Dir *open_dir_safely(TALLOC_CTX *ctx,
+					connection_struct *conn,
+					const char *name,
+					const char *wcard,
+					uint32_t attr)
+{
+	struct smb_Dir *dir_hnd = NULL;
+	char *saved_dir = vfs_GetWd(ctx, conn);
+	NTSTATUS status;
+
+	if (saved_dir == NULL) {
+		return NULL;
+	}
+
+	if (vfs_ChDir(conn, name) == -1) {
+		goto out;
+	}
+
+	/*
+	 * Now the directory is pinned, use
+	 * REALPATH to ensure we can access it.
+	 */
+	status = check_name(conn, ".");
+	if (!NT_STATUS_IS_OK(status)) {
+		goto out;
+	}
+
+	dir_hnd = OpenDir_internal(ctx,
+				conn,
+				".",
+				wcard,
+				attr);
+
+	if (dir_hnd == NULL) {
+		goto out;
+	}
+
+	/*
+	 * OpenDir_internal only gets "." as the dir name.
+	 * Store the real dir name here.
+	 */
+
+	dir_hnd->dir_path = talloc_strdup(dir_hnd, name);
+	if (!dir_hnd->dir_path) {
+		errno = ENOMEM;
+	}
+
+  out:
+
+	vfs_ChDir(conn, saved_dir);
+	TALLOC_FREE(saved_dir);
+	return dir_hnd;
+}
+
 struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn,
 			const char *name,
 			const char *mask,
 			uint32_t attr)
 {
-	return OpenDir_internal(mem_ctx,
+	return open_dir_safely(mem_ctx,
 				conn,
 				name,
 				mask,
-- 
2.13.5


From 6426ae1f9ef53158a6fbe1912dfec40d834115fe Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Mon, 19 Dec 2016 12:13:20 -0800
Subject: [PATCH 08/15] s3: smbd: OpenDir_fsp() use early returns.

CVE-2017-2619

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496

Signed-off-by: Jeremy Allison <jra@samba.org>
---
 source3/smbd/dir.c | 34 +++++++++++++++++++++-------------
 1 file changed, 21 insertions(+), 13 deletions(-)

diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index 2d168c3ba9f..6aed4a6da46 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -1485,7 +1485,17 @@ static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn,
 	struct smbd_server_connection *sconn = conn->sconn;
 
 	if (!dirp) {
-		return NULL;
+		goto fail;
+	}
+
+	if (!fsp->is_directory) {
+		errno = EBADF;
+		goto fail;
+	}
+
+	if (fsp->fh->fd == -1) {
+		errno = EBADF;
+		goto fail;
 	}
 
 	dirp->conn = conn;
@@ -1502,18 +1512,16 @@ static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn,
 	}
 	talloc_set_destructor(dirp, smb_Dir_destructor);
 
-	if (fsp->is_directory && fsp->fh->fd != -1) {
-		dirp->dir = SMB_VFS_FDOPENDIR(fsp, mask, attr);
-		if (dirp->dir != NULL) {
-			dirp->fsp = fsp;
-		} else {
-			DEBUG(10,("OpenDir_fsp: SMB_VFS_FDOPENDIR on %s returned "
-				"NULL (%s)\n",
-				dirp->dir_path,
-				strerror(errno)));
-			if (errno != ENOSYS) {
-				return NULL;
-			}
+	dirp->dir = SMB_VFS_FDOPENDIR(fsp, mask, attr);
+	if (dirp->dir != NULL) {
+		dirp->fsp = fsp;
+	} else {
+		DEBUG(10,("OpenDir_fsp: SMB_VFS_FDOPENDIR on %s returned "
+			"NULL (%s)\n",
+			dirp->dir_path,
+			strerror(errno)));
+		if (errno != ENOSYS) {
+			return NULL;
 		}
 	}
 
-- 
2.13.5


From f6581858ce665b880c5fea465ec61b1b0c504d89 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Mon, 19 Dec 2016 12:15:59 -0800
Subject: [PATCH 09/15] s3: smbd: OpenDir_fsp() - Fix memory leak on error.

CVE-2017-2619

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496

Signed-off-by: Jeremy Allison <jra@samba.org>
---
 source3/smbd/dir.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index 6aed4a6da46..efd1a73aab6 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -1521,7 +1521,7 @@ static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn,
 			dirp->dir_path,
 			strerror(errno)));
 		if (errno != ENOSYS) {
-			return NULL;
+			goto fail;
 		}
 	}
 
-- 
2.13.5


From bacba6987e58d44886d04b1dd5e36f7781dcd9b0 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Mon, 19 Dec 2016 12:32:07 -0800
Subject: [PATCH 10/15] s3: smbd: Move the reference counting and destructor
 setup to just before retuning success.

CVE-2017-2619

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496

Signed-off-by: Jeremy Allison <jra@samba.org>
---
 source3/smbd/dir.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index efd1a73aab6..5eca128c033 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -1507,11 +1507,6 @@ static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn,
 		goto fail;
 	}
 
-	if (sconn && !sconn->using_smb2) {
-		sconn->searches.dirhandles_open++;
-	}
-	talloc_set_destructor(dirp, smb_Dir_destructor);
-
 	dirp->dir = SMB_VFS_FDOPENDIR(fsp, mask, attr);
 	if (dirp->dir != NULL) {
 		dirp->fsp = fsp;
@@ -1536,6 +1531,11 @@ static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn,
 		goto fail;
 	}
 
+	if (sconn && !sconn->using_smb2) {
+		sconn->searches.dirhandles_open++;
+	}
+	talloc_set_destructor(dirp, smb_Dir_destructor);
+
 	return dirp;
 
   fail:
-- 
2.13.5


From 34b3d05b55f5c40de76ba65d6b028818518a519f Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Mon, 19 Dec 2016 12:35:32 -0800
Subject: [PATCH 11/15] s3: smbd: Correctly fallback to open_dir_safely if
 FDOPENDIR not supported on system.

CVE-2017-2619

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496

Signed-off-by: Jeremy Allison <jra@samba.org>
---
 source3/smbd/dir.c | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index 5eca128c033..7690cb18c1a 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -1521,14 +1521,13 @@ static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn,
 	}
 
 	if (dirp->dir == NULL) {
-		/* FDOPENDIR didn't work. Use OPENDIR instead. */
-		dirp->dir = SMB_VFS_OPENDIR(conn, dirp->dir_path, mask, attr);
-	}
-
-	if (!dirp->dir) {
-		DEBUG(5,("OpenDir_fsp: Can't open %s. %s\n", dirp->dir_path,
-			 strerror(errno) ));
-		goto fail;
+		/* FDOPENDIR is not supported. Use OPENDIR instead. */
+		TALLOC_FREE(dirp);
+		return open_dir_safely(mem_ctx,
+					conn,
+					fsp->fsp_name->base_name,
+					mask,
+					attr);
 	}
 
 	if (sconn && !sconn->using_smb2) {
-- 
2.13.5


From 84bc8b232a4495bff270b7800833ef6785937576 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Thu, 15 Dec 2016 12:52:13 -0800
Subject: [PATCH 12/15] s3: smbd: Remove O_NOFOLLOW guards. We insist on
 O_NOFOLLOW existing.

CVE-2017-2619

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496

Signed-off-by: Jeremy Allison <jra@samba.org>
---
 source3/smbd/open.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 35eee0a1485..8417f8aca4a 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -205,8 +205,7 @@ NTSTATUS fd_open(struct connection_struct *conn,
 	struct smb_filename *smb_fname = fsp->fsp_name;
 	NTSTATUS status = NT_STATUS_OK;
 
-#ifdef O_NOFOLLOW
-	/* 
+	/*
 	 * Never follow symlinks on a POSIX client. The
 	 * client should be doing this.
 	 */
@@ -214,7 +213,6 @@ NTSTATUS fd_open(struct connection_struct *conn,
 	if (fsp->posix_open || !lp_symlinks(SNUM(conn))) {
 		flags |= O_NOFOLLOW;
 	}
-#endif
 
 	fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode);
 	if (fsp->fh->fd == -1) {
-- 
2.13.5


From af0c5a266ae65ad2a638fe48a7ad7d77417f97d7 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Thu, 15 Dec 2016 12:56:08 -0800
Subject: [PATCH 13/15] s3: smbd: Move special handling of symlink errno's into
 a utility function.

CVE-2017-2619

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496

Signed-off-by: Jeremy Allison <jra@samba.org>
---
 source3/smbd/open.c | 30 ++++++++++++++++++++++++++++--
 1 file changed, 28 insertions(+), 2 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 8417f8aca4a..e727e89e9d8 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -194,6 +194,31 @@ static NTSTATUS check_base_file_access(struct connection_struct *conn,
 }
 
 /****************************************************************************
+ Handle differing symlink errno's
+****************************************************************************/
+
+static int link_errno_convert(int err)
+{
+#if defined(ENOTSUP) && defined(OSF1)
+	/* handle special Tru64 errno */
+	if (err == ENOTSUP) {
+		err = ELOOP;
+	}
+#endif /* ENOTSUP */
+#ifdef EFTYPE
+	/* fix broken NetBSD errno */
+	if (err == EFTYPE) {
+		err = ELOOP;
+	}
+#endif /* EFTYPE */
+	/* fix broken FreeBSD errno */
+	if (err == EMLINK) {
+		err = ELOOP;
+	}
+	return err;
+}
+
+/****************************************************************************
  fd support routines - attempt to do a dos_open.
 ****************************************************************************/
 
@@ -216,8 +241,9 @@ NTSTATUS fd_open(struct connection_struct *conn,
 
 	fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode);
 	if (fsp->fh->fd == -1) {
-		status = map_nt_error_from_unix(errno);
-		if (errno == EMFILE) {
+		int posix_errno = link_errno_convert(errno);
+		status = map_nt_error_from_unix(posix_errno);
+		if (posix_errno == EMFILE) {
 			static time_t last_warned = 0L;
 
 			if (time((time_t *) NULL) > last_warned) {
-- 
2.13.5


From c3bc4ff0367d7a3ebfd64db6defddea0bc3a5f4a Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Thu, 15 Dec 2016 13:04:46 -0800
Subject: [PATCH 14/15] s3: smbd: Add the core functions to prevent symlink
 open races.

CVE-2017-2619

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496

Signed-off-by: Jeremy Allison <jra@samba.org>
---
 source3/smbd/open.c | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 242 insertions(+)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index e727e89e9d8..0998adc416a 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -218,6 +218,248 @@ static int link_errno_convert(int err)
 	return err;
 }
 
+static int non_widelink_open(struct connection_struct *conn,
+			const char *conn_rootdir,
+			files_struct *fsp,
+			struct smb_filename *smb_fname,
+			int flags,
+			mode_t mode,
+			unsigned int link_depth);
+
+/****************************************************************************
+ Follow a symlink in userspace.
+****************************************************************************/
+
+static int process_symlink_open(struct connection_struct *conn,
+			const char *conn_rootdir,
+			files_struct *fsp,
+			struct smb_filename *smb_fname,
+			int flags,
+			mode_t mode,
+			unsigned int link_depth)
+{
+	int fd = -1;
+	char *link_target = NULL;
+	int link_len = -1;
+	char *oldwd = NULL;
+	size_t rootdir_len = 0;
+	char *resolved_name = NULL;
+	bool matched = false;
+	int saved_errno = 0;
+
+	/*
+	 * Ensure we don't get stuck in a symlink loop.
+	 */
+	link_depth++;
+	if (link_depth >= 20) {
+		errno = ELOOP;
+		goto out;
+	}
+
+	/* Allocate space for the link target. */
+	link_target = talloc_array(talloc_tos(), char, PATH_MAX);
+	if (link_target == NULL) {
+		errno = ENOMEM;
+		goto out;
+	}
+
+	/* Read the link target. */
+	link_len = SMB_VFS_READLINK(conn,
+				smb_fname->base_name,
+				link_target,
+				PATH_MAX - 1);
+	if (link_len == -1) {
+		goto out;
+	}
+
+	/* Ensure it's at least null terminated. */
+	link_target[link_len] = '\0';
+
+	/* Convert to an absolute path. */
+	resolved_name = SMB_VFS_REALPATH(conn, link_target);
+	if (resolved_name == NULL) {
+		goto out;
+	}
+
+	/*
+	 * We know conn_rootdir starts with '/' and
+	 * does not end in '/'. FIXME ! Should we
+	 * smb_assert this ?
+	 */
+	rootdir_len = strlen(conn_rootdir);
+
+	matched = (strncmp(conn_rootdir, resolved_name, rootdir_len) == 0);
+	if (!matched) {
+		errno = EACCES;
+		goto out;
+	}
+
+	/*
+	 * Turn into a path relative to the share root.
+	 */
+	if (resolved_name[rootdir_len] == '\0') {
+		/* Link to the root of the share. */
+		smb_fname->base_name = talloc_strdup(talloc_tos(), ".");
+		if (smb_fname->base_name == NULL) {
+			errno = ENOMEM;
+			goto out;
+		}
+	} else if (resolved_name[rootdir_len] == '/') {
+		smb_fname->base_name = &resolved_name[rootdir_len+1];
+	} else {
+		errno = EACCES;
+		goto out;
+	}
+
+	oldwd = vfs_GetWd(talloc_tos(), conn);
+	if (oldwd == NULL) {
+		goto out;
+	}
+
+	/* Ensure we operate from the root of the share. */
+	if (vfs_ChDir(conn, conn_rootdir) == -1) {
+		goto out;
+	}
+
+	/* And do it all again.. */
+	fd = non_widelink_open(conn,
+				conn_rootdir,
+				fsp,
+				smb_fname,
+				flags,
+				mode,
+				link_depth);
+	if (fd == -1) {
+		saved_errno = errno;
+	}
+
+  out:
+
+	SAFE_FREE(resolved_name);
+	TALLOC_FREE(link_target);
+	if (oldwd != NULL) {
+		int ret = vfs_ChDir(conn, oldwd);
+		if (ret == -1) {
+			smb_panic("unable to get back to old directory\n");
+		}
+		TALLOC_FREE(oldwd);
+	}
+	if (saved_errno != 0) {
+		errno = saved_errno;
+	}
+	return fd;
+}
+
+/****************************************************************************
+ Non-widelink open.
+****************************************************************************/
+
+static int non_widelink_open(struct connection_struct *conn,
+			const char *conn_rootdir,
+			files_struct *fsp,
+			struct smb_filename *smb_fname,
+			int flags,
+			mode_t mode,
+			unsigned int link_depth)
+{
+	NTSTATUS status;
+	int fd = -1;
+	struct smb_filename *smb_fname_rel = NULL;
+	int saved_errno = 0;
+	char *oldwd = NULL;
+	char *parent_dir = NULL;
+	const char *final_component = NULL;
+
+	if (!parent_dirname(talloc_tos(),
+			smb_fname->base_name,
+			&parent_dir,
+			&final_component)) {
+		goto out;
+	}
+
+	oldwd = vfs_GetWd(talloc_tos(), conn);
+	if (oldwd == NULL) {
+		goto out;
+	}
+
+	/* Pin parent directory in place. */
+	if (vfs_ChDir(conn, parent_dir) == -1) {
+		goto out;
+	}
+
+	/* Ensure the relative path is below the share. */
+	status = check_reduced_name(conn, final_component);
+	if (!NT_STATUS_IS_OK(status)) {
+		saved_errno = map_errno_from_nt_status(status);
+		goto out;
+	}
+
+	status = create_synthetic_smb_fname(talloc_tos(),
+				final_component,
+				smb_fname->stream_name,
+				&smb_fname->st,
+				&smb_fname_rel);
+	if (!NT_STATUS_IS_OK(status)) {
+		saved_errno = map_errno_from_nt_status(status);
+		goto out;
+	}
+
+	flags |= O_NOFOLLOW;
+
+	{
+		struct smb_filename *tmp_name = fsp->fsp_name;
+		fsp->fsp_name = smb_fname_rel;
+		fd = SMB_VFS_OPEN(conn, smb_fname_rel, fsp, flags, mode);
+		fsp->fsp_name = tmp_name;
+	}
+
+	if (fd == -1) {
+		saved_errno = link_errno_convert(errno);
+		if (saved_errno == ELOOP) {
+			if (fsp->posix_open) {
+				/* Never follow symlinks on posix open. */
+				goto out;
+			}
+			if (!lp_symlinks(SNUM(conn))) {
+				/* Explicitly no symlinks. */
+				goto out;
+			}
+			/*
+			 * We have a symlink. Follow in userspace
+			 * to ensure it's under the share definition.
+			 */
+			fd = process_symlink_open(conn,
+					conn_rootdir,
+					fsp,
+					smb_fname_rel,
+					flags,
+					mode,
+					link_depth);
+			if (fd == -1) {
+				saved_errno =
+					link_errno_convert(errno);
+			}
+		}
+	}
+
+  out:
+
+	TALLOC_FREE(parent_dir);
+	TALLOC_FREE(smb_fname_rel);
+
+	if (oldwd != NULL) {
+		int ret = vfs_ChDir(conn, oldwd);
+		if (ret == -1) {
+			smb_panic("unable to get back to old directory\n");
+		}
+		TALLOC_FREE(oldwd);
+	}
+	if (saved_errno != 0) {
+		errno = saved_errno;
+	}
+	return fd;
+}
+
 /****************************************************************************
  fd support routines - attempt to do a dos_open.
 ****************************************************************************/
-- 
2.13.5


From 6a88d1cf3deb54a784f50c8eba3b9a24a65c1b34 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Thu, 15 Dec 2016 13:06:31 -0800
Subject: [PATCH 15/15] s3: smbd: Use the new non_widelink_open() function.

CVE-2017-2619

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12496

Signed-off-by: Jeremy Allison <jra@samba.org>
---
 source3/smbd/open.c | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 0998adc416a..65ca14ec8b8 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -481,7 +481,28 @@ NTSTATUS fd_open(struct connection_struct *conn,
 		flags |= O_NOFOLLOW;
 	}
 
-	fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode);
+	/* Ensure path is below share definition. */
+	if (!lp_widelinks(SNUM(conn))) {
+		const char *conn_rootdir = SMB_VFS_CONNECTPATH(conn,
+						smb_fname->base_name);
+		if (conn_rootdir == NULL) {
+			return NT_STATUS_NO_MEMORY;
+		}
+		/*
+		 * Only follow symlinks within a share
+		 * definition.
+		 */
+		fsp->fh->fd = non_widelink_open(conn,
+					conn_rootdir,
+					fsp,
+					smb_fname,
+					flags,
+					mode,
+					0);
+	} else {
+		fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode);
+	}
+
 	if (fsp->fh->fd == -1) {
 		int posix_errno = link_errno_convert(errno);
 		status = map_nt_error_from_unix(posix_errno);
-- 
2.13.5

