diff options
author | cvs2svn <cvs2svn@FreeBSD.org> | 2002-03-17 15:01:29 +0000 |
---|---|---|
committer | cvs2svn <cvs2svn@FreeBSD.org> | 2002-03-17 15:01:29 +0000 |
commit | 9167caac56eb0dbcb6ce8c80c8ed30bd4b5fcbd5 (patch) | |
tree | a2c19973567893c19ef411550411799612c77ae4 /lib | |
parent | 236c08a5bcad524da4829074abf67755937e68ec (diff) | |
download | src-9167caac56eb0dbcb6ce8c80c8ed30bd4b5fcbd5.tar.gz src-9167caac56eb0dbcb6ce8c80c8ed30bd4b5fcbd5.zip |
This commit was manufactured by cvs2svn to create branch 'RELENG_4'.
Notes
Notes:
svn path=/stable/4/; revision=92504
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libc/gen/readpassphrase.3 | 187 | ||||
-rw-r--r-- | lib/libpam/modules/pam_opie/pam_opie.8 | 123 | ||||
-rw-r--r-- | lib/libpam/modules/pam_ssh/pam_ssh.8 | 148 | ||||
-rw-r--r-- | lib/libpam/modules/pam_ssh/pam_ssh.c | 595 |
4 files changed, 1053 insertions, 0 deletions
diff --git a/lib/libc/gen/readpassphrase.3 b/lib/libc/gen/readpassphrase.3 new file mode 100644 index 000000000000..cfa6cf0631db --- /dev/null +++ b/lib/libc/gen/readpassphrase.3 @@ -0,0 +1,187 @@ +.\" $OpenBSD: readpassphrase.3,v 1.7 2001/12/15 15:37:51 millert Exp $ +.\" +.\" Copyright (c) 2000 Todd C. Miller <Todd.Miller@courtesan.com> +.\" 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. +.\" 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 ``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 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 December 7, 2001 +.Dt READPASSPHRASE 3 +.Os +.Sh NAME +.Nm readpassphrase +.Nd get a passphrase from the user +.Sh SYNOPSIS +.In readpassphrase.h +.Ft "char *" +.Fn readpassphrase "const char *prompt" "char *buf" "size_t bufsiz" "int flags" +.Sh DESCRIPTION +The +.Fn readpassphrase +function displays a prompt to, and reads in a passphrase from, +.Pa /dev/tty . +If this file is inaccessible +and the +.Dv RPP_REQUIRE_TTY +flag is not set, +.Fn readpassphrase +displays the prompt on the standard error output and reads from the standard +input. +In this case it is generally not possible to turn off echo. +.Pp +Up to +.Fa bufsiz +\- 1 characters (one is for the +.Dv NUL ) +are read into the provided buffer +.Fa buf . +Any additional +characters and the terminating newline (or return) character are discarded. +.Pp +.Fn readpassphrase +takes the following optional +.Fa flags : +.Pp +.Bl -tag -width ".Dv RPP_REQUIRE_TTY" -compact +.It Dv RPP_ECHO_OFF +turn off echo (default behavior) +.It Dv RPP_ECHO_ON +leave echo on +.It Dv RPP_REQUIRE_TTY +fail if there is no tty +.It Dv RPP_FORCELOWER +force input to lower case +.It Dv RPP_FORCEUPPER +force input to upper case +.It Dv RPP_SEVENBIT +strip the high bit from input +.El +.Pp +The calling process should zero the passphrase as soon as possible to +avoid leaving the cleartext passphrase visible in the process's address +space. +.Sh RETURN VALUES +Upon successful completion, +.Fn readpassphrase +returns a pointer to the null-terminated passphrase. +If an error is encountered, the terminal state is restored and +a +.Dv NULL +pointer is returned. +.Sh ERRORS +.Bl -tag -width Er +.It Bq Er EINTR +The +.Fn readpassphrase +function was interrupted by a signal. +.It Bq Er EINVAL +The +.Fa bufsiz +argument was zero. +.It Bq Er EIO +The process is a member of a background process attempting to read +from its controlling terminal, the process is ignoring or blocking +the +.Dv SIGTTIN +signal or the process group is orphaned. +.It Bq Er EMFILE +The process has already reached its limit for open file descriptors. +.It Bq Er ENFILE +The system file table is full. +.It Bq Er ENOTTY +There is no controlling terminal and the +.Dv RPP_REQUIRE_TTY +flag was specified. +.El +.Sh EXAMPLES +The following code fragment will read a passphrase from +.Pa /dev/tty +into the buffer +.Fa passbuf . +.Bd -literal -offset indent +char passbuf[1024]; + +\&... + +if (readpassphrase("Response: ", passbuf, sizeof(passbuf), + RPP_REQUIRE_TTY) == NULL) + errx(1, "unable to read passphrase"); + +if (compare(transform(passbuf), epass) != 0) + errx(1, "bad passphrase"); + +\&... + +memset(passbuf, 0, sizeof(passbuf)); +.Ed +.Sh SIGNALS +.Fn readpassphrase +will catch the following signals: +.Pp +.Bl -tag -compact +.It Dv SIGINT +.It Dv SIGHUP +.It Dv SIGQUIT +.It Dv SIGTERM +.It Dv SIGTSTP +.It Dv SIGTTIN +.It Dv SIGTTOU +.El +.Pp +When one of the above signals is intercepted, terminal echo will +be restored if it had previously been turned off. +If a signal handler was installed for the signal when +.Fn readpassphrase +was called that handler is then executed. +If no handler was previously installed for the signal then the +default action is taken as per +.Xr sigaction 2 . +.Pp +The +.Dv SIGTSTP , SIGTTIN , +and +.Dv SIGTTOU +signals (stop signal generated from keyboard or due to terminal I/O +from a background proccess) are treated specially. +When the process is resumed after it has been stopped, +.Fn readpassphrase +will reprint the prompt and the user may then enter a passphrase. +.Sh FILES +.Bl -tag -width ".Pa /dev/tty" -compact +.It Pa /dev/tty +.El +.Sh SEE ALSO +.Xr sigaction 2 , +.Xr getpass 3 +.Sh STANDARDS +The +.Fn readpassphrase +function is an +extension and should not be used if portability is desired. +.Sh HISTORY +The +.Fn readpassphrase +function first appeared in +.Ox 2.9 . diff --git a/lib/libpam/modules/pam_opie/pam_opie.8 b/lib/libpam/modules/pam_opie/pam_opie.8 new file mode 100644 index 000000000000..bae696d1b16e --- /dev/null +++ b/lib/libpam/modules/pam_opie/pam_opie.8 @@ -0,0 +1,123 @@ +.\" Copyright (c) 2001 Mark R V Murray +.\" All rights reserved. +.\" Copyright (c) 2002 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. +.\" +.\" $FreeBSD$ +.\" +.Dd July 7, 2001 +.Dt PAM_OPIE 8 +.Os +.Sh NAME +.Nm pam_opie +.Nd OPIE PAM module +.Sh SYNOPSIS +.Op Ar service-name +.Ar module-type +.Ar control-flag +.Pa pam_opie +.Op Ar options +.Sh DESCRIPTION +The OPIE authentication service module for PAM, +.Nm +provides functionality for only one PAM category: +that of authentication. +In terms of the +.Ar module-type +parameter, this is the +.Dq Li auth +feature. +It also provides a null function for session management. +.Pp +Note that this module does not enforce +.Xr opieaccess 5 +checks. +There is a separate module, +.Xr pam_opieaccess 8 , +for this purpose. +.Ss OPIE Authentication Module +The OPIE authentication component +provides functions to verify the identity of a user +.Pq Fn pam_sm_authenticate , +which obtains the relevant +.Xr opie 4 +credentials. +It provides the user with an OPIE challenge, +and verifies that this is correct with +.Xr opiechallenge 3 . +.Pp +The following options may be passed to the authentication module: +.Bl -tag -width ".Cm auth_as_self" +.It Cm debug +.Xr syslog 3 +debugging information at +.Dv LOG_DEBUG +level. +.It Cm auth_as_self +This option will require the user +to authenticate themself as the user +given by +.Xr getlogin 2 , +not as the account they are attempting to access. +This is primarily for services like +.Xr su 1 , +where the user's ability to retype +their own password +might be deemed sufficient. +.It Cm no_fake_prompts +Do not generate fake challenges for users who do not have an OPIE key. +Note that this can leak information to a hypothetical attacker about +who uses OPIE and who does not, but it can be useful on systems where +some users want to use OPIE but most do not. +.El +.Pp +Note that +.Nm +ignores the standard options +.Cm try_first_pass +and +.Cm use_first_pass , +since a challenge must be generated before the user can submit a valid +response. +.Sh FILES +.Bl -tag -width ".Pa /etc/opiekeys" -compact +.It Pa /etc/opiekeys +default OPIE password database. +.El +.Sh SEE ALSO +.Xr passwd 1 , +.Xr getlogin 2 , +.Xr opiechallenge 3 , +.Xr syslog 3 , +.Xr opie 4 , +.Xr pam.conf 5 , +.Xr pam 8 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 000000000000..a932d0255671 --- /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 000000000000..7285c79aa3db --- /dev/null +++ b/lib/libpam/modules/pam_ssh/pam_ssh.c @@ -0,0 +1,595 @@ +/*- + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <dirent.h> +#include <pwd.h> +#include <signal.h> +#include <ssh.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.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" + +static int auth_via_key(pam_handle_t *, int, const char *, const char *, const struct passwd *, const char *); +static void key_cleanup(pam_handle_t *, void *, int); +static void ssh_cleanup(pam_handle_t *, void *, int); + +/* + * Generic cleanup function for SSH "Key" type. + */ + +static void +key_cleanup(pam_handle_t *pamh __unused, void *data, int error_status __unused) +{ + if (data) + key_free(data); +} + + +/* + * Generic PAM cleanup function for this module. + */ + +static void +ssh_cleanup(pam_handle_t *pamh __unused, void *data, int error_status __unused) +{ + 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, int type, 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 indx = 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)) { + syslog(LOG_CRIT, "%s: %m", MODULE_NAME); + return PAM_SERVICE_ERR; + } + saved_uid = geteuid(); + /* + * Try to decrypt the private key with the passphrase provided. + * If success, the user is authenticated. + */ + seteuid(user->pw_uid); + key = key_load_private_type(type, path, pass, &comment); + free(path); + seteuid(saved_uid); + if (key == NULL) + 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", indx)) { + syslog(LOG_CRIT, "%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", indx)) { + syslog(LOG_CRIT, "%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; + } + ++indx; + return PAM_SUCCESS; +} + + +PAM_EXTERN int +pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, int argc, const char **argv) +{ + struct options options; /* module options */ + int authenticated; /* user authenticated? */ + char *dotdir; /* .ssh2 dir name */ + struct dirent *dotdir_ent; /* .ssh2 dir entry */ + DIR *dotdir_p; /* .ssh2 dir pointer */ + const char *pass; /* passphrase */ + struct passwd *pwd; /* user's passwd entry */ + struct passwd *pwd_keep; /* our own copy */ + int retval; /* from calls */ + int pam_auth_dsa; /* Authorised via DSA */ + int pam_auth_rsa; /* Authorised via RSA */ + const char *user; /* username */ + + pam_std_option(&options, NULL, argc, argv); + + PAM_LOG("Options processed"); + + retval = pam_get_user(pamh, &user, NULL); + if (retval != PAM_SUCCESS) + PAM_RETURN(retval); + pwd = getpwnam(user); + if (pwd == NULL || pwd->pw_dir == NULL) + /* delay? */ + PAM_RETURN(PAM_AUTH_ERR); + + PAM_LOG("Got user: %s", user); + + /* + * Pass prompt message to application and receive + * passphrase. + */ + retval = pam_get_authtok(pamh, &pass, NEED_PASSPHRASE); + if (retval != PAM_SUCCESS) + PAM_RETURN(retval); + OpenSSL_add_all_algorithms(); /* required for DSA */ + + PAM_LOG("Got passphrase"); + + /* + * Either the DSA or the RSA key will authenticate us, but if + * we can decrypt both, we'll do so here so we can cache them in + * the session phase. + */ + if (!asprintf(&dotdir, "%s/%s", pwd->pw_dir, SSH_CLIENT_DIR)) { + syslog(LOG_CRIT, "%s: %m", MODULE_NAME); + PAM_RETURN(PAM_SERVICE_ERR); + } + pam_auth_dsa = auth_via_key(pamh, KEY_DSA, SSH_CLIENT_ID_DSA, dotdir, + pwd, pass); + pam_auth_rsa = auth_via_key(pamh, KEY_RSA1, SSH_CLIENT_IDENTITY, dotdir, + pwd, pass); + authenticated = 0; + if (pam_auth_dsa == PAM_SUCCESS) + authenticated++; + if (pam_auth_rsa == PAM_SUCCESS) + authenticated++; + + PAM_LOG("Done pre-authenticating; got %d", authenticated); + + /* + * Compatibility with SSH2 from SSH Communications Security. + */ + if (!asprintf(&dotdir, "%s/%s", pwd->pw_dir, SSH2_CLIENT_DIR)) { + syslog(LOG_CRIT, "%s: %m", MODULE_NAME); + PAM_RETURN(PAM_SERVICE_ERR); + } + /* + * Try to load anything that looks like a private key. For + * now, we only support DSA and RSA keys. + */ + dotdir_p = opendir(dotdir); + while (dotdir_p && (dotdir_ent = readdir(dotdir_p))) { + /* skip public keys */ + if (strcmp(&dotdir_ent->d_name[dotdir_ent->d_namlen - + strlen(SSH2_PUB_SUFFIX)], SSH2_PUB_SUFFIX) == 0) + continue; + /* DSA keys */ + if (strncmp(dotdir_ent->d_name, SSH2_DSA_PREFIX, + strlen(SSH2_DSA_PREFIX)) == 0) + retval = auth_via_key(pamh, KEY_DSA, + dotdir_ent->d_name, dotdir, pwd, pass); + /* RSA keys */ + else if (strncmp(dotdir_ent->d_name, SSH2_RSA_PREFIX, + strlen(SSH2_RSA_PREFIX)) == 0) + retval = auth_via_key(pamh, KEY_RSA, + dotdir_ent->d_name, dotdir, pwd, pass); + /* skip other files */ + else + continue; + authenticated += (retval == PAM_SUCCESS); + } + if (!authenticated) { + PAM_VERBOSE_ERROR("SSH authentication refused"); + PAM_RETURN(PAM_AUTH_ERR); + } + + PAM_LOG("Done authenticating; got %d", authenticated); + + /* + * Copy the passwd entry (in case successive calls are made) + * and save it for the session phase. + */ + pwd_keep = malloc(sizeof *pwd); + if (pwd_keep == NULL) { + syslog(LOG_CRIT, "%m"); + PAM_RETURN(PAM_SERVICE_ERR); + } + memcpy(pwd_keep, pwd, sizeof *pwd_keep); + retval = pam_set_data(pamh, "ssh_passwd_entry", pwd_keep, ssh_cleanup); + if (retval != PAM_SUCCESS) { + free(pwd_keep); + PAM_RETURN(retval); + } + + PAM_LOG("Saved ssh_passwd_entry"); + + PAM_RETURN(PAM_SUCCESS); +} + + +PAM_EXTERN int +pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv) +{ + struct options options; /* module options */ + + pam_std_option(&options, NULL, argc, argv); + + PAM_LOG("Options processed"); + + PAM_RETURN(PAM_SUCCESS); +} + +PAM_EXTERN int +pam_sm_acct_mgmt(pam_handle_t *pamh __unused, int flags __unused, int argc ,const char **argv) +{ + struct options options; + + pam_std_option(&options, NULL, argc, argv); + + PAM_LOG("Options processed"); + + PAM_RETURN(PAM_IGNORE); +} + +PAM_EXTERN int +pam_sm_chauthtok(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv) +{ + struct options options; + + pam_std_option(&options, NULL, argc, argv); + + PAM_LOG("Options processed"); + + PAM_RETURN(PAM_IGNORE); +} + +typedef AuthenticationConnection AC; + +PAM_EXTERN int +pam_sm_open_session(pam_handle_t *pamh, int flags __unused, int argc, const char **argv) +{ + struct options options; /* module options */ + AC *ac; /* to ssh-agent */ + char *agent_socket; /* agent socket */ + char *comment; /* on private key */ + char *env_end; /* end of env */ + char *env_file; /* to store env */ + FILE *env_fp; /* env_file handle */ + char *env_value; /* envariable value */ + char *data_name; /* PAM state */ + int final; /* final return value */ + int indx; /* for saved keys */ + Key *key; /* user's private key */ + FILE *lpipe; /* ssh-agent handle */ + struct passwd *pwd; /* user's passwd entry */ + int retval; /* from calls */ + uid_t saved_uid; /* caller's uid */ + const char *tty; /* tty or display name */ + char hname[MAXHOSTNAMELEN]; /* local hostname */ + char env_string[BUFSIZ]; /* environment string */ + + pam_std_option(&options, NULL, argc, argv); + + PAM_LOG("Options processed"); + + /* dump output of ssh-agent in ~/.ssh */ + retval = pam_get_data(pamh, "ssh_passwd_entry", (const void **)&pwd); + if (retval != PAM_SUCCESS) + PAM_RETURN(retval); + + PAM_LOG("Got ssh_passwd_entry"); + + /* use the tty or X display name in the filename */ + retval = pam_get_item(pamh, PAM_TTY, (const void **)&tty); + if (retval != PAM_SUCCESS) + PAM_RETURN(retval); + + PAM_LOG("Got TTY"); + + if (gethostname(hname, sizeof hname) == 0) { + if (asprintf(&env_file, "%s/.ssh/agent-%s%s%s", + pwd->pw_dir, hname, *tty == ':' ? "" : ":", tty) + == -1) { + syslog(LOG_CRIT, "%s: %m", MODULE_NAME); + PAM_RETURN(PAM_SERVICE_ERR); + } + } + else if (asprintf(&env_file, "%s/.ssh/agent-%s", pwd->pw_dir, + tty) == -1) { + syslog(LOG_CRIT, "%s: %m", MODULE_NAME); + PAM_RETURN(PAM_SERVICE_ERR); + } + + PAM_LOG("Got env_file: %s", env_file); + + /* save the filename so we can delete the file on session close */ + retval = pam_set_data(pamh, "ssh_agent_env", env_file, ssh_cleanup); + if (retval != PAM_SUCCESS) { + free(env_file); + PAM_RETURN(retval); + } + + PAM_LOG("Saved env_file"); + + /* start the agent as the user */ + saved_uid = geteuid(); + seteuid(pwd->pw_uid); + env_fp = fopen(env_file, "w"); + if (env_fp != NULL) + chmod(env_file, S_IRUSR); + lpipe = popen(SSH_AGENT, "r"); + seteuid(saved_uid); + if (!lpipe) { + syslog(LOG_ERR, "%s: %s: %m", MODULE_NAME, SSH_AGENT); + if (env_fp) + fclose(env_fp); + PAM_RETURN(PAM_SESSION_ERR); + } + + PAM_LOG("Agent started as user"); + + /* + * Save environment for application with pam_putenv(). + */ + agent_socket = NULL; + while (fgets(env_string, sizeof env_string, lpipe)) { + if (env_fp) + fputs(env_string, env_fp); + env_value = strchr(env_string, '='); + if (env_value == NULL) + continue; + env_end = strchr(env_value, ';'); + if (env_end == NULL) + continue; + *env_end = '\0'; + /* pass to the application ... */ + retval = pam_putenv(pamh, env_string); + if (retval != PAM_SUCCESS) { + pclose(lpipe); + if (env_fp) + fclose(env_fp); + PAM_RETURN(PAM_SERVICE_ERR); + } + + PAM_LOG("Put to environment: %s", env_string); + + *env_value++ = '\0'; + if (strcmp(&env_string[strlen(env_string) - + strlen(ENV_SOCKET_SUFFIX)], ENV_SOCKET_SUFFIX) == 0) { + agent_socket = strdup(env_value); + if (agent_socket == NULL) { + syslog(LOG_CRIT, "%s: %m", MODULE_NAME); + PAM_RETURN(PAM_SERVICE_ERR); + } + } + else if (strcmp(&env_string[strlen(env_string) - + strlen(ENV_PID_SUFFIX)], ENV_PID_SUFFIX) == 0) { + env_value = strdup(env_value); + if (env_value == NULL) { + syslog(LOG_CRIT, "%s: %m", MODULE_NAME); + PAM_RETURN(PAM_SERVICE_ERR); + } + retval = pam_set_data(pamh, "ssh_agent_pid", + env_value, ssh_cleanup); + if (retval != PAM_SUCCESS) + PAM_RETURN(retval); + PAM_LOG("Environment write successful"); + } + } + if (env_fp) + fclose(env_fp); + retval = pclose(lpipe); + switch (retval) { + case -1: + syslog(LOG_ERR, "%s: %s: %m", MODULE_NAME, SSH_AGENT); + PAM_RETURN(PAM_SESSION_ERR); + case 0: + break; + case 127: + syslog(LOG_ERR, "%s: cannot execute %s", MODULE_NAME, + SSH_AGENT); + PAM_RETURN(PAM_SESSION_ERR); + default: + syslog(LOG_ERR, "%s: %s exited %s %d", MODULE_NAME, + SSH_AGENT, WIFSIGNALED(retval) ? "on signal" : + "with status", WIFSIGNALED(retval) ? WTERMSIG(retval) : + WEXITSTATUS(retval)); + PAM_RETURN(PAM_SESSION_ERR); + } + if (agent_socket == NULL) + PAM_RETURN(PAM_SESSION_ERR); + + PAM_LOG("Environment saved"); + + /* + * 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; + syslog(LOG_ERR, "%s: %m", MODULE_NAME); + PAM_RETURN(PAM_BUF_ERR); + } + ac = ssh_get_authentication_connection(); + for (evp = environ; *evp; evp++) + free(*evp); + free(environ); + environ = saved; + } + if (!ac) { + syslog(LOG_ERR, "%s: %s: %m", MODULE_NAME, agent_socket); + PAM_RETURN(PAM_SESSION_ERR); + } + + PAM_LOG("Connected to agent"); + + /* hand off each private key to the agent */ + final = 0; + for (indx = 0; ; indx++) { + if (!asprintf(&data_name, "ssh_private_key_%d", indx)) { + syslog(LOG_CRIT, "%s: %m", MODULE_NAME); + ssh_close_authentication_connection(ac); + PAM_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", indx)) { + syslog(LOG_CRIT, "%s: %m", MODULE_NAME); + ssh_close_authentication_connection(ac); + PAM_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); + + PAM_LOG("Keys handed off"); + + PAM_RETURN(final ? PAM_SUCCESS : PAM_SESSION_ERR); +} + + +PAM_EXTERN int +pam_sm_close_session(pam_handle_t *pamh, int flags __unused, int argc, const char **argv) +{ + struct options options; /* module options */ + 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 */ + + pam_std_option(&options, NULL, argc, argv); + + PAM_LOG("Options processed"); + + /* retrieve environment filename, then remove the file */ + retval = pam_get_data(pamh, "ssh_agent_env", (const void **)&env_file); + if (retval != PAM_SUCCESS) + PAM_RETURN(retval); + unlink(env_file); + + PAM_LOG("Got ssh_agent_env"); + + /* retrieve the agent's process id */ + retval = pam_get_data(pamh, "ssh_agent_pid", (const void **)&ssh_agent_pid); + if (retval != PAM_SUCCESS) + PAM_RETURN(retval); + + PAM_LOG("Got ssh_agent_pid"); + + /* + * Kill the agent. SSH2 from SSH Communications Security does + * not have a -k option, so we just call kill(). + */ + pid = atoi(ssh_agent_pid); + if (pid <= 0) + PAM_RETURN(PAM_SESSION_ERR); + if (kill(pid, SIGTERM) != 0) { + syslog(LOG_ERR, "%s: %s: %m", MODULE_NAME, ssh_agent_pid); + PAM_RETURN(PAM_SESSION_ERR); + } + + PAM_LOG("Agent killed"); + + PAM_RETURN(PAM_SUCCESS); +} + +PAM_MODULE_ENTRY("pam_ssh"); |