From 75be3237306e8ac92a419e0ef78f3b0edbd4c816 Mon Sep 17 00:00:00 2001 From: jbasney Date: Fri, 25 Apr 2003 18:18:04 +0000 Subject: [PATCH] session hook patch from Olle Mulmo: http://www-unix.globus.org/mail_archive/gsi-openssh/2003/04/msg00003.html --- openssh/acconfig.h | 3 + openssh/auth.h | 3 + openssh/configure.ac | 5 ++ openssh/misc.c | 62 +++++++++++++---- openssh/servconf.c | 38 +++++++++- openssh/servconf.h | 5 ++ openssh/session.c | 160 +++++++++++++++++++++++++++++++++++++++++++ openssh/sshd_config | 106 ++++++++++++++++------------ 8 files changed, 324 insertions(+), 58 deletions(-) diff --git a/openssh/acconfig.h b/openssh/acconfig.h index b805575..68c393f 100644 --- a/openssh/acconfig.h +++ b/openssh/acconfig.h @@ -226,6 +226,9 @@ /* Define if you want GSI/Globus authentication support */ #undef GSI +/* Define this if you want support for startup/shutdown hooks */ +#undef SESSION_HOOKS + /* Define if you want S/Key support */ #undef SKEY diff --git a/openssh/auth.h b/openssh/auth.h index 8e63132..2e351d6 100644 --- a/openssh/auth.h +++ b/openssh/auth.h @@ -69,6 +69,9 @@ struct Authctxt { krb5_ccache krb5_fwd_ccache; krb5_principal krb5_user; char *krb5_ticket_file; +#endif +#ifdef SESSION_HOOKS + char *session_env_file; #endif void *methoddata; }; diff --git a/openssh/configure.ac b/openssh/configure.ac index 7214d4d..a6abd94 100644 --- a/openssh/configure.ac +++ b/openssh/configure.ac @@ -2109,6 +2109,11 @@ AC_ARG_WITH(afs, ) LIBS="$LIBS $KLIBS $K5LIBS" +AC_ARG_WITH(session-hooks, + [ --with-session-hooks Enable hooks for executing external commands before/after a session], + [ AC_DEFINE(SESSION_HOOKS) ] +) + # Looking for programs, paths and files PRIVSEP_PATH=/var/empty diff --git a/openssh/misc.c b/openssh/misc.c index 620121f..eac6ccd 100644 --- a/openssh/misc.c +++ b/openssh/misc.c @@ -1,5 +1,3 @@ -/* $OpenBSD: misc.c,v 1.12 2001/06/26 17:27:24 markus Exp $ */ - /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -25,7 +23,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: misc.c,v 1.12 2001/06/26 17:27:24 markus Exp $"); +RCSID("$OpenBSD: misc.c,v 1.20 2002/12/13 10:03:15 markus Exp $"); #include "misc.h" #include "log.h" @@ -37,7 +35,7 @@ chop(char *s) { char *t = s; while (*t) { - if(*t == '\n' || *t == '\r') { + if (*t == '\n' || *t == '\r') { *t = '\0'; return s; } @@ -65,9 +63,8 @@ set_nonblock(int fd) debug("fd %d setting O_NONBLOCK", fd); val |= O_NONBLOCK; if (fcntl(fd, F_SETFL, val) == -1) - if (errno != ENODEV) - error("fcntl(%d, F_SETFL, O_NONBLOCK): %s", - fd, strerror(errno)); + debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", + fd, strerror(errno)); } void @@ -87,19 +84,43 @@ unset_nonblock(int fd) debug("fd %d clearing O_NONBLOCK", fd); val &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, val) == -1) - if (errno != ENODEV) - error("fcntl(%d, F_SETFL, O_NONBLOCK): %s", - fd, strerror(errno)); + debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", + fd, strerror(errno)); +} + +/* disable nagle on socket */ +void +set_nodelay(int fd) +{ + int opt; + socklen_t optlen; + + optlen = sizeof opt; + if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) { + error("getsockopt TCP_NODELAY: %.100s", strerror(errno)); + return; + } + if (opt == 1) { + debug2("fd %d is TCP_NODELAY", fd); + return; + } + opt = 1; + debug2("fd %d setting TCP_NODELAY", fd); + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) + error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); } /* Characters considered whitespace in strsep calls. */ #define WHITESPACE " \t\r\n" +/* Characters considered as quotations. */ +#define QUOTES "'\"" + /* return next token in configuration line */ char * strdelim(char **s) { - char *old; + char *old, *p, *q; int wspace = 0; if (*s == NULL) @@ -107,7 +128,22 @@ strdelim(char **s) old = *s; - *s = strpbrk(*s, WHITESPACE "="); + if ((q=strchr(QUOTES, (int) *old)) && *q) + { + /* find next quote character, point old to start of quoted + * string */ + for (p = ++old;*p && *p!=*q; p++) + ; + + /* find start of next token */ + *s = (*p) ? p + strspn(p + 1, WHITESPACE) + 1 : NULL; + + /* terminate 'old' token */ + *p = '\0'; + return (old); + } + + *s = strpbrk(*s, WHITESPACE "="); if (*s == NULL) return (old); @@ -298,7 +334,7 @@ addargs(arglist *args, char *fmt, ...) if (args->list == NULL) { args->nalloc = 32; args->num = 0; - } else if (args->num+2 >= args->nalloc) + } else if (args->num+2 >= args->nalloc) args->nalloc *= 2; args->list = xrealloc(args->list, args->nalloc * sizeof(char *)); diff --git a/openssh/servconf.c b/openssh/servconf.c index 7aa0c73..12b7b83 100644 --- a/openssh/servconf.c +++ b/openssh/servconf.c @@ -101,6 +101,11 @@ initialize_server_options(ServerOptions *options) #endif #ifdef AFS options->afs_token_passing = -1; +#endif +#ifdef SESSION_HOOKS + options->session_hooks_allow = -1; + options->session_hooks_startup_cmd = NULL; + options->session_hooks_shutdown_cmd = NULL; #endif options->password_authentication = -1; options->kbd_interactive_authentication = -1; @@ -230,6 +235,14 @@ fill_default_server_options(ServerOptions *options) #ifdef AFS if (options->afs_token_passing == -1) options->afs_token_passing = 0; +#endif +#ifdef SESSION_HOOKS + if (options->session_hooks_allow == -1) + { + options->session_hooks_allow = 0; + options->session_hooks_startup_cmd = NULL; + options->session_hooks_shutdown_cmd = NULL; + } #endif if (options->password_authentication == -1) options->password_authentication = 1; @@ -306,6 +319,9 @@ typedef enum { #endif #ifdef AFS sAFSTokenPassing, +#endif +#ifdef SESSION_HOOKS + sAllowSessionHooks, sSessionHookStartupCmd, sSessionHookShutdownCmd, #endif sChallengeResponseAuthentication, sPasswordAuthentication, sKbdInteractiveAuthentication, sListenAddress, @@ -366,6 +382,11 @@ static struct { #ifdef AFS { "afstokenpassing", sAFSTokenPassing }, #endif +#ifdef SESSION_HOOKS + { "allowsessionhooks", sAllowSessionHooks }, + { "sessionhookstartupcmd", sSessionHookStartupCmd }, + { "sessionhookshutdowncmd", sSessionHookShutdownCmd }, +#endif { "passwordauthentication", sPasswordAuthentication }, { "kbdinteractiveauthentication", sKbdInteractiveAuthentication }, { "challengeresponseauthentication", sChallengeResponseAuthentication }, @@ -707,7 +728,22 @@ parse_flag: intptr = &options->afs_token_passing; goto parse_flag; #endif - +#ifdef SESSION_HOOKS + case sAllowSessionHooks: + intptr = &options->session_hooks_allow; + goto parse_flag; + case sSessionHookStartupCmd: + case sSessionHookShutdownCmd: + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: empty session hook command", + filename, linenum); + if (opcode==sSessionHookStartupCmd) + options->session_hooks_startup_cmd = strdup(arg); + else + options->session_hooks_shutdown_cmd = strdup(arg); + break; +#endif case sPasswordAuthentication: intptr = &options->password_authentication; goto parse_flag; diff --git a/openssh/servconf.h b/openssh/servconf.h index 1c947da..98cdda4 100644 --- a/openssh/servconf.h +++ b/openssh/servconf.h @@ -97,6 +97,11 @@ typedef struct { #endif #ifdef AFS int afs_token_passing; /* If true, permit AFS token passing. */ +#endif +#ifdef SESSION_HOOKS + int session_hooks_allow; /* If true, permit user hooks */ + char* session_hooks_startup_cmd; /* cmd to be executed before */ + char* session_hooks_shutdown_cmd; /* cmd to be executed after */ #endif int password_authentication; /* If true, permit password * authentication. */ diff --git a/openssh/session.c b/openssh/session.c index e697e08..97c0c0a 100644 --- a/openssh/session.c +++ b/openssh/session.c @@ -91,6 +91,11 @@ static void do_authenticated2(Authctxt *); static int session_pty_req(Session *); +#ifdef SESSION_HOOKS +static void execute_session_hook(char* prog, Authctxt *authctxt, + int startup, int save); +#endif + /* import */ extern ServerOptions options; extern char *__progname; @@ -229,6 +234,21 @@ do_authenticated(Authctxt *authctxt) /* remove agent socket */ if (auth_sock_name != NULL) auth_sock_cleanup_proc(authctxt->pw); + +#ifdef SESSION_HOOKS + if (options.session_hooks_allow && + options.session_hooks_shutdown_cmd) + { + execute_session_hook(options.session_hooks_shutdown_cmd, + authctxt, + /* startup = */ 0, /* save = */ 0); + + if (authctxt->session_env_file) + { + free(authctxt->session_env_file); + } + } +#endif #ifdef KRB4 if (options.kerberos_ticket_cleanup) krb4_cleanup_proc(authctxt); @@ -718,6 +738,25 @@ do_pre_login(Session *s) void do_exec(Session *s, const char *command) { +#if defined(SESSION_HOOKS) + if (options.session_hooks_allow && + (options.session_hooks_startup_cmd || + options.session_hooks_shutdown_cmd)) + { + char env_file[1000]; + struct stat st; + do + { + snprintf(env_file, + sizeof(env_file), + "/tmp/ssh_env_%d%d%d", + getuid(), + getpid(), + rand()); + } while (stat(env_file, &st)==0); + s->authctxt->session_env_file = strdup(env_file); + } +#endif if (forced_command) { original_command = command; command = forced_command; @@ -939,6 +978,117 @@ read_environment_file(char ***env, u_int *envsize, fclose(f); } +#ifdef SESSION_HOOKS +#define SSH_SESSION_ENV_FILE "SSH_SESSION_ENV_FILE" + +typedef enum { no_op, execute, clear_env, restore_env, + read_env, save_or_rm_env } session_action_t; + +static session_action_t action_order[2][5] = { + { clear_env, read_env, execute, save_or_rm_env, restore_env }, /*shutdown*/ + { execute, read_env, save_or_rm_env, no_op, no_op } /*startup */ +}; + +static +void execute_session_hook(char* prog, Authctxt *authctxt, + int startup, int save) +{ + extern char **environ; + + struct stat st; + char **saved_env, **tmpenv; + char *env_file = authctxt->session_env_file; + int i, status = 0; + + for (i=0; i<5; i++) + { + switch (action_order[startup][i]) + { + case no_op: + break; + + case execute: + { + FILE* fp; + char buf[1000]; + + snprintf(buf, + sizeof(buf), + "%s -c '%s'", + authctxt->pw->pw_shell, + prog); + + debug("executing session hook: [%s]", buf); + setenv(SSH_SESSION_ENV_FILE, env_file, /* overwrite = */ 1); + + /* flusing is recommended in the popen(3) man page, to avoid + intermingling of output */ + fflush(stdout); + fflush(stderr); + if ((fp=popen(buf, "w")) == NULL) + { + perror("Unable to run session hook"); + return; + } + status = pclose(fp); + debug2("session hook executed, status=%d", status); + unsetenv(SSH_SESSION_ENV_FILE); + } + break; + + case clear_env: + saved_env = environ; + tmpenv = (char**) malloc(sizeof(char*)); + tmpenv[0] = NULL; + environ = tmpenv; + break; + + case restore_env: + environ = saved_env; + free(tmpenv); + break; + + case read_env: + if (status==0 && stat(env_file, &st)==0) + { + int envsize = 0; + + debug("reading environment from %s", env_file); + while (environ[envsize++]) ; + read_environment_file(&environ, &envsize, env_file); + } + break; + + case save_or_rm_env: + if (status==0 && save) + { + FILE* fp; + int envcount=0; + + debug2("saving environment to %s", env_file); + if ((fp = fopen(env_file, "w")) == NULL) /* hmm: file perms? */ + { + perror("Unable to save session hook info"); + } + while (environ[envcount]) + { + fprintf(fp, "%s\n", environ[envcount++]); + } + fflush(fp); + fclose(fp); + } + else if (stat(env_file, &st)==0) + { + debug2("removing environment file %s", env_file); + remove(env_file); + } + break; + } + } + +} +#endif + void copy_environment(char **source, char ***env, u_int *envsize) { char *var_name, *var_val; @@ -1450,6 +1600,16 @@ do_child(Session *s, const char *command) } #endif /* AFS_KRB5 */ +#ifdef SESSION_HOOKS + if (options.session_hooks_allow && + options.session_hooks_startup_cmd) + { + execute_session_hook(options.session_hooks_startup_cmd, + s->authctxt, + /* startup = */ 1, + options.session_hooks_shutdown_cmd != NULL); + } +#endif #ifdef AFS /* Try to get AFS tokens for the local cell. */ if (k_hasafs()) { diff --git a/openssh/sshd_config b/openssh/sshd_config index e1a052a..b9dda87 100644 --- a/openssh/sshd_config +++ b/openssh/sshd_config @@ -1,80 +1,98 @@ -# $OpenBSD: sshd_config,v 1.42 2001/09/20 20:57:51 mouring Exp $ +# $OpenBSD: sshd_config,v 1.59 2002/09/25 11:17:16 markus Exp $ + +# This is the sshd server system-wide configuration file. See +# sshd_config(5) for more information. # This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin -# This is the sshd server system-wide configuration file. See sshd(8) -# for more information. +# The strategy used for options in the default sshd_config shipped with +# OpenSSH is to specify options with their default value where +# possible, but leave them commented. Uncommented options change a +# default value. -Port 22 +#Port 22 #Protocol 2,1 #ListenAddress 0.0.0.0 #ListenAddress :: # HostKey for protocol version 1 -HostKey /etc/ssh_host_key +#HostKey /etc/ssh/ssh_host_key # HostKeys for protocol version 2 -HostKey /etc/ssh_host_rsa_key -HostKey /etc/ssh_host_dsa_key +#HostKey /etc/ssh/ssh_host_rsa_key +#HostKey /etc/ssh/ssh_host_dsa_key # Lifetime and size of ephemeral version 1 server key -KeyRegenerationInterval 3600 -ServerKeyBits 768 +#KeyRegenerationInterval 3600 +#ServerKeyBits 768 # Logging -SyslogFacility AUTH -LogLevel INFO #obsoletes QuietMode and FascistLogging +#SyslogFacility AUTH +#LogLevel INFO # Authentication: -LoginGraceTime 600 -PermitRootLogin yes -StrictModes yes +#LoginGraceTime 120 +#PermitRootLogin yes +#StrictModes yes -RSAAuthentication yes -PubkeyAuthentication yes -#AuthorizedKeysFile %h/.ssh/authorized_keys +#RSAAuthentication yes +#PubkeyAuthentication yes +#AuthorizedKeysFile .ssh/authorized_keys # rhosts authentication should not be used -RhostsAuthentication no +#RhostsAuthentication no # Don't read the user's ~/.rhosts and ~/.shosts files -IgnoreRhosts yes -# For this to work you will also need host keys in /etc/ssh_known_hosts -RhostsRSAAuthentication no +#IgnoreRhosts yes +# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts +#RhostsRSAAuthentication no # similar for protocol version 2 -HostbasedAuthentication no -# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication -#IgnoreUserKnownHosts yes +#HostbasedAuthentication no +# Change to yes if you don't trust ~/.ssh/known_hosts for +# RhostsRSAAuthentication and HostbasedAuthentication +#IgnoreUserKnownHosts no # To disable tunneled clear text passwords, change to no here! -PasswordAuthentication yes -PermitEmptyPasswords no - -# Uncomment to disable s/key passwords -#ChallengeResponseAuthentication no +#PasswordAuthentication yes +#PermitEmptyPasswords no -# Uncomment to enable PAM keyboard-interactive authentication -# Warning: enabling this may bypass the setting of 'PasswordAuthentication' -#PAMAuthenticationViaKbdInt yes +# Change to no to disable s/key passwords +#ChallengeResponseAuthentication yes -# To change Kerberos options +# Kerberos options #KerberosAuthentication no #KerberosOrLocalPasswd yes +#KerberosTicketCleanup yes + #AFSTokenPassing no -#KerberosTicketCleanup no -# Kerberos TGT Passing does only work with the AFS kaserver -#KerberosTgtPassing yes +# Kerberos TGT Passing only works with the AFS kaserver +#KerberosTgtPassing no + +# Session hooks: if allowed, specify the commands to execute +#AllowSessionHooks yes +#SessionHookStartupCmd /bin/true +#SessionHookShutdownCmd /bin/true -X11Forwarding no -X11DisplayOffset 10 -PrintMotd yes -#PrintLastLog no -KeepAlive yes +# Set this to 'yes' to enable PAM keyboard-interactive authentication +# Warning: enabling this may bypass the setting of 'PasswordAuthentication' +#PAMAuthenticationViaKbdInt no + +#X11Forwarding no +#X11DisplayOffset 10 +#X11UseLocalhost yes +#PrintMotd yes +#PrintLastLog yes +#KeepAlive yes #UseLogin no +#UsePrivilegeSeparation yes +#PermitUserEnvironment no +#Compression yes -#MaxStartups 10:30:60 -#Banner /etc/issue.net -#ReverseMappingCheck yes +#MaxStartups 10 +# no default banner path +#Banner /some/path +#VerifyReverseMapping no +# override default of no subsystems Subsystem sftp /usr/libexec/sftp-server -- 2.45.2