Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 28 Nov 1999 23:15:24 -0500 (EST)
From:      ajk@waterspout.com
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   ports/15158: [PATCH] PAM module for authenticating via SSH passphrase
Message-ID:  <199911290415.XAA98920@tempest.waterspout.com>

next in thread | raw e-mail | index | archive | help

>Number:         15158
>Category:       ports
>Synopsis:       [PATCH] PAM module for authenticating via SSH passphrase
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-ports
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sun Nov 28 20:20:00 PST 1999
>Closed-Date:
>Last-Modified:
>Originator:     Andrew J. Korty
>Release:        FreeBSD 4.0-CURRENT i386
>Organization:
Waterspout Communications
>Environment:

FreeBSD tempest.waterspout.com 4.0-CURRENT FreeBSD 4.0-CURRENT #22:
Sat Nov 20 07:41:01 EST 1999
root@tempest.waterspout.com:/usr/src/sys/compile/TEMPEST  i386

>Description:

Here is a PAM module that allows users to authenticate by typing
their SSH passphrases into KDM, the login program, or anything else
with PAM support.  If used as a session module as well, an ssh-agent
is started and the private key is added on behalf of the user.  If
your users only login via SSH or directly on the console, you can
get rid of UNIX passwords in /etc/master.passwd.

Naturally, this module is only for use when logging onto the console.
The module should be placed after pam_cleartext_pass_ok.so in
/etc/pam.conf, and users should be informed what situations are
inappropriate for typing their SSH passphrases.

The patches below apply to the OpenSSH port.  (They are meant to
be applied before any of the patches in the "patches" subdirectory.)

>How-To-Repeat:

This module works best with KDM, which has full PAM support.
Configure it with --with-pam, apply the patch I submitted in
ports/15157, recompile, and reinstall KDM.

Apply the patches below to the OpenSSH sources, and then install
the port.

Add the following lines to /etc/pam.conf:

xdm  auth     sufficient  pam_skey.so
xdm  auth     requisite   pam_cleartext_pass_ok.so
xdm  auth     sufficient  pam_ssh.so                try_first_pass
xdm  auth     required    pam_unix.so               try_first_pass
xdm  account  required    pam_unix.so
xdm  session  optional    pam_ssh.so

Kill and restart KDM.

>Fix:

--- Makefile.orig	Sun Nov 28 22:04:38 1999
+++ Makefile	Sun Nov 28 22:04:43 1999
@@ -3,7 +3,7 @@
 .include <bsd.own.mk>
 .include "Makefile.inc"
 
-SUBDIR=	lib ssh sshd ssh-add ssh-keygen ssh-agent scp
+SUBDIR=	lib pam_ssh ssh sshd ssh-add ssh-keygen ssh-agent scp
 
 distribution:
 	install -C -o root -g wheel -m 0644 ${.CURDIR}/ssh_config \
