From owner-freebsd-bugs Mon Oct 9 14: 0:14 2000 Delivered-To: freebsd-bugs@freebsd.org Received: from freefall.freebsd.org (freefall.FreeBSD.org [216.136.204.21]) by hub.freebsd.org (Postfix) with ESMTP id EA01B37B66D for ; Mon, 9 Oct 2000 14:00:01 -0700 (PDT) Received: (from gnats@localhost) by freefall.freebsd.org (8.9.3/8.9.2) id OAA33053; Mon, 9 Oct 2000 14:00:01 -0700 (PDT) (envelope-from gnats@FreeBSD.org) Received: from verbal.uits.iupui.edu (verbal.uits.iupui.edu [149.166.240.10]) by hub.freebsd.org (Postfix) with ESMTP id 9E47137B66C; Mon, 9 Oct 2000 13:54:43 -0700 (PDT) Received: (from ajk@localhost) by verbal.uits.iupui.edu (8.11.1/8.11.1) id e99KsbC08757; Mon, 9 Oct 2000 15:54:37 -0500 (EST) (envelope-from ajk) Message-Id: <200010092054.e99KsbC08757@verbal.uits.iupui.edu> Date: Mon, 9 Oct 2000 15:54:37 -0500 (EST) From: ajk@iu.edu Reply-To: ajk@iu.edu To: FreeBSD-gnats-submit@freebsd.org Cc: kris@freebsd.org X-Send-Pr-Version: 3.2 Subject: bin/21877: [PATCH] DSA support for pam_ssh Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.org >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 #include +#include -#include -#include #include #include #include @@ -45,6 +43,7 @@ #include #include +#include #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