Move set_thread_credentials_permanently() to set_thread_credentials()
[mat/samba.git] / source3 / lib / util_sec.c
1 /*
2    Unix SMB/CIFS implementation.
3    Copyright (C) Jeremy Allison 1998.
4    rewritten for version 2.0.6 by Tridge
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #ifndef AUTOCONF_TEST
21 #include "includes.h"
22 #include "system/passwd.h" /* uid_wrapper */
23 #include "../lib/util/setid.h"
24
25 #else
26 /* we are running this code in autoconf test mode to see which type of setuid
27    function works */
28 #if defined(HAVE_UNISTD_H)
29 #include <unistd.h>
30 #endif
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <sys/types.h>
34 #include <errno.h>
35
36 #ifdef HAVE_SYS_PRIV_H
37 #include <sys/priv.h>
38 #endif
39 #ifdef HAVE_SYS_ID_H
40 #include <sys/id.h>
41 #endif
42
43 #define DEBUG(x, y) printf y
44 #define smb_panic(x) exit(1)
45 #define bool int
46 #endif
47
48 /* are we running as non-root? This is used by the regresison test code,
49    and potentially also for sites that want non-root smbd */
50 static uid_t initial_uid;
51 static gid_t initial_gid;
52
53 /****************************************************************************
54 remember what uid we got started as - this allows us to run correctly
55 as non-root while catching trapdoor systems
56 ****************************************************************************/
57
58 void sec_init(void)
59 {
60         static int initialized;
61
62         if (!initialized) {
63                 initial_uid = geteuid();
64                 initial_gid = getegid();
65                 initialized = 1;
66         }
67 }
68
69 /****************************************************************************
70 some code (eg. winbindd) needs to know what uid we started as
71 ****************************************************************************/
72 uid_t sec_initial_uid(void)
73 {
74         return initial_uid;
75 }
76
77 /****************************************************************************
78 some code (eg. winbindd, profiling shm) needs to know what gid we started as
79 ****************************************************************************/
80 gid_t sec_initial_gid(void)
81 {
82         return initial_gid;
83 }
84
85 /****************************************************************************
86 are we running in non-root mode?
87 ****************************************************************************/
88 bool non_root_mode(void)
89 {
90         return (initial_uid != (uid_t)0);
91 }
92
93 /****************************************************************************
94 abort if we haven't set the uid correctly
95 ****************************************************************************/
96 static void assert_uid(uid_t ruid, uid_t euid)
97 {
98         if ((euid != (uid_t)-1 && geteuid() != euid) ||
99             (ruid != (uid_t)-1 && getuid() != ruid)) {
100                 if (!non_root_mode()) {
101                         DEBUG(0,("Failed to set uid privileges to (%d,%d) now set to (%d,%d)\n",
102                                  (int)ruid, (int)euid,
103                                  (int)getuid(), (int)geteuid()));
104                         smb_panic("failed to set uid\n");
105                         exit(1);
106                 }
107         }
108 }
109
110 /****************************************************************************
111 abort if we haven't set the gid correctly
112 ****************************************************************************/
113 static void assert_gid(gid_t rgid, gid_t egid)
114 {
115         if ((egid != (gid_t)-1 && getegid() != egid) ||
116             (rgid != (gid_t)-1 && getgid() != rgid)) {
117                 if (!non_root_mode()) {
118                         DEBUG(0,("Failed to set gid privileges to (%d,%d) now set to (%d,%d) uid=(%d,%d)\n",
119                                  (int)rgid, (int)egid,
120                                  (int)getgid(), (int)getegid(),
121                                  (int)getuid(), (int)geteuid()));
122                         smb_panic("failed to set gid\n");
123                         exit(1);
124                 }
125         }
126 }
127
128 /****************************************************************************
129  Gain root privilege before doing something. 
130  We want to end up with ruid==euid==0
131 ****************************************************************************/
132 void gain_root_privilege(void)
133 {       
134 #if defined(USE_SETRESUID) || defined(USE_LINUX_THREAD_CREDENTIALS)
135         samba_setresuid(0,0,0);
136 #endif
137     
138 #if USE_SETEUID
139         samba_seteuid(0);
140 #endif
141
142 #if USE_SETREUID
143         samba_setreuid(0, 0);
144 #endif
145
146 #if USE_SETUIDX
147         samba_setuidx(ID_EFFECTIVE, 0);
148         samba_setuidx(ID_REAL, 0);
149 #endif
150
151         /* this is needed on some systems */
152         samba_setuid(0);
153
154         assert_uid(0, 0);
155 }
156
157
158 /****************************************************************************
159  Ensure our real and effective groups are zero.
160  we want to end up with rgid==egid==0
161 ****************************************************************************/
162 void gain_root_group_privilege(void)
163 {
164 #if defined(USE_SETRESUID) || defined(USE_LINUX_THREAD_CREDENTIALS)
165         samba_setresgid(0,0,0);
166 #endif
167
168 #if USE_SETREUID
169         samba_setregid(0,0);
170 #endif
171
172 #if USE_SETEUID
173         samba_setegid(0);
174 #endif
175
176 #if USE_SETUIDX
177         samba_setgidx(ID_EFFECTIVE, 0);
178         samba_setgidx(ID_REAL, 0);
179 #endif
180
181         samba_setgid(0);
182
183         assert_gid(0, 0);
184 }
185
186
187 /****************************************************************************
188  Set effective uid, and possibly the real uid too.
189  We want to end up with either:
190   
191    ruid==uid and euid==uid
192
193  or
194
195    ruid==0 and euid==uid
196
197  depending on what the local OS will allow us to regain root from.
198 ****************************************************************************/
199 void set_effective_uid(uid_t uid)
200 {
201 #if defined(USE_SETRESUID) || defined(USE_LINUX_THREAD_CREDENTIALS)
202         /* Set the effective as well as the real uid. */
203         if (samba_setresuid(uid,uid,-1) == -1) {
204                 if (errno == EAGAIN) {
205                         DEBUG(0, ("samba_setresuid failed with EAGAIN. uid(%d) "
206                                   "might be over its NPROC limit\n",
207                                   (int)uid));
208                 }
209         }
210 #endif
211
212 #if USE_SETREUID
213         samba_setreuid(-1,uid);
214 #endif
215
216 #if USE_SETEUID
217         samba_seteuid(uid);
218 #endif
219
220 #if USE_SETUIDX
221         samba_setuidx(ID_EFFECTIVE, uid);
222 #endif
223
224         assert_uid(-1, uid);
225 }
226
227 /****************************************************************************
228  Set *only* the effective gid.
229  we want to end up with rgid==0 and egid==gid
230 ****************************************************************************/
231 void set_effective_gid(gid_t gid)
232 {
233 #if defined(USE_SETRESUID) || defined(USE_LINUX_THREAD_CREDENTIALS)
234         samba_setresgid(-1,gid,-1);
235 #endif
236
237 #if USE_SETREUID
238         samba_setregid(-1,gid);
239 #endif
240
241 #if USE_SETEUID
242         samba_setegid(gid);
243 #endif
244
245 #if USE_SETUIDX
246         samba_setgidx(ID_EFFECTIVE, gid);
247 #endif
248
249         assert_gid(-1, gid);
250 }
251
252 static uid_t saved_euid, saved_ruid;
253 static gid_t saved_egid, saved_rgid;
254
255 /****************************************************************************
256  save the real and effective uid for later restoration. Used by the quotas
257  code
258 ****************************************************************************/
259 void save_re_uid(void)
260 {
261         saved_ruid = getuid();
262         saved_euid = geteuid();
263 }
264
265
266 /****************************************************************************
267  and restore them!
268 ****************************************************************************/
269
270 void restore_re_uid_fromroot(void)
271 {
272 #if defined(USE_SETRESUID) || defined(USE_LINUX_THREAD_CREDENTIALS)
273         samba_setresuid(saved_ruid, saved_euid, -1);
274 #elif USE_SETREUID
275         samba_setreuid(saved_ruid, -1);
276         samba_setreuid(-1,saved_euid);
277 #elif USE_SETUIDX
278         samba_setuidx(ID_REAL, saved_ruid);
279         samba_setuidx(ID_EFFECTIVE, saved_euid);
280 #else
281         set_effective_uid(saved_euid);
282         if (getuid() != saved_ruid)
283                 samba_setuid(saved_ruid);
284         set_effective_uid(saved_euid);
285 #endif
286
287         assert_uid(saved_ruid, saved_euid);
288 }
289
290 void restore_re_uid(void)
291 {
292         set_effective_uid(0);
293         restore_re_uid_fromroot();
294 }
295
296 /****************************************************************************
297  save the real and effective gid for later restoration. Used by the 
298  getgroups code
299 ****************************************************************************/
300 void save_re_gid(void)
301 {
302         saved_rgid = getgid();
303         saved_egid = getegid();
304 }
305
306 /****************************************************************************
307  and restore them!
308 ****************************************************************************/
309 void restore_re_gid(void)
310 {
311 #if defined(USE_SETRESUID) || defined(USE_LINUX_THREAD_CREDENTIALS)
312         samba_setresgid(saved_rgid, saved_egid, -1);
313 #elif USE_SETREUID
314         samba_setregid(saved_rgid, -1);
315         samba_setregid(-1,saved_egid);
316 #elif USE_SETUIDX
317         samba_setgidx(ID_REAL, saved_rgid);
318         samba_setgidx(ID_EFFECTIVE, saved_egid);
319 #else
320         set_effective_gid(saved_egid);
321         if (getgid() != saved_rgid)
322                 samba_setgid(saved_rgid);
323         set_effective_gid(saved_egid);
324 #endif
325
326         assert_gid(saved_rgid, saved_egid);
327 }
328
329
330 /****************************************************************************
331  set the real AND effective uid to the current effective uid in a way that
332  allows root to be regained.
333  This is only possible on some platforms.
334 ****************************************************************************/
335 int set_re_uid(void)
336 {
337         uid_t uid = geteuid();
338
339 #if defined(USE_SETRESUID) || defined(USE_LINUX_THREAD_CREDENTIALS)
340         samba_setresuid(uid, uid, -1);
341 #endif
342
343 #if USE_SETREUID
344         samba_setreuid(0, 0);
345         samba_setreuid(uid, -1);
346         samba_setreuid(-1, uid);
347 #endif
348
349 #if USE_SETEUID
350         /* can't be done */
351         return -1;
352 #endif
353
354 #if USE_SETUIDX
355         /* can't be done */
356         return -1;
357 #endif
358
359         assert_uid(uid, uid);
360         return 0;
361 }
362
363
364 /****************************************************************************
365  Become the specified uid and gid - permanently !
366  there should be no way back if possible
367 ****************************************************************************/
368 void become_user_permanently(uid_t uid, gid_t gid)
369 {
370         /*
371          * First - gain root privilege. We do this to ensure
372          * we can lose it again.
373          */
374
375         gain_root_privilege();
376         gain_root_group_privilege();
377
378 #if defined(USE_SETRESUID) || defined(USE_LINUX_THREAD_CREDENTIALS)
379         samba_setresgid(gid,gid,gid);
380         samba_setgid(gid);
381         samba_setresuid(uid,uid,uid);
382         samba_setuid(uid);
383 #endif
384
385 #if USE_SETREUID
386         samba_setregid(gid,gid);
387         samba_setgid(gid);
388         samba_setreuid(uid,uid);
389         samba_setuid(uid);
390 #endif
391
392 #if USE_SETEUID
393         samba_setegid(gid);
394         samba_setgid(gid);
395         samba_setuid(uid);
396         samba_seteuid(uid);
397         samba_setuid(uid);
398 #endif
399
400 #if USE_SETUIDX
401         samba_setgidx(ID_REAL, gid);
402         samba_setgidx(ID_EFFECTIVE, gid);
403         samba_setgid(gid);
404         samba_setuidx(ID_REAL, uid);
405         samba_setuidx(ID_EFFECTIVE, uid);
406         samba_setuid(uid);
407 #endif
408         
409         assert_uid(uid, uid);
410         assert_gid(gid, gid);
411 }
412
413 /**********************************************************
414  Function to set thread specific credentials. Leave
415  saved-set uid/gid alone.Must be thread-safe code.
416 **********************************************************/
417
418 int set_thread_credentials(uid_t uid,
419                         gid_t gid,
420                         size_t setlen,
421                         const gid_t *gidset)
422 {
423 #if defined(USE_LINUX_THREAD_CREDENTIALS)
424         /*
425          * With Linux thread-specific credentials
426          * we know we have setresuid/setresgid
427          * available.
428          */
429
430         /* Become root. */
431         /* Set ru=0, eu=0 */
432         if (samba_setresuid(0, 0, -1) != 0) {
433                 return -1;
434         }
435         /* Set our primary gid. */
436         /* Set rg=gid, eg=gid */
437         if (samba_setresgid(gid, gid, -1) != 0) {
438                 return -1;
439         }
440         /* Set extra groups list. */
441         if (samba_setgroups(setlen, gidset) != 0) {
442                 return -1;
443         }
444         /* Become the requested user. */
445         /* Set ru=uid, eu=uid */
446         if (samba_setresuid(uid, uid, -1) != 0) {
447                 return -1;
448         }
449         if (geteuid() != uid || getuid() != uid ||
450                         getegid() != gid || getgid() != gid) {
451                 smb_panic("set_thread_credentials failed\n");
452                 return -1;
453         }
454         return 0;
455 #else
456         errno = ENOSYS;
457         return -1;
458 #endif
459 }
460
461 #ifdef AUTOCONF_TEST
462
463 /****************************************************************************
464 this function just checks that we don't get ENOSYS back
465 ****************************************************************************/
466 static int have_syscall(void)
467 {
468         errno = 0;
469
470 #if defined(USE_SETRESUID) || defined(USE_LINUX_THREAD_CREDENTIALS)
471         samba_setresuid(-1,-1,-1);
472 #endif
473
474 #if USE_SETREUID
475         samba_setreuid(-1,-1);
476 #endif
477
478 #if USE_SETEUID
479         samba_seteuid(-1);
480 #endif
481
482 #if USE_SETUIDX
483         samba_setuidx(ID_EFFECTIVE, -1);
484 #endif
485
486         if (errno == ENOSYS) return -1;
487         
488         return 0;
489 }
490
491 main()
492 {
493         if (getuid() != 0) {
494 #if (defined(AIX) && defined(USE_SETREUID))
495                 /* setreuid is badly broken on AIX 4.1, we avoid it completely */
496                 fprintf(stderr,"avoiding possibly broken setreuid\n");
497                 exit(1);
498 #endif
499
500                 /* if not running as root then at least check to see if we get ENOSYS - this 
501                    handles Linux 2.0.x with glibc 2.1 */
502                 fprintf(stderr,"not running as root: checking for ENOSYS\n");
503                 exit(have_syscall());
504         }
505
506         gain_root_privilege();
507         gain_root_group_privilege();
508         set_effective_gid(1);
509         set_effective_uid(1);
510         save_re_uid();
511         restore_re_uid();
512         gain_root_privilege();
513         gain_root_group_privilege();
514         become_user_permanently(1, 1);
515         samba_setuid(0);
516         if (getuid() == 0) {
517                 fprintf(stderr,"uid not set permanently\n");
518                 exit(1);
519         }
520
521         printf("OK\n");
522
523         exit(0);
524 }
525 #endif
526
527 /****************************************************************************
528 Check if we are setuid root.  Used in libsmb and smbpasswd paranoia checks.
529 ****************************************************************************/
530 bool is_setuid_root(void) 
531 {
532         return (geteuid() == (uid_t)0) && (getuid() != (uid_t)0);
533 }