On Tue, 10 Dec 2013 13:57:43 -0700 Orion Poplawski <orion@xxxxxxxx> wrote: > 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; > +} Merged this patch, the manpage patch, and the patch I did to clean up the build warnings and whitespace. Let me know if anything is amiss and thanks for the contribution! Cheers, -- Jeff Layton <jlayton@xxxxxxxxxx> -- 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