diff -up openssh/auth.c.keycat openssh/misc.c --- openssh/auth.c.keycat 2015-06-24 10:57:50.158849606 +0200 +++ openssh/auth.c 2015-06-24 11:04:23.989868638 +0200 @@ -966,6 +966,14 @@ subprocess(const char *tag, struct passw _exit(1); } +#ifdef WITH_SELINUX + if (sshd_selinux_setup_env_variables() < 0) { + error ("failed to copy environment: %s", + strerror(errno)); + _exit(127); + } +#endif + execve(av[0], av, child_env); error("%s exec \"%s\": %s", tag, command, strerror(errno)); _exit(127); diff -up openssh/HOWTO.ssh-keycat.keycat openssh/HOWTO.ssh-keycat --- openssh/HOWTO.ssh-keycat.keycat 2015-06-24 10:57:50.157849608 +0200 +++ openssh/HOWTO.ssh-keycat 2015-06-24 10:57:50.157849608 +0200 @@ -0,0 +1,12 @@ +The ssh-keycat retrieves the content of the ~/.ssh/authorized_keys +of an user in any environment. This includes environments with +polyinstantiation of home directories and SELinux MLS policy enabled. + +To use ssh-keycat, set these options in /etc/ssh/sshd_config file: + AuthorizedKeysCommand /usr/libexec/openssh/ssh-keycat + AuthorizedKeysCommandUser root + +Do not forget to enable public key authentication: + PubkeyAuthentication yes + + diff -up openssh/Makefile.in.keycat openssh/Makefile.in --- openssh/Makefile.in.keycat 2015-06-24 10:57:50.152849621 +0200 +++ openssh/Makefile.in 2015-06-24 10:57:50.157849608 +0200 @@ -27,6 +27,7 @@ SFTP_SERVER=$(libexecdir)/sftp-server SSH_KEYSIGN=$(libexecdir)/ssh-keysign SSH_LDAP_HELPER=$(libexecdir)/ssh-ldap-helper SSH_LDAP_WRAPPER=$(libexecdir)/ssh-ldap-wrapper +SSH_KEYCAT=$(libexecdir)/ssh-keycat SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper PRIVSEP_PATH=@PRIVSEP_PATH@ SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ @@ -52,6 +52,7 @@ K5LIBS=@K5LIBS@ GSSLIBS=@GSSLIBS@ SSHLIBS=@SSHLIBS@ SSHDLIBS=@SSHDLIBS@ +KEYCATLIBS=@KEYCATLIBS@ LIBEDIT=@LIBEDIT@ AR=@AR@ AWK=@AWK@ @@ -65,7 +66,7 @@ EXEEXT=@EXEEXT@ MKDIR_P=@MKDIR_P@ INSTALL_SSH_LDAP_HELPER=@INSTALL_SSH_LDAP_HELPER@ -TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-ldap-helper$(EXEEXT) +TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-ldap-helper$(EXEEXT) ssh-keycat$(EXEEXT) XMSS_OBJS=\ ssh-xmss.o \ @@ -190,6 +191,9 @@ ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) ssh-ldap-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ldapconf.o ldapbody.o ldapmisc.o ldap-helper.o $(LD) -o $@ ldapconf.o ldapbody.o ldapmisc.o ldap-helper.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat -lfipscheck $(LIBS) $(LDAPLIBS) +ssh-keycat$(EXEEXT): $(LIBCOMPAT) $(SSHDOBJS) libssh.a ssh-keycat.o uidswap.o + $(LD) -o $@ ssh-keycat.o uidswap.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(KEYCATLIBS) $(LIBS) + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) @@ -321,6 +325,7 @@ install-files: $(INSTALL) -m 0700 $(STRIP_OPT) ssh-ldap-helper $(DESTDIR)$(SSH_LDAP_HELPER) ; \ $(INSTALL) -m 0700 ssh-ldap-wrapper $(DESTDIR)$(SSH_LDAP_WRAPPER) ; \ fi + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keycat$(EXEEXT) $(DESTDIR)$(libexecdir)/ssh-keycat$(EXEEXT) $(INSTALL) -m 0755 $(STRIP_OPT) sftp$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT) $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 diff -up openssh/openbsd-compat/port-linux.h.keycat openssh/openbsd-compat/port-linux.h --- openssh/openbsd-compat/port-linux.h.keycat 2015-06-24 10:57:50.150849626 +0200 +++ openssh/openbsd-compat/port-linux.h 2015-06-24 10:57:50.160849601 +0200 @@ -25,8 +25,10 @@ void ssh_selinux_setup_pty(char *, const void ssh_selinux_change_context(const char *); void ssh_selinux_setfscreatecon(const char *); +int sshd_selinux_enabled(void); void sshd_selinux_copy_context(void); void sshd_selinux_setup_exec_context(char *); +int sshd_selinux_setup_env_variables(void); #endif #ifdef LINUX_OOM_ADJUST diff -up openssh/openbsd-compat/port-linux-sshd.c.keycat openssh/openbsd-compat/port-linux-sshd.c --- openssh/openbsd-compat/port-linux-sshd.c.keycat 2015-06-24 10:57:50.150849626 +0200 +++ openssh/openbsd-compat/port-linux-sshd.c 2015-06-24 10:57:50.159849603 +0200 @@ -54,6 +54,20 @@ extern Authctxt *the_authctxt; extern int inetd_flag; extern int rexeced_flag; +/* Wrapper around is_selinux_enabled() to log its return value once only */ +int +sshd_selinux_enabled(void) +{ + static int enabled = -1; + + if (enabled == -1) { + enabled = (is_selinux_enabled() == 1); + debug("SELinux support %s", enabled ? "enabled" : "disabled"); + } + + return (enabled); +} + /* Send audit message */ static int sshd_selinux_send_audit_message(int success, security_context_t default_context, @@ -308,7 +322,7 @@ sshd_selinux_getctxbyname(char *pwname, /* Setup environment variables for pam_selinux */ static int -sshd_selinux_setup_pam_variables(void) +sshd_selinux_setup_variables(int(*set_it)(char *, const char *)) { const char *reqlvl; char *role; @@ -319,16 +333,16 @@ sshd_selinux_setup_pam_variables(void) ssh_selinux_get_role_level(&role, &reqlvl); - rv = do_pam_putenv("SELINUX_ROLE_REQUESTED", role ? role : ""); + rv = set_it("SELINUX_ROLE_REQUESTED", role ? role : ""); if (inetd_flag && !rexeced_flag) { use_current = "1"; } else { use_current = ""; - rv = rv || do_pam_putenv("SELINUX_LEVEL_REQUESTED", reqlvl ? reqlvl: ""); + rv = rv || set_it("SELINUX_LEVEL_REQUESTED", reqlvl ? reqlvl: ""); } - rv = rv || do_pam_putenv("SELINUX_USE_CURRENT_RANGE", use_current); + rv = rv || set_it("SELINUX_USE_CURRENT_RANGE", use_current); if (role != NULL) free(role); @@ -336,6 +350,24 @@ sshd_selinux_setup_pam_variables(void) return rv; } +static int +sshd_selinux_setup_pam_variables(void) +{ + return sshd_selinux_setup_variables(do_pam_putenv); +} + +static int +do_setenv(char *name, const char *value) +{ + return setenv(name, value, 1); +} + +int +sshd_selinux_setup_env_variables(void) +{ + return sshd_selinux_setup_variables(do_setenv); +} + /* Set the execution context to the default for the specified user */ void sshd_selinux_setup_exec_context(char *pwname) @@ -344,7 +376,7 @@ sshd_selinux_setup_exec_context(char *pw int r = 0; security_context_t default_ctx = NULL; - if (!ssh_selinux_enabled()) + if (!sshd_selinux_enabled()) return; if (options.use_pam) { @@ -415,7 +447,7 @@ sshd_selinux_copy_context(void) { security_context_t *ctx; - if (!ssh_selinux_enabled()) + if (!sshd_selinux_enabled()) return; if (getexeccon((security_context_t *)&ctx) != 0) { diff -up openssh/platform.c.keycat openssh/platform.c --- openssh/platform.c.keycat 2015-06-24 10:57:50.147849633 +0200 +++ openssh/platform.c 2015-06-24 10:57:50.160849601 +0200 @@ -103,7 +103,7 @@ platform_setusercontext(struct passwd *p { #ifdef WITH_SELINUX /* Cache selinux status for later use */ - (void)ssh_selinux_enabled(); + (void)sshd_selinux_enabled(); #endif #ifdef USE_SOLARIS_PROJECTS diff -up openssh/ssh-keycat.c.keycat openssh/ssh-keycat.c --- openssh/ssh-keycat.c.keycat 2015-06-24 10:57:50.161849599 +0200 +++ openssh/ssh-keycat.c 2015-06-24 10:57:50.161849599 +0200 @@ -0,0 +1,241 @@ +/* + * 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, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 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. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * 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. + */ + +/* + * Copyright (c) 2011 Red Hat, Inc. + * Written by Tomas Mraz +*/ + +#define _GNU_SOURCE + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif + +#include + +#include "uidswap.h" +#include "misc.h" + +#define ERR_USAGE 1 +#define ERR_PAM_START 2 +#define ERR_OPEN_SESSION 3 +#define ERR_CLOSE_SESSION 4 +#define ERR_PAM_END 5 +#define ERR_GETPWNAM 6 +#define ERR_MEMORY 7 +#define ERR_OPEN 8 +#define ERR_FILE_MODE 9 +#define ERR_FDOPEN 10 +#define ERR_STAT 11 +#define ERR_WRITE 12 +#define ERR_PAM_PUTENV 13 +#define BUFLEN 4096 + +/* Just ignore the messages in the conversation function */ +static int +dummy_conv(int num_msg, const struct pam_message **msgm, + struct pam_response **response, void *appdata_ptr) +{ + struct pam_response *rsp; + + (void)msgm; + (void)appdata_ptr; + + if (num_msg <= 0) + return PAM_CONV_ERR; + + /* Just allocate the array as empty responses */ + rsp = calloc (num_msg, sizeof (struct pam_response)); + if (rsp == NULL) + return PAM_CONV_ERR; + + *response = rsp; + return PAM_SUCCESS; +} + +static struct pam_conv conv = { + dummy_conv, + NULL +}; + +char * +make_auth_keys_name(const struct passwd *pwd) +{ + char *fname; + + if (asprintf(&fname, "%s/.ssh/authorized_keys", pwd->pw_dir) < 0) + return NULL; + + return fname; +} + +int +dump_keys(const char *user) +{ + struct passwd *pwd; + int fd = -1; + FILE *f = NULL; + char *fname = NULL; + int rv = 0; + char buf[BUFLEN]; + size_t len; + struct stat st; + + if ((pwd = getpwnam(user)) == NULL) { + return ERR_GETPWNAM; + } + + if ((fname = make_auth_keys_name(pwd)) == NULL) { + return ERR_MEMORY; + } + + temporarily_use_uid(pwd); + + if ((fd = open(fname, O_RDONLY|O_NONBLOCK|O_NOFOLLOW, 0)) < 0) { + rv = ERR_OPEN; + goto fail; + } + + if (fstat(fd, &st) < 0) { + rv = ERR_STAT; + goto fail; + } + + if (!S_ISREG(st.st_mode) || + (st.st_uid != pwd->pw_uid && st.st_uid != 0)) { + rv = ERR_FILE_MODE; + goto fail; + } + + unset_nonblock(fd); + + if ((f = fdopen(fd, "r")) == NULL) { + rv = ERR_FDOPEN; + goto fail; + } + + fd = -1; + + while ((len = fread(buf, 1, sizeof(buf), f)) > 0) { + rv = fwrite(buf, 1, len, stdout) != len ? ERR_WRITE : 0; + } + +fail: + if (fd != -1) + close(fd); + if (f != NULL) + fclose(f); + free(fname); + restore_uid(); + return rv; +} + +static const char *env_names[] = { "SELINUX_ROLE_REQUESTED", + "SELINUX_LEVEL_REQUESTED", + "SELINUX_USE_CURRENT_RANGE" +}; + +extern char **environ; + +int +set_pam_environment(pam_handle_t *pamh) +{ + int i; + size_t j; + + for (j = 0; j < sizeof(env_names)/sizeof(env_names[0]); ++j) { + int len = strlen(env_names[j]); + + for (i = 0; environ[i] != NULL; ++i) { + if (strncmp(env_names[j], environ[i], len) == 0 && + environ[i][len] == '=') { + if (pam_putenv(pamh, environ[i]) != PAM_SUCCESS) + return ERR_PAM_PUTENV; + } + } + } + + return 0; +} + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh = NULL; + int retval; + int ev = 0; + + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return ERR_USAGE; + } + + retval = pam_start("ssh-keycat", argv[1], &conv, &pamh); + if (retval != PAM_SUCCESS) { + return ERR_PAM_START; + } + + ev = set_pam_environment(pamh); + if (ev != 0) + goto finish; + + retval = pam_open_session(pamh, PAM_SILENT); + if (retval != PAM_SUCCESS) { + ev = ERR_OPEN_SESSION; + goto finish; + } + + ev = dump_keys(argv[1]); + + retval = pam_close_session(pamh, PAM_SILENT); + if (retval != PAM_SUCCESS) { + ev = ERR_CLOSE_SESSION; + } + +finish: + retval = pam_end (pamh,retval); + if (retval != PAM_SUCCESS) { + ev = ERR_PAM_END; + } + return ev; +} diff --git a/configure.ac b/configure.ac index 3bbccfd..6481f1f 100644 --- a/configure.ac +++ b/configure.ac @@ -2952,6 +2952,7 @@ AC_ARG_WITH([pam], PAM_MSG="yes" SSHDLIBS="$SSHDLIBS -lpam" + KEYCATLIBS="$KEYCATLIBS -lpam" AC_DEFINE([USE_PAM], [1], [Define if you want to enable PAM support]) @@ -3105,6 +3106,7 @@ ;; *) SSHDLIBS="$SSHDLIBS -ldl" + KEYCATLIBS="$KEYCATLIBS -ldl" ;; esac fi @@ -4042,6 +4044,7 @@ AC_ARG_WITH([selinux], ) AC_SUBST([SSHLIBS]) AC_SUBST([SSHDLIBS]) +AC_SUBST([KEYCATLIBS]) # Check whether user wants Kerberos 5 support KRB5_MSG="no" @@ -5031,6 +5034,9 @@ fi if test ! -z "${SSHLIBS}"; then echo " +for ssh: ${SSHLIBS}" fi +if test ! -z "${KEYCATLIBS}"; then +echo " +for ssh-keycat: ${KEYCATLIBS}" +fi echo ""