summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Bokovoy <abokovoy@redhat.com>2013-07-31 14:36:13 +0300
committerAlexander Bokovoy <abokovoy@redhat.com>2013-08-06 14:24:42 +0300
commit3cc64ccf520e823bd2cfe595b0e4d91efbc92ebb (patch)
treed9a4e250249a60077c66c2aa2da709b901703dd8
parent4757303fa53ef14f084606adb0ed411fdfe23a25 (diff)
downloadslapi-nis-3cc64ccf520e823bd2cfe595b0e4d91efbc92ebb.tar.gz
slapi-nis-3cc64ccf520e823bd2cfe595b0e4d91efbc92ebb.tar.xz
slapi-nis-3cc64ccf520e823bd2cfe595b0e4d91efbc92ebb.zip
schema-compat: add support for authenticating users through PAM
src/back-sch-pam.c implements PAM authentication for users not found in the LDAP tree using system-auth system service when running on FreeIPA master server.
-rw-r--r--src/back-sch-pam.c290
1 files changed, 290 insertions, 0 deletions
diff --git a/src/back-sch-pam.c b/src/back-sch-pam.c
new file mode 100644
index 0000000..9d206e3
--- /dev/null
+++ b/src/back-sch-pam.c
@@ -0,0 +1,290 @@
+/* This code is originated from pam_passthru plugin of 389-ds,
+ * thus its copyright statement is introduced below: */
+
+/** BEGIN COPYRIGHT BLOCK
+ * This Program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; version 2 of the License.
+ *
+ * This Program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * In addition, as a special exception, Red Hat, Inc. gives You the additional
+ * right to link the code of this Program with code not covered under the GNU
+ * General Public License ("Non-GPL Code") and to distribute linked combinations
+ * including the two, subject to the limitations in this paragraph. Non-GPL Code
+ * permitted under this exception must only link to the code of this Program
+ * through those well defined interfaces identified in the file named EXCEPTION
+ * found in the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline functions from
+ * the Approved Interfaces without causing the resulting work to be covered by
+ * the GNU General Public License. Only Red Hat, Inc. may make changes or
+ * additions to the list of Approved Interfaces. You must obey the GNU General
+ * Public License in all respects for all of the Program code and other code used
+ * in conjunction with the Program except the Non-GPL Code covered by this
+ * exception. If you modify this file, you may extend this exception to your
+ * version of the file, but you are not obligated to do so. If you do not wish to
+ * provide this exception without modification, you must delete this exception
+ * statement from your version and license this file solely under the GPL without
+ * exception.
+ *
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H
+#include <nspr.h>
+#include <nss.h>
+#include <dirsrv/slapi-plugin.h>
+#else
+#include <slapi-plugin.h>
+#endif
+
+
+#include <security/pam_appl.h>
+
+/* for third arg to pam_start */
+struct my_pam_conv_str {
+ Slapi_PBlock *pb;
+ const char *pam_identity;
+};
+
+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. We have to use malloc/free due to the caller freeing the response */
+ 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) {
+ /* hard to tell what prompt is for . . . */
+ /* assume prompts for password are either BINARY or ECHO_OFF */
+ switch (msg[ii]->msg_style) {
+ case PAM_PROMPT_ECHO_OFF:
+#ifdef LINUX
+ case PAM_BINARY_PROMPT:
+#endif
+ reply[ii].resp = malloc(creds->bv_len + 1);
+ if (reply[ii].resp != NULL) {
+ memcpy(reply[ii].resp, creds->bv_val, creds->bv_len);
+ reply[ii].resp[creds->bv_len] = '\0';
+ } else {
+ ret = PAM_CONV_ERR;
+ }
+ break;
+ case PAM_PROMPT_ECHO_ON:
+ reply[ii].resp = strdup(my_data->pam_identity);
+ if (reply[ii].resp == NULL) {
+ ret = PAM_CONV_ERR;
+ }
+ break;
+ case PAM_ERROR_MSG:
+ case PAM_TEXT_INFO:
+ break;
+ default:
+ ret = PAM_CONV_ERR;
+ break;
+ }
+ }
+
+ 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.
+ * 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.
+ */
+static int
+do_pam_auth(Slapi_PBlock *pb, const char *pam_service, /* name of service for pam_start() */
+ int pw_response_requested, /* do we need to send pwd policy resp control */
+ const char *username)
+{
+ const char *binddn = NULL;
+ Slapi_DN *bindsdn = NULL;
+ int rc = PAM_SUCCESS;
+ 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 *errmsg = NULL; /* free with PR_smprintf_free */
+
+ slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &bindsdn);
+ if (NULL == bindsdn) {
+ errmsg = PR_smprintf("Null bind dn");
+ retcode = LDAP_OPERATIONS_ERROR;
+ goto done; /* skip the pam stuff */
+ }
+ binddn = slapi_sdn_get_dn(bindsdn);
+
+ /* do the pam stuff */
+ my_data.pb = pb;
+ my_data.pam_identity = username;
+ my_pam_conv.appdata_ptr = &my_data;
+ rc = pam_start(pam_service, username, &my_pam_conv, &pam_handle);
+
+ if (rc == PAM_SUCCESS) {
+ /* use PAM_SILENT - there is no user interaction at this point */
+ rc = pam_authenticate(pam_handle, 0);
+ /* check different types of errors here */
+ switch (rc) {
+ case PAM_USER_UNKNOWN:
+ errmsg = PR_smprintf("User id [%s] for bind DN [%s] does not exist in PAM",
+ username, binddn);
+ retcode = LDAP_NO_SUCH_OBJECT; /* user unknown */
+ break;
+ case PAM_AUTH_ERR:
+ errmsg = PR_smprintf("Invalid PAM password for user id [%s], bind DN [%s]",
+ username, binddn);
+ retcode = LDAP_INVALID_CREDENTIALS; /* invalid creds */
+ break;
+ case PAM_MAXTRIES:
+ errmsg = PR_smprintf("Authentication retry limit exceeded in PAM for "
+ "user id [%s], bind DN [%s]", username, binddn);
+ if (pw_response_requested) {
+ slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_ACCTLOCKED);
+ }
+ retcode = LDAP_CONSTRAINT_VIOLATION; /* max retries */
+ break;
+ case PAM_SUCCESS:
+ break;
+ default:
+ errmsg = PR_smprintf("Unknown PAM error [%s] for user id [%s], bind DN [%s]",
+ pam_strerror(pam_handle, rc), username, binddn);
+ retcode = LDAP_OPERATIONS_ERROR; /* pam config or network problem */
+ break;
+ }
+ }
+
+ /* 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);
+ /* check different types of errors here */
+ switch (rc) {
+ case PAM_USER_UNKNOWN:
+ errmsg = PR_smprintf("User id [%s] for bind DN [%s] does not exist in PAM",
+ username, binddn);
+ retcode = LDAP_NO_SUCH_OBJECT; /* user unknown */
+ break;
+ case PAM_AUTH_ERR:
+ errmsg = PR_smprintf("Invalid PAM password for user id [%s], bind DN [%s]",
+ username, binddn);
+ retcode = LDAP_INVALID_CREDENTIALS; /* invalid creds */
+ break;
+ case PAM_PERM_DENIED:
+ errmsg = PR_smprintf("Access denied for PAM user id [%s], bind DN [%s]"
+ " - see administrator", username, binddn);
+ if (pw_response_requested) {
+ slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_ACCTLOCKED);
+ }
+ retcode = LDAP_UNWILLING_TO_PERFORM;
+ break;
+ case PAM_ACCT_EXPIRED:
+ case PAM_NEW_AUTHTOK_REQD:
+ errmsg = PR_smprintf("Expired PAM password for user id [%s], bind DN [%s]: "
+ "reset required", username, binddn);
+ 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;
+ break;
+ case PAM_SUCCESS:
+ break;
+ default:
+ errmsg = PR_smprintf("Unknown PAM error [%s] for user id [%s], bind DN [%s]",
+ pam_strerror(pam_handle, rc), username, binddn);
+ retcode = LDAP_OPERATIONS_ERROR; /* unknown */
+ break;
+ }
+ }
+
+ rc = pam_end(pam_handle, rc);
+
+done:
+ if ((retcode == LDAP_SUCCESS) && (rc != PAM_SUCCESS)) {
+ errmsg = PR_smprintf("Unknown PAM error [%d] for user id [%s], bind DN [%s]",
+ rc, username, binddn);
+ retcode = LDAP_OPERATIONS_ERROR;
+ }
+
+ if (retcode != LDAP_SUCCESS) {
+ slapi_send_ldap_result(pb, retcode, NULL, errmsg, 0, NULL);
+ }
+
+ if (errmsg) {
+ PR_smprintf_free(errmsg);
+ }
+
+ return retcode;
+}
+
+int
+backend_sch_do_pam_auth(Slapi_PBlock *pb, const char *username)
+{
+ int rc = LDAP_SUCCESS;
+ int pw_response_requested;
+ LDAPControl **reqctrls = NULL;
+
+ slapi_pblock_get (pb, SLAPI_REQCONTROLS, &reqctrls);
+ slapi_pblock_get (pb, SLAPI_PWPOLICY, &pw_response_requested);
+
+ rc = do_pam_auth(pb, "system-auth", pw_response_requested, username);
+
+ return rc;
+}