diff options
author | green <green@FreeBSD.org> | 1999-11-29 15:09:45 +0800 |
---|---|---|
committer | green <green@FreeBSD.org> | 1999-11-29 15:09:45 +0800 |
commit | e66275565a4a6b123d6b8d5ddf0216a8ee4c07af (patch) | |
tree | dd3ebdb95e1e629401d066ebdee7a05c022f0fc6 | |
parent | df4e81d19dcb869a79c25cdf616247321261e4a2 (diff) | |
download | freebsd-ports-gnome-e66275565a4a6b123d6b8d5ddf0216a8ee4c07af.tar.gz freebsd-ports-gnome-e66275565a4a6b123d6b8d5ddf0216a8ee4c07af.tar.zst freebsd-ports-gnome-e66275565a4a6b123d6b8d5ddf0216a8ee4c07af.zip |
Add the PAM SSH RSA key authentication module. For example, you can add,
"login auth sufficient pam_ssh.so" to your /etc/pam.conf, and
users with a ~/.ssh/identity can login(1) with their SSH key :)
PR: 15158
Submitted by: Andrew J. Korty <ajk@waterspout.com>
Reviewed by: obrien
-rw-r--r-- | security/openssh/Makefile | 22 | ||||
-rw-r--r-- | security/openssh/files/pam_ssh.c | 328 | ||||
-rw-r--r-- | security/openssh/files/pam_ssh_Makefile | 15 | ||||
-rw-r--r-- | security/openssh/files/patch-aa | 9 | ||||
-rw-r--r-- | security/openssh/files/patch-ab | 5 |
5 files changed, 373 insertions, 6 deletions
diff --git a/security/openssh/Makefile b/security/openssh/Makefile index f9f40500634b..914ad2c50097 100644 --- a/security/openssh/Makefile +++ b/security/openssh/Makefile @@ -43,6 +43,12 @@ MAKE_ENV+= AFS=yes .if defined(KERBEROS) && ${KERBEROS} == YES MAKE_ENV+= KERBEROS=yes .endif +.if exists(/usr/include/security/pam_modules.h) +PAM?= yes +.else +PAM= no +.endif +MAKE_ENV+= PAM=${PAM} .if defined(SKEY) && ${SKEY} == YES MAKE_ENV+= SKEY=yes .endif @@ -107,14 +113,26 @@ do-extract: @${MKDIR} ${WRKDIR} @${CP} -r ${DISTDIR}/${PKGNAME}/${DISTNAME} ${WRKDIR} @${CP} ${FILESDIR}/strlcat.c ${FILESDIR}/strlcpy.c ${WRKSRC}/lib/ + @${MKDIR} ${WRKSRC}/pam_ssh + @${CP} ${FILESDIR}/pam_ssh_Makefile ${WRKSRC}/pam_ssh/Makefile + @${CP} ${FILESDIR}/pam_ssh.c ${WRKSRC}/pam_ssh/ post-patch: - @${PERL} -pi.orig -e 's:__PREFIX__:${PREFIX}:g' ${WRKSRC}/ssh.h - @${PERL} -pi.orig -e 's:__PREFIX__:${PREFIX}:g' ${WRKSRC}/sshd_config + @${PERL} -pi.orig -e 's:__PREFIX__:${PREFIX}:g' ${WRKSRC}/ssh.h \ + ${WRKSRC}/sshd_config ${WRKSRC}/pam_ssh/pam_ssh.c @${PERL} -pi.openssl -e \ 's:^(\s*#\s*include\s+<)ssl(/\w+\.h>\s*)$$:$$1openssl$$2:g' \ ${WRKSRC}/*.[ch] +.if ${PAM} == yes +PLIST= ${WRKDIR}/PLIST + +do-configure: + @${CP} ${PKGDIR}/PLIST ${PLIST} + @${ECHO} "@cwd /usr" >> ${PLIST} + @${ECHO} "lib/pam_ssh.so" >> ${PLIST} +.endif + post-install: .if !exists(${PREFIX}/etc/ssh_host_key) @${ECHO_MSG} ">> Generating a secret host key." diff --git a/security/openssh/files/pam_ssh.c b/security/openssh/files/pam_ssh.c new file mode 100644 index 000000000000..8e22d3f2f25c --- /dev/null +++ b/security/openssh/files/pam_ssh.c @@ -0,0 +1,328 @@ +/*- + * Copyright (c) 1999 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$ + * + */ + + +#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 "__PREFIX__/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); diff --git a/security/openssh/files/pam_ssh_Makefile b/security/openssh/files/pam_ssh_Makefile new file mode 100644 index 000000000000..3ab738647e57 --- /dev/null +++ b/security/openssh/files/pam_ssh_Makefile @@ -0,0 +1,15 @@ +# PAM module for SSH +# $FreeBSD$ +.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> diff --git a/security/openssh/files/patch-aa b/security/openssh/files/patch-aa index 4797fe4d70a2..ffb6562b3a19 100644 --- a/security/openssh/files/patch-aa +++ b/security/openssh/files/patch-aa @@ -1,10 +1,15 @@ --- /usr/ports/distfiles/OpenSSH-1.2/src/usr.bin/ssh/Makefile Mon Oct 25 16:27:26 1999 -+++ ./Makefile Tue Nov 23 19:18:22 1999 -@@ -1,6 +1,7 @@ ++++ Makefile Mon Nov 29 01:07:28 1999 +@@ -1,8 +1,12 @@ # $OpenBSD: Makefile,v 1.5 1999/10/25 20:27:26 markus Exp $ .include <bsd.own.mk> +.include "Makefile.inc" SUBDIR= lib ssh sshd ssh-add ssh-keygen ssh-agent scp ++.if ${PAM} == yes ++SUBDIR+= pam_ssh ++.endif + distribution: + install -C -o root -g wheel -m 0644 ${.CURDIR}/ssh_config \ diff --git a/security/openssh/files/patch-ab b/security/openssh/files/patch-ab index e9be90b4c08c..ab938cc81ab5 100644 --- a/security/openssh/files/patch-ab +++ b/security/openssh/files/patch-ab @@ -1,6 +1,6 @@ --- /usr/ports/distfiles/OpenSSH-1.2/src/usr.bin/ssh/Makefile.inc Mon Oct 25 16:27:26 1999 -+++ Makefile.inc Sun Nov 28 16:29:13 1999 -@@ -2,10 +2,13 @@ ++++ Makefile.inc Mon Nov 29 01:06:23 1999 +@@ -2,10 +2,14 @@ .include <bsd.obj.mk> @@ -10,6 +10,7 @@ -.else +AFS?= no +KERBEROS?= no ++PAM?= no +SKEY?= no +TCP_WRAPPERS?= yes +.if !defined(MAKEFILE_INC_FIRST_PASS) |