/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
- */
-/*
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose. Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ *
* SSH2 support by Markus Friedl.
- * Copyright (c) 2000 Markus Friedl. All rights reserved.
+ * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "includes.h"
-RCSID("$OpenBSD: session.c,v 1.29 2000/08/21 16:23:31 millert Exp $");
+RCSID("$OpenBSD: session.c,v 1.151 2002/12/04 04:36:47 stevesk Exp $");
-#include "xmalloc.h"
#include "ssh.h"
-#include "pty.h"
+#include "ssh1.h"
+#include "ssh2.h"
+#include "xmalloc.h"
+#include "sshpty.h"
#include "packet.h"
#include "buffer.h"
-#include "cipher.h"
#include "mpaux.h"
-#include "servconf.h"
#include "uidswap.h"
#include "compat.h"
#include "channels.h"
-#include "nchan.h"
-
#include "bufaux.h"
-#include "ssh2.h"
#include "auth.h"
#include "auth-options.h"
-
-#ifdef WITH_IRIX_PROJECT
-#include <proj.h>
-#endif /* WITH_IRIX_PROJECT */
-
-#if defined(HAVE_USERSEC_H)
-#include <usersec.h>
-#endif
-
-#ifdef HAVE_OSF_SIA
-# include <sia.h>
-# include <siad.h>
-#endif
-
-/* AIX limits */
-#if defined(HAVE_GETUSERATTR) && !defined(S_UFSIZE_HARD) && defined(S_UFSIZE)
-# define S_UFSIZE_HARD S_UFSIZE "_hard"
-# define S_UCPU_HARD S_UCPU "_hard"
-# define S_UDATA_HARD S_UDATA "_hard"
-# define S_USTACK_HARD S_USTACK "_hard"
-# define S_URSS_HARD S_URSS "_hard"
-# define S_UCORE_HARD S_UCORE "_hard"
-# define S_UNOFILE_HARD S_UNOFILE "_hard"
-#endif
-
-#ifdef HAVE_LOGIN_CAP
-#include <login_cap.h>
+#include "pathnames.h"
+#include "log.h"
+#include "servconf.h"
+#include "sshlogin.h"
+#include "serverloop.h"
+#include "canohost.h"
+#include "session.h"
+#include "monitor_wrap.h"
+
+#ifdef HAVE_CYGWIN
+#include <windows.h>
+#include <sys/cygwin.h>
+#define is_winnt (GetVersion() < 0x80000000)
#endif
-/* types */
-
-#define TTYSZ 64
-typedef struct Session Session;
-struct Session {
- int used;
- int self;
- int extended;
- struct passwd *pw;
- pid_t pid;
- /* tty */
- char *term;
- int ptyfd, ttyfd, ptymaster;
- int row, col, xpixel, ypixel;
- char tty[TTYSZ];
- /* X11 */
- char *display;
- int screen;
- char *auth_proto;
- char *auth_data;
- int single_connection;
- /* proto 2 */
- int chanid;
-};
-
/* func */
Session *session_new(void);
-void session_set_fds(Session *s, int fdin, int fdout, int fderr);
-void session_pty_cleanup(Session *s);
-void session_proctitle(Session *s);
-void do_exec_pty(Session *s, const char *command, struct passwd * pw);
-void do_exec_no_pty(Session *s, const char *command, struct passwd * pw);
-void do_login(Session *s);
+void session_set_fds(Session *, int, int, int);
+void session_pty_cleanup(void *);
+void session_proctitle(Session *);
+int session_setup_x11fwd(Session *);
+void do_exec_pty(Session *, const char *);
+void do_exec_no_pty(Session *, const char *);
+void do_exec(Session *, const char *);
+void do_login(Session *, const char *);
+#ifdef LOGIN_NEEDS_UTMPX
+static void do_pre_login(Session *s);
+#endif
+void do_child(Session *, const char *);
+void do_motd(void);
+int check_quietlogin(Session *, const char *);
-void
-do_child(const char *command, struct passwd * pw, const char *term,
- const char *display, const char *auth_proto,
- const char *auth_data, const char *ttyname);
+static void do_authenticated1(Authctxt *);
+static void do_authenticated2(Authctxt *);
+
+static int session_pty_req(Session *);
/* import */
extern ServerOptions options;
-#ifdef HAVE___PROGNAME
extern char *__progname;
-#else /* HAVE___PROGNAME */
-static const char *__progname = "sshd";
-#endif /* HAVE___PROGNAME */
-
extern int log_stderr;
extern int debug_flag;
-extern unsigned int utmp_len;
-
+extern u_int utmp_len;
extern int startup_pipe;
+extern void destroy_sensitive_data(void);
-/* Local Xauthority file. */
-static char *xauthfile;
+/* original command from peer. */
+const char *original_command = NULL;
/* data */
#define MAX_SESSIONS 10
Session sessions[MAX_SESSIONS];
+
#ifdef WITH_AIXAUTHENTICATE
-/* AIX's lastlogin message, set in auth1.c */
char *aixloginmsg;
#endif /* WITH_AIXAUTHENTICATE */
#ifdef HAVE_LOGIN_CAP
-static login_cap_t *lc;
+login_cap_t *lc;
#endif
-/*
- * Remove local Xauthority file.
- */
-void
-xauthfile_cleanup_proc(void *ignore)
+/* 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)
{
- debug("xauthfile_cleanup_proc called");
-
- if (xauthfile != NULL) {
- char *p;
- unlink(xauthfile);
- p = strrchr(xauthfile, '/');
- if (p != NULL) {
- *p = '\0';
- rmdir(xauthfile);
- }
- xfree(xauthfile);
- xauthfile = NULL;
+ struct passwd *pw = _pw;
+
+ if (auth_sock_name != NULL) {
+ temporarily_use_uid(pw);
+ unlink(auth_sock_name);
+ rmdir(auth_sock_dir);
+ auth_sock_name = NULL;
+ restore_uid();
}
}
-/*
- * Function to perform cleanup if we get aborted abnormally (e.g., due to a
- * dropped connection).
- */
-void
-pty_cleanup_proc(void *session)
+static int
+auth_input_request_forwarding(struct passwd * pw)
{
- Session *s=session;
- if (s == NULL)
- fatal("pty_cleanup_proc: no session");
- debug("pty_cleanup_proc: %s", s->tty);
+ Channel *nc;
+ int sock;
+ struct sockaddr_un sunaddr;
- if (s->pid != 0) {
- /* Record that the user has logged out. */
- record_logout(s->pid, s->tty);
+ if (auth_sock_name != NULL) {
+ error("authentication forwarding requested twice.");
+ return 0;
}
- /* Release the pseudo-tty. */
- pty_release(s->tty);
+ /* Temporarily drop privileged uid for mkdir/bind. */
+ temporarily_use_uid(pw);
+
+ /* 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);
+
+ /* Create private directory for socket */
+ if (mkdtemp(auth_sock_dir) == NULL) {
+ packet_send_debug("Agent forwarding disabled: "
+ "mkdtemp() failed: %.100s", strerror(errno));
+ restore_uid();
+ xfree(auth_sock_name);
+ xfree(auth_sock_dir);
+ auth_sock_name = NULL;
+ auth_sock_dir = NULL;
+ return 0;
+ }
+ 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)
+ packet_disconnect("socket: %.100s", strerror(errno));
+
+ /* Bind it to the name. */
+ memset(&sunaddr, 0, sizeof(sunaddr));
+ 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)
+ packet_disconnect("bind: %.100s", strerror(errno));
+
+ /* Restore the privileged uid. */
+ restore_uid();
+
+ /* Start listening on the socket. */
+ if (listen(sock, 5) < 0)
+ packet_disconnect("listen: %.100s", strerror(errno));
+
+ /* Allocate a channel for the authentication agent socket. */
+ 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);
+ strlcpy(nc->path, auth_sock_name, sizeof(nc->path));
+ return 1;
}
-/*
- * Prepares for an interactive session. This is called after the user has
- * been successfully authenticated. During this message exchange, pseudo
- * terminals are allocated, X11, TCP/IP, and authentication agent forwardings
- * are requested, etc.
- */
+
void
-do_authenticated(struct passwd * pw)
+do_authenticated(Authctxt *authctxt)
{
- Session *s;
- int type;
- int compression_level = 0, enable_compression_after_reply = 0;
- int have_pty = 0;
- char *command;
- int n_bytes;
- int plen;
- unsigned int proto_len, data_len, dlen;
-
/*
* Cancel the alarm we set to limit the time taken for
* authentication.
startup_pipe = -1;
}
- /*
- * Inform the channel mechanism that we are the server side and that
- * the client may request to connect to any port at all. (The user
- * could do it anyway, and we wouldn\'t know what is permitted except
- * by the client telling us, so we can equally well trust the client
- * not to request anything bogus.)
- */
- if (!no_port_forwarding_flag)
+ /* setup the channel layer */
+ if (!no_port_forwarding_flag && options.allow_tcp_forwarding)
channel_permit_all_opens();
- s = session_new();
- s->pw = pw;
+ if (compat20)
+ do_authenticated2(authctxt);
+ else
+ do_authenticated1(authctxt);
-#ifdef HAVE_LOGIN_CAP
- if ((lc = login_getclass(pw->pw_class)) == NULL) {
- error("unable to get login class");
- return;
- }
+ /* 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
+}
+
+/*
+ * Prepares for an interactive session. This is called after the user has
+ * been successfully authenticated. During this message exchange, pseudo
+ * terminals are allocated, X11, TCP/IP, and authentication agent forwardings
+ * are requested, etc.
+ */
+static void
+do_authenticated1(Authctxt *authctxt)
+{
+ Session *s;
+ char *command;
+ int success, type, screen_flag;
+ int enable_compression_after_reply = 0;
+ u_int proto_len, data_len, dlen, compression_level = 0;
+
+ s = session_new();
+ s->authctxt = authctxt;
+ s->pw = authctxt->pw;
/*
* We stay in this loop until the client requests to execute a shell
* or a command.
*/
for (;;) {
- int success = 0;
+ success = 0;
/* Get a packet from the client. */
- type = packet_read(&plen);
+ type = packet_read();
/* Process the packet. */
switch (type) {
case SSH_CMSG_REQUEST_COMPRESSION:
- packet_integrity_check(plen, 4, type);
compression_level = packet_get_int();
+ packet_check_eom();
if (compression_level < 1 || compression_level > 9) {
packet_send_debug("Received illegal compression level %d.",
- compression_level);
+ compression_level);
+ break;
+ }
+ if (!options.compression) {
+ debug2("compression disabled");
break;
}
/* Enable compression after we have responded with SUCCESS. */
break;
case SSH_CMSG_REQUEST_PTY:
- if (no_pty_flag) {
- debug("Allocating a pty not permitted for this authentication.");
- break;
- }
- if (have_pty)
- packet_disconnect("Protocol error: you already have a pty.");
-
- debug("Allocating pty.");
-
- /* Allocate a pty and open it. */
- if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty,
- sizeof(s->tty))) {
- error("Failed to allocate pty.");
- break;
- }
- fatal_add_cleanup(pty_cleanup_proc, (void *)s);
- pty_setowner(pw, s->tty);
-
- /* Get TERM from the packet. Note that the value may be of arbitrary length. */
- s->term = packet_get_string(&dlen);
- packet_integrity_check(dlen, strlen(s->term), type);
- /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */
- /* Remaining bytes */
- n_bytes = plen - (4 + dlen + 4 * 4);
-
- if (strcmp(s->term, "") == 0) {
- xfree(s->term);
- s->term = NULL;
- }
- /* Get window size from the packet. */
- s->row = packet_get_int();
- s->col = packet_get_int();
- s->xpixel = packet_get_int();
- s->ypixel = packet_get_int();
- pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
-
- /* Get tty modes from the packet. */
- tty_parse_modes(s->ttyfd, &n_bytes);
- packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type);
-
- session_proctitle(s);
-
- /* Indicate that we now have a pty. */
- success = 1;
- have_pty = 1;
+ success = session_pty_req(s);
break;
case SSH_CMSG_X11_REQUEST_FORWARDING:
- if (!options.x11_forwarding) {
- packet_send_debug("X11 forwarding disabled in server configuration file.");
- break;
- }
- if (!options.xauth_location) {
- packet_send_debug("No xauth program; cannot forward with spoofing.");
- break;
- }
- if (no_x11_forwarding_flag) {
- packet_send_debug("X11 forwarding not permitted for this authentication.");
- break;
- }
- debug("Received request for X11 forwarding with auth spoofing.");
- if (s->display != NULL)
- packet_disconnect("Protocol error: X11 display already set.");
-
s->auth_proto = packet_get_string(&proto_len);
s->auth_data = packet_get_string(&data_len);
- packet_integrity_check(plen, 4 + proto_len + 4 + data_len + 4, type);
- if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER)
+ screen_flag = packet_get_protocol_flags() &
+ SSH_PROTOFLAG_SCREEN_NUMBER;
+ debug2("SSH_PROTOFLAG_SCREEN_NUMBER: %d", screen_flag);
+
+ if (packet_remaining() == 4) {
+ if (!screen_flag)
+ debug2("Buggy client: "
+ "X11 screen flag missing");
s->screen = packet_get_int();
- else
+ } else {
s->screen = 0;
- s->display = x11_create_display_inet(s->screen, options.x11_display_offset);
-
- if (s->display == NULL)
- break;
-
- /* Setup to always have a local .Xauthority. */
- xauthfile = xmalloc(MAXPATHLEN);
- strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
- temporarily_use_uid(pw->pw_uid);
- if (mkdtemp(xauthfile) == NULL) {
- restore_uid();
- error("private X11 dir: mkdtemp %s failed: %s",
- xauthfile, strerror(errno));
- xfree(xauthfile);
- xauthfile = NULL;
- /* XXXX remove listening channels */
- break;
}
- strlcat(xauthfile, "/cookies", MAXPATHLEN);
- open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600);
- restore_uid();
- fatal_add_cleanup(xauthfile_cleanup_proc, NULL);
- success = 1;
+ packet_check_eom();
+ success = session_setup_x11fwd(s);
+ if (!success) {
+ xfree(s->auth_proto);
+ xfree(s->auth_data);
+ s->auth_proto = NULL;
+ s->auth_data = NULL;
+ }
break;
case SSH_CMSG_AGENT_REQUEST_FORWARDING:
break;
}
debug("Received authentication agent forwarding request.");
- success = auth_input_request_forwarding(pw);
+ success = auth_input_request_forwarding(s->pw);
break;
case SSH_CMSG_PORT_FORWARD_REQUEST:
debug("Port forwarding not permitted for this authentication.");
break;
}
+ if (!options.allow_tcp_forwarding) {
+ debug("Port forwarding not permitted.");
+ break;
+ }
debug("Received TCP/IP port forwarding request.");
- channel_input_port_forward_request(pw->pw_uid == 0, options.gateway_ports);
+ channel_input_port_forward_request(s->pw->pw_uid == 0, options.gateway_ports);
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:
- /* Set interactive/non-interactive mode. */
- packet_set_interactive(have_pty || s->display != NULL,
- options.keepalives);
-
if (type == SSH_CMSG_EXEC_CMD) {
command = packet_get_string(&dlen);
debug("Exec command '%.500s'", command);
- packet_integrity_check(plen, 4 + dlen, type);
+ do_exec(s, command);
+ xfree(command);
} else {
- command = NULL;
- packet_integrity_check(plen, 0, type);
- }
- if (forced_command != NULL) {
- command = forced_command;
- debug("Forced command '%.500s'", forced_command);
+ do_exec(s, NULL);
}
- if (have_pty)
- do_exec_pty(s, command, pw);
- else
- do_exec_no_pty(s, command, pw);
-
- if (command != NULL)
- xfree(command);
- /* Cleanup user's local Xauthority file. */
- if (xauthfile)
- xauthfile_cleanup_proc(NULL);
+ packet_check_eom();
+ session_close(s);
return;
default:
* setting up file descriptors and such.
*/
void
-do_exec_no_pty(Session *s, const char *command, struct passwd * pw)
+do_exec_no_pty(Session *s, const char *command)
{
- int pid;
+ pid_t pid;
#ifdef USE_PIPES
int pin[2], pout[2], perr[2];
session_proctitle(s);
-#ifdef USE_PAM
- do_pam_setcred();
+#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");
#endif /* USE_PAM */
/* Fork the child. */
if ((pid = fork()) == 0) {
+ fatal_remove_all_cleanups();
+
/* Child. Reinitialize the log since the pid has changed. */
log_init(__progname, options.log_level, options.log_facility, log_stderr);
perror("dup2 stderr");
#endif /* USE_PIPES */
+#ifdef _UNICOS
+ cray_init_job(s->pw); /* set up cray jid and tmpdir */
+#endif
+
/* Do processing for the child (exec command etc). */
- do_child(command, pw, NULL, s->display, s->auth_proto, s->auth_data, NULL);
+ do_child(s, command);
/* NOTREACHED */
}
+#ifdef _UNICOS
+ signal(WJSIGNAL, cray_job_termination_handler);
+#endif /* _UNICOS */
+#ifdef HAVE_CYGWIN
+ if (is_winnt)
+ cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
+#endif
if (pid < 0)
packet_disconnect("fork failed: %.100s", strerror(errno));
s->pid = pid;
+ /* Set interactive/non-interactive mode. */
+ packet_set_interactive(s->display != NULL);
#ifdef USE_PIPES
/* We are the parent. Close the child sides of the pipes. */
close(pin[0]);
close(perr[1]);
if (compat20) {
- session_set_fds(s, pin[1], pout[0], s->extended ? perr[0] : -1);
+ session_set_fds(s, pin[1], pout[0], s->is_subsystem ? -1 : perr[0]);
} else {
/* Enter the interactive session. */
server_loop(pid, pin[1], pout[0], perr[0]);
- /* server_loop has closed pin[1], pout[1], and perr[1]. */
+ /* server_loop has closed pin[1], pout[0], and perr[0]. */
}
#else /* USE_PIPES */
/* We are the parent. Close the child sides of the socket pairs. */
* handle the case that fdin and fdout are the same.
*/
if (compat20) {
- session_set_fds(s, inout[1], inout[1], s->extended ? err[1] : -1);
+ session_set_fds(s, inout[1], inout[1], s->is_subsystem ? -1 : err[1]);
} else {
server_loop(pid, inout[1], inout[1], err[1]);
/* server_loop has closed inout[1] and err[1]. */
* lastlog, and other such operations.
*/
void
-do_exec_pty(Session *s, const char *command, struct passwd * pw)
+do_exec_pty(Session *s, const char *command)
{
int fdout, ptyfd, ttyfd, ptymaster;
pid_t pid;
ptyfd = s->ptyfd;
ttyfd = s->ttyfd;
-#ifdef USE_PAM
- do_pam_session(pw->pw_name, s->tty);
- do_pam_setcred();
-#endif /* USE_PAM */
+#if defined(USE_PAM)
+ do_pam_session(s->pw->pw_name, s->tty);
+ do_pam_setcred(1);
+#endif
/* Fork the child. */
if ((pid = fork()) == 0) {
+ fatal_remove_all_cleanups();
+
/* Child. Reinitialize the log because the pid has changed. */
log_init(__progname, options.log_level, options.log_facility, log_stderr);
-
/* Close the master side of the pseudo tty. */
close(ptyfd);
/* Make the pseudo tty our controlling tty. */
pty_make_controlling_tty(&ttyfd, s->tty);
- /* Redirect stdin from the pseudo tty. */
- if (dup2(ttyfd, fileno(stdin)) < 0)
- error("dup2 stdin failed: %.100s", strerror(errno));
-
- /* Redirect stdout to the pseudo tty. */
- if (dup2(ttyfd, fileno(stdout)) < 0)
- error("dup2 stdin failed: %.100s", strerror(errno));
-
- /* Redirect stderr to the pseudo tty. */
- if (dup2(ttyfd, fileno(stderr)) < 0)
- error("dup2 stdin failed: %.100s", strerror(errno));
+ /* Redirect stdin/stdout/stderr from the pseudo tty. */
+ if (dup2(ttyfd, 0) < 0)
+ error("dup2 stdin: %s", strerror(errno));
+ if (dup2(ttyfd, 1) < 0)
+ error("dup2 stdout: %s", strerror(errno));
+ if (dup2(ttyfd, 2) < 0)
+ error("dup2 stderr: %s", strerror(errno));
/* Close the extra descriptor for the pseudo tty. */
close(ttyfd);
/* record login, etc. similar to login(1) */
- if (command == NULL && !options.use_login)
- do_login(s);
+#ifndef HAVE_OSF_SIA
+ if (!(options.use_login && command == NULL)) {
+#ifdef _UNICOS
+ cray_init_job(s->pw); /* set up cray jid and tmpdir */
+#endif /* _UNICOS */
+ do_login(s, command);
+ }
+# ifdef LOGIN_NEEDS_UTMPX
+ else
+ do_pre_login(s);
+# endif
+#endif
/* Do common processing for the child, such as execing the command. */
- do_child(command, pw, s->term, s->display, s->auth_proto,
- s->auth_data, s->tty);
+ do_child(s, command);
/* NOTREACHED */
}
+#ifdef _UNICOS
+ signal(WJSIGNAL, cray_job_termination_handler);
+#endif /* _UNICOS */
+#ifdef HAVE_CYGWIN
+ if (is_winnt)
+ cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
+#endif
if (pid < 0)
packet_disconnect("fork failed: %.100s", strerror(errno));
s->pid = pid;
s->ptymaster = ptymaster;
/* Enter interactive session. */
+ packet_set_interactive(1);
if (compat20) {
session_set_fds(s, ptyfd, fdout, -1);
} else {
server_loop(pid, ptyfd, fdout, -1);
/* server_loop _has_ closed ptyfd and fdout. */
- session_pty_cleanup(s);
}
}
-const char *
-get_remote_name_or_ip(void)
+#ifdef LOGIN_NEEDS_UTMPX
+static void
+do_pre_login(Session *s)
+{
+ socklen_t fromlen;
+ struct sockaddr_storage from;
+ pid_t pid = getpid();
+
+ /*
+ * Get IP address of client. If the connection is not a socket, let
+ * the address be 0.0.0.0.
+ */
+ memset(&from, 0, sizeof(from));
+ fromlen = sizeof(from);
+ if (packet_connection_is_on_socket()) {
+ if (getpeername(packet_get_connection_in(),
+ (struct sockaddr *) & from, &fromlen) < 0) {
+ debug("getpeername: %.100s", strerror(errno));
+ fatal_cleanup();
+ }
+ }
+
+ record_utmp_only(pid, s->tty, s->pw->pw_name,
+ get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping),
+ (struct sockaddr *)&from);
+}
+#endif
+
+/*
+ * This is called to fork and execute a command. If another command is
+ * to be forced, execute that instead.
+ */
+void
+do_exec(Session *s, const char *command)
{
- static const char *remote = "";
- if (utmp_len > 0)
- remote = get_canonical_hostname();
- if (utmp_len == 0 || strlen(remote) > utmp_len)
- remote = get_remote_ipaddr();
- return remote;
+ if (forced_command) {
+ original_command = command;
+ command = forced_command;
+ debug("Forced command '%.900s'", command);
+ }
+
+ if (s->ttyfd != -1)
+ do_exec_pty(s, command);
+ else
+ do_exec_no_pty(s, command);
+
+ original_command = NULL;
}
+
/* administrative, login(1)-like work */
void
-do_login(Session *s)
+do_login(Session *s, const char *command)
{
- FILE *f;
char *time_string;
- char buf[256];
socklen_t fromlen;
struct sockaddr_storage from;
- struct stat st;
- time_t last_login_time;
struct passwd * pw = s->pw;
pid_t pid = getpid();
if (packet_connection_is_on_socket()) {
fromlen = sizeof(from);
if (getpeername(packet_get_connection_in(),
- (struct sockaddr *) & from, &fromlen) < 0) {
+ (struct sockaddr *) & from, &fromlen) < 0) {
debug("getpeername: %.100s", strerror(errno));
fatal_cleanup();
}
}
/* Record that there was a login on that tty from the remote host. */
- record_login(pid, s->tty, pw->pw_name, pw->pw_uid,
- get_remote_name_or_ip(), (struct sockaddr *)&from);
+ 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),
+ (struct sockaddr *)&from, fromlen);
- /* Done if .hushlogin exists. */
- snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir);
-#ifdef HAVE_LOGIN_CAP
- if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0)
-#else
- if (stat(buf, &st) >= 0)
+#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();
+ do_pam_chauthtok();
+ }
#endif
+
+ if (check_quietlogin(s, command))
return;
#ifdef USE_PAM
- print_pam_messages();
+ 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 */
- /*
- * Get the time when the user last logged in. 'buf' will be set
- * to contain the hostname the last login was from.
- */
- last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name,
- buf, sizeof(buf));
- if (last_login_time != 0) {
- time_string = ctime(&last_login_time);
+#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(buf, "") == 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, buf);
+ printf("Last login: %s from %s\r\n", time_string,
+ s->hostname);
}
+#endif /* NO_SSH_LASTLOG */
+
+ do_motd();
+}
+
+/*
+ * Display the message of the day.
+ */
+void
+do_motd(void)
+{
+ FILE *f;
+ char buf[256];
+
if (options.print_motd) {
#ifdef HAVE_LOGIN_CAP
f = fopen(login_getcapstr(lc, "welcome", "/etc/motd",
}
}
+
+/*
+ * Check for quiet login, either .hushlogin or command given.
+ */
+int
+check_quietlogin(Session *s, const char *command)
+{
+ char buf[256];
+ struct passwd *pw = s->pw;
+ struct stat st;
+
+ /* Return 1 if .hushlogin exists or a command given. */
+ if (command != NULL)
+ return 1;
+ snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir);
+#ifdef HAVE_LOGIN_CAP
+ if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0)
+ return 1;
+#else
+ if (stat(buf, &st) >= 0)
+ return 1;
+#endif
+ return 0;
+}
+
/*
* Sets the value of the given variable in the environment. If the variable
* already exists, its value is overriden.
*/
-void
-child_set_env(char ***envp, unsigned int *envsizep, const char *name,
- const char *value)
+static void
+child_set_env(char ***envp, u_int *envsizep, const char *name,
+ const char *value)
{
- unsigned int i, namelen;
+ u_int i, namelen;
char **env;
/*
} 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 *));
}
* Otherwise, it must consist of empty lines, comments (line starts with '#')
* and assignments of the form name=value. No other forms are allowed.
*/
-void
-read_environment_file(char ***env, unsigned int *envsize,
- const char *filename)
+static void
+read_environment_file(char ***env, u_int *envsize,
+ const char *filename)
{
FILE *f;
char buf[4096];
char *cp, *value;
+ u_int lineno = 0;
f = fopen(filename, "r");
if (!f)
return;
while (fgets(buf, sizeof(buf), f)) {
+ if (++lineno > 1000)
+ fatal("Too many lines in environment file %s", filename);
for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
;
if (!*cp || *cp == '#' || *cp == '\n')
*strchr(cp, '\n') = '\0';
value = strchr(cp, '=');
if (value == NULL) {
- fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf);
+ fprintf(stderr, "Bad line %u in %.100s\n", lineno,
+ filename);
continue;
}
/*
fclose(f);
}
-#ifdef USE_PAM
-/*
- * Sets any environment variables which have been specified by PAM
- */
-void do_pam_environment(char ***env, int *envsize)
+void copy_environment(char **source, char ***env, u_int *envsize)
{
- char *equals, var_name[512], var_val[512];
- char **pam_env;
+ char *var_name, *var_val;
int i;
- if ((pam_env = fetch_pam_environment()) == NULL)
+ if (source == NULL)
return;
-
- for(i = 0; pam_env[i] != NULL; i++) {
- if ((equals = strstr(pam_env[i], "=")) == NULL)
- continue;
-
- if (strlen(pam_env[i]) < (sizeof(var_name) - 1)) {
- memset(var_name, '\0', sizeof(var_name));
- memset(var_val, '\0', sizeof(var_val));
-
- strncpy(var_name, pam_env[i], equals - pam_env[i]);
- strcpy(var_val, equals + 1);
- debug("PAM environment: %s=%s", var_name, var_val);
-
- child_set_env(env, envsize, var_name, var_val);
+ for(i = 0; source[i] != NULL; i++) {
+ var_name = xstrdup(source[i]);
+ if ((var_val = strstr(var_name, "=")) == NULL) {
+ xfree(var_name);
+ continue;
}
+ *var_val++ = '\0';
+
+ debug3("Copy environment: %s=%s", var_name, var_val);
+ child_set_env(env, envsize, var_name, var_val);
+
+ xfree(var_name);
}
}
-#endif /* USE_PAM */
-#if defined(HAVE_GETUSERATTR)
-/*
- * AIX-specific login initialisation
- */
-void set_limit(char *user, char *soft, char *hard, int resource, int mult)
+static char **
+do_setup_env(Session *s, const char *shell)
{
- struct rlimit rlim;
- int slim, hlim;
-
- getrlimit(resource, &rlim);
-
- slim = 0;
- if (getuserattr(user, soft, &slim, SEC_INT) != -1) {
- if (slim < 0) {
- rlim.rlim_cur = RLIM_INFINITY;
- } else if (slim != 0) {
- /* See the wackiness below */
- if (rlim.rlim_cur == slim * mult)
- slim = 0;
- else
- rlim.rlim_cur = slim * mult;
- }
- }
+ char buf[256];
+ u_int i, envsize;
+ char **env;
+ struct passwd *pw = s->pw;
- hlim = 0;
- if (getuserattr(user, hard, &hlim, SEC_INT) != -1) {
- if (hlim < 0) {
- rlim.rlim_max = RLIM_INFINITY;
- } else if (hlim != 0) {
- rlim.rlim_max = hlim * mult;
- }
- }
+ /* Initialize the environment. */
+ envsize = 100;
+ env = xmalloc(envsize * sizeof(char *));
+ env[0] = NULL;
+#ifdef HAVE_CYGWIN
/*
- * XXX For cpu and fsize the soft limit is set to the hard limit
- * if the hard limit is left at its default value and the soft limit
- * is changed from its default value, either by requesting it
- * (slim == 0) or by setting it to the current default. At least
- * that's how rlogind does it. If you're confused you're not alone.
- * Bug or feature? AIX 4.3.1.2
+ * The Windows environment contains some setting which are
+ * important for a running system. They must not be dropped.
*/
- if ((!strcmp(soft, "fsize") || !strcmp(soft, "cpu"))
- && hlim == 0 && slim != 0)
- rlim.rlim_max = rlim.rlim_cur;
- /* A specified hard limit limits the soft limit */
- else if (hlim > 0 && rlim.rlim_cur > rlim.rlim_max)
- rlim.rlim_cur = rlim.rlim_max;
- /* A soft limit can increase a hard limit */
- else if (rlim.rlim_cur > rlim.rlim_max)
- rlim.rlim_max = rlim.rlim_cur;
-
- if (setrlimit(resource, &rlim) != 0)
- error("setrlimit(%.10s) failed: %.100s", soft, strerror(errno));
-}
-
-void set_limits_from_userattr(char *user)
-{
- int mask;
- char buf[16];
-
- set_limit(user, S_UFSIZE, S_UFSIZE_HARD, RLIMIT_FSIZE, 512);
- set_limit(user, S_UCPU, S_UCPU_HARD, RLIMIT_CPU, 1);
- set_limit(user, S_UDATA, S_UDATA_HARD, RLIMIT_DATA, 512);
- set_limit(user, S_USTACK, S_USTACK_HARD, RLIMIT_STACK, 512);
- set_limit(user, S_URSS, S_URSS_HARD, RLIMIT_RSS, 512);
- set_limit(user, S_UCORE, S_UCORE_HARD, RLIMIT_CORE, 512);
-#if defined(S_UNOFILE)
- set_limit(user, S_UNOFILE, S_UNOFILE_HARD, RLIMIT_NOFILE, 1);
+ copy_environment(environ, &env, &envsize);
#endif
- if (getuserattr(user, S_UMASK, &mask, SEC_INT) != -1) {
- /* Convert decimal to octal */
- (void) snprintf(buf, sizeof(buf), "%d", mask);
- if (sscanf(buf, "%o", &mask) == 1)
- umask(mask);
- }
-}
-#endif /* defined(HAVE_GETUSERATTR) */
-
-/*
- * Performs common processing for the child, such as setting up the
- * environment, closing extra file descriptors, setting the user and group
- * ids, and executing the command or shell.
- */
-void
-do_child(const char *command, struct passwd * pw, const char *term,
- const char *display, const char *auth_proto,
- const char *auth_data, const char *ttyname)
-{
- const char *shell, *hostname, *cp = NULL;
- char buf[256];
- char cmd[1024];
- FILE *f = NULL;
- unsigned int envsize, i;
- char **env;
- extern char **environ;
- struct stat st;
- char *argv[10];
-#ifdef WITH_IRIX_PROJECT
- prid_t projid;
-#endif /* WITH_IRIX_PROJECT */
-
- /* login(1) is only called if we execute the login shell */
- if (options.use_login && command != NULL)
- options.use_login = 0;
-
-#ifndef USE_PAM /* pam_nologin handles this */
- if (!options.use_login) {
-# ifdef HAVE_LOGIN_CAP
- if (!login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid)
- f = fopen(login_getcapstr(lc, "nologin", _PATH_NOLOGIN,
- _PATH_NOLOGIN), "r");
-# else /* HAVE_LOGIN_CAP */
- if (pw->pw_uid)
- f = fopen(_PATH_NOLOGIN, "r");
-# endif /* HAVE_LOGIN_CAP */
- if (f) {
- /* /etc/nologin exists. Print its contents and exit. */
- while (fgets(buf, sizeof(buf), f))
- fputs(buf, stderr);
- fclose(f);
- exit(254);
- }
- }
-#endif /* USE_PAM */
-
- /* Set login name, uid, gid, and groups. */
- /* Login(1) does this as well, and it needs uid 0 for the "-h"
- switch, so we let login(1) to this for us. */
- if (!options.use_login) {
-#ifdef HAVE_OSF_SIA
- extern char **saved_argv;
- extern int saved_argc;
- char *host = get_canonical_hostname ();
-
- if (sia_become_user(NULL, saved_argc, saved_argv, host,
- pw->pw_name, ttyname, 0, NULL, NULL, SIA_BEU_SETLUID) !=
- SIASUCCESS) {
- perror("sia_become_user");
- exit(1);
- }
- if (setreuid(geteuid(), geteuid()) < 0) {
- perror("setreuid");
- exit(1);
- }
-#else /* HAVE_OSF_SIA */
- if (getuid() == 0 || geteuid() == 0) {
-# ifdef HAVE_GETUSERATTR
- set_limits_from_userattr(pw->pw_name);
-# endif /* HAVE_GETUSERATTR */
-# ifdef HAVE_LOGIN_CAP
- if (setusercontext(lc, pw, pw->pw_uid,
- (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) {
- perror("unable to set user context");
- exit(1);
- }
-# else /* HAVE_LOGIN_CAP */
- if (setlogin(pw->pw_name) < 0)
- error("setlogin failed: %s", strerror(errno));
- if (setgid(pw->pw_gid) < 0) {
- perror("setgid");
- exit(1);
- }
- /* Initialize the group list. */
- if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
- perror("initgroups");
- exit(1);
- }
- endgrent();
-# ifdef WITH_IRIX_ARRAY
- /* initialize array session */
- if (newarraysess() != 0)
- fatal("Failed to set up new array session: %.100s",
- strerror(errno));
-# endif /* WITH_IRIX_ARRAY */
-# ifdef WITH_IRIX_PROJECT
- /* initialize irix project info */
- if ((projid = getdfltprojuser(pw->pw_name)) == -1) {
- debug("Failed to get project id, using projid 0");
- projid = 0;
- }
- if (setprid(projid))
- fatal("Failed to initialize project %d for %s: %.100s",
- (int)projid, pw->pw_name, strerror(errno));
-# endif /* WITH_IRIX_PROJECT */
- /* Permanently switch to the desired uid. */
- permanently_set_uid(pw->pw_uid);
-# endif /* HAVE_LOGIN_CAP */
- }
-#endif /* HAVE_OSF_SIA */
-
- if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
- fatal("Failed to set uids to %d.", (int) pw->pw_uid);
- }
- /*
- * Get the shell from the password data. An empty shell field is
- * legal, and means /bin/sh.
- */
- shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
-#ifdef HAVE_LOGIN_CAP
- shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell);
-#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 */
-
- /* Initialize the environment. */
- envsize = 100;
- env = xmalloc(envsize * sizeof(char *));
- env[0] = NULL;
-
if (!options.use_login) {
/* Set basic environment. */
child_set_env(&env, &envsize, "USER", pw->pw_name);
child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
child_set_env(&env, &envsize, "HOME", pw->pw_dir);
#ifdef HAVE_LOGIN_CAP
- (void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH);
- child_set_env(&env, &envsize, "PATH", getenv("PATH"));
-#else
+ if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH) < 0)
+ child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
+ else
+ child_set_env(&env, &envsize, "PATH", getenv("PATH"));
+#else /* HAVE_LOGIN_CAP */
+# ifndef HAVE_CYGWIN
+ /*
+ * There's no standard path on Windows. The path contains
+ * important components pointing to the system directories,
+ * 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
+# endif /* SUPERUSER_PATH */
+# endif /* HAVE_CYGWIN */
+#endif /* HAVE_LOGIN_CAP */
snprintf(buf, sizeof buf, "%.200s/%.50s",
_PATH_MAILDIR, pw->pw_name);
child_set_env(&env, &envsize, "TZ", getenv("TZ"));
/* Set custom environment options from RSA authentication. */
- while (custom_environment) {
- struct envstring *ce = custom_environment;
- char *s = ce->s;
- int i;
- for (i = 0; s[i] != '=' && s[i]; i++);
- if (s[i] == '=') {
- s[i] = 0;
- child_set_env(&env, &envsize, s, s + i + 1);
+ if (!options.use_login) {
+ while (custom_environment) {
+ struct envstring *ce = custom_environment;
+ char *str = ce->s;
+
+ for (i = 0; str[i] != '=' && str[i]; i++)
+ ;
+ if (str[i] == '=') {
+ str[i] = 0;
+ child_set_env(&env, &envsize, str, str + i + 1);
+ }
+ custom_environment = ce->next;
+ xfree(ce->s);
+ xfree(ce);
}
- custom_environment = ce->next;
- xfree(ce->s);
- xfree(ce);
}
+ /* SSH_CLIENT deprecated */
snprintf(buf, sizeof buf, "%.50s %d %d",
- get_remote_ipaddr(), get_remote_port(), get_local_port());
+ get_remote_ipaddr(), get_remote_port(), get_local_port());
child_set_env(&env, &envsize, "SSH_CLIENT", buf);
- if (ttyname)
- child_set_env(&env, &envsize, "SSH_TTY", ttyname);
- if (term)
- child_set_env(&env, &envsize, "TERM", term);
- if (display)
- child_set_env(&env, &envsize, "DISPLAY", display);
+ 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(&env, &envsize, "SSH_CONNECTION", buf);
+
+ if (s->ttyfd != -1)
+ child_set_env(&env, &envsize, "SSH_TTY", s->tty);
+ if (s->term)
+ child_set_env(&env, &envsize, "TERM", s->term);
+ if (s->display)
+ child_set_env(&env, &envsize, "DISPLAY", s->display);
+ if (original_command)
+ child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND",
+ original_command);
+
+#ifdef _UNICOS
+ if (cray_tmpdir[0] != '\0')
+ child_set_env(&env, &envsize, "TMPDIR", cray_tmpdir);
+#endif /* _UNICOS */
#ifdef _AIX
{
- char *authstate,*krb5cc;
-
- if ((authstate = getenv("AUTHSTATE")) != NULL)
- child_set_env(&env,&envsize,"AUTHSTATE",authstate);
+ char *cp;
- if ((krb5cc = getenv("KRB5CCNAME")) != NULL)
- child_set_env(&env,&envsize,"KRB5CCNAME",krb5cc);
+ 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)
+ child_set_env(&env, &envsize, "KRB5CCNAME",
+ s->authctxt->krb5_ticket_file);
+#endif
+#ifdef USE_PAM
+ /*
+ * Pull in any environment variables that may have
+ * been set by PAM.
+ */
{
- extern char *ticket;
+ char **p;
- if (ticket)
- child_set_env(&env, &envsize, "KRBTKFILE", ticket);
+ p = fetch_pam_environment();
+ copy_environment(p, &env, &envsize);
+ free_pam_environment(p);
}
-#endif /* KRB4 */
-
-#ifdef USE_PAM
- /* Pull in any environment variables that may have been set by PAM. */
- do_pam_environment(&env, &envsize);
#endif /* USE_PAM */
- read_environment_file(&env,&envsize,"/etc/environment");
-
- if (xauthfile)
- child_set_env(&env, &envsize, "XAUTHORITY", xauthfile);
- if (auth_get_socket_name() != NULL)
+ if (auth_sock_name != NULL)
child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
- auth_get_socket_name());
+ auth_sock_name);
/* read $HOME/.ssh/environment. */
- if (!options.use_login) {
+ if (options.permit_user_env && !options.use_login) {
snprintf(buf, sizeof buf, "%.200s/.ssh/environment",
- pw->pw_dir);
+ strcmp(pw->pw_dir, "/") ? pw->pw_dir : "");
read_environment_file(&env, &envsize, buf);
}
if (debug_flag) {
for (i = 0; env[i]; i++)
fprintf(stderr, " %.200s\n", env[i]);
}
+ return env;
+}
+
+/*
+ * Run $HOME/.ssh/rc, /etc/ssh/sshrc, or xauth (whichever is found
+ * first in this order).
+ */
+static void
+do_rc_files(Session *s, const char *shell)
+{
+ FILE *f = NULL;
+ char cmd[1024];
+ int do_xauth;
+ struct stat st;
+
+ do_xauth =
+ s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL;
+
+ /* ignore _PATH_SSH_USER_RC for subsystems */
+ if (!s->is_subsystem && (stat(_PATH_SSH_USER_RC, &st) >= 0)) {
+ snprintf(cmd, sizeof cmd, "%s -c '%s %s'",
+ shell, _PATH_BSHELL, _PATH_SSH_USER_RC);
+ if (debug_flag)
+ fprintf(stderr, "Running %s\n", cmd);
+ f = popen(cmd, "w");
+ if (f) {
+ if (do_xauth)
+ fprintf(f, "%s %s\n", s->auth_proto,
+ s->auth_data);
+ pclose(f);
+ } else
+ fprintf(stderr, "Could not run %s\n",
+ _PATH_SSH_USER_RC);
+ } else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) {
+ if (debug_flag)
+ fprintf(stderr, "Running %s %s\n", _PATH_BSHELL,
+ _PATH_SSH_SYSTEM_RC);
+ f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w");
+ if (f) {
+ if (do_xauth)
+ fprintf(f, "%s %s\n", s->auth_proto,
+ s->auth_data);
+ pclose(f);
+ } else
+ fprintf(stderr, "Could not run %s\n",
+ _PATH_SSH_SYSTEM_RC);
+ } else if (do_xauth && options.xauth_location != NULL) {
+ /* Add authority data to .Xauthority if appropriate. */
+ if (debug_flag) {
+ fprintf(stderr,
+ "Running %.500s remove %.100s\n",
+ options.xauth_location, s->auth_display);
+ fprintf(stderr,
+ "%.500s add %.100s %.100s %.100s\n",
+ options.xauth_location, s->auth_display,
+ s->auth_proto, s->auth_data);
+ }
+ snprintf(cmd, sizeof cmd, "%s -q -",
+ options.xauth_location);
+ f = popen(cmd, "w");
+ if (f) {
+ fprintf(f, "remove %s\n",
+ s->auth_display);
+ fprintf(f, "add %s %s %s\n",
+ s->auth_display, s->auth_proto,
+ s->auth_data);
+ pclose(f);
+ } else {
+ fprintf(stderr, "Could not run %s\n",
+ cmd);
+ }
+ }
+}
+
+static void
+do_nologin(struct passwd *pw)
+{
+ FILE *f = NULL;
+ char buf[1024];
+
+#ifdef HAVE_LOGIN_CAP
+ if (!login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid)
+ f = fopen(login_getcapstr(lc, "nologin", _PATH_NOLOGIN,
+ _PATH_NOLOGIN), "r");
+#else
+ if (pw->pw_uid)
+ f = fopen(_PATH_NOLOGIN, "r");
+#endif
+ if (f) {
+ /* /etc/nologin exists. Print its contents and exit. */
+ log("User %.100s not allowed because %s exists",
+ pw->pw_name, _PATH_NOLOGIN);
+ while (fgets(buf, sizeof(buf), f))
+ fputs(buf, stderr);
+ fclose(f);
+ exit(254);
+ }
+}
+
+/* Set login name, uid, gid, and groups. */
+void
+do_setusercontext(struct passwd *pw)
+{
+#ifdef HAVE_CYGWIN
+ if (is_winnt) {
+#else /* HAVE_CYGWIN */
+ if (getuid() == 0 || geteuid() == 0) {
+#endif /* HAVE_CYGWIN */
+#ifdef HAVE_SETPCRED
+ setpcred(pw->pw_name);
+#endif /* HAVE_SETPCRED */
+#ifdef HAVE_LOGIN_CAP
+# ifdef __bsdi__
+ setpgid(0, 0);
+# endif
+ if (setusercontext(lc, pw, pw->pw_uid,
+ (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) {
+ perror("unable to set user context");
+ exit(1);
+ }
+#else
+# if defined(HAVE_GETLUID) && defined(HAVE_SETLUID)
+ /* Sets login uid for accounting */
+ if (getluid() == -1 && setluid(pw->pw_uid) == -1)
+ error("setluid: %s", strerror(errno));
+# endif /* defined(HAVE_GETLUID) && defined(HAVE_SETLUID) */
+
+ if (setlogin(pw->pw_name) < 0)
+ error("setlogin failed: %s", strerror(errno));
+ if (setgid(pw->pw_gid) < 0) {
+ perror("setgid");
+ exit(1);
+ }
+ /* Initialize the group list. */
+ if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
+ perror("initgroups");
+ exit(1);
+ }
+ endgrent();
+# ifdef USE_PAM
+ /*
+ * 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);
+# endif /* USE_PAM */
+# if defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY)
+ irix_setusercontext(pw);
+# endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */
+# ifdef _AIX
+ aix_usrinfo(pw);
+# endif /* _AIX */
+ /* Permanently switch to the desired uid. */
+ permanently_set_uid(pw);
+#endif
+ }
+ if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
+ fatal("Failed to set uids to %u.", (u_int) pw->pw_uid);
+}
+
+static void
+launch_login(struct passwd *pw, const char *hostname)
+{
+ /* Launch login(1). */
+
+ execl(LOGIN_PROGRAM, "login", "-h", hostname,
+#ifdef xxxLOGIN_NEEDS_TERM
+ (s->term ? s->term : "unknown"),
+#endif /* LOGIN_NEEDS_TERM */
+#ifdef LOGIN_NO_ENDOPT
+ "-p", "-f", pw->pw_name, (char *)NULL);
+#else
+ "-p", "-f", "--", pw->pw_name, (char *)NULL);
+#endif
+
+ /* Login couldn't be executed, die. */
+
+ perror("login");
+ exit(1);
+}
+
+/*
+ * Performs common processing for the child, such as setting up the
+ * environment, closing extra file descriptors, setting the user and group
+ * ids, and executing the command or shell.
+ */
+void
+do_child(Session *s, const char *command)
+{
+ extern char **environ;
+ char **env;
+ char *argv[10];
+ const char *shell, *shell0, *hostname = NULL;
+ struct passwd *pw = s->pw;
+ u_int i;
+
+ /* remove hostkey from the child's memory */
+ destroy_sensitive_data();
+
+ /* login(1) is only called if we execute the login shell */
+ if (options.use_login && command != NULL)
+ options.use_login = 0;
+
+#ifdef _UNICOS
+ cray_setup(pw->pw_uid, pw->pw_name, command);
+#endif /* _UNICOS */
+
+ /*
+ * Login(1) does this as well, and it needs uid 0 for the "-h"
+ * switch, so we let login(1) to this for us.
+ */
+ if (!options.use_login) {
+#ifdef HAVE_OSF_SIA
+ session_setup_sia(pw->pw_name, s->ttyfd == -1 ? NULL : s->tty);
+ if (!check_quietlogin(s, command))
+ do_motd();
+#else /* HAVE_OSF_SIA */
+ do_nologin(pw);
+ do_setusercontext(pw);
+#endif /* HAVE_OSF_SIA */
+ }
+
+ /*
+ * Get the shell from the password data. An empty shell field is
+ * legal, and means /bin/sh.
+ */
+ shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
+#ifdef HAVE_LOGIN_CAP
+ shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell);
+#endif
+
+ env = do_setup_env(s, shell);
+
/* we have to stash the hostname before we close our socket. */
if (options.use_login)
- hostname = get_remote_name_or_ip();
+ hostname = get_remote_name_or_ip(utmp_len,
+ options.verify_reverse_mapping);
/*
* Close the connection descriptors; note that this is the child, and
* the server will still have the socket open, and it is important
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;
+
+#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. */
if (chdir(pw->pw_dir) < 0) {
fprintf(stderr, "Could not chdir to home directory %s: %s\n",
- pw->pw_dir, strerror(errno));
+ pw->pw_dir, strerror(errno));
#ifdef HAVE_LOGIN_CAP
if (login_getcapbool(lc, "requirehome", 0))
exit(1);
#endif
}
- /*
- * Must take new environment into use so that .ssh/rc, /etc/sshrc and
- * xauth are run in the proper environment.
- */
- environ = env;
+ if (!options.use_login)
+ do_rc_files(s, shell);
- /*
- * Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first
- * in this order).
- */
- if (!options.use_login) {
- if (stat(SSH_USER_RC, &st) >= 0) {
- if (debug_flag)
- fprintf(stderr, "Running "_PATH_BSHELL" %s\n", SSH_USER_RC);
-
- f = popen(_PATH_BSHELL " " SSH_USER_RC, "w");
- if (f) {
- if (auth_proto != NULL && auth_data != NULL)
- fprintf(f, "%s %s\n", auth_proto, auth_data);
- pclose(f);
- } else
- fprintf(stderr, "Could not run %s\n", SSH_USER_RC);
- } else if (stat(SSH_SYSTEM_RC, &st) >= 0) {
- if (debug_flag)
- fprintf(stderr, "Running "_PATH_BSHELL" %s\n", SSH_SYSTEM_RC);
-
- f = popen(_PATH_BSHELL " " SSH_SYSTEM_RC, "w");
- if (f) {
- if (auth_proto != NULL && auth_data != NULL)
- fprintf(f, "%s %s\n", auth_proto, auth_data);
- pclose(f);
- } else
- fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC);
- } else if (options.xauth_location != NULL) {
- /* Add authority data to .Xauthority if appropriate. */
- if (auth_proto != NULL && auth_data != NULL) {
- char *screen = strchr(display, ':');
- if (debug_flag) {
- fprintf(stderr,
- "Running %.100s add %.100s %.100s %.100s\n",
- options.xauth_location, display,
- auth_proto, auth_data);
- if (screen != NULL)
- fprintf(stderr,
- "Adding %.*s/unix%s %s %s\n",
- screen-display, display,
- screen, auth_proto, auth_data);
- }
- snprintf(cmd, sizeof cmd, "%s -q -",
- options.xauth_location);
- f = popen(cmd, "w");
- if (f) {
- fprintf(f, "add %s %s %s\n", display,
- auth_proto, auth_data);
- if (screen != NULL)
- fprintf(f, "add %.*s/unix%s %s %s\n",
- screen-display, display,
- screen, auth_proto, auth_data);
- pclose(f);
- } else {
- fprintf(stderr, "Could not run %s\n",
- cmd);
- }
- }
- }
- /* Get the last component of the shell name. */
- cp = strrchr(shell, '/');
- if (cp)
- cp++;
- else
- cp = shell;
+ /* restore SIGPIPE for child */
+ signal(SIGPIPE, SIG_DFL);
+
+ if (options.use_login) {
+ launch_login(pw, hostname);
+ /* NEVERREACHED */
}
+
+ /* Get the last component of the shell name. */
+ if ((shell0 = strrchr(shell, '/')) != NULL)
+ shell0++;
+ else
+ shell0 = shell;
+
/*
* If we have no command, execute the shell. In this case, the shell
* name to be passed in argv[0] is preceded by '-' to indicate that
* this is a login shell.
*/
if (!command) {
- if (!options.use_login) {
- char buf[256];
-
- /*
- * Check for mail if we have a tty and it was enabled
- * in server options.
- */
- if (ttyname && options.check_mail) {
- char *mailbox;
- struct stat mailstat;
- mailbox = getenv("MAIL");
- if (mailbox != NULL) {
- if (stat(mailbox, &mailstat) != 0 ||
- mailstat.st_size == 0)
- printf("No mail.\n");
- else if (mailstat.st_mtime < mailstat.st_atime)
- printf("You have mail.\n");
- else
- printf("You have new mail.\n");
- }
- }
- /* Start the shell. Set initial character to '-'. */
- buf[0] = '-';
- strncpy(buf + 1, cp, sizeof(buf) - 1);
- buf[sizeof(buf) - 1] = 0;
+ char argv0[256];
- /* Execute the shell. */
- argv[0] = buf;
- argv[1] = NULL;
- execve(shell, argv, env);
+ /* Start the shell. Set initial character to '-'. */
+ argv0[0] = '-';
- /* Executing the shell failed. */
+ if (strlcpy(argv0 + 1, shell0, sizeof(argv0) - 1)
+ >= sizeof(argv0) - 1) {
+ errno = EINVAL;
perror(shell);
exit(1);
+ }
- } else {
- /* Launch login(1). */
-
- execl(LOGIN_PROGRAM, "login", "-h", hostname,
- "-p", "-f", "--", pw->pw_name, NULL);
-
- /* Login couldn't be executed, die. */
+ /* Execute the shell. */
+ argv[0] = argv0;
+ argv[1] = NULL;
+ execve(shell, argv, env);
- perror("login");
- exit(1);
- }
+ /* Executing the shell failed. */
+ perror(shell);
+ exit(1);
}
/*
* Execute the command using the user's shell. This uses the -c
* option to execute the command.
*/
- argv[0] = (char *) cp;
+ argv[0] = (char *) shell0;
argv[1] = "-c";
argv[2] = (char *) command;
argv[3] = NULL;
static int did_init = 0;
if (!did_init) {
debug("session_new: init");
- for(i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < MAX_SESSIONS; i++) {
sessions[i].used = 0;
- sessions[i].self = i;
}
did_init = 1;
}
- for(i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < MAX_SESSIONS; i++) {
Session *s = &sessions[i];
if (! s->used) {
- s->pid = 0;
- s->extended = 0;
+ memset(s, 0, sizeof(*s));
s->chanid = -1;
s->ptyfd = -1;
s->ttyfd = -1;
- s->term = NULL;
- s->pw = NULL;
- s->display = NULL;
- s->screen = 0;
- s->auth_data = NULL;
- s->auth_proto = NULL;
s->used = 1;
- s->pw = NULL;
+ s->self = i;
debug("session_new: session %d", i);
return s;
}
return NULL;
}
-void
+static void
session_dump(void)
{
int i;
- for(i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < MAX_SESSIONS; i++) {
Session *s = &sessions[i];
- debug("dump: used %d session %d %p channel %d pid %d",
+ debug("dump: used %d session %d %p channel %d pid %ld",
s->used,
s->self,
s,
s->chanid,
- s->pid);
+ (long)s->pid);
}
}
int
-session_open(int chanid)
+session_open(Authctxt *authctxt, int chanid)
{
Session *s = session_new();
debug("session_open: channel %d", chanid);
error("no more sessions");
return 0;
}
- s->pw = auth_get_user();
+ s->authctxt = authctxt;
+ s->pw = authctxt->pw;
if (s->pw == NULL)
- fatal("no user for session %i", s->self);
+ fatal("no user for session %d", s->self);
debug("session_open: session %d: link with channel %d", s->self, chanid);
s->chanid = chanid;
return 1;
}
Session *
+session_by_tty(char *tty)
+{
+ int i;
+ for (i = 0; i < MAX_SESSIONS; i++) {
+ Session *s = &sessions[i];
+ if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) {
+ debug("session_by_tty: session %d tty %s", i, tty);
+ return s;
+ }
+ }
+ debug("session_by_tty: unknown tty %.100s", tty);
+ session_dump();
+ return NULL;
+}
+
+static Session *
session_by_channel(int id)
{
int i;
- for(i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < MAX_SESSIONS; i++) {
Session *s = &sessions[i];
if (s->used && s->chanid == id) {
debug("session_by_channel: session %d channel %d", i, id);
return NULL;
}
-Session *
+static Session *
session_by_pid(pid_t pid)
{
int i;
- debug("session_by_pid: pid %d", pid);
- for(i = 0; i < MAX_SESSIONS; i++) {
+ debug("session_by_pid: pid %ld", (long)pid);
+ for (i = 0; i < MAX_SESSIONS; i++) {
Session *s = &sessions[i];
if (s->used && s->pid == pid)
return s;
}
- error("session_by_pid: unknown pid %d", pid);
+ error("session_by_pid: unknown pid %ld", (long)pid);
session_dump();
return NULL;
}
-int
+static int
session_window_change_req(Session *s)
{
s->col = packet_get_int();
s->row = packet_get_int();
s->xpixel = packet_get_int();
s->ypixel = packet_get_int();
- packet_done();
+ packet_check_eom();
pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
return 1;
}
-int
+static int
session_pty_req(Session *s)
{
- unsigned int len;
- char *term_modes; /* encoded terminal modes */
+ u_int len;
+ int n_bytes;
- if (no_pty_flag)
+ if (no_pty_flag) {
+ debug("Allocating a pty not permitted for this authentication.");
return 0;
- if (s->ttyfd != -1)
+ }
+ if (s->ttyfd != -1) {
+ 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);
- s->col = packet_get_int();
- s->row = packet_get_int();
+
+ if (compat20) {
+ s->col = packet_get_int();
+ s->row = packet_get_int();
+ } else {
+ s->row = packet_get_int();
+ s->col = packet_get_int();
+ }
s->xpixel = packet_get_int();
s->ypixel = packet_get_int();
- term_modes = packet_get_string(&len);
- packet_done();
if (strcmp(s->term, "") == 0) {
xfree(s->term);
s->term = NULL;
}
+
/* Allocate a pty and open it. */
- if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) {
- xfree(s->term);
+ debug("Allocating pty.");
+ if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)))) {
+ if (s->term)
+ xfree(s->term);
s->term = NULL;
s->ptyfd = -1;
s->ttyfd = -1;
error("session_pty_req: session %d alloc failed", s->self);
- xfree(term_modes);
return 0;
}
debug("session_pty_req: session %d alloc %s", s->self, s->tty);
+
+ /* for SSH1 the tty modes length is not given */
+ if (!compat20)
+ 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(pty_cleanup_proc, (void *)s);
- pty_setowner(s->pw, s->tty);
- /* Get window size from the packet. */
+ fatal_add_cleanup(session_pty_cleanup, (void *)s);
+ if (!use_privsep)
+ pty_setowner(s->pw, s->tty);
+
+ /* Set window size from the packet. */
pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
+ packet_check_eom();
session_proctitle(s);
-
- /* XXX parse and set terminal modes */
- xfree(term_modes);
return 1;
}
-int
+static int
session_subsystem_req(Session *s)
{
- unsigned int len;
+ struct stat st;
+ u_int len;
int success = 0;
- char *subsys = packet_get_string(&len);
+ char *cmd, *subsys = packet_get_string(&len);
int i;
- packet_done();
- log("subsystem request for %s", subsys);
+ packet_check_eom();
+ log("subsystem request for %.100s", subsys);
for (i = 0; i < options.num_subsystems; i++) {
- if(strcmp(subsys, options.subsystem_name[i]) == 0) {
- debug("subsystem: exec() %s", options.subsystem_command[i]);
- do_exec_no_pty(s, options.subsystem_command[i], s->pw);
+ 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,
+ strerror(errno));
+ break;
+ }
+ debug("subsystem: exec() %s", cmd);
+ s->is_subsystem = 1;
+ do_exec(s, cmd);
success = 1;
+ break;
}
}
if (!success)
- log("subsystem request for %s failed, subsystem not found", subsys);
+ log("subsystem request for %.100s failed, subsystem not found",
+ subsys);
xfree(subsys);
return success;
}
-int
+static int
session_x11_req(Session *s)
{
- if (no_x11_forwarding_flag) {
- debug("X11 forwarding disabled in user configuration file.");
- return 0;
- }
- if (!options.x11_forwarding) {
- debug("X11 forwarding disabled in server configuration file.");
- return 0;
- }
- if (xauthfile != NULL) {
- debug("X11 fwd already started.");
- return 0;
- }
-
- debug("Received request for X11 forwarding with auth spoofing.");
- if (s->display != NULL)
- packet_disconnect("Protocol error: X11 display already set.");
+ int success;
s->single_connection = packet_get_char();
s->auth_proto = packet_get_string(NULL);
s->auth_data = packet_get_string(NULL);
s->screen = packet_get_int();
- packet_done();
+ packet_check_eom();
- s->display = x11_create_display_inet(s->screen, options.x11_display_offset);
- if (s->display == NULL) {
- xfree(s->auth_proto);
- xfree(s->auth_data);
- return 0;
- }
- xauthfile = xmalloc(MAXPATHLEN);
- strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
- temporarily_use_uid(s->pw->pw_uid);
- if (mkdtemp(xauthfile) == NULL) {
- restore_uid();
- error("private X11 dir: mkdtemp %s failed: %s",
- xauthfile, strerror(errno));
- xfree(xauthfile);
- xauthfile = NULL;
+ success = session_setup_x11fwd(s);
+ if (!success) {
xfree(s->auth_proto);
xfree(s->auth_data);
- /* XXXX remove listening channels */
- return 0;
+ s->auth_proto = NULL;
+ s->auth_data = NULL;
}
- strlcat(xauthfile, "/cookies", MAXPATHLEN);
- open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600);
- restore_uid();
- fatal_add_cleanup(xauthfile_cleanup_proc, s);
- return 1;
+ return success;
}
-int
+static int
session_shell_req(Session *s)
{
- /* if forced_command == NULL, the shell is execed */
- char *shell = forced_command;
- packet_done();
- s->extended = 1;
- if (s->ttyfd == -1)
- do_exec_no_pty(s, shell, s->pw);
- else
- do_exec_pty(s, shell, s->pw);
+ packet_check_eom();
+ do_exec(s, NULL);
return 1;
}
-int
+static int
session_exec_req(Session *s)
{
- unsigned int len;
+ u_int len;
char *command = packet_get_string(&len);
- packet_done();
- if (forced_command) {
- xfree(command);
- command = forced_command;
- debug("Forced command '%.500s'", forced_command);
- }
- s->extended = 1;
- if (s->ttyfd == -1)
- do_exec_no_pty(s, command, s->pw);
- else
- do_exec_pty(s, command, s->pw);
- if (forced_command == NULL)
- xfree(command);
+ packet_check_eom();
+ do_exec(s, command);
+ xfree(command);
return 1;
}
-void
-session_input_channel_req(int id, void *arg)
+static int
+session_auth_agent_req(Session *s)
+{
+ static int called = 0;
+ packet_check_eom();
+ if (no_agent_forwarding_flag) {
+ debug("session_auth_agent_req: no_agent_forwarding_flag");
+ return 0;
+ }
+ if (called) {
+ return 0;
+ } else {
+ called = 1;
+ return auth_input_request_forwarding(s->pw);
+ }
+}
+
+int
+session_input_channel_req(Channel *c, const char *rtype)
{
- unsigned int len;
- int reply;
int success = 0;
- char *rtype;
Session *s;
- Channel *c;
-
- rtype = packet_get_string(&len);
- reply = packet_get_char();
-
- s = session_by_channel(id);
- if (s == NULL)
- fatal("session_input_channel_req: channel %d: no session", id);
- c = channel_lookup(id);
- if (c == NULL)
- fatal("session_input_channel_req: channel %d: bad channel", id);
- debug("session_input_channel_req: session %d channel %d request %s reply %d",
- s->self, id, rtype, reply);
+ if ((s = session_by_channel(c->self)) == NULL) {
+ log("session_input_channel_req: no session %d req %.100s",
+ c->self, rtype);
+ return 0;
+ }
+ debug("session_input_channel_req: session %d req %s", s->self, rtype);
/*
- * a session is in LARVAL state until a shell
- * or programm is executed
+ * a session is in LARVAL state until a shell, a command
+ * or a subsystem is executed
*/
if (c->type == SSH_CHANNEL_LARVAL) {
if (strcmp(rtype, "shell") == 0) {
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);
}
if (strcmp(rtype, "window-change") == 0) {
success = session_window_change_req(s);
}
-
- if (reply) {
- packet_start(success ?
- SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
- packet_put_int(c->remote_id);
- packet_send();
- }
- xfree(rtype);
+ return success;
}
void
fatal("no channel for session %d", s->self);
channel_set_fds(s->chanid,
fdout, fdin, fderr,
- fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ);
+ fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
+ 1,
+ CHAN_SES_WINDOW_DEFAULT);
}
+/*
+ * Function to perform pty cleanup. Also called if we get aborted abnormally
+ * (e.g., due to a dropped connection).
+ */
void
-session_pty_cleanup(Session *s)
+session_pty_cleanup2(void *session)
{
- if (s == NULL || s->ttyfd == -1)
- return;
+ Session *s = session;
- debug("session_pty_cleanup: session %i release %s", s->self, s->tty);
+ if (s == NULL) {
+ error("session_pty_cleanup: no session");
+ return;
+ }
+ if (s->ttyfd == -1)
+ return;
- /* Cancel the cleanup function. */
- fatal_remove_cleanup(pty_cleanup_proc, (void *)s);
+ debug("session_pty_cleanup: session %d release %s", s->self, s->tty);
/* Record that the user has logged out. */
- record_logout(s->pid, s->tty);
+ if (s->pid != 0)
+ record_logout(s->pid, s->tty, s->pw->pw_name);
/* Release the pseudo-tty. */
- pty_release(s->tty);
+ if (getuid() == 0)
+ pty_release(s->tty);
/*
* Close the server side of the socket pairs. We must do this after
* while we're still cleaning up.
*/
if (close(s->ptymaster) < 0)
- error("close(s->ptymaster): %s", strerror(errno));
+ error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno));
+
+ /* unlink pty from session */
+ s->ttyfd = -1;
}
void
+session_pty_cleanup(void *session)
+{
+ PRIVSEP(session_pty_cleanup2(session));
+}
+
+static char *
+sig2name(int sig)
+{
+#define SSH_SIG(x) if (sig == SIG ## x) return #x
+ SSH_SIG(ABRT);
+ SSH_SIG(ALRM);
+ SSH_SIG(FPE);
+ SSH_SIG(HUP);
+ SSH_SIG(ILL);
+ SSH_SIG(INT);
+ SSH_SIG(KILL);
+ SSH_SIG(PIPE);
+ SSH_SIG(QUIT);
+ SSH_SIG(SEGV);
+ SSH_SIG(TERM);
+ SSH_SIG(USR1);
+ SSH_SIG(USR2);
+#undef SSH_SIG
+ return "SIG@openssh.com";
+}
+
+static void
session_exit_message(Session *s, int status)
{
Channel *c;
- if (s == NULL)
- fatal("session_close: no session");
- c = channel_lookup(s->chanid);
- if (c == NULL)
- fatal("session_close: session %d: no channel %d",
+
+ if ((c = channel_lookup(s->chanid)) == NULL)
+ fatal("session_exit_message: session %d: no channel %d",
s->self, s->chanid);
- debug("session_exit_message: session %d channel %d pid %d",
- s->self, s->chanid, s->pid);
+ debug("session_exit_message: session %d channel %d pid %ld",
+ s->self, s->chanid, (long)s->pid);
if (WIFEXITED(status)) {
- channel_request_start(s->chanid,
- "exit-status", 0);
+ channel_request_start(s->chanid, "exit-status", 0);
packet_put_int(WEXITSTATUS(status));
packet_send();
} else if (WIFSIGNALED(status)) {
- channel_request_start(s->chanid,
- "exit-signal", 0);
- packet_put_int(WTERMSIG(status));
+ channel_request_start(s->chanid, "exit-signal", 0);
+ packet_put_cstring(sig2name(WTERMSIG(status)));
#ifdef WCOREDUMP
packet_put_char(WCOREDUMP(status));
#else /* WCOREDUMP */
}
void
-session_free(Session *s)
+session_close(Session *s)
{
- debug("session_free: session %d pid %d", s->self, s->pid);
+ debug("session_close: session %d pid %ld", s->self, (long)s->pid);
+ if (s->ttyfd != -1) {
+ fatal_remove_cleanup(session_pty_cleanup, (void *)s);
+ session_pty_cleanup(s);
+ }
if (s->term)
xfree(s->term);
if (s->display)
xfree(s->display);
+ if (s->auth_display)
+ xfree(s->auth_display);
if (s->auth_data)
xfree(s->auth_data);
if (s->auth_proto)
xfree(s->auth_proto);
s->used = 0;
-}
-
-void
-session_close(Session *s)
-{
- session_pty_cleanup(s);
- session_free(s);
session_proctitle(s);
}
{
Session *s = session_by_pid(pid);
if (s == NULL) {
- debug("session_close_by_pid: no session for pid %d", s->pid);
+ debug("session_close_by_pid: no session for pid %ld",
+ (long)pid);
return;
}
if (s->chanid != -1)
{
Session *s = session_by_channel(id);
if (s == NULL) {
- debug("session_close_by_channel: no session for channel %d", id);
+ debug("session_close_by_channel: no session for id %d", id);
return;
}
- /* disconnect channel */
+ debug("session_close_by_channel: channel %d child %ld",
+ id, (long)s->pid);
+ if (s->pid != 0) {
+ debug("session_close_by_channel: channel %d: has child", id);
+ /*
+ * 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);
+ session_pty_cleanup(s);
+ }
+ return;
+ }
+ /* detach by removing callback */
channel_cancel_cleanup(s->chanid);
s->chanid = -1;
+ session_close(s);
+}
- debug("session_close_by_channel: channel %d kill %d", id, s->pid);
- if (s->pid == 0) {
- /* close session immediately */
- session_close(s);
- } else {
- /* notify child, delay session cleanup */
- if (s->pid <= 1)
- fatal("session_close_by_channel: Unsafe s->pid = %d", s->pid);
- if (kill(s->pid, (s->ttyfd == -1) ? SIGTERM : SIGHUP) < 0)
- error("session_close_by_channel: kill %d: %s",
- s->pid, strerror(errno));
+void
+session_destroy_all(void (*closefunc)(Session *))
+{
+ int i;
+ for (i = 0; i < MAX_SESSIONS; i++) {
+ Session *s = &sessions[i];
+ if (s->used) {
+ if (closefunc != NULL)
+ closefunc(s);
+ else
+ session_close(s);
+ }
}
}
-char *
+static char *
session_tty_list(void)
{
static char buf[1024];
int i;
buf[0] = '\0';
- for(i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < MAX_SESSIONS; i++) {
Session *s = &sessions[i];
if (s->used && s->ttyfd != -1) {
if (buf[0] != '\0')
setproctitle("%s@%s", s->pw->pw_name, session_tty_list());
}
-void
-do_authenticated2(void)
+int
+session_setup_x11fwd(Session *s)
{
- struct passwd *pw;
+ struct stat st;
+ char display[512], auth_display[512];
+ char hostname[MAXHOSTNAMELEN];
- /*
- * Cancel the alarm we set to limit the time taken for
- * authentication.
- */
- alarm(0);
- if (startup_pipe != -1) {
- close(startup_pipe);
- startup_pipe = -1;
+ if (no_x11_forwarding_flag) {
+ packet_send_debug("X11 forwarding disabled in user configuration file.");
+ return 0;
}
-#ifdef HAVE_LOGIN_CAP
- pw = auth_get_user();
- if ((lc = login_getclass(pw->pw_class)) == NULL) {
- error("unable to get login class");
- return;
+ if (!options.x11_forwarding) {
+ debug("X11 forwarding disabled in server configuration file.");
+ return 0;
+ }
+ if (!options.xauth_location ||
+ (stat(options.xauth_location, &st) == -1)) {
+ packet_send_debug("No xauth program; cannot forward with spoofing.");
+ return 0;
+ }
+ if (options.use_login) {
+ packet_send_debug("X11 forwarding disabled; "
+ "not compatible with UseLogin=yes.");
+ return 0;
+ }
+ if (s->display != NULL) {
+ debug("X11 display already set.");
+ return 0;
+ }
+ if (x11_create_display_inet(options.x11_display_offset,
+ options.x11_use_localhost, s->single_connection,
+ &s->display_number) == -1) {
+ debug("x11_create_display_inet failed.");
+ return 0;
}
+
+ /* Set up a suitable value for the DISPLAY variable. */
+ if (gethostname(hostname, sizeof(hostname)) < 0)
+ fatal("gethostname: %.100s", strerror(errno));
+ /*
+ * auth_display must be used as the displayname when the
+ * authorization entry is added with xauth(1). This will be
+ * different than the DISPLAY string for localhost displays.
+ */
+ if (options.x11_use_localhost) {
+ snprintf(display, sizeof display, "localhost:%u.%u",
+ s->display_number, s->screen);
+ snprintf(auth_display, sizeof auth_display, "unix:%u.%u",
+ s->display_number, s->screen);
+ s->display = xstrdup(display);
+ s->auth_display = xstrdup(auth_display);
+ } else {
+#ifdef IPADDR_IN_DISPLAY
+ struct hostent *he;
+ struct in_addr my_addr;
+
+ he = gethostbyname(hostname);
+ if (he == NULL) {
+ error("Can't get IP address for X11 DISPLAY.");
+ packet_send_debug("Can't get IP address for X11 DISPLAY.");
+ return 0;
+ }
+ memcpy(&my_addr, he->h_addr_list[0], sizeof(struct in_addr));
+ snprintf(display, sizeof display, "%.50s:%u.%u", inet_ntoa(my_addr),
+ s->display_number, s->screen);
+#else
+ snprintf(display, sizeof display, "%.400s:%u.%u", hostname,
+ s->display_number, s->screen);
#endif
- server_loop2();
- if (xauthfile)
- xauthfile_cleanup_proc(NULL);
+ s->display = xstrdup(display);
+ s->auth_display = xstrdup(display);
+ }
+
+ return 1;
+}
+
+static void
+do_authenticated2(Authctxt *authctxt)
+{
+ server_loop2(authctxt);
}