/* 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
krb5_ccache krb5_fwd_ccache;
krb5_principal krb5_user;
char *krb5_ticket_file;
+#endif
+#ifdef SESSION_HOOKS
+ char *session_env_file;
#endif
void *methoddata;
};
)
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
-/* $OpenBSD: misc.c,v 1.12 2001/06/26 17:27:24 markus Exp $ */
-
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
*/
#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"
{
char *t = s;
while (*t) {
- if(*t == '\n' || *t == '\r') {
+ if (*t == '\n' || *t == '\r') {
*t = '\0';
return s;
}
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
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)
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);
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 *));
#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;
#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;
#endif
#ifdef AFS
sAFSTokenPassing,
+#endif
+#ifdef SESSION_HOOKS
+ sAllowSessionHooks, sSessionHookStartupCmd, sSessionHookShutdownCmd,
#endif
sChallengeResponseAuthentication,
sPasswordAuthentication, sKbdInteractiveAuthentication, sListenAddress,
#ifdef AFS
{ "afstokenpassing", sAFSTokenPassing },
#endif
+#ifdef SESSION_HOOKS
+ { "allowsessionhooks", sAllowSessionHooks },
+ { "sessionhookstartupcmd", sSessionHookStartupCmd },
+ { "sessionhookshutdowncmd", sSessionHookShutdownCmd },
+#endif
{ "passwordauthentication", sPasswordAuthentication },
{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication },
{ "challengeresponseauthentication", sChallengeResponseAuthentication },
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;
#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. */
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;
/* 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);
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;
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;
}
#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()) {
-# $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