diff options
author | Arthur de Jong <arthur@arthurdejong.org> | 2009-05-09 11:27:10 +0200 |
---|---|---|
committer | Arthur de Jong <arthur@arthurdejong.org> | 2009-05-09 11:27:10 +0200 |
commit | 5f035facd24b3d15743eae48ddec15115c705e79 (patch) | |
tree | 370a83232b9e51677bbe73fced1debcc3331c08f | |
parent | be1b2c1e63beb0fc0b90e21da4679e359a9f9fdc (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-- | AUTHORS | 1 | ||||
-rw-r--r-- | Makefile.am | 14 | ||||
-rw-r--r-- | configure.ac | 40 | ||||
-rw-r--r-- | debian/copyright | 1 | ||||
-rw-r--r-- | nslcd.h | 60 | ||||
-rw-r--r-- | pam/Makefile.am | 39 | ||||
-rw-r--r-- | pam/exports.linux | 16 | ||||
-rw-r--r-- | pam/pam.c | 725 |
8 files changed, 891 insertions, 5 deletions
@@ -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 @@ -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 */ |