Initial version.
authorAndreas Schneider <asn@cryptomilk.org>
Wed, 15 May 2013 12:13:50 +0000 (14:13 +0200)
committerAndreas Schneider <asn@cryptomilk.org>
Wed, 15 May 2013 12:13:50 +0000 (14:13 +0200)
21 files changed:
CMakeLists.txt [new file with mode: 0644]
COPYING [new file with mode: 0644]
CPackConfig.cmake [new file with mode: 0644]
CTestConfig.cmake [new file with mode: 0644]
ConfigureChecks.cmake [new file with mode: 0644]
DefineOptions.cmake [new file with mode: 0644]
README [new file with mode: 0644]
cmake/Modules/COPYING-CMAKE-SCRIPTS [new file with mode: 0644]
cmake/Modules/CheckCCompilerFlagSSP.cmake [new file with mode: 0644]
cmake/Modules/DefineCMakeDefaults.cmake [new file with mode: 0644]
cmake/Modules/DefineCompilerFlags.cmake [new file with mode: 0644]
cmake/Modules/DefineInstallationPaths.cmake [new file with mode: 0644]
cmake/Modules/DefinePlatformDefaults.cmake [new file with mode: 0644]
cmake/Modules/MacroEnsureOutOfSourceBuild.cmake [new file with mode: 0644]
config.h.cmake [new file with mode: 0644]
nss_wrapper.pl [new file with mode: 0755]
src/CMakeLists.txt [new file with mode: 0644]
src/nss_wrapper.c [new file with mode: 0644]
src/nss_wrapper.h [new file with mode: 0644]
src/nss_wrapper.pl [new file with mode: 0755]
tests/testsuite.c [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..572ac8d
--- /dev/null
@@ -0,0 +1,51 @@
+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)
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..e6227e1
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,32 @@
+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.
diff --git a/CPackConfig.cmake b/CPackConfig.cmake
new file mode 100644 (file)
index 0000000..2e4c9fe
--- /dev/null
@@ -0,0 +1,53 @@
+# For help take a look at:
+# http://www.cmake.org/Wiki/CMake:CPackConfiguration
+
+### general settings
+set(CPACK_PACKAGE_NAME ${APPLICATION_NAME})
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The SSH library")
+set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README")
+set(CPACK_PACKAGE_VENDOR "The SSH Library Development Team")
+set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
+set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/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)
diff --git a/CTestConfig.cmake b/CTestConfig.cmake
new file mode 100644 (file)
index 0000000..d8a4183
--- /dev/null
@@ -0,0 +1,9 @@
+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)
diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
new file mode 100644 (file)
index 0000000..6a7c043
--- /dev/null
@@ -0,0 +1,84 @@
+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)
diff --git a/DefineOptions.cmake b/DefineOptions.cmake
new file mode 100644 (file)
index 0000000..ea8265c
--- /dev/null
@@ -0,0 +1,27 @@
+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)
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/cmake/Modules/COPYING-CMAKE-SCRIPTS b/cmake/Modules/COPYING-CMAKE-SCRIPTS
new file mode 100644 (file)
index 0000000..4b41776
--- /dev/null
@@ -0,0 +1,22 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products 
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/cmake/Modules/CheckCCompilerFlagSSP.cmake b/cmake/Modules/CheckCCompilerFlagSSP.cmake
new file mode 100644 (file)
index 0000000..2fe4395
--- /dev/null
@@ -0,0 +1,26 @@
+# - Check whether the C compiler supports a given flag in the
+# context of a stack checking compiler option.
+
+# CHECK_C_COMPILER_FLAG_SSP(FLAG VARIABLE)
+#
+#  FLAG - the compiler flag
+#  VARIABLE - variable to store the result
+#
+#  This actually calls check_c_source_compiles.
+#  See help for CheckCSourceCompiles for a listing of variables
+#  that can modify the build.
+
+# Copyright (c) 2006, Alexander Neundorf, <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)
diff --git a/cmake/Modules/DefineCMakeDefaults.cmake b/cmake/Modules/DefineCMakeDefaults.cmake
new file mode 100644 (file)
index 0000000..72893c3
--- /dev/null
@@ -0,0 +1,27 @@
+# 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)
diff --git a/cmake/Modules/DefineCompilerFlags.cmake b/cmake/Modules/DefineCompilerFlags.cmake
new file mode 100644 (file)
index 0000000..582ea1c
--- /dev/null
@@ -0,0 +1,77 @@
+# 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)
diff --git a/cmake/Modules/DefineInstallationPaths.cmake b/cmake/Modules/DefineInstallationPaths.cmake
new file mode 100644 (file)
index 0000000..d857871
--- /dev/null
@@ -0,0 +1,104 @@
+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 ()
diff --git a/cmake/Modules/DefinePlatformDefaults.cmake b/cmake/Modules/DefinePlatformDefaults.cmake
new file mode 100644 (file)
index 0000000..502d936
--- /dev/null
@@ -0,0 +1,28 @@
+# Set system vars
+
+if (CMAKE_SYSTEM_NAME MATCHES "Linux")
+    set(LINUX TRUE)
+endif(CMAKE_SYSTEM_NAME MATCHES "Linux")
+
+if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+    set(FREEBSD TRUE)
+    set(BSD TRUE)
+endif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+
+if (CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
+    set(OPENBSD TRUE)
+    set(BSD TRUE)
+endif (CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
+
+if (CMAKE_SYSTEM_NAME MATCHES "NetBSD")
+    set(NETBSD TRUE)
+    set(BSD TRUE)
+endif (CMAKE_SYSTEM_NAME MATCHES "NetBSD")
+
+if (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
+    set(SOLARIS TRUE)
+endif (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
+
+if (CMAKE_SYSTEM_NAME MATCHES "OS2")
+    set(OS2 TRUE)
+endif (CMAKE_SYSTEM_NAME MATCHES "OS2")
diff --git a/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake b/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake
new file mode 100644 (file)
index 0000000..a2e9480
--- /dev/null
@@ -0,0 +1,17 @@
+# - 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)
diff --git a/config.h.cmake b/config.h.cmake
new file mode 100644 (file)
index 0000000..d72b50b
--- /dev/null
@@ -0,0 +1,54 @@
+/* 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
diff --git a/nss_wrapper.pl b/nss_wrapper.pl
new file mode 100755 (executable)
index 0000000..48fa2c5
--- /dev/null
@@ -0,0 +1,521 @@
+#!/usr/bin/perl
+#
+
+use strict;
+
+use Getopt::Long;
+use Cwd qw(abs_path);
+
+my $opt_help = 0;
+my $opt_passwd_path = undef;
+my $opt_group_path = undef;
+my $opt_action = undef;
+my $opt_type = undef;
+my $opt_name = undef;
+my $opt_member = undef;
+my $opt_gid = 65534;# nogroup gid
+
+my $passwdfn = undef;
+my $groupfn = undef;
+my $memberfn = undef;
+my $actionfn = undef;
+
+sub passwd_add($$$$$);
+sub passwd_delete($$$$$);
+sub group_add($$$$$);
+sub group_delete($$$$$);
+sub member_add($$$$$);
+sub member_delete($$$$$);
+
+sub check_path($$);
+
+my $result = GetOptions(
+       'help|h|?'      => \$opt_help,
+       'passwd_path=s' => \$opt_passwd_path,
+       'group_path=s'  => \$opt_group_path,
+       'action=s'      => \$opt_action,
+       'type=s'        => \$opt_type,
+       'name=s'        => \$opt_name,
+       'member=s'      => \$opt_member,
+       'gid=i'         => \$opt_gid
+);
+
+sub usage($;$)
+{
+       my ($ret, $msg) = @_;
+
+       print $msg."\n\n" if defined($msg);
+
+       print "usage:
+
+       --help|-h|-?            Show this help.
+
+       --passwd_path <path>    Path of the 'passwd' file.
+       --group_path <path>     Path of the 'group' file.
+
+       --type <type>           'passwd', 'group' and 'member' are supported.
+
+       --action <action>       'add' or 'delete'.
+
+       --name <name>           The name of the object.
+
+       --member <member>       The name of the member.
+
+       --gid <gid>             Primary Group ID for new users.
+";
+       exit($ret);
+}
+
+usage(1) if (not $result);
+
+usage(0) if ($opt_help);
+
+if (not defined($opt_action)) {
+       usage(1, "missing: --action [add|delete]");
+}
+if ($opt_action eq "add") {
+       $passwdfn = \&passwd_add;
+       $groupfn = \&group_add;
+       $memberfn = \&member_add;
+} elsif ($opt_action eq "delete") {
+       $passwdfn = \&passwd_delete;
+       $groupfn = \&group_delete;
+       $memberfn = \&member_delete;
+} else {
+       usage(1, "invalid: --action [add|delete]: '$opt_action'");
+}
+
+if (not defined($opt_type)) {
+       usage(1, "missing: --type [passwd|group|member]");
+}
+if ($opt_type eq "member" and not defined($opt_member)) {
+       usage(1, "missing: --member <member>");
+}
+my $opt_fullpath_passwd;
+my $opt_fullpath_group;
+if ($opt_type eq "passwd") {
+       $actionfn = $passwdfn;
+       $opt_fullpath_passwd = check_path($opt_passwd_path, $opt_type);
+} elsif ($opt_type eq "group") {
+       $actionfn = $groupfn;
+       $opt_fullpath_group = check_path($opt_group_path, $opt_type);
+} elsif ($opt_type eq "member") {
+       $actionfn = $memberfn;
+       $opt_fullpath_passwd = check_path($opt_passwd_path, "passwd");
+       $opt_fullpath_group = check_path($opt_group_path, "group");
+} else {
+       usage(1, "invalid: --type [passwd|group]: '$opt_type'")
+}
+
+if (not defined($opt_name)) {
+       usage(1, "missing: --name <name>");
+}
+if ($opt_name eq "") {
+       usage(1, "invalid: --name <name>");
+}
+
+exit $actionfn->($opt_fullpath_passwd, $opt_member, $opt_fullpath_group, $opt_name, $opt_gid);
+
+sub check_path($$)
+{
+       my ($path,$type) = @_;
+
+       if (not defined($path)) {
+               usage(1, "missing: --$type\_path <path>");
+       }
+       if ($path eq "" or $path eq "/") {
+               usage(1, "invalid: --$type\_path <path>: '$path'");
+       }
+       my $fullpath = abs_path($path);
+       if (not defined($fullpath)) {
+               usage(1, "invalid: --$type\_path <path>: '$path'");
+       }
+       return $fullpath;
+}
+
+sub passwd_add_entry($$);
+
+sub passwd_load($)
+{
+       my ($path) = @_;
+       my @lines;
+       my $passwd = undef;
+
+       open(PWD, "<$path") or die("Unable to open '$path' for read");
+       @lines = <PWD>;
+       close(PWD);
+
+       $passwd->{array} = ();
+       $passwd->{name} = {};
+       $passwd->{uid} = {};
+       $passwd->{path} = $path;
+
+       foreach my $line (@lines) {
+               passwd_add_entry($passwd, $line);
+       }
+
+       return $passwd;
+}
+
+sub group_add_entry($$);
+
+sub group_load($)
+{
+       my ($path) = @_;
+       my @lines;
+       my $group = undef;
+
+       open(GROUP, "<$path") or die("Unable to open '$path' for read");
+       @lines = <GROUP>;
+       close(GROUP);
+
+       $group->{array} = ();
+       $group->{name} = {};
+       $group->{gid} = {};
+       $group->{path} = $path;
+
+       foreach my $line (@lines) {
+               group_add_entry($group, $line);
+       }
+
+       return $group;
+}
+
+sub passwd_lookup_name($$)
+{
+       my ($passwd, $name) = @_;
+
+       return undef unless defined($passwd->{name}{$name});
+
+       return $passwd->{name}{$name};
+}
+
+sub group_lookup_name($$)
+{
+       my ($group, $name) = @_;
+
+       return undef unless defined($group->{name}{$name});
+
+       return $group->{name}{$name};
+}
+
+sub passwd_lookup_uid($$)
+{
+       my ($passwd, $uid) = @_;
+
+       return undef unless defined($passwd->{uid}{$uid});
+
+       return $passwd->{uid}{$uid};
+}
+
+sub group_lookup_gid($$)
+{
+       my ($group, $gid) = @_;
+
+       return undef unless defined($group->{gid}{$gid});
+
+       return $group->{gid}{$gid};
+}
+
+sub passwd_get_free_uid($)
+{
+       my ($passwd) = @_;
+       my $uid = 1000;
+
+       while (passwd_lookup_uid($passwd, $uid)) {
+               $uid++;
+       }
+
+       return $uid;
+}
+
+sub group_get_free_gid($)
+{
+       my ($group) = @_;
+       my $gid = 1000;
+
+       while (group_lookup_gid($group, $gid)) {
+               $gid++;
+       }
+
+       return $gid;
+}
+
+sub passwd_add_entry($$)
+{
+       my ($passwd, $str) = @_;
+
+       chomp $str;
+       my @e = split(':', $str);
+
+       push(@{$passwd->{array}}, \@e);
+       $passwd->{name}{$e[0]} = \@e;
+       $passwd->{uid}{$e[2]} = \@e;
+}
+
+sub group_add_entry($$)
+{
+       my ($group, $str) = @_;
+
+       chomp $str;
+       my @e = split(':', $str);
+
+       push(@{$group->{array}}, \@e);
+       $group->{name}{$e[0]} = \@e;
+       $group->{gid}{$e[2]} = \@e;
+}
+
+sub passwd_remove_entry($$)
+{
+       my ($passwd, $eref) = @_;
+
+       for (my $i = 0; defined($passwd->{array}[$i]); $i++) {
+               if ($eref == $passwd->{array}[$i]) {
+                       $passwd->{array}[$i] = undef;
+               }
+       }
+
+       delete $passwd->{name}{${$eref}[0]};
+       delete $passwd->{uid}{${$eref}[2]};
+}
+
+sub group_remove_entry($$)
+{
+       my ($group, $eref) = @_;
+
+       for (my $i = 0; defined($group->{array}[$i]); $i++) {
+               if ($eref == $group->{array}[$i]) {
+                       $group->{array}[$i] = undef;
+               }
+       }
+
+       delete $group->{name}{${$eref}[0]};
+       delete $group->{gid}{${$eref}[2]};
+}
+
+sub group_add_member($$$)
+{
+       my ($group, $eref, $username) = @_;
+
+       my @members;
+       my $str = @$eref[3] || undef;
+       if ($str) {
+               @members = split(",", $str);
+       }
+
+       foreach my $member (@members) {
+               if ($member and $member eq $username) {
+                       die("account[$username] is already member of '@$eref[0]'");
+               }
+       }
+
+       push(@members, $username);
+
+       my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @members);
+
+       group_remove_entry($group, $eref);
+
+       group_add_entry($group, $gwent);
+}
+
+sub group_delete_member($$$)
+{
+       my ($group, $eref, $username) = @_;
+
+       my @members = undef;
+       my $str = @$eref[3] || undef;
+       if ($str) {
+               @members = split(",", $str);
+       }
+       my @new_members;
+       my $removed = 0;
+
+       foreach my $member (@members) {
+               if ($member and $member ne $username) {
+                       push(@new_members, $member);
+               } else {
+                       $removed = 1;
+               }
+       }
+
+       if ($removed != 1) {
+               die("account[$username] is not member of '@$eref[0]'");
+       }
+
+       my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @new_members);
+
+       group_remove_entry($group, $eref);
+
+       group_add_entry($group, $gwent);
+}
+
+sub passwd_save($)
+{
+       my ($passwd) = @_;
+       my @lines = ();
+       my $path = $passwd->{path};
+       my $tmppath = $path.$$;
+
+       foreach my $eref (@{$passwd->{array}}) {
+               next unless defined($eref);
+
+               my $line = join(':', @{$eref});
+               push(@lines, $line);
+       }
+
+       open(PWD, ">$tmppath") or die("Unable to open '$tmppath' for write");
+       print PWD join("\n", @lines)."\n";
+       close(PWD);
+       rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
+}
+
+sub group_save($)
+{
+       my ($group) = @_;
+       my @lines = ();
+       my $path = $group->{path};
+       my $tmppath = $path.$$;
+
+       foreach my $eref (@{$group->{array}}) {
+               next unless defined($eref);
+
+               my $line = join(':', @{$eref});
+               if (scalar(@{$eref}) == 3) {
+                       $line .= ":";
+               }
+               push(@lines, $line);
+       }
+
+       open(GROUP, ">$tmppath") or die("Unable to open '$tmppath' for write");
+       print GROUP join("\n", @lines)."\n";
+       close(GROUP);
+       rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
+}
+
+sub passwd_add($$$$$)
+{
+       my ($path, $dummy, $dummy2, $name, $gid) = @_;
+
+       #print "passwd_add: '$name' in '$path'\n";
+
+       my $passwd = passwd_load($path);
+
+       my $e = passwd_lookup_name($passwd, $name);
+       die("account[$name] already exists in '$path'") if defined($e);
+
+       my $uid = passwd_get_free_uid($passwd);
+
+       my $pwent = $name.":x:".$uid.":".$gid.":".$name." gecos:/nodir:/bin/false";
+
+       passwd_add_entry($passwd, $pwent);
+
+       passwd_save($passwd);
+
+       return 0;
+}
+
+sub passwd_delete($$$$$)
+{
+       my ($path, $dummy, $dummy2, $name, $dummy3) = @_;
+
+       #print "passwd_delete: '$name' in '$path'\n";
+
+       my $passwd = passwd_load($path);
+
+       my $e = passwd_lookup_name($passwd, $name);
+       die("account[$name] does not exists in '$path'") unless defined($e);
+
+       passwd_remove_entry($passwd, $e);
+
+       passwd_save($passwd);
+
+       return 0;
+}
+
+sub group_add($$$$$)
+{
+       my ($dummy, $dummy2, $path, $name, $dummy3) = @_;
+
+       #print "group_add: '$name' in '$path'\n";
+
+       my $group = group_load($path);
+
+       my $e = group_lookup_name($group, $name);
+       die("group[$name] already exists in '$path'") if defined($e);
+
+       my $gid = group_get_free_gid($group);
+
+       my $gwent = $name.":x:".$gid.":"."";
+
+       group_add_entry($group, $gwent);
+
+       group_save($group);
+
+       #printf("%d\n", $gid);
+
+       return 0;
+}
+
+sub group_delete($$$$$)
+{
+       my ($dummy, $dummy2, $path, $name, $dummy3) = @_;
+
+       #print "group_delete: '$name' in '$path'\n";
+
+       my $group = group_load($path);
+
+       my $e = group_lookup_name($group, $name);
+       die("group[$name] does not exists in '$path'") unless defined($e);
+
+       group_remove_entry($group, $e);
+
+       group_save($group);
+
+       return 0;
+}
+
+sub member_add($$$$$)
+{
+       my ($passwd_path, $username, $group_path, $groupname, $dummy) = @_;
+
+       #print "member_add: adding '$username' in '$passwd_path' to '$groupname' in '$group_path'\n";
+
+       my $group = group_load($group_path);
+
+       my $g = group_lookup_name($group, $groupname);
+       die("group[$groupname] does not exists in '$group_path'") unless defined($g);
+
+       my $passwd = passwd_load($passwd_path);
+
+       my $u = passwd_lookup_name($passwd, $username);
+       die("account[$username] does not exists in '$passwd_path'") unless defined($u);
+
+       group_add_member($group, $g, $username);
+
+       group_save($group);
+
+       return 0;
+}
+
+sub member_delete($$$$$)
+{
+       my ($passwd_path, $username, $group_path, $groupname, $dummy) = @_;
+
+       #print "member_delete: removing '$username' in '$passwd_path' from '$groupname' in '$group_path'\n";
+
+       my $group = group_load($group_path);
+
+       my $g = group_lookup_name($group, $groupname);
+       die("group[$groupname] does not exists in '$group_path'") unless defined($g);
+
+       my $passwd = passwd_load($passwd_path);
+
+       my $u = passwd_lookup_name($passwd, $username);
+       die("account[$username] does not exists in '$passwd_path'") unless defined($u);
+
+       group_delete_member($group, $g, $username);
+
+       group_save($group);
+
+       return 0;
+}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ac27681
--- /dev/null
@@ -0,0 +1,12 @@
+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}
+)
diff --git a/src/nss_wrapper.c b/src/nss_wrapper.c
new file mode 100644 (file)
index 0000000..e6393b5
--- /dev/null
@@ -0,0 +1,2234 @@
+/*
+ * 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;
+}
diff --git a/src/nss_wrapper.h b/src/nss_wrapper.h
new file mode 100644 (file)
index 0000000..5bcd42e
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * 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__ */
diff --git a/src/nss_wrapper.pl b/src/nss_wrapper.pl
new file mode 100755 (executable)
index 0000000..48fa2c5
--- /dev/null
@@ -0,0 +1,521 @@
+#!/usr/bin/perl
+#
+
+use strict;
+
+use Getopt::Long;
+use Cwd qw(abs_path);
+
+my $opt_help = 0;
+my $opt_passwd_path = undef;
+my $opt_group_path = undef;
+my $opt_action = undef;
+my $opt_type = undef;
+my $opt_name = undef;
+my $opt_member = undef;
+my $opt_gid = 65534;# nogroup gid
+
+my $passwdfn = undef;
+my $groupfn = undef;
+my $memberfn = undef;
+my $actionfn = undef;
+
+sub passwd_add($$$$$);
+sub passwd_delete($$$$$);
+sub group_add($$$$$);
+sub group_delete($$$$$);
+sub member_add($$$$$);
+sub member_delete($$$$$);
+
+sub check_path($$);
+
+my $result = GetOptions(
+       'help|h|?'      => \$opt_help,
+       'passwd_path=s' => \$opt_passwd_path,
+       'group_path=s'  => \$opt_group_path,
+       'action=s'      => \$opt_action,
+       'type=s'        => \$opt_type,
+       'name=s'        => \$opt_name,
+       'member=s'      => \$opt_member,
+       'gid=i'         => \$opt_gid
+);
+
+sub usage($;$)
+{
+       my ($ret, $msg) = @_;
+
+       print $msg."\n\n" if defined($msg);
+
+       print "usage:
+
+       --help|-h|-?            Show this help.
+
+       --passwd_path <path>    Path of the 'passwd' file.
+       --group_path <path>     Path of the 'group' file.
+
+       --type <type>           'passwd', 'group' and 'member' are supported.
+
+       --action <action>       'add' or 'delete'.
+
+       --name <name>           The name of the object.
+
+       --member <member>       The name of the member.
+
+       --gid <gid>             Primary Group ID for new users.
+";
+       exit($ret);
+}
+
+usage(1) if (not $result);
+
+usage(0) if ($opt_help);
+
+if (not defined($opt_action)) {
+       usage(1, "missing: --action [add|delete]");
+}
+if ($opt_action eq "add") {
+       $passwdfn = \&passwd_add;
+       $groupfn = \&group_add;
+       $memberfn = \&member_add;
+} elsif ($opt_action eq "delete") {
+       $passwdfn = \&passwd_delete;
+       $groupfn = \&group_delete;
+       $memberfn = \&member_delete;
+} else {
+       usage(1, "invalid: --action [add|delete]: '$opt_action'");
+}
+
+if (not defined($opt_type)) {
+       usage(1, "missing: --type [passwd|group|member]");
+}
+if ($opt_type eq "member" and not defined($opt_member)) {
+       usage(1, "missing: --member <member>");
+}
+my $opt_fullpath_passwd;
+my $opt_fullpath_group;
+if ($opt_type eq "passwd") {
+       $actionfn = $passwdfn;
+       $opt_fullpath_passwd = check_path($opt_passwd_path, $opt_type);
+} elsif ($opt_type eq "group") {
+       $actionfn = $groupfn;
+       $opt_fullpath_group = check_path($opt_group_path, $opt_type);
+} elsif ($opt_type eq "member") {
+       $actionfn = $memberfn;
+       $opt_fullpath_passwd = check_path($opt_passwd_path, "passwd");
+       $opt_fullpath_group = check_path($opt_group_path, "group");
+} else {
+       usage(1, "invalid: --type [passwd|group]: '$opt_type'")
+}
+
+if (not defined($opt_name)) {
+       usage(1, "missing: --name <name>");
+}
+if ($opt_name eq "") {
+       usage(1, "invalid: --name <name>");
+}
+
+exit $actionfn->($opt_fullpath_passwd, $opt_member, $opt_fullpath_group, $opt_name, $opt_gid);
+
+sub check_path($$)
+{
+       my ($path,$type) = @_;
+
+       if (not defined($path)) {
+               usage(1, "missing: --$type\_path <path>");
+       }
+       if ($path eq "" or $path eq "/") {
+               usage(1, "invalid: --$type\_path <path>: '$path'");
+       }
+       my $fullpath = abs_path($path);
+       if (not defined($fullpath)) {
+               usage(1, "invalid: --$type\_path <path>: '$path'");
+       }
+       return $fullpath;
+}
+
+sub passwd_add_entry($$);
+
+sub passwd_load($)
+{
+       my ($path) = @_;
+       my @lines;
+       my $passwd = undef;
+
+       open(PWD, "<$path") or die("Unable to open '$path' for read");
+       @lines = <PWD>;
+       close(PWD);
+
+       $passwd->{array} = ();
+       $passwd->{name} = {};
+       $passwd->{uid} = {};
+       $passwd->{path} = $path;
+
+       foreach my $line (@lines) {
+               passwd_add_entry($passwd, $line);
+       }
+
+       return $passwd;
+}
+
+sub group_add_entry($$);
+
+sub group_load($)
+{
+       my ($path) = @_;
+       my @lines;
+       my $group = undef;
+
+       open(GROUP, "<$path") or die("Unable to open '$path' for read");
+       @lines = <GROUP>;
+       close(GROUP);
+
+       $group->{array} = ();
+       $group->{name} = {};
+       $group->{gid} = {};
+       $group->{path} = $path;
+
+       foreach my $line (@lines) {
+               group_add_entry($group, $line);
+       }
+
+       return $group;
+}
+
+sub passwd_lookup_name($$)
+{
+       my ($passwd, $name) = @_;
+
+       return undef unless defined($passwd->{name}{$name});
+
+       return $passwd->{name}{$name};
+}
+
+sub group_lookup_name($$)
+{
+       my ($group, $name) = @_;
+
+       return undef unless defined($group->{name}{$name});
+
+       return $group->{name}{$name};
+}
+
+sub passwd_lookup_uid($$)
+{
+       my ($passwd, $uid) = @_;
+
+       return undef unless defined($passwd->{uid}{$uid});
+
+       return $passwd->{uid}{$uid};
+}
+
+sub group_lookup_gid($$)
+{
+       my ($group, $gid) = @_;
+
+       return undef unless defined($group->{gid}{$gid});
+
+       return $group->{gid}{$gid};
+}
+
+sub passwd_get_free_uid($)
+{
+       my ($passwd) = @_;
+       my $uid = 1000;
+
+       while (passwd_lookup_uid($passwd, $uid)) {
+               $uid++;
+       }
+
+       return $uid;
+}
+
+sub group_get_free_gid($)
+{
+       my ($group) = @_;
+       my $gid = 1000;
+
+       while (group_lookup_gid($group, $gid)) {
+               $gid++;
+       }
+
+       return $gid;
+}
+
+sub passwd_add_entry($$)
+{
+       my ($passwd, $str) = @_;
+
+       chomp $str;
+       my @e = split(':', $str);
+
+       push(@{$passwd->{array}}, \@e);
+       $passwd->{name}{$e[0]} = \@e;
+       $passwd->{uid}{$e[2]} = \@e;
+}
+
+sub group_add_entry($$)
+{
+       my ($group, $str) = @_;
+
+       chomp $str;
+       my @e = split(':', $str);
+
+       push(@{$group->{array}}, \@e);
+       $group->{name}{$e[0]} = \@e;
+       $group->{gid}{$e[2]} = \@e;
+}
+
+sub passwd_remove_entry($$)
+{
+       my ($passwd, $eref) = @_;
+
+       for (my $i = 0; defined($passwd->{array}[$i]); $i++) {
+               if ($eref == $passwd->{array}[$i]) {
+                       $passwd->{array}[$i] = undef;
+               }
+       }
+
+       delete $passwd->{name}{${$eref}[0]};
+       delete $passwd->{uid}{${$eref}[2]};
+}
+
+sub group_remove_entry($$)
+{
+       my ($group, $eref) = @_;
+
+       for (my $i = 0; defined($group->{array}[$i]); $i++) {
+               if ($eref == $group->{array}[$i]) {
+                       $group->{array}[$i] = undef;
+               }
+       }
+
+       delete $group->{name}{${$eref}[0]};
+       delete $group->{gid}{${$eref}[2]};
+}
+
+sub group_add_member($$$)
+{
+       my ($group, $eref, $username) = @_;
+
+       my @members;
+       my $str = @$eref[3] || undef;
+       if ($str) {
+               @members = split(",", $str);
+       }
+
+       foreach my $member (@members) {
+               if ($member and $member eq $username) {
+                       die("account[$username] is already member of '@$eref[0]'");
+               }
+       }
+
+       push(@members, $username);
+
+       my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @members);
+
+       group_remove_entry($group, $eref);
+
+       group_add_entry($group, $gwent);
+}
+
+sub group_delete_member($$$)
+{
+       my ($group, $eref, $username) = @_;
+
+       my @members = undef;
+       my $str = @$eref[3] || undef;
+       if ($str) {
+               @members = split(",", $str);
+       }
+       my @new_members;
+       my $removed = 0;
+
+       foreach my $member (@members) {
+               if ($member and $member ne $username) {
+                       push(@new_members, $member);
+               } else {
+                       $removed = 1;
+               }
+       }
+
+       if ($removed != 1) {
+               die("account[$username] is not member of '@$eref[0]'");
+       }
+
+       my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @new_members);
+
+       group_remove_entry($group, $eref);
+
+       group_add_entry($group, $gwent);
+}
+
+sub passwd_save($)
+{
+       my ($passwd) = @_;
+       my @lines = ();
+       my $path = $passwd->{path};
+       my $tmppath = $path.$$;
+
+       foreach my $eref (@{$passwd->{array}}) {
+               next unless defined($eref);
+
+               my $line = join(':', @{$eref});
+               push(@lines, $line);
+       }
+
+       open(PWD, ">$tmppath") or die("Unable to open '$tmppath' for write");
+       print PWD join("\n", @lines)."\n";
+       close(PWD);
+       rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
+}
+
+sub group_save($)
+{
+       my ($group) = @_;
+       my @lines = ();
+       my $path = $group->{path};
+       my $tmppath = $path.$$;
+
+       foreach my $eref (@{$group->{array}}) {
+               next unless defined($eref);
+
+               my $line = join(':', @{$eref});
+               if (scalar(@{$eref}) == 3) {
+                       $line .= ":";
+               }
+               push(@lines, $line);
+       }
+
+       open(GROUP, ">$tmppath") or die("Unable to open '$tmppath' for write");
+       print GROUP join("\n", @lines)."\n";
+       close(GROUP);
+       rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
+}
+
+sub passwd_add($$$$$)
+{
+       my ($path, $dummy, $dummy2, $name, $gid) = @_;
+
+       #print "passwd_add: '$name' in '$path'\n";
+
+       my $passwd = passwd_load($path);
+
+       my $e = passwd_lookup_name($passwd, $name);
+       die("account[$name] already exists in '$path'") if defined($e);
+
+       my $uid = passwd_get_free_uid($passwd);
+
+       my $pwent = $name.":x:".$uid.":".$gid.":".$name." gecos:/nodir:/bin/false";
+
+       passwd_add_entry($passwd, $pwent);
+
+       passwd_save($passwd);
+
+       return 0;
+}
+
+sub passwd_delete($$$$$)
+{
+       my ($path, $dummy, $dummy2, $name, $dummy3) = @_;
+
+       #print "passwd_delete: '$name' in '$path'\n";
+
+       my $passwd = passwd_load($path);
+
+       my $e = passwd_lookup_name($passwd, $name);
+       die("account[$name] does not exists in '$path'") unless defined($e);
+
+       passwd_remove_entry($passwd, $e);
+
+       passwd_save($passwd);
+
+       return 0;
+}
+
+sub group_add($$$$$)
+{
+       my ($dummy, $dummy2, $path, $name, $dummy3) = @_;
+
+       #print "group_add: '$name' in '$path'\n";
+
+       my $group = group_load($path);
+
+       my $e = group_lookup_name($group, $name);
+       die("group[$name] already exists in '$path'") if defined($e);
+
+       my $gid = group_get_free_gid($group);
+
+       my $gwent = $name.":x:".$gid.":"."";
+
+       group_add_entry($group, $gwent);
+
+       group_save($group);
+
+       #printf("%d\n", $gid);
+
+       return 0;
+}
+
+sub group_delete($$$$$)
+{
+       my ($dummy, $dummy2, $path, $name, $dummy3) = @_;
+
+       #print "group_delete: '$name' in '$path'\n";
+
+       my $group = group_load($path);
+
+       my $e = group_lookup_name($group, $name);
+       die("group[$name] does not exists in '$path'") unless defined($e);
+
+       group_remove_entry($group, $e);
+
+       group_save($group);
+
+       return 0;
+}
+
+sub member_add($$$$$)
+{
+       my ($passwd_path, $username, $group_path, $groupname, $dummy) = @_;
+
+       #print "member_add: adding '$username' in '$passwd_path' to '$groupname' in '$group_path'\n";
+
+       my $group = group_load($group_path);
+
+       my $g = group_lookup_name($group, $groupname);
+       die("group[$groupname] does not exists in '$group_path'") unless defined($g);
+
+       my $passwd = passwd_load($passwd_path);
+
+       my $u = passwd_lookup_name($passwd, $username);
+       die("account[$username] does not exists in '$passwd_path'") unless defined($u);
+
+       group_add_member($group, $g, $username);
+
+       group_save($group);
+
+       return 0;
+}
+
+sub member_delete($$$$$)
+{
+       my ($passwd_path, $username, $group_path, $groupname, $dummy) = @_;
+
+       #print "member_delete: removing '$username' in '$passwd_path' from '$groupname' in '$group_path'\n";
+
+       my $group = group_load($group_path);
+
+       my $g = group_lookup_name($group, $groupname);
+       die("group[$groupname] does not exists in '$group_path'") unless defined($g);
+
+       my $passwd = passwd_load($passwd_path);
+
+       my $u = passwd_lookup_name($passwd, $username);
+       die("account[$username] does not exists in '$passwd_path'") unless defined($u);
+
+       group_delete_member($group, $g, $username);
+
+       group_save($group);
+
+       return 0;
+}
diff --git a/tests/testsuite.c b/tests/testsuite.c
new file mode 100644 (file)
index 0000000..3d3f748
--- /dev/null
@@ -0,0 +1,957 @@
+/*
+   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, &current_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;
+}