--- /dev/null
+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 "0")
+set(APPLICATION_VERSION_MINOR "1")
+set(APPLICATION_VERSION_PATCH "0")
+
+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.0.1")
+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)
--- /dev/null
+Copyright (C) Jelmer Vernooij 2005,2008 <jelmer@samba.org>
+Copyright (C) Stefan Metzmacher 2006-2009 <metze@samba.org>
+Copyright (C) Andreas Schneider 2013 <asn@samba.org>
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the author nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
--- /dev/null
+# 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}/COPYING")
+
+
+### versions
+set(CPACK_PACKAGE_VERSION_MAJOR "0")
+set(CPACK_PACKAGE_VERSION_MINOR "5")
+set(CPACK_PACKAGE_VERSION_PATCH "90")
+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/;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)
--- /dev/null
+set(UPDATE_TYPE "true")
+
+set(CTEST_PROJECT_NAME "libssh")
+set(CTEST_NIGHTLY_START_TIME "01:00:00 CET")
+
+set(CTEST_DROP_METHOD "http")
+set(CTEST_DROP_SITE "test.libssh.org")
+set(CTEST_DROP_LOCATION "/submit.php?project=libssh")
+set(CTEST_DROP_SITE_CDASH TRUE)
--- /dev/null
+include(CheckIncludeFile)
+include(CheckSymbolExists)
+include(CheckFunctionExists)
+include(CheckLibraryExists)
+include(CheckTypeSize)
+include(CheckStructHasMember)
+include(CheckPrototypeDefinition)
+include(TestBigEndian)
+
+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(grp.h HAVE_GRP_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(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)
+
+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)
+
+check_function_exists(asprintf HAVE_ASPRINTF)
+if (UNIX AND HAVE_ASPRINTF)
+ add_definitions(-D_GNU_SOURCE)
+endif (UNIX AND HAVE_ASPRINTF)
+
+# ENDIAN
+if (NOT WIN32)
+ test_big_endian(WORDS_BIGENDIAN)
+endif (NOT WIN32)
--- /dev/null
+option(WITH_ZLIB "Build with ZLIB support" ON)
+option(WITH_SSH1 "Build with SSH1 support" OFF)
+option(WITH_SFTP "Build with SFTP support" ON)
+option(WITH_SERVER "Build with SSH server support" ON)
+option(WITH_STATIC_LIB "Build with a static library" OFF)
+option(WITH_DEBUG_CRYPTO "Build with cryto debug output" OFF)
+option(WITH_DEBUG_CALLTRACE "Build with calltrace debug output" ON)
+option(WITH_GCRYPT "Compile against libgcrypt" OFF)
+option(WITH_PCAP "Compile with Pcap generation support" ON)
+option(WITH_INTERNAL_DOC "Compile doxygen internal documentation" OFF)
+option(WITH_TESTING "Build with unit tests" OFF)
+option(WITH_CLIENT_TESTING "Build with client tests; requires a running sshd" OFF)
+option(WITH_BENCHMARKS "Build benchmarks tools" OFF)
+
+if (WITH_ZLIB)
+ set(WITH_LIBZ ON)
+else (WITH_ZLIB)
+ set(WITH_LIBZ OFF)
+endif (WITH_ZLIB)
+
+if(WITH_BENCHMARKS)
+ set(WITH_TESTING ON)
+endif(WITH_BENCHMARKS)
+
+if (WITH_TESTING)
+ set(WITH_STATIC_LIB ON)
+endif (WITH_TESTING)
--- /dev/null
+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.
--- /dev/null
+# - 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, <neundorf@kde.org>
+#
+# 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)
--- /dev/null
+# 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)
--- /dev/null
+# 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")
+
+ # 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" WITH_STACK_PROTECTOR)
+ if (WITH_STACK_PROTECTOR)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector")
+ endif (WITH_STACK_PROTECTOR)
+
+ if (CMAKE_BUILD_TYPE)
+ string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER)
+ if (NOT CMAKE_BUILD_TYPE_LOWER MATCHES debug)
+ check_c_compiler_flag("-D_FORTIFY_SOURCE=2" WITH_FORTIFY_SOURCE)
+ if (WITH_FORTIFY_SOURCE)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -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)
--- /dev/null
+if (WIN32)
+ # Same same
+ set(BIN_INSTALL_DIR "bin" CACHE PATH "-")
+ set(SBIN_INSTALL_DIR "." CACHE PATH "-")
+ set(LIB_INSTALL_DIR "lib" CACHE PATH "-")
+ set(INCLUDE_INSTALL_DIR "include" CACHE PATH "-")
+ set(PLUGIN_INSTALL_DIR "plugins" CACHE PATH "-")
+ set(HTML_INSTALL_DIR "doc/HTML" CACHE PATH "-")
+ set(ICON_INSTALL_DIR "." CACHE PATH "-")
+ set(SOUND_INSTALL_DIR "." CACHE PATH "-")
+ set(LOCALE_INSTALL_DIR "lang" CACHE PATH "-")
+elseif (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(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)"
+ )
+endif ()
--- /dev/null
+# 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")
--- /dev/null
+# - MACRO_ENSURE_OUT_OF_SOURCE_BUILD(<errorMessage>)
+# MACRO_ENSURE_OUT_OF_SOURCE_BUILD(<errorMessage>)
+
+# Copyright (c) 2006, Alexander Neundorf, <neundorf@kde.org>
+#
+# 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)
--- /dev/null
+/* 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_GRP_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 `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
+
+/*************************** LIBRARIES ***************************/
+
+
+/**************************** OPTIONS ****************************/
+
+
+/*************************** 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
--- /dev/null
+#!/usr/bin/perl
+#
+
+use strict;
+
+use Getopt::Long;
+use Cwd qw(abs_path);
+
+my $opt_help = 0;
+my $opt_passwd_path = undef;
+my $opt_group_path = undef;
+my $opt_action = undef;
+my $opt_type = undef;
+my $opt_name = undef;
+my $opt_member = undef;
+my $opt_gid = 65534;# nogroup gid
+
+my $passwdfn = undef;
+my $groupfn = undef;
+my $memberfn = undef;
+my $actionfn = undef;
+
+sub passwd_add($$$$$);
+sub passwd_delete($$$$$);
+sub group_add($$$$$);
+sub group_delete($$$$$);
+sub member_add($$$$$);
+sub member_delete($$$$$);
+
+sub check_path($$);
+
+my $result = GetOptions(
+ 'help|h|?' => \$opt_help,
+ 'passwd_path=s' => \$opt_passwd_path,
+ 'group_path=s' => \$opt_group_path,
+ 'action=s' => \$opt_action,
+ 'type=s' => \$opt_type,
+ 'name=s' => \$opt_name,
+ 'member=s' => \$opt_member,
+ 'gid=i' => \$opt_gid
+);
+
+sub usage($;$)
+{
+ my ($ret, $msg) = @_;
+
+ print $msg."\n\n" if defined($msg);
+
+ print "usage:
+
+ --help|-h|-? Show this help.
+
+ --passwd_path <path> Path of the 'passwd' file.
+ --group_path <path> Path of the 'group' file.
+
+ --type <type> 'passwd', 'group' and 'member' are supported.
+
+ --action <action> 'add' or 'delete'.
+
+ --name <name> The name of the object.
+
+ --member <member> The name of the member.
+
+ --gid <gid> Primary Group ID for new users.
+";
+ exit($ret);
+}
+
+usage(1) if (not $result);
+
+usage(0) if ($opt_help);
+
+if (not defined($opt_action)) {
+ usage(1, "missing: --action [add|delete]");
+}
+if ($opt_action eq "add") {
+ $passwdfn = \&passwd_add;
+ $groupfn = \&group_add;
+ $memberfn = \&member_add;
+} elsif ($opt_action eq "delete") {
+ $passwdfn = \&passwd_delete;
+ $groupfn = \&group_delete;
+ $memberfn = \&member_delete;
+} else {
+ usage(1, "invalid: --action [add|delete]: '$opt_action'");
+}
+
+if (not defined($opt_type)) {
+ usage(1, "missing: --type [passwd|group|member]");
+}
+if ($opt_type eq "member" and not defined($opt_member)) {
+ usage(1, "missing: --member <member>");
+}
+my $opt_fullpath_passwd;
+my $opt_fullpath_group;
+if ($opt_type eq "passwd") {
+ $actionfn = $passwdfn;
+ $opt_fullpath_passwd = check_path($opt_passwd_path, $opt_type);
+} elsif ($opt_type eq "group") {
+ $actionfn = $groupfn;
+ $opt_fullpath_group = check_path($opt_group_path, $opt_type);
+} elsif ($opt_type eq "member") {
+ $actionfn = $memberfn;
+ $opt_fullpath_passwd = check_path($opt_passwd_path, "passwd");
+ $opt_fullpath_group = check_path($opt_group_path, "group");
+} else {
+ usage(1, "invalid: --type [passwd|group]: '$opt_type'")
+}
+
+if (not defined($opt_name)) {
+ usage(1, "missing: --name <name>");
+}
+if ($opt_name eq "") {
+ usage(1, "invalid: --name <name>");
+}
+
+exit $actionfn->($opt_fullpath_passwd, $opt_member, $opt_fullpath_group, $opt_name, $opt_gid);
+
+sub check_path($$)
+{
+ my ($path,$type) = @_;
+
+ if (not defined($path)) {
+ usage(1, "missing: --$type\_path <path>");
+ }
+ if ($path eq "" or $path eq "/") {
+ usage(1, "invalid: --$type\_path <path>: '$path'");
+ }
+ my $fullpath = abs_path($path);
+ if (not defined($fullpath)) {
+ usage(1, "invalid: --$type\_path <path>: '$path'");
+ }
+ return $fullpath;
+}
+
+sub passwd_add_entry($$);
+
+sub passwd_load($)
+{
+ my ($path) = @_;
+ my @lines;
+ my $passwd = undef;
+
+ open(PWD, "<$path") or die("Unable to open '$path' for read");
+ @lines = <PWD>;
+ close(PWD);
+
+ $passwd->{array} = ();
+ $passwd->{name} = {};
+ $passwd->{uid} = {};
+ $passwd->{path} = $path;
+
+ foreach my $line (@lines) {
+ passwd_add_entry($passwd, $line);
+ }
+
+ return $passwd;
+}
+
+sub group_add_entry($$);
+
+sub group_load($)
+{
+ my ($path) = @_;
+ my @lines;
+ my $group = undef;
+
+ open(GROUP, "<$path") or die("Unable to open '$path' for read");
+ @lines = <GROUP>;
+ close(GROUP);
+
+ $group->{array} = ();
+ $group->{name} = {};
+ $group->{gid} = {};
+ $group->{path} = $path;
+
+ foreach my $line (@lines) {
+ group_add_entry($group, $line);
+ }
+
+ return $group;
+}
+
+sub passwd_lookup_name($$)
+{
+ my ($passwd, $name) = @_;
+
+ return undef unless defined($passwd->{name}{$name});
+
+ return $passwd->{name}{$name};
+}
+
+sub group_lookup_name($$)
+{
+ my ($group, $name) = @_;
+
+ return undef unless defined($group->{name}{$name});
+
+ return $group->{name}{$name};
+}
+
+sub passwd_lookup_uid($$)
+{
+ my ($passwd, $uid) = @_;
+
+ return undef unless defined($passwd->{uid}{$uid});
+
+ return $passwd->{uid}{$uid};
+}
+
+sub group_lookup_gid($$)
+{
+ my ($group, $gid) = @_;
+
+ return undef unless defined($group->{gid}{$gid});
+
+ return $group->{gid}{$gid};
+}
+
+sub passwd_get_free_uid($)
+{
+ my ($passwd) = @_;
+ my $uid = 1000;
+
+ while (passwd_lookup_uid($passwd, $uid)) {
+ $uid++;
+ }
+
+ return $uid;
+}
+
+sub group_get_free_gid($)
+{
+ my ($group) = @_;
+ my $gid = 1000;
+
+ while (group_lookup_gid($group, $gid)) {
+ $gid++;
+ }
+
+ return $gid;
+}
+
+sub passwd_add_entry($$)
+{
+ my ($passwd, $str) = @_;
+
+ chomp $str;
+ my @e = split(':', $str);
+
+ push(@{$passwd->{array}}, \@e);
+ $passwd->{name}{$e[0]} = \@e;
+ $passwd->{uid}{$e[2]} = \@e;
+}
+
+sub group_add_entry($$)
+{
+ my ($group, $str) = @_;
+
+ chomp $str;
+ my @e = split(':', $str);
+
+ push(@{$group->{array}}, \@e);
+ $group->{name}{$e[0]} = \@e;
+ $group->{gid}{$e[2]} = \@e;
+}
+
+sub passwd_remove_entry($$)
+{
+ my ($passwd, $eref) = @_;
+
+ for (my $i = 0; defined($passwd->{array}[$i]); $i++) {
+ if ($eref == $passwd->{array}[$i]) {
+ $passwd->{array}[$i] = undef;
+ }
+ }
+
+ delete $passwd->{name}{${$eref}[0]};
+ delete $passwd->{uid}{${$eref}[2]};
+}
+
+sub group_remove_entry($$)
+{
+ my ($group, $eref) = @_;
+
+ for (my $i = 0; defined($group->{array}[$i]); $i++) {
+ if ($eref == $group->{array}[$i]) {
+ $group->{array}[$i] = undef;
+ }
+ }
+
+ delete $group->{name}{${$eref}[0]};
+ delete $group->{gid}{${$eref}[2]};
+}
+
+sub group_add_member($$$)
+{
+ my ($group, $eref, $username) = @_;
+
+ my @members;
+ my $str = @$eref[3] || undef;
+ if ($str) {
+ @members = split(",", $str);
+ }
+
+ foreach my $member (@members) {
+ if ($member and $member eq $username) {
+ die("account[$username] is already member of '@$eref[0]'");
+ }
+ }
+
+ push(@members, $username);
+
+ my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @members);
+
+ group_remove_entry($group, $eref);
+
+ group_add_entry($group, $gwent);
+}
+
+sub group_delete_member($$$)
+{
+ my ($group, $eref, $username) = @_;
+
+ my @members = undef;
+ my $str = @$eref[3] || undef;
+ if ($str) {
+ @members = split(",", $str);
+ }
+ my @new_members;
+ my $removed = 0;
+
+ foreach my $member (@members) {
+ if ($member and $member ne $username) {
+ push(@new_members, $member);
+ } else {
+ $removed = 1;
+ }
+ }
+
+ if ($removed != 1) {
+ die("account[$username] is not member of '@$eref[0]'");
+ }
+
+ my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @new_members);
+
+ group_remove_entry($group, $eref);
+
+ group_add_entry($group, $gwent);
+}
+
+sub passwd_save($)
+{
+ my ($passwd) = @_;
+ my @lines = ();
+ my $path = $passwd->{path};
+ my $tmppath = $path.$$;
+
+ foreach my $eref (@{$passwd->{array}}) {
+ next unless defined($eref);
+
+ my $line = join(':', @{$eref});
+ push(@lines, $line);
+ }
+
+ open(PWD, ">$tmppath") or die("Unable to open '$tmppath' for write");
+ print PWD join("\n", @lines)."\n";
+ close(PWD);
+ rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
+}
+
+sub group_save($)
+{
+ my ($group) = @_;
+ my @lines = ();
+ my $path = $group->{path};
+ my $tmppath = $path.$$;
+
+ foreach my $eref (@{$group->{array}}) {
+ next unless defined($eref);
+
+ my $line = join(':', @{$eref});
+ if (scalar(@{$eref}) == 3) {
+ $line .= ":";
+ }
+ push(@lines, $line);
+ }
+
+ open(GROUP, ">$tmppath") or die("Unable to open '$tmppath' for write");
+ print GROUP join("\n", @lines)."\n";
+ close(GROUP);
+ rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
+}
+
+sub passwd_add($$$$$)
+{
+ my ($path, $dummy, $dummy2, $name, $gid) = @_;
+
+ #print "passwd_add: '$name' in '$path'\n";
+
+ my $passwd = passwd_load($path);
+
+ my $e = passwd_lookup_name($passwd, $name);
+ die("account[$name] already exists in '$path'") if defined($e);
+
+ my $uid = passwd_get_free_uid($passwd);
+
+ my $pwent = $name.":x:".$uid.":".$gid.":".$name." gecos:/nodir:/bin/false";
+
+ passwd_add_entry($passwd, $pwent);
+
+ passwd_save($passwd);
+
+ return 0;
+}
+
+sub passwd_delete($$$$$)
+{
+ my ($path, $dummy, $dummy2, $name, $dummy3) = @_;
+
+ #print "passwd_delete: '$name' in '$path'\n";
+
+ my $passwd = passwd_load($path);
+
+ my $e = passwd_lookup_name($passwd, $name);
+ die("account[$name] does not exists in '$path'") unless defined($e);
+
+ passwd_remove_entry($passwd, $e);
+
+ passwd_save($passwd);
+
+ return 0;
+}
+
+sub group_add($$$$$)
+{
+ my ($dummy, $dummy2, $path, $name, $dummy3) = @_;
+
+ #print "group_add: '$name' in '$path'\n";
+
+ my $group = group_load($path);
+
+ my $e = group_lookup_name($group, $name);
+ die("group[$name] already exists in '$path'") if defined($e);
+
+ my $gid = group_get_free_gid($group);
+
+ my $gwent = $name.":x:".$gid.":"."";
+
+ group_add_entry($group, $gwent);
+
+ group_save($group);
+
+ #printf("%d\n", $gid);
+
+ return 0;
+}
+
+sub group_delete($$$$$)
+{
+ my ($dummy, $dummy2, $path, $name, $dummy3) = @_;
+
+ #print "group_delete: '$name' in '$path'\n";
+
+ my $group = group_load($path);
+
+ my $e = group_lookup_name($group, $name);
+ die("group[$name] does not exists in '$path'") unless defined($e);
+
+ group_remove_entry($group, $e);
+
+ group_save($group);
+
+ return 0;
+}
+
+sub member_add($$$$$)
+{
+ my ($passwd_path, $username, $group_path, $groupname, $dummy) = @_;
+
+ #print "member_add: adding '$username' in '$passwd_path' to '$groupname' in '$group_path'\n";
+
+ my $group = group_load($group_path);
+
+ my $g = group_lookup_name($group, $groupname);
+ die("group[$groupname] does not exists in '$group_path'") unless defined($g);
+
+ my $passwd = passwd_load($passwd_path);
+
+ my $u = passwd_lookup_name($passwd, $username);
+ die("account[$username] does not exists in '$passwd_path'") unless defined($u);
+
+ group_add_member($group, $g, $username);
+
+ group_save($group);
+
+ return 0;
+}
+
+sub member_delete($$$$$)
+{
+ my ($passwd_path, $username, $group_path, $groupname, $dummy) = @_;
+
+ #print "member_delete: removing '$username' in '$passwd_path' from '$groupname' in '$group_path'\n";
+
+ my $group = group_load($group_path);
+
+ my $g = group_lookup_name($group, $groupname);
+ die("group[$groupname] does not exists in '$group_path'") unless defined($g);
+
+ my $passwd = passwd_load($passwd_path);
+
+ my $u = passwd_lookup_name($passwd, $username);
+ die("account[$username] does not exists in '$passwd_path'") unless defined($u);
+
+ group_delete_member($group, $g, $username);
+
+ group_save($group);
+
+ return 0;
+}
--- /dev/null
+project(libnss_wrapper C)
+
+include_directories(${CMAKE_BINARY_DIR})
+add_library(nss_wrapper SHARED nss_wrapper.c)
+
+install(
+ TARGETS
+ nss_wrapper
+ RUNTIME DESTINATION ${BIN_INSTALL_DIR}
+ LIBRARY DESTINATION ${LIB_INSTALL_DIR}
+ ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
+)
--- /dev/null
+/*
+ * Copyright (C) Stefan Metzmacher 2007 <metze@samba.org>
+ * Copyright (C) Guenther Deschner 2009 <gd@samba.org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <pwd.h>
+#include <grp.h>
+#include <nss.h>
+
+#include "nss_wrapper.h"
+
+#include <dlfcn.h>
+
+typedef enum nss_status NSS_STATUS;
+
+/* defining this gives us the posix getpwnam_r() calls on solaris
+ Thanks to heimdal for this */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+#define _POSIX_PTHREAD_SEMANTICS
+#endif
+
+#ifndef PTR_DIFF
+#define PTR_DIFF(p1, p2) ((ptrdiff_t)(((const char *)(p1)) - (const char *)(p2)))
+#endif
+
+#ifndef _PUBLIC_
+#define _PUBLIC_
+#endif
+
+/* not all systems have _r functions... */
+#ifndef HAVE_GETPWNAM_R
+#define getpwnam_r(name, pwdst, buf, buflen, pwdstp) ENOSYS
+#endif
+#ifndef HAVE_GETPWUID_R
+#define getpwuid_r(uid, pwdst, buf, buflen, pwdstp) ENOSYS
+#endif
+#ifndef HAVE_GETPWENT_R
+#define getpwent_r(pwdst, buf, buflen, pwdstp) ENOSYS
+#endif
+#ifndef HAVE_GETGRNAM_R
+#define getgrnam_r(name, grdst, buf, buflen, grdstp) ENOSYS
+#endif
+#ifndef HAVE_GETGRGID_R
+#define getgrgid_r(gid, grdst, buf, buflen, grdstp) ENOSYS
+#endif
+#ifndef HAVE_GETGRENT_R
+#define getgrent_r(grdst, buf, buflen, grdstp) ENOSYS
+#endif
+
+/* Not all systems have getgrouplist */
+#ifndef HAVE_GETGROUPLIST
+#define getgrouplist(user, group, groups, ngroups) 0
+#endif
+
+#if 0
+# ifdef DEBUG
+# define NWRAP_ERROR(args) DEBUG(0, args)
+# else
+# define NWRAP_ERROR(args) printf args
+# endif
+#else
+#define NWRAP_ERROR(args)
+#endif
+
+#if 0
+# ifdef DEBUG
+# define NWRAP_DEBUG(args) DEBUG(0, args)
+# else
+# define NWRAP_DEBUG(args) printf args
+# endif
+#else
+#define NWRAP_DEBUG(args)
+#endif
+
+#if 0
+# ifdef DEBUG
+# define NWRAP_VERBOSE(args) DEBUG(0, args)
+# else
+# define NWRAP_VERBOSE(args) printf args
+# endif
+#else
+#define NWRAP_VERBOSE(args)
+#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);
+};
+
+/* 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_main {
+ const char *nwrap_switch;
+ int num_backends;
+ struct nwrap_backend *backends;
+};
+
+struct nwrap_main *nwrap_main_global;
+struct nwrap_main __nwrap_main_global;
+
+struct nwrap_cache {
+ const char *path;
+ int fd;
+ struct stat st;
+ uint8_t *buf;
+ void *private_data;
+ bool (*parse_line)(struct nwrap_cache *, char *line);
+ void (*unload)(struct nwrap_cache *);
+};
+
+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);
+
+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;
+
+static bool nwrap_gr_parse_line(struct nwrap_cache *nwrap, char *line);
+static void nwrap_gr_unload(struct nwrap_cache *nwrap);
+
+static void *nwrap_load_module_fn(struct nwrap_backend *b,
+ const char *fn_name)
+{
+ void *res;
+ char *s;
+
+ if (!b->so_handle) {
+ NWRAP_ERROR(("%s: no handle\n",
+ __location__));
+ return NULL;
+ }
+
+ if (asprintf(&s, "_nss_%s_%s", b->name, fn_name) == -1) {
+ NWRAP_ERROR(("%s: out of memory\n",
+ __location__));
+ return NULL;
+ }
+
+ res = dlsym(b->so_handle, s);
+ if (!res) {
+ NWRAP_ERROR(("%s: cannot find function %s in %s\n",
+ __location__, s, b->so_path));
+ }
+ free(s);
+ s = NULL;
+ 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;
+ }
+
+ fns->_nss_getpwnam_r = (NSS_STATUS (*)(const char *, struct passwd *, char *, size_t, int *))
+ nwrap_load_module_fn(b, "getpwnam_r");
+ fns->_nss_getpwuid_r = (NSS_STATUS (*)(uid_t, struct passwd *, char *, size_t, int *))
+ nwrap_load_module_fn(b, "getpwuid_r");
+ fns->_nss_setpwent = (NSS_STATUS(*)(void))
+ nwrap_load_module_fn(b, "setpwent");
+ fns->_nss_getpwent_r = (NSS_STATUS (*)(struct passwd *, char *, size_t, int *))
+ nwrap_load_module_fn(b, "getpwent_r");
+ fns->_nss_endpwent = (NSS_STATUS(*)(void))
+ nwrap_load_module_fn(b, "endpwent");
+ fns->_nss_initgroups = (NSS_STATUS (*)(const char *, gid_t, long int *, long int *, gid_t **, long int, int *))
+ nwrap_load_module_fn(b, "initgroups_dyn");
+ fns->_nss_getgrnam_r = (NSS_STATUS (*)(const char *, struct group *, char *, size_t, int *))
+ nwrap_load_module_fn(b, "getgrnam_r");
+ fns->_nss_getgrgid_r = (NSS_STATUS (*)(gid_t, struct group *, char *, size_t, int *))
+ nwrap_load_module_fn(b, "getgrgid_r");
+ fns->_nss_setgrent = (NSS_STATUS(*)(void))
+ nwrap_load_module_fn(b, "setgrent");
+ fns->_nss_getgrent_r = (NSS_STATUS (*)(struct group *, char *, size_t, int *))
+ nwrap_load_module_fn(b, "getgrent_r");
+ fns->_nss_endgrent = (NSS_STATUS(*)(void))
+ 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_ERROR(("%s: cannot open shared library %s\n",
+ __location__, 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_ERROR(("%s: out of memory\n",
+ __location__));
+ 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_backend_init(struct nwrap_main *r)
+{
+ const char *winbind_so_path = getenv("NSS_WRAPPER_WINBIND_SO_PATH");
+
+ r->num_backends = 0;
+ r->backends = NULL;
+
+ if (!nwrap_module_init("files", &nwrap_files_ops, NULL,
+ &r->num_backends,
+ &r->backends)) {
+ NWRAP_ERROR(("%s: failed to initialize 'files' backend\n",
+ __location__));
+ return;
+ }
+
+ if (winbind_so_path && strlen(winbind_so_path)) {
+ if (!nwrap_module_init("winbind", &nwrap_module_ops, winbind_so_path,
+ &r->num_backends,
+ &r->backends)) {
+ NWRAP_ERROR(("%s: failed to initialize 'winbind' backend\n",
+ __location__));
+ return;
+ }
+ }
+}
+
+static void nwrap_init(void)
+{
+ static bool initialized;
+
+ if (initialized) return;
+ initialized = true;
+
+ nwrap_main_global = &__nwrap_main_global;
+
+ nwrap_backend_init(nwrap_main_global);
+
+ nwrap_pw_global.cache = &__nwrap_cache_pw;
+
+ nwrap_pw_global.cache->path = getenv("NSS_WRAPPER_PASSWD");
+ 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;
+
+ nwrap_gr_global.cache = &__nwrap_cache_gr;
+
+ nwrap_gr_global.cache->path = getenv("NSS_WRAPPER_GROUP");
+ 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;
+}
+
+static bool nwrap_enabled(void)
+{
+ nwrap_init();
+
+ if (!nwrap_pw_global.cache->path) {
+ return false;
+ }
+ if (nwrap_pw_global.cache->path[0] == '\0') {
+ return false;
+ }
+ if (!nwrap_gr_global.cache->path) {
+ return false;
+ }
+ if (nwrap_gr_global.cache->path[0] == '\0') {
+ return false;
+ }
+
+ return true;
+}
+
+static bool nwrap_parse_file(struct nwrap_cache *nwrap)
+{
+ int ret;
+ uint8_t *buf = NULL;
+ char *nline;
+
+ if (nwrap->st.st_size == 0) {
+ NWRAP_DEBUG(("%s: size == 0\n",
+ __location__));
+ goto done;
+ }
+
+ if (nwrap->st.st_size > INT32_MAX) {
+ NWRAP_ERROR(("%s: size[%u] larger than INT32_MAX\n",
+ __location__, (unsigned)nwrap->st.st_size));
+ goto failed;
+ }
+
+ ret = lseek(nwrap->fd, 0, SEEK_SET);
+ if (ret != 0) {
+ NWRAP_ERROR(("%s: lseek - %d\n",__location__,ret));
+ goto failed;
+ }
+
+ buf = (uint8_t *)malloc(nwrap->st.st_size + 1);
+ if (!buf) {
+ NWRAP_ERROR(("%s: malloc failed\n",__location__));
+ goto failed;
+ }
+
+ ret = read(nwrap->fd, buf, nwrap->st.st_size);
+ if (ret != nwrap->st.st_size) {
+ NWRAP_ERROR(("%s: read(%u) gave %d\n",
+ __location__, (unsigned)nwrap->st.st_size, ret));
+ goto failed;
+ }
+
+ buf[nwrap->st.st_size] = '\0';
+
+ nline = (char *)buf;
+ while (nline && nline[0]) {
+ char *line;
+ char *e;
+ bool ok;
+
+ line = nline;
+ nline = NULL;
+
+ e = strchr(line, '\n');
+ if (e) {
+ e[0] = '\0';
+ e++;
+ if (e[0] == '\r') {
+ e[0] = '\0';
+ e++;
+ }
+ nline = e;
+ }
+
+ NWRAP_VERBOSE(("%s:'%s'\n",__location__, line));
+
+ if (strlen(line) == 0) {
+ continue;
+ }
+
+ ok = nwrap->parse_line(nwrap, line);
+ if (!ok) {
+ goto failed;
+ }
+ }
+
+done:
+ nwrap->buf = buf;
+ return true;
+
+failed:
+ if (buf) free(buf);
+ return false;
+}
+
+static void nwrap_files_cache_unload(struct nwrap_cache *nwrap)
+{
+ nwrap->unload(nwrap);
+
+ if (nwrap->buf) free(nwrap->buf);
+
+ nwrap->buf = NULL;
+}
+
+static void nwrap_files_cache_reload(struct nwrap_cache *nwrap)
+{
+ struct stat st;
+ int ret;
+ bool ok;
+ bool retried = false;
+
+reopen:
+ if (nwrap->fd < 0) {
+ nwrap->fd = open(nwrap->path, O_RDONLY);
+ if (nwrap->fd < 0) {
+ NWRAP_ERROR(("%s: unable to open '%s' readonly %d:%s\n",
+ __location__,
+ nwrap->path, nwrap->fd,
+ strerror(errno)));
+ return;
+ }
+ NWRAP_VERBOSE(("%s: open '%s'\n", __location__, nwrap->path));
+ }
+
+ ret = fstat(nwrap->fd, &st);
+ if (ret != 0) {
+ NWRAP_ERROR(("%s: fstat(%s) - %d:%s\n",
+ __location__,
+ nwrap->path,
+ ret, strerror(errno)));
+ return;
+ }
+
+ if (retried == false && st.st_nlink == 0) {
+ /* maybe someone has replaced the file... */
+ NWRAP_DEBUG(("%s: st_nlink == 0, reopen %s\n",
+ __location__, nwrap->path));
+ retried = true;
+ memset(&nwrap->st, 0, sizeof(nwrap->st));
+ close(nwrap->fd);
+ nwrap->fd = -1;
+ goto reopen;
+ }
+
+ if (st.st_mtime == nwrap->st.st_mtime) {
+ NWRAP_VERBOSE(("%s: st_mtime[%u] hasn't changed, skip reload\n",
+ __location__, (unsigned)st.st_mtime));
+ return;
+ }
+ NWRAP_DEBUG(("%s: st_mtime has changed [%u] => [%u], start reload\n",
+ __location__, (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_ERROR(("%s: failed to reload %s\n",
+ __location__, nwrap->path));
+ nwrap_files_cache_unload(nwrap);
+ }
+ NWRAP_DEBUG(("%s: reloaded %s\n",
+ __location__, nwrap->path));
+}
+
+/*
+ * 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_ERROR(("%s:realloc(%u) failed\n",
+ __location__, list_size));
+ return false;
+ }
+ nwrap_pw->list = pw;
+
+ pw = &nwrap_pw->list[nwrap_pw->num];
+
+ c = line;
+
+ /* name */
+ p = strchr(c, ':');
+ if (!p) {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
+ __location__, line, c));
+ return false;
+ }
+ *p = '\0';
+ p++;
+ pw->pw_name = c;
+ c = p;
+
+ NWRAP_VERBOSE(("name[%s]\n", pw->pw_name));
+
+ /* password */
+ p = strchr(c, ':');
+ if (!p) {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
+ __location__, line, c));
+ return false;
+ }
+ *p = '\0';
+ p++;
+ pw->pw_passwd = c;
+ c = p;
+
+ NWRAP_VERBOSE(("password[%s]\n", pw->pw_passwd));
+
+ /* uid */
+ p = strchr(c, ':');
+ if (!p) {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
+ __location__, line, c));
+ return false;
+ }
+ *p = '\0';
+ p++;
+ e = NULL;
+ pw->pw_uid = (uid_t)strtoul(c, &e, 10);
+ if (c == e) {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
+ __location__, line, c, strerror(errno)));
+ return false;
+ }
+ if (e == NULL) {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
+ __location__, line, c, strerror(errno)));
+ return false;
+ }
+ if (e[0] != '\0') {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
+ __location__, line, c, strerror(errno)));
+ return false;
+ }
+ c = p;
+
+ NWRAP_VERBOSE(("uid[%u]\n", pw->pw_uid));
+
+ /* gid */
+ p = strchr(c, ':');
+ if (!p) {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
+ __location__, line, c));
+ return false;
+ }
+ *p = '\0';
+ p++;
+ e = NULL;
+ pw->pw_gid = (gid_t)strtoul(c, &e, 10);
+ if (c == e) {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
+ __location__, line, c, strerror(errno)));
+ return false;
+ }
+ if (e == NULL) {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
+ __location__, line, c, strerror(errno)));
+ return false;
+ }
+ if (e[0] != '\0') {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
+ __location__, line, c, strerror(errno)));
+ return false;
+ }
+ c = p;
+
+ NWRAP_VERBOSE(("gid[%u]\n", pw->pw_gid));
+
+ /* gecos */
+ p = strchr(c, ':');
+ if (!p) {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
+ __location__, line, c));
+ return false;
+ }
+ *p = '\0';
+ p++;
+ pw->pw_gecos = c;
+ c = p;
+
+ NWRAP_VERBOSE(("gecos[%s]\n", pw->pw_gecos));
+
+ /* dir */
+ p = strchr(c, ':');
+ if (!p) {
+ NWRAP_ERROR(("%s:'%s'\n",__location__,c));
+ return false;
+ }
+ *p = '\0';
+ p++;
+ pw->pw_dir = c;
+ c = p;
+
+ NWRAP_VERBOSE(("dir[%s]\n", pw->pw_dir));
+
+ /* shell */
+ pw->pw_shell = c;
+ NWRAP_VERBOSE(("shell[%s]\n", pw->pw_shell));
+
+ NWRAP_DEBUG(("add user[%s:%s:%u:%u:%s:%s:%s]\n",
+ 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;
+
+ if (nwrap_pw->list) free(nwrap_pw->list);
+
+ nwrap_pw->list = NULL;
+ 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 > 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;
+}
+
+/*
+ * 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_ERROR(("%s:realloc failed\n",__location__));
+ return false;
+ }
+ nwrap_gr->list = gr;
+
+ gr = &nwrap_gr->list[nwrap_gr->num];
+
+ c = line;
+
+ /* name */
+ p = strchr(c, ':');
+ if (!p) {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
+ __location__, line, c));
+ return false;
+ }
+ *p = '\0';
+ p++;
+ gr->gr_name = c;
+ c = p;
+
+ NWRAP_VERBOSE(("name[%s]\n", gr->gr_name));
+
+ /* password */
+ p = strchr(c, ':');
+ if (!p) {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
+ __location__, line, c));
+ return false;
+ }
+ *p = '\0';
+ p++;
+ gr->gr_passwd = c;
+ c = p;
+
+ NWRAP_VERBOSE(("password[%s]\n", gr->gr_passwd));
+
+ /* gid */
+ p = strchr(c, ':');
+ if (!p) {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
+ __location__, line, c));
+ return false;
+ }
+ *p = '\0';
+ p++;
+ e = NULL;
+ gr->gr_gid = (gid_t)strtoul(c, &e, 10);
+ if (c == e) {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
+ __location__, line, c, strerror(errno)));
+ return false;
+ }
+ if (e == NULL) {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
+ __location__, line, c, strerror(errno)));
+ return false;
+ }
+ if (e[0] != '\0') {
+ NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
+ __location__, line, c, strerror(errno)));
+ return false;
+ }
+ c = p;
+
+ NWRAP_VERBOSE(("gid[%u]\n", gr->gr_gid));
+
+ /* members */
+ gr->gr_mem = (char **)malloc(sizeof(char *));
+ if (!gr->gr_mem) {
+ NWRAP_ERROR(("%s:calloc failed\n",__location__));
+ 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_ERROR(("%s:realloc(%u) failed\n",
+ __location__, m_size));
+ return false;
+ }
+ gr->gr_mem = m;
+ gr->gr_mem[nummem] = c;
+ gr->gr_mem[nummem+1] = NULL;
+
+ NWRAP_VERBOSE(("member[%u]: '%s'\n", nummem, gr->gr_mem[nummem]));
+ }
+
+ NWRAP_DEBUG(("add group[%s:%s:%u:] with %u members\n",
+ 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++) {
+ if (nwrap_gr->list[i].gr_mem) {
+ free(nwrap_gr->list[i].gr_mem);
+ }
+ }
+ free(nwrap_gr->list);
+ }
+
+ nwrap_gr->list = NULL;
+ 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) > 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;
+}
+
+/* user functions */
+static struct passwd *nwrap_files_getpwnam(struct nwrap_backend *b,
+ const char *name)
+{
+ int i;
+
+ nwrap_files_cache_reload(nwrap_pw_global.cache);
+
+ for (i=0; i<nwrap_pw_global.num; i++) {
+ if (strcmp(nwrap_pw_global.list[i].pw_name, name) == 0) {
+ NWRAP_DEBUG(("%s: user[%s] found\n",
+ __location__, name));
+ return &nwrap_pw_global.list[i];
+ }
+ NWRAP_VERBOSE(("%s: user[%s] does not match [%s]\n",
+ __location__, name,
+ nwrap_pw_global.list[i].pw_name));
+ }
+
+ NWRAP_DEBUG(("%s: user[%s] not found\n", __location__, name));
+
+ errno = ENOENT;
+ return NULL;
+}
+
+static int nwrap_files_getpwnam_r(struct nwrap_backend *b,
+ const char *name, struct passwd *pwdst,
+ char *buf, size_t buflen, struct passwd **pwdstp)
+{
+ struct passwd *pw;
+
+ pw = nwrap_files_getpwnam(b, name);
+ if (!pw) {
+ if (errno == 0) {
+ return ENOENT;
+ }
+ return errno;
+ }
+
+ return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
+}
+
+static struct passwd *nwrap_files_getpwuid(struct nwrap_backend *b,
+ uid_t uid)
+{
+ int i;
+
+ nwrap_files_cache_reload(nwrap_pw_global.cache);
+
+ for (i=0; i<nwrap_pw_global.num; i++) {
+ if (nwrap_pw_global.list[i].pw_uid == uid) {
+ NWRAP_DEBUG(("%s: uid[%u] found\n",
+ __location__, uid));
+ return &nwrap_pw_global.list[i];
+ }
+ NWRAP_VERBOSE(("%s: uid[%u] does not match [%u]\n",
+ __location__, uid,
+ nwrap_pw_global.list[i].pw_uid));
+ }
+
+ NWRAP_DEBUG(("%s: uid[%u] not found\n", __location__, uid));
+
+ errno = ENOENT;
+ return NULL;
+}
+
+static int nwrap_files_getpwuid_r(struct nwrap_backend *b,
+ uid_t uid, struct passwd *pwdst,
+ char *buf, size_t buflen, struct passwd **pwdstp)
+{
+ struct passwd *pw;
+
+ pw = nwrap_files_getpwuid(b, uid);
+ if (!pw) {
+ if (errno == 0) {
+ return ENOENT;
+ }
+ return errno;
+ }
+
+ return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
+}
+
+/* user enum functions */
+static void nwrap_files_setpwent(struct nwrap_backend *b)
+{
+ nwrap_pw_global.idx = 0;
+}
+
+static struct passwd *nwrap_files_getpwent(struct nwrap_backend *b)
+{
+ struct passwd *pw;
+
+ if (nwrap_pw_global.idx == 0) {
+ nwrap_files_cache_reload(nwrap_pw_global.cache);
+ }
+
+ if (nwrap_pw_global.idx >= nwrap_pw_global.num) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ pw = &nwrap_pw_global.list[nwrap_pw_global.idx++];
+
+ NWRAP_VERBOSE(("%s: return user[%s] uid[%u]\n",
+ __location__, 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)
+{
+ nwrap_pw_global.idx = 0;
+}
+
+/* misc functions */
+static int nwrap_files_initgroups(struct nwrap_backend *b,
+ const char *user, gid_t group)
+{
+ /* TODO: maybe we should also fake this... */
+ return EPERM;
+}
+
+/* group functions */
+static struct group *nwrap_files_getgrnam(struct nwrap_backend *b,
+ const char *name)
+{
+ int i;
+
+ nwrap_files_cache_reload(nwrap_gr_global.cache);
+
+ for (i=0; i<nwrap_gr_global.num; i++) {
+ if (strcmp(nwrap_gr_global.list[i].gr_name, name) == 0) {
+ NWRAP_DEBUG(("%s: group[%s] found\n",
+ __location__, name));
+ return &nwrap_gr_global.list[i];
+ }
+ NWRAP_VERBOSE(("%s: group[%s] does not match [%s]\n",
+ __location__, name,
+ nwrap_gr_global.list[i].gr_name));
+ }
+
+ NWRAP_DEBUG(("%s: group[%s] not found\n", __location__, name));
+
+ errno = ENOENT;
+ return NULL;
+}
+
+static int nwrap_files_getgrnam_r(struct nwrap_backend *b,
+ const char *name, struct group *grdst,
+ char *buf, size_t buflen, struct group **grdstp)
+{
+ struct group *gr;
+
+ gr = nwrap_files_getgrnam(b, name);
+ if (!gr) {
+ if (errno == 0) {
+ return ENOENT;
+ }
+ return errno;
+ }
+
+ return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
+}
+
+static struct group *nwrap_files_getgrgid(struct nwrap_backend *b,
+ gid_t gid)
+{
+ int i;
+
+ nwrap_files_cache_reload(nwrap_gr_global.cache);
+
+ for (i=0; i<nwrap_gr_global.num; i++) {
+ if (nwrap_gr_global.list[i].gr_gid == gid) {
+ NWRAP_DEBUG(("%s: gid[%u] found\n",
+ __location__, gid));
+ return &nwrap_gr_global.list[i];
+ }
+ NWRAP_VERBOSE(("%s: gid[%u] does not match [%u]\n",
+ __location__, gid,
+ nwrap_gr_global.list[i].gr_gid));
+ }
+
+ NWRAP_DEBUG(("%s: gid[%u] not found\n", __location__, gid));
+
+ errno = ENOENT;
+ return NULL;
+}
+
+static int nwrap_files_getgrgid_r(struct nwrap_backend *b,
+ gid_t gid, struct group *grdst,
+ char *buf, size_t buflen, struct group **grdstp)
+{
+ struct group *gr;
+
+ gr = nwrap_files_getgrgid(b, gid);
+ if (!gr) {
+ if (errno == 0) {
+ return ENOENT;
+ }
+ return errno;
+ }
+
+ return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
+}
+
+/* group enum functions */
+static void nwrap_files_setgrent(struct nwrap_backend *b)
+{
+ nwrap_gr_global.idx = 0;
+}
+
+static struct group *nwrap_files_getgrent(struct nwrap_backend *b)
+{
+ struct group *gr;
+
+ if (nwrap_gr_global.idx == 0) {
+ nwrap_files_cache_reload(nwrap_gr_global.cache);
+ }
+
+ if (nwrap_gr_global.idx >= nwrap_gr_global.num) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ gr = &nwrap_gr_global.list[nwrap_gr_global.idx++];
+
+ NWRAP_VERBOSE(("%s: return group[%s] gid[%u]\n",
+ __location__, 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)
+{
+ nwrap_gr_global.idx = 0;
+}
+
+/*
+ * module backend
+ */
+
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0)
+#endif
+
+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;
+
+ 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:
+ 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;
+
+ 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:
+ 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;
+
+ if (!b->fns->_nss_getpwent_r) {
+ return ENOENT;
+ }
+
+ ret = b->fns->_nss_getpwent_r(pwdst, buf, buflen, &errno);
+ switch (ret) {
+ case NSS_STATUS_SUCCESS:
+ 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;
+
+ 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:
+ 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;
+
+ 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:
+ 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;
+
+ if (!b->fns->_nss_getgrent_r) {
+ return ENOENT;
+ }
+
+ ret = b->fns->_nss_getgrent_r(grdst, buf, buflen, &errno);
+ switch (ret) {
+ case NSS_STATUS_SUCCESS:
+ 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();
+}
+
+/*
+ * PUBLIC interface
+ */
+
+_PUBLIC_ struct passwd *nwrap_getpwnam(const char *name)
+{
+ int i;
+ struct passwd *pwd;
+
+ if (!nwrap_enabled()) {
+ return real_getpwnam(name);
+ }
+
+ 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;
+}
+
+_PUBLIC_ int nwrap_getpwnam_r(const char *name, struct passwd *pwdst,
+ char *buf, size_t buflen, struct passwd **pwdstp)
+{
+ int i,ret;
+
+ if (!nwrap_enabled()) {
+ return real_getpwnam_r(name, pwdst, buf, buflen, pwdstp);
+ }
+
+ 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;
+}
+
+_PUBLIC_ struct passwd *nwrap_getpwuid(uid_t uid)
+{
+ int i;
+ struct passwd *pwd;
+
+ if (!nwrap_enabled()) {
+ return real_getpwuid(uid);
+ }
+
+ 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;
+}
+
+_PUBLIC_ int nwrap_getpwuid_r(uid_t uid, struct passwd *pwdst,
+ char *buf, size_t buflen, struct passwd **pwdstp)
+{
+ int i,ret;
+
+ if (!nwrap_enabled()) {
+ return real_getpwuid_r(uid, pwdst, buf, buflen, pwdstp);
+ }
+
+ 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;
+}
+
+_PUBLIC_ void nwrap_setpwent(void)
+{
+ int i;
+
+ if (!nwrap_enabled()) {
+ real_setpwent();
+ return;
+ }
+
+ for (i=0; i < nwrap_main_global->num_backends; i++) {
+ struct nwrap_backend *b = &nwrap_main_global->backends[i];
+ b->ops->nw_setpwent(b);
+ }
+}
+
+_PUBLIC_ struct passwd *nwrap_getpwent(void)
+{
+ int i;
+ struct passwd *pwd;
+
+ if (!nwrap_enabled()) {
+ return real_getpwent();
+ }
+
+ 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;
+}
+
+_PUBLIC_ int nwrap_getpwent_r(struct passwd *pwdst, char *buf,
+ size_t buflen, struct passwd **pwdstp)
+{
+ int i,ret;
+
+ if (!nwrap_enabled()) {
+#ifdef SOLARIS_GETPWENT_R
+ struct passwd *pw;
+ pw = real_getpwent_r(pwdst, buf, buflen);
+ if (!pw) {
+ if (errno == 0) {
+ return ENOENT;
+ }
+ return errno;
+ }
+ if (pwdstp) {
+ *pwdstp = pw;
+ }
+ return 0;
+#else
+ return real_getpwent_r(pwdst, buf, buflen, pwdstp);
+#endif
+ }
+
+ 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;
+}
+
+_PUBLIC_ void nwrap_endpwent(void)
+{
+ int i;
+
+ if (!nwrap_enabled()) {
+ real_endpwent();
+ return;
+ }
+
+ for (i=0; i < nwrap_main_global->num_backends; i++) {
+ struct nwrap_backend *b = &nwrap_main_global->backends[i];
+ b->ops->nw_endpwent(b);
+ }
+}
+
+_PUBLIC_ int nwrap_initgroups(const char *user, gid_t group)
+{
+ int i;
+
+ if (!nwrap_enabled()) {
+ return real_initgroups(user, group);
+ }
+
+ for (i=0; i < nwrap_main_global->num_backends; i++) {
+ struct nwrap_backend *b = &nwrap_main_global->backends[i];
+ return b->ops->nw_initgroups(b, user, group);
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+_PUBLIC_ struct group *nwrap_getgrnam(const char *name)
+{
+ int i;
+ struct group *grp;
+
+ if (!nwrap_enabled()) {
+ return real_getgrnam(name);
+ }
+
+ 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;
+}
+
+_PUBLIC_ int nwrap_getgrnam_r(const char *name, struct group *grdst,
+ char *buf, size_t buflen, struct group **grdstp)
+{
+ int i,ret;
+
+ if (!nwrap_enabled()) {
+ return real_getgrnam_r(name, grdst, buf, buflen, grdstp);
+ }
+
+ 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;
+}
+
+_PUBLIC_ struct group *nwrap_getgrgid(gid_t gid)
+{
+ int i;
+ struct group *grp;
+
+ if (!nwrap_enabled()) {
+ return real_getgrgid(gid);
+ }
+
+ 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;
+}
+
+_PUBLIC_ int nwrap_getgrgid_r(gid_t gid, struct group *grdst,
+ char *buf, size_t buflen, struct group **grdstp)
+{
+ int i,ret;
+
+ if (!nwrap_enabled()) {
+ return real_getgrgid_r(gid, grdst, buf, buflen, grdstp);
+ }
+
+ 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;
+}
+
+_PUBLIC_ void nwrap_setgrent(void)
+{
+ int i;
+
+ if (!nwrap_enabled()) {
+ real_setgrent();
+ return;
+ }
+
+ for (i=0; i < nwrap_main_global->num_backends; i++) {
+ struct nwrap_backend *b = &nwrap_main_global->backends[i];
+ b->ops->nw_setgrent(b);
+ }
+}
+
+_PUBLIC_ struct group *nwrap_getgrent(void)
+{
+ int i;
+ struct group *grp;
+
+ if (!nwrap_enabled()) {
+ return real_getgrent();
+ }
+
+ 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;
+}
+
+_PUBLIC_ int nwrap_getgrent_r(struct group *grdst, char *buf,
+ size_t buflen, struct group **grdstp)
+{
+ int i,ret;
+
+ if (!nwrap_enabled()) {
+#ifdef SOLARIS_GETGRENT_R
+ struct group *gr;
+ gr = real_getgrent_r(grdst, buf, buflen);
+ if (!gr) {
+ if (errno == 0) {
+ return ENOENT;
+ }
+ return errno;
+ }
+ if (grdstp) {
+ *grdstp = gr;
+ }
+ return 0;
+#else
+ return real_getgrent_r(grdst, buf, buflen, grdstp);
+#endif
+ }
+
+ 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;
+}
+
+_PUBLIC_ void nwrap_endgrent(void)
+{
+ int i;
+
+ if (!nwrap_enabled()) {
+ real_endgrent();
+ return;
+ }
+
+ for (i=0; i < nwrap_main_global->num_backends; i++) {
+ struct nwrap_backend *b = &nwrap_main_global->backends[i];
+ b->ops->nw_endgrent(b);
+ }
+}
+
+_PUBLIC_ int nwrap_getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups)
+{
+ struct group *grp;
+ gid_t *groups_tmp;
+ int count = 1;
+ const char *name_of_group = "";
+
+ if (!nwrap_enabled()) {
+ return real_getgrouplist(user, group, groups, ngroups);
+ }
+
+ NWRAP_DEBUG(("%s: getgrouplist called for %s\n", __location__, user));
+
+ groups_tmp = (gid_t *)malloc(count * sizeof(gid_t));
+ if (!groups_tmp) {
+ NWRAP_ERROR(("%s:calloc failed\n",__location__));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ memcpy(groups_tmp, &group, sizeof(gid_t));
+
+ grp = nwrap_getgrgid(group);
+ if (grp) {
+ name_of_group = grp->gr_name;
+ }
+
+ nwrap_setgrent();
+ while ((grp = nwrap_getgrent()) != NULL) {
+ int i = 0;
+
+ NWRAP_VERBOSE(("%s: inspecting %s for group membership\n",
+ __location__, grp->gr_name));
+
+ for (i=0; grp->gr_mem && grp->gr_mem[i] != NULL; i++) {
+
+ if ((strcmp(user, grp->gr_mem[i]) == 0) &&
+ (strcmp(name_of_group, grp->gr_name) != 0)) {
+
+ NWRAP_DEBUG(("%s: %s is member of %s\n",
+ __location__, user, grp->gr_name));
+
+ groups_tmp = (gid_t *)realloc(groups_tmp, (count + 1) * sizeof(gid_t));
+ if (!groups_tmp) {
+ NWRAP_ERROR(("%s:calloc failed\n",__location__));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ memcpy(&groups_tmp[count], &grp->gr_gid, sizeof(gid_t));
+ count++;
+ }
+ }
+ }
+
+ nwrap_endgrent();
+
+ NWRAP_VERBOSE(("%s: %s is member of %d groups: %d\n",
+ __location__, 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;
+}
--- /dev/null
+/*
+ * Copyright (C) Stefan Metzmacher 2007 <metze@samba.org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __NSS_WRAPPER_H__
+#define __NSS_WRAPPER_H__
+
+struct passwd *nwrap_getpwnam(const char *name);
+int nwrap_getpwnam_r(const char *name, struct passwd *pwbuf,
+ char *buf, size_t buflen, struct passwd **pwbufp);
+struct passwd *nwrap_getpwuid(uid_t uid);
+int nwrap_getpwuid_r(uid_t uid, struct passwd *pwbuf,
+ char *buf, size_t buflen, struct passwd **pwbufp);
+void nwrap_setpwent(void);
+struct passwd *nwrap_getpwent(void);
+int nwrap_getpwent_r(struct passwd *pwbuf, char *buf,
+ size_t buflen, struct passwd **pwbufp);
+void nwrap_endpwent(void);
+int nwrap_initgroups(const char *user, gid_t group);
+int nwrap_getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups);
+struct group *nwrap_getgrnam(const char *name);
+int nwrap_getgrnam_r(const char *name, struct group *gbuf,
+ char *buf, size_t buflen, struct group **gbufp);
+struct group *nwrap_getgrgid(gid_t gid);
+int nwrap_getgrgid_r(gid_t gid, struct group *gbuf,
+ char *buf, size_t buflen, struct group **gbufp);
+void nwrap_setgrent(void);
+struct group *nwrap_getgrent(void);
+int nwrap_getgrent_r(struct group *gbuf, char *buf,
+ size_t buflen, struct group **gbufp);
+void nwrap_endgrent(void);
+
+#ifdef NSS_WRAPPER_REPLACE
+
+#ifdef getpwnam
+#undef getpwnam
+#endif
+#define getpwnam nwrap_getpwnam
+
+#ifdef getpwnam_r
+#undef getpwnam_r
+#endif
+#define getpwnam_r nwrap_getpwnam_r
+
+#ifdef getpwuid
+#undef getpwuid
+#endif
+#define getpwuid nwrap_getpwuid
+
+#ifdef getpwuid_r
+#undef getpwuid_r
+#endif
+#define getpwuid_r nwrap_getpwuid_r
+
+#ifdef setpwent
+#undef setpwent
+#endif
+#define setpwent nwrap_setpwent
+
+#ifdef getpwent
+#undef getpwent
+#endif
+#define getpwent nwrap_getpwent
+
+#ifdef getpwent_r
+#undef getpwent_r
+#endif
+#define getpwent_r nwrap_getpwent_r
+
+#ifdef endpwent
+#undef endpwent
+#endif
+#define endpwent nwrap_endpwent
+
+#ifdef getgrlst
+#undef getgrlst
+#endif
+#define getgrlst __none_nwrap_getgrlst
+
+#ifdef getgrlst_r
+#undef getgrlst_r
+#endif
+#define getgrlst_r __none_nwrap_getgrlst_r
+
+#ifdef initgroups_dyn
+#undef initgroups_dyn
+#endif
+#define initgroups_dyn __none_nwrap_initgroups_dyn
+
+#ifdef initgroups
+#undef initgroups
+#endif
+#define initgroups nwrap_initgroups
+
+#ifdef getgrouplist
+#undef getgrouplist
+#endif
+#define getgrouplist nwrap_getgrouplist
+
+#ifdef getgrnam
+#undef getgrnam
+#endif
+#define getgrnam nwrap_getgrnam
+
+#ifdef getgrnam_r
+#undef getgrnam_r
+#endif
+#define getgrnam_r nwrap_getgrnam_r
+
+#ifdef getgrgid
+#undef getgrgid
+#endif
+#define getgrgid nwrap_getgrgid
+
+#ifdef getgrgid_r
+#undef getgrgid_r
+#endif
+#define getgrgid_r nwrap_getgrgid_r
+
+#ifdef setgrent
+#undef setgrent
+#endif
+#define setgrent nwrap_setgrent
+
+#ifdef getgrent
+#undef getgrent
+#endif
+#define getgrent nwrap_getgrent
+
+#ifdef getgrent_r
+#undef getgrent_r
+#endif
+#define getgrent_r nwrap_getgrent_r
+
+#ifdef endgrent
+#undef endgrent
+#endif
+#define endgrent nwrap_endgrent
+
+#endif /* NSS_WRAPPER_REPLACE */
+
+#endif /* __NSS_WRAPPER_H__ */
--- /dev/null
+#!/usr/bin/perl
+#
+
+use strict;
+
+use Getopt::Long;
+use Cwd qw(abs_path);
+
+my $opt_help = 0;
+my $opt_passwd_path = undef;
+my $opt_group_path = undef;
+my $opt_action = undef;
+my $opt_type = undef;
+my $opt_name = undef;
+my $opt_member = undef;
+my $opt_gid = 65534;# nogroup gid
+
+my $passwdfn = undef;
+my $groupfn = undef;
+my $memberfn = undef;
+my $actionfn = undef;
+
+sub passwd_add($$$$$);
+sub passwd_delete($$$$$);
+sub group_add($$$$$);
+sub group_delete($$$$$);
+sub member_add($$$$$);
+sub member_delete($$$$$);
+
+sub check_path($$);
+
+my $result = GetOptions(
+ 'help|h|?' => \$opt_help,
+ 'passwd_path=s' => \$opt_passwd_path,
+ 'group_path=s' => \$opt_group_path,
+ 'action=s' => \$opt_action,
+ 'type=s' => \$opt_type,
+ 'name=s' => \$opt_name,
+ 'member=s' => \$opt_member,
+ 'gid=i' => \$opt_gid
+);
+
+sub usage($;$)
+{
+ my ($ret, $msg) = @_;
+
+ print $msg."\n\n" if defined($msg);
+
+ print "usage:
+
+ --help|-h|-? Show this help.
+
+ --passwd_path <path> Path of the 'passwd' file.
+ --group_path <path> Path of the 'group' file.
+
+ --type <type> 'passwd', 'group' and 'member' are supported.
+
+ --action <action> 'add' or 'delete'.
+
+ --name <name> The name of the object.
+
+ --member <member> The name of the member.
+
+ --gid <gid> Primary Group ID for new users.
+";
+ exit($ret);
+}
+
+usage(1) if (not $result);
+
+usage(0) if ($opt_help);
+
+if (not defined($opt_action)) {
+ usage(1, "missing: --action [add|delete]");
+}
+if ($opt_action eq "add") {
+ $passwdfn = \&passwd_add;
+ $groupfn = \&group_add;
+ $memberfn = \&member_add;
+} elsif ($opt_action eq "delete") {
+ $passwdfn = \&passwd_delete;
+ $groupfn = \&group_delete;
+ $memberfn = \&member_delete;
+} else {
+ usage(1, "invalid: --action [add|delete]: '$opt_action'");
+}
+
+if (not defined($opt_type)) {
+ usage(1, "missing: --type [passwd|group|member]");
+}
+if ($opt_type eq "member" and not defined($opt_member)) {
+ usage(1, "missing: --member <member>");
+}
+my $opt_fullpath_passwd;
+my $opt_fullpath_group;
+if ($opt_type eq "passwd") {
+ $actionfn = $passwdfn;
+ $opt_fullpath_passwd = check_path($opt_passwd_path, $opt_type);
+} elsif ($opt_type eq "group") {
+ $actionfn = $groupfn;
+ $opt_fullpath_group = check_path($opt_group_path, $opt_type);
+} elsif ($opt_type eq "member") {
+ $actionfn = $memberfn;
+ $opt_fullpath_passwd = check_path($opt_passwd_path, "passwd");
+ $opt_fullpath_group = check_path($opt_group_path, "group");
+} else {
+ usage(1, "invalid: --type [passwd|group]: '$opt_type'")
+}
+
+if (not defined($opt_name)) {
+ usage(1, "missing: --name <name>");
+}
+if ($opt_name eq "") {
+ usage(1, "invalid: --name <name>");
+}
+
+exit $actionfn->($opt_fullpath_passwd, $opt_member, $opt_fullpath_group, $opt_name, $opt_gid);
+
+sub check_path($$)
+{
+ my ($path,$type) = @_;
+
+ if (not defined($path)) {
+ usage(1, "missing: --$type\_path <path>");
+ }
+ if ($path eq "" or $path eq "/") {
+ usage(1, "invalid: --$type\_path <path>: '$path'");
+ }
+ my $fullpath = abs_path($path);
+ if (not defined($fullpath)) {
+ usage(1, "invalid: --$type\_path <path>: '$path'");
+ }
+ return $fullpath;
+}
+
+sub passwd_add_entry($$);
+
+sub passwd_load($)
+{
+ my ($path) = @_;
+ my @lines;
+ my $passwd = undef;
+
+ open(PWD, "<$path") or die("Unable to open '$path' for read");
+ @lines = <PWD>;
+ close(PWD);
+
+ $passwd->{array} = ();
+ $passwd->{name} = {};
+ $passwd->{uid} = {};
+ $passwd->{path} = $path;
+
+ foreach my $line (@lines) {
+ passwd_add_entry($passwd, $line);
+ }
+
+ return $passwd;
+}
+
+sub group_add_entry($$);
+
+sub group_load($)
+{
+ my ($path) = @_;
+ my @lines;
+ my $group = undef;
+
+ open(GROUP, "<$path") or die("Unable to open '$path' for read");
+ @lines = <GROUP>;
+ close(GROUP);
+
+ $group->{array} = ();
+ $group->{name} = {};
+ $group->{gid} = {};
+ $group->{path} = $path;
+
+ foreach my $line (@lines) {
+ group_add_entry($group, $line);
+ }
+
+ return $group;
+}
+
+sub passwd_lookup_name($$)
+{
+ my ($passwd, $name) = @_;
+
+ return undef unless defined($passwd->{name}{$name});
+
+ return $passwd->{name}{$name};
+}
+
+sub group_lookup_name($$)
+{
+ my ($group, $name) = @_;
+
+ return undef unless defined($group->{name}{$name});
+
+ return $group->{name}{$name};
+}
+
+sub passwd_lookup_uid($$)
+{
+ my ($passwd, $uid) = @_;
+
+ return undef unless defined($passwd->{uid}{$uid});
+
+ return $passwd->{uid}{$uid};
+}
+
+sub group_lookup_gid($$)
+{
+ my ($group, $gid) = @_;
+
+ return undef unless defined($group->{gid}{$gid});
+
+ return $group->{gid}{$gid};
+}
+
+sub passwd_get_free_uid($)
+{
+ my ($passwd) = @_;
+ my $uid = 1000;
+
+ while (passwd_lookup_uid($passwd, $uid)) {
+ $uid++;
+ }
+
+ return $uid;
+}
+
+sub group_get_free_gid($)
+{
+ my ($group) = @_;
+ my $gid = 1000;
+
+ while (group_lookup_gid($group, $gid)) {
+ $gid++;
+ }
+
+ return $gid;
+}
+
+sub passwd_add_entry($$)
+{
+ my ($passwd, $str) = @_;
+
+ chomp $str;
+ my @e = split(':', $str);
+
+ push(@{$passwd->{array}}, \@e);
+ $passwd->{name}{$e[0]} = \@e;
+ $passwd->{uid}{$e[2]} = \@e;
+}
+
+sub group_add_entry($$)
+{
+ my ($group, $str) = @_;
+
+ chomp $str;
+ my @e = split(':', $str);
+
+ push(@{$group->{array}}, \@e);
+ $group->{name}{$e[0]} = \@e;
+ $group->{gid}{$e[2]} = \@e;
+}
+
+sub passwd_remove_entry($$)
+{
+ my ($passwd, $eref) = @_;
+
+ for (my $i = 0; defined($passwd->{array}[$i]); $i++) {
+ if ($eref == $passwd->{array}[$i]) {
+ $passwd->{array}[$i] = undef;
+ }
+ }
+
+ delete $passwd->{name}{${$eref}[0]};
+ delete $passwd->{uid}{${$eref}[2]};
+}
+
+sub group_remove_entry($$)
+{
+ my ($group, $eref) = @_;
+
+ for (my $i = 0; defined($group->{array}[$i]); $i++) {
+ if ($eref == $group->{array}[$i]) {
+ $group->{array}[$i] = undef;
+ }
+ }
+
+ delete $group->{name}{${$eref}[0]};
+ delete $group->{gid}{${$eref}[2]};
+}
+
+sub group_add_member($$$)
+{
+ my ($group, $eref, $username) = @_;
+
+ my @members;
+ my $str = @$eref[3] || undef;
+ if ($str) {
+ @members = split(",", $str);
+ }
+
+ foreach my $member (@members) {
+ if ($member and $member eq $username) {
+ die("account[$username] is already member of '@$eref[0]'");
+ }
+ }
+
+ push(@members, $username);
+
+ my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @members);
+
+ group_remove_entry($group, $eref);
+
+ group_add_entry($group, $gwent);
+}
+
+sub group_delete_member($$$)
+{
+ my ($group, $eref, $username) = @_;
+
+ my @members = undef;
+ my $str = @$eref[3] || undef;
+ if ($str) {
+ @members = split(",", $str);
+ }
+ my @new_members;
+ my $removed = 0;
+
+ foreach my $member (@members) {
+ if ($member and $member ne $username) {
+ push(@new_members, $member);
+ } else {
+ $removed = 1;
+ }
+ }
+
+ if ($removed != 1) {
+ die("account[$username] is not member of '@$eref[0]'");
+ }
+
+ my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @new_members);
+
+ group_remove_entry($group, $eref);
+
+ group_add_entry($group, $gwent);
+}
+
+sub passwd_save($)
+{
+ my ($passwd) = @_;
+ my @lines = ();
+ my $path = $passwd->{path};
+ my $tmppath = $path.$$;
+
+ foreach my $eref (@{$passwd->{array}}) {
+ next unless defined($eref);
+
+ my $line = join(':', @{$eref});
+ push(@lines, $line);
+ }
+
+ open(PWD, ">$tmppath") or die("Unable to open '$tmppath' for write");
+ print PWD join("\n", @lines)."\n";
+ close(PWD);
+ rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
+}
+
+sub group_save($)
+{
+ my ($group) = @_;
+ my @lines = ();
+ my $path = $group->{path};
+ my $tmppath = $path.$$;
+
+ foreach my $eref (@{$group->{array}}) {
+ next unless defined($eref);
+
+ my $line = join(':', @{$eref});
+ if (scalar(@{$eref}) == 3) {
+ $line .= ":";
+ }
+ push(@lines, $line);
+ }
+
+ open(GROUP, ">$tmppath") or die("Unable to open '$tmppath' for write");
+ print GROUP join("\n", @lines)."\n";
+ close(GROUP);
+ rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
+}
+
+sub passwd_add($$$$$)
+{
+ my ($path, $dummy, $dummy2, $name, $gid) = @_;
+
+ #print "passwd_add: '$name' in '$path'\n";
+
+ my $passwd = passwd_load($path);
+
+ my $e = passwd_lookup_name($passwd, $name);
+ die("account[$name] already exists in '$path'") if defined($e);
+
+ my $uid = passwd_get_free_uid($passwd);
+
+ my $pwent = $name.":x:".$uid.":".$gid.":".$name." gecos:/nodir:/bin/false";
+
+ passwd_add_entry($passwd, $pwent);
+
+ passwd_save($passwd);
+
+ return 0;
+}
+
+sub passwd_delete($$$$$)
+{
+ my ($path, $dummy, $dummy2, $name, $dummy3) = @_;
+
+ #print "passwd_delete: '$name' in '$path'\n";
+
+ my $passwd = passwd_load($path);
+
+ my $e = passwd_lookup_name($passwd, $name);
+ die("account[$name] does not exists in '$path'") unless defined($e);
+
+ passwd_remove_entry($passwd, $e);
+
+ passwd_save($passwd);
+
+ return 0;
+}
+
+sub group_add($$$$$)
+{
+ my ($dummy, $dummy2, $path, $name, $dummy3) = @_;
+
+ #print "group_add: '$name' in '$path'\n";
+
+ my $group = group_load($path);
+
+ my $e = group_lookup_name($group, $name);
+ die("group[$name] already exists in '$path'") if defined($e);
+
+ my $gid = group_get_free_gid($group);
+
+ my $gwent = $name.":x:".$gid.":"."";
+
+ group_add_entry($group, $gwent);
+
+ group_save($group);
+
+ #printf("%d\n", $gid);
+
+ return 0;
+}
+
+sub group_delete($$$$$)
+{
+ my ($dummy, $dummy2, $path, $name, $dummy3) = @_;
+
+ #print "group_delete: '$name' in '$path'\n";
+
+ my $group = group_load($path);
+
+ my $e = group_lookup_name($group, $name);
+ die("group[$name] does not exists in '$path'") unless defined($e);
+
+ group_remove_entry($group, $e);
+
+ group_save($group);
+
+ return 0;
+}
+
+sub member_add($$$$$)
+{
+ my ($passwd_path, $username, $group_path, $groupname, $dummy) = @_;
+
+ #print "member_add: adding '$username' in '$passwd_path' to '$groupname' in '$group_path'\n";
+
+ my $group = group_load($group_path);
+
+ my $g = group_lookup_name($group, $groupname);
+ die("group[$groupname] does not exists in '$group_path'") unless defined($g);
+
+ my $passwd = passwd_load($passwd_path);
+
+ my $u = passwd_lookup_name($passwd, $username);
+ die("account[$username] does not exists in '$passwd_path'") unless defined($u);
+
+ group_add_member($group, $g, $username);
+
+ group_save($group);
+
+ return 0;
+}
+
+sub member_delete($$$$$)
+{
+ my ($passwd_path, $username, $group_path, $groupname, $dummy) = @_;
+
+ #print "member_delete: removing '$username' in '$passwd_path' from '$groupname' in '$group_path'\n";
+
+ my $group = group_load($group_path);
+
+ my $g = group_lookup_name($group, $groupname);
+ die("group[$groupname] does not exists in '$group_path'") unless defined($g);
+
+ my $passwd = passwd_load($passwd_path);
+
+ my $u = passwd_lookup_name($passwd, $username);
+ die("account[$username] does not exists in '$passwd_path'") unless defined($u);
+
+ group_delete_member($group, $g, $username);
+
+ group_save($group);
+
+ return 0;
+}
--- /dev/null
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of the nss wrapper
+
+ Copyright (C) Guenther Deschner 2009-2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#ifndef NSS_WRAPPER
+#define NSS_WRAPPER
+#endif
+
+#include "torture/torture.h"
+#include "lib/replace/system/passwd.h"
+
+static bool copy_passwd(struct torture_context *tctx,
+ const struct passwd *pwd,
+ struct passwd *p)
+{
+ p->pw_name = talloc_strdup(tctx, pwd->pw_name);
+ p->pw_passwd = talloc_strdup(tctx, pwd->pw_passwd);
+ p->pw_uid = pwd->pw_uid;
+ p->pw_gid = pwd->pw_gid;
+ p->pw_gecos = talloc_strdup(tctx, pwd->pw_gecos);
+ p->pw_dir = talloc_strdup(tctx, pwd->pw_dir);
+ p->pw_shell = talloc_strdup(tctx, pwd->pw_shell);
+
+ return true;
+}
+
+static void print_passwd(struct passwd *pwd)
+{
+ printf("%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(struct torture_context *tctx,
+ const char *name,
+ struct passwd *pwd_p)
+{
+ struct passwd *pwd;
+
+ torture_comment(tctx, "Testing getpwnam: %s\n", name);
+
+ pwd = getpwnam(name);
+ if (pwd) {
+ print_passwd(pwd);
+ }
+
+ if (pwd_p) {
+ copy_passwd(tctx, pwd, pwd_p);
+ }
+
+ return pwd ? true : false;
+}
+
+static bool test_nwrap_getpwnam_r(struct torture_context *tctx,
+ const char *name,
+ struct passwd *pwd_p)
+{
+ struct passwd pwd, *pwdp;
+ char buffer[4096];
+ int ret;
+
+ torture_comment(tctx, "Testing getpwnam_r: %s\n", name);
+
+ ret = getpwnam_r(name, &pwd, buffer, sizeof(buffer), &pwdp);
+ if (ret != 0) {
+ if (ret != ENOENT) {
+ torture_comment(tctx, "got %d return code\n", ret);
+ }
+ return false;
+ }
+
+ print_passwd(&pwd);
+
+ if (pwd_p) {
+ copy_passwd(tctx, &pwd, pwd_p);
+ }
+
+ return true;
+}
+
+static bool test_nwrap_getpwuid(struct torture_context *tctx,
+ uid_t uid,
+ struct passwd *pwd_p)
+{
+ struct passwd *pwd;
+
+ torture_comment(tctx, "Testing getpwuid: %lu\n", (unsigned long)uid);
+
+ pwd = getpwuid(uid);
+ if (pwd) {
+ print_passwd(pwd);
+ }
+
+ if (pwd_p) {
+ copy_passwd(tctx, pwd, pwd_p);
+ }
+
+ return pwd ? true : false;
+}
+
+static bool test_nwrap_getpwuid_r(struct torture_context *tctx,
+ uid_t uid,
+ struct passwd *pwd_p)
+{
+ struct passwd pwd, *pwdp;
+ char buffer[4096];
+ int ret;
+
+ torture_comment(tctx, "Testing getpwuid_r: %lu\n", (unsigned long)uid);
+
+ ret = getpwuid_r(uid, &pwd, buffer, sizeof(buffer), &pwdp);
+ if (ret != 0) {
+ if (ret != ENOENT) {
+ torture_comment(tctx, "got %d return code\n", ret);
+ }
+ return false;
+ }
+
+ print_passwd(&pwd);
+
+ if (pwd_p) {
+ copy_passwd(tctx, &pwd, pwd_p);
+ }
+
+ return true;
+}
+
+
+static bool copy_group(struct torture_context *tctx,
+ const struct group *grp,
+ struct group *g)
+{
+ int i;
+
+ g->gr_name = talloc_strdup(tctx, grp->gr_name);
+ g->gr_passwd = talloc_strdup(tctx, grp->gr_passwd);
+ g->gr_gid = grp->gr_gid;
+ g->gr_mem = NULL;
+
+ for (i=0; grp->gr_mem && grp->gr_mem[i]; i++) {
+ g->gr_mem = talloc_realloc(tctx, g->gr_mem, char *, i + 2);
+ g->gr_mem[i] = talloc_strdup(g->gr_mem, grp->gr_mem[i]);
+ g->gr_mem[i+1] = NULL;
+ }
+
+ return true;
+}
+
+static void print_group(struct group *grp)
+{
+ int i;
+ printf("%s:%s:%lu:",
+ grp->gr_name,
+ grp->gr_passwd,
+ (unsigned long)grp->gr_gid);
+
+ if ((grp->gr_mem == NULL) || !grp->gr_mem[0]) {
+ printf("\n");
+ return;
+ }
+
+ for (i=0; grp->gr_mem[i+1]; i++) {
+ printf("%s,", grp->gr_mem[i]);
+ }
+ printf("%s\n", grp->gr_mem[i]);
+}
+
+static bool test_nwrap_getgrnam(struct torture_context *tctx,
+ const char *name,
+ struct group *grp_p)
+{
+ struct group *grp;
+
+ torture_comment(tctx, "Testing getgrnam: %s\n", name);
+
+ grp = getgrnam(name);
+ if (grp) {
+ print_group(grp);
+ }
+
+ if (grp_p) {
+ copy_group(tctx, grp, grp_p);
+ }
+
+ return grp ? true : false;
+}
+
+static bool test_nwrap_getgrnam_r(struct torture_context *tctx,
+ const char *name,
+ struct group *grp_p)
+{
+ struct group grp, *grpp;
+ char buffer[4096];
+ int ret;
+
+ torture_comment(tctx, "Testing getgrnam_r: %s\n", name);
+
+ ret = getgrnam_r(name, &grp, buffer, sizeof(buffer), &grpp);
+ if (ret != 0) {
+ if (ret != ENOENT) {
+ torture_comment(tctx, "got %d return code\n", ret);
+ }
+ return false;
+ }
+
+ print_group(&grp);
+
+ if (grp_p) {
+ copy_group(tctx, &grp, grp_p);
+ }
+
+ return true;
+}
+
+
+static bool test_nwrap_getgrgid(struct torture_context *tctx,
+ gid_t gid,
+ struct group *grp_p)
+{
+ struct group *grp;
+
+ torture_comment(tctx, "Testing getgrgid: %lu\n", (unsigned long)gid);
+
+ grp = getgrgid(gid);
+ if (grp) {
+ print_group(grp);
+ }
+
+ if (grp_p) {
+ copy_group(tctx, grp, grp_p);
+ }
+
+ return grp ? true : false;
+}
+
+static bool test_nwrap_getgrgid_r(struct torture_context *tctx,
+ gid_t gid,
+ struct group *grp_p)
+{
+ struct group grp, *grpp;
+ char buffer[4096];
+ int ret;
+
+ torture_comment(tctx, "Testing getgrgid_r: %lu\n", (unsigned long)gid);
+
+ ret = getgrgid_r(gid, &grp, buffer, sizeof(buffer), &grpp);
+ if (ret != 0) {
+ if (ret != ENOENT) {
+ torture_comment(tctx, "got %d return code\n", ret);
+ }
+ return false;
+ }
+
+ print_group(&grp);
+
+ if (grp_p) {
+ copy_group(tctx, &grp, grp_p);
+ }
+
+ return true;
+}
+
+static bool test_nwrap_enum_passwd(struct torture_context *tctx,
+ struct passwd **pwd_array_p,
+ size_t *num_pwd_p)
+{
+ struct passwd *pwd;
+ struct passwd *pwd_array = NULL;
+ size_t num_pwd = 0;
+
+ torture_comment(tctx, "Testing setpwent\n");
+ setpwent();
+
+ while ((pwd = getpwent()) != NULL) {
+ torture_comment(tctx, "Testing getpwent\n");
+
+ print_passwd(pwd);
+ if (pwd_array_p && num_pwd_p) {
+ pwd_array = talloc_realloc(tctx, pwd_array, struct passwd, num_pwd+1);
+ torture_assert(tctx, pwd_array, "out of memory");
+ copy_passwd(tctx, pwd, &pwd_array[num_pwd]);
+ num_pwd++;
+ }
+ }
+
+ torture_comment(tctx, "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 torture_context *tctx,
+ struct passwd **pwd_array_p,
+ size_t *num_pwd_p)
+{
+ struct passwd pwd, *pwdp;
+ struct passwd *pwd_array = NULL;
+ size_t num_pwd = 0;
+ char buffer[4096];
+ int ret;
+
+ torture_comment(tctx, "Testing setpwent\n");
+ setpwent();
+
+ while (1) {
+ torture_comment(tctx, "Testing getpwent_r\n");
+
+ ret = getpwent_r(&pwd, buffer, sizeof(buffer), &pwdp);
+ if (ret != 0) {
+ if (ret != ENOENT) {
+ torture_comment(tctx, "got %d return code\n", ret);
+ }
+ break;
+ }
+ print_passwd(&pwd);
+ if (pwd_array_p && num_pwd_p) {
+ pwd_array = talloc_realloc(tctx, pwd_array, struct passwd, num_pwd+1);
+ torture_assert(tctx, pwd_array, "out of memory");
+ copy_passwd(tctx, &pwd, &pwd_array[num_pwd]);
+ num_pwd++;
+ }
+ }
+
+ torture_comment(tctx, "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 torture_assert_passwd_equal(struct torture_context *tctx,
+ const struct passwd *p1,
+ const struct passwd *p2,
+ const char *comment)
+{
+ torture_assert_str_equal(tctx, p1->pw_name, p2->pw_name, comment);
+ torture_assert_str_equal(tctx, p1->pw_passwd, p2->pw_passwd, comment);
+ torture_assert_int_equal(tctx, p1->pw_uid, p2->pw_uid, comment);
+ torture_assert_int_equal(tctx, p1->pw_gid, p2->pw_gid, comment);
+ torture_assert_str_equal(tctx, p1->pw_gecos, p2->pw_gecos, comment);
+ torture_assert_str_equal(tctx, p1->pw_dir, p2->pw_dir, comment);
+ torture_assert_str_equal(tctx, p1->pw_shell, p2->pw_shell, comment);
+
+ return true;
+}
+
+static bool test_nwrap_passwd(struct torture_context *tctx)
+{
+ int i;
+ struct passwd *pwd, pwd1, pwd2;
+ size_t num_pwd;
+
+ torture_assert(tctx, test_nwrap_enum_passwd(tctx, &pwd, &num_pwd),
+ "failed to enumerate passwd");
+
+ for (i=0; i < num_pwd; i++) {
+ torture_assert(tctx, test_nwrap_getpwnam(tctx, pwd[i].pw_name, &pwd1),
+ "failed to call getpwnam for enumerated user");
+ torture_assert_passwd_equal(tctx, &pwd[i], &pwd1,
+ "getpwent and getpwnam gave different results");
+ torture_assert(tctx, test_nwrap_getpwuid(tctx, pwd[i].pw_uid, &pwd2),
+ "failed to call getpwuid for enumerated user");
+ torture_assert_passwd_equal(tctx, &pwd[i], &pwd2,
+ "getpwent and getpwuid gave different results");
+ torture_assert_passwd_equal(tctx, &pwd1, &pwd2,
+ "getpwnam and getpwuid gave different results");
+ }
+
+ return true;
+}
+
+static bool test_nwrap_passwd_r(struct torture_context *tctx)
+{
+ int i;
+ struct passwd *pwd, pwd1, pwd2;
+ size_t num_pwd;
+
+ torture_assert(tctx, test_nwrap_enum_r_passwd(tctx, &pwd, &num_pwd),
+ "failed to enumerate passwd");
+
+ for (i=0; i < num_pwd; i++) {
+ torture_assert(tctx, test_nwrap_getpwnam_r(tctx, pwd[i].pw_name, &pwd1),
+ "failed to call getpwnam_r for enumerated user");
+ torture_assert_passwd_equal(tctx, &pwd[i], &pwd1,
+ "getpwent_r and getpwnam_r gave different results");
+ torture_assert(tctx, test_nwrap_getpwuid_r(tctx, pwd[i].pw_uid, &pwd2),
+ "failed to call getpwuid_r for enumerated user");
+ torture_assert_passwd_equal(tctx, &pwd[i], &pwd2,
+ "getpwent_r and getpwuid_r gave different results");
+ torture_assert_passwd_equal(tctx, &pwd1, &pwd2,
+ "getpwnam_r and getpwuid_r gave different results");
+ }
+
+ return true;
+}
+
+static bool test_nwrap_passwd_r_cross(struct torture_context *tctx)
+{
+ int i;
+ struct passwd *pwd, pwd1, pwd2, pwd3, pwd4;
+ size_t num_pwd;
+
+ torture_assert(tctx, test_nwrap_enum_r_passwd(tctx, &pwd, &num_pwd),
+ "failed to enumerate passwd");
+
+ for (i=0; i < num_pwd; i++) {
+ torture_assert(tctx, test_nwrap_getpwnam_r(tctx, pwd[i].pw_name, &pwd1),
+ "failed to call getpwnam_r for enumerated user");
+ torture_assert_passwd_equal(tctx, &pwd[i], &pwd1,
+ "getpwent_r and getpwnam_r gave different results");
+ torture_assert(tctx, test_nwrap_getpwuid_r(tctx, pwd[i].pw_uid, &pwd2),
+ "failed to call getpwuid_r for enumerated user");
+ torture_assert_passwd_equal(tctx, &pwd[i], &pwd2,
+ "getpwent_r and getpwuid_r gave different results");
+ torture_assert_passwd_equal(tctx, &pwd1, &pwd2,
+ "getpwnam_r and getpwuid_r gave different results");
+ torture_assert(tctx, test_nwrap_getpwnam(tctx, pwd[i].pw_name, &pwd3),
+ "failed to call getpwnam for enumerated user");
+ torture_assert_passwd_equal(tctx, &pwd[i], &pwd3,
+ "getpwent_r and getpwnam gave different results");
+ torture_assert(tctx, test_nwrap_getpwuid(tctx, pwd[i].pw_uid, &pwd4),
+ "failed to call getpwuid for enumerated user");
+ torture_assert_passwd_equal(tctx, &pwd[i], &pwd4,
+ "getpwent_r and getpwuid gave different results");
+ torture_assert_passwd_equal(tctx, &pwd3, &pwd4,
+ "getpwnam and getpwuid gave different results");
+ }
+
+ return true;
+}
+
+static bool test_nwrap_enum_group(struct torture_context *tctx,
+ struct group **grp_array_p,
+ size_t *num_grp_p)
+{
+ struct group *grp;
+ struct group *grp_array = NULL;
+ size_t num_grp = 0;
+
+ torture_comment(tctx, "Testing setgrent\n");
+ setgrent();
+
+ while ((grp = getgrent()) != NULL) {
+ torture_comment(tctx, "Testing getgrent\n");
+
+ print_group(grp);
+ if (grp_array_p && num_grp_p) {
+ grp_array = talloc_realloc(tctx, grp_array, struct group, num_grp+1);
+ torture_assert(tctx, grp_array, "out of memory");
+ copy_group(tctx, grp, &grp_array[num_grp]);
+ num_grp++;
+ }
+ }
+
+ torture_comment(tctx, "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 torture_context *tctx,
+ struct group **grp_array_p,
+ size_t *num_grp_p)
+{
+ struct group grp, *grpp;
+ struct group *grp_array = NULL;
+ size_t num_grp = 0;
+ char buffer[4096];
+ int ret;
+
+ torture_comment(tctx, "Testing setgrent\n");
+ setgrent();
+
+ while (1) {
+ torture_comment(tctx, "Testing getgrent_r\n");
+
+ ret = getgrent_r(&grp, buffer, sizeof(buffer), &grpp);
+ if (ret != 0) {
+ if (ret != ENOENT) {
+ torture_comment(tctx, "got %d return code\n", ret);
+ }
+ break;
+ }
+ print_group(&grp);
+ if (grp_array_p && num_grp_p) {
+ grp_array = talloc_realloc(tctx, grp_array, struct group, num_grp+1);
+ torture_assert(tctx, grp_array, "out of memory");
+ copy_group(tctx, &grp, &grp_array[num_grp]);
+ num_grp++;
+ }
+ }
+
+ torture_comment(tctx, "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 torture_assert_group_equal(struct torture_context *tctx,
+ const struct group *g1,
+ const struct group *g2,
+ const char *comment)
+{
+ int i;
+ torture_assert_str_equal(tctx, g1->gr_name, g2->gr_name, comment);
+ torture_assert_str_equal(tctx, g1->gr_passwd, g2->gr_passwd, comment);
+ torture_assert_int_equal(tctx, g1->gr_gid, g2->gr_gid, comment);
+ if (g1->gr_mem && !g2->gr_mem) {
+ return false;
+ }
+ if (!g1->gr_mem && g2->gr_mem) {
+ return false;
+ }
+ if (!g1->gr_mem && !g2->gr_mem) {
+ return true;
+ }
+ for (i=0; g1->gr_mem[i] && g2->gr_mem[i]; i++) {
+ torture_assert_str_equal(tctx, g1->gr_mem[i], g2->gr_mem[i], comment);
+ }
+
+ return true;
+}
+
+static bool test_nwrap_group(struct torture_context *tctx)
+{
+ int i;
+ struct group *grp, grp1, grp2;
+ size_t num_grp;
+
+ torture_assert(tctx, test_nwrap_enum_group(tctx, &grp, &num_grp),
+ "failed to enumerate group");
+
+ for (i=0; i < num_grp; i++) {
+ torture_assert(tctx, test_nwrap_getgrnam(tctx, grp[i].gr_name, &grp1),
+ "failed to call getgrnam for enumerated user");
+ torture_assert_group_equal(tctx, &grp[i], &grp1,
+ "getgrent and getgrnam gave different results");
+ torture_assert(tctx, test_nwrap_getgrgid(tctx, grp[i].gr_gid, &grp2),
+ "failed to call getgrgid for enumerated user");
+ torture_assert_group_equal(tctx, &grp[i], &grp2,
+ "getgrent and getgrgid gave different results");
+ torture_assert_group_equal(tctx, &grp1, &grp2,
+ "getgrnam and getgrgid gave different results");
+ }
+
+ return true;
+}
+
+static bool test_nwrap_group_r(struct torture_context *tctx)
+{
+ int i;
+ struct group *grp, grp1, grp2;
+ size_t num_grp;
+
+ torture_assert(tctx, test_nwrap_enum_r_group(tctx, &grp, &num_grp),
+ "failed to enumerate group");
+
+ for (i=0; i < num_grp; i++) {
+ torture_assert(tctx, test_nwrap_getgrnam_r(tctx, grp[i].gr_name, &grp1),
+ "failed to call getgrnam_r for enumerated user");
+ torture_assert_group_equal(tctx, &grp[i], &grp1,
+ "getgrent_r and getgrnam_r gave different results");
+ torture_assert(tctx, test_nwrap_getgrgid_r(tctx, grp[i].gr_gid, &grp2),
+ "failed to call getgrgid_r for enumerated user");
+ torture_assert_group_equal(tctx, &grp[i], &grp2,
+ "getgrent_r and getgrgid_r gave different results");
+ torture_assert_group_equal(tctx, &grp1, &grp2,
+ "getgrnam_r and getgrgid_r gave different results");
+ }
+
+ return true;
+}
+
+static bool test_nwrap_group_r_cross(struct torture_context *tctx)
+{
+ int i;
+ struct group *grp, grp1, grp2, grp3, grp4;
+ size_t num_grp;
+
+ torture_assert(tctx, test_nwrap_enum_r_group(tctx, &grp, &num_grp),
+ "failed to enumerate group");
+
+ for (i=0; i < num_grp; i++) {
+ torture_assert(tctx, test_nwrap_getgrnam_r(tctx, grp[i].gr_name, &grp1),
+ "failed to call getgrnam_r for enumerated user");
+ torture_assert_group_equal(tctx, &grp[i], &grp1,
+ "getgrent_r and getgrnam_r gave different results");
+ torture_assert(tctx, test_nwrap_getgrgid_r(tctx, grp[i].gr_gid, &grp2),
+ "failed to call getgrgid_r for enumerated user");
+ torture_assert_group_equal(tctx, &grp[i], &grp2,
+ "getgrent_r and getgrgid_r gave different results");
+ torture_assert_group_equal(tctx, &grp1, &grp2,
+ "getgrnam_r and getgrgid_r gave different results");
+ torture_assert(tctx, test_nwrap_getgrnam(tctx, grp[i].gr_name, &grp3),
+ "failed to call getgrnam for enumerated user");
+ torture_assert_group_equal(tctx, &grp[i], &grp3,
+ "getgrent_r and getgrnam gave different results");
+ torture_assert(tctx, test_nwrap_getgrgid(tctx, grp[i].gr_gid, &grp4),
+ "failed to call getgrgid for enumerated user");
+ torture_assert_group_equal(tctx, &grp[i], &grp4,
+ "getgrent_r and getgrgid gave different results");
+ torture_assert_group_equal(tctx, &grp3, &grp4,
+ "getgrnam and getgrgid gave different results");
+ }
+
+ return true;
+}
+
+static bool test_nwrap_getgrouplist(struct torture_context *tctx,
+ const char *user,
+ gid_t gid,
+ gid_t **gids_p,
+ int *num_gids_p)
+{
+ int ret;
+ int num_groups = 0;
+ gid_t *groups = NULL;
+
+ torture_comment(tctx, "Testing getgrouplist: %s\n", user);
+
+ ret = getgrouplist(user, gid, NULL, &num_groups);
+ if (ret == -1 || num_groups != 0) {
+
+ groups = talloc_array(tctx, gid_t, num_groups);
+ torture_assert(tctx, groups, "out of memory\n");
+
+ ret = getgrouplist(user, gid, groups, &num_groups);
+ }
+
+ torture_assert(tctx, (ret != -1), "failed to call getgrouplist");
+
+ torture_comment(tctx, "%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(struct torture_context *tctx,
+ const struct passwd *pwd,
+ const struct group *grp)
+{
+ int i;
+
+ for (i=0; grp->gr_mem && grp->gr_mem[i] != NULL; i++) {
+ if (strequal(grp->gr_mem[i], pwd->pw_name)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool test_nwrap_membership_user(struct torture_context *tctx,
+ const struct passwd *pwd,
+ struct group *grp_array,
+ size_t num_grp)
+{
+ int num_user_groups = 0;
+ int num_user_groups_from_enum = 0;
+ gid_t *user_groups = NULL;
+ int g, i;
+ bool primary_group_had_user_member = false;
+
+ torture_assert(tctx, test_nwrap_getgrouplist(tctx,
+ pwd->pw_name,
+ pwd->pw_gid,
+ &user_groups,
+ &num_user_groups),
+ "failed to test getgrouplist");
+
+ for (g=0; g < num_user_groups; g++) {
+ torture_assert(tctx, test_nwrap_getgrgid(tctx, user_groups[g], NULL),
+ "failed to find the group the user is a member of");
+ }
+
+
+ for (i=0; i < num_grp; i++) {
+
+ struct group grp = grp_array[i];
+
+ if (test_nwrap_user_in_group(tctx, pwd, &grp)) {
+
+ struct group current_grp;
+ num_user_groups_from_enum++;
+
+ torture_assert(tctx, test_nwrap_getgrnam(tctx, grp.gr_name, ¤t_grp),
+ "failed to find the group the user is a member of");
+
+ if (current_grp.gr_gid == pwd->pw_gid) {
+ torture_comment(tctx, "primary group %s of user %s lists user as member\n",
+ current_grp.gr_name,
+ pwd->pw_name);
+ primary_group_had_user_member = true;
+ }
+
+ continue;
+ }
+ }
+
+ if (!primary_group_had_user_member) {
+ num_user_groups_from_enum++;
+ }
+
+ torture_assert_int_equal(tctx, num_user_groups, num_user_groups_from_enum,
+ "getgrouplist and real inspection of grouplist gave different results\n");
+
+ return true;
+}
+
+static bool test_nwrap_membership(struct torture_context *tctx)
+{
+ 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;
+ int i;
+
+ if (!old_pwd || !old_group) {
+ torture_comment(tctx, "ENV NSS_WRAPPER_PASSWD or NSS_WRAPPER_GROUP not set\n");
+ torture_skip(tctx, "nothing to test\n");
+ }
+
+ torture_assert(tctx, test_nwrap_enum_passwd(tctx, &pwd, &num_pwd),
+ "failed to enumerate passwd");
+ torture_assert(tctx, test_nwrap_enum_group(tctx, &grp, &num_grp),
+ "failed to enumerate group");
+
+ for (i=0; i < num_pwd; i++) {
+
+ torture_assert(tctx, test_nwrap_membership_user(tctx, &pwd[i], grp, num_grp),
+ "failed to test membership for user");
+
+ }
+
+ return true;
+}
+
+static bool test_nwrap_enumeration(struct torture_context *tctx)
+{
+ const char *old_pwd = getenv("NSS_WRAPPER_PASSWD");
+ const char *old_group = getenv("NSS_WRAPPER_GROUP");
+
+ if (!old_pwd || !old_group) {
+ torture_comment(tctx, "ENV NSS_WRAPPER_PASSWD or NSS_WRAPPER_GROUP not set\n");
+ torture_skip(tctx, "nothing to test\n");
+ }
+
+ torture_assert(tctx, test_nwrap_passwd(tctx),
+ "failed to test users");
+ torture_assert(tctx, test_nwrap_group(tctx),
+ "failed to test groups");
+
+ return true;
+}
+
+static bool test_nwrap_reentrant_enumeration(struct torture_context *tctx)
+{
+ const char *old_pwd = getenv("NSS_WRAPPER_PASSWD");
+ const char *old_group = getenv("NSS_WRAPPER_GROUP");
+
+ if (!old_pwd || !old_group) {
+ torture_comment(tctx, "ENV NSS_WRAPPER_PASSWD or NSS_WRAPPER_GROUP not set\n");
+ torture_skip(tctx, "nothing to test\n");
+ }
+
+ torture_comment(tctx, "Testing re-entrant calls\n");
+
+ torture_assert(tctx, test_nwrap_passwd_r(tctx),
+ "failed to test users");
+ torture_assert(tctx, test_nwrap_group_r(tctx),
+ "failed to test groups");
+
+ return true;
+}
+
+static bool test_nwrap_reentrant_enumeration_crosschecks(struct torture_context *tctx)
+{
+ const char *old_pwd = getenv("NSS_WRAPPER_PASSWD");
+ const char *old_group = getenv("NSS_WRAPPER_GROUP");
+
+ if (!old_pwd || !old_group) {
+ torture_comment(tctx, "ENV NSS_WRAPPER_PASSWD or NSS_WRAPPER_GROUP not set\n");
+ torture_skip(tctx, "nothing to test\n");
+ }
+
+ torture_comment(tctx, "Testing re-entrant calls with cross checks\n");
+
+ torture_assert(tctx, test_nwrap_passwd_r_cross(tctx),
+ "failed to test users");
+ torture_assert(tctx, test_nwrap_group_r_cross(tctx),
+ "failed to test groups");
+
+ return true;
+}
+
+static bool test_nwrap_passwd_duplicates(struct torture_context *tctx)
+{
+ int i, d;
+ struct passwd *pwd;
+ size_t num_pwd;
+ int duplicates = 0;
+
+ torture_assert(tctx, test_nwrap_enum_passwd(tctx, &pwd, &num_pwd),
+ "failed to enumerate passwd");
+
+ 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 (!strequal(current_name, dup_name)) {
+ continue;
+ }
+
+ torture_warning(tctx, "found duplicate names:");
+ print_passwd(&pwd[d]);
+ print_passwd(&pwd[i]);
+ duplicates++;
+ }
+ }
+
+ if (duplicates) {
+ torture_fail(tctx, talloc_asprintf(tctx, "found %d duplicate names", duplicates));
+ }
+
+ return true;
+}
+
+static bool test_nwrap_group_duplicates(struct torture_context *tctx)
+{
+ int i, d;
+ struct group *grp;
+ size_t num_grp;
+ int duplicates = 0;
+
+ torture_assert(tctx, test_nwrap_enum_group(tctx, &grp, &num_grp),
+ "failed to enumerate group");
+
+ 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 (!strequal(current_name, dup_name)) {
+ continue;
+ }
+
+ torture_warning(tctx, "found duplicate names:");
+ print_group(&grp[d]);
+ print_group(&grp[i]);
+ duplicates++;
+ }
+ }
+
+ if (duplicates) {
+ torture_fail(tctx, talloc_asprintf(tctx, "found %d duplicate names", duplicates));
+ }
+
+ return true;
+}
+
+
+static bool test_nwrap_duplicates(struct torture_context *tctx)
+{
+ const char *old_pwd = getenv("NSS_WRAPPER_PASSWD");
+ const char *old_group = getenv("NSS_WRAPPER_GROUP");
+
+ if (!old_pwd || !old_group) {
+ torture_comment(tctx, "ENV NSS_WRAPPER_PASSWD or NSS_WRAPPER_GROUP not set\n");
+ torture_skip(tctx, "nothing to test\n");
+ }
+
+ torture_assert(tctx, test_nwrap_passwd_duplicates(tctx),
+ "failed to test users");
+ torture_assert(tctx, test_nwrap_group_duplicates(tctx),
+ "failed to test groups");
+
+ return true;
+}
+
+
+struct torture_suite *torture_local_nss_wrapper(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "nss-wrapper");
+
+ torture_suite_add_simple_test(suite, "enumeration", test_nwrap_enumeration);
+ torture_suite_add_simple_test(suite, "reentrant enumeration", test_nwrap_reentrant_enumeration);
+ torture_suite_add_simple_test(suite, "reentrant enumeration crosschecks", test_nwrap_reentrant_enumeration_crosschecks);
+ torture_suite_add_simple_test(suite, "membership", test_nwrap_membership);
+ torture_suite_add_simple_test(suite, "duplicates", test_nwrap_duplicates);
+
+ return suite;
+}