/* * Copyright (c) 2009 Andrew Tridgell * Copyright (c) 2011-2013 Andreas Schneider * * 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 . */ #include "config.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SYSCALL_H #include #endif #ifdef HAVE_SYSCALL_H #include #endif #include #include #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) /* 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 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" typedef int (*__libc_setuid)(uid_t uid); typedef uid_t (*__libc_getuid)(void); #ifdef HAVE_SETEUID typedef int (*__libc_seteuid)(uid_t euid); #endif #ifdef HAVE_SETREUID typedef int (*__libc_setreuid)(uid_t ruid, uid_t euid); #endif #ifdef HAVE_SETRESUID typedef int (*__libc_setresuid)(uid_t ruid, uid_t euid, uid_t suid); #endif #ifdef HAVE_GETRESUID typedef int (*__libc_getresuid)(uid_t *ruid, uid_t *euid, uid_t *suid); #endif 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 #define UWRAP_SYMBOL_ENTRY(i) \ union { \ __libc_##i f; \ void *obj; \ } _libc_##i struct uwrap_libc_symbols { UWRAP_SYMBOL_ENTRY(setuid); UWRAP_SYMBOL_ENTRY(getuid); #ifdef HAVE_SETEUID UWRAP_SYMBOL_ENTRY(seteuid); #endif #ifdef HAVE_SETREUID UWRAP_SYMBOL_ENTRY(setreuid); #endif #ifdef HAVE_SETRESUID UWRAP_SYMBOL_ENTRY(setresuid); #endif #ifdef HAVE_GETRESUID UWRAP_SYMBOL_ENTRY(getresuid); #endif UWRAP_SYMBOL_ENTRY(geteuid); UWRAP_SYMBOL_ENTRY(setgid); UWRAP_SYMBOL_ENTRY(getgid); #ifdef HAVE_SETEGID UWRAP_SYMBOL_ENTRY(setegid); #endif #ifdef HAVE_SETREGID UWRAP_SYMBOL_ENTRY(setregid); #endif #ifdef HAVE_SETRESGID UWRAP_SYMBOL_ENTRY(setresgid); #endif #ifdef HAVE_GETRESGID UWRAP_SYMBOL_ENTRY(getresgid); #endif UWRAP_SYMBOL_ENTRY(getegid); UWRAP_SYMBOL_ENTRY(getgroups); UWRAP_SYMBOL_ENTRY(setgroups); #ifdef HAVE_SYSCALL 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 { 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; struct uwrap_thread *next; struct uwrap_thread *prev; }; struct uwrap { struct { void *handle; struct uwrap_libc_symbols symbols; } libc; struct { void *handle; struct uwrap_libpthread_symbols symbols; } libpthread; bool initialised; /* 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.symbols */ static pthread_mutex_t libc_symbol_binding_mutex = PTHREAD_MUTEX_INITIALIZER; /* The mutex for accessing the global libpthread.symbols */ static pthread_mutex_t libpthread_symbol_binding_mutex = PTHREAD_MUTEX_INITIALIZER; /********************************************************* * UWRAP PROTOTYPES *********************************************************/ bool uid_wrapper_enabled(void); void uwrap_constructor(void) CONSTRUCTOR_ATTRIBUTE; void uwrap_destructor(void) DESTRUCTOR_ATTRIBUTE; /********************************************************* * UWRAP LIBC LOADER FUNCTIONS *********************************************************/ enum uwrap_lib { UWRAP_LIBC, UWRAP_LIBNSL, UWRAP_LIBSOCKET, UWRAP_LIBPTHREAD, }; 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; case UWRAP_LIBPTHREAD: handle = uwrap.libpthread.handle; if (handle == NULL) { handle = dlopen("libpthread.so.0", flags); if (handle != NULL) { break; } } break; } if (handle == NULL) { #ifdef RTLD_NEXT handle = uwrap.libc.handle = RTLD_NEXT; #else fprintf(stderr, "Failed to dlopen library: %s\n", dlerror()); exit(-1); #endif } return handle; } static void *_uwrap_bind_symbol(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_bind_symbol_libc(sym_name) \ UWRAP_LOCK(libc_symbol_binding); \ if (uwrap.libc.symbols._libc_##sym_name.obj == NULL) { \ uwrap.libc.symbols._libc_##sym_name.obj = \ _uwrap_bind_symbol(UWRAP_LIBC, #sym_name); \ } \ UWRAP_UNLOCK(libc_symbol_binding) #define uwrap_bind_symbol_libpthread(sym_name) \ UWRAP_LOCK(libpthread_symbol_binding); \ if (uwrap.libpthread.symbols._libpthread_##sym_name.obj == NULL) { \ uwrap.libpthread.symbols._libpthread_##sym_name.obj = \ _uwrap_bind_symbol(UWRAP_LIBPTHREAD, #sym_name); \ } \ UWRAP_UNLOCK(libpthread_symbol_binding) /* * IMPORTANT * * 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_bind_symbol_libc(setuid); return uwrap.libc.symbols._libc_setuid.f(uid); } static uid_t libc_getuid(void) { uwrap_bind_symbol_libc(getuid); return uwrap.libc.symbols._libc_getuid.f(); } #ifdef HAVE_SETEUID static int libc_seteuid(uid_t euid) { uwrap_bind_symbol_libc(seteuid); return uwrap.libc.symbols._libc_seteuid.f(euid); } #endif #ifdef HAVE_SETREUID static int libc_setreuid(uid_t ruid, uid_t euid) { uwrap_bind_symbol_libc(setreuid); 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_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.symbols._libc_getresuid.f(ruid, euid, suid); } #endif static uid_t libc_geteuid(void) { uwrap_bind_symbol_libc(geteuid); return uwrap.libc.symbols._libc_geteuid.f(); } static int libc_setgid(gid_t gid) { uwrap_bind_symbol_libc(setgid); return uwrap.libc.symbols._libc_setgid.f(gid); } static gid_t libc_getgid(void) { uwrap_bind_symbol_libc(getgid); return uwrap.libc.symbols._libc_getgid.f(); } #ifdef HAVE_SETEGID static int libc_setegid(gid_t egid) { uwrap_bind_symbol_libc(setegid); return uwrap.libc.symbols._libc_setegid.f(egid); } #endif #ifdef HAVE_SETREGID static int libc_setregid(gid_t rgid, gid_t egid) { uwrap_bind_symbol_libc(setregid); return uwrap.libc.symbols._libc_setregid.f(rgid, egid); } #endif #ifdef HAVE_SETRESGID static int libc_setresgid(gid_t rgid, gid_t egid, gid_t sgid) { uwrap_bind_symbol_libc(setresgid); return uwrap.libc.symbols._libc_setresgid.f(rgid, egid, sgid); } #endif #ifdef HAVE_GETRESGID static int libc_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid) { uwrap_bind_symbol_libc(setresgid); return uwrap.libc.symbols._libc_getresgid.f(rgid, egid, sgid); } #endif static gid_t libc_getegid(void) { uwrap_bind_symbol_libc(getegid); return uwrap.libc.symbols._libc_getegid.f(); } static int libc_getgroups(int size, gid_t list[]) { uwrap_bind_symbol_libc(getgroups); return uwrap.libc.symbols._libc_getgroups.f(size, list); } static int libc_setgroups(size_t size, const gid_t *list) { uwrap_bind_symbol_libc(setgroups); return 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_bind_symbol_libc(syscall); for (i = 0; i < 8; i++) { args[i] = va_arg(va, long int); } rc = uwrap.libc.symbols._libc_syscall.f(sysno, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); return rc; } #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 void uwrap_thread_prepare(void) { struct uwrap_thread *id = uwrap_tls_id; UWRAP_LOCK_ALL; /* * 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; 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; /* * "Garbage collector" - Inspired by DESTRUCTOR. * All threads (except one which called fork()) are dead now.. Dave * That's what posix said... */ while (u != NULL) { if (u == id) { /* Skip this item. */ u = uwrap.ids->next; continue; } UWRAP_DLIST_REMOVE(uwrap.ids, u); SAFE_FREE(u->groups); SAFE_FREE(u); u = uwrap.ids; } id->enabled = true; UWRAP_UNLOCK_ALL; } 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); } } id->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) { 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; } #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 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; } UWRAP_UNLOCK(uwrap_id); return 0; } #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 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 (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; } /* * 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 #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 */ 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; } 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 (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 */ 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 #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 */ 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; } else if (size > 0) { gid_t *tmp; tmp = realloc(id->groups, sizeof(gid_t) * size); if (tmp == NULL) { errno = ENOMEM; goto out; } id->groups = tmp; id->ngroups = size; memcpy(id->groups, list, size * sizeof(gid_t)); } rc = 0; out: 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; } } 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)); } } 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, gid_t); rc = uwrap_setresgid_thread(gid, -1, -1); } break; case SYS_setregid: #ifdef HAVE_LINUX_32BIT_SYSCALLS case SYS_setregid32: #endif { 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); } break; #ifdef SYS_setresgid case SYS_setresgid: #ifdef HAVE_LINUX_32BIT_SYSCALLS case SYS_setresgid32: #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_setresgid_thread(rgid, egid, sgid); } break; #endif /* SYS_setresgid */ #ifdef SYS_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 */ /* 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, uid_t); 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, uid_t); uid_t euid = (uid_t) va_arg(vp, uid_t); 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, 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 */ #ifdef SYS_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 */ /* 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_ALL; while (u != NULL) { UWRAP_DLIST_REMOVE(uwrap.ids, u); SAFE_FREE(u->groups); SAFE_FREE(u); u = uwrap.ids; } if (uwrap.libc.handle != NULL) { dlclose(uwrap.libc.handle); } if (uwrap.libpthread.handle != NULL) { dlclose(uwrap.libpthread.handle); } UWRAP_UNLOCK_ALL; }