summaryrefslogtreecommitdiff
path: root/nsswitch
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2008-12-17 07:17:54 +1100
committerAndrew Tridgell <tridge@samba.org>2008-12-17 07:17:54 +1100
commit1b3a084d60cc0004f84bc56dedbe1d87cda2a8b3 (patch)
treecee337a9c93890605fb12490faec2a7f0b2188d8 /nsswitch
parente294c4799bf6ad8da043aadf8341391644147056 (diff)
parent54dc421f5820099531a77879f52a904c2fefcf49 (diff)
downloadsamba-1b3a084d60cc0004f84bc56dedbe1d87cda2a8b3.tar.gz
samba-1b3a084d60cc0004f84bc56dedbe1d87cda2a8b3.tar.bz2
samba-1b3a084d60cc0004f84bc56dedbe1d87cda2a8b3.zip
Merge branch 'master' of ssh://git.samba.org/data/git/samba
Diffstat (limited to 'nsswitch')
-rw-r--r--nsswitch/config.m411
-rw-r--r--nsswitch/config.mk34
-rw-r--r--nsswitch/libwbclient/Doxyfile1297
-rw-r--r--nsswitch/libwbclient/libwbclient.h46
-rw-r--r--nsswitch/libwbclient/wbc_err_internal.h45
-rw-r--r--nsswitch/libwbclient/wbc_guid.c104
-rw-r--r--nsswitch/libwbclient/wbc_idmap.c468
-rw-r--r--nsswitch/libwbclient/wbc_pam.c1034
-rw-r--r--nsswitch/libwbclient/wbc_pwd.c571
-rw-r--r--nsswitch/libwbclient/wbc_sid.c672
-rw-r--r--nsswitch/libwbclient/wbc_util.c672
-rw-r--r--nsswitch/libwbclient/wbclient.c157
-rw-r--r--nsswitch/libwbclient/wbclient.h1187
-rw-r--r--nsswitch/libwbclient/wbclient_internal.h32
-rw-r--r--nsswitch/nsstest.c411
-rw-r--r--nsswitch/nsstest.h115
-rw-r--r--nsswitch/nsstest.m48
-rw-r--r--nsswitch/pam_winbind.c3204
-rw-r--r--nsswitch/pam_winbind.h169
-rwxr-xr-xnsswitch/tests/test_wbinfo.sh186
-rw-r--r--nsswitch/wb_common.c690
-rw-r--r--nsswitch/wbinfo.c1984
-rw-r--r--nsswitch/wbinfo4.c1294
-rw-r--r--nsswitch/winbind_client.h30
-rw-r--r--nsswitch/winbind_krb5_locator.c411
-rw-r--r--nsswitch/winbind_nss.h76
-rw-r--r--nsswitch/winbind_nss_aix.c1076
-rw-r--r--nsswitch/winbind_nss_config.h70
-rw-r--r--nsswitch/winbind_nss_freebsd.c79
-rw-r--r--nsswitch/winbind_nss_hpux.h137
-rw-r--r--nsswitch/winbind_nss_irix.c633
-rw-r--r--nsswitch/winbind_nss_irix.h42
-rw-r--r--nsswitch/winbind_nss_linux.c1477
-rw-r--r--nsswitch/winbind_nss_linux.h29
-rw-r--r--nsswitch/winbind_nss_netbsd.c442
-rw-r--r--nsswitch/winbind_nss_netbsd.h40
-rw-r--r--nsswitch/winbind_nss_solaris.c659
-rw-r--r--nsswitch/winbind_nss_solaris.h88
-rw-r--r--nsswitch/winbind_struct_protocol.h514
-rw-r--r--nsswitch/wins.c446
40 files changed, 20640 insertions, 0 deletions
diff --git a/nsswitch/config.m4 b/nsswitch/config.m4
new file mode 100644
index 0000000000..2c8fa17ad9
--- /dev/null
+++ b/nsswitch/config.m4
@@ -0,0 +1,11 @@
+AC_CHECK_HEADERS(nss.h nss_common.h ns_api.h )
+
+case "$host_os" in
+ *linux*)
+ SMB_LIBRARY(nss_winbind,
+ [../nsswitch/winbind_nss_linux.o],
+ [LIBWINBIND-CLIENT])
+ ;;
+ *)
+ ;;
+esac
diff --git a/nsswitch/config.mk b/nsswitch/config.mk
new file mode 100644
index 0000000000..3a4f054d1f
--- /dev/null
+++ b/nsswitch/config.mk
@@ -0,0 +1,34 @@
+[SUBSYSTEM::LIBWINBIND-CLIENT]
+PRIVATE_DEPENDENCIES = SOCKET_WRAPPER
+
+LIBWINBIND-CLIENT_OBJ_FILES = $(nsswitchsrcdir)/wb_common.o
+
+#################################
+# Start BINARY nsstest
+[BINARY::nsstest]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-UTIL \
+ LIBREPLACE_EXT \
+ LIBSAMBA-HOSTCONFIG
+# End BINARY nsstest
+#################################
+
+nsstest_OBJ_FILES = $(nsswitchsrcdir)/nsstest.o
+
+#################################
+# Start BINARY wbinfo
+[BINARY::wbinfo]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-UTIL \
+ LIBREPLACE_EXT \
+ LIBCLI_AUTH \
+ LIBPOPT \
+ POPT_SAMBA \
+ LIBWINBIND-CLIENT
+# End BINARY nsstest
+#################################
+
+wbinfo_OBJ_FILES = \
+ $(nsswitchsrcdir)/wbinfo4.o
diff --git a/nsswitch/libwbclient/Doxyfile b/nsswitch/libwbclient/Doxyfile
new file mode 100644
index 0000000000..529a16fd6a
--- /dev/null
+++ b/nsswitch/libwbclient/Doxyfile
@@ -0,0 +1,1297 @@
+# Doxyfile 1.5.3
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file that
+# follow. The default is UTF-8 which is also the encoding used for all text before
+# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into
+# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of
+# possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = Samba
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = HEAD
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = dox
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
+# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
+# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
+# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH = $(PWD)/
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to
+# include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be extracted
+# and appear in the documentation as a namespace called 'anonymous_namespace{file}',
+# where file will be replaced with the base name of the file that contains the anonymous
+# namespace. By default anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from the
+# version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = NO
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = NO
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text "
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = .
+
+# This tag can be used to specify the character encoding of the source files that
+# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default
+# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding.
+# See http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
+
+FILE_PATTERNS = *.c \
+ *.h \
+ *.idl
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE = include/includes.h \
+ include/proto.h
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the output.
+# The symbol name can be a fully qualified name, a word, or if the wildcard * is used,
+# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH
+# then you must also enable this option. If you don't then doxygen will produce
+# a warning and turn it on anyway
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = YES
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 1
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = .
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 3
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = YES
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = NO
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to
+# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to
+# specify the directory where the mscgen tool resides. If left empty the tool is assumed to
+# be found in the default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will
+# generate a caller dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the number
+# of direct children of the root node in a graph is already larger than
+# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, which results in a white background.
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
diff --git a/nsswitch/libwbclient/libwbclient.h b/nsswitch/libwbclient/libwbclient.h
new file mode 100644
index 0000000000..74cba7e796
--- /dev/null
+++ b/nsswitch/libwbclient/libwbclient.h
@@ -0,0 +1,46 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBWBCLIENT_H
+#define _LIBWBCLIENT_H
+
+/* Super header including necessary public and private header files
+ for building the wbclient library. __DO NOT__ define anything
+ in this file. Only include other headers. */
+
+/* Winbind headers */
+
+#include "nsswitch/winbind_nss_config.h"
+#include "nsswitch/winbind_struct_protocol.h"
+
+#include <talloc.h>
+
+/* Public headers */
+
+#include "wbclient.h"
+
+/* Private headers */
+
+#include "wbc_err_internal.h"
+#include "wbclient_internal.h"
+
+
+#endif /* _LIBWBCLIENT_H */
diff --git a/nsswitch/libwbclient/wbc_err_internal.h b/nsswitch/libwbclient/wbc_err_internal.h
new file mode 100644
index 0000000000..83364b8cd9
--- /dev/null
+++ b/nsswitch/libwbclient/wbc_err_internal.h
@@ -0,0 +1,45 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WBC_ERR_INTERNAL_H
+#define _WBC_ERR_INTERNAL_H
+
+/* Private macros */
+
+#define BAIL_ON_WBC_ERROR(x) \
+ do { \
+ if (!WBC_ERROR_IS_OK(x)) { \
+ goto done; \
+ } \
+ } while(0);
+
+#define BAIL_ON_PTR_ERROR(x, status) \
+ do { \
+ if ((x) == NULL) { \
+ status = WBC_ERR_NO_MEMORY; \
+ goto done; \
+ } else { \
+ status = WBC_ERR_SUCCESS; \
+ } \
+ } while (0);
+
+
+#endif /* _WBC_ERR_INTERNAL_H */
diff --git a/nsswitch/libwbclient/wbc_guid.c b/nsswitch/libwbclient/wbc_guid.c
new file mode 100644
index 0000000000..c343e24351
--- /dev/null
+++ b/nsswitch/libwbclient/wbc_guid.c
@@ -0,0 +1,104 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Required Headers */
+
+#include "libwbclient.h"
+
+/* Convert a binary GUID to a character string */
+wbcErr wbcGuidToString(const struct wbcGuid *guid,
+ char **guid_string)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!guid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ *guid_string = talloc_asprintf(NULL,
+ "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ guid->time_low, guid->time_mid,
+ guid->time_hi_and_version,
+ guid->clock_seq[0],
+ guid->clock_seq[1],
+ guid->node[0], guid->node[1],
+ guid->node[2], guid->node[3],
+ guid->node[4], guid->node[5]);
+ BAIL_ON_PTR_ERROR((*guid_string), wbc_status);
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+done:
+ return wbc_status;
+}
+
+/* @brief Convert a character string to a binary GUID */
+wbcErr wbcStringToGuid(const char *str,
+ struct wbcGuid *guid)
+{
+ uint32_t time_low;
+ uint32_t time_mid, time_hi_and_version;
+ uint32_t clock_seq[2];
+ uint32_t node[6];
+ int i;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!guid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (!str) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (11 == sscanf(str, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ &time_low, &time_mid, &time_hi_and_version,
+ &clock_seq[0], &clock_seq[1],
+ &node[0], &node[1], &node[2], &node[3], &node[4], &node[5])) {
+ wbc_status = WBC_ERR_SUCCESS;
+ } else if (11 == sscanf(str, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
+ &time_low, &time_mid, &time_hi_and_version,
+ &clock_seq[0], &clock_seq[1],
+ &node[0], &node[1], &node[2], &node[3], &node[4], &node[5])) {
+ wbc_status = WBC_ERR_SUCCESS;
+ }
+
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ guid->time_low = time_low;
+ guid->time_mid = time_mid;
+ guid->time_hi_and_version = time_hi_and_version;
+ guid->clock_seq[0] = clock_seq[0];
+ guid->clock_seq[1] = clock_seq[1];
+
+ for (i=0;i<6;i++) {
+ guid->node[i] = node[i];
+ }
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+done:
+ return wbc_status;
+}
diff --git a/nsswitch/libwbclient/wbc_idmap.c b/nsswitch/libwbclient/wbc_idmap.c
new file mode 100644
index 0000000000..5b2ab875f6
--- /dev/null
+++ b/nsswitch/libwbclient/wbc_idmap.c
@@ -0,0 +1,468 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Required Headers */
+
+#include "libwbclient.h"
+
+/* Convert a Windows SID to a Unix uid, allocating an uid if needed */
+wbcErr wbcSidToUid(const struct wbcDomainSid *sid, uid_t *puid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *sid_string = NULL;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!sid || !puid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ wbc_status = wbcSidToString(sid, &sid_string);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
+ wbcFreeMemory(sid_string);
+
+ /* Make request */
+
+ wbc_status = wbcRequestResponse(WINBINDD_SID_TO_UID,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ *puid = response.data.uid;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ return wbc_status;
+}
+
+/* Convert a Windows SID to a Unix uid if there already is a mapping */
+wbcErr wbcQuerySidToUid(const struct wbcDomainSid *sid,
+ uid_t *puid)
+{
+ return WBC_ERR_NOT_IMPLEMENTED;
+}
+
+/* Convert a Unix uid to a Windows SID, allocating a SID if needed */
+wbcErr wbcUidToSid(uid_t uid, struct wbcDomainSid *sid)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ if (!sid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.uid = uid;
+
+ /* Make request */
+
+ wbc_status = wbcRequestResponse(WINBINDD_UID_TO_SID,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ wbc_status = wbcStringToSid(response.data.sid.sid, sid);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+done:
+ return wbc_status;
+}
+
+/* Convert a Unix uid to a Windows SID if there already is a mapping */
+wbcErr wbcQueryUidToSid(uid_t uid,
+ struct wbcDomainSid *sid)
+{
+ return WBC_ERR_NOT_IMPLEMENTED;
+}
+
+/** @brief Convert a Windows SID to a Unix gid, allocating a gid if needed
+ *
+ * @param *sid Pointer to the domain SID to be resolved
+ * @param *pgid Pointer to the resolved gid_t value
+ *
+ * @return #wbcErr
+ *
+ **/
+
+wbcErr wbcSidToGid(const struct wbcDomainSid *sid, gid_t *pgid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *sid_string = NULL;
+
+ if (!sid || !pgid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ wbc_status = wbcSidToString(sid, &sid_string);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
+ wbcFreeMemory(sid_string);
+
+ /* Make request */
+
+ wbc_status = wbcRequestResponse(WINBINDD_SID_TO_GID,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ *pgid = response.data.gid;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ return wbc_status;
+}
+
+/* Convert a Windows SID to a Unix gid if there already is a mapping */
+
+wbcErr wbcQuerySidToGid(const struct wbcDomainSid *sid,
+ gid_t *pgid)
+{
+ return WBC_ERR_NOT_IMPLEMENTED;
+}
+
+/* Convert a Unix gid to a Windows SID, allocating a SID if needed */
+wbcErr wbcGidToSid(gid_t gid, struct wbcDomainSid *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!sid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.gid = gid;
+
+ /* Make request */
+
+ wbc_status = wbcRequestResponse(WINBINDD_GID_TO_SID,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ wbc_status = wbcStringToSid(response.data.sid.sid, sid);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+done:
+ return wbc_status;
+}
+
+/* Convert a Unix gid to a Windows SID if there already is a mapping */
+wbcErr wbcQueryGidToSid(gid_t gid,
+ struct wbcDomainSid *sid)
+{
+ return WBC_ERR_NOT_IMPLEMENTED;
+}
+
+/* Obtain a new uid from Winbind */
+wbcErr wbcAllocateUid(uid_t *puid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!puid)
+ return WBC_ERR_INVALID_PARAM;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Make request */
+
+ wbc_status = wbcRequestResponse(WINBINDD_ALLOCATE_UID,
+ &request, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ /* Copy out result */
+ *puid = response.data.uid;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ return wbc_status;
+}
+
+/* Obtain a new gid from Winbind */
+wbcErr wbcAllocateGid(gid_t *pgid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!pgid)
+ return WBC_ERR_INVALID_PARAM;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Make request */
+
+ wbc_status = wbcRequestResponse(WINBINDD_ALLOCATE_GID,
+ &request, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ /* Copy out result */
+ *pgid = response.data.gid;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ return wbc_status;
+}
+
+/* we can't include smb.h here... */
+#define _ID_TYPE_UID 1
+#define _ID_TYPE_GID 2
+
+/* Set an user id mapping */
+wbcErr wbcSetUidMapping(uid_t uid, const struct wbcDomainSid *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *sid_string = NULL;
+
+ if (!sid) {
+ return WBC_ERR_INVALID_PARAM;
+ }
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Make request */
+
+ request.data.dual_idmapset.id = uid;
+ request.data.dual_idmapset.type = _ID_TYPE_UID;
+
+ wbc_status = wbcSidToString(sid, &sid_string);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ strncpy(request.data.dual_idmapset.sid, sid_string,
+ sizeof(request.data.dual_idmapset.sid)-1);
+ wbcFreeMemory(sid_string);
+
+ wbc_status = wbcRequestResponse(WINBINDD_SET_MAPPING,
+ &request, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+/* Set a group id mapping */
+wbcErr wbcSetGidMapping(gid_t gid, const struct wbcDomainSid *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *sid_string = NULL;
+
+ if (!sid) {
+ return WBC_ERR_INVALID_PARAM;
+ }
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Make request */
+
+ request.data.dual_idmapset.id = gid;
+ request.data.dual_idmapset.type = _ID_TYPE_GID;
+
+ wbc_status = wbcSidToString(sid, &sid_string);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ strncpy(request.data.dual_idmapset.sid, sid_string,
+ sizeof(request.data.dual_idmapset.sid)-1);
+ wbcFreeMemory(sid_string);
+
+ wbc_status = wbcRequestResponse(WINBINDD_SET_MAPPING,
+ &request, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+/* Remove a user id mapping */
+wbcErr wbcRemoveUidMapping(uid_t uid, const struct wbcDomainSid *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *sid_string = NULL;
+
+ if (!sid) {
+ return WBC_ERR_INVALID_PARAM;
+ }
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Make request */
+
+ request.data.dual_idmapset.id = uid;
+ request.data.dual_idmapset.type = _ID_TYPE_UID;
+
+ wbc_status = wbcSidToString(sid, &sid_string);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ strncpy(request.data.dual_idmapset.sid, sid_string,
+ sizeof(request.data.dual_idmapset.sid)-1);
+ wbcFreeMemory(sid_string);
+
+ wbc_status = wbcRequestResponse(WINBINDD_REMOVE_MAPPING,
+ &request, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+/* Remove a group id mapping */
+wbcErr wbcRemoveGidMapping(gid_t gid, const struct wbcDomainSid *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *sid_string = NULL;
+
+ if (!sid) {
+ return WBC_ERR_INVALID_PARAM;
+ }
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Make request */
+
+ request.data.dual_idmapset.id = gid;
+ request.data.dual_idmapset.type = _ID_TYPE_GID;
+
+ wbc_status = wbcSidToString(sid, &sid_string);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ strncpy(request.data.dual_idmapset.sid, sid_string,
+ sizeof(request.data.dual_idmapset.sid)-1);
+ wbcFreeMemory(sid_string);
+
+ wbc_status = wbcRequestResponse(WINBINDD_REMOVE_MAPPING,
+ &request, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+/* Set the highwater mark for allocated uids. */
+wbcErr wbcSetUidHwm(uid_t uid_hwm)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Make request */
+
+ request.data.dual_idmapset.id = uid_hwm;
+ request.data.dual_idmapset.type = _ID_TYPE_UID;
+
+ wbc_status = wbcRequestResponse(WINBINDD_SET_HWM,
+ &request, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+/* Set the highwater mark for allocated gids. */
+wbcErr wbcSetGidHwm(gid_t gid_hwm)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Make request */
+
+ request.data.dual_idmapset.id = gid_hwm;
+ request.data.dual_idmapset.type = _ID_TYPE_GID;
+
+ wbc_status = wbcRequestResponse(WINBINDD_SET_HWM,
+ &request, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
diff --git a/nsswitch/libwbclient/wbc_pam.c b/nsswitch/libwbclient/wbc_pam.c
new file mode 100644
index 0000000000..92c6643631
--- /dev/null
+++ b/nsswitch/libwbclient/wbc_pam.c
@@ -0,0 +1,1034 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+ Copyright (C) Guenther Deschner 2008
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Required Headers */
+
+#include "libwbclient.h"
+
+/* Authenticate a username/password pair */
+wbcErr wbcAuthenticateUser(const char *username,
+ const char *password)
+{
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+ struct wbcAuthUserParams params;
+
+ ZERO_STRUCT(params);
+
+ params.account_name = username;
+ params.level = WBC_AUTH_USER_LEVEL_PLAIN;
+ params.password.plaintext = password;
+
+ wbc_status = wbcAuthenticateUserEx(&params, NULL, NULL);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+done:
+ return wbc_status;
+}
+
+static wbcErr wbc_create_auth_info(TALLOC_CTX *mem_ctx,
+ const struct winbindd_response *resp,
+ struct wbcAuthUserInfo **_i)
+{
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+ struct wbcAuthUserInfo *i;
+ struct wbcDomainSid domain_sid;
+ char *p;
+ uint32_t sn = 0;
+ uint32_t j;
+
+ i = talloc(mem_ctx, struct wbcAuthUserInfo);
+ BAIL_ON_PTR_ERROR(i, wbc_status);
+
+ i->user_flags = resp->data.auth.info3.user_flgs;
+
+ i->account_name = talloc_strdup(i, resp->data.auth.info3.user_name);
+ BAIL_ON_PTR_ERROR(i->account_name, wbc_status);
+ i->user_principal= NULL;
+ i->full_name = talloc_strdup(i, resp->data.auth.info3.full_name);
+ BAIL_ON_PTR_ERROR(i->full_name, wbc_status);
+ i->domain_name = talloc_strdup(i, resp->data.auth.info3.logon_dom);
+ BAIL_ON_PTR_ERROR(i->domain_name, wbc_status);
+ i->dns_domain_name= NULL;
+
+ i->acct_flags = resp->data.auth.info3.acct_flags;
+ memcpy(i->user_session_key,
+ resp->data.auth.user_session_key,
+ sizeof(i->user_session_key));
+ memcpy(i->lm_session_key,
+ resp->data.auth.first_8_lm_hash,
+ sizeof(i->lm_session_key));
+
+ i->logon_count = resp->data.auth.info3.logon_count;
+ i->bad_password_count = resp->data.auth.info3.bad_pw_count;
+
+ i->logon_time = resp->data.auth.info3.logon_time;
+ i->logoff_time = resp->data.auth.info3.logoff_time;
+ i->kickoff_time = resp->data.auth.info3.kickoff_time;
+ i->pass_last_set_time = resp->data.auth.info3.pass_last_set_time;
+ i->pass_can_change_time = resp->data.auth.info3.pass_can_change_time;
+ i->pass_must_change_time= resp->data.auth.info3.pass_must_change_time;
+
+ i->logon_server = talloc_strdup(i, resp->data.auth.info3.logon_srv);
+ BAIL_ON_PTR_ERROR(i->logon_server, wbc_status);
+ i->logon_script = talloc_strdup(i, resp->data.auth.info3.logon_script);
+ BAIL_ON_PTR_ERROR(i->logon_script, wbc_status);
+ i->profile_path = talloc_strdup(i, resp->data.auth.info3.profile_path);
+ BAIL_ON_PTR_ERROR(i->profile_path, wbc_status);
+ i->home_directory= talloc_strdup(i, resp->data.auth.info3.home_dir);
+ BAIL_ON_PTR_ERROR(i->home_directory, wbc_status);
+ i->home_drive = talloc_strdup(i, resp->data.auth.info3.dir_drive);
+ BAIL_ON_PTR_ERROR(i->home_drive, wbc_status);
+
+ i->num_sids = 2;
+ i->num_sids += resp->data.auth.info3.num_groups;
+ i->num_sids += resp->data.auth.info3.num_other_sids;
+
+ i->sids = talloc_array(i, struct wbcSidWithAttr, i->num_sids);
+ BAIL_ON_PTR_ERROR(i->sids, wbc_status);
+
+ wbc_status = wbcStringToSid(resp->data.auth.info3.dom_sid,
+ &domain_sid);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+#define _SID_COMPOSE(s, d, r, a) { \
+ (s).sid = d; \
+ if ((s).sid.num_auths < WBC_MAXSUBAUTHS) { \
+ (s).sid.sub_auths[(s).sid.num_auths++] = r; \
+ } else { \
+ wbc_status = WBC_ERR_INVALID_SID; \
+ BAIL_ON_WBC_ERROR(wbc_status); \
+ } \
+ (s).attributes = a; \
+} while (0)
+
+ sn = 0;
+ _SID_COMPOSE(i->sids[sn], domain_sid,
+ resp->data.auth.info3.user_rid,
+ 0);
+ sn++;
+ _SID_COMPOSE(i->sids[sn], domain_sid,
+ resp->data.auth.info3.group_rid,
+ 0);
+ sn++;
+
+ p = (char *)resp->extra_data.data;
+ if (!p) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ for (j=0; j < resp->data.auth.info3.num_groups; j++) {
+ uint32_t rid;
+ uint32_t attrs;
+ int ret;
+ char *s = p;
+ char *e = strchr(p, '\n');
+ if (!e) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ e[0] = '\0';
+ p = &e[1];
+
+ ret = sscanf(s, "0x%08X:0x%08X", &rid, &attrs);
+ if (ret != 2) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ _SID_COMPOSE(i->sids[sn], domain_sid,
+ rid, attrs);
+ sn++;
+ }
+
+ for (j=0; j < resp->data.auth.info3.num_other_sids; j++) {
+ uint32_t attrs;
+ int ret;
+ char *s = p;
+ char *a;
+ char *e = strchr(p, '\n');
+ if (!e) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ e[0] = '\0';
+ p = &e[1];
+
+ e = strchr(s, ':');
+ if (!e) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ e[0] = '\0';
+ a = &e[1];
+
+ ret = sscanf(a, "0x%08X",
+ &attrs);
+ if (ret != 1) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = wbcStringToSid(s, &i->sids[sn].sid);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ i->sids[sn].attributes = attrs;
+ sn++;
+ }
+
+ i->num_sids = sn;
+
+ *_i = i;
+ i = NULL;
+done:
+ talloc_free(i);
+ return wbc_status;
+}
+
+static wbcErr wbc_create_error_info(TALLOC_CTX *mem_ctx,
+ const struct winbindd_response *resp,
+ struct wbcAuthErrorInfo **_e)
+{
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+ struct wbcAuthErrorInfo *e;
+
+ e = talloc(mem_ctx, struct wbcAuthErrorInfo);
+ BAIL_ON_PTR_ERROR(e, wbc_status);
+
+ e->nt_status = resp->data.auth.nt_status;
+ e->pam_error = resp->data.auth.pam_error;
+ e->nt_string = talloc_strdup(e, resp->data.auth.nt_status_string);
+ BAIL_ON_PTR_ERROR(e->nt_string, wbc_status);
+
+ e->display_string = talloc_strdup(e, resp->data.auth.error_string);
+ BAIL_ON_PTR_ERROR(e->display_string, wbc_status);
+
+ *_e = e;
+ e = NULL;
+
+done:
+ talloc_free(e);
+ return wbc_status;
+}
+
+static wbcErr wbc_create_password_policy_info(TALLOC_CTX *mem_ctx,
+ const struct winbindd_response *resp,
+ struct wbcUserPasswordPolicyInfo **_i)
+{
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+ struct wbcUserPasswordPolicyInfo *i;
+
+ i = talloc(mem_ctx, struct wbcUserPasswordPolicyInfo);
+ BAIL_ON_PTR_ERROR(i, wbc_status);
+
+ i->min_passwordage = resp->data.auth.policy.min_passwordage;
+ i->min_length_password = resp->data.auth.policy.min_length_password;
+ i->password_history = resp->data.auth.policy.password_history;
+ i->password_properties = resp->data.auth.policy.password_properties;
+ i->expire = resp->data.auth.policy.expire;
+
+ *_i = i;
+ i = NULL;
+
+done:
+ talloc_free(i);
+ return wbc_status;
+}
+
+static wbcErr wbc_create_logon_info(TALLOC_CTX *mem_ctx,
+ const struct winbindd_response *resp,
+ struct wbcLogonUserInfo **_i)
+{
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+ struct wbcLogonUserInfo *i;
+
+ i = talloc_zero(mem_ctx, struct wbcLogonUserInfo);
+ BAIL_ON_PTR_ERROR(i, wbc_status);
+
+ wbc_status = wbc_create_auth_info(i, resp, &i->info);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ if (resp->data.auth.krb5ccname) {
+ wbc_status = wbcAddNamedBlob(&i->num_blobs,
+ &i->blobs,
+ "krb5ccname",
+ 0,
+ (uint8_t *)resp->data.auth.krb5ccname,
+ strlen(resp->data.auth.krb5ccname)+1);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (resp->data.auth.unix_username) {
+ wbc_status = wbcAddNamedBlob(&i->num_blobs,
+ &i->blobs,
+ "unix_username",
+ 0,
+ (uint8_t *)resp->data.auth.unix_username,
+ strlen(resp->data.auth.unix_username)+1);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ *_i = i;
+ i = NULL;
+done:
+ if (!WBC_ERROR_IS_OK(wbc_status) && i) {
+ wbcFreeMemory(i->blobs);
+ }
+
+ talloc_free(i);
+ return wbc_status;
+}
+
+/* Authenticate with more detailed information */
+wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params,
+ struct wbcAuthUserInfo **info,
+ struct wbcAuthErrorInfo **error)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ int cmd = 0;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (error) {
+ *error = NULL;
+ }
+
+ if (!params) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (!params->account_name) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ switch (params->level) {
+ case WBC_AUTH_USER_LEVEL_PLAIN:
+ cmd = WINBINDD_PAM_AUTH;
+ request.flags = WBFLAG_PAM_INFO3_TEXT |
+ WBFLAG_PAM_USER_SESSION_KEY |
+ WBFLAG_PAM_LMKEY;
+
+ if (!params->password.plaintext) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (params->domain_name && params->domain_name[0]) {
+ /* We need to get the winbind separator :-( */
+ struct winbindd_response sep_response;
+
+ ZERO_STRUCT(sep_response);
+
+ wbc_status = wbcRequestResponse(WINBINDD_INFO,
+ NULL, &sep_response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ snprintf(request.data.auth.user,
+ sizeof(request.data.auth.user)-1,
+ "%s%c%s",
+ params->domain_name,
+ sep_response.data.info.winbind_separator,
+ params->account_name);
+ } else {
+ strncpy(request.data.auth.user,
+ params->account_name,
+ sizeof(request.data.auth.user)-1);
+ }
+
+ strncpy(request.data.auth.pass,
+ params->password.plaintext,
+ sizeof(request.data.auth.pass)-1);
+ break;
+
+ case WBC_AUTH_USER_LEVEL_HASH:
+ wbc_status = WBC_ERR_NOT_IMPLEMENTED;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ break;
+
+ case WBC_AUTH_USER_LEVEL_RESPONSE:
+ cmd = WINBINDD_PAM_AUTH_CRAP;
+ request.flags = WBFLAG_PAM_INFO3_TEXT |
+ WBFLAG_PAM_USER_SESSION_KEY |
+ WBFLAG_PAM_LMKEY;
+
+ if (params->password.response.lm_length &&
+ !params->password.response.lm_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ if (params->password.response.lm_length == 0 &&
+ params->password.response.lm_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (params->password.response.nt_length &&
+ !params->password.response.nt_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ if (params->password.response.nt_length == 0&&
+ params->password.response.nt_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ strncpy(request.data.auth_crap.user,
+ params->account_name,
+ sizeof(request.data.auth_crap.user)-1);
+ if (params->domain_name) {
+ strncpy(request.data.auth_crap.domain,
+ params->domain_name,
+ sizeof(request.data.auth_crap.domain)-1);
+ }
+ if (params->workstation_name) {
+ strncpy(request.data.auth_crap.workstation,
+ params->workstation_name,
+ sizeof(request.data.auth_crap.workstation)-1);
+ }
+
+ request.data.auth_crap.logon_parameters =
+ params->parameter_control;
+
+ memcpy(request.data.auth_crap.chal,
+ params->password.response.challenge,
+ sizeof(request.data.auth_crap.chal));
+
+ request.data.auth_crap.lm_resp_len =
+ MIN(params->password.response.lm_length,
+ sizeof(request.data.auth_crap.lm_resp));
+ request.data.auth_crap.nt_resp_len =
+ MIN(params->password.response.nt_length,
+ sizeof(request.data.auth_crap.nt_resp));
+ if (params->password.response.lm_data) {
+ memcpy(request.data.auth_crap.lm_resp,
+ params->password.response.lm_data,
+ request.data.auth_crap.lm_resp_len);
+ }
+ if (params->password.response.nt_data) {
+ memcpy(request.data.auth_crap.nt_resp,
+ params->password.response.nt_data,
+ request.data.auth_crap.nt_resp_len);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (cmd == 0) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (params->flags) {
+ request.flags |= params->flags;
+ }
+
+ wbc_status = wbcRequestResponse(cmd,
+ &request,
+ &response);
+ if (response.data.auth.nt_status != 0) {
+ if (error) {
+ wbc_status = wbc_create_error_info(NULL,
+ &response,
+ error);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = WBC_ERR_AUTH_ERROR;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ if (info) {
+ wbc_status = wbc_create_auth_info(NULL,
+ &response,
+ info);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+done:
+ if (response.extra_data.data)
+ free(response.extra_data.data);
+
+ return wbc_status;
+}
+
+/* Trigger a verification of the trust credentials of a specific domain */
+wbcErr wbcCheckTrustCredentials(const char *domain,
+ struct wbcAuthErrorInfo **error)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (domain) {
+ /*
+ * the current protocol doesn't support
+ * specifying a domain
+ */
+ wbc_status = WBC_ERR_NOT_IMPLEMENTED;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponse(WINBINDD_CHECK_MACHACC,
+ &request,
+ &response);
+ if (response.data.auth.nt_status != 0) {
+ if (error) {
+ wbc_status = wbc_create_error_info(NULL,
+ &response,
+ error);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = WBC_ERR_AUTH_ERROR;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+/* Trigger an extended logoff notification to Winbind for a specific user */
+wbcErr wbcLogoffUserEx(const struct wbcLogoffUserParams *params,
+ struct wbcAuthErrorInfo **error)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ int i;
+
+ /* validate input */
+
+ if (!params || !params->username) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if ((params->num_blobs > 0) && (params->blobs == NULL)) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ if ((params->num_blobs == 0) && (params->blobs != NULL)) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.logoff.user, params->username,
+ sizeof(request.data.logoff.user)-1);
+
+ for (i=0; i<params->num_blobs; i++) {
+
+ if (strcasecmp(params->blobs[i].name, "ccfilename") == 0) {
+ if (params->blobs[i].blob.data) {
+ strncpy(request.data.logoff.krb5ccname,
+ (const char *)params->blobs[i].blob.data,
+ sizeof(request.data.logoff.krb5ccname) - 1);
+ }
+ continue;
+ }
+
+ if (strcasecmp(params->blobs[i].name, "user_uid") == 0) {
+ if (params->blobs[i].blob.data) {
+ memcpy(&request.data.logoff.uid,
+ params->blobs[i].blob.data,
+ MIN(params->blobs[i].blob.length,
+ sizeof(request.data.logoff.uid)));
+ }
+ continue;
+ }
+
+ if (strcasecmp(params->blobs[i].name, "flags") == 0) {
+ if (params->blobs[i].blob.data) {
+ memcpy(&request.flags,
+ params->blobs[i].blob.data,
+ MIN(params->blobs[i].blob.length,
+ sizeof(request.flags)));
+ }
+ continue;
+ }
+ }
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponse(WINBINDD_PAM_LOGOFF,
+ &request,
+ &response);
+
+ /* Take the response above and return it to the caller */
+ if (response.data.auth.nt_status != 0) {
+ if (error) {
+ wbc_status = wbc_create_error_info(NULL,
+ &response,
+ error);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = WBC_ERR_AUTH_ERROR;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+/* Trigger a logoff notification to Winbind for a specific user */
+wbcErr wbcLogoffUser(const char *username,
+ uid_t uid,
+ const char *ccfilename)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ /* validate input */
+
+ if (!username) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.logoff.user, username,
+ sizeof(request.data.logoff.user)-1);
+ request.data.logoff.uid = uid;
+
+ if (ccfilename) {
+ strncpy(request.data.logoff.krb5ccname, ccfilename,
+ sizeof(request.data.logoff.krb5ccname)-1);
+ }
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponse(WINBINDD_PAM_LOGOFF,
+ &request,
+ &response);
+
+ /* Take the response above and return it to the caller */
+
+ done:
+ return wbc_status;
+}
+
+/* Change a password for a user with more detailed information upon failure */
+wbcErr wbcChangeUserPasswordEx(const struct wbcChangePasswordParams *params,
+ struct wbcAuthErrorInfo **error,
+ enum wbcPasswordChangeRejectReason *reject_reason,
+ struct wbcUserPasswordPolicyInfo **policy)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ int cmd = 0;
+
+ /* validate input */
+
+ if (!params->account_name) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (error) {
+ *error = NULL;
+ }
+
+ if (policy) {
+ *policy = NULL;
+ }
+
+ if (reject_reason) {
+ *reject_reason = -1;
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ switch (params->level) {
+ case WBC_CHANGE_PASSWORD_LEVEL_PLAIN:
+ cmd = WINBINDD_PAM_CHAUTHTOK;
+
+ if (!params->account_name) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ strncpy(request.data.chauthtok.user, params->account_name,
+ sizeof(request.data.chauthtok.user) - 1);
+
+ if (params->old_password.plaintext) {
+ strncpy(request.data.chauthtok.oldpass,
+ params->old_password.plaintext,
+ sizeof(request.data.chauthtok.oldpass) - 1);
+ }
+
+ if (params->new_password.plaintext) {
+ strncpy(request.data.chauthtok.newpass,
+ params->new_password.plaintext,
+ sizeof(request.data.chauthtok.newpass) - 1);
+ }
+ break;
+
+ case WBC_CHANGE_PASSWORD_LEVEL_RESPONSE:
+ cmd = WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP;
+
+ if (!params->account_name || !params->domain_name) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (params->old_password.response.old_lm_hash_enc_length &&
+ !params->old_password.response.old_lm_hash_enc_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (params->old_password.response.old_lm_hash_enc_length == 0 &&
+ params->old_password.response.old_lm_hash_enc_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (params->old_password.response.old_nt_hash_enc_length &&
+ !params->old_password.response.old_nt_hash_enc_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (params->old_password.response.old_nt_hash_enc_length == 0 &&
+ params->old_password.response.old_nt_hash_enc_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (params->new_password.response.lm_length &&
+ !params->new_password.response.lm_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (params->new_password.response.lm_length == 0 &&
+ params->new_password.response.lm_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (params->new_password.response.nt_length &&
+ !params->new_password.response.nt_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (params->new_password.response.nt_length == 0 &&
+ params->new_password.response.nt_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ strncpy(request.data.chng_pswd_auth_crap.user,
+ params->account_name,
+ sizeof(request.data.chng_pswd_auth_crap.user) - 1);
+
+ strncpy(request.data.chng_pswd_auth_crap.domain,
+ params->domain_name,
+ sizeof(request.data.chng_pswd_auth_crap.domain) - 1);
+
+ if (params->new_password.response.nt_data) {
+ memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd,
+ params->new_password.response.nt_data,
+ request.data.chng_pswd_auth_crap.new_nt_pswd_len);
+ request.data.chng_pswd_auth_crap.new_nt_pswd_len =
+ params->new_password.response.nt_length;
+ }
+
+ if (params->new_password.response.lm_data) {
+ memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd,
+ params->new_password.response.lm_data,
+ request.data.chng_pswd_auth_crap.new_lm_pswd_len);
+ request.data.chng_pswd_auth_crap.new_lm_pswd_len =
+ params->new_password.response.lm_length;
+ }
+
+ if (params->old_password.response.old_nt_hash_enc_data) {
+ memcpy(request.data.chng_pswd_auth_crap.old_nt_hash_enc,
+ params->old_password.response.old_nt_hash_enc_data,
+ request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
+ request.data.chng_pswd_auth_crap.old_nt_hash_enc_len =
+ params->old_password.response.old_nt_hash_enc_length;
+ }
+
+ if (params->old_password.response.old_lm_hash_enc_data) {
+ memcpy(request.data.chng_pswd_auth_crap.old_lm_hash_enc,
+ params->old_password.response.old_lm_hash_enc_data,
+ request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
+ request.data.chng_pswd_auth_crap.old_lm_hash_enc_len =
+ params->old_password.response.old_lm_hash_enc_length;
+ }
+
+ break;
+ default:
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ break;
+ }
+
+ if (cmd == 0) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponse(cmd,
+ &request,
+ &response);
+ if (WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+
+ /* Take the response above and return it to the caller */
+
+ if (response.data.auth.nt_status != 0) {
+ if (error) {
+ wbc_status = wbc_create_error_info(NULL,
+ &response,
+ error);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ }
+
+ if (policy) {
+ wbc_status = wbc_create_password_policy_info(NULL,
+ &response,
+ policy);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (reject_reason) {
+ *reject_reason = response.data.auth.reject_reason;
+ }
+
+ wbc_status = WBC_ERR_PWD_CHANGE_FAILED;
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+/* Change a password for a user */
+wbcErr wbcChangeUserPassword(const char *username,
+ const char *old_password,
+ const char *new_password)
+{
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+ struct wbcChangePasswordParams params;
+
+ ZERO_STRUCT(params);
+
+ params.account_name = username;
+ params.level = WBC_CHANGE_PASSWORD_LEVEL_PLAIN;
+ params.old_password.plaintext = old_password;
+ params.new_password.plaintext = new_password;
+
+ wbc_status = wbcChangeUserPasswordEx(&params,
+ NULL,
+ NULL,
+ NULL);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+done:
+ return wbc_status;
+}
+
+/* Logon a User */
+wbcErr wbcLogonUser(const struct wbcLogonUserParams *params,
+ struct wbcLogonUserInfo **info,
+ struct wbcAuthErrorInfo **error,
+ struct wbcUserPasswordPolicyInfo **policy)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ int cmd = 0;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ uint32_t i;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (info) {
+ *info = NULL;
+ }
+ if (error) {
+ *error = NULL;
+ }
+ if (policy) {
+ *policy = NULL;
+ }
+
+ if (!params) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (!params->username) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if ((params->num_blobs > 0) && (params->blobs == NULL)) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ if ((params->num_blobs == 0) && (params->blobs != NULL)) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ cmd = WINBINDD_PAM_AUTH;
+ request.flags = WBFLAG_PAM_INFO3_TEXT |
+ WBFLAG_PAM_USER_SESSION_KEY |
+ WBFLAG_PAM_LMKEY;
+
+ if (!params->password) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ strncpy(request.data.auth.user,
+ params->username,
+ sizeof(request.data.auth.user)-1);
+
+ strncpy(request.data.auth.pass,
+ params->password,
+ sizeof(request.data.auth.pass)-1);
+
+ for (i=0; i<params->num_blobs; i++) {
+
+ if (strcasecmp(params->blobs[i].name, "krb5_cc_type") == 0) {
+ if (params->blobs[i].blob.data) {
+ strncpy(request.data.auth.krb5_cc_type,
+ (const char *)params->blobs[i].blob.data,
+ sizeof(request.data.auth.krb5_cc_type) - 1);
+ }
+ continue;
+ }
+
+ if (strcasecmp(params->blobs[i].name, "user_uid") == 0) {
+ if (params->blobs[i].blob.data) {
+ memcpy(&request.data.auth.uid,
+ params->blobs[i].blob.data,
+ MIN(sizeof(request.data.auth.uid),
+ params->blobs[i].blob.length));
+ }
+ continue;
+ }
+
+ if (strcasecmp(params->blobs[i].name, "flags") == 0) {
+ if (params->blobs[i].blob.data) {
+ uint32_t flags;
+ memcpy(&flags,
+ params->blobs[i].blob.data,
+ MIN(sizeof(flags),
+ params->blobs[i].blob.length));
+ request.flags |= flags;
+ }
+ continue;
+ }
+
+ if (strcasecmp(params->blobs[i].name, "membership_of") == 0) {
+ if (params->blobs[i].blob.data &&
+ params->blobs[i].blob.data[0] > 0) {
+ strncpy(request.data.auth.require_membership_of_sid,
+ (const char *)params->blobs[i].blob.data,
+ sizeof(request.data.auth.require_membership_of_sid) - 1);
+ }
+ continue;
+ }
+ }
+
+ wbc_status = wbcRequestResponse(cmd,
+ &request,
+ &response);
+
+ if (response.data.auth.nt_status != 0) {
+ if (error) {
+ wbc_status = wbc_create_error_info(NULL,
+ &response,
+ error);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = WBC_ERR_AUTH_ERROR;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ if (info) {
+ wbc_status = wbc_create_logon_info(NULL,
+ &response,
+ info);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (policy) {
+ wbc_status = wbc_create_password_policy_info(NULL,
+ &response,
+ policy);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+done:
+ if (response.extra_data.data)
+ free(response.extra_data.data);
+
+ return wbc_status;
+}
+
+/* Authenticate a user with cached credentials */
+wbcErr wbcCredentialCache(struct wbcCredentialCacheParams *params,
+ struct wbcCredentialCacheInfo **info,
+ struct wbcAuthErrorInfo **error)
+{
+ return WBC_ERR_NOT_IMPLEMENTED;
+}
diff --git a/nsswitch/libwbclient/wbc_pwd.c b/nsswitch/libwbclient/wbc_pwd.c
new file mode 100644
index 0000000000..cd945996c8
--- /dev/null
+++ b/nsswitch/libwbclient/wbc_pwd.c
@@ -0,0 +1,571 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Required Headers */
+
+#include "libwbclient.h"
+
+/** @brief The maximum number of pwent structs to get from winbindd
+ *
+ */
+#define MAX_GETPWENT_USERS 500
+
+/** @brief The maximum number of grent structs to get from winbindd
+ *
+ */
+#define MAX_GETGRENT_GROUPS 500
+
+/**
+ *
+ **/
+
+static struct passwd *copy_passwd_entry(struct winbindd_pw *p)
+{
+ struct passwd *pwd = NULL;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ pwd = talloc(NULL, struct passwd);
+ BAIL_ON_PTR_ERROR(pwd, wbc_status);
+
+ pwd->pw_name = talloc_strdup(pwd,p->pw_name);
+ BAIL_ON_PTR_ERROR(pwd->pw_name, wbc_status);
+
+ pwd->pw_passwd = talloc_strdup(pwd, p->pw_passwd);
+ BAIL_ON_PTR_ERROR(pwd->pw_passwd, wbc_status);
+
+ pwd->pw_gecos = talloc_strdup(pwd, p->pw_gecos);
+ BAIL_ON_PTR_ERROR(pwd->pw_gecos, wbc_status);
+
+ pwd->pw_shell = talloc_strdup(pwd, p->pw_shell);
+ BAIL_ON_PTR_ERROR(pwd->pw_shell, wbc_status);
+
+ pwd->pw_dir = talloc_strdup(pwd, p->pw_dir);
+ BAIL_ON_PTR_ERROR(pwd->pw_dir, wbc_status);
+
+ pwd->pw_uid = p->pw_uid;
+ pwd->pw_gid = p->pw_gid;
+
+done:
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ talloc_free(pwd);
+ pwd = NULL;
+ }
+
+ return pwd;
+}
+
+/**
+ *
+ **/
+
+static struct group *copy_group_entry(struct winbindd_gr *g,
+ char *mem_buf)
+{
+ struct group *grp = NULL;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ int i;
+ char *mem_p, *mem_q;
+
+ grp = talloc(NULL, struct group);
+ BAIL_ON_PTR_ERROR(grp, wbc_status);
+
+ grp->gr_name = talloc_strdup(grp, g->gr_name);
+ BAIL_ON_PTR_ERROR(grp->gr_name, wbc_status);
+
+ grp->gr_passwd = talloc_strdup(grp, g->gr_passwd);
+ BAIL_ON_PTR_ERROR(grp->gr_passwd, wbc_status);
+
+ grp->gr_gid = g->gr_gid;
+
+ grp->gr_mem = talloc_array(grp, char*, g->num_gr_mem+1);
+
+ mem_p = mem_q = mem_buf;
+ for (i=0; i<g->num_gr_mem && mem_p; i++) {
+ if ((mem_q = strchr(mem_p, ',')) != NULL) {
+ *mem_q = '\0';
+ }
+
+ grp->gr_mem[i] = talloc_strdup(grp, mem_p);
+ BAIL_ON_PTR_ERROR(grp->gr_mem[i], wbc_status);
+
+ if (mem_q == NULL) {
+ i += 1;
+ break;
+ }
+ mem_p = mem_q + 1;
+ }
+ grp->gr_mem[i] = NULL;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+done:
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ talloc_free(grp);
+ grp = NULL;
+ }
+
+ return grp;
+}
+
+/* Fill in a struct passwd* for a domain user based on username */
+wbcErr wbcGetpwnam(const char *name, struct passwd **pwd)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ if (!name || !pwd) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* dst is already null terminated from the memset above */
+
+ strncpy(request.data.username, name, sizeof(request.data.username)-1);
+
+ wbc_status = wbcRequestResponse(WINBINDD_GETPWNAM,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ *pwd = copy_passwd_entry(&response.data.pw);
+ BAIL_ON_PTR_ERROR(*pwd, wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+/* Fill in a struct passwd* for a domain user based on uid */
+wbcErr wbcGetpwuid(uid_t uid, struct passwd **pwd)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ if (!pwd) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.uid = uid;
+
+ wbc_status = wbcRequestResponse(WINBINDD_GETPWUID,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ *pwd = copy_passwd_entry(&response.data.pw);
+ BAIL_ON_PTR_ERROR(*pwd, wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+/* Fill in a struct passwd* for a domain user based on username */
+wbcErr wbcGetgrnam(const char *name, struct group **grp)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (!name || !grp) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* dst is already null terminated from the memset above */
+
+ strncpy(request.data.groupname, name, sizeof(request.data.groupname)-1);
+
+ wbc_status = wbcRequestResponse(WINBINDD_GETGRNAM,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ *grp = copy_group_entry(&response.data.gr,
+ (char*)response.extra_data.data);
+ BAIL_ON_PTR_ERROR(*grp, wbc_status);
+
+ done:
+ if (response.extra_data.data)
+ free(response.extra_data.data);
+
+ return wbc_status;
+}
+
+/* Fill in a struct passwd* for a domain user based on uid */
+wbcErr wbcGetgrgid(gid_t gid, struct group **grp)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (!grp) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ request.data.gid = gid;
+
+ wbc_status = wbcRequestResponse(WINBINDD_GETGRGID,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ *grp = copy_group_entry(&response.data.gr,
+ (char*)response.extra_data.data);
+ BAIL_ON_PTR_ERROR(*grp, wbc_status);
+
+ done:
+ if (response.extra_data.data)
+ free(response.extra_data.data);
+
+ return wbc_status;
+}
+
+/** @brief Number of cached passwd structs
+ *
+ */
+static uint32_t pw_cache_size;
+
+/** @brief Position of the pwent context
+ *
+ */
+static uint32_t pw_cache_idx;
+
+/** @brief Winbindd response containing the passwd structs
+ *
+ */
+static struct winbindd_response pw_response;
+
+/* Reset the passwd iterator */
+wbcErr wbcSetpwent(void)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (pw_cache_size > 0) {
+ pw_cache_idx = pw_cache_size = 0;
+ if (pw_response.extra_data.data) {
+ free(pw_response.extra_data.data);
+ }
+ }
+
+ ZERO_STRUCT(pw_response);
+
+ wbc_status = wbcRequestResponse(WINBINDD_SETPWENT,
+ NULL, NULL);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+/* Close the passwd iterator */
+wbcErr wbcEndpwent(void)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (pw_cache_size > 0) {
+ pw_cache_idx = pw_cache_size = 0;
+ if (pw_response.extra_data.data) {
+ free(pw_response.extra_data.data);
+ }
+ }
+
+ wbc_status = wbcRequestResponse(WINBINDD_ENDPWENT,
+ NULL, NULL);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+/* Return the next struct passwd* entry from the pwent iterator */
+wbcErr wbcGetpwent(struct passwd **pwd)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_pw *wb_pw;
+
+ /* If there's a cached result, return that. */
+ if (pw_cache_idx < pw_cache_size) {
+ goto return_result;
+ }
+
+ /* Otherwise, query winbindd for some entries. */
+
+ pw_cache_idx = 0;
+
+ if (pw_response.extra_data.data) {
+ free(pw_response.extra_data.data);
+ ZERO_STRUCT(pw_response);
+ }
+
+ ZERO_STRUCT(request);
+ request.data.num_entries = MAX_GETPWENT_USERS;
+
+ wbc_status = wbcRequestResponse(WINBINDD_GETPWENT, &request,
+ &pw_response);
+
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ pw_cache_size = pw_response.data.num_entries;
+
+return_result:
+
+ wb_pw = (struct winbindd_pw *) pw_response.extra_data.data;
+
+ *pwd = copy_passwd_entry(&wb_pw[pw_cache_idx]);
+
+ BAIL_ON_PTR_ERROR(*pwd, wbc_status);
+
+ pw_cache_idx++;
+
+done:
+ return wbc_status;
+}
+
+/** @brief Number of cached group structs
+ *
+ */
+static uint32_t gr_cache_size;
+
+/** @brief Position of the grent context
+ *
+ */
+static uint32_t gr_cache_idx;
+
+/** @brief Winbindd response containing the group structs
+ *
+ */
+static struct winbindd_response gr_response;
+
+/* Reset the group iterator */
+wbcErr wbcSetgrent(void)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (gr_cache_size > 0) {
+ gr_cache_idx = gr_cache_size = 0;
+ if (gr_response.extra_data.data) {
+ free(gr_response.extra_data.data);
+ }
+ }
+
+ ZERO_STRUCT(gr_response);
+
+ wbc_status = wbcRequestResponse(WINBINDD_SETGRENT,
+ NULL, NULL);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+/* Close the group iterator */
+wbcErr wbcEndgrent(void)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (gr_cache_size > 0) {
+ gr_cache_idx = gr_cache_size = 0;
+ if (gr_response.extra_data.data) {
+ free(gr_response.extra_data.data);
+ }
+ }
+
+ wbc_status = wbcRequestResponse(WINBINDD_ENDGRENT,
+ NULL, NULL);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+/* Return the next struct group* entry from the pwent iterator */
+wbcErr wbcGetgrent(struct group **grp)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_gr *wb_gr;
+ uint32_t mem_ofs;
+
+ /* If there's a cached result, return that. */
+ if (gr_cache_idx < gr_cache_size) {
+ goto return_result;
+ }
+
+ /* Otherwise, query winbindd for some entries. */
+
+ gr_cache_idx = 0;
+
+ if (gr_response.extra_data.data) {
+ free(gr_response.extra_data.data);
+ ZERO_STRUCT(gr_response);
+ }
+
+ ZERO_STRUCT(request);
+ request.data.num_entries = MAX_GETGRENT_GROUPS;
+
+ wbc_status = wbcRequestResponse(WINBINDD_GETGRENT, &request,
+ &gr_response);
+
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ gr_cache_size = gr_response.data.num_entries;
+
+return_result:
+
+ wb_gr = (struct winbindd_gr *) gr_response.extra_data.data;
+
+ mem_ofs = wb_gr[gr_cache_idx].gr_mem_ofs +
+ gr_cache_size * sizeof(struct winbindd_gr);
+
+ *grp = copy_group_entry(&wb_gr[gr_cache_idx],
+ ((char *)gr_response.extra_data.data)+mem_ofs);
+
+ BAIL_ON_PTR_ERROR(*grp, wbc_status);
+
+ gr_cache_idx++;
+
+done:
+ return wbc_status;
+}
+
+/* Return the next struct group* entry from the pwent iterator */
+wbcErr wbcGetgrlist(struct group **grp)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_gr *wb_gr;
+
+ /* If there's a cached result, return that. */
+ if (gr_cache_idx < gr_cache_size) {
+ goto return_result;
+ }
+
+ /* Otherwise, query winbindd for some entries. */
+
+ gr_cache_idx = 0;
+
+ if (gr_response.extra_data.data) {
+ free(gr_response.extra_data.data);
+ ZERO_STRUCT(gr_response);
+ }
+
+ ZERO_STRUCT(request);
+ request.data.num_entries = MAX_GETGRENT_GROUPS;
+
+ wbc_status = wbcRequestResponse(WINBINDD_GETGRLST, &request,
+ &gr_response);
+
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ gr_cache_size = gr_response.data.num_entries;
+
+return_result:
+
+ wb_gr = (struct winbindd_gr *) gr_response.extra_data.data;
+
+ *grp = copy_group_entry(&wb_gr[gr_cache_idx], NULL);
+
+ BAIL_ON_PTR_ERROR(*grp, wbc_status);
+
+ gr_cache_idx++;
+
+done:
+ return wbc_status;
+}
+
+/* Return the unix group array belonging to the given user */
+wbcErr wbcGetGroups(const char *account,
+ uint32_t *num_groups,
+ gid_t **_groups)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ uint32_t i;
+ gid_t *groups = NULL;
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (!account) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Send request */
+
+ strncpy(request.data.username, account, sizeof(request.data.username)-1);
+
+ wbc_status = wbcRequestResponse(WINBINDD_GETGROUPS,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ groups = talloc_array(NULL, gid_t, response.data.num_entries);
+ BAIL_ON_PTR_ERROR(groups, wbc_status);
+
+ for (i = 0; i < response.data.num_entries; i++) {
+ groups[i] = ((gid_t *)response.extra_data.data)[i];
+ }
+
+ *num_groups = response.data.num_entries;
+ *_groups = groups;
+ groups = NULL;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ if (response.extra_data.data) {
+ free(response.extra_data.data);
+ }
+ if (groups) {
+ talloc_free(groups);
+ }
+
+ return wbc_status;
+}
diff --git a/nsswitch/libwbclient/wbc_sid.c b/nsswitch/libwbclient/wbc_sid.c
new file mode 100644
index 0000000000..e2157b9609
--- /dev/null
+++ b/nsswitch/libwbclient/wbc_sid.c
@@ -0,0 +1,672 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Required Headers */
+
+#include "libwbclient.h"
+
+
+/* Convert a binary SID to a character string */
+wbcErr wbcSidToString(const struct wbcDomainSid *sid,
+ char **sid_string)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ uint32_t id_auth;
+ int i;
+ char *tmp = NULL;
+
+ if (!sid) {
+ wbc_status = WBC_ERR_INVALID_SID;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ id_auth = sid->id_auth[5] +
+ (sid->id_auth[4] << 8) +
+ (sid->id_auth[3] << 16) +
+ (sid->id_auth[2] << 24);
+
+ tmp = talloc_asprintf(NULL, "S-%d-%d", sid->sid_rev_num, id_auth);
+ BAIL_ON_PTR_ERROR(tmp, wbc_status);
+
+ for (i=0; i<sid->num_auths; i++) {
+ char *tmp2;
+ tmp2 = talloc_asprintf_append(tmp, "-%u", sid->sub_auths[i]);
+ BAIL_ON_PTR_ERROR(tmp2, wbc_status);
+
+ tmp = tmp2;
+ }
+
+ *sid_string = tmp;
+ tmp = NULL;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+done:
+ talloc_free(tmp);
+
+ return wbc_status;
+}
+
+/* Convert a character string to a binary SID */
+wbcErr wbcStringToSid(const char *str,
+ struct wbcDomainSid *sid)
+{
+ const char *p;
+ char *q;
+ uint32_t x;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!sid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Sanity check for either "S-" or "s-" */
+
+ if (!str
+ || (str[0]!='S' && str[0]!='s')
+ || (str[1]!='-'))
+ {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Get the SID revision number */
+
+ p = str+2;
+ x = (uint32_t)strtol(p, &q, 10);
+ if (x==0 || !q || *q!='-') {
+ wbc_status = WBC_ERR_INVALID_SID;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ sid->sid_rev_num = (uint8_t)x;
+
+ /* Next the Identifier Authority. This is stored in big-endian
+ in a 6 byte array. */
+
+ p = q+1;
+ x = (uint32_t)strtol(p, &q, 10);
+ if (!q || *q!='-') {
+ wbc_status = WBC_ERR_INVALID_SID;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ sid->id_auth[5] = (x & 0x000000ff);
+ sid->id_auth[4] = (x & 0x0000ff00) >> 8;
+ sid->id_auth[3] = (x & 0x00ff0000) >> 16;
+ sid->id_auth[2] = (x & 0xff000000) >> 24;
+ sid->id_auth[1] = 0;
+ sid->id_auth[0] = 0;
+
+ /* now read the the subauthorities */
+
+ p = q +1;
+ sid->num_auths = 0;
+ while (sid->num_auths < WBC_MAXSUBAUTHS) {
+ x=(uint32_t)strtoul(p, &q, 10);
+ if (p == q)
+ break;
+ if (q == NULL) {
+ wbc_status = WBC_ERR_INVALID_SID;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ sid->sub_auths[sid->num_auths++] = x;
+
+ if ((*q!='-') || (*q=='\0'))
+ break;
+ p = q + 1;
+ }
+
+ /* IF we ended early, then the SID could not be converted */
+
+ if (q && *q!='\0') {
+ wbc_status = WBC_ERR_INVALID_SID;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+done:
+ return wbc_status;
+
+}
+
+/* Convert a domain and name to SID */
+wbcErr wbcLookupName(const char *domain,
+ const char *name,
+ struct wbcDomainSid *sid,
+ enum wbcSidType *name_type)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!sid || !name_type) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* dst is already null terminated from the memset above */
+
+ strncpy(request.data.name.dom_name, domain,
+ sizeof(request.data.name.dom_name)-1);
+ strncpy(request.data.name.name, name,
+ sizeof(request.data.name.name)-1);
+
+ wbc_status = wbcRequestResponse(WINBINDD_LOOKUPNAME,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ wbc_status = wbcStringToSid(response.data.sid.sid, sid);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ *name_type = (enum wbcSidType)response.data.sid.type;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ return wbc_status;
+}
+
+/* Convert a SID to a domain and name */
+wbcErr wbcLookupSid(const struct wbcDomainSid *sid,
+ char **pdomain,
+ char **pname,
+ enum wbcSidType *pname_type)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *sid_string = NULL;
+ char *domain = NULL;
+ char *name = NULL;
+ enum wbcSidType name_type = WBC_SID_NAME_USE_NONE;
+
+ if (!sid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* dst is already null terminated from the memset above */
+
+ wbc_status = wbcSidToString(sid, &sid_string);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
+ wbcFreeMemory(sid_string);
+
+ /* Make request */
+
+ wbc_status = wbcRequestResponse(WINBINDD_LOOKUPSID,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ /* Copy out result */
+
+ domain = talloc_strdup(NULL, response.data.name.dom_name);
+ BAIL_ON_PTR_ERROR(domain, wbc_status);
+
+ name = talloc_strdup(NULL, response.data.name.name);
+ BAIL_ON_PTR_ERROR(name, wbc_status);
+
+ name_type = (enum wbcSidType)response.data.name.type;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ if (WBC_ERROR_IS_OK(wbc_status)) {
+ if (pdomain != NULL) {
+ *pdomain = domain;
+ }
+ if (pname != NULL) {
+ *pname = name;
+ }
+ if (pname_type != NULL) {
+ *pname_type = name_type;
+ }
+ }
+ else {
+#if 0
+ /*
+ * Found by Coverity: In this particular routine we can't end
+ * up here with a non-NULL name. Further up there are just two
+ * exit paths that lead here, neither of which leave an
+ * allocated name. If you add more paths up there, re-activate
+ * this.
+ */
+ if (name != NULL) {
+ talloc_free(name);
+ }
+#endif
+ if (domain != NULL) {
+ talloc_free(domain);
+ }
+ }
+
+ return wbc_status;
+}
+
+/* Translate a collection of RIDs within a domain to names */
+
+wbcErr wbcLookupRids(struct wbcDomainSid *dom_sid,
+ int num_rids,
+ uint32_t *rids,
+ const char **pp_domain_name,
+ const char ***pnames,
+ enum wbcSidType **ptypes)
+{
+ size_t i, len, ridbuf_size;
+ char *ridlist;
+ char *p;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *sid_string = NULL;
+ char *domain_name = NULL;
+ const char **names = NULL;
+ enum wbcSidType *types = NULL;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (!dom_sid || (num_rids == 0)) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = wbcSidToString(dom_sid, &sid_string);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
+ wbcFreeMemory(sid_string);
+
+ /* Even if all the Rids were of maximum 32bit values,
+ we would only have 11 bytes per rid in the final array
+ ("4294967296" + \n). Add one more byte for the
+ terminating '\0' */
+
+ ridbuf_size = (sizeof(char)*11) * num_rids + 1;
+
+ ridlist = talloc_zero_array(NULL, char, ridbuf_size);
+ BAIL_ON_PTR_ERROR(ridlist, wbc_status);
+
+ len = 0;
+ for (i=0; i<num_rids && (len-1)>0; i++) {
+ char ridstr[12];
+
+ len = strlen(ridlist);
+ p = ridlist + len;
+
+ snprintf( ridstr, sizeof(ridstr)-1, "%u\n", rids[i]);
+ strncat(p, ridstr, ridbuf_size-len-1);
+ }
+
+ request.extra_data.data = ridlist;
+ request.extra_len = strlen(ridlist)+1;
+
+ wbc_status = wbcRequestResponse(WINBINDD_LOOKUPRIDS,
+ &request,
+ &response);
+ talloc_free(ridlist);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ domain_name = talloc_strdup(NULL, response.data.domain_name);
+ BAIL_ON_PTR_ERROR(domain_name, wbc_status);
+
+ names = talloc_array(NULL, const char*, num_rids);
+ BAIL_ON_PTR_ERROR(names, wbc_status);
+
+ types = talloc_array(NULL, enum wbcSidType, num_rids);
+ BAIL_ON_PTR_ERROR(types, wbc_status);
+
+ p = (char *)response.extra_data.data;
+
+ for (i=0; i<num_rids; i++) {
+ char *q;
+
+ if (*p == '\0') {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ types[i] = (enum wbcSidType)strtoul(p, &q, 10);
+
+ if (*q != ' ') {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ p = q+1;
+
+ if ((q = strchr(p, '\n')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ *q = '\0';
+
+ names[i] = talloc_strdup(names, p);
+ BAIL_ON_PTR_ERROR(names[i], wbc_status);
+
+ p = q+1;
+ }
+
+ if (*p != '\0') {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ if (response.extra_data.data) {
+ free(response.extra_data.data);
+ }
+
+ if (WBC_ERROR_IS_OK(wbc_status)) {
+ *pp_domain_name = domain_name;
+ *pnames = names;
+ *ptypes = types;
+ }
+ else {
+ if (domain_name)
+ talloc_free(domain_name);
+ if (names)
+ talloc_free(names);
+ if (types)
+ talloc_free(types);
+ }
+
+ return wbc_status;
+}
+
+/* Get the groups a user belongs to */
+wbcErr wbcLookupUserSids(const struct wbcDomainSid *user_sid,
+ bool domain_groups_only,
+ uint32_t *num_sids,
+ struct wbcDomainSid **_sids)
+{
+ uint32_t i;
+ const char *s;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *sid_string = NULL;
+ struct wbcDomainSid *sids = NULL;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ int cmd;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (!user_sid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = wbcSidToString(user_sid, &sid_string);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
+ wbcFreeMemory(sid_string);
+
+ if (domain_groups_only) {
+ cmd = WINBINDD_GETUSERDOMGROUPS;
+ } else {
+ cmd = WINBINDD_GETUSERSIDS;
+ }
+
+ wbc_status = wbcRequestResponse(cmd,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ if (response.data.num_entries &&
+ !response.extra_data.data) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ sids = talloc_array(NULL, struct wbcDomainSid,
+ response.data.num_entries);
+ BAIL_ON_PTR_ERROR(sids, wbc_status);
+
+ s = (const char *)response.extra_data.data;
+ for (i = 0; i < response.data.num_entries; i++) {
+ char *n = strchr(s, '\n');
+ if (n) {
+ *n = '\0';
+ }
+ wbc_status = wbcStringToSid(s, &sids[i]);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ s += strlen(s) + 1;
+ }
+
+ *num_sids = response.data.num_entries;
+ *_sids = sids;
+ sids = NULL;
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ if (response.extra_data.data) {
+ free(response.extra_data.data);
+ }
+ if (sids) {
+ talloc_free(sids);
+ }
+
+ return wbc_status;
+}
+
+/* Lists Users */
+wbcErr wbcListUsers(const char *domain_name,
+ uint32_t *_num_users,
+ const char ***_users)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ uint32_t num_users = 0;
+ const char **users = NULL;
+ const char *next;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (domain_name) {
+ strncpy(request.domain_name, domain_name,
+ sizeof(request.domain_name)-1);
+ }
+
+ wbc_status = wbcRequestResponse(WINBINDD_LIST_USERS,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ /* Look through extra data */
+
+ next = (const char *)response.extra_data.data;
+ while (next) {
+ const char **tmp;
+ const char *current = next;
+ char *k = strchr(next, ',');
+ if (k) {
+ k[0] = '\0';
+ next = k+1;
+ } else {
+ next = NULL;
+ }
+
+ tmp = talloc_realloc(NULL, users,
+ const char *,
+ num_users+1);
+ BAIL_ON_PTR_ERROR(tmp, wbc_status);
+ users = tmp;
+
+ users[num_users] = talloc_strdup(users, current);
+ BAIL_ON_PTR_ERROR(users[num_users], wbc_status);
+
+ num_users++;
+ }
+
+ *_num_users = num_users;
+ *_users = users;
+ users = NULL;
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ if (response.extra_data.data) {
+ free(response.extra_data.data);
+ }
+ if (users) {
+ talloc_free(users);
+ }
+ return wbc_status;
+}
+
+/* Lists Groups */
+wbcErr wbcListGroups(const char *domain_name,
+ uint32_t *_num_groups,
+ const char ***_groups)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ uint32_t num_groups = 0;
+ const char **groups = NULL;
+ const char *next;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (domain_name) {
+ strncpy(request.domain_name, domain_name,
+ sizeof(request.domain_name)-1);
+ }
+
+ wbc_status = wbcRequestResponse(WINBINDD_LIST_GROUPS,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ /* Look through extra data */
+
+ next = (const char *)response.extra_data.data;
+ while (next) {
+ const char **tmp;
+ const char *current = next;
+ char *k = strchr(next, ',');
+ if (k) {
+ k[0] = '\0';
+ next = k+1;
+ } else {
+ next = NULL;
+ }
+
+ tmp = talloc_realloc(NULL, groups,
+ const char *,
+ num_groups+1);
+ BAIL_ON_PTR_ERROR(tmp, wbc_status);
+ groups = tmp;
+
+ groups[num_groups] = talloc_strdup(groups, current);
+ BAIL_ON_PTR_ERROR(groups[num_groups], wbc_status);
+
+ num_groups++;
+ }
+
+ *_num_groups = num_groups;
+ *_groups = groups;
+ groups = NULL;
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ if (response.extra_data.data) {
+ free(response.extra_data.data);
+ }
+ if (groups) {
+ talloc_free(groups);
+ }
+ return wbc_status;
+}
+
+wbcErr wbcGetDisplayName(const struct wbcDomainSid *sid,
+ char **pdomain,
+ char **pfullname,
+ enum wbcSidType *pname_type)
+{
+ wbcErr wbc_status;
+ char *domain = NULL;
+ char *name = NULL;
+ enum wbcSidType name_type;
+
+ wbc_status = wbcLookupSid(sid, &domain, &name, &name_type);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ if (name_type == WBC_SID_NAME_USER) {
+ uid_t uid;
+ struct passwd *pwd;
+
+ wbc_status = wbcSidToUid(sid, &uid);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ wbc_status = wbcGetpwuid(uid, &pwd);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ wbcFreeMemory(name);
+
+ name = talloc_strdup(NULL, pwd->pw_gecos);
+ BAIL_ON_PTR_ERROR(name, wbc_status);
+ }
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ if (WBC_ERROR_IS_OK(wbc_status)) {
+ *pdomain = domain;
+ *pfullname = name;
+ *pname_type = name_type;
+ } else {
+ wbcFreeMemory(domain);
+ wbcFreeMemory(name);
+ }
+
+ return wbc_status;
+}
diff --git a/nsswitch/libwbclient/wbc_util.c b/nsswitch/libwbclient/wbc_util.c
new file mode 100644
index 0000000000..5c5034ee13
--- /dev/null
+++ b/nsswitch/libwbclient/wbc_util.c
@@ -0,0 +1,672 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007-2008
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Required Headers */
+
+#include "libwbclient.h"
+
+
+
+/** @brief Ping winbindd to see if the daemon is running
+ *
+ * @return #wbcErr
+ **/
+
+wbcErr wbcPing(void)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ return wbcRequestResponse(WINBINDD_PING, &request, &response);
+}
+
+wbcErr wbcInterfaceDetails(struct wbcInterfaceDetails **_details)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcInterfaceDetails *info;
+ struct wbcDomainInfo *domain = NULL;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ info = talloc(NULL, struct wbcInterfaceDetails);
+ BAIL_ON_PTR_ERROR(info, wbc_status);
+
+ /* first the interface version */
+ wbc_status = wbcRequestResponse(WINBINDD_INTERFACE_VERSION, NULL, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ info->interface_version = response.data.interface_version;
+
+ /* then the samba version and the winbind separator */
+ wbc_status = wbcRequestResponse(WINBINDD_INFO, NULL, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ info->winbind_version = talloc_strdup(info,
+ response.data.info.samba_version);
+ BAIL_ON_PTR_ERROR(info->winbind_version, wbc_status);
+ info->winbind_separator = response.data.info.winbind_separator;
+
+ /* then the local netbios name */
+ wbc_status = wbcRequestResponse(WINBINDD_NETBIOS_NAME, NULL, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ info->netbios_name = talloc_strdup(info,
+ response.data.netbios_name);
+ BAIL_ON_PTR_ERROR(info->netbios_name, wbc_status);
+
+ /* then the local workgroup name */
+ wbc_status = wbcRequestResponse(WINBINDD_DOMAIN_NAME, NULL, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ info->netbios_domain = talloc_strdup(info,
+ response.data.domain_name);
+ BAIL_ON_PTR_ERROR(info->netbios_domain, wbc_status);
+
+ wbc_status = wbcDomainInfo(info->netbios_domain, &domain);
+ if (wbc_status == WBC_ERR_DOMAIN_NOT_FOUND) {
+ /* maybe it's a standalone server */
+ domain = NULL;
+ wbc_status = WBC_ERR_SUCCESS;
+ } else {
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (domain) {
+ info->dns_domain = talloc_strdup(info,
+ domain->dns_name);
+ wbcFreeMemory(domain);
+ BAIL_ON_PTR_ERROR(info->dns_domain, wbc_status);
+ } else {
+ info->dns_domain = NULL;
+ }
+
+ *_details = info;
+ info = NULL;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+done:
+ talloc_free(info);
+ return wbc_status;
+}
+
+
+/* Lookup the current status of a trusted domain */
+wbcErr wbcDomainInfo(const char *domain, struct wbcDomainInfo **dinfo)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainInfo *info = NULL;
+
+ if (!domain || !dinfo) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.domain_name, domain,
+ sizeof(request.domain_name)-1);
+
+ wbc_status = wbcRequestResponse(WINBINDD_DOMAIN_INFO,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ info = talloc(NULL, struct wbcDomainInfo);
+ BAIL_ON_PTR_ERROR(info, wbc_status);
+
+ info->short_name = talloc_strdup(info,
+ response.data.domain_info.name);
+ BAIL_ON_PTR_ERROR(info->short_name, wbc_status);
+
+ info->dns_name = talloc_strdup(info,
+ response.data.domain_info.alt_name);
+ BAIL_ON_PTR_ERROR(info->dns_name, wbc_status);
+
+ wbc_status = wbcStringToSid(response.data.domain_info.sid,
+ &info->sid);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ if (response.data.domain_info.native_mode)
+ info->domain_flags |= WBC_DOMINFO_DOMAIN_NATIVE;
+ if (response.data.domain_info.active_directory)
+ info->domain_flags |= WBC_DOMINFO_DOMAIN_AD;
+ if (response.data.domain_info.primary)
+ info->domain_flags |= WBC_DOMINFO_DOMAIN_PRIMARY;
+
+ *dinfo = info;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ talloc_free(info);
+ }
+
+ return wbc_status;
+}
+
+
+/* Resolve a NetbiosName via WINS */
+wbcErr wbcResolveWinsByName(const char *name, char **ip)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *ipaddr;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ strncpy(request.data.winsreq, name,
+ sizeof(request.data.winsreq)-1);
+
+ wbc_status = wbcRequestResponse(WINBINDD_WINS_BYNAME,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ /* Display response */
+
+ ipaddr = talloc_strdup(NULL, response.data.winsresp);
+ BAIL_ON_PTR_ERROR(ipaddr, wbc_status);
+
+ *ip = ipaddr;
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ return wbc_status;
+}
+
+/* Resolve an IP address via WINS into a NetbiosName */
+wbcErr wbcResolveWinsByIP(const char *ip, char **name)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *name_str;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ strncpy(request.data.winsreq, ip,
+ sizeof(request.data.winsreq)-1);
+
+ wbc_status = wbcRequestResponse(WINBINDD_WINS_BYIP,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ /* Display response */
+
+ name_str = talloc_strdup(NULL, response.data.winsresp);
+ BAIL_ON_PTR_ERROR(name_str, wbc_status);
+
+ *name = name_str;
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ return wbc_status;
+}
+
+/**
+ */
+
+static wbcErr process_domain_info_string(TALLOC_CTX *ctx,
+ struct wbcDomainInfo *info,
+ char *info_string)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *r = NULL;
+ char *s = NULL;
+
+ if (!info || !info_string) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ r = info_string;
+
+ /* Short Name */
+ if ((s = strchr(r, '\\')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ *s = '\0';
+ s++;
+
+ info->short_name = talloc_strdup(ctx, r);
+ BAIL_ON_PTR_ERROR(info->short_name, wbc_status);
+
+
+ /* DNS Name */
+ r = s;
+ if ((s = strchr(r, '\\')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ *s = '\0';
+ s++;
+
+ info->dns_name = talloc_strdup(ctx, r);
+ BAIL_ON_PTR_ERROR(info->dns_name, wbc_status);
+
+ /* SID */
+ r = s;
+ if ((s = strchr(r, '\\')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ *s = '\0';
+ s++;
+
+ wbc_status = wbcStringToSid(r, &info->sid);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ /* Trust type */
+ r = s;
+ if ((s = strchr(r, '\\')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ *s = '\0';
+ s++;
+
+ if (strcmp(r, "None") == 0) {
+ info->trust_type = WBC_DOMINFO_TRUSTTYPE_NONE;
+ } else if (strcmp(r, "External") == 0) {
+ info->trust_type = WBC_DOMINFO_TRUSTTYPE_EXTERNAL;
+ } else if (strcmp(r, "Forest") == 0) {
+ info->trust_type = WBC_DOMINFO_TRUSTTYPE_FOREST;
+ } else if (strcmp(r, "In Forest") == 0) {
+ info->trust_type = WBC_DOMINFO_TRUSTTYPE_IN_FOREST;
+ } else {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Transitive */
+ r = s;
+ if ((s = strchr(r, '\\')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ *s = '\0';
+ s++;
+
+ if (strcmp(r, "Yes") == 0) {
+ info->trust_flags |= WBC_DOMINFO_TRUST_TRANSITIVE;
+ }
+
+ /* Incoming */
+ r = s;
+ if ((s = strchr(r, '\\')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ *s = '\0';
+ s++;
+
+ if (strcmp(r, "Yes") == 0) {
+ info->trust_flags |= WBC_DOMINFO_TRUST_INCOMING;
+ }
+
+ /* Outgoing */
+ r = s;
+ if ((s = strchr(r, '\\')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ *s = '\0';
+ s++;
+
+ if (strcmp(r, "Yes") == 0) {
+ info->trust_flags |= WBC_DOMINFO_TRUST_OUTGOING;
+ }
+
+ /* Online/Offline status */
+
+ r = s;
+ if (r == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ if ( strcmp(r, "Offline") == 0) {
+ info->domain_flags |= WBC_DOMINFO_DOMAIN_OFFLINE;
+ }
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ return wbc_status;
+}
+
+/* Enumerate the domain trusts known by Winbind */
+wbcErr wbcListTrusts(struct wbcDomainInfo **domains, size_t *num_domains)
+{
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *p = NULL;
+ char *q = NULL;
+ char *extra_data = NULL;
+ int count = 0;
+ struct wbcDomainInfo *d_list = NULL;
+ int i = 0;
+
+ *domains = NULL;
+ *num_domains = 0;
+
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponse(WINBINDD_LIST_TRUSTDOM,
+ NULL,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ /* Decode the response */
+
+ p = (char *)response.extra_data.data;
+
+ if (strlen(p) == 0) {
+ /* We should always at least get back our
+ own SAM domain */
+
+ wbc_status = WBC_ERR_DOMAIN_NOT_FOUND;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Count number of domains */
+
+ count = 0;
+ while (p) {
+ count++;
+
+ if ((q = strchr(p, '\n')) != NULL)
+ q++;
+ p = q;
+ }
+
+ d_list = talloc_array(NULL, struct wbcDomainInfo, count);
+ BAIL_ON_PTR_ERROR(d_list, wbc_status);
+
+ extra_data = strdup((char*)response.extra_data.data);
+ BAIL_ON_PTR_ERROR(extra_data, wbc_status);
+
+ p = extra_data;
+
+ /* Outer loop processes the list of domain information */
+
+ for (i=0; i<count && p; i++) {
+ char *next = strchr(p, '\n');
+
+ if (next) {
+ *next = '\0';
+ next++;
+ }
+
+ wbc_status = process_domain_info_string(d_list, &d_list[i], p);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ p = next;
+ }
+
+ *domains = d_list;
+ *num_domains = i;
+
+ done:
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ if (d_list)
+ talloc_free(d_list);
+ if (extra_data)
+ free(extra_data);
+ }
+
+ return wbc_status;
+}
+
+/* Enumerate the domain trusts known by Winbind */
+wbcErr wbcLookupDomainController(const char *domain,
+ uint32_t flags,
+ struct wbcDomainControllerInfo **dc_info)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ struct wbcDomainControllerInfo *dc = NULL;
+
+ /* validate input params */
+
+ if (!domain || !dc_info) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.domain_name, domain, sizeof(request.domain_name)-1);
+
+ request.flags = flags;
+
+ dc = talloc(NULL, struct wbcDomainControllerInfo);
+ BAIL_ON_PTR_ERROR(dc, wbc_status);
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponse(WINBINDD_DSGETDCNAME,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ dc->dc_name = talloc_strdup(dc, response.data.dc_name);
+ BAIL_ON_PTR_ERROR(dc->dc_name, wbc_status);
+
+ *dc_info = dc;
+
+done:
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ talloc_free(dc);
+ }
+
+ return wbc_status;
+}
+
+static wbcErr wbc_create_domain_controller_info_ex(TALLOC_CTX *mem_ctx,
+ const struct winbindd_response *resp,
+ struct wbcDomainControllerInfoEx **_i)
+{
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+ struct wbcDomainControllerInfoEx *i;
+ struct wbcGuid guid;
+
+ i = talloc(mem_ctx, struct wbcDomainControllerInfoEx);
+ BAIL_ON_PTR_ERROR(i, wbc_status);
+
+ i->dc_unc = talloc_strdup(i, resp->data.dsgetdcname.dc_unc);
+ BAIL_ON_PTR_ERROR(i->dc_unc, wbc_status);
+
+ i->dc_address = talloc_strdup(i, resp->data.dsgetdcname.dc_address);
+ BAIL_ON_PTR_ERROR(i->dc_address, wbc_status);
+
+ i->dc_address_type = resp->data.dsgetdcname.dc_address_type;
+
+ wbc_status = wbcStringToGuid(resp->data.dsgetdcname.domain_guid, &guid);
+ if (WBC_ERROR_IS_OK(wbc_status)) {
+ i->domain_guid = talloc(i, struct wbcGuid);
+ BAIL_ON_PTR_ERROR(i->domain_guid, wbc_status);
+
+ *i->domain_guid = guid;
+ } else {
+ i->domain_guid = NULL;
+ }
+
+ i->domain_name = talloc_strdup(i, resp->data.dsgetdcname.domain_name);
+ BAIL_ON_PTR_ERROR(i->domain_name, wbc_status);
+
+ if (resp->data.dsgetdcname.forest_name[0] != '\0') {
+ i->forest_name = talloc_strdup(i,
+ resp->data.dsgetdcname.forest_name);
+ BAIL_ON_PTR_ERROR(i->forest_name, wbc_status);
+ } else {
+ i->forest_name = NULL;
+ }
+
+ i->dc_flags = resp->data.dsgetdcname.dc_flags;
+
+ if (resp->data.dsgetdcname.dc_site_name[0] != '\0') {
+ i->dc_site_name = talloc_strdup(i,
+ resp->data.dsgetdcname.dc_site_name);
+ BAIL_ON_PTR_ERROR(i->dc_site_name, wbc_status);
+ } else {
+ i->dc_site_name = NULL;
+ }
+
+ if (resp->data.dsgetdcname.client_site_name[0] != '\0') {
+ i->client_site_name = talloc_strdup(i,
+ resp->data.dsgetdcname.client_site_name);
+ BAIL_ON_PTR_ERROR(i->client_site_name, wbc_status);
+ } else {
+ i->client_site_name = NULL;
+ }
+
+ *_i = i;
+ i = NULL;
+
+done:
+ talloc_free(i);
+ return wbc_status;
+}
+
+/* Get extended domain controller information */
+wbcErr wbcLookupDomainControllerEx(const char *domain,
+ struct wbcGuid *guid,
+ const char *site,
+ uint32_t flags,
+ struct wbcDomainControllerInfoEx **dc_info)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ /* validate input params */
+
+ if (!domain || !dc_info) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.dsgetdcname.flags = flags;
+
+ strncpy(request.data.dsgetdcname.domain_name, domain,
+ sizeof(request.data.dsgetdcname.domain_name)-1);
+
+ if (site) {
+ strncpy(request.data.dsgetdcname.site_name, site,
+ sizeof(request.data.dsgetdcname.site_name)-1);
+ }
+
+ if (guid) {
+ char *str = NULL;
+
+ wbc_status = wbcGuidToString(guid, &str);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ strncpy(request.data.dsgetdcname.domain_guid, str,
+ sizeof(request.data.dsgetdcname.domain_guid)-1);
+
+ wbcFreeMemory(str);
+ }
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponse(WINBINDD_DSGETDCNAME,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ if (dc_info) {
+ wbc_status = wbc_create_domain_controller_info_ex(NULL,
+ &response,
+ dc_info);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = WBC_ERR_SUCCESS;
+done:
+ return wbc_status;
+}
+
+/* Initialize a named blob and add to list of blobs */
+wbcErr wbcAddNamedBlob(size_t *num_blobs,
+ struct wbcNamedBlob **blobs,
+ const char *name,
+ uint32_t flags,
+ uint8_t *data,
+ size_t length)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcNamedBlob blob;
+
+ *blobs = talloc_realloc(NULL, *blobs, struct wbcNamedBlob,
+ *(num_blobs)+1);
+ BAIL_ON_PTR_ERROR(*blobs, wbc_status);
+
+ blob.name = talloc_strdup(*blobs, name);
+ BAIL_ON_PTR_ERROR(blob.name, wbc_status);
+ blob.flags = flags;
+ blob.blob.length = length;
+ blob.blob.data = (uint8_t *)talloc_memdup(*blobs, data, length);
+ BAIL_ON_PTR_ERROR(blob.blob.data, wbc_status);
+
+ (*(blobs))[*num_blobs] = blob;
+ *(num_blobs) += 1;
+
+ wbc_status = WBC_ERR_SUCCESS;
+done:
+ if (!WBC_ERROR_IS_OK(wbc_status) && blobs) {
+ wbcFreeMemory(*blobs);
+ }
+ return wbc_status;
+}
diff --git a/nsswitch/libwbclient/wbclient.c b/nsswitch/libwbclient/wbclient.c
new file mode 100644
index 0000000000..f5c72315f2
--- /dev/null
+++ b/nsswitch/libwbclient/wbclient.c
@@ -0,0 +1,157 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Required Headers */
+
+#include "libwbclient.h"
+
+/* From wb_common.c */
+
+NSS_STATUS winbindd_request_response(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+
+/** @brief Wrapper around Winbind's send/receive API call
+ *
+ * @param cmd Winbind command operation to perform
+ * @param request Send structure
+ * @param response Receive structure
+ *
+ * @return #wbcErr
+ **/
+
+/**********************************************************************
+ result == NSS_STATUS_UNAVAIL: winbind not around
+ result == NSS_STATUS_NOTFOUND: winbind around, but domain missing
+
+ Due to a bad API NSS_STATUS_NOTFOUND is returned both when winbind_off
+ and when winbind return WINBINDD_ERROR. So the semantics of this
+ routine depends on winbind_on. Grepping for winbind_off I just
+ found 3 places where winbind is turned off, and this does not conflict
+ (as far as I have seen) with the callers of is_trusted_domains.
+
+ --Volker
+**********************************************************************/
+
+wbcErr wbcRequestResponse(int cmd,
+ struct winbindd_request *request,
+ struct winbindd_response *response)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ NSS_STATUS nss_status;
+
+ /* for some calls the request and/or response can be NULL */
+
+ nss_status = winbindd_request_response(cmd, request, response);
+
+ switch (nss_status) {
+ case NSS_STATUS_SUCCESS:
+ wbc_status = WBC_ERR_SUCCESS;
+ break;
+ case NSS_STATUS_UNAVAIL:
+ wbc_status = WBC_ERR_WINBIND_NOT_AVAILABLE;
+ break;
+ case NSS_STATUS_NOTFOUND:
+ wbc_status = WBC_ERR_DOMAIN_NOT_FOUND;
+ break;
+ default:
+ wbc_status = WBC_ERR_NSS_ERROR;
+ break;
+ }
+
+ return wbc_status;
+}
+
+/** @brief Translate an error value into a string
+ *
+ * @param error
+ *
+ * @return a pointer to a static string
+ **/
+const char *wbcErrorString(wbcErr error)
+{
+ switch (error) {
+ case WBC_ERR_SUCCESS:
+ return "WBC_ERR_SUCCESS";
+ case WBC_ERR_NOT_IMPLEMENTED:
+ return "WBC_ERR_NOT_IMPLEMENTED";
+ case WBC_ERR_UNKNOWN_FAILURE:
+ return "WBC_ERR_UNKNOWN_FAILURE";
+ case WBC_ERR_NO_MEMORY:
+ return "WBC_ERR_NO_MEMORY";
+ case WBC_ERR_INVALID_SID:
+ return "WBC_ERR_INVALID_SID";
+ case WBC_ERR_INVALID_PARAM:
+ return "WBC_ERR_INVALID_PARAM";
+ case WBC_ERR_WINBIND_NOT_AVAILABLE:
+ return "WBC_ERR_WINBIND_NOT_AVAILABLE";
+ case WBC_ERR_DOMAIN_NOT_FOUND:
+ return "WBC_ERR_DOMAIN_NOT_FOUND";
+ case WBC_ERR_INVALID_RESPONSE:
+ return "WBC_ERR_INVALID_RESPONSE";
+ case WBC_ERR_NSS_ERROR:
+ return "WBC_ERR_NSS_ERROR";
+ case WBC_ERR_UNKNOWN_USER:
+ return "WBC_ERR_UNKNOWN_USER";
+ case WBC_ERR_UNKNOWN_GROUP:
+ return "WBC_ERR_UNKNOWN_GROUP";
+ case WBC_ERR_AUTH_ERROR:
+ return "WBC_ERR_AUTH_ERROR";
+ case WBC_ERR_PWD_CHANGE_FAILED:
+ return "WBC_ERR_PWD_CHANGE_FAILED";
+ }
+
+ return "unknown wbcErr value";
+}
+
+/* Free library allocated memory */
+void wbcFreeMemory(void *p)
+{
+ if (p)
+ talloc_free(p);
+
+ return;
+}
+
+wbcErr wbcLibraryDetails(struct wbcLibraryDetails **_details)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcLibraryDetails *info;
+
+ info = talloc(NULL, struct wbcLibraryDetails);
+ BAIL_ON_PTR_ERROR(info, wbc_status);
+
+ info->major_version = WBCLIENT_MAJOR_VERSION;
+ info->minor_version = WBCLIENT_MINOR_VERSION;
+ info->vendor_version = talloc_strdup(info,
+ WBCLIENT_VENDOR_VERSION);
+ BAIL_ON_PTR_ERROR(info->vendor_version, wbc_status);
+
+ *_details = info;
+ info = NULL;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+done:
+ talloc_free(info);
+ return wbc_status;
+}
diff --git a/nsswitch/libwbclient/wbclient.h b/nsswitch/libwbclient/wbclient.h
new file mode 100644
index 0000000000..990cc52df7
--- /dev/null
+++ b/nsswitch/libwbclient/wbclient.h
@@ -0,0 +1,1187 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WBCLIENT_H
+#define _WBCLIENT_H
+
+#include <pwd.h>
+#include <grp.h>
+
+/* Define error types */
+
+/**
+ * @brief Status codes returned from wbc functions
+ **/
+
+enum _wbcErrType {
+ WBC_ERR_SUCCESS = 0, /**< Successful completion **/
+ WBC_ERR_NOT_IMPLEMENTED,/**< Function not implemented **/
+ WBC_ERR_UNKNOWN_FAILURE,/**< General failure **/
+ WBC_ERR_NO_MEMORY, /**< Memory allocation error **/
+ WBC_ERR_INVALID_SID, /**< Invalid SID format **/
+ WBC_ERR_INVALID_PARAM, /**< An Invalid parameter was supplied **/
+ WBC_ERR_WINBIND_NOT_AVAILABLE, /**< Winbind daemon is not available **/
+ WBC_ERR_DOMAIN_NOT_FOUND, /**< Domain is not trusted or cannot be found **/
+ WBC_ERR_INVALID_RESPONSE, /**< Winbind returned an invalid response **/
+ WBC_ERR_NSS_ERROR, /**< NSS_STATUS error **/
+ WBC_ERR_AUTH_ERROR, /**< Authentication failed **/
+ WBC_ERR_UNKNOWN_USER, /**< User account cannot be found */
+ WBC_ERR_UNKNOWN_GROUP, /**< Group account cannot be found */
+ WBC_ERR_PWD_CHANGE_FAILED /**< Password Change has failed */
+};
+
+typedef enum _wbcErrType wbcErr;
+
+#define WBC_ERROR_IS_OK(x) ((x) == WBC_ERR_SUCCESS)
+
+const char *wbcErrorString(wbcErr error);
+
+/**
+ * @brief Some useful details about the wbclient library
+ *
+ * 0.1: Initial version
+ * 0.2: Added wbcRemoveUidMapping()
+ * Added wbcRemoveGidMapping()
+ **/
+#define WBCLIENT_MAJOR_VERSION 0
+#define WBCLIENT_MINOR_VERSION 2
+#define WBCLIENT_VENDOR_VERSION "Samba libwbclient"
+struct wbcLibraryDetails {
+ uint16_t major_version;
+ uint16_t minor_version;
+ const char *vendor_version;
+};
+
+/**
+ * @brief Some useful details about the running winbindd
+ *
+ **/
+struct wbcInterfaceDetails {
+ uint32_t interface_version;
+ const char *winbind_version;
+ char winbind_separator;
+ const char *netbios_name;
+ const char *netbios_domain;
+ const char *dns_domain;
+};
+
+/*
+ * Data types used by the Winbind Client API
+ */
+
+#ifndef WBC_MAXSUBAUTHS
+#define WBC_MAXSUBAUTHS 15 /* max sub authorities in a SID */
+#endif
+
+/**
+ * @brief Windows Security Identifier
+ *
+ **/
+
+struct wbcDomainSid {
+ uint8_t sid_rev_num;
+ uint8_t num_auths;
+ uint8_t id_auth[6];
+ uint32_t sub_auths[WBC_MAXSUBAUTHS];
+};
+
+/**
+ * @brief Security Identifier type
+ **/
+
+enum wbcSidType {
+ WBC_SID_NAME_USE_NONE=0,
+ WBC_SID_NAME_USER=1,
+ WBC_SID_NAME_DOM_GRP=2,
+ WBC_SID_NAME_DOMAIN=3,
+ WBC_SID_NAME_ALIAS=4,
+ WBC_SID_NAME_WKN_GRP=5,
+ WBC_SID_NAME_DELETED=6,
+ WBC_SID_NAME_INVALID=7,
+ WBC_SID_NAME_UNKNOWN=8,
+ WBC_SID_NAME_COMPUTER=9
+};
+
+/**
+ * @brief Security Identifier with attributes
+ **/
+
+struct wbcSidWithAttr {
+ struct wbcDomainSid sid;
+ uint32_t attributes;
+};
+
+/* wbcSidWithAttr->attributes */
+
+#define WBC_SID_ATTR_GROUP_MANDATORY 0x00000001
+#define WBC_SID_ATTR_GROUP_ENABLED_BY_DEFAULT 0x00000002
+#define WBC_SID_ATTR_GROUP_ENABLED 0x00000004
+#define WBC_SID_ATTR_GROUP_OWNER 0x00000008
+#define WBC_SID_ATTR_GROUP_USEFOR_DENY_ONLY 0x00000010
+#define WBC_SID_ATTR_GROUP_RESOURCE 0x20000000
+#define WBC_SID_ATTR_GROUP_LOGON_ID 0xC0000000
+
+/**
+ * @brief Windows GUID
+ *
+ **/
+
+struct wbcGuid {
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint8_t clock_seq[2];
+ uint8_t node[6];
+};
+
+/**
+ * @brief Domain Information
+ **/
+
+struct wbcDomainInfo {
+ char *short_name;
+ char *dns_name;
+ struct wbcDomainSid sid;
+ uint32_t domain_flags;
+ uint32_t trust_flags;
+ uint32_t trust_type;
+};
+
+/* wbcDomainInfo->domain_flags */
+
+#define WBC_DOMINFO_DOMAIN_UNKNOWN 0x00000000
+#define WBC_DOMINFO_DOMAIN_NATIVE 0x00000001
+#define WBC_DOMINFO_DOMAIN_AD 0x00000002
+#define WBC_DOMINFO_DOMAIN_PRIMARY 0x00000004
+#define WBC_DOMINFO_DOMAIN_OFFLINE 0x00000008
+
+/* wbcDomainInfo->trust_flags */
+
+#define WBC_DOMINFO_TRUST_TRANSITIVE 0x00000001
+#define WBC_DOMINFO_TRUST_INCOMING 0x00000002
+#define WBC_DOMINFO_TRUST_OUTGOING 0x00000004
+
+/* wbcDomainInfo->trust_type */
+
+#define WBC_DOMINFO_TRUSTTYPE_NONE 0x00000000
+#define WBC_DOMINFO_TRUSTTYPE_FOREST 0x00000001
+#define WBC_DOMINFO_TRUSTTYPE_IN_FOREST 0x00000002
+#define WBC_DOMINFO_TRUSTTYPE_EXTERNAL 0x00000003
+
+
+/**
+ * @brief Auth User Parameters
+ **/
+
+struct wbcAuthUserParams {
+ const char *account_name;
+ const char *domain_name;
+ const char *workstation_name;
+
+ uint32_t flags;
+
+ uint32_t parameter_control;
+
+ enum wbcAuthUserLevel {
+ WBC_AUTH_USER_LEVEL_PLAIN = 1,
+ WBC_AUTH_USER_LEVEL_HASH = 2,
+ WBC_AUTH_USER_LEVEL_RESPONSE = 3
+ } level;
+ union {
+ const char *plaintext;
+ struct {
+ uint8_t nt_hash[16];
+ uint8_t lm_hash[16];
+ } hash;
+ struct {
+ uint8_t challenge[8];
+ uint32_t nt_length;
+ uint8_t *nt_data;
+ uint32_t lm_length;
+ uint8_t *lm_data;
+ } response;
+ } password;
+};
+
+/**
+ * @brief Generic Blob
+ **/
+
+struct wbcBlob {
+ uint8_t *data;
+ size_t length;
+};
+
+/**
+ * @brief Named Blob
+ **/
+
+struct wbcNamedBlob {
+ const char *name;
+ uint32_t flags;
+ struct wbcBlob blob;
+};
+
+/**
+ * @brief Logon User Parameters
+ **/
+
+struct wbcLogonUserParams {
+ const char *username;
+ const char *password;
+ size_t num_blobs;
+ struct wbcNamedBlob *blobs;
+};
+
+/**
+ * @brief ChangePassword Parameters
+ **/
+
+struct wbcChangePasswordParams {
+ const char *account_name;
+ const char *domain_name;
+
+ uint32_t flags;
+
+ enum wbcChangePasswordLevel {
+ WBC_CHANGE_PASSWORD_LEVEL_PLAIN = 1,
+ WBC_CHANGE_PASSWORD_LEVEL_RESPONSE = 2
+ } level;
+
+ union {
+ const char *plaintext;
+ struct {
+ uint32_t old_nt_hash_enc_length;
+ uint8_t *old_nt_hash_enc_data;
+ uint32_t old_lm_hash_enc_length;
+ uint8_t *old_lm_hash_enc_data;
+ } response;
+ } old_password;
+ union {
+ const char *plaintext;
+ struct {
+ uint32_t nt_length;
+ uint8_t *nt_data;
+ uint32_t lm_length;
+ uint8_t *lm_data;
+ } response;
+ } new_password;
+};
+
+/* wbcAuthUserParams->parameter_control */
+
+#define WBC_MSV1_0_CLEARTEXT_PASSWORD_ALLOWED 0x00000002
+#define WBC_MSV1_0_UPDATE_LOGON_STATISTICS 0x00000004
+#define WBC_MSV1_0_RETURN_USER_PARAMETERS 0x00000008
+#define WBC_MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT 0x00000020
+#define WBC_MSV1_0_RETURN_PROFILE_PATH 0x00000200
+#define WBC_MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT 0x00000800
+
+/* wbcAuthUserParams->flags */
+
+#define WBC_AUTH_PARAM_FLAGS_INTERACTIVE_LOGON 0x00000001
+
+/**
+ * @brief Auth User Information
+ *
+ * Some of the strings are maybe NULL
+ **/
+
+struct wbcAuthUserInfo {
+ uint32_t user_flags;
+
+ char *account_name;
+ char *user_principal;
+ char *full_name;
+ char *domain_name;
+ char *dns_domain_name;
+
+ uint32_t acct_flags;
+ uint8_t user_session_key[16];
+ uint8_t lm_session_key[8];
+
+ uint16_t logon_count;
+ uint16_t bad_password_count;
+
+ uint64_t logon_time;
+ uint64_t logoff_time;
+ uint64_t kickoff_time;
+ uint64_t pass_last_set_time;
+ uint64_t pass_can_change_time;
+ uint64_t pass_must_change_time;
+
+ char *logon_server;
+ char *logon_script;
+ char *profile_path;
+ char *home_directory;
+ char *home_drive;
+
+ /*
+ * the 1st one is the account sid
+ * the 2nd one is the primary_group sid
+ * followed by the rest of the groups
+ */
+ uint32_t num_sids;
+ struct wbcSidWithAttr *sids;
+};
+
+/**
+ * @brief Logon User Information
+ *
+ * Some of the strings are maybe NULL
+ **/
+
+struct wbcLogonUserInfo {
+ struct wbcAuthUserInfo *info;
+ size_t num_blobs;
+ struct wbcNamedBlob *blobs;
+};
+
+/* wbcAuthUserInfo->user_flags */
+
+#define WBC_AUTH_USER_INFO_GUEST 0x00000001
+#define WBC_AUTH_USER_INFO_NOENCRYPTION 0x00000002
+#define WBC_AUTH_USER_INFO_CACHED_ACCOUNT 0x00000004
+#define WBC_AUTH_USER_INFO_USED_LM_PASSWORD 0x00000008
+#define WBC_AUTH_USER_INFO_EXTRA_SIDS 0x00000020
+#define WBC_AUTH_USER_INFO_SUBAUTH_SESSION_KEY 0x00000040
+#define WBC_AUTH_USER_INFO_SERVER_TRUST_ACCOUNT 0x00000080
+#define WBC_AUTH_USER_INFO_NTLMV2_ENABLED 0x00000100
+#define WBC_AUTH_USER_INFO_RESOURCE_GROUPS 0x00000200
+#define WBC_AUTH_USER_INFO_PROFILE_PATH_RETURNED 0x00000400
+#define WBC_AUTH_USER_INFO_GRACE_LOGON 0x01000000
+
+/* wbcAuthUserInfo->acct_flags */
+
+#define WBC_ACB_DISABLED 0x00000001 /* 1 User account disabled */
+#define WBC_ACB_HOMDIRREQ 0x00000002 /* 1 Home directory required */
+#define WBC_ACB_PWNOTREQ 0x00000004 /* 1 User password not required */
+#define WBC_ACB_TEMPDUP 0x00000008 /* 1 Temporary duplicate account */
+#define WBC_ACB_NORMAL 0x00000010 /* 1 Normal user account */
+#define WBC_ACB_MNS 0x00000020 /* 1 MNS logon user account */
+#define WBC_ACB_DOMTRUST 0x00000040 /* 1 Interdomain trust account */
+#define WBC_ACB_WSTRUST 0x00000080 /* 1 Workstation trust account */
+#define WBC_ACB_SVRTRUST 0x00000100 /* 1 Server trust account */
+#define WBC_ACB_PWNOEXP 0x00000200 /* 1 User password does not expire */
+#define WBC_ACB_AUTOLOCK 0x00000400 /* 1 Account auto locked */
+#define WBC_ACB_ENC_TXT_PWD_ALLOWED 0x00000800 /* 1 Encryped text password is allowed */
+#define WBC_ACB_SMARTCARD_REQUIRED 0x00001000 /* 1 Smart Card required */
+#define WBC_ACB_TRUSTED_FOR_DELEGATION 0x00002000 /* 1 Trusted for Delegation */
+#define WBC_ACB_NOT_DELEGATED 0x00004000 /* 1 Not delegated */
+#define WBC_ACB_USE_DES_KEY_ONLY 0x00008000 /* 1 Use DES key only */
+#define WBC_ACB_DONT_REQUIRE_PREAUTH 0x00010000 /* 1 Preauth not required */
+#define WBC_ACB_PW_EXPIRED 0x00020000 /* 1 Password Expired */
+#define WBC_ACB_NO_AUTH_DATA_REQD 0x00080000 /* 1 = No authorization data required */
+
+struct wbcAuthErrorInfo {
+ uint32_t nt_status;
+ char *nt_string;
+ int32_t pam_error;
+ char *display_string;
+};
+
+/**
+ * @brief User Password Policy Information
+ **/
+
+/* wbcUserPasswordPolicyInfo->password_properties */
+
+#define WBC_DOMAIN_PASSWORD_COMPLEX 0x00000001
+#define WBC_DOMAIN_PASSWORD_NO_ANON_CHANGE 0x00000002
+#define WBC_DOMAIN_PASSWORD_NO_CLEAR_CHANGE 0x00000004
+#define WBC_DOMAIN_PASSWORD_LOCKOUT_ADMINS 0x00000008
+#define WBC_DOMAIN_PASSWORD_STORE_CLEARTEXT 0x00000010
+#define WBC_DOMAIN_REFUSE_PASSWORD_CHANGE 0x00000020
+
+struct wbcUserPasswordPolicyInfo {
+ uint32_t min_length_password;
+ uint32_t password_history;
+ uint32_t password_properties;
+ uint64_t expire;
+ uint64_t min_passwordage;
+};
+
+/**
+ * @brief Change Password Reject Reason
+ **/
+
+enum wbcPasswordChangeRejectReason {
+ WBC_PWD_CHANGE_REJECT_OTHER=0,
+ WBC_PWD_CHANGE_REJECT_TOO_SHORT=1,
+ WBC_PWD_CHANGE_REJECT_IN_HISTORY=2,
+ WBC_PWD_CHANGE_REJECT_COMPLEXITY=5
+};
+
+/**
+ * @brief Logoff User Parameters
+ **/
+
+struct wbcLogoffUserParams {
+ const char *username;
+ size_t num_blobs;
+ struct wbcNamedBlob *blobs;
+};
+
+/** @brief Credential cache log-on parameters
+ *
+ */
+
+struct wbcCredentialCacheParams {
+ const char *account_name;
+ const char *domain_name;
+ enum wbcCredentialCacheLevel {
+ WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP = 1
+ } level;
+ size_t num_blobs;
+ struct wbcNamedBlob *blobs;
+};
+
+
+/** @brief Info returned by credential cache auth
+ *
+ */
+
+struct wbcCredentialCacheInfo {
+ size_t num_blobs;
+ struct wbcNamedBlob *blobs;
+};
+
+/*
+ * DomainControllerInfo struct
+ */
+struct wbcDomainControllerInfo {
+ char *dc_name;
+};
+
+/*
+ * DomainControllerInfoEx struct
+ */
+struct wbcDomainControllerInfoEx {
+ const char *dc_unc;
+ const char *dc_address;
+ uint16_t dc_address_type;
+ struct wbcGuid *domain_guid;
+ const char *domain_name;
+ const char *forest_name;
+ uint32_t dc_flags;
+ const char *dc_site_name;
+ const char *client_site_name;
+};
+
+/**********************************************************
+ * Memory Management
+ **********************************************************/
+
+/**
+ * @brief Free library allocated memory
+ *
+ * @param *p Pointer to free
+ *
+ * @return void
+ **/
+void wbcFreeMemory(void*);
+
+
+/*
+ * Utility functions for dealing with SIDs
+ */
+
+/**
+ * @brief Convert a binary SID to a character string
+ *
+ * @param sid Binary Security Identifier
+ * @param **sid_string Resulting character string
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcSidToString(const struct wbcDomainSid *sid,
+ char **sid_string);
+
+/**
+ * @brief Convert a character string to a binary SID
+ *
+ * @param *str Character string in the form of S-...
+ * @param sid Resulting binary SID
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcStringToSid(const char *sid_string,
+ struct wbcDomainSid *sid);
+
+/*
+ * Utility functions for dealing with GUIDs
+ */
+
+/**
+ * @brief Convert a binary GUID to a character string
+ *
+ * @param guid Binary Guid
+ * @param **guid_string Resulting character string
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGuidToString(const struct wbcGuid *guid,
+ char **guid_string);
+
+/**
+ * @brief Convert a character string to a binary GUID
+ *
+ * @param *str Character string
+ * @param guid Resulting binary GUID
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcStringToGuid(const char *guid_string,
+ struct wbcGuid *guid);
+
+/**
+ * @brief Ping winbindd to see if the daemon is running
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcPing(void);
+
+wbcErr wbcLibraryDetails(struct wbcLibraryDetails **details);
+
+wbcErr wbcInterfaceDetails(struct wbcInterfaceDetails **details);
+
+/**********************************************************
+ * Name/SID conversion
+ **********************************************************/
+
+/**
+ * @brief Convert a domain and name to SID
+ *
+ * @param domain Domain name (possibly "")
+ * @param name User or group name
+ * @param *sid Pointer to the resolved domain SID
+ * @param *name_type Pointer to the SID type
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcLookupName(const char *dom_name,
+ const char *name,
+ struct wbcDomainSid *sid,
+ enum wbcSidType *name_type);
+
+/**
+ * @brief Convert a SID to a domain and name
+ *
+ * @param *sid Pointer to the domain SID to be resolved
+ * @param pdomain Resolved Domain name (possibly "")
+ * @param pname Resolved User or group name
+ * @param *pname_type Pointer to the resolved SID type
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcLookupSid(const struct wbcDomainSid *sid,
+ char **domain,
+ char **name,
+ enum wbcSidType *name_type);
+
+/**
+ * @brief Translate a collection of RIDs within a domain to names
+ */
+wbcErr wbcLookupRids(struct wbcDomainSid *dom_sid,
+ int num_rids,
+ uint32_t *rids,
+ const char **domain_name,
+ const char ***names,
+ enum wbcSidType **types);
+
+/*
+ * @brief Get the groups a user belongs to
+ **/
+wbcErr wbcLookupUserSids(const struct wbcDomainSid *user_sid,
+ bool domain_groups_only,
+ uint32_t *num_sids,
+ struct wbcDomainSid **sids);
+
+/**
+ * @brief Lists Users
+ **/
+wbcErr wbcListUsers(const char *domain_name,
+ uint32_t *num_users,
+ const char ***users);
+
+/**
+ * @brief Lists Groups
+ **/
+wbcErr wbcListGroups(const char *domain_name,
+ uint32_t *num_groups,
+ const char ***groups);
+
+wbcErr wbcGetDisplayName(const struct wbcDomainSid *sid,
+ char **pdomain,
+ char **pfullname,
+ enum wbcSidType *pname_type);
+
+/**********************************************************
+ * SID/uid/gid Mappings
+ **********************************************************/
+
+/**
+ * @brief Convert a Windows SID to a Unix uid, allocating an uid if needed
+ *
+ * @param *sid Pointer to the domain SID to be resolved
+ * @param *puid Pointer to the resolved uid_t value
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcSidToUid(const struct wbcDomainSid *sid,
+ uid_t *puid);
+
+/**
+ * @brief Convert a Windows SID to a Unix uid if there already is a mapping
+ *
+ * @param *sid Pointer to the domain SID to be resolved
+ * @param *puid Pointer to the resolved uid_t value
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcQuerySidToUid(const struct wbcDomainSid *sid,
+ uid_t *puid);
+
+/**
+ * @brief Convert a Unix uid to a Windows SID, allocating a SID if needed
+ *
+ * @param uid Unix uid to be resolved
+ * @param *sid Pointer to the resolved domain SID
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcUidToSid(uid_t uid,
+ struct wbcDomainSid *sid);
+
+/**
+ * @brief Convert a Unix uid to a Windows SID if there already is a mapping
+ *
+ * @param uid Unix uid to be resolved
+ * @param *sid Pointer to the resolved domain SID
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcQueryUidToSid(uid_t uid,
+ struct wbcDomainSid *sid);
+
+/**
+ * @brief Convert a Windows SID to a Unix gid, allocating a gid if needed
+ *
+ * @param *sid Pointer to the domain SID to be resolved
+ * @param *pgid Pointer to the resolved gid_t value
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcSidToGid(const struct wbcDomainSid *sid,
+ gid_t *pgid);
+
+/**
+ * @brief Convert a Windows SID to a Unix gid if there already is a mapping
+ *
+ * @param *sid Pointer to the domain SID to be resolved
+ * @param *pgid Pointer to the resolved gid_t value
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcQuerySidToGid(const struct wbcDomainSid *sid,
+ gid_t *pgid);
+
+/**
+ * @brief Convert a Unix gid to a Windows SID, allocating a SID if needed
+ *
+ * @param gid Unix gid to be resolved
+ * @param *sid Pointer to the resolved domain SID
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcGidToSid(gid_t gid,
+ struct wbcDomainSid *sid);
+
+/**
+ * @brief Convert a Unix gid to a Windows SID if there already is a mapping
+ *
+ * @param gid Unix gid to be resolved
+ * @param *sid Pointer to the resolved domain SID
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcQueryGidToSid(gid_t gid,
+ struct wbcDomainSid *sid);
+
+/**
+ * @brief Obtain a new uid from Winbind
+ *
+ * @param *puid *pointer to the allocated uid
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcAllocateUid(uid_t *puid);
+
+/**
+ * @brief Obtain a new gid from Winbind
+ *
+ * @param *pgid Pointer to the allocated gid
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcAllocateGid(gid_t *pgid);
+
+/**
+ * @brief Set an user id mapping
+ *
+ * @param uid Uid of the desired mapping.
+ * @param *sid Pointer to the sid of the diresired mapping.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcSetUidMapping(uid_t uid, const struct wbcDomainSid *sid);
+
+/**
+ * @brief Set a group id mapping
+ *
+ * @param gid Gid of the desired mapping.
+ * @param *sid Pointer to the sid of the diresired mapping.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcSetGidMapping(gid_t gid, const struct wbcDomainSid *sid);
+
+/**
+ * @brief Remove a user id mapping
+ *
+ * @param uid Uid of the mapping to remove.
+ * @param *sid Pointer to the sid of the mapping to remove.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcRemoveUidMapping(uid_t uid, const struct wbcDomainSid *sid);
+
+/**
+ * @brief Remove a group id mapping
+ *
+ * @param gid Gid of the mapping to remove.
+ * @param *sid Pointer to the sid of the mapping to remove.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcRemoveGidMapping(gid_t gid, const struct wbcDomainSid *sid);
+
+/**
+ * @brief Set the highwater mark for allocated uids.
+ *
+ * @param uid_hwm The new uid highwater mark value
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcSetUidHwm(uid_t uid_hwm);
+
+/**
+ * @brief Set the highwater mark for allocated gids.
+ *
+ * @param gid_hwm The new gid highwater mark value
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcSetGidHwm(gid_t gid_hwm);
+
+/**********************************************************
+ * NSS Lookup User/Group details
+ **********************************************************/
+
+/**
+ * @brief Fill in a struct passwd* for a domain user based
+ * on username
+ *
+ * @param *name Username to lookup
+ * @param **pwd Pointer to resulting struct passwd* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetpwnam(const char *name, struct passwd **pwd);
+
+/**
+ * @brief Fill in a struct passwd* for a domain user based
+ * on uid
+ *
+ * @param uid Uid to lookup
+ * @param **pwd Pointer to resulting struct passwd* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetpwuid(uid_t uid, struct passwd **pwd);
+
+/**
+ * @brief Fill in a struct passwd* for a domain user based
+ * on username
+ *
+ * @param *name Username to lookup
+ * @param **grp Pointer to resulting struct group* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetgrnam(const char *name, struct group **grp);
+
+/**
+ * @brief Fill in a struct passwd* for a domain user based
+ * on uid
+ *
+ * @param gid Uid to lookup
+ * @param **grp Pointer to resulting struct group* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetgrgid(gid_t gid, struct group **grp);
+
+/**
+ * @brief Reset the passwd iterator
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcSetpwent(void);
+
+/**
+ * @brief Close the passwd iterator
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcEndpwent(void);
+
+/**
+ * @brief Return the next struct passwd* entry from the pwent iterator
+ *
+ * @param **pwd Pointer to resulting struct passwd* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetpwent(struct passwd **pwd);
+
+/**
+ * @brief Reset the group iterator
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcSetgrent(void);
+
+/**
+ * @brief Close the group iterator
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcEndgrent(void);
+
+/**
+ * @brief Return the next struct group* entry from the pwent iterator
+ *
+ * @param **grp Pointer to resulting struct group* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetgrent(struct group **grp);
+
+/**
+ * @brief Return the next struct group* entry from the pwent iterator
+ *
+ * This is similar to #wbcGetgrent, just that the member list is empty
+ *
+ * @param **grp Pointer to resulting struct group* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetgrlist(struct group **grp);
+
+/**
+ * @brief Return the unix group array belonging to the given user
+ *
+ * @param *account The given user name
+ * @param *num_groups Number of elements returned in the groups array
+ * @param **_groups Pointer to resulting gid_t array.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetGroups(const char *account,
+ uint32_t *num_groups,
+ gid_t **_groups);
+
+
+/**********************************************************
+ * Lookup Domain information
+ **********************************************************/
+
+/**
+ * @brief Lookup the current status of a trusted domain
+ *
+ * @param domain Domain to query
+ * @param *dinfo Pointer to returned domain_info struct
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcDomainInfo(const char *domain,
+ struct wbcDomainInfo **info);
+
+/**
+ * @brief Enumerate the domain trusts known by Winbind
+ *
+ * @param **domains Pointer to the allocated domain list array
+ * @param *num_domains Pointer to number of domains returned
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcListTrusts(struct wbcDomainInfo **domains,
+ size_t *num_domains);
+
+/* Flags for wbcLookupDomainController */
+
+#define WBC_LOOKUP_DC_FORCE_REDISCOVERY 0x00000001
+#define WBC_LOOKUP_DC_DS_REQUIRED 0x00000010
+#define WBC_LOOKUP_DC_DS_PREFERRED 0x00000020
+#define WBC_LOOKUP_DC_GC_SERVER_REQUIRED 0x00000040
+#define WBC_LOOKUP_DC_PDC_REQUIRED 0x00000080
+#define WBC_LOOKUP_DC_BACKGROUND_ONLY 0x00000100
+#define WBC_LOOKUP_DC_IP_REQUIRED 0x00000200
+#define WBC_LOOKUP_DC_KDC_REQUIRED 0x00000400
+#define WBC_LOOKUP_DC_TIMESERV_REQUIRED 0x00000800
+#define WBC_LOOKUP_DC_WRITABLE_REQUIRED 0x00001000
+#define WBC_LOOKUP_DC_GOOD_TIMESERV_PREFERRED 0x00002000
+#define WBC_LOOKUP_DC_AVOID_SELF 0x00004000
+#define WBC_LOOKUP_DC_ONLY_LDAP_NEEDED 0x00008000
+#define WBC_LOOKUP_DC_IS_FLAT_NAME 0x00010000
+#define WBC_LOOKUP_DC_IS_DNS_NAME 0x00020000
+#define WBC_LOOKUP_DC_TRY_NEXTCLOSEST_SITE 0x00040000
+#define WBC_LOOKUP_DC_DS_6_REQUIRED 0x00080000
+#define WBC_LOOKUP_DC_RETURN_DNS_NAME 0x40000000
+#define WBC_LOOKUP_DC_RETURN_FLAT_NAME 0x80000000
+
+/**
+ * @brief Enumerate the domain trusts known by Winbind
+ *
+ * @param domain Name of the domain to query for a DC
+ * @param flags Bit flags used to control the domain location query
+ * @param *dc_info Pointer to the returned domain controller information
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcLookupDomainController(const char *domain,
+ uint32_t flags,
+ struct wbcDomainControllerInfo **dc_info);
+
+/**
+ * @brief Get extended domain controller information
+ *
+ * @param domain Name of the domain to query for a DC
+ * @param guid Guid of the domain to query for a DC
+ * @param site Site of the domain to query for a DC
+ * @param flags Bit flags used to control the domain location query
+ * @param *dc_info Pointer to the returned extended domain controller information
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcLookupDomainControllerEx(const char *domain,
+ struct wbcGuid *guid,
+ const char *site,
+ uint32_t flags,
+ struct wbcDomainControllerInfoEx **dc_info);
+
+/**********************************************************
+ * Athenticate functions
+ **********************************************************/
+
+/**
+ * @brief Authenticate a username/password pair
+ *
+ * @param username Name of user to authenticate
+ * @param password Clear text password os user
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcAuthenticateUser(const char *username,
+ const char *password);
+
+/**
+ * @brief Authenticate with more detailed information
+ *
+ * @param params Input parameters, WBC_AUTH_USER_LEVEL_HASH
+ * is not supported yet
+ * @param info Output details on WBC_ERR_SUCCESS
+ * @param error Output details on WBC_ERR_AUTH_ERROR
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params,
+ struct wbcAuthUserInfo **info,
+ struct wbcAuthErrorInfo **error);
+
+/**
+ * @brief Logon a User
+ *
+ * @param[in] params Pointer to a wbcLogonUserParams structure
+ * @param[out] info Pointer to a pointer to a wbcLogonUserInfo structure
+ * @param[out] error Pointer to a pointer to a wbcAuthErrorInfo structure
+ * @param[out] policy Pointer to a pointer to a wbcUserPasswordPolicyInfo structure
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcLogonUser(const struct wbcLogonUserParams *params,
+ struct wbcLogonUserInfo **info,
+ struct wbcAuthErrorInfo **error,
+ struct wbcUserPasswordPolicyInfo **policy);
+
+/**
+ * @brief Trigger a logoff notification to Winbind for a specific user
+ *
+ * @param username Name of user to remove from Winbind's list of
+ * logged on users.
+ * @param uid Uid assigned to the username
+ * @param ccfilename Absolute path to the Krb5 credentials cache to
+ * be removed
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcLogoffUser(const char *username,
+ uid_t uid,
+ const char *ccfilename);
+
+/**
+ * @brief Trigger an extended logoff notification to Winbind for a specific user
+ *
+ * @param params A wbcLogoffUserParams structure
+ * @param error User output details on error
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcLogoffUserEx(const struct wbcLogoffUserParams *params,
+ struct wbcAuthErrorInfo **error);
+
+/**
+ * @brief Change a password for a user
+ *
+ * @param username Name of user to authenticate
+ * @param old_password Old clear text password of user
+ * @param new_password New clear text password of user
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcChangeUserPassword(const char *username,
+ const char *old_password,
+ const char *new_password);
+
+/**
+ * @brief Change a password for a user with more detailed information upon
+ * failure
+ *
+ * @param params Input parameters
+ * @param error User output details on WBC_ERR_PWD_CHANGE_FAILED
+ * @param reject_reason New password reject reason on WBC_ERR_PWD_CHANGE_FAILED
+ * @param policy Password policy output details on WBC_ERR_PWD_CHANGE_FAILED
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcChangeUserPasswordEx(const struct wbcChangePasswordParams *params,
+ struct wbcAuthErrorInfo **error,
+ enum wbcPasswordChangeRejectReason *reject_reason,
+ struct wbcUserPasswordPolicyInfo **policy);
+
+/**
+ * @brief Authenticate a user with cached credentials
+ *
+ * @param *params Pointer to a wbcCredentialCacheParams structure
+ * @param **info Pointer to a pointer to a wbcCredentialCacheInfo structure
+ * @param **error Pointer to a pointer to a wbcAuthErrorInfo structure
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCredentialCache(struct wbcCredentialCacheParams *params,
+ struct wbcCredentialCacheInfo **info,
+ struct wbcAuthErrorInfo **error);
+
+/**********************************************************
+ * Resolve functions
+ **********************************************************/
+
+/**
+ * @brief Resolve a NetbiosName via WINS
+ *
+ * @param name Name to resolve
+ * @param *ip Pointer to the ip address string
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcResolveWinsByName(const char *name, char **ip);
+
+/**
+ * @brief Resolve an IP address via WINS into a NetbiosName
+ *
+ * @param ip The ip address string
+ * @param *name Pointer to the name
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcResolveWinsByIP(const char *ip, char **name);
+
+/**********************************************************
+ * Trusted domain functions
+ **********************************************************/
+
+/**
+ * @brief Trigger a verification of the trust credentials of a specific domain
+ *
+ * @param *domain The name of the domain, only NULL for the default domain is
+ * supported yet. Other values than NULL will result in
+ * WBC_ERR_NOT_IMPLEMENTED.
+ * @param error Output details on WBC_ERR_AUTH_ERROR
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCheckTrustCredentials(const char *domain,
+ struct wbcAuthErrorInfo **error);
+
+/**********************************************************
+ * Helper functions
+ **********************************************************/
+
+/**
+ * @brief Initialize a named blob and add to list of blobs
+ *
+ * @param[in,out] num_blobs Pointer to the number of blobs
+ * @param[in,out] blobs Pointer to an array of blobs
+ * @param[in] name Name of the new named blob
+ * @param[in] flags Flags of the new named blob
+ * @param[in] data Blob data of new blob
+ * @param[in] length Blob data length of new blob
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcAddNamedBlob(size_t *num_blobs,
+ struct wbcNamedBlob **blobs,
+ const char *name,
+ uint32_t flags,
+ uint8_t *data,
+ size_t length);
+
+#endif /* _WBCLIENT_H */
diff --git a/nsswitch/libwbclient/wbclient_internal.h b/nsswitch/libwbclient/wbclient_internal.h
new file mode 100644
index 0000000000..fc03c5409b
--- /dev/null
+++ b/nsswitch/libwbclient/wbclient_internal.h
@@ -0,0 +1,32 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WBCLIENT_INTERNAL_H
+#define _WBCLIENT_INTERNAL_H
+
+/* Private functions */
+
+wbcErr wbcRequestResponse(int cmd,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+
+
+#endif /* _WBCLIENT_INTERNAL_H */
diff --git a/nsswitch/nsstest.c b/nsswitch/nsstest.c
new file mode 100644
index 0000000000..26f816f5d1
--- /dev/null
+++ b/nsswitch/nsstest.c
@@ -0,0 +1,411 @@
+/*
+ Unix SMB/CIFS implementation.
+ nss tester for winbindd
+ Copyright (C) Andrew Tridgell 2001
+
+ 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"
+
+#include "nsswitch/nsstest.h"
+
+static const char *so_path = "/lib/libnss_winbind.so";
+static const char *nss_name = "winbind";
+static int nss_errno;
+static NSS_STATUS last_error;
+static int total_errors;
+
+static void *find_fn(const char *name)
+{
+ char s[1024];
+ static void *h;
+ void *res;
+
+ snprintf(s,sizeof(s), "_nss_%s_%s", nss_name, name);
+
+ if (!h) {
+ h = dlopen(so_path, RTLD_LAZY);
+ }
+ if (!h) {
+ printf("Can't open shared library %s\n", so_path);
+ exit(1);
+ }
+ res = dlsym(h, s);
+ if (!res) {
+ printf("Can't find function %s\n", s);
+ return NULL;
+ }
+ return res;
+}
+
+static void report_nss_error(const char *who, NSS_STATUS status)
+{
+ last_error = status;
+ total_errors++;
+ printf("ERROR %s: NSS_STATUS=%d %d (nss_errno=%d)\n",
+ who, status, NSS_STATUS_SUCCESS, nss_errno);
+}
+
+static struct passwd *nss_getpwent(void)
+{
+ NSS_STATUS (*_nss_getpwent_r)(struct passwd *, char *,
+ size_t , int *) = find_fn("getpwent_r");
+ static struct passwd pwd;
+ static char buf[1000];
+ NSS_STATUS status;
+
+ status = _nss_getpwent_r(&pwd, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getpwent", status);
+ return NULL;
+ }
+ return &pwd;
+}
+
+static struct passwd *nss_getpwnam(const char *name)
+{
+ NSS_STATUS (*_nss_getpwnam_r)(const char *, struct passwd *, char *,
+ size_t , int *) = find_fn("getpwnam_r");
+ static struct passwd pwd;
+ static char buf[1000];
+ NSS_STATUS status;
+
+ status = _nss_getpwnam_r(name, &pwd, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getpwnam", status);
+ return NULL;
+ }
+ return &pwd;
+}
+
+static struct passwd *nss_getpwuid(uid_t uid)
+{
+ NSS_STATUS (*_nss_getpwuid_r)(uid_t , struct passwd *, char *,
+ size_t , int *) = find_fn("getpwuid_r");
+ static struct passwd pwd;
+ static char buf[1000];
+ NSS_STATUS status;
+
+ status = _nss_getpwuid_r(uid, &pwd, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getpwuid", status);
+ return NULL;
+ }
+ return &pwd;
+}
+
+static void nss_setpwent(void)
+{
+ NSS_STATUS (*_nss_setpwent)(void) = find_fn("setpwent");
+ NSS_STATUS status;
+ status = _nss_setpwent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("setpwent", status);
+ }
+}
+
+static void nss_endpwent(void)
+{
+ NSS_STATUS (*_nss_endpwent)(void) = find_fn("endpwent");
+ NSS_STATUS status;
+ status = _nss_endpwent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("endpwent", status);
+ }
+}
+
+
+static struct group *nss_getgrent(void)
+{
+ NSS_STATUS (*_nss_getgrent_r)(struct group *, char *,
+ size_t , int *) = find_fn("getgrent_r");
+ static struct group grp;
+ static char *buf;
+ static int buflen = 1024;
+ NSS_STATUS status;
+
+ if (!buf) buf = malloc_array_p(char, buflen);
+
+again:
+ status = _nss_getgrent_r(&grp, buf, buflen, &nss_errno);
+ if (status == NSS_STATUS_TRYAGAIN) {
+ buflen *= 2;
+ buf = realloc_p(buf, char, buflen);
+ goto again;
+ }
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getgrent", status);
+ return NULL;
+ }
+ return &grp;
+}
+
+static struct group *nss_getgrnam(const char *name)
+{
+ NSS_STATUS (*_nss_getgrnam_r)(const char *, struct group *, char *,
+ size_t , int *) = find_fn("getgrnam_r");
+ static struct group grp;
+ static char *buf;
+ static int buflen = 1000;
+ NSS_STATUS status;
+
+ if (!buf) buf = malloc_array_p(char, buflen);
+again:
+ status = _nss_getgrnam_r(name, &grp, buf, buflen, &nss_errno);
+ if (status == NSS_STATUS_TRYAGAIN) {
+ buflen *= 2;
+ buf = realloc_p(buf, char, buflen);
+ goto again;
+ }
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getgrnam", status);
+ return NULL;
+ }
+ return &grp;
+}
+
+static struct group *nss_getgrgid(gid_t gid)
+{
+ NSS_STATUS (*_nss_getgrgid_r)(gid_t , struct group *, char *,
+ size_t , int *) = find_fn("getgrgid_r");
+ static struct group grp;
+ static char *buf;
+ static int buflen = 1000;
+ NSS_STATUS status;
+
+ if (!buf) buf = malloc_array_p(char, buflen);
+again:
+ status = _nss_getgrgid_r(gid, &grp, buf, buflen, &nss_errno);
+ if (status == NSS_STATUS_TRYAGAIN) {
+ buflen *= 2;
+ buf = realloc_p(buf, char, buflen);
+ goto again;
+ }
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getgrgid", status);
+ return NULL;
+ }
+ return &grp;
+}
+
+static void nss_setgrent(void)
+{
+ NSS_STATUS (*_nss_setgrent)(void) = find_fn("setgrent");
+ NSS_STATUS status;
+ status = _nss_setgrent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("setgrent", status);
+ }
+}
+
+static void nss_endgrent(void)
+{
+ NSS_STATUS (*_nss_endgrent)(void) = find_fn("endgrent");
+ NSS_STATUS status;
+ status = _nss_endgrent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("endgrent", status);
+ }
+}
+
+static int nss_initgroups(char *user, gid_t group, gid_t **groups, long int *start, long int *size)
+{
+ NSS_STATUS (*_nss_initgroups)(char *, gid_t , long int *,
+ long int *, gid_t **, long int , int *) =
+ find_fn("initgroups_dyn");
+ NSS_STATUS status;
+
+ if (!_nss_initgroups) return NSS_STATUS_UNAVAIL;
+
+ status = _nss_initgroups(user, group, start, size, groups, 0, &nss_errno);
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("initgroups", status);
+ }
+ return status;
+}
+
+static void print_passwd(struct passwd *pwd)
+{
+ printf("%s:%s:%d:%d:%s:%s:%s\n",
+ pwd->pw_name,
+ pwd->pw_passwd,
+ pwd->pw_uid,
+ pwd->pw_gid,
+ pwd->pw_gecos,
+ pwd->pw_dir,
+ pwd->pw_shell);
+}
+
+static void print_group(struct group *grp)
+{
+ int i;
+ printf("%s:%s:%d: ",
+ grp->gr_name,
+ grp->gr_passwd,
+ grp->gr_gid);
+
+ if (!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 void nss_test_initgroups(char *name, gid_t gid)
+{
+ long int size = 16;
+ long int start = 1;
+ gid_t *groups = NULL;
+ int i;
+ NSS_STATUS status;
+
+ groups = (gid_t *)malloc_array_p(gid_t, size);
+ groups[0] = gid;
+
+ status = nss_initgroups(name, gid, &groups, &start, &size);
+ if (status == NSS_STATUS_UNAVAIL) {
+ printf("No initgroups fn\n");
+ return;
+ }
+
+ for (i=0; i<start-1; i++) {
+ printf("%d, ", groups[i]);
+ }
+ printf("%d\n", groups[i]);
+}
+
+
+static void nss_test_users(void)
+{
+ struct passwd *pwd;
+
+ nss_setpwent();
+ /* loop over all users */
+ while ((pwd = nss_getpwent())) {
+ printf("Testing user %s\n", pwd->pw_name);
+ printf("getpwent: "); print_passwd(pwd);
+ pwd = nss_getpwuid(pwd->pw_uid);
+ if (!pwd) {
+ total_errors++;
+ printf("ERROR: can't getpwuid\n");
+ continue;
+ }
+ printf("getpwuid: "); print_passwd(pwd);
+ pwd = nss_getpwnam(pwd->pw_name);
+ if (!pwd) {
+ total_errors++;
+ printf("ERROR: can't getpwnam\n");
+ continue;
+ }
+ printf("getpwnam: "); print_passwd(pwd);
+ printf("initgroups: "); nss_test_initgroups(pwd->pw_name, pwd->pw_gid);
+ printf("\n");
+ }
+ nss_endpwent();
+}
+
+static void nss_test_groups(void)
+{
+ struct group *grp;
+
+ nss_setgrent();
+ /* loop over all groups */
+ while ((grp = nss_getgrent())) {
+ printf("Testing group %s\n", grp->gr_name);
+ printf("getgrent: "); print_group(grp);
+ grp = nss_getgrnam(grp->gr_name);
+ if (!grp) {
+ total_errors++;
+ printf("ERROR: can't getgrnam\n");
+ continue;
+ }
+ printf("getgrnam: "); print_group(grp);
+ grp = nss_getgrgid(grp->gr_gid);
+ if (!grp) {
+ total_errors++;
+ printf("ERROR: can't getgrgid\n");
+ continue;
+ }
+ printf("getgrgid: "); print_group(grp);
+ printf("\n");
+ }
+ nss_endgrent();
+}
+
+static void nss_test_errors(void)
+{
+ struct passwd *pwd;
+ struct group *grp;
+
+ pwd = getpwnam("nosuchname");
+ if (pwd || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant user gave error %d\n", last_error);
+ }
+
+ pwd = getpwuid(0xFFF0);
+ if (pwd || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant uid gave error %d\n", last_error);
+ }
+
+ grp = getgrnam("nosuchgroup");
+ if (grp || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant group gave error %d\n", last_error);
+ }
+
+ grp = getgrgid(0xFFF0);
+ if (grp || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant gid gave error %d\n", last_error);
+ }
+}
+
+ int main(int argc, char *argv[])
+{
+ if (argc > 1) so_path = argv[1];
+ if (argc > 2) nss_name = argv[2];
+
+ nss_test_users();
+ nss_test_groups();
+ nss_test_errors();
+
+ printf("total_errors=%d\n", total_errors);
+
+ return total_errors;
+}
diff --git a/nsswitch/nsstest.h b/nsswitch/nsstest.h
new file mode 100644
index 0000000000..e69f17c857
--- /dev/null
+++ b/nsswitch/nsstest.h
@@ -0,0 +1,115 @@
+/*
+ Unix SMB/CIFS implementation.
+ nss includes for the nss tester
+ Copyright (C) Kai Blin 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _NSSTEST_H
+#define _NSSTEST_H
+
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef HAVE_NSS_COMMON_H
+
+/*
+ * Sun Solaris
+ */
+
+#include <nss_common.h>
+#include <nss_dbdefs.h>
+#include <nsswitch.h>
+
+typedef nss_status_t NSS_STATUS;
+
+#define NSS_STATUS_SUCCESS NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN
+
+#elif HAVE_NSS_H
+
+/*
+ * Linux (glibc)
+ */
+
+#include <nss.h>
+typedef enum nss_status NSS_STATUS;
+
+#elif HAVE_NS_API_H
+
+/*
+ * SGI IRIX
+ */
+
+#ifdef DATUM
+#define _DATUM_DEFINED
+#endif
+
+#include <ns_api.h>
+
+typedef enum
+{
+ NSS_STATUS_SUCCESS=NS_SUCCESS,
+ NSS_STATUS_NOTFOUND=NS_NOTFOUND,
+ NSS_STATUS_UNAVAIL=NS_UNAVAIL,
+ NSS_STATUS_TRYAGAIN=NS_TRYAGAIN
+} NSS_STATUS;
+
+#define NSD_MEM_STATIC 0
+#define NSD_MEM_VOLATILE 1
+#define NSD_MEM_DYNAMIC 2
+
+#elif defined(HPUX) && defined(HAVE_NSSWITCH_H)
+
+/* HP-UX 11 */
+
+#include <nsswitch.h>
+
+#define NSS_STATUS_SUCCESS NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN
+
+#ifdef HAVE_SYNCH_H
+#include <synch.h>
+#endif
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+typedef enum {
+ NSS_SUCCESS,
+ NSS_NOTFOUND,
+ NSS_UNAVAIL,
+ NSS_TRYAGAIN
+} nss_status_t;
+
+typedef nss_status_t NSS_STATUS;
+
+#else /* Nothing's defined. Neither solaris nor gnu nor sun nor hp */
+
+typedef enum
+{
+ NSS_STATUS_SUCCESS=0,
+ NSS_STATUS_NOTFOUND=1,
+ NSS_STATUS_UNAVAIL=2,
+ NSS_STATUS_TRYAGAIN=3
+} NSS_STATUS;
+
+#endif
+
+#endif /* _NSSTEST_H */
diff --git a/nsswitch/nsstest.m4 b/nsswitch/nsstest.m4
new file mode 100644
index 0000000000..37596489c0
--- /dev/null
+++ b/nsswitch/nsstest.m4
@@ -0,0 +1,8 @@
+case "$host_os" in
+ *linux*)
+ SMB_ENABLE(nsstest,YES)
+ ;;
+ *)
+ SMB_ENABLE(nsstest,NO)
+ ;;
+esac
diff --git a/nsswitch/pam_winbind.c b/nsswitch/pam_winbind.c
new file mode 100644
index 0000000000..d049bdb1e7
--- /dev/null
+++ b/nsswitch/pam_winbind.c
@@ -0,0 +1,3204 @@
+/* pam_winbind module
+
+ Copyright Andrew Tridgell <tridge@samba.org> 2000
+ Copyright Tim Potter <tpot@samba.org> 2000
+ Copyright Andrew Bartlett <abartlet@samba.org> 2002
+ Copyright Guenther Deschner <gd@samba.org> 2005-2008
+
+ largely based on pam_userdb by Cristian Gafton <gafton@redhat.com> also
+ contains large slabs of code from pam_unix by Elliot Lee
+ <sopwith@redhat.com> (see copyright below for full details)
+*/
+
+#include "pam_winbind.h"
+
+static int wbc_error_to_pam_error(wbcErr status)
+{
+ switch (status) {
+ case WBC_ERR_SUCCESS:
+ return PAM_SUCCESS;
+ case WBC_ERR_NOT_IMPLEMENTED:
+ return PAM_SERVICE_ERR;
+ case WBC_ERR_UNKNOWN_FAILURE:
+ break;
+ case WBC_ERR_NO_MEMORY:
+ return PAM_BUF_ERR;
+ case WBC_ERR_INVALID_SID:
+ case WBC_ERR_INVALID_PARAM:
+ break;
+ case WBC_ERR_WINBIND_NOT_AVAILABLE:
+ return PAM_AUTHINFO_UNAVAIL;
+ case WBC_ERR_DOMAIN_NOT_FOUND:
+ return PAM_AUTHINFO_UNAVAIL;
+ case WBC_ERR_INVALID_RESPONSE:
+ return PAM_BUF_ERR;
+ case WBC_ERR_NSS_ERROR:
+ return PAM_USER_UNKNOWN;
+ case WBC_ERR_AUTH_ERROR:
+ return PAM_AUTH_ERR;
+ case WBC_ERR_UNKNOWN_USER:
+ return PAM_USER_UNKNOWN;
+ case WBC_ERR_UNKNOWN_GROUP:
+ return PAM_USER_UNKNOWN;
+ case WBC_ERR_PWD_CHANGE_FAILED:
+ break;
+ }
+
+ /* be paranoid */
+ return PAM_AUTH_ERR;
+}
+
+static const char *_pam_error_code_str(int err)
+{
+ switch (err) {
+ case PAM_SUCCESS:
+ return "PAM_SUCCESS";
+ case PAM_OPEN_ERR:
+ return "PAM_OPEN_ERR";
+ case PAM_SYMBOL_ERR:
+ return "PAM_SYMBOL_ERR";
+ case PAM_SERVICE_ERR:
+ return "PAM_SERVICE_ERR";
+ case PAM_SYSTEM_ERR:
+ return "PAM_SYSTEM_ERR";
+ case PAM_BUF_ERR:
+ return "PAM_BUF_ERR";
+ case PAM_PERM_DENIED:
+ return "PAM_PERM_DENIED";
+ case PAM_AUTH_ERR:
+ return "PAM_AUTH_ERR";
+ case PAM_CRED_INSUFFICIENT:
+ return "PAM_CRED_INSUFFICIENT";
+ case PAM_AUTHINFO_UNAVAIL:
+ return "PAM_AUTHINFO_UNAVAIL";
+ case PAM_USER_UNKNOWN:
+ return "PAM_USER_UNKNOWN";
+ case PAM_MAXTRIES:
+ return "PAM_MAXTRIES";
+ case PAM_NEW_AUTHTOK_REQD:
+ return "PAM_NEW_AUTHTOK_REQD";
+ case PAM_ACCT_EXPIRED:
+ return "PAM_ACCT_EXPIRED";
+ case PAM_SESSION_ERR:
+ return "PAM_SESSION_ERR";
+ case PAM_CRED_UNAVAIL:
+ return "PAM_CRED_UNAVAIL";
+ case PAM_CRED_EXPIRED:
+ return "PAM_CRED_EXPIRED";
+ case PAM_CRED_ERR:
+ return "PAM_CRED_ERR";
+ case PAM_NO_MODULE_DATA:
+ return "PAM_NO_MODULE_DATA";
+ case PAM_CONV_ERR:
+ return "PAM_CONV_ERR";
+ case PAM_AUTHTOK_ERR:
+ return "PAM_AUTHTOK_ERR";
+ case PAM_AUTHTOK_RECOVER_ERR:
+ return "PAM_AUTHTOK_RECOVER_ERR";
+ case PAM_AUTHTOK_LOCK_BUSY:
+ return "PAM_AUTHTOK_LOCK_BUSY";
+ case PAM_AUTHTOK_DISABLE_AGING:
+ return "PAM_AUTHTOK_DISABLE_AGING";
+ case PAM_TRY_AGAIN:
+ return "PAM_TRY_AGAIN";
+ case PAM_IGNORE:
+ return "PAM_IGNORE";
+ case PAM_ABORT:
+ return "PAM_ABORT";
+ case PAM_AUTHTOK_EXPIRED:
+ return "PAM_AUTHTOK_EXPIRED";
+#ifdef PAM_MODULE_UNKNOWN
+ case PAM_MODULE_UNKNOWN:
+ return "PAM_MODULE_UNKNOWN";
+#endif
+#ifdef PAM_BAD_ITEM
+ case PAM_BAD_ITEM:
+ return "PAM_BAD_ITEM";
+#endif
+#ifdef PAM_CONV_AGAIN
+ case PAM_CONV_AGAIN:
+ return "PAM_CONV_AGAIN";
+#endif
+#ifdef PAM_INCOMPLETE
+ case PAM_INCOMPLETE:
+ return "PAM_INCOMPLETE";
+#endif
+ default:
+ return NULL;
+ }
+}
+
+#define _PAM_LOG_FUNCTION_ENTER(function, ctx) \
+ do { \
+ _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] ENTER: " \
+ function " (flags: 0x%04x)", ctx->pamh, ctx->flags); \
+ _pam_log_state(ctx); \
+ } while (0)
+
+#define _PAM_LOG_FUNCTION_LEAVE(function, ctx, retval) \
+ do { \
+ _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] LEAVE: " \
+ function " returning %d (%s)", ctx->pamh, retval, \
+ _pam_error_code_str(retval)); \
+ _pam_log_state(ctx); \
+ } while (0)
+
+/* data tokens */
+
+#define MAX_PASSWD_TRIES 3
+
+#ifdef HAVE_GETTEXT
+static char initialized = 0;
+
+static inline void textdomain_init(void);
+static inline void textdomain_init(void)
+{
+ if (!initialized) {
+ bindtextdomain(MODULE_NAME, dyn_LOCALEDIR);
+ initialized = 1;
+ }
+ return;
+}
+#endif
+
+
+/*
+ * Work around the pam API that has functions with void ** as parameters
+ * These lead to strict aliasing warnings with gcc.
+ */
+static int _pam_get_item(const pam_handle_t *pamh,
+ int item_type,
+ const void *_item)
+{
+ const void **item = (const void **)_item;
+ return pam_get_item(pamh, item_type, item);
+}
+static int _pam_get_data(const pam_handle_t *pamh,
+ const char *module_data_name,
+ const void *_data)
+{
+ const void **data = (const void **)_data;
+ return pam_get_data(pamh, module_data_name, data);
+}
+
+/* some syslogging */
+
+#ifdef HAVE_PAM_VSYSLOG
+static void _pam_log_int(const pam_handle_t *pamh,
+ int err,
+ const char *format,
+ va_list args)
+{
+ pam_vsyslog(pamh, err, format, args);
+}
+#else
+static void _pam_log_int(const pam_handle_t *pamh,
+ int err,
+ const char *format,
+ va_list args)
+{
+ char *format2 = NULL;
+ const char *service;
+
+ _pam_get_item(pamh, PAM_SERVICE, &service);
+
+ format2 = (char *)malloc(strlen(MODULE_NAME)+strlen(format)+strlen(service)+5);
+ if (format2 == NULL) {
+ /* what else todo ? */
+ vsyslog(err, format, args);
+ return;
+ }
+
+ sprintf(format2, "%s(%s): %s", MODULE_NAME, service, format);
+ vsyslog(err, format2, args);
+ SAFE_FREE(format2);
+}
+#endif /* HAVE_PAM_VSYSLOG */
+
+static bool _pam_log_is_silent(int ctrl)
+{
+ return on(ctrl, WINBIND_SILENT);
+}
+
+static void _pam_log(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
+static void _pam_log(struct pwb_context *r, int err, const char *format, ...)
+{
+ va_list args;
+
+ if (_pam_log_is_silent(r->ctrl)) {
+ return;
+ }
+
+ va_start(args, format);
+ _pam_log_int(r->pamh, err, format, args);
+ va_end(args);
+}
+static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
+static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
+{
+ va_list args;
+
+ if (_pam_log_is_silent(ctrl)) {
+ return;
+ }
+
+ va_start(args, format);
+ _pam_log_int(pamh, err, format, args);
+ va_end(args);
+}
+
+static bool _pam_log_is_debug_enabled(int ctrl)
+{
+ if (ctrl == -1) {
+ return false;
+ }
+
+ if (_pam_log_is_silent(ctrl)) {
+ return false;
+ }
+
+ if (!(ctrl & WINBIND_DEBUG_ARG)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool _pam_log_is_debug_state_enabled(int ctrl)
+{
+ if (!(ctrl & WINBIND_DEBUG_STATE)) {
+ return false;
+ }
+
+ return _pam_log_is_debug_enabled(ctrl);
+}
+
+static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
+static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...)
+{
+ va_list args;
+
+ if (!_pam_log_is_debug_enabled(r->ctrl)) {
+ return;
+ }
+
+ va_start(args, format);
+ _pam_log_int(r->pamh, err, format, args);
+ va_end(args);
+}
+static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
+static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
+{
+ va_list args;
+
+ if (!_pam_log_is_debug_enabled(ctrl)) {
+ return;
+ }
+
+ va_start(args, format);
+ _pam_log_int(pamh, err, format, args);
+ va_end(args);
+}
+
+static void _pam_log_state_datum(struct pwb_context *ctx,
+ int item_type,
+ const char *key,
+ int is_string)
+{
+ const void *data = NULL;
+ if (item_type != 0) {
+ pam_get_item(ctx->pamh, item_type, &data);
+ } else {
+ pam_get_data(ctx->pamh, key, &data);
+ }
+ if (data != NULL) {
+ const char *type = (item_type != 0) ? "ITEM" : "DATA";
+ if (is_string != 0) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "[pamh: %p] STATE: %s(%s) = \"%s\" (%p)",
+ ctx->pamh, type, key, (const char *)data,
+ data);
+ } else {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "[pamh: %p] STATE: %s(%s) = %p",
+ ctx->pamh, type, key, data);
+ }
+ }
+}
+
+#define _PAM_LOG_STATE_DATA_POINTER(ctx, module_data_name) \
+ _pam_log_state_datum(ctx, 0, module_data_name, 0)
+
+#define _PAM_LOG_STATE_DATA_STRING(ctx, module_data_name) \
+ _pam_log_state_datum(ctx, 0, module_data_name, 1)
+
+#define _PAM_LOG_STATE_ITEM_POINTER(ctx, item_type) \
+ _pam_log_state_datum(ctx, item_type, #item_type, 0)
+
+#define _PAM_LOG_STATE_ITEM_STRING(ctx, item_type) \
+ _pam_log_state_datum(ctx, item_type, #item_type, 1)
+
+#ifdef DEBUG_PASSWORD
+#define _LOG_PASSWORD_AS_STRING 1
+#else
+#define _LOG_PASSWORD_AS_STRING 0
+#endif
+
+#define _PAM_LOG_STATE_ITEM_PASSWORD(ctx, item_type) \
+ _pam_log_state_datum(ctx, item_type, #item_type, \
+ _LOG_PASSWORD_AS_STRING)
+
+static void _pam_log_state(struct pwb_context *ctx)
+{
+ if (!_pam_log_is_debug_state_enabled(ctx->ctrl)) {
+ return;
+ }
+
+ _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_SERVICE);
+ _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER);
+ _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_TTY);
+ _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RHOST);
+ _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RUSER);
+ _PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_OLDAUTHTOK);
+ _PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_AUTHTOK);
+ _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER_PROMPT);
+ _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_CONV);
+#ifdef PAM_FAIL_DELAY
+ _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_FAIL_DELAY);
+#endif
+#ifdef PAM_REPOSITORY
+ _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_REPOSITORY);
+#endif
+
+ _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_HOMEDIR);
+ _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSCRIPT);
+ _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSERVER);
+ _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_PROFILEPATH);
+ _PAM_LOG_STATE_DATA_STRING(ctx,
+ PAM_WINBIND_NEW_AUTHTOK_REQD);
+ /* Use atoi to get PAM result code */
+ _PAM_LOG_STATE_DATA_STRING(ctx,
+ PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH);
+ _PAM_LOG_STATE_DATA_POINTER(ctx, PAM_WINBIND_PWD_LAST_SET);
+}
+
+static int _pam_parse(const pam_handle_t *pamh,
+ int flags,
+ int argc,
+ const char **argv,
+ dictionary **result_d)
+{
+ int ctrl = 0;
+ const char *config_file = NULL;
+ int i;
+ const char **v;
+ dictionary *d = NULL;
+
+ if (flags & PAM_SILENT) {
+ ctrl |= WINBIND_SILENT;
+ }
+
+ for (i=argc,v=argv; i-- > 0; ++v) {
+ if (!strncasecmp(*v, "config", strlen("config"))) {
+ ctrl |= WINBIND_CONFIG_FILE;
+ config_file = v[i];
+ break;
+ }
+ }
+
+ if (config_file == NULL) {
+ config_file = PAM_WINBIND_CONFIG_FILE;
+ }
+
+ d = iniparser_load(config_file);
+ if (d == NULL) {
+ goto config_from_pam;
+ }
+
+ if (iniparser_getboolean(d, "global:debug", false)) {
+ ctrl |= WINBIND_DEBUG_ARG;
+ }
+
+ if (iniparser_getboolean(d, "global:debug_state", false)) {
+ ctrl |= WINBIND_DEBUG_STATE;
+ }
+
+ if (iniparser_getboolean(d, "global:cached_login", false)) {
+ ctrl |= WINBIND_CACHED_LOGIN;
+ }
+
+ if (iniparser_getboolean(d, "global:krb5_auth", false)) {
+ ctrl |= WINBIND_KRB5_AUTH;
+ }
+
+ if (iniparser_getboolean(d, "global:silent", false)) {
+ ctrl |= WINBIND_SILENT;
+ }
+
+ if (iniparser_getstr(d, "global:krb5_ccache_type") != NULL) {
+ ctrl |= WINBIND_KRB5_CCACHE_TYPE;
+ }
+
+ if ((iniparser_getstr(d, "global:require-membership-of") != NULL) ||
+ (iniparser_getstr(d, "global:require_membership_of") != NULL)) {
+ ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
+ }
+
+ if (iniparser_getboolean(d, "global:try_first_pass", false)) {
+ ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
+ }
+
+ if (iniparser_getint(d, "global:warn_pwd_expire", 0)) {
+ ctrl |= WINBIND_WARN_PWD_EXPIRE;
+ }
+
+ if (iniparser_getboolean(d, "global:mkhomedir", false)) {
+ ctrl |= WINBIND_MKHOMEDIR;
+ }
+
+config_from_pam:
+ /* step through arguments */
+ for (i=argc,v=argv; i-- > 0; ++v) {
+
+ /* generic options */
+ if (!strcmp(*v,"debug"))
+ ctrl |= WINBIND_DEBUG_ARG;
+ else if (!strcasecmp(*v, "debug_state"))
+ ctrl |= WINBIND_DEBUG_STATE;
+ else if (!strcasecmp(*v, "silent"))
+ ctrl |= WINBIND_SILENT;
+ else if (!strcasecmp(*v, "use_authtok"))
+ ctrl |= WINBIND_USE_AUTHTOK_ARG;
+ else if (!strcasecmp(*v, "use_first_pass"))
+ ctrl |= WINBIND_USE_FIRST_PASS_ARG;
+ else if (!strcasecmp(*v, "try_first_pass"))
+ ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
+ else if (!strcasecmp(*v, "unknown_ok"))
+ ctrl |= WINBIND_UNKNOWN_OK_ARG;
+ else if (!strncasecmp(*v, "require_membership_of",
+ strlen("require_membership_of")))
+ ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
+ else if (!strncasecmp(*v, "require-membership-of",
+ strlen("require-membership-of")))
+ ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
+ else if (!strcasecmp(*v, "krb5_auth"))
+ ctrl |= WINBIND_KRB5_AUTH;
+ else if (!strncasecmp(*v, "krb5_ccache_type",
+ strlen("krb5_ccache_type")))
+ ctrl |= WINBIND_KRB5_CCACHE_TYPE;
+ else if (!strcasecmp(*v, "cached_login"))
+ ctrl |= WINBIND_CACHED_LOGIN;
+ else if (!strcasecmp(*v, "mkhomedir"))
+ ctrl |= WINBIND_MKHOMEDIR;
+ else {
+ __pam_log(pamh, ctrl, LOG_ERR,
+ "pam_parse: unknown option: %s", *v);
+ return -1;
+ }
+
+ }
+
+ if (result_d) {
+ *result_d = d;
+ } else {
+ if (d) {
+ iniparser_freedict(d);
+ }
+ }
+
+ return ctrl;
+};
+
+static int _pam_winbind_free_context(struct pwb_context *ctx)
+{
+ if (!ctx) {
+ return 0;
+ }
+
+ if (ctx->dict) {
+ iniparser_freedict(ctx->dict);
+ }
+
+ return 0;
+}
+
+static int _pam_winbind_init_context(pam_handle_t *pamh,
+ int flags,
+ int argc,
+ const char **argv,
+ struct pwb_context **ctx_p)
+{
+ struct pwb_context *r = NULL;
+
+#ifdef HAVE_GETTEXT
+ textdomain_init();
+#endif
+
+ r = TALLOC_ZERO_P(NULL, struct pwb_context);
+ if (!r) {
+ return PAM_BUF_ERR;
+ }
+
+ talloc_set_destructor(r, _pam_winbind_free_context);
+
+ r->pamh = pamh;
+ r->flags = flags;
+ r->argc = argc;
+ r->argv = argv;
+ r->ctrl = _pam_parse(pamh, flags, argc, argv, &r->dict);
+ if (r->ctrl == -1) {
+ TALLOC_FREE(r);
+ return PAM_SYSTEM_ERR;
+ }
+
+ *ctx_p = r;
+
+ return PAM_SUCCESS;
+}
+
+static void _pam_winbind_cleanup_func(pam_handle_t *pamh,
+ void *data,
+ int error_status)
+{
+ int ctrl = _pam_parse(pamh, 0, 0, NULL, NULL);
+ if (_pam_log_is_debug_state_enabled(ctrl)) {
+ __pam_log_debug(pamh, ctrl, LOG_DEBUG,
+ "[pamh: %p] CLEAN: cleaning up PAM data %p "
+ "(error_status = %d)", pamh, data,
+ error_status);
+ }
+ TALLOC_FREE(data);
+}
+
+
+static const struct ntstatus_errors {
+ const char *ntstatus_string;
+ const char *error_string;
+} ntstatus_errors[] = {
+ {"NT_STATUS_OK",
+ N_("Success")},
+ {"NT_STATUS_BACKUP_CONTROLLER",
+ N_("No primary Domain Controler available")},
+ {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
+ N_("No domain controllers found")},
+ {"NT_STATUS_NO_LOGON_SERVERS",
+ N_("No logon servers")},
+ {"NT_STATUS_PWD_TOO_SHORT",
+ N_("Password too short")},
+ {"NT_STATUS_PWD_TOO_RECENT",
+ N_("The password of this user is too recent to change")},
+ {"NT_STATUS_PWD_HISTORY_CONFLICT",
+ N_("Password is already in password history")},
+ {"NT_STATUS_PASSWORD_EXPIRED",
+ N_("Your password has expired")},
+ {"NT_STATUS_PASSWORD_MUST_CHANGE",
+ N_("You need to change your password now")},
+ {"NT_STATUS_INVALID_WORKSTATION",
+ N_("You are not allowed to logon from this workstation")},
+ {"NT_STATUS_INVALID_LOGON_HOURS",
+ N_("You are not allowed to logon at this time")},
+ {"NT_STATUS_ACCOUNT_EXPIRED",
+ N_("Your account has expired. "
+ "Please contact your System administrator")}, /* SCNR */
+ {"NT_STATUS_ACCOUNT_DISABLED",
+ N_("Your account is disabled. "
+ "Please contact your System administrator")}, /* SCNR */
+ {"NT_STATUS_ACCOUNT_LOCKED_OUT",
+ N_("Your account has been locked. "
+ "Please contact your System administrator")}, /* SCNR */
+ {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
+ N_("Invalid Trust Account")},
+ {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
+ N_("Invalid Trust Account")},
+ {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
+ N_("Invalid Trust Account")},
+ {"NT_STATUS_ACCESS_DENIED",
+ N_("Access is denied")},
+ {NULL, NULL}
+};
+
+static const char *_get_ntstatus_error_string(const char *nt_status_string)
+{
+ int i;
+ for (i=0; ntstatus_errors[i].ntstatus_string != NULL; i++) {
+ if (!strcasecmp(ntstatus_errors[i].ntstatus_string,
+ nt_status_string)) {
+ return _(ntstatus_errors[i].error_string);
+ }
+ }
+ return NULL;
+}
+
+/* --- authentication management functions --- */
+
+/* Attempt a conversation */
+
+static int converse(const pam_handle_t *pamh,
+ int nargs,
+ struct pam_message **message,
+ struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ retval = _pam_get_item(pamh, PAM_CONV, &conv);
+ if (retval == PAM_SUCCESS) {
+ retval = conv->conv(nargs,
+ (const struct pam_message **)message,
+ response, conv->appdata_ptr);
+ }
+
+ return retval; /* propagate error status */
+}
+
+
+static int _make_remark(struct pwb_context *ctx,
+ int type,
+ const char *text)
+{
+ int retval = PAM_SUCCESS;
+
+ struct pam_message *pmsg[1], msg[1];
+ struct pam_response *resp;
+
+ if (ctx->flags & WINBIND_SILENT) {
+ return PAM_SUCCESS;
+ }
+
+ pmsg[0] = &msg[0];
+ msg[0].msg = discard_const_p(char, text);
+ msg[0].msg_style = type;
+
+ resp = NULL;
+ retval = converse(ctx->pamh, 1, pmsg, &resp);
+
+ if (resp) {
+ _pam_drop_reply(resp, 1);
+ }
+ return retval;
+}
+
+static int _make_remark_v(struct pwb_context *ctx,
+ int type,
+ const char *format,
+ va_list args)
+{
+ char *var;
+ int ret;
+
+ ret = vasprintf(&var, format, args);
+ if (ret < 0) {
+ _pam_log(ctx, LOG_ERR, "memory allocation failure");
+ return ret;
+ }
+
+ ret = _make_remark(ctx, type, var);
+ SAFE_FREE(var);
+ return ret;
+}
+
+static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
+static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, format);
+ ret = _make_remark_v(ctx, type, format, args);
+ va_end(args);
+ return ret;
+}
+
+static int pam_winbind_request_log(struct pwb_context *ctx,
+ int retval,
+ const char *user,
+ const char *fn)
+{
+ switch (retval) {
+ case PAM_AUTH_ERR:
+ /* incorrect password */
+ _pam_log(ctx, LOG_WARNING, "user '%s' denied access "
+ "(incorrect password or invalid membership)", user);
+ return retval;
+ case PAM_ACCT_EXPIRED:
+ /* account expired */
+ _pam_log(ctx, LOG_WARNING, "user '%s' account expired",
+ user);
+ return retval;
+ case PAM_AUTHTOK_EXPIRED:
+ /* password expired */
+ _pam_log(ctx, LOG_WARNING, "user '%s' password expired",
+ user);
+ return retval;
+ case PAM_NEW_AUTHTOK_REQD:
+ /* new password required */
+ _pam_log(ctx, LOG_WARNING, "user '%s' new password "
+ "required", user);
+ return retval;
+ case PAM_USER_UNKNOWN:
+ /* the user does not exist */
+ _pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found",
+ user);
+ if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) {
+ return PAM_IGNORE;
+ }
+ return retval;
+ case PAM_SUCCESS:
+ /* Otherwise, the authentication looked good */
+ if (strcmp(fn, "wbcLogonUser") == 0) {
+ _pam_log(ctx, LOG_NOTICE,
+ "user '%s' granted access", user);
+ } else {
+ _pam_log(ctx, LOG_NOTICE,
+ "user '%s' OK", user);
+ }
+ return retval;
+ default:
+ /* we don't know anything about this return value */
+ _pam_log(ctx, LOG_ERR,
+ "internal module error (retval = %s(%d), user = '%s')",
+ _pam_error_code_str(retval), retval, user);
+ return retval;
+ }
+}
+
+static int wbc_auth_error_to_pam_error(struct pwb_context *ctx,
+ struct wbcAuthErrorInfo *e,
+ wbcErr status,
+ const char *username,
+ const char *fn)
+{
+ int ret = PAM_AUTH_ERR;
+
+ if (WBC_ERROR_IS_OK(status)) {
+ _pam_log_debug(ctx, LOG_DEBUG, "request %s succeeded",
+ fn);
+ ret = PAM_SUCCESS;
+ return pam_winbind_request_log(ctx, ret, username, fn);
+ }
+
+ if (e) {
+ if (e->pam_error != PAM_SUCCESS) {
+ _pam_log(ctx, LOG_ERR,
+ "request %s failed: %s, "
+ "PAM error: %s (%d), NTSTATUS: %s, "
+ "Error message was: %s",
+ fn,
+ wbcErrorString(status),
+ _pam_error_code_str(e->pam_error),
+ e->pam_error,
+ e->nt_string,
+ e->display_string);
+ ret = e->pam_error;
+ return pam_winbind_request_log(ctx, ret, username, fn);
+ }
+
+ _pam_log(ctx, LOG_ERR, "request %s failed, but PAM error 0!", fn);
+
+ ret = PAM_SERVICE_ERR;
+ return pam_winbind_request_log(ctx, ret, username, fn);
+ }
+
+ ret = wbc_error_to_pam_error(status);
+ return pam_winbind_request_log(ctx, ret, username, fn);
+}
+
+
+/**
+ * send a password expiry message if required
+ *
+ * @param ctx PAM winbind context.
+ * @param next_change expected (calculated) next expiry date.
+ * @param already_expired pointer to a boolean to indicate if the password is
+ * already expired.
+ *
+ * @return boolean Returns true if message has been sent, false if not.
+ */
+
+static bool _pam_send_password_expiry_message(struct pwb_context *ctx,
+ time_t next_change,
+ time_t now,
+ int warn_pwd_expire,
+ bool *already_expired)
+{
+ int days = 0;
+ struct tm tm_now, tm_next_change;
+
+ if (already_expired) {
+ *already_expired = false;
+ }
+
+ if (next_change <= now) {
+ PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PASSWORD_EXPIRED");
+ if (already_expired) {
+ *already_expired = true;
+ }
+ return true;
+ }
+
+ if ((next_change < 0) ||
+ (next_change > now + warn_pwd_expire * SECONDS_PER_DAY)) {
+ return false;
+ }
+
+ if ((localtime_r(&now, &tm_now) == NULL) ||
+ (localtime_r(&next_change, &tm_next_change) == NULL)) {
+ return false;
+ }
+
+ days = (tm_next_change.tm_yday+tm_next_change.tm_year*365) -
+ (tm_now.tm_yday+tm_now.tm_year*365);
+
+ if (days == 0) {
+ _make_remark(ctx, PAM_TEXT_INFO,
+ _("Your password expires today"));
+ return true;
+ }
+
+ if (days > 0 && days < warn_pwd_expire) {
+ _make_remark_format(ctx, PAM_TEXT_INFO,
+ _("Your password will expire in %d %s"),
+ days, (days > 1) ? _("days"):_("day"));
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Send a warning if the password expires in the near future
+ *
+ * @param ctx PAM winbind context.
+ * @param response The full authentication response structure.
+ * @param already_expired boolean, is the pwd already expired?
+ *
+ * @return void.
+ */
+
+static void _pam_warn_password_expiry(struct pwb_context *ctx,
+ const struct wbcAuthUserInfo *info,
+ const struct wbcUserPasswordPolicyInfo *policy,
+ int warn_pwd_expire,
+ bool *already_expired)
+{
+ time_t now = time(NULL);
+ time_t next_change = 0;
+
+ if (!info || !policy) {
+ return;
+ }
+
+ if (already_expired) {
+ *already_expired = false;
+ }
+
+ /* accounts with WBC_ACB_PWNOEXP set never receive a warning */
+ if (info->acct_flags & WBC_ACB_PWNOEXP) {
+ return;
+ }
+
+ /* no point in sending a warning if this is a grace logon */
+ if (PAM_WB_GRACE_LOGON(info->user_flags)) {
+ return;
+ }
+
+ /* check if the info3 must change timestamp has been set */
+ next_change = info->pass_must_change_time;
+
+ if (_pam_send_password_expiry_message(ctx, next_change, now,
+ warn_pwd_expire,
+ already_expired)) {
+ return;
+ }
+
+ /* now check for the global password policy */
+ /* good catch from Ralf Haferkamp: an expiry of "never" is translated
+ * to -1 */
+ if (policy->expire <= 0) {
+ return;
+ }
+
+ next_change = info->pass_last_set_time + policy->expire;
+
+ if (_pam_send_password_expiry_message(ctx, next_change, now,
+ warn_pwd_expire,
+ already_expired)) {
+ return;
+ }
+
+ /* no warning sent */
+}
+
+#define IS_SID_STRING(name) (strncmp("S-", name, 2) == 0)
+
+/**
+ * Append a string, making sure not to overflow and to always return a
+ * NULL-terminated string.
+ *
+ * @param dest Destination string buffer (must already be NULL-terminated).
+ * @param src Source string buffer.
+ * @param dest_buffer_size Size of dest buffer in bytes.
+ *
+ * @return false if dest buffer is not big enough (no bytes copied), true on
+ * success.
+ */
+
+static bool safe_append_string(char *dest,
+ const char *src,
+ int dest_buffer_size)
+{
+ int dest_length = strlen(dest);
+ int src_length = strlen(src);
+
+ if (dest_length + src_length + 1 > dest_buffer_size) {
+ return false;
+ }
+
+ memcpy(dest + dest_length, src, src_length + 1);
+ return true;
+}
+
+/**
+ * Convert a names into a SID string, appending it to a buffer.
+ *
+ * @param ctx PAM winbind context.
+ * @param user User in PAM request.
+ * @param name Name to convert.
+ * @param sid_list_buffer Where to append the string sid.
+ * @param sid_list_buffer Size of sid_list_buffer (in bytes).
+ *
+ * @return false on failure, true on success.
+ */
+static bool winbind_name_to_sid_string(struct pwb_context *ctx,
+ const char *user,
+ const char *name,
+ char *sid_list_buffer,
+ int sid_list_buffer_size)
+{
+ const char* sid_string;
+
+ /* lookup name? */
+ if (IS_SID_STRING(name)) {
+ sid_string = name;
+ } else {
+ wbcErr wbc_status;
+ struct wbcDomainSid sid;
+ enum wbcSidType type;
+ char *sid_str;
+
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "no sid given, looking up: %s\n", name);
+
+ wbc_status = wbcLookupName("", name, &sid, &type);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ _pam_log(ctx, LOG_INFO,
+ "could not lookup name: %s\n", name);
+ return false;
+ }
+
+ wbc_status = wbcSidToString(&sid, &sid_str);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ wbcFreeMemory(sid_str);
+ sid_string = sid_str;
+ }
+
+ if (!safe_append_string(sid_list_buffer, sid_string,
+ sid_list_buffer_size)) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Convert a list of names into a list of sids.
+ *
+ * @param ctx PAM winbind context.
+ * @param user User in PAM request.
+ * @param name_list List of names or string sids, separated by commas.
+ * @param sid_list_buffer Where to put the list of string sids.
+ * @param sid_list_buffer Size of sid_list_buffer (in bytes).
+ *
+ * @return false on failure, true on success.
+ */
+static bool winbind_name_list_to_sid_string_list(struct pwb_context *ctx,
+ const char *user,
+ const char *name_list,
+ char *sid_list_buffer,
+ int sid_list_buffer_size)
+{
+ bool result = false;
+ char *current_name = NULL;
+ const char *search_location;
+ const char *comma;
+
+ if (sid_list_buffer_size > 0) {
+ sid_list_buffer[0] = 0;
+ }
+
+ search_location = name_list;
+ while ((comma = strstr(search_location, ",")) != NULL) {
+ current_name = strndup(search_location,
+ comma - search_location);
+ if (NULL == current_name) {
+ goto out;
+ }
+
+ if (!winbind_name_to_sid_string(ctx, user,
+ current_name,
+ sid_list_buffer,
+ sid_list_buffer_size)) {
+ goto out;
+ }
+
+ SAFE_FREE(current_name);
+
+ if (!safe_append_string(sid_list_buffer, ",",
+ sid_list_buffer_size)) {
+ goto out;
+ }
+
+ search_location = comma + 1;
+ }
+
+ if (!winbind_name_to_sid_string(ctx, user, search_location,
+ sid_list_buffer,
+ sid_list_buffer_size)) {
+ goto out;
+ }
+
+ result = true;
+
+out:
+ SAFE_FREE(current_name);
+ return result;
+}
+
+/**
+ * put krb5ccname variable into environment
+ *
+ * @param ctx PAM winbind context.
+ * @param krb5ccname env variable retrieved from winbindd.
+ *
+ * @return void.
+ */
+
+static void _pam_setup_krb5_env(struct pwb_context *ctx,
+ struct wbcLogonUserInfo *info)
+{
+ char var[PATH_MAX];
+ int ret;
+ uint32_t i;
+ const char *krb5ccname = NULL;
+
+ if (off(ctx->ctrl, WINBIND_KRB5_AUTH)) {
+ return;
+ }
+
+ if (!info) {
+ return;
+ }
+
+ for (i=0; i < info->num_blobs; i++) {
+ if (strcasecmp(info->blobs[i].name, "krb5ccname") == 0) {
+ krb5ccname = (const char *)info->blobs[i].blob.data;
+ break;
+ }
+ }
+
+ if (!krb5ccname || (strlen(krb5ccname) == 0)) {
+ return;
+ }
+
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "request returned KRB5CCNAME: %s", krb5ccname);
+
+ if (snprintf(var, sizeof(var), "KRB5CCNAME=%s", krb5ccname) == -1) {
+ return;
+ }
+
+ ret = pam_putenv(ctx->pamh, var);
+ if (ret) {
+ _pam_log(ctx, LOG_ERR,
+ "failed to set KRB5CCNAME to %s: %s",
+ var, pam_strerror(ctx->pamh, ret));
+ }
+}
+
+/**
+ * Copy unix username if available (further processed in PAM).
+ *
+ * @param ctx PAM winbind context
+ * @param user_ret A pointer that holds a pointer to a string
+ * @param unix_username A username
+ *
+ * @return void.
+ */
+
+static void _pam_setup_unix_username(struct pwb_context *ctx,
+ char **user_ret,
+ struct wbcLogonUserInfo *info)
+{
+ const char *unix_username = NULL;
+ uint32_t i;
+
+ if (!user_ret || !info) {
+ return;
+ }
+
+ for (i=0; i < info->num_blobs; i++) {
+ if (strcasecmp(info->blobs[i].name, "unix_username") == 0) {
+ unix_username = (const char *)info->blobs[i].blob.data;
+ break;
+ }
+ }
+
+ if (!unix_username || !unix_username[0]) {
+ return;
+ }
+
+ *user_ret = strdup(unix_username);
+}
+
+/**
+ * Set string into the PAM stack.
+ *
+ * @param ctx PAM winbind context.
+ * @param data_name Key name for pam_set_data.
+ * @param value String value.
+ *
+ * @return void.
+ */
+
+static void _pam_set_data_string(struct pwb_context *ctx,
+ const char *data_name,
+ const char *value)
+{
+ int ret;
+
+ if (!data_name || !value || (strlen(data_name) == 0) ||
+ (strlen(value) == 0)) {
+ return;
+ }
+
+ ret = pam_set_data(ctx->pamh, data_name, talloc_strdup(NULL, value),
+ _pam_winbind_cleanup_func);
+ if (ret) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "Could not set data %s: %s\n",
+ data_name, pam_strerror(ctx->pamh, ret));
+ }
+}
+
+/**
+ * Set info3 strings into the PAM stack.
+ *
+ * @param ctx PAM winbind context.
+ * @param data_name Key name for pam_set_data.
+ * @param value String value.
+ *
+ * @return void.
+ */
+
+static void _pam_set_data_info3(struct pwb_context *ctx,
+ const struct wbcAuthUserInfo *info)
+{
+ _pam_set_data_string(ctx, PAM_WINBIND_HOMEDIR,
+ info->home_directory);
+ _pam_set_data_string(ctx, PAM_WINBIND_LOGONSCRIPT,
+ info->logon_script);
+ _pam_set_data_string(ctx, PAM_WINBIND_LOGONSERVER,
+ info->logon_server);
+ _pam_set_data_string(ctx, PAM_WINBIND_PROFILEPATH,
+ info->profile_path);
+}
+
+/**
+ * Free info3 strings in the PAM stack.
+ *
+ * @param pamh PAM handle
+ *
+ * @return void.
+ */
+
+static void _pam_free_data_info3(pam_handle_t *pamh)
+{
+ pam_set_data(pamh, PAM_WINBIND_HOMEDIR, NULL, NULL);
+ pam_set_data(pamh, PAM_WINBIND_LOGONSCRIPT, NULL, NULL);
+ pam_set_data(pamh, PAM_WINBIND_LOGONSERVER, NULL, NULL);
+ pam_set_data(pamh, PAM_WINBIND_PROFILEPATH, NULL, NULL);
+}
+
+/**
+ * Send PAM_ERROR_MSG for cached or grace logons.
+ *
+ * @param ctx PAM winbind context.
+ * @param username User in PAM request.
+ * @param info3_user_flgs Info3 flags containing logon type bits.
+ *
+ * @return void.
+ */
+
+static void _pam_warn_logon_type(struct pwb_context *ctx,
+ const char *username,
+ uint32_t info3_user_flgs)
+{
+ /* inform about logon type */
+ if (PAM_WB_GRACE_LOGON(info3_user_flgs)) {
+
+ _make_remark(ctx, PAM_ERROR_MSG,
+ _("Grace login. "
+ "Please change your password as soon you're "
+ "online again"));
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "User %s logged on using grace logon\n",
+ username);
+
+ } else if (PAM_WB_CACHED_LOGON(info3_user_flgs)) {
+
+ _make_remark(ctx, PAM_ERROR_MSG,
+ _("Domain Controller unreachable, "
+ "using cached credentials instead. "
+ "Network resources may be unavailable"));
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "User %s logged on using cached credentials\n",
+ username);
+ }
+}
+
+/**
+ * Send PAM_ERROR_MSG for krb5 errors.
+ *
+ * @param ctx PAM winbind context.
+ * @param username User in PAM request.
+ * @param info3_user_flgs Info3 flags containing logon type bits.
+ *
+ * @return void.
+ */
+
+static void _pam_warn_krb5_failure(struct pwb_context *ctx,
+ const char *username,
+ uint32_t info3_user_flgs)
+{
+ if (PAM_WB_KRB5_CLOCK_SKEW(info3_user_flgs)) {
+ _make_remark(ctx, PAM_ERROR_MSG,
+ _("Failed to establish your Kerberos Ticket cache "
+ "due time differences\n"
+ "with the domain controller. "
+ "Please verify the system time.\n"));
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "User %s: Clock skew when getting Krb5 TGT\n",
+ username);
+ }
+}
+
+static bool _pam_check_remark_auth_err(struct pwb_context *ctx,
+ const struct wbcAuthErrorInfo *e,
+ const char *nt_status_string,
+ int *pam_error)
+{
+ const char *ntstatus = NULL;
+ const char *error_string = NULL;
+
+ if (!e || !pam_error) {
+ return false;
+ }
+
+ ntstatus = e->nt_string;
+ if (!ntstatus) {
+ return false;
+ }
+
+ if (strcasecmp(ntstatus, nt_status_string) == 0) {
+
+ error_string = _get_ntstatus_error_string(nt_status_string);
+ if (error_string) {
+ _make_remark(ctx, PAM_ERROR_MSG, error_string);
+ *pam_error = e->pam_error;
+ return true;
+ }
+
+ if (e->display_string) {
+ _make_remark(ctx, PAM_ERROR_MSG, e->display_string);
+ *pam_error = e->pam_error;
+ return true;
+ }
+
+ _make_remark(ctx, PAM_ERROR_MSG, nt_status_string);
+ *pam_error = e->pam_error;
+
+ return true;
+ }
+
+ return false;
+};
+
+/**
+ * Compose Password Restriction String for a PAM_ERROR_MSG conversation.
+ *
+ * @param i The wbcUserPasswordPolicyInfo struct.
+ *
+ * @return string (caller needs to talloc_free).
+ */
+
+static char *_pam_compose_pwd_restriction_string(struct pwb_context *ctx,
+ struct wbcUserPasswordPolicyInfo *i)
+{
+ char *str = NULL;
+
+ if (!i) {
+ goto failed;
+ }
+
+ str = talloc_asprintf(ctx, _("Your password "));
+ if (!str) {
+ goto failed;
+ }
+
+ if (i->min_length_password > 0) {
+ str = talloc_asprintf_append(str,
+ _("must be at least %d characters; "),
+ i->min_length_password);
+ if (!str) {
+ goto failed;
+ }
+ }
+
+ if (i->password_history > 0) {
+ str = talloc_asprintf_append(str,
+ _("cannot repeat any of your previous %d "
+ "passwords; "),
+ i->password_history);
+ if (!str) {
+ goto failed;
+ }
+ }
+
+ if (i->password_properties & WBC_DOMAIN_PASSWORD_COMPLEX) {
+ str = talloc_asprintf_append(str,
+ _("must contain capitals, numerals "
+ "or punctuation; "
+ "and cannot contain your account "
+ "or full name; "));
+ if (!str) {
+ goto failed;
+ }
+ }
+
+ str = talloc_asprintf_append(str,
+ _("Please type a different password. "
+ "Type a password which meets these requirements in "
+ "both text boxes."));
+ if (!str) {
+ goto failed;
+ }
+
+ return str;
+
+ failed:
+ TALLOC_FREE(str);
+ return NULL;
+}
+
+static int _pam_create_homedir(struct pwb_context *ctx,
+ const char *dirname,
+ mode_t mode)
+{
+ struct stat sbuf;
+
+ if (stat(dirname, &sbuf) == 0) {
+ return PAM_SUCCESS;
+ }
+
+ if (mkdir(dirname, mode) != 0) {
+
+ _make_remark_format(ctx, PAM_TEXT_INFO,
+ _("Creating directory: %s failed: %s"),
+ dirname, strerror(errno));
+ _pam_log(ctx, LOG_ERR, "could not create dir: %s (%s)",
+ dirname, strerror(errno));
+ return PAM_PERM_DENIED;
+ }
+
+ return PAM_SUCCESS;
+}
+
+static int _pam_chown_homedir(struct pwb_context *ctx,
+ const char *dirname,
+ uid_t uid,
+ gid_t gid)
+{
+ if (chown(dirname, uid, gid) != 0) {
+ _pam_log(ctx, LOG_ERR, "failed to chown user homedir: %s (%s)",
+ dirname, strerror(errno));
+ return PAM_PERM_DENIED;
+ }
+
+ return PAM_SUCCESS;
+}
+
+static int _pam_mkhomedir(struct pwb_context *ctx)
+{
+ struct passwd *pwd = NULL;
+ char *token = NULL;
+ char *create_dir = NULL;
+ char *user_dir = NULL;
+ int ret;
+ const char *username;
+ mode_t mode = 0700;
+ char *safe_ptr = NULL;
+ char *p = NULL;
+
+ /* Get the username */
+ ret = pam_get_user(ctx->pamh, &username, NULL);
+ if ((ret != PAM_SUCCESS) || (!username)) {
+ _pam_log_debug(ctx, LOG_DEBUG, "can not get the username");
+ return PAM_SERVICE_ERR;
+ }
+
+ pwd = getpwnam(username);
+ if (pwd == NULL) {
+ _pam_log_debug(ctx, LOG_DEBUG, "can not get the username");
+ return PAM_USER_UNKNOWN;
+ }
+ _pam_log_debug(ctx, LOG_DEBUG, "homedir is: %s", pwd->pw_dir);
+
+ ret = _pam_create_homedir(ctx, pwd->pw_dir, 0700);
+ if (ret == PAM_SUCCESS) {
+ ret = _pam_chown_homedir(ctx, pwd->pw_dir,
+ pwd->pw_uid,
+ pwd->pw_gid);
+ }
+
+ if (ret == PAM_SUCCESS) {
+ return ret;
+ }
+
+ /* maybe we need to create parent dirs */
+ create_dir = talloc_strdup(ctx, "/");
+ if (!create_dir) {
+ return PAM_BUF_ERR;
+ }
+
+ /* find final directory */
+ user_dir = strrchr(pwd->pw_dir, '/');
+ if (!user_dir) {
+ return PAM_BUF_ERR;
+ }
+ user_dir++;
+
+ _pam_log(ctx, LOG_DEBUG, "final directory: %s", user_dir);
+
+ p = pwd->pw_dir;
+
+ while ((token = strtok_r(p, "/", &safe_ptr)) != NULL) {
+
+ mode = 0755;
+
+ p = NULL;
+
+ _pam_log_debug(ctx, LOG_DEBUG, "token is %s", token);
+
+ create_dir = talloc_asprintf_append(create_dir, "%s/", token);
+ if (!create_dir) {
+ return PAM_BUF_ERR;
+ }
+ _pam_log_debug(ctx, LOG_DEBUG, "current_dir is %s", create_dir);
+
+ if (strcmp(token, user_dir) == 0) {
+ _pam_log_debug(ctx, LOG_DEBUG, "assuming last directory: %s", token);
+ mode = 0700;
+ }
+
+ ret = _pam_create_homedir(ctx, create_dir, mode);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return _pam_chown_homedir(ctx, create_dir,
+ pwd->pw_uid,
+ pwd->pw_gid);
+}
+
+/* talk to winbindd */
+static int winbind_auth_request(struct pwb_context *ctx,
+ const char *user,
+ const char *pass,
+ const char *member,
+ const char *cctype,
+ const int warn_pwd_expire,
+ struct wbcAuthErrorInfo **p_error,
+ struct wbcLogonUserInfo **p_info,
+ struct wbcUserPasswordPolicyInfo **p_policy,
+ time_t *pwd_last_set,
+ char **user_ret)
+{
+ wbcErr wbc_status;
+
+ struct wbcLogonUserParams logon;
+ char membership_of[1024];
+ uid_t user_uid = -1;
+ uint32_t flags = WBFLAG_PAM_INFO3_TEXT |
+ WBFLAG_PAM_GET_PWD_POLICY;
+
+ struct wbcLogonUserInfo *info = NULL;
+ struct wbcAuthUserInfo *user_info = NULL;
+ struct wbcAuthErrorInfo *error = NULL;
+ struct wbcUserPasswordPolicyInfo *policy = NULL;
+
+ int ret = PAM_AUTH_ERR;
+ int i;
+ const char *codes[] = {
+ "NT_STATUS_PASSWORD_EXPIRED",
+ "NT_STATUS_PASSWORD_MUST_CHANGE",
+ "NT_STATUS_INVALID_WORKSTATION",
+ "NT_STATUS_INVALID_LOGON_HOURS",
+ "NT_STATUS_ACCOUNT_EXPIRED",
+ "NT_STATUS_ACCOUNT_DISABLED",
+ "NT_STATUS_ACCOUNT_LOCKED_OUT",
+ "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
+ "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
+ "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
+ "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
+ "NT_STATUS_NO_LOGON_SERVERS",
+ "NT_STATUS_WRONG_PASSWORD",
+ "NT_STATUS_ACCESS_DENIED"
+ };
+
+ if (pwd_last_set) {
+ *pwd_last_set = 0;
+ }
+
+ /* Krb5 auth always has to go against the KDC of the user's realm */
+
+ if (ctx->ctrl & WINBIND_KRB5_AUTH) {
+ flags |= WBFLAG_PAM_CONTACT_TRUSTDOM;
+ }
+
+ if (ctx->ctrl & (WINBIND_KRB5_AUTH|WINBIND_CACHED_LOGIN)) {
+ struct passwd *pwd = NULL;
+
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+ return PAM_USER_UNKNOWN;
+ }
+ user_uid = pwd->pw_uid;
+ }
+
+ if (ctx->ctrl & WINBIND_KRB5_AUTH) {
+
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "enabling krb5 login flag\n");
+
+ flags |= WBFLAG_PAM_KRB5 |
+ WBFLAG_PAM_FALLBACK_AFTER_KRB5;
+ }
+
+ if (ctx->ctrl & WINBIND_CACHED_LOGIN) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "enabling cached login flag\n");
+ flags |= WBFLAG_PAM_CACHED_LOGIN;
+ }
+
+ if (user_ret) {
+ *user_ret = NULL;
+ flags |= WBFLAG_PAM_UNIX_NAME;
+ }
+
+ if (cctype != NULL) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "enabling request for a %s krb5 ccache\n",
+ cctype);
+ }
+
+ if (member != NULL) {
+
+ ZERO_STRUCT(membership_of);
+
+ if (!winbind_name_list_to_sid_string_list(ctx, user, member,
+ membership_of,
+ sizeof(membership_of))) {
+ _pam_log_debug(ctx, LOG_ERR,
+ "failed to serialize membership of sid "
+ "\"%s\"\n", member);
+ return PAM_AUTH_ERR;
+ }
+ }
+
+ ZERO_STRUCT(logon);
+
+ logon.username = user;
+ logon.password = pass;
+
+ if (cctype) {
+ wbc_status = wbcAddNamedBlob(&logon.num_blobs,
+ &logon.blobs,
+ "krb5_cc_type",
+ 0,
+ (uint8_t *)cctype,
+ strlen(cctype)+1);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+ }
+
+ wbc_status = wbcAddNamedBlob(&logon.num_blobs,
+ &logon.blobs,
+ "flags",
+ 0,
+ (uint8_t *)&flags,
+ sizeof(flags));
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+
+ wbc_status = wbcAddNamedBlob(&logon.num_blobs,
+ &logon.blobs,
+ "user_uid",
+ 0,
+ (uint8_t *)&user_uid,
+ sizeof(user_uid));
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+
+ if (member) {
+ wbc_status = wbcAddNamedBlob(&logon.num_blobs,
+ &logon.blobs,
+ "membership_of",
+ 0,
+ (uint8_t *)membership_of,
+ sizeof(membership_of));
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+ }
+
+ wbc_status = wbcLogonUser(&logon, &info, &error, &policy);
+ ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
+ user, "wbcLogonUser");
+ wbcFreeMemory(logon.blobs);
+ logon.blobs = NULL;
+
+ if (info && info->info) {
+ user_info = info->info;
+ }
+
+ if (pwd_last_set && user_info) {
+ *pwd_last_set = user_info->pass_last_set_time;
+ }
+
+ if (p_info && info) {
+ *p_info = info;
+ }
+
+ if (p_policy && policy) {
+ *p_policy = policy;
+ }
+
+ if (p_error && error) {
+ /* We want to process the error in the caller. */
+ *p_error = error;
+ return ret;
+ }
+
+ for (i=0; i<ARRAY_SIZE(codes); i++) {
+ int _ret = ret;
+ if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) {
+ ret = _ret;
+ goto done;
+ }
+ }
+
+ if ((ret == PAM_SUCCESS) && user_info && policy && info) {
+
+ bool already_expired = false;
+
+ /* warn a user if the password is about to expire soon */
+ _pam_warn_password_expiry(ctx, user_info, policy,
+ warn_pwd_expire,
+ &already_expired);
+
+ if (already_expired == true) {
+
+ SMB_TIME_T last_set = user_info->pass_last_set_time;
+
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "Password has expired "
+ "(Password was last set: %lld, "
+ "the policy says it should expire here "
+ "%lld (now it's: %lu))\n",
+ (long long int)last_set,
+ (long long int)last_set +
+ policy->expire,
+ time(NULL));
+
+ return PAM_AUTHTOK_EXPIRED;
+ }
+
+ /* inform about logon type */
+ _pam_warn_logon_type(ctx, user, user_info->user_flags);
+
+ /* inform about krb5 failures */
+ _pam_warn_krb5_failure(ctx, user, user_info->user_flags);
+
+ /* set some info3 info for other modules in the stack */
+ _pam_set_data_info3(ctx, user_info);
+
+ /* put krb5ccname into env */
+ _pam_setup_krb5_env(ctx, info);
+
+ /* If winbindd returned a username, return the pointer to it
+ * here. */
+ _pam_setup_unix_username(ctx, user_ret, info);
+ }
+
+ done:
+ if (logon.blobs) {
+ wbcFreeMemory(logon.blobs);
+ }
+ if (info && info->blobs) {
+ wbcFreeMemory(info->blobs);
+ }
+ if (error && !p_error) {
+ wbcFreeMemory(error);
+ }
+ if (info && !p_info) {
+ wbcFreeMemory(info);
+ }
+ if (policy && !p_policy) {
+ wbcFreeMemory(policy);
+ }
+
+ return ret;
+}
+
+/* talk to winbindd */
+static int winbind_chauthtok_request(struct pwb_context *ctx,
+ const char *user,
+ const char *oldpass,
+ const char *newpass,
+ time_t pwd_last_set)
+{
+ wbcErr wbc_status;
+ struct wbcChangePasswordParams params;
+ struct wbcAuthErrorInfo *error = NULL;
+ struct wbcUserPasswordPolicyInfo *policy = NULL;
+ enum wbcPasswordChangeRejectReason reject_reason = -1;
+ uint32_t flags = 0;
+
+ int i;
+ const char *codes[] = {
+ "NT_STATUS_BACKUP_CONTROLLER",
+ "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
+ "NT_STATUS_NO_LOGON_SERVERS",
+ "NT_STATUS_ACCESS_DENIED",
+ "NT_STATUS_PWD_TOO_SHORT", /* TODO: tell the min pwd length ? */
+ "NT_STATUS_PWD_TOO_RECENT", /* TODO: tell the minage ? */
+ "NT_STATUS_PWD_HISTORY_CONFLICT" /* TODO: tell the history length ? */
+ };
+ int ret = PAM_AUTH_ERR;
+
+ ZERO_STRUCT(params);
+
+ if (ctx->ctrl & WINBIND_KRB5_AUTH) {
+ flags |= WBFLAG_PAM_KRB5 |
+ WBFLAG_PAM_CONTACT_TRUSTDOM;
+ }
+
+ if (ctx->ctrl & WINBIND_CACHED_LOGIN) {
+ flags |= WBFLAG_PAM_CACHED_LOGIN;
+ }
+
+ params.account_name = user;
+ params.level = WBC_AUTH_USER_LEVEL_PLAIN;
+ params.old_password.plaintext = oldpass;
+ params.new_password.plaintext = newpass;
+ params.flags = flags;
+
+ wbc_status = wbcChangeUserPasswordEx(&params, &error, &reject_reason, &policy);
+ ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
+ user, "wbcChangeUserPasswordEx");
+
+ if (WBC_ERROR_IS_OK(wbc_status)) {
+ _pam_log(ctx, LOG_NOTICE,
+ "user '%s' password changed", user);
+ return PAM_SUCCESS;
+ }
+
+ if (!error) {
+ wbcFreeMemory(policy);
+ return ret;
+ }
+
+ for (i=0; i<ARRAY_SIZE(codes); i++) {
+ int _ret = ret;
+ if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) {
+ ret = _ret;
+ goto done;
+ }
+ }
+
+ if (!strcasecmp(error->nt_string,
+ "NT_STATUS_PASSWORD_RESTRICTION")) {
+
+ char *pwd_restriction_string = NULL;
+ SMB_TIME_T min_pwd_age = 0;
+
+ if (policy) {
+ min_pwd_age = policy->min_passwordage;
+ }
+
+ /* FIXME: avoid to send multiple PAM messages after another */
+ switch (reject_reason) {
+ case -1:
+ break;
+ case WBC_PWD_CHANGE_REJECT_OTHER:
+ if ((min_pwd_age > 0) &&
+ (pwd_last_set + min_pwd_age > time(NULL))) {
+ PAM_WB_REMARK_DIRECT(ctx,
+ "NT_STATUS_PWD_TOO_RECENT");
+ }
+ break;
+ case WBC_PWD_CHANGE_REJECT_TOO_SHORT:
+ PAM_WB_REMARK_DIRECT(ctx,
+ "NT_STATUS_PWD_TOO_SHORT");
+ break;
+ case WBC_PWD_CHANGE_REJECT_IN_HISTORY:
+ PAM_WB_REMARK_DIRECT(ctx,
+ "NT_STATUS_PWD_HISTORY_CONFLICT");
+ break;
+ case WBC_PWD_CHANGE_REJECT_COMPLEXITY:
+ _make_remark(ctx, PAM_ERROR_MSG,
+ _("Password does not meet "
+ "complexity requirements"));
+ break;
+ default:
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "unknown password change "
+ "reject reason: %d",
+ reject_reason);
+ break;
+ }
+
+ pwd_restriction_string =
+ _pam_compose_pwd_restriction_string(ctx, policy);
+ if (pwd_restriction_string) {
+ _make_remark(ctx, PAM_ERROR_MSG,
+ pwd_restriction_string);
+ TALLOC_FREE(pwd_restriction_string);
+ }
+ }
+ done:
+ wbcFreeMemory(error);
+ wbcFreeMemory(policy);
+
+ return ret;
+}
+
+/*
+ * Checks if a user has an account
+ *
+ * return values:
+ * 1 = User not found
+ * 0 = OK
+ * -1 = System error
+ */
+static int valid_user(struct pwb_context *ctx,
+ const char *user)
+{
+ /* check not only if the user is available over NSS calls, also make
+ * sure it's really a winbind user, this is important when stacking PAM
+ * modules in the 'account' or 'password' facility. */
+
+ wbcErr wbc_status;
+ struct passwd *pwd = NULL;
+ struct passwd *wb_pwd = NULL;
+
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+ return 1;
+ }
+
+ wbc_status = wbcGetpwnam(user, &wb_pwd);
+ wbcFreeMemory(wb_pwd);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ _pam_log(ctx, LOG_DEBUG, "valid_user: wbcGetpwnam gave %s\n",
+ wbcErrorString(wbc_status));
+ }
+
+ switch (wbc_status) {
+ case WBC_ERR_UNKNOWN_USER:
+ return 1;
+ case WBC_ERR_SUCCESS:
+ return 0;
+ default:
+ break;
+ }
+ return -1;
+}
+
+static char *_pam_delete(register char *xx)
+{
+ _pam_overwrite(xx);
+ _pam_drop(xx);
+ return NULL;
+}
+
+/*
+ * obtain a password from the user
+ */
+
+static int _winbind_read_password(struct pwb_context *ctx,
+ unsigned int ctrl,
+ const char *comment,
+ const char *prompt1,
+ const char *prompt2,
+ const char **pass)
+{
+ int authtok_flag;
+ int retval;
+ const char *item;
+ char *token;
+
+ _pam_log(ctx, LOG_DEBUG, "getting password (0x%08x)", ctrl);
+
+ /*
+ * make sure nothing inappropriate gets returned
+ */
+
+ *pass = token = NULL;
+
+ /*
+ * which authentication token are we getting?
+ */
+
+ if (on(WINBIND__OLD_PASSWORD, ctrl)) {
+ authtok_flag = PAM_OLDAUTHTOK;
+ } else {
+ authtok_flag = PAM_AUTHTOK;
+ }
+
+ /*
+ * should we obtain the password from a PAM item ?
+ */
+
+ if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) ||
+ on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
+ retval = _pam_get_item(ctx->pamh, authtok_flag, &item);
+ if (retval != PAM_SUCCESS) {
+ /* very strange. */
+ _pam_log(ctx, LOG_ALERT,
+ "pam_get_item returned error "
+ "to unix-read-password");
+ return retval;
+ } else if (item != NULL) { /* we have a password! */
+ *pass = item;
+ item = NULL;
+ _pam_log(ctx, LOG_DEBUG,
+ "pam_get_item returned a password");
+ return PAM_SUCCESS;
+ } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
+ } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
+ && off(WINBIND__OLD_PASSWORD, ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR;
+ }
+ }
+ /*
+ * getting here implies we will have to get the password from the
+ * user directly.
+ */
+
+ {
+ struct pam_message msg[3], *pmsg[3];
+ struct pam_response *resp;
+ int i, replies;
+
+ /* prepare to converse */
+
+ if (comment != NULL && off(ctrl, WINBIND_SILENT)) {
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = discard_const_p(char, comment);
+ i = 1;
+ } else {
+ i = 0;
+ }
+
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = discard_const_p(char, prompt1);
+ replies = 1;
+
+ if (prompt2 != NULL) {
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = discard_const_p(char, prompt2);
+ ++replies;
+ }
+ /* so call the conversation expecting i responses */
+ resp = NULL;
+ retval = converse(ctx->pamh, i, pmsg, &resp);
+ if (resp == NULL) {
+ if (retval == PAM_SUCCESS) {
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ }
+ goto done;
+ }
+ if (retval != PAM_SUCCESS) {
+ _pam_drop_reply(resp, i);
+ goto done;
+ }
+
+ /* interpret the response */
+
+ token = x_strdup(resp[i - replies].resp);
+ if (!token) {
+ _pam_log(ctx, LOG_NOTICE,
+ "could not recover "
+ "authentication token");
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ goto done;
+ }
+
+ if (replies == 2) {
+ /* verify that password entered correctly */
+ if (!resp[i - 1].resp ||
+ strcmp(token, resp[i - 1].resp)) {
+ _pam_delete(token); /* mistyped */
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ _make_remark(ctx, PAM_ERROR_MSG,
+ MISTYPED_PASS);
+ }
+ }
+
+ /*
+ * tidy up the conversation (resp_retcode) is ignored
+ * -- what is it for anyway? AGM
+ */
+ _pam_drop_reply(resp, i);
+ }
+
+ done:
+ if (retval != PAM_SUCCESS) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "unable to obtain a password");
+ return retval;
+ }
+ /* 'token' is the entered password */
+
+ /* we store this password as an item */
+
+ retval = pam_set_item(ctx->pamh, authtok_flag, token);
+ _pam_delete(token); /* clean it up */
+ if (retval != PAM_SUCCESS ||
+ (retval = _pam_get_item(ctx->pamh, authtok_flag, &item)) != PAM_SUCCESS) {
+
+ _pam_log(ctx, LOG_CRIT, "error manipulating password");
+ return retval;
+
+ }
+
+ *pass = item;
+ item = NULL; /* break link to password */
+
+ return PAM_SUCCESS;
+}
+
+static const char *get_conf_item_string(struct pwb_context *ctx,
+ const char *item,
+ int config_flag)
+{
+ int i = 0;
+ const char *parm_opt = NULL;
+
+ if (!(ctx->ctrl & config_flag)) {
+ goto out;
+ }
+
+ /* let the pam opt take precedence over the pam_winbind.conf option */
+ for (i=0; i<ctx->argc; i++) {
+
+ if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) {
+ char *p;
+
+ if ((p = strchr(ctx->argv[i], '=')) == NULL) {
+ _pam_log(ctx, LOG_INFO,
+ "no \"=\" delimiter for \"%s\" found\n",
+ item);
+ goto out;
+ }
+ _pam_log_debug(ctx, LOG_INFO,
+ "PAM config: %s '%s'\n", item, p+1);
+ return p + 1;
+ }
+ }
+
+ if (ctx->dict) {
+ char *key = NULL;
+
+ key = talloc_asprintf(ctx, "global:%s", item);
+ if (!key) {
+ goto out;
+ }
+
+ parm_opt = iniparser_getstr(ctx->dict, key);
+ TALLOC_FREE(key);
+
+ _pam_log_debug(ctx, LOG_INFO, "CONFIG file: %s '%s'\n",
+ item, parm_opt);
+ }
+out:
+ return parm_opt;
+}
+
+static int get_config_item_int(struct pwb_context *ctx,
+ const char *item,
+ int config_flag)
+{
+ int i, parm_opt = -1;
+
+ if (!(ctx->ctrl & config_flag)) {
+ goto out;
+ }
+
+ /* let the pam opt take precedence over the pam_winbind.conf option */
+ for (i = 0; i < ctx->argc; i++) {
+
+ if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) {
+ char *p;
+
+ if ((p = strchr(ctx->argv[i], '=')) == NULL) {
+ _pam_log(ctx, LOG_INFO,
+ "no \"=\" delimiter for \"%s\" found\n",
+ item);
+ goto out;
+ }
+ parm_opt = atoi(p + 1);
+ _pam_log_debug(ctx, LOG_INFO,
+ "PAM config: %s '%d'\n",
+ item, parm_opt);
+ return parm_opt;
+ }
+ }
+
+ if (ctx->dict) {
+ char *key = NULL;
+
+ key = talloc_asprintf(ctx, "global:%s", item);
+ if (!key) {
+ goto out;
+ }
+
+ parm_opt = iniparser_getint(ctx->dict, key, -1);
+ TALLOC_FREE(key);
+
+ _pam_log_debug(ctx, LOG_INFO,
+ "CONFIG file: %s '%d'\n",
+ item, parm_opt);
+ }
+out:
+ return parm_opt;
+}
+
+static const char *get_krb5_cc_type_from_config(struct pwb_context *ctx)
+{
+ return get_conf_item_string(ctx, "krb5_ccache_type",
+ WINBIND_KRB5_CCACHE_TYPE);
+}
+
+static const char *get_member_from_config(struct pwb_context *ctx)
+{
+ const char *ret = NULL;
+ ret = get_conf_item_string(ctx, "require_membership_of",
+ WINBIND_REQUIRED_MEMBERSHIP);
+ if (ret) {
+ return ret;
+ }
+ return get_conf_item_string(ctx, "require-membership-of",
+ WINBIND_REQUIRED_MEMBERSHIP);
+}
+
+static int get_warn_pwd_expire_from_config(struct pwb_context *ctx)
+{
+ int ret;
+ ret = get_config_item_int(ctx, "warn_pwd_expire",
+ WINBIND_WARN_PWD_EXPIRE);
+ /* no or broken setting */
+ if (ret <= 0) {
+ return DEFAULT_DAYS_TO_WARN_BEFORE_PWD_EXPIRES;
+ }
+ return ret;
+}
+
+/**
+ * Retrieve the winbind separator.
+ *
+ * @param ctx PAM winbind context.
+ *
+ * @return string separator character. NULL on failure.
+ */
+
+static char winbind_get_separator(struct pwb_context *ctx)
+{
+ wbcErr wbc_status;
+ static struct wbcInterfaceDetails *details = NULL;
+
+ wbc_status = wbcInterfaceDetails(&details);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ _pam_log(ctx, LOG_ERR,
+ "Could not retrieve winbind interface details: %s",
+ wbcErrorString(wbc_status));
+ return '\0';
+ }
+
+ if (!details) {
+ return '\0';
+ }
+
+ return details->winbind_separator;
+}
+
+
+/**
+ * Convert a upn to a name.
+ *
+ * @param ctx PAM winbind context.
+ * @param upn USer UPN to be trabslated.
+ *
+ * @return converted name. NULL pointer on failure. Caller needs to free.
+ */
+
+static char* winbind_upn_to_username(struct pwb_context *ctx,
+ const char *upn)
+{
+ char sep;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ enum wbcSidType type;
+ char *domain;
+ char *name;
+
+ /* This cannot work when the winbind separator = @ */
+
+ sep = winbind_get_separator(ctx);
+ if (!sep || sep == '@') {
+ return NULL;
+ }
+
+ /* Convert the UPN to a SID */
+
+ wbc_status = wbcLookupName("", upn, &sid, &type);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return NULL;
+ }
+
+ /* Convert the the SID back to the sAMAccountName */
+
+ wbc_status = wbcLookupSid(&sid, &domain, &name, &type);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return NULL;
+ }
+
+ return talloc_asprintf(ctx, "%s\\%s", domain, name);
+}
+
+static int _pam_delete_cred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int retval = PAM_SUCCESS;
+ struct pwb_context *ctx = NULL;
+ struct wbcLogoffUserParams logoff;
+ struct wbcAuthErrorInfo *error = NULL;
+ const char *user;
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+
+ retval = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
+ if (retval) {
+ goto out;
+ }
+
+ _PAM_LOG_FUNCTION_ENTER("_pam_delete_cred", ctx);
+
+ if (ctx->ctrl & WINBIND_KRB5_AUTH) {
+
+ /* destroy the ccache here */
+
+ uint32_t wbc_flags = 0;
+ const char *ccname = NULL;
+ struct passwd *pwd = NULL;
+
+ retval = pam_get_user(pamh, &user, _("Username: "));
+ if (retval) {
+ _pam_log(ctx, LOG_ERR,
+ "could not identify user");
+ goto out;
+ }
+
+ if (user == NULL) {
+ _pam_log(ctx, LOG_ERR,
+ "username was NULL!");
+ retval = PAM_USER_UNKNOWN;
+ goto out;
+ }
+
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "username [%s] obtained", user);
+
+ ccname = pam_getenv(pamh, "KRB5CCNAME");
+ if (ccname == NULL) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "user has no KRB5CCNAME environment");
+ }
+
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+ retval = PAM_USER_UNKNOWN;
+ goto out;
+ }
+
+ wbc_flags = WBFLAG_PAM_KRB5 |
+ WBFLAG_PAM_CONTACT_TRUSTDOM;
+
+ ZERO_STRUCT(logoff);
+
+ logoff.username = user;
+
+ if (ccname) {
+ wbc_status = wbcAddNamedBlob(&logoff.num_blobs,
+ &logoff.blobs,
+ "ccfilename",
+ 0,
+ (uint8_t *)ccname,
+ strlen(ccname)+1);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto out;
+ }
+ }
+
+ wbc_status = wbcAddNamedBlob(&logoff.num_blobs,
+ &logoff.blobs,
+ "flags",
+ 0,
+ (uint8_t *)&wbc_flags,
+ sizeof(wbc_flags));
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto out;
+ }
+
+ wbc_status = wbcAddNamedBlob(&logoff.num_blobs,
+ &logoff.blobs,
+ "user_uid",
+ 0,
+ (uint8_t *)&pwd->pw_uid,
+ sizeof(pwd->pw_uid));
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto out;
+ }
+
+ wbc_status = wbcLogoffUserEx(&logoff, &error);
+ retval = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
+ user, "wbcLogoffUser");
+ wbcFreeMemory(error);
+ wbcFreeMemory(logoff.blobs);
+
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ _pam_log(ctx, LOG_INFO,
+ "failed to logoff user %s: %s\n",
+ user, wbcErrorString(wbc_status));
+ }
+ }
+
+out:
+ if (logoff.blobs) {
+ wbcFreeMemory(logoff.blobs);
+ }
+
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ retval = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
+ user, "wbcLogoffUser");
+ }
+
+ /*
+ * Delete the krb5 ccname variable from the PAM environment
+ * if it was set by winbind.
+ */
+ if (ctx->ctrl & WINBIND_KRB5_AUTH) {
+ pam_putenv(pamh, "KRB5CCNAME");
+ }
+
+ _PAM_LOG_FUNCTION_LEAVE("_pam_delete_cred", ctx, retval);
+
+ TALLOC_FREE(ctx);
+
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ const char *username;
+ const char *password;
+ const char *member = NULL;
+ const char *cctype = NULL;
+ int warn_pwd_expire;
+ int retval = PAM_AUTH_ERR;
+ char *username_ret = NULL;
+ char *new_authtok_required = NULL;
+ char *real_username = NULL;
+ struct pwb_context *ctx = NULL;
+
+ retval = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
+ if (retval) {
+ goto out;
+ }
+
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_authenticate", ctx);
+
+ /* Get the username */
+ retval = pam_get_user(pamh, &username, NULL);
+ if ((retval != PAM_SUCCESS) || (!username)) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "can not get the username");
+ retval = PAM_SERVICE_ERR;
+ goto out;
+ }
+
+
+#if defined(AIX)
+ /* Decode the user name since AIX does not support logn user
+ names by default. The name is encoded as _#uid. */
+
+ if (username[0] == '_') {
+ uid_t id = atoi(&username[1]);
+ struct passwd *pw = NULL;
+
+ if ((id!=0) && ((pw = getpwuid(id)) != NULL)) {
+ real_username = strdup(pw->pw_name);
+ }
+ }
+#endif
+
+ if (!real_username) {
+ /* Just making a copy of the username we got from PAM */
+ if ((real_username = strdup(username)) == NULL) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "memory allocation failure when copying "
+ "username");
+ retval = PAM_SERVICE_ERR;
+ goto out;
+ }
+ }
+
+ /* Maybe this was a UPN */
+
+ if (strchr(real_username, '@') != NULL) {
+ char *samaccountname = NULL;
+
+ samaccountname = winbind_upn_to_username(ctx,
+ real_username);
+ if (samaccountname) {
+ free(real_username);
+ real_username = strdup(samaccountname);
+ }
+ }
+
+ retval = _winbind_read_password(ctx, ctx->ctrl, NULL,
+ _("Password: "), NULL,
+ &password);
+
+ if (retval != PAM_SUCCESS) {
+ _pam_log(ctx, LOG_ERR,
+ "Could not retrieve user's password");
+ retval = PAM_AUTHTOK_ERR;
+ goto out;
+ }
+
+ /* Let's not give too much away in the log file */
+
+#ifdef DEBUG_PASSWORD
+ _pam_log_debug(ctx, LOG_INFO,
+ "Verify user '%s' with password '%s'",
+ real_username, password);
+#else
+ _pam_log_debug(ctx, LOG_INFO,
+ "Verify user '%s'", real_username);
+#endif
+
+ member = get_member_from_config(ctx);
+ cctype = get_krb5_cc_type_from_config(ctx);
+ warn_pwd_expire = get_warn_pwd_expire_from_config(ctx);
+
+ /* Now use the username to look up password */
+ retval = winbind_auth_request(ctx, real_username, password,
+ member, cctype, warn_pwd_expire,
+ NULL, NULL, NULL,
+ NULL, &username_ret);
+
+ if (retval == PAM_NEW_AUTHTOK_REQD ||
+ retval == PAM_AUTHTOK_EXPIRED) {
+
+ char *new_authtok_required_during_auth = NULL;
+
+ new_authtok_required = talloc_asprintf(NULL, "%d", retval);
+ if (!new_authtok_required) {
+ retval = PAM_BUF_ERR;
+ goto out;
+ }
+
+ pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD,
+ new_authtok_required,
+ _pam_winbind_cleanup_func);
+
+ retval = PAM_SUCCESS;
+
+ new_authtok_required_during_auth = talloc_asprintf(NULL, "%d", true);
+ if (!new_authtok_required_during_auth) {
+ retval = PAM_BUF_ERR;
+ goto out;
+ }
+
+ pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
+ new_authtok_required_during_auth,
+ _pam_winbind_cleanup_func);
+
+ goto out;
+ }
+
+out:
+ if (username_ret) {
+ pam_set_item (pamh, PAM_USER, username_ret);
+ _pam_log_debug(ctx, LOG_INFO,
+ "Returned user was '%s'", username_ret);
+ free(username_ret);
+ }
+
+ if (real_username) {
+ free(real_username);
+ }
+
+ if (!new_authtok_required) {
+ pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, NULL, NULL);
+ }
+
+ if (retval != PAM_SUCCESS) {
+ _pam_free_data_info3(pamh);
+ }
+
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_authenticate", ctx, retval);
+
+ TALLOC_FREE(ctx);
+
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int ret = PAM_SYSTEM_ERR;
+ struct pwb_context *ctx = NULL;
+
+ ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
+ if (ret) {
+ goto out;
+ }
+
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_setcred", ctx);
+
+ switch (flags & ~PAM_SILENT) {
+
+ case PAM_DELETE_CRED:
+ ret = _pam_delete_cred(pamh, flags, argc, argv);
+ break;
+ case PAM_REFRESH_CRED:
+ _pam_log_debug(ctx, LOG_WARNING,
+ "PAM_REFRESH_CRED not implemented");
+ ret = PAM_SUCCESS;
+ break;
+ case PAM_REINITIALIZE_CRED:
+ _pam_log_debug(ctx, LOG_WARNING,
+ "PAM_REINITIALIZE_CRED not implemented");
+ ret = PAM_SUCCESS;
+ break;
+ case PAM_ESTABLISH_CRED:
+ _pam_log_debug(ctx, LOG_WARNING,
+ "PAM_ESTABLISH_CRED not implemented");
+ ret = PAM_SUCCESS;
+ break;
+ default:
+ ret = PAM_SYSTEM_ERR;
+ break;
+ }
+
+ out:
+
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_setcred", ctx, ret);
+
+ TALLOC_FREE(ctx);
+
+ return ret;
+}
+
+/*
+ * Account management. We want to verify that the account exists
+ * before returning PAM_SUCCESS
+ */
+PAM_EXTERN
+int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ const char *username;
+ int ret = PAM_USER_UNKNOWN;
+ void *tmp = NULL;
+ struct pwb_context *ctx = NULL;
+
+ ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
+ if (ret) {
+ goto out;
+ }
+
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_acct_mgmt", ctx);
+
+
+ /* Get the username */
+ ret = pam_get_user(pamh, &username, NULL);
+ if ((ret != PAM_SUCCESS) || (!username)) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "can not get the username");
+ ret = PAM_SERVICE_ERR;
+ goto out;
+ }
+
+ /* Verify the username */
+ ret = valid_user(ctx, username);
+ switch (ret) {
+ case -1:
+ /* some sort of system error. The log was already printed */
+ ret = PAM_SERVICE_ERR;
+ goto out;
+ case 1:
+ /* the user does not exist */
+ _pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found",
+ username);
+ if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) {
+ ret = PAM_IGNORE;
+ goto out;
+ }
+ ret = PAM_USER_UNKNOWN;
+ goto out;
+ case 0:
+ pam_get_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD,
+ (const void **)&tmp);
+ if (tmp != NULL) {
+ ret = atoi((const char *)tmp);
+ switch (ret) {
+ case PAM_AUTHTOK_EXPIRED:
+ /* fall through, since new token is required in this case */
+ case PAM_NEW_AUTHTOK_REQD:
+ _pam_log(ctx, LOG_WARNING,
+ "pam_sm_acct_mgmt success but %s is set",
+ PAM_WINBIND_NEW_AUTHTOK_REQD);
+ _pam_log(ctx, LOG_NOTICE,
+ "user '%s' needs new password",
+ username);
+ /* PAM_AUTHTOKEN_REQD does not exist, but is documented in the manpage */
+ ret = PAM_NEW_AUTHTOK_REQD;
+ goto out;
+ default:
+ _pam_log(ctx, LOG_WARNING,
+ "pam_sm_acct_mgmt success");
+ _pam_log(ctx, LOG_NOTICE,
+ "user '%s' granted access", username);
+ ret = PAM_SUCCESS;
+ goto out;
+ }
+ }
+
+ /* Otherwise, the authentication looked good */
+ _pam_log(ctx, LOG_NOTICE,
+ "user '%s' granted access", username);
+ ret = PAM_SUCCESS;
+ goto out;
+ default:
+ /* we don't know anything about this return value */
+ _pam_log(ctx, LOG_ERR,
+ "internal module error (ret = %d, user = '%s')",
+ ret, username);
+ ret = PAM_SERVICE_ERR;
+ goto out;
+ }
+
+ /* should not be reached */
+ ret = PAM_IGNORE;
+
+ out:
+
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_acct_mgmt", ctx, ret);
+
+ TALLOC_FREE(ctx);
+
+ return ret;
+}
+
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int ret = PAM_SUCCESS;
+ struct pwb_context *ctx = NULL;
+
+ ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
+ if (ret) {
+ goto out;
+ }
+
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_open_session", ctx);
+
+ if (ctx->ctrl & WINBIND_MKHOMEDIR) {
+ /* check and create homedir */
+ ret = _pam_mkhomedir(ctx);
+ }
+ out:
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_open_session", ctx, ret);
+
+ TALLOC_FREE(ctx);
+
+ return ret;
+}
+
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int ret = PAM_SUCCESS;
+ struct pwb_context *ctx = NULL;
+
+ ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
+ if (ret) {
+ goto out;
+ }
+
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_close_session", ctx);
+
+out:
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_close_session", ctx, ret);
+
+ TALLOC_FREE(ctx);
+
+ return ret;
+}
+
+/**
+ * evaluate whether we need to re-authenticate with kerberos after a
+ * password change
+ *
+ * @param ctx PAM winbind context.
+ * @param user The username
+ *
+ * @return boolean Returns true if required, false if not.
+ */
+
+static bool _pam_require_krb5_auth_after_chauthtok(struct pwb_context *ctx,
+ const char *user)
+{
+
+ /* Make sure that we only do this if a) the chauthtok got initiated
+ * during a logon attempt (authenticate->acct_mgmt->chauthtok) b) any
+ * later password change via the "passwd" command if done by the user
+ * itself
+ * NB. If we login from gdm or xdm and the password expires,
+ * we change the password, but there is no memory cache.
+ * Thus, even for passthrough login, we should do the
+ * authentication again to update memory cache.
+ * --- BoYang
+ * */
+
+ char *new_authtok_reqd_during_auth = NULL;
+ struct passwd *pwd = NULL;
+
+ _pam_get_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
+ &new_authtok_reqd_during_auth);
+ pam_set_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
+ NULL, NULL);
+
+ if (new_authtok_reqd_during_auth) {
+ return true;
+ }
+
+ pwd = getpwnam(user);
+ if (!pwd) {
+ return false;
+ }
+
+ if (getuid() == pwd->pw_uid) {
+ return true;
+ }
+
+ return false;
+}
+
+
+PAM_EXTERN
+int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int lctrl;
+ int ret;
+ bool cached_login = false;
+
+ /* <DO NOT free() THESE> */
+ const char *user;
+ char *pass_old, *pass_new;
+ /* </DO NOT free() THESE> */
+
+ char *Announce;
+
+ int retry = 0;
+ char *username_ret = NULL;
+ struct wbcAuthErrorInfo *error = NULL;
+ struct pwb_context *ctx = NULL;
+
+ ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
+ if (ret) {
+ goto out;
+ }
+
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_chauthtok", ctx);
+
+ cached_login = (ctx->ctrl & WINBIND_CACHED_LOGIN);
+
+ /* clearing offline bit for auth */
+ ctx->ctrl &= ~WINBIND_CACHED_LOGIN;
+
+ /*
+ * First get the name of a user
+ */
+ ret = pam_get_user(pamh, &user, _("Username: "));
+ if (ret) {
+ _pam_log(ctx, LOG_ERR,
+ "password - could not identify user");
+ goto out;
+ }
+
+ if (user == NULL) {
+ _pam_log(ctx, LOG_ERR, "username was NULL!");
+ ret = PAM_USER_UNKNOWN;
+ goto out;
+ }
+
+ _pam_log_debug(ctx, LOG_DEBUG, "username [%s] obtained", user);
+
+ /* check if this is really a user in winbindd, not only in NSS */
+ ret = valid_user(ctx, user);
+ switch (ret) {
+ case 1:
+ ret = PAM_USER_UNKNOWN;
+ goto out;
+ case -1:
+ ret = PAM_SYSTEM_ERR;
+ goto out;
+ default:
+ break;
+ }
+
+ /*
+ * obtain and verify the current password (OLDAUTHTOK) for
+ * the user.
+ */
+
+ if (flags & PAM_PRELIM_CHECK) {
+ time_t pwdlastset_prelim = 0;
+
+ /* instruct user what is happening */
+
+#define greeting _("Changing password for")
+ Announce = talloc_asprintf(ctx, "%s %s", greeting, user);
+ if (!Announce) {
+ _pam_log(ctx, LOG_CRIT,
+ "password - out of memory");
+ ret = PAM_BUF_ERR;
+ goto out;
+ }
+#undef greeting
+
+ lctrl = ctx->ctrl | WINBIND__OLD_PASSWORD;
+ ret = _winbind_read_password(ctx, lctrl,
+ Announce,
+ _("(current) NT password: "),
+ NULL,
+ (const char **) &pass_old);
+ TALLOC_FREE(Announce);
+ if (ret != PAM_SUCCESS) {
+ _pam_log(ctx, LOG_NOTICE,
+ "password - (old) token not obtained");
+ goto out;
+ }
+
+ /* verify that this is the password for this user */
+
+ ret = winbind_auth_request(ctx, user, pass_old,
+ NULL, NULL, 0,
+ &error, NULL, NULL,
+ &pwdlastset_prelim, NULL);
+
+ if (ret != PAM_ACCT_EXPIRED &&
+ ret != PAM_AUTHTOK_EXPIRED &&
+ ret != PAM_NEW_AUTHTOK_REQD &&
+ ret != PAM_SUCCESS) {
+ pass_old = NULL;
+ goto out;
+ }
+
+ pam_set_data(pamh, PAM_WINBIND_PWD_LAST_SET,
+ (void *)pwdlastset_prelim, NULL);
+
+ ret = pam_set_item(pamh, PAM_OLDAUTHTOK,
+ (const void *) pass_old);
+ pass_old = NULL;
+ if (ret != PAM_SUCCESS) {
+ _pam_log(ctx, LOG_CRIT,
+ "failed to set PAM_OLDAUTHTOK");
+ }
+ } else if (flags & PAM_UPDATE_AUTHTOK) {
+
+ time_t pwdlastset_update = 0;
+
+ /*
+ * obtain the proposed password
+ */
+
+ /*
+ * get the old token back.
+ */
+
+ ret = _pam_get_item(pamh, PAM_OLDAUTHTOK, &pass_old);
+
+ if (ret != PAM_SUCCESS) {
+ _pam_log(ctx, LOG_NOTICE,
+ "user not authenticated");
+ goto out;
+ }
+
+ lctrl = ctx->ctrl & ~WINBIND_TRY_FIRST_PASS_ARG;
+
+ if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
+ lctrl |= WINBIND_USE_FIRST_PASS_ARG;
+ }
+ retry = 0;
+ ret = PAM_AUTHTOK_ERR;
+ while ((ret != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
+ /*
+ * use_authtok is to force the use of a previously entered
+ * password -- needed for pluggable password strength checking
+ */
+
+ ret = _winbind_read_password(ctx, lctrl,
+ NULL,
+ _("Enter new NT password: "),
+ _("Retype new NT password: "),
+ (const char **)&pass_new);
+
+ if (ret != PAM_SUCCESS) {
+ _pam_log_debug(ctx, LOG_ALERT,
+ "password - "
+ "new password not obtained");
+ pass_old = NULL;/* tidy up */
+ goto out;
+ }
+
+ /*
+ * At this point we know who the user is and what they
+ * propose as their new password. Verify that the new
+ * password is acceptable.
+ */
+
+ if (pass_new[0] == '\0') {/* "\0" password = NULL */
+ pass_new = NULL;
+ }
+ }
+
+ /*
+ * By reaching here we have approved the passwords and must now
+ * rebuild the password database file.
+ */
+ _pam_get_data(pamh, PAM_WINBIND_PWD_LAST_SET,
+ &pwdlastset_update);
+
+ /*
+ * if cached creds were enabled, make sure to set the
+ * WINBIND_CACHED_LOGIN bit here in order to have winbindd
+ * update the cached creds storage - gd
+ */
+ if (cached_login) {
+ ctx->ctrl |= WINBIND_CACHED_LOGIN;
+ }
+
+ ret = winbind_chauthtok_request(ctx, user, pass_old,
+ pass_new, pwdlastset_update);
+ if (ret) {
+ _pam_overwrite(pass_new);
+ _pam_overwrite(pass_old);
+ pass_old = pass_new = NULL;
+ goto out;
+ }
+
+ if (_pam_require_krb5_auth_after_chauthtok(ctx, user)) {
+
+ const char *member = NULL;
+ const char *cctype = NULL;
+ int warn_pwd_expire;
+ struct wbcLogonUserInfo *info = NULL;
+ struct wbcUserPasswordPolicyInfo *policy = NULL;
+
+ member = get_member_from_config(ctx);
+ cctype = get_krb5_cc_type_from_config(ctx);
+ warn_pwd_expire = get_warn_pwd_expire_from_config(ctx);
+
+ /* Keep WINBIND_CACHED_LOGIN bit for
+ * authentication after changing the password.
+ * This will update the cached credentials in case
+ * that winbindd_dual_pam_chauthtok() fails
+ * to update them.
+ * --- BoYang
+ * */
+
+ ret = winbind_auth_request(ctx, user, pass_new,
+ member, cctype, 0,
+ &error, &info, &policy,
+ NULL, &username_ret);
+ _pam_overwrite(pass_new);
+ _pam_overwrite(pass_old);
+ pass_old = pass_new = NULL;
+
+ if (ret == PAM_SUCCESS) {
+
+ struct wbcAuthUserInfo *user_info = NULL;
+
+ if (info && info->info) {
+ user_info = info->info;
+ }
+
+ /* warn a user if the password is about to
+ * expire soon */
+ _pam_warn_password_expiry(ctx, user_info, policy,
+ warn_pwd_expire,
+ NULL);
+
+ /* set some info3 info for other modules in the
+ * stack */
+ _pam_set_data_info3(ctx, user_info);
+
+ /* put krb5ccname into env */
+ _pam_setup_krb5_env(ctx, info);
+
+ if (username_ret) {
+ pam_set_item(pamh, PAM_USER,
+ username_ret);
+ _pam_log_debug(ctx, LOG_INFO,
+ "Returned user was '%s'",
+ username_ret);
+ free(username_ret);
+ }
+
+ wbcFreeMemory(info);
+ wbcFreeMemory(policy);
+ }
+
+ goto out;
+ }
+ } else {
+ ret = PAM_SERVICE_ERR;
+ }
+
+out:
+ {
+ /* Deal with offline errors. */
+ int i;
+ const char *codes[] = {
+ "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
+ "NT_STATUS_NO_LOGON_SERVERS",
+ "NT_STATUS_ACCESS_DENIED"
+ };
+
+ for (i=0; i<ARRAY_SIZE(codes); i++) {
+ int _ret;
+ if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) {
+ break;
+ }
+ }
+ }
+
+ wbcFreeMemory(error);
+
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_chauthtok", ctx, ret);
+
+ TALLOC_FREE(ctx);
+
+ return ret;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_winbind_modstruct = {
+ MODULE_NAME,
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok
+};
+
+#endif
+
+/*
+ * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000
+ * Copyright (c) Tim Potter <tpot@samba.org> 2000
+ * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
+ * Copyright (c) Guenther Deschner <gd@samba.org> 2005-2008
+ * Copyright (c) Jan Rêkorajski 1999.
+ * Copyright (c) Andrew G. Morgan 1996-8.
+ * Copyright (c) Alex O. Yuriev, 1996.
+ * Copyright (c) Cristian Gafton 1996.
+ * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software.
+ *
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `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/nsswitch/pam_winbind.h b/nsswitch/pam_winbind.h
new file mode 100644
index 0000000000..c6b2c5e726
--- /dev/null
+++ b/nsswitch/pam_winbind.h
@@ -0,0 +1,169 @@
+/* pam_winbind header file
+ (Solaris needs some macros from Linux for common PAM code)
+
+ Shirish Kalele 2000
+*/
+
+#include "../lib/replace/replace.h"
+#include "system/syslog.h"
+#include "system/time.h"
+#include <talloc.h>
+#include "libwbclient/wbclient.h"
+#include "localedir.h"
+
+#define MODULE_NAME "pam_winbind"
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_PASSWORD
+#define PAM_SM_SESSION
+
+#ifndef PAM_WINBIND_CONFIG_FILE
+#define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
+#endif
+
+#include <iniparser.h>
+
+#ifdef HAVE_LIBINTL_H
+#include <libintl.h>
+#endif
+
+#ifndef LINUX
+
+/* Solaris always uses dynamic pam modules */
+#define PAM_EXTERN extern
+#if defined(HAVE_SECURITY_PAM_APPL_H)
+#include <security/pam_appl.h>
+#elif defined(HAVE_PAM_PAM_APPL_H)
+#include <pam/pam_appl.h>
+#endif
+
+#ifndef PAM_AUTHTOK_RECOVER_ERR
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+#endif
+
+#endif /* defined(SUNOS5) || defined(SUNOS4) || defined(HPUX) || defined(FREEBSD) || defined(AIX) */
+
+#if defined(HAVE_SECURITY_PAM_MODULES_H)
+#include <security/pam_modules.h>
+#elif defined(HAVE_PAM_PAM_MODULES_H)
+#include <pam/pam_modules.h>
+#endif
+
+#if defined(HAVE_SECURITY__PAM_MACROS_H)
+#include <security/_pam_macros.h>
+#elif defined(HAVE_PAM__PAM_MACROS_H)
+#include <pam/_pam_macros.h>
+#else
+/* Define required macros from (Linux PAM 0.68) security/_pam_macros.h */
+#define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \
+do { \
+ int reply_i; \
+ \
+ for (reply_i=0; reply_i<replies; ++reply_i) { \
+ if (reply[reply_i].resp) { \
+ _pam_overwrite(reply[reply_i].resp); \
+ free(reply[reply_i].resp); \
+ } \
+ } \
+ if (reply) \
+ free(reply); \
+} while (0)
+
+#define _pam_overwrite(x) \
+do { \
+ register char *__xx__; \
+ if ((__xx__=(x))) \
+ while (*__xx__) \
+ *__xx__++ = '\0'; \
+} while (0)
+
+/*
+ * Don't just free it, forget it too.
+ */
+
+#define _pam_drop(X) SAFE_FREE(X)
+
+#define x_strdup(s) ( (s) ? strdup(s):NULL )
+#endif /* HAVE_SECURITY__PAM_MACROS_H */
+
+#ifdef HAVE_SECURITY_PAM_EXT_H
+#include <security/pam_ext.h>
+#endif
+
+#define WINBIND_DEBUG_ARG 0x00000001
+#define WINBIND_USE_AUTHTOK_ARG 0x00000002
+#define WINBIND_UNKNOWN_OK_ARG 0x00000004
+#define WINBIND_TRY_FIRST_PASS_ARG 0x00000008
+#define WINBIND_USE_FIRST_PASS_ARG 0x00000010
+#define WINBIND__OLD_PASSWORD 0x00000020
+#define WINBIND_REQUIRED_MEMBERSHIP 0x00000040
+#define WINBIND_KRB5_AUTH 0x00000080
+#define WINBIND_KRB5_CCACHE_TYPE 0x00000100
+#define WINBIND_CACHED_LOGIN 0x00000200
+#define WINBIND_CONFIG_FILE 0x00000400
+#define WINBIND_SILENT 0x00000800
+#define WINBIND_DEBUG_STATE 0x00001000
+#define WINBIND_WARN_PWD_EXPIRE 0x00002000
+#define WINBIND_MKHOMEDIR 0x00004000
+
+#if defined(HAVE_GETTEXT) && !defined(__LCLINT__)
+#define _(string) dgettext(MODULE_NAME, string)
+#else
+#define _(string) string
+#endif
+
+#define N_(string) string
+
+/*
+ * here is the string to inform the user that the new passwords they
+ * typed were not the same.
+ */
+
+#define MISTYPED_PASS _("Sorry, passwords do not match")
+
+#define on(x, y) (x & y)
+#define off(x, y) (!(x & y))
+
+#define PAM_WINBIND_NEW_AUTHTOK_REQD "PAM_WINBIND_NEW_AUTHTOK_REQD"
+#define PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH "PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH"
+#define PAM_WINBIND_HOMEDIR "PAM_WINBIND_HOMEDIR"
+#define PAM_WINBIND_LOGONSCRIPT "PAM_WINBIND_LOGONSCRIPT"
+#define PAM_WINBIND_LOGONSERVER "PAM_WINBIND_LOGONSERVER"
+#define PAM_WINBIND_PROFILEPATH "PAM_WINBIND_PROFILEPATH"
+#define PAM_WINBIND_PWD_LAST_SET "PAM_WINBIND_PWD_LAST_SET"
+
+#define SECONDS_PER_DAY 86400
+
+#define DEFAULT_DAYS_TO_WARN_BEFORE_PWD_EXPIRES 14
+
+#include "winbind_client.h"
+
+#define PAM_WB_REMARK_DIRECT(c,x)\
+{\
+ const char *error_string = NULL; \
+ error_string = _get_ntstatus_error_string(x);\
+ if (error_string != NULL) {\
+ _make_remark(c, PAM_ERROR_MSG, error_string);\
+ } else {\
+ _make_remark(c, PAM_ERROR_MSG, x);\
+ };\
+};
+
+#define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
+
+#define PAM_WB_CACHED_LOGON(x) (x & WBC_AUTH_USER_INFO_CACHED_ACCOUNT)
+#define PAM_WB_KRB5_CLOCK_SKEW(x) (x & LOGON_KRB5_FAIL_CLOCK_SKEW)
+#define PAM_WB_GRACE_LOGON(x) ((WBC_AUTH_USER_INFO_CACHED_ACCOUNT|WBC_AUTH_USER_INFO_GRACE_LOGON) == ( x & (WBC_AUTH_USER_INFO_CACHED_ACCOUNT|WBC_AUTH_USER_INFO_GRACE_LOGON)))
+
+struct pwb_context {
+ pam_handle_t *pamh;
+ int flags;
+ int argc;
+ const char **argv;
+ dictionary *dict;
+ uint32_t ctrl;
+};
+
+#define TALLOC_FREE(ctx) do { if ((ctx) != NULL) {talloc_free(ctx); ctx=NULL;} } while(0)
+#define TALLOC_ZERO_P(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
+#define TALLOC_P(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type)
diff --git a/nsswitch/tests/test_wbinfo.sh b/nsswitch/tests/test_wbinfo.sh
new file mode 100755
index 0000000000..8fa0db812d
--- /dev/null
+++ b/nsswitch/tests/test_wbinfo.sh
@@ -0,0 +1,186 @@
+#!/bin/sh
+# Blackbox test for wbinfo
+if [ $# -lt 4 ]; then
+cat <<EOF
+Usage: test_wbinfo.sh DOMAIN USERNAME PASSWORD TARGET
+EOF
+exit 1;
+fi
+
+DOMAIN=$1
+USERNAME=$2
+PASSWORD=$3
+TARGET=$4
+shift 4
+
+failed=0
+samba4bindir=`dirname $0`/../../source4/bin
+wbinfo=$samba4bindir/wbinfo
+
+. `dirname $0`/../../testprogs/blackbox/subunit.sh
+
+testfail() {
+ name="$1"
+ shift
+ cmdline="$*"
+ echo "test: $name"
+ $cmdline
+ status=$?
+ if [ x$status = x0 ]; then
+ echo "failure: $name"
+ else
+ echo "success: $name"
+ fi
+ return $status
+}
+
+knownfail() {
+ name="$1"
+ shift
+ cmdline="$*"
+ echo "test: $name"
+ $cmdline
+ status=$?
+ if [ x$status = x0 ]; then
+ echo "failure: $name [unexpected success]"
+ status=1
+ else
+ echo "knownfail: $name"
+ status=0
+ fi
+ return $status
+}
+
+
+testit "wbinfo -u against $TARGET" $wbinfo -u || failed=`expr $failed + 1`
+# Does not work yet
+knownfail "wbinfo -g against $TARGET" $wbinfo -g || failed=`expr $failed + 1`
+knownfail "wbinfo -N against $TARGET" $wbinfo -N || failed=`expr $failed + 1`
+knownfail "wbinfo -I against $TARGET" $wbinfo -I || failed=`expr $failed + 1`
+testit "wbinfo -n against $TARGET" $wbinfo -n "$DOMAIN/$USERNAME" || failed=`expr $failed + 1`
+admin_sid=`$wbinfo -n "$DOMAIN/$USERNAME" | cut -d " " -f1`
+echo "$DOMAIN/$USERNAME resolved to $admin_sid"
+
+testit "wbinfo -s $admin_sid against $TARGET" $wbinfo -s $admin_sid || failed=`expr $failed + 1`
+admin_name=`$wbinfo -s $admin_sid | cut -d " " -f1| tr a-z A-Z`
+echo "$admin_sid resolved to $admin_name"
+
+tested_name=`echo $DOMAIN/$USERNAME | tr a-z A-Z`
+
+echo "test: wbinfo -s check for sane mapping"
+if test x$admin_name != x$tested_name; then
+ echo "$admin_name does not match $tested_name"
+ echo "failure: wbinfo -s check for sane mapping"
+ failed=`expr $failed + 1`
+else
+ echo "success: wbinfo -s check for sane mapping"
+fi
+
+testit "wbinfo -n on the returned name against $TARGET" $wbinfo -n $admin_name || failed=`expr $failed + 1`
+test_sid=`$wbinfo -n $tested_name | cut -d " " -f1`
+
+echo "test: wbinfo -n check for sane mapping"
+if test x$admin_sid != x$test_sid; then
+ echo "$admin_sid does not match $test_sid"
+ echo "failure: wbinfo -n check for sane mapping"
+ failed=`expr $failed + 1`
+else
+ echo "success: wbinfo -n check for sane mapping"
+fi
+
+testit "wbinfo -U against $TARGET" $wbinfo -U 30000 || failed=`expr $failed + 1`
+
+echo "test: wbinfo -U check for sane mapping"
+sid_for_30000=`$wbinfo -U 30000`
+if test x$sid_for_30000 != "xS-1-22-1-30000"; then
+ echo "uid 30000 mapped to $sid_for_30000, not S-1-22-1-30000"
+ echo "failure: wbinfo -U check for sane mapping"
+ failed=`expr $failed + 1`
+else
+ echo "success: wbinfo -U check for sane mapping"
+fi
+
+admin_uid=`$wbinfo -U $admin_sid`
+
+testit "wbinfo -G against $TARGET" $wbinfo -G 30000 || failed=`expr $failed + 1`
+
+echo "test: wbinfo -G check for sane mapping"
+sid_for_30000=`$wbinfo -G 30000`
+if test x$sid_for_30000 != "xS-1-22-2-30000"; then
+ echo "gid 30000 mapped to $sid_for_30000, not S-1-22-2-30000"
+ echo "failure: wbinfo -G check for sane mapping"
+ failed=`expr $failed + 1`
+else
+ echo "success: wbinfo -G check for sane mapping"
+fi
+
+testit "wbinfo -S against $TARGET" $wbinfo -S "S-1-22-1-30000" || failed=`expr $failed + 1`
+
+echo "test: wbinfo -S check for sane mapping"
+uid_for_sid=`$wbinfo -S S-1-22-1-30000`
+if test 0$uid_for_sid -ne 30000; then
+ echo "S-1-22-1-30000 mapped to $uid_for_sid, not 30000"
+ echo "failure: wbinfo -S check for sane mapping"
+ failed=`expr $failed + 1`
+else
+ echo "success: wbinfo -S check for sane mapping"
+fi
+
+testfail "wbinfo -S against $TARGET using invalid SID" $wbinfo -S "S-1-22-2-30000" && failed=`expr $failed + 1`
+
+testit "wbinfo -Y against $TARGET" $wbinfo -Y "S-1-22-2-30000" || failed=`expr $failed + 1`
+
+echo "test: wbinfo -Y check for sane mapping"
+gid_for_sid=`$wbinfo -Y S-1-22-2-30000`
+if test 0$gid_for_sid -ne 30000; then
+ echo "S-1-22-2-30000 mapped to $gid_for_sid, not 30000"
+ echo "failure: wbinfo -Y check for sane mapping"
+ failed=`expr $failed + 1`
+else
+ echo "success: wbinfo -Y check for sane mapping"
+fi
+
+testfail "wbinfo -Y against $TARGET using invalid SID" $wbinfo -Y "S-1-22-1-30000" && failed=`expr $failed + 1`
+
+testit "wbinfo -t against $TARGET" $wbinfo -t || failed=`expr $failed + 1`
+
+testit "wbinfo --trusted-domains against $TARGET" $wbinfo --trusted-domains || failed=`expr $failed + 1`
+testit "wbinfo --all-domains against $TARGET" $wbinfo --all-domains || failed=`expr $failed + 1`
+testit "wbinfo --own-domain against $TARGET" $wbinfo --own-domain || failed=`expr $failed + 1`
+
+echo "test: wbinfo --own-domain against $TARGET check output"
+own_domain=`$wbinfo --own-domain`
+if test x$own_domain = x$DOMAIN; then
+ echo "success: wbinfo --own-domain against $TARGET check output"
+else
+ echo "Own domain reported as $own_domain instead of $DOMAIN"
+ echo "failure: wbinfo --own-domain against $TARGET check output"
+fi
+
+# this does not work
+knownfail "wbinfo --sequence against $TARGET" $wbinfo --sequence
+knownfail "wbinfo -D against $TARGET" $wbinfo -D $DOMAIN || failed=`expr $failed + 1`
+
+testit "wbinfo -i against $TARGET" $wbinfo -i "$DOMAIN/$USERNAME" || failed=`expr $failed + 1`
+
+# this does not work
+knownfail "wbinfo --uid-info against $TARGET" $wbinfo --uid-info $admin_sid
+knownfail "wbinfo --group-info against $TARGET" $wbinfo --group-info "S-1-22-2-0"
+knownfail "wbinfo -r against $TARGET" $wbinfo -r "$DOMAIN/$USERNAME"
+
+testit "wbinfo --user-domgroups against $TARGET" $wbinfo --user-domgroups $admin_sid || failed=`expr $failed + 1`
+
+testit "wbinfo --user-sids against $TARGET" $wbinfo --user-sids $admin_sid || failed=`expr $failed + 1`
+
+testit "wbinfo -a against $TARGET with domain creds" $wbinfo -a "$DOMAIN/$USERNAME"%"$PASSWORD" || failed=`expr $failed + 1`
+
+# this does not work
+knwonfail "wbinfo --getdcname against $TARGET" $wbinfo --getdcname=$DOMAIN
+
+testit "wbinfo -p against $TARGET" $wbinfo -p || failed=`expr $failed + 1`
+
+testit "wbinfo -K against $TARGET with domain creds" $wbinfo -K "$DOMAIN/$USERNAME"%"$PASSWORD" || failed=`expr $failed + 1`
+
+testit "wbinfo --separator against $TARGET" $wbinfo --separator || failed=`expr $failed + 1`
+
+exit $failed
diff --git a/nsswitch/wb_common.c b/nsswitch/wb_common.c
new file mode 100644
index 0000000000..d0dfcb8bbf
--- /dev/null
+++ b/nsswitch/wb_common.c
@@ -0,0 +1,690 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ winbind client common code
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Andrew Bartlett 2002
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "winbind_client.h"
+
+/* Global variables. These are effectively the client state information */
+
+int winbindd_fd = -1; /* fd for winbindd socket */
+static int is_privileged = 0;
+
+/* Free a response structure */
+
+void winbindd_free_response(struct winbindd_response *response)
+{
+ /* Free any allocated extra_data */
+
+ if (response)
+ SAFE_FREE(response->extra_data.data);
+}
+
+/* Initialise a request structure */
+
+void winbindd_init_request(struct winbindd_request *request, int request_type)
+{
+ request->length = sizeof(struct winbindd_request);
+
+ request->cmd = (enum winbindd_cmd)request_type;
+ request->pid = getpid();
+
+}
+
+/* Initialise a response structure */
+
+static void init_response(struct winbindd_response *response)
+{
+ /* Initialise return value */
+
+ response->result = WINBINDD_ERROR;
+}
+
+/* Close established socket */
+
+void winbind_close_sock(void)
+{
+ if (winbindd_fd != -1) {
+ close(winbindd_fd);
+ winbindd_fd = -1;
+ }
+}
+
+#define CONNECT_TIMEOUT 30
+
+/* Make sure socket handle isn't stdin, stdout or stderr */
+#define RECURSION_LIMIT 3
+
+static int make_nonstd_fd_internals(int fd, int limit /* Recursion limiter */)
+{
+ int new_fd;
+ if (fd >= 0 && fd <= 2) {
+#ifdef F_DUPFD
+ if ((new_fd = fcntl(fd, F_DUPFD, 3)) == -1) {
+ return -1;
+ }
+ /* Paranoia */
+ if (new_fd < 3) {
+ close(new_fd);
+ return -1;
+ }
+ close(fd);
+ return new_fd;
+#else
+ if (limit <= 0)
+ return -1;
+
+ new_fd = dup(fd);
+ if (new_fd == -1)
+ return -1;
+
+ /* use the program stack to hold our list of FDs to close */
+ new_fd = make_nonstd_fd_internals(new_fd, limit - 1);
+ close(fd);
+ return new_fd;
+#endif
+ }
+ return fd;
+}
+
+/****************************************************************************
+ Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
+ else
+ if SYSV use O_NDELAY
+ if BSD use FNDELAY
+ Set close on exec also.
+****************************************************************************/
+
+static int make_safe_fd(int fd)
+{
+ int result, flags;
+ int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT);
+ if (new_fd == -1) {
+ close(fd);
+ return -1;
+ }
+
+ /* Socket should be nonblocking. */
+#ifdef O_NONBLOCK
+#define FLAG_TO_SET O_NONBLOCK
+#else
+#ifdef SYSV
+#define FLAG_TO_SET O_NDELAY
+#else /* BSD */
+#define FLAG_TO_SET FNDELAY
+#endif
+#endif
+
+ if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
+ close(new_fd);
+ return -1;
+ }
+
+ flags |= FLAG_TO_SET;
+ if (fcntl(new_fd, F_SETFL, flags) == -1) {
+ close(new_fd);
+ return -1;
+ }
+
+#undef FLAG_TO_SET
+
+ /* Socket should be closed on exec() */
+#ifdef FD_CLOEXEC
+ result = flags = fcntl(new_fd, F_GETFD, 0);
+ if (flags >= 0) {
+ flags |= FD_CLOEXEC;
+ result = fcntl( new_fd, F_SETFD, flags );
+ }
+ if (result < 0) {
+ close(new_fd);
+ return -1;
+ }
+#endif
+ return new_fd;
+}
+
+/* Connect to winbindd socket */
+
+static int winbind_named_pipe_sock(const char *dir)
+{
+ struct sockaddr_un sunaddr;
+ struct stat st;
+ char *path = NULL;
+ int fd;
+ int wait_time;
+ int slept;
+
+ /* Check permissions on unix socket directory */
+
+ if (lstat(dir, &st) == -1) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (!S_ISDIR(st.st_mode) ||
+ (st.st_uid != 0 && st.st_uid != geteuid())) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* Connect to socket */
+
+ if (asprintf(&path, "%s/%s", dir, WINBINDD_SOCKET_NAME) < 0) {
+ return -1;
+ }
+
+ ZERO_STRUCT(sunaddr);
+ sunaddr.sun_family = AF_UNIX;
+ strncpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path) - 1);
+
+ /* If socket file doesn't exist, don't bother trying to connect
+ with retry. This is an attempt to make the system usable when
+ the winbindd daemon is not running. */
+
+ if (lstat(path, &st) == -1) {
+ errno = ENOENT;
+ SAFE_FREE(path);
+ return -1;
+ }
+
+ SAFE_FREE(path);
+ /* Check permissions on unix socket file */
+
+ if (!S_ISSOCK(st.st_mode) ||
+ (st.st_uid != 0 && st.st_uid != geteuid())) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* Connect to socket */
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ return -1;
+ }
+
+ /* Set socket non-blocking and close on exec. */
+
+ if ((fd = make_safe_fd( fd)) == -1) {
+ return fd;
+ }
+
+ for (wait_time = 0; connect(fd, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1;
+ wait_time += slept) {
+ struct timeval tv;
+ fd_set w_fds;
+ int ret;
+ int connect_errno = 0;
+ socklen_t errnosize;
+
+ if (wait_time >= CONNECT_TIMEOUT)
+ goto error_out;
+
+ switch (errno) {
+ case EINPROGRESS:
+ FD_ZERO(&w_fds);
+ FD_SET(fd, &w_fds);
+ tv.tv_sec = CONNECT_TIMEOUT - wait_time;
+ tv.tv_usec = 0;
+
+ ret = select(fd + 1, NULL, &w_fds, NULL, &tv);
+
+ if (ret > 0) {
+ errnosize = sizeof(connect_errno);
+
+ ret = getsockopt(fd, SOL_SOCKET,
+ SO_ERROR, &connect_errno, &errnosize);
+
+ if (ret >= 0 && connect_errno == 0) {
+ /* Connect succeed */
+ goto out;
+ }
+ }
+
+ slept = CONNECT_TIMEOUT;
+ break;
+ case EAGAIN:
+ slept = rand() % 3 + 1;
+ sleep(slept);
+ break;
+ default:
+ goto error_out;
+ }
+
+ }
+
+ out:
+
+ return fd;
+
+ error_out:
+
+ close(fd);
+ return -1;
+}
+
+static const char *winbindd_socket_dir(void)
+{
+#ifdef SOCKET_WRAPPER
+ const char *env_dir;
+
+ env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
+ if (env_dir) {
+ return env_dir;
+ }
+#endif
+
+ return WINBINDD_SOCKET_DIR;
+}
+
+/* Connect to winbindd socket */
+
+static int winbind_open_pipe_sock(int recursing, int need_priv)
+{
+#ifdef HAVE_UNIXSOCKET
+ static pid_t our_pid;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (our_pid != getpid()) {
+ winbind_close_sock();
+ our_pid = getpid();
+ }
+
+ if ((need_priv != 0) && (is_privileged == 0)) {
+ winbind_close_sock();
+ }
+
+ if (winbindd_fd != -1) {
+ return winbindd_fd;
+ }
+
+ if (recursing) {
+ return -1;
+ }
+
+ if ((winbindd_fd = winbind_named_pipe_sock(winbindd_socket_dir())) == -1) {
+ return -1;
+ }
+
+ is_privileged = 0;
+
+ /* version-check the socket */
+
+ request.wb_flags = WBFLAG_RECURSE;
+ if ((winbindd_request_response(WINBINDD_INTERFACE_VERSION, &request, &response) != NSS_STATUS_SUCCESS) || (response.data.interface_version != WINBIND_INTERFACE_VERSION)) {
+ winbind_close_sock();
+ return -1;
+ }
+
+ /* try and get priv pipe */
+
+ request.wb_flags = WBFLAG_RECURSE;
+ if (winbindd_request_response(WINBINDD_PRIV_PIPE_DIR, &request, &response) == NSS_STATUS_SUCCESS) {
+ int fd;
+ if ((fd = winbind_named_pipe_sock((char *)response.extra_data.data)) != -1) {
+ close(winbindd_fd);
+ winbindd_fd = fd;
+ is_privileged = 1;
+ }
+ }
+
+ if ((need_priv != 0) && (is_privileged == 0)) {
+ return -1;
+ }
+
+ SAFE_FREE(response.extra_data.data);
+
+ return winbindd_fd;
+#else
+ return -1;
+#endif /* HAVE_UNIXSOCKET */
+}
+
+/* Write data to winbindd socket */
+
+int winbind_write_sock(void *buffer, int count, int recursing, int need_priv)
+{
+ int result, nwritten;
+
+ /* Open connection to winbind daemon */
+
+ restart:
+
+ if (winbind_open_pipe_sock(recursing, need_priv) == -1) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* Write data to socket */
+
+ nwritten = 0;
+
+ while(nwritten < count) {
+ struct timeval tv;
+ fd_set r_fds;
+
+ /* Catch pipe close on other end by checking if a read()
+ call would not block by calling select(). */
+
+ FD_ZERO(&r_fds);
+ FD_SET(winbindd_fd, &r_fds);
+ ZERO_STRUCT(tv);
+
+ if (select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv) == -1) {
+ winbind_close_sock();
+ return -1; /* Select error */
+ }
+
+ /* Write should be OK if fd not available for reading */
+
+ if (!FD_ISSET(winbindd_fd, &r_fds)) {
+
+ /* Do the write */
+
+ result = write(winbindd_fd,
+ (char *)buffer + nwritten,
+ count - nwritten);
+
+ if ((result == -1) || (result == 0)) {
+
+ /* Write failed */
+
+ winbind_close_sock();
+ return -1;
+ }
+
+ nwritten += result;
+
+ } else {
+
+ /* Pipe has closed on remote end */
+
+ winbind_close_sock();
+ goto restart;
+ }
+ }
+
+ return nwritten;
+}
+
+/* Read data from winbindd socket */
+
+int winbind_read_sock(void *buffer, int count)
+{
+ int nread = 0;
+ int total_time = 0, selret;
+
+ if (winbindd_fd == -1) {
+ return -1;
+ }
+
+ /* Read data from socket */
+ while(nread < count) {
+ struct timeval tv;
+ fd_set r_fds;
+
+ /* Catch pipe close on other end by checking if a read()
+ call would not block by calling select(). */
+
+ FD_ZERO(&r_fds);
+ FD_SET(winbindd_fd, &r_fds);
+ ZERO_STRUCT(tv);
+ /* Wait for 5 seconds for a reply. May need to parameterise this... */
+ tv.tv_sec = 5;
+
+ if ((selret = select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv)) == -1) {
+ winbind_close_sock();
+ return -1; /* Select error */
+ }
+
+ if (selret == 0) {
+ /* Not ready for read yet... */
+ if (total_time >= 30) {
+ /* Timeout */
+ winbind_close_sock();
+ return -1;
+ }
+ total_time += 5;
+ continue;
+ }
+
+ if (FD_ISSET(winbindd_fd, &r_fds)) {
+
+ /* Do the Read */
+
+ int result = read(winbindd_fd, (char *)buffer + nread,
+ count - nread);
+
+ if ((result == -1) || (result == 0)) {
+
+ /* Read failed. I think the only useful thing we
+ can do here is just return -1 and fail since the
+ transaction has failed half way through. */
+
+ winbind_close_sock();
+ return -1;
+ }
+
+ nread += result;
+
+ }
+ }
+
+ return nread;
+}
+
+/* Read reply */
+
+int winbindd_read_reply(struct winbindd_response *response)
+{
+ int result1, result2 = 0;
+
+ if (!response) {
+ return -1;
+ }
+
+ /* Read fixed length response */
+
+ result1 = winbind_read_sock(response,
+ sizeof(struct winbindd_response));
+ if (result1 == -1) {
+ return -1;
+ }
+
+ /* We actually send the pointer value of the extra_data field from
+ the server. This has no meaning in the client's address space
+ so we clear it out. */
+
+ response->extra_data.data = NULL;
+
+ /* Read variable length response */
+
+ if (response->length > sizeof(struct winbindd_response)) {
+ int extra_data_len = response->length -
+ sizeof(struct winbindd_response);
+
+ /* Mallocate memory for extra data */
+
+ if (!(response->extra_data.data = malloc(extra_data_len))) {
+ return -1;
+ }
+
+ result2 = winbind_read_sock(response->extra_data.data,
+ extra_data_len);
+ if (result2 == -1) {
+ winbindd_free_response(response);
+ return -1;
+ }
+ }
+
+ /* Return total amount of data read */
+
+ return result1 + result2;
+}
+
+/*
+ * send simple types of requests
+ */
+
+NSS_STATUS winbindd_send_request(int req_type, int need_priv,
+ struct winbindd_request *request)
+{
+ struct winbindd_request lrequest;
+
+ /* Check for our tricky environment variable */
+
+ if (winbind_env_set()) {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ if (!request) {
+ ZERO_STRUCT(lrequest);
+ request = &lrequest;
+ }
+
+ /* Fill in request and send down pipe */
+
+ winbindd_init_request(request, req_type);
+
+ if (winbind_write_sock(request, sizeof(*request),
+ request->wb_flags & WBFLAG_RECURSE,
+ need_priv) == -1)
+ {
+ /* Set ENOENT for consistency. Required by some apps */
+ errno = ENOENT;
+
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ if ((request->extra_len != 0) &&
+ (winbind_write_sock(request->extra_data.data,
+ request->extra_len,
+ request->wb_flags & WBFLAG_RECURSE,
+ need_priv) == -1))
+ {
+ /* Set ENOENT for consistency. Required by some apps */
+ errno = ENOENT;
+
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/*
+ * Get results from winbindd request
+ */
+
+NSS_STATUS winbindd_get_response(struct winbindd_response *response)
+{
+ struct winbindd_response lresponse;
+
+ if (!response) {
+ ZERO_STRUCT(lresponse);
+ response = &lresponse;
+ }
+
+ init_response(response);
+
+ /* Wait for reply */
+ if (winbindd_read_reply(response) == -1) {
+ /* Set ENOENT for consistency. Required by some apps */
+ errno = ENOENT;
+
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* Throw away extra data if client didn't request it */
+ if (response == &lresponse) {
+ winbindd_free_response(response);
+ }
+
+ /* Copy reply data from socket */
+ if (response->result != WINBINDD_OK) {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/* Handle simple types of requests */
+
+NSS_STATUS winbindd_request_response(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response)
+{
+ NSS_STATUS status = NSS_STATUS_UNAVAIL;
+ int count = 0;
+
+ while ((status == NSS_STATUS_UNAVAIL) && (count < 10)) {
+ status = winbindd_send_request(req_type, 0, request);
+ if (status != NSS_STATUS_SUCCESS)
+ return(status);
+ status = winbindd_get_response(response);
+ count += 1;
+ }
+
+ return status;
+}
+
+NSS_STATUS winbindd_priv_request_response(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response)
+{
+ NSS_STATUS status = NSS_STATUS_UNAVAIL;
+ int count = 0;
+
+ while ((status == NSS_STATUS_UNAVAIL) && (count < 10)) {
+ status = winbindd_send_request(req_type, 1, request);
+ if (status != NSS_STATUS_SUCCESS)
+ return(status);
+ status = winbindd_get_response(response);
+ count += 1;
+ }
+
+ return status;
+}
+
+/*************************************************************************
+ ************************************************************************/
+
+const char *nss_err_str(NSS_STATUS ret)
+{
+ switch (ret) {
+ case NSS_STATUS_TRYAGAIN:
+ return "NSS_STATUS_TRYAGAIN";
+ case NSS_STATUS_SUCCESS:
+ return "NSS_STATUS_SUCCESS";
+ case NSS_STATUS_NOTFOUND:
+ return "NSS_STATUS_NOTFOUND";
+ case NSS_STATUS_UNAVAIL:
+ return "NSS_STATUS_UNAVAIL";
+#ifdef NSS_STATUS_RETURN
+ case NSS_STATUS_RETURN:
+ return "NSS_STATUS_RETURN";
+#endif
+ default:
+ return "UNKNOWN RETURN CODE!!!!!!!";
+ }
+}
diff --git a/nsswitch/wbinfo.c b/nsswitch/wbinfo.c
new file mode 100644
index 0000000000..36c2818ccf
--- /dev/null
+++ b/nsswitch/wbinfo.c
@@ -0,0 +1,1984 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind status program.
+
+ Copyright (C) Tim Potter 2000-2003
+ Copyright (C) Andrew Bartlett 2002
+
+ 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"
+#include "winbind_client.h"
+#include "libwbclient/wbclient.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+static struct wbcInterfaceDetails *init_interface_details(void)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ static struct wbcInterfaceDetails *details;
+
+ if (details) {
+ return details;
+ }
+
+ wbc_status = wbcInterfaceDetails(&details);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "could not obtain winbind interface details!\n");
+ }
+
+ return details;
+}
+
+static char winbind_separator_int(bool strict)
+{
+ struct wbcInterfaceDetails *details;
+ static bool got_sep;
+ static char sep;
+
+ if (got_sep)
+ return sep;
+
+ details = init_interface_details();
+
+ if (!details) {
+ d_fprintf(stderr, "could not obtain winbind separator!\n");
+ if (strict) {
+ return 0;
+ }
+ /* HACK: (this module should not call lp_ funtions) */
+ return *lp_winbind_separator();
+ }
+
+ sep = details->winbind_separator;
+ got_sep = true;
+
+ if (!sep) {
+ d_fprintf(stderr, "winbind separator was NULL!\n");
+ if (strict) {
+ return 0;
+ }
+ /* HACK: (this module should not call lp_ funtions) */
+ sep = *lp_winbind_separator();
+ }
+
+ return sep;
+}
+
+static char winbind_separator(void)
+{
+ return winbind_separator_int(false);
+}
+
+static const char *get_winbind_domain(void)
+{
+ static struct wbcInterfaceDetails *details;
+
+ details = init_interface_details();
+
+ if (!details) {
+ d_fprintf(stderr, "could not obtain winbind domain name!\n");
+
+ /* HACK: (this module should not call lp_ functions) */
+ return lp_workgroup();
+ }
+
+ return details->netbios_domain;
+}
+
+/* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
+ form DOMAIN/user into a domain and a user */
+
+static bool parse_wbinfo_domain_user(const char *domuser, fstring domain,
+ fstring user)
+{
+
+ char *p = strchr(domuser,winbind_separator());
+
+ if (!p) {
+ /* Maybe it was a UPN? */
+ if ((p = strchr(domuser, '@')) != NULL) {
+ fstrcpy(domain, "");
+ fstrcpy(user, domuser);
+ return true;
+ }
+
+ fstrcpy(user, domuser);
+ fstrcpy(domain, get_winbind_domain());
+ return true;
+ }
+
+ fstrcpy(user, p+1);
+ fstrcpy(domain, domuser);
+ domain[PTR_DIFF(p, domuser)] = 0;
+ strupper_m(domain);
+
+ return true;
+}
+
+/* Parse string of "uid,sid" or "gid,sid" into separate int and string values.
+ * Return true if input was valid, false otherwise. */
+static bool parse_mapping_arg(char *arg, int *id, char **sid)
+{
+ char *tmp, *endptr;
+
+ if (!arg || !*arg)
+ return false;
+
+ tmp = strtok(arg, ",");
+ *sid = strtok(NULL, ",");
+
+ if (!tmp || !*tmp || !*sid || !**sid)
+ return false;
+
+ /* Because atoi() can return 0 on invalid input, which would be a valid
+ * UID/GID we must use strtoul() and do error checking */
+ *id = strtoul(tmp, &endptr, 10);
+
+ if (endptr[0] != '\0')
+ return false;
+
+ return true;
+}
+
+/* pull pwent info for a given user */
+
+static bool wbinfo_get_userinfo(char *user)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct passwd *pwd = NULL;
+
+ wbc_status = wbcGetpwnam(user, &pwd);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ d_printf("%s:%s:%d:%d:%s:%s:%s\n",
+ pwd->pw_name,
+ pwd->pw_passwd,
+ pwd->pw_uid,
+ pwd->pw_gid,
+ pwd->pw_gecos,
+ pwd->pw_dir,
+ pwd->pw_shell);
+
+ return true;
+}
+
+/* pull pwent info for a given uid */
+static bool wbinfo_get_uidinfo(int uid)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct passwd *pwd = NULL;
+
+ wbc_status = wbcGetpwuid(uid, &pwd);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ d_printf("%s:%s:%d:%d:%s:%s:%s\n",
+ pwd->pw_name,
+ pwd->pw_passwd,
+ pwd->pw_uid,
+ pwd->pw_gid,
+ pwd->pw_gecos,
+ pwd->pw_dir,
+ pwd->pw_shell);
+
+ return true;
+}
+
+/* pull grent for a given group */
+static bool wbinfo_get_groupinfo(const char *group)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct group *grp;
+
+ wbc_status = wbcGetgrnam(group, &grp);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ d_printf("%s:%s:%d\n",
+ grp->gr_name,
+ grp->gr_passwd,
+ grp->gr_gid);
+
+ wbcFreeMemory(grp);
+
+ return true;
+}
+
+/* List groups a user is a member of */
+
+static bool wbinfo_get_usergroups(const char *user)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ uint32_t num_groups;
+ uint32_t i;
+ gid_t *groups = NULL;
+
+ /* Send request */
+
+ wbc_status = wbcGetGroups(user, &num_groups, &groups);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ for (i = 0; i < num_groups; i++) {
+ d_printf("%d\n", (int)groups[i]);
+ }
+
+ wbcFreeMemory(groups);
+
+ return true;
+}
+
+
+/* List group SIDs a user SID is a member of */
+static bool wbinfo_get_usersids(const char *user_sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ uint32_t num_sids;
+ uint32_t i;
+ struct wbcDomainSid user_sid, *sids = NULL;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(user_sid_str, &user_sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ wbc_status = wbcLookupUserSids(&user_sid, false, &num_sids, &sids);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ for (i = 0; i < num_sids; i++) {
+ char *str = NULL;
+ wbc_status = wbcSidToString(&sids[i], &str);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ wbcFreeMemory(sids);
+ return false;
+ }
+ d_printf("%s\n", str);
+ wbcFreeMemory(str);
+ }
+
+ wbcFreeMemory(sids);
+
+ return true;
+}
+
+static bool wbinfo_get_userdomgroups(const char *user_sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ uint32_t num_sids;
+ uint32_t i;
+ struct wbcDomainSid user_sid, *sids = NULL;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(user_sid_str, &user_sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ wbc_status = wbcLookupUserSids(&user_sid, true, &num_sids, &sids);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ for (i = 0; i < num_sids; i++) {
+ char *str = NULL;
+ wbc_status = wbcSidToString(&sids[i], &str);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ wbcFreeMemory(sids);
+ return false;
+ }
+ d_printf("%s\n", str);
+ wbcFreeMemory(str);
+ }
+
+ wbcFreeMemory(sids);
+
+ return true;
+}
+
+/* Convert NetBIOS name to IP */
+
+static bool wbinfo_wins_byname(const char *name)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *ip = NULL;
+
+ wbc_status = wbcResolveWinsByName(name, &ip);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s\n", ip);
+
+ wbcFreeMemory(ip);
+
+ return true;
+}
+
+/* Convert IP to NetBIOS name */
+
+static bool wbinfo_wins_byip(const char *ip)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *name = NULL;
+
+ wbc_status = wbcResolveWinsByIP(ip, &name);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s\n", name);
+
+ wbcFreeMemory(name);
+
+ return true;
+}
+
+/* List all/trusted domains */
+
+static bool wbinfo_list_domains(bool list_all_domains, bool verbose)
+{
+ struct wbcDomainInfo *domain_list = NULL;
+ size_t num_domains;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ bool print_all = !list_all_domains && verbose;
+ int i;
+
+ wbc_status = wbcListTrusts(&domain_list, &num_domains);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ if (print_all) {
+ d_printf("%-16s%-24s%-12s%-12s%-5s%-5s\n",
+ "Domain Name", "DNS Domain", "Trust Type",
+ "Transitive", "In", "Out");
+ }
+
+ for (i=0; i<num_domains; i++) {
+ if (print_all) {
+ d_printf("%-16s", domain_list[i].short_name);
+ } else {
+ d_printf("%s", domain_list[i].short_name);
+ d_printf("\n");
+ continue;
+ }
+
+ d_printf("%-24s", domain_list[i].dns_name);
+
+ switch(domain_list[i].trust_type) {
+ case WBC_DOMINFO_TRUSTTYPE_NONE:
+ d_printf("None ");
+ break;
+ case WBC_DOMINFO_TRUSTTYPE_FOREST:
+ d_printf("Forest ");
+ break;
+ case WBC_DOMINFO_TRUSTTYPE_EXTERNAL:
+ d_printf("External ");
+ break;
+ case WBC_DOMINFO_TRUSTTYPE_IN_FOREST:
+ d_printf("In-Forest ");
+ break;
+ }
+
+ if (domain_list[i].trust_flags & WBC_DOMINFO_TRUST_TRANSITIVE) {
+ d_printf("Yes ");
+ } else {
+ d_printf("No ");
+ }
+
+ if (domain_list[i].trust_flags & WBC_DOMINFO_TRUST_INCOMING) {
+ d_printf("Yes ");
+ } else {
+ d_printf("No ");
+ }
+
+ if (domain_list[i].trust_flags & WBC_DOMINFO_TRUST_OUTGOING) {
+ d_printf("Yes ");
+ } else {
+ d_printf("No ");
+ }
+
+ d_printf("\n");
+ }
+
+ return true;
+}
+
+/* List own domain */
+
+static bool wbinfo_list_own_domain(void)
+{
+ d_printf("%s\n", get_winbind_domain());
+
+ return true;
+}
+
+/* show sequence numbers */
+static bool wbinfo_show_sequence(const char *domain)
+{
+ d_printf("This command has been deprecated. Please use the --online-status option instead.\n");
+ return false;
+}
+
+/* show sequence numbers */
+static bool wbinfo_show_onlinestatus(const char *domain)
+{
+ struct wbcDomainInfo *domain_list = NULL;
+ size_t num_domains;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ int i;
+
+ wbc_status = wbcListTrusts(&domain_list, &num_domains);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ for (i=0; i<num_domains; i++) {
+ bool is_offline;
+
+ if (domain) {
+ if (!strequal(domain_list[i].short_name, domain)) {
+ continue;
+ }
+ }
+
+ is_offline = (domain_list[i].domain_flags & WBC_DOMINFO_DOMAIN_OFFLINE);
+
+ d_printf("%s : %s\n",
+ domain_list[i].short_name,
+ is_offline ? "offline" : "online" );
+ }
+
+ return true;
+}
+
+
+/* Show domain info */
+
+static bool wbinfo_domain_info(const char *domain)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainInfo *dinfo = NULL;
+ char *sid_str = NULL;
+
+ if ((domain == NULL) || (strequal(domain, ".")) || (domain[0] == '\0')) {
+ domain = get_winbind_domain();
+ }
+
+ /* Send request */
+
+ wbc_status = wbcDomainInfo(domain, &dinfo);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ wbc_status = wbcSidToString(&dinfo->sid, &sid_str);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ wbcFreeMemory(dinfo);
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("Name : %s\n", dinfo->short_name);
+ d_printf("Alt_Name : %s\n", dinfo->dns_name);
+
+ d_printf("SID : %s\n", sid_str);
+
+ d_printf("Active Directory : %s\n",
+ (dinfo->domain_flags & WBC_DOMINFO_DOMAIN_AD) ? "Yes" : "No");
+ d_printf("Native : %s\n",
+ (dinfo->domain_flags & WBC_DOMINFO_DOMAIN_NATIVE) ? "Yes" : "No");
+
+ d_printf("Primary : %s\n",
+ (dinfo->domain_flags & WBC_DOMINFO_DOMAIN_PRIMARY) ? "Yes" : "No");
+
+ wbcFreeMemory(sid_str);
+ wbcFreeMemory(dinfo);
+
+ return true;
+}
+
+/* Get a foreign DC's name */
+static bool wbinfo_getdcname(const char *domain_name)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.domain_name, domain_name);
+
+ /* Send request */
+
+ if (winbindd_request_response(WINBINDD_GETDCNAME, &request, &response) !=
+ NSS_STATUS_SUCCESS) {
+ d_fprintf(stderr, "Could not get dc name for %s\n", domain_name);
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s\n", response.data.dc_name);
+
+ return true;
+}
+
+/* Find a DC */
+static bool wbinfo_dsgetdcname(const char *domain_name, uint32_t flags)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.dsgetdcname.domain_name, domain_name);
+ request.data.dsgetdcname.flags = flags;
+
+ request.flags |= DS_DIRECTORY_SERVICE_REQUIRED;
+
+ /* Send request */
+
+ if (winbindd_request_response(WINBINDD_DSGETDCNAME, &request, &response) !=
+ NSS_STATUS_SUCCESS) {
+ d_fprintf(stderr, "Could not find dc for %s\n", domain_name);
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s\n", response.data.dsgetdcname.dc_unc);
+ d_printf("%s\n", response.data.dsgetdcname.dc_address);
+ d_printf("%d\n", response.data.dsgetdcname.dc_address_type);
+ d_printf("%s\n", response.data.dsgetdcname.domain_guid);
+ d_printf("%s\n", response.data.dsgetdcname.domain_name);
+ d_printf("%s\n", response.data.dsgetdcname.forest_name);
+ d_printf("0x%08x\n", response.data.dsgetdcname.dc_flags);
+ d_printf("%s\n", response.data.dsgetdcname.dc_site_name);
+ d_printf("%s\n", response.data.dsgetdcname.client_site_name);
+
+ return true;
+}
+
+/* Check trust account password */
+
+static bool wbinfo_check_secret(void)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcAuthErrorInfo *error = NULL;
+
+ wbc_status = wbcCheckTrustCredentials(NULL, &error);
+
+ d_printf("checking the trust secret via RPC calls %s\n",
+ WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+ if (wbc_status == WBC_ERR_AUTH_ERROR) {
+ d_fprintf(stderr, "error code was %s (0x%x)\n",
+ error->nt_string, error->nt_status);
+ wbcFreeMemory(error);
+ }
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ return true;
+}
+
+/* Convert uid to sid */
+
+static bool wbinfo_uid_to_sid(uid_t uid)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ char *sid_str = NULL;
+
+ /* Send request */
+
+ wbc_status = wbcUidToSid(uid, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ wbc_status = wbcSidToString(&sid, &sid_str);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s\n", sid_str);
+
+ wbcFreeMemory(sid_str);
+
+ return true;
+}
+
+/* Convert gid to sid */
+
+static bool wbinfo_gid_to_sid(gid_t gid)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ char *sid_str = NULL;
+
+ /* Send request */
+
+ wbc_status = wbcGidToSid(gid, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ wbc_status = wbcSidToString(&sid, &sid_str);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s\n", sid_str);
+
+ wbcFreeMemory(sid_str);
+
+ return true;
+}
+
+/* Convert sid to uid */
+
+static bool wbinfo_sid_to_uid(const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ uid_t uid;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ wbc_status = wbcSidToUid(&sid, &uid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%d\n", (int)uid);
+
+ return true;
+}
+
+static bool wbinfo_sid_to_gid(const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ gid_t gid;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ wbc_status = wbcSidToGid(&sid, &gid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%d\n", (int)gid);
+
+ return true;
+}
+
+static bool wbinfo_allocate_uid(void)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ uid_t uid;
+
+ /* Send request */
+
+ wbc_status = wbcAllocateUid(&uid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("New uid: %d\n", uid);
+
+ return true;
+}
+
+static bool wbinfo_allocate_gid(void)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ gid_t gid;
+
+ /* Send request */
+
+ wbc_status = wbcAllocateGid(&gid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("New gid: %d\n", gid);
+
+ return true;
+}
+
+static bool wbinfo_set_uid_mapping(uid_t uid, const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ wbc_status = wbcSetUidMapping(uid, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("uid %d now mapped to sid %s\n", uid, sid_str);
+
+ return true;
+}
+
+static bool wbinfo_set_gid_mapping(gid_t gid, const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ wbc_status = wbcSetGidMapping(gid, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("gid %d now mapped to sid %s\n", gid, sid_str);
+
+ return true;
+}
+
+static bool wbinfo_remove_uid_mapping(uid_t uid, const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ wbc_status = wbcRemoveUidMapping(uid, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("Removed uid %d to sid %s mapping\n", uid, sid_str);
+
+ return true;
+}
+
+static bool wbinfo_remove_gid_mapping(gid_t gid, const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ wbc_status = wbcRemoveGidMapping(gid, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("Removed gid %d to sid %s mapping\n", gid, sid_str);
+
+ return true;
+}
+
+/* Convert sid to string */
+
+static bool wbinfo_lookupsid(const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ char *domain;
+ char *name;
+ enum wbcSidType type;
+
+ /* Send off request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ wbc_status = wbcLookupSid(&sid, &domain, &name, &type);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s%c%s %d\n",
+ domain, winbind_separator(), name, type);
+
+ return true;
+}
+
+/* Convert sid to fullname */
+
+static bool wbinfo_lookupsid_fullname(const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ char *domain;
+ char *name;
+ enum wbcSidType type;
+
+ /* Send off request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ wbc_status = wbcGetDisplayName(&sid, &domain, &name, &type);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s%c%s %d\n",
+ domain, winbind_separator(), name, type);
+
+ return true;
+}
+
+/* Lookup a list of RIDs */
+
+static bool wbinfo_lookuprids(const char *domain, const char *arg)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainInfo *dinfo = NULL;
+ char *domain_name = NULL;
+ const char **names = NULL;
+ enum wbcSidType *types = NULL;
+ size_t i;
+ int num_rids;
+ uint32 *rids = NULL;
+ const char *p;
+ char *ridstr;
+ TALLOC_CTX *mem_ctx = NULL;
+ bool ret = false;
+
+ if ((domain == NULL) || (strequal(domain, ".")) || (domain[0] == '\0')) {
+ domain = get_winbind_domain();
+ }
+
+ /* Send request */
+
+ wbc_status = wbcDomainInfo(domain, &dinfo);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_printf("wbcDomainInfo(%s) failed: %s\n", domain,
+ wbcErrorString(wbc_status));
+ goto done;
+ }
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ d_printf("talloc_new failed\n");
+ goto done;
+ }
+
+ num_rids = 0;
+ rids = NULL;
+ p = arg;
+
+ while (next_token_talloc(mem_ctx, &p, &ridstr, " ,\n")) {
+ uint32 rid = strtoul(ridstr, NULL, 10);
+ ADD_TO_ARRAY(mem_ctx, uint32, rid, &rids, &num_rids);
+ }
+
+ if (rids == NULL) {
+ d_printf("no rids\n");
+ goto done;
+ }
+
+ wbc_status = wbcLookupRids(&dinfo->sid, num_rids, rids,
+ (const char **)&domain_name, &names, &types);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_printf("winbind_lookup_rids failed: %s\n",
+ wbcErrorString(wbc_status));
+ goto done;
+ }
+
+ d_printf("Domain: %s\n", domain_name);
+
+ for (i=0; i<num_rids; i++) {
+ d_printf("%8d: %s (%s)\n", rids[i], names[i],
+ sid_type_lookup(types[i]));
+ }
+
+ ret = true;
+done:
+ if (dinfo) {
+ wbcFreeMemory(dinfo);
+ }
+ if (domain_name) {
+ wbcFreeMemory(domain_name);
+ }
+ if (names) {
+ wbcFreeMemory(names);
+ }
+ if (types) {
+ wbcFreeMemory(types);
+ }
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+/* Convert string to sid */
+
+static bool wbinfo_lookupname(const char *full_name)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ char *sid_str;
+ enum wbcSidType type;
+ fstring domain_name;
+ fstring account_name;
+
+ /* Send off request */
+
+ parse_wbinfo_domain_user(full_name, domain_name,
+ account_name);
+
+ wbc_status = wbcLookupName(domain_name, account_name,
+ &sid, &type);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ wbc_status = wbcSidToString(&sid, &sid_str);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s %s (%d)\n", sid_str, sid_type_lookup(type), type);
+
+ wbcFreeMemory(sid_str);
+
+ return true;
+}
+
+static char *wbinfo_prompt_pass(const char *prefix,
+ const char *username)
+{
+ char *prompt;
+ const char *ret = NULL;
+
+ prompt = talloc_asprintf(talloc_tos(), "Enter %s's ", username);
+ if (!prompt) {
+ return NULL;
+ }
+ if (prefix) {
+ prompt = talloc_asprintf_append(prompt, "%s ", prefix);
+ if (!prompt) {
+ return NULL;
+ }
+ }
+ prompt = talloc_asprintf_append(prompt, "password: ");
+ if (!prompt) {
+ return NULL;
+ }
+
+ ret = getpass(prompt);
+ TALLOC_FREE(prompt);
+
+ return SMB_STRDUP(ret);
+}
+
+/* Authenticate a user with a plaintext password */
+
+static bool wbinfo_auth_krb5(char *username, const char *cctype, uint32 flags)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ char *p;
+ char *password;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ p = strchr(username, '%');
+
+ if (p) {
+ *p = 0;
+ fstrcpy(request.data.auth.user, username);
+ fstrcpy(request.data.auth.pass, p + 1);
+ *p = '%';
+ } else {
+ fstrcpy(request.data.auth.user, username);
+ password = wbinfo_prompt_pass(NULL, username);
+ fstrcpy(request.data.auth.pass, password);
+ SAFE_FREE(password);
+ }
+
+ request.flags = flags;
+
+ fstrcpy(request.data.auth.krb5_cc_type, cctype);
+
+ request.data.auth.uid = geteuid();
+
+ result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
+
+ /* Display response */
+
+ d_printf("plaintext kerberos password authentication for [%s] %s (requesting cctype: %s)\n",
+ username, (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed", cctype);
+
+ if (response.data.auth.nt_status)
+ d_fprintf(stderr, "error code was %s (0x%x)\nerror messsage was: %s\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status,
+ response.data.auth.error_string);
+
+ if (result == NSS_STATUS_SUCCESS) {
+
+ if (request.flags & WBFLAG_PAM_INFO3_TEXT) {
+ if (response.data.auth.info3.user_flgs & NETLOGON_CACHED_ACCOUNT) {
+ d_printf("user_flgs: NETLOGON_CACHED_ACCOUNT\n");
+ }
+ }
+
+ if (response.data.auth.krb5ccname[0] != '\0') {
+ d_printf("credentials were put in: %s\n", response.data.auth.krb5ccname);
+ } else {
+ d_printf("no credentials cached\n");
+ }
+ }
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Authenticate a user with a plaintext password */
+
+static bool wbinfo_auth(char *username)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *s = NULL;
+ char *p = NULL;
+ char *password = NULL;
+ char *name = NULL;
+
+ if ((s = SMB_STRDUP(username)) == NULL) {
+ return false;
+ }
+
+ if ((p = strchr(s, '%')) != NULL) {
+ *p = 0;
+ p++;
+ password = SMB_STRDUP(p);
+ } else {
+ password = wbinfo_prompt_pass(NULL, username);
+ }
+
+ name = s;
+
+ wbc_status = wbcAuthenticateUser(name, password);
+
+ d_printf("plaintext password authentication %s\n",
+ WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+#if 0
+ if (response.data.auth.nt_status)
+ d_fprintf(stderr, "error code was %s (0x%x)\nerror messsage was: %s\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status,
+ response.data.auth.error_string);
+#endif
+
+ SAFE_FREE(s);
+ SAFE_FREE(password);
+
+ return WBC_ERROR_IS_OK(wbc_status);
+}
+
+/* Authenticate a user with a challenge/response */
+
+static bool wbinfo_auth_crap(char *username)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcAuthUserParams params;
+ struct wbcAuthUserInfo *info = NULL;
+ struct wbcAuthErrorInfo *err = NULL;
+ DATA_BLOB lm = data_blob_null;
+ DATA_BLOB nt = data_blob_null;
+ fstring name_user;
+ fstring name_domain;
+ char *pass;
+ char *p;
+
+ p = strchr(username, '%');
+
+ if (p) {
+ *p = 0;
+ pass = SMB_STRDUP(p + 1);
+ } else {
+ pass = wbinfo_prompt_pass(NULL, username);
+ }
+
+ parse_wbinfo_domain_user(username, name_domain, name_user);
+
+ params.account_name = name_user;
+ params.domain_name = name_domain;
+ params.workstation_name = NULL;
+
+ params.flags = 0;
+ params.parameter_control= WBC_MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT |
+ WBC_MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
+
+ params.level = WBC_AUTH_USER_LEVEL_RESPONSE;
+
+ generate_random_buffer(params.password.response.challenge, 8);
+
+ if (lp_client_ntlmv2_auth()) {
+ DATA_BLOB server_chal;
+ DATA_BLOB names_blob;
+
+ server_chal = data_blob(params.password.response.challenge, 8);
+
+ /* Pretend this is a login to 'us', for blob purposes */
+ names_blob = NTLMv2_generate_names_blob(global_myname(), lp_workgroup());
+
+ if (!SMBNTLMv2encrypt(name_user, name_domain, pass, &server_chal,
+ &names_blob,
+ &lm, &nt, NULL)) {
+ data_blob_free(&names_blob);
+ data_blob_free(&server_chal);
+ SAFE_FREE(pass);
+ return false;
+ }
+ data_blob_free(&names_blob);
+ data_blob_free(&server_chal);
+
+ } else {
+ if (lp_client_lanman_auth()) {
+ bool ok;
+ lm = data_blob(NULL, 24);
+ ok = SMBencrypt(pass, params.password.response.challenge,
+ lm.data);
+ if (!ok) {
+ data_blob_free(&lm);
+ }
+ }
+ nt = data_blob(NULL, 24);
+ SMBNTencrypt(pass, params.password.response.challenge,
+ nt.data);
+ }
+
+ params.password.response.nt_length = nt.length;
+ params.password.response.nt_data = nt.data;
+ params.password.response.lm_length = lm.length;
+ params.password.response.lm_data = lm.data;
+
+ wbc_status = wbcAuthenticateUserEx(&params, &info, &err);
+
+ /* Display response */
+
+ d_printf("challenge/response password authentication %s\n",
+ WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+ if (wbc_status == WBC_ERR_AUTH_ERROR) {
+ d_fprintf(stderr, "error code was %s (0x%x)\nerror messsage was: %s\n",
+ err->nt_string,
+ err->nt_status,
+ err->display_string);
+ wbcFreeMemory(err);
+ } else if (WBC_ERROR_IS_OK(wbc_status)) {
+ wbcFreeMemory(info);
+ }
+
+ data_blob_free(&nt);
+ data_blob_free(&lm);
+ SAFE_FREE(pass);
+
+ return WBC_ERROR_IS_OK(wbc_status);
+}
+
+/* Authenticate a user with a plaintext password and set a token */
+
+static bool wbinfo_klog(char *username)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ char *p;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ p = strchr(username, '%');
+
+ if (p) {
+ *p = 0;
+ fstrcpy(request.data.auth.user, username);
+ fstrcpy(request.data.auth.pass, p + 1);
+ *p = '%';
+ } else {
+ fstrcpy(request.data.auth.user, username);
+ fstrcpy(request.data.auth.pass, getpass("Password: "));
+ }
+
+ request.flags |= WBFLAG_PAM_AFS_TOKEN;
+
+ result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
+
+ /* Display response */
+
+ d_printf("plaintext password authentication %s\n",
+ (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+ if (response.data.auth.nt_status)
+ d_fprintf(stderr, "error code was %s (0x%x)\nerror messsage was: %s\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status,
+ response.data.auth.error_string);
+
+ if (result != NSS_STATUS_SUCCESS)
+ return false;
+
+ if (response.extra_data.data == NULL) {
+ d_fprintf(stderr, "Did not get token data\n");
+ return false;
+ }
+
+ if (!afs_settoken_str((char *)response.extra_data.data)) {
+ d_fprintf(stderr, "Could not set token\n");
+ return false;
+ }
+
+ d_printf("Successfully created AFS token\n");
+ return true;
+}
+
+/* Print domain users */
+
+static bool print_domain_users(const char *domain)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ uint32_t i;
+ uint32_t num_users = 0;
+ const char **users = NULL;
+
+ /* Send request to winbind daemon */
+
+ /* '.' is the special sign for our own domain */
+ if (domain && strcmp(domain, ".") == 0) {
+ domain = get_winbind_domain();
+ }
+
+ wbc_status = wbcListUsers(domain, &num_users, &users);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ for (i=0; i < num_users; i++) {
+ d_printf("%s\n", users[i]);
+ }
+
+ wbcFreeMemory(users);
+
+ return true;
+}
+
+/* Print domain groups */
+
+static bool print_domain_groups(const char *domain)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ uint32_t i;
+ uint32_t num_groups = 0;
+ const char **groups = NULL;
+
+ /* Send request to winbind daemon */
+
+ /* '.' is the special sign for our own domain */
+ if (domain && strcmp(domain, ".") == 0) {
+ domain = get_winbind_domain();
+ }
+
+ wbc_status = wbcListGroups(domain, &num_groups, &groups);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ for (i=0; i < num_groups; i++) {
+ d_printf("%s\n", groups[i]);
+ }
+
+ wbcFreeMemory(groups);
+
+ return true;
+}
+
+/* Set the authorised user for winbindd access in secrets.tdb */
+
+static bool wbinfo_set_auth_user(char *username)
+{
+ const char *password;
+ char *p;
+ fstring user, domain;
+
+ /* Separate into user and password */
+
+ parse_wbinfo_domain_user(username, domain, user);
+
+ p = strchr(user, '%');
+
+ if (p != NULL) {
+ *p = 0;
+ password = p+1;
+ } else {
+ char *thepass = getpass("Password: ");
+ if (thepass) {
+ password = thepass;
+ } else
+ password = "";
+ }
+
+ /* Store or remove DOMAIN\username%password in secrets.tdb */
+
+ secrets_init();
+
+ if (user[0]) {
+
+ if (!secrets_store(SECRETS_AUTH_USER, user,
+ strlen(user) + 1)) {
+ d_fprintf(stderr, "error storing username\n");
+ return false;
+ }
+
+ /* We always have a domain name added by the
+ parse_wbinfo_domain_user() function. */
+
+ if (!secrets_store(SECRETS_AUTH_DOMAIN, domain,
+ strlen(domain) + 1)) {
+ d_fprintf(stderr, "error storing domain name\n");
+ return false;
+ }
+
+ } else {
+ secrets_delete(SECRETS_AUTH_USER);
+ secrets_delete(SECRETS_AUTH_DOMAIN);
+ }
+
+ if (password[0]) {
+
+ if (!secrets_store(SECRETS_AUTH_PASSWORD, password,
+ strlen(password) + 1)) {
+ d_fprintf(stderr, "error storing password\n");
+ return false;
+ }
+
+ } else
+ secrets_delete(SECRETS_AUTH_PASSWORD);
+
+ return true;
+}
+
+static void wbinfo_get_auth_user(void)
+{
+ char *user, *domain, *password;
+
+ /* Lift data from secrets file */
+
+ secrets_fetch_ipc_userpass(&user, &domain, &password);
+
+ if ((!user || !*user) && (!domain || !*domain ) && (!password || !*password)){
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+ SAFE_FREE(password);
+ d_printf("No authorised user configured\n");
+ return;
+ }
+
+ /* Pretty print authorised user info */
+
+ d_printf("%s%s%s%s%s\n", domain ? domain : "", domain ? lp_winbind_separator(): "",
+ user, password ? "%" : "", password ? password : "");
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+ SAFE_FREE(password);
+}
+
+static bool wbinfo_ping(void)
+{
+ wbcErr wbc_status;
+
+ wbc_status = wbcPing();
+
+ /* Display response */
+
+ d_printf("Ping to winbindd %s\n",
+ WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+ return WBC_ERROR_IS_OK(wbc_status);
+}
+
+static bool wbinfo_change_user_password(const char *username)
+{
+ wbcErr wbc_status;
+ char *old_password = NULL;
+ char *new_password = NULL;
+
+ old_password = wbinfo_prompt_pass("old", username);
+ new_password = wbinfo_prompt_pass("new", username);
+
+ wbc_status = wbcChangeUserPassword(username, old_password, new_password);
+
+ /* Display response */
+
+ d_printf("Password change for user %s %s\n", username,
+ WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+ SAFE_FREE(old_password);
+ SAFE_FREE(new_password);
+
+ return WBC_ERROR_IS_OK(wbc_status);
+}
+
+/* Main program */
+
+enum {
+ OPT_SET_AUTH_USER = 1000,
+ OPT_GET_AUTH_USER,
+ OPT_DOMAIN_NAME,
+ OPT_SEQUENCE,
+ OPT_GETDCNAME,
+ OPT_DSGETDCNAME,
+ OPT_USERDOMGROUPS,
+ OPT_USERSIDS,
+ OPT_ALLOCATE_UID,
+ OPT_ALLOCATE_GID,
+ OPT_SET_UID_MAPPING,
+ OPT_SET_GID_MAPPING,
+ OPT_REMOVE_UID_MAPPING,
+ OPT_REMOVE_GID_MAPPING,
+ OPT_SEPARATOR,
+ OPT_LIST_ALL_DOMAINS,
+ OPT_LIST_OWN_DOMAIN,
+ OPT_UID_INFO,
+ OPT_GROUP_INFO,
+ OPT_VERBOSE,
+ OPT_ONLINESTATUS,
+ OPT_CHANGE_USER_PASSWORD,
+ OPT_SID_TO_FULLNAME
+};
+
+int main(int argc, char **argv, char **envp)
+{
+ int opt;
+ TALLOC_CTX *frame = talloc_stackframe();
+ poptContext pc;
+ static char *string_arg;
+ char *string_subarg = NULL;
+ static char *opt_domain_name;
+ static int int_arg;
+ int int_subarg = -1;
+ int result = 1;
+ bool verbose = false;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+
+ /* longName, shortName, argInfo, argPtr, value, descrip,
+ argDesc */
+
+ { "domain-users", 'u', POPT_ARG_NONE, 0, 'u', "Lists all domain users", "domain"},
+ { "domain-groups", 'g', POPT_ARG_NONE, 0, 'g', "Lists all domain groups", "domain" },
+ { "WINS-by-name", 'N', POPT_ARG_STRING, &string_arg, 'N', "Converts NetBIOS name to IP", "NETBIOS-NAME" },
+ { "WINS-by-ip", 'I', POPT_ARG_STRING, &string_arg, 'I', "Converts IP address to NetBIOS name", "IP" },
+ { "name-to-sid", 'n', POPT_ARG_STRING, &string_arg, 'n', "Converts name to sid", "NAME" },
+ { "sid-to-name", 's', POPT_ARG_STRING, &string_arg, 's', "Converts sid to name", "SID" },
+ { "sid-to-fullname", 0, POPT_ARG_STRING, &string_arg,
+ OPT_SID_TO_FULLNAME, "Converts sid to fullname", "SID" },
+ { "lookup-rids", 'R', POPT_ARG_STRING, &string_arg, 'R', "Converts RIDs to names", "RIDs" },
+ { "uid-to-sid", 'U', POPT_ARG_INT, &int_arg, 'U', "Converts uid to sid" , "UID" },
+ { "gid-to-sid", 'G', POPT_ARG_INT, &int_arg, 'G', "Converts gid to sid", "GID" },
+ { "sid-to-uid", 'S', POPT_ARG_STRING, &string_arg, 'S', "Converts sid to uid", "SID" },
+ { "sid-to-gid", 'Y', POPT_ARG_STRING, &string_arg, 'Y', "Converts sid to gid", "SID" },
+ { "allocate-uid", 0, POPT_ARG_NONE, 0, OPT_ALLOCATE_UID,
+ "Get a new UID out of idmap" },
+ { "allocate-gid", 0, POPT_ARG_NONE, 0, OPT_ALLOCATE_GID,
+ "Get a new GID out of idmap" },
+ { "set-uid-mapping", 0, POPT_ARG_STRING, &string_arg, OPT_SET_UID_MAPPING, "Create or modify uid to sid mapping in idmap", "UID,SID" },
+ { "set-gid-mapping", 0, POPT_ARG_STRING, &string_arg, OPT_SET_GID_MAPPING, "Create or modify gid to sid mapping in idmap", "GID,SID" },
+ { "remove-uid-mapping", 0, POPT_ARG_STRING, &string_arg, OPT_REMOVE_UID_MAPPING, "Remove uid to sid mapping in idmap", "UID,SID" },
+ { "remove-gid-mapping", 0, POPT_ARG_STRING, &string_arg, OPT_REMOVE_GID_MAPPING, "Remove gid to sid mapping in idmap", "GID,SID" },
+ { "check-secret", 't', POPT_ARG_NONE, 0, 't', "Check shared secret" },
+ { "trusted-domains", 'm', POPT_ARG_NONE, 0, 'm', "List trusted domains" },
+ { "all-domains", 0, POPT_ARG_NONE, 0, OPT_LIST_ALL_DOMAINS, "List all domains (trusted and own domain)" },
+ { "own-domain", 0, POPT_ARG_NONE, 0, OPT_LIST_OWN_DOMAIN, "List own domain" },
+ { "sequence", 0, POPT_ARG_NONE, 0, OPT_SEQUENCE, "Show sequence numbers of all domains" },
+ { "online-status", 0, POPT_ARG_NONE, 0, OPT_ONLINESTATUS, "Show whether domains are marked as online or offline"},
+ { "domain-info", 'D', POPT_ARG_STRING, &string_arg, 'D', "Show most of the info we have about the domain" },
+ { "user-info", 'i', POPT_ARG_STRING, &string_arg, 'i', "Get user info", "USER" },
+ { "uid-info", 0, POPT_ARG_INT, &int_arg, OPT_UID_INFO, "Get user info from uid", "UID" },
+ { "group-info", 0, POPT_ARG_STRING, &string_arg, OPT_GROUP_INFO, "Get group info", "GROUP" },
+ { "user-groups", 'r', POPT_ARG_STRING, &string_arg, 'r', "Get user groups", "USER" },
+ { "user-domgroups", 0, POPT_ARG_STRING, &string_arg,
+ OPT_USERDOMGROUPS, "Get user domain groups", "SID" },
+ { "user-sids", 0, POPT_ARG_STRING, &string_arg, OPT_USERSIDS, "Get user group sids for user SID", "SID" },
+ { "authenticate", 'a', POPT_ARG_STRING, &string_arg, 'a', "authenticate user", "user%password" },
+ { "set-auth-user", 0, POPT_ARG_STRING, &string_arg, OPT_SET_AUTH_USER, "Store user and password used by winbindd (root only)", "user%password" },
+ { "getdcname", 0, POPT_ARG_STRING, &string_arg, OPT_GETDCNAME,
+ "Get a DC name for a foreign domain", "domainname" },
+ { "dsgetdcname", 0, POPT_ARG_STRING, &string_arg, OPT_DSGETDCNAME, "Find a DC for a domain", "domainname" },
+ { "get-auth-user", 0, POPT_ARG_NONE, NULL, OPT_GET_AUTH_USER, "Retrieve user and password used by winbindd (root only)", NULL },
+ { "ping", 'p', POPT_ARG_NONE, 0, 'p', "Ping winbindd to see if it is alive" },
+ { "domain", 0, POPT_ARG_STRING, &opt_domain_name, OPT_DOMAIN_NAME, "Define to the domain to restrict operation", "domain" },
+#ifdef WITH_FAKE_KASERVER
+ { "klog", 'k', POPT_ARG_STRING, &string_arg, 'k', "set an AFS token from winbind", "user%password" },
+#endif
+#ifdef HAVE_KRB5
+ { "krb5auth", 'K', POPT_ARG_STRING, &string_arg, 'K', "authenticate user using Kerberos", "user%password" },
+ /* destroys wbinfo --help output */
+ /* "user%password,DOM\\user%password,user@EXAMPLE.COM,EXAMPLE.COM\\user%password" }, */
+#endif
+ { "separator", 0, POPT_ARG_NONE, 0, OPT_SEPARATOR, "Get the active winbind separator", NULL },
+ { "verbose", 0, POPT_ARG_NONE, 0, OPT_VERBOSE, "Print additional information per command", NULL },
+ { "change-user-password", 0, POPT_ARG_STRING, &string_arg, OPT_CHANGE_USER_PASSWORD, "Change the password for a user", NULL },
+ POPT_COMMON_CONFIGFILE
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ /* Samba client initialisation */
+ load_case_tables();
+
+
+ /* Parse options */
+
+ pc = poptGetContext("wbinfo", argc, (const char **)argv, long_options, 0);
+
+ /* Parse command line options */
+
+ if (argc == 1) {
+ poptPrintHelp(pc, stderr, 0);
+ return 1;
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ /* get the generic configuration parameters like --domain */
+ switch (opt) {
+ case OPT_VERBOSE:
+ verbose = True;
+ break;
+ }
+ }
+
+ poptFreeContext(pc);
+
+ if (!lp_load(get_dyn_CONFIGFILE(), true, false, false, true)) {
+ d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n",
+ get_dyn_CONFIGFILE(), strerror(errno));
+ exit(1);
+ }
+
+ if (!init_names())
+ return 1;
+
+ load_interfaces();
+
+ pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'u':
+ if (!print_domain_users(opt_domain_name)) {
+ d_fprintf(stderr, "Error looking up domain users\n");
+ goto done;
+ }
+ break;
+ case 'g':
+ if (!print_domain_groups(opt_domain_name)) {
+ d_fprintf(stderr, "Error looking up domain groups\n");
+ goto done;
+ }
+ break;
+ case 's':
+ if (!wbinfo_lookupsid(string_arg)) {
+ d_fprintf(stderr, "Could not lookup sid %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case OPT_SID_TO_FULLNAME:
+ if (!wbinfo_lookupsid_fullname(string_arg)) {
+ d_fprintf(stderr, "Could not lookup sid %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'R':
+ if (!wbinfo_lookuprids(opt_domain_name, string_arg)) {
+ d_fprintf(stderr, "Could not lookup RIDs %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'n':
+ if (!wbinfo_lookupname(string_arg)) {
+ d_fprintf(stderr, "Could not lookup name %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'N':
+ if (!wbinfo_wins_byname(string_arg)) {
+ d_fprintf(stderr, "Could not lookup WINS by name %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'I':
+ if (!wbinfo_wins_byip(string_arg)) {
+ d_fprintf(stderr, "Could not lookup WINS by IP %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'U':
+ if (!wbinfo_uid_to_sid(int_arg)) {
+ d_fprintf(stderr, "Could not convert uid %d to sid\n", int_arg);
+ goto done;
+ }
+ break;
+ case 'G':
+ if (!wbinfo_gid_to_sid(int_arg)) {
+ d_fprintf(stderr, "Could not convert gid %d to sid\n",
+ int_arg);
+ goto done;
+ }
+ break;
+ case 'S':
+ if (!wbinfo_sid_to_uid(string_arg)) {
+ d_fprintf(stderr, "Could not convert sid %s to uid\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'Y':
+ if (!wbinfo_sid_to_gid(string_arg)) {
+ d_fprintf(stderr, "Could not convert sid %s to gid\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_ALLOCATE_UID:
+ if (!wbinfo_allocate_uid()) {
+ d_fprintf(stderr, "Could not allocate a uid\n");
+ goto done;
+ }
+ break;
+ case OPT_ALLOCATE_GID:
+ if (!wbinfo_allocate_gid()) {
+ d_fprintf(stderr, "Could not allocate a gid\n");
+ goto done;
+ }
+ break;
+ case OPT_SET_UID_MAPPING:
+ if (!parse_mapping_arg(string_arg, &int_subarg,
+ &string_subarg) ||
+ !wbinfo_set_uid_mapping(int_subarg, string_subarg))
+ {
+ d_fprintf(stderr, "Could not create or modify "
+ "uid to sid mapping\n");
+ goto done;
+ }
+ break;
+ case OPT_SET_GID_MAPPING:
+ if (!parse_mapping_arg(string_arg, &int_subarg,
+ &string_subarg) ||
+ !wbinfo_set_gid_mapping(int_subarg, string_subarg))
+ {
+ d_fprintf(stderr, "Could not create or modify "
+ "gid to sid mapping\n");
+ goto done;
+ }
+ break;
+ case OPT_REMOVE_UID_MAPPING:
+ if (!parse_mapping_arg(string_arg, &int_subarg,
+ &string_subarg) ||
+ !wbinfo_remove_uid_mapping(int_subarg,
+ string_subarg))
+ {
+ d_fprintf(stderr, "Could not remove uid to sid "
+ "mapping\n");
+ goto done;
+ }
+ break;
+ case OPT_REMOVE_GID_MAPPING:
+ if (!parse_mapping_arg(string_arg, &int_subarg,
+ &string_subarg) ||
+ !wbinfo_remove_gid_mapping(int_subarg,
+ string_subarg))
+ {
+ d_fprintf(stderr, "Could not remove gid to sid "
+ "mapping\n");
+ goto done;
+ }
+ break;
+ case 't':
+ if (!wbinfo_check_secret()) {
+ d_fprintf(stderr, "Could not check secret\n");
+ goto done;
+ }
+ break;
+ case 'm':
+ if (!wbinfo_list_domains(false, verbose)) {
+ d_fprintf(stderr, "Could not list trusted domains\n");
+ goto done;
+ }
+ break;
+ case OPT_SEQUENCE:
+ if (!wbinfo_show_sequence(opt_domain_name)) {
+ d_fprintf(stderr, "Could not show sequence numbers\n");
+ goto done;
+ }
+ break;
+ case OPT_ONLINESTATUS:
+ if (!wbinfo_show_onlinestatus(opt_domain_name)) {
+ d_fprintf(stderr, "Could not show online-status\n");
+ goto done;
+ }
+ break;
+ case 'D':
+ if (!wbinfo_domain_info(string_arg)) {
+ d_fprintf(stderr, "Could not get domain info\n");
+ goto done;
+ }
+ break;
+ case 'i':
+ if (!wbinfo_get_userinfo(string_arg)) {
+ d_fprintf(stderr, "Could not get info for user %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_UID_INFO:
+ if ( !wbinfo_get_uidinfo(int_arg)) {
+ d_fprintf(stderr, "Could not get info for uid "
+ "%d\n", int_arg);
+ goto done;
+ }
+ break;
+ case OPT_GROUP_INFO:
+ if ( !wbinfo_get_groupinfo(string_arg)) {
+ d_fprintf(stderr, "Could not get info for "
+ "group %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'r':
+ if (!wbinfo_get_usergroups(string_arg)) {
+ d_fprintf(stderr, "Could not get groups for user %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_USERSIDS:
+ if (!wbinfo_get_usersids(string_arg)) {
+ d_fprintf(stderr, "Could not get group SIDs for user SID %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_USERDOMGROUPS:
+ if (!wbinfo_get_userdomgroups(string_arg)) {
+ d_fprintf(stderr, "Could not get user's domain groups "
+ "for user SID %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'a': {
+ bool got_error = false;
+
+ if (!wbinfo_auth(string_arg)) {
+ d_fprintf(stderr, "Could not authenticate user %s with "
+ "plaintext password\n", string_arg);
+ got_error = true;
+ }
+
+ if (!wbinfo_auth_crap(string_arg)) {
+ d_fprintf(stderr, "Could not authenticate user %s with "
+ "challenge/response\n", string_arg);
+ got_error = true;
+ }
+
+ if (got_error)
+ goto done;
+ break;
+ }
+ case 'K': {
+ uint32 flags = WBFLAG_PAM_KRB5 |
+ WBFLAG_PAM_CACHED_LOGIN |
+ WBFLAG_PAM_FALLBACK_AFTER_KRB5 |
+ WBFLAG_PAM_INFO3_TEXT;
+
+ if (!wbinfo_auth_krb5(string_arg, "FILE", flags)) {
+ d_fprintf(stderr, "Could not authenticate user [%s] with "
+ "Kerberos (ccache: %s)\n", string_arg, "FILE");
+ goto done;
+ }
+ break;
+ }
+ case 'k':
+ if (!wbinfo_klog(string_arg)) {
+ d_fprintf(stderr, "Could not klog user\n");
+ goto done;
+ }
+ break;
+ case 'p':
+ if (!wbinfo_ping()) {
+ d_fprintf(stderr, "could not ping winbindd!\n");
+ goto done;
+ }
+ break;
+ case OPT_SET_AUTH_USER:
+ if (!wbinfo_set_auth_user(string_arg)) {
+ goto done;
+ }
+ break;
+ case OPT_GET_AUTH_USER:
+ wbinfo_get_auth_user();
+ break;
+ case OPT_GETDCNAME:
+ if (!wbinfo_getdcname(string_arg)) {
+ goto done;
+ }
+ break;
+ case OPT_DSGETDCNAME:
+ if (!wbinfo_dsgetdcname(string_arg, 0)) {
+ goto done;
+ }
+ break;
+ case OPT_SEPARATOR: {
+ const char sep = winbind_separator_int(true);
+ if ( !sep ) {
+ goto done;
+ }
+ d_printf("%c\n", sep);
+ break;
+ }
+ case OPT_LIST_ALL_DOMAINS:
+ if (!wbinfo_list_domains(true, verbose)) {
+ goto done;
+ }
+ break;
+ case OPT_LIST_OWN_DOMAIN:
+ if (!wbinfo_list_own_domain()) {
+ goto done;
+ }
+ break;
+ case OPT_CHANGE_USER_PASSWORD:
+ if (!wbinfo_change_user_password(string_arg)) {
+ d_fprintf(stderr, "Could not change user password "
+ "for user %s\n", string_arg);
+ goto done;
+ }
+ break;
+
+ /* generic configuration options */
+ case OPT_DOMAIN_NAME:
+ break;
+ case OPT_VERBOSE:
+ break;
+ default:
+ d_fprintf(stderr, "Invalid option\n");
+ poptPrintHelp(pc, stderr, 0);
+ goto done;
+ }
+ }
+
+ result = 0;
+
+ /* Exit code */
+
+ done:
+ talloc_destroy(frame);
+
+ poptFreeContext(pc);
+ return result;
+}
diff --git a/nsswitch/wbinfo4.c b/nsswitch/wbinfo4.c
new file mode 100644
index 0000000000..465c3f793c
--- /dev/null
+++ b/nsswitch/wbinfo4.c
@@ -0,0 +1,1294 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind status program.
+
+ Copyright (C) Tim Potter 2000-2003
+ Copyright (C) Andrew Bartlett 2002-2007
+
+ 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"
+#include "winbind_client.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "libcli/auth/libcli_auth.h"
+#include "libcli/security/security.h"
+#include "lib/cmdline/popt_common.h"
+#include "dynconfig/dynconfig.h"
+#include "param/param.h"
+
+#ifndef fstrcpy
+#define fstrcpy(d,s) safe_strcpy((d),(s),sizeof(fstring)-1)
+#endif
+
+extern int winbindd_fd;
+
+static char winbind_separator_int(bool strict)
+{
+ struct winbindd_response response;
+ static bool got_sep;
+ static char sep;
+
+ if (got_sep)
+ return sep;
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request_response(WINBINDD_INFO, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ d_fprintf(stderr, "could not obtain winbind separator!\n");
+ if (strict) {
+ return 0;
+ }
+ /* HACK: (this module should not call lp_ funtions) */
+ return *lp_winbind_separator(cmdline_lp_ctx);
+ }
+
+ sep = response.data.info.winbind_separator;
+ got_sep = true;
+
+ if (!sep) {
+ d_fprintf(stderr, "winbind separator was NULL!\n");
+ if (strict) {
+ return 0;
+ }
+ /* HACK: (this module should not call lp_ funtions) */
+ sep = *lp_winbind_separator(cmdline_lp_ctx);
+ }
+
+ return sep;
+}
+
+static char winbind_separator(void)
+{
+ return winbind_separator_int(false);
+}
+
+static const char *get_winbind_domain(void)
+{
+ struct winbindd_response response;
+ static fstring winbind_domain;
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request_response(WINBINDD_DOMAIN_NAME, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ d_fprintf(stderr, "could not obtain winbind domain name!\n");
+
+ /* HACK: (this module should not call lp_ funtions) */
+ return lp_workgroup(cmdline_lp_ctx);
+ }
+
+ fstrcpy(winbind_domain, response.data.domain_name);
+
+ return winbind_domain;
+
+}
+
+/* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
+ form DOMAIN/user into a domain and a user */
+
+static bool parse_wbinfo_domain_user(const char *domuser, fstring domain,
+ fstring user)
+{
+
+ char *p = strchr(domuser,winbind_separator());
+
+ if (!p) {
+ fstrcpy(user, domuser);
+ fstrcpy(domain, get_winbind_domain());
+ return true;
+ }
+
+ fstrcpy(user, p+1);
+ fstrcpy(domain, domuser);
+ domain[PTR_DIFF(p, domuser)] = 0;
+ strupper_m(domain);
+
+ return true;
+}
+
+/* pull pwent info for a given user */
+
+static bool wbinfo_get_userinfo(char *user)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.username, user);
+
+ result = winbindd_request_response(WINBINDD_GETPWNAM, &request, &response);
+
+ if (result != NSS_STATUS_SUCCESS)
+ return false;
+
+ d_printf( "%s:%s:%d:%d:%s:%s:%s\n",
+ response.data.pw.pw_name,
+ response.data.pw.pw_passwd,
+ response.data.pw.pw_uid,
+ response.data.pw.pw_gid,
+ response.data.pw.pw_gecos,
+ response.data.pw.pw_dir,
+ response.data.pw.pw_shell );
+
+ return true;
+}
+
+/* pull pwent info for a given uid */
+static bool wbinfo_get_uidinfo(int uid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.uid = uid;
+
+ result = winbindd_request_response(WINBINDD_GETPWUID, &request, &response);
+
+ if (result != NSS_STATUS_SUCCESS)
+ return false;
+
+ d_printf( "%s:%s:%d:%d:%s:%s:%s\n",
+ response.data.pw.pw_name,
+ response.data.pw.pw_passwd,
+ response.data.pw.pw_uid,
+ response.data.pw.pw_gid,
+ response.data.pw.pw_gecos,
+ response.data.pw.pw_dir,
+ response.data.pw.pw_shell );
+
+ return true;
+}
+
+/* pull grent for a given group */
+static bool wbinfo_get_groupinfo(char *group)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.groupname, group);
+
+ result = winbindd_request_response(WINBINDD_GETGRNAM, &request,
+ &response);
+
+ if ( result != NSS_STATUS_SUCCESS)
+ return false;
+
+ d_printf( "%s:%s:%d\n",
+ response.data.gr.gr_name,
+ response.data.gr.gr_passwd,
+ response.data.gr.gr_gid );
+
+ return true;
+}
+
+/* List groups a user is a member of */
+
+static bool wbinfo_get_usergroups(char *user)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ int i;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.username, user);
+
+ result = winbindd_request_response(WINBINDD_GETGROUPS, &request, &response);
+
+ if (result != NSS_STATUS_SUCCESS)
+ return false;
+
+ for (i = 0; i < response.data.num_entries; i++)
+ d_printf("%d\n", (int)((gid_t *)response.extra_data.data)[i]);
+
+ SAFE_FREE(response.extra_data.data);
+
+ return true;
+}
+
+
+/* List group SIDs a user SID is a member of */
+static bool wbinfo_get_usersids(char *user_sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ int i;
+ const char *s;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+ fstrcpy(request.data.sid, user_sid);
+
+ result = winbindd_request_response(WINBINDD_GETUSERSIDS, &request, &response);
+
+ if (result != NSS_STATUS_SUCCESS)
+ return false;
+
+ s = (const char *)response.extra_data.data;
+ for (i = 0; i < response.data.num_entries; i++) {
+ d_printf("%s\n", s);
+ s += strlen(s) + 1;
+ }
+
+ SAFE_FREE(response.extra_data.data);
+
+ return true;
+}
+
+static bool wbinfo_get_userdomgroups(const char *user_sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+ fstrcpy(request.data.sid, user_sid);
+
+ result = winbindd_request_response(WINBINDD_GETUSERDOMGROUPS, &request,
+ &response);
+
+ if (result != NSS_STATUS_SUCCESS)
+ return false;
+
+ if (response.data.num_entries != 0)
+ printf("%s", (char *)response.extra_data.data);
+
+ SAFE_FREE(response.extra_data.data);
+
+ return true;
+}
+
+/* Convert NetBIOS name to IP */
+
+static bool wbinfo_wins_byname(char *name)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.winsreq, name);
+
+ if (winbindd_request_response(WINBINDD_WINS_BYNAME, &request, &response) !=
+ NSS_STATUS_SUCCESS) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s\n", response.data.winsresp);
+
+ return true;
+}
+
+/* Convert IP to NetBIOS name */
+
+static bool wbinfo_wins_byip(char *ip)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.winsreq, ip);
+
+ if (winbindd_request_response(WINBINDD_WINS_BYIP, &request, &response) !=
+ NSS_STATUS_SUCCESS) {
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s\n", response.data.winsresp);
+
+ return true;
+}
+
+/* List trusted domains */
+
+static bool wbinfo_list_domains(bool list_all_domains)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ request.data.list_all_domains = list_all_domains;
+
+ if (winbindd_request_response(WINBINDD_LIST_TRUSTDOM, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return false;
+
+ /* Display response */
+
+ if (response.extra_data.data) {
+ const char *extra_data = (char *)response.extra_data.data;
+ fstring name;
+ char *p;
+
+ while(next_token(&extra_data, name, "\n", sizeof(fstring))) {
+ p = strchr(name, '\\');
+ if (p == 0) {
+ d_fprintf(stderr, "Got invalid response: %s\n",
+ extra_data);
+ return false;
+ }
+ *p = 0;
+ d_printf("%s\n", name);
+ }
+
+ SAFE_FREE(response.extra_data.data);
+ }
+
+ return true;
+}
+
+/* List own domain */
+
+static bool wbinfo_list_own_domain(void)
+{
+ d_printf("%s\n", get_winbind_domain());
+
+ return true;
+}
+
+/* show sequence numbers */
+static bool wbinfo_show_sequence(const char *domain)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ if ( domain )
+ fstrcpy( request.domain_name, domain );
+
+ /* Send request */
+
+ if (winbindd_request_response(WINBINDD_SHOW_SEQUENCE, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return false;
+
+ /* Display response */
+
+ if (response.extra_data.data) {
+ char *extra_data = (char *)response.extra_data.data;
+ d_printf("%s", extra_data);
+ SAFE_FREE(response.extra_data.data);
+ }
+
+ return true;
+}
+
+/* Show domain info */
+
+static bool wbinfo_domain_info(const char *domain_name)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if ((strequal(domain_name, ".")) || (domain_name[0] == '\0'))
+ fstrcpy(request.domain_name, get_winbind_domain());
+ else
+ fstrcpy(request.domain_name, domain_name);
+
+ /* Send request */
+
+ if (winbindd_request_response(WINBINDD_DOMAIN_INFO, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return false;
+
+ /* Display response */
+
+ d_printf("Name : %s\n", response.data.domain_info.name);
+ d_printf("Alt_Name : %s\n", response.data.domain_info.alt_name);
+
+ d_printf("SID : %s\n", response.data.domain_info.sid);
+
+ d_printf("Active Directory : %s\n",
+ response.data.domain_info.active_directory ? "Yes" : "No");
+ d_printf("Native : %s\n",
+ response.data.domain_info.native_mode ? "Yes" : "No");
+
+ d_printf("Primary : %s\n",
+ response.data.domain_info.primary ? "Yes" : "No");
+
+ return true;
+}
+
+/* Get a foreign DC's name */
+static bool wbinfo_getdcname(const char *domain_name)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.domain_name, domain_name);
+
+ /* Send request */
+
+ if (winbindd_request_response(WINBINDD_GETDCNAME, &request, &response) !=
+ NSS_STATUS_SUCCESS) {
+ d_fprintf(stderr, "Could not get dc name for %s\n", domain_name);
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s\n", response.data.dc_name);
+
+ return true;
+}
+
+/* Check trust account password */
+
+static bool wbinfo_check_secret(void)
+{
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ ZERO_STRUCT(response);
+
+ result = winbindd_request_response(WINBINDD_CHECK_MACHACC, NULL, &response);
+
+ d_printf("checking the trust secret via RPC calls %s\n",
+ (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+ if (result != NSS_STATUS_SUCCESS)
+ d_fprintf(stderr, "error code was %s (0x%x)\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Convert uid to sid */
+
+static bool wbinfo_uid_to_sid(uid_t uid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ request.data.uid = uid;
+
+ if (winbindd_request_response(WINBINDD_UID_TO_SID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return false;
+
+ /* Display response */
+
+ d_printf("%s\n", response.data.sid.sid);
+
+ return true;
+}
+
+/* Convert gid to sid */
+
+static bool wbinfo_gid_to_sid(gid_t gid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ request.data.gid = gid;
+
+ if (winbindd_request_response(WINBINDD_GID_TO_SID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return false;
+
+ /* Display response */
+
+ d_printf("%s\n", response.data.sid.sid);
+
+ return true;
+}
+
+/* Convert sid to uid */
+
+static bool wbinfo_sid_to_uid(char *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.sid, sid);
+
+ if (winbindd_request_response(WINBINDD_SID_TO_UID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return false;
+
+ /* Display response */
+
+ d_printf("%d\n", (int)response.data.uid);
+
+ return true;
+}
+
+static bool wbinfo_sid_to_gid(char *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.sid, sid);
+
+ if (winbindd_request_response(WINBINDD_SID_TO_GID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return false;
+
+ /* Display response */
+
+ d_printf("%d\n", (int)response.data.gid);
+
+ return true;
+}
+
+static const char *sid_type_lookup(enum lsa_SidType r)
+{
+ switch (r) {
+ case SID_NAME_USE_NONE: return "SID_NAME_USE_NONE"; break;
+ case SID_NAME_USER: return "SID_NAME_USER"; break;
+ case SID_NAME_DOM_GRP: return "SID_NAME_DOM_GRP"; break;
+ case SID_NAME_DOMAIN: return "SID_NAME_DOMAIN"; break;
+ case SID_NAME_ALIAS: return "SID_NAME_ALIAS"; break;
+ case SID_NAME_WKN_GRP: return "SID_NAME_WKN_GRP"; break;
+ case SID_NAME_DELETED: return "SID_NAME_DELETED"; break;
+ case SID_NAME_INVALID: return "SID_NAME_INVALID"; break;
+ case SID_NAME_UNKNOWN: return "SID_NAME_UNKNOWN"; break;
+ case SID_NAME_COMPUTER: return "SID_NAME_COMPUTER"; break;
+ }
+ return "Invalid sid type\n";
+}
+
+/* Convert sid to string */
+
+static bool wbinfo_lookupsid(char *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ fstrcpy(request.data.sid, sid);
+
+ if (winbindd_request_response(WINBINDD_LOOKUPSID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return false;
+
+ /* Display response */
+
+ d_printf("%s%c%s %s\n", response.data.name.dom_name,
+ winbind_separator(), response.data.name.name,
+ sid_type_lookup(response.data.name.type));
+
+ return true;
+}
+
+/* Convert string to sid */
+
+static bool wbinfo_lookupname(char *name)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ parse_wbinfo_domain_user(name, request.data.name.dom_name,
+ request.data.name.name);
+
+ if (winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return false;
+
+ /* Display response */
+
+ d_printf("%s %s (%d)\n", response.data.sid.sid, sid_type_lookup(response.data.sid.type), response.data.sid.type);
+
+ return true;
+}
+
+/* Authenticate a user with a plaintext password */
+
+static bool wbinfo_auth_krb5(char *username, const char *cctype, uint32_t flags)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ char *p;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ p = strchr(username, '%');
+
+ if (p) {
+ *p = 0;
+ fstrcpy(request.data.auth.user, username);
+ fstrcpy(request.data.auth.pass, p + 1);
+ *p = '%';
+ } else
+ fstrcpy(request.data.auth.user, username);
+
+ request.flags = flags;
+
+ fstrcpy(request.data.auth.krb5_cc_type, cctype);
+
+ request.data.auth.uid = geteuid();
+
+ result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
+
+ /* Display response */
+
+ d_printf("plaintext kerberos password authentication for [%s] %s (requesting cctype: %s)\n",
+ username, (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed", cctype);
+
+ if (response.data.auth.nt_status)
+ d_fprintf(stderr, "error code was %s (0x%x)\nerror messsage was: %s\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status,
+ response.data.auth.error_string);
+
+ if (result == NSS_STATUS_SUCCESS) {
+
+ if (request.flags & WBFLAG_PAM_INFO3_TEXT) {
+ if (response.data.auth.info3.user_flgs & NETLOGON_CACHED_ACCOUNT) {
+ d_printf("user_flgs: NETLOGON_CACHED_ACCOUNT\n");
+ }
+ }
+
+ if (response.data.auth.krb5ccname[0] != '\0') {
+ d_printf("credentials were put in: %s\n", response.data.auth.krb5ccname);
+ } else {
+ d_printf("no credentials cached\n");
+ }
+ }
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Authenticate a user with a plaintext password */
+
+static bool wbinfo_auth(char *username)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ char *p;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ p = strchr(username, '%');
+
+ if (p) {
+ *p = 0;
+ fstrcpy(request.data.auth.user, username);
+ fstrcpy(request.data.auth.pass, p + 1);
+ *p = '%';
+ } else
+ fstrcpy(request.data.auth.user, username);
+
+ result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
+
+ /* Display response */
+
+ d_printf("plaintext password authentication %s\n",
+ (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+ if (response.data.auth.nt_status)
+ d_fprintf(stderr, "error code was %s (0x%x)\nerror messsage was: %s\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status,
+ response.data.auth.error_string);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Authenticate a user with a challenge/response */
+
+static bool wbinfo_auth_crap(struct loadparm_context *lp_ctx, char *username)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ fstring name_user;
+ fstring name_domain;
+ fstring pass;
+ char *p;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ p = strchr(username, '%');
+
+ if (p) {
+ *p = 0;
+ fstrcpy(pass, p + 1);
+ }
+
+ parse_wbinfo_domain_user(username, name_domain, name_user);
+
+ request.data.auth_crap.logon_parameters = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
+
+ fstrcpy(request.data.auth_crap.user, name_user);
+
+ fstrcpy(request.data.auth_crap.domain,
+ name_domain);
+
+ generate_random_buffer(request.data.auth_crap.chal, 8);
+
+ if (lp_client_ntlmv2_auth(lp_ctx)) {
+ DATA_BLOB server_chal;
+ DATA_BLOB names_blob;
+
+ DATA_BLOB lm_response;
+ DATA_BLOB nt_response;
+
+ TALLOC_CTX *mem_ctx;
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ d_printf("talloc_new failed\n");
+ return false;
+ }
+
+ server_chal = data_blob(request.data.auth_crap.chal, 8);
+
+ /* Pretend this is a login to 'us', for blob purposes */
+ names_blob = NTLMv2_generate_names_blob(mem_ctx, lp_netbios_name(lp_ctx), lp_workgroup(lp_ctx));
+
+ if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain, pass, &server_chal,
+ &names_blob,
+ &lm_response, &nt_response, NULL, NULL)) {
+ data_blob_free(&names_blob);
+ data_blob_free(&server_chal);
+ return false;
+ }
+ data_blob_free(&names_blob);
+ data_blob_free(&server_chal);
+
+ memcpy(request.data.auth_crap.nt_resp, nt_response.data,
+ MIN(nt_response.length,
+ sizeof(request.data.auth_crap.nt_resp)));
+ request.data.auth_crap.nt_resp_len = nt_response.length;
+
+ memcpy(request.data.auth_crap.lm_resp, lm_response.data,
+ MIN(lm_response.length,
+ sizeof(request.data.auth_crap.lm_resp)));
+ request.data.auth_crap.lm_resp_len = lm_response.length;
+
+ data_blob_free(&nt_response);
+ data_blob_free(&lm_response);
+
+ } else {
+ if (lp_client_lanman_auth(lp_ctx)
+ && SMBencrypt(pass, request.data.auth_crap.chal,
+ (unsigned char *)request.data.auth_crap.lm_resp)) {
+ request.data.auth_crap.lm_resp_len = 24;
+ } else {
+ request.data.auth_crap.lm_resp_len = 0;
+ }
+ SMBNTencrypt(pass, request.data.auth_crap.chal,
+ (unsigned char *)request.data.auth_crap.nt_resp);
+
+ request.data.auth_crap.nt_resp_len = 24;
+ }
+
+ result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+
+ /* Display response */
+
+ d_printf("challenge/response password authentication %s\n",
+ (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+ if (response.data.auth.nt_status)
+ d_fprintf(stderr, "error code was %s (0x%x)\nerror messsage was: %s\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status,
+ response.data.auth.error_string);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Print domain users */
+
+static bool print_domain_users(const char *domain)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ const char *extra_data;
+ fstring name;
+
+ /* Send request to winbind daemon */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (domain) {
+ /* '.' is the special sign for our own domain */
+ if ( strequal(domain, ".") )
+ fstrcpy( request.domain_name, get_winbind_domain() );
+ else
+ fstrcpy( request.domain_name, domain );
+ }
+
+ if (winbindd_request_response(WINBINDD_LIST_USERS, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return false;
+
+ /* Look through extra data */
+
+ if (!response.extra_data.data)
+ return false;
+
+ extra_data = (const char *)response.extra_data.data;
+
+ while(next_token(&extra_data, name, ",", sizeof(fstring)))
+ d_printf("%s\n", name);
+
+ SAFE_FREE(response.extra_data.data);
+
+ return true;
+}
+
+/* Print domain groups */
+
+static bool print_domain_groups(const char *domain)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ const char *extra_data;
+ fstring name;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (domain) {
+ if ( strequal(domain, ".") )
+ fstrcpy( request.domain_name, get_winbind_domain() );
+ else
+ fstrcpy( request.domain_name, domain );
+ }
+
+ if (winbindd_request_response(WINBINDD_LIST_GROUPS, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return false;
+
+ /* Look through extra data */
+
+ if (!response.extra_data.data)
+ return false;
+
+ extra_data = (const char *)response.extra_data.data;
+
+ while(next_token(&extra_data, name, ",", sizeof(fstring)))
+ d_printf("%s\n", name);
+
+ SAFE_FREE(response.extra_data.data);
+
+ return true;
+}
+
+static bool wbinfo_ping(void)
+{
+ NSS_STATUS result;
+
+ result = winbindd_request_response(WINBINDD_PING, NULL, NULL);
+
+ /* Display response */
+
+ d_printf("Ping to winbindd %s on fd %d\n",
+ (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed", winbindd_fd);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Main program */
+
+enum {
+ OPT_SET_AUTH_USER = 1000,
+ OPT_GET_AUTH_USER,
+ OPT_DOMAIN_NAME,
+ OPT_SEQUENCE,
+ OPT_GETDCNAME,
+ OPT_USERDOMGROUPS,
+ OPT_USERSIDS,
+ OPT_ALLOCATE_UID,
+ OPT_ALLOCATE_GID,
+ OPT_SEPARATOR,
+ OPT_LIST_ALL_DOMAINS,
+ OPT_LIST_OWN_DOMAIN,
+ OPT_UID_INFO,
+ OPT_GROUP_INFO,
+};
+
+int main(int argc, char **argv, char **envp)
+{
+ int opt;
+
+ poptContext pc;
+ static char *string_arg;
+ static char *opt_domain_name;
+ static int int_arg;
+ int result = 1;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+
+ /* longName, shortName, argInfo, argPtr, value, descrip,
+ argDesc */
+
+ { "domain-users", 'u', POPT_ARG_NONE, 0, 'u', "Lists all domain users", "domain"},
+ { "domain-groups", 'g', POPT_ARG_NONE, 0, 'g', "Lists all domain groups", "domain" },
+ { "WINS-by-name", 'N', POPT_ARG_STRING, &string_arg, 'N', "Converts NetBIOS name to IP", "NETBIOS-NAME" },
+ { "WINS-by-ip", 'I', POPT_ARG_STRING, &string_arg, 'I', "Converts IP address to NetBIOS name", "IP" },
+ { "name-to-sid", 'n', POPT_ARG_STRING, &string_arg, 'n', "Converts name to sid", "NAME" },
+ { "sid-to-name", 's', POPT_ARG_STRING, &string_arg, 's', "Converts sid to name", "SID" },
+ { "uid-to-sid", 'U', POPT_ARG_INT, &int_arg, 'U', "Converts uid to sid" , "UID" },
+ { "gid-to-sid", 'G', POPT_ARG_INT, &int_arg, 'G', "Converts gid to sid", "GID" },
+ { "sid-to-uid", 'S', POPT_ARG_STRING, &string_arg, 'S', "Converts sid to uid", "SID" },
+ { "sid-to-gid", 'Y', POPT_ARG_STRING, &string_arg, 'Y', "Converts sid to gid", "SID" },
+ { "check-secret", 't', POPT_ARG_NONE, 0, 't', "Check shared secret" },
+ { "trusted-domains", 'm', POPT_ARG_NONE, 0, 'm', "List trusted domains" },
+ { "all-domains", 0, POPT_ARG_NONE, 0, OPT_LIST_ALL_DOMAINS, "List all domains (trusted and own domain)" },
+ { "own-domain", 0, POPT_ARG_NONE, 0, OPT_LIST_OWN_DOMAIN, "List own domain" },
+ { "sequence", 0, POPT_ARG_NONE, 0, OPT_SEQUENCE, "Show sequence numbers of all domains" },
+ { "domain-info", 'D', POPT_ARG_STRING, &string_arg, 'D', "Show most of the info we have about the domain" },
+ { "user-info", 'i', POPT_ARG_STRING, &string_arg, 'i', "Get user info", "USER" },
+ { "uid-info", 0, POPT_ARG_INT, &int_arg, OPT_UID_INFO, "Get user info from uid", "UID" },
+ { "group-info", 0, POPT_ARG_STRING, &string_arg, OPT_GROUP_INFO, "Get group info", "GROUP" },
+ { "user-groups", 'r', POPT_ARG_STRING, &string_arg, 'r', "Get user groups", "USER" },
+ { "user-domgroups", 0, POPT_ARG_STRING, &string_arg,
+ OPT_USERDOMGROUPS, "Get user domain groups", "SID" },
+ { "user-sids", 0, POPT_ARG_STRING, &string_arg, OPT_USERSIDS, "Get user group sids for user SID", "SID" },
+ { "authenticate", 'a', POPT_ARG_STRING, &string_arg, 'a', "authenticate user", "user%password" },
+ { "getdcname", 0, POPT_ARG_STRING, &string_arg, OPT_GETDCNAME,
+ "Get a DC name for a foreign domain", "domainname" },
+ { "ping", 'p', POPT_ARG_NONE, 0, 'p', "Ping winbindd to see if it is alive" },
+ { "domain", 0, POPT_ARG_STRING, &opt_domain_name, OPT_DOMAIN_NAME, "Define to the domain to restrict operation", "domain" },
+#ifdef HAVE_KRB5
+ { "krb5auth", 'K', POPT_ARG_STRING, &string_arg, 'K', "authenticate user using Kerberos", "user%password" },
+ /* destroys wbinfo --help output */
+ /* "user%password,DOM\\user%password,user@EXAMPLE.COM,EXAMPLE.COM\\user%password" }, */
+#endif
+ { "separator", 0, POPT_ARG_NONE, 0, OPT_SEPARATOR, "Get the active winbind separator", NULL },
+ POPT_COMMON_VERSION
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+
+ /* Parse options */
+
+ pc = poptGetContext("wbinfo", argc, (const char **)argv, long_options, 0);
+
+ /* Parse command line options */
+
+ if (argc == 1) {
+ poptPrintHelp(pc, stderr, 0);
+ return 1;
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ /* get the generic configuration parameters like --domain */
+ }
+
+ poptFreeContext(pc);
+
+ pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'u':
+ if (!print_domain_users(opt_domain_name)) {
+ d_fprintf(stderr, "Error looking up domain users\n");
+ goto done;
+ }
+ break;
+ case 'g':
+ if (!print_domain_groups(opt_domain_name)) {
+ d_fprintf(stderr, "Error looking up domain groups\n");
+ goto done;
+ }
+ break;
+ case 's':
+ if (!wbinfo_lookupsid(string_arg)) {
+ d_fprintf(stderr, "Could not lookup sid %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'n':
+ if (!wbinfo_lookupname(string_arg)) {
+ d_fprintf(stderr, "Could not lookup name %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'N':
+ if (!wbinfo_wins_byname(string_arg)) {
+ d_fprintf(stderr, "Could not lookup WINS by name %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'I':
+ if (!wbinfo_wins_byip(string_arg)) {
+ d_fprintf(stderr, "Could not lookup WINS by IP %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'U':
+ if (!wbinfo_uid_to_sid(int_arg)) {
+ d_fprintf(stderr, "Could not convert uid %d to sid\n", int_arg);
+ goto done;
+ }
+ break;
+ case 'G':
+ if (!wbinfo_gid_to_sid(int_arg)) {
+ d_fprintf(stderr, "Could not convert gid %d to sid\n",
+ int_arg);
+ goto done;
+ }
+ break;
+ case 'S':
+ if (!wbinfo_sid_to_uid(string_arg)) {
+ d_fprintf(stderr, "Could not convert sid %s to uid\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'Y':
+ if (!wbinfo_sid_to_gid(string_arg)) {
+ d_fprintf(stderr, "Could not convert sid %s to gid\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 't':
+ if (!wbinfo_check_secret()) {
+ d_fprintf(stderr, "Could not check secret\n");
+ goto done;
+ }
+ break;
+ case 'm':
+ if (!wbinfo_list_domains(false)) {
+ d_fprintf(stderr, "Could not list trusted domains\n");
+ goto done;
+ }
+ break;
+ case OPT_SEQUENCE:
+ if (!wbinfo_show_sequence(opt_domain_name)) {
+ d_fprintf(stderr, "Could not show sequence numbers\n");
+ goto done;
+ }
+ break;
+ case 'D':
+ if (!wbinfo_domain_info(string_arg)) {
+ d_fprintf(stderr, "Could not get domain info\n");
+ goto done;
+ }
+ break;
+ case 'i':
+ if (!wbinfo_get_userinfo(string_arg)) {
+ d_fprintf(stderr, "Could not get info for user %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_UID_INFO:
+ if ( !wbinfo_get_uidinfo(int_arg)) {
+ d_fprintf(stderr, "Could not get info for uid "
+ "%d\n", int_arg);
+ goto done;
+ }
+ break;
+ case OPT_GROUP_INFO:
+ if ( !wbinfo_get_groupinfo(string_arg)) {
+ d_fprintf(stderr, "Could not get info for "
+ "group %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'r':
+ if (!wbinfo_get_usergroups(string_arg)) {
+ d_fprintf(stderr, "Could not get groups for user %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_USERSIDS:
+ if (!wbinfo_get_usersids(string_arg)) {
+ d_fprintf(stderr, "Could not get group SIDs for user SID %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_USERDOMGROUPS:
+ if (!wbinfo_get_userdomgroups(string_arg)) {
+ d_fprintf(stderr, "Could not get user's domain groups "
+ "for user SID %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'a': {
+ bool got_error = false;
+
+ if (!wbinfo_auth(string_arg)) {
+ d_fprintf(stderr, "Could not authenticate user %s with "
+ "plaintext password\n", string_arg);
+ got_error = true;
+ }
+
+ if (!wbinfo_auth_crap(cmdline_lp_ctx, string_arg)) {
+ d_fprintf(stderr, "Could not authenticate user %s with "
+ "challenge/response\n", string_arg);
+ got_error = true;
+ }
+
+ if (got_error)
+ goto done;
+ break;
+ }
+ case 'K': {
+ uint32_t flags = WBFLAG_PAM_KRB5 |
+ WBFLAG_PAM_CACHED_LOGIN |
+ WBFLAG_PAM_FALLBACK_AFTER_KRB5 |
+ WBFLAG_PAM_INFO3_TEXT;
+
+ if (!wbinfo_auth_krb5(string_arg, "FILE", flags)) {
+ d_fprintf(stderr, "Could not authenticate user [%s] with "
+ "Kerberos (ccache: %s)\n", string_arg, "FILE");
+ goto done;
+ }
+ break;
+ }
+ case 'p':
+ if (!wbinfo_ping()) {
+ d_fprintf(stderr, "could not ping winbindd!\n");
+ goto done;
+ }
+ break;
+ case OPT_GETDCNAME:
+ if (!wbinfo_getdcname(string_arg)) {
+ goto done;
+ }
+ break;
+ case OPT_SEPARATOR: {
+ const char sep = winbind_separator_int(true);
+ if ( !sep ) {
+ goto done;
+ }
+ d_printf("%c\n", sep);
+ break;
+ }
+ case OPT_LIST_ALL_DOMAINS:
+ if (!wbinfo_list_domains(true)) {
+ goto done;
+ }
+ break;
+ case OPT_LIST_OWN_DOMAIN:
+ if (!wbinfo_list_own_domain()) {
+ goto done;
+ }
+ break;
+ /* generic configuration options */
+ case OPT_DOMAIN_NAME:
+ break;
+ default:
+ d_fprintf(stderr, "Invalid option\n");
+ poptPrintHelp(pc, stderr, 0);
+ goto done;
+ }
+ }
+
+ result = 0;
+
+ /* Exit code */
+
+ done:
+ poptFreeContext(pc);
+ return result;
+}
diff --git a/nsswitch/winbind_client.h b/nsswitch/winbind_client.h
new file mode 100644
index 0000000000..b004d9c2c0
--- /dev/null
+++ b/nsswitch/winbind_client.h
@@ -0,0 +1,30 @@
+#include "winbind_nss_config.h"
+#include "winbind_struct_protocol.h"
+
+void winbindd_init_request(struct winbindd_request *req,int rq_type);
+void winbindd_free_response(struct winbindd_response *response);
+NSS_STATUS winbindd_send_request(int req_type, int need_priv,
+ struct winbindd_request *request);
+NSS_STATUS winbindd_get_response(struct winbindd_response *response);
+NSS_STATUS winbindd_request_response(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+NSS_STATUS winbindd_priv_request_response(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+int winbindd_read_reply(struct winbindd_response *response);
+
+#define winbind_env_set() \
+ (strcmp(getenv(WINBINDD_DONT_ENV)?getenv(WINBINDD_DONT_ENV):"0","1") == 0)
+
+#define winbind_off() \
+ (setenv(WINBINDD_DONT_ENV, "1", 1) == 0)
+
+#define winbind_on() \
+ (setenv(WINBINDD_DONT_ENV, "0", 1) == 0)
+
+int winbind_write_sock(void *buffer, int count, int recursing, int need_priv);
+int winbind_read_sock(void *buffer, int count);
+void winbind_close_sock(void);
+
+const char *nss_err_str(NSS_STATUS ret);
diff --git a/nsswitch/winbind_krb5_locator.c b/nsswitch/winbind_krb5_locator.c
new file mode 100644
index 0000000000..b9e35bdec5
--- /dev/null
+++ b/nsswitch/winbind_krb5_locator.c
@@ -0,0 +1,411 @@
+/*
+ Unix SMB/CIFS implementation.
+ kerberos locator plugin
+ Copyright (C) Guenther Deschner 2007-2008
+
+ 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 "nsswitch/winbind_client.h"
+#include "libwbclient/wbclient.h"
+
+#ifndef DEBUG_KRB5
+#undef DEBUG_KRB5
+#endif
+
+#if defined(HAVE_KRB5) && defined(HAVE_KRB5_LOCATE_PLUGIN_H)
+
+#include <krb5/locate_plugin.h>
+
+#ifndef KRB5_PLUGIN_NO_HANDLE
+#define KRB5_PLUGIN_NO_HANDLE KRB5_KDC_UNREACH /* Heimdal */
+#endif
+
+static const char *get_service_from_locate_service_type(enum locate_service_type svc)
+{
+ switch (svc) {
+ case locate_service_kdc:
+ case locate_service_master_kdc:
+ return "88";
+ case locate_service_kadmin:
+ case locate_service_krb524:
+ /* not supported */
+ return NULL;
+ case locate_service_kpasswd:
+ return "464";
+ default:
+ break;
+ }
+ return NULL;
+
+}
+
+#ifdef DEBUG_KRB5
+static const char *locate_service_type_name(enum locate_service_type svc)
+{
+ switch (svc) {
+ case locate_service_kdc:
+ return "locate_service_kdc";
+ case locate_service_master_kdc:
+ return "locate_service_master_kdc";
+ case locate_service_kadmin:
+ return "locate_service_kadmin";
+ case locate_service_krb524:
+ return "locate_service_krb524";
+ case locate_service_kpasswd:
+ return "locate_service_kpasswd";
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static const char *socktype_name(int socktype)
+{
+ switch (socktype) {
+ case SOCK_STREAM:
+ return "SOCK_STREAM";
+ case SOCK_DGRAM:
+ return "SOCK_DGRAM";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
+static const char *family_name(int family)
+{
+ switch (family) {
+ case AF_UNSPEC:
+ return "AF_UNSPEC";
+ case AF_INET:
+ return "AF_INET";
+#if defined(HAVE_IPV6)
+ case AF_INET6:
+ return "AF_INET6";
+#endif
+ default:
+ break;
+ }
+ return "unknown";
+}
+#endif
+
+/**
+ * Check input parameters, return KRB5_PLUGIN_NO_HANDLE for unsupported ones
+ *
+ * @param svc
+ * @param realm string
+ * @param socktype integer
+ * @param family integer
+ *
+ * @return integer.
+ */
+
+static int smb_krb5_locator_lookup_sanity_check(enum locate_service_type svc,
+ const char *realm,
+ int socktype,
+ int family)
+{
+ if (!realm || strlen(realm) == 0) {
+ return EINVAL;
+ }
+
+ switch (svc) {
+ case locate_service_kdc:
+ case locate_service_master_kdc:
+ case locate_service_kpasswd:
+ break;
+ case locate_service_kadmin:
+ case locate_service_krb524:
+ return KRB5_PLUGIN_NO_HANDLE;
+ default:
+ return EINVAL;
+ }
+
+ switch (family) {
+ case AF_UNSPEC:
+ case AF_INET:
+ break;
+#if defined(HAVE_IPV6)
+ case AF_INET6:
+ break;
+#endif
+ default:
+ return EINVAL;
+ }
+
+ switch (socktype) {
+ case SOCK_STREAM:
+ case SOCK_DGRAM:
+ case 0: /* Heimdal uses that */
+ break;
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * Try to get addrinfo for a given host and call the krb5 callback
+ *
+ * @param name string
+ * @param service string
+ * @param in struct addrinfo hint
+ * @param cbfunc krb5 callback function
+ * @param cbdata void pointer cbdata
+ *
+ * @return krb5_error_code.
+ */
+
+static krb5_error_code smb_krb5_locator_call_cbfunc(const char *name,
+ const char *service,
+ struct addrinfo *in,
+ int (*cbfunc)(void *, int, struct sockaddr *),
+ void *cbdata)
+{
+ struct addrinfo *out = NULL;
+ int ret;
+ int count = 3;
+
+ while (count) {
+
+ ret = getaddrinfo(name, service, in, &out);
+ if (ret == 0) {
+ break;
+ }
+
+ if (ret == EAI_AGAIN) {
+ count--;
+ continue;
+ }
+
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "getaddrinfo failed: %s (%d)\n",
+ (unsigned int)getpid(), gai_strerror(ret), ret);
+#endif
+
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+
+ ret = cbfunc(cbdata, out->ai_socktype, out->ai_addr);
+#ifdef DEBUG_KRB5
+ if (ret) {
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "failed to call callback: %s (%d)\n",
+ (unsigned int)getpid(), error_message(ret), ret);
+ }
+#endif
+
+ freeaddrinfo(out);
+ return ret;
+}
+
+/**
+ * PUBLIC INTERFACE: locate init
+ *
+ * @param context krb5_context
+ * @param privata_data pointer to private data pointer
+ *
+ * @return krb5_error_code.
+ */
+
+static krb5_error_code smb_krb5_locator_init(krb5_context context,
+ void **private_data)
+{
+ return 0;
+}
+
+/**
+ * PUBLIC INTERFACE: close locate
+ *
+ * @param private_data pointer to private data
+ *
+ * @return void.
+ */
+
+static void smb_krb5_locator_close(void *private_data)
+{
+ return;
+}
+
+
+static bool ask_winbind(const char *realm, char **dcname)
+{
+ wbcErr wbc_status;
+ const char *dc = NULL;
+ struct wbcDomainControllerInfoEx *dc_info = NULL;
+ uint32_t flags;
+
+ flags = WBC_LOOKUP_DC_KDC_REQUIRED |
+ WBC_LOOKUP_DC_IS_DNS_NAME |
+ WBC_LOOKUP_DC_RETURN_DNS_NAME |
+ WBC_LOOKUP_DC_IP_REQUIRED;
+
+ wbc_status = wbcLookupDomainControllerEx(realm, NULL, NULL, flags, &dc_info);
+
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: failed with: %s\n",
+ (unsigned int)getpid(), wbcErrorString(wbc_status));
+#endif
+ return false;
+ }
+
+ if (dc_info->dc_address) {
+ dc = dc_info->dc_address;
+ if (dc[0] == '\\') dc++;
+ if (dc[0] == '\\') dc++;
+ }
+
+ if (!dc && dc_info->dc_unc) {
+ dc = dc_info->dc_unc;
+ if (dc[0] == '\\') dc++;
+ if (dc[0] == '\\') dc++;
+ }
+
+ if (!dc) {
+ wbcFreeMemory(dc_info);
+ return false;
+ }
+
+ *dcname = strdup(dc);
+ if (!*dcname) {
+ wbcFreeMemory(dc_info);
+ return false;
+ }
+
+ wbcFreeMemory(dc_info);
+ return true;
+}
+
+/**
+ * PUBLIC INTERFACE: locate lookup
+ *
+ * @param private_data pointer to private data
+ * @param svc enum locate_service_type.
+ * @param realm string
+ * @param socktype integer
+ * @param family integer
+ * @param cbfunc callback function to send back entries
+ * @param cbdata void pointer to cbdata
+ *
+ * @return krb5_error_code.
+ */
+
+static krb5_error_code smb_krb5_locator_lookup(void *private_data,
+ enum locate_service_type svc,
+ const char *realm,
+ int socktype,
+ int family,
+ int (*cbfunc)(void *, int, struct sockaddr *),
+ void *cbdata)
+{
+ krb5_error_code ret;
+ struct addrinfo aihints;
+ char *kdc_name = NULL;
+ const char *service = get_service_from_locate_service_type(svc);
+
+ ZERO_STRUCT(aihints);
+
+#ifdef DEBUG_KRB5
+ fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: called for '%s' "
+ "svc: '%s' (%d) "
+ "socktype: '%s' (%d), family: '%s' (%d)\n",
+ (unsigned int)getpid(), realm,
+ locate_service_type_name(svc), svc,
+ socktype_name(socktype), socktype,
+ family_name(family), family);
+#endif
+ ret = smb_krb5_locator_lookup_sanity_check(svc, realm, socktype,
+ family);
+ if (ret) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "returning ret: %s (%d)\n",
+ (unsigned int)getpid(), error_message(ret), ret);
+#endif
+ return ret;
+ }
+
+ if (!winbind_env_set()) {
+ if (!ask_winbind(realm, &kdc_name)) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "failed to query winbindd\n",
+ (unsigned int)getpid());
+#endif
+ goto failed;
+ }
+ } else {
+ const char *env = NULL;
+ char *var = NULL;
+ if (asprintf(&var, "%s_%s",
+ WINBINDD_LOCATOR_KDC_ADDRESS, realm) == -1) {
+ goto failed;
+ }
+ env = getenv(var);
+ if (!env) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "failed to get kdc from env %s\n",
+ (unsigned int)getpid(), var);
+#endif
+ free(var);
+ goto failed;
+ }
+ free(var);
+
+ kdc_name = strdup(env);
+ if (!kdc_name) {
+ goto failed;
+ }
+ }
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "got '%s' for '%s' from winbindd\n", (unsigned int)getpid(),
+ kdc_name, realm);
+#endif
+
+ aihints.ai_family = family;
+ aihints.ai_socktype = socktype;
+
+ ret = smb_krb5_locator_call_cbfunc(kdc_name,
+ service,
+ &aihints,
+ cbfunc, cbdata);
+ SAFE_FREE(kdc_name);
+
+ return ret;
+
+ failed:
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+#ifdef HEIMDAL_KRB5_LOCATE_PLUGIN_H
+#define SMB_KRB5_LOCATOR_SYMBOL_NAME resolve /* Heimdal */
+#else
+#define SMB_KRB5_LOCATOR_SYMBOL_NAME service_locator /* MIT */
+#endif
+
+const krb5plugin_service_locate_ftable SMB_KRB5_LOCATOR_SYMBOL_NAME = {
+ 0, /* version */
+ smb_krb5_locator_init,
+ smb_krb5_locator_close,
+ smb_krb5_locator_lookup,
+};
+
+#endif
diff --git a/nsswitch/winbind_nss.h b/nsswitch/winbind_nss.h
new file mode 100644
index 0000000000..05e07ad5d3
--- /dev/null
+++ b/nsswitch/winbind_nss.h
@@ -0,0 +1,76 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ A common place to work out how to define NSS_STATUS on various
+ platforms.
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _NSSWITCH_NSS_H
+#define _NSSWITCH_NSS_H
+
+#ifdef HAVE_NSS_COMMON_H
+
+/*
+ * Sun Solaris
+ */
+
+#include "nsswitch/winbind_nss_solaris.h"
+
+#elif HAVE_NSS_H
+
+/*
+ * Linux (glibc)
+ */
+
+#include "nsswitch/winbind_nss_linux.h"
+
+#elif HAVE_NS_API_H
+
+/*
+ * SGI IRIX
+ */
+
+#include "nsswitch/winbind_nss_irix.h"
+
+#elif defined(HPUX) && defined(HAVE_NSSWITCH_H)
+
+/* HP-UX 11 */
+
+#include "nsswitch/winbind_nss_hpux.h"
+
+#elif defined(__NetBSD__) && defined(HAVE_GETPWENT_R)
+
+/*
+ * NetBSD 3 and newer
+ */
+
+#include "nsswitch/winbind_nss_netbsd.h"
+
+#else /* Nothing's defined. Neither gnu nor netbsd nor sun nor hp */
+
+typedef enum
+{
+ NSS_STATUS_SUCCESS=0,
+ NSS_STATUS_NOTFOUND=1,
+ NSS_STATUS_UNAVAIL=2,
+ NSS_STATUS_TRYAGAIN=3
+} NSS_STATUS;
+
+#endif
+
+#endif /* _NSSWITCH_NSS_H */
diff --git a/nsswitch/winbind_nss_aix.c b/nsswitch/winbind_nss_aix.c
new file mode 100644
index 0000000000..17578cf350
--- /dev/null
+++ b/nsswitch/winbind_nss_aix.c
@@ -0,0 +1,1076 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ AIX loadable authentication module, providing identification and
+ authentication routines against Samba winbind/Windows NT Domain
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Steve Roylance 2003
+ Copyright (C) Andrew Tridgell 2003-2004
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+
+ To install this module copy nsswitch/WINBIND to /usr/lib/security and add
+ "WINBIND" in /usr/lib/security/methods.cfg and /etc/security/user
+
+ Note that this module also provides authentication and password
+ changing routines, so you do not need to install the winbind PAM
+ module.
+
+ see
+ http://publib16.boulder.ibm.com/doc_link/en_US/a_doc_lib/aixprggd/kernextc/sec_load_mod.htm
+ for some information in the interface that this module implements
+
+ Many thanks to Julianne Haugh for explaining some of the finer
+ details of this interface.
+
+ To debug this module use uess_test.c (which you can get from tridge)
+ or set "options=debug" in /usr/lib/security/methods.cfg
+
+*/
+
+#include "winbind_client.h"
+#include <usersec.h>
+
+/* enable this to log which entry points have not been
+ completed yet */
+#define LOG_UNIMPLEMENTED_CALLS 0
+
+
+#define WB_AIX_ENCODED '_'
+
+static int debug_enabled;
+
+
+static void logit(const char *format, ...)
+{
+ va_list ap;
+ FILE *f;
+ if (!debug_enabled) {
+ return;
+ }
+ f = fopen("/tmp/WINBIND_DEBUG.log", "a");
+ if (!f) return;
+ va_start(ap, format);
+ vfprintf(f, format, ap);
+ va_end(ap);
+ fclose(f);
+}
+
+
+#define HANDLE_ERRORS(ret) do { \
+ if ((ret) == NSS_STATUS_NOTFOUND) { \
+ errno = ENOENT; \
+ return NULL; \
+ } else if ((ret) != NSS_STATUS_SUCCESS) { \
+ errno = EIO; \
+ return NULL; \
+ } \
+} while (0)
+
+#define STRCPY_RET(dest, src) \
+do { \
+ if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return -1; } \
+ strcpy(dest, src); \
+} while (0)
+
+#define STRCPY_RETNULL(dest, src) \
+do { \
+ if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return NULL; } \
+ strcpy(dest, src); \
+} while (0)
+
+
+/* free a passwd structure */
+static void free_pwd(struct passwd *pwd)
+{
+ free(pwd->pw_name);
+ free(pwd->pw_passwd);
+ free(pwd->pw_gecos);
+ free(pwd->pw_dir);
+ free(pwd->pw_shell);
+ free(pwd);
+}
+
+/* free a group structure */
+static void free_grp(struct group *grp)
+{
+ int i;
+
+ free(grp->gr_name);
+ free(grp->gr_passwd);
+
+ if (!grp->gr_mem) {
+ free(grp);
+ return;
+ }
+
+ for (i=0; grp->gr_mem[i]; i++) {
+ free(grp->gr_mem[i]);
+ }
+
+ free(grp->gr_mem);
+ free(grp);
+}
+
+
+/* replace commas with nulls, and null terminate */
+static void replace_commas(char *s)
+{
+ char *p, *p0=s;
+ for (p=strchr(s, ','); p; p = strchr(p+1, ',')) {
+ *p=0;
+ p0 = p+1;
+ }
+
+ p0[strlen(p0)+1] = 0;
+}
+
+
+/* the decode_*() routines are used to cope with the fact that AIX 5.2
+ and below cannot handle user or group names longer than 8
+ characters in some interfaces. We use the normalize method to
+ provide a mapping to a username that fits, by using the form '_UID'
+ or '_GID'.
+
+ this only works if you can guarantee that the WB_AIX_ENCODED char
+ is not used as the first char of any other username
+*/
+static unsigned decode_id(const char *name)
+{
+ unsigned id;
+ sscanf(name+1, "%u", &id);
+ return id;
+}
+
+static struct passwd *wb_aix_getpwuid(uid_t uid);
+
+static char *decode_user(const char *name)
+{
+ struct passwd *pwd;
+ unsigned id;
+ char *ret;
+
+ sscanf(name+1, "%u", &id);
+ pwd = wb_aix_getpwuid(id);
+ if (!pwd) {
+ return NULL;
+ }
+ ret = strdup(pwd->pw_name);
+
+ free_pwd(pwd);
+
+ logit("decoded '%s' -> '%s'\n", name, ret);
+
+ return ret;
+}
+
+
+/*
+ fill a struct passwd from a winbindd_pw struct, allocating as a single block
+*/
+static struct passwd *fill_pwent(struct winbindd_pw *pw)
+{
+ struct passwd *result;
+
+ result = calloc(1, sizeof(struct passwd));
+ if (!result) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ result->pw_uid = pw->pw_uid;
+ result->pw_gid = pw->pw_gid;
+ result->pw_name = strdup(pw->pw_name);
+ result->pw_passwd = strdup(pw->pw_passwd);
+ result->pw_gecos = strdup(pw->pw_gecos);
+ result->pw_dir = strdup(pw->pw_dir);
+ result->pw_shell = strdup(pw->pw_shell);
+
+ return result;
+}
+
+
+/*
+ fill a struct group from a winbindd_pw struct, allocating as a single block
+*/
+static struct group *fill_grent(struct winbindd_gr *gr, char *gr_mem)
+{
+ int i;
+ struct group *result;
+ char *p, *name;
+
+ result = calloc(1, sizeof(struct group));
+ if (!result) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ result->gr_gid = gr->gr_gid;
+
+ result->gr_name = strdup(gr->gr_name);
+ result->gr_passwd = strdup(gr->gr_passwd);
+
+ /* Group membership */
+ if ((gr->num_gr_mem < 0) || !gr_mem) {
+ gr->num_gr_mem = 0;
+ }
+
+ if (gr->num_gr_mem == 0) {
+ /* Group is empty */
+ return result;
+ }
+
+ result->gr_mem = (char **)malloc(sizeof(char *) * (gr->num_gr_mem+1));
+ if (!result->gr_mem) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* Start looking at extra data */
+ i=0;
+ for (name = strtok_r(gr_mem, ",", &p);
+ name;
+ name = strtok_r(NULL, ",", &p)) {
+ if (i == gr->num_gr_mem) {
+ break;
+ }
+ result->gr_mem[i] = strdup(name);
+ i++;
+ }
+
+ /* Terminate list */
+ result->gr_mem[i] = NULL;
+
+ return result;
+}
+
+
+
+/* take a group id and return a filled struct group */
+static struct group *wb_aix_getgrgid(gid_t gid)
+{
+ struct winbindd_response response;
+ struct winbindd_request request;
+ struct group *grp;
+ NSS_STATUS ret;
+
+ logit("getgrgid %d\n", gid);
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ request.data.gid = gid;
+
+ ret = winbindd_request_response(WINBINDD_GETGRGID, &request, &response);
+
+ logit("getgrgid ret=%d\n", ret);
+
+ HANDLE_ERRORS(ret);
+
+ grp = fill_grent(&response.data.gr, response.extra_data.data);
+
+ winbindd_free_response(&response);
+
+ return grp;
+}
+
+/* take a group name and return a filled struct group */
+static struct group *wb_aix_getgrnam(const char *name)
+{
+ struct winbindd_response response;
+ struct winbindd_request request;
+ NSS_STATUS ret;
+ struct group *grp;
+
+ if (*name == WB_AIX_ENCODED) {
+ return wb_aix_getgrgid(decode_id(name));
+ }
+
+ logit("getgrnam '%s'\n", name);
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ STRCPY_RETNULL(request.data.groupname, name);
+
+ ret = winbindd_request_response(WINBINDD_GETGRNAM, &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ grp = fill_grent(&response.data.gr, response.extra_data.data);
+
+ winbindd_free_response(&response);
+
+ return grp;
+}
+
+
+/* this call doesn't have to fill in the gr_mem, but we do anyway
+ for simplicity */
+static struct group *wb_aix_getgracct(void *id, int type)
+{
+ if (type == 1) {
+ return wb_aix_getgrnam((char *)id);
+ }
+ if (type == 0) {
+ return wb_aix_getgrgid(*(int *)id);
+ }
+ errno = EINVAL;
+ return NULL;
+}
+
+
+/* take a username and return a string containing a comma-separated
+ list of group id numbers to which the user belongs */
+static char *wb_aix_getgrset(char *user)
+{
+ struct winbindd_response response;
+ struct winbindd_request request;
+ NSS_STATUS ret;
+ int i, idx;
+ char *tmpbuf;
+ int num_gids;
+ gid_t *gid_list;
+ char *r_user = user;
+
+ if (*user == WB_AIX_ENCODED) {
+ r_user = decode_user(r_user);
+ if (!r_user) {
+ errno = ENOENT;
+ return NULL;
+ }
+ }
+
+ logit("getgrset '%s'\n", r_user);
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ STRCPY_RETNULL(request.data.username, r_user);
+
+ if (*user == WB_AIX_ENCODED) {
+ free(r_user);
+ }
+
+ ret = winbindd_request_response(WINBINDD_GETGROUPS, &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ num_gids = response.data.num_entries;
+ gid_list = (gid_t *)response.extra_data.data;
+
+ /* allocate a space large enough to contruct the string */
+ tmpbuf = malloc(num_gids*12);
+ if (!tmpbuf) {
+ return NULL;
+ }
+
+ for (idx=i=0; i < num_gids-1; i++) {
+ idx += sprintf(tmpbuf+idx, "%u,", gid_list[i]);
+ }
+ idx += sprintf(tmpbuf+idx, "%u", gid_list[i]);
+
+ winbindd_free_response(&response);
+
+ return tmpbuf;
+}
+
+
+/* take a uid and return a filled struct passwd */
+static struct passwd *wb_aix_getpwuid(uid_t uid)
+{
+ struct winbindd_response response;
+ struct winbindd_request request;
+ NSS_STATUS ret;
+ struct passwd *pwd;
+
+ logit("getpwuid '%d'\n", uid);
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ request.data.uid = uid;
+
+ ret = winbindd_request_response(WINBINDD_GETPWUID, &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ pwd = fill_pwent(&response.data.pw);
+
+ winbindd_free_response(&response);
+
+ logit("getpwuid gave ptr %p\n", pwd);
+
+ return pwd;
+}
+
+
+/* take a username and return a filled struct passwd */
+static struct passwd *wb_aix_getpwnam(const char *name)
+{
+ struct winbindd_response response;
+ struct winbindd_request request;
+ NSS_STATUS ret;
+ struct passwd *pwd;
+
+ if (*name == WB_AIX_ENCODED) {
+ return wb_aix_getpwuid(decode_id(name));
+ }
+
+ logit("getpwnam '%s'\n", name);
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ STRCPY_RETNULL(request.data.username, name);
+
+ ret = winbindd_request_response(WINBINDD_GETPWNAM, &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ pwd = fill_pwent(&response.data.pw);
+
+ winbindd_free_response(&response);
+
+ logit("getpwnam gave ptr %p\n", pwd);
+
+ return pwd;
+}
+
+/*
+ list users
+*/
+static int wb_aix_lsuser(char *attributes[], attrval_t results[], int size)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int len;
+ char *s;
+
+ if (size != 1 || strcmp(attributes[0], S_USERS) != 0) {
+ logit("invalid lsuser op\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ ret = winbindd_request_response(WINBINDD_LIST_USERS, &request, &response);
+ if (ret != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = strlen(response.extra_data.data);
+
+ s = malloc(len+2);
+ if (!s) {
+ winbindd_free_response(&response);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ memcpy(s, response.extra_data.data, len+1);
+
+ replace_commas(s);
+
+ results[0].attr_un.au_char = s;
+ results[0].attr_flag = 0;
+
+ winbindd_free_response(&response);
+
+ return 0;
+}
+
+
+/*
+ list groups
+*/
+static int wb_aix_lsgroup(char *attributes[], attrval_t results[], int size)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int len;
+ char *s;
+
+ if (size != 1 || strcmp(attributes[0], S_GROUPS) != 0) {
+ logit("invalid lsgroup op\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ ret = winbindd_request_response(WINBINDD_LIST_GROUPS, &request, &response);
+ if (ret != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = strlen(response.extra_data.data);
+
+ s = malloc(len+2);
+ if (!s) {
+ winbindd_free_response(&response);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ memcpy(s, response.extra_data.data, len+1);
+
+ replace_commas(s);
+
+ results[0].attr_un.au_char = s;
+ results[0].attr_flag = 0;
+
+ winbindd_free_response(&response);
+
+ return 0;
+}
+
+
+static attrval_t pwd_to_group(struct passwd *pwd)
+{
+ attrval_t r;
+ struct group *grp = wb_aix_getgrgid(pwd->pw_gid);
+
+ if (!grp) {
+ r.attr_flag = EINVAL;
+ } else {
+ r.attr_flag = 0;
+ r.attr_un.au_char = strdup(grp->gr_name);
+ free_grp(grp);
+ }
+
+ return r;
+}
+
+static attrval_t pwd_to_groupsids(struct passwd *pwd)
+{
+ attrval_t r;
+ char *s, *p;
+
+ if ( (s = wb_aix_getgrset(pwd->pw_name)) == NULL ) {
+ r.attr_flag = EINVAL;
+ return r;
+ }
+
+ if ( (p = malloc(strlen(s)+2)) == NULL ) {
+ r.attr_flag = ENOMEM;
+ return r;
+ }
+
+ strcpy(p, s);
+ replace_commas(p);
+ free(s);
+
+ r.attr_un.au_char = p;
+
+ return r;
+}
+
+static attrval_t pwd_to_sid(struct passwd *pwd)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ attrval_t r;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.uid = pwd->pw_uid;
+
+ if (winbindd_request_response(WINBINDD_UID_TO_SID, &request, &response) !=
+ NSS_STATUS_SUCCESS) {
+ r.attr_flag = ENOENT;
+ } else {
+ r.attr_flag = 0;
+ r.attr_un.au_char = strdup(response.data.sid.sid);
+ }
+
+ return r;
+}
+
+static int wb_aix_user_attrib(const char *key, char *attributes[],
+ attrval_t results[], int size)
+{
+ struct passwd *pwd;
+ int i;
+
+ pwd = wb_aix_getpwnam(key);
+ if (!pwd) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ for (i=0;i<size;i++) {
+ results[i].attr_flag = 0;
+
+ if (strcmp(attributes[i], S_ID) == 0) {
+ results[i].attr_un.au_int = pwd->pw_uid;
+#ifdef _AIXVERSION_530
+ } else if (strcmp(attributes[i], S_PGID) == 0) {
+ results[i].attr_un.au_int = pwd->pw_gid;
+#endif
+ } else if (strcmp(attributes[i], S_PWD) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_passwd);
+ } else if (strcmp(attributes[i], S_HOME) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_dir);
+ } else if (strcmp(attributes[i], S_SHELL) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_shell);
+ } else if (strcmp(attributes[i], S_REGISTRY) == 0) {
+ results[i].attr_un.au_char = strdup("WINBIND");
+ } else if (strcmp(attributes[i], S_GECOS) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_gecos);
+ } else if (strcmp(attributes[i], S_PGRP) == 0) {
+ results[i] = pwd_to_group(pwd);
+ } else if (strcmp(attributes[i], S_GROUPS) == 0) {
+ results[i] = pwd_to_groupsids(pwd);
+ } else if (strcmp(attributes[i], "SID") == 0) {
+ results[i] = pwd_to_sid(pwd);
+ } else {
+ logit("Unknown user attribute '%s'\n", attributes[i]);
+ results[i].attr_flag = EINVAL;
+ }
+ }
+
+ free_pwd(pwd);
+
+ return 0;
+}
+
+static int wb_aix_group_attrib(const char *key, char *attributes[],
+ attrval_t results[], int size)
+{
+ struct group *grp;
+ int i;
+
+ grp = wb_aix_getgrnam(key);
+ if (!grp) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ for (i=0;i<size;i++) {
+ results[i].attr_flag = 0;
+
+ if (strcmp(attributes[i], S_PWD) == 0) {
+ results[i].attr_un.au_char = strdup(grp->gr_passwd);
+ } else if (strcmp(attributes[i], S_ID) == 0) {
+ results[i].attr_un.au_int = grp->gr_gid;
+ } else {
+ logit("Unknown group attribute '%s'\n", attributes[i]);
+ results[i].attr_flag = EINVAL;
+ }
+ }
+
+ free_grp(grp);
+
+ return 0;
+}
+
+
+/*
+ called for user/group enumerations
+*/
+static int wb_aix_getentry(char *key, char *table, char *attributes[],
+ attrval_t results[], int size)
+{
+ logit("Got getentry with key='%s' table='%s' size=%d attributes[0]='%s'\n",
+ key, table, size, attributes[0]);
+
+ if (strcmp(key, "ALL") == 0 &&
+ strcmp(table, "user") == 0) {
+ return wb_aix_lsuser(attributes, results, size);
+ }
+
+ if (strcmp(key, "ALL") == 0 &&
+ strcmp(table, "group") == 0) {
+ return wb_aix_lsgroup(attributes, results, size);
+ }
+
+ if (strcmp(table, "user") == 0) {
+ return wb_aix_user_attrib(key, attributes, results, size);
+ }
+
+ if (strcmp(table, "group") == 0) {
+ return wb_aix_group_attrib(key, attributes, results, size);
+ }
+
+ logit("Unknown getentry operation key='%s' table='%s'\n", key, table);
+
+ errno = ENOSYS;
+ return -1;
+}
+
+
+
+/*
+ called to start the backend
+*/
+static void *wb_aix_open(const char *name, const char *domain, int mode, char *options)
+{
+ if (strstr(options, "debug")) {
+ debug_enabled = 1;
+ }
+ logit("open name='%s' mode=%d domain='%s' options='%s'\n", name, domain,
+ mode, options);
+ return NULL;
+}
+
+static void wb_aix_close(void *token)
+{
+ logit("close\n");
+ return;
+}
+
+#ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
+/*
+ return a list of additional attributes supported by the backend
+*/
+static attrlist_t **wb_aix_attrlist(void)
+{
+ /* pretty confusing but we are allocating the array of pointers
+ and the structures we'll be pointing to all at once. So
+ you need N+1 pointers and N structures. */
+
+ attrlist_t **ret = NULL;
+ attrlist_t *offset = NULL;
+ int i;
+ int n;
+ size_t size;
+
+ struct attr_types {
+ const char *name;
+ int flags;
+ int type;
+ } attr_list[] = {
+ /* user attributes */
+ {S_ID, AL_USERATTR, SEC_INT},
+ {S_PGRP, AL_USERATTR, SEC_CHAR},
+ {S_HOME, AL_USERATTR, SEC_CHAR},
+ {S_SHELL, AL_USERATTR, SEC_CHAR},
+#ifdef _AIXVERSION_530
+ {S_PGID, AL_USERATTR, SEC_INT},
+#endif
+ {S_GECOS, AL_USERATTR, SEC_CHAR},
+ {S_SHELL, AL_USERATTR, SEC_CHAR},
+ {S_PGRP, AL_USERATTR, SEC_CHAR},
+ {S_GROUPS, AL_USERATTR, SEC_LIST},
+ {"SID", AL_USERATTR, SEC_CHAR},
+
+ /* group attributes */
+ {S_ID, AL_GROUPATTR, SEC_INT}
+ };
+
+ logit("method attrlist called\n");
+
+ n = sizeof(attr_list) / sizeof(struct attr_types);
+ size = (n*sizeof(attrlist_t *));
+
+ if ( (ret = malloc( size )) == NULL ) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* offset to where the structures start in the buffer */
+
+ offset = (attrlist_t *)(ret + n);
+
+ /* now loop over the user_attr_list[] array and add
+ all the members */
+
+ for ( i=0; i<n; i++ ) {
+ attrlist_t *a = malloc(sizeof(attrlist_t));
+
+ if ( !a ) {
+ /* this is bad. Just bail */
+ return NULL;
+ }
+
+ a->al_name = strdup(attr_list[i].name);
+ a->al_flags = attr_list[i].flags;
+ a->al_type = attr_list[i].type;
+
+ ret[i] = a;
+ }
+ ret[n] = NULL;
+
+ return ret;
+}
+#endif
+
+
+/*
+ turn a long username into a short one. Needed to cope with the 8 char
+ username limit in AIX 5.2 and below
+*/
+static int wb_aix_normalize(char *longname, char *shortname)
+{
+ struct passwd *pwd;
+
+ logit("normalize '%s'\n", longname);
+
+ /* automatically cope with AIX 5.3 with longer usernames
+ when it comes out */
+ if (S_NAMELEN > strlen(longname)) {
+ strcpy(shortname, longname);
+ return 1;
+ }
+
+ pwd = wb_aix_getpwnam(longname);
+ if (!pwd) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ sprintf(shortname, "%c%07u", WB_AIX_ENCODED, pwd->pw_uid);
+
+ free_pwd(pwd);
+
+ return 1;
+}
+
+
+/*
+ authenticate a user
+ */
+static int wb_aix_authenticate(char *user, char *pass,
+ int *reenter, char **message)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ char *r_user = user;
+
+ logit("authenticate '%s' response='%s'\n", user, pass);
+
+ *reenter = 0;
+ *message = NULL;
+
+ /* Send off request */
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (*user == WB_AIX_ENCODED) {
+ r_user = decode_user(r_user);
+ if (!r_user) {
+ return AUTH_NOTFOUND;
+ }
+ }
+
+ STRCPY_RET(request.data.auth.user, r_user);
+ STRCPY_RET(request.data.auth.pass, pass);
+
+ if (*user == WB_AIX_ENCODED) {
+ free(r_user);
+ }
+
+ result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
+
+ winbindd_free_response(&response);
+
+ logit("auth result %d for '%s'\n", result, user);
+
+ if (result == NSS_STATUS_SUCCESS) {
+ errno = 0;
+ return AUTH_SUCCESS;
+ }
+
+ return AUTH_FAILURE;
+}
+
+
+/*
+ change a user password
+*/
+static int wb_aix_chpass(char *user, char *oldpass, char *newpass, char **message)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ char *r_user = user;
+
+ if (*user == WB_AIX_ENCODED) {
+ r_user = decode_user(r_user);
+ if (!r_user) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ logit("chpass '%s' old='%s' new='%s'\n", r_user, oldpass, newpass);
+
+ *message = NULL;
+
+ /* Send off request */
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ STRCPY_RET(request.data.chauthtok.user, r_user);
+ STRCPY_RET(request.data.chauthtok.oldpass, oldpass);
+ STRCPY_RET(request.data.chauthtok.newpass, newpass);
+
+ if (*user == WB_AIX_ENCODED) {
+ free(r_user);
+ }
+
+ result = winbindd_request_response(WINBINDD_PAM_CHAUTHTOK, &request, &response);
+
+ winbindd_free_response(&response);
+
+ if (result == NSS_STATUS_SUCCESS) {
+ errno = 0;
+ return 0;
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+/*
+ don't do any password strength testing for now
+*/
+static int wb_aix_passwdrestrictions(char *user, char *newpass, char *oldpass,
+ char **message)
+{
+ logit("passwdresrictions called for '%s'\n", user);
+ return 0;
+}
+
+
+static int wb_aix_passwdexpired(char *user, char **message)
+{
+ logit("passwdexpired '%s'\n", user);
+ /* we should check the account bits here */
+ return 0;
+}
+
+
+/*
+ we can't return a crypt() password
+*/
+static char *wb_aix_getpasswd(char *user)
+{
+ logit("getpasswd '%s'\n", user);
+ errno = ENOSYS;
+ return NULL;
+}
+
+/*
+ this is called to update things like the last login time. We don't
+ currently pass this onto the DC
+*/
+static int wb_aix_putentry(char *key, char *table, char *attributes[],
+ attrval_t values[], int size)
+{
+ logit("putentry key='%s' table='%s' attrib='%s'\n",
+ key, table, size>=1?attributes[0]:"<null>");
+ errno = ENOSYS;
+ return -1;
+}
+
+static int wb_aix_commit(char *key, char *table)
+{
+ logit("commit key='%s' table='%s'\n");
+ errno = ENOSYS;
+ return -1;
+}
+
+static int wb_aix_getgrusers(char *group, void *result, int type, int *size)
+{
+ logit("getgrusers group='%s'\n", group);
+ errno = ENOSYS;
+ return -1;
+}
+
+
+#define DECL_METHOD(x) \
+int method_ ## x(void) \
+{ \
+ logit("UNIMPLEMENTED METHOD '%s'\n", #x); \
+ errno = EINVAL; \
+ return -1; \
+}
+
+#if LOG_UNIMPLEMENTED_CALLS
+DECL_METHOD(delgroup);
+DECL_METHOD(deluser);
+DECL_METHOD(newgroup);
+DECL_METHOD(newuser);
+DECL_METHOD(putgrent);
+DECL_METHOD(putgrusers);
+DECL_METHOD(putpwent);
+DECL_METHOD(lock);
+DECL_METHOD(unlock);
+DECL_METHOD(getcred);
+DECL_METHOD(setcred);
+DECL_METHOD(deletecred);
+#endif
+
+int wb_aix_init(struct secmethod_table *methods)
+{
+ ZERO_STRUCTP(methods);
+
+#ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_VERSION
+ methods->method_version = SECMETHOD_VERSION_520;
+#endif
+
+ methods->method_getgrgid = wb_aix_getgrgid;
+ methods->method_getgrnam = wb_aix_getgrnam;
+ methods->method_getgrset = wb_aix_getgrset;
+ methods->method_getpwnam = wb_aix_getpwnam;
+ methods->method_getpwuid = wb_aix_getpwuid;
+ methods->method_getentry = wb_aix_getentry;
+ methods->method_open = wb_aix_open;
+ methods->method_close = wb_aix_close;
+ methods->method_normalize = wb_aix_normalize;
+ methods->method_passwdexpired = wb_aix_passwdexpired;
+ methods->method_putentry = wb_aix_putentry;
+ methods->method_getpasswd = wb_aix_getpasswd;
+ methods->method_authenticate = wb_aix_authenticate;
+ methods->method_commit = wb_aix_commit;
+ methods->method_chpass = wb_aix_chpass;
+ methods->method_passwdrestrictions = wb_aix_passwdrestrictions;
+ methods->method_getgracct = wb_aix_getgracct;
+ methods->method_getgrusers = wb_aix_getgrusers;
+#ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
+ methods->method_attrlist = wb_aix_attrlist;
+#endif
+
+#if LOG_UNIMPLEMENTED_CALLS
+ methods->method_delgroup = method_delgroup;
+ methods->method_deluser = method_deluser;
+ methods->method_newgroup = method_newgroup;
+ methods->method_newuser = method_newuser;
+ methods->method_putgrent = method_putgrent;
+ methods->method_putgrusers = method_putgrusers;
+ methods->method_putpwent = method_putpwent;
+ methods->method_lock = method_lock;
+ methods->method_unlock = method_unlock;
+ methods->method_getcred = method_getcred;
+ methods->method_setcred = method_setcred;
+ methods->method_deletecred = method_deletecred;
+#endif
+
+ return AUTH_SUCCESS;
+}
diff --git a/nsswitch/winbind_nss_config.h b/nsswitch/winbind_nss_config.h
new file mode 100644
index 0000000000..3e2ce68252
--- /dev/null
+++ b/nsswitch/winbind_nss_config.h
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WINBIND_NSS_CONFIG_H
+#define _WINBIND_NSS_CONFIG_H
+
+/* shutup the compiler warnings due to krb5.h on 64-bit sles9 */
+#ifdef SIZEOF_LONG
+#undef SIZEOF_LONG
+#endif
+
+/*
+ * we don't need socket wrapper
+ * nor nss wrapper here and we don't
+ * want to depend on swrap_close()
+ * so we better disable both
+ */
+#define SOCKET_WRAPPER_NOT_REPLACE
+#define NSS_WRAPPER_NOT_REPLACE
+
+/* Include header files from data in config.h file */
+
+#ifndef NO_CONFIG_H
+#include "../replace/replace.h"
+#endif
+
+#include "system/filesys.h"
+#include "system/network.h"
+#include "system/passwd.h"
+
+#include "nsswitch/winbind_nss.h"
+
+/* I'm trying really hard not to include anything from smb.h with the
+ result of some silly looking redeclaration of structures. */
+
+#ifndef FSTRING_LEN
+#define FSTRING_LEN 256
+typedef char fstring[FSTRING_LEN];
+#define fstrcpy(d,s) safe_strcpy((d),(s),sizeof(fstring)-1)
+#endif
+
+/* Some systems (SCO) treat UNIX domain sockets as FIFOs */
+
+#ifndef S_IFSOCK
+#define S_IFSOCK S_IFIFO
+#endif
+
+#ifndef S_ISSOCK
+#define S_ISSOCK(mode) ((mode & S_IFSOCK) == S_IFSOCK)
+#endif
+
+#endif
diff --git a/nsswitch/winbind_nss_freebsd.c b/nsswitch/winbind_nss_freebsd.c
new file mode 100644
index 0000000000..ac230c8a9a
--- /dev/null
+++ b/nsswitch/winbind_nss_freebsd.c
@@ -0,0 +1,79 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ AIX loadable authentication module, providing identification
+ routines against Samba winbind/Windows NT Domain
+
+ Copyright (C) Aaron Collins 2003
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "winbind_client.h"
+
+/* Make sure that the module gets registered needed by freebsd 5.1 */
+
+extern enum nss_status _nss_winbind_getgrent_r(struct group *, char *, size_t,
+ int *);
+extern enum nss_status _nss_winbind_getgrnam_r(const char *, struct group *,
+ char *, size_t, int *);
+extern enum nss_status _nss_winbind_getgrgid_r(gid_t gid, struct group *, char *,
+ size_t, int *);
+extern enum nss_status _nss_winbind_setgrent(void);
+extern enum nss_status _nss_winbind_endgrent(void);
+
+extern enum nss_status _nss_winbind_getpwent_r(struct passwd *, char *, size_t,
+ int *);
+extern enum nss_status _nss_winbind_getpwnam_r(const char *, struct passwd *,
+ char *, size_t, int *);
+extern enum nss_status _nss_winbind_getpwuid_r(gid_t gid, struct passwd *, char *,
+ size_t, int *);
+extern enum nss_status _nss_winbind_setpwent(void);
+extern enum nss_status _nss_winbind_endpwent(void);
+
+NSS_METHOD_PROTOTYPE(__nss_compat_getgrnam_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getgrgid_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getgrent_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_setgrent);
+NSS_METHOD_PROTOTYPE(__nss_compat_endgrent);
+
+NSS_METHOD_PROTOTYPE(__nss_compat_getpwnam_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getpwuid_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getpwent_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_setpwent);
+NSS_METHOD_PROTOTYPE(__nss_compat_endpwent);
+
+static ns_mtab methods[] = {
+{ NSDB_GROUP, "getgrnam_r", __nss_compat_getgrnam_r, _nss_winbind_getgrnam_r },
+{ NSDB_GROUP, "getgrgid_r", __nss_compat_getgrgid_r, _nss_winbind_getgrgid_r },
+{ NSDB_GROUP, "getgrent_r", __nss_compat_getgrent_r, _nss_winbind_getgrent_r },
+{ NSDB_GROUP, "endgrent", __nss_compat_setgrent, _nss_winbind_setgrent },
+{ NSDB_GROUP, "setgrent", __nss_compat_endgrent, _nss_winbind_endgrent },
+
+{ NSDB_PASSWD, "getpwnam_r", __nss_compat_getpwnam_r, _nss_winbind_getpwnam_r },
+{ NSDB_PASSWD, "getpwuid_r", __nss_compat_getpwuid_r, _nss_winbind_getpwuid_r },
+{ NSDB_PASSWD, "getpwent_r", __nss_compat_getpwent_r, _nss_winbind_getpwent_r },
+{ NSDB_PASSWD, "endpwent", __nss_compat_setpwent, _nss_winbind_setpwent },
+{ NSDB_PASSWD, "setpwent", __nss_compat_endpwent, _nss_winbind_endpwent },
+
+};
+
+ns_mtab *
+nss_module_register(const char *source, unsigned int *mtabsize,
+ nss_module_unregister_fn *unreg)
+{
+ *mtabsize = sizeof(methods)/sizeof(methods[0]);
+ *unreg = NULL;
+ return (methods);
+}
diff --git a/nsswitch/winbind_nss_hpux.h b/nsswitch/winbind_nss_hpux.h
new file mode 100644
index 0000000000..40a352d4d3
--- /dev/null
+++ b/nsswitch/winbind_nss_hpux.h
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Donated by HP to enable Winbindd to build on HPUX 11.x.
+ Copyright (C) Jeremy Allison 2002.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WINBIND_NSS_HPUX_H
+#define _WINBIND_NSS_HPUX_H
+
+#include <nsswitch.h>
+
+#define NSS_STATUS_SUCCESS NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN
+
+#ifdef HAVE_SYNCH_H
+#include <synch.h>
+#endif
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+typedef enum {
+ NSS_SUCCESS,
+ NSS_NOTFOUND,
+ NSS_UNAVAIL,
+ NSS_TRYAGAIN
+} nss_status_t;
+
+typedef nss_status_t NSS_STATUS;
+
+struct nss_backend;
+
+typedef nss_status_t (*nss_backend_op_t)(struct nss_backend *, void *args);
+
+struct nss_backend {
+ nss_backend_op_t *ops;
+ int n_ops;
+};
+typedef struct nss_backend nss_backend_t;
+typedef int nss_dbop_t;
+
+#include <errno.h>
+#include <netdb.h>
+#include <limits.h>
+
+#ifndef NSS_INCLUDE_UNSAFE
+#define NSS_INCLUDE_UNSAFE 1 /* Build old, MT-unsafe interfaces, */
+#endif /* NSS_INCLUDE_UNSAFE */
+
+enum nss_netgr_argn {
+ NSS_NETGR_MACHINE,
+ NSS_NETGR_USER,
+ NSS_NETGR_DOMAIN,
+ NSS_NETGR_N
+};
+
+enum nss_netgr_status {
+ NSS_NETGR_FOUND,
+ NSS_NETGR_NO,
+ NSS_NETGR_NOMEM
+};
+
+typedef unsigned nss_innetgr_argc;
+typedef char **nss_innetgr_argv;
+
+struct nss_innetgr_1arg {
+ nss_innetgr_argc argc;
+ nss_innetgr_argv argv;
+};
+
+typedef struct {
+ void *result; /* "result" parameter to getXbyY_r() */
+ char *buffer; /* "buffer" " " */
+ int buflen; /* "buflen" " " */
+} nss_XbyY_buf_t;
+
+extern nss_XbyY_buf_t *_nss_XbyY_buf_alloc(int struct_size, int buffer_size);
+extern void _nss_XbyY_buf_free(nss_XbyY_buf_t *);
+
+union nss_XbyY_key {
+ uid_t uid;
+ gid_t gid;
+ const char *name;
+ int number;
+ struct {
+ long net;
+ int type;
+ } netaddr;
+ struct {
+ const char *addr;
+ int len;
+ int type;
+ } hostaddr;
+ struct {
+ union {
+ const char *name;
+ int port;
+ } serv;
+ const char *proto;
+ } serv;
+ void *ether;
+};
+
+typedef struct nss_XbyY_args {
+ nss_XbyY_buf_t buf;
+ int stayopen;
+ /*
+ * Support for setXXXent(stayopen)
+ * Used only in hosts, protocols,
+ * networks, rpc, and services.
+ */
+ int (*str2ent)(const char *instr, int instr_len, void *ent, char *buffer, int buflen);
+ union nss_XbyY_key key;
+
+ void *returnval;
+ int erange;
+ int h_errno;
+ nss_status_t status;
+} nss_XbyY_args_t;
+
+#endif /* _WINBIND_NSS_HPUX_H */
diff --git a/nsswitch/winbind_nss_irix.c b/nsswitch/winbind_nss_irix.c
new file mode 100644
index 0000000000..66fa4b529d
--- /dev/null
+++ b/nsswitch/winbind_nss_irix.c
@@ -0,0 +1,633 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Windows NT Domain nsswitch module
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) James Peach 2006
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "winbind_client.h"
+
+#ifndef PRINTF_ATTRIBUTE
+#define PRINTF_ATTRIBUTE(m, n)
+#endif
+
+#ifndef HAVE_ASPRINTF_DECL
+/*PRINTFLIKE2 */
+int asprintf(char **,const char *, ...) PRINTF_ATTRIBUTE(2,3);
+#endif
+
+#ifdef HAVE_NS_API_H
+#undef STATIC
+#undef DYNAMIC
+#include <ns_daemon.h>
+#endif
+
+/* Maximum number of users to pass back over the unix domain socket
+ per call. This is not a static limit on the total number of users
+ or groups returned in total. */
+
+#define MAX_GETPWENT_USERS 250
+#define MAX_GETGRENT_USERS 250
+
+/* Prototypes from wb_common.c */
+
+extern int winbindd_fd;
+
+#ifdef HAVE_NS_API_H
+
+/* IRIX version */
+
+static int send_next_request(nsd_file_t *, struct winbindd_request *);
+static int do_list(int state, nsd_file_t *rq);
+
+static nsd_file_t *current_rq = NULL;
+static int current_winbind_xid = 0;
+static int next_winbind_xid = 0;
+
+typedef struct winbind_xid {
+ int xid;
+ nsd_file_t *rq;
+ struct winbindd_request *request;
+ struct winbind_xid *next;
+} winbind_xid_t;
+
+static winbind_xid_t *winbind_xids = (winbind_xid_t *)0;
+
+static int
+winbind_xid_new(int xid, nsd_file_t *rq, struct winbindd_request *request)
+{
+ winbind_xid_t *new;
+
+ nsd_logprintf(NSD_LOG_LOW,
+ "entering winbind_xid_new xid = %d rq = 0x%x, request = 0x%x\n",
+ xid, rq, request);
+ new = (winbind_xid_t *)nsd_calloc(1,sizeof(winbind_xid_t));
+ if (!new) {
+ nsd_logprintf(NSD_LOG_RESOURCE,"winbind_xid_new: failed malloc\n");
+ return NSD_ERROR;
+ }
+
+ new->xid = xid;
+ new->rq = rq;
+ new->request = request;
+ new->next = winbind_xids;
+ winbind_xids = new;
+
+ return NSD_CONTINUE;
+}
+
+/*
+** This routine will look down the xid list and return the request
+** associated with an xid. We remove the record if it is found.
+*/
+nsd_file_t *
+winbind_xid_lookup(int xid, struct winbindd_request **requestp)
+{
+ winbind_xid_t **last, *dx;
+ nsd_file_t *result=0;
+
+ for (last = &winbind_xids, dx = winbind_xids; dx && (dx->xid != xid);
+ last = &dx->next, dx = dx->next);
+ if (dx) {
+ *last = dx->next;
+ result = dx->rq;
+ *requestp = dx->request;
+ SAFE_FREE(dx);
+ }
+ nsd_logprintf(NSD_LOG_LOW,
+ "entering winbind_xid_lookup xid = %d rq = 0x%x, request = 0x%x\n",
+ xid, result, dx->request);
+
+ return result;
+}
+
+static int
+winbind_startnext_timeout(nsd_file_t **rqp, nsd_times_t *to)
+{
+ nsd_file_t *rq;
+ struct winbindd_request *request;
+
+ nsd_logprintf(NSD_LOG_MIN, "timeout (winbind startnext)\n");
+ rq = to->t_file;
+ *rqp = rq;
+ nsd_timeout_remove(rq);
+ request = to->t_clientdata;
+ return(send_next_request(rq, request));
+}
+
+static void
+dequeue_request(void)
+{
+ nsd_file_t *rq;
+ struct winbindd_request *request;
+
+ /*
+ * Check for queued requests
+ */
+ if (winbind_xids) {
+ nsd_logprintf(NSD_LOG_MIN, "timeout (winbind) unqueue xid %d\n",
+ current_winbind_xid);
+ rq = winbind_xid_lookup(current_winbind_xid++, &request);
+ /* cause a timeout on the queued request so we can send it */
+ nsd_timeout_new(rq,1,winbind_startnext_timeout,request);
+ }
+}
+
+static int
+do_request(nsd_file_t *rq, struct winbindd_request *request)
+{
+ if (winbind_xids == NULL) {
+ /*
+ * No outstanding requests.
+ * Send off the request to winbindd
+ */
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) sending request\n");
+ return(send_next_request(rq, request));
+ } else {
+ /*
+ * Just queue it up for now - previous callout or timout
+ * will start it up
+ */
+ nsd_logprintf(NSD_LOG_MIN,
+ "lookup (winbind): queue request xid = %d\n",
+ next_winbind_xid);
+ return(winbind_xid_new(next_winbind_xid++, rq, request));
+ }
+}
+
+static int
+winbind_callback(nsd_file_t **rqp, int fd)
+{
+ struct winbindd_response response;
+ nsd_file_t *rq;
+ NSS_STATUS status;
+ char * result = NULL;
+ size_t rlen;
+
+ dequeue_request();
+
+ nsd_logprintf(NSD_LOG_MIN, "entering callback (winbind)\n");
+
+ rq = current_rq;
+ *rqp = rq;
+
+ nsd_timeout_remove(rq);
+ nsd_callback_remove(fd);
+
+ ZERO_STRUCT(response);
+ status = winbindd_get_response(&response);
+
+ if (status != NSS_STATUS_SUCCESS) {
+ /* free any extra data area in response structure */
+ winbindd_free_response(&response);
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) returning not found, status = %d\n",
+ status);
+
+ switch (status) {
+ case NSS_STATUS_UNAVAIL:
+ rq->f_status = NS_UNAVAIL;
+ break;
+ case NSS_STATUS_TRYAGAIN:
+ rq->f_status = NS_TRYAGAIN;
+ break;
+ case NSS_STATUS_NOTFOUND:
+ /* FALLTHRU */
+ default:
+ rq->f_status = NS_NOTFOUND;
+ }
+
+ return NSD_NEXT;
+ }
+
+ switch ((int)rq->f_cmd_data) {
+ case WINBINDD_WINS_BYNAME:
+ case WINBINDD_WINS_BYIP:
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) WINS_BYNAME | WINS_BYIP\n");
+
+ rlen = asprintf(&result, "%s\n", response.data.winsresp);
+ if (rlen == 0 || result == NULL) {
+ return NSD_ERROR;
+ }
+
+ winbindd_free_response(&response);
+
+ nsd_logprintf(NSD_LOG_MIN, " %s\n", result);
+ nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC);
+ return NSD_OK;
+
+ case WINBINDD_GETPWUID:
+ case WINBINDD_GETPWNAM:
+ {
+ struct winbindd_pw *pw = &response.data.pw;
+
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) GETPWUID | GETPWUID\n");
+
+ rlen = asprintf(&result,"%s:%s:%d:%d:%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);
+ if (rlen == 0 || result == NULL)
+ return NSD_ERROR;
+
+ winbindd_free_response(&response);
+
+ nsd_logprintf(NSD_LOG_MIN, " %s\n", result);
+ nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC);
+ return NSD_OK;
+ }
+
+ case WINBINDD_GETGRNAM:
+ case WINBINDD_GETGRGID:
+ {
+ const struct winbindd_gr *gr = &response.data.gr;
+ const char * members;
+
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) GETGRNAM | GETGRGID\n");
+
+ if (gr->num_gr_mem && response.extra_data.data) {
+ members = response.extra_data.data;
+ } else {
+ members = "";
+ }
+
+ rlen = asprintf(&result, "%s:%s:%d:%s\n",
+ gr->gr_name, gr->gr_passwd, gr->gr_gid, members);
+ if (rlen == 0 || result == NULL)
+ return NSD_ERROR;
+
+ winbindd_free_response(&response);
+
+ nsd_logprintf(NSD_LOG_MIN, " %s\n", result);
+ nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC);
+ return NSD_OK;
+ }
+
+ case WINBINDD_SETGRENT:
+ case WINBINDD_SETPWENT:
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) SETGRENT | SETPWENT\n");
+ winbindd_free_response(&response);
+ return(do_list(1,rq));
+
+ case WINBINDD_GETGRENT:
+ case WINBINDD_GETGRLST:
+ {
+ int entries;
+
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) GETGRENT | GETGRLIST %d responses\n",
+ response.data.num_entries);
+
+ if (response.data.num_entries) {
+ const struct winbindd_gr *gr = &response.data.gr;
+ const char * members;
+ fstring grp_name;
+ int i;
+
+ gr = (struct winbindd_gr *)response.extra_data.data;
+ if (! gr ) {
+ nsd_logprintf(NSD_LOG_MIN, " no extra_data\n");
+ winbindd_free_response(&response);
+ return NSD_ERROR;
+ }
+
+ members = (char *)response.extra_data.data +
+ (response.data.num_entries * sizeof(struct winbindd_gr));
+
+ for (i = 0; i < response.data.num_entries; i++) {
+ snprintf(grp_name, sizeof(grp_name) - 1, "%s:%s:%d:",
+ gr->gr_name, gr->gr_passwd, gr->gr_gid);
+
+ nsd_append_element(rq, NS_SUCCESS, result, rlen);
+ nsd_append_result(rq, NS_SUCCESS,
+ &members[gr->gr_mem_ofs],
+ strlen(&members[gr->gr_mem_ofs]));
+
+ /* Don't log the whole list, because it might be
+ * _really_ long and we probably don't want to clobber
+ * the log with it.
+ */
+ nsd_logprintf(NSD_LOG_MIN, " %s (...)\n", grp_name);
+
+ gr++;
+ }
+ }
+
+ entries = response.data.num_entries;
+ winbindd_free_response(&response);
+ if (entries < MAX_GETPWENT_USERS)
+ return(do_list(2,rq));
+ else
+ return(do_list(1,rq));
+ }
+
+ case WINBINDD_GETPWENT:
+ {
+ int entries;
+
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) GETPWENT %d responses\n",
+ response.data.num_entries);
+
+ if (response.data.num_entries) {
+ struct winbindd_pw *pw = &response.data.pw;
+ int i;
+
+ pw = (struct winbindd_pw *)response.extra_data.data;
+ if (! pw ) {
+ nsd_logprintf(NSD_LOG_MIN, " no extra_data\n");
+ winbindd_free_response(&response);
+ return NSD_ERROR;
+ }
+ for (i = 0; i < response.data.num_entries; i++) {
+ result = NULL;
+ rlen = asprintf(&result, "%s:%s:%d:%d:%s:%s:%s",
+ pw->pw_name,
+ pw->pw_passwd,
+ pw->pw_uid,
+ pw->pw_gid,
+ pw->pw_gecos,
+ pw->pw_dir,
+ pw->pw_shell);
+
+ if (rlen != 0 && result != NULL) {
+ nsd_logprintf(NSD_LOG_MIN, " %s\n",result);
+ nsd_append_element(rq, NS_SUCCESS, result, rlen);
+ free(result);
+ }
+
+ pw++;
+ }
+ }
+
+ entries = response.data.num_entries;
+ winbindd_free_response(&response);
+ if (entries < MAX_GETPWENT_USERS)
+ return(do_list(2,rq));
+ else
+ return(do_list(1,rq));
+ }
+
+ case WINBINDD_ENDGRENT:
+ case WINBINDD_ENDPWENT:
+ nsd_logprintf(NSD_LOG_MIN, "callback (winbind) ENDGRENT | ENDPWENT\n");
+ nsd_append_element(rq, NS_SUCCESS, "\n", 1);
+ winbindd_free_response(&response);
+ return NSD_NEXT;
+
+ default:
+ winbindd_free_response(&response);
+ nsd_logprintf(NSD_LOG_MIN, "callback (winbind) invalid command %d\n", (int)rq->f_cmd_data);
+ return NSD_NEXT;
+ }
+}
+
+static int
+winbind_timeout(nsd_file_t **rqp, nsd_times_t *to)
+{
+ nsd_file_t *rq;
+
+ dequeue_request();
+
+ nsd_logprintf(NSD_LOG_MIN, "timeout (winbind)\n");
+
+ rq = to->t_file;
+ *rqp = rq;
+
+ /* Remove the callback and timeout */
+ nsd_callback_remove(winbindd_fd);
+ nsd_timeout_remove(rq);
+
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+}
+
+static int
+send_next_request(nsd_file_t *rq, struct winbindd_request *request)
+{
+ NSS_STATUS status;
+ long timeout;
+
+ switch (rq->f_index) {
+ case LOOKUP:
+ timeout = nsd_attr_fetch_long(rq->f_attrs,
+ "lookup_timeout", 10, 10);
+ break;
+ case LIST:
+ timeout = nsd_attr_fetch_long(rq->f_attrs,
+ "list_timeout", 10, 10);
+ break;
+ default:
+ nsd_logprintf(NSD_LOG_OPER,
+ "send_next_request (winbind) "
+ "invalid request type %d\n", rq->f_index);
+ rq->f_status = NS_BADREQ;
+ return NSD_NEXT;
+ }
+
+ nsd_logprintf(NSD_LOG_MIN,
+ "send_next_request (winbind) %d, timeout = %d sec\n",
+ rq->f_cmd_data, timeout);
+ status = winbindd_send_request((int)rq->f_cmd_data,0,request);
+ SAFE_FREE(request);
+
+ if (status != NSS_STATUS_SUCCESS) {
+ nsd_logprintf(NSD_LOG_MIN,
+ "send_next_request (winbind) error status = %d\n",
+ status);
+ rq->f_status = status;
+ return NSD_NEXT;
+ }
+
+ current_rq = rq;
+
+ /*
+ * Set up callback and timeouts
+ */
+ nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) fd = %d\n",
+ winbindd_fd);
+
+ nsd_callback_new(winbindd_fd, winbind_callback, NSD_READ);
+ nsd_timeout_new(rq, timeout * 1000, winbind_timeout, NULL);
+ return NSD_CONTINUE;
+}
+
+int init(void)
+{
+ nsd_logprintf(NSD_LOG_MIN, "entering init (winbind)\n");
+ return(NSD_OK);
+}
+
+int lookup(nsd_file_t *rq)
+{
+ char *map;
+ char *key;
+ struct winbindd_request *request;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering lookup (winbind)\n");
+ if (! rq)
+ return NSD_ERROR;
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
+ if (! map || ! key) {
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) table or key not defined\n");
+ rq->f_status = NS_BADREQ;
+ return NSD_ERROR;
+ }
+
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind %s)\n",map);
+
+ request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
+ if (! request) {
+ nsd_logprintf(NSD_LOG_RESOURCE,
+ "lookup (winbind): failed malloc\n");
+ return NSD_ERROR;
+ }
+
+ if (strcasecmp(map,"passwd.byuid") == 0) {
+ request->data.uid = atoi(key);
+ rq->f_cmd_data = (void *)WINBINDD_GETPWUID;
+ } else if (strcasecmp(map,"passwd.byname") == 0) {
+ strncpy(request->data.username, key,
+ sizeof(request->data.username) - 1);
+ request->data.username[sizeof(request->data.username) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_GETPWNAM;
+ } else if (strcasecmp(map,"group.byname") == 0) {
+ strncpy(request->data.groupname, key,
+ sizeof(request->data.groupname) - 1);
+ request->data.groupname[sizeof(request->data.groupname) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_GETGRNAM;
+ } else if (strcasecmp(map,"group.bygid") == 0) {
+ request->data.gid = atoi(key);
+ rq->f_cmd_data = (void *)WINBINDD_GETGRGID;
+ } else if (strcasecmp(map,"hosts.byname") == 0) {
+ strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
+ request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_WINS_BYNAME;
+ } else if (strcasecmp(map,"hosts.byaddr") == 0) {
+ strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
+ request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_WINS_BYIP;
+ } else {
+ /*
+ * Don't understand this map - just return not found
+ */
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) unknown table\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+
+ return(do_request(rq, request));
+}
+
+int list(nsd_file_t *rq)
+{
+ char *map;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering list (winbind)\n");
+ if (! rq)
+ return NSD_ERROR;
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ if (! map ) {
+ nsd_logprintf(NSD_LOG_MIN, "list (winbind) table not defined\n");
+ rq->f_status = NS_BADREQ;
+ return NSD_ERROR;
+ }
+
+ nsd_logprintf(NSD_LOG_MIN, "list (winbind %s)\n",map);
+
+ return (do_list(0,rq));
+}
+
+static int
+do_list(int state, nsd_file_t *rq)
+{
+ char *map;
+ struct winbindd_request *request;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering do_list (winbind) state = %d\n",state);
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
+ if (! request) {
+ nsd_logprintf(NSD_LOG_RESOURCE,
+ "do_list (winbind): failed malloc\n");
+ return NSD_ERROR;
+ }
+
+ if (strcasecmp(map,"passwd.byname") == 0) {
+ switch (state) {
+ case 0:
+ rq->f_cmd_data = (void *)WINBINDD_SETPWENT;
+ break;
+ case 1:
+ request->data.num_entries = MAX_GETPWENT_USERS;
+ rq->f_cmd_data = (void *)WINBINDD_GETPWENT;
+ break;
+ case 2:
+ rq->f_cmd_data = (void *)WINBINDD_ENDPWENT;
+ break;
+ default:
+ nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+ } else if (strcasecmp(map,"group.byname") == 0) {
+ switch (state) {
+ case 0:
+ rq->f_cmd_data = (void *)WINBINDD_SETGRENT;
+ break;
+ case 1:
+ request->data.num_entries = MAX_GETGRENT_USERS;
+ rq->f_cmd_data = (void *)WINBINDD_GETGRENT;
+ break;
+ case 2:
+ rq->f_cmd_data = (void *)WINBINDD_ENDGRENT;
+ break;
+ default:
+ nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+ } else {
+ /*
+ * Don't understand this map - just return not found
+ */
+ nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown table\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+
+ return(do_request(rq, request));
+}
+
+#endif /* HAVE_NS_API_H */
diff --git a/nsswitch/winbind_nss_irix.h b/nsswitch/winbind_nss_irix.h
new file mode 100644
index 0000000000..b47d23e204
--- /dev/null
+++ b/nsswitch/winbind_nss_irix.h
@@ -0,0 +1,42 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WINBIND_NSS_IRIX_H
+#define _WINBIND_NSS_IRIX_H
+
+/* following required to prevent warnings of double definition
+ * of datum from ns_api.h
+*/
+#ifdef DATUM
+#define _DATUM_DEFINED
+#endif
+
+#include <ns_api.h>
+
+typedef enum
+{
+ NSS_STATUS_SUCCESS=NS_SUCCESS,
+ NSS_STATUS_NOTFOUND=NS_NOTFOUND,
+ NSS_STATUS_UNAVAIL=NS_UNAVAIL,
+ NSS_STATUS_TRYAGAIN=NS_TRYAGAIN
+} NSS_STATUS;
+
+#endif /* _WINBIND_NSS_IRIX_H */
diff --git a/nsswitch/winbind_nss_linux.c b/nsswitch/winbind_nss_linux.c
new file mode 100644
index 0000000000..2b51a8e15d
--- /dev/null
+++ b/nsswitch/winbind_nss_linux.c
@@ -0,0 +1,1477 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Windows NT Domain nsswitch module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "winbind_client.h"
+
+#if HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+#if HAVE_PTHREAD
+static pthread_mutex_t winbind_nss_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+/* Maximum number of users to pass back over the unix domain socket
+ per call. This is not a static limit on the total number of users
+ or groups returned in total. */
+
+#define MAX_GETPWENT_USERS 250
+#define MAX_GETGRENT_USERS 250
+
+NSS_STATUS _nss_winbind_setpwent(void);
+NSS_STATUS _nss_winbind_endpwent(void);
+NSS_STATUS _nss_winbind_getpwent_r(struct passwd *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result,
+ char *buffer, size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getpwnam_r(const char *name, struct passwd *result,
+ char *buffer, size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_setgrent(void);
+NSS_STATUS _nss_winbind_endgrent(void);
+NSS_STATUS _nss_winbind_getgrent_r(struct group *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getgrlst_r(struct group *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getgrnam_r(const char *name, struct group *result,
+ char *buffer, size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getgrgid_r(gid_t gid, struct group *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
+ long int *size, gid_t **groups,
+ long int limit, int *errnop);
+NSS_STATUS _nss_winbind_getusersids(const char *user_sid, char **group_sids,
+ int *num_groups, char *buffer, size_t buf_size,
+ int *errnop);
+NSS_STATUS _nss_winbind_nametosid(const char *name, char **sid, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_sidtoname(const char *sid, char **name, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_sidtouid(const char *sid, uid_t *uid, int *errnop);
+NSS_STATUS _nss_winbind_sidtogid(const char *sid, gid_t *gid, int *errnop);
+NSS_STATUS _nss_winbind_uidtosid(uid_t uid, char **sid, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_gidtosid(gid_t gid, char **sid, char *buffer,
+ size_t buflen, int *errnop);
+
+/* Prototypes from wb_common.c */
+
+/* Allocate some space from the nss static buffer. The buffer and buflen
+ are the pointers passed in by the C library to the _nss_ntdom_*
+ functions. */
+
+static char *get_static(char **buffer, size_t *buflen, size_t len)
+{
+ char *result;
+
+ /* Error check. We return false if things aren't set up right, or
+ there isn't enough buffer space left. */
+
+ if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
+ return NULL;
+ }
+
+ /* Return an index into the static buffer */
+
+ result = *buffer;
+ *buffer += len;
+ *buflen -= len;
+
+ return result;
+}
+
+/* I've copied the strtok() replacement function next_token_Xalloc() from
+ lib/util_str.c as I really don't want to have to link in any other
+ objects if I can possibly avoid it. */
+
+static bool next_token_alloc(const char **ptr,
+ char **pp_buff,
+ const char *sep)
+{
+ char *s;
+ char *saved_s;
+ char *pbuf;
+ bool quoted;
+ size_t len=1;
+
+ *pp_buff = NULL;
+ if (!ptr) {
+ return(false);
+ }
+
+ s = (char *)*ptr;
+
+ /* default to simple separators */
+ if (!sep) {
+ sep = " \t\n\r";
+ }
+
+ /* find the first non sep char */
+ while (*s && strchr(sep,*s)) {
+ s++;
+ }
+
+ /* nothing left? */
+ if (!*s) {
+ return false;
+ }
+
+ /* When restarting we need to go from here. */
+ saved_s = s;
+
+ /* Work out the length needed. */
+ for (quoted = false; *s &&
+ (quoted || !strchr(sep,*s)); s++) {
+ if (*s == '\"') {
+ quoted = !quoted;
+ } else {
+ len++;
+ }
+ }
+
+ /* We started with len = 1 so we have space for the nul. */
+ *pp_buff = (char *)malloc(len);
+ if (!*pp_buff) {
+ return false;
+ }
+
+ /* copy over the token */
+ pbuf = *pp_buff;
+ s = saved_s;
+ for (quoted = false; *s &&
+ (quoted || !strchr(sep,*s)); s++) {
+ if ( *s == '\"' ) {
+ quoted = !quoted;
+ } else {
+ *pbuf++ = *s;
+ }
+ }
+
+ *ptr = (*s) ? s+1 : s;
+ *pbuf = 0;
+
+ return true;
+}
+
+/* Fill a pwent structure from a winbindd_response structure. We use
+ the static data passed to us by libc to put strings and stuff in.
+ Return NSS_STATUS_TRYAGAIN if we run out of memory. */
+
+static NSS_STATUS fill_pwent(struct passwd *result,
+ struct winbindd_pw *pw,
+ char **buffer, size_t *buflen)
+{
+ /* User name */
+
+ if ((result->pw_name =
+ get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->pw_name, pw->pw_name);
+
+ /* Password */
+
+ if ((result->pw_passwd =
+ get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->pw_passwd, pw->pw_passwd);
+
+ /* [ug]id */
+
+ result->pw_uid = pw->pw_uid;
+ result->pw_gid = pw->pw_gid;
+
+ /* GECOS */
+
+ if ((result->pw_gecos =
+ get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->pw_gecos, pw->pw_gecos);
+
+ /* Home directory */
+
+ if ((result->pw_dir =
+ get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->pw_dir, pw->pw_dir);
+
+ /* Logon shell */
+
+ if ((result->pw_shell =
+ get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->pw_shell, pw->pw_shell);
+
+ /* The struct passwd for Solaris has some extra fields which must
+ be initialised or nscd crashes. */
+
+#if HAVE_PASSWD_PW_COMMENT
+ result->pw_comment = "";
+#endif
+
+#if HAVE_PASSWD_PW_AGE
+ result->pw_age = "";
+#endif
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/* Fill a grent structure from a winbindd_response structure. We use
+ the static data passed to us by libc to put strings and stuff in.
+ Return NSS_STATUS_TRYAGAIN if we run out of memory. */
+
+static NSS_STATUS fill_grent(struct group *result, struct winbindd_gr *gr,
+ char *gr_mem, char **buffer, size_t *buflen)
+{
+ char *name;
+ int i;
+ char *tst;
+
+ /* Group name */
+
+ if ((result->gr_name =
+ get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->gr_name, gr->gr_name);
+
+ /* Password */
+
+ if ((result->gr_passwd =
+ get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) {
+
+ /* Out of memory */
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->gr_passwd, gr->gr_passwd);
+
+ /* gid */
+
+ result->gr_gid = gr->gr_gid;
+
+ /* Group membership */
+
+ if ((gr->num_gr_mem < 0) || !gr_mem) {
+ gr->num_gr_mem = 0;
+ }
+
+ /* this next value is a pointer to a pointer so let's align it */
+
+ /* Calculate number of extra bytes needed to align on pointer size boundry */
+ if ((i = (unsigned long)(*buffer) % sizeof(char*)) != 0)
+ i = sizeof(char*) - i;
+
+ if ((tst = get_static(buffer, buflen, ((gr->num_gr_mem + 1) *
+ sizeof(char *)+i))) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+ result->gr_mem = (char **)(tst + i);
+
+ if (gr->num_gr_mem == 0) {
+
+ /* Group is empty */
+
+ *(result->gr_mem) = NULL;
+ return NSS_STATUS_SUCCESS;
+ }
+
+ /* Start looking at extra data */
+
+ i = 0;
+
+ while(next_token_alloc((const char **)&gr_mem, &name, ",")) {
+ /* Allocate space for member */
+ if (((result->gr_mem)[i] =
+ get_static(buffer, buflen, strlen(name) + 1)) == NULL) {
+ free(name);
+ /* Out of memory */
+ return NSS_STATUS_TRYAGAIN;
+ }
+ strcpy((result->gr_mem)[i], name);
+ free(name);
+ i++;
+ }
+
+ /* Terminate list */
+
+ (result->gr_mem)[i] = NULL;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/*
+ * NSS user functions
+ */
+
+static struct winbindd_response getpwent_response;
+
+static int ndx_pw_cache; /* Current index into pwd cache */
+static int num_pw_cache; /* Current size of pwd cache */
+
+/* Rewind "file pointer" to start of ntdom password database */
+
+NSS_STATUS
+_nss_winbind_setpwent(void)
+{
+ NSS_STATUS ret;
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: setpwent\n", getpid());
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ if (num_pw_cache > 0) {
+ ndx_pw_cache = num_pw_cache = 0;
+ winbindd_free_response(&getpwent_response);
+ }
+
+ ret = winbindd_request_response(WINBINDD_SETPWENT, NULL, NULL);
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: setpwent returns %s (%d)\n", getpid(),
+ nss_err_str(ret), ret);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+ return ret;
+}
+
+/* Close ntdom password database "file pointer" */
+
+NSS_STATUS
+_nss_winbind_endpwent(void)
+{
+ NSS_STATUS ret;
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: endpwent\n", getpid());
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ if (num_pw_cache > 0) {
+ ndx_pw_cache = num_pw_cache = 0;
+ winbindd_free_response(&getpwent_response);
+ }
+
+ ret = winbindd_request_response(WINBINDD_ENDPWENT, NULL, NULL);
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: endpwent returns %s (%d)\n", getpid(),
+ nss_err_str(ret), ret);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/* Fetch the next password entry from ntdom password database */
+
+NSS_STATUS
+_nss_winbind_getpwent_r(struct passwd *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ static int called_again;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwent\n", getpid());
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ /* Return an entry from the cache if we have one, or if we are
+ called again because we exceeded our static buffer. */
+
+ if ((ndx_pw_cache < num_pw_cache) || called_again) {
+ goto return_result;
+ }
+
+ /* Else call winbindd to get a bunch of entries */
+
+ if (num_pw_cache > 0) {
+ winbindd_free_response(&getpwent_response);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(getpwent_response);
+
+ request.data.num_entries = MAX_GETPWENT_USERS;
+
+ ret = winbindd_request_response(WINBINDD_GETPWENT, &request,
+ &getpwent_response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ struct winbindd_pw *pw_cache;
+
+ /* Fill cache */
+
+ ndx_pw_cache = 0;
+ num_pw_cache = getpwent_response.data.num_entries;
+
+ /* Return a result */
+
+ return_result:
+
+ pw_cache = (struct winbindd_pw *)
+ getpwent_response.extra_data.data;
+
+ /* Check data is valid */
+
+ if (pw_cache == NULL) {
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ ret = fill_pwent(result, &pw_cache[ndx_pw_cache],
+ &buffer, &buflen);
+
+ /* Out of memory - try again */
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ called_again = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+
+ *errnop = errno = 0;
+ called_again = false;
+ ndx_pw_cache++;
+
+ /* If we've finished with this lot of results free cache */
+
+ if (ndx_pw_cache == num_pw_cache) {
+ ndx_pw_cache = num_pw_cache = 0;
+ winbindd_free_response(&getpwent_response);
+ }
+ }
+ done:
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwent returns %s (%d)\n", getpid(),
+ nss_err_str(ret), ret);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+ return ret;
+}
+
+/* Return passwd struct from uid */
+
+NSS_STATUS
+_nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwuid_r %d\n", getpid(), (unsigned int)uid);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ /* If our static buffer needs to be expanded we are called again */
+ if (!keep_response || uid != response.data.pw.pw_uid) {
+
+ /* Call for the first time */
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ request.data.uid = uid;
+
+ ret = winbindd_request_response(WINBINDD_GETPWUID, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ ret = fill_pwent(result, &response.data.pw,
+ &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+
+ keep_response = false;
+ *errnop = errno = 0;
+ }
+
+ winbindd_free_response(&response);
+
+ done:
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwuid %d returns %s (%d)\n", getpid(),
+ (unsigned int)uid, nss_err_str(ret), ret);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/* Return passwd struct from username */
+NSS_STATUS
+_nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwnam_r %s\n", getpid(), name);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ /* If our static buffer needs to be expanded we are called again */
+
+ if (!keep_response || strcmp(name,response.data.pw.pw_name) != 0) {
+
+ /* Call for the first time */
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ strncpy(request.data.username, name,
+ sizeof(request.data.username) - 1);
+ request.data.username
+ [sizeof(request.data.username) - 1] = '\0';
+
+ ret = winbindd_request_response(WINBINDD_GETPWNAM, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ ret = fill_pwent(result, &response.data.pw, &buffer,
+ &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+
+ keep_response = false;
+ *errnop = errno = 0;
+ }
+
+ winbindd_free_response(&response);
+ done:
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwnam %s returns %s (%d)\n", getpid(),
+ name, nss_err_str(ret), ret);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/*
+ * NSS group functions
+ */
+
+static struct winbindd_response getgrent_response;
+
+static int ndx_gr_cache; /* Current index into grp cache */
+static int num_gr_cache; /* Current size of grp cache */
+
+/* Rewind "file pointer" to start of ntdom group database */
+
+NSS_STATUS
+_nss_winbind_setgrent(void)
+{
+ NSS_STATUS ret;
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: setgrent\n", getpid());
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ if (num_gr_cache > 0) {
+ ndx_gr_cache = num_gr_cache = 0;
+ winbindd_free_response(&getgrent_response);
+ }
+
+ ret = winbindd_request_response(WINBINDD_SETGRENT, NULL, NULL);
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: setgrent returns %s (%d)\n", getpid(),
+ nss_err_str(ret), ret);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/* Close "file pointer" for ntdom group database */
+
+NSS_STATUS
+_nss_winbind_endgrent(void)
+{
+ NSS_STATUS ret;
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: endgrent\n", getpid());
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ if (num_gr_cache > 0) {
+ ndx_gr_cache = num_gr_cache = 0;
+ winbindd_free_response(&getgrent_response);
+ }
+
+ ret = winbindd_request_response(WINBINDD_ENDGRENT, NULL, NULL);
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: endgrent returns %s (%d)\n", getpid(),
+ nss_err_str(ret), ret);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/* Get next entry from ntdom group database */
+
+static NSS_STATUS
+winbind_getgrent(enum winbindd_cmd cmd,
+ struct group *result,
+ char *buffer, size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_request request;
+ static int called_again;
+
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrent\n", getpid());
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ /* Return an entry from the cache if we have one, or if we are
+ called again because we exceeded our static buffer. */
+
+ if ((ndx_gr_cache < num_gr_cache) || called_again) {
+ goto return_result;
+ }
+
+ /* Else call winbindd to get a bunch of entries */
+
+ if (num_gr_cache > 0) {
+ winbindd_free_response(&getgrent_response);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(getgrent_response);
+
+ request.data.num_entries = MAX_GETGRENT_USERS;
+
+ ret = winbindd_request_response(cmd, &request,
+ &getgrent_response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ struct winbindd_gr *gr_cache;
+ int mem_ofs;
+
+ /* Fill cache */
+
+ ndx_gr_cache = 0;
+ num_gr_cache = getgrent_response.data.num_entries;
+
+ /* Return a result */
+
+ return_result:
+
+ gr_cache = (struct winbindd_gr *)
+ getgrent_response.extra_data.data;
+
+ /* Check data is valid */
+
+ if (gr_cache == NULL) {
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ /* Fill group membership. The offset into the extra data
+ for the group membership is the reported offset plus the
+ size of all the winbindd_gr records returned. */
+
+ mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs +
+ num_gr_cache * sizeof(struct winbindd_gr);
+
+ ret = fill_grent(result, &gr_cache[ndx_gr_cache],
+ ((char *)getgrent_response.extra_data.data)+mem_ofs,
+ &buffer, &buflen);
+
+ /* Out of memory - try again */
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ called_again = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+
+ *errnop = 0;
+ called_again = false;
+ ndx_gr_cache++;
+
+ /* If we've finished with this lot of results free cache */
+
+ if (ndx_gr_cache == num_gr_cache) {
+ ndx_gr_cache = num_gr_cache = 0;
+ winbindd_free_response(&getgrent_response);
+ }
+ }
+ done:
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrent returns %s (%d)\n", getpid(),
+ nss_err_str(ret), ret);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+
+NSS_STATUS
+_nss_winbind_getgrent_r(struct group *result,
+ char *buffer, size_t buflen, int *errnop)
+{
+ return winbind_getgrent(WINBINDD_GETGRENT, result, buffer, buflen, errnop);
+}
+
+NSS_STATUS
+_nss_winbind_getgrlst_r(struct group *result,
+ char *buffer, size_t buflen, int *errnop)
+{
+ return winbind_getgrent(WINBINDD_GETGRLST, result, buffer, buflen, errnop);
+}
+
+/* Return group struct from group name */
+
+NSS_STATUS
+_nss_winbind_getgrnam_r(const char *name,
+ struct group *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ /* If our static buffer needs to be expanded we are called again */
+ /* Or if the stored response group name differs from the request. */
+
+ if (!keep_response || strcmp(name,response.data.gr.gr_name) != 0) {
+
+ /* Call for the first time */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.groupname, name,
+ sizeof(request.data.groupname));
+ request.data.groupname
+ [sizeof(request.data.groupname) - 1] = '\0';
+
+ ret = winbindd_request_response(WINBINDD_GETGRNAM, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ ret = fill_grent(result, &response.data.gr,
+ (char *)response.extra_data.data,
+ &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_grent(result, &response.data.gr,
+ (char *)response.extra_data.data, &buffer,
+ &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+
+ keep_response = false;
+ *errnop = 0;
+ }
+
+ winbindd_free_response(&response);
+ done:
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrnam %s returns %s (%d)\n", getpid(),
+ name, nss_err_str(ret), ret);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/* Return group struct from gid */
+
+NSS_STATUS
+_nss_winbind_getgrgid_r(gid_t gid,
+ struct group *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ /* If our static buffer needs to be expanded we are called again */
+ /* Or if the stored response group name differs from the request. */
+
+ if (!keep_response || gid != response.data.gr.gr_gid) {
+
+ /* Call for the first time */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.gid = gid;
+
+ ret = winbindd_request_response(WINBINDD_GETGRGID, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+
+ ret = fill_grent(result, &response.data.gr,
+ (char *)response.extra_data.data,
+ &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_grent(result, &response.data.gr,
+ (char *)response.extra_data.data, &buffer,
+ &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+
+ keep_response = false;
+ *errnop = 0;
+ }
+
+ winbindd_free_response(&response);
+ done:
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrgid %d returns %s (%d)\n", getpid(),
+ (unsigned int)gid, nss_err_str(ret), ret);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+ return ret;
+}
+
+/* Initialise supplementary groups */
+
+NSS_STATUS
+_nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
+ long int *size, gid_t **groups, long int limit,
+ int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int i;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
+ user, group);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.username, user,
+ sizeof(request.data.username) - 1);
+
+ ret = winbindd_request_response(WINBINDD_GETGROUPS, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ int num_gids = response.data.num_entries;
+ gid_t *gid_list = (gid_t *)response.extra_data.data;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: initgroups %s: got NSS_STATUS_SUCCESS "
+ "and %d gids\n", getpid(),
+ user, num_gids);
+#endif
+ if (gid_list == NULL) {
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ /* Copy group list to client */
+
+ for (i = 0; i < num_gids; i++) {
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: initgroups %s (%d): "
+ "processing gid %d \n", getpid(),
+ user, group, gid_list[i]);
+#endif
+
+ /* Skip primary group */
+
+ if (gid_list[i] == group) {
+ continue;
+ }
+
+ /* Filled buffer ? If so, resize. */
+
+ if (*start == *size) {
+ long int newsize;
+ gid_t *newgroups;
+
+ newsize = 2 * (*size);
+ if (limit > 0) {
+ if (*size == limit) {
+ goto done;
+ }
+ if (newsize > limit) {
+ newsize = limit;
+ }
+ }
+
+ newgroups = (gid_t *)
+ realloc((*groups),
+ newsize * sizeof(**groups));
+ if (!newgroups) {
+ *errnop = ENOMEM;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+ *groups = newgroups;
+ *size = newsize;
+ }
+
+ /* Add to buffer */
+
+ (*groups)[*start] = gid_list[i];
+ *start += 1;
+ }
+ }
+
+ /* Back to your regularly scheduled programming */
+
+ done:
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: initgroups %s returns %s (%d)\n", getpid(),
+ user, nss_err_str(ret), ret);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+
+/* return a list of group SIDs for a user SID */
+NSS_STATUS
+_nss_winbind_getusersids(const char *user_sid, char **group_sids,
+ int *num_groups,
+ char *buffer, size_t buf_size, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getusersids %s\n", getpid(), user_sid);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.sid, user_sid,sizeof(request.data.sid) - 1);
+ request.data.sid[sizeof(request.data.sid) - 1] = '\0';
+
+ ret = winbindd_request_response(WINBINDD_GETUSERSIDS, &request, &response);
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ if (buf_size < response.length - sizeof(response)) {
+ ret = NSS_STATUS_TRYAGAIN;
+ errno = *errnop = ERANGE;
+ goto done;
+ }
+
+ *num_groups = response.data.num_entries;
+ *group_sids = buffer;
+ memcpy(buffer, response.extra_data.data, response.length - sizeof(response));
+ errno = *errnop = 0;
+
+ done:
+ winbindd_free_response(&response);
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+
+/* map a user or group name to a SID string */
+NSS_STATUS
+_nss_winbind_nametosid(const char *name, char **sid, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: nametosid %s\n", getpid(), name);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ strncpy(request.data.name.name, name,
+ sizeof(request.data.name.name) - 1);
+ request.data.name.name[sizeof(request.data.name.name) - 1] = '\0';
+
+ ret = winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ if (buflen < strlen(response.data.sid.sid)+1) {
+ ret = NSS_STATUS_TRYAGAIN;
+ *errnop = errno = ERANGE;
+ goto failed;
+ }
+
+ *errnop = errno = 0;
+ *sid = buffer;
+ strcpy(*sid, response.data.sid.sid);
+
+failed:
+ winbindd_free_response(&response);
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/* map a sid string to a user or group name */
+NSS_STATUS
+_nss_winbind_sidtoname(const char *sid, char **name, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+ static char sep_char;
+ unsigned needed;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: sidtoname %s\n", getpid(), sid);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ /* we need to fetch the separator first time through */
+ if (!sep_char) {
+ ret = winbindd_request_response(WINBINDD_INFO, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ sep_char = response.data.info.winbind_separator;
+ winbindd_free_response(&response);
+ }
+
+
+ strncpy(request.data.sid, sid,
+ sizeof(request.data.sid) - 1);
+ request.data.sid[sizeof(request.data.sid) - 1] = '\0';
+
+ ret = winbindd_request_response(WINBINDD_LOOKUPSID, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ needed =
+ strlen(response.data.name.dom_name) +
+ strlen(response.data.name.name) + 2;
+
+ if (buflen < needed) {
+ ret = NSS_STATUS_TRYAGAIN;
+ *errnop = errno = ERANGE;
+ goto failed;
+ }
+
+ snprintf(buffer, needed, "%s%c%s",
+ response.data.name.dom_name,
+ sep_char,
+ response.data.name.name);
+
+ *name = buffer;
+ *errnop = errno = 0;
+
+failed:
+ winbindd_free_response(&response);
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/* map a sid to a uid */
+NSS_STATUS
+_nss_winbind_sidtouid(const char *sid, uid_t *uid, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: sidtouid %s\n", getpid(), sid);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1);
+ request.data.sid[sizeof(request.data.sid) - 1] = '\0';
+
+ ret = winbindd_request_response(WINBINDD_SID_TO_UID, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ *uid = response.data.uid;
+
+failed:
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/* map a sid to a gid */
+NSS_STATUS
+_nss_winbind_sidtogid(const char *sid, gid_t *gid, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: sidtogid %s\n", getpid(), sid);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1);
+ request.data.sid[sizeof(request.data.sid) - 1] = '\0';
+
+ ret = winbindd_request_response(WINBINDD_SID_TO_GID, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ *gid = response.data.gid;
+
+failed:
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/* map a uid to a SID string */
+NSS_STATUS
+_nss_winbind_uidtosid(uid_t uid, char **sid, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5u]: uidtosid %u\n", (unsigned int)getpid(), (unsigned int)uid);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ request.data.uid = uid;
+
+ ret = winbindd_request_response(WINBINDD_UID_TO_SID, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ if (buflen < strlen(response.data.sid.sid)+1) {
+ ret = NSS_STATUS_TRYAGAIN;
+ *errnop = errno = ERANGE;
+ goto failed;
+ }
+
+ *errnop = errno = 0;
+ *sid = buffer;
+ strcpy(*sid, response.data.sid.sid);
+
+failed:
+ winbindd_free_response(&response);
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/* map a gid to a SID string */
+NSS_STATUS
+_nss_winbind_gidtosid(gid_t gid, char **sid, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5u]: gidtosid %u\n", (unsigned int)getpid(), (unsigned int)gid);
+#endif
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ request.data.gid = gid;
+
+ ret = winbindd_request_response(WINBINDD_GID_TO_SID, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ if (buflen < strlen(response.data.sid.sid)+1) {
+ ret = NSS_STATUS_TRYAGAIN;
+ *errnop = errno = ERANGE;
+ goto failed;
+ }
+
+ *errnop = errno = 0;
+ *sid = buffer;
+ strcpy(*sid, response.data.sid.sid);
+
+failed:
+ winbindd_free_response(&response);
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
diff --git a/nsswitch/winbind_nss_linux.h b/nsswitch/winbind_nss_linux.h
new file mode 100644
index 0000000000..db5a378aad
--- /dev/null
+++ b/nsswitch/winbind_nss_linux.h
@@ -0,0 +1,29 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WINBIND_NSS_LINUX_H
+#define _WINBIND_NSS_LINUX_H
+
+#include <nss.h>
+
+typedef enum nss_status NSS_STATUS;
+
+#endif /* _WINBIND_NSS_LINUX_H */
diff --git a/nsswitch/winbind_nss_netbsd.c b/nsswitch/winbind_nss_netbsd.c
new file mode 100644
index 0000000000..9b8e0a2265
--- /dev/null
+++ b/nsswitch/winbind_nss_netbsd.c
@@ -0,0 +1,442 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NetBSD loadable authentication module, providing identification
+ routines against Samba winbind/Windows NT Domain
+
+ Copyright (C) Luke Mewburn 2004-2005
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "winbind_client.h"
+
+#include <sys/param.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+ /* dynamic nsswitch with "new" getpw* nsdispatch API available */
+#if defined(NSS_MODULE_INTERFACE_VERSION) && defined(HAVE_GETPWENT_R)
+
+/*
+ group functions
+ ---------------
+*/
+
+static struct group _winbind_group;
+static char _winbind_groupbuf[1024];
+
+/*
+ * We need a proper prototype for this :-)
+ */
+
+NSS_STATUS _nss_winbind_setpwent(void);
+NSS_STATUS _nss_winbind_endpwent(void);
+NSS_STATUS _nss_winbind_getpwent_r(struct passwd *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result,
+ char *buffer, size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getpwnam_r(const char *name, struct passwd *result,
+ char *buffer, size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_setgrent(void);
+NSS_STATUS _nss_winbind_endgrent(void);
+NSS_STATUS _nss_winbind_getgrent_r(struct group *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getgrlst_r(struct group *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getgrnam_r(const char *name, struct group *result,
+ char *buffer, size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getgrgid_r(gid_t gid, struct group *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
+ long int *size, gid_t **groups,
+ long int limit, int *errnop);
+NSS_STATUS _nss_winbind_getusersids(const char *user_sid, char **group_sids,
+ int *num_groups, char *buffer, size_t buf_size,
+ int *errnop);
+NSS_STATUS _nss_winbind_nametosid(const char *name, char **sid, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_sidtoname(const char *sid, char **name, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_sidtouid(const char *sid, uid_t *uid, int *errnop);
+NSS_STATUS _nss_winbind_sidtogid(const char *sid, gid_t *gid, int *errnop);
+NSS_STATUS _nss_winbind_uidtosid(uid_t uid, char **sid, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_gidtosid(gid_t gid, char **sid, char *buffer,
+ size_t buflen, int *errnop);
+
+int
+netbsdwinbind_endgrent(void *nsrv, void *nscb, va_list ap)
+{
+ int rv;
+
+ rv = _nss_winbind_endgrent();
+ return rv;
+}
+
+int
+netbsdwinbind_setgrent(void *nsrv, void *nscb, va_list ap)
+{
+ int rv;
+
+ rv = _nss_winbind_setgrent();
+ return rv;
+}
+
+int
+netbsdwinbind_getgrent(void *nsrv, void *nscb, va_list ap)
+{
+ struct group **retval = va_arg(ap, struct group **);
+
+ int rv, rerrno;
+
+ *retval = NULL;
+ rv = _nss_winbind_getgrent_r(&_winbind_group,
+ _winbind_groupbuf, sizeof(_winbind_groupbuf), &rerrno);
+ if (rv == NS_SUCCESS)
+ *retval = &_winbind_group;
+ return rv;
+}
+
+int
+netbsdwinbind_getgrent_r(void *nsrv, void *nscb, va_list ap)
+{
+ int *retval = va_arg(ap, int *);
+ struct group *grp = va_arg(ap, struct group *);
+ char *buffer = va_arg(ap, char *);
+ size_t buflen = va_arg(ap, size_t);
+ struct group **result = va_arg(ap, struct group **);
+
+ int rv, rerrno;
+
+ *result = NULL;
+ rerrno = 0;
+
+ rv = _nss_winbind_getgrent_r(grp, buffer, buflen, rerrno);
+ if (rv == NS_SUCCESS)
+ *result = grp;
+ else
+ *retval = rerrno;
+ return rv;
+}
+
+int
+netbsdwinbind_getgrgid(void *nsrv, void *nscb, va_list ap)
+{
+ struct group **retval = va_arg(ap, struct group **);
+ gid_t gid = va_arg(ap, gid_t);
+
+ int rv, rerrno;
+
+ *retval = NULL;
+ rv = _nss_winbind_getgrgid_r(gid, &_winbind_group,
+ _winbind_groupbuf, sizeof(_winbind_groupbuf), &rerrno);
+ if (rv == NS_SUCCESS)
+ *retval = &_winbind_group;
+ return rv;
+}
+
+int
+netbsdwinbind_getgrgid_r(void *nsrv, void *nscb, va_list ap)
+{
+ int *retval = va_arg(ap, int *);
+ gid_t gid = va_arg(ap, gid_t);
+ struct group *grp = va_arg(ap, struct group *);
+ char *buffer = va_arg(ap, char *);
+ size_t buflen = va_arg(ap, size_t);
+ struct group **result = va_arg(ap, struct group **);
+
+ int rv, rerrno;
+
+ *result = NULL;
+ rerrno = 0;
+
+ rv = _nss_winbind_getgrgid_r(gid, grp, buffer, buflen, &rerrno);
+ if (rv == NS_SUCCESS)
+ *result = grp;
+ else
+ *retval = rerrno;
+ return rv;
+}
+
+int
+netbsdwinbind_getgrnam(void *nsrv, void *nscb, va_list ap)
+{
+ struct group **retval = va_arg(ap, struct group **);
+ const char *name = va_arg(ap, const char *);
+
+ int rv, rerrno;
+
+ *retval = NULL;
+ rv = _nss_winbind_getgrnam_r(name, &_winbind_group,
+ _winbind_groupbuf, sizeof(_winbind_groupbuf), &rerrno);
+ if (rv == NS_SUCCESS)
+ *retval = &_winbind_group;
+ return rv;
+}
+
+int
+netbsdwinbind_getgrnam_r(void *nsrv, void *nscb, va_list ap)
+{
+ int *retval = va_arg(ap, int *);
+ const char *name = va_arg(ap, const char *);
+ struct group *grp = va_arg(ap, struct group *);
+ char *buffer = va_arg(ap, char *);
+ size_t buflen = va_arg(ap, size_t);
+ struct group **result = va_arg(ap, struct group **);
+
+ int rv, rerrno;
+
+ *result = NULL;
+ rerrno = 0;
+
+ rv = _nss_winbind_getgrnam_r(name, grp, buffer, buflen, &rerrno);
+ if (rv == NS_SUCCESS)
+ *result = grp;
+ else
+ *retval = rerrno;
+ return rv;
+}
+
+int
+netbsdwinbind_getgroupmembership(void *nsrv, void *nscb, va_list ap)
+{
+ int *result = va_arg(ap, int *);
+ const char *uname = va_arg(ap, const char *);
+ gid_t agroup = va_arg(ap, gid_t);
+ gid_t *groups = va_arg(ap, gid_t *);
+ int maxgrp = va_arg(ap, int);
+ int *groupc = va_arg(ap, int *);
+
+ struct winbindd_request request;
+ struct winbindd_response response;
+ gid_t *wblistv;
+ int wblistc, i, isdup, dupc;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+ strncpy(request.data.username, uname,
+ sizeof(request.data.username) - 1);
+ i = winbindd_request_response(WINBINDD_GETGROUPS, &request, &response);
+ if (i != NSS_STATUS_SUCCESS)
+ return NS_NOTFOUND;
+ wblistv = (gid_t *)response.extra_data.data;
+ wblistc = response.data.num_entries;
+
+ for (i = 0; i < wblistc; i++) { /* add winbind gids */
+ isdup = 0; /* skip duplicates */
+ for (dupc = 0; dupc < MIN(maxgrp, *groupc); dupc++) {
+ if (groups[dupc] == wblistv[i]) {
+ isdup = 1;
+ break;
+ }
+ }
+ if (isdup)
+ continue;
+ if (*groupc < maxgrp) /* add this gid */
+ groups[*groupc] = wblistv[i];
+ else
+ *result = -1;
+ (*groupc)++;
+ }
+ SAFE_FREE(wblistv);
+ return NS_NOTFOUND;
+}
+
+
+/*
+ passwd functions
+ ----------------
+*/
+
+static struct passwd _winbind_passwd;
+static char _winbind_passwdbuf[1024];
+
+int
+netbsdwinbind_endpwent(void *nsrv, void *nscb, va_list ap)
+{
+ int rv;
+
+ rv = _nss_winbind_endpwent();
+ return rv;
+}
+
+int
+netbsdwinbind_setpwent(void *nsrv, void *nscb, va_list ap)
+{
+ int rv;
+
+ rv = _nss_winbind_setpwent();
+ return rv;
+}
+
+int
+netbsdwinbind_getpwent(void *nsrv, void *nscb, va_list ap)
+{
+ struct passwd **retval = va_arg(ap, struct passwd **);
+
+ int rv, rerrno;
+
+ *retval = NULL;
+
+ rv = _nss_winbind_getpwent_r(&_winbind_passwd,
+ _winbind_passwdbuf, sizeof(_winbind_passwdbuf), &rerrno);
+ if (rv == NS_SUCCESS)
+ *retval = &_winbind_passwd;
+ return rv;
+}
+
+int
+netbsdwinbind_getpwent_r(void *nsrv, void *nscb, va_list ap)
+{
+ int *retval = va_arg(ap, int *);
+ struct passwd *pw = va_arg(ap, struct passwd *);
+ char *buffer = va_arg(ap, char *);
+ size_t buflen = va_arg(ap, size_t);
+ struct passwd **result = va_arg(ap, struct passwd **);
+
+ int rv, rerrno;
+
+ *result = NULL;
+ rerrno = 0;
+
+ rv = _nss_winbind_getpwent_r(pw, buffer, buflen, rerrno);
+ if (rv == NS_SUCCESS)
+ *result = pw;
+ else
+ *retval = rerrno;
+ return rv;
+}
+
+int
+netbsdwinbind_getpwnam(void *nsrv, void *nscb, va_list ap)
+{
+ struct passwd **retval = va_arg(ap, struct passwd **);
+ const char *name = va_arg(ap, const char *);
+
+ int rv, rerrno;
+
+ *retval = NULL;
+ rv = _nss_winbind_getpwnam_r(name, &_winbind_passwd,
+ _winbind_passwdbuf, sizeof(_winbind_passwdbuf), &rerrno);
+ if (rv == NS_SUCCESS)
+ *retval = &_winbind_passwd;
+ return rv;
+}
+
+int
+netbsdwinbind_getpwnam_r(void *nsrv, void *nscb, va_list ap)
+{
+ int *retval = va_arg(ap, int *);
+ const char *name = va_arg(ap, const char *);
+ struct passwd *pw = va_arg(ap, struct passwd *);
+ char *buffer = va_arg(ap, char *);
+ size_t buflen = va_arg(ap, size_t);
+ struct passwd **result = va_arg(ap, struct passwd **);
+
+ int rv, rerrno;
+
+ *result = NULL;
+ rerrno = 0;
+
+ rv = _nss_winbind_getpwnam_r(name, pw, buffer, buflen, &rerrno);
+ if (rv == NS_SUCCESS)
+ *result = pw;
+ else
+ *retval = rerrno;
+ return rv;
+}
+
+int
+netbsdwinbind_getpwuid(void *nsrv, void *nscb, va_list ap)
+{
+ struct passwd **retval = va_arg(ap, struct passwd **);
+ uid_t uid = va_arg(ap, uid_t);
+
+ int rv, rerrno;
+
+ *retval = NULL;
+ rv = _nss_winbind_getpwuid_r(uid, &_winbind_passwd,
+ _winbind_passwdbuf, sizeof(_winbind_passwdbuf), &rerrno);
+ if (rv == NS_SUCCESS)
+ *retval = &_winbind_passwd;
+ return rv;
+}
+
+int
+netbsdwinbind_getpwuid_r(void *nsrv, void *nscb, va_list ap)
+{
+ int *retval = va_arg(ap, int *);
+ uid_t uid = va_arg(ap, uid_t);
+ struct passwd *pw = va_arg(ap, struct passwd *);
+ char *buffer = va_arg(ap, char *);
+ size_t buflen = va_arg(ap, size_t);
+ struct passwd **result = va_arg(ap, struct passwd **);
+
+ int rv, rerrno;
+
+ *result = NULL;
+ rerrno = 0;
+
+ rv = _nss_winbind_getpwuid_r(uid, pw, buffer, buflen, &rerrno);
+ if (rv == NS_SUCCESS)
+ *result = pw;
+ else
+ *retval = rerrno;
+ return rv;
+}
+
+
+/*
+ nsswitch module setup
+ ---------------------
+*/
+
+
+static ns_mtab winbind_methods[] = {
+
+{ NSDB_GROUP, "endgrent", netbsdwinbind_endgrent, NULL },
+{ NSDB_GROUP, "getgrent", netbsdwinbind_getgrent, NULL },
+{ NSDB_GROUP, "getgrent_r", netbsdwinbind_getgrent_r, NULL },
+{ NSDB_GROUP, "getgrgid", netbsdwinbind_getgrgid, NULL },
+{ NSDB_GROUP, "getgrgid_r", netbsdwinbind_getgrgid_r, NULL },
+{ NSDB_GROUP, "getgrnam", netbsdwinbind_getgrnam, NULL },
+{ NSDB_GROUP, "getgrnam_r", netbsdwinbind_getgrnam_r, NULL },
+{ NSDB_GROUP, "setgrent", netbsdwinbind_setgrent, NULL },
+{ NSDB_GROUP, "setgroupent", netbsdwinbind_setgrent, NULL },
+{ NSDB_GROUP, "getgroupmembership", netbsdwinbind_getgroupmembership, NULL },
+
+{ NSDB_PASSWD, "endpwent", netbsdwinbind_endpwent, NULL },
+{ NSDB_PASSWD, "getpwent", netbsdwinbind_getpwent, NULL },
+{ NSDB_PASSWD, "getpwent_r", netbsdwinbind_getpwent_r, NULL },
+{ NSDB_PASSWD, "getpwnam", netbsdwinbind_getpwnam, NULL },
+{ NSDB_PASSWD, "getpwnam_r", netbsdwinbind_getpwnam_r, NULL },
+{ NSDB_PASSWD, "getpwuid", netbsdwinbind_getpwuid, NULL },
+{ NSDB_PASSWD, "getpwuid_r", netbsdwinbind_getpwuid_r, NULL },
+{ NSDB_PASSWD, "setpassent", netbsdwinbind_setpwent, NULL },
+{ NSDB_PASSWD, "setpwent", netbsdwinbind_setpwent, NULL },
+
+};
+
+ns_mtab *
+nss_module_register(const char *source, unsigned int *mtabsize,
+ nss_module_unregister_fn *unreg)
+{
+ *mtabsize = sizeof(winbind_methods)/sizeof(winbind_methods[0]);
+ *unreg = NULL;
+ return (winbind_methods);
+}
+
+#endif /* NSS_MODULE_INTERFACE_VERSION && HAVE_GETPWENT_R */
diff --git a/nsswitch/winbind_nss_netbsd.h b/nsswitch/winbind_nss_netbsd.h
new file mode 100644
index 0000000000..5aeb2b9d7d
--- /dev/null
+++ b/nsswitch/winbind_nss_netbsd.h
@@ -0,0 +1,40 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NetBSD loadable authentication module, providing identification
+ routines against Samba winbind/Windows NT Domain
+
+ Copyright (C) Luke Mewburn 2004-2005
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WINBIND_NSS_NETBSD_H
+#define _WINBIND_NSS_NETBSD_H
+
+#include <nsswitch.h>
+
+ /* dynamic nsswitch with "new" getpw* nsdispatch API available */
+#if defined(NSS_MODULE_INTERFACE_VERSION) && defined(HAVE_GETPWENT_R)
+
+typedef int NSS_STATUS;
+
+#define NSS_STATUS_SUCCESS NS_SUCCESS
+#define NSS_STATUS_NOTFOUND NS_NOTFOUND
+#define NSS_STATUS_UNAVAIL NS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN NS_TRYAGAIN
+
+#endif /* NSS_MODULE_INTERFACE_VERSION && HAVE_GETPWENT_R */
+
+#endif /* _WINBIND_NSS_NETBSD_H */
diff --git a/nsswitch/winbind_nss_solaris.c b/nsswitch/winbind_nss_solaris.c
new file mode 100644
index 0000000000..5fb37643ce
--- /dev/null
+++ b/nsswitch/winbind_nss_solaris.c
@@ -0,0 +1,659 @@
+/*
+ Solaris NSS wrapper for winbind
+ - Shirish Kalele 2000
+
+ Based on Luke Howard's ldap_nss module for Solaris
+ */
+
+/*
+ Copyright (C) 1997-2003 Luke Howard.
+ This file is part of the nss_ldap library.
+
+ The nss_ldap library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 3 of the
+ License, or (at your option) any later version.
+
+ The nss_ldap library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the nss_ldap library; see the file COPYING.LIB. If not,
+ see <http://www.gnu.org/licenses/>.
+*/
+
+#undef DEVELOPER
+
+#include "winbind_client.h"
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <string.h>
+#include <pwd.h>
+#include "includes.h"
+#include <syslog.h>
+#if !defined(HPUX)
+#include <sys/syslog.h>
+#endif /*hpux*/
+
+#if defined(HAVE_NSS_COMMON_H) || defined(HPUX)
+
+#undef NSS_DEBUG
+
+#ifdef NSS_DEBUG
+#define NSS_DEBUG(str) syslog(LOG_DEBUG, "nss_winbind: %s", str);
+#else
+#define NSS_DEBUG(str) ;
+#endif
+
+#define NSS_ARGS(args) ((nss_XbyY_args_t *)args)
+
+#ifdef HPUX
+
+/*
+ * HP-UX 11 has no definiton of the nss_groupsbymem structure. This
+ * definition is taken from the nss_ldap project at:
+ * http://www.padl.com/OSS/nss_ldap.html
+ */
+
+struct nss_groupsbymem {
+ const char *username;
+ gid_t *gid_array;
+ int maxgids;
+ int force_slow_way;
+ int (*str2ent)(const char *instr, int instr_len, void *ent,
+ char *buffer, int buflen);
+ nss_status_t (*process_cstr)(const char *instr, int instr_len,
+ struct nss_groupsbymem *);
+ int numgids;
+};
+
+#endif /* HPUX */
+
+#define make_pwent_str(dest, src) \
+{ \
+ if((dest = get_static(buffer, buflen, strlen(src)+1)) == NULL) \
+ { \
+ *errnop = ERANGE; \
+ NSS_DEBUG("ERANGE error"); \
+ return NSS_STATUS_TRYAGAIN; \
+ } \
+ strcpy(dest, src); \
+}
+
+static NSS_STATUS _nss_winbind_setpwent_solwrap (nss_backend_t* be, void* args)
+{
+ NSS_DEBUG("_nss_winbind_setpwent_solwrap");
+ return _nss_winbind_setpwent();
+}
+
+static NSS_STATUS
+_nss_winbind_endpwent_solwrap (nss_backend_t * be, void *args)
+{
+ NSS_DEBUG("_nss_winbind_endpwent_solwrap");
+ return _nss_winbind_endpwent();
+}
+
+static NSS_STATUS
+_nss_winbind_getpwent_solwrap (nss_backend_t* be, void *args)
+{
+ NSS_STATUS ret;
+ char* buffer = NSS_ARGS(args)->buf.buffer;
+ int buflen = NSS_ARGS(args)->buf.buflen;
+ struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+ int* errnop = &NSS_ARGS(args)->erange;
+ char logmsg[80];
+
+ ret = _nss_winbind_getpwent_r(result, buffer,
+ buflen, errnop);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ {
+ snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning user: %s\n",
+ result->pw_name);
+ NSS_DEBUG(logmsg);
+ NSS_ARGS(args)->returnval = (void*) result;
+ } else {
+ snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning error: %d.\n",ret);
+ NSS_DEBUG(logmsg);
+ }
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getpwnam_solwrap (nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getpwnam_solwrap");
+
+ ret = _nss_winbind_getpwnam_r (NSS_ARGS(args)->key.name,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getpwuid_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getpwuid_solwrap");
+ ret = _nss_winbind_getpwuid_r (NSS_ARGS(args)->key.uid,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS _nss_winbind_passwd_destr (nss_backend_t * be, void *args)
+{
+ SAFE_FREE(be);
+ NSS_DEBUG("_nss_winbind_passwd_destr");
+ return NSS_STATUS_SUCCESS;
+}
+
+static nss_backend_op_t passwd_ops[] =
+{
+ _nss_winbind_passwd_destr,
+ _nss_winbind_endpwent_solwrap, /* NSS_DBOP_ENDENT */
+ _nss_winbind_setpwent_solwrap, /* NSS_DBOP_SETENT */
+ _nss_winbind_getpwent_solwrap, /* NSS_DBOP_GETENT */
+ _nss_winbind_getpwnam_solwrap, /* NSS_DBOP_PASSWD_BYNAME */
+ _nss_winbind_getpwuid_solwrap /* NSS_DBOP_PASSWD_BYUID */
+};
+
+nss_backend_t*
+_nss_winbind_passwd_constr (const char* db_name,
+ const char* src_name,
+ const char* cfg_args)
+{
+ nss_backend_t *be;
+
+ if(!(be = SMB_MALLOC_P(nss_backend_t)) )
+ return NULL;
+
+ be->ops = passwd_ops;
+ be->n_ops = sizeof(passwd_ops) / sizeof(nss_backend_op_t);
+
+ NSS_DEBUG("Initialized nss_winbind passwd backend");
+ return be;
+}
+
+/*****************************************************************
+ GROUP database backend
+ *****************************************************************/
+
+static NSS_STATUS _nss_winbind_setgrent_solwrap (nss_backend_t* be, void* args)
+{
+ NSS_DEBUG("_nss_winbind_setgrent_solwrap");
+ return _nss_winbind_setgrent();
+}
+
+static NSS_STATUS
+_nss_winbind_endgrent_solwrap (nss_backend_t * be, void *args)
+{
+ NSS_DEBUG("_nss_winbind_endgrent_solwrap");
+ return _nss_winbind_endgrent();
+}
+
+static NSS_STATUS
+_nss_winbind_getgrent_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ char* buffer = NSS_ARGS(args)->buf.buffer;
+ int buflen = NSS_ARGS(args)->buf.buflen;
+ struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+ int* errnop = &NSS_ARGS(args)->erange;
+ char logmsg[80];
+
+ ret = _nss_winbind_getgrent_r(result, buffer,
+ buflen, errnop);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ {
+ snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning group: %s\n", result->gr_name);
+ NSS_DEBUG(logmsg);
+ NSS_ARGS(args)->returnval = (void*) result;
+ } else {
+ snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning error: %d.\n", ret);
+ NSS_DEBUG(logmsg);
+ }
+
+ return ret;
+
+}
+
+static NSS_STATUS
+_nss_winbind_getgrnam_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getgrnam_solwrap");
+ ret = _nss_winbind_getgrnam_r(NSS_ARGS(args)->key.name,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getgrgid_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getgrgid_solwrap");
+ ret = _nss_winbind_getgrgid_r (NSS_ARGS(args)->key.gid,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getgroupsbymember_solwrap(nss_backend_t* be, void* args)
+{
+ int errnop;
+ struct nss_groupsbymem *gmem = (struct nss_groupsbymem *)args;
+ long int numgids = gmem->numgids;
+ long int maxgids = gmem->maxgids;
+
+ NSS_DEBUG("_nss_winbind_getgroupsbymember");
+
+ _nss_winbind_initgroups_dyn(gmem->username,
+ gmem->gid_array[0], /* Primary Group */
+ &numgids,
+ &maxgids,
+ &gmem->gid_array,
+ gmem->maxgids,
+ &errnop);
+
+ gmem->numgids = numgids;
+ gmem->maxgids = maxgids;
+
+ /*
+ * If the maximum number of gids have been found, return
+ * SUCCESS so the switch engine will stop searching. Otherwise
+ * return NOTFOUND so nsswitch will continue to get groups
+ * from the remaining database backends specified in the
+ * nsswitch.conf file.
+ */
+ return (gmem->numgids == gmem->maxgids ? NSS_STATUS_SUCCESS : NSS_STATUS_NOTFOUND);
+}
+
+static NSS_STATUS
+_nss_winbind_group_destr (nss_backend_t* be, void* args)
+{
+ SAFE_FREE(be);
+ NSS_DEBUG("_nss_winbind_group_destr");
+ return NSS_STATUS_SUCCESS;
+}
+
+static nss_backend_op_t group_ops[] =
+{
+ _nss_winbind_group_destr,
+ _nss_winbind_endgrent_solwrap,
+ _nss_winbind_setgrent_solwrap,
+ _nss_winbind_getgrent_solwrap,
+ _nss_winbind_getgrnam_solwrap,
+ _nss_winbind_getgrgid_solwrap,
+ _nss_winbind_getgroupsbymember_solwrap
+};
+
+nss_backend_t*
+_nss_winbind_group_constr (const char* db_name,
+ const char* src_name,
+ const char* cfg_args)
+{
+ nss_backend_t* be;
+
+ if(!(be = SMB_MALLOC_P(nss_backend_t)) )
+ return NULL;
+
+ be->ops = group_ops;
+ be->n_ops = sizeof(group_ops) / sizeof(nss_backend_op_t);
+
+ NSS_DEBUG("Initialized nss_winbind group backend");
+ return be;
+}
+
+/*****************************************************************
+ hosts and ipnodes backend
+ *****************************************************************/
+#if defined(SUNOS5) /* not compatible with HP-UX */
+
+/* this parser is shared between get*byname and get*byaddr, as key type
+ in request is stored in different locations, I had to provide the
+ address family as an argument, caller must free the winbind response. */
+
+static NSS_STATUS
+parse_response(int af, nss_XbyY_args_t* argp, struct winbindd_response *response)
+{
+ struct hostent *he = (struct hostent *)argp->buf.result;
+ char *buffer = argp->buf.buffer;
+ int buflen = argp->buf.buflen;
+ NSS_STATUS ret;
+
+ char *p, *data;
+ int addrcount = 0;
+ int len = 0;
+ struct in_addr *addrp;
+#if defined(AF_INET6)
+ struct in6_addr *addrp6;
+#endif
+ int i;
+
+ /* response is tab separated list of ip addresses with hostname
+ and newline at the end. so at first we will strip newline
+ then construct list of addresses for hostent.
+ */
+ p = strchr(response->data.winsresp, '\n');
+ if(p) *p = '\0';
+ else {/* it must be broken */
+ argp->h_errno = NO_DATA;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ for(; p != response->data.winsresp; p--) {
+ if(*p == '\t') addrcount++;
+ }
+
+ if(addrcount == 0) {/* it must be broken */
+ argp->h_errno = NO_DATA;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* allocate space for addresses and h_addr_list */
+ he->h_addrtype = af;
+ if( he->h_addrtype == AF_INET) {
+ he->h_length = sizeof(struct in_addr);
+ addrp = (struct in_addr *)ROUND_DOWN(buffer + buflen,
+ sizeof(struct in_addr));
+ addrp -= addrcount;
+ he->h_addr_list = (char **)ROUND_DOWN(addrp, sizeof (char*));
+ he->h_addr_list -= addrcount+1;
+ }
+#if defined(AF_INET6)
+ else {
+ he->h_length = sizeof(struct in6_addr);
+ addrp6 = (struct in6_addr *)ROUND_DOWN(buffer + buflen,
+ sizeof(struct in6_addr));
+ addrp6 -= addrcount;
+ he->h_addr_list = (char **)ROUND_DOWN(addrp6, sizeof (char*));
+ he->h_addr_list -= addrcount+1;
+ }
+#endif
+
+ /* buffer too small?! */
+ if((char *)he->h_addr_list < buffer ) {
+ argp->erange = 1;
+ return NSS_STR_PARSE_ERANGE;
+ }
+
+ data = response->data.winsresp;
+ for( i = 0; i < addrcount; i++) {
+ p = strchr(data, '\t');
+ if(p == NULL) break; /* just in case... */
+
+ *p = '\0'; /* terminate the string */
+ if(he->h_addrtype == AF_INET) {
+ he->h_addr_list[i] = (char *)&addrp[i];
+ if ((addrp[i].s_addr = inet_addr(data)) == -1) {
+ argp->erange = 1;
+ return NSS_STR_PARSE_ERANGE;
+ }
+ }
+#if defined(AF_INET6)
+ else {
+ he->h_addr_list[i] = (char *)&addrp6[i];
+ if (strchr(data, ':') != 0) {
+ if (inet_pton(AF_INET6, data, &addrp6[i]) != 1) {
+ argp->erange = 1;
+ return NSS_STR_PARSE_ERANGE;
+ }
+ } else {
+ struct in_addr in4;
+ if ((in4.s_addr = inet_addr(data)) == -1) {
+ argp->erange = 1;
+ return NSS_STR_PARSE_ERANGE;
+ }
+ IN6_INADDR_TO_V4MAPPED(&in4, &addrp6[i]);
+ }
+ }
+#endif
+ data = p+1;
+ }
+
+ he->h_addr_list[i] = (char *)NULL;
+
+ len = strlen(data);
+ if(len > he->h_addr_list - (char**)argp->buf.buffer) {
+ argp->erange = 1;
+ return NSS_STR_PARSE_ERANGE;
+ }
+
+ /* this is a bit overkill to use _nss_netdb_aliases here since
+ there seems to be no aliases but it will create all data for us */
+ he->h_aliases = _nss_netdb_aliases(data, len, buffer,
+ ((char*) he->h_addr_list) - buffer);
+ if(he->h_aliases == NULL) {
+ argp->erange = 1;
+ ret = NSS_STR_PARSE_ERANGE;
+ } else {
+ he->h_name = he->h_aliases[0];
+ he->h_aliases++;
+ ret = NSS_STR_PARSE_SUCCESS;
+ }
+
+ argp->returnval = (void*)he;
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_ipnodes_getbyname(nss_backend_t* be, void *args)
+{
+ nss_XbyY_args_t *argp = (nss_XbyY_args_t*) args;
+ struct winbindd_response response;
+ struct winbindd_request request;
+ NSS_STATUS ret;
+ int af;
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ /* I assume there that AI_ADDRCONFIG cases are handled in nss
+ frontend code, at least it seems done so in solaris...
+
+ we will give NO_DATA for pure IPv6; IPv4 will be returned for
+ AF_INET or for AF_INET6 and AI_ALL|AI_V4MAPPED we have to map
+ IPv4 to IPv6.
+ */
+#if defined(AF_INET6)
+#ifdef HAVE_NSS_XBYY_KEY_IPNODE
+ af = argp->key.ipnode.af_family;
+ if(af == AF_INET6 && argp->key.ipnode.flags == 0) {
+ argp->h_errno = NO_DATA;
+ return NSS_STATUS_UNAVAIL;
+ }
+#else
+ /* I'm not that sure if this is correct, but... */
+ af = AF_INET6;
+#endif
+#endif
+
+ strncpy(request.data.winsreq, argp->key.name, sizeof(request.data.winsreq) - 1);
+ request.data.winsreq[sizeof(request.data.winsreq) - 1] = '\0';
+
+ if( (ret = winbindd_request_response(WINBINDD_WINS_BYNAME, &request, &response))
+ == NSS_STATUS_SUCCESS ) {
+ ret = parse_response(af, argp, &response);
+ }
+
+ winbindd_free_response(&response);
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_hosts_getbyname(nss_backend_t* be, void *args)
+{
+ nss_XbyY_args_t *argp = (nss_XbyY_args_t*) args;
+ struct winbindd_response response;
+ struct winbindd_request request;
+ NSS_STATUS ret;
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ strncpy(request.data.winsreq, argp->key.name, sizeof(request.data.winsreq) - 1);
+ request.data.winsreq[sizeof(request.data.winsreq) - 1] = '\0';
+
+ if( (ret = winbindd_request_response(WINBINDD_WINS_BYNAME, &request, &response))
+ == NSS_STATUS_SUCCESS ) {
+ ret = parse_response(AF_INET, argp, &response);
+ }
+
+ winbindd_free_response(&response);
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_hosts_getbyaddr(nss_backend_t* be, void *args)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+ nss_XbyY_args_t *argp = (nss_XbyY_args_t *)args;
+ const char *p;
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+#if defined(AF_INET6)
+ /* winbindd currently does not resolve IPv6 */
+ if(argp->key.hostaddr.type == AF_INET6) {
+ argp->h_errno = NO_DATA;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ p = inet_ntop(argp->key.hostaddr.type, argp->key.hostaddr.addr,
+ request.data.winsreq, sizeof request.data.winsreq);
+#else
+ snprintf(request.data.winsreq, sizeof request.data.winsreq,
+ "%u.%u.%u.%u",
+ ((unsigned char *)argp->key.hostaddr.addr)[0],
+ ((unsigned char *)argp->key.hostaddr.addr)[1],
+ ((unsigned char *)argp->key.hostaddr.addr)[2],
+ ((unsigned char *)argp->key.hostaddr.addr)[3]);
+#endif
+
+ ret = winbindd_request_response(WINBINDD_WINS_BYIP, &request, &response);
+
+ if( ret == NSS_STATUS_SUCCESS) {
+ parse_response(argp->key.hostaddr.type, argp, &response);
+ }
+ winbindd_free_response(&response);
+ return ret;
+}
+
+/* winbind does not provide setent, getent, endent for wins */
+static NSS_STATUS
+_nss_winbind_common_endent(nss_backend_t* be, void *args)
+{
+ return (NSS_STATUS_UNAVAIL);
+}
+
+static NSS_STATUS
+_nss_winbind_common_setent(nss_backend_t* be, void *args)
+{
+ return (NSS_STATUS_UNAVAIL);
+}
+
+static NSS_STATUS
+_nss_winbind_common_getent(nss_backend_t* be, void *args)
+{
+ return (NSS_STATUS_UNAVAIL);
+}
+
+static nss_backend_t*
+_nss_winbind_common_constr (nss_backend_op_t ops[], int n_ops)
+{
+ nss_backend_t* be;
+
+ if(!(be = SMB_MALLOC_P(nss_backend_t)) )
+ return NULL;
+
+ be->ops = ops;
+ be->n_ops = n_ops;
+
+ return be;
+}
+
+static NSS_STATUS
+_nss_winbind_common_destr (nss_backend_t* be, void* args)
+{
+ SAFE_FREE(be);
+ return NSS_STATUS_SUCCESS;
+}
+
+static nss_backend_op_t ipnodes_ops[] = {
+ _nss_winbind_common_destr,
+ _nss_winbind_common_endent,
+ _nss_winbind_common_setent,
+ _nss_winbind_common_getent,
+ _nss_winbind_ipnodes_getbyname,
+ _nss_winbind_hosts_getbyaddr,
+};
+
+nss_backend_t *
+_nss_winbind_ipnodes_constr(dummy1, dummy2, dummy3)
+ const char *dummy1, *dummy2, *dummy3;
+{
+ return (_nss_winbind_common_constr(ipnodes_ops,
+ sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0])));
+}
+
+static nss_backend_op_t host_ops[] = {
+ _nss_winbind_common_destr,
+ _nss_winbind_common_endent,
+ _nss_winbind_common_setent,
+ _nss_winbind_common_getent,
+ _nss_winbind_hosts_getbyname,
+ _nss_winbind_hosts_getbyaddr,
+};
+
+nss_backend_t *
+_nss_winbind_hosts_constr(dummy1, dummy2, dummy3)
+ const char *dummy1, *dummy2, *dummy3;
+{
+ return (_nss_winbind_common_constr(host_ops,
+ sizeof (host_ops) / sizeof (host_ops[0])));
+}
+
+#endif /* defined(SUNOS5) */
+#endif /* defined(HAVE_NSS_COMMON_H) || defined(HPUX) */
diff --git a/nsswitch/winbind_nss_solaris.h b/nsswitch/winbind_nss_solaris.h
new file mode 100644
index 0000000000..f805542f75
--- /dev/null
+++ b/nsswitch/winbind_nss_solaris.h
@@ -0,0 +1,88 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WINBIND_NSS_SOLARIS_H
+#define _WINBIND_NSS_SOLARIS_H
+
+/* Solaris has a broken nss_common header file containing C++ reserved names. */
+#ifndef __cplusplus
+#undef class
+#undef private
+#undef public
+#undef protected
+#undef template
+#undef this
+#undef new
+#undef delete
+#undef friend
+#endif
+
+#include <nss_common.h>
+
+#ifndef __cplusplus
+#define class #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define private #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define public #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define protected #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define template #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define this #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define new #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define delete #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define friend #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#endif
+
+#include <nss_dbdefs.h>
+#include <nsswitch.h>
+
+typedef nss_status_t NSS_STATUS;
+
+#define NSS_STATUS_SUCCESS NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN
+
+/* The solaris winbind is implemented as a wrapper around the linux
+ version. */
+
+NSS_STATUS _nss_winbind_setpwent(void);
+NSS_STATUS _nss_winbind_endpwent(void);
+NSS_STATUS _nss_winbind_getpwent_r(struct passwd* result, char* buffer,
+ size_t buflen, int* errnop);
+NSS_STATUS _nss_winbind_getpwuid_r(uid_t, struct passwd*, char* buffer,
+ size_t buflen, int* errnop);
+NSS_STATUS _nss_winbind_getpwnam_r(const char* name, struct passwd* result,
+ char* buffer, size_t buflen, int* errnop);
+
+NSS_STATUS _nss_winbind_setgrent(void);
+NSS_STATUS _nss_winbind_endgrent(void);
+NSS_STATUS _nss_winbind_getgrent_r(struct group* result, char* buffer,
+ size_t buflen, int* errnop);
+NSS_STATUS _nss_winbind_getgrnam_r(const char *name,
+ struct group *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getgrgid_r(gid_t gid,
+ struct group *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
+ long int *size, gid_t **groups,
+ long int limit, int *errnop);
+
+#endif /* _WINBIND_NSS_SOLARIS_H */
diff --git a/nsswitch/winbind_struct_protocol.h b/nsswitch/winbind_struct_protocol.h
new file mode 100644
index 0000000000..fe3da81791
--- /dev/null
+++ b/nsswitch/winbind_struct_protocol.h
@@ -0,0 +1,514 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Gerald Carter 2006
+
+ You are free to use this interface definition in any way you see
+ fit, including without restriction, using this header in your own
+ products. You do not need to give any attribution.
+*/
+
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if(x) {free(x); x=NULL;} } while(0)
+#endif
+
+#ifndef _WINBINDD_NTDOM_H
+#define _WINBINDD_NTDOM_H
+
+#define WINBINDD_SOCKET_NAME "pipe" /* Name of PF_UNIX socket */
+
+/* Let the build environment override the public winbindd socket location. This
+ * is needed for launchd support -- jpeach.
+ */
+#ifndef WINBINDD_SOCKET_DIR
+#define WINBINDD_SOCKET_DIR "/tmp/.winbindd" /* Name of PF_UNIX dir */
+#endif
+
+/*
+ * when compiled with socket_wrapper support
+ * the location of the WINBINDD_SOCKET_DIR
+ * can be overwritten via an environment variable
+ */
+#define WINBINDD_SOCKET_DIR_ENVVAR "WINBINDD_SOCKET_DIR"
+
+#define WINBINDD_PRIV_SOCKET_SUBDIR "winbindd_privileged" /* name of subdirectory of lp_lockdir() to hold the 'privileged' pipe */
+#define WINBINDD_DOMAIN_ENV "WINBINDD_DOMAIN" /* Environment variables */
+#define WINBINDD_DONT_ENV "_NO_WINBINDD"
+#define WINBINDD_LOCATOR_KDC_ADDRESS "WINBINDD_LOCATOR_KDC_ADDRESS"
+
+/* Update this when you change the interface. */
+
+#define WINBIND_INTERFACE_VERSION 20
+
+/* Have to deal with time_t being 4 or 8 bytes due to structure alignment.
+ On a 64bit Linux box, we have to support a constant structure size
+ between /lib/libnss_winbind.so.2 and /li64/libnss_winbind.so.2.
+ The easiest way to do this is to always use 8byte values for time_t. */
+
+#define SMB_TIME_T int64_t
+
+/* Socket commands */
+
+enum winbindd_cmd {
+
+ WINBINDD_INTERFACE_VERSION, /* Always a well known value */
+
+ /* Get users and groups */
+
+ WINBINDD_GETPWNAM,
+ WINBINDD_GETPWUID,
+ WINBINDD_GETGRNAM,
+ WINBINDD_GETGRGID,
+ WINBINDD_GETGROUPS,
+
+ /* Enumerate users and groups */
+
+ WINBINDD_SETPWENT,
+ WINBINDD_ENDPWENT,
+ WINBINDD_GETPWENT,
+ WINBINDD_SETGRENT,
+ WINBINDD_ENDGRENT,
+ WINBINDD_GETGRENT,
+
+ /* PAM authenticate and password change */
+
+ WINBINDD_PAM_AUTH,
+ WINBINDD_PAM_AUTH_CRAP,
+ WINBINDD_PAM_CHAUTHTOK,
+ WINBINDD_PAM_LOGOFF,
+ WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP,
+
+ /* List various things */
+
+ WINBINDD_LIST_USERS, /* List w/o rid->id mapping */
+ WINBINDD_LIST_GROUPS, /* Ditto */
+ WINBINDD_LIST_TRUSTDOM,
+
+ /* SID conversion */
+
+ WINBINDD_LOOKUPSID,
+ WINBINDD_LOOKUPNAME,
+ WINBINDD_LOOKUPRIDS,
+
+ /* Lookup functions */
+
+ WINBINDD_SID_TO_UID,
+ WINBINDD_SID_TO_GID,
+ WINBINDD_SIDS_TO_XIDS,
+ WINBINDD_UID_TO_SID,
+ WINBINDD_GID_TO_SID,
+
+ WINBINDD_ALLOCATE_UID,
+ WINBINDD_ALLOCATE_GID,
+ WINBINDD_SET_MAPPING,
+ WINBINDD_REMOVE_MAPPING,
+ WINBINDD_SET_HWM,
+
+ /* Miscellaneous other stuff */
+
+ WINBINDD_CHECK_MACHACC, /* Check machine account pw works */
+ WINBINDD_PING, /* Just tell me winbind is running */
+ WINBINDD_INFO, /* Various bit of info. Currently just tidbits */
+ WINBINDD_DOMAIN_NAME, /* The domain this winbind server is a member of (lp_workgroup()) */
+
+ WINBINDD_DOMAIN_INFO, /* Most of what we know from
+ struct winbindd_domain */
+ WINBINDD_GETDCNAME, /* Issue a GetDCName Request */
+ WINBINDD_DSGETDCNAME, /* Issue a DsGetDCName Request */
+
+ WINBINDD_SHOW_SEQUENCE, /* display sequence numbers of domains */
+
+ /* WINS commands */
+
+ WINBINDD_WINS_BYIP,
+ WINBINDD_WINS_BYNAME,
+
+ /* this is like GETGRENT but gives an empty group list */
+ WINBINDD_GETGRLST,
+
+ WINBINDD_NETBIOS_NAME, /* The netbios name of the server */
+
+ /* find the location of our privileged pipe */
+ WINBINDD_PRIV_PIPE_DIR,
+
+ /* return a list of group sids for a user sid */
+ WINBINDD_GETUSERSIDS,
+
+ /* Various group queries */
+ WINBINDD_GETUSERDOMGROUPS,
+
+ /* Initialize connection in a child */
+ WINBINDD_INIT_CONNECTION,
+
+ /* Blocking calls that are not allowed on the main winbind pipe, only
+ * between parent and children */
+ WINBINDD_DUAL_SID2UID,
+ WINBINDD_DUAL_SID2GID,
+ WINBINDD_DUAL_SIDS2XIDS,
+ WINBINDD_DUAL_UID2SID,
+ WINBINDD_DUAL_GID2SID,
+ WINBINDD_DUAL_SET_MAPPING,
+ WINBINDD_DUAL_REMOVE_MAPPING,
+ WINBINDD_DUAL_SET_HWM,
+
+ /* Wrapper around possibly blocking unix nss calls */
+ WINBINDD_DUAL_USERINFO,
+ WINBINDD_DUAL_GETSIDALIASES,
+
+ /* Complete the challenge phase of the NTLM authentication
+ protocol using cached password. */
+ WINBINDD_CCACHE_NTLMAUTH,
+
+ WINBINDD_NUM_CMDS
+};
+
+typedef struct winbindd_pw {
+ fstring pw_name;
+ fstring pw_passwd;
+ uid_t pw_uid;
+ gid_t pw_gid;
+ fstring pw_gecos;
+ fstring pw_dir;
+ fstring pw_shell;
+} WINBINDD_PW;
+
+
+typedef struct winbindd_gr {
+ fstring gr_name;
+ fstring gr_passwd;
+ gid_t gr_gid;
+ uint32_t num_gr_mem;
+ uint32_t gr_mem_ofs; /* offset to group membership */
+} WINBINDD_GR;
+
+/* PAM specific request flags */
+#define WBFLAG_PAM_INFO3_NDR 0x00000001
+#define WBFLAG_PAM_INFO3_TEXT 0x00000002
+#define WBFLAG_PAM_USER_SESSION_KEY 0x00000004
+#define WBFLAG_PAM_LMKEY 0x00000008
+#define WBFLAG_PAM_CONTACT_TRUSTDOM 0x00000010
+#define WBFLAG_PAM_UNIX_NAME 0x00000080
+#define WBFLAG_PAM_AFS_TOKEN 0x00000100
+#define WBFLAG_PAM_NT_STATUS_SQUASH 0x00000200
+#define WBFLAG_PAM_KRB5 0x00001000
+#define WBFLAG_PAM_FALLBACK_AFTER_KRB5 0x00002000
+#define WBFLAG_PAM_CACHED_LOGIN 0x00004000
+#define WBFLAG_PAM_GET_PWD_POLICY 0x00008000
+
+/* generic request flags */
+#define WBFLAG_QUERY_ONLY 0x00000020 /* not used */
+/* This is a flag that can only be sent from parent to child */
+#define WBFLAG_IS_PRIVILEGED 0x00000400 /* not used */
+/* Flag to say this is a winbindd internal send - don't recurse. */
+#define WBFLAG_RECURSE 0x00000800
+/* Flag to tell winbind the NTLMv2 blob is too big for the struct and is in the
+ * extra_data field */
+#define WBFLAG_BIG_NTLMV2_BLOB 0x00010000
+
+#define WINBINDD_MAX_EXTRA_DATA (128*1024)
+
+/* Winbind request structure */
+
+/*******************************************************************************
+ * This structure MUST be the same size in the 32bit and 64bit builds
+ * for compatibility between /lib64/libnss_winbind.so and /lib/libnss_winbind.so
+ *
+ * DO NOT CHANGE THIS STRUCTURE WITHOUT TESTING THE 32BIT NSS LIB AGAINST
+ * A 64BIT WINBINDD --jerry
+ ******************************************************************************/
+
+struct winbindd_request {
+ uint32_t length;
+ enum winbindd_cmd cmd; /* Winbindd command to execute */
+ enum winbindd_cmd original_cmd; /* Original Winbindd command
+ issued to parent process */
+ pid_t pid; /* pid of calling process */
+ uint32_t wb_flags; /* generic flags */
+ uint32_t flags; /* flags relevant *only* to a given request */
+ fstring domain_name; /* name of domain for which the request applies */
+
+ union {
+ fstring winsreq; /* WINS request */
+ fstring username; /* getpwnam */
+ fstring groupname; /* getgrnam */
+ uid_t uid; /* getpwuid, uid_to_sid */
+ gid_t gid; /* getgrgid, gid_to_sid */
+ struct {
+ /* We deliberatedly don't split into domain/user to
+ avoid having the client know what the separator
+ character is. */
+ fstring user;
+ fstring pass;
+ char require_membership_of_sid[1024];
+ fstring krb5_cc_type;
+ uid_t uid;
+ } auth; /* pam_winbind auth module */
+ struct {
+ uint8_t chal[8];
+ uint32_t logon_parameters;
+ fstring user;
+ fstring domain;
+ fstring lm_resp;
+ uint32_t lm_resp_len;
+ fstring nt_resp;
+ uint32_t nt_resp_len;
+ fstring workstation;
+ fstring require_membership_of_sid;
+ } auth_crap;
+ struct {
+ fstring user;
+ fstring oldpass;
+ fstring newpass;
+ } chauthtok; /* pam_winbind passwd module */
+ struct {
+ fstring user;
+ fstring domain;
+ uint8_t new_nt_pswd[516];
+ uint16_t new_nt_pswd_len;
+ uint8_t old_nt_hash_enc[16];
+ uint16_t old_nt_hash_enc_len;
+ uint8_t new_lm_pswd[516];
+ uint16_t new_lm_pswd_len;
+ uint8_t old_lm_hash_enc[16];
+ uint16_t old_lm_hash_enc_len;
+ } chng_pswd_auth_crap;/* pam_winbind passwd module */
+ struct {
+ fstring user;
+ fstring krb5ccname;
+ uid_t uid;
+ } logoff; /* pam_winbind session module */
+ fstring sid; /* lookupsid, sid_to_[ug]id */
+ struct {
+ fstring dom_name; /* lookupname */
+ fstring name;
+ } name;
+ uint32_t num_entries; /* getpwent, getgrent */
+ struct {
+ fstring username;
+ fstring groupname;
+ } acct_mgt;
+ struct {
+ bool is_primary;
+ fstring dcname;
+ } init_conn;
+ struct {
+ fstring sid;
+ fstring name;
+ } dual_sid2id;
+ struct {
+ fstring sid;
+ uint32_t type;
+ uint32_t id;
+ } dual_idmapset;
+ bool list_all_domains;
+
+ struct {
+ uid_t uid;
+ fstring user;
+ /* the effective uid of the client, must be the uid for 'user'.
+ This is checked by the main daemon, trusted by children. */
+ /* if the blobs are length zero, then this doesn't
+ produce an actual challenge response. It merely
+ succeeds if there are cached credentials available
+ that could be used. */
+ uint32_t initial_blob_len; /* blobs in extra_data */
+ uint32_t challenge_blob_len;
+ } ccache_ntlm_auth;
+ struct {
+ fstring domain_name;
+ fstring domain_guid;
+ fstring site_name;
+ uint32_t flags;
+ } dsgetdcname;
+
+ /* padding -- needed to fix alignment between 32bit and 64bit libs.
+ The size is the sizeof the union without the padding aligned on
+ an 8 byte boundary. --jerry */
+
+ char padding[1800];
+ } data;
+ union {
+ SMB_TIME_T padding;
+ char *data;
+ } extra_data;
+ uint32_t extra_len;
+ char null_term;
+};
+
+/* Response values */
+
+enum winbindd_result {
+ WINBINDD_ERROR,
+ WINBINDD_PENDING,
+ WINBINDD_OK
+};
+
+/* Winbind response structure */
+
+/*******************************************************************************
+ * This structure MUST be the same size in the 32bit and 64bit builds
+ * for compatibility between /lib64/libnss_winbind.so and /lib/libnss_winbind.so
+ *
+ * DO NOT CHANGE THIS STRUCTURE WITHOUT TESTING THE 32BIT NSS LIB AGAINST
+ * A 64BIT WINBINDD --jerry
+ ******************************************************************************/
+
+struct winbindd_response {
+
+ /* Header information */
+
+ uint32_t length; /* Length of response */
+ enum winbindd_result result; /* Result code */
+
+ /* Fixed length return data */
+
+ union {
+ int interface_version; /* Try to ensure this is always in the same spot... */
+
+ fstring winsresp; /* WINS response */
+
+ /* getpwnam, getpwuid */
+
+ struct winbindd_pw pw;
+
+ /* getgrnam, getgrgid */
+
+ struct winbindd_gr gr;
+
+ uint32_t num_entries; /* getpwent, getgrent */
+ struct winbindd_sid {
+ fstring sid; /* lookupname, [ug]id_to_sid */
+ int type;
+ } sid;
+ struct winbindd_name {
+ fstring dom_name; /* lookupsid */
+ fstring name;
+ int type;
+ } name;
+ uid_t uid; /* sid_to_uid */
+ gid_t gid; /* sid_to_gid */
+ struct winbindd_info {
+ char winbind_separator;
+ fstring samba_version;
+ } info;
+ fstring domain_name;
+ fstring netbios_name;
+ fstring dc_name;
+
+ struct auth_reply {
+ uint32_t nt_status;
+ fstring nt_status_string;
+ fstring error_string;
+ int pam_error;
+ char user_session_key[16];
+ char first_8_lm_hash[8];
+ fstring krb5ccname;
+ uint32_t reject_reason;
+ uint32_t padding;
+ struct policy_settings {
+ uint32_t min_length_password;
+ uint32_t password_history;
+ uint32_t password_properties;
+ uint32_t padding;
+ SMB_TIME_T expire;
+ SMB_TIME_T min_passwordage;
+ } policy;
+ struct info3_text {
+ SMB_TIME_T logon_time;
+ SMB_TIME_T logoff_time;
+ SMB_TIME_T kickoff_time;
+ SMB_TIME_T pass_last_set_time;
+ SMB_TIME_T pass_can_change_time;
+ SMB_TIME_T pass_must_change_time;
+ uint32_t logon_count;
+ uint32_t bad_pw_count;
+ uint32_t user_rid;
+ uint32_t group_rid;
+ uint32_t num_groups;
+ uint32_t user_flgs;
+ uint32_t acct_flags;
+ uint32_t num_other_sids;
+ fstring dom_sid;
+ fstring user_name;
+ fstring full_name;
+ fstring logon_script;
+ fstring profile_path;
+ fstring home_dir;
+ fstring dir_drive;
+ fstring logon_srv;
+ fstring logon_dom;
+ } info3;
+ fstring unix_username;
+ } auth;
+ struct {
+ fstring name;
+ fstring alt_name;
+ fstring sid;
+ bool native_mode;
+ bool active_directory;
+ bool primary;
+ } domain_info;
+ uint32_t sequence_number;
+ struct {
+ fstring acct_name;
+ fstring full_name;
+ fstring homedir;
+ fstring shell;
+ uint32_t primary_gid;
+ uint32_t group_rid;
+ } user_info;
+ struct {
+ uint32_t auth_blob_len; /* blob in extra_data */
+ } ccache_ntlm_auth;
+ struct {
+ fstring dc_unc;
+ fstring dc_address;
+ uint32_t dc_address_type;
+ fstring domain_guid;
+ fstring domain_name;
+ fstring forest_name;
+ uint32_t dc_flags;
+ fstring dc_site_name;
+ fstring client_site_name;
+ } dsgetdcname;
+ } data;
+
+ /* Variable length return data */
+
+ union {
+ SMB_TIME_T padding;
+ void *data;
+ } extra_data;
+};
+
+struct WINBINDD_MEMORY_CREDS {
+ struct WINBINDD_MEMORY_CREDS *next, *prev;
+ const char *username; /* lookup key. */
+ uid_t uid;
+ int ref_count;
+ size_t len;
+ uint8_t *nt_hash; /* Base pointer for the following 2 */
+ uint8_t *lm_hash;
+ char *pass;
+};
+
+struct WINBINDD_CCACHE_ENTRY {
+ struct WINBINDD_CCACHE_ENTRY *next, *prev;
+ const char *principal_name;
+ const char *ccname;
+ const char *service;
+ const char *username;
+ const char *realm;
+ struct WINBINDD_MEMORY_CREDS *cred_ptr;
+ int ref_count;
+ uid_t uid;
+ time_t create_time;
+ time_t renew_until;
+ time_t refresh_time;
+ struct timed_event *event;
+};
+
+#endif
diff --git a/nsswitch/wins.c b/nsswitch/wins.c
new file mode 100644
index 0000000000..80f0119108
--- /dev/null
+++ b/nsswitch/wins.c
@@ -0,0 +1,446 @@
+/*
+ Unix SMB/CIFS implementation.
+ a WINS nsswitch module
+ Copyright (C) Andrew Tridgell 1999
+
+ 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"
+#ifdef HAVE_NS_API_H
+
+#include <ns_daemon.h>
+#endif
+
+#if HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+#if HAVE_PTHREAD
+static pthread_mutex_t wins_nss_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+#ifndef INADDRSZ
+#define INADDRSZ 4
+#endif
+
+static int initialised;
+
+extern bool AllowDebugChange;
+
+NSS_STATUS _nss_wins_gethostbyname_r(const char *hostname, struct hostent *he,
+ char *buffer, size_t buflen, int *h_errnop);
+NSS_STATUS _nss_wins_gethostbyname2_r(const char *name, int af, struct hostent *he,
+ char *buffer, size_t buflen, int *h_errnop);
+
+/* Use our own create socket code so we don't recurse.... */
+
+static int wins_lookup_open_socket_in(void)
+{
+ struct sockaddr_in sock;
+ int val=1;
+ int res;
+
+ memset((char *)&sock,'\0',sizeof(sock));
+
+#ifdef HAVE_SOCK_SIN_LEN
+ sock.sin_len = sizeof(sock);
+#endif
+ sock.sin_port = 0;
+ sock.sin_family = AF_INET;
+ sock.sin_addr.s_addr = interpret_addr("0.0.0.0");
+ res = socket(AF_INET, SOCK_DGRAM, 0);
+ if (res == -1)
+ return -1;
+
+ if (setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)) != 0) {
+ close(res);
+ return -1;
+ }
+#ifdef SO_REUSEPORT
+ if (setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val)) != 0) {
+ close(res);
+ return -1;
+ }
+#endif /* SO_REUSEPORT */
+
+ /* now we've got a socket - we need to bind it */
+
+ if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) {
+ close(res);
+ return(-1);
+ }
+
+ set_socket_options(res,"SO_BROADCAST");
+
+ return res;
+}
+
+
+static void nss_wins_init(void)
+{
+ initialised = 1;
+ DEBUGLEVEL = 0;
+ AllowDebugChange = False;
+
+ TimeInit();
+ setup_logging("nss_wins",False);
+ load_case_tables();
+ lp_load(get_dyn_CONFIGFILE(),True,False,False,True);
+ load_interfaces();
+}
+
+static struct in_addr *lookup_byname_backend(const char *name, int *count)
+{
+ int fd = -1;
+ struct ip_service *address = NULL;
+ struct in_addr *ret = NULL;
+ int j, flags = 0;
+
+ if (!initialised) {
+ nss_wins_init();
+ }
+
+ *count = 0;
+
+ /* always try with wins first */
+ if (NT_STATUS_IS_OK(resolve_wins(name,0x00,&address,count))) {
+ if ( (ret = SMB_MALLOC_P(struct in_addr)) == NULL ) {
+ free( address );
+ return NULL;
+ }
+ if (address[0].ss.ss_family != AF_INET) {
+ free(address);
+ free(ret);
+ return NULL;
+ }
+ *ret = ((struct sockaddr_in *)&address[0].ss)->sin_addr;
+ free( address );
+ return ret;
+ }
+
+ fd = wins_lookup_open_socket_in();
+ if (fd == -1) {
+ return NULL;
+ }
+
+ /* uggh, we have to broadcast to each interface in turn */
+ for (j=iface_count() - 1;j >= 0;j--) {
+ const struct in_addr *bcast = iface_n_bcast_v4(j);
+ struct sockaddr_storage ss;
+ struct sockaddr_storage *pss;
+ if (!bcast) {
+ continue;
+ }
+ in_addr_to_sockaddr_storage(&ss, *bcast);
+ pss = name_query(fd,name,0x00,True,True,&ss,count, &flags, NULL);
+ if (pss) {
+ if ((ret = SMB_MALLOC_P(struct in_addr)) == NULL) {
+ return NULL;
+ }
+ *ret = ((struct sockaddr_in *)pss)->sin_addr;
+ break;
+ }
+ }
+
+ close(fd);
+ return ret;
+}
+
+#ifdef HAVE_NS_API_H
+
+static NODE_STATUS_STRUCT *lookup_byaddr_backend(char *addr, int *count)
+{
+ int fd;
+ struct sockaddr_storage ss;
+ struct nmb_name nname;
+ NODE_STATUS_STRUCT *status;
+
+ if (!initialised) {
+ nss_wins_init();
+ }
+
+ fd = wins_lookup_open_socket_in();
+ if (fd == -1)
+ return NULL;
+
+ make_nmb_name(&nname, "*", 0);
+ if (!interpret_string_addr(&ss, addr, AI_NUMERICHOST)) {
+ return NULL;
+ }
+ status = node_status_query(fd, &nname, &ss, count, NULL);
+
+ close(fd);
+ return status;
+}
+
+/* IRIX version */
+
+int init(void)
+{
+ nsd_logprintf(NSD_LOG_MIN, "entering init (wins)\n");
+ nss_wins_init();
+ return NSD_OK;
+}
+
+int lookup(nsd_file_t *rq)
+{
+ char *map;
+ char *key;
+ char *addr;
+ struct in_addr *ip_list;
+ NODE_STATUS_STRUCT *status;
+ int i, count, len, size;
+ char response[1024];
+ bool found = False;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering lookup (wins)\n");
+ if (! rq)
+ return NSD_ERROR;
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ if (! map) {
+ rq->f_status = NS_FATAL;
+ return NSD_ERROR;
+ }
+
+ key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
+ if (! key || ! *key) {
+ rq->f_status = NS_FATAL;
+ return NSD_ERROR;
+ }
+
+ response[0] = '\0';
+ len = sizeof(response) - 2;
+
+ /*
+ * response needs to be a string of the following format
+ * ip_address[ ip_address]*\tname[ alias]*
+ */
+ if (StrCaseCmp(map,"hosts.byaddr") == 0) {
+ if ( status = lookup_byaddr_backend(key, &count)) {
+ size = strlen(key) + 1;
+ if (size > len) {
+ free(status);
+ return NSD_ERROR;
+ }
+ len -= size;
+ strncat(response,key,size);
+ strncat(response,"\t",1);
+ for (i = 0; i < count; i++) {
+ /* ignore group names */
+ if (status[i].flags & 0x80) continue;
+ if (status[i].type == 0x20) {
+ size = sizeof(status[i].name) + 1;
+ if (size > len) {
+ free(status);
+ return NSD_ERROR;
+ }
+ len -= size;
+ strncat(response, status[i].name, size);
+ strncat(response, " ", 1);
+ found = True;
+ }
+ }
+ response[strlen(response)-1] = '\n';
+ free(status);
+ }
+ } else if (StrCaseCmp(map,"hosts.byname") == 0) {
+ if (ip_list = lookup_byname_backend(key, &count)) {
+ for (i = count; i ; i--) {
+ addr = inet_ntoa(ip_list[i-1]);
+ size = strlen(addr) + 1;
+ if (size > len) {
+ free(ip_list);
+ return NSD_ERROR;
+ }
+ len -= size;
+ if (i != 0)
+ response[strlen(response)-1] = ' ';
+ strncat(response,addr,size);
+ strncat(response,"\t",1);
+ }
+ size = strlen(key) + 1;
+ if (size > len) {
+ free(ip_list);
+ return NSD_ERROR;
+ }
+ strncat(response,key,size);
+ strncat(response,"\n",1);
+ found = True;
+ free(ip_list);
+ }
+ }
+
+ if (found) {
+ nsd_logprintf(NSD_LOG_LOW, "lookup (wins %s) %s\n",map,response);
+ nsd_set_result(rq,NS_SUCCESS,response,strlen(response),VOLATILE);
+ return NSD_OK;
+ }
+ nsd_logprintf(NSD_LOG_LOW, "lookup (wins) not found\n");
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+}
+
+#else
+
+/* Allocate some space from the nss static buffer. The buffer and buflen
+ are the pointers passed in by the C library to the _nss_*_*
+ functions. */
+
+static char *get_static(char **buffer, size_t *buflen, int len)
+{
+ char *result;
+
+ /* Error check. We return false if things aren't set up right, or
+ there isn't enough buffer space left. */
+
+ if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
+ return NULL;
+ }
+
+ /* Return an index into the static buffer */
+
+ result = *buffer;
+ *buffer += len;
+ *buflen -= len;
+
+ return result;
+}
+
+/****************************************************************************
+gethostbyname() - we ignore any domain portion of the name and only
+handle names that are at most 15 characters long
+ **************************************************************************/
+NSS_STATUS
+_nss_wins_gethostbyname_r(const char *hostname, struct hostent *he,
+ char *buffer, size_t buflen, int *h_errnop)
+{
+ NSS_STATUS nss_status = NSS_STATUS_SUCCESS;
+ struct in_addr *ip_list;
+ int i, count;
+ fstring name;
+ size_t namelen;
+
+#if HAVE_PTHREAD
+ pthread_mutex_lock(&wins_nss_mutex);
+#endif
+
+ memset(he, '\0', sizeof(*he));
+ fstrcpy(name, hostname);
+
+ /* Do lookup */
+
+ ip_list = lookup_byname_backend(name, &count);
+
+ if (!ip_list) {
+ nss_status = NSS_STATUS_NOTFOUND;
+ goto out;
+ }
+
+ /* Copy h_name */
+
+ namelen = strlen(name) + 1;
+
+ if ((he->h_name = get_static(&buffer, &buflen, namelen)) == NULL) {
+ free(ip_list);
+ nss_status = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+
+ memcpy(he->h_name, name, namelen);
+
+ /* Copy h_addr_list, align to pointer boundary first */
+
+ if ((i = (unsigned long)(buffer) % sizeof(char*)) != 0)
+ i = sizeof(char*) - i;
+
+ if (get_static(&buffer, &buflen, i) == NULL) {
+ free(ip_list);
+ nss_status = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+
+ if ((he->h_addr_list = (char **)get_static(
+ &buffer, &buflen, (count + 1) * sizeof(char *))) == NULL) {
+ free(ip_list);
+ nss_status = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+
+ for (i = 0; i < count; i++) {
+ if ((he->h_addr_list[i] = get_static(&buffer, &buflen,
+ INADDRSZ)) == NULL) {
+ free(ip_list);
+ nss_status = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+ memcpy(he->h_addr_list[i], &ip_list[i], INADDRSZ);
+ }
+
+ he->h_addr_list[count] = NULL;
+
+ free(ip_list);
+
+ /* Set h_addr_type and h_length */
+
+ he->h_addrtype = AF_INET;
+ he->h_length = INADDRSZ;
+
+ /* Set h_aliases */
+
+ if ((i = (unsigned long)(buffer) % sizeof(char*)) != 0)
+ i = sizeof(char*) - i;
+
+ if (get_static(&buffer, &buflen, i) == NULL) {
+ nss_status = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+
+ if ((he->h_aliases = (char **)get_static(
+ &buffer, &buflen, sizeof(char *))) == NULL) {
+ nss_status = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+
+ he->h_aliases[0] = NULL;
+
+ nss_status = NSS_STATUS_SUCCESS;
+
+ out:
+
+#if HAVE_PTHREAD
+ pthread_mutex_unlock(&wins_nss_mutex);
+#endif
+ return nss_status;
+}
+
+
+NSS_STATUS
+_nss_wins_gethostbyname2_r(const char *name, int af, struct hostent *he,
+ char *buffer, size_t buflen, int *h_errnop)
+{
+ NSS_STATUS nss_status;
+
+ if(af!=AF_INET) {
+ *h_errnop = NO_DATA;
+ nss_status = NSS_STATUS_UNAVAIL;
+ } else {
+ nss_status = _nss_wins_gethostbyname_r(
+ name, he, buffer, buflen, h_errnop);
+ }
+ return nss_status;
+}
+#endif