mirror of
https://github.com/samba-team/samba.git
synced 2025-02-01 05:47:28 +03:00
fbdd2d4624
Signed-off-by: Robin Hack <hack.robin@gmail.com> Reviewed-by: Andreas Schneider <asn@samba.org> Reviewed-by: Stefan Metzmacher <metze@samba.org>
1482 lines
27 KiB
C
1482 lines
27 KiB
C
/*
|
|
* Copyright (c) 2009 Andrew Tridgell
|
|
* Copyright (c) 2011-2013 Andreas Schneider <asn@samba.org>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <grp.h>
|
|
#ifdef HAVE_SYS_SYSCALL_H
|
|
#include <sys/syscall.h>
|
|
#endif
|
|
#ifdef HAVE_SYSCALL_H
|
|
#include <syscall.h>
|
|
#endif
|
|
#include <dlfcn.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#ifdef HAVE_GCC_THREAD_LOCAL_STORAGE
|
|
# define UWRAP_THREAD __thread
|
|
#else
|
|
# define UWRAP_THREAD
|
|
#endif
|
|
|
|
# define UWRAP_LOCK(m) do { \
|
|
pthread_mutex_lock(&( m ## _mutex)); \
|
|
} while(0)
|
|
|
|
# define UWRAP_UNLOCK(m) do { \
|
|
pthread_mutex_unlock(&( m ## _mutex)); \
|
|
} while(0)
|
|
|
|
#ifdef HAVE_CONSTRUCTOR_ATTRIBUTE
|
|
#define CONSTRUCTOR_ATTRIBUTE __attribute__ ((constructor))
|
|
#else
|
|
#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)->prev = NULL; \
|
|
(item)->next = NULL; \
|
|
(list) = (item); \
|
|
} else { \
|
|
(item)->prev = NULL; \
|
|
(item)->next = (list); \
|
|
(list)->prev = (item); \
|
|
(list) = (item); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define UWRAP_DLIST_REMOVE(list,item) do { \
|
|
if ((list) == (item)) { \
|
|
(list) = (item)->next; \
|
|
if (list) { \
|
|
(list)->prev = NULL; \
|
|
} \
|
|
} else { \
|
|
if ((item)->prev) { \
|
|
(item)->prev->next = (item)->next; \
|
|
} \
|
|
if ((item)->next) { \
|
|
(item)->next->prev = (item)->prev; \
|
|
} \
|
|
} \
|
|
(item)->prev = NULL; \
|
|
(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
|
|
};
|
|
|
|
#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 *format, ...)
|
|
{
|
|
char buffer[1024];
|
|
va_list va;
|
|
const char *d;
|
|
unsigned int lvl = 0;
|
|
|
|
d = getenv("UID_WRAPPER_DEBUGLEVEL");
|
|
if (d != NULL) {
|
|
lvl = atoi(d);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
#endif /* NDEBUG */
|
|
|
|
/*****************
|
|
* LIBC
|
|
*****************/
|
|
|
|
#define LIBC_NAME "libc.so"
|
|
|
|
struct uwrap_libc_fns {
|
|
int (*_libc_setuid)(uid_t uid);
|
|
uid_t (*_libc_getuid)(void);
|
|
|
|
#ifdef HAVE_SETEUID
|
|
int (*_libc_seteuid)(uid_t euid);
|
|
#endif
|
|
#ifdef HAVE_SETREUID
|
|
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);
|
|
#endif
|
|
uid_t (*_libc_geteuid)(void);
|
|
|
|
int (*_libc_setgid)(gid_t gid);
|
|
gid_t (*_libc_getgid)(void);
|
|
#ifdef HAVE_SETEGID
|
|
int (*_libc_setegid)(uid_t egid);
|
|
#endif
|
|
#ifdef HAVE_SETREGID
|
|
int (*_libc_setregid)(uid_t rgid, uid_t egid);
|
|
#endif
|
|
#ifdef HAVE_SETRESGID
|
|
int (*_libc_setresgid)(uid_t rgid, uid_t egid, uid_t sgid);
|
|
#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_SYSCALL
|
|
long int (*_libc_syscall)(long int sysno, ...);
|
|
#endif
|
|
};
|
|
|
|
/*
|
|
* We keep the virtualised euid/egid/groups information here
|
|
*/
|
|
struct uwrap_thread {
|
|
pthread_t tid;
|
|
bool dead;
|
|
|
|
uid_t ruid;
|
|
uid_t euid;
|
|
uid_t suid;
|
|
|
|
gid_t rgid;
|
|
gid_t egid;
|
|
gid_t sgid;
|
|
|
|
int ngroups;
|
|
gid_t *groups;
|
|
|
|
struct uwrap_thread *next;
|
|
struct uwrap_thread *prev;
|
|
};
|
|
|
|
struct uwrap {
|
|
pthread_t tid;
|
|
|
|
struct {
|
|
void *handle;
|
|
struct uwrap_libc_fns fns;
|
|
} libc;
|
|
|
|
bool initialised;
|
|
bool enabled;
|
|
|
|
uid_t ruid;
|
|
uid_t euid;
|
|
uid_t suid;
|
|
|
|
gid_t rgid;
|
|
gid_t egid;
|
|
gid_t sgid;
|
|
|
|
int ngroups;
|
|
gid_t *groups;
|
|
|
|
/* Real uid and gid of user who run uid wrapper */
|
|
uid_t myuid;
|
|
gid_t mygid;
|
|
|
|
struct uwrap_thread *ids;
|
|
};
|
|
|
|
static struct uwrap uwrap;
|
|
|
|
/* Shortcut to the list item */
|
|
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 */
|
|
static pthread_mutex_t libc_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
|
|
*********************************************************/
|
|
|
|
enum uwrap_lib {
|
|
UWRAP_LIBC,
|
|
UWRAP_LIBNSL,
|
|
UWRAP_LIBSOCKET,
|
|
};
|
|
|
|
static void *uwrap_load_lib_handle(enum uwrap_lib lib)
|
|
{
|
|
int flags = RTLD_LAZY;
|
|
void *handle = NULL;
|
|
int i;
|
|
|
|
#ifdef RTLD_DEEPBIND
|
|
flags |= RTLD_DEEPBIND;
|
|
#endif
|
|
|
|
switch (lib) {
|
|
case UWRAP_LIBNSL:
|
|
/* FALL TROUGH */
|
|
case UWRAP_LIBSOCKET:
|
|
/* FALL TROUGH */
|
|
case UWRAP_LIBC:
|
|
handle = uwrap.libc.handle;
|
|
if (handle == NULL) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
uwrap.libc.handle = handle;
|
|
}
|
|
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)
|
|
{
|
|
void *handle;
|
|
void *func;
|
|
|
|
handle = uwrap_load_lib_handle(lib);
|
|
|
|
func = dlsym(handle, fn_name);
|
|
if (func == NULL) {
|
|
fprintf(stderr,
|
|
"Failed to find %s: %s\n",
|
|
fn_name, dlerror());
|
|
exit(-1);
|
|
}
|
|
|
|
return func;
|
|
}
|
|
|
|
#define uwrap_load_lib_function(lib, fn_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); \
|
|
} \
|
|
UWRAP_UNLOCK(libc_symbol_binding)
|
|
|
|
/*
|
|
* IMPORTANT
|
|
*
|
|
* Functions expeciall from libc need to be loaded individually, you can't load
|
|
* all at once or gdb will segfault at startup. The same applies to valgrind and
|
|
* has probably something todo with with the linker.
|
|
* So we need load each function at the point it is called the first time.
|
|
*/
|
|
static int libc_setuid(uid_t uid)
|
|
{
|
|
uwrap_load_lib_function(UWRAP_LIBC, setuid);
|
|
|
|
return uwrap.libc.fns._libc_setuid(uid);
|
|
}
|
|
|
|
static uid_t libc_getuid(void)
|
|
{
|
|
uwrap_load_lib_function(UWRAP_LIBC, getuid);
|
|
|
|
return uwrap.libc.fns._libc_getuid();
|
|
}
|
|
|
|
#ifdef HAVE_SETEUID
|
|
static int libc_seteuid(uid_t euid)
|
|
{
|
|
uwrap_load_lib_function(UWRAP_LIBC, seteuid);
|
|
|
|
return uwrap.libc.fns._libc_seteuid(euid);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_SETREUID
|
|
static int libc_setreuid(uid_t ruid, uid_t euid)
|
|
{
|
|
uwrap_load_lib_function(UWRAP_LIBC, setreuid);
|
|
|
|
return uwrap.libc.fns._libc_setreuid(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);
|
|
|
|
return uwrap.libc.fns._libc_setresuid(ruid, euid, suid);
|
|
}
|
|
#endif
|
|
|
|
static uid_t libc_geteuid(void)
|
|
{
|
|
uwrap_load_lib_function(UWRAP_LIBC, geteuid);
|
|
|
|
return uwrap.libc.fns._libc_geteuid();
|
|
}
|
|
|
|
static int libc_setgid(gid_t gid)
|
|
{
|
|
uwrap_load_lib_function(UWRAP_LIBC, setgid);
|
|
|
|
return uwrap.libc.fns._libc_setgid(gid);
|
|
}
|
|
|
|
static gid_t libc_getgid(void)
|
|
{
|
|
uwrap_load_lib_function(UWRAP_LIBC, getgid);
|
|
|
|
return uwrap.libc.fns._libc_getgid();
|
|
}
|
|
|
|
#ifdef HAVE_SETEGID
|
|
static int libc_setegid(gid_t egid)
|
|
{
|
|
uwrap_load_lib_function(UWRAP_LIBC, setegid);
|
|
|
|
return uwrap.libc.fns._libc_setegid(egid);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_SETREGID
|
|
static int libc_setregid(gid_t rgid, gid_t egid)
|
|
{
|
|
uwrap_load_lib_function(UWRAP_LIBC, setregid);
|
|
|
|
return uwrap.libc.fns._libc_setregid(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);
|
|
|
|
return uwrap.libc.fns._libc_setresgid(rgid, egid, sgid);
|
|
}
|
|
#endif
|
|
|
|
static gid_t libc_getegid(void)
|
|
{
|
|
uwrap_load_lib_function(UWRAP_LIBC, getegid);
|
|
|
|
return uwrap.libc.fns._libc_getegid();
|
|
}
|
|
|
|
static int libc_getgroups(int size, gid_t list[])
|
|
{
|
|
uwrap_load_lib_function(UWRAP_LIBC, getgroups);
|
|
|
|
return uwrap.libc.fns._libc_getgroups(size, list);
|
|
}
|
|
|
|
static int libc_setgroups(size_t size, const gid_t *list)
|
|
{
|
|
uwrap_load_lib_function(UWRAP_LIBC, setgroups);
|
|
|
|
return uwrap.libc.fns._libc_setgroups(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);
|
|
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*********************************************************
|
|
* UWRAP ID HANDLING
|
|
*********************************************************/
|
|
|
|
static struct uwrap_thread *find_uwrap_id(pthread_t tid)
|
|
{
|
|
struct uwrap_thread *id;
|
|
|
|
for (id = uwrap.ids; id; id = id->next) {
|
|
if (pthread_equal(id->tid, tid)) {
|
|
return id;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int uwrap_new_id(pthread_t tid, bool do_alloc)
|
|
{
|
|
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;
|
|
}
|
|
|
|
id->groups = malloc(sizeof(gid_t) * uwrap.ngroups);
|
|
if (id->groups == NULL) {
|
|
UWRAP_LOG(UWRAP_LOG_ERROR, "Unable to allocate memory");
|
|
SAFE_FREE(id);
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
UWRAP_DLIST_ADD(uwrap.ids, id);
|
|
uwrap_tls_id = id;
|
|
}
|
|
|
|
id->tid = tid;
|
|
id->dead = false;
|
|
|
|
id->ruid = uwrap.ruid;
|
|
id->euid = uwrap.euid;
|
|
id->suid = uwrap.suid;
|
|
|
|
id->rgid = uwrap.rgid;
|
|
id->egid = uwrap.egid;
|
|
id->sgid = uwrap.sgid;
|
|
|
|
id->ngroups = uwrap.ngroups;
|
|
if (uwrap.groups != NULL) {
|
|
memcpy(id->groups, uwrap.groups, sizeof(gid_t) * uwrap.ngroups);
|
|
} else {
|
|
id->groups = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void uwrap_thread_prepare(void)
|
|
{
|
|
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;
|
|
|
|
UWRAP_UNLOCK(libc_symbol_binding);
|
|
UWRAP_UNLOCK(uwrap_id);
|
|
}
|
|
|
|
static void uwrap_thread_child(void)
|
|
{
|
|
uwrap.enabled = true;
|
|
|
|
/* We need to update to the new tid if we fork */
|
|
uwrap.tid = pthread_self();
|
|
|
|
UWRAP_UNLOCK(libc_symbol_binding);
|
|
UWRAP_UNLOCK(uwrap_id);
|
|
}
|
|
|
|
static void uwrap_init(void)
|
|
{
|
|
const char *env;
|
|
pthread_t tid = pthread_self();
|
|
|
|
UWRAP_LOCK(uwrap_id);
|
|
if (uwrap.initialised) {
|
|
struct uwrap_thread *id = uwrap_tls_id;
|
|
int rc;
|
|
|
|
if (id != NULL) {
|
|
UWRAP_UNLOCK(uwrap_id);
|
|
return;
|
|
}
|
|
|
|
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);
|
|
}
|
|
UWRAP_UNLOCK(uwrap_id);
|
|
|
|
return;
|
|
}
|
|
|
|
UWRAP_LOG(UWRAP_LOG_DEBUG, "Initialize uid_wrapper");
|
|
|
|
uwrap.initialised = true;
|
|
uwrap.enabled = false;
|
|
|
|
env = getenv("UID_WRAPPER");
|
|
if (env != NULL && env[0] == '1') {
|
|
const char *root = getenv("UID_WRAPPER_ROOT");
|
|
int rc;
|
|
|
|
uwrap.myuid = libc_geteuid();
|
|
uwrap.mygid = libc_getegid();
|
|
/* put us in one group */
|
|
if (root != NULL && root[0] == '1') {
|
|
uwrap.ruid = uwrap.euid = uwrap.suid = 0;
|
|
uwrap.rgid = uwrap.egid = uwrap.sgid = 0;
|
|
|
|
uwrap.groups = malloc(sizeof(gid_t) * 1);
|
|
if (uwrap.groups == NULL) {
|
|
UWRAP_LOG(UWRAP_LOG_ERROR,
|
|
"Unable to allocate memory");
|
|
exit(-1);
|
|
}
|
|
|
|
uwrap.ngroups = 1;
|
|
uwrap.groups[0] = 0;
|
|
|
|
} else {
|
|
uwrap.ruid = uwrap.euid = uwrap.suid = uwrap.myuid;
|
|
uwrap.rgid = uwrap.egid = uwrap.sgid = uwrap.mygid;
|
|
|
|
uwrap.ngroups = libc_getgroups(0, NULL);
|
|
if (uwrap.ngroups == -1) {
|
|
UWRAP_LOG(UWRAP_LOG_ERROR,
|
|
"Unable to call libc_getgroups in uwrap_init.");
|
|
exit(-1);
|
|
}
|
|
uwrap.groups = malloc(sizeof(gid_t) * uwrap.ngroups);
|
|
if (uwrap.groups == NULL) {
|
|
UWRAP_LOG(UWRAP_LOG_ERROR, "Unable to allocate memory");
|
|
exit(-1);
|
|
}
|
|
if (libc_getgroups(uwrap.ngroups, uwrap.groups) == -1) {
|
|
UWRAP_LOG(UWRAP_LOG_ERROR,
|
|
"Unable to call libc_getgroups again in uwrap_init.");
|
|
uwrap.ngroups = 0;
|
|
/*
|
|
* Deallocation of uwrap.groups is handled by
|
|
* library destructor.
|
|
*/
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
rc = uwrap_new_id(tid, true);
|
|
if (rc < 0) {
|
|
exit(-1);
|
|
}
|
|
|
|
uwrap.tid = tid;
|
|
uwrap.enabled = true;
|
|
|
|
UWRAP_LOG(UWRAP_LOG_DEBUG,
|
|
"Enabled uid_wrapper as %s",
|
|
uwrap.myuid == 0 ? "root" : "user");
|
|
}
|
|
|
|
UWRAP_UNLOCK(uwrap_id);
|
|
|
|
UWRAP_LOG(UWRAP_LOG_DEBUG, "Succeccfully initialized uid_wrapper");
|
|
}
|
|
|
|
bool uid_wrapper_enabled(void)
|
|
{
|
|
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;
|
|
}
|
|
|
|
static int uwrap_setresuid_thread(uid_t ruid, uid_t euid, uid_t suid)
|
|
{
|
|
struct uwrap_thread *id = uwrap_tls_id;
|
|
|
|
if (ruid == (uid_t)-1 && euid == (uid_t)-1 && suid == (uid_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 (suid != (uid_t)-1) {
|
|
id->suid = suid;
|
|
}
|
|
|
|
/* Check If syscall is called from main thread. */
|
|
if (pthread_equal(id->tid, uwrap.tid)) {
|
|
if (ruid != (uid_t)-1) {
|
|
uwrap.ruid = ruid;
|
|
}
|
|
|
|
if (euid != (uid_t)-1) {
|
|
uwrap.euid = euid;
|
|
}
|
|
|
|
if (suid != (uid_t)-1) {
|
|
uwrap.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;
|
|
|
|
if (ruid == (uid_t)-1 && euid == (uid_t)-1 && suid == (uid_t)-1) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
UWRAP_LOCK(uwrap_id);
|
|
for (id = uwrap.ids; id; id = id->next) {
|
|
if (id->dead) {
|
|
continue;
|
|
}
|
|
|
|
if (ruid != (uid_t)-1) {
|
|
id->ruid = ruid;
|
|
}
|
|
|
|
if (euid != (uid_t)-1) {
|
|
id->euid = euid;
|
|
}
|
|
|
|
if (suid != (uid_t)-1) {
|
|
id->suid = suid;
|
|
}
|
|
}
|
|
|
|
/* Reflect changes in thread to main process. */
|
|
if (ruid != (uid_t)-1) {
|
|
uwrap.ruid = ruid;
|
|
}
|
|
|
|
if (euid != (uid_t)-1) {
|
|
uwrap.euid = euid;
|
|
}
|
|
|
|
if (suid != (uid_t)-1) {
|
|
uwrap.suid = suid;
|
|
}
|
|
|
|
UWRAP_UNLOCK(uwrap_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* SETUID
|
|
*/
|
|
int setuid(uid_t uid)
|
|
{
|
|
if (!uid_wrapper_enabled()) {
|
|
return libc_setuid(uid);
|
|
}
|
|
|
|
uwrap_init();
|
|
return uwrap_setresuid(uid, -1, -1);
|
|
}
|
|
|
|
#ifdef HAVE_SETEUID
|
|
int seteuid(uid_t euid)
|
|
{
|
|
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);
|
|
}
|
|
#endif
|
|
|
|
#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);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_SETRESUID
|
|
int setresuid(uid_t ruid, uid_t euid, uid_t suid)
|
|
{
|
|
if (!uid_wrapper_enabled()) {
|
|
return libc_setresuid(ruid, euid, suid);
|
|
}
|
|
|
|
uwrap_init();
|
|
return uwrap_setresuid(ruid, euid, suid);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* GETUID
|
|
*/
|
|
static uid_t uwrap_getuid(void)
|
|
{
|
|
struct uwrap_thread *id = uwrap_tls_id;
|
|
uid_t uid;
|
|
|
|
UWRAP_LOCK(uwrap_id);
|
|
uid = id->ruid;
|
|
UWRAP_UNLOCK(uwrap_id);
|
|
|
|
return uid;
|
|
}
|
|
|
|
uid_t getuid(void)
|
|
{
|
|
if (!uid_wrapper_enabled()) {
|
|
return libc_getuid();
|
|
}
|
|
|
|
uwrap_init();
|
|
return uwrap_getuid();
|
|
}
|
|
|
|
/*
|
|
* GETEUID
|
|
*/
|
|
static uid_t uwrap_geteuid(void)
|
|
{
|
|
const char *env = getenv("UID_WRAPPER_MYUID");
|
|
struct uwrap_thread *id = uwrap_tls_id;
|
|
uid_t uid;
|
|
|
|
UWRAP_LOCK(uwrap_id);
|
|
uid = id->euid;
|
|
UWRAP_UNLOCK(uwrap_id);
|
|
|
|
/* Disable root and return myuid */
|
|
if (env != NULL && env[0] == '1') {
|
|
uid = uwrap.myuid;
|
|
}
|
|
|
|
return uid;
|
|
}
|
|
|
|
uid_t geteuid(void)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (pthread_equal(id->tid, uwrap.tid)) {
|
|
if (rgid != (gid_t)-1) {
|
|
uwrap.rgid = rgid;
|
|
}
|
|
|
|
if (egid != (gid_t)-1) {
|
|
uwrap.egid = egid;
|
|
}
|
|
|
|
if (sgid != (gid_t)-1) {
|
|
uwrap.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;
|
|
}
|
|
}
|
|
|
|
/* Reflect changes in thread to main process. */
|
|
if (rgid != (gid_t)-1) {
|
|
uwrap.rgid = rgid;
|
|
}
|
|
|
|
if (egid != (gid_t)-1) {
|
|
uwrap.egid = egid;
|
|
}
|
|
|
|
if (sgid != (gid_t)-1) {
|
|
uwrap.sgid = sgid;
|
|
}
|
|
UWRAP_UNLOCK(uwrap_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* SETGID
|
|
*/
|
|
int setgid(gid_t gid)
|
|
{
|
|
if (!uid_wrapper_enabled()) {
|
|
return libc_setgid(gid);
|
|
}
|
|
|
|
uwrap_init();
|
|
return uwrap_setresgid(gid, -1, -1);
|
|
}
|
|
|
|
#ifdef HAVE_SETEGID
|
|
int setegid(gid_t egid)
|
|
{
|
|
if (!uid_wrapper_enabled()) {
|
|
return libc_setegid(egid);
|
|
}
|
|
|
|
uwrap_init();
|
|
return uwrap_setresgid(-1, egid, -1);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_SETREGID
|
|
int setregid(gid_t rgid, gid_t egid)
|
|
{
|
|
if (!uid_wrapper_enabled()) {
|
|
return libc_setregid(rgid, egid);
|
|
}
|
|
|
|
uwrap_init();
|
|
return uwrap_setresgid(rgid, egid, -1);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_SETRESGID
|
|
int setresgid(gid_t rgid, gid_t egid, gid_t sgid)
|
|
{
|
|
if (!uid_wrapper_enabled()) {
|
|
return libc_setresgid(rgid, egid, sgid);
|
|
}
|
|
|
|
uwrap_init();
|
|
return uwrap_setresgid(rgid, egid, sgid);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* GETGID
|
|
*/
|
|
static gid_t uwrap_getgid(void)
|
|
{
|
|
struct uwrap_thread *id = uwrap_tls_id;
|
|
gid_t gid;
|
|
|
|
UWRAP_LOCK(uwrap_id);
|
|
gid = id->rgid;
|
|
UWRAP_UNLOCK(uwrap_id);
|
|
|
|
return gid;
|
|
}
|
|
|
|
gid_t getgid(void)
|
|
{
|
|
if (!uid_wrapper_enabled()) {
|
|
return libc_getgid();
|
|
}
|
|
|
|
uwrap_init();
|
|
return uwrap_getgid();
|
|
}
|
|
|
|
/*
|
|
* GETEGID
|
|
*/
|
|
static uid_t uwrap_getegid(void)
|
|
{
|
|
struct uwrap_thread *id = uwrap_tls_id;
|
|
gid_t gid;
|
|
|
|
UWRAP_LOCK(uwrap_id);
|
|
gid = id->egid;
|
|
UWRAP_UNLOCK(uwrap_id);
|
|
|
|
return gid;
|
|
}
|
|
|
|
uid_t getegid(void)
|
|
{
|
|
if (!uid_wrapper_enabled()) {
|
|
return libc_getegid();
|
|
}
|
|
|
|
uwrap_init();
|
|
return uwrap_getegid();
|
|
}
|
|
|
|
static int uwrap_setgroups_thread(size_t size, const gid_t *list)
|
|
{
|
|
struct uwrap_thread *id = uwrap_tls_id;
|
|
int rc = -1;
|
|
|
|
UWRAP_LOCK(uwrap_id);
|
|
|
|
if (size == 0) {
|
|
SAFE_FREE(id->groups);
|
|
id->ngroups = 0;
|
|
if (pthread_equal(id->tid, uwrap.tid)) {
|
|
SAFE_FREE(uwrap.groups);
|
|
uwrap.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));
|
|
|
|
if (pthread_equal(id->tid, uwrap.tid)) {
|
|
tmp = realloc(uwrap.groups, sizeof(gid_t) * size);
|
|
if (tmp == NULL) {
|
|
/* How to return back id->groups? */
|
|
errno = ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
uwrap.groups = tmp;
|
|
uwrap.ngroups = size;
|
|
memcpy(uwrap.groups, list, size * sizeof(gid_t));
|
|
}
|
|
}
|
|
|
|
rc = 0;
|
|
out:
|
|
UWRAP_UNLOCK(uwrap_id);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int uwrap_setgroups(size_t size, const gid_t *list)
|
|
{
|
|
struct uwrap_thread *id;
|
|
int rc = -1;
|
|
|
|
UWRAP_LOCK(uwrap_id);
|
|
|
|
if (size == 0) {
|
|
for (id = uwrap.ids; id; id = id->next) {
|
|
SAFE_FREE(id->groups);
|
|
id->ngroups = 0;
|
|
|
|
}
|
|
SAFE_FREE(uwrap.groups);
|
|
uwrap.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));
|
|
}
|
|
|
|
tmp = realloc(uwrap.groups, sizeof(gid_t) * size);
|
|
if (tmp == NULL) {
|
|
/* How to return back id->groups? */
|
|
errno = ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
uwrap.groups = tmp;
|
|
uwrap.ngroups = size;
|
|
memcpy(uwrap.groups, list, size * sizeof(gid_t));
|
|
|
|
}
|
|
|
|
rc = 0;
|
|
out:
|
|
UWRAP_UNLOCK(uwrap_id);
|
|
|
|
return rc;
|
|
}
|
|
|
|
#ifdef HAVE_SETGROUPS_INT
|
|
int setgroups(int size, const gid_t *list)
|
|
#else
|
|
int setgroups(size_t size, const gid_t *list)
|
|
#endif
|
|
{
|
|
if (!uid_wrapper_enabled()) {
|
|
return libc_setgroups(size, list);
|
|
}
|
|
|
|
uwrap_init();
|
|
return uwrap_setgroups(size, list);
|
|
}
|
|
|
|
static int uwrap_getgroups(int size, gid_t *list)
|
|
{
|
|
struct uwrap_thread *id = uwrap_tls_id;
|
|
int ngroups;
|
|
|
|
UWRAP_LOCK(uwrap_id);
|
|
ngroups = id->ngroups;
|
|
|
|
if (size > ngroups) {
|
|
size = ngroups;
|
|
}
|
|
if (size == 0) {
|
|
goto out;
|
|
}
|
|
if (size < ngroups) {
|
|
errno = EINVAL;
|
|
ngroups = -1;
|
|
}
|
|
memcpy(list, id->groups, size * sizeof(gid_t));
|
|
|
|
out:
|
|
UWRAP_UNLOCK(uwrap_id);
|
|
|
|
return ngroups;
|
|
}
|
|
|
|
int getgroups(int size, gid_t *list)
|
|
{
|
|
if (!uid_wrapper_enabled()) {
|
|
return libc_getgroups(size, list);
|
|
}
|
|
|
|
uwrap_init();
|
|
return uwrap_getgroups(size, list);
|
|
}
|
|
|
|
#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)
|
|
{
|
|
long int rc;
|
|
|
|
switch (sysno) {
|
|
/* gid */
|
|
case SYS_getgid:
|
|
#ifdef HAVE_LINUX_32BIT_SYSCALLS
|
|
case SYS_getgid32:
|
|
#endif
|
|
{
|
|
rc = uwrap_getgid();
|
|
}
|
|
break;
|
|
#ifdef SYS_getegid
|
|
case SYS_getegid:
|
|
#ifdef HAVE_LINUX_32BIT_SYSCALLS
|
|
case SYS_getegid32:
|
|
#endif
|
|
{
|
|
rc = uwrap_getegid();
|
|
}
|
|
break;
|
|
#endif /* SYS_getegid */
|
|
case SYS_setgid:
|
|
#ifdef HAVE_LINUX_32BIT_SYSCALLS
|
|
case SYS_setgid32:
|
|
#endif
|
|
{
|
|
gid_t gid = (gid_t) va_arg(vp, int);
|
|
|
|
rc = uwrap_setresgid_thread(gid, -1, -1);
|
|
}
|
|
break;
|
|
case SYS_setregid:
|
|
#ifdef HAVE_LINUX_32BIT_SYSCALLS
|
|
case SYS_setregid32:
|
|
#endif
|
|
{
|
|
uid_t rgid = (uid_t) va_arg(vp, int);
|
|
uid_t egid = (uid_t) va_arg(vp, int);
|
|
|
|
rc = uwrap_setresgid_thread(rgid, egid, -1);
|
|
}
|
|
break;
|
|
#ifdef SYS_setresgid
|
|
case SYS_setresgid:
|
|
#ifdef HAVE_LINUX_32BIT_SYSCALLS
|
|
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);
|
|
|
|
rc = uwrap_setresgid_thread(rgid, egid, sgid);
|
|
}
|
|
break;
|
|
#endif /* SYS_setresgid */
|
|
|
|
/* uid */
|
|
case SYS_getuid:
|
|
#ifdef HAVE_LINUX_32BIT_SYSCALLS
|
|
case SYS_getuid32:
|
|
#endif
|
|
{
|
|
rc = uwrap_getuid();
|
|
}
|
|
break;
|
|
#ifdef SYS_geteuid
|
|
case SYS_geteuid:
|
|
#ifdef HAVE_LINUX_32BIT_SYSCALLS
|
|
case SYS_geteuid32:
|
|
#endif
|
|
{
|
|
rc = uwrap_geteuid();
|
|
}
|
|
break;
|
|
#endif /* SYS_geteuid */
|
|
case SYS_setuid:
|
|
#ifdef HAVE_LINUX_32BIT_SYSCALLS
|
|
case SYS_setuid32:
|
|
#endif
|
|
{
|
|
uid_t uid = (uid_t) va_arg(vp, int);
|
|
|
|
rc = uwrap_setresuid_thread(uid, -1, -1);
|
|
}
|
|
break;
|
|
case SYS_setreuid:
|
|
#ifdef HAVE_LINUX_32BIT_SYSCALLS
|
|
case SYS_setreuid32:
|
|
#endif
|
|
{
|
|
uid_t ruid = (uid_t) va_arg(vp, int);
|
|
uid_t euid = (uid_t) va_arg(vp, int);
|
|
|
|
rc = uwrap_setresuid_thread(ruid, euid, -1);
|
|
}
|
|
break;
|
|
#ifdef SYS_setresuid
|
|
case SYS_setresuid:
|
|
#ifdef HAVE_LINUX_32BIT_SYSCALLS
|
|
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);
|
|
|
|
rc = uwrap_setresuid_thread(ruid, euid, suid);
|
|
}
|
|
break;
|
|
#endif /* SYS_setresuid */
|
|
|
|
/* groups */
|
|
case SYS_setgroups:
|
|
#ifdef HAVE_LINUX_32BIT_SYSCALLS
|
|
case SYS_setgroups32:
|
|
#endif
|
|
{
|
|
size_t size = (size_t) va_arg(vp, size_t);
|
|
gid_t *list = (gid_t *) va_arg(vp, int *);
|
|
|
|
rc = uwrap_setgroups_thread(size, list);
|
|
}
|
|
break;
|
|
default:
|
|
UWRAP_LOG(UWRAP_LOG_DEBUG,
|
|
"UID_WRAPPER calling non-wrapped syscall %lu\n",
|
|
sysno);
|
|
|
|
rc = libc_vsyscall(sysno, vp);
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
#ifdef HAVE_SYSCALL
|
|
#ifdef HAVE_SYSCALL_INT
|
|
int syscall (int sysno, ...)
|
|
#else
|
|
long int syscall (long int sysno, ...)
|
|
#endif
|
|
{
|
|
#ifdef HAVE_SYSCALL_INT
|
|
int rc;
|
|
#else
|
|
long int rc;
|
|
#endif
|
|
va_list va;
|
|
|
|
va_start(va, sysno);
|
|
|
|
if (!uid_wrapper_enabled()) {
|
|
rc = libc_vsyscall(sysno, va);
|
|
va_end(va);
|
|
return rc;
|
|
}
|
|
|
|
uwrap_init();
|
|
rc = uwrap_syscall(sysno, va);
|
|
va_end(va);
|
|
|
|
return rc;
|
|
}
|
|
#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(uwrap_id);
|
|
UWRAP_LOCK(libc_symbol_binding);
|
|
|
|
while (u != NULL) {
|
|
UWRAP_DLIST_REMOVE(uwrap.ids, u);
|
|
|
|
SAFE_FREE(u->groups);
|
|
SAFE_FREE(u);
|
|
|
|
u = uwrap.ids;
|
|
}
|
|
|
|
SAFE_FREE(uwrap.groups);
|
|
|
|
if (uwrap.libc.handle != NULL) {
|
|
dlclose(uwrap.libc.handle);
|
|
}
|
|
|
|
UWRAP_UNLOCK(libc_symbol_binding);
|
|
UWRAP_UNLOCK(uwrap_id);
|
|
}
|