--- /dev/null	Sun Nov 28 22:52:46 1999
+++ pam_ssh/Makefile	Sun Nov 28 22:52:41 1999
@@ -0,0 +1,13 @@
+.PATH:		${.CURDIR}/..
+
+LIB=		pam_ssh
+DESTDIR=
+SHLIB_NAME=	pam_ssh.so
+SRCS=		log-client.c pam_ssh.c
+CFLAGS+=	-Wall
+DPADD+=		${LIBCRYPTO} ${LIBDES} ${LIBUTIL} ${LIBZ} ${LIBGCC_PIC}
+LDADD+=		${CRYPTOLIBS} -lutil -lz -lgcc_pic
+INTERNALLIB=	yes
+INTERNALSTATICLIB=yes
+
+.include <bsd.lib.mk>
--- /dev/null	Sun Nov 28 22:04:39 1999
+++ pam_ssh.c	Sun Nov 28 21:59:23 1999
@@ -0,0 +1,298 @@
+#include <sys/param.h>
+
+#include <fcntl.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define PAM_SM_AUTH
+#define PAM_SM_SESSION
+#include <security/pam_modules.h>
+#include <security/pam_mod_misc.h>
+
+#include "includes.h"
+#include "rsa.h"
+#include "ssh.h"
+#include "authfd.h"
+
+#define MODULE_NAME	"pam_ssh"
+#define NEED_PASSPHRASE	"Need passphrase for %s (%s).\nEnter passphrase: "
+#define PATH_SSH_AGENT	"/usr/local/bin/ssh-agent"
+
+
+void
+rsa_cleanup(pam_handle_t *pamh, void *data, int error_status)
+{
+	if (data)
+		RSA_free(data);
+}
+
+
+void
+ssh_cleanup(pam_handle_t *pamh, void *data, int error_status)
+{
+	if (data)
+		free(data);
+}
+
+
+typedef struct passwd PASSWD;
+
+PAM_EXTERN int
+pam_sm_authenticate(
+	pam_handle_t	 *pamh,
+	int		  flags,
+	int		  argc,
+	const char	**argv)
+{
+	char		*comment_priv;		/* on private key */
+	char		*comment_pub;		/* on public key */
+	char		*identity;		/* user's identity file */
+	RSA		*key;			/* user's private key */
+	int		 options;		/* module options */
+	const char	*pass;			/* passphrase */
+	char		*prompt;		/* passphrase prompt */
+	RSA		*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;
+	while (argc--)
+		pam_std_option(&options, *argv++);
+	if ((retval = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS)
+		return retval;
+	if (!((pwent = getpwnam(user)) && pwent->pw_dir)) {
+		/* 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().
+	 */
+	key = RSA_new();
+	public_key = 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);
+	/* 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);
+		return retval;
+	}
+	/*
+	 * Try to decrypt the private key with the passphrase provided.
+	 * If success, the user is authenticated.
+	 */
+	(void) setreuid(pwent->pw_uid, saved_uid);
+	retval = load_private_key(identity, pass, key, &comment_priv);
+	free(identity);
+	(void) setuid(saved_uid);
+	if (!retval)
+		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_cleanup)) != PAM_SUCCESS) {
+		RSA_free(key);
+		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.
+	 */
+	if (!(pwent_keep = malloc(sizeof *pwent))) {
+		syslog(LOG_CRIT, "%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;
+}
+
+
+typedef AuthenticationConnection AC;
+
+PAM_EXTERN int
+pam_sm_open_session(
+	pam_handle_t	 *pamh,
+	int		  flags,
+	int		  argc,
+	const char	**argv)
+{
+	AC		*ac;			/* to ssh-agent */
+	char		*comment;		/* on private key */
+	char		*env_end;		/* end of env */
+	char		*env_file;		/* to store env */
+	FILE		*env_fp;		/* env_file handle */
+	RSA		*key;			/* user's private key */
+	FILE		*pipe;			/* ssh-agent handle */
+	const PASSWD	*pwent;			/* 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		 parse[BUFSIZ];		/* commands output */
+
+	/* dump output of ssh-agent in ~/.ssh */
+	if ((retval = pam_get_data(pamh, "ssh_passwd_entry",
+	    (const void **)&pwent)) != PAM_SUCCESS)
+		return retval;
+	/* use the tty or X display name in the filename */
+	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) {
+			syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
+			return PAM_SERVICE_ERR;
+		}
+	} else if (asprintf(&env_file, "%s/.ssh/agent-%s", pwent->pw_dir,
+	    tty) == -1) {
+		syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
+		return PAM_SERVICE_ERR;
+	}
+	/* save the filename so we can delete the file on session close */
+	if ((retval = pam_set_data(pamh, "ssh_agent_env", env_file,
+	    ssh_cleanup)) != PAM_SUCCESS) {
+		free(env_file);
+		return retval;
+	}
+	/* start the agent as the user */
+	saved_uid = geteuid();
+	(void) seteuid(pwent->pw_uid);
+	env_fp = fopen(env_file, "w");
+	pipe = popen(PATH_SSH_AGENT, "r");
+	(void) seteuid(saved_uid);
+	if (!pipe) {
+		syslog(LOG_ERR, "%s: %s: %m", MODULE_NAME, PATH_SSH_AGENT);
+		if (env_fp)
+			(void) fclose(env_fp);
+		return PAM_SESSION_ERR;
+	}
+	while (fgets(parse, sizeof parse, pipe)) {
+		if (env_fp)
+			(void) fputs(parse, env_fp);
+		/*
+		 * Save environment for application with pam_putenv()
+		 * but also with putenv() for our own call to
+		 * ssh_get_authentication_connection().
+		 */
+		if (strchr(parse, '=') && (env_end = strchr(parse, ';'))) {
+			*env_end = '\0';
+			/* pass to the application ... */
+			if (!((retval = pam_putenv(pamh, parse)) ==
+			    PAM_SUCCESS && putenv(parse) == 0)) {
+				(void) pclose(pipe);
+				if (env_fp)
+					(void) fclose(env_fp);
+				return PAM_SERVICE_ERR;
+			}
+		}
+	}
+	if (env_fp)
+		(void) fclose(env_fp);
+	retval = pclose(pipe);
+	if (retval > 0) {
+		syslog(LOG_ERR, "%s: %s exited with status %d",
+		    MODULE_NAME, PATH_SSH_AGENT, WEXITSTATUS(retval));
+		return PAM_SESSION_ERR;
+	} else if (retval < 0) {
+		syslog(LOG_ERR, "%s: %s: %m", MODULE_NAME, PATH_SSH_AGENT);
+		return PAM_SESSION_ERR;
+	}
+	/* connect to the agent and hand off the private key */
+	if ((retval = pam_get_data(pamh, "ssh_private_key",
+	    (const void **)&key)) != PAM_SUCCESS)
+		return retval;
+	if ((retval = pam_get_data(pamh, "ssh_key_comment",
+	    (const void **)&comment)) != PAM_SUCCESS)
+		return retval;
+	if (!(ac = ssh_get_authentication_connection())) {
+		syslog(LOG_ERR, "%s: could not connect to agent",
+		    MODULE_NAME);
+		return PAM_SESSION_ERR;
+	}
+	retval = ssh_add_identity(ac, key, comment);
+	ssh_close_authentication_connection(ac);
+	return retval ? PAM_SUCCESS : PAM_SESSION_ERR;
+}
+
+
+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 */
+	int	 	 retval;	/* from calls */
+
+	/* kill the agent */
+	if ((retval = system(PATH_SSH_AGENT " -k")) != 0) {
+		syslog(LOG_ERR, "%s: %s -k exited with status %d",
+		    MODULE_NAME, PATH_SSH_AGENT, WEXITSTATUS(retval));
+		return PAM_SESSION_ERR;
+	}
+	/* retrieve environment filename, then remove the file */
+	if ((retval = pam_get_data(pamh, "ssh_agent_env",
+	    (const void **)&env_file)) != PAM_SUCCESS)
+		return retval;
+	(void) unlink(env_file);
+	return PAM_SUCCESS;
+}
+
+
+PAM_MODULE_ENTRY(MODULE_NAME);

>Release-Note:
>Audit-Trail:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-ports" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199911290415.XAA98920>