From b0695cc9f5478daa14d3f451ecdd39ba6e6abe0f Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Tue, 19 Jan 2016 15:29:22 +0100 Subject: [PATCH] authorized_keys_command --- pam_ssh_agent_auth-0.9.3/CONTRIBUTORS | 2 + pam_ssh_agent_auth-0.9.3/Makefile.in | 28 +- pam_ssh_agent_auth-0.9.3/README | 38 +++ pam_ssh_agent_auth-0.9.3/configure.ac | 27 +- pam_ssh_agent_auth-0.9.3/get_command_line.c | 113 +++++++ pam_ssh_agent_auth-0.9.3/get_command_line.h | 40 +++ pam_ssh_agent_auth-0.9.3/identity.h | 24 ++ pam_ssh_agent_auth-0.9.3/iterate_ssh_agent_keys.c | 137 ++++++++- pam_ssh_agent_auth-0.9.3/iterate_ssh_agent_keys.h | 2 +- pam_ssh_agent_auth-0.9.3/pam_ssh_agent_auth.c | 16 +- pam_ssh_agent_auth-0.9.3/pam_ssh_agent_auth.pod | 50 ++- pam_ssh_agent_auth-0.9.3/pam_static_macros.h | 29 ++ .../pam_user_authorized_keys.c | 23 +- .../pam_user_authorized_keys.h | 2 +- pam_ssh_agent_auth-0.9.3/pam_user_key_allowed2.c | 334 ++++++++++++++++----- pam_ssh_agent_auth-0.9.3/pam_user_key_allowed2.h | 3 +- pam_ssh_agent_auth-0.9.3/secure_filename.c | 51 +++- pam_ssh_agent_auth-0.9.3/secure_filename.h | 4 +- pam_ssh_agent_auth-0.9.3/userauth_pubkey_from_id.c | 20 +- pam_ssh_agent_auth-0.9.3/userauth_pubkey_from_id.h | 2 +- 20 files changed, 805 insertions(+), 140 deletions(-) create mode 100644 pam_ssh_agent_auth-0.9.3/README create mode 100644 pam_ssh_agent_auth-0.9.3/get_command_line.c create mode 100644 pam_ssh_agent_auth-0.9.3/get_command_line.h diff --git a/pam_ssh_agent_auth-0.9.3/CONTRIBUTORS b/pam_ssh_agent_auth-0.9.3/CONTRIBUTORS index d5a21cb..22b424a 100644 --- a/pam_ssh_agent_auth-0.9.3/CONTRIBUTORS +++ b/pam_ssh_agent_auth-0.9.3/CONTRIBUTORS @@ -1,3 +1,5 @@ * Foremost, OpenSSH from which this project is derived. * Jamie Beverly * Rafael D'Halleweyn - 2011-06-05 18:56:24 EDT +* Jan-Pieter Cornet ( johnpc ) - 2012-03-23 03:25:52 PDT +* chrysn@fsfe.org diff --git a/pam_ssh_agent_auth-0.9.3/Makefile.in b/pam_ssh_agent_auth-0.9.3/Makefile.in index 47bb103..4977838 100644 --- a/pam_ssh_agent_auth-0.9.3/Makefile.in +++ b/pam_ssh_agent_auth-0.9.3/Makefile.in @@ -1,4 +1,28 @@ # $Id: Makefile.in,v 1.289 2008/03/13 01:41:31 djm Exp $ +# +# Copyright (c) 2000 Markus Friedl. All rights reserved. +# Modifications Copyright (c) 2008-2014 Jamie Beverly. 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 ``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. +# # uncomment if you run a non bourne compatable shell. Ie. csh #SHELL = @SH@ @@ -46,9 +70,9 @@ INSTALL_SSH_RAND_HELPER=@INSTALL_SSH_RAND_HELPER@ PAM_MODULES=pam_ssh_agent_auth.so -SSHOBJS=xmalloc.o atomicio.o authfd.o bufaux.o bufbn.o buffer.o cleanup.o entropy.o fatal.o key.o log.o misc.o secure_filename.o ssh-dss.o ssh-rsa.o uuencode.o compat.o +SSHOBJS=xmalloc.o atomicio.o authfd.o bufaux.o bufbn.o buffer.o cleanup.o entropy.o fatal.o key.o log.o misc.o secure_filename.o ssh-dss.o ssh-rsa.o uuencode.o compat.o uidswap.o -PAM_SSH_AGENT_AUTH_OBJS=pam_user_key_allowed2.o iterate_ssh_agent_keys.o userauth_pubkey_from_id.o pam_user_authorized_keys.o secure_filename.o +PAM_SSH_AGENT_AUTH_OBJS=pam_user_key_allowed2.o iterate_ssh_agent_keys.o userauth_pubkey_from_id.o pam_user_authorized_keys.o secure_filename.o get_command_line.o MANPAGES_IN = pam_ssh_agent_auth.pod diff --git a/pam_ssh_agent_auth-0.9.3/README b/pam_ssh_agent_auth-0.9.3/README new file mode 100644 index 0000000..c1a49ef --- /dev/null +++ b/pam_ssh_agent_auth-0.9.3/README @@ -0,0 +1,38 @@ +pam_ssh_agent_auth is a PAM module which permits PAM authentication via your +keyring in a forwarded ssh-agent. + +Release 0.10.1 is stable, and has been tested on FreeBSD, Solaris 10, Solaris 11, +RHEL5, RHEL6, Debian Wheezy, Ubuntu 12.04 (LTS), Ubuntu 13.10, +and MacOS X 10.7. + +This module can be used to provide authentication for anything run locally that +supports PAM. It was written specifically with the intention of permitting +authentication for sudo without password entry, and also has been proven useful +for use with su as an alternative to wheel. + +It serves as middle ground between the two most common, and suboptimal +alternatives for large-scale system administration: allowing rootlogin via ssh, +or using NOPASSWD in sudoers. This module allows for ssh public-key +authentication, and it does this by leveraging an authentication mechanism you +are probably already using, ssh-agent. + +There are caveats of course, ssh-agent forwarding has it’s own security risks +which must be carefully considered for your environment. In cases where there +are not untrustworthy intermediate servers, and you wish to retain traceability, +accountability, and required authentication for privileged command invocation, +the benefits should outweigh the risks. Release 0.10.1 can be downloaded from +SourceForge: https://sourceforge.net/project/showfiles.php?group_id=249556 + +If you encounter any issues with usability or security, please use the project's +SourceForge tracker: +https://sourceforge.net/tracker2/?group_id=249556&atid=1126337 + +Note that if you wish to use this for sudo, you will need a version of sudo that +preserves the env_keep environment during authentication; and ideally a version +incorporating my minor patch which ensures RUSER is set during PAM authentication. + +sudo 1.6.8p12 does not work correctly with this PAM module, because it clears the +environment (even env_keep variables) prior to attempting PAM authentication. + +sudo 1.7.2p1 or later is preferred, as it correctly sets PAM_RUSER for +authentication. diff --git a/pam_ssh_agent_auth-0.9.3/configure.ac b/pam_ssh_agent_auth-0.9.3/configure.ac index 1f372ce..4eb1f32 100644 --- a/pam_ssh_agent_auth-0.9.3/configure.ac +++ b/pam_ssh_agent_auth-0.9.3/configure.ac @@ -18,8 +18,10 @@ AC_INIT(OpenSSH, Portable, openssh-unix-dev@mindrot.org) AC_REVISION($Revision: 1.397 $) AC_CONFIG_SRCDIR([pam_ssh_agent_auth.c]) -AC_CONFIG_HEADER(config.h) +AC_CONFIG_SRCDIR([config.h.in]) +AC_CONFIG_HEADERS([config.h]) AC_PROG_CC +AC_C_INLINE AC_CANONICAL_HOST AC_C_BIGENDIAN @@ -117,7 +119,6 @@ if test -z "$LD" ; then fi AC_SUBST(LD) -AC_C_INLINE AC_CHECK_DECL(LLONG_MAX, have_llong_max=1, , [#include ]) @@ -151,7 +152,7 @@ if test "$GCC" = "yes" || test "$GCC" = "egcs"; then ;; 2.*) no_attrib_nonnull=1 ;; 3.*) CFLAGS="$CFLAGS -Wsign-compare" ;; - 4.*) CFLAGS="$CFLAGS -Wsign-compare -Wno-pointer-sign" ;; + 4.*) CFLAGS="$CFLAGS -Wsign-compare -Wall -Wextra" ;; *) ;; esac @@ -1656,11 +1657,11 @@ fi if test "x$check_for_conflicting_getspnam" = "x1"; then AC_MSG_CHECKING(for conflicting getspnam in shadow.h) - AC_COMPILE_IFELSE( + AC_COMPILE_IFELSE([AC_LANG_SOURCE( [ #include int main(void) {exit(0);} - ], + ])], [ AC_MSG_RESULT(no) ], @@ -2720,7 +2721,7 @@ fi dnl make sure we're using the real structure members and not defines AC_CACHE_CHECK([for msg_accrights field in struct msghdr], ac_cv_have_accrights_in_msghdr, [ - AC_COMPILE_IFELSE( + AC_COMPILE_IFELSE([AC_LANG_SOURCE( [ #include #include @@ -2734,7 +2735,7 @@ struct msghdr m; m.msg_accrights = 0; exit(0); } - ], + ])], [ ac_cv_have_accrights_in_msghdr="yes" ], [ ac_cv_have_accrights_in_msghdr="no" ] ) @@ -2747,7 +2748,7 @@ fi AC_CACHE_CHECK([for msg_control field in struct msghdr], ac_cv_have_control_in_msghdr, [ - AC_COMPILE_IFELSE( + AC_COMPILE_IFELSE([AC_LANG_SOURCE( [ #include #include @@ -2761,7 +2762,7 @@ struct msghdr m; m.msg_control = 0; exit(0); } - ], + ])], [ ac_cv_have_control_in_msghdr="yes" ], [ ac_cv_have_control_in_msghdr="no" ] ) @@ -2891,14 +2892,14 @@ AC_SEARCH_LIBS(getrrsetbyname, resolv, saved_LIBS="$LIBS" LIBS="$LIBS -lresolv" AC_MSG_CHECKING(for res_query in -lresolv) - AC_LINK_IFELSE([ + AC_LINK_IFELSE([AC_LANG_SOURCE([ #include int main() { res_query (0, 0, 0, 0, 0); return 0; } - ], + ])], [LIBS="$LIBS -lresolv" AC_MSG_RESULT(yes)], [LIBS="$saved_LIBS" @@ -2915,7 +2916,7 @@ int main() ]) AC_MSG_CHECKING(if struct __res_state _res is an extern) -AC_LINK_IFELSE([ +AC_LINK_IFELSE([AC_LANG_SOURCE([ #include #if HAVE_SYS_TYPES_H # include @@ -2925,7 +2926,7 @@ AC_LINK_IFELSE([ #include extern struct __res_state _res; int main() { return 0; } - ], + ])], [AC_MSG_RESULT(yes) AC_DEFINE(HAVE__RES_EXTERN, 1, [Define if you have struct __res_state _res as an extern]) diff --git a/pam_ssh_agent_auth-0.9.3/get_command_line.c b/pam_ssh_agent_auth-0.9.3/get_command_line.c new file mode 100644 index 0000000..e880fee --- /dev/null +++ b/pam_ssh_agent_auth-0.9.3/get_command_line.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2014, Jamie Beverly. + * 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 Jamie Beverly ``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 Jamie Beverly 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. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of Jamie Beverly. + */ + +#include +#include +#include + +#include "includes.h" +#include "xmalloc.h" +#include "get_command_line.h" + +#ifdef HAVE_PROC_PID_CMDLINE + +static size_t +proc_pid_cmdline(char *** inargv) +{ + pid_t pid; + FILE *f = NULL; + char filename[64] = { 0 }, c = '\0'; + char ** argv; + char argbuf[MAX_LEN_PER_CMDLINE_ARG + 1] = { 0 }; + size_t count = 0, len = 0; + + pid = getpid(); + argv = NULL; + + snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid); + f = fopen(filename, "r"); + + if (f) { + while (!feof(f) && count < MAX_CMDLINE_ARGS) { + if (len > MAX_LEN_PER_CMDLINE_ARG) { + while (!feof(f) && (c = fgetc(f)) != '\0'); + } + else { + c = fgetc(f); + } + switch (c) { + case EOF: + case '\0': + if (len > 0) { + argv = xrealloc(argv, count + 1, sizeof(*argv)); + argv[count] = xcalloc(len + 1, sizeof(*argv[count])); + strncpy(argv[count++], argbuf, len); + memset(argbuf, '\0', MAX_LEN_PER_CMDLINE_ARG + 1); + len = 0; + } + break; + default: + argbuf[len++] = c; + break; + } + } + fclose(f); + } + *inargv = argv; + return count; +} +#endif + + +/* + * takes a pointer to an unallocated array of arrays of strings, populates the + * given pointer with the address of the allocated array of strings collected + */ +size_t +pamsshagentauth_get_command_line(char *** argv) +{ +#ifdef HAVE_PROC_PID_CMDLINE + return proc_pid_cmdline(argv); +#else + /* No other supported implementations at this time */ + return 0; +#endif +} + +void +pamsshagentauth_free_command_line(char ** argv, size_t n_args) +{ + size_t i; + for (i = 0; i < n_args; i++) + free(argv[i]); + + free(argv); + return; +} + diff --git a/pam_ssh_agent_auth-0.9.3/get_command_line.h b/pam_ssh_agent_auth-0.9.3/get_command_line.h new file mode 100644 index 0000000..37cd077 --- /dev/null +++ b/pam_ssh_agent_auth-0.9.3/get_command_line.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, Jamie Beverly. + * 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 Jamie Beverly ``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 Jamie Beverly 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. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of Jamie Beverly. + */ + +#ifndef _GET_COMMAND_LINE_H +#define _GET_COMMAND_LINE_H + +#include "includes.h" + +size_t pamsshagentauth_get_command_line(char ***); +void pamsshagentauth_free_command_line(char **, size_t); +#define MAX_CMDLINE_ARGS 255 +#define MAX_LEN_PER_CMDLINE_ARG 255 + +#endif diff --git a/pam_ssh_agent_auth-0.9.3/identity.h b/pam_ssh_agent_auth-0.9.3/identity.h index eb21320..0bde782 100644 --- a/pam_ssh_agent_auth-0.9.3/identity.h +++ b/pam_ssh_agent_auth-0.9.3/identity.h @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2000 Markus Friedl. 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 ``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. + */ + #ifndef _IDENTITY_H #define _IDENTITY_H #include "includes.h" diff --git a/pam_ssh_agent_auth-0.9.3/iterate_ssh_agent_keys.c b/pam_ssh_agent_auth-0.9.3/iterate_ssh_agent_keys.c index 11ab6e2..6b4d531 100644 --- a/pam_ssh_agent_auth-0.9.3/iterate_ssh_agent_keys.c +++ b/pam_ssh_agent_auth-0.9.3/iterate_ssh_agent_keys.c @@ -28,6 +28,8 @@ */ +#include + #include "includes.h" #include "config.h" @@ -48,26 +50,75 @@ #include #include #include +#include "ssh2.h" +#include "misc.h" #include "userauth_pubkey_from_id.h" #include "identity.h" +#include "get_command_line.h" +extern char **environ; + +#define PAM_SSH_AGENT_AUTH_REQUESTv1 101 + +static char * +log_action(char ** action, size_t count) +{ + size_t i; + char *buf = NULL; + + if (count == 0) + return NULL; + + buf = xcalloc((count * MAX_LEN_PER_CMDLINE_ARG) + (count * 3), sizeof(*buf)); + for (i = 0; i < count; i++) { + strcat(buf, (i > 0) ? " '" : "'"); + strncat(buf, action[i], MAX_LEN_PER_CMDLINE_ARG); + strcat(buf, "'"); + } + return buf; +} + +void +agent_action(Buffer *buf, char ** action, size_t count) +{ + size_t i; + buffer_init(buf); -u_char * session_id2 = NULL; -uint8_t session_id_len = 0; + buffer_put_int(buf, count); + + for (i = 0; i < count; i++) { + buffer_put_cstring(buf, action[i]); + } +} -u_char * -session_id2_gen() +void +session_id2_gen(Buffer * session_id2, const char * user, + const char * ruser, const char * servicename) { char *cookie = NULL; uint8_t i = 0; uint32_t rnd = 0; + uint8_t cookie_len; + char hostname[256] = { 0 }; + char pwd[1024] = { 0 }; + time_t ts; + char ** reported_argv = NULL; + size_t count = 0; + char * action_logbuf = NULL; + Buffer action_agentbuf; + uint8_t free_logbuf = 0; + char * retc; + int32_t reti; rnd = arc4random(); - session_id_len = (uint8_t) rnd; + cookie_len = ((uint8_t) rnd); + while (cookie_len < 16) { + cookie_len += 16; /* Add 16 bytes to the size to ensure that while the length is random, the length is always reasonable; ticket #18 */ + } - cookie = calloc(1,session_id_len); + cookie = xcalloc(1, cookie_len); - for (i = 0; i < session_id_len; i++) { + for (i = 0; i < cookie_len; i++) { if (i % 4 == 0) { rnd = arc4random(); } @@ -75,7 +126,64 @@ session_id2_gen() rnd >>= 8; } - return cookie; + count = pamsshagentauth_get_command_line(&reported_argv); + if (count > 0) { + free_logbuf = 1; + action_logbuf = log_action(reported_argv, count); + agent_action(&action_agentbuf, reported_argv, count); + pamsshagentauth_free_command_line(reported_argv, count); + } + else { + action_logbuf = "unknown on this platform"; + buffer_init(&action_agentbuf); /* stays empty, means unavailable */ + } + + /* + action = getenv("SUDO_COMMAND"); + if(!action) { + action = getenv("PAM_AUTHORIZED_ACTION"); + if(!action) { + action = empty; + } + } + */ + + reti = gethostname(hostname, sizeof(hostname) - 1); + retc = getcwd(pwd, sizeof(pwd) - 1); + time(&ts); + + buffer_init(session_id2); + + buffer_put_int(session_id2, PAM_SSH_AGENT_AUTH_REQUESTv1); + /* debug3("cookie: %s", tohex(cookie, cookie_len)); */ + buffer_put_string(session_id2, cookie, cookie_len); + /* debug3("user: %s", user); */ + buffer_put_cstring(session_id2, user); + /* debug3("ruser: %s", ruser); */ + buffer_put_cstring(session_id2, ruser); + /* debug3("servicename: %s", servicename); */ + buffer_put_cstring(session_id2, servicename); + /* debug3("pwd: %s", pwd); */ + if(retc) + buffer_put_cstring(session_id2, pwd); + else + buffer_put_cstring(session_id2, ""); + /* debug3("action: %s", action_logbuf); */ + buffer_put_string(session_id2, action_agentbuf.buf + action_agentbuf.offset, action_agentbuf.end - action_agentbuf.offset); + if (free_logbuf) { + free(action_logbuf); + buffer_free(&action_agentbuf); + } + /* debug3("hostname: %s", hostname); */ + if(reti >= 0) + buffer_put_cstring(session_id2, hostname); + else + buffer_put_cstring(session_id2, ""); + /* debug3("ts: %ld", ts); */ + buffer_put_int64(session_id2, (uint64_t) ts); + + free(cookie); + return; } /* @@ -174,19 +282,21 @@ ssh_get_authentication_connection_for_uid(uid_t uid) } int -find_authorized_keys(uid_t uid) +find_authorized_keys(const char * user, const char * ruser, const char * servicename) { + Buffer session_id2 = { 0 }; Identity *id; Key *key; AuthenticationConnection *ac; char *comment; uint8_t retval = 0; + uid_t uid = getpwnam(ruser)->pw_uid; OpenSSL_add_all_digests(); - session_id2 = session_id2_gen(); + session_id2_gen(&session_id2, user, ruser, servicename); if ((ac = ssh_get_authentication_connection_for_uid(uid))) { - verbose("Contacted ssh-agent of user %s (%u)", getpwuid(uid)->pw_name, uid); + verbose("Contacted ssh-agent of user %s (%u)", ruser, uid); for (key = ssh_get_first_identity(ac, &comment, 2); key != NULL; key = ssh_get_next_identity(ac, &comment, 2)) { if(key != NULL) { @@ -194,7 +304,7 @@ find_authorized_keys(uid_t uid) id->key = key; id->filename = comment; id->ac = ac; - if(userauth_pubkey_from_id(id)) { + if(userauth_pubkey_from_id(ruser, id, &session_id2)) { retval = 1; } free(id->filename); @@ -204,12 +314,13 @@ find_authorized_keys(uid_t uid) break; } } + buffer_free(&session_id2); ssh_close_authentication_connection(ac); } else { verbose("No ssh-agent could be contacted"); } - free(session_id2); + /*free(session_id2);*/ EVP_cleanup(); return retval; } diff --git a/pam_ssh_agent_auth-0.9.3/iterate_ssh_agent_keys.h b/pam_ssh_agent_auth-0.9.3/iterate_ssh_agent_keys.h index ed7549e..e6c75aa 100644 --- a/pam_ssh_agent_auth-0.9.3/iterate_ssh_agent_keys.h +++ b/pam_ssh_agent_auth-0.9.3/iterate_ssh_agent_keys.h @@ -31,6 +31,6 @@ #ifndef _ITERATE_SSH_AGENT_KEYS_H #define _ITERATE_SSH_AGENT_KEYS_H -int find_authorized_keys(uid_t); +int find_authorized_keys(const char * user, const char * ruser, const char * servicename); #endif diff --git a/pam_ssh_agent_auth-0.9.3/pam_ssh_agent_auth.c b/pam_ssh_agent_auth-0.9.3/pam_ssh_agent_auth.c index d3f4325..37309f7 100644 --- a/pam_ssh_agent_auth-0.9.3/pam_ssh_agent_auth.c +++ b/pam_ssh_agent_auth-0.9.3/pam_ssh_agent_auth.c @@ -59,9 +59,12 @@ #include "pam_user_authorized_keys.h" #define strncasecmp_literal(A,B) strncasecmp( A, B, sizeof(B) - 1) +#define UNUSED(expr) do { (void)(expr); } while (0) char *authorized_keys_file = NULL; uint8_t allow_user_owned_authorized_keys_file = 0; +char *authorized_keys_command = NULL; +char *authorized_keys_command_user = NULL; #if ! HAVE___PROGNAME || HAVE_BUNDLE char *__progname; @@ -90,6 +93,7 @@ pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char **argv) facility = SYSLOG_FACILITY_AUTHPRIV; #endif + UNUSED(flags); pam_get_item(pamh, PAM_SERVICE, (void *) &servicename); /* * XXX: @@ -113,6 +117,12 @@ pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char **argv) if(strncasecmp_literal(*argv_ptr, "file=") == 0 ) { authorized_keys_file_input = *argv_ptr + sizeof("file=") - 1; } + if(strncasecmp_literal(*argv_ptr, "authorized_keys_command=") == 0 ) { + authorized_keys_command = *argv_ptr + sizeof("authorized_keys_command=") - 1; + } + if(strncasecmp_literal(*argv_ptr, "authorized_keys_command_user=") == 0 ) { + authorized_keys_command_user = *argv_ptr + sizeof("authorized_keys_command_user=") - 1; + } #ifdef ENABLE_SUDO_HACK if(strncasecmp_literal(*argv_ptr, "sudo_service_name=") == 0) { strncpy( sudo_service_name, *argv_ptr + sizeof("sudo_service_name=") - 1, sizeof(sudo_service_name) - 1); @@ -182,7 +192,7 @@ pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char **argv) /* * this pw_uid is used to validate the SSH_AUTH_SOCK, and so must be the uid of the ruser invoking the program, not the target-user */ - if(find_authorized_keys(getpwnam(ruser)->pw_uid)) { + if(find_authorized_keys(user, ruser, servicename)) { /* getpwnam(ruser)->pw_uid)) { */ logit("Authenticated: `%s' as `%s' using %s", ruser, user, authorized_keys_file); retval = PAM_SUCCESS; } else { @@ -207,6 +217,10 @@ cleanexit: PAM_EXTERN int __attribute__ ((visibility ("default"))) pam_sm_setcred(pam_handle_t * pamh, int flags, int argc, const char **argv) { + UNUSED(pamh); + UNUSED(flags); + UNUSED(argc); + UNUSED(argv); return PAM_SUCCESS; } diff --git a/pam_ssh_agent_auth-0.9.3/pam_ssh_agent_auth.pod b/pam_ssh_agent_auth-0.9.3/pam_ssh_agent_auth.pod index 4570746..76b1f0f 100644 --- a/pam_ssh_agent_auth-0.9.3/pam_ssh_agent_auth.pod +++ b/pam_ssh_agent_auth-0.9.3/pam_ssh_agent_auth.pod @@ -1,8 +1,12 @@ -=head1 PAM_SSH_AGENT_AUTH +=head1 NAME + +pam_ssh_agent_auth - PAM module for granting permissions based on SSH agent requests + +=head1 DESCRIPTION This module provides authentication via ssh-agent. If an ssh-agent listening at SSH_AUTH_SOCK can successfully authenticate that it has the secret key for a public key in the specified file, authentication is granted, otherwise authentication fails. -=head1 SUMMARY +=head1 CONFIGURATION =over @@ -10,7 +14,8 @@ This module provides authentication via ssh-agent. If an ssh-agent listening at auth sufficient pam_ssh_agent_auth.so file=/etc/security/authorized_keys =item /etc/sudoers: - + +In older versions of sudo (< 1.8.5) it was necessary to set: Defaults env_keep += "SSH_AUTH_SOCK" =back @@ -33,6 +38,15 @@ Specify the path to the authorized_keys file(s) you would like to use for authen A flag which enables authorized_keys files to be owned by the invoking user, instead of root. This flag is enabled automatically whenever the expansions %h or ~ are used. +=item authorized_keys_command= + +Specify an external command to run, which should take a single argument, the username of the person being authenticated, and emit to its stdout a file in authorized_keys format. +This is ideally suited for use with sssd's sss_ssh_authorizedkeys, for authenticating users via authorized_keys stored in ldap or other sssd supported security service. + +=item authorized_keys_command_user= + +Specify a user to run the authorized_keys_command as. If this option is not specified, the authorized_keys_command will be run as the user being authenticated. + =item debug A flag which enables verbose logging @@ -105,4 +119,34 @@ so this file must be owned by root. =back +=head1 COPYRIGHT + + Copyright (c) 2008-2014, Jamie Beverly. + And is based on openssh, and the included works by Markus Friedl, Darren Tucker, + Todd C. Miller, Ben Lindstrom, Tim Rice, Damien Miller, and many others. + + All rights reserved. + + See sources for complete attributions. + + 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 Jamie Beverly ``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 Jamie Beverly 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. + =cut diff --git a/pam_ssh_agent_auth-0.9.3/pam_static_macros.h b/pam_ssh_agent_auth-0.9.3/pam_static_macros.h index a4938d3..a991704 100644 --- a/pam_ssh_agent_auth-0.9.3/pam_static_macros.h +++ b/pam_ssh_agent_auth-0.9.3/pam_static_macros.h @@ -1,3 +1,32 @@ +/* + * Copyright (c) 2008, Jamie Beverly. + * 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 Jamie Beverly ``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 Jamie Beverly 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. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of Jamie Beverly. + */ + #ifndef __PAM_STATIC_MACROS_H #define __PAM_STATIC_MACROS_H diff --git a/pam_ssh_agent_auth-0.9.3/pam_user_authorized_keys.c b/pam_ssh_agent_auth-0.9.3/pam_user_authorized_keys.c index 60bef68..abc0a62 100644 --- a/pam_ssh_agent_auth-0.9.3/pam_user_authorized_keys.c +++ b/pam_ssh_agent_auth-0.9.3/pam_user_authorized_keys.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2008, Jamie Beverly. * All rights reserved. * @@ -80,9 +80,15 @@ #include "identity.h" #include "pam_user_key_allowed2.h" -extern char *authorized_keys_file; -extern uint8_t allow_user_owned_authorized_keys_file; -uid_t authorized_keys_file_allowed_owner_uid; +extern char *authorized_keys_file; + +extern char *authorized_keys_command; + +extern char *authorized_keys_command_user; + +extern uint8_t allow_user_owned_authorized_keys_file; + +uid_t authorized_keys_file_allowed_owner_uid; void parse_authorized_key_file(const char *user, const char *authorized_keys_file_input) @@ -137,8 +143,11 @@ parse_authorized_key_file(const char *user, const char *authorized_keys_file_inp } int -pam_user_key_allowed(Key * key) +pam_user_key_allowed(const char *ruser, Key * key) { return pam_user_key_allowed2(getpwuid(authorized_keys_file_allowed_owner_uid), key, authorized_keys_file) - || pam_user_key_allowed2(getpwuid(0), key, authorized_keys_file); + || pam_user_key_allowed2(getpwuid(0), key, authorized_keys_file) + || pam_user_key_command_allowed2(authorized_keys_command, + authorized_keys_command_user, + getpwnam(ruser), key); } diff --git a/pam_ssh_agent_auth-0.9.3/pam_user_authorized_keys.h b/pam_ssh_agent_auth-0.9.3/pam_user_authorized_keys.h index ae9a4b8..a871bf0 100644 --- a/pam_ssh_agent_auth-0.9.3/pam_user_authorized_keys.h +++ b/pam_ssh_agent_auth-0.9.3/pam_user_authorized_keys.h @@ -32,7 +32,7 @@ #define _PAM_USER_KEY_ALLOWED_H #include "identity.h" -int pam_user_key_allowed(Key *); +int pam_user_key_allowed(const char *, Key *); void parse_authorized_key_file(const char *, const char *); #endif diff --git a/pam_ssh_agent_auth-0.9.3/pam_user_key_allowed2.c b/pam_ssh_agent_auth-0.9.3/pam_user_key_allowed2.c index c6680e4..4aceecb 100644 --- a/pam_ssh_agent_auth-0.9.3/pam_user_key_allowed2.c +++ b/pam_ssh_agent_auth-0.9.3/pam_user_key_allowed2.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,9 +33,14 @@ #include #include +#include +#include #include #include +#include +#include +#include #include "xmalloc.h" #include "ssh.h" @@ -48,87 +53,263 @@ #include "pathnames.h" #include "misc.h" #include "secure_filename.h" +#include "uidswap.h" +#include #include "identity.h" /* return 1 if user allows given key */ /* Modified slightly from original found in auth2-pubkey.c */ +static int +pamsshagentauth_check_authkeys_file(FILE * f, char *file, Key * key) +{ + char line[SSH_MAX_PUBKEY_BYTES]; + int found_key = 0; + u_long linenum = 0; + Key *found; + char *fp; + + found_key = 0; + found = key_new(key->type); + + while(read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { + char *cp = NULL; /* *key_options = NULL; */ + + /* Skip leading whitespace, empty and comment lines. */ + for(cp = line; *cp == ' ' || *cp == '\t'; cp++); + if(!*cp || *cp == '\n' || *cp == '#') + continue; + + if(key_read(found, &cp) != 1) { + /* no key? check if there are options for this key */ + int quoted = 0; + + verbose("user_key_allowed: check options: '%s'", cp); + /* key_options = cp; */ + for(; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { + if(*cp == '\\' && cp[1] == '"') + cp++; /* Skip both */ + else if(*cp == '"') + quoted = !quoted; + } + /* Skip remaining whitespace. */ + for(; *cp == ' ' || *cp == '\t'; cp++); + if(key_read(found, &cp) != 1) { + verbose("user_key_allowed: advance: '%s'", cp); + /* still no key? advance to next line */ + continue; + } + } + if(key_equal(found, key)) { + found_key = 1; + logit("matching key found: file/command %s, line %lu", file, + linenum); + fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); + logit("Found matching %s key: %s", + key_type(found), fp); + free(fp); + break; + } + } + key_free(found); + if(!found_key) + verbose("key not found"); + return found_key; +} + +/* + * Checks whether key is allowed in file. + * returns 1 if the key is allowed or 0 otherwise. + */ int pam_user_key_allowed2(struct passwd *pw, Key *key, char *file) { - char line[SSH_MAX_PUBKEY_BYTES]; - int found_key = 0; - FILE *f; - u_long linenum = 0; - struct stat st; - Key *found; - char *fp; - - verbose("trying public key file %s", file); - - /* Fail not so quietly if file does not exist */ - if (stat(file, &st) < 0) { + FILE *f; + int found_key = 0; + struct stat st; + char buf[SSH_MAX_PUBKEY_BYTES]; + + /* Temporarily use the user's uid. */ + verbose("trying public key file %s", file); + + /* Fail not so quietly if file does not exist */ + if(stat(file, &st) < 0) { verbose("File not found: %s", file); - return 0; - } - /* Open the file containing the authorized keys. */ - f = fopen(file, "r"); - if (!f) { - return 0; - } - if ( - secure_filename(f, file, pw, line, sizeof(line)) != 0) { - fclose(f); - logit("Authentication refused: %s", line); - return 0; - } - - found_key = 0; - found = key_new(key->type); - - while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { - char *cp, *key_options = NULL; - - /* Skip leading whitespace, empty and comment lines. */ - for (cp = line; *cp == ' ' || *cp == '\t'; cp++) - ; - if (!*cp || *cp == '\n' || *cp == '#') - continue; - - if (key_read(found, &cp) != 1) { - /* no key? check if there are options for this key */ - int quoted = 0; - verbose("user_key_allowed: check options: '%s'", cp); - key_options = cp; - for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { - if (*cp == '\\' && cp[1] == '"') - cp++; /* Skip both */ - else if (*cp == '"') - quoted = !quoted; - } - /* Skip remaining whitespace. */ - for (; *cp == ' ' || *cp == '\t'; cp++) - ; - if (key_read(found, &cp) != 1) { - verbose("user_key_allowed: advance: '%s'", cp); - /* still no key? advance to next line*/ - continue; - } - } - if (key_equal(found, key)) { - found_key = 1; - logit("matching key found: file %s, line %lu", - file, linenum); - fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); - logit("Found matching %s key: %s", - key_type(found), fp); - free(fp); - break; - } - } - fclose(f); - key_free(found); - if (!found_key) - verbose("key not found"); - return found_key; + return 0; + } + + /* Open the file containing the authorized keys. */ + f = fopen(file, "r"); + if(!f) { + return 0; + } + + if(secure_filename(f, file, pw, buf, sizeof(buf)) != 0) { + fclose(f); + logit("Authentication refused: %s", buf); + return 0; + } + + found_key = pamsshagentauth_check_authkeys_file(f, file, key); + fclose(f); + return found_key; +} + +/* + * Checks whether key is allowed in output of command. + * returns 1 if the key is allowed or 0 otherwise. + */ +int +pam_user_key_command_allowed2(char *authorized_keys_command, + char *authorized_keys_command_user, + struct passwd *user_pw, Key * key) +{ + FILE *f; + int ok, found_key = 0; + struct passwd *pw; + struct stat st; + int status, devnull, p[2], i; + pid_t pid; + char errmsg[512]; + char username[512] = { 0 }; + + + + if(authorized_keys_command == NULL || authorized_keys_command[0] != '/') + return 0; + + + /* getpwnam of authorized_keys_command_user will overwrite the statics used by getpwnam (including pw_name) */ + strncpy(username, user_pw->pw_name, sizeof(username) - 1); + + /* If no user specified to run commands the default to target user */ + if(authorized_keys_command_user == NULL) { + pw = user_pw; + } + else { + pw = getpwnam(authorized_keys_command_user); + if(pw == NULL) { + error("authorized_keys_command_user \"%s\" not found: %s", + authorized_keys_command_user, strerror(errno)); + return 0; + } + } + + temporarily_use_uid(pw); + + if(stat(authorized_keys_command, &st) < 0) { + error + ("Could not stat AuthorizedKeysCommand \"%s\": %s", + authorized_keys_command, strerror(errno)); + goto out; + } + if(pamsshagentauth_auth_secure_path + (authorized_keys_command, &st, NULL, 0, errmsg, sizeof(errmsg)) != 0) { + error("Unsafe AuthorizedKeysCommand: %s", errmsg); + goto out; + } + + /* open the pipe and read the keys */ + if(pipe(p) != 0) { + error("%s: pipe: %s", __func__, strerror(errno)); + goto out; + } + + debug("Running AuthorizedKeysCommand: \"%s\" as \"%s\" with argument: \"%s\"", + authorized_keys_command, pw->pw_name, username); + + /* + * Don't want to call this in the child, where it can fatal() and + * run cleanup_exit() code. + */ + restore_uid(); + + switch ((pid = fork())) { + case -1: /* error */ + error("%s: fork: %s", __func__, strerror(errno)); + close(p[0]); + close(p[1]); + return 0; + case 0: /* child */ + for(i = 0; i < NSIG; i++) + signal(i, SIG_DFL); + + /* do this before the setresuid so thta they can be logged */ + if((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { + error("%s: open %s: %s", __func__, _PATH_DEVNULL, + strerror(errno)); + _exit(1); + } + if(dup2(devnull, STDIN_FILENO) == -1 || dup2(p[1], STDOUT_FILENO) == -1 + || dup2(devnull, STDERR_FILENO) == -1) { + error("%s: dup2: %s", __func__, strerror(errno)); + _exit(1); + } +#if defined(HAVE_SETRESGID) && !defined(BROKEN_SETRESGID) + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { +#else + if (setgid(pw->pw_gid) != 0 || setegid(pw->pw_gid) != 0) { +#endif + error("setresgid %u: %s", (u_int) pw->pw_gid, + strerror(errno)); + _exit(1); + } + +#ifdef HAVE_SETRESUID + if(setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) { +#else + if (setuid(pw->pw_uid) != 0 || seteuid(pw->pw_uid) != 0) { +#endif + error("setresuid %u: %s", (u_int) pw->pw_uid, + strerror(errno)); + _exit(1); + } + + close(p[0]); + closefrom(STDERR_FILENO + 1); + + execl(authorized_keys_command, authorized_keys_command, username, (char *)NULL); + + /* pretty sure this will barf because we are now suid, but since we + should't reach this anyway, I'll leave it here */ + error("AuthorizedKeysCommand %s exec failed: %s", + authorized_keys_command, strerror(errno)); + _exit(127); + default: /* parent */ + break; + } + + temporarily_use_uid(pw); + + close(p[1]); + if((f = fdopen(p[0], "r")) == NULL) { + error("%s: fdopen: %s", __func__, strerror(errno)); + close(p[0]); + /* Don't leave zombie child */ + while(waitpid(pid, NULL, 0) == -1 && errno == EINTR); + goto out; + } + ok = pamsshagentauth_check_authkeys_file(f, authorized_keys_command, key); + fclose(f); + + while(waitpid(pid, &status, 0) == -1) { + if(errno != EINTR) { + error("%s: waitpid: %s", __func__, + strerror(errno)); + goto out; + } + } + if(WIFSIGNALED(status)) { + error("AuthorizedKeysCommand %s exited on signal %d", + authorized_keys_command, WTERMSIG(status)); + goto out; + } else if(WEXITSTATUS(status) != 0) { + error("AuthorizedKeysCommand %s returned status %d", + authorized_keys_command, WEXITSTATUS(status)); + goto out; + } + found_key = ok; + out: + restore_uid(); + return found_key; } diff --git a/pam_ssh_agent_auth-0.9.3/pam_user_key_allowed2.h b/pam_ssh_agent_auth-0.9.3/pam_user_key_allowed2.h index 416d055..24533a0 100644 --- a/pam_ssh_agent_auth-0.9.3/pam_user_key_allowed2.h +++ b/pam_ssh_agent_auth-0.9.3/pam_user_key_allowed2.h @@ -32,5 +32,6 @@ #include "identity.h" int pam_user_key_allowed2(struct passwd *, Key *, char *); +int pam_user_key_command_allowed2(char *, char *, struct passwd *, Key *); #endif diff --git a/pam_ssh_agent_auth-0.9.3/secure_filename.c b/pam_ssh_agent_auth-0.9.3/secure_filename.c index c24cab2..d685599 100644 --- a/pam_ssh_agent_auth-0.9.3/secure_filename.c +++ b/pam_ssh_agent_auth-0.9.3/secure_filename.c @@ -71,29 +71,38 @@ * Returns 0 on success and -1 on failure */ int -secure_filename(FILE *f, const char *file, struct passwd *pw, - char *err, size_t errlen) +pamsshagentauth_auth_secure_path(const char *name, struct stat *stp, + const char *pw_dir, uid_t uid, char *err, size_t errlen) { - uid_t uid = pw->pw_uid; char buf[MAXPATHLEN], homedir[MAXPATHLEN]; char *cp; int comparehome = 0; struct stat st; - verbose("secure_filename: checking for uid: %u", uid); + verbose("auth_secure_filename: checking for uid: %u", uid); - if (realpath(file, buf) == NULL) { - snprintf(err, errlen, "realpath %s failed: %s", file, + /* if (realpath(pw->pw_dir, homedir) != NULL) */ + if (realpath(name, buf) == NULL) { + snprintf(err, errlen, "realpath %s failed: %s", name, strerror(errno)); return -1; } - if (realpath(pw->pw_dir, homedir) != NULL) + if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL) comparehome = 1; /* check the open file to avoid races */ - if (fstat(fileno(f), &st) < 0 || - (st.st_uid != 0 && st.st_uid != uid) || - (st.st_mode & 022) != 0) { + /* + * if (fstat(fileno(f), &st) < 0 || + * (st.st_uid != 0 && st.st_uid != uid) || + * (st.st_mode & 022) != 0) { + */ + if (!S_ISREG(stp->st_mode)) { + snprintf(err, errlen, "%s is not a regular file", buf); + return -1; + } + + if ((stp->st_uid != 0 && stp->st_uid != uid) || + (stp->st_mode & 022) != 0) { snprintf(err, errlen, "bad ownership or modes for file %s", buf); return -1; @@ -132,3 +141,25 @@ secure_filename(FILE *f, const char *file, struct passwd *pw, return 0; } +/* + * Version of secure_path() that accepts an open file descriptor to + * avoid races. + * + * Returns 0 on success and -1 on failure + */ +int +secure_filename(FILE *f, const char *file, struct passwd *pw, + char *err, size_t errlen) +{ + struct stat st; + char buf[MAXPATHLEN] = { 0 }; + + /* check the open file to avoid races */ + if (fstat(fileno(f), &st) < 0) { + snprintf(err, errlen, "cannot stat file %s: %s", + buf, strerror(errno)); + return -1; + } + return pamsshagentauth_auth_secure_path(file, &st, pw->pw_dir, pw->pw_uid, err, errlen); +} + diff --git a/pam_ssh_agent_auth-0.9.3/secure_filename.h b/pam_ssh_agent_auth-0.9.3/secure_filename.h index 198c13d..4c1a208 100644 --- a/pam_ssh_agent_auth-0.9.3/secure_filename.h +++ b/pam_ssh_agent_auth-0.9.3/secure_filename.h @@ -28,5 +28,7 @@ #define _SECURE_FILENAME_H #include #include +struct stat; int secure_filename(FILE *, const char *, struct passwd *, char *, size_t); +int pamsshagentauth_auth_secure_path(const char *, struct stat *, const char *, uid_t, char *, size_t); #endif diff --git a/pam_ssh_agent_auth-0.9.3/userauth_pubkey_from_id.c b/pam_ssh_agent_auth-0.9.3/userauth_pubkey_from_id.c index 163278b..31849f8 100644 --- a/pam_ssh_agent_auth-0.9.3/userauth_pubkey_from_id.c +++ b/pam_ssh_agent_auth-0.9.3/userauth_pubkey_from_id.c @@ -48,11 +48,14 @@ #include "identity.h" #include "pam_user_authorized_keys.h" -extern u_char *session_id2; +#define SSH2_MSG_USERAUTH_TRUST_REQUEST 54 + +/* extern u_char *session_id2; extern uint8_t session_id_len; + */ int -userauth_pubkey_from_id(Identity * id) +userauth_pubkey_from_id(const char *ruser, Identity * id, Buffer * session_id2) { Buffer b = { 0 }; char *pkalg = NULL; @@ -63,7 +66,7 @@ userauth_pubkey_from_id(Identity * id) pkalg = (char *) key_ssh_name(id->key); /* first test if this key is even allowed */ - if(! pam_user_key_allowed(id->key)) + if(! pam_user_key_allowed(ruser, id->key)) goto user_auth_clean_exit; if(key_to_blob(id->key, &pkblob, &blen) == 0) @@ -72,10 +75,10 @@ userauth_pubkey_from_id(Identity * id) /* construct packet to sign and test */ buffer_init(&b); - buffer_put_string(&b, session_id2, session_id_len); - buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); - buffer_put_cstring(&b, "root"); - buffer_put_cstring(&b, "ssh-userauth"); + buffer_put_string(&b, session_id2->buf + session_id2->offset, session_id2->end - session_id2->offset); + buffer_put_char(&b, SSH2_MSG_USERAUTH_TRUST_REQUEST); + buffer_put_cstring(&b, ruser); + buffer_put_cstring(&b, "pam_ssh_agent_auth"); buffer_put_cstring(&b, "publickey"); buffer_put_char(&b, 1); buffer_put_cstring(&b, pkalg); @@ -89,8 +92,7 @@ userauth_pubkey_from_id(Identity * id) authenticated = 1; user_auth_clean_exit: - if(&b != NULL) - buffer_free(&b); + buffer_free(&b); if(sig != NULL) free(sig); if(pkblob != NULL) diff --git a/pam_ssh_agent_auth-0.9.3/userauth_pubkey_from_id.h b/pam_ssh_agent_auth-0.9.3/userauth_pubkey_from_id.h index 1e14231..7758808 100644 --- a/pam_ssh_agent_auth-0.9.3/userauth_pubkey_from_id.h +++ b/pam_ssh_agent_auth-0.9.3/userauth_pubkey_from_id.h @@ -32,6 +32,6 @@ #define _USERAUTH_PUBKEY_FROM_ID_H #include -int userauth_pubkey_from_id(Identity *); +int userauth_pubkey_from_id(const char *, Identity *, Buffer *); #endif -- 2.5.0 --- openssh-6.6p1/pam_ssh_agent_auth-0.9.3/pam_user_authorized_keys.c.psaa-command 2016-04-20 09:31:32.164686370 +0200 +++ openssh-6.6p1/pam_ssh_agent_auth-0.9.3/pam_user_authorized_keys.c 2016-04-20 09:35:49.778344576 +0200 @@ -145,8 +145,12 @@ int pam_user_key_allowed(const char *ruser, Key * key) { - return pam_user_key_allowed2(getpwuid(authorized_keys_file_allowed_owner_uid), key, authorized_keys_file) - || pam_user_key_allowed2(getpwuid(0), key, authorized_keys_file) + struct passwd *pw; + return + ((pw = getpwuid(authorized_keys_file_allowed_owner_uid)) && + pam_user_key_allowed2(pw, key, authorized_keys_file)) + || ((pw = getpwuid(0)) && + pam_user_key_allowed2(pw, key, authorized_keys_file)) || pam_user_key_command_allowed2(authorized_keys_command, authorized_keys_command_user, getpwnam(ruser), key);