uwrap: First do garbage collection before exporting ids
[uid_wrapper.git] / src / uid_wrapper.c
index 187cc113862900587c88bf45ec828ceb26aa6d51..a48cc3bda275597cec8126c14446ef5e747e0f0d 100644 (file)
        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 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)))
@@ -116,54 +133,52 @@ enum uwrap_dbglvl_e {
        UWRAP_LOG_TRACE
 };
 
-#ifdef NDEBUG
-# define UWRAP_LOG(...)
-#else /* NDEBUG */
-static void uwrap_log(enum uwrap_dbglvl_e dbglvl, const char *format, ...) PRINTF_ATTRIBUTE(2, 3);
-# define UWRAP_LOG(dbglvl, ...) uwrap_log((dbglvl), __VA_ARGS__)
+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 *format, ...)
+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);
 
-       if (lvl >= dbglvl) {
-               switch (dbglvl) {
-                       case UWRAP_LOG_ERROR:
-                               fprintf(stderr,
-                                       "UWRAP_ERROR(%d): %s\n",
-                                       (int)getpid(), buffer);
-                               break;
-                       case UWRAP_LOG_WARN:
-                               fprintf(stderr,
-                                       "UWRAP_WARN(%d): %s\n",
-                                       (int)getpid(), buffer);
-                               break;
-                       case UWRAP_LOG_DEBUG:
-                               fprintf(stderr,
-                                       "UWRAP_DEBUG(%d): %s\n",
-                                       (int)getpid(), buffer);
-                               break;
-                       case UWRAP_LOG_TRACE:
-                               fprintf(stderr,
-                                       "UWRAP_TRACE(%d): %s\n",
-                                       (int)getpid(), buffer);
-                               break;
-               }
+       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);
 }
-#endif /* NDEBUG */
 
 /*****************
  * LIBC
@@ -171,46 +186,130 @@ static void uwrap_log(enum uwrap_dbglvl_e dbglvl, const char *format, ...)
 
 #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_SETRESUID
-       int (*_libc_setresuid)(uid_t ruid, uid_t euid, uid_t suid);
+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
+
+typedef uid_t (*__libc_geteuid)(void);
+
+typedef int (*__libc_setgid)(gid_t gid);
+
+typedef gid_t (*__libc_getgid)(void);
+
+#ifdef HAVE_SETEGID
+typedef int (*__libc_setegid)(uid_t egid);
+#endif
+
+#ifdef HAVE_SETREGID
+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
-       uid_t (*_libc_geteuid)(void);
 
-       int (*_libc_setgid)(gid_t gid);
-       gid_t (*_libc_getgid)(void);
+#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
-       int (*_libc_setegid)(uid_t egid);
+       UWRAP_SYMBOL_ENTRY(setegid);
 #endif
 #ifdef HAVE_SETREGID
-       int (*_libc_setregid)(uid_t rgid, uid_t egid);
+       UWRAP_SYMBOL_ENTRY(setregid);
 #endif
 #ifdef HAVE_SETRESGID
-       int (*_libc_setresgid)(uid_t rgid, uid_t egid, uid_t sgid);
+       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;
@@ -220,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;
@@ -230,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;
 };
@@ -250,9 +354,12 @@ 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.fns */
+/* 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
  *********************************************************/
@@ -269,6 +376,7 @@ enum uwrap_lib {
     UWRAP_LIBC,
     UWRAP_LIBNSL,
     UWRAP_LIBSOCKET,
+    UWRAP_LIBPTHREAD,
 };
 
 static void *uwrap_load_lib_handle(enum uwrap_lib lib)
@@ -297,11 +405,27 @@ static void *uwrap_load_lib_handle(enum uwrap_lib lib)
                                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;
                }
                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) {
@@ -318,7 +442,7 @@ static void *uwrap_load_lib_handle(enum uwrap_lib lib)
        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;
@@ -336,14 +460,22 @@ static void *_uwrap_load_lib_function(enum uwrap_lib lib, const char *fn_name)
        return func;
 }
 
-#define uwrap_load_lib_function(lib, fn_name) \
+#define uwrap_bind_symbol_libc(sym_name) \
        UWRAP_LOCK(libc_symbol_binding); \
