aa3f30212a843a4db272d28aed2a86a5689c204b
[sfrench/samba-autobuild/.git] / lib / uid_wrapper / uid_wrapper.c
1 /*
2  * Copyright (c) 2009      Andrew Tridgell
3  * Copyright (c) 2011-2013 Andreas Schneider <asn@samba.org>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "config.h"
20
21 #include <errno.h>
22 #include <stdarg.h>
23 #include <stdbool.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 #include <grp.h>
30 #ifdef HAVE_SYS_SYSCALL_H
31 #include <sys/syscall.h>
32 #endif
33 #ifdef HAVE_SYSCALL_H
34 #include <syscall.h>
35 #endif
36 #include <dlfcn.h>
37
38 #include <pthread.h>
39
40 #ifdef HAVE_GCC_THREAD_LOCAL_STORAGE
41 # define UWRAP_THREAD __thread
42 #else
43 # define UWRAP_THREAD
44 #endif
45
46 # define UWRAP_LOCK(m) do { \
47         pthread_mutex_lock(&( m ## _mutex)); \
48 } while(0)
49
50 # define UWRAP_UNLOCK(m) do { \
51         pthread_mutex_unlock(&( m ## _mutex)); \
52 } while(0)
53
54 #ifdef HAVE_CONSTRUCTOR_ATTRIBUTE
55 #define CONSTRUCTOR_ATTRIBUTE __attribute__ ((constructor))
56 #else
57 #define CONSTRUCTOR_ATTRIBUTE
58 #endif /* HAVE_CONSTRUCTOR_ATTRIBUTE */
59
60 #ifdef HAVE_DESTRUCTOR_ATTRIBUTE
61 #define DESTRUCTOR_ATTRIBUTE __attribute__ ((destructor))
62 #else
63 #define DESTRUCTOR_ATTRIBUTE
64 #endif /* HAVE_DESTRUCTOR_ATTRIBUTE */
65
66 /* GCC have printf type attribute check. */
67 #ifdef HAVE_FUNCTION_ATTRIBUTE_FORMAT
68 #define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
69 #else
70 #define PRINTF_ATTRIBUTE(a,b)
71 #endif /* HAVE_FUNCTION_ATTRIBUTE_FORMAT */
72
73 #define UWRAP_DLIST_ADD(list,item) do { \
74         if (!(list)) { \
75                 (item)->prev    = NULL; \
76                 (item)->next    = NULL; \
77                 (list)          = (item); \
78         } else { \
79                 (item)->prev    = NULL; \
80                 (item)->next    = (list); \
81                 (list)->prev    = (item); \
82                 (list)          = (item); \
83         } \
84 } while (0)
85
86 #define UWRAP_DLIST_REMOVE(list,item) do { \
87         if ((list) == (item)) { \
88                 (list)          = (item)->next; \
89                 if (list) { \
90                         (list)->prev    = NULL; \
91                 } \
92         } else { \
93                 if ((item)->prev) { \
94                         (item)->prev->next      = (item)->next; \
95                 } \
96                 if ((item)->next) { \
97                         (item)->next->prev      = (item)->prev; \
98                 } \
99         } \
100         (item)->prev    = NULL; \
101         (item)->next    = NULL; \
102 } while (0)
103
104 #ifndef SAFE_FREE
105 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0)
106 #endif
107
108 /*****************
109  * LOGGING
110  *****************/
111
112 enum uwrap_dbglvl_e {
113         UWRAP_LOG_ERROR = 0,
114         UWRAP_LOG_WARN,
115         UWRAP_LOG_DEBUG,
116         UWRAP_LOG_TRACE
117 };
118
119 #ifdef NDEBUG
120 # define UWRAP_LOG(...)
121 #else /* NDEBUG */
122 static void uwrap_log(enum uwrap_dbglvl_e dbglvl, const char *format, ...) PRINTF_ATTRIBUTE(2, 3);
123 # define UWRAP_LOG(dbglvl, ...) uwrap_log((dbglvl), __VA_ARGS__)
124
125 static void uwrap_log(enum uwrap_dbglvl_e dbglvl, const char *format, ...)
126 {
127         char buffer[1024];
128         va_list va;
129         const char *d;
130         unsigned int lvl = 0;
131
132         d = getenv("UID_WRAPPER_DEBUGLEVEL");
133         if (d != NULL) {
134                 lvl = atoi(d);
135         }
136
137         va_start(va, format);
138         vsnprintf(buffer, sizeof(buffer), format, va);
139         va_end(va);
140
141         if (lvl >= dbglvl) {
142                 switch (dbglvl) {
143                         case UWRAP_LOG_ERROR:
144                                 fprintf(stderr,
145                                         "UWRAP_ERROR(%d): %s\n",
146                                         (int)getpid(), buffer);
147                                 break;
148                         case UWRAP_LOG_WARN:
149                                 fprintf(stderr,
150                                         "UWRAP_WARN(%d): %s\n",
151                                         (int)getpid(), buffer);
152                                 break;
153                         case UWRAP_LOG_DEBUG:
154                                 fprintf(stderr,
155                                         "UWRAP_DEBUG(%d): %s\n",
156                                         (int)getpid(), buffer);
157                                 break;
158                         case UWRAP_LOG_TRACE:
159                                 fprintf(stderr,
160                                         "UWRAP_TRACE(%d): %s\n",
161                                         (int)getpid(), buffer);
162                                 break;
163                 }
164         }
165 }
166 #endif /* NDEBUG */
167
168 /*****************
169  * LIBC
170  *****************/
171
172 #define LIBC_NAME "libc.so"
173
174 struct uwrap_libc_fns {
175         int (*_libc_setuid)(uid_t uid);
176         uid_t (*_libc_getuid)(void);
177
178 #ifdef HAVE_SETEUID
179         int (*_libc_seteuid)(uid_t euid);
180 #endif
181 #ifdef HAVE_SETREUID
182         int (*_libc_setreuid)(uid_t ruid, uid_t euid);
183 #endif
184 #ifdef HAVE_SETRESUID
185         int (*_libc_setresuid)(uid_t ruid, uid_t euid, uid_t suid);
186 #endif
187         uid_t (*_libc_geteuid)(void);
188
189         int (*_libc_setgid)(gid_t gid);
190         gid_t (*_libc_getgid)(void);
191 #ifdef HAVE_SETEGID
192         int (*_libc_setegid)(uid_t egid);
193 #endif
194 #ifdef HAVE_SETREGID
195         int (*_libc_setregid)(uid_t rgid, uid_t egid);
196 #endif
197 #ifdef HAVE_SETRESGID
198         int (*_libc_setresgid)(uid_t rgid, uid_t egid, uid_t sgid);
199 #endif
200         gid_t (*_libc_getegid)(void);
201         int (*_libc_getgroups)(int size, gid_t list[]);
202         int (*_libc_setgroups)(size_t size, const gid_t *list);
203 #ifdef HAVE_SYSCALL
204         long int (*_libc_syscall)(long int sysno, ...);
205 #endif
206 };
207
208 /*
209  * We keep the virtualised euid/egid/groups information here
210  */
211 struct uwrap_thread {
212         pthread_t tid;
213         bool dead;
214
215         uid_t ruid;
216         uid_t euid;
217         uid_t suid;
218
219         gid_t rgid;
220         gid_t egid;
221         gid_t sgid;
222
223         gid_t *groups;
224         int ngroups;
225
226         struct uwrap_thread *next;
227         struct uwrap_thread *prev;
228 };
229
230 struct uwrap {
231         struct {
232                 void *handle;
233                 struct uwrap_libc_fns fns;
234         } libc;
235
236         bool initialised;
237         bool enabled;
238
239         uid_t myuid;
240         uid_t mygid;
241
242         struct uwrap_thread *ids;
243 };
244
245 static struct uwrap uwrap;
246
247 /* Shortcut to the list item */
248 static UWRAP_THREAD struct uwrap_thread *uwrap_tls_id;
249
250 /* The mutex or accessing the id */
251 static pthread_mutex_t uwrap_id_mutex = PTHREAD_MUTEX_INITIALIZER;
252
253 /* The mutex for accessing the global libc.fns */
254 static pthread_mutex_t libc_symbol_binding_mutex = PTHREAD_MUTEX_INITIALIZER;
255
256 /*********************************************************
257  * UWRAP PROTOTYPES
258  *********************************************************/
259
260 bool uid_wrapper_enabled(void);
261 void uwrap_constructor(void) CONSTRUCTOR_ATTRIBUTE;
262 void uwrap_destructor(void) DESTRUCTOR_ATTRIBUTE;
263
264 /*********************************************************
265  * UWRAP LIBC LOADER FUNCTIONS
266  *********************************************************/
267
268 enum uwrap_lib {
269     UWRAP_LIBC,
270     UWRAP_LIBNSL,
271     UWRAP_LIBSOCKET,
272 };
273
274 static void *uwrap_load_lib_handle(enum uwrap_lib lib)
275 {
276         int flags = RTLD_LAZY;
277         void *handle = NULL;
278         int i;
279
280 #ifdef RTLD_DEEPBIND
281         flags |= RTLD_DEEPBIND;
282 #endif
283
284         switch (lib) {
285         case UWRAP_LIBNSL:
286                 /* FALL TROUGH */
287         case UWRAP_LIBSOCKET:
288                 /* FALL TROUGH */
289         case UWRAP_LIBC:
290                 handle = uwrap.libc.handle;
291                 if (handle == NULL) {
292                         for (i = 10; i >= 0; i--) {
293                                 char soname[256] = {0};
294
295                                 snprintf(soname, sizeof(soname), "libc.so.%d", i);
296                                 handle = dlopen(soname, flags);
297                                 if (handle != NULL) {
298                                         break;
299                                 }
300                         }
301
302                         uwrap.libc.handle = handle;
303                 }
304                 break;
305         }
306
307         if (handle == NULL) {
308 #ifdef RTLD_NEXT
309                 handle = uwrap.libc.handle = RTLD_NEXT;
310 #else
311                 fprintf(stderr,
312                         "Failed to dlopen library: %s\n",
313                         dlerror());
314                 exit(-1);
315 #endif
316         }
317
318         return handle;
319 }
320
321 static void *_uwrap_load_lib_function(enum uwrap_lib lib, const char *fn_name)
322 {
323         void *handle;
324         void *func;
325
326         handle = uwrap_load_lib_handle(lib);
327
328         func = dlsym(handle, fn_name);
329         if (func == NULL) {
330                 fprintf(stderr,
331                         "Failed to find %s: %s\n",
332                         fn_name, dlerror());
333                 exit(-1);
334         }
335
336         return func;
337 }
338
339 #define uwrap_load_lib_function(lib, fn_name) \
340         UWRAP_LOCK(libc_symbol_binding); \
341         if (uwrap.libc.fns._libc_##fn_name == NULL) { \
342                 *(void **) (&uwrap.libc.fns._libc_##fn_name) = \
343                         _uwrap_load_lib_function(lib, #fn_name); \
344         } \
345         UWRAP_UNLOCK(libc_symbol_binding)
346
347 /*
348  * IMPORTANT
349  *
350  * Functions expeciall from libc need to be loaded individually, you can't load
351  * all at once or gdb will segfault at startup. The same applies to valgrind and
352  * has probably something todo with with the linker.
353  * So we need load each function at the point it is called the first time.
354  */
355 static int libc_setuid(uid_t uid)
356 {
357         uwrap_load_lib_function(UWRAP_LIBC, setuid);
358
359         return uwrap.libc.fns._libc_setuid(uid);
360 }
361
362 static uid_t libc_getuid(void)
363 {
364         uwrap_load_lib_function(UWRAP_LIBC, getuid);
365
366         return uwrap.libc.fns._libc_getuid();
367 }
368
369 #ifdef HAVE_SETEUID
370 static int libc_seteuid(uid_t euid)
371 {
372         uwrap_load_lib_function(UWRAP_LIBC, seteuid);
373
374         return uwrap.libc.fns._libc_seteuid(euid);
375 }
376 #endif
377
378 #ifdef HAVE_SETREUID
379 static int libc_setreuid(uid_t ruid, uid_t euid)
380 {
381         uwrap_load_lib_function(UWRAP_LIBC, setreuid);
382
383         return uwrap.libc.fns._libc_setreuid(ruid, euid);
384 }
385 #endif
386
387 #ifdef HAVE_SETRESUID
388 static int libc_setresuid(uid_t ruid, uid_t euid, uid_t suid)
389 {
390         uwrap_load_lib_function(UWRAP_LIBC, setresuid);
391
392         return uwrap.libc.fns._libc_setresuid(ruid, euid, suid);
393 }
394 #endif
395
396 static uid_t libc_geteuid(void)
397 {
398         uwrap_load_lib_function(UWRAP_LIBC, geteuid);
399
400         return uwrap.libc.fns._libc_geteuid();
401 }
402
403 static int libc_setgid(gid_t gid)
404 {
405         uwrap_load_lib_function(UWRAP_LIBC, setgid);
406
407         return uwrap.libc.fns._libc_setgid(gid);
408 }
409
410 static gid_t libc_getgid(void)
411 {
412         uwrap_load_lib_function(UWRAP_LIBC, getgid);
413
414         return uwrap.libc.fns._libc_getgid();
415 }
416
417 #ifdef HAVE_SETEGID
418 static int libc_setegid(gid_t egid)
419 {
420         uwrap_load_lib_function(UWRAP_LIBC, setegid);
421
422         return uwrap.libc.fns._libc_setegid(egid);
423 }
424 #endif
425
426 #ifdef HAVE_SETREGID
427 static int libc_setregid(gid_t rgid, gid_t egid)
428 {
429         uwrap_load_lib_function(UWRAP_LIBC, setregid);
430
431         return uwrap.libc.fns._libc_setregid(rgid, egid);
432 }
433 #endif
434
435 #ifdef HAVE_SETRESGID
436 static int libc_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
437 {
438         uwrap_load_lib_function(UWRAP_LIBC, setresgid);
439
440         return uwrap.libc.fns._libc_setresgid(rgid, egid, sgid);
441 }
442 #endif
443
444 static gid_t libc_getegid(void)
445 {
446         uwrap_load_lib_function(UWRAP_LIBC, getegid);
447
448         return uwrap.libc.fns._libc_getegid();
449 }
450
451 static int libc_getgroups(int size, gid_t list[])
452 {
453         uwrap_load_lib_function(UWRAP_LIBC, getgroups);
454
455         return uwrap.libc.fns._libc_getgroups(size, list);
456 }
457
458 static int libc_setgroups(size_t size, const gid_t *list)
459 {
460         uwrap_load_lib_function(UWRAP_LIBC, setgroups);
461
462         return uwrap.libc.fns._libc_setgroups(size, list);
463 }
464
465 #ifdef HAVE_SYSCALL
466 static long int libc_vsyscall(long int sysno, va_list va)
467 {
468         long int args[8];
469         long int rc;
470         int i;
471
472         uwrap_load_lib_function(UWRAP_LIBC, syscall);
473
474         for (i = 0; i < 8; i++) {
475                 args[i] = va_arg(va, long int);
476         }
477
478         rc = uwrap.libc.fns._libc_syscall(sysno,
479                                           args[0],
480                                           args[1],
481                                           args[2],
482                                           args[3],
483                                           args[4],
484                                           args[5],
485                                           args[6],
486                                           args[7]);
487
488         return rc;
489 }
490 #endif
491
492 /*********************************************************
493  * UWRAP ID HANDLING
494  *********************************************************/
495
496 static struct uwrap_thread *find_uwrap_id(pthread_t tid)
497 {
498         struct uwrap_thread *id;
499
500         for (id = uwrap.ids; id; id = id->next) {
501                 if (pthread_equal(id->tid, tid)) {
502                         return id;
503                 }
504         }
505
506         return NULL;
507 }
508
509 static int uwrap_new_id(pthread_t tid, bool do_alloc)
510 {
511         struct uwrap_thread *id = uwrap_tls_id;
512
513         if (do_alloc) {
514                 id = malloc(sizeof(struct uwrap_thread));
515                 if (id == NULL) {
516                         UWRAP_LOG(UWRAP_LOG_ERROR, "Unable to allocate memory");
517                         errno = ENOMEM;
518                         return -1;
519                 }
520
521                 id->groups = malloc(sizeof(gid_t) * 1);
522                 if (id->groups == NULL) {
523                         UWRAP_LOG(UWRAP_LOG_ERROR, "Unable to allocate memory");
524                         SAFE_FREE(id);
525                         errno = ENOMEM;
526                         return -1;
527                 }
528
529                 UWRAP_DLIST_ADD(uwrap.ids, id);
530                 uwrap_tls_id = id;
531         }
532
533         id->tid = tid;
534         id->dead = false;
535
536         id->ruid = id->euid = id->suid = uwrap.myuid;
537         id->rgid = id->egid = id->sgid = uwrap.mygid;
538
539         id->ngroups = 1;
540         id->groups[0] = uwrap.mygid;
541
542         return 0;
543 }
544
545 static void uwrap_thread_prepare(void)
546 {
547         UWRAP_LOCK(uwrap_id);
548         UWRAP_LOCK(libc_symbol_binding);
549         /*
550          * What happens if another atfork prepare functions calls a uwrap
551          * function? So disable it in case another atfork prepare function
552          * calls a (s)uid function.
553          */
554         uwrap.enabled = false;
555 }
556
557 static void uwrap_thread_parent(void)
558 {
559         uwrap.enabled = true;
560
561         UWRAP_UNLOCK(libc_symbol_binding);
562         UWRAP_UNLOCK(uwrap_id);
563 }
564
565 static void uwrap_thread_child(void)
566 {
567         uwrap.enabled = true;
568
569         UWRAP_UNLOCK(libc_symbol_binding);
570         UWRAP_UNLOCK(uwrap_id);
571 }
572
573 static void uwrap_init(void)
574 {
575         const char *env = getenv("UID_WRAPPER");
576         pthread_t tid = pthread_self();
577
578         UWRAP_LOCK(uwrap_id);
579         if (uwrap.initialised) {
580                 struct uwrap_thread *id = uwrap_tls_id;
581                 int rc;
582
583                 if (id != NULL) {
584                         UWRAP_UNLOCK(uwrap_id);
585                         return;
586                 }
587
588                 id = find_uwrap_id(tid);
589                 if (id == NULL) {
590                         rc = uwrap_new_id(tid, true);
591                         if (rc < 0) {
592                                 exit(-1);
593                         }
594                 } else {
595                         /* We reuse an old thread id */
596                         uwrap_tls_id = id;
597
598                         uwrap_new_id(tid, false);
599                 }
600                 UWRAP_UNLOCK(uwrap_id);
601
602                 return;
603         }
604
605         UWRAP_LOG(UWRAP_LOG_DEBUG, "Initialize uid_wrapper");
606
607         uwrap.initialised = true;
608         uwrap.enabled = false;
609
610         if (env != NULL && env[0] == '1') {
611                 const char *root = getenv("UID_WRAPPER_ROOT");
612                 int rc;
613
614                 /* put us in one group */
615                 if (root != NULL && root[0] == '1') {
616                         uwrap.myuid = 0;
617                         uwrap.mygid = 0;
618                 } else {
619                         uwrap.myuid = libc_geteuid();
620                         uwrap.mygid = libc_getegid();
621                 }
622
623                 rc = uwrap_new_id(tid, true);
624                 if (rc < 0) {
625                         exit(-1);
626                 }
627
628                 uwrap.enabled = true;
629
630                 UWRAP_LOG(UWRAP_LOG_DEBUG,
631                           "Enabled uid_wrapper as %s",
632                           uwrap.myuid == 0 ? "root" : "user");
633         }
634
635         UWRAP_UNLOCK(uwrap_id);
636
637         UWRAP_LOG(UWRAP_LOG_DEBUG, "Succeccfully initialized uid_wrapper");
638 }
639
640 bool uid_wrapper_enabled(void)
641 {
642         bool enabled = false;
643         #ifdef HAVE_GCC_ATOMIC_BUILTINS
644                 __atomic_load(&uwrap.enabled, &enabled, __ATOMIC_RELAXED);
645         #else
646                 UWRAP_LOCK(uwrap_id);
647                 enabled = uwrap.enabled;
648                 UWRAP_UNLOCK(uwrap_id);
649         #endif
650         return enabled;
651 }
652
653 static int uwrap_setresuid_thread(uid_t ruid, uid_t euid, uid_t suid)
654 {
655         struct uwrap_thread *id = uwrap_tls_id;
656
657         if (ruid == (uid_t)-1 && euid == (uid_t)-1 && suid == (uid_t)-1) {
658                 errno = EINVAL;
659                 return -1;
660         }
661
662         UWRAP_LOCK(uwrap_id);
663         if (ruid != (uid_t)-1) {
664                 id->ruid = ruid;
665         }
666
667         if (euid != (uid_t)-1) {
668                 id->euid = euid;
669         }
670
671         if (suid != (uid_t)-1) {
672                 id->suid = suid;
673         }
674         UWRAP_UNLOCK(uwrap_id);
675
676         return 0;
677 }
678
679 static int uwrap_setresuid(uid_t ruid, uid_t euid, uid_t suid)
680 {
681         struct uwrap_thread *id;
682
683         if (ruid == (uid_t)-1 && euid == (uid_t)-1 && suid == (uid_t)-1) {
684                 errno = EINVAL;
685                 return -1;
686         }
687
688         UWRAP_LOCK(uwrap_id);
689         for (id = uwrap.ids; id; id = id->next) {
690                 if (id->dead) {
691                         continue;
692                 }
693
694                 if (ruid != (uid_t)-1) {
695                         id->ruid = ruid;
696                 }
697
698                 if (euid != (uid_t)-1) {
699                         id->euid = euid;
700                 }
701
702                 if (suid != (uid_t)-1) {
703                         id->suid = suid;
704                 }
705         }
706         UWRAP_UNLOCK(uwrap_id);
707
708         return 0;
709 }
710
711 /*
712  * SETUID
713  */
714 int setuid(uid_t uid)
715 {
716         if (!uid_wrapper_enabled()) {
717                 return libc_setuid(uid);
718         }
719
720         uwrap_init();
721         return uwrap_setresuid(uid, -1, -1);
722 }
723
724 #ifdef HAVE_SETEUID
725 int seteuid(uid_t euid)
726 {
727         if (euid == (uid_t)-1) {
728                 errno = EINVAL;
729                 return -1;
730         }
731
732         if (!uid_wrapper_enabled()) {
733                 return libc_seteuid(euid);
734         }
735
736         uwrap_init();
737         return uwrap_setresuid(-1, euid, -1);
738 }
739 #endif
740
741 #ifdef HAVE_SETREUID
742 int setreuid(uid_t ruid, uid_t euid)
743 {
744         if (ruid == (uid_t)-1 && euid == (uid_t)-1) {
745                 errno = EINVAL;
746                 return -1;
747         }
748
749         if (!uid_wrapper_enabled()) {
750                 return libc_setreuid(ruid, euid);
751         }
752
753         uwrap_init();
754         return uwrap_setresuid(ruid, euid, -1);
755 }
756 #endif
757
758 #ifdef HAVE_SETRESUID
759 int setresuid(uid_t ruid, uid_t euid, uid_t suid)
760 {
761         if (!uid_wrapper_enabled()) {
762                 return libc_setresuid(ruid, euid, suid);
763         }
764
765         uwrap_init();
766         return uwrap_setresuid(ruid, euid, suid);
767 }
768 #endif
769
770 /*
771  * GETUID
772  */
773 static uid_t uwrap_getuid(void)
774 {
775         struct uwrap_thread *id = uwrap_tls_id;
776         uid_t uid;
777
778         UWRAP_LOCK(uwrap_id);
779         uid = id->ruid;
780         UWRAP_UNLOCK(uwrap_id);
781
782         return uid;
783 }
784
785 uid_t getuid(void)
786 {
787         if (!uid_wrapper_enabled()) {
788                 return libc_getuid();
789         }
790
791         uwrap_init();
792         return uwrap_getuid();
793 }
794
795 /*
796  * GETEUID
797  */
798 static uid_t uwrap_geteuid(void)
799 {
800         const char *env = getenv("UID_WRAPPER_MYUID");
801         struct uwrap_thread *id = uwrap_tls_id;
802         uid_t uid;
803
804         UWRAP_LOCK(uwrap_id);
805         uid = id->euid;
806         UWRAP_UNLOCK(uwrap_id);
807
808         /* Disable root and return myuid */
809         if (env != NULL && env[0] == '1') {
810                 uid = uwrap.myuid;
811         }
812
813         return uid;
814 }
815
816 uid_t geteuid(void)
817 {
818         if (!uid_wrapper_enabled()) {
819                 return libc_geteuid();
820         }
821
822         uwrap_init();
823         return uwrap_geteuid();
824 }
825
826 static int uwrap_setresgid_thread(gid_t rgid, gid_t egid, gid_t sgid)
827 {
828         struct uwrap_thread *id = uwrap_tls_id;
829
830         if (rgid == (gid_t)-1 && egid == (gid_t)-1 && sgid == (gid_t)-1) {
831                 errno = EINVAL;
832                 return -1;
833         }
834
835         UWRAP_LOCK(uwrap_id);
836         if (rgid != (gid_t)-1) {
837                 id->rgid = rgid;
838         }
839
840         if (egid != (gid_t)-1) {
841                 id->egid = egid;
842         }
843
844         if (sgid != (gid_t)-1) {
845                 id->sgid = sgid;
846         }
847         UWRAP_UNLOCK(uwrap_id);
848
849         return 0;
850 }
851
852 static int uwrap_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
853 {
854         struct uwrap_thread *id;
855
856         if (rgid == (gid_t)-1 && egid == (gid_t)-1 && sgid == (gid_t)-1) {
857                 errno = EINVAL;
858                 return -1;
859         }
860
861         UWRAP_LOCK(uwrap_id);
862         for (id = uwrap.ids; id; id = id->next) {
863                 if (id->dead) {
864                         continue;
865                 }
866
867                 if (rgid != (gid_t)-1) {
868                         id->rgid = rgid;
869                 }
870
871                 if (egid != (gid_t)-1) {
872                         id->egid = egid;
873                 }
874
875                 if (sgid != (gid_t)-1) {
876                         id->sgid = sgid;
877                 }
878         }
879         UWRAP_UNLOCK(uwrap_id);
880
881         return 0;
882 }
883
884 /*
885  * SETGID
886  */
887 int setgid(gid_t gid)
888 {
889         if (!uid_wrapper_enabled()) {
890                 return libc_setgid(gid);
891         }
892
893         uwrap_init();
894         return uwrap_setresgid(gid, -1, -1);
895 }
896
897 #ifdef HAVE_SETEGID
898 int setegid(gid_t egid)
899 {
900         if (!uid_wrapper_enabled()) {
901                 return libc_setegid(egid);
902         }
903
904         uwrap_init();
905         return uwrap_setresgid(-1, egid, -1);
906 }
907 #endif
908
909 #ifdef HAVE_SETREGID
910 int setregid(gid_t rgid, gid_t egid)
911 {
912         if (!uid_wrapper_enabled()) {
913                 return libc_setregid(rgid, egid);
914         }
915
916         uwrap_init();
917         return uwrap_setresgid(rgid, egid, -1);
918 }
919 #endif
920
921 #ifdef HAVE_SETRESGID
922 int setresgid(gid_t rgid, gid_t egid, gid_t sgid)
923 {
924         if (!uid_wrapper_enabled()) {
925                 return libc_setresgid(rgid, egid, sgid);
926         }
927
928         uwrap_init();
929         return uwrap_setresgid(rgid, egid, sgid);
930 }
931 #endif
932
933 /*
934  * GETGID
935  */
936 static gid_t uwrap_getgid(void)
937 {
938         struct uwrap_thread *id = uwrap_tls_id;
939         gid_t gid;
940
941         UWRAP_LOCK(uwrap_id);
942         gid = id->rgid;
943         UWRAP_UNLOCK(uwrap_id);
944
945         return gid;
946 }
947
948 gid_t getgid(void)
949 {
950         if (!uid_wrapper_enabled()) {
951                 return libc_getgid();
952         }
953
954         uwrap_init();
955         return uwrap_getgid();
956 }
957
958 /*
959  * GETEGID
960  */
961 static uid_t uwrap_getegid(void)
962 {
963         struct uwrap_thread *id = uwrap_tls_id;
964         gid_t gid;
965
966         UWRAP_LOCK(uwrap_id);
967         gid = id->egid;
968         UWRAP_UNLOCK(uwrap_id);
969
970         return gid;
971 }
972
973 uid_t getegid(void)
974 {
975         if (!uid_wrapper_enabled()) {
976                 return libc_getegid();
977         }
978
979         uwrap_init();
980         return uwrap_getegid();
981 }
982
983 static int uwrap_setgroups_thread(size_t size, const gid_t *list)
984 {
985         struct uwrap_thread *id = uwrap_tls_id;
986         int rc = -1;
987
988         UWRAP_LOCK(uwrap_id);
989
990         if (size == 0) {
991                 free(id->groups);
992                 id->groups = NULL;
993                 id->ngroups = 0;
994         } else if (size > 0) {
995                 gid_t *tmp;
996
997                 tmp = realloc(id->groups, sizeof(gid_t) * size);
998                 if (tmp == NULL) {
999                         errno = ENOMEM;
1000                         goto out;
1001                 }
1002                 id->groups = tmp;
1003
1004                 id->ngroups = size;
1005                 memcpy(id->groups, list, size * sizeof(gid_t));
1006         }
1007
1008         rc = 0;
1009 out:
1010         UWRAP_UNLOCK(uwrap_id);
1011
1012         return rc;
1013 }
1014
1015 static int uwrap_setgroups(size_t size, const gid_t *list)
1016 {
1017         struct uwrap_thread *id;
1018         int rc = -1;
1019
1020         UWRAP_LOCK(uwrap_id);
1021
1022         if (size == 0) {
1023                 for (id = uwrap.ids; id; id = id->next) {
1024                         free(id->groups);
1025                         id->groups = NULL;
1026                         id->ngroups = 0;
1027                 }
1028         } else if (size > 0) {
1029                 for (id = uwrap.ids; id; id = id->next) {
1030                         gid_t *tmp;
1031
1032                         tmp = realloc(id->groups, sizeof(gid_t) * size);
1033                         if (tmp == NULL) {
1034                                 errno = ENOMEM;
1035                                 goto out;
1036                         }
1037                         id->groups = tmp;
1038
1039                         id->ngroups = size;
1040                         memcpy(id->groups, list, size * sizeof(gid_t));
1041                 }
1042         }
1043
1044         rc = 0;
1045 out:
1046         UWRAP_UNLOCK(uwrap_id);
1047
1048         return rc;
1049 }
1050
1051 #ifdef HAVE_SETGROUPS_INT
1052 int setgroups(int size, const gid_t *list)
1053 #else
1054 int setgroups(size_t size, const gid_t *list)
1055 #endif
1056 {
1057         if (!uid_wrapper_enabled()) {
1058                 return libc_setgroups(size, list);
1059         }
1060
1061         uwrap_init();
1062         return uwrap_setgroups(size, list);
1063 }
1064
1065 static int uwrap_getgroups(int size, gid_t *list)
1066 {
1067         struct uwrap_thread *id = uwrap_tls_id;
1068         int ngroups;
1069
1070         UWRAP_LOCK(uwrap_id);
1071         ngroups = id->ngroups;
1072
1073         if (size > ngroups) {
1074                 size = ngroups;
1075         }
1076         if (size == 0) {
1077                 goto out;
1078         }
1079         if (size < ngroups) {
1080                 errno = EINVAL;
1081                 ngroups = -1;
1082         }
1083         memcpy(list, id->groups, size * sizeof(gid_t));
1084
1085 out:
1086         UWRAP_UNLOCK(uwrap_id);
1087
1088         return ngroups;
1089 }
1090
1091 int getgroups(int size, gid_t *list)
1092 {
1093         if (!uid_wrapper_enabled()) {
1094                 return libc_getgroups(size, list);
1095         }
1096
1097         uwrap_init();
1098         return uwrap_getgroups(size, list);
1099 }
1100
1101 #if (defined(HAVE_SYS_SYSCALL_H) || defined(HAVE_SYSCALL_H)) \
1102     && (defined(SYS_setreuid) || defined(SYS_setreuid32))
1103 static long int uwrap_syscall (long int sysno, va_list vp)
1104 {
1105         long int rc;
1106
1107         switch (sysno) {
1108                 /* gid */
1109                 case SYS_getgid:
1110 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1111                 case SYS_getgid32:
1112 #endif
1113                         {
1114                                 rc = uwrap_getgid();
1115                         }
1116                         break;
1117 #ifdef SYS_getegid
1118                 case SYS_getegid:
1119 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1120                 case SYS_getegid32:
1121 #endif
1122                         {
1123                                 rc = uwrap_getegid();
1124                         }
1125                         break;
1126 #endif /* SYS_getegid */
1127                 case SYS_setgid:
1128 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1129                 case SYS_setgid32:
1130 #endif
1131                         {
1132                                 gid_t gid = (gid_t) va_arg(vp, int);
1133
1134                                 rc = uwrap_setresgid_thread(gid, -1, -1);
1135                         }
1136                         break;
1137                 case SYS_setregid:
1138 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1139                 case SYS_setregid32:
1140 #endif
1141                         {
1142                                 uid_t rgid = (uid_t) va_arg(vp, int);
1143                                 uid_t egid = (uid_t) va_arg(vp, int);
1144
1145                                 rc = uwrap_setresgid_thread(rgid, egid, -1);
1146                         }
1147                         break;
1148 #ifdef SYS_setresgid
1149                 case SYS_setresgid:
1150 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1151                 case SYS_setresgid32:
1152 #endif
1153                         {
1154                                 uid_t rgid = (uid_t) va_arg(vp, int);
1155                                 uid_t egid = (uid_t) va_arg(vp, int);
1156                                 uid_t sgid = (uid_t) va_arg(vp, int);
1157
1158                                 rc = uwrap_setresgid_thread(rgid, egid, sgid);
1159                         }
1160                         break;
1161 #endif /* SYS_setresgid */
1162
1163                 /* uid */
1164                 case SYS_getuid:
1165 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1166                 case SYS_getuid32:
1167 #endif
1168                         {
1169                                 rc = uwrap_getuid();
1170                         }
1171                         break;
1172 #ifdef SYS_geteuid
1173                 case SYS_geteuid:
1174 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1175                 case SYS_geteuid32:
1176 #endif
1177                         {
1178                                 rc = uwrap_geteuid();
1179                         }
1180                         break;
1181 #endif /* SYS_geteuid */
1182                 case SYS_setuid:
1183 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1184                 case SYS_setuid32:
1185 #endif
1186                         {
1187                                 uid_t uid = (uid_t) va_arg(vp, int);
1188
1189                                 rc = uwrap_setresuid_thread(uid, -1, -1);
1190                         }
1191                         break;
1192                 case SYS_setreuid:
1193 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1194                 case SYS_setreuid32:
1195 #endif
1196                         {
1197                                 uid_t ruid = (uid_t) va_arg(vp, int);
1198                                 uid_t euid = (uid_t) va_arg(vp, int);
1199
1200                                 rc = uwrap_setresuid_thread(ruid, euid, -1);
1201                         }
1202                         break;
1203 #ifdef SYS_setresuid
1204                 case SYS_setresuid:
1205 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1206                 case SYS_setresuid32:
1207 #endif
1208                         {
1209                                 uid_t ruid = (uid_t) va_arg(vp, int);
1210                                 uid_t euid = (uid_t) va_arg(vp, int);
1211                                 uid_t suid = (uid_t) va_arg(vp, int);
1212
1213                                 rc = uwrap_setresuid_thread(ruid, euid, suid);
1214                         }
1215                         break;
1216 #endif /* SYS_setresuid */
1217
1218                 /* groups */
1219                 case SYS_setgroups:
1220 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1221                 case SYS_setgroups32:
1222 #endif
1223                         {
1224                                 size_t size = (size_t) va_arg(vp, size_t);
1225                                 gid_t *list = (gid_t *) va_arg(vp, int *);
1226
1227                                 rc = uwrap_setgroups_thread(size, list);
1228                         }
1229                         break;
1230                 default:
1231                         UWRAP_LOG(UWRAP_LOG_DEBUG,
1232                                   "UID_WRAPPER calling non-wrapped syscall %lu\n",
1233                                   sysno);
1234
1235                         rc = libc_vsyscall(sysno, vp);
1236                         break;
1237         }
1238
1239         return rc;
1240 }
1241
1242 #ifdef HAVE_SYSCALL
1243 #ifdef HAVE_SYSCALL_INT
1244 int syscall (int sysno, ...)
1245 #else
1246 long int syscall (long int sysno, ...)
1247 #endif
1248 {
1249 #ifdef HAVE_SYSCALL_INT
1250         int rc;
1251 #else
1252         long int rc;
1253 #endif
1254         va_list va;
1255
1256         va_start(va, sysno);
1257
1258         if (!uid_wrapper_enabled()) {
1259                 rc = libc_vsyscall(sysno, va);
1260                 va_end(va);
1261                 return rc;
1262         }
1263
1264         uwrap_init();
1265         rc = uwrap_syscall(sysno, va);
1266         va_end(va);
1267
1268         return rc;
1269 }
1270 #endif /* HAVE_SYSCALL */
1271 #endif /* HAVE_SYS_SYSCALL_H || HAVE_SYSCALL_H */
1272
1273 /****************************
1274  * CONSTRUCTOR
1275  ***************************/
1276 void uwrap_constructor(void)
1277 {
1278         /*
1279         * If we hold a lock and the application forks, then the child
1280         * is not able to unlock the mutex and we are in a deadlock.
1281         * This should prevent such deadlocks.
1282         */
1283         pthread_atfork(&uwrap_thread_prepare,
1284                        &uwrap_thread_parent,
1285                        &uwrap_thread_child);
1286
1287         /* Here is safe place to call uwrap_init() and initialize data
1288          * for main process.
1289          */
1290         uwrap_init();
1291 }
1292
1293 /****************************
1294  * DESTRUCTOR
1295  ***************************/
1296
1297 /*
1298  * This function is called when the library is unloaded and makes sure that
1299  * resources are freed.
1300  */
1301 void uwrap_destructor(void)
1302 {
1303         struct uwrap_thread *u = uwrap.ids;
1304
1305         UWRAP_LOCK(uwrap_id);
1306         UWRAP_LOCK(libc_symbol_binding);
1307
1308         while (u != NULL) {
1309                 UWRAP_DLIST_REMOVE(uwrap.ids, u);
1310
1311                 SAFE_FREE(u->groups);
1312                 SAFE_FREE(u);
1313
1314                 u = uwrap.ids;
1315         }
1316
1317         if (uwrap.libc.handle != NULL) {
1318                 dlclose(uwrap.libc.handle);
1319         }
1320
1321         UWRAP_UNLOCK(libc_symbol_binding);
1322         UWRAP_UNLOCK(uwrap_id);
1323 }