diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /third_party/nss_wrapper | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/nss_wrapper')
-rw-r--r-- | third_party/nss_wrapper/nss_utils.c | 131 | ||||
-rw-r--r-- | third_party/nss_wrapper/nss_utils.h | 46 | ||||
-rw-r--r-- | third_party/nss_wrapper/nss_wrapper.c | 6570 | ||||
-rwxr-xr-x | third_party/nss_wrapper/nss_wrapper.pl | 521 | ||||
-rw-r--r-- | third_party/nss_wrapper/wscript | 97 |
5 files changed, 7365 insertions, 0 deletions
diff --git a/third_party/nss_wrapper/nss_utils.c b/third_party/nss_wrapper/nss_utils.c new file mode 100644 index 0000000..1d0a3d0 --- /dev/null +++ b/third_party/nss_wrapper/nss_utils.c @@ -0,0 +1,131 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2007, Stefan Metzmacher <metze@samba.org> + * Copyright (c) 2009, Guenther Deschner <gd@samba.org> + * Copyright (c) 2014-2015, Michael Adam <obnox@samba.org> + * Copyright (c) 2015, Robin Hack <hack.robin@gmail.com> + * Copyright (c) 2013-2018, Andreas Schneider <asn@samba.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <errno.h> +#include <grp.h> +#include <string.h> +#include <stdint.h> + +#include "nss_utils.h" + +int nwrap_gr_copy_r(const struct group *src, struct group *dst, + char *buf, size_t buflen, struct group **dstp) +{ + char *p = NULL; + uintptr_t align = 0; + unsigned int gr_mem_cnt = 0; + unsigned i; + size_t total_len; + size_t gr_name_len = strlen(src->gr_name) + 1; + size_t gr_passwd_len = strlen(src->gr_passwd) + 1; + union { + char *ptr; + char **data; + } g_mem; + + for (i = 0; src->gr_mem[i] != NULL; i++) { + gr_mem_cnt++; + } + + /* Align the memory for storing pointers */ + align = __alignof__(char *) - ((p - (char *)0) % __alignof__(char *)); + total_len = align + + (1 + gr_mem_cnt) * sizeof(char *) + + gr_name_len + gr_passwd_len; + + if (total_len > buflen) { + errno = ERANGE; + return -1; + } + buflen -= total_len; + + /* gr_mem */ + p = buf + align; + g_mem.ptr = p; + dst->gr_mem = g_mem.data; + + /* gr_name */ + p += (1 + gr_mem_cnt) * sizeof(char *); + dst->gr_name = p; + + /* gr_passwd */ + p += gr_name_len; + dst->gr_passwd = p; + + /* gr_mem[x] */ + p += gr_passwd_len; + + /* gr_gid */ + dst->gr_gid = src->gr_gid; + + memcpy(dst->gr_name, src->gr_name, gr_name_len); + + memcpy(dst->gr_passwd, src->gr_passwd, gr_passwd_len); + + /* Set the terminating entry */ + dst->gr_mem[gr_mem_cnt] = NULL; + + /* Now add the group members content */ + total_len = 0; + for (i = 0; i < gr_mem_cnt; i++) { + size_t len = strlen(src->gr_mem[i]) + 1; + + dst->gr_mem[i] = p; + total_len += len; + p += len; + } + + if (total_len > buflen) { + errno = ERANGE; + return -1; + } + + for (i = 0; i < gr_mem_cnt; i++) { + size_t len = strlen(src->gr_mem[i]) + 1; + + memcpy(dst->gr_mem[i], + src->gr_mem[i], + len); + } + + if (dstp != NULL) { + *dstp = dst; + } + + return 0; +} diff --git a/third_party/nss_wrapper/nss_utils.h b/third_party/nss_wrapper/nss_utils.h new file mode 100644 index 0000000..6a9c478 --- /dev/null +++ b/third_party/nss_wrapper/nss_utils.h @@ -0,0 +1,46 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2007, Stefan Metzmacher <metze@samba.org> + * Copyright (c) 2009, Guenther Deschner <gd@samba.org> + * Copyright (c) 2014-2015, Michael Adam <obnox@samba.org> + * Copyright (c) 2015, Robin Hack <hack.robin@gmail.com> + * Copyright (c) 2013-2018, Andreas Schneider <asn@samba.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef NSS_UTILS_H +#define NSS_UTILS_H +#include <grp.h> + +int nwrap_gr_copy_r(const struct group *src, struct group *dst, + char *buf, size_t buflen, struct group **dstp); + +#endif //NSS_UTILS_H diff --git a/third_party/nss_wrapper/nss_wrapper.c b/third_party/nss_wrapper/nss_wrapper.c new file mode 100644 index 0000000..3399f06 --- /dev/null +++ b/third_party/nss_wrapper/nss_wrapper.c @@ -0,0 +1,6570 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2007, Stefan Metzmacher <metze@samba.org> + * Copyright (c) 2009, Guenther Deschner <gd@samba.org> + * Copyright (c) 2014-2015, Michael Adam <obnox@samba.org> + * Copyright (c) 2015, Robin Hack <hack.robin@gmail.com> + * Copyright (c) 2013-2018, Andreas Schneider <asn@samba.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" + +#include <pthread.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <limits.h> + +#include <netinet/in.h> + +#include <search.h> +#include <assert.h> + +#include "nss_utils.h" +/* + * Defining _POSIX_PTHREAD_SEMANTICS before including pwd.h and grp.h gives us + * the posix getpwnam_r(), getpwuid_r(), getgrnam_r and getgrgid_r calls on + * Solaris + */ +#ifndef _POSIX_PTHREAD_SEMANTICS +#define _POSIX_PTHREAD_SEMANTICS +#endif + +#include <pwd.h> +#include <grp.h> +#ifdef HAVE_SHADOW_H +#include <shadow.h> +#endif /* HAVE_SHADOW_H */ + +#include <netdb.h> +#include <arpa/inet.h> +#include <netinet/in.h> + +#include <dlfcn.h> + +#if defined(HAVE_NSS_H) +/* Linux and BSD */ +#include <nss.h> + +typedef enum nss_status NSS_STATUS; +#elif defined(HAVE_NSS_COMMON_H) +/* Solaris */ +#include <nss_common.h> +#include <nss_dbdefs.h> +#include <nsswitch.h> + +typedef nss_status_t NSS_STATUS; + +# define NSS_STATUS_SUCCESS NSS_SUCCESS +# define NSS_STATUS_NOTFOUND NSS_NOTFOUND +# define NSS_STATUS_UNAVAIL NSS_UNAVAIL +# define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN +#else +# error "No nsswitch support detected" +#endif + +#ifndef PTR_DIFF +#define PTR_DIFF(p1, p2) ((ptrdiff_t)(((const char *)(p1)) - (const char *)(p2))) +#endif + +#ifndef _PUBLIC_ +#define _PUBLIC_ +#endif + +#ifndef EAI_NODATA +#define EAI_NODATA EAI_NONAME +#endif + +#ifndef EAI_ADDRFAMILY +#define EAI_ADDRFAMILY EAI_FAMILY +#endif + +#ifndef __STRING +#define __STRING(x) #x +#endif + +#ifndef __STRINGSTRING +#define __STRINGSTRING(x) __STRING(x) +#endif + +#ifndef __LINESTR__ +#define __LINESTR__ __STRINGSTRING(__LINE__) +#endif + +#ifndef __location__ +#define __location__ __FILE__ ":" __LINESTR__ +#endif + +#ifndef DNS_NAME_MAX +#define DNS_NAME_MAX 255 +#endif + +/* GCC have printf type attribute check. */ +#ifdef HAVE_ATTRIBUTE_PRINTF_FORMAT +#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) +#else +#define PRINTF_ATTRIBUTE(a,b) +#endif /* HAVE_ATTRIBUTE_PRINTF_FORMAT */ + +#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 */ + +#define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } while(0) + +#ifndef SAFE_FREE +#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0) +#endif + +#ifndef discard_const +#define discard_const(ptr) ((void *)((uintptr_t)(ptr))) +#endif + +#ifndef discard_const_p +#define discard_const_p(type, ptr) ((type *)discard_const(ptr)) +#endif + +#ifdef HAVE_IPV6 +#define NWRAP_INET_ADDRSTRLEN INET6_ADDRSTRLEN +#else +#define NWRAP_INET_ADDRSTRLEN INET_ADDRSTRLEN +#endif + +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define MIN(a,b) ((a) > (b) ? (b) : (a)) + +static bool nwrap_initialized = false; +static pthread_mutex_t nwrap_initialized_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* The mutex or accessing the id */ +static pthread_mutex_t nwrap_global_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t nwrap_gr_global_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t nwrap_he_global_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t nwrap_pw_global_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t nwrap_sp_global_mutex = PTHREAD_MUTEX_INITIALIZER; + +#define nss_wrapper_init_mutex(m) \ + _nss_wrapper_init_mutex(m, #m) + +/* Add new global locks here please */ +/* Also don't forget to add locks to + * nwrap_init() function. + */ +# define NWRAP_REINIT_ALL do { \ + int ret; \ + ret = nss_wrapper_init_mutex(&nwrap_initialized_mutex); \ + if (ret != 0) exit(-1); \ + ret = nss_wrapper_init_mutex(&nwrap_global_mutex); \ + if (ret != 0) exit(-1); \ + ret = nss_wrapper_init_mutex(&nwrap_gr_global_mutex); \ + if (ret != 0) exit(-1); \ + ret = nss_wrapper_init_mutex(&nwrap_he_global_mutex); \ + if (ret != 0) exit(-1); \ + ret = nss_wrapper_init_mutex(&nwrap_pw_global_mutex); \ + if (ret != 0) exit(-1); \ + ret = nss_wrapper_init_mutex(&nwrap_sp_global_mutex); \ + if (ret != 0) exit(-1); \ +} while(0) + +# define NWRAP_LOCK_ALL do { \ + nwrap_mutex_lock(&nwrap_initialized_mutex); \ + nwrap_mutex_lock(&nwrap_global_mutex); \ + nwrap_mutex_lock(&nwrap_gr_global_mutex); \ + nwrap_mutex_lock(&nwrap_he_global_mutex); \ + nwrap_mutex_lock(&nwrap_pw_global_mutex); \ + nwrap_mutex_lock(&nwrap_sp_global_mutex); \ +} while (0); + +# define NWRAP_UNLOCK_ALL do {\ + nwrap_mutex_unlock(&nwrap_sp_global_mutex); \ + nwrap_mutex_unlock(&nwrap_pw_global_mutex); \ + nwrap_mutex_unlock(&nwrap_he_global_mutex); \ + nwrap_mutex_unlock(&nwrap_gr_global_mutex); \ + nwrap_mutex_unlock(&nwrap_global_mutex); \ + nwrap_mutex_unlock(&nwrap_initialized_mutex); \ +} while (0); + +static void nwrap_init(void); + +enum nwrap_dbglvl_e { + NWRAP_LOG_ERROR = 0, + NWRAP_LOG_WARN, + NWRAP_LOG_DEBUG, + NWRAP_LOG_TRACE +}; + +#ifndef HAVE_GETPROGNAME +static const char *getprogname(void) +{ +#if defined(HAVE_PROGRAM_INVOCATION_SHORT_NAME) + return program_invocation_short_name; +#elif defined(HAVE_GETEXECNAME) + return getexecname(); +#else + return NULL; +#endif /* HAVE_PROGRAM_INVOCATION_SHORT_NAME */ +} +#endif /* HAVE_GETPROGNAME */ + +static void nwrap_log(enum nwrap_dbglvl_e dbglvl, const char *func, const char *format, ...) PRINTF_ATTRIBUTE(3, 4); +# define NWRAP_LOG(dbglvl, ...) nwrap_log((dbglvl), __func__, __VA_ARGS__) + +static void nwrap_log(enum nwrap_dbglvl_e dbglvl, + const char *func, + const char *format, ...) +{ + char buffer[1024]; + va_list va; + const char *d; + unsigned int lvl = 0; + const char *prefix = "NWRAP"; + const char *progname = getprogname(); + + d = getenv("NSS_WRAPPER_DEBUGLEVEL"); + if (d != NULL) { + lvl = atoi(d); + } + + if (lvl < dbglvl) { + return; + } + + va_start(va, format); + vsnprintf(buffer, sizeof(buffer), format, va); + va_end(va); + + switch (dbglvl) { + case NWRAP_LOG_ERROR: + prefix = "NWRAP_ERROR"; + break; + case NWRAP_LOG_WARN: + prefix = "NWRAP_WARN"; + break; + case NWRAP_LOG_DEBUG: + prefix = "NWRAP_DEBUG"; + break; + case NWRAP_LOG_TRACE: + prefix = "NWRAP_TRACE"; + break; + } + + if (progname == NULL) { + progname = "<unknown>"; + } + + fprintf(stderr, + "%s[%s (%u)] - %s: %s\n", + prefix, + progname, + (unsigned int)getpid(), + func, + buffer); +} + +/***************** + * LIBC + *****************/ + +#define LIBC_NAME "libc.so" + +typedef struct passwd *(*__libc_getpwnam)(const char *name); + +typedef int (*__libc_getpwnam_r)(const char *name, + struct passwd *pwd, + char *buf, + size_t buflen, + struct passwd **result); + +typedef struct passwd *(*__libc_getpwuid)(uid_t uid); + +typedef int (*__libc_getpwuid_r)(uid_t uid, + struct passwd *pwd, + char *buf, + size_t buflen, + struct passwd **result); + +typedef void (*__libc_setpwent)(void); + +typedef struct passwd *(*__libc_getpwent)(void); + +#ifdef HAVE_GETPWENT_R +# ifdef HAVE_SOLARIS_GETPWENT_R +typedef struct passwd *(*__libc_getpwent_r)(struct passwd *pwbuf, + char *buf, + size_t buflen); +# else /* HAVE_SOLARIS_GETPWENT_R */ +typedef int (*__libc_getpwent_r)(struct passwd *pwbuf, + char *buf, + size_t buflen, + struct passwd **pwbufp); +# endif /* HAVE_SOLARIS_GETPWENT_R */ +#endif /* HAVE_GETPWENT_R */ + +typedef void (*__libc_endpwent)(void); + +typedef int (*__libc_initgroups)(const char *user, gid_t gid); + +typedef struct group *(*__libc_getgrnam)(const char *name); + +typedef int (*__libc_getgrnam_r)(const char *name, + struct group *grp, + char *buf, + size_t buflen, + struct group **result); + +typedef struct group *(*__libc_getgrgid)(gid_t gid); + +typedef int (*__libc_getgrgid_r)(gid_t gid, + struct group *grp, + char *buf, + size_t buflen, + struct group **result); + +typedef void (*__libc_setgrent)(void); + +typedef struct group *(*__libc_getgrent)(void); + +#ifdef HAVE_GETGRENT_R +# ifdef HAVE_SOLARIS_GETGRENT_R +typedef struct group *(*__libc_getgrent_r)(struct group *group, + char *buf, + size_t buflen); +# else /* HAVE_SOLARIS_GETGRENT_R */ +typedef int (*__libc_getgrent_r)(struct group *group, + char *buf, + size_t buflen, + struct group **result); +# endif /* HAVE_SOLARIS_GETGRENT_R */ +#endif /* HAVE_GETGRENT_R */ + +typedef void (*__libc_endgrent)(void); + +typedef int (*__libc_getgrouplist)(const char *user, + gid_t group, + gid_t *groups, + int *ngroups); + +typedef void (*__libc_sethostent)(int stayopen); + +typedef struct hostent *(*__libc_gethostent)(void); + +typedef void (*__libc_endhostent)(void); + +typedef struct hostent *(*__libc_gethostbyname)(const char *name); + +#ifdef HAVE_GETHOSTBYNAME2 /* GNU extension */ +typedef struct hostent *(*__libc_gethostbyname2)(const char *name, int af); +#endif + +#ifdef HAVE_GETHOSTBYNAME2_R /* GNU extension */ +typedef int (*__libc_gethostbyname2_r)(const char *name, + int af, + struct hostent *ret, + char *buf, + size_t buflen, + struct hostent **result, + int *h_errnop); +#endif + +typedef struct hostent *(*__libc_gethostbyaddr)(const void *addr, + socklen_t len, + int type); + +typedef int (*__libc_getaddrinfo)(const char *node, + const char *service, + const struct addrinfo *hints, + struct addrinfo **res); +typedef int (*__libc_getnameinfo)(const struct sockaddr *sa, + socklen_t salen, + char *host, + size_t hostlen, + char *serv, + size_t servlen, + int flags); + +typedef int (*__libc_gethostname)(char *name, size_t len); + +#ifdef HAVE_GETHOSTBYNAME_R +typedef int (*__libc_gethostbyname_r)(const char *name, + struct hostent *ret, + char *buf, size_t buflen, + struct hostent **result, int *h_errnop); +#endif + +#ifdef HAVE_GETHOSTBYADDR_R +typedef int (*__libc_gethostbyaddr_r)(const void *addr, + socklen_t len, + int type, + struct hostent *ret, + char *buf, + size_t buflen, + struct hostent **result, + int *h_errnop); +#endif + +#define NWRAP_SYMBOL_ENTRY(i) \ + union { \ + __libc_##i f; \ + void *obj; \ + } _libc_##i + +struct nwrap_libc_symbols { + NWRAP_SYMBOL_ENTRY(getpwnam); + NWRAP_SYMBOL_ENTRY(getpwnam_r); + NWRAP_SYMBOL_ENTRY(getpwuid); + NWRAP_SYMBOL_ENTRY(getpwuid_r); + NWRAP_SYMBOL_ENTRY(setpwent); + NWRAP_SYMBOL_ENTRY(getpwent); +#ifdef HAVE_GETPWENT_R + NWRAP_SYMBOL_ENTRY(getpwent_r); +#endif + NWRAP_SYMBOL_ENTRY(endpwent); + + NWRAP_SYMBOL_ENTRY(initgroups); + NWRAP_SYMBOL_ENTRY(getgrnam); + NWRAP_SYMBOL_ENTRY(getgrnam_r); + NWRAP_SYMBOL_ENTRY(getgrgid); + NWRAP_SYMBOL_ENTRY(getgrgid_r); + NWRAP_SYMBOL_ENTRY(setgrent); + NWRAP_SYMBOL_ENTRY(getgrent); +#ifdef HAVE_GETGRENT_R + NWRAP_SYMBOL_ENTRY(getgrent_r); +#endif + NWRAP_SYMBOL_ENTRY(endgrent); + NWRAP_SYMBOL_ENTRY(getgrouplist); + + NWRAP_SYMBOL_ENTRY(sethostent); + NWRAP_SYMBOL_ENTRY(gethostent); + NWRAP_SYMBOL_ENTRY(endhostent); + NWRAP_SYMBOL_ENTRY(gethostbyname); +#ifdef HAVE_GETHOSTBYNAME_R + NWRAP_SYMBOL_ENTRY(gethostbyname_r); +#endif +#ifdef HAVE_GETHOSTBYNAME2 + NWRAP_SYMBOL_ENTRY(gethostbyname2); +#endif +#ifdef HAVE_GETHOSTBYNAME2_R + NWRAP_SYMBOL_ENTRY(gethostbyname2_r); +#endif + NWRAP_SYMBOL_ENTRY(gethostbyaddr); +#ifdef HAVE_GETHOSTBYADDR_R + NWRAP_SYMBOL_ENTRY(gethostbyaddr_r); +#endif + NWRAP_SYMBOL_ENTRY(getaddrinfo); + NWRAP_SYMBOL_ENTRY(getnameinfo); + NWRAP_SYMBOL_ENTRY(gethostname); +}; +#undef NWRAP_SYMBOL_ENTRY + +typedef NSS_STATUS (*__nss_getpwnam_r)(const char *name, + struct passwd *result, + char *buffer, + size_t buflen, + int *errnop); +typedef NSS_STATUS (*__nss_getpwuid_r)(uid_t uid, + struct passwd *result, + char *buffer, + size_t buflen, + int *errnop); +typedef NSS_STATUS (*__nss_setpwent)(void); +typedef NSS_STATUS (*__nss_getpwent_r)(struct passwd *result, + char *buffer, + size_t buflen, + int *errnop); +typedef NSS_STATUS (*__nss_endpwent)(void); +typedef NSS_STATUS (*__nss_initgroups_dyn)(const char *user, + gid_t group, + long int *start, + long int *size, + gid_t **groups, + long int limit, + int *errnop); +typedef NSS_STATUS (*__nss_getgrnam_r)(const char *name, + struct group *result, + char *buffer, + size_t buflen, + int *errnop); +typedef NSS_STATUS (*__nss_getgrgid_r)(gid_t gid, + struct group *result, + char *buffer, + size_t buflen, + int *errnop); +typedef NSS_STATUS (*__nss_setgrent)(void); +typedef NSS_STATUS (*__nss_getgrent_r)(struct group *result, + char *buffer, + size_t buflen, + int *errnop); +typedef NSS_STATUS (*__nss_endgrent)(void); +typedef NSS_STATUS (*__nss_gethostbyaddr_r)(const void *addr, + socklen_t addrlen, + int af, + struct hostent *result, + char *buffer, + size_t buflen, + int *errnop, + int *h_errnop); +typedef NSS_STATUS (*__nss_gethostbyname2_r)(const char *name, + int af, + struct hostent *result, + char *buffer, + size_t buflen, + int *errnop, + int *h_errnop); + +#define NWRAP_NSS_MODULE_SYMBOL_ENTRY(i) \ + union { \ + __nss_##i f; \ + void *obj; \ + } _nss_##i + +struct nwrap_nss_module_symbols { + NWRAP_NSS_MODULE_SYMBOL_ENTRY(getpwnam_r); + NWRAP_NSS_MODULE_SYMBOL_ENTRY(getpwuid_r); + NWRAP_NSS_MODULE_SYMBOL_ENTRY(setpwent); + NWRAP_NSS_MODULE_SYMBOL_ENTRY(getpwent_r); + NWRAP_NSS_MODULE_SYMBOL_ENTRY(endpwent); + + NWRAP_NSS_MODULE_SYMBOL_ENTRY(initgroups_dyn); + NWRAP_NSS_MODULE_SYMBOL_ENTRY(getgrnam_r); + NWRAP_NSS_MODULE_SYMBOL_ENTRY(getgrgid_r); + NWRAP_NSS_MODULE_SYMBOL_ENTRY(setgrent); + NWRAP_NSS_MODULE_SYMBOL_ENTRY(getgrent_r); + NWRAP_NSS_MODULE_SYMBOL_ENTRY(endgrent); + + NWRAP_NSS_MODULE_SYMBOL_ENTRY(gethostbyaddr_r); + NWRAP_NSS_MODULE_SYMBOL_ENTRY(gethostbyname2_r); +}; + +struct nwrap_backend { + const char *name; + const char *so_path; + void *so_handle; + struct nwrap_ops *ops; + struct nwrap_nss_module_symbols *symbols; +}; + +struct nwrap_vector; + +struct nwrap_ops { + struct passwd * (*nw_getpwnam)(struct nwrap_backend *b, + const char *name); + int (*nw_getpwnam_r)(struct nwrap_backend *b, + const char *name, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp); + struct passwd * (*nw_getpwuid)(struct nwrap_backend *b, + uid_t uid); + int (*nw_getpwuid_r)(struct nwrap_backend *b, + uid_t uid, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp); + void (*nw_setpwent)(struct nwrap_backend *b); + struct passwd * (*nw_getpwent)(struct nwrap_backend *b); + int (*nw_getpwent_r)(struct nwrap_backend *b, + struct passwd *pwdst, char *buf, + size_t buflen, struct passwd **pwdstp); + void (*nw_endpwent)(struct nwrap_backend *b); + int (*nw_initgroups_dyn)(struct nwrap_backend *b, + const char *user, + gid_t group, + long int *start, + long int *size, + gid_t **groups, + long int limit, + int *errnop); + struct group * (*nw_getgrnam)(struct nwrap_backend *b, + const char *name); + int (*nw_getgrnam_r)(struct nwrap_backend *b, + const char *name, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp); + struct group * (*nw_getgrgid)(struct nwrap_backend *b, + gid_t gid); + int (*nw_getgrgid_r)(struct nwrap_backend *b, + gid_t gid, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp); + void (*nw_setgrent)(struct nwrap_backend *b); + struct group * (*nw_getgrent)(struct nwrap_backend *b); + int (*nw_getgrent_r)(struct nwrap_backend *b, + struct group *grdst, char *buf, + size_t buflen, struct group **grdstp); + void (*nw_endgrent)(struct nwrap_backend *b); + struct hostent *(*nw_gethostbyaddr)(struct nwrap_backend *b, + const void *addr, + socklen_t len, int type); + struct hostent *(*nw_gethostbyname)(struct nwrap_backend *b, + const char *name); + struct hostent *(*nw_gethostbyname2)(struct nwrap_backend *b, + const char *name, int af); + int (*nw_gethostbyname2_r)(struct nwrap_backend *b, + const char *name, int af, + struct hostent *hedst, + char *buf, size_t buflen, + struct hostent **hedstp); +}; + +/* Public prototypes */ + +bool nss_wrapper_enabled(void); +bool nss_wrapper_shadow_enabled(void); +bool nss_wrapper_hosts_enabled(void); + +/* prototypes for files backend */ + + +static struct passwd *nwrap_files_getpwnam(struct nwrap_backend *b, + const char *name); +static int nwrap_files_getpwnam_r(struct nwrap_backend *b, + const char *name, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp); +static struct passwd *nwrap_files_getpwuid(struct nwrap_backend *b, + uid_t uid); +static int nwrap_files_getpwuid_r(struct nwrap_backend *b, + uid_t uid, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp); +static void nwrap_files_setpwent(struct nwrap_backend *b); +static struct passwd *nwrap_files_getpwent(struct nwrap_backend *b); +static int nwrap_files_getpwent_r(struct nwrap_backend *b, + struct passwd *pwdst, char *buf, + size_t buflen, struct passwd **pwdstp); +static void nwrap_files_endpwent(struct nwrap_backend *b); +static int nwrap_files_initgroups_dyn(struct nwrap_backend *b, + const char *user, + gid_t group, + long int *start, + long int *size, + gid_t **groups, + long int limit, + int *errnop); +static struct group *nwrap_files_getgrnam(struct nwrap_backend *b, + const char *name); +static int nwrap_files_getgrnam_r(struct nwrap_backend *b, + const char *name, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp); +static struct group *nwrap_files_getgrgid(struct nwrap_backend *b, + gid_t gid); +static int nwrap_files_getgrgid_r(struct nwrap_backend *b, + gid_t gid, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp); +static void nwrap_files_setgrent(struct nwrap_backend *b); +static struct group *nwrap_files_getgrent(struct nwrap_backend *b); +static int nwrap_files_getgrent_r(struct nwrap_backend *b, + struct group *grdst, char *buf, + size_t buflen, struct group **grdstp); +static void nwrap_files_endgrent(struct nwrap_backend *b); +static struct hostent *nwrap_files_gethostbyaddr(struct nwrap_backend *b, + const void *addr, + socklen_t len, int type); +static struct hostent *nwrap_files_gethostbyname(struct nwrap_backend *b, + const char *name); +#ifdef HAVE_GETHOSTBYNAME2 +static struct hostent *nwrap_files_gethostbyname2(struct nwrap_backend *b, + const char *name, int af); +#endif /* HAVE_GETHOSTBYNAME2 */ +static int nwrap_files_gethostbyname2_r(struct nwrap_backend *b, + const char *name, int af, + struct hostent *hedst, + char *buf, size_t buflen, + struct hostent **hedstp); + +/* prototypes for module backend */ + +static struct passwd *nwrap_module_getpwent(struct nwrap_backend *b); +static int nwrap_module_getpwent_r(struct nwrap_backend *b, + struct passwd *pwdst, char *buf, + size_t buflen, struct passwd **pwdstp); +static struct passwd *nwrap_module_getpwnam(struct nwrap_backend *b, + const char *name); +static int nwrap_module_getpwnam_r(struct nwrap_backend *b, + const char *name, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp); +static struct passwd *nwrap_module_getpwuid(struct nwrap_backend *b, + uid_t uid); +static int nwrap_module_getpwuid_r(struct nwrap_backend *b, + uid_t uid, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp); +static void nwrap_module_setpwent(struct nwrap_backend *b); +static void nwrap_module_endpwent(struct nwrap_backend *b); +static struct group *nwrap_module_getgrent(struct nwrap_backend *b); +static int nwrap_module_getgrent_r(struct nwrap_backend *b, + struct group *grdst, char *buf, + size_t buflen, struct group **grdstp); +static struct group *nwrap_module_getgrnam(struct nwrap_backend *b, + const char *name); +static int nwrap_module_getgrnam_r(struct nwrap_backend *b, + const char *name, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp); +static struct group *nwrap_module_getgrgid(struct nwrap_backend *b, + gid_t gid); +static int nwrap_module_getgrgid_r(struct nwrap_backend *b, + gid_t gid, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp); +static void nwrap_module_setgrent(struct nwrap_backend *b); +static void nwrap_module_endgrent(struct nwrap_backend *b); +static int nwrap_module_initgroups_dyn(struct nwrap_backend *b, + const char *user, + gid_t group, + long int *start, + long int *size, + gid_t **groups, + long int limit, + int *errnop); +static struct hostent *nwrap_module_gethostbyaddr(struct nwrap_backend *b, + const void *addr, + socklen_t len, int type); +static struct hostent *nwrap_module_gethostbyname(struct nwrap_backend *b, + const char *name); +static struct hostent *nwrap_module_gethostbyname2(struct nwrap_backend *b, + const char *name, int af); +static int nwrap_module_gethostbyname2_r(struct nwrap_backend *b, + const char *name, int af, + struct hostent *hedst, + char *buf, size_t buflen, + struct hostent **hedstp); + +struct nwrap_ops nwrap_files_ops = { + .nw_getpwnam = nwrap_files_getpwnam, + .nw_getpwnam_r = nwrap_files_getpwnam_r, + .nw_getpwuid = nwrap_files_getpwuid, + .nw_getpwuid_r = nwrap_files_getpwuid_r, + .nw_setpwent = nwrap_files_setpwent, + .nw_getpwent = nwrap_files_getpwent, + .nw_getpwent_r = nwrap_files_getpwent_r, + .nw_endpwent = nwrap_files_endpwent, + .nw_initgroups_dyn = nwrap_files_initgroups_dyn, + .nw_getgrnam = nwrap_files_getgrnam, + .nw_getgrnam_r = nwrap_files_getgrnam_r, + .nw_getgrgid = nwrap_files_getgrgid, + .nw_getgrgid_r = nwrap_files_getgrgid_r, + .nw_setgrent = nwrap_files_setgrent, + .nw_getgrent = nwrap_files_getgrent, + .nw_getgrent_r = nwrap_files_getgrent_r, + .nw_endgrent = nwrap_files_endgrent, + .nw_gethostbyaddr = nwrap_files_gethostbyaddr, + .nw_gethostbyname = nwrap_files_gethostbyname, +#ifdef HAVE_GETHOSTBYNAME2 + .nw_gethostbyname2 = nwrap_files_gethostbyname2, +#endif /* HAVE_GETHOSTBYNAME2 */ + .nw_gethostbyname2_r = nwrap_files_gethostbyname2_r, +}; + +struct nwrap_ops nwrap_module_ops = { + .nw_getpwnam = nwrap_module_getpwnam, + .nw_getpwnam_r = nwrap_module_getpwnam_r, + .nw_getpwuid = nwrap_module_getpwuid, + .nw_getpwuid_r = nwrap_module_getpwuid_r, + .nw_setpwent = nwrap_module_setpwent, + .nw_getpwent = nwrap_module_getpwent, + .nw_getpwent_r = nwrap_module_getpwent_r, + .nw_endpwent = nwrap_module_endpwent, + .nw_initgroups_dyn = nwrap_module_initgroups_dyn, + .nw_getgrnam = nwrap_module_getgrnam, + .nw_getgrnam_r = nwrap_module_getgrnam_r, + .nw_getgrgid = nwrap_module_getgrgid, + .nw_getgrgid_r = nwrap_module_getgrgid_r, + .nw_setgrent = nwrap_module_setgrent, + .nw_getgrent = nwrap_module_getgrent, + .nw_getgrent_r = nwrap_module_getgrent_r, + .nw_endgrent = nwrap_module_endgrent, + .nw_gethostbyaddr = nwrap_module_gethostbyaddr, + .nw_gethostbyname = nwrap_module_gethostbyname, + .nw_gethostbyname2 = nwrap_module_gethostbyname2, + .nw_gethostbyname2_r = nwrap_module_gethostbyname2_r, +}; + +struct nwrap_libc { + void *handle; + void *nsl_handle; + void *sock_handle; + struct nwrap_libc_symbols symbols; +}; + +struct nwrap_main { + size_t num_backends; + struct nwrap_backend *backends; + struct nwrap_libc *libc; +}; + +static struct nwrap_main *nwrap_main_global; +static struct nwrap_main __nwrap_main_global; + +/* + * PROTOTYPES + */ +static int nwrap_convert_he_ai(const struct hostent *he, + unsigned short port, + const struct addrinfo *hints, + struct addrinfo **pai, + bool skip_canonname); + +#ifdef HAVE_GETGROUPLIST +static int nwrap_getgrouplist(const char *user, + gid_t group, + long int *size, + gid_t **groupsp, + long int limit); +#endif + +/* + * VECTORS + */ + +#define DEFAULT_VECTOR_CAPACITY 16 + +struct nwrap_vector { + void **items; + size_t count; + size_t capacity; +}; + +/* Macro returns pointer to first element of vector->items array. + * + * nwrap_vector is used as a memory backend which take care of + * memory allocations and other stuff like memory growing. + * nwrap_vectors should not be considered as some abstract structures. + * On this level, vectors are more handy than direct realloc/malloc + * calls. + * + * nwrap_vector->items is array inside nwrap_vector which can be + * directly pointed by libc structure assembled by cwrap itself. + * + * EXAMPLE: + * + * 1) struct hostent contains char **h_addr_list element. + * 2) nwrap_vector holds array of pointers to addresses. + * It's easier to use vector to store results of + * file parsing etc. + * + * Now, pretend that cwrap assembled struct hostent and + * we need to set h_addr_list to point to nwrap_vector. + * Idea behind is to shield users from internal nwrap_vector + * implementation. + * (Yes, not fully - array terminated by NULL is needed because + * it's result expected by libc function caller.) + * + * + * CODE EXAMPLE: + * + * struct hostent he; + * struct nwrap_vector *vector = malloc(sizeof(struct nwrap_vector)); + * ... don't care about failed allocation now ... + * + * ... fill nwrap vector ... + * + * struct hostent he; + * he.h_addr_list = nwrap_vector_head(vector); + * + */ +#define nwrap_vector_head(vect) ((void *)((vect)->items)) + +#define nwrap_vector_foreach(item, vect, iter) \ + for (iter = 0, (item) = (vect).items == NULL ? NULL : (vect).items[0]; \ + item != NULL; \ + (item) = (vect).items[++iter]) + +#define nwrap_vector_is_initialized(vector) ((vector)->items != NULL) + +static inline bool nwrap_vector_init(struct nwrap_vector *const vector) +{ + if (vector == NULL) { + return false; + } + + /* count is initialized by ZERO_STRUCTP */ + ZERO_STRUCTP(vector); + vector->items = malloc(sizeof(void *) * (DEFAULT_VECTOR_CAPACITY + 1)); + if (vector->items == NULL) { + return false; + } + vector->capacity = DEFAULT_VECTOR_CAPACITY; + memset(vector->items, '\0', sizeof(void *) * (DEFAULT_VECTOR_CAPACITY + 1)); + + return true; +} + +static bool nwrap_vector_add_item(struct nwrap_vector *vector, void *const item) +{ + assert (vector != NULL); + + if (vector->items == NULL) { + nwrap_vector_init(vector); + } + + if (vector->count == vector->capacity) { + /* Items array _MUST_ be NULL terminated because it's passed + * as result to caller which expect NULL terminated array from libc. + */ + void **items = realloc(vector->items, sizeof(void *) * ((vector->capacity * 2) + 1)); + if (items == NULL) { + return false; + } + vector->items = items; + + /* Don't count ending NULL to capacity */ + vector->capacity *= 2; + } + + vector->items[vector->count] = item; + + vector->count += 1; + vector->items[vector->count] = NULL; + + return true; +} + +static bool nwrap_vector_merge(struct nwrap_vector *dst, + struct nwrap_vector *src) +{ + void **dst_items = NULL; + size_t count; + + if (src->count == 0) { + return true; + } + + count = dst->count + src->count; + + /* We don't need reallocation if we have enough capacity. */ + if (src->count > (dst->capacity - dst->count)) { + dst_items = (void **)realloc(dst->items, (count + 1) * sizeof(void *)); + if (dst_items == NULL) { + return false; + } + dst->items = dst_items; + dst->capacity = count; + } + + memcpy((void *)(((long *)dst->items) + dst->count), + src->items, + src->count * sizeof(void *)); + dst->count = count; + + return true; +} + +struct nwrap_cache { + const char *path; + int fd; + FILE *fp; + struct stat st; + void *private_data; + + struct nwrap_vector lines; + + bool (*parse_line)(struct nwrap_cache *, char *line); + void (*unload)(struct nwrap_cache *); +}; + +/* passwd */ +struct nwrap_pw { + struct nwrap_cache *cache; + + struct passwd *list; + int num; + int idx; +}; + +struct nwrap_cache __nwrap_cache_pw; +struct nwrap_pw nwrap_pw_global; + +static bool nwrap_pw_parse_line(struct nwrap_cache *nwrap, char *line); +static void nwrap_pw_unload(struct nwrap_cache *nwrap); + +/* shadow */ +#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) +struct nwrap_sp { + struct nwrap_cache *cache; + + struct spwd *list; + int num; + int idx; +}; + +struct nwrap_cache __nwrap_cache_sp; +struct nwrap_sp nwrap_sp_global; + +static bool nwrap_sp_parse_line(struct nwrap_cache *nwrap, char *line); +static void nwrap_sp_unload(struct nwrap_cache *nwrap); +#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */ + +/* group */ +struct nwrap_gr { + struct nwrap_cache *cache; + + struct group *list; + int num; + int idx; +}; + +struct nwrap_cache __nwrap_cache_gr; +struct nwrap_gr nwrap_gr_global; + +/* hosts */ +static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line); +static void nwrap_he_unload(struct nwrap_cache *nwrap); + +struct nwrap_addrdata { + unsigned char host_addr[16]; /* IPv4 or IPv6 address */ +}; + +static size_t max_hostents = 100; + +struct nwrap_entdata { + struct nwrap_addrdata addr; + struct hostent ht; + + struct nwrap_vector nwrap_addrdata; + + ssize_t aliases_count; +}; + +struct nwrap_entlist { + struct nwrap_entlist *next; + struct nwrap_entdata *ed; +}; + +struct nwrap_he { + struct nwrap_cache *cache; + + struct nwrap_vector entries; + struct nwrap_vector lists; + + int num; + int idx; +}; + +static struct nwrap_cache __nwrap_cache_he; +static struct nwrap_he nwrap_he_global; + + +/********************************************************* + * NWRAP PROTOTYPES + *********************************************************/ + +static bool nwrap_gr_parse_line(struct nwrap_cache *nwrap, char *line); +static void nwrap_gr_unload(struct nwrap_cache *nwrap); +#if ! defined(HAVE_CONSTRUCTOR_ATTRIBUTE) && defined(HAVE_PRAGMA_INIT) +/* xlC and other oldschool compilers support (only) this */ +#pragma init (nwrap_constructor) +#endif +void nwrap_constructor(void) CONSTRUCTOR_ATTRIBUTE; +#if ! defined(HAVE_DESTRUCTOR_ATTRIBUTE) && defined(HAVE_PRAGMA_FINI) +#pragma fini (nwrap_destructor) +#endif +void nwrap_destructor(void) DESTRUCTOR_ATTRIBUTE; + +/********************************************************* + * NWRAP LIBC LOADER FUNCTIONS + *********************************************************/ + +enum nwrap_lib { + NWRAP_LIBC, + NWRAP_LIBNSL, + NWRAP_LIBSOCKET, +}; + +static const char *nwrap_str_lib(enum nwrap_lib lib) +{ + switch (lib) { + case NWRAP_LIBC: + return "libc"; + case NWRAP_LIBNSL: + return "libnsl"; + case NWRAP_LIBSOCKET: + return "libsocket"; + } + + /* Compiler would warn us about unhandled enum value if we get here */ + return "unknown"; +} + +static void *nwrap_load_lib_handle(enum nwrap_lib lib) +{ + int flags = RTLD_LAZY; + void *handle = NULL; + int i; + +#ifdef RTLD_DEEPBIND + const char *env_preload = getenv("LD_PRELOAD"); + const char *env_deepbind = getenv("NSS_WRAPPER_DISABLE_DEEPBIND"); + bool enable_deepbind = true; + + /* Don't do a deepbind if we run with libasan */ + if (env_preload != NULL && strlen(env_preload) < 1024) { + const char *p = strstr(env_preload, "libasan.so"); + if (p != NULL) { + enable_deepbind = false; + } + } + + if (env_deepbind != NULL && strlen(env_deepbind) >= 1) { + enable_deepbind = false; + } + + if (enable_deepbind) { + flags |= RTLD_DEEPBIND; + } +#endif + + switch (lib) { + case NWRAP_LIBNSL: +#ifdef HAVE_LIBNSL + handle = nwrap_main_global->libc->nsl_handle; + if (handle == NULL) { + for (i = 10; i >= 0; i--) { + char soname[256] = {0}; + + snprintf(soname, sizeof(soname), "libnsl.so.%d", i); + handle = dlopen(soname, flags); + if (handle != NULL) { + break; + } + } + + nwrap_main_global->libc->nsl_handle = handle; + } + break; +#endif + /* FALL TROUGH */ + case NWRAP_LIBSOCKET: +#ifdef HAVE_LIBSOCKET + handle = nwrap_main_global->libc->sock_handle; + if (handle == NULL) { + for (i = 10; i >= 0; i--) { + char soname[256] = {0}; + + snprintf(soname, sizeof(soname), "libsocket.so.%d", i); + handle = dlopen(soname, flags); + if (handle != NULL) { + break; + } + } + + nwrap_main_global->libc->sock_handle = handle; + } + break; +#endif + /* FALL TROUGH */ + case NWRAP_LIBC: + handle = nwrap_main_global->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; + } + } + + nwrap_main_global->libc->handle = handle; + } + break; + } + + if (handle == NULL) { +#ifdef RTLD_NEXT + handle = nwrap_main_global->libc->handle + = nwrap_main_global->libc->sock_handle + = nwrap_main_global->libc->nsl_handle + = RTLD_NEXT; +#else + NWRAP_LOG(NWRAP_LOG_ERROR, + "Failed to dlopen library: %s\n", + dlerror()); + exit(-1); +#endif + } + + return handle; +} + +static void *_nwrap_bind_symbol(enum nwrap_lib lib, const char *fn_name) +{ + void *handle; + void *func; + + nwrap_init(); + + handle = nwrap_load_lib_handle(lib); + + func = dlsym(handle, fn_name); + if (func == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Failed to find %s: %s\n", + fn_name, dlerror()); + exit(-1); + } + + NWRAP_LOG(NWRAP_LOG_TRACE, + "Loaded %s from %s", + fn_name, nwrap_str_lib(lib)); + return func; +} + +#define nwrap_mutex_lock(m) _nwrap_mutex_lock(m, #m, __func__, __LINE__) +static void _nwrap_mutex_lock(pthread_mutex_t *mutex, const char *name, const char *caller, unsigned line) +{ + int ret; + + ret = pthread_mutex_lock(mutex); + if (ret != 0) { + NWRAP_LOG(NWRAP_LOG_ERROR, "PID(%d):PPID(%d): %s(%u): Couldn't lock pthread mutex(%s) - %s", + getpid(), getppid(), caller, line, name, strerror(ret)); + abort(); + } +} + +#define nwrap_mutex_unlock(m) _nwrap_mutex_unlock(m, #m, __func__, __LINE__) +static void _nwrap_mutex_unlock(pthread_mutex_t *mutex, const char *name, const char *caller, unsigned line) +{ + int ret; + + ret = pthread_mutex_unlock(mutex); + if (ret != 0) { + NWRAP_LOG(NWRAP_LOG_ERROR, "PID(%d):PPID(%d): %s(%u): Couldn't unlock pthread mutex(%s) - %s", + getpid(), getppid(), caller, line, name, strerror(ret)); + abort(); + } +} + +#define nwrap_bind_symbol_libc(sym_name) \ + if (nwrap_main_global->libc->symbols._libc_##sym_name.obj == NULL) { \ + nwrap_main_global->libc->symbols._libc_##sym_name.obj = \ + _nwrap_bind_symbol(NWRAP_LIBC, #sym_name); \ + } \ + +#define nwrap_bind_symbol_libc_posix(sym_name) \ + if (nwrap_main_global->libc->symbols._libc_##sym_name.obj == NULL) { \ + nwrap_main_global->libc->symbols._libc_##sym_name.obj = \ + _nwrap_bind_symbol(NWRAP_LIBC, "__posix_" #sym_name); \ + } \ + +#define nwrap_bind_symbol_libnsl(sym_name) \ + if (nwrap_main_global->libc->symbols._libc_##sym_name.obj == NULL) { \ + nwrap_main_global->libc->symbols._libc_##sym_name.obj = \ + _nwrap_bind_symbol(NWRAP_LIBNSL, #sym_name); \ + } \ + +#define nwrap_bind_symbol_libsocket(sym_name) \ + if (nwrap_main_global->libc->symbols._libc_##sym_name.obj == NULL) { \ + nwrap_main_global->libc->symbols._libc_##sym_name.obj = \ + _nwrap_bind_symbol(NWRAP_LIBSOCKET, #sym_name); \ + } \ + +static void nwrap_bind_symbol_all(void); + +/* INTERNAL HELPER FUNCTIONS */ +static void nwrap_lines_unload(struct nwrap_cache *const nwrap) +{ + size_t p; + void *item; + nwrap_vector_foreach(item, nwrap->lines, p) { + /* Maybe some vectors were merged ... */ + SAFE_FREE(item); + } + SAFE_FREE(nwrap->lines.items); + ZERO_STRUCTP(&nwrap->lines); +} + +/* + * 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 struct passwd *libc_getpwnam(const char *name) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getpwnam.f(name); +} + +#ifdef HAVE_GETPWNAM_R +static int libc_getpwnam_r(const char *name, + struct passwd *pwd, + char *buf, + size_t buflen, + struct passwd **result) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getpwnam_r.f(name, + pwd, + buf, + buflen, + result); +} +#endif + +static struct passwd *libc_getpwuid(uid_t uid) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getpwuid.f(uid); +} + +#ifdef HAVE_GETPWUID_R +static int libc_getpwuid_r(uid_t uid, + struct passwd *pwd, + char *buf, + size_t buflen, + struct passwd **result) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getpwuid_r.f(uid, + pwd, + buf, + buflen, + result); +} +#endif + +static inline void str_tolower(char *dst, char *src) +{ + register char *src_tmp = src; + register char *dst_tmp = dst; + + while (*src_tmp != '\0') { + *dst_tmp = tolower(*src_tmp); + ++src_tmp; + ++dst_tmp; + } +} + +static bool str_tolower_copy(char **dst_name, const char *const src_name) +{ + char *h_name_lower; + + if ((dst_name == NULL) || (src_name == NULL)) { + return false; + } + + h_name_lower = strdup(src_name); + if (h_name_lower == NULL) { + NWRAP_LOG(NWRAP_LOG_DEBUG, "Out of memory while strdup"); + return false; + } + + str_tolower(h_name_lower, h_name_lower); + *dst_name = h_name_lower; + return true; +} + +static void libc_setpwent(void) +{ + nwrap_bind_symbol_all(); + + nwrap_main_global->libc->symbols._libc_setpwent.f(); +} + +static struct passwd *libc_getpwent(void) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getpwent.f(); +} + +#ifdef HAVE_GETPWENT_R +# ifdef HAVE_SOLARIS_GETPWENT_R +static struct passwd *libc_getpwent_r(struct passwd *pwdst, + char *buf, + int buflen) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getpwent_r.f(pwdst, + buf, + buflen); +} +# else /* HAVE_SOLARIS_GETPWENT_R */ +static int libc_getpwent_r(struct passwd *pwdst, + char *buf, + size_t buflen, + struct passwd **pwdstp) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getpwent_r.f(pwdst, + buf, + buflen, + pwdstp); +} +# endif /* HAVE_SOLARIS_GETPWENT_R */ +#endif /* HAVE_GETPWENT_R */ + +static void libc_endpwent(void) +{ + nwrap_bind_symbol_all(); + + nwrap_main_global->libc->symbols._libc_endpwent.f(); +} + +static int libc_initgroups(const char *user, gid_t gid) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_initgroups.f(user, gid); +} + +static struct group *libc_getgrnam(const char *name) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getgrnam.f(name); +} + +#ifdef HAVE_GETGRNAM_R +static int libc_getgrnam_r(const char *name, + struct group *grp, + char *buf, + size_t buflen, + struct group **result) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getgrnam_r.f(name, + grp, + buf, + buflen, + result); +} +#endif + +static struct group *libc_getgrgid(gid_t gid) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getgrgid.f(gid); +} + +#ifdef HAVE_GETGRGID_R +static int libc_getgrgid_r(gid_t gid, + struct group *grp, + char *buf, + size_t buflen, + struct group **result) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getgrgid_r.f(gid, + grp, + buf, + buflen, + result); +} +#endif + +static void libc_setgrent(void) +{ + nwrap_bind_symbol_all(); + + nwrap_main_global->libc->symbols._libc_setgrent.f(); +} + +static struct group *libc_getgrent(void) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getgrent.f(); +} + +#ifdef HAVE_GETGRENT_R +# ifdef HAVE_SOLARIS_GETGRENT_R +static struct group *libc_getgrent_r(struct group *group, + char *buf, + size_t buflen) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getgrent_r.f(group, + buf, + buflen); +} +# else /* HAVE_SOLARIS_GETGRENT_R */ +static int libc_getgrent_r(struct group *group, + char *buf, + size_t buflen, + struct group **result) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getgrent_r.f(group, + buf, + buflen, + result); +} +# endif /* HAVE_SOLARIS_GETGRENT_R */ +#endif /* HAVE_GETGRENT_R */ + +static void libc_endgrent(void) +{ + nwrap_bind_symbol_all(); + + nwrap_main_global->libc->symbols._libc_endgrent.f(); +} + +#ifdef HAVE_GETGROUPLIST +static int libc_getgrouplist(const char *user, + gid_t group, + gid_t *groups, + int *ngroups) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getgrouplist.f(user, + group, + groups, + ngroups); +} +#endif + +static void libc_sethostent(int stayopen) +{ + nwrap_bind_symbol_all(); + + nwrap_main_global->libc->symbols._libc_sethostent.f(stayopen); +} + +static struct hostent *libc_gethostent(void) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_gethostent.f(); +} + +static void libc_endhostent(void) +{ + nwrap_bind_symbol_all(); + + nwrap_main_global->libc->symbols._libc_endhostent.f(); +} + +static struct hostent *libc_gethostbyname(const char *name) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_gethostbyname.f(name); +} + +#ifdef HAVE_GETHOSTBYNAME2 /* GNU extension */ +static struct hostent *libc_gethostbyname2(const char *name, int af) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_gethostbyname2.f(name, af); +} +#endif + +#ifdef HAVE_GETHOSTBYNAME2_R /* GNU extension */ +static int libc_gethostbyname2_r(const char *name, + int af, + struct hostent *ret, + char *buf, + size_t buflen, + struct hostent **result, + int *h_errnop) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_gethostbyname2_r.f(name, + af, + ret, + buf, + buflen, + result, + h_errnop); +} +#endif + +static struct hostent *libc_gethostbyaddr(const void *addr, + socklen_t len, + int type) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_gethostbyaddr.f(addr, + len, + type); +} + +static int libc_gethostname(char *name, size_t len) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_gethostname.f(name, len); +} + +#ifdef HAVE_GETHOSTBYNAME_R +static int libc_gethostbyname_r(const char *name, + struct hostent *ret, + char *buf, + size_t buflen, + struct hostent **result, + int *h_errnop) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_gethostbyname_r.f(name, + ret, + buf, + buflen, + result, + h_errnop); +} +#endif + +#ifdef HAVE_GETHOSTBYADDR_R +static int libc_gethostbyaddr_r(const void *addr, + socklen_t len, + int type, + struct hostent *ret, + char *buf, + size_t buflen, + struct hostent **result, + int *h_errnop) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_gethostbyaddr_r.f(addr, + len, + type, + ret, + buf, + buflen, + result, + h_errnop); +} +#endif + +static int libc_getaddrinfo(const char *node, + const char *service, + const struct addrinfo *hints, + struct addrinfo **res) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getaddrinfo.f(node, + service, + hints, + res); +} + +static int libc_getnameinfo(const struct sockaddr *sa, + socklen_t salen, + char *host, + size_t hostlen, + char *serv, + size_t servlen, + int flags) +{ + nwrap_bind_symbol_all(); + + return nwrap_main_global->libc->symbols._libc_getnameinfo.f(sa, + salen, + host, + hostlen, + serv, + servlen, + flags); +} + +static void __nwrap_bind_symbol_all_once(void) +{ + nwrap_bind_symbol_libc(getpwnam); +#ifdef HAVE_GETPWNAM_R +# ifdef HAVE___POSIX_GETPWNAM_R + nwrap_bind_symbol_libc_posix(getpwnam_r); +# else + nwrap_bind_symbol_libc(getpwnam_r); +# endif +#endif + nwrap_bind_symbol_libc(getpwuid); +#ifdef HAVE_GETPWUID_R +# ifdef HAVE___POSIX_GETPWUID_R + nwrap_bind_symbol_libc_posix(getpwuid_r); +# else + nwrap_bind_symbol_libc(getpwuid_r); +# endif +#endif + nwrap_bind_symbol_libc(setpwent); + nwrap_bind_symbol_libc(getpwent); +#ifdef HAVE_GETPWENT_R + nwrap_bind_symbol_libc(getpwent_r); +#endif + nwrap_bind_symbol_libc(endpwent); + nwrap_bind_symbol_libc(initgroups); + nwrap_bind_symbol_libc(getgrnam); +#ifdef HAVE_GETGRNAM_R +# ifdef HAVE___POSIX_GETGRNAM_R + nwrap_bind_symbol_libc_posix(getgrnam_r); +# else + nwrap_bind_symbol_libc(getgrnam_r); +# endif +#endif + nwrap_bind_symbol_libc(getgrgid); +#ifdef HAVE_GETGRGID_R +# ifdef HAVE___POSIX_GETGRGID_R + nwrap_bind_symbol_libc_posix(getgrgid_r); +# else + nwrap_bind_symbol_libc(getgrgid_r); +# endif +#endif + nwrap_bind_symbol_libc(setgrent); + nwrap_bind_symbol_libc(getgrent); + nwrap_bind_symbol_libc(getgrent_r); + nwrap_bind_symbol_libc(endgrent); + nwrap_bind_symbol_libc(getgrouplist); + nwrap_bind_symbol_libnsl(sethostent); + nwrap_bind_symbol_libnsl(gethostent); + nwrap_bind_symbol_libnsl(endhostent); + nwrap_bind_symbol_libnsl(gethostbyname); +#ifdef HAVE_GETHOSTBYNAME2 /* GNU extension */ + nwrap_bind_symbol_libnsl(gethostbyname2); +#endif +#ifdef HAVE_GETHOSTBYNAME2_R /* GNU extension */ + nwrap_bind_symbol_libnsl(gethostbyname2_r); +#endif + nwrap_bind_symbol_libnsl(gethostbyaddr); + nwrap_bind_symbol_libnsl(gethostname); +#ifdef HAVE_GETHOSTBYNAME_R + nwrap_bind_symbol_libnsl(gethostbyname_r); +#endif +#ifdef HAVE_GETHOSTBYADDR_R + nwrap_bind_symbol_libnsl(gethostbyaddr_r); +#endif + nwrap_bind_symbol_libsocket(getaddrinfo); + nwrap_bind_symbol_libsocket(getnameinfo); +} + +static void nwrap_bind_symbol_all(void) +{ + static pthread_once_t all_symbol_binding_once = PTHREAD_ONCE_INIT; + + pthread_once(&all_symbol_binding_once, __nwrap_bind_symbol_all_once); +} + +/********************************************************* + * NWRAP NSS MODULE LOADER FUNCTIONS + *********************************************************/ + +static void *_nwrap_bind_nss_module_symbol(struct nwrap_backend *b, + const char *fn_name) +{ + void *res = NULL; + char *s = NULL; + int rc; + + if (b->so_handle == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, "No handle"); + return NULL; + } + + rc = asprintf(&s, "_nss_%s_%s", b->name, fn_name); + if (rc == -1) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Out of memory"); + return NULL; + } + + res = dlsym(b->so_handle, s); + if (res == NULL) { + NWRAP_LOG(NWRAP_LOG_WARN, + "Cannot find function %s in %s", + s, b->so_path); + } + SAFE_FREE(s); + return res; +} + +#define nwrap_nss_module_bind_symbol(sym_name) \ + if (symbols->_nss_##sym_name.obj == NULL) { \ + symbols->_nss_##sym_name.obj = \ + _nwrap_bind_nss_module_symbol(b, #sym_name); \ + } + +#define nwrap_nss_module_bind_symbol2(sym_name, alt_name) \ + if (symbols->_nss_##sym_name.obj == NULL) { \ + symbols->_nss_##sym_name.obj = \ + _nwrap_bind_nss_module_symbol(b, #alt_name); \ + } + +static struct nwrap_nss_module_symbols * +nwrap_bind_nss_module_symbols(struct nwrap_backend *b) +{ + struct nwrap_nss_module_symbols *symbols; + + if (!b->so_handle) { + return NULL; + } + + symbols = calloc(1, sizeof(struct nwrap_nss_module_symbols)); + if (symbols == NULL) { + return NULL; + } + + nwrap_nss_module_bind_symbol(getpwnam_r); + nwrap_nss_module_bind_symbol(getpwuid_r); + nwrap_nss_module_bind_symbol(setpwent); + nwrap_nss_module_bind_symbol(getpwent_r); + nwrap_nss_module_bind_symbol(endpwent); + nwrap_nss_module_bind_symbol(initgroups_dyn); + nwrap_nss_module_bind_symbol(getgrnam_r); + nwrap_nss_module_bind_symbol(getgrgid_r); + nwrap_nss_module_bind_symbol(setgrent); + nwrap_nss_module_bind_symbol(getgrent_r); + nwrap_nss_module_bind_symbol(endgrent); + nwrap_nss_module_bind_symbol(gethostbyaddr_r); + nwrap_nss_module_bind_symbol(gethostbyname2_r); + + return symbols; +} + +static void *nwrap_load_module(const char *so_path) +{ + void *h; + + if (!so_path || !strlen(so_path)) { + return NULL; + } + + h = dlopen(so_path, RTLD_LAZY); + if (!h) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Cannot open shared library %s", + so_path); + return NULL; + } + + return h; +} + +static bool nwrap_module_init(const char *name, + struct nwrap_ops *ops, + const char *so_path, + size_t *num_backends, + struct nwrap_backend **backends) +{ + struct nwrap_backend *b = NULL; + size_t n = *num_backends + 1; + + b = realloc(*backends, sizeof(struct nwrap_backend) * n); + if (b == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Out of memory"); + return false; + } + *backends = b; + + b = &((*backends)[*num_backends]); + + *b = (struct nwrap_backend) { + .name = name, + .ops = ops, + .so_path = so_path, + }; + + if (so_path != NULL) { + b->so_handle = nwrap_load_module(so_path); + b->symbols = nwrap_bind_nss_module_symbols(b); + if (b->symbols == NULL) { + return false; + } + } + + *num_backends = n; + + return true; +} + +static void nwrap_libc_init(struct nwrap_main *r) +{ + r->libc = calloc(1, sizeof(struct nwrap_libc)); + if (r->libc == NULL) { + printf("Failed to allocate memory for libc"); + exit(-1); + } +} + +static void nwrap_backend_init(struct nwrap_main *r) +{ + const char *module_so_path = getenv("NSS_WRAPPER_MODULE_SO_PATH"); + const char *module_fn_name = getenv("NSS_WRAPPER_MODULE_FN_PREFIX"); + + r->num_backends = 0; + r->backends = NULL; + + if (!nwrap_module_init("files", &nwrap_files_ops, NULL, + &r->num_backends, + &r->backends)) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Failed to initialize 'files' backend"); + return; + } + + if (module_so_path != NULL && + module_so_path[0] != '\0' && + module_fn_name != NULL && + module_fn_name[0] != '\0') { + if (!nwrap_module_init(module_fn_name, + &nwrap_module_ops, + module_so_path, + &r->num_backends, + &r->backends)) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Failed to initialize '%s' backend", + module_fn_name); + return; + } + } +} + +static int _nss_wrapper_init_mutex(pthread_mutex_t *m, const char *name) +{ + pthread_mutexattr_t ma; + bool need_destroy = false; + int ret = 0; + +#define __CHECK(cmd) do { \ + ret = cmd; \ + if (ret != 0) { \ + NWRAP_LOG(NWRAP_LOG_ERROR, \ + "%s: %s - failed %d", \ + name, #cmd, ret); \ + goto done; \ + } \ +} while(0) + + *m = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER; + __CHECK(pthread_mutexattr_init(&ma)); + need_destroy = true; + __CHECK(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK)); + __CHECK(pthread_mutex_init(m, &ma)); +done: + if (need_destroy) { + pthread_mutexattr_destroy(&ma); + } + return ret; +} + +static void nwrap_init(void) +{ + const char *env; + char *endptr; + size_t max_hostents_tmp; + int ok; + + nwrap_mutex_lock(&nwrap_initialized_mutex); + if (nwrap_initialized) { + nwrap_mutex_unlock(&nwrap_initialized_mutex); + return; + } + + /* + * Still holding nwrap_initialized lock here. + * We don't use NWRAP_(UN)LOCK_ALL macros here because we + * want to avoid overhead when other threads do their job. + */ + nwrap_mutex_lock(&nwrap_global_mutex); + nwrap_mutex_lock(&nwrap_gr_global_mutex); + nwrap_mutex_lock(&nwrap_he_global_mutex); + nwrap_mutex_lock(&nwrap_pw_global_mutex); + nwrap_mutex_lock(&nwrap_sp_global_mutex); + + nwrap_initialized = true; + + env = getenv("NSS_WRAPPER_MAX_HOSTENTS"); + if (env != NULL) { + max_hostents_tmp = (size_t)strtoul(env, &endptr, 10); + if ((*env == '\0') || + (*endptr != '\0') || + (max_hostents_tmp == 0)) { + NWRAP_LOG(NWRAP_LOG_DEBUG, + "Error parsing NSS_WRAPPER_MAX_HOSTENTS " + "value or value is too small. " + "Using default value: %lu.", + (unsigned long)max_hostents); + } else { + max_hostents = max_hostents_tmp; + } + } + /* Initialize hash table */ + NWRAP_LOG(NWRAP_LOG_DEBUG, + "Initializing hash table of size %lu items.", + (unsigned long)max_hostents); + ok = hcreate(max_hostents); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Failed to initialize hash table"); + exit(-1); + } + + nwrap_main_global = &__nwrap_main_global; + + nwrap_libc_init(nwrap_main_global); + + nwrap_backend_init(nwrap_main_global); + + /* passwd */ + nwrap_pw_global.cache = &__nwrap_cache_pw; + + nwrap_pw_global.cache->path = getenv("NSS_WRAPPER_PASSWD"); + nwrap_pw_global.cache->fp = NULL; + nwrap_pw_global.cache->fd = -1; + nwrap_pw_global.cache->private_data = &nwrap_pw_global; + nwrap_pw_global.cache->parse_line = nwrap_pw_parse_line; + nwrap_pw_global.cache->unload = nwrap_pw_unload; + + /* shadow */ +#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) + nwrap_sp_global.cache = &__nwrap_cache_sp; + + nwrap_sp_global.cache->path = getenv("NSS_WRAPPER_SHADOW"); + nwrap_sp_global.cache->fp = NULL; + nwrap_sp_global.cache->fd = -1; + nwrap_sp_global.cache->private_data = &nwrap_sp_global; + nwrap_sp_global.cache->parse_line = nwrap_sp_parse_line; + nwrap_sp_global.cache->unload = nwrap_sp_unload; +#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */ + + /* group */ + nwrap_gr_global.cache = &__nwrap_cache_gr; + + nwrap_gr_global.cache->path = getenv("NSS_WRAPPER_GROUP"); + nwrap_gr_global.cache->fp = NULL; + nwrap_gr_global.cache->fd = -1; + nwrap_gr_global.cache->private_data = &nwrap_gr_global; + nwrap_gr_global.cache->parse_line = nwrap_gr_parse_line; + nwrap_gr_global.cache->unload = nwrap_gr_unload; + + /* hosts */ + nwrap_he_global.cache = &__nwrap_cache_he; + + nwrap_he_global.cache->path = getenv("NSS_WRAPPER_HOSTS"); + nwrap_he_global.cache->fp = NULL; + nwrap_he_global.cache->fd = -1; + nwrap_he_global.cache->private_data = &nwrap_he_global; + nwrap_he_global.cache->parse_line = nwrap_he_parse_line; + nwrap_he_global.cache->unload = nwrap_he_unload; + + /* We hold all locks here so we can use NWRAP_UNLOCK_ALL. */ + nwrap_mutex_unlock(&nwrap_sp_global_mutex); + nwrap_mutex_unlock(&nwrap_pw_global_mutex); + nwrap_mutex_unlock(&nwrap_he_global_mutex); + nwrap_mutex_unlock(&nwrap_gr_global_mutex); + nwrap_mutex_unlock(&nwrap_global_mutex); + nwrap_mutex_unlock(&nwrap_initialized_mutex); +} + +bool nss_wrapper_enabled(void) +{ + nwrap_init(); + + if (nwrap_pw_global.cache->path == NULL || + nwrap_pw_global.cache->path[0] == '\0') { + return false; + } + if (nwrap_gr_global.cache->path == NULL || + nwrap_gr_global.cache->path[0] == '\0') { + return false; + } + + return true; +} + +#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) +bool nss_wrapper_shadow_enabled(void) +{ + nwrap_init(); + + if (nwrap_sp_global.cache->path == NULL || + nwrap_sp_global.cache->path[0] == '\0') { + return false; + } + + return true; +} +#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */ + +bool nss_wrapper_hosts_enabled(void) +{ + nwrap_init(); + + if (nwrap_he_global.cache->path == NULL || + nwrap_he_global.cache->path[0] == '\0') { + return false; + } + + return true; +} + +static bool nwrap_hostname_enabled(void) +{ + nwrap_init(); + + if (getenv("NSS_WRAPPER_HOSTNAME") == NULL) { + return false; + } + + return true; +} + +static bool nwrap_parse_file(struct nwrap_cache *nwrap) +{ + char *line = NULL; + ssize_t n; + /* Unused but getline needs it */ + size_t len; + bool ok; + + if (nwrap->st.st_size == 0) { + NWRAP_LOG(NWRAP_LOG_DEBUG, "size == 0"); + return true; + } + + /* Support for 32-bit system I guess */ + if (nwrap->st.st_size > INT32_MAX) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Size[%u] larger than INT32_MAX", + (unsigned)nwrap->st.st_size); + return false; + } + + rewind(nwrap->fp); + + do { + n = getline(&line, &len, nwrap->fp); + if (n < 0) { + SAFE_FREE(line); + if (feof(nwrap->fp)) { + break; + } + + NWRAP_LOG(NWRAP_LOG_ERROR, + "Unable to read line from file: %s", + nwrap->path); + return false; + } + + if (line[n - 1] == '\n') { + line[n - 1] = '\0'; + } + + if (line[0] == '\0') { + SAFE_FREE(line); + continue; + } + + ok = nwrap->parse_line(nwrap, line); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Unable to parse line file: %s", + line); + SAFE_FREE(line); + return false; + } + + /* Line is parsed without issues so add it to list */ + ok = nwrap_vector_add_item(&(nwrap->lines), (void *const) line); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Unable to add line to vector"); + return false; + } + + /* This forces getline to allocate new memory for line. */ + line = NULL; + } while (!feof(nwrap->fp)); + + return true; +} + +static void nwrap_files_cache_unload(struct nwrap_cache *nwrap) +{ + nwrap->unload(nwrap); + + nwrap_lines_unload(nwrap); +} + +static bool nwrap_files_cache_reload(struct nwrap_cache *nwrap) +{ + struct stat st; + int ret; + bool ok; + bool retried = false; + + assert(nwrap != NULL); + +reopen: + if (nwrap->fd < 0) { + nwrap->fp = fopen(nwrap->path, "re"); + if (nwrap->fp == NULL) { + nwrap->fd = -1; + NWRAP_LOG(NWRAP_LOG_ERROR, + "Unable to open '%s' readonly %d:%s", + nwrap->path, nwrap->fd, + strerror(errno)); + return false; + + } + nwrap->fd = fileno(nwrap->fp); + NWRAP_LOG(NWRAP_LOG_DEBUG, "Open '%s'", nwrap->path); + } + + ret = fstat(nwrap->fd, &st); + if (ret != 0 && errno == EBADF && retried == false) { + /* maybe something closed the fd on our behalf */ + NWRAP_LOG(NWRAP_LOG_TRACE, + "fstat(%s) - %d:%s - reopen", + nwrap->path, + ret, + strerror(errno)); + retried = true; + memset(&nwrap->st, 0, sizeof(nwrap->st)); + fclose(nwrap->fp); + nwrap->fp = NULL; + nwrap->fd = -1; + goto reopen; + } + else if (ret != 0) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "fstat(%s) - %d:%s", + nwrap->path, + ret, + strerror(errno)); + fclose(nwrap->fp); + nwrap->fp = NULL; + nwrap->fd = -1; + return false; + } + + if (retried == false && st.st_nlink == 0) { + /* maybe someone has replaced the file... */ + NWRAP_LOG(NWRAP_LOG_TRACE, + "st_nlink == 0, reopen %s", + nwrap->path); + retried = true; + memset(&nwrap->st, 0, sizeof(nwrap->st)); + fclose(nwrap->fp); + nwrap->fp = NULL; + nwrap->fd = -1; + goto reopen; + } + + if (st.st_mtime == nwrap->st.st_mtime) { + NWRAP_LOG(NWRAP_LOG_TRACE, + "st_mtime[%u] hasn't changed, skip reload", + (unsigned)st.st_mtime); + return true; + } + + NWRAP_LOG(NWRAP_LOG_TRACE, + "st_mtime has changed [%u] => [%u], start reload", + (unsigned)st.st_mtime, + (unsigned)nwrap->st.st_mtime); + + nwrap->st = st; + + nwrap_files_cache_unload(nwrap); + + ok = nwrap_parse_file(nwrap); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Failed to reload %s", nwrap->path); + nwrap_files_cache_unload(nwrap); + return false; + } + + NWRAP_LOG(NWRAP_LOG_TRACE, "Reloaded %s", nwrap->path); + return true; +} + +/* + * the caller has to call nwrap_unload() on failure + */ +static bool nwrap_pw_parse_line(struct nwrap_cache *nwrap, char *line) +{ + struct nwrap_pw *nwrap_pw; + char *c; + char *p; + char *e; + struct passwd *pw; + size_t list_size; + + nwrap_pw = (struct nwrap_pw *)nwrap->private_data; + + list_size = sizeof(*nwrap_pw->list) * (nwrap_pw->num+1); + pw = (struct passwd *)realloc(nwrap_pw->list, list_size); + if (!pw) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "realloc(%u) failed", + (unsigned)list_size); + return false; + } + nwrap_pw->list = pw; + + pw = &nwrap_pw->list[nwrap_pw->num]; + + c = line; + + /* name */ + p = strchr(c, ':'); + if (!p) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Invalid line[%s]: '%s'", + line, + c); + return false; + } + *p = '\0'; + p++; + pw->pw_name = c; + c = p; + + NWRAP_LOG(NWRAP_LOG_TRACE, "name[%s]\n", pw->pw_name); + + /* password */ + p = strchr(c, ':'); + if (!p) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c); + return false; + } + *p = '\0'; + p++; + pw->pw_passwd = c; + c = p; + + NWRAP_LOG(NWRAP_LOG_TRACE, "password[%s]\n", pw->pw_passwd); + + /* uid */ + p = strchr(c, ':'); + if (!p) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c); + return false; + } + *p = '\0'; + p++; + e = NULL; + pw->pw_uid = (uid_t)strtoul(c, &e, 10); + if (c == e) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e[0] != '\0') { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + c = p; + + NWRAP_LOG(NWRAP_LOG_TRACE, "uid[%u]", pw->pw_uid); + + /* gid */ + p = strchr(c, ':'); + if (!p) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c); + return false; + } + *p = '\0'; + p++; + e = NULL; + pw->pw_gid = (gid_t)strtoul(c, &e, 10); + if (c == e) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e[0] != '\0') { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + c = p; + + NWRAP_LOG(NWRAP_LOG_TRACE, "gid[%u]\n", pw->pw_gid); + +#ifdef HAVE_STRUCT_PASSWD_PW_CLASS + pw->pw_class = discard_const_p(char, ""); + + NWRAP_LOG(NWRAP_LOG_TRACE, "class[%s]", pw->pw_class); +#endif /* HAVE_STRUCT_PASSWD_PW_CLASS */ + +#ifdef HAVE_STRUCT_PASSWD_PW_CHANGE + pw->pw_change = 0; + + NWRAP_LOG(NWRAP_LOG_TRACE, + "change[%lu]", + (unsigned long)pw->pw_change); +#endif /* HAVE_STRUCT_PASSWD_PW_CHANGE */ + +#ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE + pw->pw_expire = 0; + + NWRAP_LOG(NWRAP_LOG_TRACE, + "expire[%lu]", + (unsigned long)pw->pw_expire); +#endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE */ + + /* gecos */ + p = strchr(c, ':'); + if (!p) { + NWRAP_LOG(NWRAP_LOG_ERROR, "invalid line[%s]: '%s'", line, c); + return false; + } + *p = '\0'; + p++; + pw->pw_gecos = c; + c = p; + + NWRAP_LOG(NWRAP_LOG_TRACE, "gecos[%s]", pw->pw_gecos); + + /* dir */ + p = strchr(c, ':'); + if (!p) { + NWRAP_LOG(NWRAP_LOG_ERROR, "'%s'", c); + return false; + } + *p = '\0'; + p++; + pw->pw_dir = c; + c = p; + + NWRAP_LOG(NWRAP_LOG_TRACE, "dir[%s]", pw->pw_dir); + + /* shell */ + pw->pw_shell = c; + NWRAP_LOG(NWRAP_LOG_TRACE, "shell[%s]", pw->pw_shell); + + NWRAP_LOG(NWRAP_LOG_DEBUG, + "Added user[%s:%s:%u:%u:%s:%s:%s]", + pw->pw_name, pw->pw_passwd, + pw->pw_uid, pw->pw_gid, + pw->pw_gecos, pw->pw_dir, pw->pw_shell); + + nwrap_pw->num++; + return true; +} + +static void nwrap_pw_unload(struct nwrap_cache *nwrap) +{ + struct nwrap_pw *nwrap_pw; + nwrap_pw = (struct nwrap_pw *)nwrap->private_data; + + SAFE_FREE(nwrap_pw->list); + nwrap_pw->num = 0; + nwrap_pw->idx = 0; +} + +static int nwrap_pw_copy_r(const struct passwd *src, struct passwd *dst, + char *buf, size_t buflen, struct passwd **dstp) +{ + char *first; + char *last; + off_t ofs; + + first = src->pw_name; + + last = src->pw_shell; + while (*last) last++; + + ofs = PTR_DIFF(last + 1, first); + + if (ofs > (off_t) buflen) { + return ERANGE; + } + + memcpy(buf, first, ofs); + + ofs = PTR_DIFF(src->pw_name, first); + dst->pw_name = buf + ofs; + ofs = PTR_DIFF(src->pw_passwd, first); + dst->pw_passwd = buf + ofs; + dst->pw_uid = src->pw_uid; + dst->pw_gid = src->pw_gid; +#ifdef HAVE_STRUCT_PASSWD_PW_CLASS + ofs = PTR_DIFF(src->pw_class, first); + dst->pw_class = buf + ofs; +#endif /* HAVE_STRUCT_PASSWD_PW_CLASS */ + +#ifdef HAVE_STRUCT_PASSWD_PW_CHANGE + dst->pw_change = 0; +#endif /* HAVE_STRUCT_PASSWD_PW_CHANGE */ + +#ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE + dst->pw_expire = 0; +#endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE */ + + ofs = PTR_DIFF(src->pw_gecos, first); + dst->pw_gecos = buf + ofs; + ofs = PTR_DIFF(src->pw_dir, first); + dst->pw_dir = buf + ofs; + ofs = PTR_DIFF(src->pw_shell, first); + dst->pw_shell = buf + ofs; + + if (dstp) { + *dstp = dst; + } + + return 0; +} + +#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) +static bool nwrap_sp_parse_line(struct nwrap_cache *nwrap, char *line) +{ + struct nwrap_sp *nwrap_sp; + struct spwd *sp; + size_t list_size; + char *c; + char *e; + char *p; + + nwrap_sp = (struct nwrap_sp *)nwrap->private_data; + + list_size = sizeof(*nwrap_sp->list) * (nwrap_sp->num+1); + sp = (struct spwd *)realloc(nwrap_sp->list, list_size); + if (sp == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "realloc(%u) failed", + (unsigned)list_size); + return false; + } + nwrap_sp->list = sp; + + sp = &nwrap_sp->list[nwrap_sp->num]; + + c = line; + + /* name */ + p = strchr(c, ':'); + if (p == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "name -- Invalid line[%s]: '%s'", + line, + c); + return false; + } + *p = '\0'; + p++; + sp->sp_namp = c; + c = p; + + NWRAP_LOG(NWRAP_LOG_TRACE, "name[%s]\n", sp->sp_namp); + + /* pwd */ + p = strchr(c, ':'); + if (p == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "pwd -- Invalid line[%s]: '%s'", + line, + c); + return false; + } + *p = '\0'; + p++; + sp->sp_pwdp = c; + c = p; + + /* lstchg (long) */ + if (c[0] == ':') { + sp->sp_lstchg = -1; + p++; + } else { + p = strchr(c, ':'); + if (p == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "lstchg -- Invalid line[%s]: '%s'", + line, + c); + return false; + } + *p = '\0'; + p++; + sp->sp_lstchg = strtol(c, &e, 10); + if (c == e) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "lstchg -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "lstchg -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e[0] != '\0') { + NWRAP_LOG(NWRAP_LOG_ERROR, + "lstchg -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + } + c = p; + + /* min (long) */ + if (c[0] == ':') { + sp->sp_min = -1; + p++; + } else { + p = strchr(c, ':'); + if (p == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "min -- Invalid line[%s]: '%s'", + line, + c); + return false; + } + *p = '\0'; + p++; + sp->sp_min = strtol(c, &e, 10); + if (c == e) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "min -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "min -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e[0] != '\0') { + NWRAP_LOG(NWRAP_LOG_ERROR, + "min -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + } + c = p; + + /* max (long) */ + if (c[0] == ':') { + sp->sp_max = -1; + p++; + } else { + p = strchr(c, ':'); + if (p == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "max -- Invalid line[%s]: '%s'", + line, + c); + return false; + } + *p = '\0'; + p++; + sp->sp_max = strtol(c, &e, 10); + if (c == e) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "max -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "max -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e[0] != '\0') { + NWRAP_LOG(NWRAP_LOG_ERROR, + "max -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + } + c = p; + + /* warn (long) */ + if (c[0] == ':') { + sp->sp_warn = -1; + p++; + } else { + p = strchr(c, ':'); + if (p == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "warn -- Invalid line[%s]: '%s'", + line, + c); + return false; + } + *p = '\0'; + p++; + sp->sp_warn = strtol(c, &e, 10); + if (c == e) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "warn -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "warn -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e[0] != '\0') { + NWRAP_LOG(NWRAP_LOG_ERROR, + "warn -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + } + c = p; + + /* inact (long) */ + if (c[0] == ':') { + sp->sp_inact = -1; + p++; + } else { + p = strchr(c, ':'); + if (p == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "inact -- Invalid line[%s]: '%s'", + line, + c); + return false; + } + *p = '\0'; + p++; + sp->sp_inact = strtol(c, &e, 10); + if (c == e) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "inact -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "inact -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e[0] != '\0') { + NWRAP_LOG(NWRAP_LOG_ERROR, + "inact -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + } + c = p; + + /* expire (long) */ + if (c[0] == ':') { + sp->sp_expire = -1; + p++; + } else { + p = strchr(c, ':'); + if (p == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "expire -- Invalid line[%s]: '%s'", + line, + c); + return false; + } + *p = '\0'; + p++; + sp->sp_expire = strtol(c, &e, 10); + if (c == e) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "expire -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "expire -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e[0] != '\0') { + NWRAP_LOG(NWRAP_LOG_ERROR, + "expire -- Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + } + c = p; + + nwrap_sp->num++; + return true; +} + +static void nwrap_sp_unload(struct nwrap_cache *nwrap) +{ + struct nwrap_sp *nwrap_sp; + nwrap_sp = (struct nwrap_sp *)nwrap->private_data; + + SAFE_FREE(nwrap_sp->list); + nwrap_sp->num = 0; + nwrap_sp->idx = 0; +} +#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */ + +/* + * the caller has to call nwrap_unload() on failure + */ +static bool nwrap_gr_parse_line(struct nwrap_cache *nwrap, char *line) +{ + struct nwrap_gr *nwrap_gr; + char *c; + char *p; + char *e; + struct group *gr; + size_t list_size; + unsigned nummem; + + nwrap_gr = (struct nwrap_gr *)nwrap->private_data; + + list_size = sizeof(*nwrap_gr->list) * (nwrap_gr->num+1); + gr = (struct group *)realloc(nwrap_gr->list, list_size); + if (!gr) { + NWRAP_LOG(NWRAP_LOG_ERROR, "realloc failed"); + return false; + } + nwrap_gr->list = gr; + + gr = &nwrap_gr->list[nwrap_gr->num]; + + c = line; + + /* name */ + p = strchr(c, ':'); + if (!p) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c); + return false; + } + *p = '\0'; + p++; + gr->gr_name = c; + c = p; + + NWRAP_LOG(NWRAP_LOG_TRACE, "name[%s]", gr->gr_name); + + /* password */ + p = strchr(c, ':'); + if (!p) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c); + return false; + } + *p = '\0'; + p++; + gr->gr_passwd = c; + c = p; + + NWRAP_LOG(NWRAP_LOG_TRACE, "password[%s]", gr->gr_passwd); + + /* gid */ + p = strchr(c, ':'); + if (!p) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c); + return false; + } + *p = '\0'; + p++; + e = NULL; + gr->gr_gid = (gid_t)strtoul(c, &e, 10); + if (c == e) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + if (e[0] != '\0') { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Invalid line[%s]: '%s' - %s", + line, c, strerror(errno)); + return false; + } + c = p; + + NWRAP_LOG(NWRAP_LOG_TRACE, "gid[%u]", gr->gr_gid); + + /* members */ + gr->gr_mem = (char **)malloc(sizeof(char *)); + if (!gr->gr_mem) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Out of memory"); + return false; + } + gr->gr_mem[0] = NULL; + + for(nummem = 0; p != NULL && p[0] != '\0'; nummem++) { + char **m; + size_t m_size; + c = p; + p = strchr(c, ','); + if (p) { + *p = '\0'; + p++; + } + + if (strlen(c) == 0) { + break; + } + + m_size = sizeof(char *) * (nummem+2); + m = (char **)realloc(gr->gr_mem, m_size); + if (!m) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "realloc(%zd) failed", + m_size); + return false; + } + gr->gr_mem = m; + gr->gr_mem[nummem] = c; + gr->gr_mem[nummem+1] = NULL; + + NWRAP_LOG(NWRAP_LOG_TRACE, + "member[%u]: '%s'", + nummem, gr->gr_mem[nummem]); + } + + NWRAP_LOG(NWRAP_LOG_DEBUG, + "Added group[%s:%s:%u:] with %u members", + gr->gr_name, gr->gr_passwd, gr->gr_gid, nummem); + + nwrap_gr->num++; + return true; +} + +static void nwrap_gr_unload(struct nwrap_cache *nwrap) +{ + int i; + struct nwrap_gr *nwrap_gr; + nwrap_gr = (struct nwrap_gr *)nwrap->private_data; + + if (nwrap_gr->list) { + for (i=0; i < nwrap_gr->num; i++) { + SAFE_FREE(nwrap_gr->list[i].gr_mem); + } + SAFE_FREE(nwrap_gr->list); + } + + nwrap_gr->num = 0; + nwrap_gr->idx = 0; +} + +static struct nwrap_entlist *nwrap_entlist_init(struct nwrap_entdata *ed) +{ + struct nwrap_entlist *el; + + if (ed == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "entry is NULL, can't create list item"); + return NULL; + } + + el = (struct nwrap_entlist *)malloc(sizeof(struct nwrap_entlist)); + if (el == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, "malloc failed"); + return NULL; + } + + el->next = NULL; + el->ed = ed; + + return el; +} + +static bool nwrap_ed_inventarize_add_new(char *const h_name, + struct nwrap_entdata *const ed) +{ + ENTRY e; + ENTRY *p; + struct nwrap_entlist *el; + bool ok; + + if (h_name == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, "h_name NULL - can't add"); + return false; + } + + el = nwrap_entlist_init(ed); + if (el == NULL) { + return false; + } + + e.key = h_name; + e.data = (void *)el; + + p = hsearch(e, ENTER); + if (p == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Hash table is full (%s)!", + strerror(errno)); + return false; + } + + ok = nwrap_vector_add_item(&(nwrap_he_global.lists), (void *)el); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Failed to add list entry to vector."); + return false; + } + + return true; +} + +static bool nwrap_ed_inventarize_add_to_existing(struct nwrap_entdata *const ed, + struct nwrap_entlist *const el) +{ + struct nwrap_entlist *cursor; + struct nwrap_entlist *el_new; + + if (el == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, "list is NULL, can not add"); + return false; + } + + + for (cursor = el; cursor->next != NULL; cursor = cursor->next) + { + if (cursor->ed == ed) { + /* The entry already exists in this list. */ + return true; + } + } + + if (cursor->ed == ed) { + /* The entry already exists in this list. */ + return true; + } + + el_new = nwrap_entlist_init(ed); + if (el_new == NULL) { + return false; + } + + cursor->next = el_new; + return true; +} + +static bool nwrap_ed_inventarize(char *const name, + struct nwrap_entdata *const ed) +{ + ENTRY e; + ENTRY *p; + bool ok; + + e.key = name; + e.data = NULL; + + NWRAP_LOG(NWRAP_LOG_DEBUG, "Searching name: %s", e.key); + + p = hsearch(e, FIND); + if (p == NULL) { + NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found. Adding...", name); + ok = nwrap_ed_inventarize_add_new(name, ed); + } else { + struct nwrap_entlist *el = (struct nwrap_entlist *)p->data; + + NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s found. Add record to list.", name); + ok = nwrap_ed_inventarize_add_to_existing(ed, el); + } + + return ok; +} + +static bool nwrap_add_hname(struct nwrap_entdata *const ed) +{ + char *const h_name = (char *const)(ed->ht.h_name); + unsigned i; + bool ok; + + ok = nwrap_ed_inventarize(h_name, ed); + if (!ok) { + return false; + } + + if (ed->ht.h_aliases == NULL) { + return true; + } + + /* Itemize aliases */ + for (i = 0; ed->ht.h_aliases[i] != NULL; ++i) { + char *h_name_alias; + + h_name_alias = ed->ht.h_aliases[i]; + + NWRAP_LOG(NWRAP_LOG_DEBUG, "Add alias: %s", h_name_alias); + + if (!nwrap_ed_inventarize(h_name_alias, ed)) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Unable to add alias: %s", h_name_alias); + return false; + } + } + + return true; +} + +static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line) +{ + struct nwrap_he *nwrap_he = (struct nwrap_he *)nwrap->private_data; + bool do_aliases = true; + ssize_t aliases_count = 0; + char *p; + char *i; + char *n; + + char *ip; + bool ok; + + struct nwrap_entdata *ed = (struct nwrap_entdata *) + malloc(sizeof(struct nwrap_entdata)); + if (ed == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Unable to allocate memory for nwrap_entdata"); + return false; + } + ZERO_STRUCTP(ed); + + i = line; + + /* + * IP + */ + + /* Walk to first char */ + for (p = i; *p != '.' && *p != ':' && !isxdigit((int) *p); p++) { + if (*p == '\0') { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Invalid line[%s]: '%s'", + line, i); + free(ed); + return false; + } + } + + for (i = p; !isspace((int)*p); p++) { + if (*p == '\0') { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Invalid line[%s]: '%s'", + line, i); + free(ed); + return false; + } + } + + *p = '\0'; + + if (inet_pton(AF_INET, i, ed->addr.host_addr)) { + ed->ht.h_addrtype = AF_INET; + ed->ht.h_length = 4; +#ifdef HAVE_IPV6 + } else if (inet_pton(AF_INET6, i, ed->addr.host_addr)) { + ed->ht.h_addrtype = AF_INET6; + ed->ht.h_length = 16; +#endif + } else { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Invalid line[%s]: '%s'", + line, i); + + free(ed); + return false; + } + ip = i; + + ok = nwrap_vector_add_item(&(ed->nwrap_addrdata), + (void *const)ed->addr.host_addr); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Unable to add addrdata to vector"); + free(ed); + return false; + } + ed->ht.h_addr_list = nwrap_vector_head(&ed->nwrap_addrdata); + + p++; + + /* + * FQDN + */ + + /* Walk to first char */ + for (n = p; *p != '_' && !isalnum((int) *p); p++) { + if (*p == '\0') { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Invalid line[%s]: '%s'", + line, n); + + free(ed); + return false; + } + } + + for (n = p; !isspace((int)*p); p++) { + if (*p == '\0') { + do_aliases = false; + break; + } + } + + *p = '\0'; + + /* Convert to lowercase. This operate on same memory region */ + str_tolower(n, n); + ed->ht.h_name = n; + + /* glib's getent always dereferences he->h_aliases */ + ed->ht.h_aliases = malloc(sizeof(char *)); + if (ed->ht.h_aliases == NULL) { + free(ed); + return false; + } + ed->ht.h_aliases[0] = NULL; + + /* + * Aliases + */ + while (do_aliases) { + char **aliases; + char *a; + + p++; + + /* Walk to first char */ + for (a = p; *p != '_' && !isalnum((int) *p); p++) { + if (*p == '\0') { + do_aliases = false; + break; + } + } + /* Only trailing spaces are left */ + if (!do_aliases) { + break; + } + + for (a = p; !isspace((int)*p); p++) { + if (*p == '\0') { + do_aliases = false; + break; + } + } + + *p = '\0'; + + aliases = realloc(ed->ht.h_aliases, sizeof(char *) * (aliases_count + 2)); + if (aliases == NULL) { + free(ed); + return false; + } + ed->ht.h_aliases = aliases; + + str_tolower(a, a); + aliases[aliases_count] = a; + aliases[aliases_count + 1] = NULL; + + aliases_count += 1; + } + + ok = nwrap_vector_add_item(&(nwrap_he->entries), (void *const)ed); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Unable to add entry to vector"); + free(ed); + return false; + } + + ed->aliases_count = aliases_count; + /* Inventarize item */ + ok = nwrap_add_hname(ed); + if (!ok) { + return false; + } + + ok = nwrap_ed_inventarize(ip, ed); + if (!ok) { + return false; + } + + nwrap_he->num++; + return true; +} + +static void nwrap_he_unload(struct nwrap_cache *nwrap) +{ + struct nwrap_he *nwrap_he = + (struct nwrap_he *)nwrap->private_data; + struct nwrap_entdata *ed; + struct nwrap_entlist *el; + size_t i; + int rc; + + nwrap_vector_foreach (ed, nwrap_he->entries, i) + { + SAFE_FREE(ed->nwrap_addrdata.items); + SAFE_FREE(ed->ht.h_aliases); + SAFE_FREE(ed); + } + SAFE_FREE(nwrap_he->entries.items); + nwrap_he->entries.count = nwrap_he->entries.capacity = 0; + + nwrap_vector_foreach(el, nwrap_he->lists, i) + { + while (el != NULL) { + struct nwrap_entlist *el_next; + + el_next = el->next; + SAFE_FREE(el); + el = el_next; + } + } + SAFE_FREE(nwrap_he->lists.items); + nwrap_he->lists.count = nwrap_he->lists.capacity = 0; + + nwrap_he->num = 0; + nwrap_he->idx = 0; + + /* + * If we unload the file, the pointers in the hash table point to + * invalid memory. So we need to destroy the hash table and recreate + * it. + */ + hdestroy(); + rc = hcreate(max_hostents); + if (rc == 0) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Failed to initialize hash table"); + exit(-1); + } +} + + +/* user functions */ +static struct passwd *nwrap_files_getpwnam(struct nwrap_backend *b, + const char *name) +{ + int i; + bool ok; + + (void) b; /* unused */ + + NWRAP_LOG(NWRAP_LOG_DEBUG, "Lookup user %s in files", name); + + ok = nwrap_files_cache_reload(nwrap_pw_global.cache); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading passwd file"); + return NULL; + } + + for (i=0; i<nwrap_pw_global.num; i++) { + if (strcmp(nwrap_pw_global.list[i].pw_name, name) == 0) { + NWRAP_LOG(NWRAP_LOG_DEBUG, "user[%s] found", name); + return &nwrap_pw_global.list[i]; + } + NWRAP_LOG(NWRAP_LOG_DEBUG, + "user[%s] does not match [%s]", + name, + nwrap_pw_global.list[i].pw_name); + } + + NWRAP_LOG(NWRAP_LOG_DEBUG, "user[%s] not found\n", name); + + errno = ENOENT; + return NULL; +} + +static int nwrap_files_getpwnam_r(struct nwrap_backend *b, + const char *name, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp) +{ + struct passwd *pw; + + pw = nwrap_files_getpwnam(b, name); + if (!pw) { + if (errno == 0) { + return ENOENT; + } + return errno; + } + + return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp); +} + +static struct passwd *nwrap_files_getpwuid(struct nwrap_backend *b, + uid_t uid) +{ + int i; + bool ok; + + (void) b; /* unused */ + + ok = nwrap_files_cache_reload(nwrap_pw_global.cache); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading passwd file"); + return NULL; + } + + for (i=0; i<nwrap_pw_global.num; i++) { + if (nwrap_pw_global.list[i].pw_uid == uid) { + NWRAP_LOG(NWRAP_LOG_DEBUG, "uid[%u] found", uid); + return &nwrap_pw_global.list[i]; + } + NWRAP_LOG(NWRAP_LOG_DEBUG, + "uid[%u] does not match [%u]", + uid, + nwrap_pw_global.list[i].pw_uid); + } + + NWRAP_LOG(NWRAP_LOG_DEBUG, "uid[%u] not found\n", uid); + + errno = ENOENT; + return NULL; +} + +static int nwrap_files_getpwuid_r(struct nwrap_backend *b, + uid_t uid, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp) +{ + struct passwd *pw; + + pw = nwrap_files_getpwuid(b, uid); + if (!pw) { + if (errno == 0) { + return ENOENT; + } + return errno; + } + + return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp); +} + +/* user enum functions */ +static void nwrap_files_setpwent(struct nwrap_backend *b) +{ + (void) b; /* unused */ + + nwrap_pw_global.idx = 0; +} + +static struct passwd *nwrap_files_getpwent(struct nwrap_backend *b) +{ + struct passwd *pw; + + (void) b; /* unused */ + + if (nwrap_pw_global.idx == 0) { + bool ok; + ok = nwrap_files_cache_reload(nwrap_pw_global.cache); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading passwd file"); + return NULL; + } + } + + if (nwrap_pw_global.idx >= nwrap_pw_global.num) { + errno = ENOENT; + return NULL; + } + + pw = &nwrap_pw_global.list[nwrap_pw_global.idx++]; + + NWRAP_LOG(NWRAP_LOG_DEBUG, + "return user[%s] uid[%u]", + pw->pw_name, pw->pw_uid); + + return pw; +} + +static int nwrap_files_getpwent_r(struct nwrap_backend *b, + struct passwd *pwdst, char *buf, + size_t buflen, struct passwd **pwdstp) +{ + struct passwd *pw; + + pw = nwrap_files_getpwent(b); + if (!pw) { + if (errno == 0) { + return ENOENT; + } + return errno; + } + + return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp); +} + +static void nwrap_files_endpwent(struct nwrap_backend *b) +{ + (void) b; /* unused */ + + nwrap_pw_global.idx = 0; +} + +/* shadow */ + +#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) + +#ifdef HAVE_SETSPENT +static void nwrap_files_setspent(void) +{ + nwrap_sp_global.idx = 0; +} + +static struct spwd *nwrap_files_getspent(void) +{ + struct spwd *sp; + + if (nwrap_sp_global.idx == 0) { + bool ok; + + ok = nwrap_files_cache_reload(nwrap_sp_global.cache); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading shadow file"); + return NULL; + } + } + + if (nwrap_sp_global.idx >= nwrap_sp_global.num) { + errno = ENOENT; + return NULL; + } + + sp = &nwrap_sp_global.list[nwrap_sp_global.idx++]; + + NWRAP_LOG(NWRAP_LOG_DEBUG, + "return user[%s]", + sp->sp_namp); + + return sp; +} + +static void nwrap_files_endspent(void) +{ + nwrap_sp_global.idx = 0; +} +#endif /* HAVE_SETSPENT */ + +static struct spwd *nwrap_files_getspnam(const char *name) +{ + int i; + bool ok; + + NWRAP_LOG(NWRAP_LOG_DEBUG, "Lookup user %s in files", name); + + ok = nwrap_files_cache_reload(nwrap_sp_global.cache); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading shadow file"); + return NULL; + } + + for (i=0; i<nwrap_sp_global.num; i++) { + if (strcmp(nwrap_sp_global.list[i].sp_namp, name) == 0) { + NWRAP_LOG(NWRAP_LOG_DEBUG, "user[%s] found", name); + return &nwrap_sp_global.list[i]; + } + NWRAP_LOG(NWRAP_LOG_DEBUG, + "user[%s] does not match [%s]", + name, + nwrap_sp_global.list[i].sp_namp); + } + + NWRAP_LOG(NWRAP_LOG_DEBUG, "user[%s] not found\n", name); + + errno = ENOENT; + return NULL; +} +#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */ + +/* misc functions */ +static int nwrap_files_initgroups_dyn(struct nwrap_backend *b, + const char *user, + gid_t group, + long int *start, + long int *size, + gid_t **groups, + long int limit, + int *errnop) +{ + struct group *grp; + int i = 0; + + (void)errnop; /* unused */ + nwrap_files_setgrent(b); + while ((grp = nwrap_files_getgrent(b)) != NULL) { + NWRAP_LOG(NWRAP_LOG_DEBUG, + "Inspecting %s for group membership", + grp->gr_name); + + for (i=0; grp->gr_mem && grp->gr_mem[i] != NULL; i++) { + if (group != grp->gr_gid && + (strcmp(user, grp->gr_mem[i]) == 0)) { + NWRAP_LOG(NWRAP_LOG_DEBUG, + "%s is member of %s", + user, + grp->gr_name); + + if (*start == *size) { + long int newsize; + gid_t *newgroups; + + newsize = 2 * (*size); + if (limit > 0 && newsize > limit) { + newsize = MAX(limit, *size); + } + newgroups = (gid_t *) realloc((*groups), + newsize * sizeof(**groups)); + if (!newgroups) { + errno = ENOMEM; + return -1; + } + *groups = newgroups; + *size = newsize; + } + (*groups)[*start] = grp->gr_gid; + (*start)++; + } + } + } + + nwrap_files_endgrent(b); + return *start; +} + +/* group functions */ +static struct group *nwrap_files_getgrnam(struct nwrap_backend *b, + const char *name) +{ + int i; + bool ok; + + (void) b; /* unused */ + + ok = nwrap_files_cache_reload(nwrap_gr_global.cache); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading group file"); + return NULL; + } + + for (i=0; i<nwrap_gr_global.num; i++) { + if (strcmp(nwrap_gr_global.list[i].gr_name, name) == 0) { + NWRAP_LOG(NWRAP_LOG_DEBUG, "group[%s] found", name); + return &nwrap_gr_global.list[i]; + } + NWRAP_LOG(NWRAP_LOG_DEBUG, + "group[%s] does not match [%s]", + name, + nwrap_gr_global.list[i].gr_name); + } + + NWRAP_LOG(NWRAP_LOG_DEBUG, "group[%s] not found", name); + + errno = ENOENT; + return NULL; +} + +static int nwrap_files_getgrnam_r(struct nwrap_backend *b, + const char *name, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp) +{ + struct group *gr; + + gr = nwrap_files_getgrnam(b, name); + if (!gr) { + if (errno == 0) { + return ENOENT; + } + return errno; + } + + return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp); +} + +static struct group *nwrap_files_getgrgid(struct nwrap_backend *b, + gid_t gid) +{ + int i; + bool ok; + + (void) b; /* unused */ + + ok = nwrap_files_cache_reload(nwrap_gr_global.cache); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading group file"); + return NULL; + } + + for (i=0; i<nwrap_gr_global.num; i++) { + if (nwrap_gr_global.list[i].gr_gid == gid) { + NWRAP_LOG(NWRAP_LOG_DEBUG, "gid[%u] found", gid); + return &nwrap_gr_global.list[i]; + } + NWRAP_LOG(NWRAP_LOG_DEBUG, + "gid[%u] does not match [%u]", + gid, + nwrap_gr_global.list[i].gr_gid); + } + + NWRAP_LOG(NWRAP_LOG_DEBUG, "gid[%u] not found", gid); + + errno = ENOENT; + return NULL; +} + +static int nwrap_files_getgrgid_r(struct nwrap_backend *b, + gid_t gid, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp) +{ + struct group *gr; + + gr = nwrap_files_getgrgid(b, gid); + if (!gr) { + if (errno == 0) { + return ENOENT; + } + return errno; + } + + return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp); +} + +/* group enum functions */ +static void nwrap_files_setgrent(struct nwrap_backend *b) +{ + (void) b; /* unused */ + + nwrap_gr_global.idx = 0; +} + +static struct group *nwrap_files_getgrent(struct nwrap_backend *b) +{ + struct group *gr; + + (void) b; /* unused */ + + if (nwrap_gr_global.idx == 0) { + bool ok; + + ok = nwrap_files_cache_reload(nwrap_gr_global.cache); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading group file"); + return NULL; + } + } + + if (nwrap_gr_global.idx >= nwrap_gr_global.num) { + errno = ENOENT; + return NULL; + } + + gr = &nwrap_gr_global.list[nwrap_gr_global.idx++]; + + NWRAP_LOG(NWRAP_LOG_DEBUG, + "return group[%s] gid[%u]", + gr->gr_name, gr->gr_gid); + + return gr; +} + +static int nwrap_files_getgrent_r(struct nwrap_backend *b, + struct group *grdst, char *buf, + size_t buflen, struct group **grdstp) +{ + struct group *gr; + + gr = nwrap_files_getgrent(b); + if (!gr) { + if (errno == 0) { + return ENOENT; + } + return errno; + } + + return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp); +} + +static void nwrap_files_endgrent(struct nwrap_backend *b) +{ + (void) b; /* unused */ + + nwrap_gr_global.idx = 0; +} + +/* hosts functions */ +static int nwrap_files_internal_gethostbyname(const char *name, int af, + struct hostent *result, + struct nwrap_vector *addr_list) +{ + struct nwrap_entlist *el; + struct hostent *he; + char *h_name_lower; + ENTRY e; + ENTRY *e_p; + char canon_name[DNS_NAME_MAX] = { 0 }; + size_t name_len; + bool he_found = false; + bool ok; + + /* + * We need to make sure we have zeroed return pointer for consumers + * which don't check return values, e.g. OpenLDAP. + */ + ZERO_STRUCTP(result); + + ok = nwrap_files_cache_reload(nwrap_he_global.cache); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, "error loading hosts file"); + goto no_ent; + } + + name_len = strlen(name); + if (name_len < sizeof(canon_name) && name[name_len - 1] == '.') { + memcpy(canon_name, name, name_len - 1); + canon_name[name_len] = '\0'; + name = canon_name; + } + + if (!str_tolower_copy(&h_name_lower, name)) { + NWRAP_LOG(NWRAP_LOG_DEBUG, + "Out of memory while converting to lower case"); + goto no_ent; + } + + /* Look at hash table for element */ + NWRAP_LOG(NWRAP_LOG_DEBUG, "Searching for name: %s", h_name_lower); + e.key = h_name_lower; + e.data = NULL; + e_p = hsearch(e, FIND); + if (e_p == NULL) { + NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found.", h_name_lower); + SAFE_FREE(h_name_lower); + goto no_ent; + } + SAFE_FREE(h_name_lower); + + /* Always cleanup vector and results */ + if (!nwrap_vector_is_initialized(addr_list)) { + if (!nwrap_vector_init(addr_list)) { + NWRAP_LOG(NWRAP_LOG_DEBUG, + "Unable to initialize memory for addr_list vector"); + goto no_ent; + } + } else { + /* When vector is initialized data are valid no more. + * Quick way how to free vector is: */ + addr_list->count = 0; + } + + /* Iterate through results */ + for (el = (struct nwrap_entlist *)e_p->data; el != NULL; el = el->next) + { + he = &(el->ed->ht); + + /* Filter by address familiy if provided */ + if (af != AF_UNSPEC && he->h_addrtype != af) { + continue; + } + + /* + * GLIBC HACK? + * glibc doesn't return ipv6 addresses when AF_UNSPEC is used + */ + if (af == AF_UNSPEC && he->h_addrtype != AF_INET) { + continue; + } + + if (!he_found) { + memcpy(result, he, sizeof(struct hostent)); + NWRAP_LOG(NWRAP_LOG_DEBUG, + "Name found. Returning record for %s", + he->h_name); + he_found = true; + } + nwrap_vector_merge(addr_list, &el->ed->nwrap_addrdata); + result->h_addr_list = nwrap_vector_head(addr_list); + } + + if (he_found) { + return 0; + } + NWRAP_LOG(NWRAP_LOG_DEBUG, + "Name found in database. No records matches type."); + +no_ent: + errno = ENOENT; + return -1; +} + +static int nwrap_files_gethostbyname2_r(struct nwrap_backend *b, + const char *name, int af, + struct hostent *hedst, + char *buf, size_t buflen, + struct hostent **hedstp) +{ + struct nwrap_vector *addr_list = NULL; + union { + char *ptr; + char **list; + } g; + int rc; + + (void) b; /* unused */ + (void) af; /* unused */ + + if (name == NULL || hedst == NULL || buf == NULL || buflen == 0) { + errno = EINVAL; + return -1; + } + *hedstp = NULL; + buf[0] = '\0'; + + addr_list = calloc(1, sizeof(struct nwrap_vector)); + if (addr_list == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Unable to allocate memory for address list"); + errno = ENOENT; + return -1; + } + + rc = nwrap_files_internal_gethostbyname(name, af, hedst, + addr_list); + if (rc == -1) { + SAFE_FREE(addr_list->items); + SAFE_FREE(addr_list); + errno = ENOENT; + return -1; + } + + /* +1 i for ending NULL pointer */ + if (buflen < ((addr_list->count + 1) * sizeof(void *))) { + SAFE_FREE(addr_list->items); + SAFE_FREE(addr_list); + return ERANGE; + } + + /* Copy all to user provided buffer and change + * pointers in returned structure. + * +1 is for ending NULL pointer. */ + memcpy(buf, addr_list->items, (addr_list->count + 1) * sizeof(void *)); + + SAFE_FREE(addr_list->items); + SAFE_FREE(addr_list); + + g.ptr = buf; + hedst->h_addr_list = g.list; + *hedstp = hedst; + return 0; +} + +#ifdef HAVE_GETHOSTBYNAME_R +static int nwrap_gethostbyname_r(const char *name, + struct hostent *ret, + char *buf, size_t buflen, + struct hostent **result, int *h_errnop) +{ + int rc; + size_t i; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + rc = b->ops->nw_gethostbyname2_r(b, name, AF_UNSPEC, ret, + buf, buflen, result); + if (rc == 0) { + return 0; + } else if (rc == ERANGE) { + return ERANGE; + } + } + *h_errnop = h_errno; + return ENOENT; +} + +int gethostbyname_r(const char *name, + struct hostent *ret, + char *buf, size_t buflen, + struct hostent **result, int *h_errnop) +{ + if (!nss_wrapper_hosts_enabled()) { + return libc_gethostbyname_r(name, + ret, + buf, + buflen, + result, + h_errnop); + } + + return nwrap_gethostbyname_r(name, ret, buf, buflen, result, h_errnop); +} +#endif + +#ifdef HAVE_GETHOSTBYNAME2_R +static int nwrap_gethostbyname2_r(const char *name, int af, + struct hostent *ret, + char *buf, size_t buflen, + struct hostent **result, int *h_errnop) +{ + int rc; + size_t i; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + rc = b->ops->nw_gethostbyname2_r(b, name, af, ret, + buf, buflen, result); + if (rc == 0) { + return 0; + } else if (rc == ERANGE) { + return ERANGE; + } + } + *h_errnop = h_errno; + return ENOENT; +} + +int gethostbyname2_r(const char *name, int af, + struct hostent *ret, + char *buf, size_t buflen, + struct hostent **result, int *h_errnop) +{ + if (!nss_wrapper_hosts_enabled()) { + return libc_gethostbyname2_r(name, af, ret, buf, buflen, + result, h_errnop); + } + + return nwrap_gethostbyname2_r(name, af, ret, buf, buflen, result, + h_errnop); +} +#endif + +static int nwrap_files_getaddrinfo(const char *name, + unsigned short port, + const struct addrinfo *hints, + struct addrinfo **ai) +{ + struct nwrap_entlist *el; + struct hostent *he; + struct addrinfo *ai_head = NULL; + struct addrinfo *ai_cur = NULL; + char *h_name_lower; + size_t name_len; + char canon_name[DNS_NAME_MAX] = { 0 }; + bool skip_canonname = false; + ENTRY e = { + .key = NULL, + }; + ENTRY *e_p = NULL; + int rc; + bool ok; + + ok = nwrap_files_cache_reload(nwrap_he_global.cache); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, "error loading hosts file"); + return EAI_SYSTEM; + } + + name_len = strlen(name); + if (name_len == 0) { + return EAI_NONAME; + } + + if (name_len < sizeof(canon_name) && name[name_len - 1] == '.') { + memcpy(canon_name, name, name_len - 1); + canon_name[name_len] = '\0'; + name = canon_name; + } + + if (!str_tolower_copy(&h_name_lower, name)) { + NWRAP_LOG(NWRAP_LOG_DEBUG, + "Out of memory while converting to lower case"); + return EAI_MEMORY; + } + + NWRAP_LOG(NWRAP_LOG_DEBUG, "Searching for name: %s", h_name_lower); + e.key = h_name_lower; + e.data = NULL; + e_p = hsearch(e, FIND); + if (e_p == NULL) { + NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found.", h_name_lower); + SAFE_FREE(h_name_lower); + errno = ENOENT; + return EAI_NONAME; + } + NWRAP_LOG(NWRAP_LOG_DEBUG, "Name: %s found.", h_name_lower); + SAFE_FREE(h_name_lower); + + rc = EAI_NONAME; + for (el = (struct nwrap_entlist *)e_p->data; el != NULL; el = el->next) + { + int rc2; + struct addrinfo *ai_new = NULL; + + he = &(el->ed->ht); + + if (hints->ai_family != AF_UNSPEC && + he->h_addrtype != hints->ai_family) + { + NWRAP_LOG(NWRAP_LOG_DEBUG, + "Entry found but with wrong AF - " + "remembering EAI_ADDRINFO."); + rc = EAI_ADDRFAMILY; + continue; + } + + /* Function allocates memory and returns it in ai. */ + rc2 = nwrap_convert_he_ai(he, + port, + hints, + &ai_new, + skip_canonname); + if (rc2 != 0) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Error converting he to ai"); + if (ai_head != NULL) { + freeaddrinfo(ai_head); + } + return rc2; + } + skip_canonname = true; + + if (ai_head == NULL) { + ai_head = ai_new; + } + if (ai_cur != NULL) { + ai_cur->ai_next = ai_new; + } + ai_cur = ai_new; + } + + if (ai_head != NULL) { + rc = 0; + } + + *ai = ai_head; + + return rc; +} + +static struct hostent *nwrap_files_gethostbyaddr(struct nwrap_backend *b, + const void *addr, + socklen_t len, int type) +{ + struct hostent *he; + char ip[NWRAP_INET_ADDRSTRLEN] = {0}; + struct nwrap_entdata *ed; + const char *a; + size_t i; + bool ok; + + (void) b; /* unused */ + (void) len; /* unused */ + + ok = nwrap_files_cache_reload(nwrap_he_global.cache); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, "error loading hosts file"); + return NULL; + } + + a = inet_ntop(type, addr, ip, sizeof(ip)); + if (a == NULL) { + errno = EINVAL; + return NULL; + } + + nwrap_vector_foreach(ed, nwrap_he_global.entries, i) + { + he = &(ed->ht); + if (he->h_addrtype != type) { + continue; + } + + if (memcmp(addr, he->h_addr_list[0], he->h_length) == 0) { + return he; + } + } + + errno = ENOENT; + return NULL; +} + +#ifdef HAVE_GETHOSTBYADDR_R +static int nwrap_gethostbyaddr_r(const void *addr, socklen_t len, int type, + struct hostent *ret, + char *buf, size_t buflen, + struct hostent **result, int *h_errnop) +{ + size_t i; + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + *result = b->ops->nw_gethostbyaddr(b, addr, len, type); + if (*result != NULL) { + break; + } + } + + if (*result != NULL) { + memset(buf, '\0', buflen); + *ret = **result; + return 0; + } + + *h_errnop = h_errno; + return -1; +} + +int gethostbyaddr_r(const void *addr, socklen_t len, int type, + struct hostent *ret, + char *buf, size_t buflen, + struct hostent **result, int *h_errnop) +{ + if (!nss_wrapper_hosts_enabled()) { + return libc_gethostbyaddr_r(addr, + len, + type, + ret, + buf, + buflen, + result, + h_errnop); + } + + return nwrap_gethostbyaddr_r(addr, len, type, ret, buf, buflen, result, h_errnop); +} +#endif + +/* hosts enum functions */ +static void nwrap_files_sethostent(void) +{ + nwrap_he_global.idx = 0; +} + +static struct hostent *nwrap_files_gethostent(void) +{ + struct hostent *he; + + if (nwrap_he_global.idx == 0) { + bool ok; + + ok = nwrap_files_cache_reload(nwrap_he_global.cache); + if (!ok) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading hosts file"); + return NULL; + } + } + + if (nwrap_he_global.idx >= nwrap_he_global.num) { + errno = ENOENT; + return NULL; + } + + he = &((struct nwrap_entdata *)nwrap_he_global.entries.items[nwrap_he_global.idx++])->ht; + + NWRAP_LOG(NWRAP_LOG_DEBUG, "return hosts[%s]", he->h_name); + + return he; +} + +static void nwrap_files_endhostent(void) +{ + nwrap_he_global.idx = 0; +} + +/* + * module backend + */ + + +static struct passwd *nwrap_module_getpwnam(struct nwrap_backend *b, + const char *name) +{ + static struct passwd pwd; + static char buf[1000]; + NSS_STATUS status; + + if (b->symbols->_nss_getpwnam_r.f == NULL) { + return NULL; + } + + status = b->symbols->_nss_getpwnam_r.f(name, + &pwd, + buf, + sizeof(buf), + &errno); + if (status == NSS_STATUS_NOTFOUND) { + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + return NULL; + } + + return &pwd; +} + +static int nwrap_module_getpwnam_r(struct nwrap_backend *b, + const char *name, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp) +{ + int ret; + + *pwdstp = NULL; + + if (b->symbols->_nss_getpwnam_r.f == NULL) { + return NSS_STATUS_NOTFOUND; + } + + ret = b->symbols->_nss_getpwnam_r.f(name, pwdst, buf, buflen, &errno); + switch (ret) { + case NSS_STATUS_SUCCESS: + *pwdstp = pwdst; + return 0; + case NSS_STATUS_NOTFOUND: + if (errno != 0) { + return errno; + } + return ENOENT; + case NSS_STATUS_TRYAGAIN: + if (errno != 0) { + return errno; + } + return ERANGE; + default: + if (errno != 0) { + return errno; + } + return ret; + } +} + +static struct passwd *nwrap_module_getpwuid(struct nwrap_backend *b, + uid_t uid) +{ + static struct passwd pwd; + static char buf[1000]; + NSS_STATUS status; + + if (b->symbols->_nss_getpwuid_r.f == NULL) { + return NULL; + } + + status = b->symbols->_nss_getpwuid_r.f(uid, + &pwd, + buf, + sizeof(buf), + &errno); + if (status == NSS_STATUS_NOTFOUND) { + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + return NULL; + } + return &pwd; +} + +static int nwrap_module_getpwuid_r(struct nwrap_backend *b, + uid_t uid, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp) +{ + int ret; + + *pwdstp = NULL; + + if (b->symbols->_nss_getpwuid_r.f == NULL) { + return ENOENT; + } + + ret = b->symbols->_nss_getpwuid_r.f(uid, pwdst, buf, buflen, &errno); + switch (ret) { + case NSS_STATUS_SUCCESS: + *pwdstp = pwdst; + return 0; + case NSS_STATUS_NOTFOUND: + if (errno != 0) { + return errno; + } + return ENOENT; + case NSS_STATUS_TRYAGAIN: + if (errno != 0) { + return errno; + } + return ERANGE; + default: + if (errno != 0) { + return errno; + } + return ret; + } +} + +static void nwrap_module_setpwent(struct nwrap_backend *b) +{ + if (b->symbols->_nss_setpwent.f == NULL) { + return; + } + + b->symbols->_nss_setpwent.f(); +} + +static struct passwd *nwrap_module_getpwent(struct nwrap_backend *b) +{ + static struct passwd pwd; + static char buf[1000]; + NSS_STATUS status; + + if (b->symbols->_nss_getpwent_r.f == NULL) { + return NULL; + } + + status = b->symbols->_nss_getpwent_r.f(&pwd, buf, sizeof(buf), &errno); + if (status == NSS_STATUS_NOTFOUND) { + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + return NULL; + } + return &pwd; +} + +static int nwrap_module_getpwent_r(struct nwrap_backend *b, + struct passwd *pwdst, char *buf, + size_t buflen, struct passwd **pwdstp) +{ + int ret; + + *pwdstp = NULL; + + if (b->symbols->_nss_getpwent_r.f == NULL) { + return ENOENT; + } + + ret = b->symbols->_nss_getpwent_r.f(pwdst, buf, buflen, &errno); + switch (ret) { + case NSS_STATUS_SUCCESS: + *pwdstp = pwdst; + return 0; + case NSS_STATUS_NOTFOUND: + if (errno != 0) { + return errno; + } + return ENOENT; + case NSS_STATUS_TRYAGAIN: + if (errno != 0) { + return errno; + } + return ERANGE; + default: + if (errno != 0) { + return errno; + } + return ret; + } +} + +static void nwrap_module_endpwent(struct nwrap_backend *b) +{ + if (b->symbols->_nss_endpwent.f == NULL) { + return; + } + + b->symbols->_nss_endpwent.f(); +} + +static int nwrap_module_initgroups_dyn(struct nwrap_backend *b, + const char *user, + gid_t group, + long int *start, + long int *size, + gid_t **groups, + long int limit, + int *errnop) +{ + if (b->symbols->_nss_initgroups_dyn.f == NULL) { + return NSS_STATUS_UNAVAIL; + } + + return b->symbols->_nss_initgroups_dyn.f(user, + group, + start, + size, + groups, + limit, + errnop); +} + +static struct group *nwrap_module_getgrnam(struct nwrap_backend *b, + const char *name) +{ + static struct group grp; + static char *buf; + static int buflen = 1000; + NSS_STATUS status; + + if (b->symbols->_nss_getgrnam_r.f == NULL) { + return NULL; + } + + if (!buf) { + buf = (char *)malloc(buflen); + } +again: + status = b->symbols->_nss_getgrnam_r.f(name, &grp, buf, buflen, &errno); + if (status == NSS_STATUS_TRYAGAIN) { + buflen *= 2; + buf = (char *)realloc(buf, buflen); + if (!buf) { + return NULL; + } + goto again; + } + if (status == NSS_STATUS_NOTFOUND) { + SAFE_FREE(buf); + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + SAFE_FREE(buf); + return NULL; + } + return &grp; +} + +static int nwrap_module_getgrnam_r(struct nwrap_backend *b, + const char *name, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp) +{ + int ret; + + *grdstp = NULL; + + if (b->symbols->_nss_getgrnam_r.f == NULL) { + return ENOENT; + } + + ret = b->symbols->_nss_getgrnam_r.f(name, grdst, buf, buflen, &errno); + switch (ret) { + case NSS_STATUS_SUCCESS: + *grdstp = grdst; + return 0; + case NSS_STATUS_NOTFOUND: + if (errno != 0) { + return errno; + } + return ENOENT; + case NSS_STATUS_TRYAGAIN: + if (errno != 0) { + return errno; + } + return ERANGE; + default: + if (errno != 0) { + return errno; + } + return ret; + } +} + +static struct group *nwrap_module_getgrgid(struct nwrap_backend *b, + gid_t gid) +{ + static struct group grp; + static char *buf; + static int buflen = 1000; + NSS_STATUS status; + + if (b->symbols->_nss_getgrgid_r.f == NULL) { + return NULL; + } + + if (!buf) { + buf = (char *)malloc(buflen); + } + +again: + status = b->symbols->_nss_getgrgid_r.f(gid, &grp, buf, buflen, &errno); + if (status == NSS_STATUS_TRYAGAIN) { + buflen *= 2; + buf = (char *)realloc(buf, buflen); + if (!buf) { + return NULL; + } + goto again; + } + if (status == NSS_STATUS_NOTFOUND) { + SAFE_FREE(buf); + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + SAFE_FREE(buf); + return NULL; + } + return &grp; +} + +static int nwrap_module_getgrgid_r(struct nwrap_backend *b, + gid_t gid, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp) +{ + int ret; + + *grdstp = NULL; + + if (b->symbols->_nss_getgrgid_r.f == NULL) { + return ENOENT; + } + + ret = b->symbols->_nss_getgrgid_r.f(gid, grdst, buf, buflen, &errno); + switch (ret) { + case NSS_STATUS_SUCCESS: + *grdstp = grdst; + return 0; + case NSS_STATUS_NOTFOUND: + if (errno != 0) { + return errno; + } + return ENOENT; + case NSS_STATUS_TRYAGAIN: + if (errno != 0) { + return errno; + } + return ERANGE; + default: + if (errno != 0) { + return errno; + } + return ret; + } +} + +static void nwrap_module_setgrent(struct nwrap_backend *b) +{ + if (b->symbols->_nss_setgrent.f == NULL) { + return; + } + + b->symbols->_nss_setgrent.f(); +} + +static struct group *nwrap_module_getgrent(struct nwrap_backend *b) +{ + static struct group grp; + static char *buf; + static int buflen = 1024; + NSS_STATUS status; + + if (b->symbols->_nss_getgrent_r.f == NULL) { + return NULL; + } + + if (!buf) { + buf = (char *)malloc(buflen); + } + +again: + status = b->symbols->_nss_getgrent_r.f(&grp, buf, buflen, &errno); + if (status == NSS_STATUS_TRYAGAIN) { + buflen *= 2; + buf = (char *)realloc(buf, buflen); + if (!buf) { + return NULL; + } + goto again; + } + if (status == NSS_STATUS_NOTFOUND) { + SAFE_FREE(buf); + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + SAFE_FREE(buf); + return NULL; + } + return &grp; +} + +static int nwrap_module_getgrent_r(struct nwrap_backend *b, + struct group *grdst, char *buf, + size_t buflen, struct group **grdstp) +{ + int ret; + + *grdstp = NULL; + + if (b->symbols->_nss_getgrent_r.f == NULL) { + return ENOENT; + } + + ret = b->symbols->_nss_getgrent_r.f(grdst, buf, buflen, &errno); + switch (ret) { + case NSS_STATUS_SUCCESS: + *grdstp = grdst; + return 0; + case NSS_STATUS_NOTFOUND: + if (errno != 0) { + return errno; + } + return ENOENT; + case NSS_STATUS_TRYAGAIN: + if (errno != 0) { + return errno; + } + return ERANGE; + default: + if (errno != 0) { + return errno; + } + return ret; + } +} + +static void nwrap_module_endgrent(struct nwrap_backend *b) +{ + if (b->symbols->_nss_endgrent.f == NULL) { + return; + } + + b->symbols->_nss_endgrent.f(); +} + +static struct hostent *nwrap_module_gethostbyaddr(struct nwrap_backend *b, + const void *addr, + socklen_t len, int type) +{ + static struct hostent he; + static char *buf = NULL; + static size_t buflen = 1000; + NSS_STATUS status; + + if (b->symbols->_nss_gethostbyaddr_r.f == NULL) { + return NULL; + } + + if (buf == NULL) { + buf = (char *)malloc(buflen); + if (buf == NULL) { + return NULL; + } + } +again: + status = b->symbols->_nss_gethostbyaddr_r.f(addr, + len, + type, + &he, + buf, + buflen, + &errno, + &h_errno); + if (status == NSS_STATUS_TRYAGAIN) { + char *p = NULL; + + buflen *= 2; + p = (char *)realloc(buf, buflen); + if (p == NULL) { + SAFE_FREE(buf); + return NULL; + } + buf = p; + goto again; + } + if (status == NSS_STATUS_NOTFOUND) { + SAFE_FREE(buf); + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + SAFE_FREE(buf); + return NULL; + } + + return &he; +} + +static int nwrap_module_gethostbyname2_r(struct nwrap_backend *b, + const char *name, int af, + struct hostent *hedst, + char *buf, size_t buflen, + struct hostent **hedstp) +{ + NSS_STATUS status; + + *hedstp = NULL; + + if (b->symbols->_nss_gethostbyname2_r.f == NULL) { + return ENOENT; + } + + status = b->symbols->_nss_gethostbyname2_r.f(name, + af, + hedst, + buf, + buflen, + &errno, + &h_errno); + switch (status) { + case NSS_STATUS_SUCCESS: + *hedstp = hedst; + return 0; + case NSS_STATUS_NOTFOUND: + if (errno != 0) { + return errno; + } + return ENOENT; + case NSS_STATUS_TRYAGAIN: + if (errno != 0) { + return errno; + } + return ERANGE; + default: + if (errno != 0) { + return errno; + } + return status; + } +} + +static struct hostent *nwrap_module_gethostbyname(struct nwrap_backend *b, + const char *name) +{ + static struct hostent he; + static char *buf = NULL; + static size_t buflen = 1000; + NSS_STATUS status; + + if (b->symbols->_nss_gethostbyname2_r.f == NULL) { + return NULL; + } + + if (buf == NULL) { + buf = (char *)malloc(buflen); + if (buf == NULL) { + return NULL; + } + } + +again: + status = b->symbols->_nss_gethostbyname2_r.f(name, + AF_UNSPEC, + &he, + buf, + buflen, + &errno, + &h_errno); + if (status == NSS_STATUS_TRYAGAIN) { + char *p = NULL; + + buflen *= 2; + p = (char *)realloc(buf, buflen); + if (p == NULL) { + SAFE_FREE(buf); + return NULL; + } + buf = p; + goto again; + } + if (status == NSS_STATUS_NOTFOUND) { + SAFE_FREE(buf); + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + SAFE_FREE(buf); + return NULL; + } + + return &he; +} + +static struct hostent *nwrap_module_gethostbyname2(struct nwrap_backend *b, + const char *name, int af) +{ + static struct hostent he; + static char *buf = NULL; + static size_t buflen = 1000; + NSS_STATUS status; + + if (b->symbols->_nss_gethostbyname2_r.f == NULL) { + return NULL; + } + + if (buf == NULL) { + buf = (char *)malloc(buflen); + if (buf == NULL) { + return NULL; + } + } + +again: + status = b->symbols->_nss_gethostbyname2_r.f(name, + af, + &he, + buf, + buflen, + &errno, + &h_errno); + if (status == NSS_STATUS_TRYAGAIN) { + char *p = NULL; + + buflen *= 2; + p = (char *)realloc(buf, buflen); + if (p == NULL) { + SAFE_FREE(buf); + return NULL; + } + buf = p; + goto again; + } + if (status == NSS_STATUS_NOTFOUND) { + SAFE_FREE(buf); + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + SAFE_FREE(buf); + return NULL; + } + + return &he; +} + +/**************************************************************************** + * GETPWNAM + ***************************************************************************/ + +static struct passwd *nwrap_getpwnam(const char *name) +{ + size_t i; + struct passwd *pwd; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + pwd = b->ops->nw_getpwnam(b, name); + if (pwd) { + return pwd; + } + } + + return NULL; +} + +struct passwd *getpwnam(const char *name) +{ + if (!nss_wrapper_enabled()) { + return libc_getpwnam(name); + } + + return nwrap_getpwnam(name); +} + +/**************************************************************************** + * GETPWNAM_R + ***************************************************************************/ + +static int nwrap_getpwnam_r(const char *name, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp) +{ + size_t i; + int ret; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + ret = b->ops->nw_getpwnam_r(b, name, pwdst, buf, buflen, pwdstp); + if (ret == ENOENT) { + continue; + } + return ret; + } + + return ENOENT; +} + +#ifdef HAVE_GETPWNAM_R +# ifdef HAVE_SOLARIS_GETPWNAM_R +int getpwnam_r(const char *name, struct passwd *pwdst, + char *buf, int buflen, struct passwd **pwdstp) +# else /* HAVE_SOLARIS_GETPWNAM_R */ +int getpwnam_r(const char *name, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp) +# endif /* HAVE_SOLARIS_GETPWNAM_R */ +{ + if (!nss_wrapper_enabled()) { + return libc_getpwnam_r(name, pwdst, buf, buflen, pwdstp); + } + + return nwrap_getpwnam_r(name, pwdst, buf, buflen, pwdstp); +} +#endif + +/**************************************************************************** + * GETPWUID + ***************************************************************************/ + +static struct passwd *nwrap_getpwuid(uid_t uid) +{ + size_t i; + struct passwd *pwd; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + pwd = b->ops->nw_getpwuid(b, uid); + if (pwd) { + return pwd; + } + } + + return NULL; +} + +struct passwd *getpwuid(uid_t uid) +{ + if (!nss_wrapper_enabled()) { + return libc_getpwuid(uid); + } + + return nwrap_getpwuid(uid); +} + +/**************************************************************************** + * GETPWUID_R + ***************************************************************************/ + +static int nwrap_getpwuid_r(uid_t uid, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp) +{ + size_t i; + int ret; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + ret = b->ops->nw_getpwuid_r(b, uid, pwdst, buf, buflen, pwdstp); + if (ret == ENOENT) { + continue; + } + return ret; + } + + return ENOENT; +} + +#ifdef HAVE_SOLARIS_GETPWUID_R +int getpwuid_r(uid_t uid, struct passwd *pwdst, + char *buf, int buflen, struct passwd **pwdstp) +#else +int getpwuid_r(uid_t uid, struct passwd *pwdst, + char *buf, size_t buflen, struct passwd **pwdstp) +#endif +{ + if (!nss_wrapper_enabled()) { + return libc_getpwuid_r(uid, pwdst, buf, buflen, pwdstp); + } + + return nwrap_getpwuid_r(uid, pwdst, buf, buflen, pwdstp); +} + +/**************************************************************************** + * SETPWENT + ***************************************************************************/ + +static void nwrap_setpwent(void) +{ + size_t i; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + b->ops->nw_setpwent(b); + } +} + +void setpwent(void) +{ + if (!nss_wrapper_enabled()) { + libc_setpwent(); + return; + } + + nwrap_setpwent(); +} + +/**************************************************************************** + * GETPWENT + ***************************************************************************/ + +static struct passwd *nwrap_getpwent(void) +{ + size_t i; + struct passwd *pwd; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + pwd = b->ops->nw_getpwent(b); + if (pwd) { + return pwd; + } + } + + return NULL; +} + +struct passwd *getpwent(void) +{ + if (!nss_wrapper_enabled()) { + return libc_getpwent(); + } + + return nwrap_getpwent(); +} + +/**************************************************************************** + * GETPWENT_R + ***************************************************************************/ + +#ifdef HAVE_GETPWENT_R +static int nwrap_getpwent_r(struct passwd *pwdst, char *buf, + size_t buflen, struct passwd **pwdstp) +{ + size_t i; + int ret; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + ret = b->ops->nw_getpwent_r(b, pwdst, buf, buflen, pwdstp); + if (ret == ENOENT) { + continue; + } + return ret; + } + + return ENOENT; +} + +# ifdef HAVE_SOLARIS_GETPWENT_R +struct passwd *getpwent_r(struct passwd *pwdst, char *buf, int buflen) +{ + struct passwd *pwdstp = NULL; + int rc; + + if (!nss_wrapper_enabled()) { + return libc_getpwent_r(pwdst, buf, buflen); + } + rc = nwrap_getpwent_r(pwdst, buf, buflen, &pwdstp); + if (rc < 0) { + return NULL; + } + + return pwdstp; +} +# else /* HAVE_SOLARIS_GETPWENT_R */ +int getpwent_r(struct passwd *pwdst, char *buf, + size_t buflen, struct passwd **pwdstp) +{ + if (!nss_wrapper_enabled()) { + return libc_getpwent_r(pwdst, buf, buflen, pwdstp); + } + + return nwrap_getpwent_r(pwdst, buf, buflen, pwdstp); +} +# endif /* HAVE_SOLARIS_GETPWENT_R */ +#endif /* HAVE_GETPWENT_R */ + +/**************************************************************************** + * ENDPWENT + ***************************************************************************/ + +static void nwrap_endpwent(void) +{ + size_t i; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + b->ops->nw_endpwent(b); + } +} + +void endpwent(void) +{ + if (!nss_wrapper_enabled()) { + libc_endpwent(); + return; + } + + nwrap_endpwent(); +} + +/**************************************************************************** + * INITGROUPS + ***************************************************************************/ + +static int nwrap_initgroups(const char *user, gid_t group) +{ +#if defined(NGROUPS_MAX) && NGROUPS_MAX == 0 + /* No extra groups allowed. */ + return 0; +#elif !defined(HAVE_GETGROUPLIST) + return 0; +#else + long int size; + long int limit; + gid_t *groups; + int ngroups; + int result; + const char *env = getenv("UID_WRAPPER"); + + if (env == NULL || env[0] != '1') { + NWRAP_LOG(NWRAP_LOG_WARN, + "initgroups() requires uid_wrapper to work!"); + return 0; + } + + limit = sysconf(_SC_NGROUPS_MAX); + if (limit > 0) { + size = MIN(limit, 64); + } else { + size = 16; + } + + groups = (gid_t *)malloc(size * sizeof(gid_t)); + if (groups == NULL) { + /* No more memory. */ + return -1; + } + + ngroups = nwrap_getgrouplist(user, group, &size, &groups, limit); + + /* Try to set the maximum number of groups the kernel can handle. */ + do { + result = setgroups(ngroups, groups); + } while (result == -1 && errno == EINVAL && --ngroups > 0); + + free(groups); + + return result; +#endif +} + +int initgroups(const char *user, gid_t group) +{ + if (!nss_wrapper_enabled()) { + return libc_initgroups(user, group); + } + + return nwrap_initgroups(user, group); +} + +/**************************************************************************** + * GETGRNAM + ***************************************************************************/ + +static struct group *nwrap_getgrnam(const char *name) +{ + size_t i; + struct group *grp; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + grp = b->ops->nw_getgrnam(b, name); + if (grp) { + return grp; + } + } + + return NULL; +} + +struct group *getgrnam(const char *name) +{ + if (!nss_wrapper_enabled()) { + return libc_getgrnam(name); + } + + return nwrap_getgrnam(name); +} + +/**************************************************************************** + * GETGRNAM_R + ***************************************************************************/ + +static int nwrap_getgrnam_r(const char *name, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp) +{ + size_t i; + int ret; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + ret = b->ops->nw_getgrnam_r(b, name, grdst, buf, buflen, grdstp); + if (ret == ENOENT) { + continue; + } + return ret; + } + + return ENOENT; +} + +#ifdef HAVE_GETGRNAM_R +# ifdef HAVE_SOLARIS_GETGRNAM_R +int getgrnam_r(const char *name, struct group *grp, + char *buf, int buflen, struct group **pgrp) +# else /* HAVE_SOLARIS_GETGRNAM_R */ +int getgrnam_r(const char *name, struct group *grp, + char *buf, size_t buflen, struct group **pgrp) +# endif /* HAVE_SOLARIS_GETGRNAM_R */ +{ + if (!nss_wrapper_enabled()) { + return libc_getgrnam_r(name, + grp, + buf, + buflen, + pgrp); + } + + return nwrap_getgrnam_r(name, grp, buf, buflen, pgrp); +} +#endif /* HAVE_GETGRNAM_R */ + +/**************************************************************************** + * GETGRGID + ***************************************************************************/ + +static struct group *nwrap_getgrgid(gid_t gid) +{ + size_t i; + struct group *grp; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + grp = b->ops->nw_getgrgid(b, gid); + if (grp) { + return grp; + } + } + + return NULL; +} + +struct group *getgrgid(gid_t gid) +{ + if (!nss_wrapper_enabled()) { + return libc_getgrgid(gid); + } + + return nwrap_getgrgid(gid); +} + +/**************************************************************************** + * GETGRGID_R + ***************************************************************************/ + +static int nwrap_getgrgid_r(gid_t gid, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp) +{ + size_t i; + int ret; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + ret = b->ops->nw_getgrgid_r(b, gid, grdst, buf, buflen, grdstp); + if (ret == ENOENT) { + continue; + } + return ret; + } + + return ENOENT; +} + +#ifdef HAVE_GETGRGID_R +# ifdef HAVE_SOLARIS_GETGRGID_R +int getgrgid_r(gid_t gid, struct group *grdst, + char *buf, int buflen, struct group **grdstp) +# else /* HAVE_SOLARIS_GETGRGID_R */ +int getgrgid_r(gid_t gid, struct group *grdst, + char *buf, size_t buflen, struct group **grdstp) +# endif /* HAVE_SOLARIS_GETGRGID_R */ +{ + if (!nss_wrapper_enabled()) { + return libc_getgrgid_r(gid, grdst, buf, buflen, grdstp); + } + + return nwrap_getgrgid_r(gid, grdst, buf, buflen, grdstp); +} +#endif + +/**************************************************************************** + * SETGRENT + ***************************************************************************/ + +static void nwrap_setgrent(void) +{ + size_t i; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + b->ops->nw_setgrent(b); + } +} + +#ifdef HAVE_BSD_SETGRENT +int setgrent(void) +#else +void setgrent(void) +#endif +{ + if (!nss_wrapper_enabled()) { + libc_setgrent(); + goto out; + } + + nwrap_setgrent(); + +out: +#ifdef HAVE_BSD_SETGRENT + return 0; +#else + return; +#endif +} + +/**************************************************************************** + * GETGRENT + ***************************************************************************/ + +static struct group *nwrap_getgrent(void) +{ + size_t i; + struct group *grp; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + grp = b->ops->nw_getgrent(b); + if (grp) { + return grp; + } + } + + return NULL; +} + +struct group *getgrent(void) +{ + if (!nss_wrapper_enabled()) { + return libc_getgrent(); + } + + return nwrap_getgrent(); +} + +/**************************************************************************** + * GETGRENT_R + ***************************************************************************/ + +#ifdef HAVE_GETGRENT_R +static int nwrap_getgrent_r(struct group *grdst, char *buf, + size_t buflen, struct group **grdstp) +{ + size_t i; + int ret; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + ret = b->ops->nw_getgrent_r(b, grdst, buf, buflen, grdstp); + if (ret == ENOENT) { + continue; + } + return ret; + } + + return ENOENT; +} + +# ifdef HAVE_SOLARIS_GETGRENT_R +struct group *getgrent_r(struct group *src, char *buf, int buflen) +{ + struct group *grdstp = NULL; + int rc; + + if (!nss_wrapper_enabled()) { + return libc_getgrent_r(src, buf, buflen); + } + + rc = nwrap_getgrent_r(src, buf, buflen, &grdstp); + if (rc < 0) { + return NULL; + } + + return grdstp; +} +# else /* HAVE_SOLARIS_GETGRENT_R */ +int getgrent_r(struct group *src, char *buf, + size_t buflen, struct group **grdstp) +{ + if (!nss_wrapper_enabled()) { + return libc_getgrent_r(src, buf, buflen, grdstp); + } + + return nwrap_getgrent_r(src, buf, buflen, grdstp); +} +# endif /* HAVE_SOLARIS_GETGRENT_R */ +#endif /* HAVE_GETGRENT_R */ + +/**************************************************************************** + * ENDGRENT + ***************************************************************************/ + +static void nwrap_endgrent(void) +{ + size_t i; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + b->ops->nw_endgrent(b); + } +} + +void endgrent(void) +{ + if (!nss_wrapper_enabled()) { + libc_endgrent(); + return; + } + + nwrap_endgrent(); +} + +/**************************************************************************** + * GETGROUPLIST + ***************************************************************************/ + +#ifdef HAVE_GETGROUPLIST +static int nwrap_getgrouplist(const char *user, + gid_t group, + long int *size, + gid_t **groupsp, + long int limit) +{ + enum nss_status status = NSS_STATUS_UNAVAIL; + /* Start is one, because we have the first group as parameter. */ + long int start = 1; + size_t i; + + /* Never store more than the starting *SIZE number of elements. */ + assert(*size > 0); + (*groupsp)[0] = group; + + for (i = 0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + long int prev_start = start; + long int cnt = prev_start; + + status = b->ops->nw_initgroups_dyn(b, + user, + group, + &start, + size, + groupsp, + limit, + &errno); + + /* Remove duplicates. */ + while (cnt < start) { + long int inner; + for (inner = 0; inner < prev_start; ++inner) + if ((*groupsp)[inner] == (*groupsp)[cnt]) + break; + + if (inner < prev_start) + (*groupsp)[cnt] = (*groupsp)[--start]; + else + ++cnt; + } + NWRAP_LOG(NWRAP_LOG_DEBUG, + "Resource '%s' returned status=%d and increased " + "count of groups to %ld", + b->name, + status, + start); + } + return start; +} + +int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups) +{ + long int size; + int total, retval; + gid_t *newgroups; + + if (!nss_wrapper_enabled()) { + return libc_getgrouplist(user, group, groups, ngroups); + } + + size = MAX(1, *ngroups); + newgroups = (gid_t *)malloc(size * sizeof(gid_t)); + if (newgroups == NULL) { + return -1; + } + + total = nwrap_getgrouplist(user, group, &size, &newgroups, -1); + + if (groups != NULL) { + memcpy(groups, newgroups, MIN(*ngroups, total) * sizeof(gid_t)); + } + + free(newgroups); + + retval = total > *ngroups ? -1 : total; + *ngroups = total; + + return retval; +} +#endif + +/********************************************************** + * SHADOW + **********************************************************/ + +#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) + +#ifdef HAVE_SETSPENT +static void nwrap_setspent(void) +{ + nwrap_files_setspent(); +} + +void setspent(void) +{ + if (!nss_wrapper_shadow_enabled()) { + return; + } + + nwrap_setspent(); +} + +static struct spwd *nwrap_getspent(void) +{ + return nwrap_files_getspent(); +} + +struct spwd *getspent(void) +{ + if (!nss_wrapper_shadow_enabled()) { + return NULL; + } + + return nwrap_getspent(); +} + +static void nwrap_endspent(void) +{ + nwrap_files_endspent(); +} + +void endspent(void) +{ + if (!nss_wrapper_shadow_enabled()) { + return; + } + + nwrap_endspent(); +} +#endif /* HAVE_SETSPENT */ + +static struct spwd *nwrap_getspnam(const char *name) +{ + return nwrap_files_getspnam(name); +} + +struct spwd *getspnam(const char *name) +{ + if (!nss_wrapper_shadow_enabled()) { + return NULL; + } + + return nwrap_getspnam(name); +} + +#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */ + +/********************************************************** + * NETDB + **********************************************************/ + +static void nwrap_sethostent(int stayopen) { + (void) stayopen; /* ignored */ + + nwrap_files_sethostent(); +} + +#ifdef HAVE_SOLARIS_SETHOSTENT +int sethostent(int stayopen) +{ + if (!nss_wrapper_hosts_enabled()) { + libc_sethostent(stayopen); + return 0; + } + + nwrap_sethostent(stayopen); + + return 0; +} +#else /* HAVE_SOLARIS_SETHOSTENT */ +void sethostent(int stayopen) +{ + if (!nss_wrapper_hosts_enabled()) { + libc_sethostent(stayopen); + return; + } + + nwrap_sethostent(stayopen); +} +#endif /* HAVE_SOLARIS_SETHOSTENT */ + +static struct hostent *nwrap_gethostent(void) +{ + return nwrap_files_gethostent(); +} + +struct hostent *gethostent(void) { + if (!nss_wrapper_hosts_enabled()) { + return libc_gethostent(); + } + + return nwrap_gethostent(); +} + +static void nwrap_endhostent(void) { + nwrap_files_endhostent(); +} + +#ifdef HAVE_SOLARIS_ENDHOSTENT +int endhostent(void) +{ + if (!nss_wrapper_hosts_enabled()) { + libc_endhostent(); + return 0; + } + + nwrap_endhostent(); + + return 0; +} +#else /* HAVE_SOLARIS_ENDHOSTENT */ +void endhostent(void) +{ + if (!nss_wrapper_hosts_enabled()) { + libc_endhostent(); + return; + } + + nwrap_endhostent(); +} +#endif /* HAVE_SOLARIS_ENDHOSTENT */ + + +#ifdef BSD +/* BSD implementation stores data in thread local storage but GLIBC does not */ +static __thread struct hostent user_he; +static __thread struct nwrap_vector user_addrlist; +#else +static struct hostent user_he; +static struct nwrap_vector user_addrlist; +#endif /* BSD */ + +static struct hostent *nwrap_files_gethostbyname(struct nwrap_backend *b, + const char *name) +{ + int ret; + + (void) b; /* unused */ + + ret = nwrap_files_internal_gethostbyname(name, AF_UNSPEC, &user_he, + &user_addrlist); + if (ret == 0) { + return &user_he; + } + + return NULL; +} + +static struct hostent *nwrap_gethostbyname(const char *name) +{ + size_t i; + struct hostent *he = NULL; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + he = b->ops->nw_gethostbyname(b, name); + if (he != NULL) { + return he; + } + } + + return NULL; +} + +struct hostent *gethostbyname(const char *name) +{ + if (!nss_wrapper_hosts_enabled()) { + return libc_gethostbyname(name); + } + + return nwrap_gethostbyname(name); +} + +/* This is a GNU extension - Also can be found on BSD systems */ +#ifdef HAVE_GETHOSTBYNAME2 +#ifdef BSD +/* BSD implementation stores data in thread local storage but GLIBC not */ +static __thread struct hostent user_he2; +static __thread struct nwrap_vector user_addrlist2; +#else +static struct hostent user_he2; +static struct nwrap_vector user_addrlist2; +#endif /* BSD */ + +static struct hostent *nwrap_files_gethostbyname2(struct nwrap_backend *b, + const char *name, int af) +{ + int ret; + + (void) b; /* unused */ + + ret = nwrap_files_internal_gethostbyname(name, af, &user_he2, + &user_addrlist2); + if (ret == 0) { + return &user_he2; + } + + return NULL; +} + +static struct hostent *nwrap_gethostbyname2(const char *name, int af) +{ + size_t i; + struct hostent *he = NULL; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + he = b->ops->nw_gethostbyname2(b, name, af); + if (he != NULL) { + return he; + } + } + + return NULL; +} + +struct hostent *gethostbyname2(const char *name, int af) +{ + if (!nss_wrapper_hosts_enabled()) { + return libc_gethostbyname2(name, af); + } + + return nwrap_gethostbyname2(name, af); +} +#endif + +static struct hostent *nwrap_gethostbyaddr(const void *addr, + socklen_t len, int type) +{ + size_t i; + struct hostent *he = NULL; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + he = b->ops->nw_gethostbyaddr(b, addr, len, type); + if (he != NULL) { + return he; + } + } + + return NULL; +} + +struct hostent *gethostbyaddr(const void *addr, + socklen_t len, int type) +{ + if (!nss_wrapper_hosts_enabled()) { + return libc_gethostbyaddr(addr, len, type); + } + + return nwrap_gethostbyaddr(addr, len, type); +} + +static const struct addrinfo default_hints = +{ + .ai_flags = AI_ADDRCONFIG|AI_V4MAPPED, + .ai_family = AF_UNSPEC, + .ai_socktype = 0, + .ai_protocol = 0, + .ai_addrlen = 0, + .ai_addr = NULL, + .ai_canonname = NULL, + .ai_next = NULL +}; + +static int nwrap_convert_he_ai(const struct hostent *he, + unsigned short port, + const struct addrinfo *hints, + struct addrinfo **pai, + bool skip_canonname) +{ + struct addrinfo *ai; + socklen_t socklen; + + if (he == NULL) { + return EAI_MEMORY; + } + + switch (he->h_addrtype) { + case AF_INET: + socklen = sizeof(struct sockaddr_in); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + socklen = sizeof(struct sockaddr_in6); + break; +#endif + default: + return EAI_FAMILY; + } + + ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) + socklen); + if (ai == NULL) { + return EAI_MEMORY; + } + + ai->ai_flags = hints->ai_flags; + ai->ai_family = he->h_addrtype; + ai->ai_socktype = hints->ai_socktype; + ai->ai_protocol = hints->ai_protocol; + ai->ai_canonname = NULL; + + if (ai->ai_socktype == 0) { + ai->ai_socktype = SOCK_DGRAM; + } + if (ai->ai_protocol == 0) { + if (ai->ai_socktype == SOCK_DGRAM) { + ai->ai_protocol = IPPROTO_UDP; + } else if (ai->ai_socktype == SOCK_STREAM) { + ai->ai_protocol = IPPROTO_TCP; + } + } + + ai->ai_addrlen = socklen; + ai->ai_addr = (void *)(ai + 1); + +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + ai->ai_addr->sa_len = socklen; +#endif + ai->ai_addr->sa_family = he->h_addrtype; + + switch (he->h_addrtype) { + case AF_INET: + { + union { + struct sockaddr *sa; + struct sockaddr_in *in; + } addr; + + addr.sa = ai->ai_addr; + + memset(addr.in, 0, sizeof(struct sockaddr_in)); + + addr.in->sin_port = htons(port); + addr.in->sin_family = AF_INET; + + memset(addr.in->sin_zero, + '\0', + sizeof (addr.in->sin_zero)); + memcpy(&(addr.in->sin_addr), + he->h_addr_list[0], + he->h_length); + + } + break; +#ifdef HAVE_IPV6 + case AF_INET6: + { + union { + struct sockaddr *sa; + struct sockaddr_in6 *in6; + } addr; + + addr.sa = ai->ai_addr; + + memset(addr.in6, 0, sizeof(struct sockaddr_in6)); + + addr.in6->sin6_port = htons(port); + addr.in6->sin6_family = AF_INET6; + + memcpy(&addr.in6->sin6_addr, + he->h_addr_list[0], + he->h_length); + } + break; +#endif + } + + ai->ai_next = NULL; + + if (he->h_name && !skip_canonname) { + ai->ai_canonname = strdup(he->h_name); + if (ai->ai_canonname == NULL) { + freeaddrinfo(ai); + return EAI_MEMORY; + } + } + + *pai = ai; + return 0; +} + +static int nwrap_getaddrinfo(const char *node, + const char *service, + const struct addrinfo *hints, + struct addrinfo **res) +{ + struct addrinfo *ai = NULL; + unsigned short port = 0; + struct { + int family; + union { + struct in_addr v4; +#ifdef HAVE_IPV6 + struct in6_addr v6; + } in; +#endif + } addr = { + .family = AF_UNSPEC, + }; + int rc; + + if (node == NULL && service == NULL) { + return EAI_NONAME; + } + + if (hints == NULL) { + hints = &default_hints; + } + + /* EAI_BADFLAGS + hints.ai_flags contains invalid flags; or, hints.ai_flags + included AI_CANONNAME and name was NULL. + */ + if ((hints->ai_flags & AI_CANONNAME) && (node == NULL)) { + return EAI_BADFLAGS; + } + + /* If no node has been specified, let glibc deal with it */ + if (node == NULL) { + int ret; + struct addrinfo *p = NULL; + + ret = libc_getaddrinfo(node, service, hints, &p); + + if (ret == 0) { + *res = p; + } + return ret; + } + + if (service != NULL && service[0] != '\0') { + const char *proto = NULL; + struct servent *s; + char *end_ptr; + long sl; + + errno = 0; + sl = strtol(service, &end_ptr, 10); + + if (*end_ptr == '\0') { + port = sl; + goto valid_port; + } else if (hints->ai_flags & AI_NUMERICSERV) { + return EAI_NONAME; + } + + if (hints->ai_protocol != 0) { + struct protoent *pent; + + pent = getprotobynumber(hints->ai_protocol); + if (pent != NULL) { + proto = pent->p_name; + } + } + + s = getservbyname(service, proto); + if (s == NULL) { + return EAI_NONAME; + } + port = ntohs(s->s_port); + } + +valid_port: + + rc = inet_pton(AF_INET, node, &addr.in.v4); + if (rc == 1) { + addr.family = AF_INET; + } +#ifdef HAVE_IPV6 + if (addr.family == AF_UNSPEC) { + rc = inet_pton(AF_INET6, node, &addr.in.v6); + if (rc == 1) { + addr.family = AF_INET6; + } + } +#endif + + if (addr.family == AF_UNSPEC) { + if (hints->ai_flags & AI_NUMERICHOST) { + return EAI_NONAME; + } + } else if ((hints->ai_family != AF_UNSPEC) && + (hints->ai_family != addr.family)) + { + return EAI_ADDRFAMILY; + } + + rc = nwrap_files_getaddrinfo(node, port, hints, &ai); + if (rc != 0) { + int ret; + struct addrinfo *p = NULL; + + ret = libc_getaddrinfo(node, service, hints, &p); + + if (ret == 0) { + /* + * nwrap_files_getaddrinfo failed, but libc was + * successful -- use the result from libc. + */ + *res = p; + return 0; + } + + return rc; + } + + /* + * If the socktype was not specified, duplicate + * each ai returned, so that we have variants for + * both UDP and TCP. + */ + if (hints->ai_socktype == 0) { + struct addrinfo *ai_cur; + + /* freeaddrinfo() frees ai_canonname and ai so allocate them */ + for (ai_cur = ai; ai_cur != NULL; ai_cur = ai_cur->ai_next) { + struct addrinfo *ai_new; + + /* duplicate the current entry */ + + ai_new = malloc(sizeof(struct addrinfo)); + if (ai_new == NULL) { + freeaddrinfo(ai); + return EAI_MEMORY; + } + + memcpy(ai_new, ai_cur, sizeof(struct addrinfo)); + ai_new->ai_next = NULL; + + /* We need a deep copy or freeaddrinfo() will blow up */ + if (ai_cur->ai_canonname != NULL) { + ai_new->ai_canonname = + strdup(ai_cur->ai_canonname); + } + + if (ai_cur->ai_socktype == SOCK_DGRAM) { + ai_new->ai_socktype = SOCK_STREAM; + } else if (ai_cur->ai_socktype == SOCK_STREAM) { + ai_new->ai_socktype = SOCK_DGRAM; + } + if (ai_cur->ai_protocol == IPPROTO_TCP) { + ai_new->ai_protocol = IPPROTO_UDP; + } else if (ai_cur->ai_protocol == IPPROTO_UDP) { + ai_new->ai_protocol = IPPROTO_TCP; + } + + /* now insert the new entry */ + + ai_new->ai_next = ai_cur->ai_next; + ai_cur->ai_next = ai_new; + + /* and move on (don't duplicate the new entry) */ + + ai_cur = ai_new; + } + } + + *res = ai; + + return 0; +} + +int getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res) +{ + if (!nss_wrapper_hosts_enabled()) { + return libc_getaddrinfo(node, service, hints, res); + } + + return nwrap_getaddrinfo(node, service, hints, res); +} + +static int nwrap_getnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, + char *serv, size_t servlen, + int flags) +{ + struct hostent *he; + struct servent *service; + const char *proto; + const void *addr; + socklen_t addrlen; + uint16_t port; + sa_family_t type; + size_t i; + + if (sa == NULL || salen < sizeof(sa_family_t)) { + return EAI_FAMILY; + } + + if ((flags & NI_NAMEREQD) && host == NULL && serv == NULL) { + return EAI_NONAME; + } + + type = sa->sa_family; + switch (type) { + case AF_INET: { + union { + const struct sockaddr *sa; + const struct sockaddr_in *in; + } a; + + if (salen < sizeof(struct sockaddr_in)) { + return EAI_FAMILY; + } + + a.sa = sa; + + addr = &(a.in->sin_addr); + addrlen = sizeof(a.in->sin_addr); + port = ntohs(a.in->sin_port); + break; + } +#ifdef HAVE_IPV6 + case AF_INET6: { + union { + const struct sockaddr *sa; + const struct sockaddr_in6 *in6; + } a; + + if (salen < sizeof(struct sockaddr_in6)) { + return EAI_FAMILY; + } + + a.sa = sa; + + addr = &(a.in6->sin6_addr); + addrlen = sizeof(a.in6->sin6_addr); + port = ntohs(a.in6->sin6_port); + break; + } +#endif + default: + return EAI_FAMILY; + } + + if (host != NULL) { + he = NULL; + if ((flags & NI_NUMERICHOST) == 0) { + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + he = b->ops->nw_gethostbyaddr(b, addr, addrlen, type); + if (he != NULL) { + break; + } + } + if ((flags & NI_NAMEREQD) && (he == NULL || he->h_name == NULL)) + return EAI_NONAME; + } + if (he != NULL && he->h_name != NULL) { + if (strlen(he->h_name) >= hostlen) + return EAI_OVERFLOW; + snprintf(host, hostlen, "%s", he->h_name); + if (flags & NI_NOFQDN) + host[strcspn(host, ".")] = '\0'; + } else { + if (inet_ntop(type, addr, host, hostlen) == NULL) + return (errno == ENOSPC) ? EAI_OVERFLOW : EAI_FAIL; + } + } + + if (serv != NULL) { + service = NULL; + if ((flags & NI_NUMERICSERV) == 0) { + proto = (flags & NI_DGRAM) ? "udp" : "tcp"; + service = getservbyport(htons(port), proto); + } + if (service != NULL) { + if (strlen(service->s_name) >= servlen) + return EAI_OVERFLOW; + snprintf(serv, servlen, "%s", service->s_name); + } else { + if (snprintf(serv, servlen, "%u", port) >= (int) servlen) + return EAI_OVERFLOW; + } + } + + return 0; +} + +#ifdef HAVE_LINUX_GETNAMEINFO +int getnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, socklen_t hostlen, + char *serv, socklen_t servlen, + int flags) +#elif defined(HAVE_LINUX_GETNAMEINFO_UNSIGNED) +int getnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, socklen_t hostlen, + char *serv, socklen_t servlen, + unsigned int flags) +#else +int getnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, + char *serv, size_t servlen, + int flags) +#endif +{ + if (!nss_wrapper_hosts_enabled()) { + return libc_getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); + } + + return nwrap_getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); +} + +static int nwrap_gethostname(char *name, size_t len) +{ + const char *hostname = getenv("NSS_WRAPPER_HOSTNAME"); + + if (strlen(hostname) >= len) { + errno = ENAMETOOLONG; + return -1; + } + snprintf(name, len, "%s", hostname); + + return 0; +} + +#ifdef HAVE_SOLARIS_GETHOSTNAME +int gethostname(char *name, int len) +#else /* HAVE_SOLARIS_GETHOSTNAME */ +int gethostname(char *name, size_t len) +#endif /* HAVE_SOLARIS_GETHOSTNAME */ +{ + if (!nwrap_hostname_enabled()) { + return libc_gethostname(name, len); + } + + return nwrap_gethostname(name, len); +} + +static void nwrap_thread_prepare(void) +{ + nwrap_init(); + NWRAP_LOCK_ALL; +} + +static void nwrap_thread_parent(void) +{ + NWRAP_UNLOCK_ALL; +} + +static void nwrap_thread_child(void) +{ + NWRAP_REINIT_ALL; +} + +/**************************** + * CONSTRUCTOR + ***************************/ +void nwrap_constructor(void) +{ + NWRAP_REINIT_ALL; + + /* + * 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. + * + * Setting these handlers should prevent such deadlocks. + */ + pthread_atfork(&nwrap_thread_prepare, + &nwrap_thread_parent, + &nwrap_thread_child); + + /* Do not call nwrap_init() here. */ +} + +/**************************** + * DESTRUCTOR + ***************************/ + +/* + * This function is called when the library is unloaded and makes sure that + * sockets get closed and the unix file for the socket are unlinked. + */ +void nwrap_destructor(void) +{ + size_t i; + + NWRAP_LOCK_ALL; + if (nwrap_main_global != NULL) { + struct nwrap_main *m = nwrap_main_global; + + /* libc */ + if (m->libc != NULL) { + if (m->libc->handle != NULL +#ifdef RTLD_NEXT + && m->libc->handle != RTLD_NEXT +#endif + ) { + dlclose(m->libc->handle); + } + if (m->libc->nsl_handle != NULL +#ifdef RTLD_NEXT + && m->libc->nsl_handle != RTLD_NEXT +#endif + ) { + dlclose(m->libc->nsl_handle); + } + if (m->libc->sock_handle != NULL +#ifdef RTLD_NEXT + && m->libc->sock_handle != RTLD_NEXT +#endif + ) { + dlclose(m->libc->sock_handle); + } + SAFE_FREE(m->libc); + } + + /* backends */ + if (m->backends != NULL) { + for (i = 0; i < m->num_backends; i++) { + struct nwrap_backend *b = &(m->backends[i]); + + if (b->so_handle != NULL) { + dlclose(b->so_handle); + } + SAFE_FREE(b->symbols); + } + SAFE_FREE(m->backends); + } + } + + if (nwrap_pw_global.cache != NULL) { + struct nwrap_cache *c = nwrap_pw_global.cache; + + nwrap_files_cache_unload(c); + if (c->fd >= 0) { + fclose(c->fp); + c->fd = -1; + } + + SAFE_FREE(nwrap_pw_global.list); + nwrap_pw_global.num = 0; + } + + if (nwrap_gr_global.cache != NULL) { + struct nwrap_cache *c = nwrap_gr_global.cache; + + nwrap_files_cache_unload(c); + if (c->fd >= 0) { + fclose(c->fp); + c->fd = -1; + } + + SAFE_FREE(nwrap_gr_global.list); + nwrap_pw_global.num = 0; + } + +#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) + if (nwrap_sp_global.cache != NULL) { + struct nwrap_cache *c = nwrap_sp_global.cache; + + nwrap_files_cache_unload(c); + if (c->fd >= 0) { + fclose(c->fp); + c->fd = -1; + } + + nwrap_sp_global.num = 0; + } +#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */ + + if (nwrap_he_global.cache != NULL) { + struct nwrap_cache *c = nwrap_he_global.cache; + + nwrap_files_cache_unload(c); + if (c->fd >= 0) { + fclose(c->fp); + c->fd = -1; + } + + nwrap_he_global.num = 0; + } + + free(user_addrlist.items); +#ifdef HAVE_GETHOSTBYNAME2 + free(user_addrlist2.items); +#endif + + hdestroy(); + NWRAP_UNLOCK_ALL; +} diff --git a/third_party/nss_wrapper/nss_wrapper.pl b/third_party/nss_wrapper/nss_wrapper.pl new file mode 100755 index 0000000..48fa2c5 --- /dev/null +++ b/third_party/nss_wrapper/nss_wrapper.pl @@ -0,0 +1,521 @@ +#!/usr/bin/perl +# + +use strict; + +use Getopt::Long; +use Cwd qw(abs_path); + +my $opt_help = 0; +my $opt_passwd_path = undef; +my $opt_group_path = undef; +my $opt_action = undef; +my $opt_type = undef; +my $opt_name = undef; +my $opt_member = undef; +my $opt_gid = 65534;# nogroup gid + +my $passwdfn = undef; +my $groupfn = undef; +my $memberfn = undef; +my $actionfn = undef; + +sub passwd_add($$$$$); +sub passwd_delete($$$$$); +sub group_add($$$$$); +sub group_delete($$$$$); +sub member_add($$$$$); +sub member_delete($$$$$); + +sub check_path($$); + +my $result = GetOptions( + 'help|h|?' => \$opt_help, + 'passwd_path=s' => \$opt_passwd_path, + 'group_path=s' => \$opt_group_path, + 'action=s' => \$opt_action, + 'type=s' => \$opt_type, + 'name=s' => \$opt_name, + 'member=s' => \$opt_member, + 'gid=i' => \$opt_gid +); + +sub usage($;$) +{ + my ($ret, $msg) = @_; + + print $msg."\n\n" if defined($msg); + + print "usage: + + --help|-h|-? Show this help. + + --passwd_path <path> Path of the 'passwd' file. + --group_path <path> Path of the 'group' file. + + --type <type> 'passwd', 'group' and 'member' are supported. + + --action <action> 'add' or 'delete'. + + --name <name> The name of the object. + + --member <member> The name of the member. + + --gid <gid> Primary Group ID for new users. +"; + exit($ret); +} + +usage(1) if (not $result); + +usage(0) if ($opt_help); + +if (not defined($opt_action)) { + usage(1, "missing: --action [add|delete]"); +} +if ($opt_action eq "add") { + $passwdfn = \&passwd_add; + $groupfn = \&group_add; + $memberfn = \&member_add; +} elsif ($opt_action eq "delete") { + $passwdfn = \&passwd_delete; + $groupfn = \&group_delete; + $memberfn = \&member_delete; +} else { + usage(1, "invalid: --action [add|delete]: '$opt_action'"); +} + +if (not defined($opt_type)) { + usage(1, "missing: --type [passwd|group|member]"); +} +if ($opt_type eq "member" and not defined($opt_member)) { + usage(1, "missing: --member <member>"); +} +my $opt_fullpath_passwd; +my $opt_fullpath_group; +if ($opt_type eq "passwd") { + $actionfn = $passwdfn; + $opt_fullpath_passwd = check_path($opt_passwd_path, $opt_type); +} elsif ($opt_type eq "group") { + $actionfn = $groupfn; + $opt_fullpath_group = check_path($opt_group_path, $opt_type); +} elsif ($opt_type eq "member") { + $actionfn = $memberfn; + $opt_fullpath_passwd = check_path($opt_passwd_path, "passwd"); + $opt_fullpath_group = check_path($opt_group_path, "group"); +} else { + usage(1, "invalid: --type [passwd|group]: '$opt_type'") +} + +if (not defined($opt_name)) { + usage(1, "missing: --name <name>"); +} +if ($opt_name eq "") { + usage(1, "invalid: --name <name>"); +} + +exit $actionfn->($opt_fullpath_passwd, $opt_member, $opt_fullpath_group, $opt_name, $opt_gid); + +sub check_path($$) +{ + my ($path,$type) = @_; + + if (not defined($path)) { + usage(1, "missing: --$type\_path <path>"); + } + if ($path eq "" or $path eq "/") { + usage(1, "invalid: --$type\_path <path>: '$path'"); + } + my $fullpath = abs_path($path); + if (not defined($fullpath)) { + usage(1, "invalid: --$type\_path <path>: '$path'"); + } + return $fullpath; +} + +sub passwd_add_entry($$); + +sub passwd_load($) +{ + my ($path) = @_; + my @lines; + my $passwd = undef; + + open(PWD, "<$path") or die("Unable to open '$path' for read"); + @lines = <PWD>; + close(PWD); + + $passwd->{array} = (); + $passwd->{name} = {}; + $passwd->{uid} = {}; + $passwd->{path} = $path; + + foreach my $line (@lines) { + passwd_add_entry($passwd, $line); + } + + return $passwd; +} + +sub group_add_entry($$); + +sub group_load($) +{ + my ($path) = @_; + my @lines; + my $group = undef; + + open(GROUP, "<$path") or die("Unable to open '$path' for read"); + @lines = <GROUP>; + close(GROUP); + + $group->{array} = (); + $group->{name} = {}; + $group->{gid} = {}; + $group->{path} = $path; + + foreach my $line (@lines) { + group_add_entry($group, $line); + } + + return $group; +} + +sub passwd_lookup_name($$) +{ + my ($passwd, $name) = @_; + + return undef unless defined($passwd->{name}{$name}); + + return $passwd->{name}{$name}; +} + +sub group_lookup_name($$) +{ + my ($group, $name) = @_; + + return undef unless defined($group->{name}{$name}); + + return $group->{name}{$name}; +} + +sub passwd_lookup_uid($$) +{ + my ($passwd, $uid) = @_; + + return undef unless defined($passwd->{uid}{$uid}); + + return $passwd->{uid}{$uid}; +} + +sub group_lookup_gid($$) +{ + my ($group, $gid) = @_; + + return undef unless defined($group->{gid}{$gid}); + + return $group->{gid}{$gid}; +} + +sub passwd_get_free_uid($) +{ + my ($passwd) = @_; + my $uid = 1000; + + while (passwd_lookup_uid($passwd, $uid)) { + $uid++; + } + + return $uid; +} + +sub group_get_free_gid($) +{ + my ($group) = @_; + my $gid = 1000; + + while (group_lookup_gid($group, $gid)) { + $gid++; + } + + return $gid; +} + +sub passwd_add_entry($$) +{ + my ($passwd, $str) = @_; + + chomp $str; + my @e = split(':', $str); + + push(@{$passwd->{array}}, \@e); + $passwd->{name}{$e[0]} = \@e; + $passwd->{uid}{$e[2]} = \@e; +} + +sub group_add_entry($$) +{ + my ($group, $str) = @_; + + chomp $str; + my @e = split(':', $str); + + push(@{$group->{array}}, \@e); + $group->{name}{$e[0]} = \@e; + $group->{gid}{$e[2]} = \@e; +} + +sub passwd_remove_entry($$) +{ + my ($passwd, $eref) = @_; + + for (my $i = 0; defined($passwd->{array}[$i]); $i++) { + if ($eref == $passwd->{array}[$i]) { + $passwd->{array}[$i] = undef; + } + } + + delete $passwd->{name}{${$eref}[0]}; + delete $passwd->{uid}{${$eref}[2]}; +} + +sub group_remove_entry($$) +{ + my ($group, $eref) = @_; + + for (my $i = 0; defined($group->{array}[$i]); $i++) { + if ($eref == $group->{array}[$i]) { + $group->{array}[$i] = undef; + } + } + + delete $group->{name}{${$eref}[0]}; + delete $group->{gid}{${$eref}[2]}; +} + +sub group_add_member($$$) +{ + my ($group, $eref, $username) = @_; + + my @members; + my $str = @$eref[3] || undef; + if ($str) { + @members = split(",", $str); + } + + foreach my $member (@members) { + if ($member and $member eq $username) { + die("account[$username] is already member of '@$eref[0]'"); + } + } + + push(@members, $username); + + my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @members); + + group_remove_entry($group, $eref); + + group_add_entry($group, $gwent); +} + +sub group_delete_member($$$) +{ + my ($group, $eref, $username) = @_; + + my @members = undef; + my $str = @$eref[3] || undef; + if ($str) { + @members = split(",", $str); + } + my @new_members; + my $removed = 0; + + foreach my $member (@members) { + if ($member and $member ne $username) { + push(@new_members, $member); + } else { + $removed = 1; + } + } + + if ($removed != 1) { + die("account[$username] is not member of '@$eref[0]'"); + } + + my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @new_members); + + group_remove_entry($group, $eref); + + group_add_entry($group, $gwent); +} + +sub passwd_save($) +{ + my ($passwd) = @_; + my @lines = (); + my $path = $passwd->{path}; + my $tmppath = $path.$$; + + foreach my $eref (@{$passwd->{array}}) { + next unless defined($eref); + + my $line = join(':', @{$eref}); + push(@lines, $line); + } + + open(PWD, ">$tmppath") or die("Unable to open '$tmppath' for write"); + print PWD join("\n", @lines)."\n"; + close(PWD); + rename($tmppath, $path) or die("Unable to rename $tmppath => $path"); +} + +sub group_save($) +{ + my ($group) = @_; + my @lines = (); + my $path = $group->{path}; + my $tmppath = $path.$$; + + foreach my $eref (@{$group->{array}}) { + next unless defined($eref); + + my $line = join(':', @{$eref}); + if (scalar(@{$eref}) == 3) { + $line .= ":"; + } + push(@lines, $line); + } + + open(GROUP, ">$tmppath") or die("Unable to open '$tmppath' for write"); + print GROUP join("\n", @lines)."\n"; + close(GROUP); + rename($tmppath, $path) or die("Unable to rename $tmppath => $path"); +} + +sub passwd_add($$$$$) +{ + my ($path, $dummy, $dummy2, $name, $gid) = @_; + + #print "passwd_add: '$name' in '$path'\n"; + + my $passwd = passwd_load($path); + + my $e = passwd_lookup_name($passwd, $name); + die("account[$name] already exists in '$path'") if defined($e); + + my $uid = passwd_get_free_uid($passwd); + + my $pwent = $name.":x:".$uid.":".$gid.":".$name." gecos:/nodir:/bin/false"; + + passwd_add_entry($passwd, $pwent); + + passwd_save($passwd); + + return 0; +} + +sub passwd_delete($$$$$) +{ + my ($path, $dummy, $dummy2, $name, $dummy3) = @_; + + #print "passwd_delete: '$name' in '$path'\n"; + + my $passwd = passwd_load($path); + + my $e = passwd_lookup_name($passwd, $name); + die("account[$name] does not exists in '$path'") unless defined($e); + + passwd_remove_entry($passwd, $e); + + passwd_save($passwd); + + return 0; +} + +sub group_add($$$$$) +{ + my ($dummy, $dummy2, $path, $name, $dummy3) = @_; + + #print "group_add: '$name' in '$path'\n"; + + my $group = group_load($path); + + my $e = group_lookup_name($group, $name); + die("group[$name] already exists in '$path'") if defined($e); + + my $gid = group_get_free_gid($group); + + my $gwent = $name.":x:".$gid.":".""; + + group_add_entry($group, $gwent); + + group_save($group); + + #printf("%d\n", $gid); + + return 0; +} + +sub group_delete($$$$$) +{ + my ($dummy, $dummy2, $path, $name, $dummy3) = @_; + + #print "group_delete: '$name' in '$path'\n"; + + my $group = group_load($path); + + my $e = group_lookup_name($group, $name); + die("group[$name] does not exists in '$path'") unless defined($e); + + group_remove_entry($group, $e); + + group_save($group); + + return 0; +} + +sub member_add($$$$$) +{ + my ($passwd_path, $username, $group_path, $groupname, $dummy) = @_; + + #print "member_add: adding '$username' in '$passwd_path' to '$groupname' in '$group_path'\n"; + + my $group = group_load($group_path); + + my $g = group_lookup_name($group, $groupname); + die("group[$groupname] does not exists in '$group_path'") unless defined($g); + + my $passwd = passwd_load($passwd_path); + + my $u = passwd_lookup_name($passwd, $username); + die("account[$username] does not exists in '$passwd_path'") unless defined($u); + + group_add_member($group, $g, $username); + + group_save($group); + + return 0; +} + +sub member_delete($$$$$) +{ + my ($passwd_path, $username, $group_path, $groupname, $dummy) = @_; + + #print "member_delete: removing '$username' in '$passwd_path' from '$groupname' in '$group_path'\n"; + + my $group = group_load($group_path); + + my $g = group_lookup_name($group, $groupname); + die("group[$groupname] does not exists in '$group_path'") unless defined($g); + + my $passwd = passwd_load($passwd_path); + + my $u = passwd_lookup_name($passwd, $username); + die("account[$username] does not exists in '$passwd_path'") unless defined($u); + + group_delete_member($group, $g, $username); + + group_save($group); + + return 0; +} diff --git a/third_party/nss_wrapper/wscript b/third_party/nss_wrapper/wscript new file mode 100644 index 0000000..28aeb51 --- /dev/null +++ b/third_party/nss_wrapper/wscript @@ -0,0 +1,97 @@ +#!/usr/bin/env python + +import os + +VERSION="1.1.15" + +def configure(conf): + if conf.CHECK_NSS_WRAPPER(): + conf.DEFINE('USING_SYSTEM_NSS_WRAPPER', 1) + libnss_wrapper_so_path = 'libnss_wrapper.so' + else: + conf.CHECK_HEADERS('nss.h') + + if conf.CONFIG_SET("HAVE___THREAD"): + conf.DEFINE("HAVE_GCC_THREAD_LOCAL_STORAGE", 1) + + # check HAVE_ATTRIBUTE_PRINTF_FORMAT + conf.CHECK_CODE(''' + void log_fn(const char *format, ...) __attribute__ ((format (printf, 1, 2))); + + int main(void) { + return 0; + } + ''', + 'HAVE_ATTRIBUTE_PRINTF_FORMAT', + addmain=False, + strict=True, + msg='Checking for printf format validation support') + + conf.CHECK_FUNCS('gethostbyaddr_r gethostbyname_r') + conf.CHECK_FUNCS('gethostbyname2 gethostbyname2_r') + # Solaris + conf.CHECK_FUNCS('__posix_getpwnam_r __posix_getpwuid_r') + conf.CHECK_FUNCS('__posix_getgrgid_r __posix_getgrnam_r') + + conf.CHECK_LIB('nsl socket') + conf.CHECK_FUNCS_IN('gethostname', + 'nsl', + checklibc=True, + headers='unistd.h') + + # Prototype checks + conf.CHECK_C_PROTOTYPE('getpwent_r', + 'struct passwd *getpwent_r(struct passwd *src, char *buf, int buflen)', + define='HAVE_SOLARIS_GETPWENT_R', headers='unistd.h pwd.h') + conf.CHECK_C_PROTOTYPE('getpwnam_r', + 'int getpwnam_r(const char *name, struct passwd *pwd, char *buf, int buflen, struct passwd **ppwd)', + define='HAVE_SOLARIS_GETPWNAM_R', headers='unistd.h pwd.h') + conf.CHECK_C_PROTOTYPE('getpwuid_r', + 'int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, int buflen, struct passwd **ppwd)', + define='HAVE_SOLARIS_GETPWUID_R', headers='unistd.h pwd.h') + conf.CHECK_C_PROTOTYPE('getgrent_r', + 'struct group *getgrent_r(struct group *src, char *buf, int buflen)', + define='HAVE_SOLARIS_GETGRENT_R', headers='unistd.h grp.h') + conf.CHECK_C_PROTOTYPE('getgrnam_r', + 'int getgrnam_r(const char *name, struct group *grp, char *buf, int buflen, struct group **pgrp)', + define='HAVE_SOLARIS_GETGRNAM_R', headers='unistd.h grp.h') + conf.CHECK_C_PROTOTYPE('getgrgid_r', + 'int getgrgid_r(gid_t gid, struct group *grp, char *buf, int buflen, struct group **pgrp)', + define='HAVE_SOLARIS_GETGRGID_R', headers='unistd.h grp.h') + conf.CHECK_C_PROTOTYPE('sethostent', + 'int sethostent(int stayopen)', + define='HAVE_SOLARIS_SETHOSTENT', headers='unistd.h netdb.h') + conf.CHECK_C_PROTOTYPE('endhostent', + 'int endhostent(void)', + define='HAVE_SOLARIS_ENDHOSTENT', headers='unistd.h netdb.h') + conf.CHECK_C_PROTOTYPE('gethostname', + 'int gethostname(char *name, int len)', + define='HAVE_SOLARIS_GETHOSTNAME', headers='unistd.h netdb.h') + conf.CHECK_C_PROTOTYPE('setgrent', + 'int setgrent(void)', + define='HAVE_BSD_SETGRENT', headers='unistd.h grp.h') + conf.CHECK_C_PROTOTYPE('getnameinfo', + 'int getnameinfo (const struct sockaddr *sa, socklen_t salen, char *host, socklen_t __hostlen, char *serv, socklen_t servlen, int flags)', + define='HAVE_LINUX_GETNAMEINFO', headers='unistd.h netdb.h') + conf.CHECK_C_PROTOTYPE('getnameinfo', + 'int getnameinfo (const struct sockaddr *sa, socklen_t salen, char *host, socklen_t __hostlen, char *serv, socklen_t servlen, unsigned int flags)', + define='HAVE_LINUX_GETNAMEINFO_UNSIGNED', headers='unistd.h netdb.h') + + # Create full path to nss_wrapper + blddir = os.path.realpath(conf.bldnode.abspath()) + libnss_wrapper_so_path = blddir + '/default/third_party/nss_wrapper/libnss-wrapper.so' + + conf.DEFINE('LIBNSS_WRAPPER_SO_PATH', libnss_wrapper_so_path) + conf.DEFINE('NSS_WRAPPER', 1) + +def build(bld): + if bld.CONFIG_SET("HAVE_NSS_H") and not bld.CONFIG_SET("USING_SYSTEM_NSS_WRAPPER"): + # We need to do it this way or the library wont work. + # We need force_unversioned=True as symbol versioning + # breaks preloading! + bld.SAMBA_LIBRARY('nss_wrapper', + source='nss_wrapper.c nss_utils.c', + deps='dl pthread', + install=False, + force_unversioned=True, + realname='libnss-wrapper.so') |