-       if (uwrap.libc.fns._libc_##fn_name == NULL) { \
-               *(void **) (&uwrap.libc.fns._libc_##fn_name) = \
-                       _uwrap_load_lib_function(lib, #fn_name); \
+       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
  *
@@ -354,128 +486,147 @@ 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 uid_t libc_getuid(void)
 {
-       uwrap_load_lib_function(UWRAP_LIBC, getuid);
+       uwrap_bind_symbol_libc(getuid);
 
-       return uwrap.libc.fns._libc_getuid();
+       return uwrap.libc.symbols._libc_getuid.f();
 }
 
 #ifdef HAVE_SETEUID
 static int libc_seteuid(uid_t euid)
 {
-       uwrap_load_lib_function(UWRAP_LIBC, seteuid);
+       uwrap_bind_symbol_libc(seteuid);
 
-       return uwrap.libc.fns._libc_seteuid(euid);
+       return uwrap.libc.symbols._libc_seteuid.f(euid);
 }
 #endif
 
 #ifdef HAVE_SETREUID
 static int libc_setreuid(uid_t ruid, uid_t euid)
 {
-       uwrap_load_lib_function(UWRAP_LIBC, setreuid);
+       uwrap_bind_symbol_libc(setreuid);
 
-       return uwrap.libc.fns._libc_setreuid(ruid, euid);
+       return uwrap.libc.symbols._libc_setreuid.f(ruid, euid);
 }
 #endif
 
 #ifdef HAVE_SETRESUID
 static int libc_setresuid(uid_t ruid, uid_t euid, uid_t suid)
 {
-       uwrap_load_lib_function(UWRAP_LIBC, setresuid);
+       uwrap_bind_symbol_libc(setresuid);
+
+       return uwrap.libc.symbols._libc_setresuid.f(ruid, euid, suid);
+}
+#endif
+
+#ifdef HAVE_GETRESUID
+static int libc_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid)
+{
+       uwrap_bind_symbol_libc(getresuid);
 
-       return uwrap.libc.fns._libc_setresuid(ruid, euid, suid);
+       return uwrap.libc.symbols._libc_getresuid.f(ruid, euid, suid);
 }
 #endif
 
 static uid_t libc_geteuid(void)
 {
-       uwrap_load_lib_function(UWRAP_LIBC, geteuid);
+       uwrap_bind_symbol_libc(geteuid);
 
-       return uwrap.libc.fns._libc_geteuid();
+       return uwrap.libc.symbols._libc_geteuid.f();
 }
 
 static int libc_setgid(gid_t gid)
 {
-       uwrap_load_lib_function(UWRAP_LIBC, setgid);
+       uwrap_bind_symbol_libc(setgid);
 
-       return uwrap.libc.fns._libc_setgid(gid);
+       return uwrap.libc.symbols._libc_setgid.f(gid);
 }
 
 static gid_t libc_getgid(void)
 {
-       uwrap_load_lib_function(UWRAP_LIBC, getgid);
+       uwrap_bind_symbol_libc(getgid);
 
-       return uwrap.libc.fns._libc_getgid();
+       return uwrap.libc.symbols._libc_getgid.f();
 }
 
 #ifdef HAVE_SETEGID
 static int libc_setegid(gid_t egid)
 {
-       uwrap_load_lib_function(UWRAP_LIBC, setegid);
+       uwrap_bind_symbol_libc(setegid);
 
-       return uwrap.libc.fns._libc_setegid(egid);
+       return uwrap.libc.symbols._libc_setegid.f(egid);
 }
 #endif
 
 #ifdef HAVE_SETREGID
 static int libc_setregid(gid_t rgid, gid_t egid)
 {
-       uwrap_load_lib_function(UWRAP_LIBC, setregid);
+       uwrap_bind_symbol_libc(setregid);
 
-       return uwrap.libc.fns._libc_setregid(rgid, egid);
+       return uwrap.libc.symbols._libc_setregid.f(rgid, egid);
 }
 #endif
 
 #ifdef HAVE_SETRESGID
 static int libc_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
 {
-       uwrap_load_lib_function(UWRAP_LIBC, setresgid);
+       uwrap_bind_symbol_libc(setresgid);
 
-       return uwrap.libc.fns._libc_setresgid(rgid, egid, sgid);
+       return uwrap.libc.symbols._libc_setresgid.f(rgid, egid, sgid);
+}
+#endif
+
+#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_load_lib_function(UWRAP_LIBC, getegid);
+       uwrap_bind_symbol_libc(getegid);
 
-       return uwrap.libc.fns._libc_getegid();
+       return uwrap.libc.symbols._libc_getegid.f();
 }
 
 static int libc_getgroups(int size, gid_t list[])
 {
-       uwrap_load_lib_function(UWRAP_LIBC, getgroups);
+       uwrap_bind_symbol_libc(getgroups);
 
-       return uwrap.libc.fns._libc_getgroups(size, list);
+       return uwrap.libc.symbols._libc_getgroups.f(size, list);
 }
 
 static int libc_setgroups(size_t size, const gid_t *list)
 {
-       uwrap_load_lib_function(UWRAP_LIBC, setgroups);
+       uwrap_bind_symbol_libc(setgroups);
 
-       return uwrap.libc.fns._libc_setgroups(size, list);
+       return uwrap.libc.symbols._libc_setgroups.f(size, list);
 }
 
 #ifdef HAVE_SYSCALL
+DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE
 static long int libc_vsyscall(long int sysno, va_list va)
 {
        long int args[8];
        long int rc;
        int i;
 
-       uwrap_load_lib_function(UWRAP_LIBC, syscall);
+       uwrap_bind_symbol_libc(syscall);
 
        for (i = 0; i < 8; i++) {
                args[i] = va_arg(va, long int);
        }
 
-       rc = uwrap.libc.fns._libc_syscall(sysno,
+       rc = uwrap.libc.symbols._libc_syscall.f(sysno,
                                          args[0],
                                          args[1],
                                          args[2],
@@ -489,224 +640,1089 @@ static long int libc_vsyscall(long int sysno, va_list va)
 }
 #endif
 
+/*
+ * This part is "optimistic".
+ * Thread can ends without pthread_exit call.
+ */
+static void libpthread_pthread_exit(void *retval)
+{
+       uwrap_bind_symbol_libpthread(pthread_exit);
+
+       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;
+       }
+
+       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);
+}
+
+void pthread_exit(void *retval)
+{
+       if (!uid_wrapper_enabled()) {
+               libpthread_pthread_exit(retval);
+       };
+
+       uwrap_pthread_exit(retval);
+
+       /* Calm down gcc warning. */
+       exit(666);
+}
+
+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)
+{
+       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);
+
+       uwrap_tls_id = id;
+
+       return start_routine(arg);
+}
+
+static int uwrap_pthread_create(pthread_t *thread,
+                                const pthread_attr_t *attr,
+                                void *(*start_routine) (void *),
+                                void *arg)
+{
+       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);
+
+       ret = libpthread_pthread_create(thread, attr,
+                                       uwrap_pthread_create_start,
+                                       args);
+       if (ret != 0) {
+               return ret;
+       }
+
+       return ret;
+}
+
+int pthread_create(pthread_t *thread,
+                   const pthread_attr_t *attr,
+                   void *(*start_routine) (void *),
+                   void *arg)
+{
+       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
  *********************************************************/
 
-static struct uwrap_thread *find_uwrap_id(pthread_t tid)
+#define GROUP_STRING_SIZE 16384
+#define GROUP_MAX_COUNT (GROUP_STRING_SIZE / (10 + 1))
+
+/**
+ * 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)
 {
-       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;
 
-       for (id = uwrap.ids; id; id = id->next) {
-               if (pthread_equal(id->tid, tid)) {
-                       return id;
+       /* 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;
+               }
+
+               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;
                }
        }
 
-       return NULL;
+       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 int uwrap_new_id(pthread_t tid, bool do_alloc)
+static void uwrap_thread_prepare(void)
 {
        struct uwrap_thread *id = uwrap_tls_id;
 
-       if (do_alloc) {
-               id = malloc(sizeof(struct uwrap_thread));
-               if (id == NULL) {
-                       UWRAP_LOG(UWRAP_LOG_ERROR, "Unable to allocate memory");
-                       errno = ENOMEM;
-                       return -1;
+       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;
                }
 
-               id->groups = malloc(sizeof(gid_t) * 1);
+               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");
-                       SAFE_FREE(id);
-                       errno = ENOMEM;
-                       return -1;
+                       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);
                }
 
-               UWRAP_DLIST_ADD(uwrap.ids, id);
-               uwrap_tls_id = id;
+               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) {
+                       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 {
+                       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;
        }
 
-       id->tid = tid;
-       id->dead = false;
+       if (egid != (gid_t)-1) {
+               id->egid = egid;
+       }
 
-       id->ruid = id->euid = id->suid = uwrap.myuid;
-       id->rgid = id->egid = id->sgid = uwrap.mygid;
+       if (sgid != (gid_t)-1) {
+               id->sgid = sgid;
+       }
 
-       id->ngroups = 1;
-       id->groups[0] = uwrap.mygid;
+       UWRAP_UNLOCK(uwrap_id);
 
        return 0;
 }
 
-static void uwrap_thread_prepare(void)
+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);
-       UWRAP_LOCK(libc_symbol_binding);
-       /*
-        * 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;
-}
 
-static void uwrap_thread_parent(void)
-{
-       uwrap.enabled = true;
+       for (id = uwrap.ids; id; id = id->next) {
+               if (rgid != (gid_t)-1) {
+                       id->rgid = rgid;
+               }
 
-       UWRAP_UNLOCK(libc_symbol_binding);
-       UWRAP_UNLOCK(uwrap_id);
-}
+               if (egid != (gid_t)-1) {
+                       id->egid = egid;
+               }
 
-static void uwrap_thread_child(void)
-{
-       uwrap.enabled = true;
+               if (sgid != (gid_t)-1) {
+                       id->sgid = sgid;
+               }
+       }
 
-       UWRAP_UNLOCK(libc_symbol_binding);
        UWRAP_UNLOCK(uwrap_id);
+
+       return 0;
 }
 
-static void uwrap_init(void)
+static int uwrap_setregid_args(gid_t rgid, gid_t egid,
+                              gid_t *_new_rgid,
+                              gid_t *_new_egid,
+                              gid_t *_new_sgid)
 {
-       const char *env;
-       pthread_t tid = pthread_self();
+       struct uwrap_thread *id = uwrap_tls_id;
+       gid_t new_rgid = -1, new_egid = -1, new_sgid = -1;
 
-       UWRAP_LOCK(uwrap_id);
-       if (uwrap.initialised) {
-               struct uwrap_thread *id = uwrap_tls_id;
-               int rc;
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "rgid %d -> %d, egid %d -> %d",
+                 id->rgid, rgid, id->egid, egid);
 
-               if (id != NULL) {
-                       UWRAP_UNLOCK(uwrap_id);
-                       return;
+       if (rgid != (gid_t)-1) {
+               new_rgid = rgid;
+               if (rgid != id->rgid &&
+                   rgid != id->egid &&
+                   id->euid != 0) {
+                       errno = EPERM;
+                       return -1;
                }
+       }
 
-               id = find_uwrap_id(tid);
-               if (id == NULL) {
-                       rc = uwrap_new_id(tid, true);
-                       if (rc < 0) {
-                               exit(-1);
-                       }
-               } else {
-                       /* We reuse an old thread id */
-                       uwrap_tls_id = id;
-
-                       uwrap_new_id(tid, false);
+       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_UNLOCK(uwrap_id);
-
-               return;
        }
 
