--- a/security/sandbox/linux/SandboxBrokerClient.cpp
+++ b/security/sandbox/linux/SandboxBrokerClient.cpp
@@ -30,17 +30,17 @@ SandboxBrokerClient::SandboxBrokerClient
SandboxBrokerClient::~SandboxBrokerClient()
{
close(mFileDesc);
}
int
SandboxBrokerClient::DoCall(const Request* aReq, const char* aPath,
- const char* aPath2, struct stat* aStat,
+ const char* aPath2, void* aBuff,
bool expectFd)
{
// Remap /proc/self to the actual pid, so that the broker can open
// it. This happens here instead of in the broker to follow the
// principle of least privilege and keep the broker as simple as
// possible. (Note: when pid namespaces happen, this will also need
// to remap the inner pid to the outer pid.)
// We only remap the first path.
@@ -101,44 +101,44 @@ SandboxBrokerClient::DoCall(const Reques
close(respFds[0]);
return -sendErrno;
}
// Set up iovecs for response.
Response resp;
ios[0].iov_base = &resp;
ios[0].iov_len = sizeof(resp);
- if (aStat) {
- ios[1].iov_base = aStat;
- ios[1].iov_len = sizeof(*aStat);
+ if (aBuff) {
+ ios[1].iov_base = aBuff;
+ ios[1].iov_len = aReq->mBufSize;
} else {
ios[1].iov_base = nullptr;
ios[1].iov_len = 0;
}
// Wait for response and return appropriately.
int openedFd = -1;
- const ssize_t recvd = RecvWithFd(respFds[0], ios, aStat ? 2 : 1,
+ const ssize_t recvd = RecvWithFd(respFds[0], ios, aBuff ? 2 : 1,
expectFd ? &openedFd : nullptr);
const int recvErrno = errno;
close(respFds[0]);
if (recvd < 0) {
return -recvErrno;
}
if (recvd == 0) {
SANDBOX_LOG_ERROR("Unexpected EOF, op %d flags 0%o path %s",
aReq->mOp, aReq->mFlags, path);
return -EIO;
}
if (resp.mError != 0) {
// If the operation fails, the return payload will be empty;
// adjust the iov_len for the following assertion.
ios[1].iov_len = 0;
}
- MOZ_ASSERT(static_cast<size_t>(recvd) == ios[0].iov_len + ios[1].iov_len);
+ MOZ_ASSERT(static_cast<size_t>(recvd) <= ios[0].iov_len + ios[1].iov_len);
if (resp.mError == 0) {
// Success!
if (expectFd) {
MOZ_ASSERT(openedFd >= 0);
return openedFd;
}
return 0;
}
@@ -154,93 +154,98 @@ SandboxBrokerClient::DoCall(const Reques
close(openedFd);
}
return -resp.mError;
}
int
SandboxBrokerClient::Open(const char* aPath, int aFlags)
{
- Request req = { SANDBOX_FILE_OPEN, aFlags };
+ Request req = { SANDBOX_FILE_OPEN, aFlags, 0 };
int maybeFd = DoCall(&req, aPath, nullptr, nullptr, true);
if (maybeFd >= 0) {
// NSPR has opinions about file flags. Fix O_CLOEXEC.
if ((aFlags & O_CLOEXEC) == 0) {
fcntl(maybeFd, F_SETFD, 0);
}
}
return maybeFd;
}
int
SandboxBrokerClient::Access(const char* aPath, int aMode)
{
- Request req = { SANDBOX_FILE_ACCESS, aMode };
+ Request req = { SANDBOX_FILE_ACCESS, aMode, 0 };
return DoCall(&req, aPath, nullptr, nullptr, false);
}
int
SandboxBrokerClient::Stat(const char* aPath, struct stat* aStat)
{
- Request req = { SANDBOX_FILE_STAT, 0 };
- return DoCall(&req, aPath, nullptr, aStat, false);
+ Request req = { SANDBOX_FILE_STAT, 0, sizeof(struct stat) };
+ return DoCall(&req, aPath, nullptr, (void*)aStat, false);
}
int
SandboxBrokerClient::LStat(const char* aPath, struct stat* aStat)
{
- Request req = { SANDBOX_FILE_STAT, O_NOFOLLOW };
- return DoCall(&req, aPath, nullptr, aStat, false);
+ Request req = { SANDBOX_FILE_STAT, O_NOFOLLOW, sizeof(struct stat) };
+ return DoCall(&req, aPath, nullptr, (void*)aStat, false);
}
int
SandboxBrokerClient::Chmod(const char* aPath, int aMode)
{
- Request req = {SANDBOX_FILE_CHMOD, aMode};
+ Request req = {SANDBOX_FILE_CHMOD, aMode, 0};
return DoCall(&req, aPath, nullptr, nullptr, false);
}
int
SandboxBrokerClient::Link(const char* aOldPath, const char* aNewPath)
{
- Request req = {SANDBOX_FILE_LINK, 0};
+ Request req = {SANDBOX_FILE_LINK, 0, 0};
return DoCall(&req, aOldPath, aNewPath, nullptr, false);
}
int
SandboxBrokerClient::Symlink(const char* aOldPath, const char* aNewPath)
{
- Request req = {SANDBOX_FILE_SYMLINK, 0};
+ Request req = {SANDBOX_FILE_SYMLINK, 0, 0};
return DoCall(&req, aOldPath, aNewPath, nullptr, false);
-
}
int
SandboxBrokerClient::Rename(const char* aOldPath, const char* aNewPath)
{
- Request req = {SANDBOX_FILE_RENAME, 0};
+ Request req = {SANDBOX_FILE_RENAME, 0, 0};
return DoCall(&req, aOldPath, aNewPath, nullptr, false);
-
}
int
SandboxBrokerClient::Mkdir(const char* aPath, int aMode)
{
- Request req = {SANDBOX_FILE_MKDIR, aMode};
+ Request req = {SANDBOX_FILE_MKDIR, aMode, 0};
return DoCall(&req, aPath, nullptr, nullptr, false);
}
int
SandboxBrokerClient::Unlink(const char* aPath)
{
- Request req = {SANDBOX_FILE_UNLINK, 0};
+ Request req = {SANDBOX_FILE_UNLINK, 0, 0};
return DoCall(&req, aPath, nullptr, nullptr, false);
}
int
SandboxBrokerClient::Rmdir(const char* aPath)
{
- Request req = {SANDBOX_FILE_RMDIR, 0};
+ Request req = {SANDBOX_FILE_RMDIR, 0, 0};
return DoCall(&req, aPath, nullptr, nullptr, false);
}
+int
+SandboxBrokerClient::Readlink(const char* aPath, void* aBuff, size_t aSize)
+{
+ Request req = {SANDBOX_FILE_READLINK, 0, aSize};
+ return DoCall(&req, aPath, nullptr, aBuff, false);
+}
+
} // namespace mozilla
--- a/security/sandbox/linux/SandboxBrokerClient.h
+++ b/security/sandbox/linux/SandboxBrokerClient.h
@@ -35,22 +35,23 @@ class SandboxBrokerClient final : privat
int LStat(const char* aPath, struct stat* aStat);
int Chmod(const char* aPath, int aMode);
int Link(const char* aPath, const char* aPath2);
int Mkdir(const char* aPath, int aMode);
int Symlink(const char* aOldPath, const char* aNewPath);
int Rename(const char* aOldPath, const char* aNewPath);
int Unlink(const char* aPath);
int Rmdir(const char* aPath);
+ int Readlink(const char* aPath, void* aBuf, size_t aBufSize);
private:
int mFileDesc;
int DoCall(const Request* aReq,
const char* aPath,
const char* aPath2,
- struct stat* aStat,
+ void *aBuff,
bool expectFd);
};
} // namespace mozilla
#endif // mozilla_SandboxBrokerClient_h
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -451,16 +451,24 @@ class ContentSandboxPolicy : public Sand
}
static intptr_t UnlinkTrap(ArgsRef aArgs, void* aux) {
auto broker = static_cast<SandboxBrokerClient*>(aux);
auto path = reinterpret_cast<const char*>(aArgs.args[0]);
return broker->Unlink(path);
}
+ static intptr_t ReadlinkTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto buf = reinterpret_cast<char*>(aArgs.args[1]);
+ auto size = static_cast<size_t>(aArgs.args[2]);
+ return broker->Readlink(path, buf, size);
+ }
+
static intptr_t GetPPidTrap(ArgsRef aArgs, void* aux) {
// In a pid namespace, getppid() will return 0. We will return 0 instead
// of the real parent pid to see what breaks when we introduce the
// pid namespace (Bug 1151624).
return 0;
}
public:
@@ -562,16 +570,18 @@ public:
case __NR_symlink:
return Trap(SymlinkTrap, mBroker);
case __NR_rename:
return Trap(RenameTrap, mBroker);
case __NR_rmdir:
return Trap(RmdirTrap, mBroker);
case __NR_unlink:
return Trap(UnlinkTrap, mBroker);
+ case __NR_readlink:
+ return Trap(ReadlinkTrap, mBroker);
}
} else {
// No broker; allow the syscalls directly. )-:
switch(sysno) {
case __NR_open:
case __NR_openat:
case __NR_access:
case __NR_faccessat:
@@ -594,17 +604,16 @@ public:
CASES_FOR_fstatfs:
case __NR_quotactl:
CASES_FOR_fchown:
case __NR_fchmod:
case __NR_flock:
#endif
return Allow();
- case __NR_readlink:
case __NR_readlinkat:
#ifdef DESKTOP
// Bug 1290896
return Allow();
#else
// Workaround for bug 964455:
return Error(EINVAL);
#endif
--- a/security/sandbox/linux/broker/SandboxBroker.cpp
+++ b/security/sandbox/linux/broker/SandboxBroker.cpp
@@ -356,22 +356,22 @@ AllowOpen(int aReqFlags, int aPerms)
}
if (aReqFlags & O_CREAT) {
needed |= SandboxBroker::MAY_CREATE;
}
return (aPerms & needed) == needed;
}
static int
-DoStat(const char* aPath, struct stat* aStat, int aFlags)
+DoStat(const char* aPath, void* aBuff, int aFlags)
{
if (aFlags & O_NOFOLLOW) {
- return lstat(aPath, aStat);
+ return lstat(aPath, (struct stat*)aBuff);
}
- return stat(aPath, aStat);
+ return stat(aPath, (struct stat*)aBuff);
}
static int
DoChmod(const char* aPath, int aMode)
{
return chmod(aPath, aMode);
}
@@ -406,16 +406,22 @@ DoRmdir(const char* aPath)
}
static int
DoUnlink(const char* aPath)
{
return unlink(aPath);
}
+static ssize_t
+DoReadlink(const char* aPath, void* aBuff, size_t aBufSize)
+{
+ return readlink(aPath, (char*)aBuff, aBufSize);
+}
+
size_t
SandboxBroker::ConvertToRealPath(char* aPath, size_t aBufSize, size_t aPathLen)
{
if (strstr(aPath, "..") != NULL) {
char* result = realpath(aPath, NULL);
if (result != NULL) {
strncpy(aPath, result, aBufSize);
aPath[aBufSize - 1] = '\0';
@@ -457,21 +463,24 @@ SandboxBroker::ThreadMain(void)
while (true) {
struct iovec ios[2];
// We will receive the path strings in 1 buffer an split them back up.
char recvBuf[2 * (kMaxPathLen + 1)];
char pathBuf[kMaxPathLen + 1];
char pathBuf2[kMaxPathLen + 1];
size_t pathLen;
size_t pathLen2;
- struct stat statBuf;
+ char respBuf[kMaxPathLen + 1]; // Also serves as struct stat
Request req;
Response resp;
int respfd;
+ // Make sure stat responses fit in the response buffer
+ MOZ_ASSERT((kMaxPathLen + 1) > sizeof(struct stat));
+
// This makes our string handling below a bit less error prone.
memset(recvBuf, 0, sizeof(recvBuf));
ios[0].iov_base = &req;
ios[0].iov_len = sizeof(req);
ios[1].iov_base = recvBuf;
ios[1].iov_len = sizeof(recvBuf);
@@ -500,17 +509,17 @@ SandboxBroker::ThreadMain(void)
if (respfd == -1) {
SANDBOX_LOG_ERROR("no response fd from pid %d", mChildPid);
shutdown(mFileDesc, SHUT_RD);
break;
}
// Initialize the response with the default failure.
memset(&resp, 0, sizeof(resp));
- memset(&statBuf, 0, sizeof(statBuf));
+ memset(&respBuf, 0, sizeof(respBuf));
resp.mError = EACCES;
ios[0].iov_base = &resp;
ios[0].iov_len = sizeof(resp);
ios[1].iov_base = nullptr;
ios[1].iov_len = 0;
int openedFd = -1;
// Clear permissions
@@ -581,31 +590,31 @@ SandboxBroker::ThreadMain(void)
// would work, but Linux doesn't actually implement the
// flags != 0 case; glibc has a hack which doesn't even work
// in this case so it'll ignore the flag, and Bionic just
// passes through the syscall and always ignores the flags.
//
// Instead, because we've already checked the requested
// r/w/x bits against the policy, just return success if the
// file exists and hope that's close enough.
- if (stat(pathBuf, &statBuf) == 0) {
+ if (stat(pathBuf, (struct stat*)&respBuf) == 0) {
resp.mError = 0;
} else {
resp.mError = errno;
}
} else {
AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
}
break;
case SANDBOX_FILE_STAT:
- if (DoStat(pathBuf, &statBuf, req.mFlags) == 0) {
+ if (DoStat(pathBuf, (struct stat*)&respBuf, req.mFlags) == 0) {
resp.mError = 0;
- ios[1].iov_base = &statBuf;
- ios[1].iov_len = sizeof(statBuf);
+ ios[1].iov_base = &respBuf;
+ ios[1].iov_len = sizeof(struct stat);
} else {
resp.mError = errno;
}
break;
case SANDBOX_FILE_CHMOD:
if (permissive || AllowOperation(W_OK, perms)) {
if (DoChmod(pathBuf, req.mFlags) == 0) {
@@ -661,16 +670,30 @@ SandboxBroker::ThreadMain(void)
if (permissive || AllowOperation(W_OK | X_OK, perms)) {
if (DoRmdir(pathBuf) == 0) {
resp.mError = 0;
} else {
resp.mError = errno;
}
}
break;
+
+ case SANDBOX_FILE_READLINK:
+ if (permissive || AllowOperation(R_OK, perms)) {
+ ssize_t respSize = DoReadlink(pathBuf, &respBuf, sizeof(respBuf));
+ if (respSize >= 0) {
+ resp.mError = 0;
+ ios[1].iov_base = &respBuf;
+ ios[1].iov_len = respSize;
+ } else {
+ resp.mError = errno;
+ }
+ }
+ break;
+
}
} else {
MOZ_ASSERT(perms == 0);
AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
}
const size_t numIO = ios[1].iov_len > 0 ? 2 : 1;
DebugOnly<const ssize_t> sent = SendWithFd(respfd, ios, numIO, openedFd);
--- a/security/sandbox/linux/broker/SandboxBrokerCommon.h
+++ b/security/sandbox/linux/broker/SandboxBrokerCommon.h
@@ -31,22 +31,25 @@ public:
SANDBOX_FILE_STAT,
SANDBOX_FILE_CHMOD,
SANDBOX_FILE_LINK,
SANDBOX_FILE_SYMLINK,
SANDBOX_FILE_MKDIR,
SANDBOX_FILE_RENAME,
SANDBOX_FILE_RMDIR,
SANDBOX_FILE_UNLINK,
+ SANDBOX_FILE_READLINK,
};
struct Request {
Operation mOp;
// For open, flags; for access, "mode"; for stat, O_NOFOLLOW for lstat.
int mFlags;
+ // Size of return value buffer, if any
+ size_t mBufSize;
// The rest of the packet is the pathname.
// SCM_RIGHTS for response socket attached.
};
struct Response {
int mError; // errno, or 0 for no error
// Followed by struct stat for stat/lstat.
// SCM_RIGHTS attached for successful open.