+/* $OpenBSD: session.c,v 1.221 2007/01/21 01:41:54 stevesk Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
*/
#include "includes.h"
-RCSID("$OpenBSD: session.c,v 1.154 2003/03/05 22:33:43 markus Exp $");
+#include <sys/types.h>
+#include <sys/param.h>
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <grp.h>
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "xmalloc.h"
#include "ssh.h"
#include "ssh1.h"
#include "ssh2.h"
-#include "xmalloc.h"
#include "sshpty.h"
#include "packet.h"
#include "buffer.h"
-#include "mpaux.h"
+#include "match.h"
#include "uidswap.h"
#include "compat.h"
#include "channels.h"
-#include "bufaux.h"
+#include "key.h"
+#include "cipher.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+#include "hostfile.h"
#include "auth.h"
#include "auth-options.h"
#include "pathnames.h"
#include "serverloop.h"
#include "canohost.h"
#include "session.h"
+#include "kex.h"
#include "monitor_wrap.h"
-#ifdef GSSAPI
-#include "ssh-gss.h"
-#endif
-
-#ifdef HAVE_CYGWIN
-#include <windows.h>
-#include <sys/cygwin.h>
-#define is_winnt (GetVersion() < 0x80000000)
+#if defined(KRB5) && defined(USE_AFS)
+#include <kafs.h>
#endif
/* func */
Session *session_new(void);
void session_set_fds(Session *, int, int, int);
-void session_pty_cleanup(void *);
+void session_pty_cleanup(Session *);
void session_proctitle(Session *);
int session_setup_x11fwd(Session *);
void do_exec_pty(Session *, const char *);
extern u_int utmp_len;
extern int startup_pipe;
extern void destroy_sensitive_data(void);
+extern Buffer loginmsg;
/* original command from peer. */
const char *original_command = NULL;
#define MAX_SESSIONS 10
Session sessions[MAX_SESSIONS];
-#ifdef WITH_AIXAUTHENTICATE
-char *aixloginmsg;
-#endif /* WITH_AIXAUTHENTICATE */
-
#ifdef HAVE_LOGIN_CAP
login_cap_t *lc;
#endif
+static int is_child = 0;
+
/* Name and directory of socket for authentication agent forwarding. */
static char *auth_sock_name = NULL;
static char *auth_sock_dir = NULL;
/* removes the agent forwarding socket */
static void
-auth_sock_cleanup_proc(void *_pw)
+auth_sock_cleanup_proc(struct passwd *pw)
{
- struct passwd *pw = _pw;
-
if (auth_sock_name != NULL) {
temporarily_use_uid(pw);
unlink(auth_sock_name);
/* Allocate a buffer for the socket name, and format the name. */
auth_sock_name = xmalloc(MAXPATHLEN);
auth_sock_dir = xmalloc(MAXPATHLEN);
- strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
+ strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN);
/* Create private directory for socket */
if (mkdtemp(auth_sock_dir) == NULL) {
snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%ld",
auth_sock_dir, (long) getpid());
- /* delete agent socket on fatal() */
- fatal_add_cleanup(auth_sock_cleanup_proc, pw);
-
/* Create the socket. */
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0)
sunaddr.sun_family = AF_UNIX;
strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path));
- if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0)
+ if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)
packet_disconnect("bind: %.100s", strerror(errno));
/* Restore the privileged uid. */
restore_uid();
/* Start listening on the socket. */
- if (listen(sock, 5) < 0)
+ if (listen(sock, SSH_LISTEN_BACKLOG) < 0)
packet_disconnect("listen: %.100s", strerror(errno));
/* Allocate a channel for the authentication agent socket. */
+ /* this shouldn't matter if its hpn or not - cjr */
nc = channel_new("auth socket",
SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1,
CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
- 0, xstrdup("auth socket"), 1);
+ 0, "auth socket", 1);
strlcpy(nc->path, auth_sock_name, sizeof(nc->path));
return 1;
}
+static void
+display_loginmsg(void)
+{
+ if (buffer_len(&loginmsg) > 0) {
+ buffer_append(&loginmsg, "\0", 1);
+ printf("%s", (char *)buffer_ptr(&loginmsg));
+ buffer_clear(&loginmsg);
+ }
+}
void
do_authenticated(Authctxt *authctxt)
{
setproctitle("%s", authctxt->pw->pw_name);
- /*
- * Cancel the alarm we set to limit the time taken for
- * authentication.
- */
- alarm(0);
- if (startup_pipe != -1) {
- close(startup_pipe);
- startup_pipe = -1;
- }
-
/* setup the channel layer */
if (!no_port_forwarding_flag && options.allow_tcp_forwarding)
channel_permit_all_opens();
else
do_authenticated1(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)
}
}
#endif
-#ifdef KRB4
- if (options.kerberos_ticket_cleanup)
- krb4_cleanup_proc(authctxt);
-#endif
-#ifdef KRB5
- if (options.kerberos_ticket_cleanup)
- krb5_cleanup_proc(authctxt);
-#endif
+
+ do_cleanup(authctxt);
}
/*
u_int proto_len, data_len, dlen, compression_level = 0;
s = session_new();
+ if (s == NULL) {
+ error("no more sessions");
+ return;
+ }
s->authctxt = authctxt;
s->pw = authctxt->pw;
compression_level = packet_get_int();
packet_check_eom();
if (compression_level < 1 || compression_level > 9) {
- packet_send_debug("Received illegal compression level %d.",
+ packet_send_debug("Received invalid compression level %d.",
compression_level);
break;
}
- if (!options.compression) {
+ if (options.compression == COMP_NONE) {
debug2("compression disabled");
break;
}
break;
}
debug("Received TCP/IP port forwarding request.");
- channel_input_port_forward_request(s->pw->pw_uid == 0, options.gateway_ports);
+ if (channel_input_port_forward_request(s->pw->pw_uid == 0,
+ options.gateway_ports, options.hpn_disabled,
+ options.hpn_buffer_size) < 0) {
+ debug("Port forwarding failed.");
+ break;
+ }
success = 1;
break;
success = 1;
break;
-#if defined(AFS) || defined(KRB5)
- case SSH_CMSG_HAVE_KERBEROS_TGT:
- if (!options.kerberos_tgt_passing) {
- verbose("Kerberos TGT passing disabled.");
- } else {
- char *kdata = packet_get_string(&dlen);
- packet_check_eom();
-
- /* XXX - 0x41, see creds_to_radix version */
- if (kdata[0] != 0x41) {
-#ifdef KRB5
- krb5_data tgt;
- tgt.data = kdata;
- tgt.length = dlen;
-
- if (auth_krb5_tgt(s->authctxt, &tgt))
- success = 1;
- else
- verbose("Kerberos v5 TGT refused for %.100s", s->authctxt->user);
-#endif /* KRB5 */
- } else {
-#ifdef AFS
- if (auth_krb4_tgt(s->authctxt, kdata))
- success = 1;
- else
- verbose("Kerberos v4 TGT refused for %.100s", s->authctxt->user);
-#endif /* AFS */
- }
- xfree(kdata);
- }
- break;
-#endif /* AFS || KRB5 */
-
-#ifdef AFS
- case SSH_CMSG_HAVE_AFS_TOKEN:
- if (!options.afs_token_passing || !k_hasafs()) {
- verbose("AFS token passing disabled.");
- } else {
- /* Accept AFS token. */
- char *token = packet_get_string(&dlen);
- packet_check_eom();
-
- if (auth_afs_token(s->authctxt, token))
- success = 1;
- else
- verbose("AFS token refused for %.100s",
- s->authctxt->user);
- xfree(token);
- }
- break;
-#endif /* AFS */
-
case SSH_CMSG_EXEC_SHELL:
case SSH_CMSG_EXEC_CMD:
if (type == SSH_CMSG_EXEC_CMD) {
* Any unknown messages in this phase are ignored,
* and a failure message is returned.
*/
- log("Unknown packet type received after authentication: %d", type);
+ logit("Unknown packet type received after authentication: %d", type);
}
packet_start(success ? SSH_SMSG_SUCCESS : SSH_SMSG_FAILURE);
packet_send();
session_proctitle(s);
-#if defined(GSSAPI)
- temporarily_use_uid(s->pw);
- ssh_gssapi_storecreds();
- restore_uid();
-#endif
-
#if defined(USE_PAM)
- do_pam_session(s->pw->pw_name, NULL);
- do_pam_setcred(1);
- if (is_pam_password_change_required())
- packet_disconnect("Password change required but no "
- "TTY available");
+ if (options.use_pam && !use_privsep)
+ do_pam_setcred(1);
#endif /* USE_PAM */
/* Fork the child. */
if ((pid = fork()) == 0) {
- fatal_remove_all_cleanups();
+ is_child = 1;
/* Child. Reinitialize the log since the pid has changed. */
log_init(__progname, options.log_level, options.log_facility, log_stderr);
close(perr[1]);
if (compat20) {
- session_set_fds(s, pin[1], pout[0], s->is_subsystem ? -1 : perr[0]);
+ if (s->is_subsystem) {
+ close(perr[0]);
+ perr[0] = -1;
+ }
+ session_set_fds(s, pin[1], pout[0], perr[0]);
} else {
/* Enter the interactive session. */
server_loop(pid, pin[1], pout[0], perr[0]);
close(inout[0]);
close(err[0]);
+ /*
+ * Clear loginmsg, since it's the child's responsibility to display
+ * it to the user, otherwise multiple sessions may accumulate
+ * multiple copies of the login messages.
+ */
+ buffer_clear(&loginmsg);
+
/*
* Enter the interactive session. Note: server_loop must be able to
* handle the case that fdin and fdout are the same.
ptyfd = s->ptyfd;
ttyfd = s->ttyfd;
-#if defined(GSSAPI)
- temporarily_use_uid(s->pw);
- ssh_gssapi_storecreds();
- restore_uid();
-#endif
-
#if defined(USE_PAM)
- do_pam_session(s->pw->pw_name, s->tty);
- do_pam_setcred(1);
+ if (options.use_pam) {
+ do_pam_set_tty(s->tty);
+ if (!use_privsep)
+ do_pam_setcred(1);
+ }
#endif
/* Fork the child. */
if ((pid = fork()) == 0) {
- fatal_remove_all_cleanups();
+ is_child = 1;
/* Child. Reinitialize the log because the pid has changed. */
log_init(__progname, options.log_level, options.log_facility, log_stderr);
fromlen = sizeof(from);
if (packet_connection_is_on_socket()) {
if (getpeername(packet_get_connection_in(),
- (struct sockaddr *) & from, &fromlen) < 0) {
+ (struct sockaddr *)&from, &fromlen) < 0) {
debug("getpeername: %.100s", strerror(errno));
- fatal_cleanup();
+ cleanup_exit(255);
}
}
record_utmp_only(pid, s->tty, s->pw->pw_name,
- get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping),
+ get_remote_name_or_ip(utmp_len, options.use_dns),
(struct sockaddr *)&from, fromlen);
}
#endif
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) {
+ if (options.adm_forced_command) {
+ original_command = command;
+ command = options.adm_forced_command;
+ debug("Forced command (config) '%.900s'", command);
+ } else if (forced_command) {
original_command = command;
command = forced_command;
- debug("Forced command '%.900s'", command);
+ debug("Forced command (key option) '%.900s'", 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
+
+#ifdef SSH_AUDIT_EVENTS
+ if (command != NULL)
+ PRIVSEP(audit_run_command(command));
+ else if (s->ttyfd == -1) {
+ char *shell = s->pw->pw_shell;
+
+ if (shell[0] == '\0') /* empty shell means /bin/sh */
+ shell =_PATH_BSHELL;
+ PRIVSEP(audit_run_command(shell));
+ }
+#endif
if (s->ttyfd != -1)
do_exec_pty(s, command);
do_exec_no_pty(s, command);
original_command = NULL;
-}
+ /*
+ * Clear loginmsg: it's the child's responsibility to display
+ * it to the user, otherwise multiple sessions may accumulate
+ * multiple copies of the login messages.
+ */
+ buffer_clear(&loginmsg);
+}
/* administrative, login(1)-like work */
void
do_login(Session *s, const char *command)
{
- char *time_string;
socklen_t fromlen;
struct sockaddr_storage from;
struct passwd * pw = s->pw;
if (getpeername(packet_get_connection_in(),
(struct sockaddr *) & from, &fromlen) < 0) {
debug("getpeername: %.100s", strerror(errno));
- fatal_cleanup();
+ cleanup_exit(255);
}
}
if (!use_privsep)
record_login(pid, s->tty, pw->pw_name, pw->pw_uid,
get_remote_name_or_ip(utmp_len,
- options.verify_reverse_mapping),
+ options.use_dns),
(struct sockaddr *)&from, fromlen);
#ifdef USE_PAM
* If password change is needed, do it now.
* This needs to occur before the ~/.hushlogin check.
*/
- if (is_pam_password_change_required()) {
- print_pam_messages();
+ if (options.use_pam && !use_privsep && s->authctxt->force_pwchange) {
+ display_loginmsg();
do_pam_chauthtok();
+ s->authctxt->force_pwchange = 0;
+ /* XXX - signal [net] parent to enable forwardings */
}
#endif
if (check_quietlogin(s, command))
return;
-#ifdef USE_PAM
- if (!is_pam_password_change_required())
- print_pam_messages();
-#endif /* USE_PAM */
-#ifdef WITH_AIXAUTHENTICATE
- if (aixloginmsg && *aixloginmsg)
- printf("%s\n", aixloginmsg);
-#endif /* WITH_AIXAUTHENTICATE */
-
-#ifndef NO_SSH_LASTLOG
- if (options.print_lastlog && s->last_login_time != 0) {
- time_string = ctime(&s->last_login_time);
- if (strchr(time_string, '\n'))
- *strchr(time_string, '\n') = 0;
- if (strcmp(s->hostname, "") == 0)
- printf("Last login: %s\r\n", time_string);
- else
- printf("Last login: %s from %s\r\n", time_string,
- s->hostname);
- }
-#endif /* NO_SSH_LASTLOG */
+ display_loginmsg();
do_motd();
}
child_set_env(char ***envp, u_int *envsizep, const char *name,
const char *value)
{
- u_int i, namelen;
char **env;
+ u_int envsize;
+ u_int i, namelen;
+
+ /*
+ * If we're passed an uninitialized list, allocate a single null
+ * entry before continuing.
+ */
+ if (*envp == NULL && *envsizep == 0) {
+ *envp = xmalloc(sizeof(char *));
+ *envp[0] = NULL;
+ *envsizep = 1;
+ }
/*
* Find the slot where the value should be stored. If the variable
xfree(env[i]);
} else {
/* New variable. Expand if necessary. */
- if (i >= (*envsizep) - 1) {
- if (*envsizep >= 1000)
- fatal("child_set_env: too many env vars,"
- " skipping: %.100s", name);
- (*envsizep) += 50;
- env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *));
+ envsize = *envsizep;
+ if (i >= envsize - 1) {
+ if (envsize >= 1000)
+ fatal("child_set_env: too many env vars");
+ envsize += 50;
+ env = (*envp) = xrealloc(env, envsize, sizeof(char *));
+ *envsizep = envsize;
}
/* Need to set the NULL pointer at end of array beyond the new slot. */
env[i + 1] = NULL;
}
#endif
-void copy_environment(char **source, char ***env, u_int *envsize)
+#ifdef HAVE_ETC_DEFAULT_LOGIN
+/*
+ * Return named variable from specified environment, or NULL if not present.
+ */
+static char *
+child_get_env(char **env, const char *name)
+{
+ int i;
+ size_t len;
+
+ len = strlen(name);
+ for (i=0; env[i] != NULL; i++)
+ if (strncmp(name, env[i], len) == 0 && env[i][len] == '=')
+ return(env[i] + len + 1);
+ return NULL;
+}
+
+/*
+ * Read /etc/default/login.
+ * We pick up the PATH (or SUPATH for root) and UMASK.
+ */
+static void
+read_etc_default_login(char ***env, u_int *envsize, uid_t uid)
+{
+ char **tmpenv = NULL, *var;
+ u_int i, tmpenvsize = 0;
+ u_long mask;
+
+ /*
+ * We don't want to copy the whole file to the child's environment,
+ * so we use a temporary environment and copy the variables we're
+ * interested in.
+ */
+ read_environment_file(&tmpenv, &tmpenvsize, "/etc/default/login");
+
+ if (tmpenv == NULL)
+ return;
+
+ if (uid == 0)
+ var = child_get_env(tmpenv, "SUPATH");
+ else
+ var = child_get_env(tmpenv, "PATH");
+ if (var != NULL)
+ child_set_env(env, envsize, "PATH", var);
+
+ if ((var = child_get_env(tmpenv, "UMASK")) != NULL)
+ if (sscanf(var, "%5lo", &mask) == 1)
+ umask((mode_t)mask);
+
+ for (i = 0; tmpenv[i] != NULL; i++)
+ xfree(tmpenv[i]);
+ xfree(tmpenv);
+}
+#endif /* HAVE_ETC_DEFAULT_LOGIN */
+
+void
+copy_environment(char **source, char ***env, u_int *envsize)
{
char *var_name, *var_val;
int i;
debug3("Copy environment: %s=%s", var_name, var_val);
child_set_env(env, envsize, var_name, var_val);
-
+
xfree(var_name);
}
}
u_int i, envsize;
char **env, *laddr;
struct passwd *pw = s->pw;
+#ifndef HAVE_LOGIN_CAP
+ char *path = NULL;
+#endif
/* Initialize the environment. */
envsize = 100;
- env = xmalloc(envsize * sizeof(char *));
+ env = xcalloc(envsize, sizeof(char *));
env[0] = NULL;
#ifdef HAVE_CYGWIN
* The Windows environment contains some setting which are
* important for a running system. They must not be dropped.
*/
- copy_environment(environ, &env, &envsize);
+ {
+ char **p;
+
+ p = fetch_windows_environment();
+ copy_environment(p, &env, &envsize);
+ free_windows_environment(p);
+ }
#endif
#ifdef GSSAPI
- /* Allow any GSSAPI methods that we've used to alter
+ /* Allow any GSSAPI methods that we've used to alter
* the childs environment as they see fit
*/
- ssh_gssapi_do_child(&env,&envsize);
+ ssh_gssapi_do_child(&env, &envsize);
#endif
if (!options.use_login) {
/* Set basic environment. */
+ for (i = 0; i < s->num_env; i++)
+ child_set_env(&env, &envsize, s->env[i].name,
+ s->env[i].val);
+
child_set_env(&env, &envsize, "USER", pw->pw_name);
child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
#ifdef _AIX
* needed for loading shared libraries. So the path better
* remains intact here.
*/
-# ifdef SUPERUSER_PATH
- child_set_env(&env, &envsize, "PATH",
- s->pw->pw_uid == 0 ? SUPERUSER_PATH : _PATH_STDPATH);
-# else
- child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
-# endif /* SUPERUSER_PATH */
+# ifdef HAVE_ETC_DEFAULT_LOGIN
+ read_etc_default_login(&env, &envsize, pw->pw_uid);
+ path = child_get_env(env, "PATH");
+# endif /* HAVE_ETC_DEFAULT_LOGIN */
+ if (path == NULL || *path == '\0') {
+ child_set_env(&env, &envsize, "PATH",
+ s->pw->pw_uid == 0 ?
+ SUPERUSER_PATH : _PATH_STDPATH);
+ }
# endif /* HAVE_CYGWIN */
#endif /* HAVE_LOGIN_CAP */
if (getenv("TZ"))
child_set_env(&env, &envsize, "TZ", getenv("TZ"));
+#ifdef GSI /* GSI shared libs typically installed in non-system locations. */
+ {
+ char *cp;
+
+ if ((cp = getenv("LD_LIBRARY_PATH")) != NULL)
+ child_set_env(&env, &envsize, "LD_LIBRARY_PATH", cp);
+ if ((cp = getenv("LIBPATH")) != NULL)
+ child_set_env(&env, &envsize, "LIBPATH", cp);
+ if ((cp = getenv("SHLIB_PATH")) != NULL)
+ child_set_env(&env, &envsize, "SHLIB_PATH", cp);
+ if ((cp = getenv("LD_LIBRARYN32_PATH")) != NULL)
+ child_set_env(&env, &envsize, "LD_LIBRARYN32_PATH",cp);
+ if ((cp = getenv("LD_LIBRARY64_PATH")) != NULL)
+ child_set_env(&env, &envsize, "LD_LIBRARY64_PATH",cp);
+ }
+#endif
+
/* Set custom environment options from RSA authentication. */
if (!options.use_login) {
while (custom_environment) {
child_set_env(&env, &envsize, "TMPDIR", cray_tmpdir);
#endif /* _UNICOS */
+ /*
+ * Since we clear KRB5CCNAME at startup, if it's set now then it
+ * must have been set by a native authentication method (eg AIX or
+ * SIA), so copy it to the child.
+ */
+ {
+ char *cp;
+
+ if ((cp = getenv("KRB5CCNAME")) != NULL)
+ child_set_env(&env, &envsize, "KRB5CCNAME", cp);
+ }
+
#ifdef _AIX
{
char *cp;
if ((cp = getenv("AUTHSTATE")) != NULL)
child_set_env(&env, &envsize, "AUTHSTATE", cp);
- if ((cp = getenv("KRB5CCNAME")) != NULL)
- child_set_env(&env, &envsize, "KRB5CCNAME", cp);
read_environment_file(&env, &envsize, "/etc/environment");
}
#endif
-#ifdef KRB4
- if (s->authctxt->krb4_ticket_file)
- child_set_env(&env, &envsize, "KRBTKFILE",
- s->authctxt->krb4_ticket_file);
-#endif
#ifdef KRB5
- if (s->authctxt->krb5_ticket_file)
+ if (s->authctxt->krb5_ccname)
child_set_env(&env, &envsize, "KRB5CCNAME",
- s->authctxt->krb5_ticket_file);
+ s->authctxt->krb5_ccname);
#endif
#ifdef USE_PAM
/*
* Pull in any environment variables that may have
* been set by PAM.
*/
- {
+ if (options.use_pam) {
char **p;
+ p = fetch_pam_child_environment();
+ copy_environment(p, &env, &envsize);
+ free_pam_environment(p);
+
p = fetch_pam_environment();
copy_environment(p, &env, &envsize);
free_pam_environment(p);
if (debug_flag) {
fprintf(stderr,
"Running %.500s remove %.100s\n",
- options.xauth_location, s->auth_display);
+ options.xauth_location, s->auth_display);
fprintf(stderr,
"%.500s add %.100s %.100s %.100s\n",
options.xauth_location, s->auth_display,
#endif
if (f) {
/* /etc/nologin exists. Print its contents and exit. */
- log("User %.100s not allowed because %s exists",
+ logit("User %.100s not allowed because %s exists",
pw->pw_name, _PATH_NOLOGIN);
while (fgets(buf, sizeof(buf), f))
fputs(buf, stderr);
{
#ifdef HAVE_SETPCRED
- setpcred(pw->pw_name);
+ if (setpcred(pw->pw_name, (char **)NULL) == -1)
+ fatal("Failed to set process credentials");
#endif /* HAVE_SETPCRED */
#ifdef HAVE_LOGIN_CAP
# ifdef __bsdi__
setpgid(0, 0);
# endif
+#ifdef GSSAPI
+ if (options.gss_authentication) {
+ temporarily_use_uid(pw);
+ ssh_gssapi_storecreds();
+ restore_uid();
+ }
+#endif
+# ifdef USE_PAM
+ if (options.use_pam) {
+ do_pam_session();
+ do_pam_setcred(use_privsep);
+ }
+# endif /* USE_PAM */
if (setusercontext(lc, pw, pw->pw_uid,
(LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) {
perror("unable to set user context");
exit(1);
}
endgrent();
+#ifdef GSSAPI
+ if (options.gss_authentication) {
+ temporarily_use_uid(pw);
+ ssh_gssapi_storecreds();
+ restore_uid();
+ }
+#endif
# ifdef USE_PAM
/*
- * PAM credentials may take the form of supplementary groups.
+ * PAM credentials may take the form of supplementary groups.
* These will have been wiped by the above initgroups() call.
* Reestablish them here.
*/
- do_pam_setcred(0);
+ if (options.use_pam) {
+ do_pam_session();
+ do_pam_setcred(use_privsep);
+ }
# endif /* USE_PAM */
# if defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY)
irix_setusercontext(pw);
# ifdef _AIX
aix_usrinfo(pw);
# endif /* _AIX */
+#ifdef USE_LIBIAF
+ if (set_id(pw->pw_name) != 0) {
+ exit(1);
+ }
+#endif /* USE_LIBIAF */
/* Permanently switch to the desired uid. */
permanently_set_uid(pw);
#endif
#endif
if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
fatal("Failed to set uids to %u.", (u_int) pw->pw_uid);
+
+#ifdef WITH_SELINUX
+ ssh_selinux_setup_exec_context(pw->pw_name);
+#endif
+}
+
+static void
+do_pwchange(Session *s)
+{
+ fflush(NULL);
+ fprintf(stderr, "WARNING: Your password has expired.\n");
+ if (s->ttyfd != -1) {
+ fprintf(stderr,
+ "You must change your password now and login again!\n");
+#ifdef PASSWD_NEEDS_USERNAME
+ execl(_PATH_PASSWD_PROG, "passwd", s->pw->pw_name,
+ (char *)NULL);
+#else
+ execl(_PATH_PASSWD_PROG, "passwd", (char *)NULL);
+#endif
+ perror("passwd");
+ } else {
+ fprintf(stderr,
+ "Password change required but no TTY available.\n");
+ }
+ exit(1);
}
static void
exit(1);
}
+static void
+child_close_fds(void)
+{
+ int i;
+
+ if (packet_get_connection_in() == packet_get_connection_out())
+ close(packet_get_connection_in());
+ else {
+ close(packet_get_connection_in());
+ close(packet_get_connection_out());
+ }
+ /*
+ * Close all descriptors related to channels. They will still remain
+ * open in the parent.
+ */
+ /* XXX better use close-on-exec? -markus */
+ channel_close_all();
+
+ /*
+ * Close any extra file descriptors. Note that there may still be
+ * descriptors left by system functions. They will be closed later.
+ */
+ endpwent();
+
+ /*
+ * Close any extra open file descriptors so that we don't have them
+ * hanging around in clients. Note that we want to do this after
+ * initgroups, because at least on Solaris 2.3 it leaves file
+ * descriptors open.
+ */
+ for (i = 3; i < 64; i++)
+ close(i);
+}
+
/*
* Performs common processing for the child, such as setting up the
* environment, closing extra file descriptors, setting the user and group
char *argv[10];
const char *shell, *shell0, *hostname = NULL;
struct passwd *pw = s->pw;
- u_int i;
#ifdef AFS_KRB5
/* Default place to look for aklog. */
/* remove hostkey from the child's memory */
destroy_sensitive_data();
+ /* Force a password change */
+ if (s->authctxt->force_pwchange) {
+ do_setusercontext(pw);
+ child_close_fds();
+ do_pwchange(s);
+ exit(1);
+ }
+
/* login(1) is only called if we execute the login shell */
if (options.use_login && command != NULL)
options.use_login = 0;
if (!check_quietlogin(s, command))
do_motd();
#else /* HAVE_OSF_SIA */
- do_nologin(pw);
+ /* When PAM is enabled we rely on it to do the nologin check */
+ if (!options.use_pam)
+ do_nologin(pw);
do_setusercontext(pw);
+ /*
+ * PAM session modules in do_setusercontext may have
+ * generated messages, so if this in an interactive
+ * login then display them too.
+ */
+ if (!check_quietlogin(s, command))
+ display_loginmsg();
#endif /* HAVE_OSF_SIA */
}
+#ifdef USE_PAM
+ if (options.use_pam && !options.use_login && !is_pam_session_open()) {
+ debug3("PAM session not opened, exiting");
+ display_loginmsg();
+ exit(254);
+ }
+#endif
+
/*
* Get the shell from the password data. An empty shell field is
* legal, and means /bin/sh.
/* we have to stash the hostname before we close our socket. */
if (options.use_login)
hostname = get_remote_name_or_ip(utmp_len,
- options.verify_reverse_mapping);
+ options.use_dns);
/*
* Close the connection descriptors; note that this is the child, and
* the server will still have the socket open, and it is important
* closed before building the environment, as we call
* get_remote_ipaddr there.
*/
- if (packet_get_connection_in() == packet_get_connection_out())
- close(packet_get_connection_in());
- else {
- close(packet_get_connection_in());
- close(packet_get_connection_out());
- }
- /*
- * Close all descriptors related to channels. They will still remain
- * open in the parent.
- */
- /* XXX better use close-on-exec? -markus */
- channel_close_all();
+ child_close_fds();
/*
- * Close any extra file descriptors. Note that there may still be
- * descriptors left by system functions. They will be closed later.
+ * Must take new environment into use so that .ssh/rc,
+ * /etc/ssh/sshrc and xauth are run in the proper environment.
*/
- endpwent();
+ environ = env;
+#if defined(KRB5) && defined(USE_AFS)
/*
- * Close any extra open file descriptors so that we don\'t have them
- * hanging around in clients. Note that we want to do this after
- * initgroups, because at least on Solaris 2.3 it leaves file
- * descriptors open.
+ * At this point, we check to see if AFS is active and if we have
+ * a valid Kerberos 5 TGT. If so, it seems like a good idea to see
+ * if we can (and need to) extend the ticket into an AFS token. If
+ * we don't do this, we run into potential problems if the user's
+ * home directory is in AFS and it's not world-readable.
*/
- for (i = 3; i < 64; i++)
- close(i);
- /*
- * Must take new environment into use so that .ssh/rc,
- * /etc/ssh/sshrc and xauth are run in the proper environment.
- */
- environ = env;
+ if (options.kerberos_get_afs_token && k_hasafs() &&
+ (s->authctxt->krb5_ctx != NULL)) {
+ char cell[64];
+
+ debug("Getting AFS token");
+
+ k_setpag();
+
+ if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
+ krb5_afslog(s->authctxt->krb5_ctx,
+ s->authctxt->krb5_fwd_ccache, cell, NULL);
+
+ krb5_afslog_home(s->authctxt->krb5_ctx,
+ s->authctxt->krb5_fwd_ccache, NULL, NULL, pw->pw_dir);
+ }
+#endif
#ifdef AFS_KRB5
options.session_hooks_shutdown_cmd != NULL);
}
#endif
-#ifdef AFS
- /* Try to get AFS tokens for the local cell. */
- if (k_hasafs()) {
- char cell[64];
- if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
- krb_afslog(cell, 0);
-
- krb_afslog(0, 0);
- }
-#endif /* AFS */
-
- /* Change current directory to the user\'s home directory. */
+ /* Change current directory to the user's home directory. */
if (chdir(pw->pw_dir) < 0) {
fprintf(stderr, "Could not chdir to home directory %s: %s\n",
pw->pw_dir, strerror(errno));
do_rc_files(s, shell);
/* restore SIGPIPE for child */
- signal(SIGPIPE, SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
if (options.use_login) {
launch_login(pw, hostname);
/* Execute the shell. */
argv[0] = argv0;
argv[1] = NULL;
- execve(shell, argv, env);
+ execve(shell, argv, environ);
/* Executing the shell failed. */
perror(shell);
argv[1] = "-c";
argv[2] = (char *) command;
argv[3] = NULL;
- execve(shell, argv, env);
+ execve(shell, argv, environ);
perror(shell);
exit(1);
}
s->ttyfd = -1;
s->used = 1;
s->self = i;
+ s->x11_chanids = NULL;
debug("session_new: session %d", i);
return s;
}
}
s->authctxt = authctxt;
s->pw = authctxt->pw;
- if (s->pw == NULL)
+ if (s->pw == NULL || !authctxt->valid)
fatal("no user for session %d", s->self);
debug("session_open: session %d: link with channel %d", s->self, chanid);
s->chanid = chanid;
return NULL;
}
+static Session *
+session_by_x11_channel(int id)
+{
+ int i, j;
+
+ for (i = 0; i < MAX_SESSIONS; i++) {
+ Session *s = &sessions[i];
+
+ if (s->x11_chanids == NULL || !s->used)
+ continue;
+ for (j = 0; s->x11_chanids[j] != -1; j++) {
+ if (s->x11_chanids[j] == id) {
+ debug("session_by_x11_channel: session %d "
+ "channel %d", s->self, id);
+ return s;
+ }
+ }
+ }
+ debug("session_by_x11_channel: unknown channel %d", id);
+ session_dump();
+ return NULL;
+}
+
static Session *
session_by_pid(pid_t pid)
{
packet_disconnect("Protocol error: you already have a pty.");
return 0;
}
- /* Get the time and hostname when the user last logged in. */
- if (options.print_lastlog) {
- s->hostname[0] = '\0';
- s->last_login_time = get_last_login_time(s->pw->pw_uid,
- s->pw->pw_name, s->hostname, sizeof(s->hostname));
- }
s->term = packet_get_string(&len);
n_bytes = packet_remaining();
tty_parse_modes(s->ttyfd, &n_bytes);
- /*
- * Add a cleanup function to clear the utmp entry and record logout
- * time in case we call fatal() (e.g., the connection gets closed).
- */
- fatal_add_cleanup(session_pty_cleanup, (void *)s);
if (!use_privsep)
pty_setowner(s->pw, s->tty);
struct stat st;
u_int len;
int success = 0;
- char *cmd, *subsys = packet_get_string(&len);
- int i;
+ char *prog, *cmd, *subsys = packet_get_string(&len);
+ u_int i;
packet_check_eom();
- log("subsystem request for %.100s", subsys);
+ logit("subsystem request for %.100s", subsys);
for (i = 0; i < options.num_subsystems; i++) {
if (strcmp(subsys, options.subsystem_name[i]) == 0) {
- cmd = options.subsystem_command[i];
- if (stat(cmd, &st) < 0) {
- error("subsystem: cannot stat %s: %s", cmd,
+ prog = options.subsystem_command[i];
+ cmd = options.subsystem_args[i];
+ if (stat(prog, &st) < 0) {
+ error("subsystem: cannot stat %s: %s", prog,
strerror(errno));
break;
}
}
if (!success)
- log("subsystem request for %.100s failed, subsystem not found",
+ logit("subsystem request for %.100s failed, subsystem not found",
subsys);
xfree(subsys);
{
int success;
+ if (s->auth_proto != NULL || s->auth_data != NULL) {
+ error("session_x11_req: session %d: "
+ "x11 forwarding already active", s->self);
+ return 0;
+ }
s->single_connection = packet_get_char();
s->auth_proto = packet_get_string(NULL);
s->auth_data = packet_get_string(NULL);
return 1;
}
+static int
+session_break_req(Session *s)
+{
+
+ packet_get_int(); /* ignored */
+ packet_check_eom();
+
+ if (s->ttyfd == -1 ||
+ tcsendbreak(s->ttyfd, 0) < 0)
+ return 0;
+ return 1;
+}
+
+static int
+session_env_req(Session *s)
+{
+ char *name, *val;
+ u_int name_len, val_len, i;
+
+ name = packet_get_string(&name_len);
+ val = packet_get_string(&val_len);
+ packet_check_eom();
+
+ /* Don't set too many environment variables */
+ if (s->num_env > 128) {
+ debug2("Ignoring env request %s: too many env vars", name);
+ goto fail;
+ }
+
+ for (i = 0; i < options.num_accept_env; i++) {
+ if (match_pattern(name, options.accept_env[i])) {
+ debug2("Setting env %d: %s=%s", s->num_env, name, val);
+ s->env = xrealloc(s->env, s->num_env + 1,
+ sizeof(*s->env));
+ s->env[s->num_env].name = name;
+ s->env[s->num_env].val = val;
+ s->num_env++;
+ return (1);
+ }
+ }
+ debug2("Ignoring env request %s: disallowed name", name);
+
+ fail:
+ xfree(name);
+ xfree(val);
+ return (0);
+}
+
static int
session_auth_agent_req(Session *s)
{
Session *s;
if ((s = session_by_channel(c->self)) == NULL) {
- log("session_input_channel_req: no session %d req %.100s",
+ logit("session_input_channel_req: no session %d req %.100s",
c->self, rtype);
return 0;
}
} else if (strcmp(rtype, "exec") == 0) {
success = session_exec_req(s);
} else if (strcmp(rtype, "pty-req") == 0) {
- success = session_pty_req(s);
+ success = session_pty_req(s);
} else if (strcmp(rtype, "x11-req") == 0) {
success = session_x11_req(s);
} else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) {
success = session_auth_agent_req(s);
} else if (strcmp(rtype, "subsystem") == 0) {
success = session_subsystem_req(s);
+ } else if (strcmp(rtype, "env") == 0) {
+ success = session_env_req(s);
}
}
if (strcmp(rtype, "window-change") == 0) {
success = session_window_change_req(s);
+ } else if (strcmp(rtype, "break") == 0) {
+ success = session_break_req(s);
}
+
return success;
}
*/
if (s->chanid == -1)
fatal("no channel for session %d", s->self);
- channel_set_fds(s->chanid,
- fdout, fdin, fderr,
- fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
- 1,
- CHAN_SES_WINDOW_DEFAULT);
+ if(options.hpn_disabled)
+ channel_set_fds(s->chanid,
+ fdout, fdin, fderr,
+ fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
+ 1,
+ CHAN_SES_WINDOW_DEFAULT);
+ else
+ channel_set_fds(s->chanid,
+ fdout, fdin, fderr,
+ fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
+ 1,
+ options.hpn_buffer_size);
}
/*
* (e.g., due to a dropped connection).
*/
void
-session_pty_cleanup2(void *session)
+session_pty_cleanup2(Session *s)
{
- Session *s = session;
-
if (s == NULL) {
error("session_pty_cleanup: no session");
return;
}
void
-session_pty_cleanup(void *session)
+session_pty_cleanup(Session *s)
{
- PRIVSEP(session_pty_cleanup2(session));
+ PRIVSEP(session_pty_cleanup2(s));
}
static char *
return "SIG@openssh.com";
}
+static void
+session_close_x11(int id)
+{
+ Channel *c;
+
+ if ((c = channel_by_id(id)) == NULL) {
+ debug("session_close_x11: x11 channel %d missing", id);
+ } else {
+ /* Detach X11 listener */
+ debug("session_close_x11: detach x11 channel %d", id);
+ channel_cancel_cleanup(id);
+ if (c->ostate != CHAN_OUTPUT_CLOSED)
+ chan_mark_dead(c);
+ }
+}
+
+static void
+session_close_single_x11(int id, void *arg)
+{
+ Session *s;
+ u_int i;
+
+ debug3("session_close_single_x11: channel %d", id);
+ channel_cancel_cleanup(id);
+ if ((s = session_by_x11_channel(id)) == NULL)
+ fatal("session_close_single_x11: no x11 channel %d", id);
+ for (i = 0; s->x11_chanids[i] != -1; i++) {
+ debug("session_close_single_x11: session %d: "
+ "closing channel %d", s->self, s->x11_chanids[i]);
+ /*
+ * The channel "id" is already closing, but make sure we
+ * close all of its siblings.
+ */
+ if (s->x11_chanids[i] != id)
+ session_close_x11(s->x11_chanids[i]);
+ }
+ xfree(s->x11_chanids);
+ s->x11_chanids = NULL;
+ if (s->display) {
+ xfree(s->display);
+ s->display = NULL;
+ }
+ if (s->auth_proto) {
+ xfree(s->auth_proto);
+ s->auth_proto = NULL;
+ }
+ if (s->auth_data) {
+ xfree(s->auth_data);
+ s->auth_data = NULL;
+ }
+ if (s->auth_display) {
+ xfree(s->auth_display);
+ s->auth_display = NULL;
+ }
+}
+
static void
session_exit_message(Session *s, int status)
{
/* disconnect channel */
debug("session_exit_message: release channel %d", s->chanid);
- channel_cancel_cleanup(s->chanid);
+
+ /*
+ * Adjust cleanup callback attachment to send close messages when
+ * the channel gets EOF. The session will be then be closed
+ * by session_close_by_channel when the childs close their fds.
+ */
+ channel_register_cleanup(c->self, session_close_by_channel, 1);
+
/*
* emulate a write failure with 'chan_write_failed', nobody will be
* interested in data we write.
*/
if (c->ostate != CHAN_OUTPUT_CLOSED)
chan_write_failed(c);
- s->chanid = -1;
}
void
session_close(Session *s)
{
+ u_int i;
+
debug("session_close: session %d pid %ld", s->self, (long)s->pid);
- if (s->ttyfd != -1) {
- fatal_remove_cleanup(session_pty_cleanup, (void *)s);
+ if (s->ttyfd != -1)
session_pty_cleanup(s);
- }
if (s->term)
xfree(s->term);
if (s->display)
xfree(s->display);
+ if (s->x11_chanids)
+ xfree(s->x11_chanids);
if (s->auth_display)
xfree(s->auth_display);
if (s->auth_data)
if (s->auth_proto)
xfree(s->auth_proto);
s->used = 0;
+ if (s->env != NULL) {
+ for (i = 0; i < s->num_env; i++) {
+ xfree(s->env[i].name);
+ xfree(s->env[i].val);
+ }
+ xfree(s->env);
+ }
session_proctitle(s);
}
}
if (s->chanid != -1)
session_exit_message(s, status);
- session_close(s);
+ if (s->ttyfd != -1)
+ session_pty_cleanup(s);
+ s->pid = 0;
}
/*
session_close_by_channel(int id, void *arg)
{
Session *s = session_by_channel(id);
+ u_int i;
+
if (s == NULL) {
debug("session_close_by_channel: no session for id %d", id);
return;
* delay detach of session, but release pty, since
* the fd's to the child are already closed
*/
- if (s->ttyfd != -1) {
- fatal_remove_cleanup(session_pty_cleanup, (void *)s);
+ if (s->ttyfd != -1)
session_pty_cleanup(s);
- }
return;
}
/* detach by removing callback */
channel_cancel_cleanup(s->chanid);
+
+ /* Close any X11 listeners associated with this session */
+ if (s->x11_chanids != NULL) {
+ for (i = 0; s->x11_chanids[i] != -1; i++) {
+ session_close_x11(s->x11_chanids[i]);
+ s->x11_chanids[i] = -1;
+ }
+ }
+
s->chanid = -1;
session_close(s);
}
for (i = 0; i < MAX_SESSIONS; i++) {
Session *s = &sessions[i];
if (s->used && s->ttyfd != -1) {
-
+
if (strncmp(s->tty, "/dev/", 5) != 0) {
cp = strrchr(s->tty, '/');
cp = (cp == NULL) ? s->tty : cp + 1;
} else
cp = s->tty + 5;
-
+
if (buf[0] != '\0')
strlcat(buf, ",", sizeof buf);
strlcat(buf, cp, sizeof buf);
struct stat st;
char display[512], auth_display[512];
char hostname[MAXHOSTNAMELEN];
+ u_int i;
if (no_x11_forwarding_flag) {
packet_send_debug("X11 forwarding disabled in user configuration file.");
}
if (x11_create_display_inet(options.x11_display_offset,
options.x11_use_localhost, s->single_connection,
- &s->display_number) == -1) {
+ &s->display_number, &s->x11_chanids,
+ options.hpn_disabled, options.hpn_buffer_size) == -1) {
debug("x11_create_display_inet failed.");
return 0;
}
+ for (i = 0; s->x11_chanids[i] != -1; i++) {
+ channel_register_cleanup(s->x11_chanids[i],
+ session_close_single_x11, 0);
+ }
/* Set up a suitable value for the DISPLAY variable. */
if (gethostname(hostname, sizeof(hostname)) < 0)
do_authenticated2(Authctxt *authctxt)
{
server_loop2(authctxt);
-#if defined(GSSAPI)
- ssh_gssapi_cleanup_creds(NULL);
+}
+
+void
+do_cleanup(Authctxt *authctxt)
+{
+ static int called = 0;
+
+ debug("do_cleanup");
+
+ /* no cleanup if we're in the child for login shell */
+ if (is_child)
+ return;
+
+ /* avoid double cleanup */
+ if (called)
+ return;
+ called = 1;
+
+ if (authctxt == NULL)
+ return;
+
+#ifdef USE_PAM
+ if (options.use_pam) {
+ sshpam_cleanup();
+ sshpam_thread_cleanup();
+ }
+#endif
+
+ if (!authctxt->authenticated)
+ return;
+
+#ifdef KRB5
+ if (options.kerberos_ticket_cleanup &&
+ authctxt->krb5_ctx)
+ krb5_cleanup_proc(authctxt);
+#endif
+
+#ifdef GSSAPI
+ if (compat20 && options.gss_cleanup_creds)
+ ssh_gssapi_cleanup_creds();
#endif
+
+ /* remove agent socket */
+ auth_sock_cleanup_proc(authctxt->pw);
+
+ /*
+ * Cleanup ptys/utmp only if privsep is disabled,
+ * or if running in monitor.
+ */
+ if (!use_privsep || mm_is_monitor())
+ session_destroy_all(session_pty_cleanup2);
}