-       UWRAP_LOG(UWRAP_LOG_DEBUG, "Initialize uid_wrapper");
-
-       uwrap.initialised = true;
-       uwrap.enabled = false;
+       if (rgid != (gid_t) -1 ||
+           (egid != (gid_t)-1 && id->rgid != egid)) {
+               new_sgid = new_egid;
+       }
 
-       env = getenv("UID_WRAPPER");
-       if (env != NULL && env[0] == '1') {
-               const char *root = getenv("UID_WRAPPER_ROOT");
-               int rc;
+       *_new_rgid = new_rgid;
+       *_new_egid = new_egid;
+       *_new_sgid = new_sgid;
 
-               /* put us in one group */
-               if (root != NULL && root[0] == '1') {
-                       uwrap.myuid = 0;
-                       uwrap.mygid = 0;
-               } else {
-                       uwrap.myuid = libc_geteuid();
-                       uwrap.mygid = libc_getegid();
-               }
+       return 0;
+}
 
-               rc = uwrap_new_id(tid, true);
-               if (rc < 0) {
-                       exit(-1);
-               }
+static int uwrap_setregid_thread(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;
 
-               uwrap.enabled = true;
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "rgid %d -> %d, egid %d -> %d",
+                 id->rgid, rgid, id->egid, egid);
 
