From cbbcd6e71c0a58e79236670463b9eb3f00347021 Mon Sep 17 00:00:00 2001 From: Orion Poplawski Date: Wed, 13 Nov 2013 13:53:30 -0700 Subject: [cifs-utils PATCH] cifscreds: create PAM module to insert credentials at login Split out some of the cifscreds key handling routines into a separate file, and then link that in to both cifscreds and the new PAM module. Fix up autoconf to handle building this automatically. Signed-off-by: Orion Poplawski --- 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 #include #include +#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@samba.org) + * Copyright (C) 2010 Igor Druzhinin (jaxbrigs@gmail.com) + * + * 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 . + */ + +#include +#include +#include +#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@samba.org) + * Copyright (C) 2010 Igor Druzhinin (jaxbrigs@gmail.com) + * + * 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 . + */ + +#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 + * + * 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 +#include +#include +#include +#include +#include +#include +/* +#include +#include +#include +*/ + +#include + +#include +#include +#include + +#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.4.2