summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErik Trauschke <Erik.Trauschke@Sun.COM>2009-12-04 13:53:45 -0800
committerErik Trauschke <Erik.Trauschke@Sun.COM>2009-12-04 13:53:45 -0800
commitd8a94255794826f3bfbe8bc4d12e3d19e711a0ac (patch)
treef8565b583f7e6ae9715895bcd2be30c9196a709c
parent095007817cff0953c8b7143880f19adf849c5103 (diff)
downloadillumos-joyent-d8a94255794826f3bfbe8bc4d12e3d19e711a0ac.tar.gz
PSARC 2009/449 sshd PreUserAuthHook
6850175 Extension of SSHD to run an executable prior to user authentication
-rw-r--r--usr/src/cmd/ssh/include/auth.h6
-rw-r--r--usr/src/cmd/ssh/include/servconf.h1
-rw-r--r--usr/src/cmd/ssh/include/session.h6
-rw-r--r--usr/src/cmd/ssh/sshd/auth.c140
-rw-r--r--usr/src/cmd/ssh/sshd/auth2.c18
-rw-r--r--usr/src/cmd/ssh/sshd/servconf.c8
-rw-r--r--usr/src/cmd/ssh/sshd/session.c10
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