| From cbbcd6e71c0a58e79236670463b9eb3f00347021 Mon Sep 17 00:00:00 2001 |
| From: Orion Poplawski <orion@nwra.com> |
| 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 <orion@nwra.com> |
| |
| 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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 <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 |
| |
| |
| @@ -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 <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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -0,0 +1,550 @@ |
| +/* |
| + * Copyright (C) 2013 Orion Poplawski <orion@cora.nwra.com> |
| + * |
| + * 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.4.2 |
| |