summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRich Megginson <rmeggins@redhat.com>2005-02-05 01:30:00 +0000
committerRich Megginson <rmeggins@redhat.com>2005-02-05 01:30:00 +0000
commit9eb2b56ec631aa3fffc9a80afcbe40f6cc645d8a (patch)
treefb1d804bace42936ec86cab5f1cc55338a94b8e7
parentca998e8f9dfa4e306433eb58b139825684c7fc0a (diff)
downloadds-9eb2b56ec631aa3fffc9a80afcbe40f6cc645d8a.tar.gz
ds-9eb2b56ec631aa3fffc9a80afcbe40f6cc645d8a.tar.xz
ds-9eb2b56ec631aa3fffc9a80afcbe40f6cc645d8a.zip
checkin first version of pam plugin
-rw-r--r--ldap/servers/plugins/pam_passthru/Makefile93
-rw-r--r--ldap/servers/plugins/pam_passthru/README184
-rw-r--r--ldap/servers/plugins/pam_passthru/config.ldif20
-rw-r--r--ldap/servers/plugins/pam_passthru/libpam_passthru.def13
-rw-r--r--ldap/servers/plugins/pam_passthru/pam_passthru.h104
-rw-r--r--ldap/servers/plugins/pam_passthru/pam_ptconfig.c539
-rw-r--r--ldap/servers/plugins/pam_passthru/pam_ptdebug.c22
-rw-r--r--ldap/servers/plugins/pam_passthru/pam_ptdllmain.c130
-rw-r--r--ldap/servers/plugins/pam_passthru/pam_ptimpl.c397
-rw-r--r--ldap/servers/plugins/pam_passthru/pam_ptpreop.c221
10 files changed, 1723 insertions, 0 deletions
diff --git a/ldap/servers/plugins/pam_passthru/Makefile b/ldap/servers/plugins/pam_passthru/Makefile
new file mode 100644
index 00000000..c71fdea7
--- /dev/null
+++ b/ldap/servers/plugins/pam_passthru/Makefile
@@ -0,0 +1,93 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2005 Red Hat
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server "PAM Pass Through Authentication" plugin
+#
+#
+
+LDAP_SRC = ../../..
+BUILD_ROOT = ../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libpam_passthru
+LIBDIR = $(LIB_RELDIR)
+
+include $(BUILD_ROOT)/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./libpam_passthru.def
+endif
+
+CFLAGS+=$(SLCFLAGS)
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd
+
+PAM_PASSTHRU_OBJS= pam_ptimpl.o pam_ptconfig.o pam_ptdebug.o pam_ptpreop.o
+
+OBJS = $(addprefix $(OBJDEST)/, $(PAM_PASSTHRU_OBJS))
+
+ifeq ($(ARCH), WINNT)
+LIBPAM_PASSTHRU_DLL_OBJ = $(addprefix $(OBJDEST)/, pam_ptdllmain.o)
+endif
+
+LIBPAM_PASSTHRU= $(addprefix $(LIBDIR)/, $(PAM_PASSTHRU_DLL).$(DLL_SUFFIX))
+
+EXTRA_LIBS += -lpam
+#LD += -Xlinker --no-undefined -Xlinker --no-allow-shlib-undefined
+#LD += -Xlinker --export-dynamic
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL) $(NSPRLINK)
+endif
+
+
+ifeq ($(ARCH), WINNT)
+DLL_LDFLAGS += -def:"./libpam_passthru.def"
+endif # WINNT
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPDLINK) $(LDAP_SDK_LIBLDAP_DLL) $(NSPRLINK)
+EXTRA_LIBS += $(DLL_EXTRA_LIBS)
+LD=ld
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(LIBPAM_PASSTHRU)
+
+$(LIBPAM_PASSTHRU): $(OBJS) $(LIBPAM_PASSTHRU_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(LIBPAM_PASSTHRU_DLL_OBJ) $(PLATFORMLIBS) $(EXTRA_LIBS)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(LIBPAM_PASSTHRU_DLL_OBJ)
+endif
+ $(RM) $(LIBPAM_PASSTHRU)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+#
+# header file dependencies (incomplete)
+#
+$(OBJS): pam_passthru.h
diff --git a/ldap/servers/plugins/pam_passthru/README b/ldap/servers/plugins/pam_passthru/README
new file mode 100644
index 00000000..efbc48a3
--- /dev/null
+++ b/ldap/servers/plugins/pam_passthru/README
@@ -0,0 +1,184 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2005 Red Hat
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+PAM pass through authentication plugin for Directory Server
+
+Introduction
+
+Many organizations have authentication mechanisms
+already in place. They may not want to have the LDAP server be the
+central repository for authentication credentials and the
+authentication mechanism. The typical deployment would use PAM as the
+gateway to authentication. They do want to have many apps use the LDAP
+server for authentication and for authorization, user information,
+etc., just not as the authoritative data source for
+credentials. GSS/SASL is typically used for this e.g. for Kerberos,
+you can use your ticket to authenticate to the DS - the DS "passes
+through" the authentication to Kerberos. But many apps cannot (or will
+not) use SASL as their authentication mechanism - they must use simple
+cleartext password BINDs. For these applications, it would be very
+useful to have the DS pass through the auth creds to PAM.
+
+BIND Preoperation Plugin for PAM
+
+The PAM BIND Preoperation Plugin intercepts the BIND request and uses
+the PAM API to authenticate the user.
+
+If PAM supports password expiration or creation, how to handle that
+with LDAP? Use standard LDAP mechanisms for password expiration
+handling? Should probably make this configurable - ignore and error
+out vs. sending back an appropriate control or error code.
+
+Configuration
+
+The administrator must be able to configure the following options in
+the plugin. These are the attributes of the objectclass pamConfig
+which is one of the objectclasses of the plugin entry:
+
+* subtrees (list of DNs) - suffixes and/or subtrees to which this applies
+ o pamExcludeSuffix - suffixes to be excluded from checking
+ o pamIncludeSuffix - suffixes to be included in checking and exclude all others
+ o excludes "win" in case of duplicates
+ o default is to apply to all suffixes if no includes or excludes are specified
+* pamMissingSuffix (string)
+ o ERROR: error and abort if excluded or included suffix does not exist
+ o ALLOW (default): warn if exclude or include is missing, but continue
+ o IGNORE: allow missing suffixes and don't log error
+* pamFallback (boolean) - if false, if PAM auth fails, the BIND
+ operation fails. If true, if PAM auth fails, the DS will attempt other
+ BIND mechanisms e.g. userPassword.
+* pamSecure (boolean) - if true, a secure connection is required
+* pamIDAttr (string) - The value of this attribute, present in the
+ user's entry, holds the PAM identity of the user - it maps the LDAP
+ identity to the PAM identity
+* pamMapMethod (string)
+ o RDN (default) - uses the value from the leftmost RDN in the BIND DN
+ o ENTRY - gets the value of the PAM identity attribute from the BIND DN entry
+ o DN - uses the full DN string
+ o NOTE: if ENTRY is specified as the method, pamIDAttr must
+ be set in the plugin config entry, and user entries should have the
+ named attribute
+* pamService (string) - the service argument to pam_start()
+* others to keep statistics - TBD
+
+Design
+
+BIND -> this plugin -> get config -> make sure arguments and state
+conform to config settings -> pam_start() -> pam handshakes -> get
+auth status -> pam_end() -> report BIND status back to LDAP client
+
+The big problem is - how to map the BIND DN to the user name given to
+pam_start(). There are a couple of different ways to do this. One way
+is to just use the value part of the leftmost RDN in the BIND DN
+e.g. if you bound as uid=richm,ou=people,dc=redhat,dc=com, you would
+pass "richm" to PAM. Another way is to specify some attribute that
+must exist in the user's entry and use that value e.g. if my entry
+looks like this:
+
+dn: uid=richm,ou=people,dc=redhat,dc=com
+...
+objectclass: inetOrgPerson
+objectclass: redHatOrgPerson # has the extra attr
+...
+rhuid: rmeggins
+...
+
+"rhuid" would be specified in the PAM plugin config. When I bind as
+uid=richm, the plugin would look up my entry, get the value of the
+"rhuid" attribute, and use that value for PAM.
+
+The password is the same password passed in as the BIND credentials.
+
+We do not need to worry about PAM sessions - all we want to do is use
+PAM to verify the auth creds. However, we could implement sessions -
+we could do a pam_open_session() upon bind success and a
+pam_close_session() upon rebind, unbind, closure, or
+shutdown. However, this adds considerable complexity - must persist
+the pam_handle_t throughout the connection (probably in a connection
+extension), must ensure thread safe access to connection extension
+resources, must ensure clean up in a variety of situations. So, best
+to avoid it if possible.
+
+We may have to worry about different PAM policy in different subtrees
+e.g. maybe for dc=coke,dc=com you want to use the ENTRY map method,
+but for dc=pepsi,dc=com you want to use the RDN method. We could
+probably do this by having the pamMapMethod attr be multivalued, and
+have it's value like this:
+
+pamMapMethod: RDN dc=coke,dc=com
+pamMapMethod: RDN dc=sprite,dc=com
+pamMapMethod: ENTRY dc=pepsi,dc=com
+pamMapMethod: DN (the default for all other suffixes)
+
+The suffix that uses that map method would follow the map method used.
+
+We need to worry about account expiration or lockout e.g. the user's
+credentials are valid but the user has been locked out of his/her
+account, or the password has expired, or something like that. Some of
+this can be handled by LDAP e.g. returning password policy control
+values when the password has expired. So we need to call
+pam_acct_mgmt() somewhere during the pam handshakes and before
+pam_end() to get this information. We also try to return an
+appropriate LDAP error code.
+PAM Error Code LDAP Error Code Meaning
+PAM_USER_UNKNOWN LDAP_NO_SUCH_OBJECT User ID does not exist
+PAM_AUTH_ERROR LDAP_INVALID_CREDENTIALS Password is not correct
+PAM_ACCT_EXPIRED LDAP_INVALID_CREDENTIALS User's password is expired
+PAM_PERM_DENIED LDAP_UNWILLING_TO_PERFORM User's account is locked out
+PAM_NEW_AUTHTOK_REQD LDAP_INVALID_CREDENTIALS User's password has expired and must be renewed
+PAM_MAXTRIES LDAP_CONSTRAINT_VIOLATION Max retry count has been exceeded
+Other codes LDAP_OPERATIONS_ERROR PAM config is incorrect, machine problem, etc.
+There are three controls we might possibly add to the response:
+* the auth response control - returned upon success - contains the BIND DN (u: not currently supported)
+* LDAP_CONTROL_PWEXPIRED - returned when PAM reports ACCT_EXPIRED or NEW_AUTHTOK_REQD
+* the new password policy control - returned when PAM reports
+ ACCT_EXPIRED, NEW_AUTHTOK_REQD, PERM_DENIED, MAXTRIES The controls can
+ be used to get more information in the case of error (password
+ incorrect or expired?).
+
+The latter two must be requested by the client.
+
+The plugin should report status in attributes of the plugin entry
+e.g. successfuls auth attempts, failed attempts, last pam error code
+and message, etc. We could also do this in an entry under
+cn=monitor. TBD.
+
+Configuration
+
+1. Shutdown the server
+2. Make sure the slapd-instance/config/schema contains the 60pam-config.ldif file
+3. Make sure serverroot/lib/pam-passthru-plugin.so exists
+4. Make sure /etc/pam.d/ldapserver exists and is configured correctly
+5. If the configuration is not already in dse.ldif, append the following to slapd-instance/config/dse.ldif
+
+dn: cn=PAM Pass Through Auth,cn=plugins,cn=config
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+objectclass: pamConfig
+cn: PAM Pass Through Auth
+nsslapd-pluginpath: /opt/ldapserver/lib/pam-passthru-plugin.so
+nsslapd-plugininitfunc: pam_passthruauth_init
+nsslapd-plugintype: preoperation
+nsslapd-pluginenabled: on
+nsslapd-pluginLoadGlobal: true
+nsslapd-plugin-depends-on-type: database
+pamMissingSuffix: ALLOW
+pamExcludeSuffix: o=NetscapeRoot
+pamExcludeSuffix: cn=config
+pamMapMethod: RDN
+pamFallback: FALSE
+pamSecure: TRUE
+pamService: ldapserver
+
+Make sure there is a blank line at the end. The line with
+o=NetscapeRoot may be omitted if this is not a configuration DS. Then
+restart slapd.
+
+See Also
+PAM API for Linux http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/pam_appl.html
+PAM API for Solaris Writing PAM Applications and Services from the Solaris Security for Developers Guide http://docs.sun.com/app/docs/doc/816-4863/6mb20lvfh?a=view
+PAM API for HP-UX http://docs.hp.com/en/B2355-60103/pam.3.html
diff --git a/ldap/servers/plugins/pam_passthru/config.ldif b/ldap/servers/plugins/pam_passthru/config.ldif
new file mode 100644
index 00000000..2dd9065c
--- /dev/null
+++ b/ldap/servers/plugins/pam_passthru/config.ldif
@@ -0,0 +1,20 @@
+dn: cn=PAM Pass Through Auth,cn=plugins,cn=config
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+objectclass: pamConfig
+cn: PAM Pass Through Auth
+nsslapd-pluginpath: /opt/ldapserver/lib/pam-passthru-plugin.so
+nsslapd-plugininitfunc: pam_passthruauth_init
+nsslapd-plugintype: preoperation
+nsslapd-pluginenabled: on
+nsslapd-pluginLoadGlobal: true
+nsslapd-plugin-depends-on-type: database
+pamMissingSuffix: ALLOW
+pamExcludeSuffix: o=NetscapeRoot
+pamExcludeSuffix: cn=config
+pamMapMethod: RDN
+pamFallback: FALSE
+pamSecure: TRUE
+pamService: ldapserver
+
diff --git a/ldap/servers/plugins/pam_passthru/libpam_passthru.def b/ldap/servers/plugins/pam_passthru/libpam_passthru.def
new file mode 100644
index 00000000..02c2f0f7
--- /dev/null
+++ b/ldap/servers/plugins/pam_passthru/libpam_passthru.def
@@ -0,0 +1,13 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2005 Red Hat
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+;
+;
+DESCRIPTION 'Directory Server Pass Through Authentication Plugin'
+;CODE SHARED READ EXECUTE
+;DATA SHARED READ WRITE
+EXPORTS
+ passthruauth_init @1
+ plugin_init_debug_level @2
diff --git a/ldap/servers/plugins/pam_passthru/pam_passthru.h b/ldap/servers/plugins/pam_passthru/pam_passthru.h
new file mode 100644
index 00000000..5b437880
--- /dev/null
+++ b/ldap/servers/plugins/pam_passthru/pam_passthru.h
@@ -0,0 +1,104 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2005 Red Hat
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * pam_passthru.h - Pass Through Authentication shared definitions
+ *
+ */
+
+#ifndef _PAM_PASSTHRU_H_
+#define _PAM_PASSTHRU_H_
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include "portable.h"
+#include "slapi-plugin.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+#include "dirver.h"
+#include <nspr.h>
+
+/* Private API: to get slapd_pr_strerror() and SLAPI_COMPONENT_NAME_NSPR */
+#include "slapi-private.h"
+
+/*
+ * macros
+ */
+#define PAM_PASSTHRU_PLUGIN_SUBSYSTEM "pam_passthru-plugin" /* for logging */
+
+#define PAM_PASSTHRU_ASSERT( expr ) PR_ASSERT( expr )
+
+#define PAM_PASSTHRU_OP_NOT_HANDLED 0
+#define PAM_PASSTHRU_OP_HANDLED 1
+
+/* #define PAM_PASSTHRU_VERBOSE_LOGGING */
+
+/*
+ * structs
+ */
+typedef struct pam_passthrusuffix {
+ Slapi_DN *pamptsuffix_dn;
+ struct pam_passthrusuffix *pamptsuffix_next;
+} Pam_PassthruSuffix;
+
+#define PAMPT_MISSING_SUFFIX_ERROR 0 /* error out if an included or excluded suffix is missing */
+#define PAMPT_MISSING_SUFFIX_ALLOW 1 /* allow but log missing suffixes */
+#define PAMPT_MISSING_SUFFIX_IGNORE 2 /* allow and don't log missing suffixes */
+
+#define PAMPT_MISSING_SUFFIX_ERROR_STRING "ERROR"
+#define PAMPT_MISSING_SUFFIX_ALLOW_STRING "ALLOW"
+#define PAMPT_MISSING_SUFFIX_IGNORE_STRING "IGNORE"
+
+typedef struct pam_passthruconfig {
+ Slapi_Mutex *lock; /* for config access */
+ Pam_PassthruSuffix *pamptconfig_includes; /* list of suffixes to include in this op */
+ Pam_PassthruSuffix *pamptconfig_excludes; /* list of suffixes to exclude in this op */
+ PRBool pamptconfig_fallback; /* if false, failure here fails entire bind */
+ /* if true, failure here falls through to regular bind */
+ PRBool pamptconfig_secure; /* if true, plugin only operates on secure connections */
+ char *pamptconfig_pam_ident_attr; /* name of attribute in user entry for ENTRY map method */
+ int pamptconfig_map_method1; /* how to map the BIND DN to the PAM identity */
+ int pamptconfig_map_method2; /* how to map the BIND DN to the PAM identity */
+ int pamptconfig_map_method3; /* how to map the BIND DN to the PAM identity */
+#define PAMPT_MAP_METHOD_NONE -1 /* do not map */
+#define PAMPT_MAP_METHOD_DN 0 /* use the full DN as the PAM identity */
+#define PAMPT_MAP_METHOD_RDN 1 /* use the leftmost RDN value as the PAM identity */
+#define PAMPT_MAP_METHOD_ENTRY 2 /* use the PAM identity attribute in the entry */
+ char *pamptconfig_service; /* the PAM service name for pam_start() */
+} Pam_PassthruConfig;
+
+#define PAMPT_MAP_METHOD_DN_STRING "DN"
+#define PAMPT_MAP_METHOD_RDN_STRING "RDN"
+#define PAMPT_MAP_METHOD_ENTRY_STRING "ENTRY"
+
+#define PAMPT_MISSING_SUFFIX_ATTR "pamMissingSuffix" /* single valued */
+#define PAMPT_EXCLUDES_ATTR "pamExcludeSuffix" /* multi valued */
+#define PAMPT_INCLUDES_ATTR "pamIncludeSuffix" /* multi valued */
+#define PAMPT_PAM_IDENT_ATTR "pamIDAttr" /* single valued (for now) */
+#define PAMPT_MAP_METHOD_ATTR "pamIDMapMethod" /* single valued */
+#define PAMPT_FALLBACK_ATTR "pamFallback" /* single */
+#define PAMPT_SECURE_ATTR "pamSecure" /* single */
+#define PAMPT_SERVICE_ATTR "pamService" /* single */
+
+/*
+ * public functions
+ */
+
+void pam_passthruauth_set_plugin_identity(void * identity);
+void * pam_passthruauth_get_plugin_identity();
+
+/*
+ * pam_ptconfig.c:
+ */
+int pam_passthru_config( Slapi_Entry *config_e );
+Pam_PassthruConfig *pam_passthru_get_config( void );
+int pam_passthru_check_suffix(Pam_PassthruConfig *cfg, char *binddn);
+
+/*
+ * pam_ptimpl.c
+ */
+int pam_passthru_do_pam_auth(Slapi_PBlock *pb, Pam_PassthruConfig *cfg);
+
+#endif /* _PAM_PASSTHRU_H_ */
diff --git a/ldap/servers/plugins/pam_passthru/pam_ptconfig.c b/ldap/servers/plugins/pam_passthru/pam_ptconfig.c
new file mode 100644
index 00000000..4ba28f9c
--- /dev/null
+++ b/ldap/servers/plugins/pam_passthru/pam_ptconfig.c
@@ -0,0 +1,539 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2005 Red Hat
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * ptconfig.c - configuration-related code for Pass Through Authentication
+ *
+ */
+
+#include <plstr.h>
+
+#include "pam_passthru.h"
+
+#define PAM_PT_CONFIG_FILTER "(objectclass=*)"
+#ifndef SLAPI_DSE_RETURNTEXT_SIZE
+#define SLAPI_DSE_RETURNTEXT_SIZE 512 /* for use by callback functions */
+#endif /* SLAPI_DSE_RETURNTEXT_SIZE */
+
+/*
+ * The configuration attributes are contained in the plugin entry e.g.
+ * cn=PAM Pass Through,cn=plugins,cn=config
+ *
+ * Configuration is a two step process. The first pass is a validation step which
+ * occurs pre-op - check inputs and error out if bad. The second pass actually
+ * applies the changes to the run time config.
+ */
+
+
+/*
+ * function prototypes
+ */
+static int pam_passthru_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg);
+static int pam_passthru_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg);
+static int pam_passthru_search (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg)
+{
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/*
+ * static variables
+ */
+/* for now, there is only one configuration and it is global to the plugin */
+static Pam_PassthruConfig theConfig;
+static int inited = 0;
+
+
+static int dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg)
+{
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+}
+
+/*
+ * Read configuration and create a configuration data structure.
+ * This is called after the server has configured itself so we can check
+ * for things like collisions between our suffixes and backend's suffixes.
+ * Returns an LDAP error code (LDAP_SUCCESS if all goes well).
+ */
+int
+pam_passthru_config(Slapi_Entry *config_e)
+{
+ int returncode = LDAP_SUCCESS;
+ char returntext[SLAPI_DSE_RETURNTEXT_SIZE];
+
+ if ( inited ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "only one PAM pass through plugin instance can be used\n" );
+ return( LDAP_PARAM_ERROR );
+ }
+
+ /* initialize fields */
+ if ((theConfig.lock = slapi_new_mutex()) == NULL) {
+ return( LDAP_LOCAL_ERROR );
+ }
+ /* do not fallback to regular bind */
+ theConfig.pamptconfig_fallback = PR_FALSE;
+ /* require TLS/SSL security */
+ theConfig.pamptconfig_secure = PR_TRUE;
+ /* use the RDN method to derive the PAM identity */
+ theConfig.pamptconfig_map_method1 = PAMPT_MAP_METHOD_RDN;
+ theConfig.pamptconfig_map_method2 = PAMPT_MAP_METHOD_NONE;
+ theConfig.pamptconfig_map_method3 = PAMPT_MAP_METHOD_NONE;
+
+ if (SLAPI_DSE_CALLBACK_OK == pam_passthru_validate_config(NULL, NULL, config_e,
+ &returncode, returntext, NULL)) {
+ pam_passthru_apply_config(NULL, NULL, config_e,
+ &returncode, returntext, NULL);
+ }
+
+ /* config DSE must be initialized before we get here */
+ if (returncode == LDAP_SUCCESS) {
+ const char *config_dn = slapi_entry_get_dn_const(config_e);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE,
+ PAM_PT_CONFIG_FILTER, pam_passthru_validate_config,NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, config_dn, LDAP_SCOPE_BASE,
+ PAM_PT_CONFIG_FILTER, pam_passthru_apply_config,NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_MODRDN, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE,
+ PAM_PT_CONFIG_FILTER, dont_allow_that, NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE,
+ PAM_PT_CONFIG_FILTER, dont_allow_that, NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE,
+ PAM_PT_CONFIG_FILTER, pam_passthru_search,NULL);
+ }
+
+ inited = 1;
+
+ if (returncode != LDAP_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "Error %d: %s\n", returncode, returntext);
+ }
+
+ return returncode;
+}
+
+static int
+missing_suffix_to_int(char *missing_suffix)
+{
+ int retval = -1; /* -1 is error */
+ if (!PL_strcasecmp(missing_suffix, PAMPT_MISSING_SUFFIX_ERROR_STRING)) {
+ retval = PAMPT_MISSING_SUFFIX_ERROR;
+ } else if (!PL_strcasecmp(missing_suffix, PAMPT_MISSING_SUFFIX_ALLOW_STRING)) {
+ retval = PAMPT_MISSING_SUFFIX_ALLOW;
+ } else if (!PL_strcasecmp(missing_suffix, PAMPT_MISSING_SUFFIX_IGNORE_STRING)) {
+ retval = PAMPT_MISSING_SUFFIX_IGNORE;
+ }
+
+ return retval;
+}
+
+static PRBool
+check_missing_suffix_flag(int val) {
+ if (val == PAMPT_MISSING_SUFFIX_ERROR ||
+ val == PAMPT_MISSING_SUFFIX_ALLOW ||
+ val == PAMPT_MISSING_SUFFIX_IGNORE) {
+ return PR_TRUE;
+ }
+
+ return PR_FALSE;
+}
+
+#define MAKE_STR(x) #x
+static char *get_missing_suffix_values()
+{
+ return MAKE_STR(PAMPT_MISSING_SUFFIX_ERROR) ", " MAKE_STR(PAMPT_MISSING_SUFFIX_ALLOW) ", "
+ MAKE_STR(PAMPT_MISSING_SUFFIX_IGNORE);
+}
+
+static char *get_map_method_values()
+{
+ return PAMPT_MAP_METHOD_DN_STRING " or " PAMPT_MAP_METHOD_RDN_STRING " or " PAMPT_MAP_METHOD_ENTRY_STRING;
+}
+
+static int
+meth_to_int(char **map_method, int *err)
+{
+ char *end;
+ int len;
+ int ret;
+
+ *err = 0;
+ if (!map_method || !*map_method) {
+ return PAMPT_MAP_METHOD_NONE;
+ }
+
+ end = strchr(*map_method, ' ');
+ if (!end) {
+ len = strlen(*map_method);
+ } else {
+ len = end - *map_method;
+ }
+ if (!PL_strncasecmp(*map_method, PAMPT_MAP_METHOD_DN_STRING, len)) {
+ ret = PAMPT_MAP_METHOD_DN;
+ } else if (!PL_strncasecmp(*map_method, PAMPT_MAP_METHOD_RDN_STRING, len)) {
+ ret = PAMPT_MAP_METHOD_RDN;
+ } else if (!PL_strncasecmp(*map_method, PAMPT_MAP_METHOD_ENTRY_STRING, len)) {
+ ret = PAMPT_MAP_METHOD_ENTRY;
+ } else {
+ *err = 1;
+ }
+
+ if (!err) {
+ if (end && *end) {
+ *map_method = end + 1;
+ } else {
+ *map_method = NULL;
+ }
+ }
+
+ return ret;
+}
+
+static int
+parse_map_method(char *map_method, int *one, int *two, int *three, char *returntext)
+{
+ int err = 0;
+ int extra;
+
+ *one = *two = *three = PAMPT_MAP_METHOD_NONE;
+ *one = meth_to_int(&map_method, &err);
+ if (err) {
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "The map method in the string [%s] is invalid: must be "
+ "one of %s", map_method, get_map_method_values());
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ *two = meth_to_int(&map_method, &err);
+ if (err) {
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "The map method in the string [%s] is invalid: must be "
+ "one of %s", map_method, get_map_method_values());
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ *three = meth_to_int(&map_method, &err);
+ if (err) {
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "The map method in the string [%s] is invalid: must be "
+ "one of %s", map_method, get_map_method_values());
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ if (((extra = meth_to_int(&map_method, &err)) != PAMPT_MAP_METHOD_NONE) ||
+ err) {
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "Invalid extra text [%s] after last map method",
+ map_method);
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ return err;
+}
+
+/*
+ Validate the pending changes in the e entry.
+*/
+static int
+pam_passthru_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg)
+{
+ char *missing_suffix_str = NULL;
+ int missing_suffix;
+ int ii;
+ char **excludes = NULL;
+ char **includes = NULL;
+ char *pam_ident_attr = NULL;
+ char *map_method = NULL;
+
+ *returncode = LDAP_UNWILLING_TO_PERFORM; /* be pessimistic */
+ /* first, get the missing_suffix flag and validate it */
+ missing_suffix_str = slapi_entry_attr_get_charptr(e, PAMPT_MISSING_SUFFIX_ATTR);
+ if ((missing_suffix = missing_suffix_to_int(missing_suffix_str)) < 0 ||
+ !check_missing_suffix_flag(missing_suffix)) {
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "Error: valid values for %s are %s",
+ PAMPT_MISSING_SUFFIX_ATTR, get_missing_suffix_values());
+ goto done;
+ }
+
+ if (missing_suffix != PAMPT_MISSING_SUFFIX_IGNORE) {
+ char **missing_list = NULL;
+ Slapi_DN *comp_dn = slapi_sdn_new();
+
+ /* get the list of excluded suffixes */
+ excludes = slapi_entry_attr_get_charray(e, PAMPT_EXCLUDES_ATTR);
+ for (ii = 0; excludes && excludes[ii]; ++ii) {
+ slapi_sdn_init_dn_byref(comp_dn, excludes[ii]);
+ if (!slapi_be_exist(comp_dn)) {
+ charray_add(&missing_list, slapi_ch_strdup(excludes[ii]));
+ }
+ slapi_sdn_done(comp_dn);
+ }
+
+ /* get the list of included suffixes */
+ includes = slapi_entry_attr_get_charray(e, PAMPT_INCLUDES_ATTR);
+ for (ii = 0; includes && includes[ii]; ++ii) {
+ slapi_sdn_init_dn_byref(comp_dn, includes[ii]);
+ if (!slapi_be_exist(comp_dn)) {
+ charray_add(&missing_list, slapi_ch_strdup(includes[ii]));
+ }
+ slapi_sdn_done(comp_dn);
+ }
+
+ slapi_sdn_free(&comp_dn);
+
+ if (missing_list) {
+ PRUint32 size =
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "The following suffixes listed in %s or %s are not present in this "
+ "server: ", PAMPT_EXCLUDES_ATTR, PAMPT_INCLUDES_ATTR);
+ for (ii = 0; missing_list[ii]; ++ii) {
+ if (size < SLAPI_DSE_RETURNTEXT_SIZE) {
+ size += PR_snprintf(returntext+size, SLAPI_DSE_RETURNTEXT_SIZE-size,
+ "%s%s", (ii > 0) ? "; " : "",
+ missing_list[ii]);
+ }
+ }
+ slapi_ch_array_free(missing_list);
+ missing_list = NULL;
+ if (missing_suffix != PAMPT_MISSING_SUFFIX_ERROR) {
+ slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "Warning: %s\n", returntext);
+ *returntext = 0; /* log error, don't report back to user */
+ } else {
+ goto done;
+ }
+ }
+ }
+
+ pam_ident_attr = slapi_entry_attr_get_charptr(e, PAMPT_PAM_IDENT_ATTR);
+ map_method = slapi_entry_attr_get_charptr(e, PAMPT_MAP_METHOD_ATTR);
+ if (map_method) {
+ int one, two, three;
+ *returncode = parse_map_method(map_method, &one, &two, &three, returntext);
+ if (!pam_ident_attr &&
+ ((one == PAMPT_MAP_METHOD_ENTRY) || (two == PAMPT_MAP_METHOD_ENTRY) ||
+ (three == PAMPT_MAP_METHOD_ENTRY))) {
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, "Error: the %s method"
+ " was specified, but no %s was given",
+ PAMPT_MAP_METHOD_ENTRY_STRING, PAMPT_PAM_IDENT_ATTR);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+ if (one == two == three == PAMPT_MAP_METHOD_NONE) {
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, "Error: no method(s)"
+ " specified for %s, should be one or more of %s",
+ PAMPT_MAP_METHOD_ATTR, get_map_method_values());
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+ }
+
+ /* success */
+ *returncode = LDAP_SUCCESS;
+
+done:
+ slapi_ch_free_string(&map_method);
+ slapi_ch_free_string(&pam_ident_attr);
+ slapi_ch_array_free(excludes);
+ excludes = NULL;
+ slapi_ch_array_free(includes);
+ includes = NULL;
+ slapi_ch_free_string(&missing_suffix_str);
+
+ if (*returncode != LDAP_SUCCESS)
+ {
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ else
+ {
+ return SLAPI_DSE_CALLBACK_OK;
+ }
+}
+
+static Pam_PassthruSuffix *
+New_Pam_PassthruSuffix(char *suffix)
+{
+ Pam_PassthruSuffix *newone = NULL;
+ if (suffix) {
+ newone = (Pam_PassthruSuffix *)slapi_ch_malloc(sizeof(Pam_PassthruSuffix));
+ newone->pamptsuffix_dn = slapi_sdn_new();
+ slapi_sdn_init_dn_byval(newone->pamptsuffix_dn, suffix);
+ newone->pamptsuffix_next = NULL;
+ }
+ return newone;
+}
+
+static Pam_PassthruSuffix *
+pam_ptconfig_add_suffixes(char **str_list)
+{
+ Pam_PassthruSuffix *head = NULL;
+ Pam_PassthruSuffix *suffixent = NULL;
+
+ if (str_list && *str_list) {
+ int ii;
+ for (ii = 0; str_list[ii]; ++ii) {
+ Pam_PassthruSuffix *tmp = New_Pam_PassthruSuffix(str_list[ii]);
+ if (!suffixent) {
+ head = suffixent = tmp;
+ } else {
+ suffixent->pamptsuffix_next = tmp;
+ suffixent = suffixent->pamptsuffix_next;
+ }
+ }
+ }
+ return head;
+}
+
+static void
+Delete_Pam_PassthruSuffix(Pam_PassthruSuffix *one)
+{
+ if (one) {
+ slapi_sdn_free(&one->pamptsuffix_dn);
+ slapi_ch_free((void **)&one);
+ }
+}
+
+static void
+pam_ptconfig_free_suffixes(Pam_PassthruSuffix *list)
+{
+ while (list) {
+ Pam_PassthruSuffix *next = list->pamptsuffix_next;
+ Delete_Pam_PassthruSuffix(list);
+ list = next;
+ }
+}
+
+/*
+ Apply the pending changes in the e entry to our config struct.
+ validate must have already been called
+*/
+static int
+pam_passthru_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg)
+{
+ char **excludes = NULL;
+ char **includes = NULL;
+ char *new_service = NULL;
+ char *pam_ident_attr = NULL;
+ char *map_method = NULL;
+ int fallback;
+ int secure;
+
+ *returncode = LDAP_SUCCESS;
+
+ pam_ident_attr = slapi_entry_attr_get_charptr(e, PAMPT_PAM_IDENT_ATTR);
+ map_method = slapi_entry_attr_get_charptr(e, PAMPT_MAP_METHOD_ATTR);
+ new_service = slapi_entry_attr_get_charptr(e, PAMPT_SERVICE_ATTR);
+ excludes = slapi_entry_attr_get_charray(e, PAMPT_EXCLUDES_ATTR);
+ includes = slapi_entry_attr_get_charray(e, PAMPT_INCLUDES_ATTR);
+ fallback = slapi_entry_attr_get_int(e, PAMPT_FALLBACK_ATTR);
+ secure = slapi_entry_attr_get_int(e, PAMPT_SECURE_ATTR);
+
+ /* lock config here */
+ slapi_lock_mutex(theConfig.lock);
+
+ theConfig.pamptconfig_fallback = fallback;
+ theConfig.pamptconfig_secure = secure;
+ if (!theConfig.pamptconfig_service ||
+ (new_service && PL_strcmp(theConfig.pamptconfig_service, new_service))) {
+ slapi_ch_free_string(&theConfig.pamptconfig_service);
+ theConfig.pamptconfig_service = new_service;
+ new_service = NULL; /* config now owns memory */
+ }
+
+ /* get the list of excluded suffixes */
+ pam_ptconfig_free_suffixes(theConfig.pamptconfig_excludes);
+ theConfig.pamptconfig_excludes = pam_ptconfig_add_suffixes(excludes);
+
+ /* get the list of included suffixes */
+ pam_ptconfig_free_suffixes(theConfig.pamptconfig_includes);
+ theConfig.pamptconfig_includes = pam_ptconfig_add_suffixes(includes);
+
+ if (!theConfig.pamptconfig_pam_ident_attr ||
+ (pam_ident_attr && PL_strcmp(theConfig.pamptconfig_pam_ident_attr, pam_ident_attr))) {
+ slapi_ch_free_string(&theConfig.pamptconfig_pam_ident_attr);
+ theConfig.pamptconfig_pam_ident_attr = pam_ident_attr;
+ pam_ident_attr = NULL; /* config now owns memory */
+ }
+
+ if (map_method) {
+ parse_map_method(map_method,
+ &theConfig.pamptconfig_map_method1,
+ &theConfig.pamptconfig_map_method2,
+ &theConfig.pamptconfig_map_method3,
+ NULL);
+ }
+
+ /* unlock config here */
+ slapi_unlock_mutex(theConfig.lock);
+
+ slapi_ch_free_string(&new_service);
+ slapi_ch_free_string(&map_method);
+ slapi_ch_free_string(&pam_ident_attr);
+ slapi_ch_array_free(excludes);
+ slapi_ch_array_free(includes);
+
+ if (*returncode != LDAP_SUCCESS)
+ {
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ else
+ {
+ return SLAPI_DSE_CALLBACK_OK;
+ }
+}
+
+int
+pam_passthru_check_suffix(Pam_PassthruConfig *cfg, char *binddn)
+{
+ Slapi_DN *comp_dn;
+ Pam_PassthruSuffix *try;
+ int ret = LDAP_SUCCESS;
+
+ comp_dn = slapi_sdn_new();
+ slapi_sdn_init_dn_byref(comp_dn, binddn);
+
+ slapi_lock_mutex(cfg->lock);
+ if (!cfg->pamptconfig_includes && !cfg->pamptconfig_excludes) {
+ goto done; /* NULL means allow */
+ }
+
+ /* exclude trumps include - if suffix is on exclude list, then
+ deny */
+ for (try = cfg->pamptconfig_excludes; try; try = try->pamptsuffix_next) {
+ if (slapi_sdn_issuffix(comp_dn, try->pamptsuffix_dn)) {
+ ret = LDAP_UNWILLING_TO_PERFORM; /* suffix is excluded */
+ goto done;
+ }
+ }
+
+ /* ok, now flip it - deny access unless dn is on include list */
+ if (cfg->pamptconfig_includes) {
+ ret = LDAP_UNWILLING_TO_PERFORM; /* suffix is excluded */
+ for (try = cfg->pamptconfig_includes; try; try = try->pamptsuffix_next) {
+ if (slapi_sdn_issuffix(comp_dn, try->pamptsuffix_dn)) {
+ ret = LDAP_SUCCESS; /* suffix is included */
+ goto done;
+ }
+ }
+ }
+
+done:
+ slapi_unlock_mutex(cfg->lock);
+ slapi_sdn_free(&comp_dn);
+
+ return ret;
+}
+
+/*
+ * Get the pass though configuration data. For now, there is only one
+ * configuration and it is global to the plugin.
+ */
+Pam_PassthruConfig *
+pam_passthru_get_config( void )
+{
+ return( &theConfig );
+}
diff --git a/ldap/servers/plugins/pam_passthru/pam_ptdebug.c b/ldap/servers/plugins/pam_passthru/pam_ptdebug.c
new file mode 100644
index 00000000..dec8632d
--- /dev/null
+++ b/ldap/servers/plugins/pam_passthru/pam_ptdebug.c
@@ -0,0 +1,22 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2005 Red Hat
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * pam_ptdebug.c - debugging-related code for PAM Pass Through Authentication
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "pam_passthru.h"
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
diff --git a/ldap/servers/plugins/pam_passthru/pam_ptdllmain.c b/ldap/servers/plugins/pam_passthru/pam_ptdllmain.c
new file mode 100644
index 00000000..28d8d805
--- /dev/null
+++ b/ldap/servers/plugins/pam_passthru/pam_ptdllmain.c
@@ -0,0 +1,130 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2005 Red Hat
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "ldap.h"
+#include "lber.h"
+#include "passthru.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
+
+#ifdef LDAP_DEBUG
+#ifndef _WIN32
+#include <stdarg.h>
+#include <stdio.h>
+
+void LDAPDebug( int level, char* fmt, ... )
+{
+ static char debugBuf[1024];
+
+ if (module_ldap_debug && (*module_ldap_debug & level))
+ {
+ va_list ap;
+ va_start (ap, fmt);
+ _snprintf (debugBuf, sizeof(debugBuf), fmt, ap);
+ va_end (ap);
+
+ OutputDebugString (debugBuf);
+ }
+}
+#endif
+#endif
+
+#ifndef _WIN32
+
+/* The 16-bit version of the RTL does not implement perror() */
+
+#include <stdio.h>
+
+void perror( const char *msg )
+{
+ char buf[128];
+ wsprintf( buf, "%s: error %d\n", msg, WSAGetLastError()) ;
+ OutputDebugString( buf );
+}
+
+#endif
diff --git a/ldap/servers/plugins/pam_passthru/pam_ptimpl.c b/ldap/servers/plugins/pam_passthru/pam_ptimpl.c
new file mode 100644
index 00000000..d7558af3
--- /dev/null
+++ b/ldap/servers/plugins/pam_passthru/pam_ptimpl.c
@@ -0,0 +1,397 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2005 Red Hat
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <security/pam_appl.h>
+
+#include "pam_passthru.h"
+
+
+/* Utility struct to wrap strings to avoid mallocs if possible - use
+ stack allocated string space */
+#define MY_STATIC_BUF_SIZE 256
+typedef struct my_str_buf {
+ char fixbuf[MY_STATIC_BUF_SIZE];
+ char *str;
+} MyStrBuf;
+
+static char *
+init_my_str_buf(MyStrBuf *buf, const char *s)
+{
+ if (s && (strlen(s) < MY_STATIC_BUF_SIZE)) {
+ strcpy(buf->fixbuf, s);
+ buf->str = buf->fixbuf;
+ } else {
+ buf->str = slapi_ch_strdup(s);
+ }
+
+ return buf->str;
+}
+
+static void
+delete_my_str_buf(MyStrBuf *buf)
+{
+ if (buf->str != buf->fixbuf) {
+ slapi_ch_free_string(&buf->str);
+ }
+}
+
+/* for third arg to pam_start */
+struct my_pam_conv_str {
+ Slapi_PBlock *pb;
+ char *pam_identity;
+};
+
+/*
+ * Get the PAM identity from the value of the leftmost RDN in the BIND DN.
+ */
+static char *
+derive_from_bind_dn(Slapi_PBlock *pb, char *binddn, MyStrBuf *pam_id)
+{
+ Slapi_RDN *rdn;
+ char *type = NULL;
+ char *value = NULL;
+
+ rdn = slapi_rdn_new_dn(binddn);
+ slapi_rdn_get_first(rdn, &type, &value);
+ init_my_str_buf(pam_id, value);
+ slapi_rdn_free(&rdn);
+
+ return pam_id->str;
+}
+
+static char *
+derive_from_bind_entry(Slapi_PBlock *pb, char *binddn, MyStrBuf *pam_id, char *map_ident_attr)
+{
+ char buf[BUFSIZ];
+ Slapi_Entry *entry = NULL;
+ Slapi_DN *sdn = slapi_sdn_new_dn_byref(binddn);
+ char *attrs[] = { map_ident_attr, NULL };
+ int rc = slapi_search_internal_get_entry(sdn, attrs, &entry,
+ pam_passthruauth_get_plugin_identity());
+
+ slapi_sdn_free(&sdn);
+
+ if (rc != LDAP_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "Could not find BIND dn %s (error %d - %s)\n",
+ escape_string(binddn, buf), rc, ldap_err2string(rc));
+ init_my_str_buf(pam_id, NULL);
+ } else if (NULL == entry) {
+ slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "Could not find entry for BIND dn %s\n",
+ escape_string(binddn, buf));
+ init_my_str_buf(pam_id, NULL);
+ } else {
+ char *val = slapi_entry_attr_get_charptr(entry, map_ident_attr);
+ init_my_str_buf(pam_id, val);
+ slapi_ch_free_string(&val);
+ }
+
+ slapi_entry_free(entry);
+
+ return pam_id->str;
+}
+
+static void
+report_pam_error(char *str, int rc, pam_handle_t *pam_handle)
+{
+ if (rc != PAM_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "Error from PAM %s (%d: %s)\n",
+ str, rc, pam_strerror(pam_handle, rc));
+ }
+}
+
+/* returns a berval value as a null terminated string */
+static char *strdupbv(struct berval *bv)
+{
+ char *str = malloc(bv->bv_len+1);
+ memcpy(str, bv->bv_val, bv->bv_len);
+ str[bv->bv_len] = 0;
+ return str;
+}
+
+static void
+free_pam_response(int nresp, struct pam_response *resp)
+{
+ int ii;
+ for (ii = 0; ii < nresp; ++ii) {
+ if (resp[ii].resp) {
+ free(resp[ii].resp);
+ }
+ }
+ free(resp);
+}
+
+/*
+ * This is the conversation function passed into pam_start(). This is what sets the password
+ * that PAM uses to authenticate. This function is sort of stupid - it assumes all echo off
+ * or binary prompts are for the password, and other prompts are for the username. Time will
+ * tell if this is actually the case.
+ */
+static int
+pam_conv_func(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *mydata)
+{
+ int ii;
+ struct berval *creds;
+ struct my_pam_conv_str *my_data = (struct my_pam_conv_str *)mydata;
+ struct pam_response *reply;
+ int ret = PAM_SUCCESS;
+
+ if (num_msg <= 0) {
+ return PAM_CONV_ERR;
+ }
+
+ /* empty reply structure */
+ reply = (struct pam_response *)calloc(num_msg,
+ sizeof(struct pam_response));
+ slapi_pblock_get( my_data->pb, SLAPI_BIND_CREDENTIALS, &creds ); /* the password */
+ for (ii = 0; ii < num_msg; ++ii) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "pam msg [%d] = %d %s\n", ii, msg[ii]->msg_style,
+ msg[ii]->msg);
+ /* hard to tell what prompt is for . . . */
+ /* assume prompts for password are either BINARY or ECHO_OFF */
+ if ((msg[ii]->msg_style == PAM_PROMPT_ECHO_OFF) ||
+ (msg[ii]->msg_style == PAM_BINARY_PROMPT)) {
+ reply[ii].resp = strdupbv(creds);
+ } else if (msg[ii]->msg_style == PAM_PROMPT_ECHO_ON) { /* assume username */
+ reply[ii].resp = strdup(my_data->pam_identity);
+ } else if (msg[ii]->msg_style == PAM_ERROR_MSG) {
+ slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "pam msg [%d] error [%s]\n", ii, msg[ii]->msg);
+ } else if (msg[ii]->msg_style == PAM_TEXT_INFO) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "pam msg [%d] text info [%s]\n", ii, msg[ii]->msg);
+ } else {
+ slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "Error: unknown pam message type (%d: %s)\n",
+ msg[ii]->msg_style, msg[ii]->msg);
+ ret = PAM_CONV_ERR;
+ }
+ }
+
+ if (ret == PAM_CONV_ERR) {
+ free_pam_response(num_msg, reply);
+ reply = NULL;
+ }
+
+ *resp = reply;
+
+ return ret;
+}
+
+/*
+ * Do the actual work of authenticating with PAM. First, get the PAM identity
+ * based on the method used to convert the BIND identity to the PAM identity.
+ * Set up the structures that pam_start needs and call pam_start(). After
+ * that, call pam_authenticate and pam_acct_mgmt. Check the various return
+ * values from these functions and map them to their corresponding LDAP BIND
+ * return values. Return the appropriate LDAP error code.
+ * This function will also set the appropriate LDAP response controls in
+ * the given pblock.
+ * Since this function can be called multiple times, we only want to return
+ * the errors and controls to the user if this is the final call, so the
+ * final_method parameter tells us if this is the last one. Coupled with
+ * the fallback argument, we can tell if we are able to send the response
+ * back to the client.
+ */
+static int
+do_one_pam_auth(
+ Slapi_PBlock *pb,
+ int method, /* get pam identity from ENTRY, RDN, or DN */
+ PRBool final_method, /* which method is the last one to try */
+ char *pam_service, /* name of service for pam_start() */
+ char *map_ident_attr, /* for ENTRY method, name of attribute holding pam identity */
+ PRBool fallback, /* if true, failure here should fallback to regular bind */
+ int pw_response_requested /* do we need to send pwd policy resp control */
+)
+{
+ MyStrBuf pam_id;
+ char *binddn = NULL;
+ int rc;
+ int retcode = LDAP_SUCCESS;
+ pam_handle_t *pam_handle;
+ struct my_pam_conv_str my_data;
+ struct pam_conv my_pam_conv = {pam_conv_func, NULL};
+ char buf[BUFSIZ]; /* for error messages */
+ char *errmsg = NULL; /* free with PR_smprintf_free */
+
+ slapi_pblock_get( pb, SLAPI_BIND_TARGET, &binddn );
+
+ if (method == PAMPT_MAP_METHOD_RDN) {
+ derive_from_bind_dn(pb, binddn, &pam_id);
+ } else if (method == PAMPT_MAP_METHOD_ENTRY) {
+ derive_from_bind_entry(pb, binddn, &pam_id, map_ident_attr);
+ } else {
+ init_my_str_buf(&pam_id, binddn);
+ }
+
+ /* do the pam stuff */
+ my_data.pb = pb;
+ my_data.pam_identity = pam_id.str;
+ my_pam_conv.appdata_ptr = &my_data;
+ rc = pam_start(pam_service, pam_id.str, &my_pam_conv, &pam_handle);
+ report_pam_error("during pam_start", rc, pam_handle);
+
+ if (rc == PAM_SUCCESS) {
+ /* use PAM_SILENT - there is no user interaction at this point */
+ rc = pam_authenticate(pam_handle, 0);
+ report_pam_error("during pam_authenticate", rc, pam_handle);
+ /* check different types of errors here */
+ if (rc == PAM_USER_UNKNOWN) {
+ errmsg = PR_smprintf("User id [%s] for bind DN [%s] does not exist in PAM",
+ pam_id.str, escape_string(binddn, buf));
+ retcode = LDAP_NO_SUCH_OBJECT; /* user unknown */
+ } else if (rc == PAM_AUTH_ERR) {
+ errmsg = PR_smprintf("Invalid PAM password for user id [%s], bind DN [%s]",
+ pam_id.str, escape_string(binddn, buf));
+ retcode = LDAP_INVALID_CREDENTIALS; /* invalid creds */
+ } else if (rc == PAM_MAXTRIES) {
+ errmsg = PR_smprintf("Authentication retry limit exceeded in PAM for "
+ "user id [%s], bind DN [%s]",
+ pam_id.str, escape_string(binddn, buf));
+ if (pw_response_requested) {
+ slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_ACCTLOCKED);
+ }
+ retcode = LDAP_CONSTRAINT_VIOLATION; /* max retries */
+ } else if (rc != PAM_SUCCESS) {
+ errmsg = PR_smprintf("Unknown PAM error [%s] for user id [%s], bind DN [%s]",
+ pam_strerror(pam_handle, rc), pam_id.str, escape_string(binddn, buf));
+ retcode = LDAP_OPERATIONS_ERROR; /* pam config or network problem */
+ }
+ }
+
+ /* if user authenticated successfully, see if there is anything we need
+ to report back w.r.t. password or account lockout */
+ if (rc == PAM_SUCCESS) {
+ rc = pam_acct_mgmt(pam_handle, 0);
+ report_pam_error("during pam_acct_mgmt", rc, pam_handle);
+ /* check different types of errors here */
+ if (rc == PAM_USER_UNKNOWN) {
+ errmsg = PR_smprintf("User id [%s] for bind DN [%s] does not exist in PAM",
+ pam_id.str, escape_string(binddn, buf));
+ retcode = LDAP_NO_SUCH_OBJECT; /* user unknown */
+ } else if (rc == PAM_AUTH_ERR) {
+ errmsg = PR_smprintf("Invalid PAM password for user id [%s], bind DN [%s]",
+ pam_id.str, escape_string(binddn, buf));
+ retcode = LDAP_INVALID_CREDENTIALS; /* invalid creds */
+ } else if (rc == PAM_PERM_DENIED) {
+ errmsg = PR_smprintf("Access denied for PAM user id [%s], bind DN [%s]"
+ " - see administrator",
+ pam_id.str, escape_string(binddn, buf));
+ if (pw_response_requested) {
+ slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_ACCTLOCKED);
+ }
+ retcode = LDAP_UNWILLING_TO_PERFORM;
+ } else if (rc == PAM_ACCT_EXPIRED) {
+ errmsg = PR_smprintf("Expired PAM password for user id [%s], bind DN [%s]: "
+ "reset required",
+ pam_id.str, escape_string(binddn, buf));
+ slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);
+ if (pw_response_requested) {
+ slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_PWDEXPIRED);
+ }
+ retcode = LDAP_INVALID_CREDENTIALS;
+ } else if (rc == PAM_NEW_AUTHTOK_REQD) { /* handled same way as ACCT_EXPIRED */
+ errmsg = PR_smprintf("Expired PAM password for user id [%s], bind DN [%s]: "
+ "reset required",
+ pam_id.str, escape_string(binddn, buf));
+ slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);
+ if (pw_response_requested) {
+ slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_PWDEXPIRED);
+ }
+ retcode = LDAP_INVALID_CREDENTIALS;
+ } else if (rc != PAM_SUCCESS) {
+ errmsg = PR_smprintf("Unknown PAM error [%s] for user id [%s], bind DN [%s]",
+ pam_strerror(pam_handle, rc), pam_id.str, escape_string(binddn, buf));
+ retcode = LDAP_OPERATIONS_ERROR; /* unknown */
+ }
+ }
+
+ rc = pam_end(pam_handle, rc);
+ report_pam_error("during pam_end", rc, pam_handle);
+
+ delete_my_str_buf(&pam_id);
+
+ if ((retcode == LDAP_SUCCESS) && (rc != PAM_SUCCESS)) {
+ errmsg = PR_smprintf("Unknown PAM error [%d] for user id [%d], bind DN [%s]",
+ rc, pam_id.str, escape_string(binddn, buf));
+ retcode = LDAP_OPERATIONS_ERROR;
+ }
+
+ if (retcode != LDAP_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "%s\n", errmsg);
+ if (final_method && !fallback) {
+ slapi_send_ldap_result(pb, retcode, NULL, errmsg, 0, NULL);
+ }
+ }
+
+ if (errmsg) {
+ PR_smprintf_free(errmsg);
+ }
+
+ return retcode;
+}
+
+/*
+ * Entry point into the PAM auth code. Shields the rest of the app
+ * from PAM API code. Get our config params, then call the actual
+ * code that does the PAM auth. Can call that code up to 3 times,
+ * depending on what methods are set in the config.
+ */
+int
+pam_passthru_do_pam_auth(Slapi_PBlock *pb, Pam_PassthruConfig *cfg)
+{
+ int rc = LDAP_SUCCESS;
+ MyStrBuf pam_id_attr; /* avoid malloc if possible */
+ MyStrBuf pam_service; /* avoid malloc if possible */
+ int method1, method2, method3;
+ PRBool final_method;
+ PRBool fallback = PR_FALSE;
+ int pw_response_requested;
+ LDAPControl **reqctrls = NULL;
+
+ /* first lock and get the methods and other info */
+ /* we do this so we can acquire and release the lock quickly to
+ avoid potential deadlocks */
+ slapi_lock_mutex(cfg->lock);
+ method1 = cfg->pamptconfig_map_method1;
+ method2 = cfg->pamptconfig_map_method2;
+ method3 = cfg->pamptconfig_map_method3;
+
+ init_my_str_buf(&pam_id_attr, cfg->pamptconfig_pam_ident_attr);
+ init_my_str_buf(&pam_service, cfg->pamptconfig_service);
+
+ fallback = cfg->pamptconfig_fallback;
+
+ slapi_unlock_mutex(cfg->lock);
+
+ slapi_pblock_get (pb, SLAPI_REQCONTROLS, &reqctrls);
+ slapi_pblock_get (pb, SLAPI_PWPOLICY, &pw_response_requested);
+
+ /* figure out which method is the last one - we only return error codes, controls
+ to the client and send a response on the last method */
+
+ final_method = (method2 == PAMPT_MAP_METHOD_NONE);
+ rc = do_one_pam_auth(pb, method1, final_method, pam_service.str, pam_id_attr.str, fallback,
+ pw_response_requested);
+ if ((rc != LDAP_SUCCESS) && !final_method) {
+ final_method = (method3 == PAMPT_MAP_METHOD_NONE);
+ rc = do_one_pam_auth(pb, method2, final_method, pam_service.str, pam_id_attr.str, fallback,
+ pw_response_requested);
+ if ((rc != LDAP_SUCCESS) && !final_method) {
+ final_method = PR_TRUE;
+ rc = do_one_pam_auth(pb, method3, final_method, pam_service.str, pam_id_attr.str, fallback,
+ pw_response_requested);
+ }
+ }
+
+ delete_my_str_buf(&pam_id_attr);
+ delete_my_str_buf(&pam_service);
+
+ return rc;
+}
diff --git a/ldap/servers/plugins/pam_passthru/pam_ptpreop.c b/ldap/servers/plugins/pam_passthru/pam_ptpreop.c
new file mode 100644
index 00000000..71ee4973
--- /dev/null
+++ b/ldap/servers/plugins/pam_passthru/pam_ptpreop.c
@@ -0,0 +1,221 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2005 Red Hat
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * pamptpreop.c - bind pre-operation plugin for Pass Through Authentication to PAM
+ *
+ */
+
+#include "pam_passthru.h"
+
+static Slapi_PluginDesc pdesc = { "pam_passthruauth", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "PAM pass through authentication plugin" };
+
+static void * pam_passthruauth_plugin_identity = NULL;
+
+/*
+ * function prototypes
+ */
+static int pam_passthru_bindpreop( Slapi_PBlock *pb );
+static int pam_passthru_bindpreop_start( Slapi_PBlock *pb );
+static int pam_passthru_bindpreop_close( Slapi_PBlock *pb );
+
+
+/*
+** Plugin identity mgmt
+*/
+
+void pam_passthruauth_set_plugin_identity(void * identity)
+{
+ pam_passthruauth_plugin_identity=identity;
+}
+
+void * pam_passthruauth_get_plugin_identity()
+{
+ return pam_passthruauth_plugin_identity;
+}
+
+/*
+ * Plugin initialization function (which must be listed in the appropriate
+ * slapd config file).
+ */
+int
+pam_passthruauth_init( Slapi_PBlock *pb )
+{
+ PAM_PASSTHRU_ASSERT( pb != NULL );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "=> pam_passthruauth_init\n" );
+
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *)SLAPI_PLUGIN_VERSION_01 ) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc ) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN,
+ (void *)pam_passthru_bindpreop_start ) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_BIND_FN,
+ (void *)pam_passthru_bindpreop ) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *)pam_passthru_bindpreop_close ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "pam_passthruauth_init failed\n" );
+ return( -1 );
+ }
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= pam_passthruauth_init succeeded\n" );
+
+ return( 0 );
+}
+
+/*
+ * pam_passthru_bindpreop_start() is called before the directory server
+ * is fully up. We parse our configuration and initialize any mutexes, etc.
+ */
+static int
+pam_passthru_bindpreop_start( Slapi_PBlock *pb )
+{
+ int rc;
+ Slapi_Entry *config_e = NULL; /* entry containing plugin config */
+
+ PAM_PASSTHRU_ASSERT( pb != NULL );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "=> pam_passthru_bindpreop_start\n" );
+
+ if ( slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &config_e ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "missing config entry\n" );
+ return( -1 );
+ }
+
+ if (( rc = pam_passthru_config( config_e )) != LDAP_SUCCESS ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "configuration failed (%s)\n", ldap_err2string( rc ));
+ return( -1 );
+ }
+
+ return( 0 );
+}
+
+
+/*
+ * Called right before the Directory Server shuts down.
+ */
+static int
+pam_passthru_bindpreop_close( Slapi_PBlock *pb )
+{
+ PAM_PASSTHRU_ASSERT( pb != NULL );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "=> pam_passthru_bindpreop_close\n" );
+
+ return( 0 );
+}
+
+
+static int
+pam_passthru_bindpreop( Slapi_PBlock *pb )
+{
+ int rc, method;
+ char *normbinddn, *errmsg = NULL;
+ Pam_PassthruConfig *cfg;
+ struct berval *creds;
+ int retcode = PAM_PASSTHRU_OP_NOT_HANDLED;
+
+ PAM_PASSTHRU_ASSERT( pb != NULL );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "=> pam_passthru_bindpreop\n" );
+
+ /*
+ * retrieve parameters for bind operation
+ */
+ if ( slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_TARGET, &normbinddn ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &creds ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= not handled (unable to retrieve bind parameters)\n" );
+ return retcode;
+ }
+
+ /*
+ * We only handle simple bind requests that include non-NULL binddn and
+ * credentials. Let the Directory Server itself handle everything else.
+ */
+ if ( method != LDAP_AUTH_SIMPLE || *normbinddn == '\0' ||
+ creds->bv_len == 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= not handled (not simple bind or NULL dn/credentials)\n" );
+ return retcode;
+ }
+
+ /* get the config */
+ cfg = pam_passthru_get_config();
+
+ /* don't lock mutex here - simple integer access - assume atomic */
+ if (cfg->pamptconfig_secure) { /* is a secure connection required? */
+ int is_ssl = 0;
+ slapi_pblock_get(pb, SLAPI_CONN_IS_SSL_SESSION, &is_ssl);
+ if (!is_ssl) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= connection not secure (secure connection required; check config)");
+ return retcode;
+ }
+ }
+
+ /*
+ * Check to see if the target DN is one we should "pass through" to
+ * PAM
+ */
+ if ( pam_passthru_check_suffix( cfg, normbinddn ) != LDAP_SUCCESS ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= not handled (not one of our suffixes)\n" );
+ return retcode;
+ }
+
+ /*
+ * We are now committed to handling this bind request.
+ * Chain it off to PAM
+ */
+ rc = pam_passthru_do_pam_auth(pb, cfg);
+
+ /*
+ * If bind succeeded, change authentication information associated
+ * with this connection.
+ */
+ if (rc == LDAP_SUCCESS) {
+ char *ndn = slapi_ch_strdup(normbinddn);
+ if ((slapi_pblock_set(pb, SLAPI_CONN_DN, ndn) != 0) ||
+ (slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD,
+ SLAPD_AUTH_SIMPLE) != 0)) {
+ slapi_ch_free_string(&ndn);
+ rc = LDAP_OPERATIONS_ERROR;
+ errmsg = "unable to set connection DN or AUTHTYPE";
+ slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "%s\n", errmsg);
+ } else {
+ LDAPControl **reqctrls = NULL;
+ slapi_pblock_get(pb, SLAPI_REQCONTROLS, &reqctrls);
+ if (slapi_control_present(reqctrls, LDAP_CONTROL_AUTH_REQUEST, NULL, NULL)) {
+ slapi_add_auth_response_control(pb, ndn);
+ }
+ }
+ }
+
+ if (rc == LDAP_SUCCESS) {
+ /* we are handling the result */
+ slapi_send_ldap_result(pb, rc, NULL, errmsg, 0, NULL);
+ /* tell bind code we handled the result */
+ retcode = PAM_PASSTHRU_OP_HANDLED;
+ } else if (!cfg->pamptconfig_fallback) {
+ /* tell bind code we already sent back the error result in pam_ptimpl.c */
+ retcode = PAM_PASSTHRU_OP_HANDLED;
+ }
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= handled (error %d - %s)\n", rc, ldap_err2string(rc));
+
+ return retcode;
+}