*/
#include "includes.h"
-RCSID("$OpenBSD: session.c,v 1.158 2003/06/02 09:17:34 markus Exp $");
+RCSID("$OpenBSD: session.c,v 1.171 2004/01/13 19:23:15 markus Exp $");
#include "ssh.h"
#include "ssh1.h"
#include "session.h"
#include "monitor_wrap.h"
-#ifdef HAVE_CYGWIN
-#include <windows.h>
-#include <sys/cygwin.h>
-#define is_winnt (GetVersion() < 0x80000000)
+#ifdef KRB5
+#include <kafs.h>
+#endif
+
+#ifdef GSSAPI
+#include "ssh-gss.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 *);
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)
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. */
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 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);
}
/*
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) {
#if defined(USE_PAM)
if (options.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 "
/* 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);
#if defined(USE_PAM)
if (options.use_pam) {
- do_pam_session(s->pw->pw_name, s->tty);
+ do_pam_set_tty(s->tty);
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);
if (getpeername(packet_get_connection_in(),
(struct sockaddr *) & from, &fromlen) < 0) {
debug("getpeername: %.100s", strerror(errno));
- fatal_cleanup();
+ cleanup_exit(255);
}
}
debug("Forced command '%.900s'", command);
}
+#ifdef GSSAPI
+ if (options.gss_authentication) {
+ temporarily_use_uid(s->pw);
+ ssh_gssapi_storecreds();
+ restore_uid();
+ }
+#endif
+
if (s->ttyfd != -1)
do_exec_pty(s, command);
else
if (getpeername(packet_get_connection_in(),
(struct sockaddr *) & from, &fromlen) < 0) {
debug("getpeername: %.100s", strerror(errno));
- fatal_cleanup();
+ cleanup_exit(255);
}
}
if (options.use_pam && is_pam_password_change_required()) {
print_pam_messages();
do_pam_chauthtok();
+ /* XXX - signal [net] parent to enable forwardings */
}
#endif
* Sets the value of the given variable in the environment. If the variable
* already exists, its value is overriden.
*/
-static void
+void
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;
fclose(f);
}
+#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;
debug3("Copy environment: %s=%s", var_name, var_val);
child_set_env(env, envsize, var_name, var_val);
-
+
xfree(var_name);
}
}
{
char buf[256];
u_int i, envsize;
- char **env, *laddr;
+ char **env, *laddr, *path = NULL;
struct passwd *pw = s->pw;
/* Initialize the environment. */
copy_environment(environ, &env, &envsize);
#endif
+#ifdef GSSAPI
+ /* Allow any GSSAPI methods that we've used to alter
+ * the childs environment as they see fit
+ */
+ ssh_gssapi_do_child(&env, &envsize);
+#endif
+
if (!options.use_login) {
/* Set basic environment. */
child_set_env(&env, &envsize, "USER", pw->pw_name);
* 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 */
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)
child_set_env(&env, &envsize, "KRB5CCNAME",
* been set by PAM.
*/
if (options.use_pam) {
- char **p = fetch_pam_environment();
+ 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,
endgrent();
# 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.
*/
- if (options.use_pam)
+ if (options.use_pam) {
+ do_pam_session();
do_pam_setcred(0);
+ }
# endif /* USE_PAM */
# if defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY)
irix_setusercontext(pw);
*/
environ = env;
-#ifdef AFS
- /* Try to get AFS tokens for the local cell. */
- if (k_hasafs()) {
+#if defined(KRB5) && defined(AFS)
+ /*
+ * 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.
+ */
+
+ 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)
- krb_afslog(cell, 0);
+ krb5_afslog(s->authctxt->krb5_ctx,
+ s->authctxt->krb5_fwd_ccache, cell, NULL);
- krb_afslog(0, 0);
+ krb5_afslog_home(s->authctxt->krb5_ctx,
+ s->authctxt->krb5_fwd_ccache, NULL, NULL, pw->pw_dir);
}
-#endif /* AFS */
+#endif
/* Change current directory to the user\'s home directory. */
if (chdir(pw->pw_dir) < 0) {
}
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;
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);
{
u_int break_length;
- break_length = packet_get_int();
+ break_length = packet_get_int(); /* ignored */
packet_check_eom();
- if (s->ttyfd == -1)
+ if (s->ttyfd == -1 ||
+ tcsendbreak(s->ttyfd, 0) < 0)
return 0;
- /* we will sleep from 500ms to 3000ms */
- break_length = MIN(break_length, 3000);
- break_length = MAX(break_length, 500);
- ioctl(s->ttyfd, TIOCSBRK, NULL);
- /* should we care about EINTR? */
- usleep(break_length * 1000);
- ioctl(s->ttyfd, TIOCCBRK, NULL);
return 1;
}
* (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 *
session_close(Session *s)
{
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)
* 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 */
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);
{
server_loop2(authctxt);
}
+
+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 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
+
+#ifdef USE_PAM
+ if (options.use_pam) {
+ sshpam_cleanup();
+ sshpam_thread_cleanup();
+ }
+#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);
+}