XRootD
XrdAccGroups.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d A c c G r o u p s . c c */
4 /* */
5 /* (c) 2003 by the Board of Trustees of the Leland Stanford, Jr., University */
6 /* All Rights Reserved */
7 /* Produced by Andrew Hanushevsky for Stanford University under contract */
8 /* DE-AC02-76-SFO0515 with the Department of Energy */
9 /* */
10 /* This file is part of the XRootD software suite. */
11 /* */
12 /* XRootD is free software: you can redistribute it and/or modify it under */
13 /* the terms of the GNU Lesser General Public License as published by the */
14 /* Free Software Foundation, either version 3 of the License, or (at your */
15 /* option) any later version. */
16 /* */
17 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
18 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
19 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
20 /* License for more details. */
21 /* */
22 /* You should have received a copy of the GNU Lesser General Public License */
23 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
24 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
25 /* */
26 /* The copyright holder's institutional names and contributor's names may not */
27 /* be used to endorse or promote products derived from this software without */
28 /* specific prior written permission of the institution or contributor. */
29 /******************************************************************************/
30 
31 #include <unistd.h>
32 #include <cctype>
33 #include <cerrno>
34 #include <limits.h>
35 #include <netdb.h>
36 #include <pwd.h>
37 #include <cstring>
38 #include <strings.h>
39 #include <sys/param.h>
40 #include <sys/types.h>
41 
42 #include "XrdSys/XrdSysHeaders.hh"
43 #include "XrdSys/XrdSysPlatform.hh"
44 #include "XrdSys/XrdSysPwd.hh"
46 #include "XrdAcc/XrdAccGroups.hh"
47 #include "XrdAcc/XrdAccPrivs.hh"
48 
49 #ifdef MUSL
50 int innetgr(const char *netgroup, const char *host, const char *user,
51  const char *domain)
52 {
53  return 0;
54 }
55 #endif
56 
57 // Additionally, this routine does not support a user in more than
58 // NGROUPS_MAX groups. This is a standard unix limit defined in limits.h.
59 
60 /******************************************************************************/
61 /* G l o b a l G r o u p s O b j e c t */
62 /******************************************************************************/
63 
64 // There is only one Groups object that handles group memberships. Others
65 // needing access to this object should declare an extern to this object.
66 //
68 
69 /******************************************************************************/
70 /* G r o u p C o n s t r u c t i o n A r g u m e n t s */
71 /******************************************************************************/
72 
73 struct XrdAccGroupArgs {const char *user;
74  const char *host;
75  int gtabi;
76  const char *Gtab[NGROUPS_MAX];
77  };
78 
79 /******************************************************************************/
80 /* C o n s t r u c t o r */
81 /******************************************************************************/
82 
84 {
85 
86 // Do standard initialization
87 //
88  retrancnt = 0;
89  HaveGroups = 0;
90  HaveNetGroups = 0;
91  options = No_Group_Opt;
92  domain = 0;
93  LifeTime = 60*60*12;
94 }
95 
96 /******************************************************************************/
97 /* A d d N a m e */
98 /******************************************************************************/
99 
100 char *XrdAccGroups::AddName(const XrdAccGroupType gtype, const char *name)
101 {
102  char *np;
103  XrdOucHash<char> *hp;
104 
105 // Prepare to add a group name
106 //
107  if (gtype == XrdAccNetGroup) {hp = &NetGroup_Names; HaveNetGroups = 1;}
108  else {hp = &Group_Names; HaveGroups = 1;}
109 
110 // Lock the Name hash table
111 //
112  Group_Name_Context.Lock();
113 
114 // Add a name into the name hash table. We need to only keep a single
115 // read/only copy of the group name to speed multi-threading.
116 //
117  if (!(np = hp->Find(name)))
118  {hp->Add(name, 0, 0, Hash_data_is_key);
119  if (!(np = hp->Find(name)))
120  std::cerr <<"XrdAccGroups: Unable to add group " <<name <<std::endl;
121  }
122 
123 // All done.
124 //
125  Group_Name_Context.UnLock();
126  return np;
127 }
128 
129 /******************************************************************************/
130 /* F i n d N a m e */
131 /******************************************************************************/
132 
133 char *XrdAccGroups::FindName(const XrdAccGroupType gtype, const char *name)
134 {
135  char *np;
136 
137 // Lock the Name hash table
138 //
139  Group_Name_Context.Lock();
140 
141 // Lookup the actual name in the hash table
142 //
143  if (gtype == XrdAccNetGroup) np = NetGroup_Names.Find(name);
144  else np = Group_Names.Find(name);
145 
146 // All done.
147 //
148  Group_Name_Context.UnLock();
149  return np;
150 }
151 
152 /******************************************************************************/
153 /* */
154 /* G r o u p s ( u s e r ) */
155 /* */
156 /******************************************************************************/
157 
159 {
160 struct group *gr;
161 struct passwd *pw;
162 char **cp;
163 XrdAccGroupList *glist;
164 int gtabi;
165 char *Gtab[NGROUPS_MAX];
166 
167 // Check if we have any referenced groups
168 //
169  if (!HaveGroups) return (XrdAccGroupList *)0;
170 
171 
172 // Check if we already have this user in the group cache. Since we may be
173 // modifying the cache, we need to have exclusive control over it. We must
174 // copy the group cache because the original may be deleted at any time.
175 //
176  Group_Cache_Context.Lock();
177  if ((glist = Group_Cache.Find(user)))
178  {if (glist->First()) glist = new XrdAccGroupList(*glist);
179  else glist = 0;
180  Group_Cache_Context.UnLock();
181  return glist;
182  }
183  Group_Cache_Context.UnLock();
184 
185 // If the user has no password file entry, then we have no groups for user.
186 // All code that tries to construct a group list is protected by the
187 // Group_Build_Context mutex, obtained after we get the pwd entry.
188 //
189  XrdSysPwd thePwd(user, &pw);
190  if (pw == NULL) return (XrdAccGroupList *)0;
191 
192 // Build first entry for the primary group. We will ignore the primary group
193 // listing later. We do this to ensure that the user has at least one group
194 // regardless of what the groups file actually says.
195 //
196  Group_Build_Context.Lock();
197  gtabi = addGroup(user, pw->pw_gid, 0, Gtab, 0);
198 
199 // Now run through all of the group entries getting the list of user's groups
200 // Do this only when Primary_Only is not turned on (i.e., SVR5 semantics)
201 //
202  if (!(options & Primary_Only))
203  {
204  setgrent() ;
205  while ((gr = getgrent()))
206  {
207  if (pw->pw_gid == gr->gr_gid) continue; /*Already have this one.*/
208  for (cp = gr->gr_mem; cp && *cp; cp++)
209  if (strcmp(*cp, user) == 0)
210  gtabi = addGroup(user, gr->gr_gid,
211  Dotran(gr->gr_gid,gr->gr_name),
212  Gtab, gtabi);
213  }
214  endgrent();
215  }
216 
217 // All done with non mt-safe routines
218 //
219  Group_Build_Context.UnLock();
220 
221 // Allocate a new GroupList object
222 //
223  glist = new XrdAccGroupList(gtabi, (const char **)Gtab);
224 
225 // Add this user to the group cache to speed things up the next time
226 //
227  Group_Cache_Context.Lock();
228  Group_Cache.Add(user, glist, LifeTime);
229  Group_Cache_Context.UnLock();
230 
231 // Return a copy of the group list since the original may be deleted
232 //
233  if (!gtabi) return (XrdAccGroupList *)0;
234  return new XrdAccGroupList(gtabi, (const char **)Gtab);
235 }
236 
237 /******************************************************************************/
238 /* N e t G r o u p s ( u s e r , h o s t ) */
239 /******************************************************************************/
240 
241 XrdAccGroupList *XrdAccGroups::NetGroups(const char *user, const char *host)
242 {
243 XrdAccGroupList *glist;
244 int i, j;
245 char uh_key[MAXHOSTNAMELEN+96];
246 struct XrdAccGroupArgs GroupTab;
247 int XrdAccCheckNetGroup(const char *netgroup, char *key, void *Arg);
248 
249 // Check if we have any Netgroups
250 //
251  if (!HaveNetGroups) return (XrdAccGroupList *)0;
252 
253 // Construct the key for this user
254 //
255  i = strlen(user); j = strlen(host);
256  if (i+j+2 > (int)sizeof(uh_key)) return (XrdAccGroupList *)0;
257  strcpy(uh_key, user);
258  uh_key[i] = '@';
259  strcpy(&uh_key[i+1], host);
260 
261 // Check if we already have this user in the group cache. Since we may be
262 // modifying the cache, we need to have exclusive control over it. We must
263 // copy the group cache entry because the original may be deleted at any time.
264 //
265  NetGroup_Cache_Context.Lock();
266  if ((glist = NetGroup_Cache.Find(uh_key)))
267  {if (glist->First()) glist = new XrdAccGroupList(*glist);
268  else glist = 0;
269  NetGroup_Cache_Context.UnLock();
270  return glist;
271  }
272  NetGroup_Cache_Context.UnLock();
273 
274 // For each known netgroup, check to see if the user is in the netgroup.
275 //
276  GroupTab.user = user;
277  GroupTab.host = host;
278  GroupTab.gtabi = 0;
279  Group_Name_Context.Lock();
280  NetGroup_Names.Apply(XrdAccCheckNetGroup, (void *)&GroupTab);
281  Group_Name_Context.UnLock();
282 
283 // Allocate a new GroupList object
284 //
285  glist = new XrdAccGroupList(GroupTab.gtabi,
286  (const char **)GroupTab.Gtab);
287 
288 // Add this user to the group cache to speed things up the next time
289 //
290  NetGroup_Cache_Context.Lock();
291  NetGroup_Cache.Add((const char *)uh_key, glist, LifeTime);
292  NetGroup_Cache_Context.UnLock();
293 
294 // Return a copy of the group list
295 //
296  if (!GroupTab.gtabi) return (XrdAccGroupList *)0;
297  return new XrdAccGroupList(GroupTab.gtabi,
298  (const char **)GroupTab.Gtab);
299 }
300 
301 /******************************************************************************/
302 /* P u r g e C a c h e */
303 /******************************************************************************/
304 
306 {
307 
308 // Purge the group cache
309 //
310  Group_Cache_Context.Lock();
311  Group_Cache.Purge();
312  Group_Cache_Context.UnLock();
313 
314 // Purge the netgroup cache
315 //
316  NetGroup_Cache_Context.Lock();
317  NetGroup_Cache.Purge();
318  NetGroup_Cache_Context.UnLock();
319 }
320 
321 /******************************************************************************/
322 /* R e t r a n */
323 /******************************************************************************/
324 
325 int XrdAccGroups::Retran(const gid_t gid)
326 {
327  if ((int)gid < 0) retrancnt = 0;
328  else {if (retrancnt > (int)(sizeof(retrangid)/sizeof(gid_t))) return -1;
329  retrangid[retrancnt++] = gid;
330  }
331  return 0;
332 }
333 
334 /******************************************************************************/
335 /* P r i v a t e M e t h o d s */
336 /******************************************************************************/
337 
338 /******************************************************************************/
339 /* a d d G r o u p */
340 /******************************************************************************/
341 
342 int XrdAccGroups::addGroup(const char *user, const gid_t gid, char *gname,
343  char **Gtab, int gtabi)
344 {
345  char *gp;
346 
347 // Check if we have room to add another group. We can squeek by such errors
348 // because all it means is that the user normally has fewer privs (which is
349 // not always true, sigh).
350 //
351  if (gtabi >= NGROUPS_MAX)
352  {if (gtabi == NGROUPS_MAX)
353  std::cerr <<"XrdAccGroups: More than " <<gtabi <<"groups for " <<user <<std::endl;
354  return gtabi;
355  }
356 
357 // See if we should lookup the group name. The caller had better be holding the
358 // Group_Build_Context mutex.
359 //
360 if (!gname || !gname[0])
361  {struct group *gp;
362  if ((gp = getgrgid(gid)) == NULL) return gtabi;
363  else gname = gp->gr_name;
364  }
365 
366 // Check if we have this group registered. Only a handful of groups are
367 // actually relevant. Ignore the unreferenced groups. If registered, we
368 // need the persistent name because of multi-threading issues.
369 //
370  if (!(gp = Group_Names.Find(gname)) ) return gtabi;
371 
372 // Add the groupname to the table of groups for the user
373 //
374  Gtab[gtabi++] = gp;
375  return gtabi;
376 }
377 
378 /******************************************************************************/
379 /* D o t r a n */
380 /******************************************************************************/
381 
382 char *XrdAccGroups::Dotran(const gid_t gid, char *gname)
383 {
384  int i;
385 
386  // See if the groupname needs to be retranslated. This is necessary
387  // When multiple groups share the same gid due to NIS constraints.
388  //
389  for (i = 0; i < retrancnt; i++) if (retrangid[i] == gid) return (char *)0;
390  return gname;
391 }
392 
393 /******************************************************************************/
394 /* E x t e r n a l F u n c t i o n s */
395 /******************************************************************************/
396 
397 /******************************************************************************/
398 /* o o a c c _ C h e c k N e t G r o u p */
399 /******************************************************************************/
400 
401 int XrdAccCheckNetGroup(const char *netgroup, char *key, void *Arg)
402 {
403  struct XrdAccGroupArgs *grp = static_cast<struct XrdAccGroupArgs *>(Arg);
404 
405  // Check if this netgroup, user, host, domain combination exists.
406  //
407  if (innetgr(netgroup, (const char *)grp->host, (const char *)grp->user,
409  {if (grp->gtabi >= NGROUPS_MAX)
410  {if (grp->gtabi == NGROUPS_MAX)
411  std::cerr <<"XrdAccGroups: More than " <<grp->gtabi <<"netgroups for " <<grp->user <<std::endl;
412  return 1;
413  }
414 
415  // Add the groupname into the groupname hash table. We have already
416  // been passed the read/only copy of the name.
417  //
418  grp->Gtab[grp->gtabi] = netgroup; grp->gtabi++;
419  }
420  return 0;
421 }
const char * user
Definition: XrdAccGroups.cc:73
int XrdAccCheckNetGroup(const char *netgroup, char *key, void *Arg)
XrdAccGroups XrdAccGroupMaster
Definition: XrdAccGroups.cc:67
const char * Gtab[NGROUPS_MAX]
Definition: XrdAccGroups.cc:76
const char * host
Definition: XrdAccGroups.cc:74
XrdAccGroupType
Definition: XrdAccGroups.hh:90
@ XrdAccNetGroup
Definition: XrdAccGroups.hh:90
@ No_Group_Opt
Definition: XrdAccGroups.hh:83
@ Primary_Only
Definition: XrdAccGroups.hh:81
@ Hash_data_is_key
Definition: XrdOucHash.hh:52
const char * First()
Definition: XrdAccGroups.hh:47
void PurgeCache()
char * AddName(const XrdAccGroupType gtype, const char *name)
int Retran(const gid_t gid)
XrdAccGroupList * Groups(const char *user)
XrdAccGroupList * NetGroups(const char *user, const char *host)
char * FindName(const XrdAccGroupType gtype, const char *name)
const char * Domain()
void Purge()
Definition: XrdOucHash.icc:193
T * Apply(int(*func)(const char *, T *, void *), void *Arg)
Definition: XrdOucHash.icc:102
T * Add(const char *KeyVal, T *KeyData, const int LifeTime=0, XrdOucHash_Options opt=Hash_default)
Definition: XrdOucHash.icc:61
T * Find(const char *KeyVal, time_t *KeyTime=0)
Definition: XrdOucHash.icc:160