diff --git a/cvmfs/CMakeLists.txt b/cvmfs/CMakeLists.txt index 84540fea3a..5a785e9345 100644 --- a/cvmfs/CMakeLists.txt +++ b/cvmfs/CMakeLists.txt @@ -52,6 +52,7 @@ if (BUILD_CVMFS OR BUILD_LIBCVMFS) cache_stream.cc cache_tiered.cc cache_transport.cc + capabilities.cc catalog.cc catalog_counters.cc catalog_mgr_client.cc @@ -167,6 +168,7 @@ if (BUILD_CVMFS) # add_executable (cvmfs2 fuse_main.cc + capabilities.cc util/exception.cc util/logging.cc util/posix.cc @@ -174,9 +176,8 @@ if (BUILD_CVMFS) ) set_target_properties (cvmfs2 PROPERTIES COMPILE_FLAGS "-DCVMFS_NAMESPACE_GUARD=stub -DCVMFS_FUSE_MODULE" - LINK_FLAGS "-ldl" ) - target_link_libraries(cvmfs2 pthread dl) + target_link_libraries(cvmfs2 pthread dl ${CAP_LIBRARIES}) # # /usr/lib/libcvmfs_fuse_stub[3] @@ -185,6 +186,7 @@ if (BUILD_CVMFS) # libcvmfs-fuse. # set (CVMFS_STUB_SOURCES + capabilities.cc globals.cc loader.cc loader_talk.cc @@ -446,6 +448,7 @@ if (BUILD_LIBCVMFS_CACHE) cache_plugin/libcvmfs_cache_options.cc cache_plugin/channel.cc cache_transport.cc + capabilities.cc monitor.cc options.cc sanitizer.cc @@ -474,6 +477,7 @@ if (BUILD_LIBCVMFS_CACHE) target_link_libraries(cvmfs_cache PUBLIC cvmfs_crypto cvmfs_util + ${CAP_LIBRARIES} PRIVATE ${PROTOBUF_LITE_LIBRARY} ) @@ -483,6 +487,7 @@ if (BUILD_LIBCVMFS_CACHE) add_executable (cvmfs_cache_null cache_plugin/cvmfs_cache_null.cc) target_link_libraries (cvmfs_cache_null cvmfs_cache + ${CAP_LIBRARIES} ${RT_LIBRARY} pthread ) @@ -573,6 +578,7 @@ if (BUILD_SERVER) # set (CVMFS_SWISSKNIFE_SOURCES backoff.cc + capabilities.cc catalog.cc catalog_counters.cc catalog_mgr_ro.cc @@ -640,7 +646,6 @@ if (BUILD_SERVER) supervisor.cc swissknife.cc swissknife_assistant.cc - swissknife_capabilities.cc swissknife_check.cc swissknife_gc.cc swissknife_graft.cc @@ -719,6 +724,7 @@ if (BUILD_SERVER) # set (LIBCVMFS_SERVER_SOURCES backoff.cc + capabilities.cc catalog.cc catalog_counters.cc catalog_rw.cc @@ -837,6 +843,7 @@ if (BUILD_SERVER) # set (CVMFS_PUBLISH_SOURCES backoff.cc + capabilities.cc compression/compression.cc directory_entry.cc manifest.cc @@ -1013,6 +1020,7 @@ if(BUILD_RECEIVER) receiver/receiver.cc receiver/session_token.cc backoff.cc + capabilities.cc catalog.cc catalog_rw.cc catalog_counters.cc @@ -1085,6 +1093,7 @@ if(BUILD_RECEIVER) ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${RT_LIBRARY} + ${CAP_LIBRARIES} ${LibArchive_LIBRARY} pthread dl @@ -1136,7 +1145,6 @@ if (BUILD_SHRINKWRAP) endif () add_executable (cvmfs_shrinkwrap - monitor.cc shrinkwrap/fs_traversal.cc shrinkwrap/fs_traversal_libcvmfs.cc shrinkwrap/posix/data_dir_mgmt.cc @@ -1156,6 +1164,7 @@ if (BUILD_SHRINKWRAP) cvmfs_client cvmfs_crypto cvmfs_util + ${CAP_LIBRARIES} pthread dl ) diff --git a/cvmfs/auto_umount.cc b/cvmfs/auto_umount.cc index 9a4366ccf6..3f2b319deb 100644 --- a/cvmfs/auto_umount.cc +++ b/cvmfs/auto_umount.cc @@ -15,6 +15,7 @@ #include #include +#include "capabilities.h" #include "util/logging.h" #include "util/platform.h" #include "util/posix.h" @@ -35,21 +36,28 @@ void SetMountpoint(const string &mountpoint) { } -void UmountOnCrash() { +void UmountOnExit(const bool crashed) { + const char *cleanuptype = "exit"; + if (crashed) + cleanuptype = "crash"; + if (!mountpoint_) { - LogCvmfs(kLogCvmfs, kLogSyslogErr, "crash cleanup handler: no mountpoint"); + LogCvmfs(kLogCvmfs, kLogSyslogErr, + "%s cleanup handler: no mountpoint", cleanuptype); return; } std::vector all_mountpoints = platform_mountlist(); if (all_mountpoints.empty()) { - LogCvmfs(kLogCvmfs, kLogSyslogErr, "crash cleanup handler: " - "failed to read mount point list"); + LogCvmfs(kLogCvmfs, kLogSyslogErr, "%s cleanup handler: " + "failed to read mount point list", cleanuptype); return; } - // Mitigate auto-mount - crash - umount - auto-mount loops - SafeSleepMs(2000); + if (crashed) { + // Mitigate auto-mount - crash - umount - auto-mount loops + SafeSleepMs(2000); + } // Check if *mountpoint_ is still mounted // (we don't want to trigger a mount by immediately doing stat *mountpoint_) @@ -61,42 +69,52 @@ void UmountOnCrash() { } } if (!still_mounted) { - LogCvmfs(kLogCvmfs, kLogSyslog, "crash cleanup handler: %s not mounted", - mountpoint_->c_str()); + int logtype = kLogDebug; + if (crashed) + logtype = kLogSyslog; + LogCvmfs(kLogCvmfs, logtype, "%s cleanup handler: %s not mounted", + cleanuptype, mountpoint_->c_str()); return; } - // stat() might be served from caches. Opendir ensures fuse module is called. - int expected_error; + if (crashed) { + // stat() might be served from caches. Opendir ensures fuse module is called. + int expected_error; #ifdef __APPLE__ - expected_error = ENXIO; + expected_error = ENXIO; #else - expected_error = ENOTCONN; + expected_error = ENOTCONN; #endif - DIR *dirp = opendir(mountpoint_->c_str()); - if (dirp || (errno != expected_error)) { - if (dirp) closedir(dirp); - LogCvmfs(kLogCvmfs, kLogSyslog, "crash cleanup handler: " - "%s seems not to be stalled (%d)", mountpoint_->c_str(), errno); - return; + DIR *dirp = opendir(mountpoint_->c_str()); + if (dirp || (errno != expected_error)) { + if (dirp) closedir(dirp); + LogCvmfs(kLogCvmfs, kLogSyslog, "crash cleanup handler: " + "%s seems not to be stalled (%d)", mountpoint_->c_str(), errno); + return; + } } // sudo umount -l *mountpoint_ - if (!SwitchCredentials(0, getegid(), true)) { - LogCvmfs(kLogCvmfs, kLogSyslogErr, "crash cleanup handler: " - "failed to re-gain root privileges"); + if (!ObtainSysAdminCapability()) { + LogCvmfs(kLogCvmfs, kLogSyslogErr, "%s cleanup handler: " + "failed to re-gain sys_admin capability", cleanuptype); return; } const bool lazy = true; bool retval = platform_umount(mountpoint_->c_str(), lazy); if (!retval) { - LogCvmfs(kLogCvmfs, kLogSyslogErr, "crash cleanup handler: " - "failed to unmount %s", mountpoint_->c_str()); + LogCvmfs(kLogCvmfs, kLogSyslogErr, "%s cleanup handler: " + "failed to unmount %s", mountpoint_->c_str(), cleanuptype); return; } - LogCvmfs(kLogCvmfs, kLogSyslog, "crash cleanup handler unmounted stalled %s", - mountpoint_->c_str()); + if (crashed) { + LogCvmfs(kLogCvmfs, kLogSyslog, + "crash cleanup handler unmounted stalled %s", mountpoint_->c_str()); + } else { + LogCvmfs(kLogCvmfs, kLogSyslog, + "exit cleanup handler unmounted %s", mountpoint_->c_str()); + } } } // namespace auto_umount diff --git a/cvmfs/auto_umount.h b/cvmfs/auto_umount.h index a76bd380f5..9c731a69cb 100644 --- a/cvmfs/auto_umount.h +++ b/cvmfs/auto_umount.h @@ -10,7 +10,7 @@ namespace auto_umount { void SetMountpoint(const std::string &mountpoint); -void UmountOnCrash(); +void UmountOnExit(const bool crashed); } // namespace auto_umount diff --git a/cvmfs/swissknife_capabilities.cc b/cvmfs/capabilities.cc similarity index 100% rename from cvmfs/swissknife_capabilities.cc rename to cvmfs/capabilities.cc diff --git a/cvmfs/swissknife_capabilities.h b/cvmfs/capabilities.h similarity index 100% rename from cvmfs/swissknife_capabilities.h rename to cvmfs/capabilities.h diff --git a/cvmfs/cvmfs.cc b/cvmfs/cvmfs.cc index 039b610f1d..1793be95a8 100644 --- a/cvmfs/cvmfs.cc +++ b/cvmfs/cvmfs.cc @@ -2323,7 +2323,7 @@ static int Init(const loader::LoaderExports *loader_exports) { // Monitor, check for maximum number of open files if (cvmfs::UseWatchdog()) { auto_umount::SetMountpoint(loader_exports->mount_point); - cvmfs::watchdog_ = Watchdog::Create(auto_umount::UmountOnCrash); + cvmfs::watchdog_ = Watchdog::Create(auto_umount::UmountOnExit); if (cvmfs::watchdog_ == NULL) { *g_boot_error = "failed to initialize watchdog."; return loader::kFailMonitor; @@ -2509,7 +2509,7 @@ static void ShutdownMountpoint() { delete cvmfs::notification_client_; cvmfs::notification_client_ = NULL; - // The remonter has a reference to the mount point and the inode generation + // The remounter has a reference to the mount point and the inode generation delete cvmfs::fuse_remounter_; cvmfs::fuse_remounter_ = NULL; @@ -2571,6 +2571,8 @@ static bool MaintenanceMode(const int fd_progress) { "s)\n"; SendMsg2Socket(fd_progress, msg_progress); cvmfs::fuse_remounter_->EnterMaintenanceMode(); + // this keeps the watchdog from unmounting when it gets deleted + cvmfs::watchdog_->ClearOnExitFn(); return true; } diff --git a/cvmfs/loader.cc b/cvmfs/loader.cc index a3205e8788..5cb26e1460 100644 --- a/cvmfs/loader.cc +++ b/cvmfs/loader.cc @@ -36,6 +36,7 @@ #include #include +#include "capabilities.h" #include "duplex_fuse.h" #include "fence.h" #include "fuse_main.h" @@ -881,7 +882,8 @@ int FuseMain(int argc, char *argv[]) { } } - // Drop credentials + // Drop credentials, most likely temporarily since by default there is + // a watchdog if ((uid_ != 0) || (gid_ != 0)) { LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: running with credentials %d:%d", uid_, gid_); @@ -894,7 +896,9 @@ int FuseMain(int argc, char *argv[]) { } if (disable_watchdog_) { LogCvmfs(kLogCvmfs, kLogDebug, "No watchdog, enabling core files"); - prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + retval = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + if (retval != 0) + LogCvmfs(kLogCvmfs, kLogDebug, "Failed to set process dumpable"); } // Only set usyslog now, otherwise file permissions are wrong @@ -1055,8 +1059,8 @@ int FuseMain(int argc, char *argv[]) { } #endif - // drop credentials if (suid_mode_) { + // Drop credentials again for now const bool retrievable = !disable_watchdog_; if (!SwitchCredentials(uid_, gid_, retrievable)) { LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr, @@ -1064,6 +1068,32 @@ int FuseMain(int argc, char *argv[]) { cvmfs_exports_->fnFini(); return kFailPermission; } + } else if ((getuid() == 0) && (geteuid() != 0)) { + LogCvmfs(kLogCvmfs, kLogDebug, "Reducing to minimum capabilities"); + // Have temporarily dropped credentials, now drop credentials permanently. + // CAP_DAC_READ_SEARCH will be sometimes needed for a future feature; + // for now reserve it all the time for testing. + cap_value_t reservecaps[] = {CAP_DAC_READ_SEARCH}; + retval = ClearPermittedCapabilities( + sizeof(reservecaps)/sizeof(cap_value_t), reservecaps, 0, 0); + if (!retval) { + LogCvmfs(kLogCvmfs, kLogStderr, + "Failed to reduce to minimum capabilities"); + return 1; + } + if (!disable_watchdog_) { + // allow ptracing by the watchdog + retval = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + if (retval != 0) + LogCvmfs(kLogCvmfs, kLogDebug, "Failed to set process dumpable"); + // but still disallow core dump + retval = SetLimitCore(0); + if (retval != 0) + LogCvmfs(kLogCvmfs, kLogDebug, "Failed to set core dump limit to 0"); + } + } else { + LogCvmfs(kLogCvmfs, kLogDebug, "Not clearing capabilities, uid %d euid%d", + getuid(), geteuid()); } // Determine device id @@ -1135,6 +1165,11 @@ int FuseMain(int argc, char *argv[]) { loader_talk::Fini(); cvmfs_exports_->fnFini(); + if (!ObtainSysAdminCapability()) { + LogCvmfs(kLogCvmfs, kLogDebug, + "Failed to regain SYS_ADMIN capability"); + } + // Unmount #if CVMFS_USE_LIBFUSE == 2 fuse_remove_signal_handlers(session); @@ -1155,7 +1190,8 @@ int FuseMain(int argc, char *argv[]) { CloseLibrary(); - LogCvmfs(kLogCvmfs, kLogSyslog, "CernVM-FS: unmounted %s (%s)", + LogCvmfs(kLogCvmfs, kLogSyslog, + "CernVM-FS: mount point %s (%s) session ended", mount_point_->c_str(), repository_name_->c_str()); delete fence_reload_; diff --git a/cvmfs/monitor.cc b/cvmfs/monitor.cc index 599bd17618..7884ba0296 100644 --- a/cvmfs/monitor.cc +++ b/cvmfs/monitor.cc @@ -38,6 +38,7 @@ #include #include +#include "capabilities.h" #if defined(CVMFS_FUSE_MODULE) #include "cvmfs.h" #endif @@ -67,9 +68,9 @@ int Watchdog::g_crash_signals[] = { SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGBUS, SIGPIPE, SIGXFSZ }; -Watchdog *Watchdog::Create(FnOnCrash on_crash) { +Watchdog *Watchdog::Create(FnOnExit on_exit) { assert(instance_ == NULL); - instance_ = new Watchdog(on_crash); + instance_ = new Watchdog(on_exit); instance_->Fork(); return instance_; } @@ -83,10 +84,11 @@ string Watchdog::GenerateStackTrace(pid_t pid) { int retval; string result = ""; - // re-gain root permissions to allow for ptrace of died cvmfs2 process - const bool retrievable = true; - if (!SwitchCredentials(0, getgid(), retrievable)) { - result += "failed to re-gain root permissions... still give it a try\n"; + // Get capability to ptrace died cvmfs2 process. + // This is often necessary because the cvmfs2 process can have its own + // elevated capability which would otherwise block ptrace. + if (!ObtainSysPtraceCapability()) { + result += "failed to gain ptrace capability... still give it a try\n"; } // run gdb and attach to the dying process @@ -400,6 +402,25 @@ void Watchdog::Fork() { case 0: { pipe_watchdog_->CloseWriteFd(); Daemonize(); + if ((getuid() == 0) && (geteuid() != 0)) { + if (on_exit_) { + // Reduce to minimum capabilities, which unfortunately is + // still quite powerful. + // CAP_SYS_ADMIN is needed to unmount, and CAP_DAC_READ_SEARCH + // is needed because the main process might have it and ptrace + // is not allowed on a process with more capabilities. + cap_value_t reservecaps[] = {CAP_SYS_ADMIN, CAP_DAC_READ_SEARCH}; + cap_value_t inheritablecaps[] = {CAP_DAC_READ_SEARCH}; + assert(ClearPermittedCapabilities( + sizeof(reservecaps)/sizeof(cap_value_t), reservecaps, + sizeof(inheritablecaps)/sizeof(cap_value_t), inheritablecaps)); + } else { + // Only need to be able to do the stack trace, and the + // main process needs no extra capabilities, so we can + // drop all capabilities. + assert(ClearPermittedCapabilities(0, 0, 0, 0)); + } + } // send the watchdog PID to the supervisee pid_t watchdog_pid = getpid(); pipe_pid.Write(watchdog_pid); @@ -601,12 +622,16 @@ void Watchdog::Supervise() { if (!pipe_watchdog_->TryRead(&control_flow)) { LogEmergency("watchdog: unexpected termination (" + StringifyInt(control_flow) + ")"); - if (on_crash_) on_crash_(); + if (on_exit_) on_exit_(true); } else { switch (control_flow) { case ControlFlow::kProduceStacktrace: LogEmergency(ReportStacktrace()); - if (on_crash_) on_crash_(); + if (on_exit_) on_exit_(true); + break; + + case ControlFlow::kQuitWithExit: + if (on_exit_) on_exit_(false); break; case ControlFlow::kQuit: @@ -620,11 +645,11 @@ void Watchdog::Supervise() { } -Watchdog::Watchdog(FnOnCrash on_crash) +Watchdog::Watchdog(FnOnExit on_exit) : spawned_(false) , exe_path_(string(platform_getexepath())) , watchdog_pid_(0) - , on_crash_(on_crash) + , on_exit_(on_exit) { int retval = platform_spinlock_init(&lock_handler_, 0); assert(retval == 0); @@ -646,12 +671,18 @@ Watchdog::~Watchdog() { free(sighandler_stack_.ss_sp); sighandler_stack_.ss_size = 0; - pipe_terminate_->Write(ControlFlow::kQuit); + if (on_exit_) + pipe_terminate_->Write(ControlFlow::kQuitWithExit); + else + pipe_terminate_->Write(ControlFlow::kQuit); pthread_join(thread_listener_, NULL); pipe_terminate_->Close(); } - pipe_watchdog_->Write(ControlFlow::kQuit); + if (on_exit_) + pipe_watchdog_->Write(ControlFlow::kQuitWithExit); + else + pipe_watchdog_->Write(ControlFlow::kQuit); pipe_watchdog_->CloseWriteFd(); pipe_listener_->CloseReadFd(); diff --git a/cvmfs/monitor.h b/cvmfs/monitor.h index 69a16028e6..00d110d6c9 100644 --- a/cvmfs/monitor.h +++ b/cvmfs/monitor.h @@ -35,12 +35,13 @@ class Watchdog : SingleCopy { /** * Crash cleanup handler signature. */ - typedef void (*FnOnCrash)(void); + typedef void (*FnOnExit)(const bool crashed); - static Watchdog *Create(FnOnCrash on_crash); + static Watchdog *Create(FnOnExit on_exit); static pid_t GetPid(); ~Watchdog(); void Spawn(const std::string &crash_dump_path); + void ClearOnExitFn() { on_exit_ = NULL; } /** * Signals that watchdog should not receive. If it does, report and exit. @@ -64,6 +65,7 @@ class Watchdog : SingleCopy { enum Flags { kProduceStacktrace = 0, kQuit, + kQuitWithExit, kSupervise, kUnknown, }; @@ -89,7 +91,7 @@ class Watchdog : SingleCopy { void *context); static void SendTrace(int sig, siginfo_t *siginfo, void *context); - explicit Watchdog(FnOnCrash on_crash); + explicit Watchdog(FnOnExit on_exit); void Fork(); bool WaitForSupervisee(); SigactionMap SetSignalHandlers(const SigactionMap &signal_handlers); @@ -109,7 +111,7 @@ class Watchdog : SingleCopy { /// Send the terminate signal to the listener UniquePtr > pipe_terminate_; pthread_t thread_listener_; - FnOnCrash on_crash_; + FnOnExit on_exit_; platform_spinlock lock_handler_; stack_t sighandler_stack_; SigactionMap old_signal_handlers_; diff --git a/cvmfs/publish/cmd_abort.cc b/cvmfs/publish/cmd_abort.cc index c0b38600ca..aba6019721 100644 --- a/cvmfs/publish/cmd_abort.cc +++ b/cvmfs/publish/cmd_abort.cc @@ -61,8 +61,7 @@ int CmdAbort::Main(const Options &options) { throw; } - if (!SwitchCredentials(settings->owner_uid(), settings->owner_gid(), - false /* temporarily */)) + if (!SwitchCredentials(settings->owner_uid(), settings->owner_gid(), false)) { throw EPublish("No write permission to repository", EPublish::kFailPermission); diff --git a/cvmfs/publish/cmd_commit.cc b/cvmfs/publish/cmd_commit.cc index 17c77e5a32..0481f3c71b 100644 --- a/cvmfs/publish/cmd_commit.cc +++ b/cvmfs/publish/cmd_commit.cc @@ -49,8 +49,7 @@ int CmdCommit::Main(const Options &options) { throw; } - if (!SwitchCredentials(settings->owner_uid(), settings->owner_gid(), - false /* temporarily */)) + if (!SwitchCredentials(settings->owner_uid(), settings->owner_gid(), false)) { throw EPublish("No write permission to repository"); } diff --git a/cvmfs/publish/cmd_transaction.cc b/cvmfs/publish/cmd_transaction.cc index 08fed1ca94..ac1d16c546 100644 --- a/cvmfs/publish/cmd_transaction.cc +++ b/cvmfs/publish/cmd_transaction.cc @@ -72,8 +72,7 @@ int CmdTransaction::Main(const Options &options) { settings->GetTransaction()->SetTemplate(tokens[0], tokens[1]); } - if (!SwitchCredentials(settings->owner_uid(), settings->owner_gid(), - false /* temporarily */)) + if (!SwitchCredentials(settings->owner_uid(), settings->owner_gid(), false)) { throw EPublish("No write permission to repository", EPublish::kFailPermission); diff --git a/cvmfs/publish/repository_env.cc b/cvmfs/publish/repository_env.cc index 1f6eecb103..e1ca42a8fe 100644 --- a/cvmfs/publish/repository_env.cc +++ b/cvmfs/publish/repository_env.cc @@ -5,30 +5,23 @@ #include "repository.h" -#include -#include #include +#include "capabilities.h" #include "publish/except.h" #include "util/posix.h" namespace publish { void Env::DropCapabilities() { - int retval; - // Because the process has file capabilities, its dumpable state is set to // false, which in turn makes the /proc/self/... files owned by root. We // need to reset this to have them owned by the effective UID in order to // set, e.g., uid_map/gid_map of user namespaces. - retval = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); - if (retval != 0) + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) != 0) throw EPublish("cannot clear dumpable state"); - cap_t caps = cap_get_proc(); - retval = cap_clear(caps); - cap_free(caps); - if (retval != 0) + if (!ClearPermittedCapabilities(0, 0, 0, 0)) throw EPublish("cannot clear process capabilities"); } diff --git a/cvmfs/quota_posix.cc b/cvmfs/quota_posix.cc index 71adcd6508..d9e16b8d99 100644 --- a/cvmfs/quota_posix.cc +++ b/cvmfs/quota_posix.cc @@ -45,6 +45,7 @@ #include #include +#include "capabilities.h" #include "crypto/hash.h" #include "duplex_sqlite3.h" #include "monitor.h" @@ -1093,6 +1094,15 @@ int PosixQuotaManager::MainCacheManager(int argc, char **argv) { assert(watchdog.IsValid()); watchdog->Spawn("./stacktrace.cachemgr"); + if ((getuid() == 0) && (geteuid() != 0)) { + // Permanently drop credentials + assert(ClearPermittedCapabilities(0, 0, 0, 0)); + // Leave this process ptraceable + assert(prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == 0); + // but without core dumps + assert(SetLimitCore(0) == 0); + } + // Initialize pipe, open non-blocking as cvmfs is not yet connected const int fd_lockfile_fifo = LockFile(shared_manager.workspace_dir_ + "/lock_cachemgr.fifo"); diff --git a/cvmfs/swissknife_ingest.cc b/cvmfs/swissknife_ingest.cc index 8c41c0b8e6..76e9169419 100644 --- a/cvmfs/swissknife_ingest.cc +++ b/cvmfs/swissknife_ingest.cc @@ -7,11 +7,11 @@ #include #include +#include "capabilities.h" #include "catalog_virtual.h" #include "manifest.h" #include "statistics.h" #include "statistics_database.h" -#include "swissknife_capabilities.h" #include "sync_mediator.h" #include "sync_union.h" #include "sync_union_tarball.h" diff --git a/cvmfs/swissknife_sync.cc b/cvmfs/swissknife_sync.cc index 0cc8e07b4a..7c12d4e615 100644 --- a/cvmfs/swissknife_sync.cc +++ b/cvmfs/swissknife_sync.cc @@ -29,13 +29,13 @@ #include #include #include -#include #include #include #include #include +#include "capabilities.h" #include "catalog_mgr_ro.h" #include "catalog_mgr_rw.h" #include "catalog_virtual.h" @@ -47,7 +47,6 @@ #include "sanitizer.h" #include "statistics.h" #include "statistics_database.h" -#include "swissknife_capabilities.h" #include "sync_mediator.h" #include "sync_union.h" #include "sync_union_aufs.h" diff --git a/cvmfs/sync_union_overlayfs.cc b/cvmfs/sync_union_overlayfs.cc index 08924c6fb4..f688c735dc 100644 --- a/cvmfs/sync_union_overlayfs.cc +++ b/cvmfs/sync_union_overlayfs.cc @@ -7,10 +7,10 @@ #include "sync_union.h" #include "sync_union_overlayfs.h" -#include #include #include +#include "capabilities.h" #include "sync_mediator.h" #include "util/exception.h" #include "util/fs_traversal.h" @@ -31,86 +31,6 @@ bool SyncUnionOverlayfs::Initialize() { return ObtainSysAdminCapability() && SyncUnion::Initialize(); } -bool ObtainSysAdminCapabilityInternal(cap_t caps) { - /*const*/ cap_value_t cap = CAP_SYS_ADMIN; // is non-const as cap_set_flag() - // expects a non-const pointer - // on RHEL 5 and older - -// do sanity-check if supported in otherwise just pray... -// Note: CAP_SYS_ADMIN is a rather common capability and is very likely to be -// supported by all our target systems. If it is not, one of the next -// commands will fail with a less descriptive error message. -#ifdef CAP_IS_SUPPORTED - if (!CAP_IS_SUPPORTED(cap)) { - LogCvmfs(kLogUnionFs, kLogStderr, "System doesn't support CAP_SYS_ADMIN"); - return false; - } -#endif - - if (caps == NULL) { - LogCvmfs(kLogUnionFs, kLogStderr, - "Failed to obtain capability state " - "of current process (errno: %d)", - errno); - return false; - } - - cap_flag_value_t cap_state; - if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &cap_state) != 0) { - LogCvmfs(kLogUnionFs, kLogStderr, - "Failed to check effective set for " - "CAP_SYS_ADMIN (errno: %d)", - errno); - return false; - } - - if (cap_state == CAP_SET) { - LogCvmfs(kLogUnionFs, kLogDebug, "CAP_SYS_ADMIN is already effective"); - return true; - } - - if (cap_get_flag(caps, cap, CAP_PERMITTED, &cap_state) != 0) { - LogCvmfs(kLogUnionFs, kLogStderr, - "Failed to check permitted set for " - "CAP_SYS_ADMIN (errno: %d)", - errno); - return false; - } - - if (cap_state != CAP_SET) { - LogCvmfs(kLogUnionFs, kLogStderr, - "CAP_SYS_ADMIN cannot be obtained. It's " - "not in the process's permitted-set."); - return false; - } - - if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_SET) != 0) { - LogCvmfs(kLogUnionFs, kLogStderr, - "Cannot set CAP_SYS_ADMIN as effective " - "for the current process (errno: %d)", - errno); - return false; - } - - if (cap_set_proc(caps) != 0) { - LogCvmfs(kLogUnionFs, kLogStderr, - "Cannot reset capabilities for current " - "process (errno: %d)", - errno); - return false; - } - - LogCvmfs(kLogUnionFs, kLogDebug, "Successfully obtained CAP_SYS_ADMIN"); - return true; -} - -bool SyncUnionOverlayfs::ObtainSysAdminCapability() const { - cap_t caps = cap_get_proc(); - const bool result = ObtainSysAdminCapabilityInternal(caps); - cap_free(caps); - return result; -} - void SyncUnionOverlayfs::PreprocessSyncItem(SharedPtr entry) const { SyncUnion::PreprocessSyncItem(entry); if (entry->IsGraftMarker() || entry->IsWhiteout() || entry->IsDirectory()) { diff --git a/cvmfs/sync_union_overlayfs.h b/cvmfs/sync_union_overlayfs.h index 6c6dca099b..fd767e0814 100644 --- a/cvmfs/sync_union_overlayfs.h +++ b/cvmfs/sync_union_overlayfs.h @@ -48,8 +48,6 @@ class SyncUnionOverlayfs : public SyncUnion { void CheckForBrokenHardlink(SharedPtr entry) const; void MaskFileHardlinks(SharedPtr entry) const; - bool ObtainSysAdminCapability() const; - private: bool IsOpaqueDirPath(const std::string &path) const; diff --git a/cvmfs/util/logging.h b/cvmfs/util/logging.h index 5fc0f1051c..41b5797427 100644 --- a/cvmfs/util/logging.h +++ b/cvmfs/util/logging.h @@ -5,8 +5,6 @@ #ifndef CVMFS_UTIL_LOGGING_H_ #define CVMFS_UTIL_LOGGING_H_ -#include - #include "util/export.h" // Shared declarations of debug and non-debug logging #include "util/logging_internal.h" diff --git a/cvmfs/util/logging_internal.h b/cvmfs/util/logging_internal.h index ffaf5cb897..a17ebf90ad 100644 --- a/cvmfs/util/logging_internal.h +++ b/cvmfs/util/logging_internal.h @@ -2,7 +2,7 @@ * This file is part of the CernVM File System. */ -// Internal use, include only logging.h! +// Internal use, included only by logging.h and logging.cc! #ifndef CVMFS_UTIL_LOGGING_INTERNAL_H_ #define CVMFS_UTIL_LOGGING_INTERNAL_H_ diff --git a/cvmfs/util/platform_osx.h b/cvmfs/util/platform_osx.h index c6258aa230..5809b05484 100644 --- a/cvmfs/util/platform_osx.h +++ b/cvmfs/util/platform_osx.h @@ -316,5 +316,8 @@ inline uint64_t platform_memsize() { inline int prctl(int, uint64_t, uint64_t, uint64_t, uint64_t) { return 0; } #define PR_SET_DUMPABLE 0 +#define PR_SET_KEEPCAPS 0 +#define PR_CAP_AMBIENT 0 +#define PR_CAP_AMBIENT_RAISE 0 #endif // CVMFS_UTIL_PLATFORM_OSX_H_ diff --git a/cvmfs/util/posix.cc b/cvmfs/util/posix.cc index 7390f51b54..cf7f493b57 100644 --- a/cvmfs/util/posix.cc +++ b/cvmfs/util/posix.cc @@ -768,6 +768,7 @@ std::string GetHostname() { /** * set(e){g/u}id wrapper. + * If temporarily is true, reserve the ability to switch back. */ bool SwitchCredentials(const uid_t uid, const gid_t gid, const bool temporarily) @@ -783,7 +784,7 @@ bool SwitchCredentials(const uid_t uid, const gid_t gid, retval = seteuid(uid); } else { // If effective uid is not root, we must first gain root access back - if ((getuid() == 0) && (getuid() != geteuid())) { + if ((getuid() == 0) && (geteuid() != 0)) { retval = SwitchCredentials(0, getgid(), true); if (!retval) return false; @@ -1507,6 +1508,18 @@ void GetLimitNoFile(unsigned *soft_limit, unsigned *hard_limit) { } +/** + * Sets soft and hard limit for maximum core size + */ +int SetLimitCore(unsigned limit_core) { + struct rlimit rpl; + memset(&rpl, 0, sizeof(rpl)); + rpl.rlim_max = limit_core; + rpl.rlim_cur = limit_core; + return (setrlimit(RLIMIT_CORE, &rpl)); +} + + std::vector Lsof(const std::string &path) { std::vector result; diff --git a/cvmfs/util/posix.h b/cvmfs/util/posix.h index 8f0ade7904..19a9cd2484 100644 --- a/cvmfs/util/posix.h +++ b/cvmfs/util/posix.h @@ -157,6 +157,7 @@ CVMFS_EXPORT std::string GetArch(); CVMFS_EXPORT int SetLimitNoFile(unsigned limit_nofile); CVMFS_EXPORT void GetLimitNoFile(unsigned *soft_limit, unsigned *hard_limit); +CVMFS_EXPORT int SetLimitCore(unsigned limit_core); /** * Searches for open file descriptors on the subtree starting at path. diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index ed79b5d4c0..369c6f5a0a 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -99,6 +99,7 @@ if (BUILD_SHRINKWRAP) cvmfs_crypto cvmfs_util ${GTEST_LIBRARIES} + ${CAP_LIBRARIES} pthread dl ) @@ -150,6 +151,7 @@ if (BUILD_SERVER) ${CVMFS_SOURCE_DIR}/publish/settings.cc ${CVMFS_SOURCE_DIR}/backoff.cc + ${CVMFS_SOURCE_DIR}/capabilities.cc ${CVMFS_SOURCE_DIR}/catalog.cc ${CVMFS_SOURCE_DIR}/catalog_counters.cc ${CVMFS_SOURCE_DIR}/catalog_sql.cc @@ -414,6 +416,7 @@ set(CVMFS_UNITTEST_SOURCES ${CVMFS_SOURCE_DIR}/cache_stream.cc ${CVMFS_SOURCE_DIR}/cache_tiered.cc ${CVMFS_SOURCE_DIR}/cache_transport.cc + ${CVMFS_SOURCE_DIR}/capabilities.cc ${CVMFS_SOURCE_DIR}/catalog.cc ${CVMFS_SOURCE_DIR}/catalog_counters.cc ${CVMFS_SOURCE_DIR}/catalog_mgr_client.cc @@ -551,6 +554,7 @@ list (APPEND CVMFS_UNITTESTS_LINK_LIBRARIES ${PACPARSER_LIBRARIES} ${VJSON_LIBRARIES} ${PROTOBUF_LITE_LIBRARY} + ${CAP_LIBRARIES} ${LibArchive_LIBRARY} ${RT_LIBRARY} pthread