uwrap: First do garbage collection before exporting ids
[uid_wrapper.git] / src / uid_wrapper.c
index a891f38d01875bb2d8f83d653282f2603c9b0a24..a48cc3bda275597cec8126c14446ef5e747e0f0d 100644 (file)
 # define UWRAP_THREAD
 #endif
 
-#ifdef NDEBUG
-#define UWRAP_DEBUG(...)
+# define UWRAP_LOCK(m) do { \
+       pthread_mutex_lock(&( m ## _mutex)); \
+} while(0)
+
+# define UWRAP_UNLOCK(m) do { \
+       pthread_mutex_unlock(&( m ## _mutex)); \
+} while(0)
+
+/* Add new global locks here please */
+# define UWRAP_LOCK_ALL \
+       UWRAP_LOCK(uwrap_id); \
+       UWRAP_LOCK(libc_symbol_binding); \
+       UWRAP_LOCK(libpthread_symbol_binding)
+
+# define UWRAP_UNLOCK_ALL \
+       UWRAP_UNLOCK(libpthread_symbol_binding); \
+       UWRAP_UNLOCK(libc_symbol_binding); \
+       UWRAP_UNLOCK(uwrap_id)
+
+#ifdef HAVE_CONSTRUCTOR_ATTRIBUTE
+#define CONSTRUCTOR_ATTRIBUTE __attribute__ ((constructor))
 #else
-#define UWRAP_DEBUG(...) fprintf(stderr, __VA_ARGS__)
-#endif
+#define CONSTRUCTOR_ATTRIBUTE
+#endif /* HAVE_CONSTRUCTOR_ATTRIBUTE */
+
+#ifdef HAVE_DESTRUCTOR_ATTRIBUTE
+#define DESTRUCTOR_ATTRIBUTE __attribute__ ((destructor))
+#else
+#define DESTRUCTOR_ATTRIBUTE
+#endif /* HAVE_DESTRUCTOR_ATTRIBUTE */
+
+#ifdef HAVE_ADDRESS_SANITIZER_ATTRIBUTE
+#define DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE __attribute__((no_sanitize_address))
+#else /* DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE */
+#define DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE
+#endif /* DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE */
+
+/* GCC have printf type attribute check. */
+#ifdef HAVE_FUNCTION_ATTRIBUTE_FORMAT
+#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#else
+#define PRINTF_ATTRIBUTE(a,b)
+#endif /* HAVE_FUNCTION_ATTRIBUTE_FORMAT */
 
 #define UWRAP_DLIST_ADD(list,item) do { \
        if (!(list)) { \
        (item)->next    = NULL; \
 } while (0)
 
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0)
+#endif
+
+/*****************
+ * LOGGING
+ *****************/
+
+enum uwrap_dbglvl_e {
+       UWRAP_LOG_ERROR = 0,
+       UWRAP_LOG_WARN,
+       UWRAP_LOG_DEBUG,
+       UWRAP_LOG_TRACE
+};
+
+static void uwrap_log(enum uwrap_dbglvl_e dbglvl, const char *function, const char *format, ...) PRINTF_ATTRIBUTE(3, 4);
+# define UWRAP_LOG(dbglvl, ...) uwrap_log((dbglvl), __func__, __VA_ARGS__)
+
+static void uwrap_log(enum uwrap_dbglvl_e dbglvl, const char *function, const char *format, ...)
+{
+       char buffer[1024];
+       va_list va;
+       const char *d;
+       unsigned int lvl = 0;
+       const char *prefix = "UWRAP";
+
+       d = getenv("UID_WRAPPER_DEBUGLEVEL");
+       if (d != NULL) {
+               lvl = atoi(d);
+       }
+
+       if (lvl < dbglvl) {
+               return;
+       }
+
+       va_start(va, format);
+       vsnprintf(buffer, sizeof(buffer), format, va);
+       va_end(va);
+
+       switch (dbglvl) {
+               case UWRAP_LOG_ERROR:
+                       prefix = "UWRAP_ERROR";
+                       break;
+               case UWRAP_LOG_WARN:
+                       prefix = "UWRAP_WARN";
+                       break;
+               case UWRAP_LOG_DEBUG:
+                       prefix = "UWRAP_DEBUG";
+                       break;
+               case UWRAP_LOG_TRACE:
+                       prefix = "UWRAP_TRACE";
+                       break;
+       }
+
+       fprintf(stderr,
+               "%s(%d) - %s: %s\n",
+               prefix,
+               (int)getpid(),
+               function,
+               buffer);
+}
+
+/*****************
+ * LIBC
+ *****************/
+
 #define LIBC_NAME "libc.so"
 
-struct uwrap_libc_fns {
-       int (*_libc_setuid)(uid_t uid);
-       uid_t (*_libc_getuid)(void);
+typedef int (*__libc_setuid)(uid_t uid);
+
+typedef        uid_t (*__libc_getuid)(void);
 
 #ifdef HAVE_SETEUID
-       int (*_libc_seteuid)(uid_t euid);
+typedef int (*__libc_seteuid)(uid_t euid);
 #endif
+
 #ifdef HAVE_SETREUID
-       int (*_libc_setreuid)(uid_t ruid, uid_t euid);
+typedef int (*__libc_setreuid)(uid_t ruid, uid_t euid);
 #endif
-#ifdef HAVE_SETREUID
-       int (*_libc_setresuid)(uid_t ruid, uid_t euid, uid_t suid);
+
+#ifdef HAVE_SETRESUID
+typedef int (*__libc_setresuid)(uid_t ruid, uid_t euid, uid_t suid);
+#endif
+
+#ifdef HAVE_GETRESUID
+typedef int (*__libc_getresuid)(uid_t *ruid, uid_t *euid, uid_t *suid);
 #endif
-       uid_t (*_libc_geteuid)(void);
 
-       int (*_libc_setgid)(gid_t gid);
-       gid_t (*_libc_getgid)(void);
+typedef uid_t (*__libc_geteuid)(void);
+
+typedef int (*__libc_setgid)(gid_t gid);
+
+typedef gid_t (*__libc_getgid)(void);
+
 #ifdef HAVE_SETEGID
-       int (*_libc_setegid)(uid_t egid);
+typedef int (*__libc_setegid)(uid_t egid);
 #endif
+
 #ifdef HAVE_SETREGID
-       int (*_libc_setregid)(uid_t rgid, uid_t egid);
+typedef int (*__libc_setregid)(uid_t rgid, uid_t egid);
+#endif
+
+#ifdef HAVE_SETRESGID
+typedef int (*__libc_setresgid)(uid_t rgid, uid_t egid, uid_t sgid);
+#endif
+
+#ifdef HAVE_GETRESGID
+typedef int (*__libc_getresgid)(gid_t *rgid, gid_t *egid, gid_t *sgid);
+#endif
+
+typedef gid_t (*__libc_getegid)(void);
+
+typedef int (*__libc_getgroups)(int size, gid_t list[]);
+
+typedef int (*__libc_setgroups)(size_t size, const gid_t *list);
+
+#ifdef HAVE_SYSCALL
+typedef long int (*__libc_syscall)(long int sysno, ...);
+#endif
+
+#define UWRAP_SYMBOL_ENTRY(i) \
+       union { \
+               __libc_##i f; \
+               void *obj; \
+       } _libc_##i
+
+struct uwrap_libc_symbols {
+       UWRAP_SYMBOL_ENTRY(setuid);
+       UWRAP_SYMBOL_ENTRY(getuid);
+#ifdef HAVE_SETEUID
+       UWRAP_SYMBOL_ENTRY(seteuid);
+#endif
+#ifdef HAVE_SETREUID
+       UWRAP_SYMBOL_ENTRY(setreuid);
+#endif
+#ifdef HAVE_SETRESUID
+       UWRAP_SYMBOL_ENTRY(setresuid);
+#endif
+#ifdef HAVE_GETRESUID
+       UWRAP_SYMBOL_ENTRY(getresuid);
+#endif
+       UWRAP_SYMBOL_ENTRY(geteuid);
+       UWRAP_SYMBOL_ENTRY(setgid);
+       UWRAP_SYMBOL_ENTRY(getgid);
+#ifdef HAVE_SETEGID
+       UWRAP_SYMBOL_ENTRY(setegid);
 #endif
 #ifdef HAVE_SETREGID
-       int (*_libc_setresgid)(uid_t rgid, uid_t egid, uid_t sgid);
+       UWRAP_SYMBOL_ENTRY(setregid);
+#endif
+#ifdef HAVE_SETRESGID
+       UWRAP_SYMBOL_ENTRY(setresgid);
 #endif
-       gid_t (*_libc_getegid)(void);
-       int (*_libc_getgroups)(int size, gid_t list[]);
-       int (*_libc_setgroups)(size_t size, const gid_t *list);
+#ifdef HAVE_GETRESGID
+       UWRAP_SYMBOL_ENTRY(getresgid);
+#endif
+       UWRAP_SYMBOL_ENTRY(getegid);
+       UWRAP_SYMBOL_ENTRY(getgroups);
+       UWRAP_SYMBOL_ENTRY(setgroups);
 #ifdef HAVE_SYSCALL
-       long int (*_libc_syscall)(long int sysno, ...);
+       UWRAP_SYMBOL_ENTRY(syscall);
 #endif
 };
+#undef UWRAP_SYMBOL_ENTRY
+
+/*****************
+ * LIBPTHREAD
+ *****************/
+/* Yeah... I'm pig. I overloading macro here... So what? */
+#define UWRAP_SYMBOL_ENTRY(i) \
+       union { \
+               __libpthread_##i f; \
+               void *obj; \
+       } _libpthread_##i
+
+typedef int (*__libpthread_pthread_create)(pthread_t *thread,
+                                   const pthread_attr_t *attr,
+                                   void *(*start_routine) (void *),
+                                   void *arg);
+typedef void (*__libpthread_pthread_exit)(void *retval);
+
+struct uwrap_libpthread_symbols {
+       UWRAP_SYMBOL_ENTRY(pthread_create);
+       UWRAP_SYMBOL_ENTRY(pthread_exit);
+};
+#undef UWRAP_SYMBOL_ENTRY
 
 /*
  * We keep the virtualised euid/egid/groups information here
  */
 struct uwrap_thread {
-       pthread_t tid;
-       bool dead;
+       bool enabled;
 
        uid_t ruid;
        uid_t euid;
@@ -131,8 +319,8 @@ struct uwrap_thread {
        gid_t egid;
        gid_t sgid;
 
-       gid_t *groups;
        int ngroups;
+       gid_t *groups;
 
        struct uwrap_thread *next;
        struct uwrap_thread *prev;
@@ -141,14 +329,19 @@ struct uwrap_thread {
 struct uwrap {
        struct {
                void *handle;
-               struct uwrap_libc_fns fns;
+               struct uwrap_libc_symbols symbols;
        } libc;
 
+       struct {
+               void *handle;
+               struct uwrap_libpthread_symbols symbols;
+       } libpthread;
+
        bool initialised;
-       bool enabled;
 
+       /* Real uid and gid of user who run uid wrapper */
        uid_t myuid;
-       uid_t mygid;
+       gid_t mygid;
 
        struct uwrap_thread *ids;
 };
@@ -161,6 +354,20 @@ static UWRAP_THREAD struct uwrap_thread *uwrap_tls_id;
 /* The mutex or accessing the id */
 static pthread_mutex_t uwrap_id_mutex = PTHREAD_MUTEX_INITIALIZER;
 
+/* The mutex for accessing the global libc.symbols */
+static pthread_mutex_t libc_symbol_binding_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* The mutex for accessing the global libpthread.symbols */
+static pthread_mutex_t libpthread_symbol_binding_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/*********************************************************
+ * UWRAP PROTOTYPES
+ *********************************************************/
+
+bool uid_wrapper_enabled(void);
+void uwrap_constructor(void) CONSTRUCTOR_ATTRIBUTE;
+void uwrap_destructor(void) DESTRUCTOR_ATTRIBUTE;
+
 /*********************************************************
  * UWRAP LIBC LOADER FUNCTIONS
  *********************************************************/
@@ -169,6 +376,7 @@ enum uwrap_lib {
     UWRAP_LIBC,
     UWRAP_LIBNSL,
     UWRAP_LIBSOCKET,
+    UWRAP_LIBPTHREAD,
 };
 
 static void *uwrap_load_lib_handle(enum uwrap_lib lib)
@@ -177,10 +385,6 @@ static void *uwrap_load_lib_handle(enum uwrap_lib lib)
        void *handle = NULL;
        int i;
 
-#ifdef HAVE_APPLE
-       return RTLD_NEXT;
-#endif
-
 #ifdef RTLD_DEEPBIND
        flags |= RTLD_DEEPBIND;
 #endif
@@ -191,32 +395,54 @@ static void *uwrap_load_lib_handle(enum uwrap_lib lib)
        case UWRAP_LIBSOCKET:
                /* FALL TROUGH */
        case UWRAP_LIBC:
+               handle = uwrap.libc.handle;
                if (handle == NULL) {
-                       for (handle = NULL, i = 10; handle == NULL && i >= 0; i--) {
+                       for (i = 10; i >= 0; i--) {
                                char soname[256] = {0};
 
                                snprintf(soname, sizeof(soname), "libc.so.%d", i);
                                handle = dlopen(soname, flags);
+                               if (handle != NULL) {
+                                       break;
+                               }
+
+                               /* glibc on Alpha and IA64 is libc.so.6.1 */
+                               snprintf(soname, sizeof(soname), "libc.so.%d.1", i);
+                               handle = dlopen(soname, flags);
+                               if (handle != NULL) {
+                                       break;
+                               }
                        }
 
                        uwrap.libc.handle = handle;
-               } else {
-                       handle = uwrap.libc.handle;
+               }
+               break;
+       case UWRAP_LIBPTHREAD:
+               handle = uwrap.libpthread.handle;
+               if (handle == NULL) {
+                       handle = dlopen("libpthread.so.0", flags);
+                       if (handle != NULL) {
+                               break;
+                       }
                }
                break;
        }
 
        if (handle == NULL) {
+#ifdef RTLD_NEXT
+               handle = uwrap.libc.handle = RTLD_NEXT;
+#else
                fprintf(stderr,
                        "Failed to dlopen library: %s\n",
                        dlerror());
                exit(-1);
+#endif
        }
 
        return handle;
 }
 
-static void *_uwrap_load_lib_function(enum uwrap_lib lib, const char *fn_name)
+static void *_uwrap_bind_symbol(enum uwrap_lib lib, const char *fn_name)
 {
        void *handle;
        void *func;
@@ -234,11 +460,21 @@ static void *_uwrap_load_lib_function(enum uwrap_lib lib, const char *fn_name)
        return func;
 }
 
-#define uwrap_load_lib_function(lib, fn_name) \
-       if (uwrap.libc.fns._libc_##fn_name == NULL) { \
-               *(void **) (&uwrap.libc.fns._libc_##fn_name) = \
-                       _uwrap_load_lib_function(lib, #fn_name); \
-       }
+#define uwrap_bind_symbol_libc(sym_name) \
+       UWRAP_LOCK(libc_symbol_binding); \
+       if (uwrap.libc.symbols._libc_##sym_name.obj == NULL) { \
+               uwrap.libc.symbols._libc_##sym_name.obj = \
+                       _uwrap_bind_symbol(UWRAP_LIBC, #sym_name); \
+       } \
+       UWRAP_UNLOCK(libc_symbol_binding)
+
+#define uwrap_bind_symbol_libpthread(sym_name) \
+       UWRAP_LOCK(libpthread_symbol_binding); \
+       if (uwrap.libpthread.symbols._libpthread_##sym_name.obj == NULL) { \
+               uwrap.libpthread.symbols._libpthread_##sym_name.obj = \
+                       _uwrap_bind_symbol(UWRAP_LIBPTHREAD, #sym_name); \
+       } \
+       UWRAP_UNLOCK(libpthread_symbol_binding)
 
 /*
  * IMPORTANT
@@ -250,295 +486,1243 @@ static void *_uwrap_load_lib_function(enum uwrap_lib lib, const char *fn_name)
  */
 static int libc_setuid(uid_t uid)
 {
-       uwrap_load_lib_function(UWRAP_LIBC, setuid);
+       uwrap_bind_symbol_libc(setuid);
 
-       return uwrap.libc.fns._libc_setuid(uid);
+       return uwrap.libc.symbols._libc_setuid.f(uid);
 }
 
-static void *uwrap_libc_fn(struct uwrap *u, const char *fn_name)
+static uid_t libc_getuid(void)
 {
-       void *func;
+       uwrap_bind_symbol_libc(getuid);
 
-#ifdef HAVE_APPLE
-       func = dlsym(RTLD_NEXT, fn_name);
-#else
-       if (u->libc.handle == NULL) {
-               return NULL;
-       }
+       return uwrap.libc.symbols._libc_getuid.f();
+}
 
-       func = dlsym(u->libc.handle, fn_name);
-#endif
-       if (func == NULL) {
-               printf("Failed to find %s in %s: %s\n",
-                               fn_name, LIBC_NAME, dlerror());
-               exit(-1);
-       }
+#ifdef HAVE_SETEUID
+static int libc_seteuid(uid_t euid)
+{
+       uwrap_bind_symbol_libc(seteuid);
 
-       return func;
+       return uwrap.libc.symbols._libc_seteuid.f(euid);
 }
+#endif
 
-static void uwrap_libc_init(struct uwrap *u)
+#ifdef HAVE_SETREUID
+static int libc_setreuid(uid_t ruid, uid_t euid)
 {
-       unsigned int i = 0;
-#ifndef HAVE_APPLE
-       int flags = RTLD_LAZY;
+       uwrap_bind_symbol_libc(setreuid);
 
-#ifdef RTLD_DEEPBIND
-       flags |= RTLD_DEEPBIND;
+       return uwrap.libc.symbols._libc_setreuid.f(ruid, euid);
+}
 #endif
 
-       for (u->libc.handle = NULL, i = 10; u->libc.handle == NULL; i--) {
-               char soname[256] = {0};
-
-               snprintf(soname, sizeof(soname), "%s.%u", LIBC_NAME, i);
-               u->libc.handle = dlopen(soname, flags);
-       }
+#ifdef HAVE_SETRESUID
+static int libc_setresuid(uid_t ruid, uid_t euid, uid_t suid)
+{
+       uwrap_bind_symbol_libc(setresuid);
 
-       if (u->libc.handle == NULL) {
-               printf("Failed to dlopen %s.%u: %s\n", LIBC_NAME, i, dlerror());
-               exit(-1);
-       }
+       return uwrap.libc.symbols._libc_setresuid.f(ruid, euid, suid);
+}
 #endif
 
-       *(void **) (&u->libc.fns._libc_getuid) = uwrap_libc_fn(u, "getuid");
+#ifdef HAVE_GETRESUID
+static int libc_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid)
+{
+       uwrap_bind_symbol_libc(getresuid);
 
-#ifdef HAVE_SETEUID
-       *(void **) (&u->libc.fns._libc_seteuid) = uwrap_libc_fn(u, "seteuid");
-#endif
-#ifdef HAVE_SETREUID
-       *(void **) (&u->libc.fns._libc_setreuid) = uwrap_libc_fn(u, "setreuid");
-#endif
-#ifdef HAVE_SETRESUID
-       *(void **) (&u->libc.fns._libc_setresuid) = uwrap_libc_fn(u, "setresuid");
+       return uwrap.libc.symbols._libc_getresuid.f(ruid, euid, suid);
+}
 #endif
-       *(void **) (&u->libc.fns._libc_geteuid) = uwrap_libc_fn(u, "geteuid");
 
-       *(void **) (&u->libc.fns._libc_setgid) = uwrap_libc_fn(u, "setgid");
-       *(void **) (&u->libc.fns._libc_getgid) = uwrap_libc_fn(u, "getgid");
+static uid_t libc_geteuid(void)
+{
+       uwrap_bind_symbol_libc(geteuid);
+
+       return uwrap.libc.symbols._libc_geteuid.f();
+}
+
+static int libc_setgid(gid_t gid)
+{
+       uwrap_bind_symbol_libc(setgid);
+
+       return uwrap.libc.symbols._libc_setgid.f(gid);
+}
+
+static gid_t libc_getgid(void)
+{
+       uwrap_bind_symbol_libc(getgid);
+
+       return uwrap.libc.symbols._libc_getgid.f();
+}
+
 #ifdef HAVE_SETEGID
-       *(void **) (&u->libc.fns._libc_setegid) = uwrap_libc_fn(u, "setegid");
+static int libc_setegid(gid_t egid)
+{
+       uwrap_bind_symbol_libc(setegid);
+
+       return uwrap.libc.symbols._libc_setegid.f(egid);
+}
 #endif
+
 #ifdef HAVE_SETREGID
-       *(void **) (&u->libc.fns._libc_setregid) = uwrap_libc_fn(u, "setregid");
+static int libc_setregid(gid_t rgid, gid_t egid)
+{
+       uwrap_bind_symbol_libc(setregid);
+
+       return uwrap.libc.symbols._libc_setregid.f(rgid, egid);
+}
 #endif
+
 #ifdef HAVE_SETRESGID
-       *(void **) (&u->libc.fns._libc_setresgid) = uwrap_libc_fn(u, "setresgid");
+static int libc_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
+{
+       uwrap_bind_symbol_libc(setresgid);
+
+       return uwrap.libc.symbols._libc_setresgid.f(rgid, egid, sgid);
+}
 #endif
-       *(void **) (&u->libc.fns._libc_getegid) = uwrap_libc_fn(u, "getegid");
-       *(void **) (&u->libc.fns._libc_getgroups) = uwrap_libc_fn(u, "getgroups");
-       *(void **) (&u->libc.fns._libc_setgroups) = uwrap_libc_fn(u, "setgroups");
-       *(void **) (&u->libc.fns._libc_getuid) = uwrap_libc_fn(u, "getuid");
-       *(void **) (&u->libc.fns._libc_getgid) = uwrap_libc_fn(u, "getgid");
-#ifdef HAVE_SYSCALL
-       *(void **) (&u->libc.fns._libc_syscall) = uwrap_libc_fn(u, "syscall");
+
+#ifdef HAVE_GETRESGID
+static int libc_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid)
+{
+       uwrap_bind_symbol_libc(setresgid);
+
+       return uwrap.libc.symbols._libc_getresgid.f(rgid, egid, sgid);
+}
 #endif
+
+static gid_t libc_getegid(void)
+{
+       uwrap_bind_symbol_libc(getegid);
+
+       return uwrap.libc.symbols._libc_getegid.f();
 }
 
-static struct uwrap_thread *find_uwrap_id(pthread_t tid)
+static int libc_getgroups(int size, gid_t list[])
 {
-       struct uwrap_thread *id;
+       uwrap_bind_symbol_libc(getgroups);
 
-       for (id = uwrap.ids; id; id = id->next) {
-               if (pthread_equal(id->tid, tid)) {
-                       return id;
-               }
-       }
+       return uwrap.libc.symbols._libc_getgroups.f(size, list);
+}
+
+static int libc_setgroups(size_t size, const gid_t *list)
+{
+       uwrap_bind_symbol_libc(setgroups);
 
-       return NULL;
+       return uwrap.libc.symbols._libc_setgroups.f(size, list);
 }
 
-static int uwrap_new_id(pthread_t tid, bool do_alloc)
+#ifdef HAVE_SYSCALL
+DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE
+static long int libc_vsyscall(long int sysno, va_list va)
 {
-       struct uwrap_thread *id = uwrap_tls_id;
+       long int args[8];
+       long int rc;
+       int i;
 
-       if (do_alloc) {
-               id = malloc(sizeof(struct uwrap_thread));
-               if (id == NULL) {
-                       errno = ENOMEM;
-                       return -1;
-               }
+       uwrap_bind_symbol_libc(syscall);
+
+       for (i = 0; i < 8; i++) {
+               args[i] = va_arg(va, long int);
        }
 
-       id->tid = tid;
-       id->dead = false;
+       rc = uwrap.libc.symbols._libc_syscall.f(sysno,
+                                         args[0],
+                                         args[1],
+                                         args[2],
+                                         args[3],
+                                         args[4],
+                                         args[5],
+                                         args[6],
+                                         args[7]);
 
-       id->ruid = id->euid = id->suid = uwrap.myuid;
-       id->rgid = id->egid = id->sgid = uwrap.mygid;
+       return rc;
+}
+#endif
 
-       id->ngroups = 1;
-       id->groups = malloc(sizeof(gid_t) * id->ngroups);
-       id->groups[0] = uwrap.mygid;
+/*
+ * This part is "optimistic".
+ * Thread can ends without pthread_exit call.
+ */
+static void libpthread_pthread_exit(void *retval)
+{
+       uwrap_bind_symbol_libpthread(pthread_exit);
 
-       if (do_alloc) {
-               UWRAP_DLIST_ADD(uwrap.ids, id);
-               uwrap_tls_id = id;
+       uwrap.libpthread.symbols._libpthread_pthread_exit.f(retval);
+}
+
+static void uwrap_pthread_exit(void *retval)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+
+       UWRAP_LOG(UWRAP_LOG_DEBUG, "Cleanup thread");
+
+       UWRAP_LOCK(uwrap_id);
+       if (id == NULL) {
+               UWRAP_UNLOCK(uwrap_id);
+               libpthread_pthread_exit(retval);
+               return;
        }
 
-       return 0;
+       UWRAP_DLIST_REMOVE(uwrap.ids, id);
+       SAFE_FREE(id->groups);
+       SAFE_FREE(id);
+       uwrap_tls_id = NULL;
+
+       UWRAP_UNLOCK(uwrap_id);
+
+       libpthread_pthread_exit(retval);
 }
 
-static void uwrap_thread_prepare(void)
+void pthread_exit(void *retval)
 {
-       pthread_mutex_lock(&uwrap_id_mutex);
+       if (!uid_wrapper_enabled()) {
+               libpthread_pthread_exit(retval);
+       };
 
-       /*
-        * What happens if another atfork prepare functions calls a uwrap
-        * function? So disable it in case another atfork prepare function
-        * calls a (s)uid function.
-        */
-       uwrap.enabled = false;
+       uwrap_pthread_exit(retval);
+
+       /* Calm down gcc warning. */
+       exit(666);
 }
 
-static void uwrap_thread_parent(void)
+static int libpthread_pthread_create(pthread_t *thread,
+                               const pthread_attr_t *attr,
+                               void *(*start_routine) (void *),
+                               void *arg)
+{
+       uwrap_bind_symbol_libpthread(pthread_create);
+       return uwrap.libpthread.symbols._libpthread_pthread_create.f(thread,
+                                                                    attr,
+                                                                    start_routine,
+                                                                    arg);
+}
+
+struct uwrap_pthread_create_args {
+       struct uwrap_thread *id;
+       void *(*start_routine) (void *);
+       void *arg;
+};
+
+static void *uwrap_pthread_create_start(void *_a)
 {
-       uwrap.enabled = true;
+       struct uwrap_pthread_create_args *a =
+               (struct uwrap_pthread_create_args *)_a;
+       void *(*start_routine) (void *) = a->start_routine;
+       void *arg = a->arg;
+       struct uwrap_thread *id = a->id;
+
+       SAFE_FREE(a);
 
-       pthread_mutex_unlock(&uwrap_id_mutex);
+       uwrap_tls_id = id;
+
+       return start_routine(arg);
 }
 
-static void uwrap_thread_child(void)
+static int uwrap_pthread_create(pthread_t *thread,
+                                const pthread_attr_t *attr,
+                                void *(*start_routine) (void *),
+                                void *arg)
 {
-       uwrap.enabled = true;
+       struct uwrap_pthread_create_args *args;
+       struct uwrap_thread *src_id = uwrap_tls_id;
+       int ret;
+
+       args = malloc(sizeof(struct uwrap_pthread_create_args));
+       if (args == NULL) {
+               UWRAP_LOG(UWRAP_LOG_ERROR,
+                         "uwrap_pthread_create: Unable to allocate memory");
+               errno = ENOMEM;
+               return -1;
+       }
+       args->start_routine = start_routine;
+       args->arg = arg;
+
+       args->id = calloc(1, sizeof(struct uwrap_thread));
+       if (args->id == NULL) {
+               SAFE_FREE(args);
+               UWRAP_LOG(UWRAP_LOG_ERROR,
+                         "uwrap_pthread_create: Unable to allocate memory");
+               errno = ENOMEM;
+               return -1;
+       }
+
+       UWRAP_LOCK(uwrap_id);
+
+       args->id->groups = malloc(sizeof(gid_t) * src_id->ngroups);
+       if (args->id->groups == NULL) {
+               UWRAP_UNLOCK(uwrap_id);
+               SAFE_FREE(args->id);
+               SAFE_FREE(args);
+               UWRAP_LOG(UWRAP_LOG_ERROR,
+                         "uwrap_pthread_create: Unable to allocate memory again");
+               errno = ENOMEM;
+               return -1;
+       }
+
+       args->id->ruid = src_id->ruid;
+       args->id->euid = src_id->euid;
+       args->id->suid = src_id->suid;
+
+       args->id->rgid = src_id->rgid;
+       args->id->egid = src_id->egid;
+       args->id->sgid = src_id->sgid;
+
+       args->id->enabled = src_id->enabled;
+
+       args->id->ngroups = src_id->ngroups;
+       if (src_id->groups != NULL) {
+               memcpy(args->id->groups, src_id->groups,
+                      sizeof(gid_t) * src_id->ngroups);
+       } else {
+               SAFE_FREE(args->id->groups);
+       }
+
+       UWRAP_DLIST_ADD(uwrap.ids, args->id);
+       UWRAP_UNLOCK(uwrap_id);
 
-       pthread_mutex_unlock(&uwrap_id_mutex);
+       ret = libpthread_pthread_create(thread, attr,
+                                       uwrap_pthread_create_start,
+                                       args);
+       if (ret != 0) {
+               return ret;
+       }
+
+       return ret;
 }
 
-static void uwrap_init(void)
+int pthread_create(pthread_t *thread,
+                   const pthread_attr_t *attr,
+                   void *(*start_routine) (void *),
+                   void *arg)
 {
-       const char *env = getenv("UID_WRAPPER");
-       pthread_t tid = pthread_self();
+       if (!uid_wrapper_enabled()) {
+               return libpthread_pthread_create(thread,
+                                          attr,
+                                          start_routine,
+                                          arg);
+       };
+
+       return uwrap_pthread_create(thread,
+                                   attr,
+                                   start_routine,
+                                   arg);
+}
 
+/*********************************************************
+ * UWRAP ID HANDLING
+ *********************************************************/
 
+#define GROUP_STRING_SIZE 16384
+#define GROUP_MAX_COUNT (GROUP_STRING_SIZE / (10 + 1))
 
-       if (uwrap.initialised) {
-               struct uwrap_thread *id = uwrap_tls_id;
-               int rc;
+/**
+ * This function exports all the IDs of the current user so if
+ * we fork and then exec we can setup uid_wrapper in the new process
+ * with those IDs.
+ */
+static void uwrap_export_ids(struct uwrap_thread *id)
+{
+       char groups_str[GROUP_STRING_SIZE] = {0};
+       size_t groups_str_size = sizeof(groups_str);
+       char unsigned_str[16] = {0}; /* We need 10 + 1 (+ 1) */
+       int i;
 
-               if (id != NULL) {
-                       return;
+       /* UIDS */
+       snprintf(unsigned_str, sizeof(unsigned_str), "%u", id->ruid);
+       setenv("UID_WRAPPER_INITIAL_RUID", unsigned_str, 1);
+
+       snprintf(unsigned_str, sizeof(unsigned_str), "%u", id->euid);
+       setenv("UID_WRAPPER_INITIAL_EUID", unsigned_str, 1);
+
+       snprintf(unsigned_str, sizeof(unsigned_str), "%u", id->suid);
+       setenv("UID_WRAPPER_INITIAL_SUID", unsigned_str, 1);
+
+       /* GIDS */
+       snprintf(unsigned_str, sizeof(unsigned_str), "%u", id->rgid);
+       setenv("UID_WRAPPER_INITIAL_RGID", unsigned_str, 1);
+
+       snprintf(unsigned_str, sizeof(unsigned_str), "%u", id->egid);
+       setenv("UID_WRAPPER_INITIAL_EGID", unsigned_str, 1);
+
+       snprintf(unsigned_str, sizeof(unsigned_str), "%u", id->sgid);
+       setenv("UID_WRAPPER_INITIAL_SGID", unsigned_str, 1);
+
+       if (id->ngroups > GROUP_MAX_COUNT) {
+               UWRAP_LOG(UWRAP_LOG_ERROR,
+                         "ERROR: Number of groups (%u) exceeds maximum value "
+                         "uid_wrapper can handle (%u).",
+                         id->ngroups,
+                         GROUP_MAX_COUNT);
+               exit(-1);
+       }
+
+       /* GROUPS */
+       for (i = 0; i < id->ngroups; i++) {
+               size_t groups_str_len = strlen(groups_str);
+               size_t groups_str_avail = groups_str_size - groups_str_len - 1;
+               int len;
+
+               len = snprintf(unsigned_str, sizeof(unsigned_str), ",%u", id->groups[i]);
+               if (len <= 1) {
+                       UWRAP_LOG(UWRAP_LOG_ERROR,
+                                 "snprintf failed for groups[%d]=%u",
+                                 i,
+                                 id->groups[i]);
+                       break;
+               }
+               if (((size_t)len) >= groups_str_avail) {
+                       UWRAP_LOG(UWRAP_LOG_ERROR,
+                                 "groups env string is to small for %d groups",
+                                 i);
+                       break;
                }
 
-               pthread_mutex_lock(&uwrap_id_mutex);
-               id = find_uwrap_id(tid);
+               len = snprintf(groups_str + groups_str_len,
+                              groups_str_size - groups_str_len,
+                              "%s",
+                              i == 0 ? unsigned_str + 1 : unsigned_str);
+               if (len < 1) {
+                       UWRAP_LOG(UWRAP_LOG_ERROR,
+                                 "snprintf failed to create groups string at groups[%d]=%u",
+                                 i,
+                                 id->groups[i]);
+                       break;
+               }
+       }
+
+       if (id->ngroups == i) {
+               setenv("UID_WRAPPER_INITIAL_GROUPS", groups_str, 1);
+
+               snprintf(unsigned_str, sizeof(unsigned_str), "%u", id->ngroups);
+               setenv("UID_WRAPPER_INITIAL_GROUPS_COUNT", unsigned_str, 1);
+       }
+}
+
+static void uwrap_thread_prepare(void)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+
+       UWRAP_LOCK_ALL;
+
+       /* uid_wrapper is loaded but not enabled */
+       if (id == NULL) {
+               return;
+       }
+
+       /*
+        * What happens if another atfork prepare functions calls a uwrap
+        * function? So disable it in case another atfork prepare function
+        * calls a (s)uid function. We disable uid_wrapper only for thread
+        * (process) which called fork.
+        */
+       id->enabled = false;
+}
+
+static void uwrap_thread_parent(void)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+
+       /* uid_wrapper is loaded but not enabled */
+       if (id == NULL) {
+               UWRAP_UNLOCK_ALL;
+               return;
+       }
+
+       id->enabled = true;
+
+       UWRAP_UNLOCK_ALL;
+}
+
+static void uwrap_thread_child(void)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+       struct uwrap_thread *u = uwrap.ids;
+
+       /* uid_wrapper is loaded but not enabled */
+       if (id == NULL) {
+               UWRAP_UNLOCK_ALL;
+               return;
+       }
+
+       /*
+        * "Garbage collector" - Inspired by DESTRUCTOR.
+        * All threads (except one which called fork()) are dead now.. Dave
+        * That's what posix said...
+        */
+       while (u != NULL) {
+               if (u == id) {
+                       /* Skip this item. */
+                       u = uwrap.ids->next;
+                       continue;
+               }
+
+               UWRAP_DLIST_REMOVE(uwrap.ids, u);
+
+               SAFE_FREE(u->groups);
+               SAFE_FREE(u);
+
+               u = uwrap.ids;
+       }
+
+       uwrap_export_ids(id);
+
+       id->enabled = true;
+
+       UWRAP_UNLOCK_ALL;
+}
+
+/*
+ * This initializes uid_wrapper with the IDs exported to the environment. Those
+ * are normally set after we forked and executed.
+ */
+static void uwrap_init_env(struct uwrap_thread *id)
+{
+       const char *env;
+       int ngroups = 0;
+
+       env = getenv("UID_WRAPPER_INITIAL_RUID");
+       if (env != NULL && env[0] != '\0') {
+               UWRAP_LOG(UWRAP_LOG_DEBUG, "Initialize ruid with %s", env);
+               id->ruid = strtoul(env, (char **)NULL, 10);
+               unsetenv("UID_WRAPPER_INITIAL_RUID");
+       }
+
+       env = getenv("UID_WRAPPER_INITIAL_EUID");
+       if (env != NULL && env[0] != '\0') {
+               UWRAP_LOG(UWRAP_LOG_DEBUG, "Initalize euid with %s", env);
+               id->euid = strtoul(env, (char **)NULL, 10);
+               unsetenv("UID_WRAPPER_INITIAL_EUID");
+       }
+
+       env = getenv("UID_WRAPPER_INITIAL_SUID");
+       if (env != NULL && env[0] != '\0') {
+               UWRAP_LOG(UWRAP_LOG_DEBUG, "Initalize suid with %s", env);
+               id->suid = strtoul(env, (char **)NULL, 10);
+               unsetenv("UID_WRAPPER_INITIAL_SUID");
+       }
+
+       env = getenv("UID_WRAPPER_INITIAL_RGID");
+       if (env != NULL && env[0] != '\0') {
+               UWRAP_LOG(UWRAP_LOG_DEBUG, "Initialize ruid with %s", env);
+               id->rgid = strtoul(env, (char **)NULL, 10);
+               unsetenv("UID_WRAPPER_INITIAL_RGID");
+       }
+
+       env = getenv("UID_WRAPPER_INITIAL_EGID");
+       if (env != NULL && env[0] != '\0') {
+               UWRAP_LOG(UWRAP_LOG_DEBUG, "Initalize egid with %s", env);
+               id->egid = strtoul(env, (char **)NULL, 10);
+               unsetenv("UID_WRAPPER_INITIAL_EGID");
+       }
+
+       env = getenv("UID_WRAPPER_INITIAL_SGID");
+       if (env != NULL && env[0] != '\0') {
+               UWRAP_LOG(UWRAP_LOG_DEBUG, "Initalize sgid with %s", env);
+               id->sgid = strtoul(env, (char **)NULL, 10);
+               unsetenv("UID_WRAPPER_INITIAL_SGID");
+       }
+
+       env = getenv("UID_WRAPPER_INITIAL_GROUPS_COUNT");
+       if (env != NULL && env[0] != '\0') {
+               ngroups = strtol(env, (char **)NULL, 10);
+               unsetenv("UID_WRAPPER_INITIAL_GROUPS_COUNT");
+       }
+
+       if (ngroups > 0 && ngroups < GROUP_MAX_COUNT) {
+               int i = 0;
+
+               id->ngroups = 0;
+
+               free(id->groups);
+               id->groups = malloc(sizeof(gid_t) * ngroups);
+               if (id->groups == NULL) {
+                       UWRAP_LOG(UWRAP_LOG_ERROR,
+                                 "Unable to allocate memory");
+                       exit(-1);
+               }
+
+               env = getenv("UID_WRAPPER_INITIAL_GROUPS");
+               if (env != NULL && env[0] != '\0') {
+                       char *groups_str = NULL;
+                       char *saveptr = NULL;
+                       const char *p = NULL;
+
+                       groups_str = strdup(env);
+                       if (groups_str == NULL) {
+                               exit(-1);
+                       }
+
+                       p = strtok_r(groups_str, ",", &saveptr);
+                       while (p != NULL) {
+                               id->groups[i] = strtol(p, (char **)NULL, 10);
+                               i++;
+
+                               p = strtok_r(NULL, ",", &saveptr);
+                       }
+                       SAFE_FREE(groups_str);
+               }
+
+               if (i != ngroups) {
+                       UWRAP_LOG(UWRAP_LOG_ERROR,
+                                 "ERROR: The number of groups (%u) passed, "
+                                 "does not match the number of groups (%u) "
+                                 "we parsed.",
+                                 ngroups,
+                                 i);
+                       exit(-1);
+               }
+
+               UWRAP_LOG(UWRAP_LOG_DEBUG, "Initalize groups with %s", env);
+               id->ngroups = ngroups;
+       }
+}
+
+static void uwrap_init(void)
+{
+       const char *env;
+
+       UWRAP_LOCK(uwrap_id);
+
+       if (uwrap.initialised) {
+               struct uwrap_thread *id = uwrap_tls_id;
+
+               if (uwrap.ids == NULL) {
+                       UWRAP_UNLOCK(uwrap_id);
+                       return;
+               }
+
+               if (id == NULL) {
+                       UWRAP_LOG(UWRAP_LOG_ERROR,
+                                 "Invalid id for thread");
+                       exit(-1);
+               }
+
+               UWRAP_UNLOCK(uwrap_id);
+               return;
+       }
+
+       UWRAP_LOG(UWRAP_LOG_DEBUG, "Initialize uid_wrapper");
+
+       uwrap.initialised = true;
+
+       env = getenv("UID_WRAPPER");
+       if (env != NULL && env[0] == '1') {
+               const char *root = getenv("UID_WRAPPER_ROOT");
+               struct uwrap_thread *id;
+
+               id = calloc(1, sizeof(struct uwrap_thread));
                if (id == NULL) {
-                       rc = uwrap_new_id(tid, 1);
-                       if (rc < 0) {
+                       UWRAP_LOG(UWRAP_LOG_ERROR,
+                                 "Unable to allocate memory for main id");
+                       exit(-1);
+               }
+
+               UWRAP_DLIST_ADD(uwrap.ids, id);
+               uwrap_tls_id = id;
+
+               uwrap.myuid = libc_geteuid();
+               uwrap.mygid = libc_getegid();
+
+               /* put us in one group */
+               if (root != NULL && root[0] == '1') {
+                       id->ruid = id->euid = id->suid = 0;
+                       id->rgid = id->egid = id->sgid = 0;
+
+                       id->groups = malloc(sizeof(gid_t) * 1);
+                       if (id->groups == NULL) {
+                               UWRAP_LOG(UWRAP_LOG_ERROR,
+                                         "Unable to allocate memory");
                                exit(-1);
                        }
+
+                       id->ngroups = 1;
+                       id->groups[0] = 0;
+
                } else {
-                       /* We reuse an old thread id */
-                       uwrap_tls_id = id;
+                       id->ruid = id->euid = id->suid = uwrap.myuid;
+                       id->rgid = id->egid = id->sgid = uwrap.mygid;
+
+                       id->ngroups = libc_getgroups(0, NULL);
+                       if (id->ngroups == -1) {
+                               UWRAP_LOG(UWRAP_LOG_ERROR,
+                                         "Unable to call libc_getgroups in uwrap_init.");
+                               exit(-1);
+                       }
+                       id->groups = malloc(sizeof(gid_t) * id->ngroups);
+                       if (id->groups == NULL) {
+                               UWRAP_LOG(UWRAP_LOG_ERROR, "Unable to allocate memory");
+                               exit(-1);
+                       }
+                       if (libc_getgroups(id->ngroups, id->groups) == -1) {
+                               UWRAP_LOG(UWRAP_LOG_ERROR,
+                                         "Unable to call libc_getgroups again in uwrap_init.");
+                               id->groups = 0;
+                               /*
+                                * Deallocation of uwrap.groups is handled by
+                                * library destructor.
+                                */
+                               exit(-1);
+                       }
+               }
+
+               uwrap_init_env(id);
+
+               id->enabled = true;
+
+               UWRAP_LOG(UWRAP_LOG_DEBUG,
+                         "Enabled uid_wrapper as %s (real uid=%u)",
+                         id->ruid == 0 ? "root" : "user",
+                         (unsigned int)uwrap.myuid);
+       }
+
+       UWRAP_UNLOCK(uwrap_id);
+
+       UWRAP_LOG(UWRAP_LOG_DEBUG, "Successfully initialized uid_wrapper");
+}
+
+bool uid_wrapper_enabled(void)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+       bool enabled;
+
+       if (id == NULL) {
+               return false;
+       }
+
+       UWRAP_LOCK(uwrap_id);
+       enabled = id->enabled;
+       UWRAP_UNLOCK(uwrap_id);
+
+       return enabled;
+}
+
+/*
+ * UWRAP_SETxUID FUNCTIONS
+ */
+
+static int uwrap_setresuid_args(uid_t ruid, uid_t euid, uid_t suid)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "ruid %d -> %d, euid %d -> %d, suid %d -> %d",
+                 id->ruid, ruid, id->euid, euid, id->suid, suid);
+
+       if (id->euid != 0) {
+               if (ruid != (uid_t)-1 &&
+                   ruid != id->ruid &&
+                   ruid != id->euid &&
+                   ruid != id->suid) {
+                       errno = EPERM;
+                       return -1;
+               }
+               if (euid != (uid_t)-1 &&
+                   euid != id->ruid &&
+                   euid != id->euid &&
+                   euid != id->suid) {
+                       errno = EPERM;
+                       return -1;
+               }
+               if (suid != (uid_t)-1 &&
+                   suid != id->ruid &&
+                   suid != id->euid &&
+                   suid != id->suid) {
+                       errno = EPERM;
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+static int uwrap_setresuid_thread(uid_t ruid, uid_t euid, uid_t suid)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+       int rc;
+
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "ruid %d -> %d, euid %d -> %d, suid %d -> %d",
+                 id->ruid, ruid, id->euid, euid, id->suid, suid);
+
+       rc = uwrap_setresuid_args(ruid, euid, suid);
+       if (rc != 0) {
+               return rc;
+       }
+
+       UWRAP_LOCK(uwrap_id);
+
+       if (ruid != (uid_t)-1) {
+               id->ruid = ruid;
+       }
+
+       if (euid != (uid_t)-1) {
+               id->euid = euid;
+       }
+
+       if (suid != (uid_t)-1) {
+               id->suid = suid;
+       }
+
+       UWRAP_UNLOCK(uwrap_id);
+
+       return 0;
+}
+
+static int uwrap_setresuid(uid_t ruid, uid_t euid, uid_t suid)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+       int rc;
+
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "ruid %d -> %d, euid %d -> %d, suid %d -> %d",
+                 id->ruid, ruid, id->euid, euid, id->suid, suid);
+
+       rc = uwrap_setresuid_args(ruid, euid, suid);
+       if (rc != 0) {
+               return rc;
+       }
+
+       UWRAP_LOCK(uwrap_id);
+
+       for (id = uwrap.ids; id; id = id->next) {
+               if (ruid != (uid_t)-1) {
+                       id->ruid = ruid;
+               }
+
+               if (euid != (uid_t)-1) {
+                       id->euid = euid;
+               }
+
+               if (suid != (uid_t)-1) {
+                       id->suid = suid;
+               }
+       }
+
+       UWRAP_UNLOCK(uwrap_id);
+
+       return 0;
+}
+
+static int uwrap_setreuid_args(uid_t ruid, uid_t euid,
+                              uid_t *_new_ruid,
+                              uid_t *_new_euid,
+                              uid_t *_new_suid)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+       uid_t new_ruid = -1, new_euid = -1, new_suid = -1;
+
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "ruid %d -> %d, euid %d -> %d",
+                 id->ruid, ruid, id->euid, euid);
+
+       if (ruid != (uid_t)-1) {
+               new_ruid = ruid;
+               if (ruid != id->ruid &&
+                   ruid != id->euid &&
+                   id->euid != 0) {
+                       errno = EPERM;
+                       return -1;
+               }
+       }
+
+       if (euid != (uid_t)-1) {
+               new_euid = euid;
+               if (euid != id->ruid &&
+                   euid != id->euid &&
+                   euid != id->suid &&
+                   id->euid != 0) {
+                       errno = EPERM;
+                       return -1;
+               }
+       }
+
+       if (ruid != (uid_t) -1 ||
+           (euid != (uid_t)-1 && id->ruid != euid)) {
+               new_suid = new_euid;
+       }
+
+       *_new_ruid = new_ruid;
+       *_new_euid = new_euid;
+       *_new_suid = new_suid;
+
+       return 0;
+}
+
+static int uwrap_setreuid_thread(uid_t ruid, uid_t euid)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+       uid_t new_ruid = -1, new_euid = -1, new_suid = -1;
+       int rc;
+
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "ruid %d -> %d, euid %d -> %d",
+                 id->ruid, ruid, id->euid, euid);
+
+       rc = uwrap_setreuid_args(ruid, euid, &new_ruid, &new_euid, &new_suid);
+       if (rc != 0) {
+               return rc;
+       }
+
+       return uwrap_setresuid_thread(new_ruid, new_euid, new_suid);
+}
+
+#ifdef HAVE_SETREUID
+static int uwrap_setreuid(uid_t ruid, uid_t euid)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+       uid_t new_ruid = -1, new_euid = -1, new_suid = -1;
+       int rc;
+
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "ruid %d -> %d, euid %d -> %d",
+                 id->ruid, ruid, id->euid, euid);
+
+       rc = uwrap_setreuid_args(ruid, euid, &new_ruid, &new_euid, &new_suid);
+       if (rc != 0) {
+               return rc;
+       }
+
+       return uwrap_setresuid(new_ruid, new_euid, new_suid);
+}
+#endif
+
+static int uwrap_setuid_args(uid_t uid,
+                            uid_t *new_ruid,
+                            uid_t *new_euid,
+                            uid_t *new_suid)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "uid %d -> %d",
+                 id->ruid, uid);
+
+       if (uid == (uid_t)-1) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (id->euid == 0) {
+               *new_suid = *new_ruid = uid;
+       } else if (uid != id->ruid &&
+                  uid != id->suid) {
+               errno = EPERM;
+               return -1;
+       }
+
+       *new_euid = uid;
+
+       return 0;
+}
+
+static int uwrap_setuid_thread(uid_t uid)
+{
+       uid_t new_ruid = -1, new_euid = -1, new_suid = -1;
+       int rc;
+
+       rc = uwrap_setuid_args(uid, &new_ruid, &new_euid, &new_suid);
+       if (rc != 0) {
+               return rc;
+       }
+
+       return uwrap_setresuid_thread(new_ruid, new_euid, new_suid);
+}
+
+static int uwrap_setuid(uid_t uid)
+{
+       uid_t new_ruid = -1, new_euid = -1, new_suid = -1;
+       int rc;
+
+       rc = uwrap_setuid_args(uid, &new_ruid, &new_euid, &new_suid);
+       if (rc != 0) {
+               return rc;
+       }
+
+       return uwrap_setresuid(new_ruid, new_euid, new_suid);
+}
+
+/*
+ * UWRAP_GETxUID FUNCTIONS
+ */
+
+#ifdef HAVE_GETRESUID
+static int uwrap_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+
+       UWRAP_LOCK(uwrap_id);
+
+       *ruid = id->ruid;
+       *euid = id->euid;
+       *suid = id->suid;
+
+       UWRAP_UNLOCK(uwrap_id);
+
+       return 0;
+}
+#endif
+
+#ifdef HAVE_GETRESGID
+static int uwrap_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+
+       UWRAP_LOCK(uwrap_id);
+
+       *rgid = id->rgid;
+       *egid = id->egid;
+       *sgid = id->sgid;
+
+       UWRAP_UNLOCK(uwrap_id);
+
+       return 0;
+}
+#endif
+
+/*
+ * UWRAP_SETxGID FUNCTIONS
+ */
+
+static int uwrap_setresgid_args(gid_t rgid, gid_t egid, gid_t sgid)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "rgid %d -> %d, egid %d -> %d, sgid %d -> %d",
+                 id->rgid, rgid, id->egid, egid, id->sgid, sgid);
+
+       if (id->euid != 0) {
+               if (rgid != (gid_t)-1 &&
+                   rgid != id->rgid &&
+                   rgid != id->egid &&
+                   rgid != id->sgid) {
+                       errno = EPERM;
+                       return -1;
+               }
+               if (egid != (gid_t)-1 &&
+                   egid != id->rgid &&
+                   egid != id->egid &&
+                   egid != id->sgid) {
+                       errno = EPERM;
+                       return -1;
+               }
+               if (sgid != (gid_t)-1 &&
+                   sgid != id->rgid &&
+                   sgid != id->egid &&
+                   sgid != id->sgid) {
+                       errno = EPERM;
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+static int uwrap_setresgid_thread(gid_t rgid, gid_t egid, gid_t sgid)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+       int rc;
+
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "rgid %d -> %d, egid %d -> %d, sgid %d -> %d",
+                 id->rgid, rgid, id->egid, egid, id->sgid, sgid);
+
+       rc = uwrap_setresgid_args(rgid, egid, sgid);
+       if (rc != 0) {
+               return rc;
+       }
+
+       UWRAP_LOCK(uwrap_id);
+
+       if (rgid != (gid_t)-1) {
+               id->rgid = rgid;
+       }
+
+       if (egid != (gid_t)-1) {
+               id->egid = egid;
+       }
+
+       if (sgid != (gid_t)-1) {
+               id->sgid = sgid;
+       }
+
+       UWRAP_UNLOCK(uwrap_id);
+
+       return 0;
+}
+
+static int uwrap_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+       int rc;
+
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "rgid %d -> %d, egid %d -> %d, sgid %d -> %d",
+                 id->rgid, rgid, id->egid, egid, id->sgid, sgid);
+
+       rc = uwrap_setresgid_args(rgid, egid, sgid);
+       if (rc != 0) {
+               return rc;
+       }
+
+       UWRAP_LOCK(uwrap_id);
+
+       for (id = uwrap.ids; id; id = id->next) {
+               if (rgid != (gid_t)-1) {
+                       id->rgid = rgid;
+               }
 
-                       uwrap_new_id(tid, 0);
+               if (egid != (gid_t)-1) {
+                       id->egid = egid;
                }
-               pthread_mutex_unlock(&uwrap_id_mutex);
 
-               return;
+               if (sgid != (gid_t)-1) {
+                       id->sgid = sgid;
+               }
        }
 
