From ac46107a01f4bef2e30c9d324a80ea214fad49fc Mon Sep 17 00:00:00 2001 From: Packit Date: Sep 16 2020 08:06:28 +0000 Subject: nss_wrapper-1.1.5 base --- diff --git a/.clang_complete b/.clang_complete new file mode 100644 index 0000000..8ea074a --- /dev/null +++ b/.clang_complete @@ -0,0 +1,5 @@ +-DHAVE_IPV6 +-DHAVE_LIBNSL +-DHAVE_LIBSOCKET +-Isrc +-Iobj diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py new file mode 100644 index 0000000..fbb2a99 --- /dev/null +++ b/.ycm_extra_conf.py @@ -0,0 +1,104 @@ +import os +import ycm_core + +flags = [ +] + +# Set this to the absolute path to the folder (NOT the file!) containing the +# compile_commands.json file to use that instead of 'flags'. See here for +# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html +# +# Most projects will NOT need to set this to anything; you can just change the +# 'flags' list of compilation flags. Notice that YCM itself uses that approach. +compilation_database_folder = 'obj' + +if os.path.exists( compilation_database_folder ): + database = ycm_core.CompilationDatabase( compilation_database_folder ) +else: + database = None + +SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ] + +def DirectoryOfThisScript(): + return os.path.dirname( os.path.abspath( __file__ ) ) + + +def MakeRelativePathsInFlagsAbsolute( flags, working_directory ): + if not working_directory: + return list( flags ) + new_flags = [] + make_next_absolute = False + path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ] + for flag in flags: + new_flag = flag + + if make_next_absolute: + make_next_absolute = False + if not flag.startswith( '/' ): + new_flag = os.path.join( working_directory, flag ) + + for path_flag in path_flags: + if flag == path_flag: + make_next_absolute = True + break + + if flag.startswith( path_flag ): + path = flag[ len( path_flag ): ] + new_flag = path_flag + os.path.join( working_directory, path ) + break + + if new_flag: + new_flags.append( new_flag ) + return new_flags + + +def IsHeaderFile( filename ): + extension = os.path.splitext( filename )[ 1 ] + return extension in [ '.h', '.hxx', '.hpp', '.hh' ] + + +def GetCompilationInfoForFile( filename ): + # The compilation_commands.json file generated by CMake does not have entries + # for header files. So we do our best by asking the db for flags for a + # corresponding source file, if any. If one exists, the flags for that file + # should be good enough. + if IsHeaderFile( filename ): + basename = os.path.splitext( filename )[ 0 ] + for extension in SOURCE_EXTENSIONS: + replacement_file = basename + extension + if os.path.exists( replacement_file ): + compilation_info = database.GetCompilationInfoForFile( + replacement_file ) + if compilation_info.compiler_flags_: + return compilation_info + return None + return database.GetCompilationInfoForFile( filename ) + + +def FlagsForFile( filename, **kwargs ): + if database: + # Bear in mind that compilation_info.compiler_flags_ does NOT return a + # python list, but a "list-like" StringVec object + compilation_info = GetCompilationInfoForFile( filename ) + if not compilation_info: + return None + + final_flags = MakeRelativePathsInFlagsAbsolute( + compilation_info.compiler_flags_, + compilation_info.compiler_working_dir_ ) + + # NOTE: This is just for YouCompleteMe; it's highly likely that your project + # does NOT need to remove the stdlib flag. DO NOT USE THIS IN YOUR + # ycm_extra_conf IF YOU'RE NOT 100% SURE YOU NEED IT. + try: + final_flags.remove( '-stdlib=libc++' ) + except ValueError: + pass + else: + relative_to = DirectoryOfThisScript() + final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to ) + + return { + 'flags': final_flags, + 'do_cache': True + } diff --git a/.ycm_extra_conf.pyc b/.ycm_extra_conf.pyc new file mode 100644 index 0000000..42ef362 Binary files /dev/null and b/.ycm_extra_conf.pyc differ diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..49a2a20 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,5 @@ +Stefan Metzmacher +Guenther Deschner +Andreas Schneider +Robin Hack +Michael Adam diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e91c266 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,100 @@ +project(nss_wrapper C) + +# Required cmake version +cmake_minimum_required(VERSION 2.8.0) + +# global needed variables +set(APPLICATION_NAME ${PROJECT_NAME}) + +set(APPLICATION_VERSION_MAJOR "1") +set(APPLICATION_VERSION_MINOR "1") +set(APPLICATION_VERSION_PATCH "5") + +set(APPLICATION_VERSION "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}.${APPLICATION_VERSION_PATCH}") + +# SOVERSION scheme: CURRENT.AGE.REVISION +# If there was an incompatible interface change: +# Increment CURRENT. Set AGE and REVISION to 0 +# If there was a compatible interface change: +# Increment AGE. Set REVISION to 0 +# If the source code was changed, but there were no interface changes: +# Increment REVISION. +set(LIBRARY_VERSION "0.2.5") +set(LIBRARY_SOVERSION "0") + +# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked +set(CMAKE_MODULE_PATH + ${CMAKE_SOURCE_DIR}/cmake/Modules +) + +# add definitions +include(DefineCMakeDefaults) +include(DefinePlatformDefaults) +include(DefineCompilerFlags) +include(DefineInstallationPaths) +include(DefineOptions.cmake) +include(CPackConfig.cmake) + +# disallow in-source build +include(MacroEnsureOutOfSourceBuild) +macro_ensure_out_of_source_build("${PROJECT_NAME} requires an out of source build. Please create a separate build directory and run 'cmake /path/to/${PROJECT_NAME} [options]' there.") + +# Find out if we have threading available +set(CMAKE_THREAD_PREFER_PTHREADS ON) +find_package(Threads) + +# config.h checks +include(ConfigureChecks.cmake) +configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) + +# check subdirectories +add_subdirectory(src) + +install( + PROGRAMS + nss_wrapper.pl + DESTINATION + ${BIN_INSTALL_DIR}) + +if (UNIT_TESTING) + find_package(CMocka REQUIRED) + + file(TO_CMAKE_PATH "${CMAKE_SOURCE_DIR}/tests/valgrind.supp" VALGRIND_SUPPRESSION_FILE) + # add the valgrind suppressions + set(MEMORYCHECK_SUPPRESSIONS_FILE + ${VALGRIND_SUPPRESSION_FILE} + CACHE + FILEPATH + "Path to the memory checking command, used for memory error detection.") + + include(AddCMockaTest) + add_subdirectory(tests) +endif (UNIT_TESTING) + +# pkg-config file +get_filename_component(NSS_WRAPPER_LIB ${NSS_WRAPPER_LOCATION} NAME) + +configure_file(nss_wrapper.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/nss_wrapper.pc @ONLY) +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/nss_wrapper.pc + DESTINATION + ${LIB_INSTALL_DIR}/pkgconfig + COMPONENT + pkgconfig +) + +# cmake config files +configure_file(nss_wrapper-config-version.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/nss_wrapper-config-version.cmake @ONLY) +configure_file(nss_wrapper-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/nss_wrapper-config.cmake @ONLY) +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/nss_wrapper-config-version.cmake + ${CMAKE_CURRENT_BINARY_DIR}/nss_wrapper-config.cmake + DESTINATION + ${CMAKE_INSTALL_DIR}/nss_wrapper + COMPONENT + devel +) + +add_subdirectory(doc) diff --git a/CPackConfig.cmake b/CPackConfig.cmake new file mode 100644 index 0000000..e1b83ac --- /dev/null +++ b/CPackConfig.cmake @@ -0,0 +1,53 @@ +# For help take a look at: +# http://www.cmake.org/Wiki/CMake:CPackConfiguration + +### general settings +set(CPACK_PACKAGE_NAME ${APPLICATION_NAME}) +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The SSH library") +set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README") +set(CPACK_PACKAGE_VENDOR "The SSH Library Development Team") +set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME}) +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") + + +### versions +set(CPACK_PACKAGE_VERSION_MAJOR "${APPLICATION_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${APPLICATION_VERSION_MINOR}") +set(CPACK_PACKAGE_VERSION_PATCH "${APPLICATION_VERSION_PATCH}") +set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") + + +### source generator +set(CPACK_SOURCE_GENERATOR "TGZ") +set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]svn/;/[.]git/;.gitignore;/build*;/obj*;tags;cscope.*") +set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") + +if (WIN32) + set(CPACK_GENERATOR "ZIP") + + ### nsis generator + find_package(NSIS) + if (NSIS_MAKE) + set(CPACK_GENERATOR "${CPACK_GENERATOR};NSIS") + set(CPACK_NSIS_DISPLAY_NAME "The SSH Library") + set(CPACK_NSIS_COMPRESSOR "/SOLID zlib") + set(CPACK_NSIS_MENU_LINKS "http://www.libssh.org/" "libssh homepage") + endif (NSIS_MAKE) +endif (WIN32) + +set(CPACK_PACKAGE_INSTALL_DIRECTORY "libssh") + +set(CPACK_PACKAGE_FILE_NAME ${APPLICATION_NAME}-${CPACK_PACKAGE_VERSION}) + +set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries") +set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "C/C++ Headers") +set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION + "Libraries used to build programs which use libssh") +set(CPACK_COMPONENT_HEADERS_DESCRIPTION + "C/C++ header files for use with libssh") +set(CPACK_COMPONENT_HEADERS_DEPENDS libraries) +#set(CPACK_COMPONENT_APPLICATIONS_GROUP "Runtime") +set(CPACK_COMPONENT_LIBRARIES_GROUP "Development") +set(CPACK_COMPONENT_HEADERS_GROUP "Development") + +include(CPack) diff --git a/CTestConfig.cmake b/CTestConfig.cmake new file mode 100644 index 0000000..14dd8d1 --- /dev/null +++ b/CTestConfig.cmake @@ -0,0 +1,9 @@ +set(UPDATE_TYPE "true") + +set(CTEST_PROJECT_NAME "nss_wrapper") +set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC") + +set(CTEST_DROP_METHOD "https") +set(CTEST_DROP_SITE "test.cmocka.org") +set(CTEST_DROP_LOCATION "/submit.php?project=${CTEST_PROJECT_NAME}") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..b158baf --- /dev/null +++ b/ChangeLog @@ -0,0 +1,52 @@ +ChangeLog +========== + +version 1.1.5 (released 2018-10-31) + * Fixed running on older distributions + +version 1.1.4 (released 2018-10-31) + * Fixed module getpw* functions + * Fixed nss_wrapper.pl to use correct perl binary + * Fixed compatibility with musl-libc + +version 1.1.3 (released 2015-03-18) + * Added support for BSD 'struct passwd' members + * Replaced strcpy() with snprintf() + +version 1.1.2 (released 2015-12-17) + * Fixed segfault while reloading hosts file + * Fixed issue where are not fault tolerant if an alias has already + been added + +version 1.1.1 (released 2015-11-23) + * Fixed nss_wrapper build on Solaris + +version 1.1.0 (released 2015-11-20) + * Added support for initgroups() + * Added support for shadow files (getspnam(), etc.) + * Improved support for multi address handling in getaddrinfo() + * Improved file parser + * Fixed compilation on machines without IPv4 support + * Fixed service string sanity check in getaddrinfo() (bso #11501) + * Fixed AI_NUMERICHOST handling in getaddrinfo() (bso # 11477) + +version 1.0.3 (released 2014-09-11) + * Added a nss_wrapper manpage. + * Fixed cmake find_package configs. + * Fixed resolving hostnames with a trailing dot. + * Fixed an overflow when checking if a IPv6 address is IPv4. + * Fall back to RTLD_NEXT for symbol binding if we can't find libc. + +version 1.0.2 (released 2014-04-08) + * Added public nss_wrapper_enabled(). + * Added public nss_wrapper_hosts_enabled(). + * Fixed segfault in 'getent hosts' without aliases. + * Fixed IPv4 and IPv6 resolving with 'getent ahosts'. + +version 1.0.1 (released 2014-02-04) + * Added --libs to pkg-config. + * Added nss_wrapper-config.cmake + * Fixed a bug packaging the obj directory. + +version 1.0.0 (released 2014-02-02) + * Initial release diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake new file mode 100644 index 0000000..41029d4 --- /dev/null +++ b/ConfigureChecks.cmake @@ -0,0 +1,341 @@ +include(CheckIncludeFile) +include(CheckSymbolExists) +include(CheckFunctionExists) +include(CheckLibraryExists) +include(CheckTypeSize) +include(CheckStructHasMember) +include(CheckPrototypeDefinition) +include(TestBigEndian) +include(CheckCSourceRuns) + +set(PACKAGE ${APPLICATION_NAME}) +set(VERSION ${APPLICATION_VERSION}) +set(DATADIR ${DATA_INSTALL_DIR}) +set(LIBDIR ${LIB_INSTALL_DIR}) +set(PLUGINDIR "${PLUGIN_INSTALL_DIR}-${LIBRARY_SOVERSION}") +set(SYSCONFDIR ${SYSCONF_INSTALL_DIR}) + +set(BINARYDIR ${CMAKE_BINARY_DIR}) +set(SOURCEDIR ${CMAKE_SOURCE_DIR}) + +function(COMPILER_DUMPVERSION _OUTPUT_VERSION) + # Remove whitespaces from the argument. + # This is needed for CC="ccache gcc" cmake .. + string(REPLACE " " "" _C_COMPILER_ARG "${CMAKE_C_COMPILER_ARG1}") + + execute_process( + COMMAND + ${CMAKE_C_COMPILER} ${_C_COMPILER_ARG} -dumpversion + OUTPUT_VARIABLE _COMPILER_VERSION + ) + + string(REGEX REPLACE "([0-9])\\.([0-9])(\\.[0-9])?" "\\1\\2" + _COMPILER_VERSION "${_COMPILER_VERSION}") + + set(${_OUTPUT_VERSION} ${_COMPILER_VERSION} PARENT_SCOPE) +endfunction() + +if(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW AND NOT OS2) + compiler_dumpversion(GNUCC_VERSION) + if (NOT GNUCC_VERSION EQUAL 34) + set(CMAKE_REQUIRED_FLAGS "-fvisibility=hidden") + check_c_source_compiles( +"void __attribute__((visibility(\"default\"))) test() {} +int main(void){ return 0; } +" WITH_VISIBILITY_HIDDEN) + set(CMAKE_REQUIRED_FLAGS "") + endif (NOT GNUCC_VERSION EQUAL 34) +endif(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW AND NOT OS2) + +# HEADERS +check_include_file(sys/types.h HAVE_SYS_TYPES_H) +check_include_file(pwd.h HAVE_PWD_H) +check_include_file(shadow.h HAVE_SHADOW_H) +check_include_file(grp.h HAVE_GRP_H) +check_include_file(nss.h HAVE_NSS_H) +check_include_file(nss_common.h HAVE_NSS_COMMON_H) + +# FUNCTIONS +check_function_exists(strncpy HAVE_STRNCPY) +check_function_exists(vsnprintf HAVE_VSNPRINTF) +check_function_exists(snprintf HAVE_SNPRINTF) + +check_function_exists(getpwnam_r HAVE_GETPWNAM_R) +check_function_exists(getpwuid_r HAVE_GETPWUID_R) +check_function_exists(getpwent_r HAVE_GETPWENT_R) + +check_function_exists(setspent HAVE_SETSPENT) +check_function_exists(getspnam HAVE_GETSPNAM) + +check_function_exists(getgrnam_r HAVE_GETGRNAM_R) +check_function_exists(getgrgid_r HAVE_GETGRGID_R) +check_function_exists(getgrent_r HAVE_GETGRENT_R) + +check_function_exists(getgrouplist HAVE_GETGROUPLIST) + +check_function_exists(gethostbyaddr_r HAVE_GETHOSTBYADDR_R) +check_function_exists(gethostbyname_r HAVE_GETHOSTBYNAME_R) + +check_function_exists(gethostbyname2 HAVE_GETHOSTBYNAME2) + +if (WIN32) + check_function_exists(_vsnprintf_s HAVE__VSNPRINTF_S) + check_function_exists(_vsnprintf HAVE__VSNPRINTF) + check_function_exists(_snprintf HAVE__SNPRINTF) + check_function_exists(_snprintf_s HAVE__SNPRINTF_S) +endif (WIN32) + +if (UNIX) + if (NOT LINUX) + # libsocket (Solaris) + check_library_exists(socket getaddrinfo "" HAVE_LIBSOCKET) + if (HAVE_LIBSOCKET) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} socket) + endif (HAVE_LIBSOCKET) + + # libnsl/inet_pton (Solaris) + check_library_exists(nsl inet_pton "" HAVE_LIBNSL) + if (HAVE_LIBNSL) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} nsl) + endif (HAVE_LIBNSL) + endif (NOT LINUX) + + check_function_exists(getaddrinfo HAVE_GETADDRINFO) +endif (UNIX) + +if (SOLARIS) + check_function_exists(__posix_getpwnam_r HAVE___POSIX_GETPWNAM_R) + check_function_exists(__posix_getpwuid_r HAVE___POSIX_GETPWUID_R) + + check_function_exists(__posix_getgrgid_r HAVE___POSIX_GETGRGID_R) + check_function_exists(__posix_getgrnam_r HAVE___POSIX_GETGRNAM_R) +endif (SOLARIS) + +check_function_exists(asprintf HAVE_ASPRINTF) +if (UNIX AND HAVE_ASPRINTF) + add_definitions(-D_GNU_SOURCE) +endif (UNIX AND HAVE_ASPRINTF) + +set(CMAKE_REQUIRED_DEFINITIONS -D_POSIX_PTHREAD_SEMANTICS) +check_prototype_definition(getpwent_r + "struct passwd *getpwent_r(struct passwd *src, char *buf, int buflen)" + "NULL" + "unistd.h;pwd.h" + HAVE_SOLARIS_GETPWENT_R) + +check_prototype_definition(getpwnam_r + "int getpwnam_r(const char *name, struct passwd *pwd, char *buf, int buflen, struct passwd **ppwd)" + "-1" + "unistd.h;pwd.h" + HAVE_SOLARIS_GETPWNAM_R) + +check_prototype_definition(getpwuid_r + "int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, int buflen, struct passwd **ppwd)" + "-1" + "unistd.h;pwd.h" + HAVE_SOLARIS_GETPWUID_R) + +check_prototype_definition(getgrent_r + "struct group *getgrent_r(struct group *src, char *buf, int buflen)" + "NULL" + "unistd.h;grp.h" + HAVE_SOLARIS_GETGRENT_R) + +check_prototype_definition(getgrnam_r + "int getgrnam_r(const char *name, struct group *grp, char *buf, int buflen, struct group **pgrp)" + "-1" + "unistd.h;grp.h" + HAVE_SOLARIS_GETGRNAM_R) + +check_prototype_definition(getgrgid_r + "int getgrgid_r(gid_t gid, struct group *grp, char *buf, int buflen, struct group **pgrp)" + "-1" + "unistd.h;grp.h" + HAVE_SOLARIS_GETGRGID_R) + +check_prototype_definition(sethostent + "int sethostent(int stayopen)" + "-1" + "unistd.h;netdb.h" + HAVE_SOLARIS_SETHOSTENT) + +check_prototype_definition(endhostent + "int endhostent(void)" + "-1" + "unistd.h;netdb.h" + HAVE_SOLARIS_ENDHOSTENT) + +check_prototype_definition(gethostname + "int gethostname(char *name, int len)" + "-1" + "unistd.h;netdb.h" + HAVE_SOLARIS_GETHOSTNAME) +set(CMAKE_REQUIRED_DEFINITIONS) + +check_prototype_definition(setgrent + "int setgrent(void)" + "-1" + "unistd.h;grp.h" + HAVE_BSD_SETGRENT) + +check_prototype_definition(getnameinfo + "int getnameinfo (const struct sockaddr *sa, socklen_t salen, char *host, socklen_t __hostlen, char *serv, socklen_t servlen, int flags)" + "-1" + "unistd.h;netdb.h" + HAVE_LINUX_GETNAMEINFO) + +check_prototype_definition(getnameinfo + "int getnameinfo (const struct sockaddr *sa, socklen_t salen, char *host, socklen_t __hostlen, char *serv, socklen_t servlen, unsigned int flags)" + "-1" + "unistd.h;netdb.h" + HAVE_LINUX_GETNAMEINFO_UNSIGNED) + +# STRUCT MEMBERS +check_struct_has_member("struct sockaddr" sa_len "sys/socket.h netinet/in.h" HAVE_STRUCT_SOCKADDR_SA_LEN) +check_struct_has_member("struct passwd" pw_class "pwd.h" HAVE_STRUCT_PASSWD_PW_CLASS) +check_struct_has_member("struct passwd" pw_change "pwd.h" HAVE_STRUCT_PASSWD_PW_CHANGE) +check_struct_has_member("struct passwd" pw_expire "pwd.h" HAVE_STRUCT_PASSWD_PW_EXPIRE) + +# IPV6 +check_c_source_compiles(" + #include + #include + #include + #include + #include + +int main(void) { + struct sockaddr_storage sa_store; + struct addrinfo *ai = NULL; + struct in6_addr in6addr; + int idx = if_nametoindex(\"iface1\"); + int s = socket(AF_INET6, SOCK_STREAM, 0); + int ret = getaddrinfo(NULL, NULL, NULL, &ai); + if (ret != 0) { + const char *es = gai_strerror(ret); + } + + freeaddrinfo(ai); + { + int val = 1; +#ifdef HAVE_LINUX_IPV6_V6ONLY_26 +#define IPV6_V6ONLY 26 +#endif + ret = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + (const void *)&val, sizeof(val)); + } + + return 0; +}" HAVE_IPV6) + +check_c_source_compiles(" +void log_fn(const char *format, ...) __attribute__ ((format (printf, 1, 2))); + +int main(void) { + return 0; +}" HAVE_ATTRIBUTE_PRINTF_FORMAT) + +check_c_source_compiles(" +void test_constructor_attribute(void) __attribute__ ((constructor)); + +void test_constructor_attribute(void) +{ + return; +} + +int main(void) { + return 0; +}" HAVE_CONSTRUCTOR_ATTRIBUTE) + +check_c_source_compiles(" +void test_destructor_attribute(void) __attribute__ ((destructor)); + +void test_destructor_attribute(void) +{ + return; +} + +int main(void) { + return 0; +}" HAVE_DESTRUCTOR_ATTRIBUTE) + +check_library_exists(dl dlopen "" HAVE_LIBDL) +if (HAVE_LIBDL) + find_library(DLFCN_LIBRARY dl) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${DLFCN_LIBRARY}) +endif (HAVE_LIBDL) + +# ENDIAN +if (NOT WIN32) + test_big_endian(WORDS_BIGENDIAN) +endif (NOT WIN32) + +set(NWRAP_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} CACHE INTERNAL "nss_wrapper required system libraries") + +# check whether getaddrinfo() returns "node" in "ai_canonname" for IP-addresses +check_c_source_runs("#include +#include +#include +#include +#include +int main(void) { + struct addrinfo hints; + struct addrinfo *res = NULL; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; + + if (getaddrinfo(\"0.0.0.0\", \"389\", &hints, &res) != 0) { + return 2; + } + + if (res == NULL) { + return 3; + } + + return strncmp(res->ai_canonname, \"0.0.0.0\", sizeof(\"0.0.0.0\")) != 0; +}" HAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES) +if (HAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES) + add_definitions(-DHAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES) +endif (HAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES) + +# check whether getaddrinfo() returns EAI_SERVICE when the requested service is not available for the requested socket type. +check_c_source_runs("#include +#include +#include +#include +#include +int main(void) { + struct addrinfo hints; + struct addrinfo *res = NULL; + int rc; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + + rc = getaddrinfo(NULL, \"echo\", &hints, &res); + return rc != EAI_SERVICE; +}" HAVE_GETADDRINFO_USES_EAI_SERVICE) +if (HAVE_GETADDRINFO_USES_EAI_SERVICE) + add_definitions(-DHAVE_GETADDRINFO_USES_EAI_SERVICE) +endif (HAVE_GETADDRINFO_USES_EAI_SERVICE) + +# check for non-NULL gethostent() +check_c_source_runs("#include +#include +int main(void) { + struct hostent *hostent = NULL; + sethostent(0); + hostent = gethostent(); + endhostent(); + return hostent == NULL; +}" HAVE_NONNULL_GETHOSTENT) +if (HAVE_NONNULL_GETHOSTENT) + add_definitions(-DHAVE_NONNULL_GETHOSTENT) +endif (HAVE_NONNULL_GETHOSTENT) diff --git a/DefineOptions.cmake b/DefineOptions.cmake new file mode 100644 index 0000000..6030e79 --- /dev/null +++ b/DefineOptions.cmake @@ -0,0 +1 @@ +option(UNIT_TESTING "Build with unit tests" OFF) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..be6062e --- /dev/null +++ b/LICENSE @@ -0,0 +1,35 @@ +BSD 3-Clause License + +Copyright (c) 2007, Stefan Metzmacher +Copyright (c) 2009, Guenther Deschner +Copyright (c) 2014-2015, Michael Adam +Copyright (c) 2015, Robin Hack +Copyright (c) 2013-2018, Andreas Schneider +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. diff --git a/README b/README new file mode 100644 index 0000000..2d26237 --- /dev/null +++ b/README @@ -0,0 +1,24 @@ +NSS_WRAPPER +=========== + +This is a wrapper for the user, group and hosts NSS API. + +DESCRIPTION +----------- + +More details can be found in the manpage: + + man -l ./doc/nss_wrapper.1 + +or the raw text version: + + less ./doc/nss_wrapper.1.txt + +For installation instructions please take a look at the README.install file. + +MAILINGLIST +----------- + +As the mailing list samba-technical is used and can be found here: + +https://lists.samba.org/mailman/listinfo/samba-technical diff --git a/README.install b/README.install new file mode 100644 index 0000000..7ee4e71 --- /dev/null +++ b/README.install @@ -0,0 +1,78 @@ +Obtaining the sources +===================== + +Source tarballs for nss_wrapper can be downloaded from + + https://ftp.samba.org/pub/cwrap/ + +The source code repository for socket wrapper is located under + + git://git.samba.org/nss_wrapper.git + +To create a local copy, run + + $ git clone git://git.samba.org/nss_wrapper.git + $ cd nss_wrapper + +Building from sources +===================== + +nss_wrapper uses cmake (www.cmake.org) as its build system. + +In an unpacked sources base directory, create a directory to +contain the build results, e.g. + + $ mkdir obj + $ cd obj + +Note that "obj" is just an example. The directory can +be named arbitrarily. + +Next, run cmake to configure the build, e.g. + + $ cmake -DCMAKE_INSTALL_PREFIX= .. + +or on a 64 bit red hat system: + + $ cmake -DCMAKE_INSTALL_PREFIX= -DLIB_SUFFIX=64 .. + +The "" should be replaced by the intended installation +target prefix directory, typically /usr or /usr/local. + +Note that the target directory does not have to be a direct +or indirect subdirectory of the source base directory: It can +be an arbitrary directory in the system. In the general case, +".." has to be replaced by a relative or absolute path of the +source base directory in the "cmake" command line. + +One can control the build type with "-DCMAKE_BUILD_TYPE=" +where can be one of Debug, Release, RelWithDebInfo, and +some more (see cmake.org). The default is "RelWithDebInfo". + +After configuring with cmake, run the build with + + $ make + +Unit testing +============ + +In order to support running the test suite after building, +the cmocka unit test framework needs to be installed (cmocka.org), +and you need to specify + + -DUNIT_TESTING=ON + +in the cmake run. After running "make", + + $ make test + +runs the test suite. + +Installing +========== + +nss_wrapper is installed into the prefix directory +after running "cmake" and "make" with + + $ make install + diff --git a/TODO b/TODO new file mode 100644 index 0000000..a83f850 --- /dev/null +++ b/TODO @@ -0,0 +1,14 @@ +TODO +===== + +Library +-------- +* None at the moment. + +Testing +-------- +* Split the testsuite in several tests. +* Add missing tests. + Check the code coverage on + http://mock.cryptomilk.org/index.php?project=nsswrapper + to see what tests are still needed. diff --git a/cmake/Modules/AddCMockaTest.cmake b/cmake/Modules/AddCMockaTest.cmake new file mode 100644 index 0000000..b2d1ca8 --- /dev/null +++ b/cmake/Modules/AddCMockaTest.cmake @@ -0,0 +1,23 @@ +# - ADD_CHECK_TEST(test_name test_source linklib1 ... linklibN) + +# Copyright (c) 2007 Daniel Gollub +# Copyright (c) 2007-2010 Andreas Schneider +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +enable_testing() +include(CTest) + +if(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW) + set(CMAKE_C_FLAGS_PROFILING "-g -O0 -Wall -W -Wshadow -Wunused-variable -Wunused-parameter -Wunused-function -Wunused -Wno-system-headers -Wwrite-strings -fprofile-arcs -ftest-coverage" CACHE STRING "Profiling Compiler Flags") + set(CMAKE_SHARED_LINKER_FLAGS_PROFILING " -fprofile-arcs -ftest-coverage" CACHE STRING "Profiling Linker Flags") + set(CMAKE_MODULE_LINKER_FLAGS_PROFILING " -fprofile-arcs -ftest-coverage" CACHE STRING "Profiling Linker Flags") + set(CMAKE_EXEC_LINKER_FLAGS_PROFILING " -fprofile-arcs -ftest-coverage" CACHE STRING "Profiling Linker Flags") +endif(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW) + +function (ADD_CMOCKA_TEST _testName _testSource) + add_executable(${_testName} ${_testSource}) + target_link_libraries(${_testName} ${ARGN}) + add_test(${_testName} ${CMAKE_CURRENT_BINARY_DIR}/${_testName}) +endfunction (ADD_CMOCKA_TEST) diff --git a/cmake/Modules/COPYING-CMAKE-SCRIPTS b/cmake/Modules/COPYING-CMAKE-SCRIPTS new file mode 100644 index 0000000..4b41776 --- /dev/null +++ b/cmake/Modules/COPYING-CMAKE-SCRIPTS @@ -0,0 +1,22 @@ +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 copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. diff --git a/cmake/Modules/CheckCCompilerFlagSSP.cmake b/cmake/Modules/CheckCCompilerFlagSSP.cmake new file mode 100644 index 0000000..2fe4395 --- /dev/null +++ b/cmake/Modules/CheckCCompilerFlagSSP.cmake @@ -0,0 +1,26 @@ +# - Check whether the C compiler supports a given flag in the +# context of a stack checking compiler option. + +# CHECK_C_COMPILER_FLAG_SSP(FLAG VARIABLE) +# +# FLAG - the compiler flag +# VARIABLE - variable to store the result +# +# This actually calls check_c_source_compiles. +# See help for CheckCSourceCompiles for a listing of variables +# that can modify the build. + +# Copyright (c) 2006, Alexander Neundorf, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +include(CheckCSourceCompiles) + +function(CHECK_C_COMPILER_FLAG_SSP _FLAG _RESULT) + set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") + set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}") + check_c_source_compiles("int main(int argc, char **argv) { char buffer[256]; return buffer[argc]=0;}" ${_RESULT}) + set(CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") +endfunction(CHECK_C_COMPILER_FLAG_SSP) diff --git a/cmake/Modules/DefineCMakeDefaults.cmake b/cmake/Modules/DefineCMakeDefaults.cmake new file mode 100644 index 0000000..22eda6f --- /dev/null +++ b/cmake/Modules/DefineCMakeDefaults.cmake @@ -0,0 +1,30 @@ +# Always include srcdir and builddir in include path +# This saves typing ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY} in +# about every subdir +# since cmake 2.4.0 +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +# Put the include dirs which are in the source or build tree +# before all other include dirs, so the headers in the sources +# are prefered over the already installed ones +# since cmake 2.4.1 +set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) + +# Use colored output +# since cmake 2.4.0 +set(CMAKE_COLOR_MAKEFILE ON) + +# Define the generic version of the libraries here +set(GENERIC_LIB_VERSION "0.1.0") +set(GENERIC_LIB_SOVERSION "0") + +# Set the default build type to release with debug info +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE RelWithDebInfo + CACHE STRING + "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." + ) +endif (NOT CMAKE_BUILD_TYPE) + +# Create the compile command database for clang by default +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/cmake/Modules/DefineCompilerFlags.cmake b/cmake/Modules/DefineCompilerFlags.cmake new file mode 100644 index 0000000..111b2f4 --- /dev/null +++ b/cmake/Modules/DefineCompilerFlags.cmake @@ -0,0 +1,88 @@ +# define system dependent compiler flags + +include(CheckCCompilerFlag) +include(CheckCCompilerFlagSSP) + +if (UNIX AND NOT WIN32) + # + # Define GNUCC compiler flags + # + if (${CMAKE_C_COMPILER_ID} MATCHES "(GNU|Clang)") + + # add -Wconversion ? + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -pedantic -pedantic-errors") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wshadow -Wmissing-prototypes -Wdeclaration-after-statement") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused -Wfloat-equal -Wpointer-arith -Wwrite-strings -Wformat-security") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-format-attribute -fstrict-aliasing -Wbad-function-cast") + + # check -Wstrict-aliasing support + check_c_compiler_flag("-Wstrict-aliasing=3" WITH_WSTRICT_ALIASING) + if (WITH_WSTRICT_ALIASING) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-aliasing=3") + endif (WITH_WSTRICT_ALIASING) + + # with -fPIC + check_c_compiler_flag("-fPIC" WITH_FPIC) + if (WITH_FPIC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + endif (WITH_FPIC) + + check_c_compiler_flag_ssp("-fstack-protector-strong" WITH_STACK_PROTECTOR_STRONG) + if (WITH_STACK_PROTECTOR_STRONG) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong") + else (!WITH_STACK_PROTECTOR_STRONG) + check_c_compiler_flag_ssp("-fstack-protector" WITH_STACK_PROTECTOR) + if (WITH_STACK_PROTECTOR) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector") + endif (WITH_STACK_PROTECTOR) + endif (WITH_STACK_PROTECTOR_STRONG) + + if (CMAKE_BUILD_TYPE) + string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) + if (CMAKE_BUILD_TYPE_LOWER MATCHES (release|relwithdebinfo|minsizerel)) + check_c_compiler_flag("-Wp,-D_FORTIFY_SOURCE=2" WITH_FORTIFY_SOURCE) + if (WITH_FORTIFY_SOURCE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wp,-D_FORTIFY_SOURCE=2") + endif (WITH_FORTIFY_SOURCE) + endif() + endif() + endif (${CMAKE_C_COMPILER_ID} MATCHES "(GNU|Clang)") + + # + # Check for large filesystem support + # + if (CMAKE_SIZEOF_VOID_P MATCHES "8") + # with large file support + execute_process( + COMMAND + getconf LFS64_CFLAGS + OUTPUT_VARIABLE + _lfs_CFLAGS + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + else (CMAKE_SIZEOF_VOID_P MATCHES "8") + # with large file support + execute_process( + COMMAND + getconf LFS_CFLAGS + OUTPUT_VARIABLE + _lfs_CFLAGS + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + endif (CMAKE_SIZEOF_VOID_P MATCHES "8") + if (_lfs_CFLAGS) + string(REGEX REPLACE "[\r\n]" " " "${_lfs_CFLAGS}" "${${_lfs_CFLAGS}}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_lfs_CFLAGS}") + endif (_lfs_CFLAGS) + +endif (UNIX AND NOT WIN32) + +if (MSVC) + # Use secure functions by defaualt and suppress warnings about + #"deprecated" functions + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1") +endif (MSVC) diff --git a/cmake/Modules/DefineInstallationPaths.cmake b/cmake/Modules/DefineInstallationPaths.cmake new file mode 100644 index 0000000..88e08ca --- /dev/null +++ b/cmake/Modules/DefineInstallationPaths.cmake @@ -0,0 +1,109 @@ +if (UNIX OR OS2) + IF (NOT APPLICATION_NAME) + MESSAGE(STATUS "${PROJECT_NAME} is used as APPLICATION_NAME") + SET(APPLICATION_NAME ${PROJECT_NAME}) + ENDIF (NOT APPLICATION_NAME) + + # Suffix for Linux + SET(LIB_SUFFIX + CACHE STRING "Define suffix of directory name (32/64)" + ) + + SET(EXEC_INSTALL_PREFIX + "${CMAKE_INSTALL_PREFIX}" + CACHE PATH "Base directory for executables and libraries" + ) + SET(SHARE_INSTALL_PREFIX + "${CMAKE_INSTALL_PREFIX}/share" + CACHE PATH "Base directory for files which go to share/" + ) + SET(DATA_INSTALL_PREFIX + "${SHARE_INSTALL_PREFIX}/${APPLICATION_NAME}" + CACHE PATH "The parent directory where applications can install their data") + + # The following are directories where stuff will be installed to + SET(BIN_INSTALL_DIR + "${EXEC_INSTALL_PREFIX}/bin" + CACHE PATH "The ${APPLICATION_NAME} binary install dir (default prefix/bin)" + ) + SET(SBIN_INSTALL_DIR + "${EXEC_INSTALL_PREFIX}/sbin" + CACHE PATH "The ${APPLICATION_NAME} sbin install dir (default prefix/sbin)" + ) + SET(LIB_INSTALL_DIR + "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" + CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is prefix/lib)" + ) + SET(LIBEXEC_INSTALL_DIR + "${EXEC_INSTALL_PREFIX}/libexec" + CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is prefix/libexec)" + ) + SET(PLUGIN_INSTALL_DIR + "${LIB_INSTALL_DIR}/${APPLICATION_NAME}" + CACHE PATH "The subdirectory relative to the install prefix where plugins will be installed (default is prefix/lib/${APPLICATION_NAME})" + ) + SET(INCLUDE_INSTALL_DIR + "${CMAKE_INSTALL_PREFIX}/include" + CACHE PATH "The subdirectory to the header prefix (default prefix/include)" + ) + + set(CMAKE_INSTALL_DIR + "${LIB_INSTALL_DIR}/cmake" + CACHE PATH "The subdirectory to install cmake config files") + + SET(DATA_INSTALL_DIR + "${DATA_INSTALL_PREFIX}" + CACHE PATH "The parent directory where applications can install their data (default prefix/share/${APPLICATION_NAME})" + ) + SET(HTML_INSTALL_DIR + "${DATA_INSTALL_PREFIX}/doc/HTML" + CACHE PATH "The HTML install dir for documentation (default data/doc/html)" + ) + SET(ICON_INSTALL_DIR + "${DATA_INSTALL_PREFIX}/icons" + CACHE PATH "The icon install dir (default data/icons/)" + ) + SET(SOUND_INSTALL_DIR + "${DATA_INSTALL_PREFIX}/sounds" + CACHE PATH "The install dir for sound files (default data/sounds)" + ) + + SET(LOCALE_INSTALL_DIR + "${SHARE_INSTALL_PREFIX}/locale" + CACHE PATH "The install dir for translations (default prefix/share/locale)" + ) + + SET(XDG_APPS_DIR + "${SHARE_INSTALL_PREFIX}/applications/" + CACHE PATH "The XDG apps dir" + ) + SET(XDG_DIRECTORY_DIR + "${SHARE_INSTALL_PREFIX}/desktop-directories" + CACHE PATH "The XDG directory" + ) + + SET(SYSCONF_INSTALL_DIR + "${EXEC_INSTALL_PREFIX}/etc" + CACHE PATH "The ${APPLICATION_NAME} sysconfig install dir (default prefix/etc)" + ) + SET(MAN_INSTALL_DIR + "${SHARE_INSTALL_PREFIX}/man" + CACHE PATH "The ${APPLICATION_NAME} man install dir (default prefix/man)" + ) + SET(INFO_INSTALL_DIR + "${SHARE_INSTALL_PREFIX}/info" + CACHE PATH "The ${APPLICATION_NAME} info install dir (default prefix/info)" + ) +else() + # Same same + set(BIN_INSTALL_DIR "bin" CACHE PATH "-") + set(SBIN_INSTALL_DIR "sbin" CACHE PATH "-") + set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH "-") + set(INCLUDE_INSTALL_DIR "include" CACHE PATH "-") + set(CMAKE_INSTALL_DIR "CMake" CACHE PATH "-") + set(PLUGIN_INSTALL_DIR "plugins" CACHE PATH "-") + set(HTML_INSTALL_DIR "doc/HTML" CACHE PATH "-") + set(ICON_INSTALL_DIR "icons" CACHE PATH "-") + set(SOUND_INSTALL_DIR "soudns" CACHE PATH "-") + set(LOCALE_INSTALL_DIR "lang" CACHE PATH "-") +endif () diff --git a/cmake/Modules/DefinePlatformDefaults.cmake b/cmake/Modules/DefinePlatformDefaults.cmake new file mode 100644 index 0000000..502d936 --- /dev/null +++ b/cmake/Modules/DefinePlatformDefaults.cmake @@ -0,0 +1,28 @@ +# Set system vars + +if (CMAKE_SYSTEM_NAME MATCHES "Linux") + set(LINUX TRUE) +endif(CMAKE_SYSTEM_NAME MATCHES "Linux") + +if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + set(FREEBSD TRUE) + set(BSD TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + +if (CMAKE_SYSTEM_NAME MATCHES "OpenBSD") + set(OPENBSD TRUE) + set(BSD TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "OpenBSD") + +if (CMAKE_SYSTEM_NAME MATCHES "NetBSD") + set(NETBSD TRUE) + set(BSD TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") + +if (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") + set(SOLARIS TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") + +if (CMAKE_SYSTEM_NAME MATCHES "OS2") + set(OS2 TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "OS2") diff --git a/cmake/Modules/FindCMocka.cmake b/cmake/Modules/FindCMocka.cmake new file mode 100644 index 0000000..2dd9fc5 --- /dev/null +++ b/cmake/Modules/FindCMocka.cmake @@ -0,0 +1,49 @@ +# - Try to find CMocka +# Once done this will define +# +# CMOCKA_ROOT_DIR - Set this variable to the root installation of CMocka +# +# Read-Only variables: +# CMOCKA_FOUND - system has CMocka +# CMOCKA_INCLUDE_DIR - the CMocka include directory +# CMOCKA_LIBRARIES - Link these to use CMocka +# CMOCKA_DEFINITIONS - Compiler switches required for using CMocka +# +#============================================================================= +# Copyright (c) 2011-2012 Andreas Schneider +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# + +find_path(CMOCKA_INCLUDE_DIR + NAMES + cmocka.h + PATHS + ${CMOCKA_ROOT_DIR}/include +) + +find_library(CMOCKA_LIBRARY + NAMES + cmocka + PATHS + ${CMOCKA_ROOT_DIR}/include +) + +if (CMOCKA_LIBRARY) + set(CMOCKA_LIBRARIES + ${CMOCKA_LIBRARIES} + ${CMOCKA_LIBRARY} + ) +endif (CMOCKA_LIBRARY) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(CMocka DEFAULT_MSG CMOCKA_LIBRARIES CMOCKA_INCLUDE_DIR) + +# show the CMOCKA_INCLUDE_DIR and CMOCKA_LIBRARIES variables only in the advanced view +mark_as_advanced(CMOCKA_INCLUDE_DIR CMOCKA_LIBRARIES) diff --git a/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake b/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake new file mode 100644 index 0000000..a2e9480 --- /dev/null +++ b/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake @@ -0,0 +1,17 @@ +# - MACRO_ENSURE_OUT_OF_SOURCE_BUILD() +# MACRO_ENSURE_OUT_OF_SOURCE_BUILD() + +# Copyright (c) 2006, Alexander Neundorf, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +macro (MACRO_ENSURE_OUT_OF_SOURCE_BUILD _errorMessage) + + string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" _insource) + if (_insource) + message(SEND_ERROR "${_errorMessage}") + message(FATAL_ERROR "Remove the file CMakeCache.txt in ${CMAKE_SOURCE_DIR} first.") + endif (_insource) + +endmacro (MACRO_ENSURE_OUT_OF_SOURCE_BUILD) diff --git a/config.h.cmake b/config.h.cmake new file mode 100644 index 0000000..b18dff2 --- /dev/null +++ b/config.h.cmake @@ -0,0 +1,103 @@ +/* Name of package */ +#cmakedefine PACKAGE "${APPLICATION_NAME}" + +/* Version number of package */ +#cmakedefine VERSION "${APPLICATION_VERSION}" + +#cmakedefine LOCALEDIR "${LOCALE_INSTALL_DIR}" +#cmakedefine DATADIR "${DATADIR}" +#cmakedefine LIBDIR "${LIBDIR}" +#cmakedefine PLUGINDIR "${PLUGINDIR}" +#cmakedefine SYSCONFDIR "${SYSCONFDIR}" +#cmakedefine BINARYDIR "${BINARYDIR}" +#cmakedefine SOURCEDIR "${SOURCEDIR}" + +/************************** HEADER FILES *************************/ + +#cmakedefine HAVE_SYS_TYPES_H 1 +#cmakedefine HAVE_PWD_H 1 +#cmakedefine HAVE_SHADOW_H 1 +#cmakedefine HAVE_GRP_H 1 +#cmakedefine HAVE_NSS_H 1 +#cmakedefine HAVE_NSS_COMMON_H 1 + +/*************************** FUNCTIONS ***************************/ + +/* Define to 1 if you have the `getpwnam_r' function. */ +#cmakedefine HAVE_GETPWNAM_R 1 + +/* Define to 1 if you have the `getpwuid_r' function. */ +#cmakedefine HAVE_GETPWUID_R 1 + +/* Define to 1 if you have the `getpwent_r' function. */ +#cmakedefine HAVE_GETPWENT_R 1 + +/* Define to 1 if you have the `setspent' function. */ +#cmakedefine HAVE_SETSPENT 1 + +/* Define to 1 if you have the `getspnam' function. */ +#cmakedefine HAVE_GETSPNAM 1 + +/* Define to 1 if you have the `getgrnam_r' function. */ +#cmakedefine HAVE_GETGRNAM_R 1 + +/* Define to 1 if you have the `getgrgid_r' function. */ +#cmakedefine HAVE_GETGRGID_R 1 + +/* Define to 1 if you have the `getgrent_r' function. */ +#cmakedefine HAVE_GETGRENT_R 1 + +/* Define to 1 if you have the `getgrouplist' function. */ +#cmakedefine HAVE_GETGROUPLIST 1 + +/* Define to 1 if you have the `gethostbyaddr_r' function. */ +#cmakedefine HAVE_GETHOSTBYADDR_R 1 + +/* Define to 1 if you have the `gethostbyname_r' function. */ +#cmakedefine HAVE_GETHOSTBYNAME_R 1 + +/* Define to 1 if you have the `gethostbyname2' function. */ +#cmakedefine HAVE_GETHOSTBYNAME2 1 + +#cmakedefine HAVE___POSIX_GETPWNAM_R 1 +#cmakedefine HAVE___POSIX_GETPWUID_R 1 + +#cmakedefine HAVE___POSIX_GETGRGID_R 1 +#cmakedefine HAVE___POSIX_GETGRNAM_R 1 + +/*************************** LIBRARIES ***************************/ + +#cmakedefine HAVE_LIBNSL 1 +#cmakedefine HAVE_LIBSOCKET 1 + +/**************************** OPTIONS ****************************/ + +#cmakedefine HAVE_SOLARIS_GETPWENT_R 1 +#cmakedefine HAVE_SOLARIS_GETPWNAM_R 1 +#cmakedefine HAVE_SOLARIS_GETPWUID_R 1 +#cmakedefine HAVE_SOLARIS_GETGRENT_R 1 +#cmakedefine HAVE_SOLARIS_GETGRNAM_R 1 +#cmakedefine HAVE_SOLARIS_GETGRGID_R 1 +#cmakedefine HAVE_SOLARIS_SETHOSTENT 1 +#cmakedefine HAVE_SOLARIS_ENDHOSTENT 1 +#cmakedefine HAVE_SOLARIS_GETHOSTNAME 1 +#cmakedefine HAVE_BSD_SETGRENT 1 +#cmakedefine HAVE_LINUX_GETNAMEINFO 1 +#cmakedefine HAVE_LINUX_GETNAMEINFO_UNSIGNED 1 + +#cmakedefine HAVE_STRUCT_SOCKADDR_SA_LEN 1 +#cmakedefine HAVE_STRUCT_PASSWD_PW_CLASS 1 +#cmakedefine HAVE_STRUCT_PASSWD_PW_CHANGE 1 +#cmakedefine HAVE_STRUCT_PASSWD_PW_EXPIRE 1 + +#cmakedefine HAVE_IPV6 1 + +#cmakedefine HAVE_ATTRIBUTE_PRINTF_FORMAT 1 +#cmakedefine HAVE_CONSTRUCTOR_ATTRIBUTE 1 +#cmakedefine HAVE_DESTRUCTOR_ATTRIBUTE 1 + +/*************************** ENDIAN *****************************/ + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#cmakedefine WORDS_BIGENDIAN 1 diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 0000000..353168a --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,4 @@ +install(FILES + nss_wrapper.1 + DESTINATION + ${MAN_INSTALL_DIR}/man1) diff --git a/doc/README b/doc/README new file mode 100644 index 0000000..7217244 --- /dev/null +++ b/doc/README @@ -0,0 +1,3 @@ +The manpage is written with asciidoc. To generate the manpage use: + +a2x --doctype manpage --format manpage doc/nss_wrapper.1.txt diff --git a/doc/nss_wrapper.1 b/doc/nss_wrapper.1 new file mode 100644 index 0000000..940438d --- /dev/null +++ b/doc/nss_wrapper.1 @@ -0,0 +1,183 @@ +'\" t +.\" Title: nss_wrapper +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.78.1 +.\" Date: 2015-09-12 +.\" Manual: \ \& +.\" Source: \ \& +.\" Language: English +.\" +.TH "NSS_WRAPPER" "1" "2015\-09\-12" "\ \&" "\ \&" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +nss_wrapper \- A wrapper for the user, group and hosts NSS API +.SH "SYNOPSIS" +.sp +LD_PRELOAD=libnss_wrapper\&.so NSS_WRAPPER_PASSWD=/path/to/passwd NSS_WRAPPER_GROUP=/path/to/group NSS_WRAPPER_HOSTS=/path/to/host \fB\&./myapplication\fR +.SH "DESCRIPTION" +.sp +There are projects which provide daemons needing to be able to create, modify and delete Unix users\&. Or just switch user ids to interact with the system e\&.g\&. a user space file server\&. To be able to test that you need the privilege to modify the passwd and groups file\&. With nss_wrapper it is possible to define your own passwd and groups file which will be used by software to act correctly while under test\&. +.sp +If you have a client and server under test they normally use functions to resolve network names to addresses (dns) or vice versa\&. The nss_wrappers allow you to create a hosts file to setup name resolution for the addresses you use with socket_wrapper\&. +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Provides information for user and group accounts\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Network name resolution using a hosts file\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Loading and testing of NSS modules\&. +.RE +.SH "LIMITATIONS" +.sp +Some calls in nss_wrapper will only work if uid_wrapper is loaded and active\&. One of this functions is initgroups() which needs to run setgroups() to set the groups for the user\&. setgroups() is wrapped by uid_wrapper\&. +.SH "ENVIRONMENT VARIABLES" +.PP +\fBNSS_WRAPPER_PASSWD\fR, \fBNSS_WRAPPER_GROUP\fR +.RS 4 +For user and group accounts you need to create two files: +\fIpasswd\fR +and +\fIgroup\fR\&. The format of the passwd file is described in +\fIman 5 passwd\fR +and the group file in +\fIman 5 group\fR\&. So you can fill these files with made up accounts\&. You point nss_wrapper to them using the two variables NSS_WRAPPER_PASSWD=/path/to/your/passwd and NSS_WRAPPER_GROUP=/path/to/your/group\&. +.RE +.PP +\fBNSS_WRAPPER_HOSTS\fR +.RS 4 +If you also need to emulate network name resolution in your enviornment, especially with socket_wrapper, you can write a hosts file\&. The format is described in +\fIman 5 hosts\fR\&. Then you can point nss_wrapper to your hosts file using: NSS_WRAPPER_HOSTS=/path/to/your/hosts +.RE +.PP +\fBNSS_WRAPPER_HOSTNAME\fR +.RS 4 +If you need to return a hostname which is different from the one of your machine is using you can use: NSS_WRAPPER_HOSTNAME=test\&.example\&.org +.RE +.PP +\fBNSS_WRAPPER_MODULE_SO_PATH\fR, \fBNSS_WRAPPER_MODULE_FN_PREFIX\fR +.RS 4 +If you have a project which also provides user and group information out of a database, you normally write your own nss modules\&. nss_wrapper is able to load nss modules and ask them first before looking into the faked passwd and group file\&. To point nss_wrapper to the module you can do that using NSS_WRAPPER_MODULE_SO_PATH=/path/to/libnss_yourmodule\&.so\&. As each nss module has a special prefix like _nss_winbind_getpwnam() you need to set the prefix too so nss_wrapper can load the functions with NSS_WRAPPER_MODULE_FN_PREFIX=\&. +.RE +.sp +For _nss_winbind_getpwnam() this would be: +.sp +.if n \{\ +.RS 4 +.\} +.nf +NSS_WRAPPER_MODULE_FN_PREFIX=winbind +.fi +.if n \{\ +.RE +.\} +.PP +\fBNSS_WRAPPER_DEBUGLEVEL\fR +.RS 4 +If you need to see what is going on in nss_wrapper itself or try to find a bug, you can enable logging support in nss_wrapper if you built it with debug symbols\&. +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +0 = ERROR +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +1 = WARNING +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +2 = DEBUG +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +3 = TRACE +.RE +.RE +.SH "EXAMPLE" +.sp +.if n \{\ +.RS 4 +.\} +.nf +$ echo "bob:x:1000:1000:bob gecos:/home/test/bob:/bin/false" > passwd +$ echo "root:x:65534:65532:root gecos:/home/test/root:/bin/false" >> passwd +$ echo "users:x:1000:" > group +$ echo "root:x:65532:" >> group +$ LD_PRELOAD=libnss_wrapper\&.so NSS_WRAPPER_PASSWD=passwd \e + NSS_WRAPPER_GROUP=group getent passwd bob +bob:x:1000:1000:bob gecos:/home/test/bob:/bin/false +$ LD_PRELOAD=libnss_wrapper\&.so NSS_WRAPPER_HOSTNAME=test\&.example\&.org hostname +test\&.example\&.org +.fi +.if n \{\ +.RE +.\} diff --git a/doc/nss_wrapper.1.txt b/doc/nss_wrapper.1.txt new file mode 100644 index 0000000..d541e31 --- /dev/null +++ b/doc/nss_wrapper.1.txt @@ -0,0 +1,104 @@ +nss_wrapper(1) +============== +:revdate: 2015-09-12 + +NAME +---- + +nss_wrapper - A wrapper for the user, group and hosts NSS API + +SYNOPSIS +-------- + +LD_PRELOAD=libnss_wrapper.so NSS_WRAPPER_PASSWD=/path/to/passwd NSS_WRAPPER_GROUP=/path/to/group NSS_WRAPPER_HOSTS=/path/to/host *./myapplication* + +DESCRIPTION +----------- + +There are projects which provide daemons needing to be able to create, modify +and delete Unix users. Or just switch user ids to interact with the system e.g. +a user space file server. To be able to test that you need the privilege to +modify the passwd and groups file. With nss_wrapper it is possible to define +your own passwd and groups file which will be used by software to act correctly +while under test. + +If you have a client and server under test they normally use functions to +resolve network names to addresses (dns) or vice versa. The nss_wrappers allow +you to create a hosts file to setup name resolution for the addresses you use +with socket_wrapper. + +- Provides information for user and group accounts. +- Network name resolution using a hosts file. +- Loading and testing of NSS modules. + +LIMITATIONS +----------- + +Some calls in nss_wrapper will only work if uid_wrapper is loaded and active. +One of this functions is initgroups() which needs to run setgroups() to set +the groups for the user. setgroups() is wrapped by uid_wrapper. + +ENVIRONMENT VARIABLES +--------------------- + +*NSS_WRAPPER_PASSWD*:: +*NSS_WRAPPER_GROUP*:: + +For user and group accounts you need to create two files: 'passwd' and 'group'. +The format of the passwd file is described in 'man 5 passwd' and the group file +in 'man 5 group'. So you can fill these files with made up accounts. You point +nss_wrapper to them using the two variables +NSS_WRAPPER_PASSWD=/path/to/your/passwd and +NSS_WRAPPER_GROUP=/path/to/your/group. + +*NSS_WRAPPER_HOSTS*:: + +If you also need to emulate network name resolution in your enviornment, +especially with socket_wrapper, you can write a hosts file. The format is +described in 'man 5 hosts'. Then you can point nss_wrapper to your hosts +file using: NSS_WRAPPER_HOSTS=/path/to/your/hosts + +*NSS_WRAPPER_HOSTNAME*:: + +If you need to return a hostname which is different from the one of your +machine is using you can use: NSS_WRAPPER_HOSTNAME=test.example.org + +*NSS_WRAPPER_MODULE_SO_PATH*:: +*NSS_WRAPPER_MODULE_FN_PREFIX*:: + +If you have a project which also provides user and group information out of a +database, you normally write your own nss modules. nss_wrapper is able to load +nss modules and ask them first before looking into the faked passwd and group +file. To point nss_wrapper to the module you can do that using +NSS_WRAPPER_MODULE_SO_PATH=/path/to/libnss_yourmodule.so. As each nss module +has a special prefix like _nss_winbind_getpwnam() you need to set the prefix +too so nss_wrapper can load the functions with +NSS_WRAPPER_MODULE_FN_PREFIX=. + +For _nss_winbind_getpwnam() this would be: + + NSS_WRAPPER_MODULE_FN_PREFIX=winbind + +*NSS_WRAPPER_DEBUGLEVEL*:: + +If you need to see what is going on in nss_wrapper itself or try to find a +bug, you can enable logging support in nss_wrapper if you built it with +debug symbols. + +- 0 = ERROR +- 1 = WARNING +- 2 = DEBUG +- 3 = TRACE + +EXAMPLE +------- + + $ echo "bob:x:1000:1000:bob gecos:/home/test/bob:/bin/false" > passwd + $ echo "root:x:65534:65532:root gecos:/home/test/root:/bin/false" >> passwd + $ echo "users:x:1000:" > group + $ echo "root:x:65532:" >> group + $ LD_PRELOAD=libnss_wrapper.so NSS_WRAPPER_PASSWD=passwd \ + NSS_WRAPPER_GROUP=group getent passwd bob + bob:x:1000:1000:bob gecos:/home/test/bob:/bin/false + $ LD_PRELOAD=libnss_wrapper.so NSS_WRAPPER_HOSTNAME=test.example.org hostname + test.example.org diff --git a/nss_wrapper-config-version.cmake.in b/nss_wrapper-config-version.cmake.in new file mode 100644 index 0000000..98f292c --- /dev/null +++ b/nss_wrapper-config-version.cmake.in @@ -0,0 +1,11 @@ +set(PACKAGE_VERSION @APPLICATION_VERSION@) + +# Check whether the requested PACKAGE_FIND_VERSION is compatible +if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/nss_wrapper-config.cmake.in b/nss_wrapper-config.cmake.in new file mode 100644 index 0000000..3932a87 --- /dev/null +++ b/nss_wrapper-config.cmake.in @@ -0,0 +1 @@ +set(NSS_WRAPPER_LIBRARY @LIB_INSTALL_DIR@/@NSS_WRAPPER_LIB@) diff --git a/nss_wrapper.pc.cmake b/nss_wrapper.pc.cmake new file mode 100644 index 0000000..9fca0f8 --- /dev/null +++ b/nss_wrapper.pc.cmake @@ -0,0 +1,4 @@ +Name: @APPLICATION_NAME@ +Description: The nss_wrapper library +Version: @APPLICATION_VERSION@ +Libs: @LIB_INSTALL_DIR@/@NSS_WRAPPER_LIB@ diff --git a/nss_wrapper.pl b/nss_wrapper.pl new file mode 100755 index 0000000..1171e80 --- /dev/null +++ b/nss_wrapper.pl @@ -0,0 +1,521 @@ +#!/usr/bin/env 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 of the 'passwd' file. + --group_path Path of the 'group' file. + + --type 'passwd', 'group' and 'member' are supported. + + --action 'add' or 'delete'. + + --name The name of the object. + + --member The name of the member. + + --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 "); +} +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 "); +} +if ($opt_name eq "") { + usage(1, "invalid: --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 "); + } + if ($path eq "" or $path eq "/") { + usage(1, "invalid: --$type\_path : '$path'"); + } + my $fullpath = abs_path($path); + if (not defined($fullpath)) { + usage(1, "invalid: --$type\_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 = ; + 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 = ; + 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/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..2656e9b --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,28 @@ +project(libnss_wrapper C) + +include_directories(${CMAKE_BINARY_DIR}) +add_library(nss_wrapper SHARED nss_wrapper.c) +target_link_libraries(nss_wrapper ${NWRAP_REQUIRED_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + +if (BSD) + add_definitions(-DBSD) +endif (BSD) + +set_target_properties( + nss_wrapper + PROPERTIES + VERSION + ${LIBRARY_VERSION} + SOVERSION + ${LIBRARY_SOVERSION} +) + +install( + TARGETS + nss_wrapper + RUNTIME DESTINATION ${BIN_INSTALL_DIR} + LIBRARY DESTINATION ${LIB_INSTALL_DIR} + ARCHIVE DESTINATION ${LIB_INSTALL_DIR} +) + +set(NSS_WRAPPER_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}nss_wrapper${CMAKE_SHARED_LIBRARY_SUFFIX}" PARENT_SCOPE) diff --git a/src/nss_wrapper.c b/src/nss_wrapper.c new file mode 100644 index 0000000..81d900c --- /dev/null +++ b/src/nss_wrapper.c @@ -0,0 +1,5686 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2007, Stefan Metzmacher + * Copyright (c) 2009, Guenther Deschner + * Copyright (c) 2014-2015, Michael Adam + * Copyright (c) 2015, Robin Hack + * Copyright (c) 2013-2018, Andreas Schneider + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* + * 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 +#include +#ifdef HAVE_SHADOW_H +#include +#endif /* HAVE_SHADOW_H */ + +#include +#include +#include + +#include + +#if defined(HAVE_NSS_H) +/* Linux and BSD */ +#include + +typedef enum nss_status NSS_STATUS; +#elif defined(HAVE_NSS_COMMON_H) +/* Solaris */ +#include +#include +#include + +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 NWRAP_LOCK(m) do { \ + pthread_mutex_lock(&( m ## _mutex)); \ +} while(0) + +#define NWRAP_UNLOCK(m) do { \ + pthread_mutex_unlock(&( m ## _mutex)); \ +} while(0) + + +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; + +/* Add new global locks here please */ +/* Also don't forget to add locks to + * nwrap_init() function. + */ +# define NWRAP_LOCK_ALL do { \ + NWRAP_LOCK(nwrap_initialized); \ + NWRAP_LOCK(nwrap_global); \ + NWRAP_LOCK(nwrap_gr_global); \ + NWRAP_LOCK(nwrap_he_global); \ + NWRAP_LOCK(nwrap_pw_global); \ + NWRAP_LOCK(nwrap_sp_global); \ +} while (0); + +# define NWRAP_UNLOCK_ALL do {\ + NWRAP_UNLOCK(nwrap_sp_global); \ + NWRAP_UNLOCK(nwrap_pw_global); \ + NWRAP_UNLOCK(nwrap_he_global); \ + NWRAP_UNLOCK(nwrap_gr_global); \ + NWRAP_UNLOCK(nwrap_global); \ + NWRAP_UNLOCK(nwrap_initialized); \ +} while (0); + +static void nwrap_init(void); + +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_UNLOCK_ALL; +} + +enum nwrap_dbglvl_e { + NWRAP_LOG_ERROR = 0, + NWRAP_LOG_WARN, + NWRAP_LOG_DEBUG, + NWRAP_LOG_TRACE +}; + +#ifdef NDEBUG +# define NWRAP_LOG(...) +#else + +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; + int pid = getpid(); + + d = getenv("NSS_WRAPPER_DEBUGLEVEL"); + if (d != NULL) { + lvl = atoi(d); + } + + va_start(va, format); + vsnprintf(buffer, sizeof(buffer), format, va); + va_end(va); + + if (lvl >= dbglvl) { + switch (dbglvl) { + case NWRAP_LOG_ERROR: + fprintf(stderr, + "NWRAP_ERROR(%d) - %s: %s\n", + pid, func, buffer); + break; + case NWRAP_LOG_WARN: + fprintf(stderr, + "NWRAP_WARN(%d) - %s: %s\n", + pid, func, buffer); + break; + case NWRAP_LOG_DEBUG: + fprintf(stderr, + "NWRAP_DEBUG(%d) - %s: %s\n", + pid, func, buffer); + break; + case NWRAP_LOG_TRACE: + fprintf(stderr, + "NWRAP_TRACE(%d) - %s: %s\n", + pid, func, buffer); + break; + } + } +} +#endif /* NDEBUG NWRAP_LOG */ + +struct nwrap_libc_fns { + struct passwd *(*_libc_getpwnam)(const char *name); + int (*_libc_getpwnam_r)(const char *name, struct passwd *pwd, + char *buf, size_t buflen, struct passwd **result); + struct passwd *(*_libc_getpwuid)(uid_t uid); + int (*_libc_getpwuid_r)(uid_t uid, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result); + void (*_libc_setpwent)(void); + struct passwd *(*_libc_getpwent)(void); +#ifdef HAVE_GETPWENT_R +# ifdef HAVE_SOLARIS_GETPWENT_R + struct passwd *(*_libc_getpwent_r)(struct passwd *pwbuf, char *buf, size_t buflen); +# else /* HAVE_SOLARIS_GETPWENT_R */ + int (*_libc_getpwent_r)(struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp); +# endif /* HAVE_SOLARIS_GETPWENT_R */ +#endif /* HAVE_GETPWENT_R */ + void (*_libc_endpwent)(void); + int (*_libc_initgroups)(const char *user, gid_t gid); + struct group *(*_libc_getgrnam)(const char *name); + int (*_libc_getgrnam_r)(const char *name, struct group *grp, char *buf, size_t buflen, struct group **result); + struct group *(*_libc_getgrgid)(gid_t gid); + int (*_libc_getgrgid_r)(gid_t gid, struct group *grp, char *buf, size_t buflen, struct group **result); + void (*_libc_setgrent)(void); + struct group *(*_libc_getgrent)(void); +#ifdef HAVE_GETGRENT_R +# ifdef HAVE_SOLARIS_GETGRENT_R + struct group *(*_libc_getgrent_r)(struct group *group, char *buf, size_t buflen); +# else /* HAVE_SOLARIS_GETGRENT_R */ + int (*_libc_getgrent_r)(struct group *group, char *buf, size_t buflen, struct group **result); +# endif /* HAVE_SOLARIS_GETGRENT_R */ +#endif /* HAVE_GETGRENT_R */ + void (*_libc_endgrent)(void); + int (*_libc_getgrouplist)(const char *user, gid_t group, gid_t *groups, int *ngroups); + + void (*_libc_sethostent)(int stayopen); + struct hostent *(*_libc_gethostent)(void); + void (*_libc_endhostent)(void); + + struct hostent *(*_libc_gethostbyname)(const char *name); +#ifdef HAVE_GETHOSTBYNAME2 /* GNU extension */ + struct hostent *(*_libc_gethostbyname2)(const char *name, int af); +#endif + struct hostent *(*_libc_gethostbyaddr)(const void *addr, socklen_t len, int type); + + int (*_libc_getaddrinfo)(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res); + int (*_libc_getnameinfo)(const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, + char *serv, size_t servlen, + int flags); + int (*_libc_gethostname)(char *name, size_t len); +#ifdef HAVE_GETHOSTBYNAME_R + 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 + 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 +}; + +struct nwrap_module_nss_fns { + NSS_STATUS (*_nss_getpwnam_r)(const char *name, struct passwd *result, char *buffer, + size_t buflen, int *errnop); + NSS_STATUS (*_nss_getpwuid_r)(uid_t uid, struct passwd *result, char *buffer, + size_t buflen, int *errnop); + NSS_STATUS (*_nss_setpwent)(void); + NSS_STATUS (*_nss_getpwent_r)(struct passwd *result, char *buffer, + size_t buflen, int *errnop); + NSS_STATUS (*_nss_endpwent)(void); + NSS_STATUS (*_nss_initgroups)(const char *user, gid_t group, long int *start, + long int *size, gid_t **groups, long int limit, int *errnop); + NSS_STATUS (*_nss_getgrnam_r)(const char *name, struct group *result, char *buffer, + size_t buflen, int *errnop); + NSS_STATUS (*_nss_getgrgid_r)(gid_t gid, struct group *result, char *buffer, + size_t buflen, int *errnop); + NSS_STATUS (*_nss_setgrent)(void); + NSS_STATUS (*_nss_getgrent_r)(struct group *result, char *buffer, + size_t buflen, int *errnop); + NSS_STATUS (*_nss_endgrent)(void); +}; + +struct nwrap_backend { + const char *name; + const char *so_path; + void *so_handle; + struct nwrap_ops *ops; + struct nwrap_module_nss_fns *fns; +}; + +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)(struct nwrap_backend *b, + const char *user, gid_t group); + 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); +}; + +/* 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(struct nwrap_backend *b, + const char *user, gid_t group); +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); + +/* 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(struct nwrap_backend *b, + const char *user, gid_t group); + +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 = nwrap_files_initgroups, + .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, +}; + +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 = nwrap_module_initgroups, + .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, +}; + +struct nwrap_libc { + void *handle; + void *nsl_handle; + void *sock_handle; + struct nwrap_libc_fns *fns; +}; + +struct nwrap_main { + int 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); + +/* + * 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); +void nwrap_constructor(void) CONSTRUCTOR_ATTRIBUTE; +void nwrap_destructor(void) DESTRUCTOR_ATTRIBUTE; + +/********************************************************* + * NWRAP LIBC LOADER FUNCTIONS + *********************************************************/ + +enum nwrap_lib { + NWRAP_LIBC, + NWRAP_LIBNSL, + NWRAP_LIBSOCKET, +}; + +#ifndef NDEBUG +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"; +} +#endif + +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 = getenv("LD_PRELOAD"); + + /* Don't do a deepbind if we run with libasan */ + if (env != NULL && strlen(env) < 1024) { + const char *p = strstr(env, "libasan.so"); + if (p == NULL) { + 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_load_lib_function(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_load_lib_function(lib, fn_name) \ + if (nwrap_main_global->libc->fns->_libc_##fn_name == NULL) { \ + *(void **) (&nwrap_main_global->libc->fns->_libc_##fn_name) = \ + _nwrap_load_lib_function(lib, #fn_name); \ + } + +/* 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_load_lib_function(NWRAP_LIBC, getpwnam); + + return nwrap_main_global->libc->fns->_libc_getpwnam(name); +} + +#ifdef HAVE_GETPWNAM_R +static int libc_getpwnam_r(const char *name, + struct passwd *pwd, + char *buf, + size_t buflen, + struct passwd **result) +{ +#ifdef HAVE___POSIX_GETPWNAM_R + if (nwrap_main_global->libc->fns->_libc_getpwnam_r == NULL) { + *(void **) (&nwrap_main_global->libc->fns->_libc_getpwnam_r) = + _nwrap_load_lib_function(NWRAP_LIBC, "__posix_getpwnam_r"); + } +#else + nwrap_load_lib_function(NWRAP_LIBC, getpwnam_r); +#endif + + return nwrap_main_global->libc->fns->_libc_getpwnam_r(name, + pwd, + buf, + buflen, + result); +} +#endif + +static struct passwd *libc_getpwuid(uid_t uid) +{ + nwrap_load_lib_function(NWRAP_LIBC, getpwuid); + + return nwrap_main_global->libc->fns->_libc_getpwuid(uid); +} + +#ifdef HAVE_GETPWUID_R +static int libc_getpwuid_r(uid_t uid, + struct passwd *pwd, + char *buf, + size_t buflen, + struct passwd **result) +{ +#ifdef HAVE___POSIX_GETPWUID_R + if (nwrap_main_global->libc->fns->_libc_getpwuid_r == NULL) { + *(void **) (&nwrap_main_global->libc->fns->_libc_getpwuid_r) = + _nwrap_load_lib_function(NWRAP_LIBC, "__posix_getpwuid_r"); + } +#else + nwrap_load_lib_function(NWRAP_LIBC, getpwuid_r); +#endif + + return nwrap_main_global->libc->fns->_libc_getpwuid_r(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_load_lib_function(NWRAP_LIBC, setpwent); + + nwrap_main_global->libc->fns->_libc_setpwent(); +} + +static struct passwd *libc_getpwent(void) +{ + nwrap_load_lib_function(NWRAP_LIBC, getpwent); + + return nwrap_main_global->libc->fns->_libc_getpwent(); +} + +#ifdef HAVE_GETPWENT_R +# ifdef HAVE_SOLARIS_GETPWENT_R +static struct passwd *libc_getpwent_r(struct passwd *pwdst, + char *buf, + int buflen) +{ + nwrap_load_lib_function(NWRAP_LIBC, getpwent_r); + + return nwrap_main_global->libc->fns->_libc_getpwent_r(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_load_lib_function(NWRAP_LIBC, getpwent_r); + + return nwrap_main_global->libc->fns->_libc_getpwent_r(pwdst, + buf, + buflen, + pwdstp); +} +# endif /* HAVE_SOLARIS_GETPWENT_R */ +#endif /* HAVE_GETPWENT_R */ + +static void libc_endpwent(void) +{ + nwrap_load_lib_function(NWRAP_LIBC, endpwent); + + nwrap_main_global->libc->fns->_libc_endpwent(); +} + +static int libc_initgroups(const char *user, gid_t gid) +{ + nwrap_load_lib_function(NWRAP_LIBC, initgroups); + + return nwrap_main_global->libc->fns->_libc_initgroups(user, gid); +} + +static struct group *libc_getgrnam(const char *name) +{ + nwrap_load_lib_function(NWRAP_LIBC, getgrnam); + + return nwrap_main_global->libc->fns->_libc_getgrnam(name); +} + +#ifdef HAVE_GETGRNAM_R +static int libc_getgrnam_r(const char *name, + struct group *grp, + char *buf, + size_t buflen, + struct group **result) +{ +#ifdef HAVE___POSIX_GETGRNAM_R + if (nwrap_main_global->libc->fns->_libc_getgrnam_r == NULL) { + *(void **) (&nwrap_main_global->libc->fns->_libc_getgrnam_r) = + _nwrap_load_lib_function(NWRAP_LIBC, "__posix_getgrnam_r"); + } +#else + nwrap_load_lib_function(NWRAP_LIBC, getgrnam_r); +#endif + + return nwrap_main_global->libc->fns->_libc_getgrnam_r(name, + grp, + buf, + buflen, + result); +} +#endif + +static struct group *libc_getgrgid(gid_t gid) +{ + nwrap_load_lib_function(NWRAP_LIBC, getgrgid); + + return nwrap_main_global->libc->fns->_libc_getgrgid(gid); +} + +#ifdef HAVE_GETGRGID_R +static int libc_getgrgid_r(gid_t gid, + struct group *grp, + char *buf, + size_t buflen, + struct group **result) +{ +#ifdef HAVE___POSIX_GETGRGID_R + if (nwrap_main_global->libc->fns->_libc_getgrgid_r == NULL) { + *(void **) (&nwrap_main_global->libc->fns->_libc_getgrgid_r) = + _nwrap_load_lib_function(NWRAP_LIBC, "__posix_getgrgid_r"); + } +#else + nwrap_load_lib_function(NWRAP_LIBC, getgrgid_r); +#endif + + return nwrap_main_global->libc->fns->_libc_getgrgid_r(gid, + grp, + buf, + buflen, + result); +} +#endif + +static void libc_setgrent(void) +{ + nwrap_load_lib_function(NWRAP_LIBC, setgrent); + + nwrap_main_global->libc->fns->_libc_setgrent(); +} + +static struct group *libc_getgrent(void) +{ + nwrap_load_lib_function(NWRAP_LIBC, getgrent); + + return nwrap_main_global->libc->fns->_libc_getgrent(); +} + +#ifdef HAVE_GETGRENT_R +# ifdef HAVE_SOLARIS_GETGRENT_R +static struct group *libc_getgrent_r(struct group *group, + char *buf, + size_t buflen) +{ + nwrap_load_lib_function(NWRAP_LIBC, getgrent_r); + + return nwrap_main_global->libc->fns->_libc_getgrent_r(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_load_lib_function(NWRAP_LIBC, getgrent_r); + + return nwrap_main_global->libc->fns->_libc_getgrent_r(group, + buf, + buflen, + result); +} +# endif /* HAVE_SOLARIS_GETGRENT_R */ +#endif /* HAVE_GETGRENT_R */ + +static void libc_endgrent(void) +{ + nwrap_load_lib_function(NWRAP_LIBC, endgrent); + + nwrap_main_global->libc->fns->_libc_endgrent(); +} + +#ifdef HAVE_GETGROUPLIST +static int libc_getgrouplist(const char *user, + gid_t group, + gid_t *groups, + int *ngroups) +{ + nwrap_load_lib_function(NWRAP_LIBC, getgrouplist); + + return nwrap_main_global->libc->fns->_libc_getgrouplist(user, + group, + groups, + ngroups); +} +#endif + +static void libc_sethostent(int stayopen) +{ + nwrap_load_lib_function(NWRAP_LIBNSL, sethostent); + + nwrap_main_global->libc->fns->_libc_sethostent(stayopen); +} + +static struct hostent *libc_gethostent(void) +{ + nwrap_load_lib_function(NWRAP_LIBNSL, gethostent); + + return nwrap_main_global->libc->fns->_libc_gethostent(); +} + +static void libc_endhostent(void) +{ + nwrap_load_lib_function(NWRAP_LIBNSL, endhostent); + + nwrap_main_global->libc->fns->_libc_endhostent(); +} + +static struct hostent *libc_gethostbyname(const char *name) +{ + nwrap_load_lib_function(NWRAP_LIBNSL, gethostbyname); + + return nwrap_main_global->libc->fns->_libc_gethostbyname(name); +} + +#ifdef HAVE_GETHOSTBYNAME2 /* GNU extension */ +static struct hostent *libc_gethostbyname2(const char *name, int af) +{ + nwrap_load_lib_function(NWRAP_LIBNSL, gethostbyname2); + + return nwrap_main_global->libc->fns->_libc_gethostbyname2(name, af); +} +#endif + +static struct hostent *libc_gethostbyaddr(const void *addr, + socklen_t len, + int type) +{ + nwrap_load_lib_function(NWRAP_LIBNSL, gethostbyaddr); + + return nwrap_main_global->libc->fns->_libc_gethostbyaddr(addr, + len, + type); +} + +static int libc_gethostname(char *name, size_t len) +{ + nwrap_load_lib_function(NWRAP_LIBNSL, gethostname); + + return nwrap_main_global->libc->fns->_libc_gethostname(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_load_lib_function(NWRAP_LIBNSL, gethostbyname_r); + + return nwrap_main_global->libc->fns->_libc_gethostbyname_r(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_load_lib_function(NWRAP_LIBNSL, gethostbyaddr_r); + + return nwrap_main_global->libc->fns->_libc_gethostbyaddr_r(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_load_lib_function(NWRAP_LIBSOCKET, getaddrinfo); + + return nwrap_main_global->libc->fns->_libc_getaddrinfo(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_load_lib_function(NWRAP_LIBSOCKET, getnameinfo); + + return nwrap_main_global->libc->fns->_libc_getnameinfo(sa, + salen, + host, + hostlen, + serv, + servlen, + flags); +} + +/********************************************************* + * NWRAP NSS MODULE LOADER FUNCTIONS + *********************************************************/ + +static void *nwrap_load_module_fn(struct nwrap_backend *b, + const char *fn_name) +{ + void *res; + char *s; + + if (!b->so_handle) { + NWRAP_LOG(NWRAP_LOG_ERROR, "No handle"); + return NULL; + } + + if (asprintf(&s, "_nss_%s_%s", b->name, fn_name) == -1) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Out of memory"); + return NULL; + } + + res = dlsym(b->so_handle, s); + if (!res) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Cannot find function %s in %s", + s, b->so_path); + } + SAFE_FREE(s); + return res; +} + +static struct nwrap_module_nss_fns *nwrap_load_module_fns(struct nwrap_backend *b) +{ + struct nwrap_module_nss_fns *fns; + + if (!b->so_handle) { + return NULL; + } + + fns = (struct nwrap_module_nss_fns *)malloc(sizeof(struct nwrap_module_nss_fns)); + if (!fns) { + return NULL; + } + + *(void **)(&fns->_nss_getpwnam_r) = + nwrap_load_module_fn(b, "getpwnam_r"); + *(void **)(&fns->_nss_getpwuid_r) = + nwrap_load_module_fn(b, "getpwuid_r"); + *(void **)(&fns->_nss_setpwent) = + nwrap_load_module_fn(b, "setpwent"); + *(void **)(&fns->_nss_getpwent_r) = + nwrap_load_module_fn(b, "getpwent_r"); + *(void **)(&fns->_nss_endpwent) = + nwrap_load_module_fn(b, "endpwent"); + *(void **)(&fns->_nss_initgroups) = + nwrap_load_module_fn(b, "initgroups_dyn"); + *(void **)(&fns->_nss_getgrnam_r) = + nwrap_load_module_fn(b, "getgrnam_r"); + *(void **)(&fns->_nss_getgrgid_r)= + nwrap_load_module_fn(b, "getgrgid_r"); + *(void **)(&fns->_nss_setgrent) = + nwrap_load_module_fn(b, "setgrent"); + *(void **)(&fns->_nss_getgrent_r) = + nwrap_load_module_fn(b, "getgrent_r"); + *(void **)(&fns->_nss_endgrent) = + nwrap_load_module_fn(b, "endgrent"); + + return fns; +} + +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, + int *num_backends, + struct nwrap_backend **backends) +{ + struct nwrap_backend *b; + + *backends = (struct nwrap_backend *)realloc(*backends, + sizeof(struct nwrap_backend) * ((*num_backends) + 1)); + if (!*backends) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Out of memory"); + return false; + } + + b = &((*backends)[*num_backends]); + + b->name = name; + b->ops = ops; + b->so_path = so_path; + + if (so_path != NULL) { + b->so_handle = nwrap_load_module(so_path); + b->fns = nwrap_load_module_fns(b); + if (b->fns == NULL) { + return false; + } + } else { + b->so_handle = NULL; + b->fns = NULL; + } + + (*num_backends)++; + + 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); + } + + r->libc->fns = calloc(1, sizeof(struct nwrap_libc_fns)); + if (r->libc->fns == NULL) { + printf("Failed to allocate memory for libc functions"); + 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 void nwrap_init(void) +{ + const char *env; + char *endptr; + size_t max_hostents_tmp; + int ok; + + NWRAP_LOCK(nwrap_initialized); + if (nwrap_initialized) { + NWRAP_UNLOCK(nwrap_initialized); + 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_LOCK(nwrap_global); + NWRAP_LOCK(nwrap_gr_global); + NWRAP_LOCK(nwrap_he_global); + NWRAP_LOCK(nwrap_pw_global); + NWRAP_LOCK(nwrap_sp_global); + + 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_UNLOCK_ALL; +} + +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) { + 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; + 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; 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 int nwrap_gr_copy_r(const struct group *src, struct group *dst, + char *buf, size_t buflen, struct group **dstp) +{ + char *first; + char **lastm; + char *last = NULL; + off_t ofsb; + off_t ofsm; + off_t ofs; + unsigned i; + + first = src->gr_name; + + lastm = src->gr_mem; + while (*lastm) { + last = *lastm; + lastm++; + } + + if (last == NULL) { + last = src->gr_passwd; + } + while (*last) last++; + + ofsb = PTR_DIFF(last + 1, first); + ofsm = PTR_DIFF(lastm + 1, src->gr_mem); + + if ((ofsb + ofsm) > (off_t) buflen) { + return ERANGE; + } + + memcpy(buf, first, ofsb); + memcpy(buf + ofsb, src->gr_mem, ofsm); + + ofs = PTR_DIFF(src->gr_name, first); + dst->gr_name = buf + ofs; + ofs = PTR_DIFF(src->gr_passwd, first); + dst->gr_passwd = buf + ofs; + dst->gr_gid = src->gr_gid; + + dst->gr_mem = (char **)(buf + ofsb); + for (i=0; src->gr_mem[i]; i++) { + ofs = PTR_DIFF(src->gr_mem[i], first); + dst->gr_mem[i] = buf + ofs; + } + + if (dstp) { + *dstp = dst; + } + + return 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) { + 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; igr_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); + + groups = (gid_t *)realloc(groups, + (size + 1) * sizeof(gid_t)); + if (groups == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Out of memory"); + errno = ENOMEM; + return -1; + } + + groups[size] = grp->gr_gid; + size++; + } + } + } + + nwrap_files_endgrent(b); + + NWRAP_LOG(NWRAP_LOG_DEBUG, + "%s is member of %d groups", + user, size); + + /* This really only works if uid_wrapper is loaded */ + rc = setgroups(size, groups); + + free(groups); + + return rc; +} + +/* 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) { + 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_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; + + 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] == '.') { + strncpy(canon_name, name, name_len - 1); + 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; +} + +#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) +{ + struct nwrap_vector *addr_list = malloc(sizeof(struct nwrap_vector)); + int rc; + + if (addr_list == NULL) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Unable to allocate memory for address list"); + errno = ENOENT; + return -1; + } + + ZERO_STRUCTP(addr_list); + + rc = nwrap_files_gethostbyname(name, AF_UNSPEC, ret, addr_list); + if (rc == -1) { + *h_errnop = h_errno; + if (addr_list->items != NULL) { + free(addr_list->items); + } + SAFE_FREE(addr_list); + errno = ENOENT; + return -1; + } + + if (buflen < (addr_list->count * 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 *)); + + free(addr_list->items); + free(addr_list); + + ret->h_addr_list = (char **)buf; + *result = ret; + return 0; +} + +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 + +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 < DNS_NAME_MAX && name[name_len - 1] == '.') { + strncpy(canon_name, name, name_len - 1); + 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(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) 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) +{ + *result = nwrap_files_gethostbyaddr(addr, len, type); + if (*result != NULL) { + memset(buf, '\0', buflen); + *ret = **result; + return 0; + } else { + *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->fns->_nss_getpwnam_r) { + return NULL; + } + + status = b->fns->_nss_getpwnam_r(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->fns->_nss_getpwnam_r) { + return NSS_STATUS_NOTFOUND; + } + + ret = b->fns->_nss_getpwnam_r(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->fns->_nss_getpwuid_r) { + return NULL; + } + + status = b->fns->_nss_getpwuid_r(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->fns->_nss_getpwuid_r) { + return ENOENT; + } + + ret = b->fns->_nss_getpwuid_r(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->fns->_nss_setpwent) { + return; + } + + b->fns->_nss_setpwent(); +} + +static struct passwd *nwrap_module_getpwent(struct nwrap_backend *b) +{ + static struct passwd pwd; + static char buf[1000]; + NSS_STATUS status; + + if (!b->fns->_nss_getpwent_r) { + return NULL; + } + + status = b->fns->_nss_getpwent_r(&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->fns->_nss_getpwent_r) { + return ENOENT; + } + + ret = b->fns->_nss_getpwent_r(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->fns->_nss_endpwent) { + return; + } + + b->fns->_nss_endpwent(); +} + +static int nwrap_module_initgroups(struct nwrap_backend *b, + const char *user, gid_t group) +{ + gid_t *groups; + long int start; + long int size; + + if (!b->fns->_nss_initgroups) { + return NSS_STATUS_UNAVAIL; + } + + return b->fns->_nss_initgroups(user, group, &start, &size, &groups, 0, &errno); +} + +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->fns->_nss_getgrnam_r) { + return NULL; + } + + if (!buf) { + buf = (char *)malloc(buflen); + } +again: + status = b->fns->_nss_getgrnam_r(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->fns->_nss_getgrnam_r) { + return ENOENT; + } + + ret = b->fns->_nss_getgrnam_r(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->fns->_nss_getgrgid_r) { + return NULL; + } + + if (!buf) { + buf = (char *)malloc(buflen); + } + +again: + status = b->fns->_nss_getgrgid_r(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->fns->_nss_getgrgid_r) { + return ENOENT; + } + + ret = b->fns->_nss_getgrgid_r(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->fns->_nss_setgrent) { + return; + } + + b->fns->_nss_setgrent(); +} + +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->fns->_nss_getgrent_r) { + return NULL; + } + + if (!buf) { + buf = (char *)malloc(buflen); + } + +again: + status = b->fns->_nss_getgrent_r(&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->fns->_nss_getgrent_r) { + return ENOENT; + } + + ret = b->fns->_nss_getgrent_r(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->fns->_nss_endgrent) { + return; + } + + b->fns->_nss_endgrent(); +} + +/**************************************************************************** + * GETPWNAM + ***************************************************************************/ + +static struct passwd *nwrap_getpwnam(const char *name) +{ + int 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) +{ + int i,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) +{ + int 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) +{ + int i,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) +{ + int 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) +{ + int 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) +{ + int i,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) +{ + int 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) +{ + int i; + + for (i=0; i < nwrap_main_global->num_backends; i++) { + struct nwrap_backend *b = &nwrap_main_global->backends[i]; + int rc; + + rc = b->ops->nw_initgroups(b, user, group); + if (rc == 0) { + return 0; + } + } + + errno = ENOENT; + return -1; +} + +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) +{ + int 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) +{ + int i, 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) +{ + int 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) +{ + int i,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) +{ + int 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) +{ + int 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) +{ + int i,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) +{ + int 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, + gid_t *groups, int *ngroups) +{ + struct group *grp; + gid_t *groups_tmp; + int count = 1; + + NWRAP_LOG(NWRAP_LOG_DEBUG, "getgrouplist called for %s", user); + + groups_tmp = (gid_t *)malloc(count * sizeof(gid_t)); + if (!groups_tmp) { + NWRAP_LOG(NWRAP_LOG_ERROR, "Out of memory"); + errno = ENOMEM; + return -1; + } + groups_tmp[0] = group; + + nwrap_setgrent(); + while ((grp = nwrap_getgrent()) != NULL) { + int i = 0; + + 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); + + groups_tmp = (gid_t *)realloc(groups_tmp, (count + 1) * sizeof(gid_t)); + if (!groups_tmp) { + NWRAP_LOG(NWRAP_LOG_ERROR, + "Out of memory"); + errno = ENOMEM; + return -1; + } + groups_tmp[count] = grp->gr_gid; + + count++; + } + } + } + + nwrap_endgrent(); + + NWRAP_LOG(NWRAP_LOG_DEBUG, + "%s is member of %d groups", + user, *ngroups); + + if (*ngroups < count) { + *ngroups = count; + free(groups_tmp); + return -1; + } + + *ngroups = count; + memcpy(groups, groups_tmp, count * sizeof(gid_t)); + free(groups_tmp); + + return count; +} + +int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups) +{ + if (!nss_wrapper_enabled()) { + return libc_getgrouplist(user, group, groups, ngroups); + } + + return nwrap_getgrouplist(user, group, groups, ngroups); +} +#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_gethostbyname(const char *name) +{ + if (nwrap_files_gethostbyname(name, AF_UNSPEC, &user_he, &user_addrlist) == -1) { + return NULL; + } + return &user_he; +} + +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_gethostbyname2(const char *name, int af) +{ + if (nwrap_files_gethostbyname(name, af, &user_he2, &user_addrlist2) == -1) { + return NULL; + } + return &user_he2; +} + +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) +{ + return nwrap_files_gethostbyaddr(addr, len, type); +} + +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: + { + struct sockaddr_in *sinp = + (struct sockaddr_in *) ai->ai_addr; + + memset(sinp, 0, sizeof(struct sockaddr_in)); + + sinp->sin_port = htons(port); + sinp->sin_family = AF_INET; + + memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero)); + memcpy(&sinp->sin_addr, he->h_addr_list[0], he->h_length); + + } + break; +#ifdef HAVE_IPV6 + case AF_INET6: + { + struct sockaddr_in6 *sin6p = + (struct sockaddr_in6 *) ai->ai_addr; + + memset(sin6p, 0, sizeof(struct sockaddr_in6)); + + sin6p->sin6_port = htons(port); + sin6p->sin6_family = AF_INET6; + + memcpy(&sin6p->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; + + 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: + if (salen < sizeof(struct sockaddr_in)) + return EAI_FAMILY; + addr = &((const struct sockaddr_in *)sa)->sin_addr; + addrlen = sizeof(((const struct sockaddr_in *)sa)->sin_addr); + port = ntohs(((const struct sockaddr_in *)sa)->sin_port); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + if (salen < sizeof(struct sockaddr_in6)) + return EAI_FAMILY; + addr = &((const struct sockaddr_in6 *)sa)->sin6_addr; + addrlen = sizeof(((const struct sockaddr_in6 *)sa)->sin6_addr); + port = ntohs(((const struct sockaddr_in6 *)sa)->sin6_port); + break; +#endif + default: + return EAI_FAMILY; + } + + if (host != NULL) { + he = NULL; + if ((flags & NI_NUMERICHOST) == 0) { + he = nwrap_files_gethostbyaddr(addr, addrlen, type); + 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); +} + +/**************************** + * CONSTRUCTOR + ***************************/ +void nwrap_constructor(void) +{ + /* + * If we hold a lock and the application forks, then the child + * is not able to unlock the mutex and we are in a deadlock. + * + * 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) +{ + int i; + + NWRAP_LOCK_ALL; + if (nwrap_main_global != NULL) { + struct nwrap_main *m = nwrap_main_global; + + /* libc */ + if (m->libc != NULL) { + SAFE_FREE(m->libc->fns); + if (m->libc->handle != NULL) { + dlclose(m->libc->handle); + } + if (m->libc->nsl_handle != NULL) { + dlclose(m->libc->nsl_handle); + } + if (m->libc->sock_handle != NULL) { + 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->fns); + } + 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/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..3635686 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,67 @@ +project(tests C) + +include_directories( + ${CMAKE_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMOCKA_INCLUDE_DIR} +) + +set(TESTSUITE_LIBRARIES ${NWRAP_REQUIRED_LIBRARIES} ${CMOCKA_LIBRARY}) + +add_library(nss_nwrap SHARED nss_nwrap.c) + +set(HOMEDIR ${CMAKE_CURRENT_BINARY_DIR}) + +configure_file(passwd.in ${CMAKE_CURRENT_BINARY_DIR}/passwd @ONLY) +configure_file(group.in ${CMAKE_CURRENT_BINARY_DIR}/group @ONLY) +configure_file(hosts.in ${CMAKE_CURRENT_BINARY_DIR}/hosts @ONLY) +configure_file(shadow.in ${CMAKE_CURRENT_BINARY_DIR}/shadow @ONLY) + +set(TEST_ENVIRONMENT LD_PRELOAD=${NSS_WRAPPER_LOCATION}) +list(APPEND TEST_ENVIRONMENT NSS_WRAPPER_PASSWD=${CMAKE_CURRENT_BINARY_DIR}/passwd) +list(APPEND TEST_ENVIRONMENT NSS_WRAPPER_GROUP=${CMAKE_CURRENT_BINARY_DIR}/group) +list(APPEND TEST_ENVIRONMENT NSS_WRAPPER_SHADOW=${CMAKE_CURRENT_BINARY_DIR}/shadow) +list(APPEND TEST_ENVIRONMENT NSS_WRAPPER_HOSTS=${CMAKE_CURRENT_BINARY_DIR}/hosts) +list(APPEND TEST_ENVIRONMENT NSS_WRAPPER_MODULE_SO_PATH=${CMAKE_CURRENT_BINARY_DIR}/libnss_nwrap.so) +list(APPEND TEST_ENVIRONMENT NSS_WRAPPER_MODULE_FN_PREFIX=nwrap) + +set(NWRAP_TESTS + testsuite + test_nwrap_vector + test_getaddrinfo + test_getnameinfo + test_gethostby_name_addr + test_gethostent) + +if (HAVE_SHADOW_H) + list(APPEND NWRAP_TESTS test_shadow) +endif (HAVE_SHADOW_H) + +foreach(_NWRAP_TEST ${NWRAP_TESTS}) + add_cmocka_test(${_NWRAP_TEST} ${_NWRAP_TEST}.c ${TESTSUITE_LIBRARIES}) + set_property( + TEST + ${_NWRAP_TEST} + PROPERTY + ENVIRONMENT ${TEST_ENVIRONMENT}) +endforeach() + +if (HAVE_SHADOW_H) + # This is needed to check the hash in tests/shadow.in + target_link_libraries(test_shadow crypt) +endif (HAVE_SHADOW_H) + +target_link_libraries(test_nwrap_vector ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(test_gethostby_name_addr ${CMAKE_THREAD_LIBS_INIT}) + +if (BSD) + add_definitions(-DBSD) +endif (BSD) + +# Test nwrap without wrapping so the libc functions are called +add_cmocka_test(test_nwrap_disabled test_nwrap_disabled.c ${TESTSUITE_LIBRARIES}) +set_property( + TEST + test_nwrap_disabled + PROPERTY + ENVIRONMENT LD_PRELOAD=${CMAKE_BINARY_DIR}/src/libnss_wrapper.so) diff --git a/tests/group.in b/tests/group.in new file mode 100644 index 0000000..1ccd9fa --- /dev/null +++ b/tests/group.in @@ -0,0 +1,4 @@ +users:x:1000: +nobody:x:65533: +nogroup:x:65534:nobody +root:x:65532: diff --git a/tests/hosts.in b/tests/hosts.in new file mode 100644 index 0000000..9f767c3 --- /dev/null +++ b/tests/hosts.in @@ -0,0 +1,100 @@ +127.0.0.11 magrathea.galaxy.site magrathea +127.0.0.12 magrathea.galaxy.site magrathea +::29a magrathea.galaxy.site magrathea +127.0.0.12 maximegalon.galaxy.site +::13 beteigeuze.galaxy.site beteigeuze mail +127.0.0.14 krikkit.galaxy.site +::14 krikkit.galaxy.site +127.1.1.1 pumpkin.bunny.net pumpkin.bunny.net +127.0.0.66 pumpkin.bunny.net +2666::22 pumpkin.bunny.net +DEAD:BEEF:1:2:3::4 pumpkin.bunny.net +B00B:5::4 pumpkin.bunny.net +127.0.0.21 localdc.samba.example.com samba.example.com localdc +fd00:0000:0000:0000:0000:0000:5357:5f15 localdc.samba.example.com samba.example.com localdc +127.0.0.3 localnt4dc2.samba.example.com localnt4dc2 +fd00:0000:0000:0000:0000:0000:5357:5f03 localnt4dc2.samba.example.com localnt4dc2 +127.0.0.5 localshare4.samba.example.com localshare4 +fd00:0000:0000:0000:0000:0000:5357:5f05 localshare4.samba.example.com localshare4 +127.0.0.4 localnt4member3.samba.example.com localnt4member3 +fd00:0000:0000:0000:0000:0000:5357:5f04 localnt4member3.samba.example.com localnt4member3 +127.0.0.29 localadmember.samba.example.com localadmember +fd00:0000:0000:0000:0000:0000:5357:5f1d localadmember.samba.example.com localadmember +127.0.0.23 s4member.samba.example.com s4member +fd00:0000:0000:0000:0000:0000:5357:5f17 s4member.samba.example.com s4member +127.0.0.7 localktest6.samba.example.com localktest6 +fd00:0000:0000:0000:0000:0000:5357:5f07 localktest6.samba.example.com localktest6 +127.0.0.8 maptoguest.samba.example.com maptoguest +fd00:0000:0000:0000:0000:0000:5357:5f08 maptoguest.samba.example.com maptoguest +127.0.0.35 fileserver.samba.example.com fileserver +fd00:0000:0000:0000:0000:0000:5357:5f23 fileserver.samba.example.com fileserver +127.0.0.30 addc.addc.samba.example.com addc +fd00:0000:0000:0000:0000:0000:5357:5f1e addc.addc.samba.example.com addc +127.0.0.9 localnt4dc9.samba.example.com localnt4dc9 +fd00:0000:0000:0000:0000:0000:5357:5f09 localnt4dc9.samba.example.com localnt4dc9 +127.0.0.34 rfc2307member.samba.example.com rfc2307member +fd00:0000:0000:0000:0000:0000:5357:5f22 rfc2307member.samba.example.com rfc2307member +127.0.0.25 dc5.samba2000.example.com dc5 +fd00:0000:0000:0000:0000:0000:5357:5f19 dc5.samba2000.example.com dc5 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 +127.0.0.26 dc6.samba2003.example.com dc6 +fd00:0000:0000:0000:0000:0000:5357:5f1a dc6.samba2003.example.com dc6 diff --git a/tests/nss_nwrap.c b/tests/nss_nwrap.c new file mode 100644 index 0000000..1e63dce --- /dev/null +++ b/tests/nss_nwrap.c @@ -0,0 +1,151 @@ +#include "config.h" + +#include +#include + +#if defined(HAVE_NSS_H) +/* Linux and BSD */ +#include + +typedef enum nss_status NSS_STATUS; +#elif defined(HAVE_NSS_COMMON_H) +/* Solaris */ +#include +#include +#include + +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 + +NSS_STATUS _nss_nwrap_setpwent(void); +NSS_STATUS _nss_nwrap_endpwent(void); +NSS_STATUS _nss_nwrap_getpwent_r(struct passwd *result, char *buffer, + size_t buflen, int *errnop); +NSS_STATUS _nss_nwrap_getpwuid_r(uid_t uid, struct passwd *result, + char *buffer, size_t buflen, int *errnop); +NSS_STATUS _nss_nwrap_getpwnam_r(const char *name, struct passwd *result, + char *buffer, size_t buflen, int *errnop); +NSS_STATUS _nss_nwrap_setgrent(void); +NSS_STATUS _nss_nwrap_endgrent(void); +NSS_STATUS _nss_nwrap_getgrent_r(struct group *result, char *buffer, + size_t buflen, int *errnop); +NSS_STATUS _nss_nwrap_getgrnam_r(const char *name, struct group *result, + char *buffer, size_t buflen, int *errnop); +NSS_STATUS _nss_nwrap_getgrgid_r(gid_t gid, struct group *result, char *buffer, + size_t buflen, int *errnop); +NSS_STATUS _nss_nwrap_initgroups_dyn(char *user, gid_t group, long int *start, + long int *size, gid_t **groups, + long int limit, int *errnop); + +NSS_STATUS _nss_nwrap_setpwent(void) +{ + return NSS_STATUS_UNAVAIL; +} + +NSS_STATUS _nss_nwrap_endpwent(void) +{ + return NSS_STATUS_UNAVAIL; +} + +NSS_STATUS _nss_nwrap_getpwent_r(struct passwd *result, char *buffer, + size_t buflen, int *errnop) +{ + (void) result; + (void) buffer; + (void) buflen; + (void) errnop; + + return NSS_STATUS_UNAVAIL; +} + +NSS_STATUS _nss_nwrap_getpwuid_r(uid_t uid, struct passwd *result, + char *buffer, size_t buflen, int *errnop) +{ + (void) uid; + (void) result; + (void) buffer; + (void) buflen; + (void) errnop; + + return NSS_STATUS_UNAVAIL; +} + +NSS_STATUS _nss_nwrap_getpwnam_r(const char *name, struct passwd *result, + char *buffer, size_t buflen, int *errnop) +{ + (void) name; + (void) result; + (void) buffer; + (void) buflen; + (void) errnop; + + return NSS_STATUS_UNAVAIL; +} + +NSS_STATUS _nss_nwrap_setgrent(void) +{ + return NSS_STATUS_UNAVAIL; +} + +NSS_STATUS _nss_nwrap_endgrent(void) +{ + return NSS_STATUS_UNAVAIL; +} + +NSS_STATUS _nss_nwrap_getgrent_r(struct group *result, char *buffer, + size_t buflen, int *errnop) +{ + (void) result; + (void) buffer; + (void) buflen; + (void) errnop; + + return NSS_STATUS_UNAVAIL; +} + +NSS_STATUS _nss_nwrap_getgrnam_r(const char *name, struct group *result, + char *buffer, size_t buflen, int *errnop) +{ + (void) name; + (void) result; + (void) buffer; + (void) buflen; + (void) errnop; + + return NSS_STATUS_UNAVAIL; +} + +NSS_STATUS _nss_nwrap_getgrgid_r(gid_t gid, struct group *result, char *buffer, + size_t buflen, int *errnop) +{ + (void) gid; + (void) result; + (void) buffer; + (void) buflen; + (void) errnop; + + return NSS_STATUS_UNAVAIL; +} + +NSS_STATUS _nss_nwrap_initgroups_dyn(char *user, gid_t group, long int *start, + long int *size, gid_t **groups, + long int limit, int *errnop) +{ + (void) user; + (void) group; + (void) start; + (void) size; + (void) groups; + (void) limit; + (void) errnop; + + return NSS_STATUS_UNAVAIL; +} + diff --git a/tests/passwd.in b/tests/passwd.in new file mode 100644 index 0000000..f06fc2a --- /dev/null +++ b/tests/passwd.in @@ -0,0 +1,4 @@ +bob:x:1000:1000:bob gecos:@HOMEDIR@:/bin/false +alice:1001:1000:alice gecos:@HOMEDIR@:/bin/false +nobody:x:65533:65534:bob gecos:@HOMEDIR@:/bin/false +root:x:65534:65532:root gecos:@HOMEDIR@:/bin/false diff --git a/tests/shadow.in b/tests/shadow.in new file mode 100644 index 0000000..baf8ada --- /dev/null +++ b/tests/shadow.in @@ -0,0 +1,2 @@ +alice:$6$0jWkA8VP$MvBUvtGy38jWCZ5KtqnZEKQWXvvImDkDhDQII1kTqtAp3/xH31b71c.AjGkBFle.2QwCJQH7OzB/NXiMprusr/:1:0:99999:7::: +bob:$6$0jWkA8VP$MvBUvtGy38jWCZ5KtqnZEKQWXvvImDkDhDQII1kTqtAp3/xH31b71c.AjGkBFle.2QwCJQH7OzB/NXiMprusr/:2:0:99999:7::: diff --git a/tests/test_getaddrinfo.c b/tests/test_getaddrinfo.c new file mode 100644 index 0000000..47eb8f2 --- /dev/null +++ b/tests/test_getaddrinfo.c @@ -0,0 +1,751 @@ +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static void test_nwrap_getaddrinfo(void **state) +{ + struct addrinfo hints; + struct addrinfo *res = NULL; + struct sockaddr_in *sinp; + struct sockaddr_in6 *sin6p; + char ip6[INET6_ADDRSTRLEN]; + char *ip; + int rc; + + (void) state; /* unused */ + + /* IPv4 */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + rc = getaddrinfo("127.0.0.11", NULL, &hints, &res); + assert_int_equal(rc, 0); + assert_non_null(res); + + assert_non_null(res->ai_canonname); + assert_string_equal(res->ai_canonname, "magrathea.galaxy.site"); + + assert_int_equal(res->ai_family, AF_INET); + + sinp = (struct sockaddr_in *)res->ai_addr; + + assert_int_equal(sinp->sin_family, AF_INET); + ip = inet_ntoa(sinp->sin_addr); + + assert_string_equal(ip, "127.0.0.11"); + + freeaddrinfo(res); + res = NULL; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + rc = getaddrinfo("::13", NULL, &hints, &res); + assert_non_null(res); + assert_int_equal(rc, 0); + + assert_non_null(res->ai_canonname); + assert_string_equal(res->ai_canonname, "beteigeuze.galaxy.site"); + + assert_int_equal(res->ai_family, AF_INET6); + + sin6p = (struct sockaddr_in6 *)res->ai_addr; + + assert_int_equal(sin6p->sin6_family, AF_INET6); + inet_ntop(AF_INET6, (void *)&sin6p->sin6_addr, ip6, sizeof(ip6)); + + assert_string_equal(ip6, "::13"); + + freeaddrinfo(res); +} + +/* + * The purpose of this test is to verify that reloading of the hosts + * file (triggered by a timestamp change) correctly frees and re-creates + * the internal data structures, so we do not end up using invalid memory. + */ +static void test_nwrap_getaddrinfo_reload(void **state) +{ + struct addrinfo hints; + struct addrinfo *res = NULL; + const char *env; + char touch_cmd[1024]; + int rc; + + (void) state; /* unused */ + + /* IPv4 */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + rc = getaddrinfo("127.0.0.11", NULL, &hints, &res); + assert_int_equal(rc, 0); + assert_non_null(res); + + freeaddrinfo(res); + res = NULL; + + env = getenv("NSS_WRAPPER_HOSTS"); + assert_non_null(env); + + snprintf(touch_cmd, sizeof(touch_cmd), "touch %s", env); + + rc = system(touch_cmd); + assert_return_code(rc, errno); + + rc = getaddrinfo("127.0.0.11", NULL, &hints, &res); + assert_int_equal(rc, 0); + assert_non_null(res); + + + freeaddrinfo(res); +} + +static void test_nwrap_getaddrinfo_samba(void **state) +{ + struct addrinfo hints; + struct addrinfo *res = NULL; + int rc; + + (void) state; /* unused */ + + /* IPv4 */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* Stream socket */ + hints.ai_flags = 0; /* For wildcard IP address */ + hints.ai_protocol = IPPROTO_TCP; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + rc = getaddrinfo("127.0.0.21", NULL, &hints, &res); + assert_int_equal(rc, 0); + assert_non_null(res); + freeaddrinfo(res); + res = NULL; + + rc = getaddrinfo("samba.example.com", NULL, &hints, &res); + assert_int_equal(rc, 0); + assert_non_null(res); + freeaddrinfo(res); + res = NULL; + + rc = getaddrinfo("localdc", NULL, &hints, &res); + assert_int_equal(rc, 0); + assert_non_null(res); + freeaddrinfo(res); + res = NULL; + + rc = getaddrinfo("localdc.samba.example.com", NULL, &hints, &res); + assert_int_equal(rc, 0); + assert_non_null(res); + freeaddrinfo(res); + res = NULL; + + rc = getaddrinfo("fd00:0000:0000:0000:0000:0000:5357:5f15", NULL, &hints, &res); + assert_int_equal(rc, 0); + assert_non_null(res); + + freeaddrinfo(res); +} + +static void test_nwrap_getaddrinfo_any(void **state) +{ + struct addrinfo hints; + struct addrinfo *res = NULL; + struct sockaddr_in *sinp; + struct sockaddr_in6 *sin6p; + char ip6[INET6_ADDRSTRLEN]; + char *ip; + int rc; + + (void) state; /* unused */ + + /* IPv4 */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; + + rc = getaddrinfo("0.0.0.0", "389", &hints, &res); + assert_int_equal(rc, 0); + assert_non_null(res); + + assert_int_equal(res->ai_family, AF_INET); + assert_int_equal(res->ai_socktype, SOCK_STREAM); + +#ifdef HAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES + assert_string_equal(res->ai_canonname, "0.0.0.0"); +#else /* HAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES */ + assert_null(res->ai_canonname); +#endif /* HAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES */ + + sinp = (struct sockaddr_in *)res->ai_addr; + + assert_int_equal(389, htons(sinp->sin_port)); + + ip = inet_ntoa(sinp->sin_addr); + + assert_string_equal(ip, "0.0.0.0"); + + freeaddrinfo(res); + res = NULL; + + /* IPv4 */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; + + rc = getaddrinfo("::", "389", &hints, &res); + assert_int_equal(rc, 0); + assert_non_null(res); + + assert_int_equal(res->ai_family, AF_INET6); + assert_int_equal(res->ai_socktype, SOCK_STREAM); + +#ifdef HAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES + assert_string_equal(res->ai_canonname, "::"); +#else /* HAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES */ + assert_null(res->ai_canonname); +#endif /* HAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES */ + + sin6p = (struct sockaddr_in6 *)res->ai_addr; + + assert_int_equal(389, htons(sin6p->sin6_port)); + + inet_ntop(AF_INET6, (void *)&sin6p->sin6_addr, ip6, sizeof(ip6)); + + assert_string_equal(ip6, "::"); + + freeaddrinfo(res); +} + +static void test_nwrap_getaddrinfo_local(void **state) +{ + struct addrinfo hints; + struct addrinfo *res; + struct sockaddr_in *sinp; + char *ip; + int rc; + + (void) state; /* unused */ + + /* IPv4 */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + + rc = getaddrinfo("127.0.0.1", NULL, &hints, &res); + assert_int_equal(rc, 0); + + assert_int_equal(res->ai_family, AF_INET); + assert_int_equal(res->ai_socktype, SOCK_STREAM); + +#ifdef HAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES + assert_string_equal(res->ai_canonname, "127.0.0.1"); +#else /* HAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES */ + assert_null(res->ai_canonname); +#endif /* HAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES */ + + sinp = (struct sockaddr_in *)res->ai_addr; + ip = inet_ntoa(sinp->sin_addr); + + assert_string_equal(ip, "127.0.0.1"); + + freeaddrinfo(res); +} + +static void test_nwrap_getaddrinfo_name(void **state) +{ + struct addrinfo hints; + struct addrinfo *res = NULL; + struct sockaddr_in *sinp; + char *ip; + int rc; + + (void) state; /* unused */ + + /* IPv4 */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + + rc = getaddrinfo("maximegalon.galaxy.site", NULL, &hints, &res); + assert_int_equal(rc, 0); + assert_non_null(res); + + assert_non_null(res); + assert_int_equal(res->ai_family, AF_INET); + assert_int_equal(res->ai_socktype, SOCK_STREAM); + + assert_non_null(res->ai_canonname); + assert_string_equal(res->ai_canonname, "maximegalon.galaxy.site"); + + sinp = (struct sockaddr_in *)res->ai_addr; + ip = inet_ntoa(sinp->sin_addr); + + assert_string_equal(ip, "127.0.0.12"); + + freeaddrinfo(res); + res = NULL; + + /* IPv4 */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + + rc = getaddrinfo("MAGRATHEA", NULL, &hints, &res); + assert_int_equal(rc, 0); + + assert_non_null(res); + assert_int_equal(res->ai_family, AF_INET); + assert_int_equal(res->ai_socktype, SOCK_STREAM); + + assert_non_null(res->ai_canonname); + assert_string_equal(res->ai_canonname, "magrathea.galaxy.site"); + + sinp = (struct sockaddr_in *)res->ai_addr; + ip = inet_ntoa(sinp->sin_addr); + + assert_string_equal(ip, "127.0.0.11"); + + freeaddrinfo(res); +} + +static void test_nwrap_getaddrinfo_service(void **state) +{ + struct addrinfo hints; + struct addrinfo *res = NULL; + struct sockaddr_in *sinp; + char *ip; + int rc; + + (void) state; /* unused */ + + /* IPv4 */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + + rc = getaddrinfo("magrathea", "wurst", &hints, &res); + assert_int_equal(rc, EAI_NONAME); + + /* Check ldap port */ + rc = getaddrinfo("magrathea", "ldap", &hints, &res); + assert_int_equal(rc, 0); + assert_non_null(res); + + assert_int_equal(res->ai_family, AF_INET); + assert_int_equal(res->ai_socktype, SOCK_STREAM); + + assert_non_null(res->ai_canonname); + assert_string_equal(res->ai_canonname, "magrathea.galaxy.site"); + + assert_non_null(res->ai_addr); + sinp = (struct sockaddr_in *)res->ai_addr; + ip = inet_ntoa(sinp->sin_addr); + + assert_string_equal(ip, "127.0.0.11"); + + assert_int_equal(ntohs(sinp->sin_port), 389); + + freeaddrinfo(res); +} + +static void test_nwrap_getaddrinfo_null(void **state) +{ + struct addrinfo hints; + struct addrinfo *res = NULL; + struct sockaddr_in6 *sin6p; + char ip6[INET6_ADDRSTRLEN]; + int rc; + + (void) state; /* unused */ + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = 17; + hints.ai_flags = 0; + + rc = getaddrinfo(NULL, NULL, &hints, &res); + assert_int_equal(rc, EAI_NONAME); + + /* Check dns service */ + rc = getaddrinfo(NULL, "domain", &hints, &res); + assert_int_equal(rc, 0); + + assert_non_null(res); + assert_null(res->ai_canonname); + + assert_int_equal(res->ai_family, AF_INET6); + assert_int_equal(res->ai_socktype, SOCK_DGRAM); + + assert_non_null(res->ai_addr); + sin6p = (struct sockaddr_in6 *)res->ai_addr; + inet_ntop(AF_INET6, (void *)&sin6p->sin6_addr, ip6, sizeof(ip6)); + + assert_string_equal(ip6, "::1"); + + freeaddrinfo(res); + res = NULL; + + /* Check dns service */ + rc = getaddrinfo("magrathea", "domain", NULL, &res); + assert_non_null(res); + assert_int_equal(rc, 0); + + assert_non_null(res->ai_canonname); + + freeaddrinfo(res); +} + +static void test_nwrap_getaddrinfo_dot(void **state) +{ + struct addrinfo hints = { + .ai_family = AF_INET, + }; + struct addrinfo *res = NULL; + struct sockaddr_in *sinp; + char ip[INET_ADDRSTRLEN]; + int rc; + + (void) state; /* unused */ + + /* Check with a dot at the end */ + rc = getaddrinfo("magrathea.galaxy.site.", NULL, &hints, &res); + assert_int_equal(rc, 0); + assert_non_null(res); + + assert_non_null(res->ai_next); + assert_int_equal(res->ai_family, AF_INET); + + sinp = (struct sockaddr_in *)res->ai_addr; + assert_int_equal(sinp->sin_family, AF_INET); + inet_ntop(AF_INET, (void *)&sinp->sin_addr, ip, sizeof(ip)); + + assert_string_equal(ip, "127.0.0.11"); + + freeaddrinfo(res); +} + +static void test_nwrap_getaddrinfo_ipv6(void **state) +{ + struct addrinfo hints; + struct addrinfo *res = NULL; + struct sockaddr_in6 *sin6p; + char ip6[INET6_ADDRSTRLEN]; + int rc; + + (void) state; /* unused */ + + /* + * krikkit.galaxy has an IPv4 and IPv6 address, this should only + * return the IPv6 address. + */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET6; + + rc = getaddrinfo("krikkit.galaxy.site", NULL, &hints, &res); + assert_int_equal(rc, 0); + assert_non_null(res); + + assert_non_null(res->ai_next); + assert_int_equal(res->ai_family, AF_INET6); + + sin6p = (struct sockaddr_in6 *)res->ai_addr; + assert_int_equal(sin6p->sin6_family, AF_INET6); + inet_ntop(AF_INET6, (void *)&sin6p->sin6_addr, ip6, sizeof(ip6)); + + assert_string_equal(ip6, "::14"); + + freeaddrinfo(res); +} + +static void test_nwrap_getaddrinfo_multiple_mixed(void **state) +{ + struct addrinfo *res, *res_head; + struct addrinfo hints; + unsigned int ipv6_count = 0; + unsigned int ipv4_count = 0; + int rc; + int p; + + struct sockaddr_in *r_addr; + struct sockaddr_in6 *r_addr6; + + const char *result = NULL; + const char *value = NULL; + + /* For inet_ntop call */ + char buf[4096]; + + /* 2 - ipv4 and 3 ipv6 addresses */ + const char *ipvX_results[] = {"127.1.1.1", "127.0.0.66", "2666::22", "B00B:5::4", "DEAD:BEEF:1:2:3::4", NULL}; + + (void) state; /* unused */ + + memset(&hints, '\0', sizeof(struct addrinfo)); + hints.ai_protocol = IPPROTO_TCP; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + rc = getaddrinfo("pumpkin.bunny.net", NULL, &hints, &res_head); + assert_return_code(rc, 0); + assert_non_null(res_head); + + for (res = res_head; res != NULL; res = res->ai_next) { + if (res->ai_family == AF_INET) { + r_addr = (struct sockaddr_in *) res->ai_addr; + assert_non_null(r_addr); + ++ipv4_count; + result = inet_ntop(AF_INET, + &r_addr->sin_addr, + buf, + 4096); + } else if (res->ai_family == AF_INET6) { + r_addr6 = (struct sockaddr_in6 *) res->ai_addr; + assert_non_null(r_addr6); + ++ipv6_count; + result = inet_ntop(AF_INET6, + &r_addr6->sin6_addr, + buf, + 4096); + } else { + /* Unknown family type */ + assert_int_equal(1,0); + } + + /* Important part */ + assert_non_null(result); + + /* This could be part of cmocka library */ + for (value = ipvX_results[0], p = 0; value != NULL; value = ipvX_results[++p]) { + if (strcasecmp(value, result) == 0) { + break; + } + } + assert_non_null(value); + } + + assert_int_equal(ipv6_count, 3); + assert_int_equal(ipv4_count, 2); + + freeaddrinfo(res_head); +} + +static void test_nwrap_getaddrinfo_flags_ai_numericserv(void **state) +{ + struct addrinfo hints; + struct addrinfo *res; + int rc; + + (void) state; /* unused */ + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + + /* + * Calls with NULL name are handled by libc, + * even if nss_wrapper is enabled + */ + + rc = getaddrinfo(NULL, "echo", &hints, &res); +#ifdef HAVE_GETADDRINFO_USES_EAI_SERVICE + assert_int_equal(rc, EAI_SERVICE); +#else /* HAVE_GETADDRINFO_USES_EAI_SERVICE */ + assert_int_equal(rc, EAI_NONAME); +#endif /* HAVE_GETADDRINFO_USES_EAI_SERVICE */ + + rc = getaddrinfo(NULL, "80", &hints, &res); + assert_int_equal(rc, 0); + freeaddrinfo(res); + res = NULL; + + /* Crippled input */ + rc = getaddrinfo(NULL, "80a1", &hints, &res); +#ifdef HAVE_GETADDRINFO_USES_EAI_SERVICE + assert_int_equal(rc, EAI_SERVICE); +#else /* HAVE_GETADDRINFO_USES_EAI_SERVICE */ + assert_int_equal(rc, EAI_NONAME); +#endif /* HAVE_GETADDRINFO_USES_EAI_SERVICE */ + + /* + * Calls with non-NULL name are handled by nwrap + */ + + rc = getaddrinfo("magrathea.galaxy.site", "echo", &hints, &res); + assert_int_equal(rc, EAI_NONAME); + + rc = getaddrinfo("magrathea.galaxy.site", "80", &hints, &res); + assert_int_equal(rc, 0); + freeaddrinfo(res); + res = NULL; + + /* Crippled input */ + rc = getaddrinfo("magrathea.galaxy.site", "80a1", &hints, &res); + assert_int_equal(rc, EAI_NONAME); +} + +static void test_nwrap_getaddrinfo_flags_ai_numerichost(void **state) +{ + struct addrinfo hints; + struct addrinfo *res; + int rc; + + (void) state; /* unused */ + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + + /* IPv4 or IPv6 */ + + hints.ai_family = AF_UNSPEC; + + rc = getaddrinfo("127.0.0.11", NULL, &hints, &res); + assert_int_equal(rc, 0); + freeaddrinfo(res); + + rc = getaddrinfo("::1", NULL, &hints, &res); + assert_int_equal(rc, 0); + freeaddrinfo(res); + + rc = getaddrinfo(NULL, "echo", &hints, &res); + assert_int_equal(rc, 0); + freeaddrinfo(res); + + rc = getaddrinfo("magrathea.galaxy.site", NULL, &hints, &res); + assert_int_equal(rc, EAI_NONAME); + + rc = getaddrinfo("", NULL, &hints, &res); + assert_int_equal(rc, EAI_NONAME); + + rc = getaddrinfo("fail.me", "echo", &hints, &res); + assert_int_equal(rc, EAI_NONAME); + + /* IPv4 */ + + hints.ai_family = AF_INET; + + rc = getaddrinfo("127.0.0.11", NULL, &hints, &res); + assert_int_equal(rc, 0); + freeaddrinfo(res); + + rc = getaddrinfo("::1", NULL, &hints, &res); +#ifdef EAI_ADDRFAMILY + assert_int_equal(rc, EAI_ADDRFAMILY); +#else + assert_int_equal(rc, EAI_FAMILY); +#endif + + rc = getaddrinfo(NULL, "echo", &hints, &res); + assert_int_equal(rc, 0); + freeaddrinfo(res); + + rc = getaddrinfo("magrathea.galaxy.site", NULL, &hints, &res); + assert_int_equal(rc, EAI_NONAME); + + rc = getaddrinfo("", NULL, &hints, &res); + assert_int_equal(rc, EAI_NONAME); + + rc = getaddrinfo("fail.me", "echo", &hints, &res); + assert_int_equal(rc, EAI_NONAME); + + + /* IPv6 */ + + hints.ai_family = AF_INET6; + + rc = getaddrinfo("127.0.0.11", NULL, &hints, &res); +#ifdef EAI_ADDRFAMILY + assert_int_equal(rc, EAI_ADDRFAMILY); +#else + assert_int_equal(rc, EAI_FAMILY); +#endif + + rc = getaddrinfo("::1", NULL, &hints, &res); + assert_int_equal(rc, 0); + freeaddrinfo(res); + + rc = getaddrinfo(NULL, "echo", &hints, &res); + assert_int_equal(rc, 0); + freeaddrinfo(res); + + rc = getaddrinfo("magrathea.galaxy.site", NULL, &hints, &res); + assert_int_equal(rc, EAI_NONAME); + + rc = getaddrinfo("", NULL, &hints, &res); + assert_int_equal(rc, EAI_NONAME); + + rc = getaddrinfo("fail.me", "echo", &hints, &res); + assert_int_equal(rc, EAI_NONAME); +} + +int main(void) { + int rc; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_nwrap_getaddrinfo), + cmocka_unit_test(test_nwrap_getaddrinfo_reload), + cmocka_unit_test(test_nwrap_getaddrinfo_any), + cmocka_unit_test(test_nwrap_getaddrinfo_local), + cmocka_unit_test(test_nwrap_getaddrinfo_name), + cmocka_unit_test(test_nwrap_getaddrinfo_service), + cmocka_unit_test(test_nwrap_getaddrinfo_null), + cmocka_unit_test(test_nwrap_getaddrinfo_dot), + cmocka_unit_test(test_nwrap_getaddrinfo_ipv6), + cmocka_unit_test(test_nwrap_getaddrinfo_multiple_mixed), + cmocka_unit_test(test_nwrap_getaddrinfo_flags_ai_numericserv), + cmocka_unit_test(test_nwrap_getaddrinfo_flags_ai_numerichost), + cmocka_unit_test(test_nwrap_getaddrinfo_samba), + }; + + rc = cmocka_run_group_tests(tests, NULL, NULL); + + return rc; +} diff --git a/tests/test_gethostby_name_addr.c b/tests/test_gethostby_name_addr.c new file mode 100644 index 0000000..cb55c06 --- /dev/null +++ b/tests/test_gethostby_name_addr.c @@ -0,0 +1,307 @@ +#include "config.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define NSS_WRAPPER_HOSTNAME_ENV "NSS_WRAPPER_HOSTNAME" + +static void test_nwrap_gethostname(void **state) +{ + const char *hostname = "milliways"; + char sys_host[256] = {0}; + char host[16] = {0}; + int rc; + + (void) state; /* unused */ + + rc = setenv(NSS_WRAPPER_HOSTNAME_ENV, hostname, 1); + assert_int_equal(rc, 0); + + rc = gethostname(host, sizeof(host)); + assert_int_equal(rc, 0); + + assert_string_equal(host, hostname); + + rc = setenv(NSS_WRAPPER_HOSTNAME_ENV, "this_hostname_is_too_long", 1); + assert_int_equal(rc, 0); + + rc = gethostname(host, sizeof(host)); + assert_int_equal(rc, -1); + assert_int_equal(errno, ENAMETOOLONG); + + unsetenv(NSS_WRAPPER_HOSTNAME_ENV); + + rc = gethostname(sys_host, sizeof(sys_host)); + assert_int_equal(rc, 0); + +} + +static void *thread_test_gethostbyname(void *u) +{ + struct hostent *he; + + (void) u; /* unused */ + + he = gethostbyname("magrathea"); + assert_non_null(he); + assert_non_null(he->h_name); + assert_string_equal(he->h_name, "magrathea.galaxy.site"); + pthread_exit(NULL); +} + +static void test_nwrap_gethostbyname_thread(void **state) +{ + struct hostent *he; + pthread_t th; + + (void) state; /* unused */ + + he = gethostbyname("maximegalon.galaxy.site"); + assert_non_null(he); + assert_non_null(he->h_name); + assert_string_equal(he->h_name, "maximegalon.galaxy.site"); + + pthread_create(&th, NULL, &thread_test_gethostbyname, NULL); + pthread_join(th, NULL); + + assert_non_null(he); + assert_non_null(he->h_name); +#ifdef BSD + /* + * On *BSD (and Mac OS X) systems, + * data is stored in thread local storage. + */ + assert_string_equal(he->h_name, "maximegalon.galaxy.site"); +#else + /* + * Glibc doesn't store data in thread local storage, so calling + * gethostbyname from a thread overwrites the parent thread's data. + */ + assert_string_equal(he->h_name, "magrathea.galaxy.site"); +#endif +} + +static void test_nwrap_gethostbyname(void **state) +{ + char ip[INET_ADDRSTRLEN]; + struct hostent *he; + const char *a; + + (void) state; /* unused */ + + he = gethostbyname("magrathea.galaxy.site"); + assert_non_null(he); + assert_non_null(he->h_name); + assert_non_null(he->h_addr_list); + + assert_string_equal(he->h_name, "magrathea.galaxy.site"); + assert_int_equal(he->h_addrtype, AF_INET); + + a = inet_ntop(AF_INET, he->h_addr_list[0], ip, sizeof(ip)); + assert_non_null(a); + + assert_string_equal(ip, "127.0.0.11"); +} + +static void test_nwrap_gethostbyname_multiple(void **state) +{ + struct hostent *he; + char **list; + + /* For inet_ntop call */ + char buf[4096]; + const char *result; + char *p = buf; + + /* List of ips in hosts file - order matters */ + const char *const result_ips[] = { "127.0.0.11", "127.0.0.12", NULL }; + const char *actual_ip = result_ips[0]; + unsigned int ac; + + (void) state; /* unused */ + + he = gethostbyname("magrathea.galaxy.site"); + assert_non_null(he); + assert_non_null(he->h_name); + assert_non_null(he->h_addr_list); + + list = he->h_addr_list; + for (ac = 0; *list != NULL; ++ac, ++list) { + actual_ip = result_ips[ac]; + /* When test fails here more records are returned */ + assert_non_null(actual_ip); + result = inet_ntop(AF_INET, *list, p, 4096); + assert_non_null(p); + assert_string_equal(actual_ip, result); + } +} + +#ifdef HAVE_GETHOSTBYNAME2 +static void test_nwrap_gethostbyname2(void **state) +{ + char ip[INET6_ADDRSTRLEN]; + struct hostent *he; + const char *a; + + (void) state; /* unused */ + + he = gethostbyname2("magrathea.galaxy.site", AF_INET6); + assert_non_null(he); + + he = gethostbyname2("magrathea.galaxy.site", AF_INET); + assert_non_null(he); + + /* Check ipv6 he */ + he = gethostbyname2("krikkit.galaxy.site", AF_INET6); + assert_non_null(he); + assert_non_null(he->h_name); + assert_non_null(he->h_addr_list); + + assert_string_equal(he->h_name, "krikkit.galaxy.site"); + assert_int_equal(he->h_addrtype, AF_INET6); + + a = inet_ntop(AF_INET6, he->h_addr_list[0], ip, sizeof(ip)); + assert_non_null(a); + + assert_string_equal(ip, "::14"); + + /* Check ipv4 he */ + he = gethostbyname2("krikkit.galaxy.site", AF_INET); + assert_non_null(he); + assert_non_null(he->h_name); + assert_non_null(he->h_addr_list); + + assert_string_equal(he->h_name, "krikkit.galaxy.site"); + assert_int_equal(he->h_addrtype, AF_INET); + + a = inet_ntop(AF_INET, he->h_addr_list[0], ip, sizeof(ip)); + assert_non_null(a); + + assert_string_equal(ip, "127.0.0.14"); +} +#endif /* HAVE_GETHOSTBYNAME2 */ + +static void test_nwrap_gethostbyaddr(void **state) +{ + struct hostent *he; + struct in_addr in; + int rc; + + (void) state; /* unused */ + + rc = inet_aton("127.0.0.11", &in); + assert_int_equal(rc, 1); + + he = gethostbyaddr(&in, sizeof(struct in_addr), AF_INET); + assert_non_null(he); + assert_non_null(he->h_name); + assert_non_null(he->h_addr_list); + + assert_string_equal(he->h_name, "magrathea.galaxy.site"); + assert_int_equal(he->h_addrtype, AF_INET); + assert_memory_equal(&in, he->h_addr_list[0], he->h_length); +} + +#ifdef HAVE_GETHOSTBYNAME_R +static void test_nwrap_gethostbyname_r(void **state) +{ + char buf[1024] = {0}; + char ip[INET_ADDRSTRLEN]; + struct hostent hb, *he; + const char *a; + int herr = 0; + int rc; + + (void) state; /* unused */ + + rc = gethostbyname_r("magrathea.galaxy.site", + &hb, + buf, sizeof(buf), + &he, + &herr); + assert_int_equal(rc, 0); + assert_non_null(he); + assert_non_null(he->h_name); + assert_non_null(he->h_addr_list); + + assert_string_equal(he->h_name, "magrathea.galaxy.site"); + assert_int_equal(he->h_addrtype, AF_INET); + + a = inet_ntop(AF_INET, he->h_addr_list[0], ip, sizeof(ip)); + assert_non_null(a); + + assert_string_equal(ip, "127.0.0.11"); +} +#endif + +#ifdef HAVE_GETHOSTBYADDR_R +static void test_nwrap_gethostbyaddr_r(void **state) +{ + char buf[1024] = {0}; + struct hostent hb, *he; + struct in_addr in; + int herr = 0; + int rc; + + (void) state; /* unused */ + + rc = inet_aton("127.0.0.11", &in); + assert_int_equal(rc, 1); + + rc = gethostbyaddr_r(&in, sizeof(struct in_addr), + AF_INET, + &hb, + buf, sizeof(buf), + &he, + &herr); + assert_int_equal(rc, 0); + assert_non_null(he); + assert_non_null(he->h_name); + assert_non_null(he->h_addr_list); + + assert_string_equal(he->h_name, "magrathea.galaxy.site"); + assert_int_equal(he->h_addrtype, AF_INET); + assert_memory_equal(&in, he->h_addr_list[0], he->h_length); +} +#endif + +int main(void) { + int rc; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_nwrap_gethostname), + cmocka_unit_test(test_nwrap_gethostbyname), + cmocka_unit_test(test_nwrap_gethostbyname_thread), +#ifdef HAVE_GETHOSTBYNAME2 + cmocka_unit_test(test_nwrap_gethostbyname2), +#endif + cmocka_unit_test(test_nwrap_gethostbyaddr), +#ifdef HAVE_GETHOSTBYNAME_R + cmocka_unit_test(test_nwrap_gethostbyname_r), +#endif +#ifdef HAVE_GETHOSTBYADDR_R + cmocka_unit_test(test_nwrap_gethostbyaddr_r), +#endif + cmocka_unit_test(test_nwrap_gethostbyname_multiple), + }; + + rc = cmocka_run_group_tests(tests, NULL, NULL); + + return rc; +} + diff --git a/tests/test_gethostent.c b/tests/test_gethostent.c new file mode 100644 index 0000000..aa61696 --- /dev/null +++ b/tests/test_gethostent.c @@ -0,0 +1,61 @@ +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +static void test_nwrap_gethostent(void **state) +{ + struct hostent *he; + uint32_t i; + + (void)state; /* unused */ + + sethostent(0); + + for (he = gethostent(); he != NULL; he = gethostent()) { + assert_non_null(he->h_addr_list); + assert_non_null(he->h_aliases); + + for (i = 0; he->h_addr_list[i] != NULL; i++) { + char buf[INET6_ADDRSTRLEN]; + uint32_t j; + const char *ip; + + ip = inet_ntop(he->h_addrtype, + he->h_addr_list[i], + buf, + sizeof(buf)); + + printf("ip: %s\n", ip); + + for (j = 0; he->h_aliases[j] != NULL; j++) { + printf("alias: %s\n", he->h_aliases[j]); + } + } + } + + endhostent(); +} + +int main(void) { + int rc; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_nwrap_gethostent), + }; + + rc = cmocka_run_group_tests(tests, NULL, NULL); + + return rc; +} diff --git a/tests/test_getnameinfo.c b/tests/test_getnameinfo.c new file mode 100644 index 0000000..bfafa98 --- /dev/null +++ b/tests/test_getnameinfo.c @@ -0,0 +1,343 @@ +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +static void test_nwrap_getnameinfo(void **state) +{ + char host[256] = {0}; + char serv[256] = {0}; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + int flags = 0; + int rc; + + (void) state; /* unused */ + + /* IPv4 */ + sin.sin_family = AF_INET; + sin.sin_port = htons(53); + rc = inet_pton(AF_INET, "127.0.0.11", &sin.sin_addr); + assert_int_equal(rc, 1); + + rc = getnameinfo((const struct sockaddr *)&sin, + sizeof(struct sockaddr_in), + host, sizeof(host), + serv, sizeof(serv), + flags); + assert_int_equal(rc, 0); + + assert_string_equal(host, "magrathea.galaxy.site"); + assert_string_equal(serv, "domain"); + + /* IPv6 */ + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(53); + rc = inet_pton(AF_INET6, "::13", &sin6.sin6_addr); + assert_int_equal(rc, 1); + + rc = getnameinfo((const struct sockaddr *)&sin6, + sizeof(struct sockaddr_in6), + host, sizeof(host), + serv, sizeof(serv), + flags); + assert_int_equal(rc, 0); + + assert_string_equal(host, "beteigeuze.galaxy.site"); + assert_string_equal(serv, "domain"); +} + +static void test_nwrap_getnameinfo_numeric(void **state) +{ + char host[256] = {0}; + char serv[256] = {0}; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + int flags = 0; + int rc; + + (void) state; /* unused */ + + /* IPv4 */ + sin.sin_family = AF_INET; + sin.sin_port = htons(53); + rc = inet_pton(AF_INET, "127.0.0.11", &sin.sin_addr); + assert_int_equal(rc, 1); + + flags = NI_NUMERICHOST; + + rc = getnameinfo((const struct sockaddr *)&sin, + sizeof(struct sockaddr_in), + host, sizeof(host), + serv, sizeof(serv), + flags); + assert_int_equal(rc, 0); + + assert_string_equal(host, "127.0.0.11"); + assert_string_equal(serv, "domain"); + + /* IPv6 */ + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(53); + rc = inet_pton(AF_INET6, "::13", &sin6.sin6_addr); + assert_int_equal(rc, 1); + + flags = NI_NUMERICSERV; + + rc = getnameinfo((const struct sockaddr *)&sin6, + sizeof(struct sockaddr_in6), + host, sizeof(host), + serv, sizeof(serv), + flags); + assert_int_equal(rc, 0); + + assert_string_equal(host, "beteigeuze.galaxy.site"); + assert_string_equal(serv, "53"); +} + +static void test_nwrap_getnameinfo_any(void **state) +{ + char host[256] = {0}; + char serv[256] = {0}; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + int flags = 0; + int rc; + + (void) state; /* unused */ + + /* IPv4 */ + sin.sin_family = AF_INET; + sin.sin_port = htons(22); + rc = inet_pton(AF_INET, "0.0.0.0", &sin.sin_addr); + assert_int_equal(rc, 1); + + rc = getnameinfo((const struct sockaddr *)&sin, + sizeof(struct sockaddr_in), + host, sizeof(host), + serv, sizeof(serv), + flags); + assert_int_equal(rc, 0); + + assert_string_equal(host, "0.0.0.0"); + assert_string_equal(serv, "ssh"); + + /* IPv6 */ + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(22); + rc = inet_pton(AF_INET6, "::", &sin6.sin6_addr); + assert_int_equal(rc, 1); + + rc = getnameinfo((const struct sockaddr *)&sin6, + sizeof(struct sockaddr_in6), + host, sizeof(host), + serv, sizeof(serv), + flags); + assert_int_equal(rc, 0); + + assert_string_equal(host, "::"); + assert_string_equal(serv, "ssh"); +} + +static void test_nwrap_getnameinfo_local(void **state) +{ + char host[256] = {0}; + char serv[256] = {0}; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + int flags = 0; + int rc; + + (void) state; /* unused */ + + /* IPv4 */ + sin.sin_family = AF_INET; + sin.sin_port = htons(22); + rc = inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr); + assert_int_equal(rc, 1); + + rc = getnameinfo((const struct sockaddr *)&sin, + sizeof(struct sockaddr_in), + host, sizeof(host), + serv, sizeof(serv), + flags); + assert_int_equal(rc, 0); + + assert_string_equal(host, "127.0.0.1"); + assert_string_equal(serv, "ssh"); + + /* IPv6 */ + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(22); + rc = inet_pton(AF_INET6, "::1", &sin6.sin6_addr); + assert_int_equal(rc, 1); + + rc = getnameinfo((const struct sockaddr *)&sin6, + sizeof(struct sockaddr_in6), + host, sizeof(host), + serv, sizeof(serv), + flags); + assert_int_equal(rc, 0); + + assert_string_equal(host, "::1"); + assert_string_equal(serv, "ssh"); +} + +static void test_nwrap_getnameinfo_null(void **state) +{ + char host[256] = {0}; + char serv[256] = {0}; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + int flags = 0; + int rc; + + (void) state; /* unused */ + + rc = getnameinfo(NULL, + 0, + host, sizeof(host), + serv, sizeof(serv), + flags); + assert_int_equal(rc, EAI_FAMILY); + + /* IPv4 */ + sin.sin_family = AF_INET; + sin.sin_port = htons(22); + rc = inet_pton(AF_INET, "127.0.0.11", &sin.sin_addr); + assert_int_equal(rc, 1); + + rc = getnameinfo((const struct sockaddr *)&sin, + sizeof(struct sockaddr_in), + NULL, 0, + serv, sizeof(serv), + flags); + assert_int_equal(rc, 0); + + assert_string_equal(serv, "ssh"); + + /* IPv6 */ + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(22); + rc = inet_pton(AF_INET6, "::13", &sin6.sin6_addr); + assert_int_equal(rc, 1); + + rc = getnameinfo((const struct sockaddr *)&sin6, + sizeof(struct sockaddr_in6), + host, sizeof(host), + NULL, 0, + flags); + assert_int_equal(rc, 0); + + assert_string_equal(host, "beteigeuze.galaxy.site"); + + /* IPv6 */ + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(22); + rc = inet_pton(AF_INET6, "::13", &sin6.sin6_addr); + assert_int_equal(rc, 1); + + rc = getnameinfo((const struct sockaddr *)&sin6, + sizeof(struct sockaddr_in6), + NULL, 0, + NULL, 0, + flags); + assert_int_equal(rc, 0); + + assert_string_equal(host, "beteigeuze.galaxy.site"); +} + +static void test_nwrap_getnameinfo_flags(void **state) +{ + char host[256] = {0}; + char serv[256] = {0}; + struct sockaddr_in sin; + int flags = 0; + int rc; + + (void) state; /* unused */ + + /* NI_NAMEREQD */ + sin.sin_family = AF_INET; + sin.sin_port = htons(22); + rc = inet_pton(AF_INET, "127.0.0.11", &sin.sin_addr); + assert_int_equal(rc, 1); + + flags = NI_NAMEREQD; + + rc = getnameinfo((const struct sockaddr *)&sin, + sizeof(struct sockaddr_in), + NULL, 0, + NULL, 0, + flags); + assert_int_equal(rc, EAI_NONAME); + + /* NI_DGRAM */ + sin.sin_family = AF_INET; + sin.sin_port = htons(513); + rc = inet_pton(AF_INET, "127.0.0.11", &sin.sin_addr); + assert_int_equal(rc, 1); + + flags = NI_DGRAM; + + rc = getnameinfo((const struct sockaddr *)&sin, + sizeof(struct sockaddr_in), + host, sizeof(host), + serv, sizeof(serv), + flags); + assert_int_equal(rc, 0); + + assert_string_equal(serv, "who"); + + /* STREAM (port 513) */ + flags = 0; + + rc = getnameinfo((const struct sockaddr *)&sin, + sizeof(struct sockaddr_in), + host, sizeof(host), + serv, sizeof(serv), + flags); + assert_int_equal(rc, 0); + + assert_string_equal(serv, "login"); + + /* NI_NOFQDN */ + flags = NI_NOFQDN; + + rc = getnameinfo((const struct sockaddr *)&sin, + sizeof(struct sockaddr_in), + host, sizeof(host), + serv, sizeof(serv), + flags); + assert_int_equal(rc, 0); + + assert_string_equal(host, "magrathea"); +} + +int main(void) { + int rc; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_nwrap_getnameinfo), + cmocka_unit_test(test_nwrap_getnameinfo_numeric), + cmocka_unit_test(test_nwrap_getnameinfo_any), + cmocka_unit_test(test_nwrap_getnameinfo_local), + cmocka_unit_test(test_nwrap_getnameinfo_null), + cmocka_unit_test(test_nwrap_getnameinfo_flags), + }; + + rc = cmocka_run_group_tests(tests, NULL, NULL); + + return rc; +} diff --git a/tests/test_nwrap_disabled.c b/tests/test_nwrap_disabled.c new file mode 100644 index 0000000..474dda7 --- /dev/null +++ b/tests/test_nwrap_disabled.c @@ -0,0 +1,121 @@ +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void test_nwrap_passwd_group(void **state) +{ + struct passwd *pwd; + struct group *grp; + + (void) state; /* unused */ + + pwd = getpwuid(getuid()); + assert_non_null(pwd); + + pwd = getpwnam(pwd->pw_name); + assert_non_null(pwd); + + grp = getgrgid(pwd->pw_gid); + assert_non_null(grp); + + grp = getgrnam(grp->gr_name); + assert_non_null(grp); +} + +/* Test libnsl */ +static void test_nwrap_hostent(void **state) +{ +#ifdef HAVE_NONNULL_GETHOSTENT + struct hostent *he; + + (void) state; /* unused */ + + sethostent(0); + + he = gethostent(); + assert_non_null(he); + + endhostent(); +#else + (void) state; /* unused */ + + sethostent(0); + gethostent(); + endhostent(); +#endif +} + +static void test_nwrap_gethostname(void **state) +{ + char host[256] = {0}; + int rc; + + (void) state; /* unused */ + + rc = gethostname(host, sizeof(host)); + assert_int_equal(rc, 0); + + assert_true(strlen(host) > 1); +} + +static void test_nwrap_getaddrinfo_local(void **state) +{ + struct addrinfo hints; + struct addrinfo *res; + struct sockaddr_in *sinp; + int rc; + + (void) state; /* unused */ + + /* IPv4 */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + + rc = getaddrinfo("127.0.0.1", NULL, &hints, &res); + assert_int_equal(rc, 0); + + assert_int_equal(res->ai_family, AF_INET); + assert_int_equal(res->ai_socktype, SOCK_STREAM); + +#ifdef HAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES + assert_string_equal(res->ai_canonname, "127.0.0.1"); +#else /* HAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES */ + assert_null(res->ai_canonname); +#endif /* HAVE_GETADDRINFO_SETS_CANONNAME_FOR_IPADDRESSES */ + + sinp = (struct sockaddr_in *)res->ai_addr; + + assert_int_equal(ntohl(sinp->sin_addr.s_addr), INADDR_LOOPBACK); + + freeaddrinfo(res); +} + +int main(void) { + int rc; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_nwrap_passwd_group), + cmocka_unit_test(test_nwrap_hostent), + cmocka_unit_test(test_nwrap_gethostname), + cmocka_unit_test(test_nwrap_getaddrinfo_local), + }; + + rc = cmocka_run_group_tests(tests, NULL, NULL); + + return rc; +} diff --git a/tests/test_nwrap_vector.c b/tests/test_nwrap_vector.c new file mode 100644 index 0000000..26baf01 --- /dev/null +++ b/tests/test_nwrap_vector.c @@ -0,0 +1,162 @@ +#include "config.h" + +#include +#include +#include +#include + +#include "../src/nss_wrapper.c" + +static void test_nwrap_vector_basic_add(void **state) +{ + struct nwrap_vector v; + char string[] = "string!"; + char string2[] = "2string!"; + + (void) state; /* unused */ + + nwrap_vector_init(&v); + assert_non_null(v.items); + + nwrap_vector_add_item(&v, string); + assert_null(v.items[1]); + assert_int_equal(v.count, 1); + assert_string_equal(v.items[0], string); + assert_int_equal(v.capacity, DEFAULT_VECTOR_CAPACITY); + + assert_string_equal(v.items[0], "string!"); + + nwrap_vector_add_item(&v, string2); + assert_null(v.items[2]); + assert_int_equal(v.count, 2); + assert_string_equal(v.items[0], string); + assert_string_equal(v.items[1], string2); + + free(v.items); +} + +static void test_nwrap_vector_merge_empty(void **state) +{ + struct nwrap_vector v1; + struct nwrap_vector v2; + char string[] = "string!"; + char string2[] = "2string!"; + + (void) state; /* unused */ + + nwrap_vector_init(&v1); + assert_non_null(v1.items); + assert_int_equal(v1.count, 0); + + nwrap_vector_init(&v2); + assert_non_null(v2.items); + assert_int_equal(v2.count, 0); + + nwrap_vector_merge(&v1, &v2); + assert_int_equal(v1.count, 0); + assert_null(v1.items[0]); + + nwrap_vector_add_item(&v1, string); + nwrap_vector_add_item(&v1, string2); + assert_int_equal(v1.count, 2); + + nwrap_vector_merge(&v1, &v2); + assert_int_equal(v1.count, 2); + assert_string_equal(v1.items[0], string); + assert_string_equal(v1.items[1], string2); + assert_null(v1.items[2]); + + nwrap_vector_merge(&v2, &v1); + assert_int_equal(v2.count, 2); + assert_string_equal(v2.items[0], string); + assert_string_equal(v2.items[1], string2); + assert_null(v2.items[2]); + + free(v1.items); + free(v2.items); +} + +static void test_nwrap_vector_merge(void **state) +{ + struct nwrap_vector v1; + struct nwrap_vector v2; + char string[] = "string!"; + char string2[] = "2string!"; + + (void) state; /* unused */ + + nwrap_vector_init(&v1); + assert_non_null(v1.items); + nwrap_vector_add_item(&v1, string); + nwrap_vector_add_item(&v1, string2); + assert_int_equal(v1.count, 2); + + nwrap_vector_init(&v2); + assert_non_null(v2.items); + nwrap_vector_add_item(&v2, string2); + nwrap_vector_add_item(&v2, string); + assert_int_equal(v2.count, 2); + + nwrap_vector_merge(&v1, &v2); + assert_int_equal(v1.count, 4); + assert_string_equal(v1.items[0], string); + assert_string_equal(v1.items[1], string2); + assert_string_equal(v1.items[2], string2); + assert_string_equal(v1.items[3], string); + assert_null(v1.items[4]); + + free(v1.items); + free(v2.items); +} + +static void test_nwrap_vector_merge_max(void **state) +{ + struct nwrap_vector v1; + struct nwrap_vector v2; + char string[] = "string!"; + char string2[] = "2string!"; + int p; + + (void) state; /* unused */ + + nwrap_vector_init(&v1); + assert_non_null(v1.items); + + for (p = 0; p < 64; ++p) { + nwrap_vector_add_item(&v1, string); + } + assert_int_equal(v1.count, 64); + + nwrap_vector_init(&v2); + assert_non_null(v2.items); + + nwrap_vector_merge(&v2, &v1); + assert_int_equal(v2.count, 64); + for (p = 0; p < 64; ++p) { + assert_string_equal(v2.items[p], string); + } + + nwrap_vector_add_item(&v2, string2); + assert_string_equal(v2.items[64], string2); + assert_int_equal(v2.count, 65); + assert_null(v2.items[65]); + + free(v1.items); + free(v2.items); +} + +int main(void) { + int rc; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_nwrap_vector_basic_add), + cmocka_unit_test(test_nwrap_vector_merge_empty), + cmocka_unit_test(test_nwrap_vector_merge), + cmocka_unit_test(test_nwrap_vector_merge_max), + }; + + rc = cmocka_run_group_tests(tests, NULL, NULL); + + return rc; +} + diff --git a/tests/test_shadow.c b/tests/test_shadow.c new file mode 100644 index 0000000..7af120b --- /dev/null +++ b/tests/test_shadow.c @@ -0,0 +1,73 @@ +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +static void test_nwrap_getspent(void **state) +{ + struct spwd *sp; + uint32_t i; + + (void)state; /* unused */ + + setspent(); + + for (sp = getspent(), i = 0; sp != NULL; sp = getspent(), i++) { + if (i == 0) { + assert_string_equal(sp->sp_namp, "alice"); + } else { + assert_string_equal(sp->sp_namp, "bob"); + } + } + + endspent(); +} + +static void test_nwrap_getspnam(void **state) +{ + char *encrypted_password; + struct spwd *sp; + + (void)state; /* unused */ + + sp = getspnam("alice"); + assert_non_null(sp); + + assert_string_equal(sp->sp_namp, "alice"); + + encrypted_password = crypt("secret", sp->sp_pwdp); + assert_non_null(encrypted_password); + + assert_string_equal(encrypted_password, sp->sp_pwdp); + + sp = getspnam("bob"); + assert_non_null(sp); + + assert_string_equal(sp->sp_namp, "bob"); + + encrypted_password = crypt("secret", sp->sp_pwdp); + assert_non_null(encrypted_password); + + assert_string_equal(encrypted_password, sp->sp_pwdp); +} + +int main(void) { + int rc; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_nwrap_getspent), + cmocka_unit_test(test_nwrap_getspnam), + }; + + rc = cmocka_run_group_tests(tests, NULL, NULL); + + return rc; +} diff --git a/tests/testsuite.c b/tests/testsuite.c new file mode 100644 index 0000000..a423c55 --- /dev/null +++ b/tests/testsuite.c @@ -0,0 +1,1020 @@ +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * 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 +#include +#include + +#include +#include +#include + +#ifdef NDEBUG +#define DEBUG(...) +#else +#define DEBUG(...) printf(__VA_ARGS__) +#endif + +#ifndef SAFE_FREE +#define SAFE_FREE(x) do { if ((x) != NULL) {free((x)); (x)=NULL;} } while(0) +#endif + +static void assert_passwd_equal(const struct passwd *p1, + const struct passwd *p2) +{ + assert_string_equal(p1->pw_name, p2->pw_name); + assert_string_equal(p1->pw_passwd, p2->pw_passwd); + assert_int_equal(p1->pw_uid, p2->pw_uid); + assert_int_equal(p1->pw_gid, p2->pw_gid); + assert_string_equal(p1->pw_gecos, p2->pw_gecos); + assert_string_equal(p1->pw_dir, p2->pw_dir); + assert_string_equal(p1->pw_shell, p2->pw_shell); +} + +static void assert_group_equal(const struct group *g1, + const struct group *g2) +{ + int i; + + assert_string_equal(g1->gr_name, g2->gr_name); + assert_string_equal(g1->gr_passwd, g2->gr_passwd); + assert_int_equal(g1->gr_gid, g2->gr_gid); + assert_false(g1->gr_mem != NULL && g2->gr_mem == NULL); + assert_false(g1->gr_mem == NULL && g2->gr_mem != NULL); + if (g1->gr_mem == NULL && g2->gr_mem == NULL) { + return; + } + for (i=0; g1->gr_mem[i] && g2->gr_mem[i]; i++) { + assert_string_equal(g1->gr_mem[i], g2->gr_mem[i]); + } +} + +static bool copy_passwd(const struct passwd *pwd, struct passwd *p) +{ + p->pw_name = strdup(pwd->pw_name); + p->pw_passwd = strdup(pwd->pw_passwd); + p->pw_uid = pwd->pw_uid; + p->pw_gid = pwd->pw_gid; +#ifdef HAVE_STRUCT_PASSWD_PW_CLASS + p->pw_class = strdup(pwd->pw_class); +#endif +#ifdef HAVE_STRUCT_PASSWD_PW_CHANGE + p->pw_change = pwd->pw_change; +#endif +#ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE + p->pw_expire = pwd->pw_expire; +#endif + p->pw_gecos = strdup(pwd->pw_gecos); + p->pw_dir = strdup(pwd->pw_dir); + p->pw_shell = strdup(pwd->pw_shell); + + return true; +} + +static void free_passwd(struct passwd *p) +{ + SAFE_FREE(p->pw_name); + SAFE_FREE(p->pw_passwd); +#ifdef HAVE_STRUCT_PASSWD_PW_CLASS + SAFE_FREE(p->pw_class); +#endif + SAFE_FREE(p->pw_gecos); + SAFE_FREE(p->pw_dir); + SAFE_FREE(p->pw_shell); +} + +static void free_passwds(struct passwd *pwds, size_t num_pwds) +{ + size_t i; + + for(i = 0; i < num_pwds; i++) { + free_passwd(&pwds[i]); + } + free(pwds); +} + +static void print_passwd(struct passwd *pwd) +{ + (void)pwd; + + DEBUG("%s:%s:%lu:%lu:%s:%s:%s\n", + pwd->pw_name, + pwd->pw_passwd, + (unsigned long)pwd->pw_uid, + (unsigned long)pwd->pw_gid, + pwd->pw_gecos, + pwd->pw_dir, + pwd->pw_shell); +} + +static bool test_nwrap_getpwnam(const char *name, struct passwd *pwd_p) +{ + struct passwd *pwd = NULL; + bool ok; + + DEBUG("Testing getpwnam: %s\n", name); + + pwd = getpwnam(name); + if (pwd == NULL) { + return false; + } + + print_passwd(pwd); + + if (pwd_p == NULL) { + return true; + } + + ok = copy_passwd(pwd, pwd_p); + + return ok; +} + +static void test_nwrap_getpwnam_r(const char *name, + struct passwd *pwd_p) +{ + struct passwd pwd, *pwdp; + char buffer[4096]; + int ret; + + DEBUG("Testing getpwnam_r: %s\n", name); + + ret = getpwnam_r(name, &pwd, buffer, sizeof(buffer), &pwdp); + if (ret != 0) { + if (ret != ENOENT) { + DEBUG("got %d return code\n", ret); + } + assert_true(ret); + } + assert_ptr_equal(&pwd, pwdp); + + print_passwd(&pwd); + + if (pwd_p) { + copy_passwd(&pwd, pwd_p); + } +} + +static bool test_nwrap_getpwuid(uid_t uid, + struct passwd *pwd_p) +{ + struct passwd *pwd = NULL; + bool ok; + + DEBUG("Testing getpwuid: %lu\n", (unsigned long)uid); + + pwd = getpwuid(uid); + if (pwd == NULL) { + return false; + } + + print_passwd(pwd); + + if (pwd_p == NULL) { + return true; + } + + ok = copy_passwd(pwd, pwd_p); + + return ok; +} + +static bool test_nwrap_getpwuid_r(uid_t uid, + struct passwd *pwd_p) +{ + struct passwd pwd, *pwdp; + char buffer[4096]; + int ret; + + DEBUG("Testing getpwuid_r: %lu\n", (unsigned long)uid); + + ret = getpwuid_r(uid, &pwd, buffer, sizeof(buffer), &pwdp); + if (ret != 0) { + if (ret != ENOENT) { + DEBUG("got %d return code\n", ret); + } + assert_true(ret); + } + assert_ptr_equal(&pwd, pwdp); + + print_passwd(&pwd); + + if (pwd_p) { + copy_passwd(&pwd, pwd_p); + } + + return true; +} + +static bool copy_group(const struct group *grp, + struct group *g) +{ + int i; + + g->gr_name = strdup(grp->gr_name); + g->gr_passwd = strdup(grp->gr_passwd); + g->gr_gid = grp->gr_gid; + g->gr_mem = NULL; + + for (i = 0; grp->gr_mem != NULL && grp->gr_mem[i] != NULL; i++) { + char **mem; + + mem = realloc(g->gr_mem, sizeof(char *) * (i + 2)); + assert_non_null(mem); + g->gr_mem = mem; + + g->gr_mem[i] = strdup(grp->gr_mem[i]); + assert_non_null(g->gr_mem[i]); + + g->gr_mem[i + 1] = NULL; + } + + return true; +} + +static void free_group(struct group *g) +{ + SAFE_FREE(g->gr_name); + SAFE_FREE(g->gr_passwd); + if (g->gr_mem != NULL) { + int i; + + for (i = 0; g->gr_mem[i] != NULL; i++) { + SAFE_FREE(g->gr_mem[i]); + } + + SAFE_FREE(g->gr_mem); + } +} + +static void free_groups(struct group *grps, size_t num_grps) +{ + size_t i; + + for(i = 0; i < num_grps; i++) { + free_group(&grps[i]); + } + free(grps); +} + +static void print_group(struct group *grp) +{ + int i; + DEBUG("%s:%s:%lu:", + grp->gr_name, + grp->gr_passwd, + (unsigned long)grp->gr_gid); + + if ((grp->gr_mem == NULL) || !grp->gr_mem[0]) { + DEBUG("\n"); + return; + } + + for (i=0; grp->gr_mem[i+1]; i++) { + DEBUG("%s,", grp->gr_mem[i]); + } + DEBUG("%s\n", grp->gr_mem[i]); +} + +static bool test_nwrap_getgrnam(const char *name, + struct group *grp_p) +{ + struct group *grp = NULL; + bool ok; + + DEBUG("Testing getgrnam: %s\n", name); + + grp = getgrnam(name); + if (grp == NULL) { + return false; + } + + print_group(grp); + + if (grp_p == NULL) { + return true; + } + + ok = copy_group(grp, grp_p); + + return ok; +} + +static bool test_nwrap_getgrnam_r(const char *name, + struct group *grp_p) +{ + struct group grp, *grpp; + char buffer[4096]; + int ret; + + DEBUG("Testing getgrnam_r: %s\n", name); + + ret = getgrnam_r(name, &grp, buffer, sizeof(buffer), &grpp); + if (ret != 0) { + if (ret != ENOENT) { + DEBUG("got %d return code\n", ret); + } + assert_true(ret); + } + assert_ptr_equal(&grp, grpp); + + print_group(&grp); + + if (grp_p) { + copy_group(&grp, grp_p); + } + + return true; +} + +static bool test_nwrap_getgrgid(gid_t gid, + struct group *grp_p) +{ + struct group *grp = NULL; + bool ok; + + DEBUG("Testing getgrgid: %lu\n", (unsigned long)gid); + + grp = getgrgid(gid); + if (grp == NULL) { + return false; + } + + print_group(grp); + + if (grp_p == NULL) { + return true; + } + + ok = copy_group(grp, grp_p); + + return ok; +} + +static bool test_nwrap_getgrgid_r(gid_t gid, + struct group *grp_p) +{ + struct group grp, *grpp; + char buffer[4096]; + int ret; + + DEBUG("Testing getgrgid_r: %lu\n", (unsigned long)gid); + + ret = getgrgid_r(gid, &grp, buffer, sizeof(buffer), &grpp); + if (ret != 0) { + if (ret != ENOENT) { + DEBUG("got %d return code\n", ret); + } + assert_true(ret); + } + assert_ptr_equal(&grp, grpp); + + print_group(&grp); + + if (grp_p) { + copy_group(&grp, grp_p); + } + + return true; +} + +static bool test_nwrap_enum_passwd(struct passwd **pwd_array_p, + size_t *num_pwd_p) +{ + struct passwd *pwd; + struct passwd *pwd_array = NULL; + size_t num_pwd = 0; + + DEBUG("Testing setpwent\n"); + + setpwent(); + + while ((pwd = getpwent()) != NULL) { + DEBUG("Testing getpwent\n"); + + print_passwd(pwd); + if (pwd_array_p && num_pwd_p) { + pwd_array = realloc(pwd_array, sizeof(struct passwd) * (num_pwd + 1)); + assert_non_null(pwd_array); + copy_passwd(pwd, &pwd_array[num_pwd]); + num_pwd++; + } + } + + DEBUG("Testing endpwent\n"); + endpwent(); + + if (pwd_array_p) { + *pwd_array_p = pwd_array; + } + if (num_pwd_p) { + *num_pwd_p = num_pwd; + } + + return true; +} + +static bool test_nwrap_enum_r_passwd(struct passwd **pwd_array_p, + size_t *num_pwd_p) +{ + struct passwd *pwd_array = NULL; + size_t num_pwd = 0; +/* Skip these tests if the platform does not provide getpwent_r() */ +#ifdef HAVE_GETPWENT_R + struct passwd pwd, *pwdp; + char buffer[4096]; + int ret; + + DEBUG("Testing setpwent\n"); + setpwent(); + + while (1) { + DEBUG("Testing getpwent_r\n"); + +#ifdef HAVE_SOLARIS_GETPWENT_R + pwdp = getpwent_r(&pwd, buffer, sizeof(buffer)); + if (pwdp == NULL) { + break; + } +#else /* HAVE_SOLARIS_GETPWENT_R */ + ret = getpwent_r(&pwd, buffer, sizeof(buffer), &pwdp); + if (ret != 0) { + if (ret != ENOENT) { + DEBUG("got %d return code\n", ret); + } + break; + } +#endif /* HAVE_SOLARIS_GETPWENT_R */ + print_passwd(&pwd); + if (pwd_array_p && num_pwd_p) { + pwd_array = realloc(pwd_array, sizeof(struct passwd) * (num_pwd + 1)); + assert_non_null(pwd_array); + copy_passwd(&pwd, &pwd_array[num_pwd]); + num_pwd++; + } + } + + DEBUG("Testing endpwent\n"); + endpwent(); +#endif /* HAVE_GETPWENT_R */ + + if (pwd_array_p) { + *pwd_array_p = pwd_array; + } + if (num_pwd_p) { + *num_pwd_p = num_pwd; + } + + return true; +} + +static bool test_nwrap_passwd(void) +{ + struct passwd *pwd, pwd1, pwd2; + size_t i, num_pwd; + + test_nwrap_enum_passwd(&pwd, &num_pwd); + + for (i=0; i < num_pwd; i++) { + test_nwrap_getpwnam(pwd[i].pw_name, &pwd1); + assert_passwd_equal(&pwd[i], &pwd1); + + test_nwrap_getpwuid(pwd[i].pw_uid, &pwd2); + assert_passwd_equal(&pwd[i], &pwd2); + assert_passwd_equal(&pwd1, &pwd2); + + free_passwd(&pwd1); + free_passwd(&pwd2); + } + free_passwds(pwd, num_pwd); + + return true; +} + +static void test_nwrap_passwd_r(void) +{ + struct passwd *pwd, pwd1, pwd2; + size_t i, num_pwd; + + test_nwrap_enum_r_passwd(&pwd, &num_pwd); + + for (i=0; i < num_pwd; i++) { + test_nwrap_getpwnam_r(pwd[i].pw_name, &pwd1); + assert_passwd_equal(&pwd[i], &pwd1); + + test_nwrap_getpwuid_r(pwd[i].pw_uid, &pwd2); + assert_passwd_equal(&pwd[i], &pwd2); + + assert_passwd_equal(&pwd1, &pwd2); + + free_passwd(&pwd1); + free_passwd(&pwd2); + } + free_passwds(pwd, num_pwd); +} + +static bool test_nwrap_passwd_r_cross(void) +{ + struct passwd *pwd, pwd1, pwd2, pwd3, pwd4; + size_t i, num_pwd; + + test_nwrap_enum_r_passwd(&pwd, &num_pwd); + + for (i=0; i < num_pwd; i++) { + test_nwrap_getpwnam_r(pwd[i].pw_name, &pwd1); + assert_passwd_equal(&pwd[i], &pwd1); + + test_nwrap_getpwuid_r(pwd[i].pw_uid, &pwd2); + assert_passwd_equal(&pwd[i], &pwd2); + + assert_passwd_equal(&pwd1, &pwd2); + + test_nwrap_getpwnam(pwd[i].pw_name, &pwd3); + assert_passwd_equal(&pwd[i], &pwd3); + + test_nwrap_getpwuid(pwd[i].pw_uid, &pwd4); + assert_passwd_equal(&pwd[i], &pwd4); + + assert_passwd_equal(&pwd3, &pwd4); + + free_passwd(&pwd1); + free_passwd(&pwd2); + free_passwd(&pwd3); + free_passwd(&pwd4); + } + free_passwds(pwd, num_pwd); + + return true; +} + +static bool test_nwrap_enum_group(struct group **grp_array_p, + size_t *num_grp_p) +{ + struct group *grp; + struct group *grp_array = NULL; + size_t num_grp = 0; + + DEBUG("Testing setgrent\n"); + setgrent(); + + while ((grp = getgrent()) != NULL) { + DEBUG("Testing getgrent\n"); + + print_group(grp); + if (grp_array_p && num_grp_p) { + grp_array = realloc(grp_array, sizeof(struct group) * (num_grp + 1)); + assert_non_null(grp_array); + copy_group(grp, &grp_array[num_grp]); + num_grp++; + } + } + + DEBUG("Testing endgrent\n"); + endgrent(); + + if (grp_array_p) { + *grp_array_p = grp_array; + } + if (num_grp_p) { + *num_grp_p = num_grp; + } + + return true; +} + +static bool test_nwrap_enum_r_group(struct group **grp_array_p, + size_t *num_grp_p) +{ + struct group *grp_array = NULL; + size_t num_grp = 0; +/* Skip these tests if the platform does not provide getgrent_r() */ +#ifdef HAVE_GETGRENT_R + struct group grp, *grpp; + char buffer[4096]; + int ret; + + DEBUG("Testing setgrent\n"); + setgrent(); + + while (1) { + DEBUG("Testing getgrent_r\n"); + +#ifdef HAVE_SOLARIS_GETGRENT_R + grpp = getgrent_r(&grp, buffer, sizeof(buffer)); + if (grpp == NULL) { + break; + } +#else /* HAVE_SOLARIS_GETGRENT_R */ + ret = getgrent_r(&grp, buffer, sizeof(buffer), &grpp); + if (ret != 0) { + if (ret != ENOENT) { + DEBUG("got %d return code\n", ret); + } + break; + } +#endif /* HAVE_SOLARIS_GETGRENT_R */ + print_group(&grp); + if (grp_array_p && num_grp_p) { + grp_array = realloc(grp_array, sizeof(struct group) * (num_grp + 1)); + assert_non_null(grp_array); + copy_group(&grp, &grp_array[num_grp]); + num_grp++; + } + } + + DEBUG("Testing endgrent\n"); + endgrent(); +#endif /* HAVE_GETGRENT_R */ + + if (grp_array_p) { + *grp_array_p = grp_array; + } + if (num_grp_p) { + *num_grp_p = num_grp; + } + + return true; +} + +static bool test_nwrap_group(void) +{ + struct group *grp, grp1, grp2; + size_t i, num_grp; + + test_nwrap_enum_group(&grp, &num_grp); + + for (i=0; i < num_grp; i++) { + test_nwrap_getgrnam(grp[i].gr_name, &grp1); + assert_group_equal(&grp[i], &grp1); + + test_nwrap_getgrgid(grp[i].gr_gid, &grp2); + assert_group_equal(&grp[i], &grp2); + + assert_group_equal(&grp1, &grp2); + + free_group(&grp1); + free_group(&grp2); + } + free_groups(grp, num_grp); + + return true; +} + +static bool test_nwrap_group_r(void) +{ + struct group *grp, grp1, grp2; + size_t i, num_grp; + + test_nwrap_enum_r_group(&grp, &num_grp); + + for (i=0; i < num_grp; i++) { + test_nwrap_getgrnam_r(grp[i].gr_name, &grp1); + assert_group_equal(&grp[i], &grp1); + + test_nwrap_getgrgid_r(grp[i].gr_gid, &grp2); + assert_group_equal(&grp[i], &grp2); + + assert_group_equal(&grp1, &grp2); + + free_group(&grp1); + free_group(&grp2); + } + free_groups(grp, num_grp); + + return true; +} + +static bool test_nwrap_group_r_cross(void) +{ + struct group *grp, grp1, grp2, grp3, grp4; + size_t i, num_grp; + + test_nwrap_enum_r_group(&grp, &num_grp); + + for (i=0; i < num_grp; i++) { + test_nwrap_getgrnam_r(grp[i].gr_name, &grp1); + assert_group_equal(&grp[i], &grp1); + + test_nwrap_getgrgid_r(grp[i].gr_gid, &grp2); + assert_group_equal(&grp[i], &grp2); + + assert_group_equal(&grp1, &grp2); + + test_nwrap_getgrnam(grp[i].gr_name, &grp3); + assert_group_equal(&grp[i], &grp3); + + test_nwrap_getgrgid(grp[i].gr_gid, &grp4); + assert_group_equal(&grp[i], &grp4); + + assert_group_equal(&grp3, &grp4); + + free_group(&grp1); + free_group(&grp2); + free_group(&grp3); + free_group(&grp4); + } + free_groups(grp, num_grp); + + return true; +} + +#ifdef HAVE_GETGROUPLIST +static bool test_nwrap_getgrouplist(const char *user, + gid_t gid, + gid_t **gids_p, + int *num_gids_p) +{ + int ret; + int num_groups = 0; + gid_t *groups = NULL; + + DEBUG("Testing getgrouplist: %s\n", user); + + ret = getgrouplist(user, gid, NULL, &num_groups); + if (ret == -1 || num_groups != 0) { + + groups = malloc(sizeof(gid_t) * num_groups); + assert_non_null(groups); + + ret = getgrouplist(user, gid, groups, &num_groups); + } + + assert_false(ret == -1); + + DEBUG("%s is member in %d groups\n", user, num_groups); + + if (gids_p) { + *gids_p = groups; + } + if (num_gids_p) { + *num_gids_p = num_groups; + } + + return true; +} + +static bool test_nwrap_user_in_group(const struct passwd *pwd, + const struct group *grp) +{ + int i; + + for (i = 0; grp->gr_mem != NULL && grp->gr_mem[i] != NULL; i++) { + if (strcmp(grp->gr_mem[i], pwd->pw_name) == 0) { + return true; + } + } + + return false; +} + +static bool test_nwrap_membership_user(const struct passwd *pwd, + struct group *grp_array, + size_t num_grp) +{ + int num_user_groups = 0; + size_t num_user_groups_from_enum = 0; + gid_t *user_groups = NULL; + size_t i; + int g; + bool primary_group_had_user_member = false; + + test_nwrap_getgrouplist(pwd->pw_name, + pwd->pw_gid, + &user_groups, + &num_user_groups); + + for (g=0; g < num_user_groups; g++) { + test_nwrap_getgrgid(user_groups[g], NULL); + } + free(user_groups); + + for (i=0; i < num_grp; i++) { + struct group grp = grp_array[i]; + + if (test_nwrap_user_in_group(pwd, &grp)) { + + struct group current_grp; + num_user_groups_from_enum++; + + test_nwrap_getgrnam(grp.gr_name, ¤t_grp); + + if (current_grp.gr_gid == pwd->pw_gid) { + DEBUG("primary group %s of user %s lists user as member\n", + current_grp.gr_name, + pwd->pw_name); + primary_group_had_user_member = true; + } + free_group(¤t_grp); + + continue; + } + } + + if (!primary_group_had_user_member) { + num_user_groups_from_enum++; + } + + assert_int_equal(num_user_groups, num_user_groups_from_enum); + + return true; +} + +static void test_nwrap_membership(void **state) +{ + const char *old_pwd = getenv("NSS_WRAPPER_PASSWD"); + const char *old_group = getenv("NSS_WRAPPER_GROUP"); + struct passwd *pwd; + size_t num_pwd; + struct group *grp; + size_t num_grp; + size_t i; + + (void) state; /* unused */ + + if (!old_pwd || !old_group) { + DEBUG("ENV NSS_WRAPPER_PASSWD or NSS_WRAPPER_GROUP not set\n"); + return; + } + + test_nwrap_enum_passwd(&pwd, &num_pwd); + test_nwrap_enum_group(&grp, &num_grp); + + for (i=0; i < num_pwd; i++) { + test_nwrap_membership_user(&pwd[i], grp, num_grp); + } + free_passwds(pwd, num_pwd); + free_groups(grp, num_grp); +} +#endif /* HAVE_GETGROUPLIST */ + +static void test_nwrap_enumeration(void **state) +{ + const char *old_pwd = getenv("NSS_WRAPPER_PASSWD"); + const char *old_group = getenv("NSS_WRAPPER_GROUP"); + + (void) state; /* unused */ + + if (!old_pwd || !old_group) { + DEBUG("ENV NSS_WRAPPER_PASSWD or NSS_WRAPPER_GROUP not set\n"); + return; + } + + test_nwrap_passwd(); + test_nwrap_group(); +} + +static void test_nwrap_reentrant_enumeration(void **state) +{ + const char *old_pwd = getenv("NSS_WRAPPER_PASSWD"); + const char *old_group = getenv("NSS_WRAPPER_GROUP"); + + (void) state; /* unused */ + + if (!old_pwd || !old_group) { + DEBUG("ENV NSS_WRAPPER_PASSWD or NSS_WRAPPER_GROUP not set\n"); + return; + } + + DEBUG("Testing re-entrant calls\n"); + + test_nwrap_passwd_r(); + test_nwrap_group_r(); +} + +static void test_nwrap_reentrant_enumeration_crosschecks(void **state) +{ + const char *old_pwd = getenv("NSS_WRAPPER_PASSWD"); + const char *old_group = getenv("NSS_WRAPPER_GROUP"); + + (void) state; /* unused */ + + if (!old_pwd || !old_group) { + DEBUG("ENV NSS_WRAPPER_PASSWD or NSS_WRAPPER_GROUP not set\n"); + return; + } + + DEBUG("Testing re-entrant calls with cross checks\n"); + + test_nwrap_passwd_r_cross(); + test_nwrap_group_r_cross(); +} + +static bool test_nwrap_passwd_duplicates(void) +{ + struct passwd *pwd; + size_t d, i, num_pwd; + int duplicates = 0; + + test_nwrap_enum_passwd(&pwd, &num_pwd); + + for (i=0; i < num_pwd; i++) { + const char *current_name = pwd[i].pw_name; + for (d=0; d < num_pwd; d++) { + const char *dup_name = pwd[d].pw_name; + if (d == i) { + continue; + } + if (strcmp(current_name, dup_name) != 0) { + continue; + } + + DEBUG("found duplicate names:"); + + print_passwd(&pwd[d]); + print_passwd(&pwd[i]); + duplicates++; + } + } + free_passwds(pwd, num_pwd); + + assert_false(duplicates); + + return true; +} + +static bool test_nwrap_group_duplicates(void) +{ + struct group *grp; + size_t d, i, num_grp; + int duplicates = 0; + + test_nwrap_enum_group(&grp, &num_grp); + + for (i=0; i < num_grp; i++) { + const char *current_name = grp[i].gr_name; + for (d=0; d < num_grp; d++) { + const char *dup_name = grp[d].gr_name; + if (d == i) { + continue; + } + if (strcmp(current_name, dup_name) != 0) { + continue; + } + + DEBUG("found duplicate names:"); + + print_group(&grp[d]); + print_group(&grp[i]); + duplicates++; + } + } + free_groups(grp, num_grp); + + assert_false(duplicates); + + return true; +} + +static void test_nwrap_duplicates(void **state) +{ + const char *old_pwd = getenv("NSS_WRAPPER_PASSWD"); + const char *old_group = getenv("NSS_WRAPPER_GROUP"); + + (void) state; /* unused */ + + if (!old_pwd || !old_group) { + DEBUG("ENV NSS_WRAPPER_PASSWD or NSS_WRAPPER_GROUP not set\n"); + return; + } + + test_nwrap_passwd_duplicates(); + test_nwrap_group_duplicates(); +} + +int main(void) { + int rc; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_nwrap_enumeration), + cmocka_unit_test(test_nwrap_reentrant_enumeration), + cmocka_unit_test(test_nwrap_reentrant_enumeration_crosschecks), +#ifdef HAVE_GETGROUPLIST + cmocka_unit_test(test_nwrap_membership), +#endif + cmocka_unit_test(test_nwrap_duplicates), + }; + + rc = cmocka_run_group_tests(tests, NULL, NULL); + + return rc; +} diff --git a/tests/valgrind.supp b/tests/valgrind.supp new file mode 100644 index 0000000..aac3f28 --- /dev/null +++ b/tests/valgrind.supp @@ -0,0 +1,39 @@ +### GLIBC + +{ + glibc_dlopen_worker_alloc + Memcheck:Leak + fun:*alloc + ... + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 +} + +{ + glibc_dlopen_alloc + Memcheck:Leak + fun:calloc + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 +} + +{ + glibc_dlclose_alloc + Memcheck:Leak + fun:calloc + fun:_dlerror_run + fun:dlclose +} + +{ + glibc___sha512_crypt_alloc + Memcheck:Leak + fun:malloc + fun:realloc + fun:__sha512_crypt +}