Arthur de Jong

Open Source / Free Software developer

summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur de Jong <arthur@arthurdejong.org>2009-05-09 11:27:10 +0200
committerArthur de Jong <arthur@arthurdejong.org>2009-05-09 11:27:10 +0200
commit5f035facd24b3d15743eae48ddec15115c705e79 (patch)
tree370a83232b9e51677bbe73fced1debcc3331c08f
parentbe1b2c1e63beb0fc0b90e21da4679e359a9f9fdc (diff)
import the PAM module from the nss-ldapd branch (r875) based on the OpenLDAP nssov tree and allow configuring which modules should be built (PAM module disabled by default)
git-svn-id: http://arthurdejong.org/svn/nss-pam-ldapd/nss-ldapd@876 ef36b2f9-881f-0410-afb5-c4e39611909c
-rw-r--r--AUTHORS1
-rw-r--r--Makefile.am14
-rw-r--r--configure.ac40
-rw-r--r--debian/copyright1
-rw-r--r--nslcd.h60
-rw-r--r--pam/Makefile.am39
-rw-r--r--pam/exports.linux16
-rw-r--r--pam/pam.c725
8 files changed, 891 insertions, 5 deletions
diff --git a/AUTHORS b/AUTHORS
index 2146a0e..0c08917 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -6,6 +6,7 @@ code are:
Luke Howard <lukeh@padl.com>
West Consulting <info@west.nl>
Arthur de Jong <arthur@ch.tudelft.nl>
+Howard Chu <hyc@symas.com>
The following people (in no particular order) have also volunteered their
time, effort, and ideas to make this software available. If you feel you are
diff --git a/Makefile.am b/Makefile.am
index c5caee4..8ae5037 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,7 +2,7 @@
#
# Copyright (C) 2006 Luke Howard
# Copyright (C) 2006 West Consulting
-# Copyright (C) 2006, 2007, 2008 Arthur de Jong
+# Copyright (C) 2006, 2007, 2008, 2009 Arthur de Jong
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -19,7 +19,17 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
-SUBDIRS = compat common nss nslcd man tests
+if ENABLE_NSS
+ MAYBE_NSS = nss
+endif
+if ENABLE_PAM
+ MAYBE_PAM = pam
+endif
+if ENABLE_NSLCD
+ ENABLE_NSLCD = nslcd
+endif
+
+SUBDIRS = compat common $(MAYBE_NSS) $(MAYBE_PAM) $(ENABLE_NSLCD) man tests
DEBIAN_FILES = debian/changelog debian/compat debian/control \
debian/copyright debian/rules \
diff --git a/configure.ac b/configure.ac
index a88ea27..63a097a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -105,6 +105,37 @@ do
fi
done
+# check whether the NSS module should be built
+AC_MSG_CHECKING([whether to build the NSS module])
+AC_ARG_ENABLE(nss,
+ AS_HELP_STRING([--disable-nss],
+ [build the NSS module [[default=yes]]]),,
+ [enable_nss="yes"])
+AC_MSG_RESULT($enable_nss)
+AM_CONDITIONAL([ENABLE_NSS], [test "x$enable_nss" = "xyes"])
+
+# check whether the PAM module should be built
+AC_MSG_CHECKING([whether to build the PAM module])
+AC_ARG_ENABLE(pam,
+ AS_HELP_STRING([--disable-pam],
+ [build the PAM module [[default=yes]]]),,
+ [enable_pam="no"])
+AC_MSG_RESULT($enable_pam)
+AM_CONDITIONAL([ENABLE_PAM], [test "x$enable_pam" = "xyes"])
+if test "x$enable_pam" = "xyes"
+then
+ AC_MSG_WARN([the PAM module is experimental and support in nslcd is not yet finished])
+fi
+
+# check whether the nslcd daemon should be built
+AC_MSG_CHECKING([whether to build the nslcd server])
+AC_ARG_ENABLE(nslcd,
+ AS_HELP_STRING([--disable-nslcd],
+ [build the nslcd server [[default=yes]]]),,
+ [enable_nslcd="yes"])
+AC_MSG_RESULT($enable_nslcd)
+AM_CONDITIONAL([ENABLE_NSLCD], [test "x$enable_nslcd" = "xyes"])
+
AC_ARG_WITH(ldap-lib,
AS_HELP_STRING([--with-ldap-lib=TYPE],
[select ldap library (auto|netscape5|netscape4|netscape3|umich|openldap) @<:@auto@:>@]))
@@ -162,6 +193,11 @@ AC_CHECK_HEADERS(grp.h)
AC_CHECK_HEADERS(sys/socket.h)
AC_CHECK_HEADERS(sys/ucred.h)
AC_CHECK_HEADERS(ucred.h)
+if test "x$enable_pam" = "xyes"
+then
+ AC_CHECK_HEADERS(security/pam_modules.h)
+ AC_CHECK_HEADERS(pam/pam_modules.h)
+fi
# set up directory with compatibility replacement files
AC_CONFIG_LIBOBJ_DIR([compat])
@@ -349,6 +385,6 @@ AC_SUBST(nss_ldap_so_LIBS)
AC_SUBST(nslcd_LIBS)
# generate files
-AC_CONFIG_FILES([Makefile compat/Makefile common/Makefile
- nss/Makefile nslcd/Makefile man/Makefile tests/Makefile])
+AC_CONFIG_FILES([Makefile compat/Makefile common/Makefile nss/Makefile
+ pam/Makefile nslcd/Makefile man/Makefile tests/Makefile])
AC_OUTPUT
diff --git a/debian/copyright b/debian/copyright
index 7b2ccc1..b430b2a 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -17,6 +17,7 @@ Davide Puricelli (evo), Sami Haahtinen and Stephen Frost.
Copyright (C) 1997-2006 Luke Howard
Copyright (C) 2006-2007 West Consulting
Copyright (C) 2006-2009 Arthur de Jong
+ Copyright (C) 2009 Howard Chu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
diff --git a/nslcd.h b/nslcd.h
index 323a239..437c874 100644
--- a/nslcd.h
+++ b/nslcd.h
@@ -180,15 +180,73 @@
#define NSLCD_ACTION_SHADOW_BYNAME 2001
#define NSLCD_ACTION_SHADOW_ALL 2005
-/* PAM-related requests. The requests and responses need to be defined. */
+/* PAM-related requests. The request parameters for all these requests
+ begin with:
+ STRING user name
+ STRING DN (if value is known already, otherwise empty)
+ STRING service name
+ all requests, except the SESSION requests start the result value with:
+ STRING user name (cannonical name)
+ STRING DN (can be used to speed up requests) */
+
+/*
+ WARNING: the PAM code is under development and the details of the protocol
+ may change between releases.
+*/
+
+/* PAM authentication check request. The extra request values are:
+ STRING password
+ and the result value ends with:
+ INT32 authc NSLCD_PAM_* result code
+ INT32 authz NSLCD_PAM_* result code
+ STRING authorisation error message */
#define NSLCD_ACTION_PAM_AUTHC 20001
+
+/* PAM authorisation check request. This request does not have any extra
+ request values. The result value ends with:
+ INT32 authz NSLCD_PAM_* result code
+ STRING authorisation error message */
#define NSLCD_ACTION_PAM_AUTHZ 20002
+
+/* PAM session open and close requests. These requests have the following
+ extra request values:
+ STRING tty
+ STRING rhost
+ STRING ruser
+ INT32 session id (ignored for SESS_O)
+ and these calls only return the session ID:
+ INT32 session id
+ The SESS_C must contain the ID that is retured by SESS_O to close the
+ correct session. */
#define NSLCD_ACTION_PAM_SESS_O 20003
#define NSLCD_ACTION_PAM_SESS_C 20004
+
+/* PAM password modification request. This requests has the following extra
+ request values:
+ STRING old password
+ STRING new password
+ and returns there extra result values:
+ INT32 authz NSLCD_PAM_* result code
+ STRING authorisation error message */
#define NSLCD_ACTION_PAM_PWMOD 20005
/* Request result codes. */
#define NSLCD_RESULT_BEGIN 0
#define NSLCD_RESULT_END 3
+/* Partial list of PAM result codes. */
+#define NSLCD_PAM_SUCCESS 0 /* everything ok */
+#define NSLCD_PAM_PERM_DENIED 6 /* Permission denied */
+#define NSLCD_PAM_AUTH_ERR 7 /* Authc failure */
+#define NSLCD_PAM_CRED_INSUFFICIENT 8 /* Cannot access authc data */
+#define NSLCD_PAM_AUTHINFO_UNAVAIL 9 /* Cannot retrieve authc info */
+#define NSLCD_PAM_USER_UNKNOWN 10 /* User not known */
+#define NSLCD_PAM_MAXTRIES 11 /* Retry limit reached */
+#define NSLCD_PAM_NEW_AUTHTOK_REQD 12 /* Password expired */
+#define NSLCD_PAM_ACCT_EXPIRED 13 /* Account expired */
+#define NSLCD_PAM_SESSION_ERR 14 /* Cannot make/remove session record */
+#define NSLCD_PAM_AUTHTOK_DISABLE_AGING 23 /* Password aging disabled */
+#define NSLCD_PAM_IGNORE 25 /* Ignore module */
+#define NSLCD_PAM_ABORT 26 /* Fatal error */
+
#endif /* not _NSLCD_H */
diff --git a/pam/Makefile.am b/pam/Makefile.am
new file mode 100644
index 0000000..c816ffd
--- /dev/null
+++ b/pam/Makefile.am
@@ -0,0 +1,39 @@
+# Makefile.am - use automake to generate Makefile.in
+#
+# Copyright (C) 2009 Arthur de Jong
+#
+# 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 2.1 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
+# Lesser 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 USA
+
+noinst_PROGRAMS = pam_ldap.so
+
+AM_CPPFLAGS=-I$(top_srcdir)
+AM_CFLAGS = -fPIC
+
+pam_ldap_so_SOURCES = ../nslcd.h ../nslcd-common.h \
+ ../compat/attrs.h pam.c
+pam_ldap_so_LDFLAGS = -shared -Wl,--version-script,\$(srcdir)/exports.linux
+pam_ldap_so_LDADD = ../common/libtio.a ../nss/common.o -lpam
+
+EXTRA_DIST = exports.linux
+
+install-exec-local: install-pam_ldap_so
+uninstall-local: uninstall-pam_ldap_so
+
+# install pam_ldap.so
+install-pam_ldap_so: pam_ldap.so
+ $(INSTALL_PROGRAM) -D pam_ldap.so $(DESTDIR)$(libdir)/security/pam_ldap.so
+uninstall-pam_ldap_so:
+ -rm -f $(DESTDIR)$(libdir)/security/pam_ldap.so
diff --git a/pam/exports.linux b/pam/exports.linux
new file mode 100644
index 0000000..21c98f8
--- /dev/null
+++ b/pam/exports.linux
@@ -0,0 +1,16 @@
+EXPORTED {
+
+ # published PAM service functions
+ global:
+ pam_sm_acct_mgmt;
+ pam_sm_authenticate;
+ pam_sm_chauthtok;
+ pam_sm_close_session;
+ pam_sm_open_session;
+ pam_sm_setcred;
+
+ # everything else should not be exported
+ local:
+ *;
+
+};
diff --git a/pam/pam.c b/pam/pam.c
new file mode 100644
index 0000000..89e6ca0
--- /dev/null
+++ b/pam/pam.c
@@ -0,0 +1,725 @@
+/*
+ pam.c - pam module functions
+
+ Copyright (C) 2009 Howard Chu
+ Copyright (C) 2009 Arthur de Jong
+
+ 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 2.1 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
+ Lesser 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+/*
+ WARNING: this code is under development and the details of the protocol
+ may change between releases.
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include "nss/common.h"
+#include "compat/attrs.h"
+
+/* these are defined (before including pam_modules.h) for staticly linking */
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_SESSION
+#define PAM_SM_PASSWORD
+
+#ifndef HAVE_PAM_PAM_MODULES_H
+#include <security/pam_modules.h>
+#else
+#include <pam/pam_modules.h>
+#endif
+
+#define CONST_ARG const
+
+#define IGNORE_UNKNOWN 1
+#define IGNORE_UNAVAIL 2
+
+#define PLD_CTX "PAM_LDAPD_CTX"
+
+#define NSS2PAM_RC(rc,ignore,ok) \
+ switch(rc) { \
+ case NSS_STATUS_SUCCESS: \
+ rc = ok; break; \
+ case NSS_STATUS_UNAVAIL: \
+ rc = (ignore & IGNORE_UNAVAIL) ? PAM_IGNORE : PAM_AUTHINFO_UNAVAIL; \
+ break; \
+ case NSS_STATUS_NOTFOUND: \
+ rc = (ignore & IGNORE_UNKNOWN) ? PAM_IGNORE: PAM_USER_UNKNOWN; \
+ break; \
+ default: \
+ rc = PAM_SYSTEM_ERR; break; \
+ }
+
+typedef struct pld_ctx {
+ char *user;
+ char *dn;
+ char *tmpluser;
+ char *authzmsg;
+ char *oldpw;
+ int authok;
+ int authz;
+ int sessid;
+ char buf[1024];
+} pld_ctx;
+
+static int nslcd2pam_rc(int rc)
+{
+#define map(i) case NSLCD_##i : rc = i; break
+ switch(rc) {
+ map(PAM_SUCCESS);
+ map(PAM_PERM_DENIED);
+ map(PAM_AUTH_ERR);
+ map(PAM_CRED_INSUFFICIENT);
+ map(PAM_AUTHINFO_UNAVAIL);
+ map(PAM_USER_UNKNOWN);
+ map(PAM_MAXTRIES);
+ map(PAM_NEW_AUTHTOK_REQD);
+ map(PAM_ACCT_EXPIRED);
+ map(PAM_SESSION_ERR);
+ map(PAM_AUTHTOK_DISABLE_AGING);
+ map(PAM_IGNORE);
+ map(PAM_ABORT);
+ default: rc = PAM_ABORT; break;
+ }
+ return rc;
+}
+
+static void pam_clr_ctx(
+ pld_ctx *ctx)
+{
+ if (ctx->user) {
+ free(ctx->user);
+ ctx->user = NULL;
+ }
+ if (ctx->oldpw) {
+ memset(ctx->oldpw,0,strlen(ctx->oldpw));
+ free(ctx->oldpw);
+ ctx->oldpw = NULL;
+ }
+ ctx->dn = NULL;
+ ctx->tmpluser = NULL;
+ ctx->authzmsg = NULL;
+ ctx->authok = 0;
+ ctx->authz = 0;
+}
+
+static void pam_del_ctx(
+ pam_handle_t *UNUSED(pamh), void *data, int UNUSED(err))
+{
+ pld_ctx *ctx = data;
+ pam_clr_ctx(ctx);
+ free(ctx);
+}
+
+static int pam_get_ctx(
+ pam_handle_t *pamh, const char *user, pld_ctx **pctx)
+{
+ pld_ctx *ctx = NULL;
+ int rc;
+
+ if (pam_get_data(pamh, PLD_CTX, (CONST_ARG void **)&ctx) == PAM_SUCCESS) {
+ if (ctx->user && strcmp(ctx->user, user)) {
+ pam_clr_ctx(ctx);
+ }
+ rc = PAM_SUCCESS;
+ }
+ if (!ctx) {
+ ctx = calloc(1, sizeof(*ctx));
+ if (!ctx)
+ return PAM_BUF_ERR;
+ rc = pam_set_data(pamh, PLD_CTX, ctx, pam_del_ctx);
+ if (rc != PAM_SUCCESS)
+ pam_del_ctx(pamh, ctx, 0);
+ }
+ if (rc == PAM_SUCCESS)
+ *pctx = ctx;
+ return rc;
+}
+
+static int pam_get_authtok(
+ pam_handle_t *pamh, int flags, char *prompt1, char *prompt2, char **pwd)
+{
+ int rc;
+ char *p;
+ struct pam_message msg[1], *pmsg[1];
+ struct pam_response *resp;
+ struct pam_conv *conv;
+
+ *pwd = NULL;
+
+ rc = pam_get_item (pamh, PAM_CONV, (CONST_ARG void **) &conv);
+ if (rc == PAM_SUCCESS) {
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[0].msg = prompt1;
+ resp = NULL;
+ rc = conv->conv (1,
+ (CONST_ARG struct pam_message **) pmsg,
+ &resp, conv->appdata_ptr);
+ } else {
+ return rc;
+ }
+
+ if (resp != NULL) {
+ if ((flags & PAM_DISALLOW_NULL_AUTHTOK) && resp[0].resp == NULL)
+ {
+ free (resp);
+ return PAM_AUTH_ERR;
+ }
+
+ p = resp[0].resp;
+ resp[0].resp = NULL;
+ free (resp);
+ } else {
+ return PAM_CONV_ERR;
+ }
+
+ if (prompt2) {
+ msg[0].msg = prompt2;
+ resp = NULL;
+ rc = conv->conv (1,
+ (CONST_ARG struct pam_message **) pmsg,
+ &resp, conv->appdata_ptr);
+ if (resp && resp[0].resp && !strcmp(resp[0].resp, p))
+ rc = PAM_SUCCESS;
+ else
+ rc = PAM_AUTHTOK_RECOVERY_ERR;
+ if (resp) {
+ if (resp[0].resp) {
+ (void) memset(resp[0].resp, 0, strlen(resp[0].resp));
+ free(resp[0].resp);
+ }
+ free(resp);
+ }
+ }
+
+ if (rc == PAM_SUCCESS)
+ *pwd = p;
+ else if (p) {
+ memset(p, 0, strlen(p));
+ free(p);
+ }
+
+ return rc;
+}
+
+static enum nss_status pam_read_authc(
+ TFILE *fp,pld_ctx *ctx,int *errnop)
+{
+ char *buffer = ctx->buf;
+ size_t buflen = sizeof(ctx->buf);
+ size_t bufptr = 0;
+ int32_t tmpint32;
+
+ READ_STRING_BUF(fp,ctx->tmpluser);
+ READ_STRING_BUF(fp,ctx->dn);
+ READ_INT32(fp,ctx->authok);
+ READ_INT32(fp,ctx->authz);
+ READ_STRING_BUF(fp,ctx->authzmsg);
+ ctx->authok = nslcd2pam_rc(ctx->authok);
+ ctx->authz = nslcd2pam_rc(ctx->authz);
+ return NSS_STATUS_SUCCESS;
+}
+
+static enum nss_status pam_do_authc(
+ pld_ctx *ctx, const char *user, const char *svc,const char *pwd,int *errnop)
+{
+ NSS_BYGEN(NSLCD_ACTION_PAM_AUTHC,
+ WRITE_STRING(fp,user);
+ WRITE_STRING(fp,ctx->dn);
+ WRITE_STRING(fp,svc);
+ WRITE_STRING(fp,pwd),
+ pam_read_authc(fp,ctx,errnop));
+}
+
+#define USE_FIRST 1
+#define TRY_FIRST 2
+#define USE_TOKEN 4
+
+int pam_sm_authenticate(
+ pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ int err, rc;
+ const char *username, *svc;
+ char *p = NULL;
+ int first_pass = 0, ignore_flags = 0;
+ int i;
+ pld_ctx *ctx;
+
+ for (i = 0; i < argc; i++) {
+ if (!strcmp (argv[i], "use_first_pass"))
+ first_pass |= USE_FIRST;
+ else if (!strcmp (argv[i], "try_first_pass"))
+ first_pass |= TRY_FIRST;
+ else if (!strcmp (argv[i], "ignore_unknown_user"))
+ ignore_flags |= IGNORE_UNKNOWN;
+ else if (!strcmp (argv[i], "ignore_authinfo_unavail"))
+ ignore_flags |= IGNORE_UNAVAIL;
+ else if (!strcmp (argv[i], "no_warn"))
+ ;
+ else if (!strcmp (argv[i], "debug"))
+ ;
+ else
+ syslog (LOG_ERR, "illegal option %s", argv[i]);
+ }
+
+ rc = pam_get_user (pamh, (CONST_ARG char **) &username, NULL);
+ if (rc != PAM_SUCCESS)
+ return rc;
+
+ rc = pam_get_ctx(pamh, username, &ctx);
+ if (rc != PAM_SUCCESS)
+ return rc;
+
+ rc = pam_get_item (pamh, PAM_SERVICE, (CONST_ARG void **) &svc);
+ if (rc != PAM_SUCCESS)
+ return rc;
+
+ for (i=0;i<2;i++) {
+ if (!first_pass) {
+ rc = pam_get_authtok(pamh, flags, i ? "LDAP Password: " :
+ "Password: ", NULL, &p);
+ i = 2;
+ if (rc == PAM_SUCCESS) {
+ pam_set_item(pamh, PAM_AUTHTOK, p);
+ memset(p, 0, strlen(p));
+ free(p);
+ } else {
+ break;
+ }
+ }
+ rc = pam_get_item (pamh, PAM_AUTHTOK, (CONST_ARG void **) &p);
+ if (rc == PAM_SUCCESS) {
+ rc = pam_do_authc(ctx, username, svc, p, &err);
+ NSS2PAM_RC(rc, ignore_flags, ctx->authok);
+ }
+ if (rc == PAM_SUCCESS || (first_pass & USE_FIRST)) {
+ break;
+ }
+ first_pass = 0;
+ }
+
+ if (rc == PAM_SUCCESS) {
+ ctx->user = strdup(username);
+ if (ctx->authz == PAM_NEW_AUTHTOK_REQD)
+ ctx->oldpw = strdup(p);
+ }
+
+ /* update caller's idea of the user name */
+ if ( (rc==PAM_SUCCESS) && ctx->tmpluser && ctx->tmpluser[0] &&
+ (strcmp(ctx->tmpluser,username)!=0) ) {
+ rc = pam_set_item(pamh, PAM_USER, ctx->tmpluser);
+ }
+
+ return rc;
+}
+
+int pam_sm_setcred(
+ pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+static int
+pam_warn(
+ struct pam_conv *aconv, const char *message, int style, int no_warn)
+{
+ struct pam_message msg, *pmsg;
+ struct pam_response *resp;
+
+ if (no_warn)
+ return PAM_SUCCESS;
+
+ pmsg = &msg;
+
+ msg.msg_style = style;
+ msg.msg = (char *) message;
+ resp = NULL;
+
+ return aconv->conv (1,
+ (CONST_ARG struct pam_message **) &pmsg,
+ &resp, aconv->appdata_ptr);
+}
+
+static enum nss_status pam_read_authz(
+ TFILE *fp,pld_ctx *ctx,int *errnop)
+{
+ char *buffer = ctx->buf;
+ size_t buflen = sizeof(ctx->buf);
+ size_t bufptr = 0;
+ int32_t tmpint32;
+
+ READ_STRING_BUF(fp,ctx->tmpluser);
+ READ_STRING_BUF(fp,ctx->dn);
+ READ_INT32(fp,ctx->authz);
+ READ_STRING_BUF(fp,ctx->authzmsg);
+ ctx->authz = nslcd2pam_rc(ctx->authz);
+ return NSS_STATUS_SUCCESS;
+}
+
+static enum nss_status pam_do_authz(
+ pld_ctx *ctx,const char *username,const char *svc,int *errnop)
+{
+ NSS_BYGEN(NSLCD_ACTION_PAM_AUTHZ,
+ WRITE_STRING(fp,username);
+ WRITE_STRING(fp,ctx->dn);
+ WRITE_STRING(fp,svc),
+ pam_read_authz(fp,ctx,errnop));
+}
+
+int pam_sm_acct_mgmt(
+ pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ int rc, err;
+ const char *username, *svc;
+ int no_warn = 0, ignore_flags = 0;
+ int i;
+ struct pam_conv *appconv;
+ pld_ctx *ctx = NULL, ctx2;
+
+ for (i = 0; i < argc; i++)
+ {
+ if (!strcmp (argv[i], "use_first_pass"))
+ ;
+ else if (!strcmp (argv[i], "try_first_pass"))
+ ;
+ else if (!strcmp (argv[i], "no_warn"))
+ no_warn = 1;
+ else if (!strcmp (argv[i], "ignore_unknown_user"))
+ ignore_flags |= IGNORE_UNKNOWN;
+ else if (!strcmp (argv[i], "ignore_authinfo_unavail"))
+ ignore_flags |= IGNORE_UNAVAIL;
+ else if (!strcmp (argv[i], "debug"))
+ ;
+ else
+ syslog (LOG_ERR, "illegal option %s", argv[i]);
+ }
+
+ if (flags & PAM_SILENT)
+ no_warn = 1;
+
+ rc = pam_get_item (pamh, PAM_CONV, (CONST_ARG void **) &appconv);
+ if (rc != PAM_SUCCESS)
+ return rc;
+
+ rc = pam_get_user (pamh, (CONST_ARG char **) &username, NULL);
+ if (rc != PAM_SUCCESS)
+ return rc;
+
+ if (username == NULL)
+ return PAM_USER_UNKNOWN;
+
+ rc = pam_get_ctx(pamh, username, &ctx);
+ if (rc != PAM_SUCCESS)
+ return rc;
+
+ rc = pam_get_item (pamh, PAM_SERVICE, (CONST_ARG void **) &svc);
+ if (rc != PAM_SUCCESS)
+ return rc;
+
+ ctx2.dn = ctx->dn;
+ ctx2.user = ctx->user;
+ rc = pam_do_authz(&ctx2, username, svc, &err);
+ NSS2PAM_RC(rc, ignore_flags, PAM_SUCCESS);
+ if (rc != PAM_SUCCESS) {
+ if (rc != PAM_IGNORE)
+ pam_warn(appconv, "LDAP authorization failed", PAM_ERROR_MSG, no_warn);
+ } else {
+ if (ctx2.authzmsg && ctx2.authzmsg[0])
+ pam_warn(appconv, ctx2.authzmsg, PAM_TEXT_INFO, no_warn);
+ if (ctx2.authz == PAM_SUCCESS) {
+ rc = ctx->authz;
+ if (ctx->authzmsg && ctx->authzmsg[0])
+ pam_warn(appconv, ctx->authzmsg, PAM_TEXT_INFO, no_warn);
+ }
+ }
+
+ /* update caller's idea of the user name */
+ if ( (rc==PAM_SUCCESS) && ctx->tmpluser && ctx->tmpluser[0] &&
+ (strcmp(ctx->tmpluser,username)!=0) ) {
+ rc = pam_set_item(pamh, PAM_USER, ctx->tmpluser);
+ }
+ return rc;
+}
+
+static enum nss_status pam_read_sess(
+ TFILE *fp,pld_ctx *ctx,int *errnop)
+{
+ int tmpint32;
+ READ_INT32(fp,ctx->sessid);
+ return NSS_STATUS_SUCCESS;
+}
+
+static enum nss_status pam_do_sess(
+ pam_handle_t *pamh,pld_ctx *ctx,int action,int *errnop)
+{
+ const char *svc = NULL, *tty = NULL, *rhost = NULL, *ruser = NULL;
+
+ pam_get_item (pamh, PAM_SERVICE, (CONST_ARG void **) &svc);
+ pam_get_item (pamh, PAM_TTY, (CONST_ARG void **) &tty);
+ pam_get_item (pamh, PAM_RHOST, (CONST_ARG void **) &rhost);
+ pam_get_item (pamh, PAM_RUSER, (CONST_ARG void **) &ruser);
+
+ {
+ NSS_BYGEN(action,
+ WRITE_STRING(fp,ctx->user);
+ WRITE_STRING(fp,ctx->dn);
+ WRITE_STRING(fp,svc);
+ WRITE_STRING(fp,tty);
+ WRITE_STRING(fp,rhost);
+ WRITE_STRING(fp,ruser);
+ WRITE_INT32(fp,ctx->sessid),
+ pam_read_sess(fp,ctx,errnop));
+ }
+}
+
+static int pam_sm_session(
+ pam_handle_t *pamh, int flags, int argc, const char **argv,
+ int action, int *no_warn)
+{
+ int rc, err;
+ const char *username;
+ int ignore_flags = 0;
+ int i, success = PAM_SUCCESS;
+ pld_ctx *ctx = NULL;
+
+ for (i = 0; i < argc; i++)
+ {
+ if (!strcmp (argv[i], "use_first_pass"))
+ ;
+ else if (!strcmp (argv[i], "try_first_pass"))
+ ;
+ else if (!strcmp (argv[i], "no_warn"))
+ *no_warn = 1;
+ else if (!strcmp (argv[i], "ignore_unknown_user"))
+ ignore_flags |= IGNORE_UNKNOWN;
+ else if (!strcmp (argv[i], "ignore_authinfo_unavail"))
+ ignore_flags |= IGNORE_UNAVAIL;
+ else if (!strcmp (argv[i], "debug"))
+ ;
+ else
+ syslog (LOG_ERR, "illegal option %s", argv[i]);
+ }
+
+ if (flags & PAM_SILENT)
+ *no_warn = 1;
+
+ rc = pam_get_user (pamh, (CONST_ARG char **) &username, NULL);
+ if (rc != PAM_SUCCESS)
+ return rc;
+
+ if (username == NULL)
+ return PAM_USER_UNKNOWN;
+
+ rc = pam_get_ctx(pamh, username, &ctx);
+ if (rc != PAM_SUCCESS)
+ return rc;
+
+ rc = pam_do_sess(pamh, ctx, action, &err);
+ NSS2PAM_RC(rc, ignore_flags, PAM_SUCCESS);
+ return rc;
+}
+
+int pam_sm_open_session(
+ pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ int rc, no_warn = 0;
+ struct pam_conv *appconv;
+
+ rc = pam_get_item (pamh, PAM_CONV, (CONST_ARG void **) &appconv);
+ if (rc != PAM_SUCCESS)
+ return rc;
+
+ rc = pam_sm_session(pamh,flags,argc,argv,NSLCD_ACTION_PAM_SESS_O,&no_warn);
+ if (rc != PAM_SUCCESS && rc != PAM_IGNORE)
+ pam_warn(appconv, "LDAP open_session failed", PAM_ERROR_MSG, no_warn);
+ return rc;
+}
+
+int pam_sm_close_session(
+ pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ int rc, no_warn = 0;;
+ struct pam_conv *appconv;
+
+ rc = pam_get_item (pamh, PAM_CONV, (CONST_ARG void **) &appconv);
+ if (rc != PAM_SUCCESS)
+ return rc;
+
+ rc = pam_sm_session(pamh,flags,argc,argv,NSLCD_ACTION_PAM_SESS_C,&no_warn);
+ if (rc != PAM_SUCCESS && rc != PAM_IGNORE)
+ pam_warn(appconv, "LDAP close_session failed", PAM_ERROR_MSG, no_warn);
+ return rc;
+}
+
+static enum nss_status pam_read_pwmod(
+ TFILE *fp,pld_ctx *ctx,int *errnop)
+{
+ char *buffer = ctx->buf, *user;
+ size_t buflen = sizeof(ctx->buf);
+ size_t bufptr = 0;
+ int32_t tmpint32;
+
+ READ_STRING_BUF(fp,ctx->tmpluser);
+ READ_STRING_BUF(fp,ctx->dn);
+ READ_INT32(fp,ctx->authz);
+ READ_STRING_BUF(fp,ctx->authzmsg);
+ ctx->authz = nslcd2pam_rc(ctx->authz);
+ return NSS_STATUS_SUCCESS;
+}
+
+static enum nss_status pam_do_pwmod(
+ pld_ctx *ctx, const char *user, const char *svc,
+ const char *oldpw, const char *newpw, int *errnop)
+{
+ NSS_BYGEN(NSLCD_ACTION_PAM_PWMOD,
+ WRITE_STRING(fp,user);
+ WRITE_STRING(fp,ctx->dn);
+ WRITE_STRING(fp,svc);
+ WRITE_STRING(fp,oldpw);
+ WRITE_STRING(fp,newpw),
+ pam_read_pwmod(fp,ctx,errnop));
+}
+
+int pam_sm_chauthtok(
+ pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ int rc, err;
+ const char *username, *p = NULL, *q = NULL, *svc;
+ int first_pass = 0, no_warn = 0, ignore_flags = 0;
+ int i, success = PAM_SUCCESS;
+ struct pam_conv *appconv;
+ pld_ctx *ctx = NULL;
+
+ for (i = 0; i < argc; i++)
+ {
+ if (!strcmp (argv[i], "use_first_pass"))
+ first_pass |= USE_FIRST;
+ else if (!strcmp (argv[i], "try_first_pass"))
+ first_pass |= TRY_FIRST;
+ else if (!strcmp (argv[i], "use_authtok"))
+ first_pass |= USE_TOKEN;
+ else if (!strcmp (argv[i], "no_warn"))
+ no_warn = 1;
+ else if (!strcmp (argv[i], "ignore_unknown_user"))
+ ignore_flags |= IGNORE_UNKNOWN;
+ else if (!strcmp (argv[i], "ignore_authinfo_unavail"))
+ ignore_flags |= IGNORE_UNAVAIL;
+ else if (!strcmp (argv[i], "debug"))
+ ;
+ else
+ syslog (LOG_ERR, "illegal option %s", argv[i]);
+ }
+
+ if (flags & PAM_SILENT)
+ no_warn = 1;
+
+ rc = pam_get_item (pamh, PAM_CONV, (CONST_ARG void **) &appconv);
+ if (rc != PAM_SUCCESS)
+ return rc;
+
+ rc = pam_get_user (pamh, (CONST_ARG char **) &username, NULL);
+ if (rc != PAM_SUCCESS)
+ return rc;
+
+ if (username == NULL)
+ return PAM_USER_UNKNOWN;
+
+ rc = pam_get_ctx(pamh, username, &ctx);
+ if (rc != PAM_SUCCESS)
+ return rc;
+
+ rc = pam_get_item (pamh, PAM_SERVICE, (CONST_ARG void **) &svc);
+ if (rc != PAM_SUCCESS)
+ return rc;
+
+ if (flags & PAM_PRELIM_CHECK) {
+ if (getuid()) {
+ if (!first_pass) {
+ rc = pam_get_authtok(pamh, flags, "(current) LDAP Password: ",
+ NULL, &p);
+ if (rc == PAM_SUCCESS) {
+ pam_set_item(pamh, PAM_OLDAUTHTOK, p);
+ memset(p, 0, strlen(p));
+ free(p);
+ }
+ }
+ rc = pam_get_item(pamh, PAM_OLDAUTHTOK, &p);
+ if (rc) return rc;
+ } else {
+ rc = PAM_SUCCESS;
+ }
+ if (!ctx->dn) {
+ rc = pam_do_pwmod(ctx, username, svc, p, NULL, &err);
+ NSS2PAM_RC(rc, ignore_flags, PAM_SUCCESS);
+ }
+ return rc;
+ }
+
+ rc = pam_get_item(pamh, PAM_OLDAUTHTOK, &p);
+ if (rc) return rc;
+
+ if (!p)
+ p = ctx->oldpw;
+
+ if (first_pass) {
+ rc = pam_get_item(pamh, PAM_AUTHTOK, &q);
+ if ((rc != PAM_SUCCESS || !q) && (first_pass & (USE_FIRST|USE_TOKEN))) {
+ if (rc == PAM_SUCCESS)
+ rc = PAM_AUTHTOK_RECOVERY_ERR;
+ return rc;
+ }
+ }
+ if (!q) {
+ rc = pam_get_authtok(pamh, flags, "Enter new LDAP Password: ",
+ "Retype new LDAP Password: ", &q);
+ if (rc == PAM_SUCCESS) {
+ pam_set_item(pamh, PAM_AUTHTOK, q);
+ memset(q, 0, strlen(q));
+ free(q);
+ rc = pam_get_item(pamh, PAM_AUTHTOK, &q);
+ }
+ if (rc != PAM_SUCCESS)
+ return rc;
+ }
+ rc = pam_do_pwmod(ctx, username, svc, p, q, &err);
+ p = NULL; q = NULL;
+ NSS2PAM_RC(rc, ignore_flags, PAM_SUCCESS);
+ if (rc == PAM_SUCCESS) {
+ rc = ctx->authz;
+ if (rc != PAM_SUCCESS)
+ pam_warn(appconv, ctx->authzmsg, PAM_ERROR_MSG, no_warn);
+ } else if (rc != PAM_IGNORE)
+ pam_warn(appconv, "LDAP pwmod failed", PAM_ERROR_MSG, no_warn);
+ return rc;
+}
+
+#ifdef PAM_STATIC
+struct pam_module _pam_ldap_modstruct = {
+ "pam_ldap",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok
+};
+#endif /* PAM_STATIC */