summaryrefslogtreecommitdiffstats
path: root/modules/pam_userdb/pam_userdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pam_userdb/pam_userdb.c')
-rw-r--r--modules/pam_userdb/pam_userdb.c512
1 files changed, 512 insertions, 0 deletions
diff --git a/modules/pam_userdb/pam_userdb.c b/modules/pam_userdb/pam_userdb.c
new file mode 100644
index 0000000..cab37b3
--- /dev/null
+++ b/modules/pam_userdb/pam_userdb.c
@@ -0,0 +1,512 @@
+/* pam_userdb module */
+
+/*
+ * Written by Cristian Gafton <gafton@redhat.com> 1996/09/10
+ * See the end of the file for Copyright Information
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#ifdef HAVE_LIBXCRYPT
+#include <xcrypt.h>
+#elif defined(HAVE_CRYPT_H)
+#include <crypt.h>
+#endif
+
+#include "pam_userdb.h"
+
+#ifdef HAVE_NDBM_H
+# include <ndbm.h>
+#else
+# ifdef HAVE_DB_H
+# define DB_DBM_HSEARCH 1 /* use the dbm interface */
+# define HAVE_DBM /* for BerkDB 5.0 and later */
+# include <db.h>
+# else
+# error "failed to find a libdb or equivalent"
+# endif
+#endif
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+
+#include <security/pam_modules.h>
+#include <security/pam_ext.h>
+#include <security/_pam_macros.h>
+
+/*
+ * Conversation function to obtain the user's password
+ */
+static int
+obtain_authtok(pam_handle_t *pamh)
+{
+ char *resp;
+ const void *item;
+ int retval;
+
+ retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, &resp, _("Password: "));
+
+ if (retval != PAM_SUCCESS)
+ return retval;
+
+ if (resp == NULL)
+ return PAM_CONV_ERR;
+
+ /* set the auth token */
+ retval = pam_set_item(pamh, PAM_AUTHTOK, resp);
+
+ /* clean it up */
+ _pam_overwrite(resp);
+ _pam_drop(resp);
+
+ if ( (retval != PAM_SUCCESS) ||
+ (retval = pam_get_item(pamh, PAM_AUTHTOK, &item))
+ != PAM_SUCCESS ) {
+ return retval;
+ }
+
+ return retval;
+}
+
+static int
+_pam_parse (pam_handle_t *pamh, int argc, const char **argv,
+ const char **database, const char **cryptmode)
+{
+ int ctrl;
+
+ *database = NULL;
+ *cryptmode = NULL;
+
+ /* step through arguments */
+ for (ctrl = 0; argc-- > 0; ++argv)
+ {
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else if (!strcasecmp(*argv, "icase"))
+ ctrl |= PAM_ICASE_ARG;
+ else if (!strcasecmp(*argv, "dump"))
+ ctrl |= PAM_DUMP_ARG;
+ else if (!strcasecmp(*argv, "unknown_ok"))
+ ctrl |= PAM_UNKNOWN_OK_ARG;
+ else if (!strcasecmp(*argv, "key_only"))
+ ctrl |= PAM_KEY_ONLY_ARG;
+ else if (!strcasecmp(*argv, "use_first_pass"))
+ ctrl |= PAM_USE_FPASS_ARG;
+ else if (!strcasecmp(*argv, "try_first_pass"))
+ ctrl |= PAM_TRY_FPASS_ARG;
+ else if (!strncasecmp(*argv,"db=", 3))
+ {
+ *database = (*argv) + 3;
+ if (**database == '\0') {
+ *database = NULL;
+ pam_syslog(pamh, LOG_ERR,
+ "db= specification missing argument - ignored");
+ }
+ }
+ else if (!strncasecmp(*argv,"crypt=", 6))
+ {
+ *cryptmode = (*argv) + 6;
+ if (**cryptmode == '\0')
+ pam_syslog(pamh, LOG_ERR,
+ "crypt= specification missing argument - ignored");
+ }
+ else
+ {
+ pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
+ }
+ }
+
+ return ctrl;
+}
+
+
+/*
+ * Looks up an user name in a database and checks the password
+ *
+ * return values:
+ * 1 = User not found
+ * 0 = OK
+ * -1 = Password incorrect
+ * -2 = System error
+ */
+static int
+user_lookup (pam_handle_t *pamh, const char *database, const char *cryptmode,
+ const char *user, const char *pass, int ctrl)
+{
+ DBM *dbm;
+ datum key, data;
+
+ /* Open the DB file. */
+ dbm = dbm_open(database, O_RDONLY, 0644);
+ if (dbm == NULL) {
+ pam_syslog(pamh, LOG_ERR,
+ "user_lookup: could not open database `%s': %m", database);
+ return -2;
+ }
+
+ /* dump out the database contents for debugging */
+ if (ctrl & PAM_DUMP_ARG) {
+ pam_syslog(pamh, LOG_INFO, "Database dump:");
+ for (key = dbm_firstkey(dbm); key.dptr != NULL;
+ key = dbm_nextkey(dbm)) {
+ data = dbm_fetch(dbm, key);
+ pam_syslog(pamh, LOG_INFO,
+ "key[len=%d] = `%s', data[len=%d] = `%s'",
+ key.dsize, key.dptr, data.dsize, data.dptr);
+ }
+ }
+
+ /* do some more init work */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ if (ctrl & PAM_KEY_ONLY_ARG) {
+ if (asprintf(&key.dptr, "%s-%s", user, pass) < 0)
+ key.dptr = NULL;
+ else
+ key.dsize = strlen(key.dptr);
+ } else {
+ key.dptr = strdup(user);
+ key.dsize = strlen(user);
+ }
+
+ if (key.dptr) {
+ data = dbm_fetch(dbm, key);
+ memset(key.dptr, 0, key.dsize);
+ free(key.dptr);
+ }
+
+ if (ctrl & PAM_DEBUG_ARG) {
+ pam_syslog(pamh, LOG_INFO,
+ "password in database is [%p]`%.*s', len is %d",
+ data.dptr, data.dsize, (char *) data.dptr, data.dsize);
+ }
+
+ if (data.dptr != NULL) {
+ int compare = 0;
+
+ if (ctrl & PAM_KEY_ONLY_ARG)
+ {
+ dbm_close (dbm);
+ return 0; /* found it, data contents don't matter */
+ }
+
+ if (cryptmode && strncasecmp(cryptmode, "crypt", 5) == 0) {
+
+ /* crypt(3) password storage */
+
+ char *cryptpw = NULL;
+
+ if (data.dsize < 13) {
+ compare = -2;
+ } else if (ctrl & PAM_ICASE_ARG) {
+ compare = -2;
+ } else {
+#ifdef HAVE_CRYPT_R
+ struct crypt_data *cdata = NULL;
+ cdata = malloc(sizeof(*cdata));
+ if (cdata != NULL) {
+ cdata->initialized = 0;
+ cryptpw = crypt_r(pass, data.dptr, cdata);
+ }
+#else
+ cryptpw = crypt (pass, data.dptr);
+#endif
+ if (cryptpw && strlen(cryptpw) == (size_t)data.dsize) {
+ compare = memcmp(data.dptr, cryptpw, data.dsize);
+ } else {
+ compare = -2;
+ if (ctrl & PAM_DEBUG_ARG) {
+ if (cryptpw)
+ pam_syslog(pamh, LOG_INFO, "lengths of computed and stored hashes differ");
+ else
+ pam_syslog(pamh, LOG_INFO, "crypt() returned NULL");
+ }
+ }
+#ifdef HAVE_CRYPT_R
+ free(cdata);
+#endif
+ }
+
+ } else {
+
+ /* Unknown password encryption method -
+ * default to plaintext password storage
+ */
+
+ if (strlen(pass) != (size_t)data.dsize) {
+ compare = 1; /* wrong password len -> wrong password */
+ } else if (ctrl & PAM_ICASE_ARG) {
+ compare = strncasecmp(data.dptr, pass, data.dsize);
+ } else {
+ compare = strncmp(data.dptr, pass, data.dsize);
+ }
+
+ if (cryptmode && strncasecmp(cryptmode, "none", 4)
+ && (ctrl & PAM_DEBUG_ARG)) {
+ pam_syslog(pamh, LOG_INFO, "invalid value for crypt parameter: %s",
+ cryptmode);
+ pam_syslog(pamh, LOG_INFO, "defaulting to plaintext password mode");
+ }
+
+ }
+
+ dbm_close(dbm);
+ if (compare == 0)
+ return 0; /* match */
+ else
+ return -1; /* wrong */
+ } else {
+ int saw_user = 0;
+
+ if (ctrl & PAM_DEBUG_ARG) {
+ pam_syslog(pamh, LOG_INFO, "error returned by dbm_fetch: %m");
+ }
+
+ /* probably we should check dbm_error() here */
+
+ if ((ctrl & PAM_KEY_ONLY_ARG) == 0) {
+ dbm_close(dbm);
+ return 1; /* not key_only, so no entry => no entry for the user */
+ }
+
+ /* now handle the key_only case */
+ for (key = dbm_firstkey(dbm);
+ key.dptr != NULL;
+ key = dbm_nextkey(dbm)) {
+ int compare;
+ /* first compare the user portion (case sensitive) */
+ compare = strncmp(key.dptr, user, strlen(user));
+ if (compare == 0) {
+ /* assume failure */
+ compare = -1;
+ /* if we have the divider where we expect it to be... */
+ if (key.dptr[strlen(user)] == '-') {
+ saw_user = 1;
+ if ((size_t)key.dsize == strlen(user) + 1 + strlen(pass)) {
+ if (ctrl & PAM_ICASE_ARG) {
+ /* compare the password portion (case insensitive)*/
+ compare = strncasecmp(key.dptr + strlen(user) + 1,
+ pass,
+ strlen(pass));
+ } else {
+ /* compare the password portion (case sensitive) */
+ compare = strncmp(key.dptr + strlen(user) + 1,
+ pass,
+ strlen(pass));
+ }
+ }
+ }
+ if (compare == 0) {
+ dbm_close(dbm);
+ return 0; /* match */
+ }
+ }
+ }
+ dbm_close(dbm);
+ if (saw_user)
+ return -1; /* saw the user, but password mismatch */
+ else
+ return 1; /* not found */
+ }
+
+ /* NOT REACHED */
+ return -2;
+}
+
+/* --- authentication management functions (only) --- */
+
+int
+pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
+ int argc, const char **argv)
+{
+ const char *username;
+ const void *password;
+ const char *database = NULL;
+ const char *cryptmode = NULL;
+ int retval = PAM_AUTH_ERR, ctrl;
+
+ /* parse arguments */
+ ctrl = _pam_parse(pamh, argc, argv, &database, &cryptmode);
+ if (database == NULL) {
+ pam_syslog(pamh, LOG_ERR, "can not get the database name");
+ return PAM_SERVICE_ERR;
+ }
+
+ /* Get the username */
+ retval = pam_get_user(pamh, &username, NULL);
+ if ((retval != PAM_SUCCESS) || (!username)) {
+ pam_syslog(pamh, LOG_ERR, "can not get the username");
+ return PAM_SERVICE_ERR;
+ }
+
+ if ((ctrl & PAM_USE_FPASS_ARG) == 0 && (ctrl & PAM_TRY_FPASS_ARG) == 0) {
+ /* Converse to obtain a password */
+ retval = obtain_authtok(pamh);
+ if (retval != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "can not obtain password from user");
+ return retval;
+ }
+ }
+
+ /* Check if we got a password */
+ retval = pam_get_item(pamh, PAM_AUTHTOK, &password);
+ if (retval != PAM_SUCCESS || password == NULL) {
+ if ((ctrl & PAM_TRY_FPASS_ARG) != 0) {
+ /* Converse to obtain a password */
+ retval = obtain_authtok(pamh);
+ if (retval != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "can not obtain password from user");
+ return retval;
+ }
+ retval = pam_get_item(pamh, PAM_AUTHTOK, &password);
+ }
+ if (retval != PAM_SUCCESS || password == NULL) {
+ pam_syslog(pamh, LOG_ERR, "can not recover user password");
+ return PAM_AUTHTOK_RECOVERY_ERR;
+ }
+ }
+
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh, LOG_INFO, "Verify user `%s' with a password",
+ username);
+
+ /* Now use the username to look up password in the database file */
+ retval = user_lookup(pamh, database, cryptmode, username, password, ctrl);
+ switch (retval) {
+ case -2:
+ /* some sort of system error. The log was already printed */
+ return PAM_SERVICE_ERR;
+ case -1:
+ /* incorrect password */
+ pam_syslog(pamh, LOG_NOTICE,
+ "user `%s' denied access (incorrect password)",
+ username);
+ return PAM_AUTH_ERR;
+ case 1:
+ /* the user does not exist in the database */
+ if (ctrl & PAM_DEBUG_ARG)
+ pam_syslog(pamh, LOG_NOTICE,
+ "user `%s' not found in the database", username);
+ return PAM_USER_UNKNOWN;
+ case 0:
+ /* Otherwise, the authentication looked good */
+ pam_syslog(pamh, LOG_NOTICE, "user '%s' granted access", username);
+ return PAM_SUCCESS;
+ default:
+ /* we don't know anything about this return value */
+ pam_syslog(pamh, LOG_ERR,
+ "internal module error (retval = %d, user = `%s'",
+ retval, username);
+ return PAM_SERVICE_ERR;
+ }
+
+ /* should not be reached */
+ return PAM_IGNORE;
+}
+
+int
+pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
+ int argc UNUSED, const char **argv UNUSED)
+{
+ return PAM_SUCCESS;
+}
+
+int
+pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
+ int argc, const char **argv)
+{
+ const char *username;
+ const char *database = NULL;
+ const char *cryptmode = NULL;
+ int retval = PAM_AUTH_ERR, ctrl;
+
+ /* parse arguments */
+ ctrl = _pam_parse(pamh, argc, argv, &database, &cryptmode);
+
+ /* Get the username */
+ retval = pam_get_user(pamh, &username, NULL);
+ if ((retval != PAM_SUCCESS) || (!username)) {
+ pam_syslog(pamh, LOG_ERR,"can not get the username");
+ return PAM_SERVICE_ERR;
+ }
+
+ /* Now use the username to look up password in the database file */
+ retval = user_lookup(pamh, database, cryptmode, username, "", ctrl);
+ switch (retval) {
+ case -2:
+ /* some sort of system error. The log was already printed */
+ return PAM_SERVICE_ERR;
+ case -1:
+ /* incorrect password, but we don't care */
+ /* FALL THROUGH */
+ case 0:
+ /* authentication succeeded. dumbest password ever. */
+ return PAM_SUCCESS;
+ case 1:
+ /* the user does not exist in the database */
+ return PAM_USER_UNKNOWN;
+ default:
+ /* we don't know anything about this return value */
+ pam_syslog(pamh, LOG_ERR,
+ "internal module error (retval = %d, user = `%s'",
+ retval, username);
+ return PAM_SERVICE_ERR;
+ }
+
+ return PAM_SUCCESS;
+}
+
+/*
+ * Copyright (c) Cristian Gafton <gafton@redhat.com>, 1999
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */