Skip to content

Commit 64de6e5

Browse files
committed
Use setgroups syscall to set supplementary groups on individual threads
When the operation is finished, we clear the supplementary groups. We don't save and restore the original groups, as the *-privileged processes have none defined.
1 parent e62908d commit 64de6e5

1 file changed

Lines changed: 34 additions & 0 deletions

File tree

src/UserSentry.hh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414

1515
#include <dlfcn.h>
1616
#include <fcntl.h>
17+
#include <grp.h>
1718
#include <pwd.h>
1819
#include <sys/capability.h>
1920
#include <sys/fsuid.h>
2021
#include <sys/types.h>
2122
#include <sys/stat.h>
23+
#include <sys/syscall.h>
2224
#include <unistd.h>
2325

2426
// TODO: set this via library parameters.
@@ -119,6 +121,13 @@ public:
119121
return false;
120122
}
121123

124+
// Use syscall to set supplementary groups instead of through glibc so
125+
// changes are applied to individual threads only. See nptl(7).
126+
static int ThreadSetgroups(size_t size, const gid_t *list)
127+
{
128+
return syscall(SYS_setgroups, size, list);
129+
}
130+
122131
void Init(const std::string username, XrdSysError &log)
123132
{
124133
struct passwd pwd, *result = nullptr;
@@ -156,6 +165,24 @@ public:
156165
return;
157166
}
158167

168+
// Get supplementary groups for user
169+
int ngroups = 16;
170+
std::vector<gid_t> groups(ngroups);
171+
do {
172+
int old_ngroups = ngroups;
173+
retval = getgrouplist(username.c_str(), pwd.pw_gid, groups.data(), &ngroups);
174+
if (-1 == retval && ngroups > old_ngroups) {
175+
// Too many groups. Resize buffer and try again.
176+
groups.resize(ngroups);
177+
continue;
178+
}
179+
break;
180+
} while (1);
181+
if (-1 == retval) {
182+
m_log.Emsg("UserSentry", "Failure when looking up supplementary groups for username", username.c_str());
183+
return;
184+
}
185+
159186
// Note: Capabilities need to be set per thread, so we need to do this
160187
ConfigCaps(m_log, nullptr);
161188

@@ -167,6 +194,7 @@ public:
167194
return;
168195
}
169196
m_orig_gid = setfsgid(result->pw_gid);
197+
ThreadSetgroups(ngroups, groups.data());
170198
}
171199

172200
~UserSentry() {
@@ -176,6 +204,12 @@ public:
176204
if ((m_orig_gid != -1) && (-1 == setfsgid(m_orig_gid))) {
177205
m_log.Emsg("UserSentry", "Failed to return fsgid to original state", strerror(errno));
178206
}
207+
// Clear supplementary groups
208+
// We don't need to restore the daemon's original groups, as the
209+
// *-privileged processes run without supplementary groups defined.
210+
if ((0 != ThreadSetgroups(0, nullptr))) {
211+
m_log.Emsg("UserSentry", "Failed to clear supplementary groups", strerror(errno));
212+
}
179213
}
180214

181215
bool IsValid() const {return ((m_orig_gid != -1) && (m_orig_uid != -1)) || m_is_anonymous;}

0 commit comments

Comments
 (0)