mirror of https://github.com/trapexit/mergerfs.git
Browse Source
Rework credential handling to support chroot & idmap (#1595)
Rework credential handling to support chroot & idmap (#1595)
Run in an elevated credential mode (root) and let the kernel manage entitlements. Will result in slightly different behavior but should not be noticed by most.pull/1598/head
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
69 changed files with 789 additions and 1067 deletions
-
1Makefile
-
19libfuse/lib/fuse_lowlevel.cpp
-
9mkdocs/docs/config/options.md
-
44mkdocs/docs/faq/compatibility_and_integration.md
-
56mkdocs/docs/faq/recommendations_and_warnings.md
-
88mkdocs/docs/faq/technical_behavior_and_limitations.md
-
21mkdocs/docs/faq/usage_and_functionality.md
-
35mkdocs/docs/known_issues_bugs.md
-
1mkdocs/docs/resource_usage.md
-
2mkdocs/docs/runtime_interface.md
-
1mkdocs/mkdocs.yml
-
104src/caps.cpp
-
6src/caps.hpp
-
8src/config.cpp
-
3src/config.hpp
-
65src/config_gidcache.cpp
-
25src/config_gidcache.hpp
-
73src/fs_clonepath.cpp
-
5src/fs_clonepath.hpp
-
2src/fs_is_rofs.hpp
-
49src/fs_mkdir_as.hpp
-
35src/fs_mkdir_as_root.hpp
-
48src/fs_mkdirat.hpp
-
49src/fs_mknod_as.hpp
-
2src/fs_movefile_and_open.cpp
-
56src/fs_open_as.hpp
-
49src/fs_symlink_as.hpp
-
1src/fuse_access.cpp
-
1src/fuse_chmod.cpp
-
1src/fuse_chown.cpp
-
29src/fuse_create.cpp
-
2src/fuse_getattr.cpp
-
2src/fuse_getxattr.cpp
-
1src/fuse_init.cpp
-
3src/fuse_ioctl.cpp
-
5src/fuse_link.cpp
-
2src/fuse_listxattr.cpp
-
33src/fuse_mkdir.cpp
-
33src/fuse_mknod.cpp
-
2src/fuse_open.cpp
-
4src/fuse_passthrough.hpp
-
12src/fuse_readdir_cor.cpp
-
10src/fuse_readdir_cosr.cpp
-
7src/fuse_readdir_cosr_getdents.icpp
-
7src/fuse_readdir_cosr_readdir.icpp
-
1src/fuse_readdir_seq.cpp
-
1src/fuse_readlink.cpp
-
2src/fuse_removexattr.cpp
-
8src/fuse_rename.cpp
-
1src/fuse_rmdir.cpp
-
7src/fuse_setxattr.cpp
-
1src/fuse_statfs.cpp
-
4src/fuse_statx_supported.icpp
-
60src/fuse_symlink.cpp
-
1src/fuse_truncate.cpp
-
1src/fuse_unlink.cpp
-
1src/fuse_utimens.cpp
-
181src/gidcache.cpp
-
50src/gidcache.hpp
-
11src/mergerfs.cpp
-
2src/policy_ff.cpp
-
4src/predictability.h
-
33src/ugid.cpp
-
111src/ugid.hpp
-
128src/ugid_linux.hpp
-
34src/ugid_linux.icpp
-
106src/ugid_rwlock.hpp
-
45src/ugid_rwlock.icpp
@ -1,56 +0,0 @@ |
|||
# Recommendations and Warnings |
|||
|
|||
## What should mergerfs NOT be used for? |
|||
|
|||
* Situations where you need large amounts of contiguous space beyond |
|||
that available on any singular device. Such as putting 10GiB file on |
|||
2 6GiB filesystems. |
|||
* databases: Even if the database stored data in separate files |
|||
(mergerfs wouldn't offer much otherwise) the higher latency of the |
|||
indirection will really harm performance. If it is a lightly used |
|||
sqlite3 database then it should be fine. |
|||
* VM images: For the same reasons as databases. VM images are accessed |
|||
very aggressively and mergerfs will introduce a lot of extra latency. |
|||
* As replacement for RAID: mergerfs is just for pooling branches. If |
|||
you need device performance aggregation or high availability you |
|||
should stick with RAID. However, it is fine to put a filesystem |
|||
which is on a RAID setup in mergerfs. |
|||
|
|||
**However, if using [passthrough](../config/passthrough.md) the |
|||
performance related issues above are less likely to be a concern. Best |
|||
to do testing for your specific use case.** |
|||
|
|||
|
|||
## It's mentioned that there are some security issues with mhddfs. What are they? How does mergerfs address them? |
|||
|
|||
[mhddfs](https://github.com/trapexit/mhddfs) manages running as |
|||
`root` by calling |
|||
[getuid()](https://github.com/trapexit/mhddfs/blob/cae96e6251dd91e2bdc24800b4a18a74044f6672/src/main.c#L319) |
|||
and if it returns `0` then it will |
|||
[chown](http://linux.die.net/man/1/chown) the file. Not only is that a |
|||
race condition but it doesn't handle other situations. Rather than |
|||
attempting to simulate POSIX ACL behavior the proper way to manage |
|||
this is to use [seteuid](http://linux.die.net/man/2/seteuid) and |
|||
[setegid](http://linux.die.net/man/2/setegid), in effect, becoming the |
|||
user making the original call, and perform the action as them. This is |
|||
what mergerfs does and why mergerfs should always run as root. |
|||
|
|||
In Linux setreuid syscalls apply only to the thread. glibc hides this |
|||
away by using realtime signals to inform all threads to change |
|||
credentials. Taking after Samba, mergerfs uses |
|||
`syscall(SYS_setreuid,...)` to set the callers credentials for that |
|||
thread only. Jumping back to `root` as necessary should escalated |
|||
privileges be needed (for instance: to clone paths between |
|||
filesystems). |
|||
|
|||
For non-Linux systems, mergerfs uses a read-write lock and changes |
|||
credentials only when necessary. If multiple threads are to be user X |
|||
then only the first one will need to change the processes |
|||
credentials. So long as the other threads need to be user X they will |
|||
take a readlock allowing multiple threads to share the |
|||
credentials. Once a request comes in to run as user Y that thread will |
|||
attempt a write lock and change to Y's credentials when it can. If the |
|||
ability to give writers priority is supported then that flag will be |
|||
used so threads trying to change credentials don't starve. This isn't |
|||
the best solution but should work reasonably well assuming there are |
|||
few users. |
|||
@ -0,0 +1,104 @@ |
|||
#include "caps.hpp"
|
|||
|
|||
#if defined __linux__
|
|||
|
|||
#include <stdio.h>
|
|||
#include <stdlib.h>
|
|||
#include <unistd.h>
|
|||
#include <sys/prctl.h>
|
|||
#include <sys/types.h>
|
|||
#include <sys/stat.h>
|
|||
#include <sys/syscall.h>
|
|||
#include <fcntl.h>
|
|||
#include <errno.h>
|
|||
#include <string.h>
|
|||
#include <grp.h>
|
|||
#include <linux/capability.h>
|
|||
#include <linux/securebits.h>
|
|||
|
|||
|
|||
static |
|||
int |
|||
capset(cap_user_header_t header_, |
|||
const cap_user_data_t data_) |
|||
{ |
|||
return ::syscall(SYS_capset,header_,data_); |
|||
} |
|||
|
|||
static |
|||
int |
|||
capget(cap_user_header_t header_, |
|||
cap_user_data_t data_) |
|||
{ |
|||
return ::syscall(SYS_capget,header_,data_); |
|||
} |
|||
|
|||
static |
|||
int |
|||
capset(int cap_bit_) |
|||
{ |
|||
int rv; |
|||
struct __user_cap_header_struct header; |
|||
struct __user_cap_data_struct data[2]; |
|||
|
|||
header.version = _LINUX_CAPABILITY_VERSION_3; |
|||
header.pid = 0; |
|||
|
|||
rv = capget(&header,data); |
|||
if(rv < 0) |
|||
return rv; |
|||
|
|||
int word = cap_bit_ / 32; |
|||
int bit = cap_bit_ % 32; |
|||
|
|||
data[word].permitted |= (1 << bit); |
|||
data[word].effective |= (1 << bit); |
|||
data[word].inheritable |= (1 << bit); |
|||
|
|||
rv = capset(&header,data); |
|||
if(rv < 0) |
|||
return rv; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
int |
|||
caps::setup() |
|||
{ |
|||
int rv; |
|||
|
|||
rv = capset(CAP_DAC_OVERRIDE); |
|||
if(rv < 0) |
|||
return rv; |
|||
rv = capset(CAP_DAC_READ_SEARCH); |
|||
if(rv < 0) |
|||
return rv; |
|||
rv = capset(CAP_FOWNER); |
|||
if(rv < 0) |
|||
return rv; |
|||
rv = capset(CAP_CHOWN); |
|||
if(rv < 0) |
|||
return rv; |
|||
rv = capset(CAP_SETUID); |
|||
if(rv < 0) |
|||
return rv; |
|||
rv = capset(CAP_SETGID); |
|||
if(rv < 0) |
|||
return rv; |
|||
|
|||
rv = prctl(PR_SET_SECUREBITS, |
|||
SECBIT_KEEP_CAPS | SECBIT_NO_SETUID_FIXUP); |
|||
if(rv < 0) |
|||
return -errno; |
|||
|
|||
return 0; |
|||
} |
|||
#else
|
|||
#include <errno.h>
|
|||
|
|||
int |
|||
caps::setup() |
|||
{ |
|||
return -ENOSYS; |
|||
} |
|||
#endif
|
|||
@ -0,0 +1,6 @@ |
|||
#pragma once
|
|||
|
|||
namespace caps |
|||
{ |
|||
int setup(); |
|||
} |
|||
@ -1,65 +0,0 @@ |
|||
#include "config_gidcache.hpp"
|
|||
|
|||
#include "gidcache.hpp"
|
|||
|
|||
#include "to_string.hpp"
|
|||
#include "from_string.hpp"
|
|||
|
|||
|
|||
GIDCacheExpireTimeout::GIDCacheExpireTimeout(const int i_) |
|||
{ |
|||
GIDCache::expire_timeout = i_; |
|||
} |
|||
|
|||
GIDCacheExpireTimeout::GIDCacheExpireTimeout(const std::string &s_) |
|||
{ |
|||
from_string(s_); |
|||
} |
|||
|
|||
std::string |
|||
GIDCacheExpireTimeout::to_string(void) const |
|||
{ |
|||
return str::to(GIDCache::expire_timeout); |
|||
} |
|||
|
|||
int |
|||
GIDCacheExpireTimeout::from_string(const std::string_view s_) |
|||
{ |
|||
int rv; |
|||
|
|||
rv = str::from(s_,&GIDCache::expire_timeout); |
|||
if(rv < 0) |
|||
return rv; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
GIDCacheRemoveTimeout::GIDCacheRemoveTimeout(const int i_) |
|||
{ |
|||
GIDCache::remove_timeout = i_; |
|||
} |
|||
|
|||
|
|||
GIDCacheRemoveTimeout::GIDCacheRemoveTimeout(const std::string &s_) |
|||
{ |
|||
from_string(s_); |
|||
} |
|||
|
|||
std::string |
|||
GIDCacheRemoveTimeout::to_string(void) const |
|||
{ |
|||
return str::to(GIDCache::remove_timeout); |
|||
} |
|||
|
|||
int |
|||
GIDCacheRemoveTimeout::from_string(const std::string_view s_) |
|||
{ |
|||
int rv; |
|||
|
|||
rv = str::from(s_,&GIDCache::remove_timeout); |
|||
if(rv < 0) |
|||
return rv; |
|||
|
|||
return 0; |
|||
} |
|||
@ -1,25 +0,0 @@ |
|||
#pragma once
|
|||
|
|||
#include "tofrom_string.hpp"
|
|||
|
|||
class GIDCacheExpireTimeout : public ToFromString |
|||
{ |
|||
public: |
|||
GIDCacheExpireTimeout(const int i = 60 * 60); |
|||
GIDCacheExpireTimeout(const std::string &); |
|||
|
|||
public: |
|||
std::string to_string(void) const final; |
|||
int from_string(const std::string_view) final; |
|||
}; |
|||
|
|||
class GIDCacheRemoveTimeout : public ToFromString |
|||
{ |
|||
public: |
|||
GIDCacheRemoveTimeout(const int = 60 * 60 * 12); |
|||
GIDCacheRemoveTimeout(const std::string &); |
|||
|
|||
public: |
|||
std::string to_string(void) const final; |
|||
int from_string(const std::string_view) final; |
|||
}; |
|||
@ -0,0 +1,49 @@ |
|||
#pragma once
|
|||
|
|||
#include "fs_mkdir.hpp"
|
|||
#include "ugid.hpp"
|
|||
|
|||
|
|||
#if defined __linux__
|
|||
namespace fs |
|||
{ |
|||
template<typename T> |
|||
static |
|||
inline |
|||
int |
|||
mkdir_as(const ugid_t ugid_, |
|||
const T &path_, |
|||
const mode_t mode_) |
|||
{ |
|||
const ugid::SetGuard _(ugid_); |
|||
|
|||
return fs::mkdir(path_,mode_); |
|||
} |
|||
} |
|||
#elif defined __FreeBSD__
|
|||
#include "fs_lchown.hpp"
|
|||
|
|||
namespace fs |
|||
{ |
|||
template<typename T> |
|||
static |
|||
inline |
|||
int |
|||
mkdir_as(const ugid_t ugid_, |
|||
const T &path_, |
|||
const mode_t mode_) |
|||
{ |
|||
int rv; |
|||
|
|||
rv = fs::mkdir(path_,mode_); |
|||
if(rv < 0) |
|||
return rv; |
|||
|
|||
fs::lchown(path_,ugid_.uid,ugid_.gid); |
|||
|
|||
return 0; |
|||
} |
|||
} |
|||
#else
|
|||
#error "Not Supported"
|
|||
#endif
|
|||
@ -1,35 +0,0 @@ |
|||
/*
|
|||
ISC License |
|||
|
|||
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link> |
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for any |
|||
purpose with or without fee is hereby granted, provided that the above |
|||
copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
#include "fs_mkdir.hpp"
|
|||
#include "ugid.hpp"
|
|||
|
|||
namespace fs |
|||
{ |
|||
template<typename T> |
|||
static |
|||
inline |
|||
int |
|||
mkdir_as_root(const T &path_, |
|||
const mode_t mode_) |
|||
{ |
|||
const ugid::SetRootGuard guard; |
|||
|
|||
return fs::mkdir(path_,mode_); |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
#pragma once
|
|||
|
|||
#include "to_neg_errno.hpp"
|
|||
#include "fs_path.hpp"
|
|||
|
|||
#include <fcntl.h>
|
|||
#include <sys/stat.h>
|
|||
|
|||
namespace fs |
|||
{ |
|||
static |
|||
inline |
|||
int |
|||
mkdirat(const int dirfd_, |
|||
const char *pathname_, |
|||
const mode_t mode_) |
|||
{ |
|||
int rv; |
|||
|
|||
rv = ::mkdirat(dirfd_,pathname_,mode_); |
|||
|
|||
return ::to_neg_errno(rv); |
|||
} |
|||
|
|||
static |
|||
inline |
|||
int |
|||
mkdirat(const int dirfd_, |
|||
const std::string &pathname_, |
|||
const mode_t mode_) |
|||
{ |
|||
return fs::mkdirat(dirfd_, |
|||
pathname_.c_str(), |
|||
mode_); |
|||
} |
|||
|
|||
static |
|||
inline |
|||
int |
|||
mkdirat(const int dirfd_, |
|||
const fs::path &pathname_, |
|||
const mode_t mode_) |
|||
{ |
|||
return fs::mkdirat(dirfd_, |
|||
pathname_.c_str(), |
|||
mode_); |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
#pragma once
|
|||
|
|||
#include "fs_mknod.hpp"
|
|||
#include "ugid.hpp"
|
|||
|
|||
|
|||
#if defined __linux__
|
|||
namespace fs |
|||
{ |
|||
template<typename T> |
|||
static |
|||
inline |
|||
int |
|||
mknod_as(const ugid_t ugid_, |
|||
const T &path_, |
|||
const mode_t mode_, |
|||
const dev_t dev_) |
|||
{ |
|||
const ugid::SetGuard _(ugid_); |
|||
|
|||
return fs::mknod(path_,mode_,dev_); |
|||
} |
|||
} |
|||
#elif defined __FreeBSD__
|
|||
#include "fs_lchown.hpp"
|
|||
|
|||
namespace fs |
|||
{ |
|||
template<typename T> |
|||
static |
|||
inline |
|||
int |
|||
mknod_as(const ugid_t ugid_, |
|||
const T &path_, |
|||
const mode_t mode_, |
|||
const dev_t dev_) |
|||
{ |
|||
int rv; |
|||
|
|||
rv = fs::mknod(path_,mode_,dev_); |
|||
|
|||
fs::lchown(path_,ugid_.uid,ugid_.gid); |
|||
|
|||
return rv; |
|||
} |
|||
} |
|||
#else
|
|||
#error "Not Supported!"
|
|||
#endif
|
|||
@ -0,0 +1,56 @@ |
|||
#pragma once
|
|||
|
|||
#include "fs_open.hpp"
|
|||
#include "ugid.hpp"
|
|||
|
|||
// Linux can set ugid because it allows for credentials per
|
|||
// thread. FreeBSD however does not and must create as root then
|
|||
// chown. Another option for both platforms would be to create a temp
|
|||
// file, chown, then rename to target. Unfortunately, depending on if
|
|||
// the target filesystem is POSIX compliant or uses POSIX / extended
|
|||
// ACLs it is not possible to know what is best.
|
|||
|
|||
|
|||
#if defined __linux__
|
|||
namespace fs |
|||
{ |
|||
static |
|||
inline |
|||
int |
|||
open_as(const ugid_t ugid_, |
|||
const fs::path &path_, |
|||
const int flags_, |
|||
const mode_t mode_) |
|||
{ |
|||
const ugid::SetGuard _(ugid_); |
|||
|
|||
return fs::open(path_,flags_,mode_); |
|||
} |
|||
} |
|||
#elif defined __FreeBSD__
|
|||
#include "fs_fchown.hpp"
|
|||
|
|||
namespace fs |
|||
{ |
|||
static |
|||
inline |
|||
int |
|||
open_as(const ugid_t ugid_, |
|||
const fs::path &path_, |
|||
const int flags_, |
|||
const mode_t mode_) |
|||
{ |
|||
int rv; |
|||
|
|||
rv = fs::open(path_,flags_,mode_); |
|||
if(rv < 0) |
|||
return rv; |
|||
|
|||
fs::fchown(rv,ugid_.uid,ugid_.gid); |
|||
|
|||
return rv; |
|||
} |
|||
} |
|||
#else
|
|||
#error "Not Supported!"
|
|||
#endif
|
|||
@ -0,0 +1,49 @@ |
|||
#pragma once
|
|||
|
|||
#include "fs_symlink.hpp"
|
|||
#include "ugid.hpp"
|
|||
|
|||
|
|||
#if defined __linux__
|
|||
namespace fs |
|||
{ |
|||
template<typename T> |
|||
static |
|||
inline |
|||
int |
|||
symlink_as(const ugid_t ugid_, |
|||
const char *target_, |
|||
const T &linkpath_) |
|||
{ |
|||
const ugid::SetGuard _(ugid_); |
|||
|
|||
return fs::symlink(target_,linkpath_); |
|||
} |
|||
} |
|||
#elif defined __FreeBSD__
|
|||
#include "fs_lchown.hpp"
|
|||
|
|||
namespace fs |
|||
{ |
|||
template<typename T> |
|||
static |
|||
inline |
|||
int |
|||
symlink_as(const ugid_t ugid_, |
|||
const char *target_, |
|||
const T &linkpath_) |
|||
{ |
|||
int rv; |
|||
|
|||
rv = fs::symlink(target_,linkpath_); |
|||
if(rv < 0) |
|||
return rv; |
|||
|
|||
fs::lchown(linkpath_,ugid_.uid,ugid_.gid); |
|||
|
|||
return 0; |
|||
} |
|||
} |
|||
#else
|
|||
#error "Not Supported!"
|
|||
#endif
|
|||
@ -1,181 +0,0 @@ |
|||
/*
|
|||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link> |
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for any |
|||
purpose with or without fee is hereby granted, provided that the above |
|||
copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
#include "gidcache.hpp"
|
|||
|
|||
#include "syslog.hpp"
|
|||
|
|||
#include <grp.h>
|
|||
#include <pwd.h>
|
|||
#include <stdlib.h>
|
|||
#include <sys/types.h>
|
|||
#include <unistd.h>
|
|||
|
|||
#if defined __linux__ and UGID_USE_RWLOCK == 0
|
|||
# include <sys/syscall.h>
|
|||
#elif __APPLE__
|
|||
# include <sys/param.h>
|
|||
#endif
|
|||
|
|||
int GIDCache::expire_timeout = (60 * 60); |
|||
int GIDCache::remove_timeout = (60 * 60 * 12); |
|||
boost::concurrent_flat_map<uid_t,GIDRecord> GIDCache::_records; |
|||
|
|||
static |
|||
int |
|||
_getgrouplist(const char *user, |
|||
const gid_t group, |
|||
gid_t *groups, |
|||
int *ngroups) |
|||
{ |
|||
#if __APPLE__
|
|||
return ::getgrouplist(user,group,(int*)groups,ngroups); |
|||
#else
|
|||
return ::getgrouplist(user,group,groups,ngroups); |
|||
#endif
|
|||
} |
|||
|
|||
static |
|||
inline |
|||
int |
|||
_setgroups(const std::vector<gid_t> gids_) |
|||
{ |
|||
if(gids_.empty()) |
|||
return 0; |
|||
|
|||
#if defined __linux__ and UGID_USE_RWLOCK == 0
|
|||
# if defined SYS_setgroups32
|
|||
return ::syscall(SYS_setgroups32,gids_.size(),gids_.data()); |
|||
# else
|
|||
return ::syscall(SYS_setgroups,gids_.size(),gids_.data()); |
|||
# endif
|
|||
#else
|
|||
return ::setgroups(gids_.size(),gids_.data()); |
|||
#endif
|
|||
} |
|||
|
|||
static |
|||
void |
|||
_getgroups(const uid_t uid_, |
|||
const gid_t gid_, |
|||
std::vector<gid_t> &gids_) |
|||
{ |
|||
int rv; |
|||
int ngroups; |
|||
char buf[4096]; |
|||
struct passwd pwd; |
|||
struct passwd *pwdrv; |
|||
|
|||
gids_.clear(); |
|||
rv = ::getpwuid_r(uid_,&pwd,buf,sizeof(buf),&pwdrv); |
|||
if((rv == -1) || (pwdrv == NULL)) |
|||
goto error; |
|||
|
|||
ngroups = 0; |
|||
rv = ::_getgrouplist(pwd.pw_name,gid_,NULL,&ngroups); |
|||
gids_.resize(ngroups); |
|||
|
|||
rv = ::_getgrouplist(pwd.pw_name,gid_,gids_.data(),&ngroups); |
|||
if((size_t)ngroups < gids_.size()) |
|||
gids_.resize(ngroups); |
|||
|
|||
return; |
|||
|
|||
error: |
|||
gids_.clear(); |
|||
//gids_.push_back(gid_);
|
|||
} |
|||
|
|||
|
|||
int |
|||
GIDCache::initgroups(const uid_t uid_, |
|||
const gid_t gid_) |
|||
{ |
|||
auto first_func = |
|||
[=](auto &x) |
|||
{ |
|||
x.second.last_update = ::time(NULL); |
|||
::_getgroups(uid_,gid_,x.second.gids); |
|||
::_setgroups(x.second.gids); |
|||
}; |
|||
auto exists_func = |
|||
[=](auto &x) |
|||
{ |
|||
time_t now; |
|||
|
|||
now = ::time(NULL); |
|||
if((now - x.second.last_update) > GIDCache::expire_timeout) |
|||
{ |
|||
::_getgroups(uid_,gid_,x.second.gids); |
|||
x.second.last_update = now; |
|||
} |
|||
::_setgroups(x.second.gids); |
|||
}; |
|||
|
|||
_records.try_emplace_and_visit(uid_, |
|||
first_func, |
|||
exists_func); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
void |
|||
GIDCache::invalidate_all() |
|||
{ |
|||
size_t size; |
|||
|
|||
size = _records.size(); |
|||
_records.visit_all([](auto &x) |
|||
{ |
|||
x.second.last_update = 0; |
|||
}); |
|||
|
|||
SysLog::info("gid cache invalidated, {} entries",size); |
|||
} |
|||
|
|||
void |
|||
GIDCache::clear_all() |
|||
{ |
|||
size_t size; |
|||
|
|||
size = _records.size(); |
|||
_records.clear(); |
|||
|
|||
SysLog::info("gid cache cleared, {} entries",size); |
|||
} |
|||
|
|||
void |
|||
GIDCache::clear_unused() |
|||
{ |
|||
int erased = 0; |
|||
time_t now = ::time(NULL); |
|||
auto erase_func = |
|||
[now,&erased](auto &x) |
|||
{ |
|||
bool should_erase; |
|||
time_t time_delta; |
|||
|
|||
time_delta = (now - x.second.last_update); |
|||
should_erase = (time_delta > GIDCache::remove_timeout); |
|||
erased += should_erase; |
|||
|
|||
return should_erase; |
|||
}; |
|||
|
|||
_records.erase_if(erase_func); |
|||
|
|||
SysLog::info("cleared {} unused gid cache entries",erased); |
|||
} |
|||
@ -1,50 +0,0 @@ |
|||
/*
|
|||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link> |
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for any |
|||
purpose with or without fee is hereby granted, provided that the above |
|||
copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
#pragma once
|
|||
|
|||
#include "boost/unordered/concurrent_flat_map.hpp"
|
|||
|
|||
#include <sys/types.h>
|
|||
#include <unistd.h>
|
|||
|
|||
#include <vector>
|
|||
|
|||
struct GIDRecord |
|||
{ |
|||
std::vector<gid_t> gids; |
|||
time_t last_update; |
|||
}; |
|||
|
|||
struct GIDCache |
|||
{ |
|||
public: |
|||
static |
|||
int |
|||
initgroups(const uid_t uid, |
|||
const gid_t gid); |
|||
|
|||
static void invalidate_all(); |
|||
static void clear_all(); |
|||
static void clear_unused(); |
|||
|
|||
public: |
|||
static int expire_timeout; |
|||
static int remove_timeout; |
|||
|
|||
private: |
|||
static boost::concurrent_flat_map<uid_t,GIDRecord> _records; |
|||
}; |
|||
@ -0,0 +1,4 @@ |
|||
#pragma once |
|||
|
|||
#define likely(x) __builtin_expect(!!(x), 1) |
|||
#define unlikely(x) __builtin_expect(!!(x), 0) |
|||
@ -1,33 +1,8 @@ |
|||
/*
|
|||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link> |
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for any |
|||
purpose with or without fee is hereby granted, provided that the above |
|||
copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
#include "gidcache.hpp"
|
|||
|
|||
#if defined __linux__ and UGID_USE_RWLOCK == 0
|
|||
#include "ugid_linux.icpp"
|
|||
#else
|
|||
#include "ugid_rwlock.icpp"
|
|||
#endif
|
|||
#include <unistd.h>
|
|||
|
|||
namespace ugid |
|||
{ |
|||
void |
|||
initgroups(const uid_t uid_, |
|||
const gid_t gid_) |
|||
{ |
|||
GIDCache::initgroups(uid_,gid_); |
|||
} |
|||
thread_local uid_t currentuid = 0; |
|||
thread_local gid_t currentgid = 0; |
|||
thread_local bool initialized = false; |
|||
} |
|||
@ -1,128 +0,0 @@ |
|||
/*
|
|||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link> |
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for any |
|||
purpose with or without fee is hereby granted, provided that the above |
|||
copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
#pragma once
|
|||
|
|||
#include "fuse_req_ctx.h"
|
|||
|
|||
#include <assert.h>
|
|||
#include <sys/stat.h>
|
|||
#include <sys/syscall.h>
|
|||
#include <sys/types.h>
|
|||
#include <unistd.h>
|
|||
|
|||
#include <grp.h>
|
|||
#include <pwd.h>
|
|||
|
|||
#include <map>
|
|||
#include <vector>
|
|||
|
|||
#if defined SYS_setreuid32
|
|||
#define SETREUID(R,E) (::syscall(SYS_setreuid32,(R),(E)))
|
|||
#else
|
|||
#define SETREUID(R,E) (::syscall(SYS_setreuid,(R),(E)))
|
|||
#endif
|
|||
|
|||
#if defined SYS_setregid32
|
|||
#define SETREGID(R,E) (::syscall(SYS_setregid32,(R),(E)))
|
|||
#else
|
|||
#define SETREGID(R,E) (::syscall(SYS_setregid,(R),(E)))
|
|||
#endif
|
|||
|
|||
#if defined SYS_geteuid32
|
|||
#define GETEUID() (::syscall(SYS_geteuid32))
|
|||
#else
|
|||
#define GETEUID() (::syscall(SYS_geteuid))
|
|||
#endif
|
|||
|
|||
#if defined SYS_getegid32
|
|||
#define GETEGID() (::syscall(SYS_getegid32))
|
|||
#else
|
|||
#define GETEGID() (::syscall(SYS_getegid))
|
|||
#endif
|
|||
|
|||
namespace ugid |
|||
{ |
|||
extern thread_local uid_t currentuid; |
|||
extern thread_local gid_t currentgid; |
|||
extern thread_local bool initialized; |
|||
|
|||
struct Set |
|||
{ |
|||
Set(const uid_t newuid_, |
|||
const gid_t newgid_) |
|||
{ |
|||
assert((int)newuid_ != -1); |
|||
assert((int)newgid_ != -1); |
|||
|
|||
if(!initialized) |
|||
{ |
|||
currentuid = GETEUID(); |
|||
currentgid = GETEGID(); |
|||
initialized = true; |
|||
} |
|||
|
|||
if((newuid_ == currentuid) && (newgid_ == currentgid)) |
|||
return; |
|||
|
|||
if(currentuid != 0) |
|||
{ |
|||
SETREUID(-1,0); |
|||
SETREGID(-1,0); |
|||
} |
|||
|
|||
if(newgid_) |
|||
{ |
|||
SETREGID(-1,newgid_); |
|||
ugid::initgroups(newuid_,newgid_); |
|||
} |
|||
|
|||
if(newuid_) |
|||
SETREUID(-1,newuid_); |
|||
|
|||
currentuid = newuid_; |
|||
currentgid = newgid_; |
|||
} |
|||
|
|||
Set(const fuse_req_ctx_t *ctx_) |
|||
: Set(ctx_->uid,ctx_->gid) |
|||
{ |
|||
} |
|||
}; |
|||
|
|||
struct SetRootGuard |
|||
{ |
|||
SetRootGuard() : |
|||
prevuid(currentuid), |
|||
prevgid(currentgid) |
|||
{ |
|||
Set(0,0); |
|||
} |
|||
|
|||
~SetRootGuard() |
|||
{ |
|||
Set(prevuid,prevgid); |
|||
} |
|||
|
|||
const uid_t prevuid; |
|||
const gid_t prevgid; |
|||
}; |
|||
} |
|||
|
|||
#undef SETREUID
|
|||
#undef SETREGID
|
|||
#undef GETEUID
|
|||
#undef GETEGID
|
|||
@ -1,34 +0,0 @@ |
|||
/* |
|||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link> |
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for any |
|||
purpose with or without fee is hereby granted, provided that the above |
|||
copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
#include <stdlib.h> |
|||
#include <sys/syscall.h> |
|||
#include <sys/types.h> |
|||
#include <unistd.h> |
|||
|
|||
#include <vector> |
|||
|
|||
namespace ugid |
|||
{ |
|||
thread_local uid_t currentuid = 0; |
|||
thread_local gid_t currentgid = 0; |
|||
thread_local bool initialized = false; |
|||
|
|||
void |
|||
init() |
|||
{ |
|||
} |
|||
} |
|||
@ -1,106 +0,0 @@ |
|||
/*
|
|||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link> |
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for any |
|||
purpose with or without fee is hereby granted, provided that the above |
|||
copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
#pragma once
|
|||
|
|||
#include "fuse_req_ctx.h"
|
|||
|
|||
#include <pthread.h>
|
|||
#include <sys/stat.h>
|
|||
#include <sys/types.h>
|
|||
#include <unistd.h>
|
|||
|
|||
namespace ugid |
|||
{ |
|||
extern uid_t currentuid; |
|||
extern gid_t currentgid; |
|||
extern pthread_rwlock_t rwlock; |
|||
|
|||
static |
|||
void |
|||
ugid_set(const uid_t newuid_, |
|||
const gid_t newgid_) |
|||
{ |
|||
pthread_rwlock_rdlock(&rwlock); |
|||
|
|||
if((newuid_ == currentuid) && (newgid_ == currentgid)) |
|||
return; |
|||
|
|||
pthread_rwlock_unlock(&rwlock); |
|||
pthread_rwlock_wrlock(&rwlock); |
|||
|
|||
if((newuid_ == currentuid) && (newgid_ == currentgid)) |
|||
return; |
|||
|
|||
if(currentuid != 0) |
|||
{ |
|||
::seteuid(0); |
|||
::setegid(0); |
|||
} |
|||
|
|||
if(newgid_) |
|||
{ |
|||
::setegid(newgid_); |
|||
initgroups(newuid_,newgid_); |
|||
} |
|||
|
|||
if(newuid_) |
|||
::seteuid(newuid_); |
|||
|
|||
currentuid = newuid_; |
|||
currentgid = newgid_; |
|||
} |
|||
|
|||
struct Set |
|||
{ |
|||
Set(const uid_t newuid_, |
|||
const gid_t newgid_) |
|||
{ |
|||
ugid_set(newuid_,newgid_); |
|||
} |
|||
|
|||
Set(const fuse_req_ctx_t *ctx_) |
|||
: Set(ctx_->uid,ctx_->gid) |
|||
{ |
|||
|
|||
} |
|||
|
|||
~Set() |
|||
{ |
|||
pthread_rwlock_unlock(&rwlock); |
|||
} |
|||
}; |
|||
|
|||
struct SetRootGuard |
|||
{ |
|||
SetRootGuard() : |
|||
prevuid(currentuid), |
|||
prevgid(currentgid) |
|||
{ |
|||
pthread_rwlock_unlock(&rwlock); |
|||
ugid_set(0,0); |
|||
} |
|||
|
|||
~SetRootGuard() |
|||
{ |
|||
pthread_rwlock_unlock(&rwlock); |
|||
ugid_set(prevuid,prevgid); |
|||
} |
|||
|
|||
const uid_t prevuid; |
|||
const gid_t prevgid; |
|||
}; |
|||
} |
|||
@ -1,45 +0,0 @@ |
|||
/* |
|||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link> |
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for any |
|||
purpose with or without fee is hereby granted, provided that the above |
|||
copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
#include <pthread.h> |
|||
#include <stdlib.h> |
|||
#include <sys/types.h> |
|||
#include <unistd.h> |
|||
|
|||
#include <vector> |
|||
|
|||
namespace ugid |
|||
{ |
|||
uid_t currentuid; |
|||
gid_t currentgid; |
|||
pthread_rwlock_t rwlock; |
|||
|
|||
void |
|||
init() |
|||
{ |
|||
pthread_rwlockattr_t attr; |
|||
|
|||
pthread_rwlockattr_init(&attr); |
|||
# if defined PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP |
|||
pthread_rwlockattr_setkind_np(&attr,PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); |
|||
# endif |
|||
|
|||
pthread_rwlock_init(&rwlock,&attr); |
|||
|
|||
currentuid = ::geteuid(); |
|||
currentgid = ::getegid(); |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue