diff options
-rw-r--r-- | usr/src/cmd/ssh/include/auth.h | 6 | ||||
-rw-r--r-- | usr/src/cmd/ssh/include/servconf.h | 1 | ||||
-rw-r--r-- | usr/src/cmd/ssh/include/session.h | 6 | ||||
-rw-r--r-- | usr/src/cmd/ssh/sshd/auth.c | 140 | ||||
-rw-r--r-- | usr/src/cmd/ssh/sshd/auth2.c | 18 | ||||
-rw-r--r-- | usr/src/cmd/ssh/sshd/servconf.c | 8 | ||||
-rw-r--r-- | usr/src/cmd/ssh/sshd/session.c | 10 |
7 files changed, 178 insertions, 11 deletions
diff --git a/usr/src/cmd/ssh/include/auth.h b/usr/src/cmd/ssh/include/auth.h index d2bd9815b5..c932fafa6d 100644 --- a/usr/src/cmd/ssh/include/auth.h +++ b/usr/src/cmd/ssh/include/auth.h @@ -3,8 +3,6 @@ #ifndef _AUTH_H #define _AUTH_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -35,7 +33,7 @@ extern "C" { * */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -284,6 +282,8 @@ int skey_respond(void *, u_int, char **); struct passwd * getpwnamallow(const char *user); +int run_auth_hook(const char *, const char *, const char *); + char *get_challenge(Authctxt *); int verify_response(Authctxt *, const char *); diff --git a/usr/src/cmd/ssh/include/servconf.h b/usr/src/cmd/ssh/include/servconf.h index 1058d00b47..d6458ab619 100644 --- a/usr/src/cmd/ssh/include/servconf.h +++ b/usr/src/cmd/ssh/include/servconf.h @@ -163,6 +163,7 @@ typedef struct { int lookup_client_hostnames; int use_openssl_engine; char *chroot_directory; + char *pre_userauth_hook; } ServerOptions; diff --git a/usr/src/cmd/ssh/include/session.h b/usr/src/cmd/ssh/include/session.h index c1b5ca8ed5..0d81189842 100644 --- a/usr/src/cmd/ssh/include/session.h +++ b/usr/src/cmd/ssh/include/session.h @@ -22,15 +22,13 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SESSION_H #define _SESSION_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -83,6 +81,8 @@ void session_close(Session *); void do_setusercontext(struct passwd *); void child_set_env(char ***envp, u_int *envsizep, const char *name, const char *value); +void child_set_env_silent(char ***envp, u_int *envsizep, const char *name, + const char *value); #ifdef __cplusplus diff --git a/usr/src/cmd/ssh/sshd/auth.c b/usr/src/cmd/ssh/sshd/auth.c index 9df2117ea8..64e6959ecf 100644 --- a/usr/src/cmd/ssh/sshd/auth.c +++ b/usr/src/cmd/ssh/sshd/auth.c @@ -55,6 +55,8 @@ RCSID("$OpenBSD: auth.c,v 1.45 2002/09/20 18:41:29 stevesk Exp $"); #include "misc.h" #include "bufaux.h" #include "packet.h" +#include "channels.h" +#include "session.h" #ifdef HAVE_BSM #include "bsmaudit.h" @@ -613,6 +615,144 @@ getpwnamallow(const char *user) return (NULL); } + +/* + * The fatal_cleanup method to kill the hook. Since hook has been put into + * new process group all descendants will be killed as well. + */ +static void +kill_hook(void *arg) +{ + pid_t pid; + + pid = *(pid_t*)arg; + debug("killing hook and all it's children, process group: %ld", pid); + xfree(arg); + (void)killpg(pid, SIGTERM); +} + +/* + * Runs the PreUserauthHook. + * Returns -1 on execution error or the exit code of the hook if execution is + * successful. + */ +int +run_auth_hook(const char *path, const char *user, const char *method) +{ + struct stat st; + int i, status, ret = 1; + u_int envsize, argsize; + char buf[256]; + char **env, **args; + pid_t pid, *ppid; + + if (path == NULL || user == NULL || method == NULL) { + return (-1); + } + + /* Initialize the environment/arguments for the hook. */ + envsize = 4; /* 3 env vars + EndOfList marker */ + argsize = 4; /* 2 args + exe name + EndOfList marker */ + env = xmalloc(envsize * sizeof (char *)); + args = xmalloc(argsize * sizeof (char *)); + env[0] = NULL; + + /* we use the SSH env handling scheme */ + child_set_env_silent(&env, &envsize, "PATH", "/usr/bin:/bin"); + child_set_env_silent(&env, &envsize, "IFS", " \t\n"); + + (void) snprintf(buf, sizeof (buf), "%.50s %d %.50s %d", + get_remote_ipaddr(), get_remote_port(), + get_local_ipaddr(packet_get_connection_in()), get_local_port()); + child_set_env_silent(&env, &envsize, "SSH_CONNECTION", buf); + + args[0] = xstrdup(path); + args[1] = xstrdup(method); + args[2] = xstrdup(user); + args[3] = NULL; + + /* + * sanity checks + * note: the checks do not make sure that the file checked is actually + * the same which is executed. However, in this case it shouldn't be a + * major issue since the hook is rather static and the worst case would + * be an uncorrect message in the log or a hook is run even though the + * permissions are not right. + */ + + /* check if script does exist */ + if (stat(path, &st) < 0) { + log("Error executing PreUserauthHook \"%s\": %s", path, + strerror(errno)); + goto cleanup; + } + + /* Check correct permissions for script (uid of SSHD, mode 500) */ + if (st.st_uid != getuid() || ((st.st_mode & 0777) != 0500)) { + log("PreUserauthHook has invalid permissions (should be 500, is" + " %o) or ownership (should be %d, is %d)", + (uint) st.st_mode & 0777, getuid(), st.st_uid); + goto cleanup; + } + + if ((pid = fork()) == 0) { + /* + * We put the hook and all its (possible) descendants into + * a new process group so that in case of a hanging hook + * we can wipe out the whole "family". + */ + if (setpgid(0, 0) != 0) { + log("setpgid: %s", strerror(errno)); + _exit(255); + } + (void) execve(path, args, env); + /* child is gone so we shouldn't get here */ + log("Error executing PreUserauthHook \"%s\": %s", path, + strerror(errno)); + _exit(255); + } else if (pid == -1) { + log("Error executing PreUserauthHook \"%s\": %s", path, + strerror(errno)); + goto cleanup; + } + + /* make preparations to kill hook if it is hanging */ + ppid = xmalloc(sizeof (pid_t)); + *ppid = pid; + fatal_add_cleanup((void (*)(void *))kill_hook, (void *) ppid); + + if (waitpid(pid, &status, 0) == -1) { + log("Error executing PreUserauthHook \"%s\": %s", path, + strerror(errno)); + goto cleanup; + } + + ret = WEXITSTATUS(status); + + if (ret == 255) { + ret = -1; /* execve() failed, error msg already logged */ + } else if (ret != 0) { + log("PreUserauthHook \"%s\" failed with exit code %d", + path, ret); + } else { + debug("PreUserauthHook \"%s\" finished successfully", path); + } + +cleanup: + for (i = 0; args[i] != NULL; i++) { + xfree(args[i]); + } + for (i = 0; env[i] != NULL; i++) { + xfree(env[i]); + } + xfree(args); + xfree(env); + + fatal_remove_cleanup((void (*)(void *))kill_hook, (void *) ppid); + + return (ret); +} + void auth_debug_add(const char *fmt,...) { diff --git a/usr/src/cmd/ssh/sshd/auth2.c b/usr/src/cmd/ssh/sshd/auth2.c index ecad72a39e..b86b36457a 100644 --- a/usr/src/cmd/ssh/sshd/auth2.c +++ b/usr/src/cmd/ssh/sshd/auth2.c @@ -22,7 +22,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -164,6 +164,7 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt) Authctxt *authctxt = ctxt; Authmethod *m = NULL; char *user, *service, *method, *style = NULL; + int valid_attempt; if (authctxt == NULL) fatal("input_userauth_request: no authctxt"); @@ -186,13 +187,22 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt) if (m != NULL && m->is_initial) authctxt->init_attempt++; + if (options.pre_userauth_hook != NULL && + run_auth_hook(options.pre_userauth_hook, user, m->name) != 0) { + valid_attempt = 0; + } else { + valid_attempt = 1; + } + if (authctxt->attempt == 1) { /* setup auth context */ authctxt->pw = getpwnamallow(user); /* May want to abstract SSHv2 services someday */ if (authctxt->pw && strcmp(service, "ssh-connection")==0) { /* enforced in userauth_finish() below */ - authctxt->valid = 1; + if (valid_attempt) { + authctxt->valid = 1; + } debug2("input_userauth_request: setting up authctxt for %s", user); } else { log("input_userauth_request: illegal user %s", user); @@ -323,7 +333,9 @@ userauth_finish(Authctxt *authctxt, char *method) done_checking: if (!authctxt->valid && authenticated) { /* - * Should never happen -- if it does PAM's at fault + * We get here if the PreUserauthHook fails but the + * user is otherwise valid. + * An error in the PAM handling could also get us here * but we need not panic, just treat as a failure. */ authctxt->method->authenticated = 0; diff --git a/usr/src/cmd/ssh/sshd/servconf.c b/usr/src/cmd/ssh/sshd/servconf.c index 9a66f49216..aa923ce57c 100644 --- a/usr/src/cmd/ssh/sshd/servconf.c +++ b/usr/src/cmd/ssh/sshd/servconf.c @@ -154,6 +154,7 @@ initialize_server_options(ServerOptions *options) options->lookup_client_hostnames = -1; options->use_openssl_engine = -1; options->chroot_directory = NULL; + options->pre_userauth_hook = NULL; } #ifdef HAVE_DEFOPEN @@ -420,7 +421,7 @@ typedef enum { sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, sMaxAuthTries, sMaxAuthTriesLog, sUsePrivilegeSeparation, sLookupClientHostnames, sUseOpenSSLEngine, sChrootDirectory, - sMatch, + sPreUserauthHook, sMatch, sDeprecated } ServerOpCodes; @@ -522,6 +523,7 @@ static struct { { "lookupclienthostnames", sLookupClientHostnames, SSHCFG_GLOBAL }, { "useopensslengine", sUseOpenSSLEngine, SSHCFG_GLOBAL }, { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, + { "preuserauthhook", sPreUserauthHook, SSHCFG_ALL}, { "match", sMatch, SSHCFG_ALL }, { NULL, sBadOption, 0 } @@ -1298,6 +1300,10 @@ parse_flag: *charptr = xstrdup(arg); break; + case sPreUserauthHook: + charptr = &options->pre_userauth_hook; + goto parse_filename; + case sMatch: if (cmdline) fatal("Match directive not supported as a command-line " diff --git a/usr/src/cmd/ssh/sshd/session.c b/usr/src/cmd/ssh/sshd/session.c index a546880398..91db243f0c 100644 --- a/usr/src/cmd/ssh/sshd/session.c +++ b/usr/src/cmd/ssh/sshd/session.c @@ -799,10 +799,18 @@ void child_set_env(char ***envp, u_int *envsizep, const char *name, const char *value) { + debug3("child_set_env(%s, %s)", name, value); + child_set_env_silent(envp, envsizep, name, value); +} + + +void +child_set_env_silent(char ***envp, u_int *envsizep, const char *name, + const char *value) +{ u_int i, namelen; char **env; - debug3("child_set_env(%s, %s)", name, value); /* * Find the slot where the value should be stored. If the variable * already exists, we reuse the slot; otherwise we append a new slot |