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