add 'mount_namespaces' option to use linux mount namespaces
if experimental_features and mount_namespaces is enabled and it is possible to "unshare" then instead of coolforkit exec coolforkitns which doesn't have any capabilities set and inprocess mount inside a namespace instead of calling coolmount Signed-off-by: Caolán McNamara <caolan.mcnamara@collabora.com> Change-Id: I48bef12b9156f41c78221e750a30aacee8a737a9
This commit is contained in:
parent
60c15d35e8
commit
85eaec2311
10 changed files with 509 additions and 324 deletions
13
Makefile.am
13
Makefile.am
|
@ -20,6 +20,7 @@ export ENABLE_DEBUG
|
|||
|
||||
bin_PROGRAMS = \
|
||||
coolforkit \
|
||||
coolforkitns \
|
||||
coolmount \
|
||||
coolstress \
|
||||
coolconvert coolconfig
|
||||
|
@ -120,6 +121,7 @@ shared_sources = common/FileUtil.cpp \
|
|||
common/Authorization.cpp \
|
||||
common/CommandControl.cpp \
|
||||
common/Simd.cpp \
|
||||
common/CoolMount.cpp \
|
||||
kit/KitQueue.cpp \
|
||||
net/DelaySocket.cpp \
|
||||
net/HttpRequest.cpp \
|
||||
|
@ -209,6 +211,12 @@ coolforkit_SOURCES = $(coolforkit_sources) \
|
|||
|
||||
coolforkit_LDADD = libsimd.a
|
||||
|
||||
coolforkitns_SOURCES = $(coolforkit_sources) \
|
||||
$(shared_sources) \
|
||||
kit/forkit-main.cpp
|
||||
|
||||
coolforkitns_LDADD = libsimd.a
|
||||
|
||||
if ENABLE_DEBUG
|
||||
coolwsd_inproc_SOURCES = $(coolwsd_sources) \
|
||||
$(shared_sources) \
|
||||
|
@ -273,7 +281,8 @@ clientnb_SOURCES = net/clientnb.cpp \
|
|||
common/Util.cpp \
|
||||
common/Util-desktop.cpp
|
||||
|
||||
coolmount_SOURCES = tools/mount.cpp
|
||||
coolmount_SOURCES = tools/mount.cpp \
|
||||
common/CoolMount.cpp
|
||||
|
||||
coolmap_SOURCES = tools/map.cpp
|
||||
|
||||
|
@ -476,7 +485,7 @@ endif
|
|||
if ENABLE_LIBFUZZER
|
||||
CLEANUP_DEPS=
|
||||
else
|
||||
CLEANUP_DEPS=coolwsd coolmount coolforkit
|
||||
CLEANUP_DEPS=coolwsd coolmount coolforkit coolforkitns
|
||||
endif
|
||||
|
||||
# Build coolwsd and coolmount first, so we can cleanup before updating
|
||||
|
|
341
common/CoolMount.cpp
Normal file
341
common/CoolMount.cpp
Normal file
|
@ -0,0 +1,341 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
||||
/*
|
||||
* Copyright the Collabora Online contributors.
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
/*
|
||||
* This is a very tiny helper to allow overlay mounting.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <security.h>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <stdlib.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#define MOUNT mount_wrapper
|
||||
#define MS_MGC_VAL 0
|
||||
#define MS_SILENT 0
|
||||
#define MS_NODEV 0
|
||||
#define MS_UNBINDABLE 0
|
||||
#define MS_BIND 1
|
||||
#define MS_REC 2
|
||||
#define MS_REMOUNT 4
|
||||
#define MS_NOATIME 8
|
||||
#define MS_NOSUID 16
|
||||
#define MS_RDONLY 32
|
||||
|
||||
void
|
||||
build_iovec(struct iovec **iov, int *iovlen, const char *name, const void *val,
|
||||
size_t len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (*iovlen < 0)
|
||||
return;
|
||||
i = *iovlen;
|
||||
*iov = reinterpret_cast<struct iovec*>(realloc(*iov, sizeof **iov * (i + 2)));
|
||||
if (*iov == NULL) {
|
||||
*iovlen = -1;
|
||||
return;
|
||||
}
|
||||
(*iov)[i].iov_base = strdup(name);
|
||||
(*iov)[i].iov_len = strlen(name) + 1;
|
||||
i++;
|
||||
(*iov)[i].iov_base = const_cast<void*>(val);
|
||||
if (len == (size_t)-1) {
|
||||
if (val != NULL)
|
||||
len = strlen(reinterpret_cast<const char*>(val)) + 1;
|
||||
else
|
||||
len = 0;
|
||||
}
|
||||
(*iov)[i].iov_len = (int)len;
|
||||
*iovlen = ++i;
|
||||
}
|
||||
|
||||
int mount_wrapper(const char *source, const char *target,
|
||||
const char *filesystemtype, unsigned long mountflags,
|
||||
const void *data)
|
||||
{
|
||||
struct iovec *iov = NULL;
|
||||
int iovlen = 0;
|
||||
int freebsd_flags = 0;
|
||||
(void)data;
|
||||
|
||||
if(mountflags & MS_BIND)
|
||||
filesystemtype = "nullfs";
|
||||
|
||||
if(mountflags & MS_REMOUNT)
|
||||
freebsd_flags |= MNT_UPDATE;
|
||||
|
||||
if(mountflags & MS_NOATIME)
|
||||
freebsd_flags |= MNT_NOATIME;
|
||||
|
||||
if(mountflags & MS_NOSUID)
|
||||
freebsd_flags |= MNT_NOSUID;
|
||||
|
||||
if(mountflags & MS_RDONLY)
|
||||
freebsd_flags |= MNT_RDONLY;
|
||||
|
||||
// TODO: handle MS_REC?
|
||||
|
||||
build_iovec(&iov, &iovlen, "fstype", reinterpret_cast<const void*>(filesystemtype), (size_t)-1);
|
||||
build_iovec(&iov, &iovlen, "fspath", reinterpret_cast<const void*>(target), (size_t)-1);
|
||||
build_iovec(&iov, &iovlen, "from", reinterpret_cast<const void*>(source), (size_t)-1);
|
||||
|
||||
return nmount(iov, iovlen, freebsd_flags);
|
||||
}
|
||||
|
||||
#define MNT_DETACH 1
|
||||
|
||||
int umount2(const char *target, int flags)
|
||||
{
|
||||
struct statfs* mntbufs;
|
||||
int numInfos = getmntinfo(&mntbufs, MNT_WAIT);
|
||||
bool targetMounted = false;
|
||||
|
||||
for (int i = 0; i < numInfos; i++)
|
||||
{
|
||||
if (!strcmp(target, mntbufs[i].f_mntonname))
|
||||
{
|
||||
targetMounted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetMounted)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(flags == MNT_DETACH)
|
||||
flags = 0;
|
||||
|
||||
return unmount(target, flags);
|
||||
}
|
||||
#else
|
||||
#define MOUNT mount
|
||||
#endif
|
||||
|
||||
void usage(const char* program)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <-b|-r> <source path> <target path>\n", program);
|
||||
fprintf(stderr, " %s -u <target>.\n", program);
|
||||
#ifdef __FreeBSD__
|
||||
fprintf(stderr, " %s -d <target>.\n", program);
|
||||
#endif
|
||||
fprintf(stderr, " -b bind and mount the source to target.\n");
|
||||
fprintf(stderr, " -r bind and mount the source to target as readonly.\n");
|
||||
#ifdef __FreeBSD__
|
||||
fprintf(stderr, " -d mount minimal devfs layout (random and urandom) to target.\n");
|
||||
#endif
|
||||
fprintf(stderr, " -u to unmount the target.\n");
|
||||
}
|
||||
|
||||
int domount(int argc, const char* const* argv)
|
||||
{
|
||||
/*WARNING: PRIVILEGED CODE CHECKING START */
|
||||
/*WARNING*/ if (!hasCorrectUID(/* appName = */ "coolmount"))
|
||||
/*WARNING*/ {
|
||||
/*WARNING*/ fprintf(stderr, "Aborting.\n");
|
||||
/*WARNING*/ return EX_SOFTWARE;
|
||||
/*WARNING*/ }
|
||||
/*WARNING: PRIVILEGED CODE CHECKING END */
|
||||
|
||||
const char* program = argv[0];
|
||||
if (argc < 3)
|
||||
{
|
||||
usage(program);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
const char* option = argv[1];
|
||||
if (argc == 3 && strcmp(option, "-u") == 0) // Unmount
|
||||
{
|
||||
const char* target = argv[2];
|
||||
|
||||
struct stat sb;
|
||||
const bool target_exists = (stat(target, &sb) == 0 && S_ISDIR(sb.st_mode));
|
||||
|
||||
// Do nothing if target doesn't exist.
|
||||
if (target_exists)
|
||||
{
|
||||
// Unmount the target, first by detaching. This should succeed.
|
||||
int retval = umount2(target, MNT_DETACH);
|
||||
if (retval != 0)
|
||||
{
|
||||
if (errno != EINVAL)
|
||||
{
|
||||
// Don't complain where MNT_DETACH is unsupported. Fall back to forcing instead.
|
||||
fprintf(stderr, "%s: unmount failed to detach [%s]: %s.\n", program, target,
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
// Now try to force the unmounting, which isn't supported on all filesystems.
|
||||
retval = umount2(target, MNT_FORCE);
|
||||
if (retval != 0)
|
||||
{
|
||||
// From man umount(2), MNT_FORCE is not commonly supported:
|
||||
// As at Linux 4.12, MNT_FORCE is supported only on the following filesystems: 9p (since
|
||||
// Linux 2.6.16), ceph (since Linux 2.6.34), cifs (since Linux 2.6.12),
|
||||
// fuse (since Linux 2.6.16), lustre (since Linux 3.11), and NFS (since Linux 2.1.116).
|
||||
if (errno != EINVAL)
|
||||
{
|
||||
// Complain to capture the reason of failure.
|
||||
fprintf(stderr, "%s: forced unmount of [%s] failed: %s.\n", program, target,
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
return EX_SOFTWARE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef __FreeBSD__
|
||||
else if (argc == 3 && strcmp(option, "-d") == 0) // Mount devfs
|
||||
{
|
||||
const char* target = argv[2];
|
||||
|
||||
struct stat sb;
|
||||
const bool target_exists = (stat(target, &sb) == 0 && S_ISDIR(sb.st_mode));
|
||||
|
||||
if (!target_exists)
|
||||
{
|
||||
fprintf(stderr, "%s: cannot mount on invalid target directory [%s].\n", program,
|
||||
target);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
struct iovec *iov = NULL;
|
||||
int iovlen = 0;
|
||||
|
||||
build_iovec(&iov, &iovlen, "fstype", "devfs", (size_t)-1);
|
||||
build_iovec(&iov, &iovlen, "fspath", reinterpret_cast<const void*>(target), (size_t)-1);
|
||||
build_iovec(&iov, &iovlen, "from", "devfs", (size_t)-1);
|
||||
// See /etc/defaults/devfs.rules
|
||||
// [devfsrules_jail=4]
|
||||
build_iovec(&iov, &iovlen, "ruleset", "4", (size_t)-1);
|
||||
|
||||
int retval = nmount(iov, iovlen, 0);
|
||||
if (retval)
|
||||
{
|
||||
fprintf(stderr, "%s: mount failed create to devfs layout in [%s]: %s.\n", program, target,
|
||||
strerror(errno));
|
||||
return EX_SOFTWARE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (argc == 4) // Mount
|
||||
{
|
||||
const char* source = argv[2];
|
||||
struct stat sb;
|
||||
if (stat(source, &sb))
|
||||
{
|
||||
fprintf(stderr, "%s: cannot mount from invalid source [%s]. stat failed with %s\n",
|
||||
program, source, strerror(errno));
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
const bool isDir = S_ISDIR(sb.st_mode);
|
||||
const bool isCharDev = S_ISCHR(sb.st_mode); // We don't support regular files.
|
||||
if (isCharDev)
|
||||
{
|
||||
// Even for character devices, we only support the random devices.
|
||||
if (strstr("/dev/random", source) && strstr("/dev/urandom", source))
|
||||
{
|
||||
fprintf(stderr, "%s: cannot mount untrusted character-device [%s]", program,
|
||||
source);
|
||||
return EX_USAGE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isDir && !isCharDev)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"%s: cannot mount from invalid source [%s], it is neither a file nor a "
|
||||
"directory.\n",
|
||||
program, source);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
const char* target = argv[3];
|
||||
if (stat(target, &sb))
|
||||
{
|
||||
fprintf(stderr, "%s: cannot mount on invalid target [%s]. stat failed with %s\n",
|
||||
program, target, strerror(errno));
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
const bool target_exists =
|
||||
((isDir && S_ISDIR(sb.st_mode)) || (isCharDev && S_ISREG(sb.st_mode)));
|
||||
if (!target_exists)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"%s: cannot mount on invalid target [%s], it is not a %s as the source\n",
|
||||
program, target, isDir ? "directory" : "file");
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
// Mount the source path as the target path.
|
||||
// First bind to mount an existing directory node into the chroot.
|
||||
// MS_BIND ignores other flags.
|
||||
if (strcmp(option, "-b") == 0) // Shared or Bind Mount.
|
||||
{
|
||||
const int retval
|
||||
= MOUNT(source, target, nullptr, (MS_MGC_VAL | MS_BIND | MS_REC), nullptr);
|
||||
if (retval)
|
||||
{
|
||||
fprintf(stderr, "%s: mount failed to bind [%s] to [%s]: %s.\n", program, source,
|
||||
target, strerror(errno));
|
||||
return EX_SOFTWARE;
|
||||
}
|
||||
}
|
||||
else if (strcmp(option, "-r") == 0) // Readonly Mount.
|
||||
{
|
||||
// Now we need to set read-only and other flags with a remount.
|
||||
int retval = MOUNT(source, target, nullptr,
|
||||
(MS_BIND | MS_REC | MS_REMOUNT | MS_NOATIME | MS_NODEV | MS_NOSUID
|
||||
| MS_RDONLY | MS_SILENT),
|
||||
nullptr);
|
||||
if (retval)
|
||||
{
|
||||
fprintf(stderr, "%s: mount failed remount [%s] readonly: %s.\n", program, target,
|
||||
strerror(errno));
|
||||
return EX_SOFTWARE;
|
||||
}
|
||||
|
||||
retval = MOUNT(source, target, nullptr, (MS_UNBINDABLE | MS_REC), nullptr);
|
||||
if (retval)
|
||||
{
|
||||
fprintf(stderr, "%s: mount failed make [%s] private: %s.\n", program, target,
|
||||
strerror(errno));
|
||||
return EX_SOFTWARE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
usage(program);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
fflush(stderr);
|
||||
return EX_OK;
|
||||
}
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
|
@ -14,8 +14,10 @@
|
|||
#include "FileUtil.hpp"
|
||||
#include "JailUtil.hpp"
|
||||
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sysexits.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#ifdef __linux__
|
||||
|
@ -25,20 +27,112 @@
|
|||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "Log.hpp"
|
||||
#include <SigUtil.hpp>
|
||||
|
||||
extern int domount(int argc, const char* const* argv);
|
||||
|
||||
namespace JailUtil
|
||||
{
|
||||
|
||||
static const std::string CoolTestMountpoint = "cool_test_mount";
|
||||
|
||||
static void setdeny()
|
||||
{
|
||||
std::ofstream of("/proc/self/setgroups");
|
||||
of << "deny";
|
||||
}
|
||||
|
||||
static void mapuser(uid_t origuid, uid_t newuid, gid_t origgid, gid_t newgid)
|
||||
{
|
||||
{
|
||||
std::ofstream of("/proc/self/uid_map");
|
||||
of << newuid << " " << origuid << " 1";
|
||||
}
|
||||
|
||||
{
|
||||
std::ofstream of("/proc/self/gid_map");
|
||||
of << newgid << " " << origgid << " 1";
|
||||
}
|
||||
}
|
||||
|
||||
bool becomeMountingUser(uid_t uid, gid_t gid)
|
||||
{
|
||||
#ifdef __linux__
|
||||
// Put this process into its own user and mount namespace.
|
||||
if (unshare(CLONE_NEWNS | CLONE_NEWUSER) != 0)
|
||||
{
|
||||
// having multiple threads is a source of failure f.e.
|
||||
fprintf(stderr, "becomeMountingUser, unshare failed %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do not propagate any mounts from this new namespace to the system.
|
||||
if (mount("none", "/", nullptr, MS_REC | MS_PRIVATE, nullptr) != 0)
|
||||
{
|
||||
fprintf(stderr, "becomeMountingUser, root mount failed %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
setdeny();
|
||||
|
||||
// Map this user as the root user of the new namespace
|
||||
mapuser(uid, 0, gid, 0);
|
||||
|
||||
return true;
|
||||
#else
|
||||
(void)uid;
|
||||
(void)gid;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool restorePremountUser(uid_t uid, gid_t gid)
|
||||
{
|
||||
#ifdef __linux__
|
||||
if (unshare(CLONE_NEWUSER) != 0)
|
||||
{
|
||||
// having multiple threads is a source of failure f.e.
|
||||
fprintf(stderr, "restorePremountUser, unshare failed %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
// undo map of this user to root
|
||||
mapuser(0, uid, 0, gid);
|
||||
|
||||
assert(geteuid() == uid);
|
||||
assert(getegid() == gid);
|
||||
|
||||
return true;
|
||||
#else
|
||||
(void)uid;
|
||||
(void)gid;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool coolmount(const std::string& arg, std::string source, std::string target)
|
||||
{
|
||||
source = Util::trim(source, '/');
|
||||
target = Util::trim(target, '/');
|
||||
|
||||
if (isMountNamespacesEnabled())
|
||||
{
|
||||
const char *argv[4];
|
||||
argv[0] = "notcoolmount";
|
||||
int argc = 1;
|
||||
if (!arg.empty())
|
||||
argv[argc++] = arg.c_str();
|
||||
if (!source.empty())
|
||||
argv[argc++] = source.c_str();
|
||||
if (!target.empty())
|
||||
argv[argc++] = target.c_str();
|
||||
return domount(argc, argv) == EX_OK;
|
||||
}
|
||||
|
||||
const std::string cmd = Poco::Path(Util::getApplicationPath(), "coolmount").toString() + ' '
|
||||
+ arg + ' ' + source + ' ' + target;
|
||||
LOG_TRC("Executing coolmount command: " << cmd);
|
||||
|
@ -343,6 +437,21 @@ bool isBindMountingEnabled()
|
|||
return std::getenv(BIND_MOUNTING_ENVAR_NAME) != nullptr;
|
||||
}
|
||||
|
||||
constexpr const char* NAMESPACE_MOUNTING_ENVAR_NAME = "COOL_NAMESPACE_MOUNT";
|
||||
|
||||
void enableMountNamespaces()
|
||||
{
|
||||
// Set the envar to enable.
|
||||
setenv(NAMESPACE_MOUNTING_ENVAR_NAME, "1", 1);
|
||||
}
|
||||
|
||||
bool isMountNamespacesEnabled()
|
||||
{
|
||||
// Check if we have a valid envar set.
|
||||
return std::getenv(NAMESPACE_MOUNTING_ENVAR_NAME) != nullptr;
|
||||
}
|
||||
|
||||
|
||||
namespace SysTemplate
|
||||
{
|
||||
/// The network and other system files we need to keep up-to-date in jails.
|
||||
|
|
|
@ -28,6 +28,14 @@ constexpr const char CHILDROOT_TMP_INCOMING_PATH[] = "/tmp/incoming";
|
|||
/// The LO installation directory with jail.
|
||||
constexpr const char LO_JAIL_SUBPATH[] = "lo";
|
||||
|
||||
/// Try to put this process into its own user and mount namespace and
|
||||
/// set as root within that namespace to allow mounting
|
||||
/// There cannot be other threads running when calling these or it will
|
||||
/// fail.
|
||||
bool becomeMountingUser(uid_t uid, gid_t gid);
|
||||
/// We could switch back to normal user within the namespace too
|
||||
bool restorePremountUser(uid_t uid, gid_t gid);
|
||||
|
||||
/// Bind mount a jail directory.
|
||||
bool bind(const std::string& source, const std::string& target);
|
||||
|
||||
|
@ -61,6 +69,12 @@ void disableBindMounting();
|
|||
/// Returns true iff bind-mounting is enabled in this process.
|
||||
bool isBindMountingEnabled();
|
||||
|
||||
/// Enable namespace-mounting in this process.
|
||||
void enableMountNamespaces();
|
||||
|
||||
/// Returns true iff namespace-mounting is enabled in this process.
|
||||
bool isMountNamespacesEnabled();
|
||||
|
||||
namespace SysTemplate
|
||||
{
|
||||
/// Setup links for /dev/random and /dev/urandom in systemplate.
|
||||
|
|
|
@ -57,6 +57,7 @@ echo "account required pam_unix.so" >> %{buildroot}/etc/pam.d/coolwsd
|
|||
/usr/bin/coolwsd-systemplate-setup
|
||||
/usr/bin/loolwsd-systemplate-setup
|
||||
/usr/bin/coolforkit
|
||||
/usr/bin/coolforkitns
|
||||
/usr/bin/coolconvert
|
||||
/usr/bin/coolconfig
|
||||
/usr/bin/loolconfig
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
<file_server_root_path desc="Path to the directory that should be considered root for the file server. This should be the directory containing cool." type="path" relative="true" default="browser/../"></file_server_root_path>
|
||||
<hexify_embedded_urls desc="Enable to protect encoded URLs from getting decoded by intermediate hops. Particularly useful on Azure deployments" type="bool" default="false"></hexify_embedded_urls>
|
||||
<experimental_features desc="Enable/Disable experimental features" type="bool" default="@ENABLE_EXPERIMENTAL@">@ENABLE_EXPERIMENTAL@</experimental_features>
|
||||
<mount_namespaces desc="Use mount namespaces instead of coolmount. Only possible to experimental_features is enabled." type="bool" default="false"></mount_namespaces>
|
||||
|
||||
<memproportion desc="The maximum percentage of available memory consumed by all of the @APP_NAME@ processes, after which we start cleaning up idle documents. If cgroup memory limits are set, this is the maximum percentage of that limit to consume." type="double" default="80.0"></memproportion>
|
||||
<num_prespawn_children desc="Number of child processes to keep started in advance and waiting for new clients." type="uint" default="4">@NUM_PRESPAWN_CHILDREN@</num_prespawn_children>
|
||||
|
|
|
@ -251,12 +251,12 @@ static bool haveCapability(cap_value_t capability)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool haveCorrectCapabilities()
|
||||
static bool haveCorrectCapabilities(bool useMountNamespaces)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
// Do check them all, don't shortcut with &&
|
||||
if (!haveCapability(CAP_SYS_CHROOT))
|
||||
if (!useMountNamespaces && !haveCapability(CAP_SYS_CHROOT))
|
||||
result = false;
|
||||
if (!haveCapability(CAP_FOWNER))
|
||||
result = false;
|
||||
|
@ -266,7 +266,7 @@ static bool haveCorrectCapabilities()
|
|||
return result;
|
||||
}
|
||||
#else
|
||||
static bool haveCorrectCapabilities()
|
||||
static bool haveCorrectCapabilities(bool /*useMountNamespaces*/)
|
||||
{
|
||||
// chroot() can only be called by root
|
||||
return getuid() == 0;
|
||||
|
@ -621,6 +621,7 @@ int forkit_main(int argc, char** argv)
|
|||
}
|
||||
LogDisabledAreas = logDisabledAreas ? logDisabledAreas : "";
|
||||
|
||||
bool useMountNamespaces = false;
|
||||
std::string childRoot;
|
||||
std::string sysTemplate;
|
||||
std::string loTemplate;
|
||||
|
@ -709,6 +710,9 @@ int forkit_main(int argc, char** argv)
|
|||
NoSeccomp = true;
|
||||
}
|
||||
|
||||
else if (std::strstr(cmd, "--namespace") == cmd)
|
||||
useMountNamespaces = true;
|
||||
|
||||
else if (std::strstr(cmd, "--ui") == cmd)
|
||||
{
|
||||
eq = std::strchr(cmd, '=');
|
||||
|
@ -736,7 +740,7 @@ int forkit_main(int argc, char** argv)
|
|||
if (!std::getenv("LD_BIND_NOW")) // must be set by parent.
|
||||
LOG_INF("Note: LD_BIND_NOW is not set.");
|
||||
|
||||
if (!NoCapsForKit && !haveCorrectCapabilities())
|
||||
if (!NoCapsForKit && !haveCorrectCapabilities(useMountNamespaces))
|
||||
{
|
||||
LOG_FTL("Capabilities are not set for the coolforkit program.");
|
||||
LOG_FTL("Please make sure that the current partition was *not* mounted with the 'nosuid' option.");
|
||||
|
|
319
tools/mount.cpp
319
tools/mount.cpp
|
@ -13,329 +13,14 @@
|
|||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <security.h>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <stdlib.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#define MOUNT mount_wrapper
|
||||
#define MS_MGC_VAL 0
|
||||
#define MS_SILENT 0
|
||||
#define MS_NODEV 0
|
||||
#define MS_UNBINDABLE 0
|
||||
#define MS_BIND 1
|
||||
#define MS_REC 2
|
||||
#define MS_REMOUNT 4
|
||||
#define MS_NOATIME 8
|
||||
#define MS_NOSUID 16
|
||||
#define MS_RDONLY 32
|
||||
|
||||
void
|
||||
build_iovec(struct iovec **iov, int *iovlen, const char *name, const void *val,
|
||||
size_t len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (*iovlen < 0)
|
||||
return;
|
||||
i = *iovlen;
|
||||
*iov = reinterpret_cast<struct iovec*>(realloc(*iov, sizeof **iov * (i + 2)));
|
||||
if (*iov == NULL) {
|
||||
*iovlen = -1;
|
||||
return;
|
||||
}
|
||||
(*iov)[i].iov_base = strdup(name);
|
||||
(*iov)[i].iov_len = strlen(name) + 1;
|
||||
i++;
|
||||
(*iov)[i].iov_base = const_cast<void*>(val);
|
||||
if (len == (size_t)-1) {
|
||||
if (val != NULL)
|
||||
len = strlen(reinterpret_cast<const char*>(val)) + 1;
|
||||
else
|
||||
len = 0;
|
||||
}
|
||||
(*iov)[i].iov_len = (int)len;
|
||||
*iovlen = ++i;
|
||||
}
|
||||
|
||||
int mount_wrapper(const char *source, const char *target,
|
||||
const char *filesystemtype, unsigned long mountflags,
|
||||
const void *data)
|
||||
{
|
||||
struct iovec *iov = NULL;
|
||||
int iovlen = 0;
|
||||
int freebsd_flags = 0;
|
||||
(void)data;
|
||||
|
||||
if(mountflags & MS_BIND)
|
||||
filesystemtype = "nullfs";
|
||||
|
||||
if(mountflags & MS_REMOUNT)
|
||||
freebsd_flags |= MNT_UPDATE;
|
||||
|
||||
if(mountflags & MS_NOATIME)
|
||||
freebsd_flags |= MNT_NOATIME;
|
||||
|
||||
if(mountflags & MS_NOSUID)
|
||||
freebsd_flags |= MNT_NOSUID;
|
||||
|
||||
if(mountflags & MS_RDONLY)
|
||||
freebsd_flags |= MNT_RDONLY;
|
||||
|
||||
// TODO: handle MS_REC?
|
||||
|
||||
build_iovec(&iov, &iovlen, "fstype", reinterpret_cast<const void*>(filesystemtype), (size_t)-1);
|
||||
build_iovec(&iov, &iovlen, "fspath", reinterpret_cast<const void*>(target), (size_t)-1);
|
||||
build_iovec(&iov, &iovlen, "from", reinterpret_cast<const void*>(source), (size_t)-1);
|
||||
|
||||
return nmount(iov, iovlen, freebsd_flags);
|
||||
}
|
||||
|
||||
#define MNT_DETACH 1
|
||||
|
||||
int umount2(const char *target, int flags)
|
||||
{
|
||||
struct statfs* mntbufs;
|
||||
int numInfos = getmntinfo(&mntbufs, MNT_WAIT);
|
||||
bool targetMounted = false;
|
||||
|
||||
for (int i = 0; i < numInfos; i++)
|
||||
{
|
||||
if (!strcmp(target, mntbufs[i].f_mntonname))
|
||||
{
|
||||
targetMounted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetMounted)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(flags == MNT_DETACH)
|
||||
flags = 0;
|
||||
|
||||
return unmount(target, flags);
|
||||
}
|
||||
#else
|
||||
#define MOUNT mount
|
||||
#endif
|
||||
|
||||
void usage(const char* program)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <-b|-r> <source path> <target path>\n", program);
|
||||
fprintf(stderr, " %s -u <target>.\n", program);
|
||||
#ifdef __FreeBSD__
|
||||
fprintf(stderr, " %s -d <target>.\n", program);
|
||||
#endif
|
||||
fprintf(stderr, " -b bind and mount the source to target.\n");
|
||||
fprintf(stderr, " -r bind and mount the source to target as readonly.\n");
|
||||
#ifdef __FreeBSD__
|
||||
fprintf(stderr, " -d mount minimal devfs layout (random and urandom) to target.\n");
|
||||
#endif
|
||||
fprintf(stderr, " -u to unmount the target.\n");
|
||||
}
|
||||
extern int domount(int argc, const char* const* argv);
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
/*WARNING: PRIVILEGED CODE CHECKING START */
|
||||
/*WARNING*/ if (!hasCorrectUID(/* appName = */ "coolmount"))
|
||||
/*WARNING*/ {
|
||||
/*WARNING*/ fprintf(stderr, "Aborting.\n");
|
||||
/*WARNING*/ return EX_SOFTWARE;
|
||||
/*WARNING*/ }
|
||||
/*WARNING: PRIVILEGED CODE CHECKING END */
|
||||
|
||||
const char* program = argv[0];
|
||||
if (argc < 3)
|
||||
{
|
||||
usage(program);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
const char* option = argv[1];
|
||||
if (argc == 3 && strcmp(option, "-u") == 0) // Unmount
|
||||
{
|
||||
const char* target = argv[2];
|
||||
|
||||
struct stat sb;
|
||||
const bool target_exists = (stat(target, &sb) == 0 && S_ISDIR(sb.st_mode));
|
||||
|
||||
// Do nothing if target doesn't exist.
|
||||
if (target_exists)
|
||||
{
|
||||
// Unmount the target, first by detaching. This should succeed.
|
||||
int retval = umount2(target, MNT_DETACH);
|
||||
if (retval != 0)
|
||||
{
|
||||
if (errno != EINVAL)
|
||||
{
|
||||
// Don't complain where MNT_DETACH is unsupported. Fall back to forcing instead.
|
||||
fprintf(stderr, "%s: unmount failed to detach [%s]: %s.\n", program, target,
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
// Now try to force the unmounting, which isn't supported on all filesystems.
|
||||
retval = umount2(target, MNT_FORCE);
|
||||
if (retval != 0)
|
||||
{
|
||||
// From man umount(2), MNT_FORCE is not commonly supported:
|
||||
// As at Linux 4.12, MNT_FORCE is supported only on the following filesystems: 9p (since
|
||||
// Linux 2.6.16), ceph (since Linux 2.6.34), cifs (since Linux 2.6.12),
|
||||
// fuse (since Linux 2.6.16), lustre (since Linux 3.11), and NFS (since Linux 2.1.116).
|
||||
if (errno != EINVAL)
|
||||
{
|
||||
// Complain to capture the reason of failure.
|
||||
fprintf(stderr, "%s: forced unmount of [%s] failed: %s.\n", program, target,
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
return EX_SOFTWARE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef __FreeBSD__
|
||||
else if (argc == 3 && strcmp(option, "-d") == 0) // Mount devfs
|
||||
{
|
||||
const char* target = argv[2];
|
||||
|
||||
struct stat sb;
|
||||
const bool target_exists = (stat(target, &sb) == 0 && S_ISDIR(sb.st_mode));
|
||||
|
||||
if (!target_exists)
|
||||
{
|
||||
fprintf(stderr, "%s: cannot mount on invalid target directory [%s].\n", program,
|
||||
target);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
struct iovec *iov = NULL;
|
||||
int iovlen = 0;
|
||||
|
||||
build_iovec(&iov, &iovlen, "fstype", "devfs", (size_t)-1);
|
||||
build_iovec(&iov, &iovlen, "fspath", reinterpret_cast<const void*>(target), (size_t)-1);
|
||||
build_iovec(&iov, &iovlen, "from", "devfs", (size_t)-1);
|
||||
// See /etc/defaults/devfs.rules
|
||||
// [devfsrules_jail=4]
|
||||
build_iovec(&iov, &iovlen, "ruleset", "4", (size_t)-1);
|
||||
|
||||
int retval = nmount(iov, iovlen, 0);
|
||||
if (retval)
|
||||
{
|
||||
fprintf(stderr, "%s: mount failed create to devfs layout in [%s]: %s.\n", program, target,
|
||||
strerror(errno));
|
||||
return EX_SOFTWARE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (argc == 4) // Mount
|
||||
{
|
||||
const char* source = argv[2];
|
||||
struct stat sb;
|
||||
if (stat(source, &sb))
|
||||
{
|
||||
fprintf(stderr, "%s: cannot mount from invalid source [%s]. stat failed with %s\n",
|
||||
program, source, strerror(errno));
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
const bool isDir = S_ISDIR(sb.st_mode);
|
||||
const bool isCharDev = S_ISCHR(sb.st_mode); // We don't support regular files.
|
||||
if (isCharDev)
|
||||
{
|
||||
// Even for character devices, we only support the random devices.
|
||||
if (strstr("/dev/random", source) && strstr("/dev/urandom", source))
|
||||
{
|
||||
fprintf(stderr, "%s: cannot mount untrusted character-device [%s]", program,
|
||||
source);
|
||||
return EX_USAGE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isDir && !isCharDev)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"%s: cannot mount from invalid source [%s], it is neither a file nor a "
|
||||
"directory.\n",
|
||||
program, source);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
const char* target = argv[3];
|
||||
if (stat(target, &sb))
|
||||
{
|
||||
fprintf(stderr, "%s: cannot mount on invalid target [%s]. stat failed with %s\n",
|
||||
program, target, strerror(errno));
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
const bool target_exists =
|
||||
((isDir && S_ISDIR(sb.st_mode)) || (isCharDev && S_ISREG(sb.st_mode)));
|
||||
if (!target_exists)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"%s: cannot mount on invalid target [%s], it is not a %s as the source\n",
|
||||
program, target, isDir ? "directory" : "file");
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
// Mount the source path as the target path.
|
||||
// First bind to mount an existing directory node into the chroot.
|
||||
// MS_BIND ignores other flags.
|
||||
if (strcmp(option, "-b") == 0) // Shared or Bind Mount.
|
||||
{
|
||||
const int retval
|
||||
= MOUNT(source, target, nullptr, (MS_MGC_VAL | MS_BIND | MS_REC), nullptr);
|
||||
if (retval)
|
||||
{
|
||||
fprintf(stderr, "%s: mount failed to bind [%s] to [%s]: %s.\n", program, source,
|
||||
target, strerror(errno));
|
||||
return EX_SOFTWARE;
|
||||
}
|
||||
}
|
||||
else if (strcmp(option, "-r") == 0) // Readonly Mount.
|
||||
{
|
||||
// Now we need to set read-only and other flags with a remount.
|
||||
int retval = MOUNT(source, target, nullptr,
|
||||
(MS_BIND | MS_REC | MS_REMOUNT | MS_NOATIME | MS_NODEV | MS_NOSUID
|
||||
| MS_RDONLY | MS_SILENT),
|
||||
nullptr);
|
||||
if (retval)
|
||||
{
|
||||
fprintf(stderr, "%s: mount failed remount [%s] readonly: %s.\n", program, target,
|
||||
strerror(errno));
|
||||
return EX_SOFTWARE;
|
||||
}
|
||||
|
||||
retval = MOUNT(source, target, nullptr, (MS_UNBINDABLE | MS_REC), nullptr);
|
||||
if (retval)
|
||||
{
|
||||
fprintf(stderr, "%s: mount failed make [%s] private: %s.\n", program, target,
|
||||
strerror(errno));
|
||||
return EX_SOFTWARE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
usage(program);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
fflush(stderr);
|
||||
return EX_OK;
|
||||
return domount(argc, argv);
|
||||
}
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||
|
|
|
@ -718,6 +718,7 @@ std::string COOLWSD::ConfigFile = COOLWSD_CONFIGDIR "/coolwsd.xml";
|
|||
std::string COOLWSD::ConfigDir = COOLWSD_CONFIGDIR "/conf.d";
|
||||
bool COOLWSD::EnableTraceEventLogging = false;
|
||||
bool COOLWSD::EnableAccessibility = false;
|
||||
bool COOLWSD::EnableMountNamespaces= false;
|
||||
FILE *COOLWSD::TraceEventFile = NULL;
|
||||
std::string COOLWSD::LogLevel = "trace";
|
||||
std::string COOLWSD::LogLevelStartup = "trace";
|
||||
|
@ -1997,6 +1998,7 @@ void COOLWSD::innerInitialize(Application& self)
|
|||
{ "logging.disable_server_audit", "false" },
|
||||
{ "browser_logging", "false" },
|
||||
{ "mount_jail_tree", "true" },
|
||||
{ "mount_namespaces", "false" },
|
||||
{ "net.connection_timeout_secs", "30" },
|
||||
{ "net.listen", "any" },
|
||||
{ "net.proto", "all" },
|
||||
|
@ -2167,6 +2169,12 @@ void COOLWSD::innerInitialize(Application& self)
|
|||
// Experimental features.
|
||||
EnableExperimental = getConfigValue<bool>(conf, "experimental_features", false);
|
||||
|
||||
const bool UseMountNamespaces = EnableExperimental && getConfigValue<bool>(conf, "mount_namespaces", false);
|
||||
// attempt this early before starting logging which spawns a thread
|
||||
EnableMountNamespaces = UseMountNamespaces && JailUtil::becomeMountingUser(geteuid(), getegid());
|
||||
if (EnableMountNamespaces)
|
||||
JailUtil::enableMountNamespaces();
|
||||
|
||||
EnableAccessibility = getConfigValue<bool>(conf, "accessibility.enable", false);
|
||||
|
||||
// Setup user interface mode
|
||||
|
@ -2532,6 +2540,9 @@ void COOLWSD::innerInitialize(Application& self)
|
|||
std::string fontsMissingHandling = config::getString("fonts_missing.handling", "log");
|
||||
setenv("FONTS_MISSING_HANDLING", fontsMissingHandling.c_str(), 1);
|
||||
|
||||
if (UseMountNamespaces && !EnableMountNamespaces)
|
||||
LOG_WRN("Using mount namespaces failed.");
|
||||
|
||||
IsBindMountingEnabled = getConfigValue<bool>(conf, "mount_jail_tree", true);
|
||||
#if CODE_COVERAGE
|
||||
// Code coverage is not supported with bind-mounting.
|
||||
|
@ -3477,7 +3488,16 @@ bool COOLWSD::createForKit()
|
|||
#elif VALGRIND_COOLFORKIT
|
||||
std::string forKitPath = "/usr/bin/valgrind";
|
||||
#else
|
||||
std::string forKitPath = parentPath + "coolforkit";
|
||||
std::string forKitPath = parentPath;
|
||||
if (EnableMountNamespaces)
|
||||
{
|
||||
forKitPath += "coolforkitns";
|
||||
args.push_back("--namespace");
|
||||
}
|
||||
else
|
||||
{
|
||||
forKitPath += "coolforkit";
|
||||
}
|
||||
#endif
|
||||
|
||||
// Always reap first, in case we haven't done so yet.
|
||||
|
|
|
@ -276,6 +276,7 @@ public:
|
|||
static std::string LOKitVersion;
|
||||
static bool EnableTraceEventLogging;
|
||||
static bool EnableAccessibility;
|
||||
static bool EnableMountNamespaces;
|
||||
static FILE *TraceEventFile;
|
||||
static void writeTraceEventRecording(const char *data, std::size_t nbytes);
|
||||
static void writeTraceEventRecording(const std::string &recording);
|
||||
|
|
Loading…
Reference in a new issue