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