-       /*
-        * If we hold a lock and the application forks, then the child
-        * is not able to unlock the mutex and we are in a deadlock.
-        * This should prevent such deadlocks.
-        */
-       pthread_atfork(&uwrap_thread_prepare,
-                      &uwrap_thread_parent,
-                      &uwrap_thread_child);
-
-       pthread_mutex_lock(&uwrap_id_mutex);
+       UWRAP_UNLOCK(uwrap_id);
 
-       uwrap_libc_init(&uwrap);
+       return 0;
+}
 
-       uwrap.initialised = true;
-       uwrap.enabled = false;
+static int uwrap_setregid_args(gid_t rgid, gid_t egid,
+                              gid_t *_new_rgid,
+                              gid_t *_new_egid,
+                              gid_t *_new_sgid)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+       gid_t new_rgid = -1, new_egid = -1, new_sgid = -1;
 
-       if (env != NULL && env[0] == '1') {
-               const char *root = getenv("UID_WRAPPER_ROOT");
-               int rc;
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "rgid %d -> %d, egid %d -> %d",
+                 id->rgid, rgid, id->egid, egid);
 
-               /* put us in one group */
-               if (root != NULL && root[0] == '1') {
-                       uwrap.myuid = 0;
-                       uwrap.mygid = 0;
-               } else {
-                       uwrap.myuid = uwrap.libc.fns._libc_geteuid();
-                       uwrap.mygid = uwrap.libc.fns._libc_getegid();
+       if (rgid != (gid_t)-1) {
+               new_rgid = rgid;
+               if (rgid != id->rgid &&
+                   rgid != id->egid &&
+                   id->euid != 0) {
+                       errno = EPERM;
+                       return -1;
                }
+       }
 
-               rc = uwrap_new_id(tid, 1);
-               if (rc < 0) {
-                       exit(-1);
+       if (egid != (gid_t)-1) {
+               new_egid = egid;
+               if (egid != id->rgid &&
+                   egid != id->egid &&
+                   egid != id->sgid &&
+                   id->euid != 0) {
+                       errno = EPERM;
+                       return -1;
                }
+       }
 
-               uwrap.enabled = true;
+       if (rgid != (gid_t) -1 ||
+           (egid != (gid_t)-1 && id->rgid != egid)) {
+               new_sgid = new_egid;
        }
 
-       pthread_mutex_unlock(&uwrap_id_mutex);
+       *_new_rgid = new_rgid;
+       *_new_egid = new_egid;
+       *_new_sgid = new_sgid;
+
+       return 0;
 }
 
-static int uwrap_enabled(void)
+static int uwrap_setregid_thread(gid_t rgid, gid_t egid)
 {
-       uwrap_init();
+       struct uwrap_thread *id = uwrap_tls_id;
+       gid_t new_rgid = -1, new_egid = -1, new_sgid = -1;
+       int rc;
+
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "rgid %d -> %d, egid %d -> %d",
+                 id->rgid, rgid, id->egid, egid);
+
+       rc = uwrap_setregid_args(rgid, egid, &new_rgid, &new_egid, &new_sgid);
+       if (rc != 0) {
+               return rc;
+       }
 
-       return uwrap.enabled ? 1 : 0;
+       return uwrap_setresgid_thread(new_rgid, new_egid, new_sgid);
 }
 
