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