[PATCH] Create cifscreds PAM module to insert credentials at login

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Adds a PAM module to add CIFS credentials at login.  Resending with tag.

Signed-off-by:  Orion Poplawski <orion@xxxxxxxx>

---
 Makefile.am     |  11 +-
 cifscreds.c     |  49 +----
 cifskey.c       |  52 ++++++
 cifskey.h       |  47 +++++
 configure.ac    |  24 ++-
 pam_cifscreds.c | 550 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 681 insertions(+), 52 deletions(-)
 create mode 100644 cifskey.c
 create mode 100644 cifskey.h
 create mode 100644 pam_cifscreds.c

diff --git a/Makefile.am b/Makefile.am
index 6407520..6e86cd3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -34,7 +34,7 @@ endif
 
 if CONFIG_CIFSCREDS
 bin_PROGRAMS += cifscreds
-cifscreds_SOURCES = cifscreds.c resolve_host.c util.c
+cifscreds_SOURCES = cifscreds.c cifskey.c resolve_host.c util.c
 cifscreds_LDADD = -lkeyutils
 man_MANS += cifscreds.1
 endif
@@ -91,4 +91,13 @@ idmapwb.8: idmapwb.8.in
 
 endif
 
+if CONFIG_PAM
+pamdir = $(libdir)/security
+
+pam_PROGRAMS = pam_cifscreds.so
+
+pam_cifscreds.so: pam_cifscreds.c cifskey.c resolve_host.c util.c
+	$(CC) $(CFLAGS) $(AM_CFLAGS) $(LDFLAGS) -shared -fpic -o $@ $+ -lpam -lkeyutils
+endif
+
 SUBDIRS = contrib
diff --git a/cifscreds.c b/cifscreds.c
index 60be4e5..fa05dc8 100644
--- a/cifscreds.c
+++ b/cifscreds.c
@@ -29,35 +29,16 @@
 #include <keyutils.h>
 #include <getopt.h>
 #include <errno.h>
+#include "cifskey.h"
 #include "mount.h"
 #include "resolve_host.h"
 #include "util.h"
 
 #define THIS_PROGRAM_NAME "cifscreds"
-#define KEY_PREFIX	  "cifs"
 
 /* max length of appropriate command */
 #define MAX_COMMAND_SIZE 32
 
-/* max length of username, password and domain name */
-#define MAX_USERNAME_SIZE 32
-#define MOUNT_PASSWD_SIZE 128
-#define MAX_DOMAIN_SIZE 64
-
-/*
- * disallowed characters for user and domain names. See:
- * http://technet.microsoft.com/en-us/library/bb726984.aspx
- * http://support.microsoft.com/kb/909264
- */
-#define USER_DISALLOWED_CHARS "\\/\"[]:|<>+=;,?*"
-#define DOMAIN_DISALLOWED_CHARS "\\/:*?\"<>|"
-
-/* destination keyring */
-#define DEST_KEYRING KEY_SPEC_SESSION_KEYRING
-#define CIFS_KEY_TYPE  "logon"
-#define CIFS_KEY_PERMS (KEY_POS_VIEW|KEY_POS_WRITE|KEY_POS_SEARCH| \
-			KEY_USR_VIEW|KEY_USR_WRITE|KEY_USR_SEARCH)
-
 struct cmdarg {
 	char		*host;
 	char		*user;
@@ -106,17 +87,6 @@ usage(void)
 	return EXIT_FAILURE;
 }
 
-/* search a specific key in keyring */
-static key_serial_t
-key_search(const char *addr, char keytype)
-{
-	char desc[INET6_ADDRSTRLEN + sizeof(KEY_PREFIX) + 4];
-
-	sprintf(desc, "%s:%c:%s", KEY_PREFIX, keytype, addr);
-
-	return keyctl_search(DEST_KEYRING, CIFS_KEY_TYPE, desc, 0);
-}
-
 /* search all program's keys in keyring */
 static key_serial_t key_search_all(void)
 {
@@ -170,23 +140,6 @@ key_search_all_out:
 	return ret;
 }
 