-static int uwrap_setresuid_thread(uid_t ruid, uid_t euid, uid_t suid)
+#ifdef HAVE_SETREGID
+static int uwrap_setregid(gid_t rgid, gid_t egid)
 {
        struct uwrap_thread *id = uwrap_tls_id;
+       gid_t new_rgid = -1, new_egid = -1, new_sgid = -1;
+       int rc;
 
-       if (ruid == (uid_t)-1 && euid == (uid_t)-1 && suid == (uid_t)-1) {
-               errno = EINVAL;
-               return -1;
-       }
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "rgid %d -> %d, egid %d -> %d",
+                 id->rgid, rgid, id->egid, egid);
 
-       pthread_mutex_lock(&uwrap_id_mutex);
-       if (ruid != (uid_t)-1) {
-               id->ruid = ruid;
+       rc = uwrap_setregid_args(rgid, egid, &new_rgid, &new_egid, &new_sgid);
+       if (rc != 0) {
+               return rc;
        }
 
-       if (euid != (uid_t)-1) {
-               id->euid = euid;
+       return uwrap_setresgid(new_rgid, new_egid, new_sgid);
+}
+#endif
+
+static int uwrap_setgid_args(gid_t gid,
+                            gid_t *new_rgid,
+                            gid_t *new_egid,
+                            gid_t *new_sgid)
+{
+       struct uwrap_thread *id = uwrap_tls_id;
+
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "gid %d -> %d",
+                 id->rgid, gid);
+
+       if (gid == (gid_t)-1) {
+               errno = EINVAL;
+               return -1;
        }
 
-       if (suid != (uid_t)-1) {
-               id->suid = suid;
+       if (id->euid == 0) {
+               *new_sgid = *new_rgid = gid;
+       } else if (gid != id->rgid &&
+                  gid != id->sgid) {
+               errno = EPERM;
+               return -1;
        }
-       pthread_mutex_unlock(&uwrap_id_mutex);
+
+       *new_egid = gid;
 
        return 0;
 }
 
-static int uwrap_setresuid(uid_t ruid, uid_t euid, uid_t suid)
+static int uwrap_setgid_thread(gid_t gid)
 {
-       struct uwrap_thread *id;
+       gid_t new_rgid = -1, new_egid = -1, new_sgid = -1;
+       int rc;
 
-       if (ruid == (uid_t)-1 && euid == (uid_t)-1 && suid == (uid_t)-1) {
-               errno = EINVAL;
-               return -1;
+       rc = uwrap_setgid_args(gid, &new_rgid, &new_egid, &new_sgid);
+       if (rc != 0) {
+               return rc;
        }
 
-       pthread_mutex_lock(&uwrap_id_mutex);
-       for (id = uwrap.ids; id; id = id->next) {
-               if (id->dead) {
-                       continue;
-               }
-
-               if (ruid != (uid_t)-1) {
-                       id->ruid = ruid;
-               }
+       return uwrap_setresgid_thread(new_rgid, new_egid, new_sgid);
+}
 
-               if (euid != (uid_t)-1) {
-                       id->euid = euid;
-               }
+static int uwrap_setgid(gid_t gid)
+{
+       gid_t new_rgid = -1, new_egid = -1, new_sgid = -1;
+       int rc;
 
-               if (suid != (uid_t)-1) {
-                       id->suid = suid;
-               }
+       rc = uwrap_setgid_args(gid, &new_rgid, &new_egid, &new_sgid);
+       if (rc != 0) {
+               return rc;
        }
-       pthread_mutex_unlock(&uwrap_id_mutex);
 
-       return 0;
+       return uwrap_setresgid(new_rgid, new_egid, new_sgid);
 }
 
 /*
@@ -546,25 +1730,28 @@ static int uwrap_setresuid(uid_t ruid, uid_t euid, uid_t suid)
  */
 int setuid(uid_t uid)
 {
-       if (!uwrap_enabled()) {
+       if (!uid_wrapper_enabled()) {
                return libc_setuid(uid);
        }
 
-       return uwrap_setresuid(uid, -1, -1);
+       uwrap_init();
+       return uwrap_setuid(uid);
 }
 
 #ifdef HAVE_SETEUID
 int seteuid(uid_t euid)
 {
+       if (!uid_wrapper_enabled()) {
+               return libc_seteuid(euid);
+       }
+
+       /* On FreeBSD the uid_t -1 is set and doesn't produce and error */
        if (euid == (uid_t)-1) {
                errno = EINVAL;
                return -1;
        }
 
-       if (!uwrap_enabled()) {
-               return uwrap.libc.fns._libc_seteuid(euid);
-       }
-
+       uwrap_init();
        return uwrap_setresuid(-1, euid, -1);
 }
 #endif
@@ -572,30 +1759,39 @@ int seteuid(uid_t euid)
 #ifdef HAVE_SETREUID
 int setreuid(uid_t ruid, uid_t euid)
 {
-       if (ruid == (uid_t)-1 && euid == (uid_t)-1) {
-               errno = EINVAL;
-               return -1;
-       }
-
-       if (!uwrap_enabled()) {
-               return uwrap.libc.fns._libc_setreuid(ruid, euid);
+       if (!uid_wrapper_enabled()) {
+               return libc_setreuid(ruid, euid);
        }
 
-       return uwrap_setresuid(ruid, euid, -1);
+       uwrap_init();
+       return uwrap_setreuid(ruid, euid);
 }
 #endif
 
 #ifdef HAVE_SETRESUID
 int setresuid(uid_t ruid, uid_t euid, uid_t suid)
 {
-       if (!uwrap_enabled()) {
-               return uwrap.libc.fns._libc_setresuid(ruid, euid, suid);
+       if (!uid_wrapper_enabled()) {
+               return libc_setresuid(ruid, euid, suid);
        }
 
+       uwrap_init();
        return uwrap_setresuid(ruid, euid, suid);
 }
 #endif
 
+#ifdef HAVE_GETRESUID
+int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid)
+{
+       if (!uid_wrapper_enabled()) {
+               return libc_getresuid(ruid, euid, suid);
+       }
+
+       uwrap_init();
+       return uwrap_getresuid(ruid, euid, suid);
+}
+#endif
+
 /*
  * GETUID
  */
@@ -604,19 +1800,20 @@ static uid_t uwrap_getuid(void)
        struct uwrap_thread *id = uwrap_tls_id;
        uid_t uid;
 
-       pthread_mutex_lock(&uwrap_id_mutex);
+       UWRAP_LOCK(uwrap_id);
        uid = id->ruid;
-       pthread_mutex_unlock(&uwrap_id_mutex);
+       UWRAP_UNLOCK(uwrap_id);
 
        return uid;
 }
 
 uid_t getuid(void)
 {
-       if (!uwrap_enabled()) {
-               return uwrap.libc.fns._libc_getuid();
+       if (!uid_wrapper_enabled()) {
+               return libc_getuid();
        }
 
+       uwrap_init();
        return uwrap_getuid();
 }
 
@@ -625,16 +1822,16 @@ uid_t getuid(void)
  */
 static uid_t uwrap_geteuid(void)
 {
-       const char *env = getenv("UID_WRAPPER_ROOT");
+       const char *env = getenv("UID_WRAPPER_MYUID");
        struct uwrap_thread *id = uwrap_tls_id;
        uid_t uid;
 
-       pthread_mutex_lock(&uwrap_id_mutex);
+       UWRAP_LOCK(uwrap_id);
        uid = id->euid;
-       pthread_mutex_unlock(&uwrap_id_mutex);
+       UWRAP_UNLOCK(uwrap_id);
 
        /* Disable root and return myuid */
-       if (env != NULL && env[0] == '2') {
+       if (env != NULL && env[0] == '1') {
                uid = uwrap.myuid;
        }
 
@@ -643,90 +1840,41 @@ static uid_t uwrap_geteuid(void)
 
 uid_t geteuid(void)
 {
-       if (!uwrap_enabled()) {
-               return uwrap.libc.fns._libc_geteuid();
+       if (!uid_wrapper_enabled()) {
+               return libc_geteuid();
        }
 
+       uwrap_init();
        return uwrap_geteuid();
 }
 
-static int uwrap_setresgid_thread(gid_t rgid, gid_t egid, gid_t sgid)
-{
-       struct uwrap_thread *id = uwrap_tls_id;
-
-       if (rgid == (gid_t)-1 && egid == (gid_t)-1 && sgid == (gid_t)-1) {
-               errno = EINVAL;
-               return -1;
-       }
-
-       pthread_mutex_lock(&uwrap_id_mutex);
-       if (rgid != (gid_t)-1) {
-               id->rgid = rgid;
-       }
-
-       if (egid != (gid_t)-1) {
-               id->egid = egid;
-       }
-
-       if (sgid != (gid_t)-1) {
-               id->sgid = sgid;
-       }
-       pthread_mutex_unlock(&uwrap_id_mutex);
-
-       return 0;
-}
-
-static int uwrap_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
-{
-       struct uwrap_thread *id;
-
-       if (rgid == (gid_t)-1 && egid == (gid_t)-1 && sgid == (gid_t)-1) {
-               errno = EINVAL;
-               return -1;
-       }
-
-       pthread_mutex_lock(&uwrap_id_mutex);
-       for (id = uwrap.ids; id; id = id->next) {
-               if (id->dead) {
-                       continue;
-               }
-
-               if (rgid != (gid_t)-1) {
-                       id->rgid = rgid;
-               }
-
-               if (egid != (gid_t)-1) {
-                       id->egid = egid;
-               }
-
-               if (sgid != (gid_t)-1) {
-                       id->sgid = sgid;
-               }
-       }
-       pthread_mutex_unlock(&uwrap_id_mutex);
-
-       return 0;
-}
-
 /*
  * SETGID
  */
 int setgid(gid_t gid)
 {
-       if (!uwrap_enabled()) {
-               return uwrap.libc.fns._libc_setgid(gid);
+       if (!uid_wrapper_enabled()) {
+               return libc_setgid(gid);
        }
 
-       return uwrap_setresgid(gid, -1, -1);
+       uwrap_init();
+       return uwrap_setgid(gid);
 }
 
 #ifdef HAVE_SETEGID
 int setegid(gid_t egid)
 {
-       if (!uwrap_enabled()) {
-               return uwrap.libc.fns._libc_setegid(egid);
+       if (!uid_wrapper_enabled()) {
+               return libc_setegid(egid);
        }
 
+       /* On FreeBSD the uid_t -1 is set and doesn't produce and error */
+       if (egid == (gid_t)-1) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       uwrap_init();
        return uwrap_setresgid(-1, egid, -1);
 }
 #endif
@@ -734,25 +1882,39 @@ int setegid(gid_t egid)
 #ifdef HAVE_SETREGID
 int setregid(gid_t rgid, gid_t egid)
 {
-       if (!uwrap_enabled()) {
-               return uwrap.libc.fns._libc_setregid(rgid, egid);
+       if (!uid_wrapper_enabled()) {
+               return libc_setregid(rgid, egid);
        }
 
-       return uwrap_setresgid(rgid, egid, -1);
+       uwrap_init();
+       return uwrap_setregid(rgid, egid);
 }
 #endif
 
 #ifdef HAVE_SETRESGID
 int setresgid(gid_t rgid, gid_t egid, gid_t sgid)
 {
-       if (!uwrap_enabled()) {
-               return uwrap.libc.fns._libc_setregid(rgid, egid, sgid);
+       if (!uid_wrapper_enabled()) {
+               return libc_setresgid(rgid, egid, sgid);
        }
 
+       uwrap_init();
        return uwrap_setresgid(rgid, egid, sgid);
 }
 #endif
 
+#ifdef HAVE_GETRESGID
+int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid)
+{
+       if (!uid_wrapper_enabled()) {
+               return libc_getresgid(rgid, egid, sgid);
+       }
+
+       uwrap_init();
+       return uwrap_getresgid(rgid, egid, sgid);
+}
+#endif
+
 /*
  * GETGID
  */
@@ -761,19 +1923,20 @@ static gid_t uwrap_getgid(void)
        struct uwrap_thread *id = uwrap_tls_id;
        gid_t gid;
 
-       pthread_mutex_lock(&uwrap_id_mutex);
+       UWRAP_LOCK(uwrap_id);
        gid = id->rgid;
-       pthread_mutex_unlock(&uwrap_id_mutex);
+       UWRAP_UNLOCK(uwrap_id);
 
        return gid;
 }
 
 gid_t getgid(void)
 {
-       if (!uwrap_enabled()) {
-               return uwrap.libc.fns._libc_getgid();
+       if (!uid_wrapper_enabled()) {
+               return libc_getgid();
        }
 
+       uwrap_init();
        return uwrap_getgid();
 }
 
@@ -785,19 +1948,20 @@ static uid_t uwrap_getegid(void)
        struct uwrap_thread *id = uwrap_tls_id;
        gid_t gid;
 
-       pthread_mutex_lock(&uwrap_id_mutex);
+       UWRAP_LOCK(uwrap_id);
        gid = id->egid;
-       pthread_mutex_unlock(&uwrap_id_mutex);
+       UWRAP_UNLOCK(uwrap_id);
 
        return gid;
 }
 
 uid_t getegid(void)
 {
-       if (!uwrap_enabled()) {
-               return uwrap.libc.fns._libc_getegid();
+       if (!uid_wrapper_enabled()) {
+               return libc_getegid();
        }
 
+       uwrap_init();
        return uwrap_getegid();
 }
 
@@ -806,24 +1970,27 @@ static int uwrap_setgroups_thread(size_t size, const gid_t *list)
        struct uwrap_thread *id = uwrap_tls_id;
        int rc = -1;
 
-       pthread_mutex_lock(&uwrap_id_mutex);
-       free(id->groups);
-       id->groups = NULL;
-       id->ngroups = 0;
+       UWRAP_LOCK(uwrap_id);
 
-       if (size != 0) {
-               id->groups = malloc(sizeof(gid_t) * size);
-               if (id->groups == NULL) {
+       if (size == 0) {
+               SAFE_FREE(id->groups);
+               id->ngroups = 0;
+       } else if (size > 0) {
+               gid_t *tmp;
+
+               tmp = realloc(id->groups, sizeof(gid_t) * size);
+               if (tmp == NULL) {
                        errno = ENOMEM;
                        goto out;
                }
+               id->groups = tmp;
                id->ngroups = size;
                memcpy(id->groups, list, size * sizeof(gid_t));
        }
 
        rc = 0;
 out:
-       pthread_mutex_unlock(&uwrap_id_mutex);
+       UWRAP_UNLOCK(uwrap_id);
 
        return rc;
 }
@@ -833,18 +2000,25 @@ static int uwrap_setgroups(size_t size, const gid_t *list)
        struct uwrap_thread *id;
        int rc = -1;
 
-       pthread_mutex_lock(&uwrap_id_mutex);
-       for (id = uwrap.ids; id; id = id->next) {
-               free(id->groups);
-               id->groups = NULL;
-               id->ngroups = 0;
+       UWRAP_LOCK(uwrap_id);
 
-               if (size != 0) {
-                       id->groups = malloc(sizeof(gid_t) * size);
-                       if (id->groups == NULL) {
+       if (size == 0) {
+               for (id = uwrap.ids; id; id = id->next) {
+                       SAFE_FREE(id->groups);
+                       id->ngroups = 0;
+
+               }
+       } else if (size > 0) {
+               gid_t *tmp;
+
+               for (id = uwrap.ids; id; id = id->next) {
+                       tmp = realloc(id->groups, sizeof(gid_t) * size);
+                       if (tmp == NULL) {
                                errno = ENOMEM;
                                goto out;
                        }
+                       id->groups = tmp;
+
                        id->ngroups = size;
                        memcpy(id->groups, list, size * sizeof(gid_t));
                }
@@ -852,7 +2026,7 @@ static int uwrap_setgroups(size_t size, const gid_t *list)
 
        rc = 0;
 out:
-       pthread_mutex_unlock(&uwrap_id_mutex);
+       UWRAP_UNLOCK(uwrap_id);
 
        return rc;
 }
@@ -863,10 +2037,11 @@ int setgroups(int size, const gid_t *list)
 int setgroups(size_t size, const gid_t *list)
 #endif
 {
-       if (!uwrap_enabled()) {
-               return uwrap.libc.fns._libc_setgroups(size, list);
+       if (!uid_wrapper_enabled()) {
+               return libc_setgroups(size, list);
        }
 
+       uwrap_init();
        return uwrap_setgroups(size, list);
 }
 
@@ -875,7 +2050,7 @@ static int uwrap_getgroups(int size, gid_t *list)
        struct uwrap_thread *id = uwrap_tls_id;
        int ngroups;
 
-       pthread_mutex_lock(&uwrap_id_mutex);
+       UWRAP_LOCK(uwrap_id);
        ngroups = id->ngroups;
 
        if (size > ngroups) {
@@ -891,43 +2066,21 @@ static int uwrap_getgroups(int size, gid_t *list)
        memcpy(list, id->groups, size * sizeof(gid_t));
 
 out:
-       pthread_mutex_unlock(&uwrap_id_mutex);
+       UWRAP_UNLOCK(uwrap_id);
 
        return ngroups;
 }
 
 int getgroups(int size, gid_t *list)
 {
-       if (!uwrap_enabled()) {
-               return uwrap.libc.fns._libc_getgroups(size, list);
+       if (!uid_wrapper_enabled()) {
+               return libc_getgroups(size, list);
        }
 
+       uwrap_init();
        return uwrap_getgroups(size, list);
 }
 
-static long int libc_vsyscall(long int sysno, va_list va)
-{
-       long int args[8];
-       long int rc;
-       int i;
-
-       for (i = 0; i < 8; i++) {
-               args[i] = va_arg(va, long int);
-       }
-
-       rc = uwrap.libc.fns._libc_syscall(sysno,
-                                         args[0],
-                                         args[1],
-                                         args[2],
-                                         args[3],
-                                         args[4],
-                                         args[5],
-                                         args[6],
-                                         args[7]);
-
-       return rc;
-}
-
 #if (defined(HAVE_SYS_SYSCALL_H) || defined(HAVE_SYSCALL_H)) \
     && (defined(SYS_setreuid) || defined(SYS_setreuid32))
 static long int uwrap_syscall (long int sysno, va_list vp)
@@ -936,7 +2089,11 @@ static long int uwrap_syscall (long int sysno, va_list vp)
 
        switch (sysno) {
                /* gid */
+#ifdef __alpha__
+               case SYS_getxgid:
+#else
                case SYS_getgid:
+#endif
 #ifdef HAVE_LINUX_32BIT_SYSCALLS
                case SYS_getgid32:
 #endif
@@ -959,9 +2116,9 @@ static long int uwrap_syscall (long int sysno, va_list vp)
                case SYS_setgid32:
 #endif
                        {
-                               gid_t gid = (gid_t) va_arg(vp, int);
+                               gid_t gid = (gid_t) va_arg(vp, gid_t);
 
-                               rc = uwrap_setresgid_thread(gid, -1, -1);
+                               rc = uwrap_setgid_thread(gid);
                        }
                        break;
                case SYS_setregid:
@@ -969,10 +2126,10 @@ static long int uwrap_syscall (long int sysno, va_list vp)
                case SYS_setregid32:
 #endif
                        {
-                               uid_t rgid = (uid_t) va_arg(vp, int);
-                               uid_t egid = (uid_t) va_arg(vp, int);
+                               gid_t rgid = (gid_t) va_arg(vp, gid_t);
+                               gid_t egid = (gid_t) va_arg(vp, gid_t);
 
-                               rc = uwrap_setresgid_thread(rgid, egid, -1);
+                               rc = uwrap_setregid_thread(rgid, egid);
                        }
                        break;
 #ifdef SYS_setresgid
@@ -981,17 +2138,35 @@ static long int uwrap_syscall (long int sysno, va_list vp)
                case SYS_setresgid32:
 #endif
                        {
-                               uid_t rgid = (uid_t) va_arg(vp, int);
-                               uid_t egid = (uid_t) va_arg(vp, int);
-                               uid_t sgid = (uid_t) va_arg(vp, int);
+                               gid_t rgid = (gid_t) va_arg(vp, gid_t);
+                               gid_t egid = (gid_t) va_arg(vp, gid_t);
+                               gid_t sgid = (gid_t) va_arg(vp, gid_t);
 
                                rc = uwrap_setresgid_thread(rgid, egid, sgid);
                        }
                        break;
 #endif /* SYS_setresgid */
+#if defined(SYS_getresgid) && defined(HAVE_GETRESGID)
+               case SYS_getresgid:
+#ifdef HAVE_LINUX_32BIT_SYSCALLS
+               case SYS_getresgid32:
+#endif
+                       {
+                               gid_t *rgid = (gid_t *) va_arg(vp, gid_t *);
+                               gid_t *egid = (gid_t *) va_arg(vp, gid_t *);
+                               gid_t *sgid = (gid_t *) va_arg(vp, gid_t *);
+
+                               rc = uwrap_getresgid(rgid, egid, sgid);
+                       }
+                       break;
+#endif /* SYS_getresgid && HAVE_GETRESGID */
 
                /* uid */
+#ifdef __alpha__
+               case SYS_getxuid:
+#else
                case SYS_getuid:
+#endif
 #ifdef HAVE_LINUX_32BIT_SYSCALLS
                case SYS_getuid32:
 #endif
@@ -1014,9 +2189,9 @@ static long int uwrap_syscall (long int sysno, va_list vp)
                case SYS_setuid32:
 #endif
                        {
-                               uid_t uid = (uid_t) va_arg(vp, int);
+                               uid_t uid = (uid_t) va_arg(vp, uid_t);
 
-                               rc = uwrap_setresuid_thread(uid, -1, -1);
+                               rc = uwrap_setuid_thread(uid);
                        }
                        break;
                case SYS_setreuid:
@@ -1024,10 +2199,10 @@ static long int uwrap_syscall (long int sysno, va_list vp)
                case SYS_setreuid32:
 #endif
                        {
-                               uid_t ruid = (uid_t) va_arg(vp, int);
-                               uid_t euid = (uid_t) va_arg(vp, int);
+                               uid_t ruid = (uid_t) va_arg(vp, uid_t);
+                               uid_t euid = (uid_t) va_arg(vp, uid_t);
 
-                               rc = uwrap_setresuid_thread(ruid, euid, -1);
+                               rc = uwrap_setreuid_thread(ruid, euid);
                        }
                        break;
 #ifdef SYS_setresuid
@@ -1036,15 +2211,28 @@ static long int uwrap_syscall (long int sysno, va_list vp)
                case SYS_setresuid32:
 #endif
                        {
-                               uid_t ruid = (uid_t) va_arg(vp, int);
-                               uid_t euid = (uid_t) va_arg(vp, int);
-                               uid_t suid = (uid_t) va_arg(vp, int);
+                               uid_t ruid = (uid_t) va_arg(vp, uid_t);
+                               uid_t euid = (uid_t) va_arg(vp, uid_t);
+                               uid_t suid = (uid_t) va_arg(vp, uid_t);
 
                                rc = uwrap_setresuid_thread(ruid, euid, suid);
                        }
                        break;
 #endif /* SYS_setresuid */
+#if defined(SYS_getresuid) && defined(HAVE_GETRESUID)
+               case SYS_getresuid:
+#ifdef HAVE_LINUX_32BIT_SYSCALLS
+               case SYS_getresuid32:
+#endif
+                       {
+                               uid_t *ruid = (uid_t *) va_arg(vp, uid_t *);
+                               uid_t *euid = (uid_t *) va_arg(vp, uid_t *);
+                               uid_t *suid = (uid_t *) va_arg(vp, uid_t *);
 
+                               rc = uwrap_getresuid(ruid, euid, suid);
+                       }
+                       break;
+#endif /* SYS_getresuid && HAVE_GETRESUID*/
                /* groups */
                case SYS_setgroups:
 #ifdef HAVE_LINUX_32BIT_SYSCALLS
@@ -1058,8 +2246,9 @@ static long int uwrap_syscall (long int sysno, va_list vp)
                        }
                        break;
                default:
-                       UWRAP_DEBUG("UID_WRAPPER calling non-wrapped syscall "
-                                   "%lu\n", sysno);
+                       UWRAP_LOG(UWRAP_LOG_DEBUG,
+                                 "UID_WRAPPER calling non-wrapped syscall %lu",
+                                 sysno);
 
                        rc = libc_vsyscall(sysno, vp);
                        break;
@@ -1084,12 +2273,13 @@ long int syscall (long int sysno, ...)
 
        va_start(va, sysno);
 
-       if (!uwrap_enabled()) {
+       if (!uid_wrapper_enabled()) {
                rc = libc_vsyscall(sysno, va);
                va_end(va);
                return rc;
        }
 
+       uwrap_init();
        rc = uwrap_syscall(sysno, va);
        va_end(va);
 
@@ -1097,3 +2287,58 @@ long int syscall (long int sysno, ...)
 }
 #endif /* HAVE_SYSCALL */
 #endif /* HAVE_SYS_SYSCALL_H || HAVE_SYSCALL_H */
+
+/****************************
+ * CONSTRUCTOR
+ ***************************/
+void uwrap_constructor(void)
+{
+       /*
+       * If we hold a lock and the application forks, then the child
+       * is not able to unlock the mutex and we are in a deadlock.
+       * This should prevent such deadlocks.
+       */
+       pthread_atfork(&uwrap_thread_prepare,
+                      &uwrap_thread_parent,
+                      &uwrap_thread_child);
+
+       /* Here is safe place to call uwrap_init() and initialize data
+        * for main process.
+        */
+       uwrap_init();
+}
+
+/****************************
+ * DESTRUCTOR
+ ***************************/
+
+/*
+ * This function is called when the library is unloaded and makes sure that
+ * resources are freed.
+ */
+void uwrap_destructor(void)
+{
+       struct uwrap_thread *u = uwrap.ids;
+
+       UWRAP_LOCK_ALL;
+
+       while (u != NULL) {
+               UWRAP_DLIST_REMOVE(uwrap.ids, u);
+
+               SAFE_FREE(u->groups);
+               SAFE_FREE(u);
+
+               u = uwrap.ids;
+       }
+
+
+       if (uwrap.libc.handle != NULL) {
+               dlclose(uwrap.libc.handle);
+       }
+
+       if (uwrap.libpthread.handle != NULL) {
+               dlclose(uwrap.libpthread.handle);
+       }
+
+       UWRAP_UNLOCK_ALL;
+}