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