diff options
author | Andreas Schneider <asn@samba.org> | 2014-01-17 14:43:01 +0100 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2014-04-17 14:56:05 +0200 |
commit | 6d23354f72a487741177dd83c561a1bb72fa6412 (patch) | |
tree | 1b34ec910d6c88e5120e9f0f39fbf2cd1ff0f4c0 /lib | |
parent | f318a44ec79da33a8972da9822c9ac3e4b39acff (diff) | |
download | samba-6d23354f72a487741177dd83c561a1bb72fa6412.tar.gz samba-6d23354f72a487741177dd83c561a1bb72fa6412.tar.xz samba-6d23354f72a487741177dd83c561a1bb72fa6412.zip |
lib: Change uid_wrapper to preloadable version.
This imports version 1.0.1 of uid_wrapper.
Signed-off-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/replace/system/filesys.h | 11 | ||||
-rw-r--r-- | lib/replace/system/network.h | 11 | ||||
-rw-r--r-- | lib/replace/system/passwd.h | 11 | ||||
-rw-r--r-- | lib/uid_wrapper/uid_wrapper.c | 1215 | ||||
-rw-r--r-- | lib/uid_wrapper/uid_wrapper.h | 101 | ||||
-rw-r--r-- | lib/uid_wrapper/wscript | 64 | ||||
-rw-r--r-- | lib/uid_wrapper/wscript_build | 10 | ||||
-rw-r--r-- | lib/util/util.c | 4 | ||||
-rwxr-xr-x | lib/util/wscript_build | 2 |
9 files changed, 1124 insertions, 305 deletions
diff --git a/lib/replace/system/filesys.h b/lib/replace/system/filesys.h index c8ac2b4f74..b234f101ef 100644 --- a/lib/replace/system/filesys.h +++ b/lib/replace/system/filesys.h @@ -206,17 +206,6 @@ #define mkdir(d,m) _mkdir(d) #endif -#ifdef UID_WRAPPER -# ifndef UID_WRAPPER_DISABLE -# ifndef UID_WRAPPER_NOT_REPLACE -# define UID_WRAPPER_REPLACE -# endif /* UID_WRAPPER_NOT_REPLACE */ -# include "../uid_wrapper/uid_wrapper.h" -# endif /* UID_WRAPPER_DISABLE */ -#else /* UID_WRAPPER */ -# define uwrap_enabled() 0 -#endif /* UID_WRAPPER */ - /* this allows us to use a uniform error handling for our xattr wrappers diff --git a/lib/replace/system/network.h b/lib/replace/system/network.h index 7cb8d7becf..5159fc8c38 100644 --- a/lib/replace/system/network.h +++ b/lib/replace/system/network.h @@ -376,15 +376,4 @@ struct addrinfo { #endif /* SOCKET_WRAPPER_DISABLE */ #endif /* SOCKET_WRAPPER */ -#ifdef UID_WRAPPER -# ifndef UID_WRAPPER_DISABLE -# ifndef UID_WRAPPER_NOT_REPLACE -# define UID_WRAPPER_REPLACE -# endif /* UID_WRAPPER_NOT_REPLACE */ -# include "../uid_wrapper/uid_wrapper.h" -# endif /* UID_WRAPPER_DISABLE */ -#else /* UID_WRAPPER */ -# define uwrap_enabled() 0 -#endif /* UID_WRAPPER */ - #endif diff --git a/lib/replace/system/passwd.h b/lib/replace/system/passwd.h index e5c16a0429..ba738a7edb 100644 --- a/lib/replace/system/passwd.h +++ b/lib/replace/system/passwd.h @@ -97,15 +97,4 @@ #endif /* NSS_WRAPPER_DISABLE */ #endif /* NSS_WRAPPER */ -#ifdef UID_WRAPPER -# ifndef UID_WRAPPER_DISABLE -# ifndef UID_WRAPPER_NOT_REPLACE -# define UID_WRAPPER_REPLACE -# endif /* UID_WRAPPER_NOT_REPLACE */ -# include "../uid_wrapper/uid_wrapper.h" -# endif /* UID_WRAPPER_DISABLE */ -#else /* UID_WRAPPER */ -# define uwrap_enabled() 0 -#endif /* UID_WRAPPER */ - #endif diff --git a/lib/uid_wrapper/uid_wrapper.c b/lib/uid_wrapper/uid_wrapper.c index 7a85a9563a..cb99481993 100644 --- a/lib/uid_wrapper/uid_wrapper.c +++ b/lib/uid_wrapper/uid_wrapper.c @@ -1,255 +1,1176 @@ /* - Copyright (C) Andrew Tridgell 2009 - Copyright (c) 2011 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/>. + * 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/>. */ -#ifdef _SAMBA_BUILD_ +#include "config.h" -#define UID_WRAPPER_NOT_REPLACE -#include "replace.h" -#include "system/passwd.h" -#include <talloc.h> -#include "../lib/util/setid.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 + +#ifdef HAVE_DESTRUCTOR_ATTRIBUTE +#define DESTRUCTOR_ATTRIBUTE __attribute__ ((destructor)) +#else +#define DESTRUCTOR_ATTRIBUTE +#endif /* HAVE_DESTRUCTOR_ATTRIBUTE */ + +#ifdef NDEBUG +#define UWRAP_DEBUG(...) +#else +#define UWRAP_DEBUG(...) fprintf(stderr, __VA_ARGS__) +#endif + +#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) -#else /* _SAMBA_BUILD_ */ +#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 -#error uid_wrapper_only_supported_in_samba_yet +#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); -#ifndef _PUBLIC_ -#define _PUBLIC_ + 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 + * We keep the virtualised euid/egid/groups information here */ -static struct { +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; + + gid_t *groups; + int ngroups; + + struct uwrap_thread *next; + struct uwrap_thread *prev; +}; + +struct uwrap { + struct { + void *handle; + struct uwrap_libc_fns fns; + } libc; + bool initialised; bool enabled; + uid_t myuid; - uid_t euid; uid_t mygid; - gid_t egid; - gid_t *groups; -} uwrap; + + 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; + +/********************************************************* + * UWRAP PROTOTYPES + *********************************************************/ + +bool uid_wrapper_enabled(void); +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 HAVE_APPLE + return RTLD_NEXT; +#endif + +#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 (handle = NULL, i = 10; handle == NULL && i >= 0; i--) { + char soname[256] = {0}; + + snprintf(soname, sizeof(soname), "libc.so.%d", i); + handle = dlopen(soname, flags); + } + + uwrap.libc.handle = handle; + } + break; + } + + if (handle == NULL) { + fprintf(stderr, + "Failed to dlopen library: %s\n", + dlerror()); + exit(-1); + } + + 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) \ + if (uwrap.libc.fns._libc_##fn_name == NULL) { \ + *(void **) (&uwrap.libc.fns._libc_##fn_name) = \ + _uwrap_load_lib_function(lib, #fn_name); \ + } + +/* + * 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 +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) { + errno = ENOMEM; + return -1; + } + + id->groups = malloc(sizeof(gid_t) * 1); + if (id->groups == NULL) { + errno = ENOMEM; + return -1; + } + + UWRAP_DLIST_ADD(uwrap.ids, id); + uwrap_tls_id = id; + } + + id->tid = tid; + id->dead = false; + + id->ruid = id->euid = id->suid = uwrap.myuid; + id->rgid = id->egid = id->sgid = uwrap.mygid; + + id->ngroups = 1; + id->groups[0] = uwrap.mygid; + + return 0; +} + +static void uwrap_thread_prepare(void) +{ + pthread_mutex_lock(&uwrap_id_mutex); + + /* + * 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; + + pthread_mutex_unlock(&uwrap_id_mutex); +} + +static void uwrap_thread_child(void) +{ + uwrap.enabled = true; + + pthread_mutex_unlock(&uwrap_id_mutex); +} static void uwrap_init(void) { - if (uwrap.initialised) return; + const char *env = getenv("UID_WRAPPER"); + pthread_t tid = pthread_self(); + + + + if (uwrap.initialised) { + struct uwrap_thread *id = uwrap_tls_id; + int rc; + + if (id != NULL) { + return; + } + + pthread_mutex_lock(&uwrap_id_mutex); + 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); + } + pthread_mutex_unlock(&uwrap_id_mutex); + + return; + } + + /* + * If we hold a lock and the application forks, then the child + * is not able to unlock the mutex and we are in a deadlock. + * This should prevent such deadlocks. + */ + pthread_atfork(&uwrap_thread_prepare, + &uwrap_thread_parent, + &uwrap_thread_child); + + pthread_mutex_lock(&uwrap_id_mutex); + uwrap.initialised = true; - if (getenv("UID_WRAPPER")) { - uwrap.enabled = true; + uwrap.enabled = false; + + if (env != NULL && env[0] == '1') { + const char *root = getenv("UID_WRAPPER_ROOT"); + int rc; + /* put us in one group */ - uwrap.myuid = uwrap.euid = geteuid(); - uwrap.mygid = uwrap.egid = getegid(); - uwrap.groups = talloc_array(NULL, gid_t, 1); - uwrap.groups[0] = 0; + if (root != NULL && root[0] == '1') { + uwrap.myuid = 0; + uwrap.mygid = 0; + } else { + uwrap.myuid = libc_geteuid(); + uwrap.mygid = libc_getegid(); + } + + rc = uwrap_new_id(tid, true); + if (rc < 0) { + exit(-1); + } + + uwrap.enabled = true; } + + pthread_mutex_unlock(&uwrap_id_mutex); } -#undef uwrap_enabled -_PUBLIC_ int uwrap_enabled(void) +bool uid_wrapper_enabled(void) { uwrap_init(); - return uwrap.enabled?1:0; + + return uwrap.enabled ? true : false; } -#ifdef HAVE_SETEUID -_PUBLIC_ int uwrap_seteuid(uid_t euid) +static int uwrap_setresuid_thread(uid_t ruid, uid_t euid, uid_t suid) { - uwrap_init(); - if (!uwrap.enabled) { - return samba_seteuid(euid); + struct uwrap_thread *id = uwrap_tls_id; + + if (ruid == (uid_t)-1 && euid == (uid_t)-1 && suid == (uid_t)-1) { + errno = EINVAL; + return -1; + } + + pthread_mutex_lock(&uwrap_id_mutex); + if (ruid != (uid_t)-1) { + id->ruid = ruid; } - /* assume for now that the ruid stays as root */ - if (euid == 0) { - uwrap.euid = uwrap.myuid; - } else { - uwrap.euid = euid; + + if (euid != (uid_t)-1) { + id->euid = euid; } + + if (suid != (uid_t)-1) { + id->suid = suid; + } + pthread_mutex_unlock(&uwrap_id_mutex); + 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; + } + + pthread_mutex_lock(&uwrap_id_mutex); + for (id = uwrap.ids; id; id = id->next) { + if (id->dead) { + continue; + } + + if (ruid != (uid_t)-1) { + id->ruid = ruid; + } + + if (euid != (uid_t)-1) { + id->euid = euid; + } + + if (suid != (uid_t)-1) { + id->suid = suid; + } + } + pthread_mutex_unlock(&uwrap_id_mutex); + + return 0; +} + +/* + * SETUID + */ +int setuid(uid_t uid) +{ + if (!uid_wrapper_enabled()) { + return libc_setuid(uid); + } + + 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); + } + + return uwrap_setresuid(-1, euid, -1); +} #endif #ifdef HAVE_SETREUID -_PUBLIC_ int uwrap_setreuid(uid_t ruid, uid_t euid) +int setreuid(uid_t ruid, uid_t euid) { - uwrap_init(); - if (!uwrap.enabled) { - return samba_setreuid(ruid, euid); + if (ruid == (uid_t)-1 && euid == (uid_t)-1) { + errno = EINVAL; + return -1; } - /* assume for now that the ruid stays as root */ - if (euid == 0) { - uwrap.euid = uwrap.myuid; - } else { - uwrap.euid = euid; + + if (!uid_wrapper_enabled()) { + return libc_setreuid(ruid, euid); } - return 0; + + return uwrap_setresuid(ruid, euid, -1); } #endif #ifdef HAVE_SETRESUID -_PUBLIC_ int uwrap_setresuid(uid_t ruid, uid_t euid, uid_t suid) +int setresuid(uid_t ruid, uid_t euid, uid_t suid) { - uwrap_init(); - if (!uwrap.enabled) { - return samba_setresuid(ruid, euid, suid); - } - /* assume for now that the ruid stays as root */ - if (euid == 0) { - uwrap.euid = uwrap.myuid; - } else { - uwrap.euid = euid; + if (!uid_wrapper_enabled()) { + return libc_setresuid(ruid, euid, suid); } - return 0; + + return uwrap_setresuid(ruid, euid, suid); } #endif -_PUBLIC_ uid_t uwrap_geteuid(void) +/* + * GETUID + */ +static uid_t uwrap_getuid(void) { - uwrap_init(); - if (!uwrap.enabled) { - return geteuid(); + struct uwrap_thread *id = uwrap_tls_id; + uid_t uid; + + pthread_mutex_lock(&uwrap_id_mutex); + uid = id->ruid; + pthread_mutex_unlock(&uwrap_id_mutex); + + return uid; +} + +uid_t getuid(void) +{ + if (!uid_wrapper_enabled()) { + return libc_getuid(); } - return uwrap.euid; + + return uwrap_getuid(); } -#ifdef HAVE_SETEGID -_PUBLIC_ int uwrap_setegid(gid_t egid) +/* + * GETEUID + */ +static uid_t uwrap_geteuid(void) { - uwrap_init(); - if (!uwrap.enabled) { - return samba_setegid(egid); + const char *env = getenv("UID_WRAPPER_MYUID"); + struct uwrap_thread *id = uwrap_tls_id; + uid_t uid; + + pthread_mutex_lock(&uwrap_id_mutex); + uid = id->euid; + pthread_mutex_unlock(&uwrap_id_mutex); + + /* Disable root and return myuid */ + if (env != NULL && env[0] == '1') { + uid = uwrap.myuid; } - /* assume for now that the ruid stays as root */ - if (egid == 0) { - uwrap.egid = uwrap.mygid; - } else { - uwrap.egid = egid; + + return uid; +} + +uid_t geteuid(void) +{ + if (!uid_wrapper_enabled()) { + return libc_geteuid(); } + + return uwrap_geteuid(); +} + +static int uwrap_setresgid_thread(gid_t rgid, gid_t egid, gid_t sgid) +{ + struct uwrap_thread *id = uwrap_tls_id; + + if (rgid == (gid_t)-1 && egid == (gid_t)-1 && sgid == (gid_t)-1) { + errno = EINVAL; + return -1; + } + + pthread_mutex_lock(&uwrap_id_mutex); + if (rgid != (gid_t)-1) { + id->rgid = rgid; + } + + if (egid != (gid_t)-1) { + id->egid = egid; + } + + if (sgid != (gid_t)-1) { + id->sgid = sgid; + } + pthread_mutex_unlock(&uwrap_id_mutex); + return 0; } -#endif -#ifdef HAVE_SETREGID -_PUBLIC_ int uwrap_setregid(gid_t rgid, gid_t egid) +static int uwrap_setresgid(gid_t rgid, gid_t egid, gid_t sgid) { - uwrap_init(); - if (!uwrap.enabled) { - return samba_setregid(rgid, egid); + struct uwrap_thread *id; + + if (rgid == (gid_t)-1 && egid == (gid_t)-1 && sgid == (gid_t)-1) { + errno = EINVAL; + return -1; } - /* assume for now that the ruid stays as root */ - if (egid == 0) { - uwrap.egid = uwrap.mygid; - } else { - uwrap.egid = egid; + + pthread_mutex_lock(&uwrap_id_mutex); + for (id = uwrap.ids; id; id = id->next) { + if (id->dead) { + continue; + } + + if (rgid != (gid_t)-1) { + id->rgid = rgid; + } + + if (egid != (gid_t)-1) { + id->egid = egid; + } + + if (sgid != (gid_t)-1) { + id->sgid = sgid; + } } + pthread_mutex_unlock(&uwrap_id_mutex); + return 0; } + +/* + * SETGID + */ +int setgid(gid_t gid) +{ + if (!uid_wrapper_enabled()) { + return libc_setgid(gid); + } + + return uwrap_setresgid(gid, -1, -1); +} + +#ifdef HAVE_SETEGID +int setegid(gid_t egid) +{ + if (!uid_wrapper_enabled()) { + return libc_setegid(egid); + } + + return uwrap_setresgid(-1, egid, -1); +} #endif -#ifdef HAVE_SETRESGID -_PUBLIC_ int uwrap_setresgid(gid_t rgid, gid_t egid, gid_t sgid) +#ifdef HAVE_SETREGID +int setregid(gid_t rgid, gid_t egid) { - uwrap_init(); - if (!uwrap.enabled) { - return samba_setresgid(rgid, egid, sgid); + if (!uid_wrapper_enabled()) { + return libc_setregid(rgid, egid); } - /* assume for now that the ruid stays as root */ - if (egid == 0) { - uwrap.egid = uwrap.mygid; - } else { - uwrap.egid = egid; + + 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); } - return 0; + + return uwrap_setresgid(rgid, egid, sgid); } #endif -_PUBLIC_ uid_t uwrap_getegid(void) +/* + * GETGID + */ +static gid_t uwrap_getgid(void) { - uwrap_init(); - if (!uwrap.enabled) { - return getegid(); + struct uwrap_thread *id = uwrap_tls_id; + gid_t gid; + + pthread_mutex_lock(&uwrap_id_mutex); + gid = id->rgid; + pthread_mutex_unlock(&uwrap_id_mutex); + + return gid; +} + +gid_t getgid(void) +{ + if (!uid_wrapper_enabled()) { + return libc_getgid(); } - return uwrap.egid; + + return uwrap_getgid(); } -_PUBLIC_ int uwrap_setgroups(size_t size, const gid_t *list) +/* + * GETEGID + */ +static uid_t uwrap_getegid(void) { - uwrap_init(); - if (!uwrap.enabled) { - return samba_setgroups(size, list); + struct uwrap_thread *id = uwrap_tls_id; + gid_t gid; + + pthread_mutex_lock(&uwrap_id_mutex); + gid = id->egid; + pthread_mutex_unlock(&uwrap_id_mutex); + + return gid; +} + +uid_t getegid(void) +{ + if (!uid_wrapper_enabled()) { + return libc_getegid(); } - talloc_free(uwrap.groups); - uwrap.groups = NULL; + 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; + + pthread_mutex_lock(&uwrap_id_mutex); + + if (size > 0) { + gid_t *tmp; - if (size != 0) { - uwrap.groups = talloc_array(NULL, gid_t, size); - if (uwrap.groups == NULL) { + tmp = realloc(id->groups, sizeof(gid_t) * size); + if (tmp == NULL) { errno = ENOMEM; - return -1; + goto out; } - memcpy(uwrap.groups, list, size*sizeof(gid_t)); + id->groups = tmp; + + id->ngroups = size; + memcpy(id->groups, list, size * sizeof(gid_t)); } - return 0; + + rc = 0; +out: + pthread_mutex_unlock(&uwrap_id_mutex); + + return rc; } -_PUBLIC_ int uwrap_getgroups(int size, gid_t *list) +static int uwrap_setgroups(size_t size, const gid_t *list) { - size_t ngroups; + struct uwrap_thread *id; + int rc = -1; - uwrap_init(); - if (!uwrap.enabled) { - return getgroups(size, list); + pthread_mutex_lock(&uwrap_id_mutex); + + if (size > 0) { + for (id = uwrap.ids; id; id = id->next) { + 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)); + } } - ngroups = talloc_array_length(uwrap.groups); + rc = 0; +out: + pthread_mutex_unlock(&uwrap_id_mutex); + + 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); + } + + return uwrap_setgroups(size, list); +} + +static int uwrap_getgroups(int size, gid_t *list) +{ + struct uwrap_thread *id = uwrap_tls_id; + int ngroups; + + pthread_mutex_lock(&uwrap_id_mutex); + ngroups = id->ngroups; if (size > ngroups) { size = ngroups; } if (size == 0) { - return ngroups; + goto out; } if (size < ngroups) { errno = EINVAL; - return -1; + ngroups = -1; } - memcpy(list, uwrap.groups, size*sizeof(gid_t)); + memcpy(list, id->groups, size * sizeof(gid_t)); + +out: + pthread_mutex_unlock(&uwrap_id_mutex); + return ngroups; } -_PUBLIC_ uid_t uwrap_getuid(void) +int getgroups(int size, gid_t *list) { - uwrap_init(); - if (!uwrap.enabled) { - return getuid(); + if (!uid_wrapper_enabled()) { + return libc_getgroups(size, list); } - /* we don't simulate ruid changing */ - return 0; + + return uwrap_getgroups(size, list); } -_PUBLIC_ gid_t uwrap_getgid(void) +#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) { - uwrap_init(); - if (!uwrap.enabled) { - return getgid(); + 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_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; + } + + rc = uwrap_syscall(sysno, va); + va_end(va); + + return rc; +} +#endif /* HAVE_SYSCALL */ +#endif /* HAVE_SYS_SYSCALL_H || HAVE_SYSCALL_H */ + +/**************************** + * 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; + + 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); } - /* we don't simulate rgid changing */ - return 0; } diff --git a/lib/uid_wrapper/uid_wrapper.h b/lib/uid_wrapper/uid_wrapper.h deleted file mode 100644 index 21d0795d41..0000000000 --- a/lib/uid_wrapper/uid_wrapper.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - Copyright (C) Andrew Tridgell 2009 - Copyright (c) 2011 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/>. - */ - -#ifndef __UID_WRAPPER_H__ -#define __UID_WRAPPER_H__ -#ifndef uwrap_enabled - -int uwrap_enabled(void); -int uwrap_seteuid(uid_t euid); -int uwrap_setreuid(uid_t reuid, uid_t euid); -int uwrap_setresuid(uid_t reuid, uid_t euid, uid_t suid); -uid_t uwrap_geteuid(void); -int uwrap_setegid(gid_t egid); -int uwrap_setregid(gid_t rgid, gid_t egid); -int uwrap_setresgid(gid_t regid, gid_t egid, gid_t sgid); -uid_t uwrap_getegid(void); -int uwrap_setgroups(size_t size, const gid_t *list); -int uwrap_getgroups(int size, gid_t *list); -uid_t uwrap_getuid(void); -gid_t uwrap_getgid(void); - -#ifdef UID_WRAPPER_REPLACE - -#ifdef samba_seteuid -#undef samba_seteuid -#endif -#define samba_seteuid uwrap_seteuid - -#ifdef samba_setreuid -#undef samba_setreuid -#endif -#define samba_setreuid uwrap_setreuid - -#ifdef samba_setresuid -#undef samba_setresuid -#endif -#define samba_setresuid uwrap_setresuid - -#ifdef samba_setegid -#undef samba_setegid -#endif -#define samba_setegid uwrap_setegid - -#ifdef samba_setregid -#undef samba_setregid -#endif -#define samba_setregid uwrap_setregid - -#ifdef samba_setresgid -#undef samba_setresgid -#endif -#define samba_setresgid uwrap_setresgid - -#ifdef geteuid -#undef geteuid -#endif -#define geteuid uwrap_geteuid - -#ifdef getegid -#undef getegid -#endif -#define getegid uwrap_getegid - -#ifdef samba_setgroups -#undef samba_setgroups -#endif -#define samba_setgroups uwrap_setgroups - -#ifdef getgroups -#undef getgroups -#endif -#define getgroups uwrap_getgroups - -#ifdef getuid -#undef getuid -#endif -#define getuid uwrap_getuid - -#ifdef getgid -#undef getgid -#endif -#define getgid uwrap_getgid - -#endif /* UID_WRAPPER_REPLACE */ -#endif /* uwrap_enabled */ -#endif /* __UID_WRAPPER_H__ */ diff --git a/lib/uid_wrapper/wscript b/lib/uid_wrapper/wscript index 1501d0e5ce..63be4734fa 100644 --- a/lib/uid_wrapper/wscript +++ b/lib/uid_wrapper/wscript @@ -1,17 +1,59 @@ #!/usr/bin/env python -import Options +import os -def set_options(opt): - gr = opt.option_group('developer options') - gr.add_option('--enable-uid-wrapper', - help=("Turn on uid wrapper library (default=no)"), - action="store_true", dest='enable_uid_wrapper', default=False) +VERSION="1.0.1" def configure(conf): - if (Options.options.enable_uid_wrapper or - Options.options.developer or - Options.options.enable_selftest): - conf.DEFINE('UID_WRAPPER', 1) - conf.ADD_GLOBAL_DEPENDENCY('uid_wrapper') + if conf.CHECK_BUNDLED_SYSTEM('uid_wrapper', minversion=VERSION, set_target=False): + conf.DEFINE('USING_SYSTEM_UID_WRAPPER', 1) + libuid_wrapper_so_path = 'libuid_wrapper.so' + else: + # check HAVE_GCC_THREAD_LOCAL_STORAGE + conf.CHECK_CODE(''' + __thread int tls; + + int main(void) { + return 0; + } + ''', + 'HAVE_GCC_THREAD_LOCAL_STORAGE', + addmain=False, + msg='Checking for thread local storage') + + # check HAVE_DESTRUCTOR_ATTRIBUTE + conf.CHECK_CODE(''' + void test_destructor_attribute(void) __attribute__ ((destructor)); + + void test_destructor_attribute(void) + { + return; + } + + int main(void) { + return 0; + } + ''', + 'HAVE_DESTRUCTOR_ATTRIBUTE', + addmain=False, + msg='Checking for library destructor support') + + # Create full path to uid_wrapper + srcdir = os.path.realpath(conf.srcdir) + libuid_wrapper_so_path = srcdir + '/bin/default/lib/uid_wrapper/libuid-wrapper.so' + + conf.DEFINE('LIBUID_WRAPPER_SO_PATH', libuid_wrapper_so_path) + conf.DEFINE('UID_WRAPPER', 1) + +def build(bld): + if not bld.CONFIG_SET("USING_SYSTEM_UID_WRAPPER"): + # We need to do it this way or the library wont work. + # Using private_library=True will add symbol version which + # breaks preloading! + bld.SAMBA_LIBRARY('uid_wrapper', + source='uid_wrapper.c', + cflags='-DNDEBUG', + deps='dl', + install=False, + realname='libuid-wrapper.so') diff --git a/lib/uid_wrapper/wscript_build b/lib/uid_wrapper/wscript_build deleted file mode 100644 index 76d4b17fce..0000000000 --- a/lib/uid_wrapper/wscript_build +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python - - -bld.SAMBA_LIBRARY('uid_wrapper', - source='uid_wrapper.c', - deps='talloc util_setid', - private_library=True, - enabled=bld.CONFIG_SET("UID_WRAPPER"), - ) - diff --git a/lib/util/util.c b/lib/util/util.c index 1429750fd4..b6f60c4754 100644 --- a/lib/util/util.c +++ b/lib/util/util.c @@ -146,7 +146,7 @@ _PUBLIC_ bool file_check_permissions(const char *fname, return false; } - if (pst->st_uid != uid && !uwrap_enabled()) { + if (pst->st_uid != uid && !uid_wrapper_enabled()) { DEBUG(0, ("invalid ownership of file '%s': " "owned by uid %u, should be %u\n", fname, (unsigned int)pst->st_uid, @@ -271,7 +271,7 @@ _PUBLIC_ bool directory_create_or_exist_strict(const char *dname, dname)); return false; } - if (st.st_uid != uid && !uwrap_enabled()) { + if (st.st_uid != uid && !uid_wrapper_enabled()) { DEBUG(0, ("invalid ownership on directory " "%s\n", dname)); return false; diff --git a/lib/util/wscript_build b/lib/util/wscript_build index 39a16135ef..a45dbde9a2 100755 --- a/lib/util/wscript_build +++ b/lib/util/wscript_build @@ -10,7 +10,7 @@ bld.SAMBA_LIBRARY('samba-util', server_id.c dprintf.c parmlist.c bitmap.c pidfile.c tevent_debug.c util_process.c''', deps='DYNCONFIG', - public_deps='talloc tevent execinfo uid_wrapper pthread LIBCRYPTO charset util_setid', + public_deps='talloc tevent execinfo pthread LIBCRYPTO charset util_setid', public_headers='debug.h attr.h byteorder.h data_blob.h memory.h safe_string.h time.h talloc_stack.h xfile.h dlinklist.h samba_util.h string_wrappers.h', header_path= [ ('dlinklist.h samba_util.h', '.'), ('*', 'util') ], local_include=False, |