diff options
Diffstat (limited to 'lib/libpam/modules/pam_ssh')
-rw-r--r-- | lib/libpam/modules/pam_ssh/Makefile | 14 | ||||
-rw-r--r-- | lib/libpam/modules/pam_ssh/pam_ssh.8 | 148 | ||||
-rw-r--r-- | lib/libpam/modules/pam_ssh/pam_ssh.c | 628 | ||||
-rw-r--r-- | lib/libpam/modules/pam_ssh/pam_ssh.h | 45 |
4 files changed, 835 insertions, 0 deletions
diff --git a/lib/libpam/modules/pam_ssh/Makefile b/lib/libpam/modules/pam_ssh/Makefile new file mode 100644 index 0000000..945bb4c --- /dev/null +++ b/lib/libpam/modules/pam_ssh/Makefile @@ -0,0 +1,14 @@ +# PAM module for SSH +# $FreeBSD$ + +SSHSRC= ${.CURDIR}/../../../../crypto/openssh + +LIB= pam_ssh +SHLIB_NAME= ${LIB}.so.${SHLIB_MAJOR} +SRCS= pam_ssh.c +CFLAGS+= -I${SSHSRC} +DPADD= ${LIBSSH} ${LIBCRYPTO} ${LIBCRYPT} ${LIBUTIL} +LDADD= -lssh -lcrypto -lcrypt -lutil +MAN= pam_ssh.8 + +.include <bsd.lib.mk> diff --git a/lib/libpam/modules/pam_ssh/pam_ssh.8 b/lib/libpam/modules/pam_ssh/pam_ssh.8 new file mode 100644 index 0000000..a932d02 --- /dev/null +++ b/lib/libpam/modules/pam_ssh/pam_ssh.8 @@ -0,0 +1,148 @@ +.\" Copyright (c) 2001 Mark R V Murray +.\" All rights reserved. +.\" Copyright (c) 2001 Networks Associates Technology, Inc. +.\" All rights reserved. +.\" +.\" This software was developed for the FreeBSD Project by ThinkSec AS and +.\" NAI Labs, the Security Research Division of Network Associates, Inc. +.\" under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the +.\" DARPA CHATS research program. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd November 26, 2001 +.Dt PAM_SSH 8 +.Os +.Sh NAME +.Nm pam_ssh +.Nd SSH PAM module +.Sh SYNOPSIS +.Op Ar service-name +.Ar module-type +.Ar control-flag +.Pa pam_ssh +.Op Ar options +.Sh DESCRIPTION +The +SSH +authentication service module for PAM, +.Nm +provides functionality for two PAM categories: +authentication +and session management. +In terms of the +.Ar module-type +parameter, they are the +.Dq Li auth +and +.Dq Li session +features. +It also provides null functions for the remaining categories. +.Ss SSH Authentication Module +The +SSH +authentication component +provides a function to verify the identity of a user +.Pq Fn pam_sm_authenticate , +by prompting the user for a passphrase and verifying that it can +decrypt the target user's SSH key using that passphrase. +.Pp +The following options may be passed to the authentication module: +.Bl -tag -width ".Cm use_first_pass" +.It Cm debug +.Xr syslog 3 +debugging information at +.Dv LOG_DEBUG +level. +.It Cm use_first_pass +If the authentication module +is not the first in the stack, +and a previous module +obtained the user's password, +that password is used +to authenticate the user. +If this fails, +the authentication module returns failure +without prompting the user for a password. +This option has no effect +if the authentication module +is the first in the stack, +or if no previous modules +obtained the user's password. +.It Cm try_first_pass +This option is similar to the +.Cm use_first_pass +option, +except that if the previously obtained password fails, +the user is prompted for another password. +.El +.Ss SSH Session Management Module +The +SSH +session management component +provides functions to initiate +.Pq Fn pam_sm_open_session +and terminate +.Pq Fn pam_sm_close_session +sessions. +The +.Fn pam_sm_open_session +function starts an SSH agent, +passing it any private keys it decrypted +during the authentication phase, +and sets the environment variables +the agent specifies. +The +.Fn pam_sm_close_session +function kills the previously started SSH agent +by sending it a +.Dv SIGTERM . +.Pp +The following options may be passed to the session management module: +.Bl -tag -width ".Cm use_first_pass" +.It Cm debug +.Xr syslog 3 +debugging information at +.Dv LOG_DEBUG +level. +.El +.Sh FILES +.Bl -tag -width ".Pa $HOME/.ssh2/id_dsa_*" -compact +.It Pa $HOME/.ssh/identity +SSH1/OpenSSH RSA key. +.It Pa $HOME/.ssh/id_dsa +OpenSSH DSA key. +.It Pa $HOME/.ssh2/id_rsa_* +SSH2 RSA keys. +.It Pa $HOME/.ssh2/id_dsa_* +SSH2 DSA keys. +.El +.Sh SEE ALSO +.Xr ssh-agent 1 , +.Xr syslog 3 , +.Xr pam.conf 5 , +.Xr pam 8 diff --git a/lib/libpam/modules/pam_ssh/pam_ssh.c b/lib/libpam/modules/pam_ssh/pam_ssh.c new file mode 100644 index 0000000..853bac7 --- /dev/null +++ b/lib/libpam/modules/pam_ssh/pam_ssh.c @@ -0,0 +1,628 @@ +/*- + * Copyright (c) 1999, 2000 Andrew J. Korty + * All rights reserved. + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Portions of this software were developed for the FreeBSD Project by + * ThinkSec AS and NAI Labs, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 + * ("CBOSS"), as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: pam_ssh.c,v 1.23 2001/08/20 01:44:02 akorty Exp $ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <fcntl.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define PAM_SM_AUTH +#define PAM_SM_ACCOUNT +#define PAM_SM_SESSION +#define PAM_SM_PASSWORD + +#include <security/pam_appl.h> +#include <security/pam_modules.h> +#include <security/pam_mod_misc.h> + +#include <openssl/dsa.h> +#include <openssl/evp.h> + +#include "key.h" +#include "authfd.h" +#include "authfile.h" +#include "log.h" +#include "pam_ssh.h" + +/* + * Generic cleanup function for OpenSSH "Key" type. + */ + +void +key_cleanup(pam_handle_t *pamh, void *data, int error_status) +{ + if (data) + key_free(data); +} + + +/* + * Generic PAM cleanup function for this module. + */ + +void +ssh_cleanup(pam_handle_t *pamh, void *data, int error_status) +{ + if (data) + free(data); +} + + +/* + * Authenticate a user's key by trying to decrypt it with the password + * provided. The key and its comment are then stored for later + * retrieval by the session phase. An increasing index is embedded in + * the PAM variable names so this function may be called multiple times + * for multiple keys. + */ + +static int +auth_via_key(pam_handle_t *pamh, const char *file, const char *dir, + const struct passwd *user, const char *pass) +{ + char *comment; /* private key comment */ + char *data_name; /* PAM state */ + static int index = 0; /* for saved keys */ + Key *key; /* user's key */ + char *path; /* to key files */ + int retval; /* from calls */ + uid_t saved_uid; /* caller's uid */ + + /* locate the user's private key file */ + + if (!asprintf(&path, "%s/%s", dir, file)) { + openpam_log(PAM_LOG_ERROR, "%s: %m", MODULE_NAME); + return PAM_SERVICE_ERR; + } + + saved_uid = getuid(); + + /* Try to decrypt the private key with the passphrase provided. If + success, the user is authenticated. */ + + comment = NULL; + (void) setreuid(user->pw_uid, saved_uid); + key = key_load_private(path, pass, &comment); + (void) setuid(saved_uid); + free(path); + if (!comment) + comment = strdup(file); + if (!key) { + free(comment); + return PAM_AUTH_ERR; + } + + /* save the key and comment to pass to ssh-agent in the session + phase */ + + if (!asprintf(&data_name, "ssh_private_key_%d", index)) { + openpam_log(PAM_LOG_ERROR, "%s: %m", MODULE_NAME); + free(comment); + return PAM_SERVICE_ERR; + } + retval = pam_set_data(pamh, data_name, key, key_cleanup); + free(data_name); + if (retval != PAM_SUCCESS) { + key_free(key); + free(comment); + return retval; + } + if (!asprintf(&data_name, "ssh_key_comment_%d", index)) { + openpam_log(PAM_LOG_ERROR, "%s: %m", MODULE_NAME); + free(comment); + return PAM_SERVICE_ERR; + } + retval = pam_set_data(pamh, data_name, comment, ssh_cleanup); + free(data_name); + if (retval != PAM_SUCCESS) { + free(comment); + return retval; + } + + ++index; + return PAM_SUCCESS; +} + + +/* + * Add the keys stored by auth_via_key() to the agent connected to the + * socket provided. + */ + +static int +add_keys(pam_handle_t *pamh, char *socket) +{ + AuthenticationConnection *ac; /* connection to ssh-agent */ + char *comment; /* private key comment */ + char *data_name; /* PAM state */ + int final; /* final return value */ + int index; /* for saved keys */ + Key *key; /* user's private key */ + int retval; /* from calls */ + + /* + * Connect to the agent. + * + * XXX Because ssh_get_authentication_connection() gets the + * XXX agent parameters from the environment, we have to + * XXX temporarily replace the environment with the PAM + * XXX environment list. This is a hack. + */ + { + extern char **environ; + char **saved, **evp; + + saved = environ; + if ((environ = pam_getenvlist(pamh)) == NULL) { + environ = saved; + openpam_log(PAM_LOG_ERROR, "%s: %m", MODULE_NAME); + return (PAM_BUF_ERR); + } + ac = ssh_get_authentication_connection(); + for (evp = environ; *evp; evp++) + free(*evp); + free(environ); + environ = saved; + } + if (!ac) { + openpam_log(PAM_LOG_ERROR, "%s: %s: %m", MODULE_NAME, socket); + return PAM_SESSION_ERR; + } + + /* hand off each private key to the agent */ + + final = 0; + for (index = 0; ; index++) { + if (!asprintf(&data_name, "ssh_private_key_%d", index)) { + openpam_log(PAM_LOG_ERROR, "%s: %m", MODULE_NAME); + ssh_close_authentication_connection(ac); + return PAM_SERVICE_ERR; + } + retval = pam_get_data(pamh, data_name, (const void **)&key); + free(data_name); + if (retval != PAM_SUCCESS) + break; + if (!asprintf(&data_name, "ssh_key_comment_%d", index)) { + openpam_log(PAM_LOG_ERROR, "%s: %m", MODULE_NAME); + ssh_close_authentication_connection(ac); + return PAM_SERVICE_ERR; + } + retval = pam_get_data(pamh, data_name, + (const void **)&comment); + free(data_name); + if (retval != PAM_SUCCESS) + break; + retval = ssh_add_identity(ac, key, comment); + if (!final) + final = retval; + } + ssh_close_authentication_connection(ac); + + return final ? PAM_SUCCESS : PAM_SESSION_ERR; +} + + +PAM_EXTERN int +pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + int authenticated; /* user authenticated? */ + char *dotdir; /* .ssh dir name */ + char *file; /* current key file */ + char *keyfiles; /* list of key files to add */ + int options; /* options for pam_get_pass() */ + const char *pass; /* passphrase */ + const struct passwd *pwent; /* user's passwd entry */ + struct passwd *pwent_keep; /* our own copy */ + int retval; /* from calls */ + const char *user; /* username */ + + keyfiles = DEF_KEYFILES; + options = 0; + for (; argc; argc--, argv++) + if (strncmp(*argv, OPT_KEYFILES "=", sizeof OPT_KEYFILES) + == 0) { + if (!(keyfiles = strchr(*argv, '=') + 1)) + return PAM_AUTH_ERR; + } else if (strcmp(*argv, OPT_TRY_FIRST_PASS) == 0) + options |= PAM_OPT_TRY_FIRST_PASS; + else if (strcmp(*argv, OPT_USE_FIRST_PASS) == 0) + options |= PAM_OPT_USE_FIRST_PASS; + + + if ((retval = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) + return retval; + if (!((pwent = getpwnam(user)) && pwent->pw_dir)) + return PAM_AUTH_ERR; + + /* pass prompt message to application and receive passphrase */ + + if ((retval = pam_get_authtok(pamh, &pass, NEED_PASSPHRASE)) + != PAM_SUCCESS) + return retval; + + OpenSSL_add_all_algorithms(); /* required for DSA */ + + /* any key will authenticate us, but if we can decrypt all of the + specified keys, we'll do so here so we can cache them in the + session phase */ + + if (!asprintf(&dotdir, "%s/%s", pwent->pw_dir, SSH_CLIENT_DIR)) { + openpam_log(PAM_LOG_ERROR, "%s: %m", MODULE_NAME); + return PAM_SERVICE_ERR; + } + authenticated = 0; + keyfiles = strdup(keyfiles); + for (file = strtok(keyfiles, SEP_KEYFILES); file; + file = strtok(NULL, SEP_KEYFILES)) + if (auth_via_key(pamh, file, dotdir, pwent, pass) == + PAM_SUCCESS) + authenticated++; + free(dotdir); + free(keyfiles); + if (!authenticated) + return PAM_AUTH_ERR; + + /* copy the passwd entry (in case successive calls are made) and + save it for the session phase */ + + if (!(pwent_keep = malloc(sizeof *pwent))) { + openpam_log(PAM_LOG_ERROR, "%m"); + return PAM_SERVICE_ERR; + } + (void) memcpy(pwent_keep, pwent, sizeof *pwent_keep); + if ((retval = pam_set_data(pamh, "ssh_passwd_entry", pwent_keep, + ssh_cleanup)) != PAM_SUCCESS) { + free(pwent_keep); + return retval; + } + + return PAM_SUCCESS; +} + + +PAM_EXTERN int +pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + return PAM_SUCCESS; +} + + +PAM_EXTERN int +pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + char *agent_socket; /* agent socket */ + char *env_end; /* end of env */ + FILE *env_read; /* env data source */ + char env_string[BUFSIZ]; /* environment string */ + char *env_value; /* envariable value */ + int env_write; /* env file descriptor */ + char hname[MAXHOSTNAMELEN]; /* local hostname */ + int no_link; /* link per-agent file? */ + char *per_agent; /* to store env */ + char *per_session; /* per-session filename */ + char *agent_pid; /* agent pid */ + const struct passwd *pwent; /* user's passwd entry */ + int retval; /* from calls */ + uid_t saved_uid; /* caller's uid */ + int start_agent; /* start agent? */ + const char *tty; /* tty or display name */ + + /* dump output of ssh-agent in ~/.ssh */ + if ((retval = pam_get_data(pamh, "ssh_passwd_entry", + (const void **)&pwent)) != PAM_SUCCESS) + return retval; + + /* + * Use reference counts to limit agents to one per user per host. + * + * Technique: Create an environment file containing + * information about the agent. Only one file is created, but + * it may be given many names. One name is given for the + * agent itself, agent-<host>. Another name is given for each + * session, agent-<host>-<display> or agent-<host>-<tty>. We + * delete the per-session filename on session close, and when + * the link count goes to unity on the per-agent file, we + * delete the file and kill the agent. + */ + + /* the per-agent file contains just the hostname */ + + (void) gethostname(hname, sizeof hname); + if (asprintf(&per_agent, "%s/.ssh/agent-%s", pwent->pw_dir, hname) + == -1) { + openpam_log(PAM_LOG_ERROR, "%s: %m", MODULE_NAME); + return PAM_SERVICE_ERR; + } + + /* save the per-agent filename in case we want to delete it on + session close */ + + if ((retval = pam_set_data(pamh, "ssh_agent_env_agent", per_agent, + ssh_cleanup)) != PAM_SUCCESS) { + free(per_agent); + return retval; + } + + /* take on the user's privileges for writing files and starting the + agent */ + + saved_uid = geteuid(); + (void) seteuid(pwent->pw_uid); + + /* Try to create the per-agent file or open it for reading if it + exists. If we can't do either, we won't try to link a + per-session filename later. Start the agent if we can't open + the file for reading. */ + + env_write = no_link = 0; + env_read = NULL; + if ((env_write = open(per_agent, O_CREAT | O_EXCL | O_WRONLY, + S_IRUSR)) < 0 && !(env_read = fopen(per_agent, "r"))) + no_link = 1; + if (env_read) { + start_agent = 0; + (void) seteuid(saved_uid); + } else { + start_agent = 1; + env_read = popen(SSH_AGENT, "r"); + (void) seteuid(saved_uid); + if (!env_read) { + openpam_log(PAM_LOG_ERROR, "%s: %s: %m", MODULE_NAME, + SSH_AGENT); + if (env_write >= 0) + (void) close(env_write); + return PAM_SESSION_ERR; + } + } + + /* save environment for application with pam_putenv() */ + + agent_socket = NULL; + while (fgets(env_string, sizeof env_string, env_read)) { + + /* parse environment definitions */ + + if (env_write >= 0) + (void) write(env_write, env_string, + strlen(env_string)); + if (!(env_value = strchr(env_string, '=')) || + !(env_end = strchr(env_value, ';'))) + continue; + *env_end = '\0'; + + /* pass to the application */ + + if (!((retval = pam_putenv(pamh, env_string)) == + PAM_SUCCESS)) { + if (start_agent) + (void) pclose(env_read); + else + (void) fclose(env_read); + if (env_write >= 0) + (void) close(env_write); + if (agent_socket) + free(agent_socket); + return PAM_SERVICE_ERR; + } + + *env_value++ = '\0'; + + /* save the agent socket so we can connect to it and add + the keys as well as the PID so we can kill the agent on + session close. */ + + if (strcmp(&env_string[strlen(env_string) - + strlen(ENV_SOCKET_SUFFIX)], ENV_SOCKET_SUFFIX) == 0 && + !(agent_socket = strdup(env_value))) { + openpam_log(PAM_LOG_ERROR, "%s: %m", MODULE_NAME); + if (start_agent) + (void) pclose(env_read); + else + (void) fclose(env_read); + if (env_write >= 0) + (void) close(env_write); + if (agent_socket) + free(agent_socket); + return PAM_SERVICE_ERR; + } else if (strcmp(&env_string[strlen(env_string) - + strlen(ENV_PID_SUFFIX)], ENV_PID_SUFFIX) == 0 && + ((agent_pid = strdup(env_value)) == NULL || + (retval = pam_set_data(pamh, "ssh_agent_pid", + agent_pid, ssh_cleanup)) != PAM_SUCCESS)) { + if (start_agent) + (void) pclose(env_read); + else + (void) fclose(env_read); + if (env_write >= 0) + (void) close(env_write); + if (agent_socket) + free(agent_socket); + if (agent_pid) + free(agent_pid); + return retval; + } + + } + if (env_write >= 0) + (void) close(env_write); + + if (start_agent) { + switch (retval = pclose(env_read)) { + case -1: + openpam_log(PAM_LOG_ERROR, "%s: %s: %m", MODULE_NAME, + SSH_AGENT); + if (agent_socket) + free(agent_socket); + return PAM_SESSION_ERR; + case 0: + break; + case 127: + openpam_log(PAM_LOG_ERROR, "%s: cannot execute %s", + MODULE_NAME, SSH_AGENT); + if (agent_socket) + free(agent_socket); + return PAM_SESSION_ERR; + default: + openpam_log(PAM_LOG_ERROR, "%s: %s exited %s %d", + MODULE_NAME, + SSH_AGENT, WIFSIGNALED(retval) ? "on signal" : + "with status", WIFSIGNALED(retval) ? + WTERMSIG(retval) : WEXITSTATUS(retval)); + if (agent_socket) + free(agent_socket); + return PAM_SESSION_ERR; + } + } else + (void) fclose(env_read); + + if (!agent_socket) + return PAM_SESSION_ERR; + + if (start_agent && (retval = add_keys(pamh, agent_socket)) + != PAM_SUCCESS) + return retval; + free(agent_socket); + + /* if we couldn't access the per-agent file, don't link a + per-session filename to it */ + + if (no_link) + return PAM_SUCCESS; + + /* the per-session file contains the display name or tty name as + well as the hostname */ + + if ((retval = pam_get_item(pamh, PAM_TTY, (const void **)&tty)) + != PAM_SUCCESS) + return retval; + if (asprintf(&per_session, "%s/.ssh/agent-%s-%s", pwent->pw_dir, + hname, tty) == -1) { + openpam_log(PAM_LOG_ERROR, "%s: %m", MODULE_NAME); + return PAM_SERVICE_ERR; + } + + /* save the per-session filename so we can delete it on session + close */ + + if ((retval = pam_set_data(pamh, "ssh_agent_env_session", + per_session, ssh_cleanup)) != PAM_SUCCESS) { + free(per_session); + return retval; + } + + (void) unlink(per_session); /* remove cruft */ + (void) link(per_agent, per_session); + + return PAM_SUCCESS; +} + + +PAM_EXTERN int +pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + const char *env_file; /* ssh-agent environment */ + pid_t pid; /* ssh-agent process id */ + int retval; /* from calls */ + const char *ssh_agent_pid; /* ssh-agent pid string */ + struct stat sb; /* to check st_nlink */ + + if ((retval = pam_get_data(pamh, "ssh_agent_env_session", + (const void **)&env_file)) == PAM_SUCCESS && env_file) + (void) unlink(env_file); + + /* Retrieve per-agent filename and check link count. If it's + greater than unity, other sessions are still using this + agent. */ + + if ((retval = pam_get_data(pamh, "ssh_agent_env_agent", + (const void **)&env_file)) == PAM_SUCCESS && env_file && + stat(env_file, &sb) == 0) { + if (sb.st_nlink > 1) + return PAM_SUCCESS; + (void) unlink(env_file); + } + + /* retrieve the agent's process id */ + + if ((retval = pam_get_data(pamh, "ssh_agent_pid", + (const void **)&ssh_agent_pid)) != PAM_SUCCESS) + return retval; + + /* Kill the agent. SSH's ssh-agent does not have a -k option, so + just call kill(). */ + + pid = atoi(ssh_agent_pid); + if (ssh_agent_pid <= 0) + return PAM_SESSION_ERR; + if (kill(pid, SIGTERM) != 0) { + openpam_log(PAM_LOG_ERROR, "%s: %s: %m", MODULE_NAME, + ssh_agent_pid); + return PAM_SESSION_ERR; + } + + return PAM_SUCCESS; +} + +PAM_EXTERN int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return (PAM_IGNORE); +} + +PAM_EXTERN int +pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return (PAM_IGNORE); +} + +PAM_MODULE_ENTRY(MODULE_NAME); diff --git a/lib/libpam/modules/pam_ssh/pam_ssh.h b/lib/libpam/modules/pam_ssh/pam_ssh.h new file mode 100644 index 0000000..19a99ec --- /dev/null +++ b/lib/libpam/modules/pam_ssh/pam_ssh.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 1999, 2000 Andrew J. Korty + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#define SSH_CLIENT_DIR ".ssh" + +#define MODULE_NAME "pam_ssh" +#define NEED_PASSPHRASE "SSH passphrase: " +#define SSH_AGENT "ssh-agent" + +#define ENV_PID_SUFFIX "_AGENT_PID" +#define ENV_SOCKET_SUFFIX "_AUTH_SOCK" + +#define DEF_KEYFILES "id_dsa,id_rsa,identity" + +#define OPT_KEYFILES "keyfiles" +#define OPT_TRY_FIRST_PASS "try_first_pass" +#define OPT_USE_FIRST_PASS "use_first_pass" + +#define SEP_KEYFILES "," |