XRootD
XrdSysPriv.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d S y s P r i v . c c */
4 /* */
5 /* (c) 2006 G. Ganis (CERN) */
6 /* */
7 /* This file is part of the XRootD software suite. */
8 /* */
9 /* XRootD is free software: you can redistribute it and/or modify it under */
10 /* the terms of the GNU Lesser General Public License as published by the */
11 /* Free Software Foundation, either version 3 of the License, or (at your */
12 /* option) any later version. */
13 /* */
14 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
15 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
16 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
17 /* License for more details. */
18 /* */
19 /* You should have received a copy of the GNU Lesser General Public License */
20 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
21 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
22 /* */
23 /* The copyright holder's institutional names and contributor's names may not */
24 /* be used to endorse or promote products derived from this software without */
25 /* specific prior written permission of the institution or contributor. */
26 /* All Rights Reserved. See XrdInfo.cc for complete License Terms */
27 /******************************************************************************/
28 
30 // //
31 // XrdSysPriv //
32 // //
33 // Author: G. Ganis, CERN, 2006 //
34 // //
35 // Implementation of a privileges handling API following the paper //
36 // "Setuid Demystified" by H.Chen, D.Wagner, D.Dean //
37 // also quoted in "Secure programming Cookbook" by J.Viega & M.Messier. //
38 // //
40 
41 #include "XrdSys/XrdSysPriv.hh"
42 
43 #if !defined(WINDOWS)
44 #include <cstdio>
45 #include "XrdSys/XrdSysHeaders.hh"
46 #include "XrdSys/XrdSysPwd.hh"
47 #include <unistd.h>
48 #include <pwd.h>
49 #include <cerrno>
50 
51 #define NOUC ((uid_t)(-1))
52 #define NOGC ((gid_t)(-1))
53 #define XSPERR(x) ((x == 0) ? -1 : -x)
54 
55 // Some machine specific stuff
56 #if defined(__sgi) && !defined(__GNUG__) && (SGI_REL<62)
57 extern "C" {
58  int seteuid(int euid);
59  int setegid(int egid);
60  int geteuid();
61  int getegid();
62 }
63 #endif
64 
65 #if defined(_AIX)
66 extern "C" {
67  int seteuid(uid_t euid);
68  int setegid(gid_t egid);
69  uid_t geteuid();
70  gid_t getegid();
71 }
72 #endif
73 
74 #if !defined(HAVE_SETRESUID)
75 static int setresgid(gid_t r, gid_t e, gid_t)
76 {
77  if (r != NOGC && setgid(r) == -1)
78  return XSPERR(errno);
79  return ((e != NOGC) ? setegid(e) : 0);
80 }
81 
82 static int setresuid(uid_t r, uid_t e, uid_t)
83 {
84  if (r != NOUC && setuid(r) == -1)
85  return XSPERR(errno);
86  return ((e != NOUC) ? seteuid(e) : 0);
87 }
88 
89 static int getresgid(gid_t *r, gid_t *e, gid_t *)
90 {
91  *r = getgid();
92  *e = getegid();
93  return 0;
94 }
95 
96 static int getresuid(uid_t *r, uid_t *e, uid_t *)
97 {
98  *r = getuid();
99  *e = geteuid();
100  return 0;
101 }
102 
103 #else
104 #if (defined(__linux__) || \
105  (defined(__CYGWIN__) && defined(__GNUC__))) && !defined(linux)
106 # define linux
107 #endif
108 #if defined(linux) && !defined(HAVE_SETRESUID)
109 extern "C" {
110  int setresgid(gid_t r, gid_t e, gid_t s);
111  int setresuid(uid_t r, uid_t e, uid_t s);
112  int getresgid(gid_t *r, gid_t *e, gid_t *s);
113  int getresuid(uid_t *r, uid_t *e, uid_t *s);
114 }
115 #endif
116 #endif
117 #endif // not WINDOWS
118 
119 bool XrdSysPriv::fDebug = 0; // debug switch
120 
121 // Gloval mutex
122 XrdSysRecMutex XrdSysPriv::fgMutex;
123 
124 //______________________________________________________________________________
125 int XrdSysPriv::Restore(bool saved)
126 {
127  // Restore the 'saved' (saved = TRUE) or 'real' entity as effective.
128  // Return 0 on success, < 0 (== -errno) if any error occurs.
129 
130 #if !defined(WINDOWS)
131  // Get the UIDs
132  uid_t ruid = 0, euid = 0, suid = 0;
133  if (getresuid(&ruid, &euid, &suid) != 0)
134  return XSPERR(errno);
135 
136  // Set the wanted value
137  uid_t uid = saved ? suid : ruid;
138 
139  // Act only if a change is needed
140  if (euid != uid) {
141 
142  // Set uid as effective
143  if (setresuid(NOUC, uid, NOUC) != 0)
144  return XSPERR(errno);
145 
146  // Make sure the new effective UID is the one wanted
147  if (geteuid() != uid)
148  return XSPERR(errno);
149  }
150 
151  // Get the GIDs
152  uid_t rgid = 0, egid = 0, sgid = 0;
153  if (getresgid(&rgid, &egid, &sgid) != 0)
154  return XSPERR(errno);
155 
156  // Set the wanted value
157  gid_t gid = saved ? sgid : rgid;
158 
159  // Act only if a change is needed
160  if (egid != gid) {
161 
162  // Set newuid as effective, saving the current effective GID
163  if (setresgid(NOGC, gid, NOGC) != 0)
164  return XSPERR(errno);
165 
166  // Make sure the new effective GID is the one wanted
167  if (getegid() != gid)
168  return XSPERR(errno);
169  }
170 
171 #endif
172  // Done
173  return 0;
174 }
175 
176 //______________________________________________________________________________
177 int XrdSysPriv::ChangeTo(uid_t newuid, gid_t newgid)
178 {
179  // Change effective to entity newuid. Current entity is saved.
180  // Real entity is not touched. Use RestoreSaved to go back to
181  // previous settings.
182  // Return 0 on success, < 0 (== -errno) if any error occurs.
183 
184 #if !defined(WINDOWS)
185  // Current UGID
186  uid_t oeuid = geteuid();
187  gid_t oegid = getegid();
188 
189  // Restore privileges, if needed
190  if (oeuid && XrdSysPriv::Restore(0) != 0)
191  return XSPERR(errno);
192 
193  // Act only if a change is needed
194  if (newgid != oegid) {
195 
196  // Set newgid as effective, saving the current effective GID
197  if (setresgid(NOGC, newgid, oegid) != 0)
198  return XSPERR(errno);
199 
200  // Get the GIDs
201  uid_t rgid = 0, egid = 0, sgid = 0;
202  if (getresgid(&rgid, &egid, &sgid) != 0)
203  return XSPERR(errno);
204 
205  // Make sure the new effective GID is the one wanted
206  if (egid != newgid)
207  return XSPERR(errno);
208  }
209 
210  // Act only if a change is needed
211  if (newuid != oeuid) {
212 
213  // Set newuid as effective, saving the current effective UID
214  if (setresuid(NOUC, newuid, oeuid) != 0)
215  return XSPERR(errno);
216 
217  // Get the UIDs
218  uid_t ruid = 0, euid = 0, suid = 0;
219  if (getresuid(&ruid, &euid, &suid) != 0)
220  return XSPERR(errno);
221 
222  // Make sure the new effective UID is the one wanted
223  if (euid != newuid)
224  return XSPERR(errno);
225  }
226 
227 #endif
228  // Done
229  return 0;
230 }
231 
232 //______________________________________________________________________________
233 int XrdSysPriv::ChangePerm(uid_t newuid, gid_t newgid)
234 {
235  // Change permanently to entity newuid. Requires super-userprivileges.
236  // Provides a way to drop permanently su privileges.
237  // Return 0 on success, < 0 (== -errno) if any error occurs.
238 
239  // Atomic action
240  XrdSysPriv::fgMutex.Lock();
241 #if !defined(WINDOWS)
242  // Get UIDs
243  uid_t cruid = 0, ceuid = 0, csuid = 0;
244  if (getresuid(&cruid, &ceuid, &csuid) != 0) {
245  XrdSysPriv::fgMutex.UnLock();
246  return XSPERR(errno);
247  }
248 
249  // Get GIDs
250  uid_t crgid = 0, cegid = 0, csgid = 0;
251  if (getresgid(&crgid, &cegid, &csgid) != 0) {
252  XrdSysPriv::fgMutex.UnLock();
253  return XSPERR(errno);
254  }
255  // Restore privileges, if needed
256  if (ceuid && XrdSysPriv::Restore(0) != 0) {
257  XrdSysPriv::fgMutex.UnLock();
258  return XSPERR(errno);
259  }
260  // Act only if needed
261  if (newgid != cegid || newgid != crgid) {
262 
263  // Set newgid as GID, all levels
264  if (setresgid(newgid, newgid, newgid) != 0) {
265  XrdSysPriv::fgMutex.UnLock();
266  return XSPERR(errno);
267  }
268  // Get GIDs
269  uid_t rgid = 0, egid = 0, sgid = 0;
270  if (getresgid(&rgid, &egid, &sgid) != 0) {
271  XrdSysPriv::fgMutex.UnLock();
272  return XSPERR(errno);
273  }
274  // Make sure the new GIDs are all equal to the one asked
275  if (rgid != newgid || egid != newgid) {
276  XrdSysPriv::fgMutex.UnLock();
277  return XSPERR(errno);
278  }
279  }
280 
281  // Act only if needed
282  if (newuid != ceuid || newuid != cruid) {
283 
284  // Set newuid as UID, all levels
285  if (setresuid(newuid, newuid, newuid) != 0) {
286  XrdSysPriv::fgMutex.UnLock();
287  return XSPERR(errno);
288  }
289  // Get UIDs
290  uid_t ruid = 0, euid = 0, suid = 0;
291  if (getresuid(&ruid, &euid, &suid) != 0) {
292  XrdSysPriv::fgMutex.UnLock();
293  return XSPERR(errno);
294  }
295  // Make sure the new UIDs are all equal to the one asked
296  if (ruid != newuid || euid != newuid) {
297  XrdSysPriv::fgMutex.UnLock();
298  return XSPERR(errno);
299  }
300  }
301 #endif
302  // Release the mutex
303  XrdSysPriv::fgMutex.UnLock();
304 
305  // Done
306  return 0;
307 }
308 
309 //______________________________________________________________________________
310 void XrdSysPriv::DumpUGID(const char *msg)
311 {
312  // Dump current entity
313 
314 #if !defined(WINDOWS)
315  XrdSysPriv::fgMutex.Lock();
316  // Get the UIDs
317  uid_t ruid = 0, euid = 0, suid = 0;
318  if (getresuid(&ruid, &euid, &suid) != 0)
319  return;
320 
321  // Get the GIDs
322  uid_t rgid = 0, egid = 0, sgid = 0;
323  if (getresgid(&rgid, &egid, &sgid) != 0)
324  return;
325 
326  std::cout << "XrdSysPriv: " << std::endl;
327  std::cout << "XrdSysPriv: dump values: " << (msg ? msg : "") << std::endl;
328  std::cout << "XrdSysPriv: " << std::endl;
329  std::cout << "XrdSysPriv: real = (" << ruid <<","<< rgid <<")" << std::endl;
330  std::cout << "XrdSysPriv: effective = (" << euid <<","<< egid <<")" << std::endl;
331  std::cout << "XrdSysPriv: saved = (" << suid <<","<< sgid <<")" << std::endl;
332  std::cout << "XrdSysPriv: " << std::endl;
333  XrdSysPriv::fgMutex.UnLock();
334 #endif
335 }
336 
337 //
338 // Guard class
339 //______________________________________________________________________________
340 XrdSysPrivGuard::XrdSysPrivGuard(uid_t uid, gid_t gid)
341 {
342  // Constructor. Create a guard object for temporarily change to privileges
343  // of {'uid', 'gid'}
344 
345  dum = 1;
346  valid = 0;
347 
348  Init(uid, gid);
349 }
350 
351 //______________________________________________________________________________
353 {
354  // Constructor. Create a guard object for temporarily change to privileges
355  // of 'usr'
356 
357  dum = 1;
358  valid = 0;
359 
360 #if !defined(WINDOWS)
361  if (usr && strlen(usr) > 0) {
362  struct passwd *pw;
363  XrdSysPwd thePwd(usr, &pw);
364  if (pw)
365  Init(pw->pw_uid, pw->pw_gid);
366  }
367 #else
368  if (usr) { }
369 #endif
370 }
371 
372 //______________________________________________________________________________
374 {
375  // Destructor. Restore state and unlock the global mutex.
376 
377  if (!dum) {
378  XrdSysPriv::Restore();
379  XrdSysPriv::fgMutex.UnLock();
380  }
381 }
382 
383 //______________________________________________________________________________
384 void XrdSysPrivGuard::Init(uid_t uid, gid_t gid)
385 {
386  // Init a change of privileges guard. Act only if superuser.
387  // The result of initialization can be tested with the Valid() method.
388 
389  dum = 1;
390  valid = 1;
391 
392  // Debug hook
393  if (XrdSysPriv::fDebug)
394  XrdSysPriv::DumpUGID("before Init()");
395 
396 #if !defined(WINDOWS)
397  XrdSysPriv::fgMutex.Lock();
398  uid_t ruid = 0, euid = 0, suid = 0;
399  gid_t rgid = 0, egid = 0, sgid = 0;
400  if (getresuid(&ruid, &euid, &suid) == 0 &&
401  getresgid(&rgid, &egid, &sgid) == 0) {
402  if ((euid != uid) || (egid != gid)) {
403  if (!ruid) {
404  // Change temporarily identity
405  if (XrdSysPriv::ChangeTo(uid, gid) != 0)
406  valid = 0;
407  dum = 0;
408  } else {
409  // Change requested but not enough privileges
410  valid = 0;
411  }
412  }
413  } else {
414  // Something bad happened: memory corruption?
415  valid = 0;
416  }
417  // Unlock if no action
418  if (dum)
419  XrdSysPriv::fgMutex.UnLock();
420 #endif
421  // Debug hook
422  if (XrdSysPriv::fDebug)
423  XrdSysPriv::DumpUGID("after Init()");
424 }
#define NOUC
Definition: XrdSysPriv.cc:51
#define XSPERR(x)
Definition: XrdSysPriv.cc:53
#define NOGC
Definition: XrdSysPriv.cc:52
static int getresgid(gid_t *r, gid_t *e, gid_t *)
Definition: XrdSysPriv.cc:89
static int setresgid(gid_t r, gid_t e, gid_t)
Definition: XrdSysPriv.cc:75
static int getresuid(uid_t *r, uid_t *e, uid_t *)
Definition: XrdSysPriv.cc:96
static int setresuid(uid_t r, uid_t e, uid_t)
Definition: XrdSysPriv.cc:82
XrdSysPrivGuard(uid_t uid, gid_t gid)
Definition: XrdSysPriv.cc:340
virtual ~XrdSysPrivGuard()
Definition: XrdSysPriv.cc:373
static int ChangePerm(uid_t uid, gid_t gid)
Definition: XrdSysPriv.cc:233