uwrap: Add libc_getgroups().
[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_SETRESUID
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_SETRESGID
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 #ifdef HAVE_SETEUID
266 static int libc_seteuid(uid_t euid)
267 {
268         uwrap_load_lib_function(UWRAP_LIBC, seteuid);
269
270         return uwrap.libc.fns._libc_seteuid(euid);
271 }
272 #endif
273
274 #ifdef HAVE_SETREUID
275 static int libc_setreuid(uid_t ruid, uid_t euid)
276 {
277         uwrap_load_lib_function(UWRAP_LIBC, setreuid);
278
279         return uwrap.libc.fns._libc_setreuid(ruid, euid);
280 }
281 #endif
282
283 #ifdef HAVE_SETRESUID
284 int libc_setresuid(uid_t ruid, uid_t euid, uid_t suid);
285 {
286         uwrap_load_lib_function(UWRAP_LIBC, setresuid);
287
288         return uwrap.libc.fns._libc_setresuid(ruid, euid, suid);
289 }
290 #endif
291
292 static uid_t libc_geteuid(void)
293 {
294         uwrap_load_lib_function(UWRAP_LIBC, geteuid);
295
296         return uwrap.libc.fns._libc_geteuid();
297 }
298
299 static int libc_setgid(gid_t gid)
300 {
301         uwrap_load_lib_function(UWRAP_LIBC, setgid);
302
303         return uwrap.libc.fns._libc_setgid(gid);
304 }
305
306 static gid_t libc_getgid(void)
307 {
308         uwrap_load_lib_function(UWRAP_LIBC, getgid);
309
310         return uwrap.libc.fns._libc_getgid();
311 }
312
313 #ifdef HAVE_SETEGID
314 static int libc_setegid(gid_t egid)
315 {
316         uwrap_load_lib_function(UWRAP_LIBC, setegid);
317
318         return uwrap.libc.fns._libc_setegid(egid);
319 }
320 #endif
321
322 #ifdef HAVE_SETREGID
323 static int libc_setregid(gid_t rgid, gid_t egid)
324 {
325         uwrap_load_lib_function(UWRAP_LIBC, setregid);
326
327         return uwrap.libc.fns._libc_setregid(rgid, egid);
328 }
329 #endif
330
331 #ifdef HAVE_SETRESGID
332 static int libc_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
333 {
334         uwrap_load_lib_function(UWRAP_LIBC, setresgid);
335
336         return uwrap.libc.fns._libc_setresgid(rgid, egid, sgid);
337 }
338 #endif
339
340 static gid_t libc_getegid(void)
341 {
342         uwrap_load_lib_function(UWRAP_LIBC, getegid);
343
344         return uwrap.libc.fns._libc_getegid();
345 }
346
347 static int libc_getgroups(int size, gid_t list[])
348 {
349         uwrap_load_lib_function(UWRAP_LIBC, getgroups);
350
351         return uwrap.libc.fns._libc_getgroups(size, list);
352 }
353
354 static int libc_setgroups(size_t size, const gid_t *list)
355 {
356         uwrap_load_lib_function(UWRAP_LIBC, setgroups);
357
358         return uwrap.libc.fns._libc_setgroups(size, list);
359 }
360
361 static void *uwrap_libc_fn(struct uwrap *u, const char *fn_name)
362 {
363         void *func;
364
365 #ifdef HAVE_APPLE
366         func = dlsym(RTLD_NEXT, fn_name);
367 #else
368         if (u->libc.handle == NULL) {
369                 return NULL;
370         }
371
372         func = dlsym(u->libc.handle, fn_name);
373 #endif
374         if (func == NULL) {
375                 printf("Failed to find %s in %s: %s\n",
376                                 fn_name, LIBC_NAME, dlerror());
377                 exit(-1);
378         }
379
380         return func;
381 }
382
383 static void uwrap_libc_init(struct uwrap *u)
384 {
385         unsigned int i = 0;
386 #ifndef HAVE_APPLE
387         int flags = RTLD_LAZY;
388
389 #ifdef RTLD_DEEPBIND
390         flags |= RTLD_DEEPBIND;
391 #endif
392
393         for (u->libc.handle = NULL, i = 10; u->libc.handle == NULL; i--) {
394                 char soname[256] = {0};
395
396                 snprintf(soname, sizeof(soname), "%s.%u", LIBC_NAME, i);
397                 u->libc.handle = dlopen(soname, flags);
398         }
399
400         if (u->libc.handle == NULL) {
401                 printf("Failed to dlopen %s.%u: %s\n", LIBC_NAME, i, dlerror());
402                 exit(-1);
403         }
404 #endif
405
406 #ifdef HAVE_SYSCALL
407         *(void **) (&u->libc.fns._libc_syscall) = uwrap_libc_fn(u, "syscall");
408 #endif
409 }
410
411 static struct uwrap_thread *find_uwrap_id(pthread_t tid)
412 {
413         struct uwrap_thread *id;
414
415         for (id = uwrap.ids; id; id = id->next) {
416                 if (pthread_equal(id->tid, tid)) {
417                         return id;
418                 }
419         }
420
421         return NULL;
422 }
423
424 static int uwrap_new_id(pthread_t tid, bool do_alloc)
425 {
426         struct uwrap_thread *id = uwrap_tls_id;
427
428         if (do_alloc) {
429                 id = malloc(sizeof(struct uwrap_thread));
430                 if (id == NULL) {
431                         errno = ENOMEM;
432                         return -1;
433                 }
434         }
435
436         id->tid = tid;
437         id->dead = false;
438
439         id->ruid = id->euid = id->suid = uwrap.myuid;
440         id->rgid = id->egid = id->sgid = uwrap.mygid;
441
442         id->ngroups = 1;
443         id->groups = malloc(sizeof(gid_t) * id->ngroups);
444         id->groups[0] = uwrap.mygid;
445
446         if (do_alloc) {
447                 UWRAP_DLIST_ADD(uwrap.ids, id);
448                 uwrap_tls_id = id;
449         }
450
451         return 0;
452 }
453
454 static void uwrap_thread_prepare(void)
455 {
456         pthread_mutex_lock(&uwrap_id_mutex);
457
458         /*
459          * What happens if another atfork prepare functions calls a uwrap
460          * function? So disable it in case another atfork prepare function
461          * calls a (s)uid function.
462          */
463         uwrap.enabled = false;
464 }
465
466 static void uwrap_thread_parent(void)
467 {
468         uwrap.enabled = true;
469
470         pthread_mutex_unlock(&uwrap_id_mutex);
471 }
472
473 static void uwrap_thread_child(void)
474 {
475         uwrap.enabled = true;
476
477         pthread_mutex_unlock(&uwrap_id_mutex);
478 }
479
480 static void uwrap_init(void)
481 {
482         const char *env = getenv("UID_WRAPPER");
483         pthread_t tid = pthread_self();
484
485
486
487         if (uwrap.initialised) {
488                 struct uwrap_thread *id = uwrap_tls_id;
489                 int rc;
490
491                 if (id != NULL) {
492                         return;
493                 }
494
495                 pthread_mutex_lock(&uwrap_id_mutex);
496                 id = find_uwrap_id(tid);
497                 if (id == NULL) {
498                         rc = uwrap_new_id(tid, 1);
499                         if (rc < 0) {
500                                 exit(-1);
501                         }
502                 } else {
503                         /* We reuse an old thread id */
504                         uwrap_tls_id = id;
505
506                         uwrap_new_id(tid, 0);
507                 }
508                 pthread_mutex_unlock(&uwrap_id_mutex);
509
510                 return;
511         }
512
513         /*
514          * If we hold a lock and the application forks, then the child
515          * is not able to unlock the mutex and we are in a deadlock.
516          * This should prevent such deadlocks.
517          */
518         pthread_atfork(&uwrap_thread_prepare,
519                        &uwrap_thread_parent,
520                        &uwrap_thread_child);
521
522         pthread_mutex_lock(&uwrap_id_mutex);
523
524         uwrap_libc_init(&uwrap);
525
526         uwrap.initialised = true;
527         uwrap.enabled = false;
528
529         if (env != NULL && env[0] == '1') {
530                 const char *root = getenv("UID_WRAPPER_ROOT");
531                 int rc;
532
533                 /* put us in one group */
534                 if (root != NULL && root[0] == '1') {
535                         uwrap.myuid = 0;
536                         uwrap.mygid = 0;
537                 } else {
538                         uwrap.myuid = libc_geteuid();
539                         uwrap.mygid = libc_getegid();
540                 }
541
542                 rc = uwrap_new_id(tid, 1);
543                 if (rc < 0) {
544                         exit(-1);
545                 }
546
547                 uwrap.enabled = true;
548         }
549
550         pthread_mutex_unlock(&uwrap_id_mutex);
551 }
552
553 static int uwrap_enabled(void)
554 {
555         uwrap_init();
556
557         return uwrap.enabled ? 1 : 0;
558 }
559
560 static int uwrap_setresuid_thread(uid_t ruid, uid_t euid, uid_t suid)
561 {
562         struct uwrap_thread *id = uwrap_tls_id;
563
564         if (ruid == (uid_t)-1 && euid == (uid_t)-1 && suid == (uid_t)-1) {
565                 errno = EINVAL;
566                 return -1;
567         }
568
569         pthread_mutex_lock(&uwrap_id_mutex);
570         if (ruid != (uid_t)-1) {
571                 id->ruid = ruid;
572         }
573
574         if (euid != (uid_t)-1) {
575                 id->euid = euid;
576         }
577
578         if (suid != (uid_t)-1) {
579                 id->suid = suid;
580         }
581         pthread_mutex_unlock(&uwrap_id_mutex);
582
583         return 0;
584 }
585
586 static int uwrap_setresuid(uid_t ruid, uid_t euid, uid_t suid)
587 {
588         struct uwrap_thread *id;
589
590         if (ruid == (uid_t)-1 && euid == (uid_t)-1 && suid == (uid_t)-1) {
591                 errno = EINVAL;
592                 return -1;
593         }
594
595         pthread_mutex_lock(&uwrap_id_mutex);
596         for (id = uwrap.ids; id; id = id->next) {
597                 if (id->dead) {
598                         continue;
599                 }
600
601                 if (ruid != (uid_t)-1) {
602                         id->ruid = ruid;
603                 }
604
605                 if (euid != (uid_t)-1) {
606                         id->euid = euid;
607                 }
608
609                 if (suid != (uid_t)-1) {
610                         id->suid = suid;
611                 }
612         }
613         pthread_mutex_unlock(&uwrap_id_mutex);
614
615         return 0;
616 }
617
618 /*
619  * SETUID
620  */
621 int setuid(uid_t uid)
622 {
623         if (!uwrap_enabled()) {
624                 return libc_setuid(uid);
625         }
626
627         return uwrap_setresuid(uid, -1, -1);
628 }
629
630 #ifdef HAVE_SETEUID
631 int seteuid(uid_t euid)
632 {
633         if (euid == (uid_t)-1) {
634                 errno = EINVAL;
635                 return -1;
636         }
637
638         if (!uwrap_enabled()) {
639                 return libc_seteuid(euid);
640         }
641
642         return uwrap_setresuid(-1, euid, -1);
643 }
644 #endif
645
646 #ifdef HAVE_SETREUID
647 int setreuid(uid_t ruid, uid_t euid)
648 {
649         if (ruid == (uid_t)-1 && euid == (uid_t)-1) {
650                 errno = EINVAL;
651                 return -1;
652         }
653
654         if (!uwrap_enabled()) {
655                 return libc_setreuid(ruid, euid);
656         }
657
658         return uwrap_setresuid(ruid, euid, -1);
659 }
660 #endif
661
662 #ifdef HAVE_SETRESUID
663 int setresuid(uid_t ruid, uid_t euid, uid_t suid)
664 {
665         if (!uwrap_enabled()) {
666                 return libc_setresuid(ruid, euid, suid);
667         }
668
669         return uwrap_setresuid(ruid, euid, suid);
670 }
671 #endif
672
673 /*
674  * GETUID
675  */
676 static uid_t uwrap_getuid(void)
677 {
678         struct uwrap_thread *id = uwrap_tls_id;
679         uid_t uid;
680
681         pthread_mutex_lock(&uwrap_id_mutex);
682         uid = id->ruid;
683         pthread_mutex_unlock(&uwrap_id_mutex);
684
685         return uid;
686 }
687
688 uid_t getuid(void)
689 {
690         if (!uwrap_enabled()) {
691                 return libc_getuid();
692         }
693
694         return uwrap_getuid();
695 }
696
697 /*
698  * GETEUID
699  */
700 static uid_t uwrap_geteuid(void)
701 {
702         const char *env = getenv("UID_WRAPPER_ROOT");
703         struct uwrap_thread *id = uwrap_tls_id;
704         uid_t uid;
705
706         pthread_mutex_lock(&uwrap_id_mutex);
707         uid = id->euid;
708         pthread_mutex_unlock(&uwrap_id_mutex);
709
710         /* Disable root and return myuid */
711         if (env != NULL && env[0] == '2') {
712                 uid = uwrap.myuid;
713         }
714
715         return uid;
716 }
717
718 uid_t geteuid(void)
719 {
720         if (!uwrap_enabled()) {
721                 return libc_geteuid();
722         }
723
724         return uwrap_geteuid();
725 }
726
727 static int uwrap_setresgid_thread(gid_t rgid, gid_t egid, gid_t sgid)
728 {
729         struct uwrap_thread *id = uwrap_tls_id;
730
731         if (rgid == (gid_t)-1 && egid == (gid_t)-1 && sgid == (gid_t)-1) {
732                 errno = EINVAL;
733                 return -1;
734         }
735
736         pthread_mutex_lock(&uwrap_id_mutex);
737         if (rgid != (gid_t)-1) {
738                 id->rgid = rgid;
739         }
740
741         if (egid != (gid_t)-1) {
742                 id->egid = egid;
743         }
744
745         if (sgid != (gid_t)-1) {
746                 id->sgid = sgid;
747         }
748         pthread_mutex_unlock(&uwrap_id_mutex);
749
750         return 0;
751 }
752
753 static int uwrap_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
754 {
755         struct uwrap_thread *id;
756
757         if (rgid == (gid_t)-1 && egid == (gid_t)-1 && sgid == (gid_t)-1) {
758                 errno = EINVAL;
759                 return -1;
760         }
761
762         pthread_mutex_lock(&uwrap_id_mutex);
763         for (id = uwrap.ids; id; id = id->next) {
764                 if (id->dead) {
765                         continue;
766                 }
767
768                 if (rgid != (gid_t)-1) {
769                         id->rgid = rgid;
770                 }
771
772                 if (egid != (gid_t)-1) {
773                         id->egid = egid;
774                 }
775
776                 if (sgid != (gid_t)-1) {
777                         id->sgid = sgid;
778                 }
779         }
780         pthread_mutex_unlock(&uwrap_id_mutex);
781
782         return 0;
783 }
784
785 /*
786  * SETGID
787  */
788 int setgid(gid_t gid)
789 {
790         if (!uwrap_enabled()) {
791                 return libc_setgid(gid);
792         }
793
794         return uwrap_setresgid(gid, -1, -1);
795 }
796
797 #ifdef HAVE_SETEGID
798 int setegid(gid_t egid)
799 {
800         if (!uwrap_enabled()) {
801                 return libc_setegid(egid);
802         }
803
804         return uwrap_setresgid(-1, egid, -1);
805 }
806 #endif
807
808 #ifdef HAVE_SETREGID
809 int setregid(gid_t rgid, gid_t egid)
810 {
811         if (!uwrap_enabled()) {
812                 return libc_setregid(rgid, egid);
813         }
814
815         return uwrap_setresgid(rgid, egid, -1);
816 }
817 #endif
818
819 #ifdef HAVE_SETRESGID
820 int setresgid(gid_t rgid, gid_t egid, gid_t sgid)
821 {
822         if (!uwrap_enabled()) {
823                 return libc_setresgid(rgid, egid, sgid);
824         }
825
826         return uwrap_setresgid(rgid, egid, sgid);
827 }
828 #endif
829
830 /*
831  * GETGID
832  */
833 static gid_t uwrap_getgid(void)
834 {
835         struct uwrap_thread *id = uwrap_tls_id;
836         gid_t gid;
837
838         pthread_mutex_lock(&uwrap_id_mutex);
839         gid = id->rgid;
840         pthread_mutex_unlock(&uwrap_id_mutex);
841
842         return gid;
843 }
844
845 gid_t getgid(void)
846 {
847         if (!uwrap_enabled()) {
848                 return libc_getgid();
849         }
850
851         return uwrap_getgid();
852 }
853
854 /*
855  * GETEGID
856  */
857 static uid_t uwrap_getegid(void)
858 {
859         struct uwrap_thread *id = uwrap_tls_id;
860         gid_t gid;
861
862         pthread_mutex_lock(&uwrap_id_mutex);
863         gid = id->egid;
864         pthread_mutex_unlock(&uwrap_id_mutex);
865
866         return gid;
867 }
868
869 uid_t getegid(void)
870 {
871         if (!uwrap_enabled()) {
872                 return libc_getegid();
873         }
874
875         return uwrap_getegid();
876 }
877
878 static int uwrap_setgroups_thread(size_t size, const gid_t *list)
879 {
880         struct uwrap_thread *id = uwrap_tls_id;
881         int rc = -1;
882
883         pthread_mutex_lock(&uwrap_id_mutex);
884         free(id->groups);
885         id->groups = NULL;
886         id->ngroups = 0;
887
888         if (size != 0) {
889                 id->groups = malloc(sizeof(gid_t) * size);
890                 if (id->groups == NULL) {
891                         errno = ENOMEM;
892                         goto out;
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         for (id = uwrap.ids; id; id = id->next) {
912                 free(id->groups);
913                 id->groups = NULL;
914                 id->ngroups = 0;
915
916                 if (size != 0) {
917                         id->groups = malloc(sizeof(gid_t) * size);
918                         if (id->groups == NULL) {
919                                 errno = ENOMEM;
920                                 goto out;
921                         }
922                         id->ngroups = size;
923                         memcpy(id->groups, list, size * sizeof(gid_t));
924                 }
925         }
926
927         rc = 0;
928 out:
929         pthread_mutex_unlock(&uwrap_id_mutex);
930
931         return rc;
932 }
933
934 #ifdef HAVE_SETGROUPS_INT
935 int setgroups(int size, const gid_t *list)
936 #else
937 int setgroups(size_t size, const gid_t *list)
938 #endif
939 {
940         if (!uwrap_enabled()) {
941                 return libc_setgroups(size, list);
942         }
943
944         return uwrap_setgroups(size, list);
945 }
946
947 static int uwrap_getgroups(int size, gid_t *list)
948 {
949         struct uwrap_thread *id = uwrap_tls_id;
950         int ngroups;
951
952         pthread_mutex_lock(&uwrap_id_mutex);
953         ngroups = id->ngroups;
954
955         if (size > ngroups) {
956                 size = ngroups;
957         }
958         if (size == 0) {
959                 goto out;
960         }
961         if (size < ngroups) {
962                 errno = EINVAL;
963                 ngroups = -1;
964         }
965         memcpy(list, id->groups, size * sizeof(gid_t));
966
967 out:
968         pthread_mutex_unlock(&uwrap_id_mutex);
969
970         return ngroups;
971 }
972
973 int getgroups(int size, gid_t *list)
974 {
975         if (!uwrap_enabled()) {
976                 return libc_getgroups(size, list);
977         }
978
979         return uwrap_getgroups(size, list);
980 }
981
982 static long int libc_vsyscall(long int sysno, va_list va)
983 {
984         long int args[8];
985         long int rc;
986         int i;
987
988         for (i = 0; i < 8; i++) {
989                 args[i] = va_arg(va, long int);
990         }
991
992         rc = uwrap.libc.fns._libc_syscall(sysno,
993                                           args[0],
994                                           args[1],
995                                           args[2],
996                                           args[3],
997                                           args[4],
998                                           args[5],
999                                           args[6],
1000                                           args[7]);
1001
1002         return rc;
1003 }
1004
1005 #if (defined(HAVE_SYS_SYSCALL_H) || defined(HAVE_SYSCALL_H)) \
1006     && (defined(SYS_setreuid) || defined(SYS_setreuid32))
1007 static long int uwrap_syscall (long int sysno, va_list vp)
1008 {
1009         long int rc;
1010
1011         switch (sysno) {
1012                 /* gid */
1013                 case SYS_getgid:
1014 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1015                 case SYS_getgid32:
1016 #endif
1017                         {
1018                                 rc = uwrap_getgid();
1019                         }
1020                         break;
1021 #ifdef SYS_getegid
1022                 case SYS_getegid:
1023 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1024                 case SYS_getegid32:
1025 #endif
1026                         {
1027                                 rc = uwrap_getegid();
1028                         }
1029                         break;
1030 #endif /* SYS_getegid */
1031                 case SYS_setgid:
1032 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1033                 case SYS_setgid32:
1034 #endif
1035                         {
1036                                 gid_t gid = (gid_t) va_arg(vp, int);
1037
1038                                 rc = uwrap_setresgid_thread(gid, -1, -1);
1039                         }
1040                         break;
1041                 case SYS_setregid:
1042 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1043                 case SYS_setregid32:
1044 #endif
1045                         {
1046                                 uid_t rgid = (uid_t) va_arg(vp, int);
1047                                 uid_t egid = (uid_t) va_arg(vp, int);
1048
1049                                 rc = uwrap_setresgid_thread(rgid, egid, -1);
1050                         }
1051                         break;
1052 #ifdef SYS_setresgid
1053                 case SYS_setresgid:
1054 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1055                 case SYS_setresgid32:
1056 #endif
1057                         {
1058                                 uid_t rgid = (uid_t) va_arg(vp, int);
1059                                 uid_t egid = (uid_t) va_arg(vp, int);
1060                                 uid_t sgid = (uid_t) va_arg(vp, int);
1061
1062                                 rc = uwrap_setresgid_thread(rgid, egid, sgid);
1063                         }
1064                         break;
1065 #endif /* SYS_setresgid */
1066
1067                 /* uid */
1068                 case SYS_getuid:
1069 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1070                 case SYS_getuid32:
1071 #endif
1072                         {
1073                                 rc = uwrap_getuid();
1074                         }
1075                         break;
1076 #ifdef SYS_geteuid
1077                 case SYS_geteuid:
1078 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1079                 case SYS_geteuid32:
1080 #endif
1081                         {
1082                                 rc = uwrap_geteuid();
1083                         }
1084                         break;
1085 #endif /* SYS_geteuid */
1086                 case SYS_setuid:
1087 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1088                 case SYS_setuid32:
1089 #endif
1090                         {
1091                                 uid_t uid = (uid_t) va_arg(vp, int);
1092
1093                                 rc = uwrap_setresuid_thread(uid, -1, -1);
1094                         }
1095                         break;
1096                 case SYS_setreuid:
1097 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1098                 case SYS_setreuid32:
1099 #endif
1100                         {
1101                                 uid_t ruid = (uid_t) va_arg(vp, int);
1102                                 uid_t euid = (uid_t) va_arg(vp, int);
1103
1104                                 rc = uwrap_setresuid_thread(ruid, euid, -1);
1105                         }
1106                         break;
1107 #ifdef SYS_setresuid
1108                 case SYS_setresuid:
1109 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1110                 case SYS_setresuid32:
1111 #endif
1112                         {
1113                                 uid_t ruid = (uid_t) va_arg(vp, int);
1114                                 uid_t euid = (uid_t) va_arg(vp, int);
1115                                 uid_t suid = (uid_t) va_arg(vp, int);
1116
1117                                 rc = uwrap_setresuid_thread(ruid, euid, suid);
1118                         }
1119                         break;
1120 #endif /* SYS_setresuid */
1121
1122                 /* groups */
1123                 case SYS_setgroups:
1124 #ifdef HAVE_LINUX_32BIT_SYSCALLS
1125                 case SYS_setgroups32:
1126 #endif
1127                         {
1128                                 size_t size = (size_t) va_arg(vp, size_t);
1129                                 gid_t *list = (gid_t *) va_arg(vp, int *);
1130
1131                                 rc = uwrap_setgroups_thread(size, list);
1132                         }
1133                         break;
1134                 default:
1135                         UWRAP_DEBUG("UID_WRAPPER calling non-wrapped syscall "
1136                                     "%lu\n", sysno);
1137
1138                         rc = libc_vsyscall(sysno, vp);
1139                         break;
1140         }
1141
1142         return rc;
1143 }
1144
1145 #ifdef HAVE_SYSCALL
1146 #ifdef HAVE_SYSCALL_INT
1147 int syscall (int sysno, ...)
1148 #else
1149 long int syscall (long int sysno, ...)
1150 #endif
1151 {
1152 #ifdef HAVE_SYSCALL_INT
1153         int rc;
1154 #else
1155         long int rc;
1156 #endif
1157         va_list va;
1158
1159         va_start(va, sysno);
1160
1161         if (!uwrap_enabled()) {
1162                 rc = libc_vsyscall(sysno, va);
1163                 va_end(va);
1164                 return rc;
1165         }
1166
1167         rc = uwrap_syscall(sysno, va);
1168         va_end(va);
1169
1170         return rc;
1171 }
1172 #endif /* HAVE_SYSCALL */
1173 #endif /* HAVE_SYS_SYSCALL_H || HAVE_SYSCALL_H */