143499a82c
- fix link of libwbclient - add pam_cifscreds module and manpage Signed-off-by: Jeff Layton <jlayton@redhat.com>
863 lines
24 KiB
Diff
863 lines
24 KiB
Diff
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
|
|
--- 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@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
|
|
--- /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 <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@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
|
|
|