-               UWRAP_LOG(UWRAP_LOG_DEBUG,
-                         "Enabled uid_wrapper as %s",
-                         uwrap.myuid == 0 ? "root" : "user");
+       rc = uwrap_setregid_args(rgid, egid, &new_rgid, &new_egid, &new_sgid);
+       if (rc != 0) {
+               return rc;
        }
 
-       UWRAP_UNLOCK(uwrap_id);
-
-       UWRAP_LOG(UWRAP_LOG_DEBUG, "Succeccfully initialized uid_wrapper");
+       return uwrap_setresgid_thread(new_rgid, new_egid, new_sgid);
 }
 
-bool uid_wrapper_enabled(void)
+#ifdef HAVE_SETREGID
+static int uwrap_setregid(gid_t rgid, gid_t egid)
 {
-       bool enabled = false;
-       #ifdef HAVE_GCC_ATOMIC_BUILTINS
-               __atomic_load(&uwrap.enabled, &enabled, __ATOMIC_RELAXED);
-       #else
-               UWRAP_LOCK(uwrap_id);
-               enabled = uwrap.enabled;
-               UWRAP_UNLOCK(uwrap_id);
-       #endif
-       return enabled;
+       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_setresgid(new_rgid, new_egid, new_sgid);
 }
+#endif
 
