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