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