-static int uwrap_setresuid_thread(uid_t ruid, uid_t euid, uid_t suid)
+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;
 
-       if (ruid == (uid_t)-1 && euid == (uid_t)-1 && suid == (uid_t)-1) {
+       UWRAP_LOG(UWRAP_LOG_TRACE,
+                 "gid %d -> %d",
+                 id->rgid, gid);
+
+       if (gid == (gid_t)-1) {
                errno = EINVAL;
                return -1;
        }
 
-       UWRAP_LOCK(uwrap_id);
-       if (ruid != (uid_t)-1) {
-               id->ruid = ruid;
-       }
-
-       if (euid != (uid_t)-1) {
-               id->euid = euid;
+       if (id->euid == 0) {
+               *new_sgid = *new_rgid = gid;
+       } else if (gid != id->rgid &&
+                  gid != id->sgid) {
+               errno = EPERM;
+               return -1;
        }
 
-       if (suid != (uid_t)-1) {
-               id->suid = suid;
-       }
-       UWRAP_UNLOCK(uwrap_id);
+       *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;
        }
 
-       UWRAP_LOCK(uwrap_id);
-       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;
        }
-       UWRAP_UNLOCK(uwrap_id);
 
-       return 0;
+       return uwrap_setresgid(new_rgid, new_egid, new_sgid);
 }
 
 /*
@@ -719,21 +1735,22 @@ int setuid(uid_t uid)
        }
 
        uwrap_init();
-       return uwrap_setresuid(uid, -1, -1);
+       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 (!uid_wrapper_enabled()) {
-               return libc_seteuid(euid);
-       }
-
        uwrap_init();
        return uwrap_setresuid(-1, euid, -1);
 }
@@ -742,17 +1759,12 @@ 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 (!uid_wrapper_enabled()) {
                return libc_setreuid(ruid, euid);
        }
 
        uwrap_init();
-       return uwrap_setresuid(ruid, euid, -1);
+       return uwrap_setreuid(ruid, euid);
 }
 #endif
 
@@ -768,6 +1780,18 @@ int setresuid(uid_t ruid, uid_t euid, uid_t 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
  */
