XRootD
Loading...
Searching...
No Matches
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>
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)
57extern "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)
66extern "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)
75static 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
82static 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
89static int getresgid(gid_t *r, gid_t *e, gid_t *)
90{
91 *r = getgid();
92 *e = getegid();
93 return 0;
94}
95
96static 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)
109extern "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
119bool XrdSysPriv::fDebug = 0; // debug switch
120
121// Gloval mutex
122XrdSysRecMutex XrdSysPriv::fgMutex;
123
124//______________________________________________________________________________
125int 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//______________________________________________________________________________
177int 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//______________________________________________________________________________
233int 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//______________________________________________________________________________
310void 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//______________________________________________________________________________
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//______________________________________________________________________________
384void 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)
virtual ~XrdSysPrivGuard()
static int ChangePerm(uid_t uid, gid_t gid)