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