-/* add or update a specific key to keyring */
-static key_serial_t
-key_add(const char *addr, const char *user, const char *pass, char keytype)
-{
-	int len;
-	char desc[INET6_ADDRSTRLEN + sizeof(KEY_PREFIX) + 4];
-	char val[MOUNT_PASSWD_SIZE +  MAX_USERNAME_SIZE + 2];
-
-	/* set key description */
-	sprintf(desc, "%s:%c:%s", KEY_PREFIX, keytype, addr);
-
-	/* set payload contents */
-	len = sprintf(val, "%s:%s", user, pass);
-
-	return add_key(CIFS_KEY_TYPE, desc, val, len + 1, DEST_KEYRING);
-}
-
 /* add command handler */
 static int cifscreds_add(struct cmdarg *arg)
 {
diff --git a/cifskey.c b/cifskey.c
new file mode 100644
index 0000000..7716c42
--- /dev/null
+++ b/cifskey.c
@@ -0,0 +1,52 @@
+/*
+ * Credentials stashing routines for Linux CIFS VFS (virtual filesystem)
+ * Copyright (C) 2010 Jeff Layton (jlayton@xxxxxxxxx)
+ * Copyright (C) 2010 Igor Druzhinin (jaxbrigs@xxxxxxxxx)
+ *
+ * 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; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <keyutils.h>
+#include <stdio.h>
+#include "cifskey.h"
+#include "resolve_host.h"
+
+/* search a specific key in keyring */
+key_serial_t
+key_search(const char *addr, char keytype)
+{
+	char desc[INET6_ADDRSTRLEN + sizeof(KEY_PREFIX) + 4];
+
+	sprintf(desc, "%s:%c:%s", KEY_PREFIX, keytype, addr);
+
+	return keyctl_search(DEST_KEYRING, CIFS_KEY_TYPE, desc, 0);
+}
+
+/* add or update a specific key to keyring */
+key_serial_t
+key_add(const char *addr, const char *user, const char *pass, char keytype)
+{
+	int len;
+	char desc[INET6_ADDRSTRLEN + sizeof(KEY_PREFIX) + 4];
+	char val[MOUNT_PASSWD_SIZE +  MAX_USERNAME_SIZE + 2];
+
+	/* set key description */
+	sprintf(desc, "%s:%c:%s", KEY_PREFIX, keytype, addr);
+
+	/* set payload contents */
+	len = sprintf(val, "%s:%s", user, pass);
+
+	return add_key(CIFS_KEY_TYPE, desc, val, len + 1, DEST_KEYRING);
+}
diff --git a/cifskey.h b/cifskey.h
new file mode 100644
index 0000000..ed0c469
--- /dev/null
+++ b/cifskey.h
@@ -0,0 +1,47 @@
+/*
+ * Credentials stashing utility for Linux CIFS VFS (virtual filesystem) definitions
+ * Copyright (C) 2010 Jeff Layton (jlayton@xxxxxxxxx)
+ * Copyright (C) 2010 Igor Druzhinin (jaxbrigs@xxxxxxxxx)
+ *
+ * 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; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _CIFSKEY_H
+#define _CIFSKEY_H
+
+#define KEY_PREFIX	  "cifs"
+
+/* max length of username, password and domain name */
+#define MAX_USERNAME_SIZE 32
+#define MOUNT_PASSWD_SIZE 128
+#define MAX_DOMAIN_SIZE 64
+
+/*
+ * disallowed characters for user and domain names. See:
+ * http://technet.microsoft.com/en-us/library/bb726984.aspx
+ * http://support.microsoft.com/kb/909264
+ */
+#define USER_DISALLOWED_CHARS "\\/\"[]:|<>+=;,?*"
+#define DOMAIN_DISALLOWED_CHARS "\\/:*?\"<>|"
+
+/* destination keyring */
+#define DEST_KEYRING KEY_SPEC_SESSION_KEYRING
+#define CIFS_KEY_TYPE  "logon"
+#define CIFS_KEY_PERMS (KEY_POS_VIEW|KEY_POS_WRITE|KEY_POS_SEARCH| \
+			KEY_USR_VIEW|KEY_USR_WRITE|KEY_USR_SEARCH)
+
+key_serial_t key_search(const char *addr, char keytype);
+key_serial_t key_add(const char *addr, const char *user, const char *pass, char keytype);
+
+#endif /* _CIFSKEY_H */
diff --git a/configure.ac b/configure.ac
index c5b2244..4a9cb6d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -40,6 +40,11 @@ AC_ARG_ENABLE(cifsacl,
 	enable_cifsacl=$enableval,
 	enable_cifsacl="maybe")
 
