Date: Mon, 9 Oct 2000 15:54:37 -0500 (EST) From: ajk@iu.edu To: FreeBSD-gnats-submit@freebsd.org Cc: kris@freebsd.org Subject: bin/21877: [PATCH] DSA support for pam_ssh Message-ID: <200010092054.e99KsbC08757@verbal.uits.iupui.edu>
next in thread | raw e-mail | index | archive | help
>Number: 21877
>Category: bin
>Synopsis: [PATCH] DSA support for pam_ssh
>Confidential: no
>Severity: serious
>Priority: high
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Mon Oct 09 14:00:01 PDT 2000
>Closed-Date:
>Last-Modified:
>Originator: Andrew J. Korty
>Release: FreeBSD 5.0-CURRENT i386
>Organization:
Indiana University
>Environment:
FreeBSD 5.0-CURRENT #1: Sun Oct 8 00:30:52 EST 2000
>Description:
When DSA support was added to ssh-agent, pam_ssh stopped working.
To fix this problem, I have added DSA support to pam_ssh. The user
is authenticated if the supplied passphrase decrypts either the
DSA or RSA key. Any and all decrypted keys are passed to the agent.
This patch also fixes a possible memory leak and ensures the user's
environment file (~/.ssh/agent-*) has sensible permissions.
>How-To-Repeat:
Apply the patch below.
>Fix:
Index: pam_ssh.c
===================================================================
RCS file: /var/cvs/src/crypto/openssh/pam_ssh/pam_ssh.c,v
retrieving revision 1.8
diff -u -r1.8 pam_ssh.c
--- pam_ssh.c 2000/07/16 05:52:28 1.8
+++ pam_ssh.c 2000/10/09 20:39:35
@@ -28,11 +28,9 @@
*/
-#include <sys/param.h>
#include <sys/queue.h>
+#include <sys/stat.h>
-#include <fcntl.h>
-#include <paths.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
@@ -45,6 +43,7 @@
#include <security/pam_mod_misc.h>
#include <openssl/dsa.h>
+#include <openssl/evp.h>
#include "includes.h"
#include "rsa.h"
@@ -54,18 +53,26 @@
#include "authfile.h"
#define MODULE_NAME "pam_ssh"
-#define NEED_PASSPHRASE "Need passphrase for %s (%s).\nEnter passphrase: "
+#define NEED_PASSPHRASE "SSH passphrase: "
#define PATH_SSH_AGENT "/usr/bin/ssh-agent"
+/*
+ * Generic cleanup function for SSH "Key" type.
+ */
+
void
-rsa_cleanup(pam_handle_t *pamh, void *data, int error_status)
+key_cleanup(pam_handle_t *pamh, void *data, int error_status)
{
if (data)
- RSA_free(data);
+ key_free(data);
}
+/*
+ * Generic PAM cleanup function for this module.
+ */
+
void
ssh_cleanup(pam_handle_t *pamh, void *data, int error_status)
{
@@ -107,6 +114,10 @@
extern char **environ;
+/*
+ * Create a new environment list.
+ */
+
static ENV *
env_new(void)
{
@@ -123,8 +134,12 @@
}
+/*
+ * Insert a new entry into the list.
+ */
+
static int
-env_put(ENV *self, char *s)
+env_put(ENV *self, const char *s)
{
struct env_entry *env;
@@ -139,13 +154,21 @@
}
+/*
+ * Switch between the original environ string and our crafted one.
+ */
+
static void
-env_swap(ENV *self, int which)
+env_swap(const ENV *self, int which)
{
environ = which ? self->e_environ_new : self->e_environ_orig;
}
+/*
+ * Craft an environ string out of the list we've built.
+ */
+
static int
env_commit(ENV *self)
{
@@ -171,15 +194,20 @@
}
+/*
+ * Destroy our list and environ string.
+ */
+
static void
env_destroy(ENV *self)
{
struct env_entry *p;
env_swap(self, 0);
- SLIST_FOREACH(p, &self->e_head, ee_entries) {
+ while ((p = SLIST_FIRST(&self->e_head))) {
free(p->ee_env);
free(p);
+ SLIST_REMOVE_HEAD(&self->e_head, ee_entries);
}
if (self->e_committed)
free(self->e_environ_new);
@@ -187,6 +215,10 @@
}
+/*
+ * Cleanup function for PAM data storage.
+ */
+
void
env_cleanup(pam_handle_t *pamh, void *data, int error_status)
{
@@ -195,6 +227,80 @@
}
+/*
+ * 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.
+ */
+
+int
+auth_via_key(
+ pam_handle_t *pamh,
+ int type,
+ const char *file,
+ 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", user->pw_dir, file)) {
+ syslog(LOG_CRIT, "%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.
+ */
+ key = key_new(type);
+ (void)setreuid(user->pw_uid, saved_uid);
+ retval = load_private_key(path, pass, key, &comment);
+ free(path);
+ (void)setuid(saved_uid);
+ if (!retval)
+ return retval;
+ /*
+ * Save the key and comment to pass to ssh-agent in the session
+ * phase.
+ */
+ if (!asprintf(&data_name, "ssh_private_key_%d", index)) {
+ 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", index)) {
+ 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;
+ }
+ ++index;
+ return PAM_SUCCESS;
+}
+
+
typedef struct passwd PASSWD;
PAM_EXTERN int
@@ -204,18 +310,11 @@
int argc,
const char **argv)
{
- char *comment_priv; /* on private key */
- char *comment_pub; /* on public key */
- char *identity; /* user's identity file */
- Key key; /* user's private key */
int options; /* module options */
const char *pass; /* passphrase */
- char *prompt; /* passphrase prompt */
- Key public_key; /* user's public key */
const PASSWD *pwent; /* user's passwd entry */
PASSWD *pwent_keep; /* our own copy */
int retval; /* from calls */
- uid_t saved_uid; /* caller's uid */
const char *user; /* username */
options = 0;
@@ -227,70 +326,25 @@
/* delay? */
return PAM_AUTH_ERR;
}
- /* locate the user's private key file */
- if (!asprintf(&identity, "%s/%s", pwent->pw_dir,
- SSH_CLIENT_IDENTITY)) {
- syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
- return PAM_SERVICE_ERR;
- }
/*
- * Fail unless we can load the public key. Change to the
- * owner's UID to appease load_public_key().
+ * Pass prompt message to application and receive
+ * passphrase.
*/
- key.type = KEY_RSA;
- key.rsa = RSA_new();
- public_key.type = KEY_RSA;
- public_key.rsa = RSA_new();
- saved_uid = getuid();
- (void)setreuid(pwent->pw_uid, saved_uid);
- retval = load_public_key(identity, &public_key, &comment_pub);
- (void)setuid(saved_uid);
- if (!retval) {
- free(identity);
- return PAM_AUTH_ERR;
- }
- RSA_free(public_key.rsa);
- /* build the passphrase prompt */
- retval = asprintf(&prompt, NEED_PASSPHRASE, identity, comment_pub);
- free(comment_pub);
- if (!retval) {
- syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
- free(identity);
- return PAM_SERVICE_ERR;
- }
- /* pass prompt message to application and receive passphrase */
- retval = pam_get_pass(pamh, &pass, prompt, options);
- free(prompt);
- if (retval != PAM_SUCCESS) {
- free(identity);
+ if ((retval = pam_get_pass(pamh, &pass, NEED_PASSPHRASE, options))
+ != PAM_SUCCESS)
return retval;
- }
+ OpenSSL_add_all_algorithms(); /* required for DSA */
/*
- * Try to decrypt the private key with the passphrase provided.
- * If success, the user is authenticated.
+ * 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.
*/
- (void)setreuid(pwent->pw_uid, saved_uid);
- retval = load_private_key(identity, pass, &key, &comment_priv);
- free(identity);
- (void)setuid(saved_uid);
- if (!retval)
+ retval = auth_via_key(pamh, KEY_DSA, SSH_CLIENT_ID_DSA, pwent,
+ pass);
+ if (auth_via_key(pamh, KEY_RSA, SSH_CLIENT_IDENTITY, pwent, pass)
+ != PAM_SUCCESS && retval != PAM_SUCCESS)
return PAM_AUTH_ERR;
/*
- * Save the key and comment to pass to ssh-agent in the session
- * phase.
- */
- if ((retval = pam_set_data(pamh, "ssh_private_key", key.rsa,
- rsa_cleanup)) != PAM_SUCCESS) {
- RSA_free(key.rsa);
- free(comment_priv);
- return retval;
- }
- if ((retval = pam_set_data(pamh, "ssh_key_comment", comment_priv,
- ssh_cleanup)) != PAM_SUCCESS) {
- free(comment_priv);
- return retval;
- }
- /*
* Copy the passwd entry (in case successive calls are made)
* and save it for the session phase.
*/
@@ -333,7 +387,10 @@
char *env_end; /* end of env */
char *env_file; /* to store env */
FILE *env_fp; /* env_file handle */
- Key key; /* user's private key */
+ char *data_name; /* PAM state */
+ int final; /* final return value */
+ int index; /* for saved keys */
+ Key *key; /* user's private key */
FILE *pipe; /* ssh-agent handle */
const PASSWD *pwent; /* user's passwd entry */
int retval; /* from calls */
@@ -351,9 +408,10 @@
if ((retval = pam_get_item(pamh, PAM_TTY, (const void **)&tty))
!= PAM_SUCCESS)
return retval;
- if (*tty == ':' && gethostname(hname, sizeof hname) == 0) {
- if (asprintf(&env_file, "%s/.ssh/agent-%s%s",
- pwent->pw_dir, hname, tty) == -1) {
+ if (gethostname(hname, sizeof hname) == 0) {
+ if (asprintf(&env_file, "%s/.ssh/agent-%s%s%s",
+ pwent->pw_dir, hname, *tty == ':' ? "" : ":", tty)
+ == -1) {
syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
return PAM_SERVICE_ERR;
}
@@ -371,7 +429,8 @@
/* start the agent as the user */
saved_uid = geteuid();
(void)seteuid(pwent->pw_uid);
- env_fp = fopen(env_file, "w");
+ if ((env_fp = fopen(env_file, "w")))
+ (void)chmod(env_file, S_IRUSR);
pipe = popen(PATH_SSH_AGENT, "r");
(void)seteuid(saved_uid);
if (!pipe) {
@@ -380,6 +439,11 @@
(void)fclose(env_fp);
return PAM_SESSION_ERR;
}
+ /*
+ * Save environment for application with pam_putenv()
+ * but also with env_* functions for our own call to
+ * ssh_get_authentication_connection().
+ */
if (!(ssh_env = env_new()))
return PAM_SESSION_ERR;
if ((retval = pam_set_data(pamh, "ssh_env_handle", ssh_env,
@@ -388,11 +452,6 @@
while (fgets(parse, sizeof parse, pipe)) {
if (env_fp)
(void)fputs(parse, env_fp);
- /*
- * Save environment for application with pam_putenv()
- * but also with env_* functions for our own call to
- * ssh_get_authentication_connection().
- */
if (strchr(parse, '=') && (env_end = strchr(parse, ';'))) {
*env_end = '\0';
/* pass to the application ... */
@@ -427,13 +486,8 @@
env_destroy(ssh_env);
return PAM_SESSION_ERR;
}
- key.type = KEY_RSA;
- /* connect to the agent and hand off the private key */
- if ((retval = pam_get_data(pamh, "ssh_private_key",
- (const void **)&key.rsa)) != PAM_SUCCESS ||
- (retval = pam_get_data(pamh, "ssh_key_comment",
- (const void **)&comment)) != PAM_SUCCESS ||
- (retval = env_commit(ssh_env)) != PAM_SUCCESS) {
+ /* connect to the agent */
+ if ((retval = env_commit(ssh_env)) != PAM_SUCCESS) {
env_destroy(ssh_env);
return retval;
}
@@ -442,11 +496,38 @@
MODULE_NAME);
env_destroy(ssh_env);
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)) {
+ syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
+ ssh_close_authentication_connection(ac);
+ env_destroy(ssh_env);
+ 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)) {
+ syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
+ ssh_close_authentication_connection(ac);
+ env_destroy(ssh_env);
+ 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;
}
- retval = ssh_add_identity(ac, key.rsa, comment);
ssh_close_authentication_connection(ac);
- env_swap(ssh_env, 0);
- return retval ? PAM_SUCCESS : PAM_SESSION_ERR;
+ env_swap(ssh_env, 0); /* restore original environment */
+ return final ? PAM_SUCCESS : PAM_SESSION_ERR;
}
@@ -461,6 +542,7 @@
int retval; /* from calls */
ENV *ssh_env; /* env handle */
+ /* invoke the cached environment to re-access the agent */
if ((retval = pam_get_data(pamh, "ssh_env_handle",
(const void **)&ssh_env)) != PAM_SUCCESS)
return retval;
>Release-Note:
>Audit-Trail:
>Unformatted:
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200010092054.e99KsbC08757>
