Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,23 @@ To enable the checksum (only on XRootD 5.2+):
ofs.ckslib * libXrdMultiuser.so
```

The following optional directives can also be set in the Xrootd configuration file:

| Directive | Default | Description |
| --- | --- | --- |
| `multiuser.umask <octal>` | (unset) | Apply this umask to files and directories created through the plugin. |
| `multiuser.checksumonwrite <on\|off>` | `off` | Compute checksums while a file is being written. |
| `multiuser.minuid <n>` | `500` | Minimum UID a mapped username may resolve to; usernames mapping to a lower UID are treated as system accounts and denied. |
| `multiuser.mingid <n>` | `500` | Minimum GID a mapped username may resolve to; usernames mapping to a lower GID are treated as system accounts and denied. |

For example, to allow users and groups with IDs as low as 100 (e.g., groups
imported from a Lustre file system):

```
multiuser.minuid 100
multiuser.mingid 100
```

Startup
-------

Expand Down
7 changes: 7 additions & 0 deletions configs/60-osg-multiuser.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,11 @@ if defined ?~XC_ENABLE_MULTIUSER
# checksum while it is writing a file. To turn this on, uncomment the
# following line:
# multiuser.checksumonwrite on

# Usernames that map to a UID or GID below these thresholds are treated as
# system accounts and are denied access. Both default to 500. Lower them
# if your site has legitimate users/groups with smaller IDs (for example,
# groups imported from a Lustre file system):
# multiuser.minuid 500
# multiuser.mingid 500
fi
64 changes: 64 additions & 0 deletions src/MultiuserFileSystem.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
#include "MultiuserFile.hh"

#include <exception>
#include <limits>
#include <memory>
#include <mutex>
#include <vector>
#include <sstream>
#include <iomanip>
#include <stdint.h>

#include <dlfcn.h>
#include <fcntl.h>
Expand Down Expand Up @@ -68,6 +70,31 @@ MultiuserFileSystem::Config(XrdSysLogger *lp, const char *configfn)
}
Config.Attach(cfgFD);
const char *val;

// Parse a single non-negative integer argument for the given directive.
// On success stores the value in `out` and returns true; on any error it
// logs an appropriate message and returns false.
auto parse_min_id = [&](const char *directive, long int &out) -> bool {
val = Config.GetWord();
if (!val || !val[0]) {
m_log.Emsg("Config", directive, "must specify a value");
return false;
}
char *endptr = NULL;
errno = 0;
long int id_val = strtol(val, &endptr, 10);
if (errno || (endptr && *endptr != '\0')) {
m_log.Emsg("Config", directive, "must specify a valid integer value");
return false;
}
if (id_val < 0) {
m_log.Emsg("Config", directive, "must not be negative");
return false;
}
out = id_val;
return true;
};

while ((val = Config.GetMyFirstWord())) {
if (!strcmp("multiuser.umask", val)) {
val = Config.GetWord();
Expand All @@ -92,6 +119,36 @@ MultiuserFileSystem::Config(XrdSysLogger *lp, const char *configfn)
m_umask_mode = umask_val;
}

// Minimum UID a mapped username may resolve to.
if (!strcmp("multiuser.minuid", val)) {
long int min_uid = 0;
if (!parse_min_id("multiuser.minuid", min_uid)) {
Config.Close();
return false;
}
if (static_cast<uintmax_t>(min_uid) > static_cast<uintmax_t>(std::numeric_limits<uid_t>::max())) {
m_log.Emsg("Config", "multiuser.minuid exceeds the maximum supported UID value");
Config.Close();
return false;
}
UserSentry::SetMinimumUid(static_cast<uid_t>(min_uid));
}

// Minimum GID a mapped username may resolve to.
if (!strcmp("multiuser.mingid", val)) {
long int min_gid = 0;
if (!parse_min_id("multiuser.mingid", min_gid)) {
Config.Close();
return false;
}
if (static_cast<uintmax_t>(min_gid) > static_cast<uintmax_t>(std::numeric_limits<gid_t>::max())) {
m_log.Emsg("Config", "multiuser.mingid exceeds the maximum supported GID value");
Config.Close();
return false;
}
UserSentry::SetMinimumGid(static_cast<gid_t>(min_gid));
}

// Checksum on write
if (!strcmp("multiuser.checksumonwrite", val)) {
val = Config.GetWord();
Expand Down Expand Up @@ -159,6 +216,13 @@ MultiuserFileSystem::Config(XrdSysLogger *lp, const char *configfn)
umask(m_umask_mode);
}

{
std::stringstream ss;
ss << "Requiring mapped users to have a minimum UID of " << UserSentry::GetMinimumUid()
<< " and a minimum GID of " << UserSentry::GetMinimumGid();
m_log.Emsg("Config", ss.str().c_str());
}

return true;

}
Expand Down
21 changes: 15 additions & 6 deletions src/UserSentry.hh
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@
#include <sys/syscall.h>
#include <unistd.h>

// TODO: set this via library parameters.
static const int g_minimum_uid = 500;
static const int g_minimum_gid = 500;


/**
* Note: originally, we tried to use CAP_DAC_READ_SEARCH as that provides exactly what we need -
Expand Down Expand Up @@ -110,6 +106,14 @@ public:

static bool IsCmsd() {return m_is_cmsd;}

// Configure the minimum UID/GID a mapped username may resolve to. Any
// username resolving to an ID below these thresholds is treated as a
// system account and denied access. Defaults to 500 (see multiuser.cpp).
static void SetMinimumUid(uid_t uid) {m_min_uid = uid;}
static void SetMinimumGid(gid_t gid) {m_min_gid = gid;}
static uid_t GetMinimumUid() {return m_min_uid;}
static gid_t GetMinimumGid() {return m_min_gid;}

static bool IsGsiUserMapped(const XrdSecEntity *client) {
// If VOMS was used to map client, return true
if (client->vorg) { return true; }
Expand Down Expand Up @@ -164,11 +168,11 @@ public:
}
return;
}
if (pwd.pw_uid < g_minimum_uid) {
if (pwd.pw_uid < m_min_uid) {
m_log.Emsg("UserSentry", "Multiuser denying access: Username", username.c_str(), "maps to a system UID; rejecting lookup");
return;
}
if (pwd.pw_gid < g_minimum_gid) {
if (pwd.pw_gid < m_min_gid) {
m_log.Emsg("UserSentry", "Multiuser denying access: Username", username.c_str(), "maps to a system GID; rejecting lookup");
return;
}
Expand Down Expand Up @@ -229,6 +233,11 @@ private:

static bool m_is_cmsd;

// Minimum UID/GID thresholds; configurable via multiuser.minuid /
// multiuser.mingid. Definitions (and defaults) live in multiuser.cpp.
static uid_t m_min_uid;
static gid_t m_min_gid;

XrdSysError &m_log;
};

Expand Down
5 changes: 5 additions & 0 deletions src/multiuser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ ChecksumManager* g_checksum_manager = nullptr;

bool UserSentry::m_is_cmsd = false;

// Default minimum UID/GID. Usernames mapping to an ID below these values are
// rejected as system accounts. Override via multiuser.minuid / multiuser.mingid.
uid_t UserSentry::m_min_uid = 500;
gid_t UserSentry::m_min_gid = 500;

XrdVERSIONINFO(XrdOssGetFileSystem, Multiuser);

// The status-quo to retrieve the default object is to copy/paste the
Expand Down