2 * Copyright (c) 2009 Andrew Tridgell
3 * Copyright (c) 2011-2013 Andreas Schneider <asn@samba.org>
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.
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.
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/>.
27 #include <sys/types.h>
30 #ifdef HAVE_SYS_SYSCALL_H
31 #include <sys/syscall.h>
40 #ifdef HAVE_GCC_THREAD_LOCAL_STORAGE
41 # define UWRAP_THREAD __thread
47 #define UWRAP_DEBUG(...)
49 #define UWRAP_DEBUG(...) fprintf(stderr, __VA_ARGS__)
52 #define UWRAP_DLIST_ADD(list,item) do { \
54 (item)->prev = NULL; \
55 (item)->next = NULL; \
58 (item)->prev = NULL; \
59 (item)->next = (list); \
60 (list)->prev = (item); \
65 #define UWRAP_DLIST_REMOVE(list,item) do { \
66 if ((list) == (item)) { \
67 (list) = (item)->next; \
69 (list)->prev = NULL; \
73 (item)->prev->next = (item)->next; \
76 (item)->next->prev = (item)->prev; \
79 (item)->prev = NULL; \
80 (item)->next = NULL; \
83 #define LIBC_NAME "libc.so"
85 struct uwrap_libc_fns {
86 int (*_libc_setuid)(uid_t uid);
87 uid_t (*_libc_getuid)(void);
90 int (*_libc_seteuid)(uid_t euid);
93 int (*_libc_setreuid)(uid_t ruid, uid_t euid);
96 int (*_libc_setresuid)(uid_t ruid, uid_t euid, uid_t suid);
98 uid_t (*_libc_geteuid)(void);
100 int (*_libc_setgid)(gid_t gid);
101 gid_t (*_libc_getgid)(void);
103 int (*_libc_setegid)(uid_t egid);
106 int (*_libc_setregid)(uid_t rgid, uid_t egid);
109 int (*_libc_setresgid)(uid_t rgid, uid_t egid, uid_t sgid);
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);
115 long int (*_libc_syscall)(long int sysno, ...);
120 * We keep the virtualised euid/egid/groups information here
122 struct uwrap_thread {
137 struct uwrap_thread *next;
138 struct uwrap_thread *prev;
144 struct uwrap_libc_fns fns;
153 struct uwrap_thread *ids;
156 static struct uwrap uwrap;
158 /* Shortcut to the list item */
159 static UWRAP_THREAD struct uwrap_thread *uwrap_tls_id;
161 /* The mutex or accessing the id */
162 static pthread_mutex_t uwrap_id_mutex = PTHREAD_MUTEX_INITIALIZER;
164 static void *uwrap_libc_fn(struct uwrap *u, const char *fn_name)
168 if (u->libc.handle == NULL) {
172 func = dlsym(u->libc.handle, fn_name);
174 printf("Failed to find %s in %s: %s\n",
175 fn_name, LIBC_NAME, dlerror());
182 static void uwrap_libc_init(struct uwrap *u)
185 int flags = RTLD_LAZY;
188 flags |= RTLD_DEEPBIND;
191 for (u->libc.handle = NULL, i = 10; u->libc.handle == NULL; i--) {
192 char soname[256] = {0};
194 snprintf(soname, sizeof(soname), "%s.%u", LIBC_NAME, i);
195 u->libc.handle = dlopen(soname, flags);
198 if (u->libc.handle == NULL) {
199 printf("Failed to dlopen %s.%u: %s\n", LIBC_NAME, i, dlerror());
203 *(void **) (&u->libc.fns._libc_setuid) = uwrap_libc_fn(u, "setuid");
204 *(void **) (&u->libc.fns._libc_getuid) = uwrap_libc_fn(u, "getuid");
207 *(void **) (&u->libc.fns._libc_seteuid) = uwrap_libc_fn(u, "seteuid");
210 *(void **) (&u->libc.fns._libc_setreuid) = uwrap_libc_fn(u, "setreuid");
212 #ifdef HAVE_SETRESUID
213 *(void **) (&u->libc.fns._libc_setresuid) = uwrap_libc_fn(u, "setresuid");
215 *(void **) (&u->libc.fns._libc_geteuid) = uwrap_libc_fn(u, "geteuid");
217 *(void **) (&u->libc.fns._libc_setgid) = uwrap_libc_fn(u, "setgid");
218 *(void **) (&u->libc.fns._libc_getgid) = uwrap_libc_fn(u, "getgid");
220 *(void **) (&u->libc.fns._libc_setegid) = uwrap_libc_fn(u, "setegid");
223 *(void **) (&u->libc.fns._libc_setregid) = uwrap_libc_fn(u, "setregid");
225 #ifdef HAVE_SETRESGID
226 *(void **) (&u->libc.fns._libc_setresgid) = uwrap_libc_fn(u, "setresgid");
228 *(void **) (&u->libc.fns._libc_getegid) = uwrap_libc_fn(u, "getegid");
229 *(void **) (&u->libc.fns._libc_getgroups) = uwrap_libc_fn(u, "getgroups");
230 *(void **) (&u->libc.fns._libc_setgroups) = uwrap_libc_fn(u, "setgroups");
231 *(void **) (&u->libc.fns._libc_getuid) = uwrap_libc_fn(u, "getuid");
232 *(void **) (&u->libc.fns._libc_getgid) = uwrap_libc_fn(u, "getgid");
234 *(void **) (&u->libc.fns._libc_syscall) = uwrap_libc_fn(u, "syscall");
238 static struct uwrap_thread *find_uwrap_id(pthread_t tid)
240 struct uwrap_thread *id;
242 for (id = uwrap.ids; id; id = id->next) {
243 if (pthread_equal(id->tid, tid)) {
251 static int uwrap_new_id(pthread_t tid, bool do_alloc)
253 struct uwrap_thread *id = uwrap_tls_id;
256 id = malloc(sizeof(struct uwrap_thread));
266 id->ruid = id->euid = id->suid = uwrap.myuid;
267 id->rgid = id->egid = id->sgid = uwrap.mygid;
270 id->groups = malloc(sizeof(gid_t) * id->ngroups);
271 id->groups[0] = uwrap.mygid;
274 UWRAP_DLIST_ADD(uwrap.ids, id);
281 static void uwrap_thread_prepare(void)
283 pthread_mutex_lock(&uwrap_id_mutex);
286 * What happens if another atfork prepare functions calls a uwrap
287 * function? So disable it in case another atfork prepare function
288 * calls a (s)uid function.
290 uwrap.enabled = false;
293 static void uwrap_thread_parent(void)
295 uwrap.enabled = true;
297 pthread_mutex_unlock(&uwrap_id_mutex);
300 static void uwrap_thread_child(void)
302 uwrap.enabled = true;
304 pthread_mutex_unlock(&uwrap_id_mutex);
307 static void uwrap_init(void)
309 const char *env = getenv("UID_WRAPPER");
310 pthread_t tid = pthread_self();
314 if (uwrap.initialised) {
315 struct uwrap_thread *id = uwrap_tls_id;
319 * If we hold a lock and the application forks, then the child
320 * is not able to unlock the mutex and we are in a deadlock.
321 * This should prevent such deadlocks.
323 pthread_atfork(&uwrap_thread_prepare,
324 &uwrap_thread_parent,
325 &uwrap_thread_child);
331 pthread_mutex_lock(&uwrap_id_mutex);
332 id = find_uwrap_id(tid);
334 rc = uwrap_new_id(tid, 1);
339 /* We reuse an old thread id */
342 uwrap_new_id(tid, 0);
344 pthread_mutex_unlock(&uwrap_id_mutex);
349 pthread_mutex_lock(&uwrap_id_mutex);
351 uwrap_libc_init(&uwrap);
353 uwrap.initialised = true;
354 uwrap.enabled = false;
356 if (env != NULL && env[0] == '1') {
357 const char *root = getenv("UID_WRAPPER_ROOT");
360 /* put us in one group */
361 if (root != NULL && root[0] == '1') {
365 uwrap.myuid = uwrap.libc.fns._libc_geteuid();
366 uwrap.mygid = uwrap.libc.fns._libc_getegid();
369 rc = uwrap_new_id(tid, 1);
374 uwrap.enabled = true;
377 pthread_mutex_unlock(&uwrap_id_mutex);
380 static int uwrap_enabled(void)
384 return uwrap.enabled ? 1 : 0;
387 static int uwrap_setresuid_thread(uid_t ruid, uid_t euid, uid_t suid)
389 struct uwrap_thread *id = uwrap_tls_id;
391 if (ruid == (uid_t)-1 && euid == (uid_t)-1 && suid == (uid_t)-1) {
396 pthread_mutex_lock(&uwrap_id_mutex);
397 if (ruid != (uid_t)-1) {
401 if (euid != (uid_t)-1) {
405 if (suid != (uid_t)-1) {
408 pthread_mutex_unlock(&uwrap_id_mutex);
413 static int uwrap_setresuid(uid_t ruid, uid_t euid, uid_t suid)
415 struct uwrap_thread *id;
417 if (ruid == (uid_t)-1 && euid == (uid_t)-1 && suid == (uid_t)-1) {
422 pthread_mutex_lock(&uwrap_id_mutex);
423 for (id = uwrap.ids; id; id = id->next) {
428 if (ruid != (uid_t)-1) {
432 if (euid != (uid_t)-1) {
436 if (suid != (uid_t)-1) {
440 pthread_mutex_unlock(&uwrap_id_mutex);
448 int setuid(uid_t uid)
450 if (!uwrap_enabled()) {
451 return uwrap.libc.fns._libc_setuid(uid);
454 return uwrap_setresuid(uid, -1, -1);
458 int seteuid(uid_t euid)
460 if (euid == (uid_t)-1) {
465 if (!uwrap_enabled()) {
466 return uwrap.libc.fns._libc_seteuid(euid);
469 return uwrap_setresuid(-1, euid, -1);
474 int setreuid(uid_t ruid, uid_t euid)
476 if (ruid == (uid_t)-1 && euid == (uid_t)-1) {
481 if (!uwrap_enabled()) {
482 return uwrap.libc.fns._libc_setreuid(ruid, euid);
485 return uwrap_setresuid(ruid, euid, -1);
489 #ifdef HAVE_SETRESUID
490 int setresuid(uid_t ruid, uid_t euid, uid_t suid)
492 if (!uwrap_enabled()) {
493 return uwrap.libc.fns._libc_setresuid(ruid, euid, suid);
496 return uwrap_setresuid(ruid, euid, suid);
503 static uid_t uwrap_getuid(void)
505 struct uwrap_thread *id = uwrap_tls_id;
508 pthread_mutex_lock(&uwrap_id_mutex);
510 pthread_mutex_unlock(&uwrap_id_mutex);
517 if (!uwrap_enabled()) {
518 return uwrap.libc.fns._libc_getuid();
521 return uwrap_getuid();
527 static uid_t uwrap_geteuid(void)
529 const char *env = getenv("UID_WRAPPER_ROOT");
530 struct uwrap_thread *id = uwrap_tls_id;
533 pthread_mutex_lock(&uwrap_id_mutex);
535 pthread_mutex_unlock(&uwrap_id_mutex);
537 /* Disable root and return myuid */
538 if (env != NULL && env[0] == '2') {
547 if (!uwrap_enabled()) {
548 return uwrap.libc.fns._libc_geteuid();
551 return uwrap_geteuid();
554 static int uwrap_setresgid_thread(gid_t rgid, gid_t egid, gid_t sgid)
556 struct uwrap_thread *id = uwrap_tls_id;
558 if (rgid == (gid_t)-1 && egid == (gid_t)-1 && sgid == (gid_t)-1) {
563 pthread_mutex_lock(&uwrap_id_mutex);
564 if (rgid != (gid_t)-1) {
568 if (egid != (gid_t)-1) {
572 if (sgid != (gid_t)-1) {
575 pthread_mutex_unlock(&uwrap_id_mutex);
580 static int uwrap_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
582 struct uwrap_thread *id;
584 if (rgid == (gid_t)-1 && egid == (gid_t)-1 && sgid == (gid_t)-1) {
589 pthread_mutex_lock(&uwrap_id_mutex);
590 for (id = uwrap.ids; id; id = id->next) {
595 if (rgid != (gid_t)-1) {
599 if (egid != (gid_t)-1) {
603 if (sgid != (gid_t)-1) {
607 pthread_mutex_unlock(&uwrap_id_mutex);
615 int setgid(gid_t gid)
617 if (!uwrap_enabled()) {
618 return uwrap.libc.fns._libc_setgid(gid);
621 return uwrap_setresgid(gid, -1, -1);
625 int setegid(gid_t egid)
627 if (!uwrap_enabled()) {
628 return uwrap.libc.fns._libc_setegid(egid);
631 return uwrap_setresgid(-1, egid, -1);
636 int setregid(gid_t rgid, gid_t egid)
638 if (!uwrap_enabled()) {
639 return uwrap.libc.fns._libc_setregid(rgid, egid);
642 return uwrap_setresgid(rgid, egid, -1);
646 #ifdef HAVE_SETRESGID
647 int setresgid(gid_t rgid, gid_t egid, gid_t sgid)
649 if (!uwrap_enabled()) {
650 return uwrap.libc.fns._libc_setregid(rgid, egid, sgid);
653 return uwrap_setresgid(rgid, egid, sgid);
660 static gid_t uwrap_getgid(void)
662 struct uwrap_thread *id = uwrap_tls_id;
665 pthread_mutex_lock(&uwrap_id_mutex);
667 pthread_mutex_unlock(&uwrap_id_mutex);
674 if (!uwrap_enabled()) {
675 return uwrap.libc.fns._libc_getgid();
678 return uwrap_getgid();
684 static uid_t uwrap_getegid(void)
686 struct uwrap_thread *id = uwrap_tls_id;
689 pthread_mutex_lock(&uwrap_id_mutex);
691 pthread_mutex_unlock(&uwrap_id_mutex);
698 if (!uwrap_enabled()) {
699 return uwrap.libc.fns._libc_getegid();
702 return uwrap_getegid();
705 static int uwrap_setgroups_thread(size_t size, const gid_t *list)
707 struct uwrap_thread *id = uwrap_tls_id;
710 pthread_mutex_lock(&uwrap_id_mutex);
716 id->groups = malloc(sizeof(gid_t) * size);
717 if (id->groups == NULL) {
722 memcpy(id->groups, list, size * sizeof(gid_t));
727 pthread_mutex_unlock(&uwrap_id_mutex);
732 static int uwrap_setgroups(size_t size, const gid_t *list)
734 struct uwrap_thread *id;
737 pthread_mutex_lock(&uwrap_id_mutex);
738 for (id = uwrap.ids; id; id = id->next) {
744 id->groups = malloc(sizeof(gid_t) * size);
745 if (id->groups == NULL) {
750 memcpy(id->groups, list, size * sizeof(gid_t));
756 pthread_mutex_unlock(&uwrap_id_mutex);
761 int setgroups(size_t size, const gid_t *list)
763 if (!uwrap_enabled()) {
764 return uwrap.libc.fns._libc_setgroups(size, list);
767 return uwrap_setgroups(size, list);
770 static int uwrap_getgroups(int size, gid_t *list)
772 struct uwrap_thread *id = uwrap_tls_id;
775 pthread_mutex_lock(&uwrap_id_mutex);
776 ngroups = id->ngroups;
778 if (size > ngroups) {
784 if (size < ngroups) {
788 memcpy(list, id->groups, size * sizeof(gid_t));
791 pthread_mutex_unlock(&uwrap_id_mutex);
796 int getgroups(int size, gid_t *list)
798 if (!uwrap_enabled()) {
799 return uwrap.libc.fns._libc_getgroups(size, list);
802 return uwrap_getgroups(size, list);
805 static long int libc_vsyscall(long int sysno, va_list va)
811 for (i = 0; i < 8; i++) {
812 args[i] = va_arg(va, long int);
815 rc = uwrap.libc.fns._libc_syscall(sysno,
828 #if (defined(HAVE_SYS_SYSCALL_H) || defined(HAVE_SYSCALL_H)) \
829 && (defined(SYS_setreuid) || defined(SYS_setreuid32))
830 static long int uwrap_syscall (long int sysno, va_list vp)
837 #ifdef HAVE_LINUX_32BIT_SYSCALLS
845 #ifdef HAVE_LINUX_32BIT_SYSCALLS
849 rc = uwrap_getegid();
853 #ifdef HAVE_LINUX_32BIT_SYSCALLS
857 gid_t gid = (gid_t) va_arg(vp, int);
859 rc = uwrap_setresgid_thread(gid, -1, -1);
863 #ifdef HAVE_LINUX_32BIT_SYSCALLS
867 uid_t rgid = (uid_t) va_arg(vp, int);
868 uid_t egid = (uid_t) va_arg(vp, int);
870 rc = uwrap_setresgid_thread(rgid, egid, -1);
874 #ifdef HAVE_LINUX_32BIT_SYSCALLS
875 case SYS_setresgid32:
878 uid_t rgid = (uid_t) va_arg(vp, int);
879 uid_t egid = (uid_t) va_arg(vp, int);
880 uid_t sgid = (uid_t) va_arg(vp, int);
882 rc = uwrap_setresgid_thread(rgid, egid, sgid);
888 #ifdef HAVE_LINUX_32BIT_SYSCALLS
896 #ifdef HAVE_LINUX_32BIT_SYSCALLS
900 rc = uwrap_geteuid();
904 #ifdef HAVE_LINUX_32BIT_SYSCALLS
908 uid_t uid = (uid_t) va_arg(vp, int);
910 rc = uwrap_setresuid_thread(uid, -1, -1);
914 #ifdef HAVE_LINUX_32BIT_SYSCALLS
918 uid_t ruid = (uid_t) va_arg(vp, int);
919 uid_t euid = (uid_t) va_arg(vp, int);
921 rc = uwrap_setresuid_thread(ruid, euid, -1);
925 #ifdef HAVE_LINUX_32BIT_SYSCALLS
926 case SYS_setresuid32:
929 uid_t ruid = (uid_t) va_arg(vp, int);
930 uid_t euid = (uid_t) va_arg(vp, int);
931 uid_t suid = (uid_t) va_arg(vp, int);
933 rc = uwrap_setresuid_thread(ruid, euid, suid);
939 #ifdef HAVE_LINUX_32BIT_SYSCALLS
940 case SYS_setgroups32:
943 size_t size = (size_t) va_arg(vp, size_t);
944 gid_t *list = (gid_t *) va_arg(vp, int *);
946 rc = uwrap_setgroups_thread(size, list);
950 UWRAP_DEBUG("UID_WRAPPER calling non-wrapped syscall "
953 rc = libc_vsyscall(sysno, vp);
961 long int syscall (long int sysno, ...)
968 if (!uwrap_enabled()) {
969 rc = libc_vsyscall(sysno, va);
974 rc = uwrap_syscall(sysno, va);
979 #endif /* HAVE_SYSCALL */
980 #endif /* HAVE_SYS_SYSCALL_H || HAVE_SYSCALL_H */