@@ -824,64 +1848,6 @@ uid_t geteuid(void)
        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;
-       }
-
-       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;
-
-       if (rgid == (gid_t)-1 && egid == (gid_t)-1 && sgid == (gid_t)-1) {
-               errno = EINVAL;
-               return -1;
-       }
-
-       UWRAP_LOCK(uwrap_id);
-       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;
-               }
-       }
-       UWRAP_UNLOCK(uwrap_id);
-
-       return 0;
-}
-
 /*
  * SETGID
  */
@@ -892,7 +1858,7 @@ int setgid(gid_t gid)
        }
 
        uwrap_init();
-       return uwrap_setresgid(gid, -1, -1);
+       return uwrap_setgid(gid);
 }
 
 #ifdef HAVE_SETEGID
@@ -902,6 +1868,12 @@ int setegid(gid_t egid)
                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);
 }
@@ -915,7 +1887,7 @@ int setregid(gid_t rgid, gid_t egid)
        }
 
        uwrap_init();
-       return uwrap_setresgid(rgid, egid, -1);
+       return uwrap_setregid(rgid, egid);
 }
 #endif
 
@@ -931,6 +1903,18 @@ int setresgid(gid_t rgid, gid_t egid, gid_t 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
  */
@@ -989,8 +1973,7 @@ static int uwrap_setgroups_thread(size_t size, const gid_t *list)
        UWRAP_LOCK(uwrap_id);
 
        if (size == 0) {
-               free(id->groups);
-               id->groups = NULL;
+               SAFE_FREE(id->groups);
                id->ngroups = 0;
        } else if (size > 0) {
                gid_t *tmp;
@@ -1001,7 +1984,6 @@ static int uwrap_setgroups_thread(size_t size, const gid_t *list)
                        goto out;
                }
                id->groups = tmp;
-
                id->ngroups = size;
                memcpy(id->groups, list, size * sizeof(gid_t));
        }
@@ -1022,14 +2004,14 @@ static int uwrap_setgroups(size_t size, const gid_t *list)
 
        if (size == 0) {
                for (id = uwrap.ids; id; id = id->next) {
-                       free(id->groups);
-                       id->groups = NULL;
+                       SAFE_FREE(id->groups);
                        id->ngroups = 0;
+
                }
        } else if (size > 0) {
-               for (id = uwrap.ids; id; id = id->next) {
-                       gid_t *tmp;
+               gid_t *tmp;
 
+               for (id = uwrap.ids; id; id = id->next) {
                        tmp = realloc(id->groups, sizeof(gid_t) * size);
                        if (tmp == NULL) {
                                errno = ENOMEM;
@@ -1107,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
@@ -1130,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:
@@ -1140,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
@@ -1152,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
@@ -1185,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:
@@ -1195,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
@@ -1207,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
@@ -1230,7 +2247,7 @@ static long int uwrap_syscall (long int sysno, va_list vp)
                        break;
                default:
                        UWRAP_LOG(UWRAP_LOG_DEBUG,
-                                 "UID_WRAPPER calling non-wrapped syscall %lu\n",
+                                 "UID_WRAPPER calling non-wrapped syscall %lu",
                                  sysno);
 
                        rc = libc_vsyscall(sysno, vp);
@@ -1303,8 +2320,7 @@ void uwrap_destructor(void)
 {
        struct uwrap_thread *u = uwrap.ids;
 
-       UWRAP_LOCK(uwrap_id);
-       UWRAP_LOCK(libc_symbol_binding);
+       UWRAP_LOCK_ALL;
 
        while (u != NULL) {
                UWRAP_DLIST_REMOVE(uwrap.ids, u);
@@ -1315,10 +2331,14 @@ void uwrap_destructor(void)
                u = uwrap.ids;
        }
 
+
        if (uwrap.libc.handle != NULL) {
                dlclose(uwrap.libc.handle);
        }
 
-       UWRAP_UNLOCK(libc_symbol_binding);
-       UWRAP_UNLOCK(uwrap_id);
+       if (uwrap.libpthread.handle != NULL) {
+               dlclose(uwrap.libpthread.handle);
+       }
+
+       UWRAP_UNLOCK_ALL;
 }