+AC_ARG_ENABLE(pam,
+	[AS_HELP_STRING([--enable-pam],[Create cifscreds PAM module @<:@default=yes@:>@])],
+	enable_pam=$enableval,
+	enable_pam="maybe")
+
 AC_ARG_ENABLE(systemd,
 	[AS_HELP_STRING([--enable-systemd],[Enable systemd specific behavior for mount.cifs @<:@default=yes@:>@])],
 	enable_systemd=$enableval,
@@ -190,18 +195,30 @@ AC_TEST_WBCHL
 # test for presence of WBC_ID_TYPE_BOTH enum value
 AC_TEST_WBC_IDMAP_BOTH
 
-if test $enable_cifscreds != "no"; then
+if test $enable_cifscreds != "no" -o $enable_pam != "no"; then
 	AC_CHECK_HEADERS([keyutils.h], , [
 
-				if test $enable_cifscreds = "yes"; then
+				if test $enable_cifscreds = "yes" -o $enable_pam = "yes"; then
 					AC_MSG_ERROR([keyutils.h not found, consider installing keyutils-libs-devel.])
 				else
-					AC_MSG_WARN([keyutils.h not found, consider installing keyutils-libs-devel. Disabling cifscreds.])
+					AC_MSG_WARN([keyutils.h not found, consider installing keyutils-libs-devel. Disabling cifscreds and cifscreds PAM module.])
 					enable_cifscreds="no"
+					enable_pam="no"
 				fi
 			])
 fi
 
+if test $enable_pam != "no"; then
+	AC_CHECK_HEADERS([security/pam_appl.h], , [
+
+				if test $enable_pam = "yes"; then
+					AC_MSG_ERROR([security/pam_appl.h not found, consider installing keyutils-libs-devel.])
+				else
+					AC_MSG_WARN([security/pam_appl.h not found, consider installing pam-devel. Disabling cifscreds PAM module.])
+					enable_pam="no"
+				fi
+			])
+fi
 
 # ugly, but I'm not sure how to check for functions in a library that's not in $LIBS
 cu_saved_libs=$LIBS
@@ -231,6 +248,7 @@ AM_CONDITIONAL(CONFIG_CIFSUPCALL, [test "$enable_cifsupcall" != "no"])
 AM_CONDITIONAL(CONFIG_CIFSCREDS, [test "$enable_cifscreds" != "no"])
 AM_CONDITIONAL(CONFIG_CIFSIDMAP, [test "$enable_cifsidmap" != "no"])
 AM_CONDITIONAL(CONFIG_CIFSACL, [test "$enable_cifsacl" != "no"])
+AM_CONDITIONAL(CONFIG_PAM, [test "$enable_pam" != "no"])
 AM_CONDITIONAL(CONFIG_PLUGIN, [test "$enable_cifsidmap" != "no" -o "$enable_cifsacl" != "no"])
 
 LIBCAP_NG_PATH
diff --git a/pam_cifscreds.c b/pam_cifscreds.c
new file mode 100644
index 0000000..1385146
--- /dev/null
+++ b/pam_cifscreds.c
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2013 Orion Poplawski <orion@xxxxxxxxxxxxx>
+ *
+ * based on gkr-pam-module.c, Copyright (C) 2007 Stef Walter
+ *
+ * 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; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/types.h>
+/*
+#include <signal.h>
+#include <unistd.h>
+#include <sys/wait.h>
+*/
+
+#include <keyutils.h>
+
+#include <security/pam_appl.h>
+#include <security/pam_modules.h>
+#include <security/pam_ext.h>
+
+#include "cifskey.h"
+#include "mount.h"
+#include "resolve_host.h"
+#include "util.h"
+
+/**
+ * Flags that can be passed to the PAM module
+ */
+enum {
+	ARG_DOMAIN	   = 1 << 0,	/** Set domain password */
+	ARG_DEBUG	   = 1 << 1	/** Print debug messages */
+};
+
+/**
+ * Parse the arguments passed to the PAM module.
+ *
+ * @param ph PAM handle
+ * @param argc number of arguments
+ * @param argv array of arguments
+ * @param kwalletopener kwalletopener argument, path to the kwalletopener binary
+ * @return ORed flags that have been parsed
+ */
+static uint parse_args (pam_handle_t *ph, int argc, const char **argv, const char **hostdomain)
+{
+	uint args = 0;
+	const void *svc;
+	int i;
+	const char *host = NULL;
+	const char *domain = NULL;
+
+	svc = NULL;
+	if (pam_get_item (ph, PAM_SERVICE, &svc) != PAM_SUCCESS) {
+		svc = NULL;
+	}
+
+	size_t host_len = strlen("host=");
+	size_t domain_len = strlen("domain=");
+
+	/* Parse the arguments */
+	for (i = 0; i < argc; i++) {
+		if (strncmp(argv[i], "host=", host_len) == 0) {
+			host = (argv[i]) + host_len;
+			if (*host == '\0') {
+				host = NULL;
+				pam_syslog(ph, LOG_ERR, ""
+					   "host= specification missing argument");
+			} else {
+				*hostdomain = host;
+			}
+		} else if (strncmp(argv[i], "domain=", domain_len) == 0) {
+			domain = (argv[i]) + domain_len;
+			if (*domain == '\0') {
+				domain = NULL;
+				pam_syslog(ph, LOG_ERR, ""
+					   "domain= specification missing argument");
+			} else {
+				*hostdomain = domain;
+				args |= ARG_DOMAIN;
+			}
+		} else if (strcmp(argv[i], "debug") == 0) {
+			args |= ARG_DEBUG;
+		} else {
+			pam_syslog(ph, LOG_ERR, "invalid option %s",
+				   argv[i]);
+		}
+	}
+
+	if (host && domain) {
+		pam_syslog(ph, LOG_ERR, "cannot specify both host= and "
+			   "domain= arguments");
+	}
+
+	return args;
+}
+
+static void
+free_password (char *password)
+{
+	volatile char *vp;
+	size_t len;
+
+	if (!password) {
+		return;
+	}
+
+	/* Defeats some optimizations */
+	len = strlen (password);
+	memset (password, 0xAA, len);
+	memset (password, 0xBB, len);
+
+	/* Defeats others */
+	vp = (volatile char*)password;
+	while (*vp) {
+		*(vp++) = 0xAA;
+	}
+
+	free (password);
+}
+
+static void
+cleanup_free_password (pam_handle_t *ph, void *data, int pam_end_status)
+{
+	free_password (data);
+}
+
+/**
+ * Set the cifs credentials
+ *
+ * @param ph PAM handle
+ * @param user
+ * @param password
+ * @param args ORed flags for this module
+ * @param hostdomain hostname or domainname
+ */
+static int cifscreds_pam_add(pam_handle_t *ph, const char *user, const char *password,
+			     uint args, const char *hostdomain)
+{
+	int ret = PAM_SUCCESS;
+	char addrstr[MAX_ADDR_LIST_LEN];
+	char *currentaddress, *nextaddress;
+	char keytype = ((args & ARG_DOMAIN) == ARG_DOMAIN) ? 'd' : 'a';
+
+	assert(user);
+	assert(password);
+	assert(hostdomain);
+
+	if (keytype == 'd') {
+		if (strpbrk(hostdomain, DOMAIN_DISALLOWED_CHARS)) {
+			pam_syslog(ph, LOG_ERR, "Domain name contains invalid characters");
+			return PAM_SERVICE_ERR;
+		}
+		strlcpy(addrstr, hostdomain, MAX_ADDR_LIST_LEN);
+	} else {
+		ret = resolve_host(hostdomain, addrstr);
+	}
+
+	switch (ret) {
+	case EX_USAGE:
+		pam_syslog(ph, LOG_ERR, "Could not resolve address for %s", hostdomain);
+		return PAM_SERVICE_ERR;
+
+	case EX_SYSERR:
+		pam_syslog(ph, LOG_ERR, "Problem parsing address list");
+		return PAM_SERVICE_ERR;
+	}
+
+	if (strpbrk(user, USER_DISALLOWED_CHARS)) {
+		pam_syslog(ph, LOG_ERR, "Incorrect username");
+		return PAM_SERVICE_ERR;
+	}
+
+	/* search for same credentials stashed for current host */
+	currentaddress = addrstr;
+	nextaddress = strchr(currentaddress, ',');
+	if (nextaddress)
+		*nextaddress++ = '\0';
+
+	while (currentaddress) {
+		if (key_search(currentaddress, keytype) > 0) {
+			pam_syslog(ph, LOG_WARNING, "You already have stashed credentials "
+				"for %s (%s)", currentaddress, hostdomain);
+
+			return PAM_SERVICE_ERR;
+		}
+
+		currentaddress = nextaddress;
+		if (currentaddress) {
+			*(currentaddress - 1) = ',';
+			nextaddress = strchr(currentaddress, ',');
+			if (nextaddress)
+				*nextaddress++ = '\0';
+		}
+	}
+
+	/* Set the password */
+	currentaddress = addrstr;
+	nextaddress = strchr(currentaddress, ',');
+	if (nextaddress)
+		*nextaddress++ = '\0';
+
+	while (currentaddress) {
+		key_serial_t key = key_add(currentaddress, user, password, keytype);
+		if (key <= 0) {
+			pam_syslog(ph, LOG_ERR, "error: Add credential key for %s",
+				currentaddress);
+		} else {
+			if ((args & ARG_DEBUG) == ARG_DEBUG) {
+				pam_syslog(ph, LOG_DEBUG, "credential key for \\\\%s\\%s added",
+					   currentaddress, user);
+			}
+			if (keyctl(KEYCTL_SETPERM, key, CIFS_KEY_PERMS) < 0) {
+				pam_syslog(ph, LOG_ERR,"error: Setting permissons "
+					"on key, attempt to delete...");
+
+				if (keyctl(KEYCTL_UNLINK, key, DEST_KEYRING) < 0) {
+					pam_syslog(ph, LOG_ERR, "error: Deleting key from "
+						"keyring for %s (%s)",
+						currentaddress, hostdomain);
+				}
+			}
+		}
+
+		currentaddress = nextaddress;
+		if (currentaddress) {
+			nextaddress = strchr(currentaddress, ',');
+			if (nextaddress)
+				*nextaddress++ = '\0';
+		}
+	}
+
+	return PAM_SUCCESS;
+}
+
+/**
+ * Update the cifs credentials
+ *
+ * @param ph PAM handle
+ * @param user
+ * @param password
+ * @param args ORed flags for this module
+ * @param hostdomain hostname or domainname
+ */
+static int cifscreds_pam_update(pam_handle_t *ph, const char *user, const char *password,
+				uint args, const char *hostdomain)
+{
+	int ret = PAM_SUCCESS;
+	char addrstr[MAX_ADDR_LIST_LEN];
+	char *currentaddress, *nextaddress;
+	char *addrs[16];
+	int id, count = 0;
+	char keytype = ((args & ARG_DOMAIN) == ARG_DOMAIN) ? 'd' : 'a';
+
+	assert(user);
+	assert(password);
+	assert(hostdomain);
+
+	if (keytype == 'd') {
+		if (strpbrk(hostdomain, DOMAIN_DISALLOWED_CHARS)) {
+			pam_syslog(ph, LOG_ERR, "Domain name contains invalid characters");
+			return PAM_SERVICE_ERR;
+		}
+		strlcpy(addrstr, hostdomain, MAX_ADDR_LIST_LEN);
+	} else {
+		ret = resolve_host(hostdomain, addrstr);
+	}
+
+	switch (ret) {
+	case EX_USAGE:
+		pam_syslog(ph, LOG_ERR, "Could not resolve address for %s", hostdomain);
+		return PAM_SERVICE_ERR;
+
+	case EX_SYSERR:
+		pam_syslog(ph, LOG_ERR, "Problem parsing address list");
+		return PAM_SERVICE_ERR;
+	}
+
+	if (strpbrk(user, USER_DISALLOWED_CHARS)) {
+		pam_syslog(ph, LOG_ERR, "Incorrect username");
+		return PAM_SERVICE_ERR;
+	}
+
+	/* search for necessary credentials stashed in session keyring */
+	currentaddress = addrstr;
+	nextaddress = strchr(currentaddress, ',');
+	if (nextaddress)
+		*nextaddress++ = '\0';
+
+	while (currentaddress) {
+		if (key_search(currentaddress, keytype) > 0) {
+			addrs[count] = currentaddress;
+			count++;
+		}
+
+		currentaddress = nextaddress;
+		if (currentaddress) {
+			nextaddress = strchr(currentaddress, ',');
+			if (nextaddress)
+				*nextaddress++ = '\0';
+		}
+	}
+
+	if (!count) {
+		pam_syslog(ph, LOG_ERR, "You have no same stached credentials for %s", hostdomain);
+		return PAM_SERVICE_ERR;
+	}
+
+	for (id = 0; id < count; id++) {
+		key_serial_t key = key_add(currentaddress, user, password, keytype);
+		if (key <= 0) {
+			pam_syslog(ph, LOG_ERR, "error: Update credential key for %s",
+				currentaddress);
+		}
+	}
+
+	return PAM_SUCCESS;
+}
+
+/**
+ * PAM function called during authentication.
+ *
+ * This function first tries to get a password from PAM. Afterwards two
+ * scenarios are possible:
+ *
+ * - A session is already available which usually means that the user is already
+ *	logged on and PAM has been used inside the screensaver. In that case, no need to 
+ *	do anything(?).
+ *
+ * - A session is not yet available. Store the password inside PAM data so
+ *	it can be retrieved during pam_open_session to set the credentials.
+ *
+ * @param ph PAM handle
+ * @param unused unused
+ * @param argc number of arguments for this PAM module
+ * @param argv array of arguments for this PAM module
+ * @return any of the PAM return values
+ */
+PAM_EXTERN int pam_sm_authenticate(pam_handle_t *ph, int unused, int argc, const char **argv)
+{
+	const char *hostdomain;
+	const char *user;
+	const char *password;
+	uint args;
+	int ret;
+
+	args = parse_args(ph, argc, argv, &hostdomain);
+	
+	/* Figure out and/or prompt for the user name */
+	ret = pam_get_user(ph, &user, NULL);
+	if (ret != PAM_SUCCESS || !user) {
+		pam_syslog(ph, LOG_ERR, "couldn't get the user name: %s",
+			   pam_strerror(ph, ret));
+		return PAM_SERVICE_ERR;
+	}
+
+	/* Lookup the password */
+	ret = pam_get_item(ph, PAM_AUTHTOK, (const void**)&password);
+	if (ret != PAM_SUCCESS || password == NULL) {
+		if (ret == PAM_SUCCESS) {
+			pam_syslog(ph, LOG_WARNING, "no password is available for user");
+		} else {
+			pam_syslog(ph, LOG_WARNING, "no password is available for user: %s",
+				   pam_strerror(ph, ret));
+		}
+		return PAM_SUCCESS;
+	}
+
+	/* set password as pam data and launch during open_session. */
+	if (pam_set_data(ph, "cifscreds_password", strdup(password), cleanup_free_password) != PAM_SUCCESS) {
+		pam_syslog(ph, LOG_ERR, "error storing password");
+		return PAM_AUTHTOK_RECOVER_ERR;
+	}
+
+	if ((args & ARG_DEBUG) == ARG_DEBUG) {
+		pam_syslog(ph, LOG_DEBUG, "password stored");
+	}
+
+	return PAM_SUCCESS;
+}
+
+/**
+ * PAM function called during opening the session.
+ *
+ * Retrieves the password stored during authentication from PAM data, then uses
+ * it set the cifs key.
+ *
+ * @param ph PAM handle
+ * @param flags currently unused, TODO: check for silent flag
+ * @param argc number of arguments for this PAM module
+ * @param argv array of arguments for this PAM module
+ * @return any of the PAM return values
+ */
+PAM_EXTERN int pam_sm_open_session(pam_handle_t *ph, int flags, int argc, const char **argv)
+{
+	const char *user = NULL;
+	const char *password = NULL;
+	const char *hostdomain = NULL;
+	uint args;
+	int retval;
+	key_serial_t	ses_key, uses_key;
+
+	args = parse_args(ph, argc, argv, &hostdomain);
+
+	/* Figure out the user name */
+	retval = pam_get_user(ph, &user, NULL);
+	if (retval != PAM_SUCCESS || !user) {
+		pam_syslog(ph, LOG_ERR, "couldn't get the user name: %s",
+			   pam_strerror(ph, retval));
+		return PAM_SERVICE_ERR;
+	}
+
+	/* retrieve the stored password */
+	if (pam_get_data(ph, "cifscreds_password", (const void**)&password) != PAM_SUCCESS) {
+		/*
+		 * No password, no worries, maybe this (PAM using) application
+		 * didn't do authentication, or is hopeless and wants to call
+		 * different PAM callbacks from different processes.
+		 *
+		 *
+		 */
+		password = NULL;
+		if ((args & ARG_DEBUG) == ARG_DEBUG) {
+			pam_syslog(ph, LOG_DEBUG, "no stored password found");
+		}
+		return PAM_SUCCESS;
+	}
+
+	/* make sure we have a host or domain name */
+	if (!hostdomain) {
+		pam_syslog(ph, LOG_ERR, "one of host= or domain= must be specified");
+		return PAM_SERVICE_ERR;
+	}
+
+	/* make sure there is a session keyring */
+	ses_key = keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 0);
+	if (ses_key == -1) {
+		if (errno == ENOKEY)
+			pam_syslog(ph, LOG_ERR, "you have no session keyring. "
+					"Consider using pam_keyinit to "
+					"install one.");
+		else
+			pam_syslog(ph, LOG_ERR, "unable to query session "
+					"keyring: %s", strerror(errno));
+	}
+
+	/* A problem querying the user-session keyring isn't fatal. */
+	uses_key = keyctl_get_keyring_ID(KEY_SPEC_USER_SESSION_KEYRING, 0);
+	if ((uses_key >= 0) && (ses_key == uses_key))
+		pam_syslog(ph, LOG_ERR, "you have no persistent session "
+				"keyring. cifscreds keys will not persist.");
+
+	return cifscreds_pam_add(ph, user, password, args, hostdomain);
+}
+
+/**
+ * This is called when the PAM session is closed.
+ *
+ * Currently it does nothing.  The session closing should remove the passwords
+ *
+ * @param ph PAM handle
+ * @param flags currently unused, TODO: check for silent flag
+ * @param argc number of arguments for this PAM module
+ * @param argv array of arguments for this PAM module
+ * @return PAM_SUCCESS
+ */
+PAM_EXTERN int pam_sm_close_session(pam_handle_t *ph, int flags, int argc, const char **argv)
+{
+	return PAM_SUCCESS;
+}
+
+/**
+ * This is called when pam_set_cred() is invoked.
+ *
+ * @param ph PAM handle
+ * @param flags currently unused, TODO: check for silent flag
+ * @param argc number of arguments for this PAM module
+ * @param argv array of arguments for this PAM module
+ * @return PAM_SUCCESS
+ */
+PAM_EXTERN int pam_sm_setcred(pam_handle_t *ph, int flags, int argc, const char **argv)
+{
+	return PAM_SUCCESS;
+}
+
+/**
+ * This is called when the user's password is changed
+ *
+ * @param ph PAM handle
+ * @param flags currently unused, TODO: check for silent flag
+ * @param argc number of arguments for this PAM module
+ * @param argv array of arguments for this PAM module
+ * @return PAM_SUCCESS
+ */
+PAM_EXTERN int
+pam_sm_chauthtok (pam_handle_t *ph, int flags, int argc, const char **argv)
+{
+	const char *hostdomain = NULL;
+	const char *user = NULL;
+	const char *password = NULL;
+	uint args;
+	int ret;
+	
+	args = parse_args(ph, argc, argv, &hostdomain);
+
+	if (flags & PAM_UPDATE_AUTHTOK) {
+		/* Figure out the user name */
+		ret = pam_get_user(ph, &user, NULL);
+		if (ret != PAM_SUCCESS) {
+			pam_syslog(ph, LOG_ERR, "couldn't get the user name: %s", 
+				pam_strerror (ph, ret));
+			return PAM_SERVICE_ERR;
+		}
+
+		ret = pam_get_item(ph, PAM_AUTHTOK, (const void**)&password);
+		if (ret != PAM_SUCCESS || password == NULL) {
+			if (ret == PAM_SUCCESS) {
+				pam_syslog(ph, LOG_WARNING, "no password is available for user");
+			} else {
+				pam_syslog(ph, LOG_WARNING, "no password is available for user: %s",
+					pam_strerror(ph, ret));
+			}
+			return PAM_AUTHTOK_RECOVER_ERR;
+		}
+	
+		return cifscreds_pam_update(ph, user, password, args, hostdomain);
+	}
+	else 
+		return PAM_IGNORE;
+}
-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux