version 1.42, 2000/10/27 07:32:18 |
version 1.42.2.6, 2001/09/27 00:15:42 |
|
|
* called by a name other than "ssh" or "Secure Shell". |
* called by a name other than "ssh" or "Secure Shell". |
* |
* |
* SSH2 support by Markus Friedl. |
* 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 |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* modification, are permitted provided that the following conditions |
|
|
#include "includes.h" |
#include "includes.h" |
RCSID("$OpenBSD$"); |
RCSID("$OpenBSD$"); |
|
|
#include "xmalloc.h" |
|
#include "ssh.h" |
#include "ssh.h" |
#include "pty.h" |
#include "ssh1.h" |
|
#include "ssh2.h" |
|
#include "xmalloc.h" |
|
#include "sshpty.h" |
#include "packet.h" |
#include "packet.h" |
#include "buffer.h" |
#include "buffer.h" |
#include "mpaux.h" |
#include "mpaux.h" |
#include "servconf.h" |
|
#include "uidswap.h" |
#include "uidswap.h" |
#include "compat.h" |
#include "compat.h" |
#include "channels.h" |
#include "channels.h" |
#include "nchan.h" |
|
|
|
#include "bufaux.h" |
#include "bufaux.h" |
#include "ssh2.h" |
|
#include "auth.h" |
#include "auth.h" |
#include "auth-options.h" |
#include "auth-options.h" |
|
#include "pathnames.h" |
|
#include "log.h" |
|
#include "servconf.h" |
|
#include "sshlogin.h" |
|
#include "serverloop.h" |
|
#include "canohost.h" |
|
#include "session.h" |
|
|
#ifdef HAVE_LOGIN_CAP |
|
#include <login_cap.h> |
|
#endif |
|
|
|
/* types */ |
/* types */ |
|
|
#define TTYSZ 64 |
#define TTYSZ 64 |
|
|
struct Session { |
struct Session { |
int used; |
int used; |
int self; |
int self; |
int extended; |
struct passwd *pw; |
struct passwd *pw; |
Authctxt *authctxt; |
pid_t pid; |
pid_t pid; |
/* tty */ |
/* tty */ |
char *term; |
char *term; |
|
|
int single_connection; |
int single_connection; |
/* proto 2 */ |
/* proto 2 */ |
int chanid; |
int chanid; |
|
int is_subsystem; |
}; |
}; |
|
|
/* func */ |
/* func */ |
|
|
Session *session_new(void); |
Session *session_new(void); |
void session_set_fds(Session *s, int fdin, int fdout, int fderr); |
void session_set_fds(Session *, int, int, int); |
void session_pty_cleanup(Session *s); |
static void session_pty_cleanup(void *); |
void session_proctitle(Session *s); |
void session_proctitle(Session *); |
void do_exec_pty(Session *s, const char *command, struct passwd * pw); |
int session_setup_x11fwd(Session *); |
void do_exec_no_pty(Session *s, const char *command, struct passwd * pw); |
void do_exec_pty(Session *, const char *); |
void do_login(Session *s, const char *command); |
void do_exec_no_pty(Session *, const char *); |
|
void do_exec(Session *, const char *); |
|
void do_login(Session *, const char *); |
|
void do_child(Session *, const char *); |
|
void do_motd(void); |
|
int check_quietlogin(Session *, const char *); |
|
|
void |
static void do_authenticated1(Authctxt *); |
do_child(const char *command, struct passwd * pw, const char *term, |
static void do_authenticated2(Authctxt *); |
const char *display, const char *auth_proto, |
|
const char *auth_data, const char *ttyname); |
|
|
|
|
static void session_close(Session *); |
|
static int session_pty_req(Session *); |
|
|
/* import */ |
/* import */ |
extern ServerOptions options; |
extern ServerOptions options; |
extern char *__progname; |
extern char *__progname; |
extern int log_stderr; |
extern int log_stderr; |
extern int debug_flag; |
extern int debug_flag; |
extern unsigned int utmp_len; |
extern u_int utmp_len; |
|
|
extern int startup_pipe; |
extern int startup_pipe; |
|
extern void destroy_sensitive_data(void); |
|
|
/* Local Xauthority file. */ |
|
static char *xauthfile; |
|
|
|
/* original command from peer. */ |
/* original command from peer. */ |
char *original_command = NULL; |
const char *original_command = NULL; |
|
|
/* data */ |
/* data */ |
#define MAX_SESSIONS 10 |
#define MAX_SESSIONS 10 |
|
|
static login_cap_t *lc; |
static login_cap_t *lc; |
#endif |
#endif |
|
|
/* |
|
* Remove local Xauthority file. |
|
*/ |
|
void |
void |
xauthfile_cleanup_proc(void *ignore) |
do_authenticated(Authctxt *authctxt) |
{ |
{ |
debug("xauthfile_cleanup_proc called"); |
/* |
|
* Cancel the alarm we set to limit the time taken for |
if (xauthfile != NULL) { |
* authentication. |
char *p; |
*/ |
unlink(xauthfile); |
alarm(0); |
p = strrchr(xauthfile, '/'); |
if (startup_pipe != -1) { |
if (p != NULL) { |
close(startup_pipe); |
*p = '\0'; |
startup_pipe = -1; |
rmdir(xauthfile); |
|
} |
|
xfree(xauthfile); |
|
xauthfile = NULL; |
|
} |
} |
} |
#ifdef HAVE_LOGIN_CAP |
|
if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL) { |
/* |
error("unable to get login class"); |
* Function to perform cleanup if we get aborted abnormally (e.g., due to a |
return; |
* dropped connection). |
|
*/ |
|
void |
|
pty_cleanup_proc(void *session) |
|
{ |
|
Session *s=session; |
|
if (s == NULL) |
|
fatal("pty_cleanup_proc: no session"); |
|
debug("pty_cleanup_proc: %s", s->tty); |
|
|
|
if (s->pid != 0) { |
|
/* Record that the user has logged out. */ |
|
record_logout(s->pid, s->tty); |
|
} |
} |
|
#ifdef BSD_AUTH |
|
if (auth_approval(NULL, lc, authctxt->pw->pw_name, "ssh") <= 0) { |
|
packet_disconnect("Approval failure for %s", |
|
authctxt->pw->pw_name); |
|
} |
|
#endif |
|
#endif |
|
/* setup the channel layer */ |
|
if (!no_port_forwarding_flag && options.allow_tcp_forwarding) |
|
channel_permit_all_opens(); |
|
|
/* Release the pseudo-tty. */ |
if (compat20) |
pty_release(s->tty); |
do_authenticated2(authctxt); |
|
else |
|
do_authenticated1(authctxt); |
|
|
|
/* remove agent socket */ |
|
if (auth_get_socket_name()) |
|
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 |
} |
} |
|
|
/* |
/* |
|
|
* terminals are allocated, X11, TCP/IP, and authentication agent forwardings |
* terminals are allocated, X11, TCP/IP, and authentication agent forwardings |
* are requested, etc. |
* are requested, etc. |
*/ |
*/ |
void |
static void |
do_authenticated(struct passwd * pw) |
do_authenticated1(Authctxt *authctxt) |
{ |
{ |
Session *s; |
Session *s; |
int type, fd; |
|
int compression_level = 0, enable_compression_after_reply = 0; |
|
int have_pty = 0; |
|
char *command; |
char *command; |
int n_bytes; |
int success, type, plen, screen_flag; |
int plen; |
int compression_level = 0, enable_compression_after_reply = 0; |
unsigned int proto_len, data_len, dlen; |
u_int proto_len, data_len, dlen; |
|
|
/* |
|
* Cancel the alarm we set to limit the time taken for |
|
* authentication. |
|
*/ |
|
alarm(0); |
|
if (startup_pipe != -1) { |
|
close(startup_pipe); |
|
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 && options.allow_tcp_forwarding) |
|
channel_permit_all_opens(); |
|
|
|
s = session_new(); |
s = session_new(); |
s->pw = pw; |
s->authctxt = authctxt; |
|
s->pw = authctxt->pw; |
|
|
#ifdef HAVE_LOGIN_CAP |
|
if ((lc = login_getclass(pw->pw_class)) == NULL) { |
|
error("unable to get login class"); |
|
return; |
|
} |
|
#endif |
|
|
|
/* |
/* |
* We stay in this loop until the client requests to execute a shell |
* We stay in this loop until the client requests to execute a shell |
* or a command. |
* or a command. |
*/ |
*/ |
for (;;) { |
for (;;) { |
int success = 0; |
success = 0; |
|
|
/* Get a packet from the client. */ |
/* Get a packet from the client. */ |
type = packet_read(&plen); |
type = packet_read(&plen); |
|
|
break; |
break; |
|
|
case SSH_CMSG_REQUEST_PTY: |
case SSH_CMSG_REQUEST_PTY: |
if (no_pty_flag) { |
success = session_pty_req(s); |
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; |
|
break; |
break; |
|
|
case SSH_CMSG_X11_REQUEST_FORWARDING: |
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_proto = packet_get_string(&proto_len); |
s->auth_data = packet_get_string(&data_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(); |
s->screen = packet_get_int(); |
else |
} else { |
s->screen = 0; |
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); |
packet_done(); |
fd = open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); |
success = session_setup_x11fwd(s); |
if (fd >= 0) |
if (!success) { |
close(fd); |
xfree(s->auth_proto); |
restore_uid(); |
xfree(s->auth_data); |
fatal_add_cleanup(xauthfile_cleanup_proc, NULL); |
s->auth_proto = NULL; |
success = 1; |
s->auth_data = NULL; |
|
} |
break; |
break; |
|
|
case SSH_CMSG_AGENT_REQUEST_FORWARDING: |
case SSH_CMSG_AGENT_REQUEST_FORWARDING: |
|
|
break; |
break; |
} |
} |
debug("Received authentication agent forwarding request."); |
debug("Received authentication agent forwarding request."); |
success = auth_input_request_forwarding(pw); |
success = auth_input_request_forwarding(s->pw); |
break; |
break; |
|
|
case SSH_CMSG_PORT_FORWARD_REQUEST: |
case SSH_CMSG_PORT_FORWARD_REQUEST: |
|
|
break; |
break; |
} |
} |
debug("Received TCP/IP port forwarding request."); |
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; |
success = 1; |
break; |
break; |
|
|
|
|
if (packet_set_maxsize(packet_get_int()) > 0) |
if (packet_set_maxsize(packet_get_int()) > 0) |
success = 1; |
success = 1; |
break; |
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_integrity_check(plen, 4 + dlen, type); |
|
|
|
/* 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_integrity_check(plen, 4 + dlen, type); |
|
|
|
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_SHELL: |
case SSH_CMSG_EXEC_CMD: |
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) { |
if (type == SSH_CMSG_EXEC_CMD) { |
command = packet_get_string(&dlen); |
command = packet_get_string(&dlen); |
debug("Exec command '%.500s'", command); |
debug("Exec command '%.500s'", command); |
packet_integrity_check(plen, 4 + dlen, type); |
do_exec(s, command); |
|
xfree(command); |
} else { |
} else { |
command = NULL; |
do_exec(s, NULL); |
packet_integrity_check(plen, 0, type); |
|
} |
} |
if (forced_command != NULL) { |
packet_done(); |
original_command = command; |
session_close(s); |
command = forced_command; |
|
debug("Forced command '%.500s'", forced_command); |
|
} |
|
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); |
|
return; |
return; |
|
|
default: |
default: |
|
|
* setting up file descriptors and such. |
* setting up file descriptors and such. |
*/ |
*/ |
void |
void |
do_exec_no_pty(Session *s, const char *command, struct passwd * pw) |
do_exec_no_pty(Session *s, const char *command) |
{ |
{ |
int pid; |
int pid; |
|
|
|
|
#endif /* USE_PIPES */ |
#endif /* USE_PIPES */ |
|
|
/* Do processing for the child (exec command etc). */ |
/* 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 */ |
/* NOTREACHED */ |
} |
} |
if (pid < 0) |
if (pid < 0) |
packet_disconnect("fork failed: %.100s", strerror(errno)); |
packet_disconnect("fork failed: %.100s", strerror(errno)); |
s->pid = pid; |
s->pid = pid; |
|
/* Set interactive/non-interactive mode. */ |
|
packet_set_interactive(s->display != NULL); |
#ifdef USE_PIPES |
#ifdef USE_PIPES |
/* We are the parent. Close the child sides of the pipes. */ |
/* We are the parent. Close the child sides of the pipes. */ |
close(pin[0]); |
close(pin[0]); |
|
|
close(perr[1]); |
close(perr[1]); |
|
|
if (compat20) { |
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 { |
} else { |
/* Enter the interactive session. */ |
/* Enter the interactive session. */ |
server_loop(pid, pin[1], pout[0], perr[0]); |
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 */ |
#else /* USE_PIPES */ |
/* We are the parent. Close the child sides of the socket pairs. */ |
/* We are the parent. Close the child sides of the socket pairs. */ |
|
|
* handle the case that fdin and fdout are the same. |
* handle the case that fdin and fdout are the same. |
*/ |
*/ |
if (compat20) { |
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 { |
} else { |
server_loop(pid, inout[1], inout[1], err[1]); |
server_loop(pid, inout[1], inout[1], err[1]); |
/* server_loop has closed inout[1] and err[1]. */ |
/* server_loop has closed inout[1] and err[1]. */ |
|
|
* lastlog, and other such operations. |
* lastlog, and other such operations. |
*/ |
*/ |
void |
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; |
int fdout, ptyfd, ttyfd, ptymaster; |
pid_t pid; |
pid_t pid; |
|
|
|
|
/* Fork the child. */ |
/* Fork the child. */ |
if ((pid = fork()) == 0) { |
if ((pid = fork()) == 0) { |
|
|
/* Child. Reinitialize the log because the pid has changed. */ |
/* Child. Reinitialize the log because the pid has changed. */ |
log_init(__progname, options.log_level, options.log_facility, log_stderr); |
log_init(__progname, options.log_level, options.log_facility, log_stderr); |
|
|
/* Close the master side of the pseudo tty. */ |
/* Close the master side of the pseudo tty. */ |
close(ptyfd); |
close(ptyfd); |
|
|
|
|
do_login(s, command); |
do_login(s, command); |
|
|
/* Do common processing for the child, such as execing the command. */ |
/* Do common processing for the child, such as execing the command. */ |
do_child(command, pw, s->term, s->display, s->auth_proto, |
do_child(s, command); |
s->auth_data, s->tty); |
|
/* NOTREACHED */ |
/* NOTREACHED */ |
} |
} |
if (pid < 0) |
if (pid < 0) |
|
|
s->ptymaster = ptymaster; |
s->ptymaster = ptymaster; |
|
|
/* Enter interactive session. */ |
/* Enter interactive session. */ |
|
packet_set_interactive(1); |
if (compat20) { |
if (compat20) { |
session_set_fds(s, ptyfd, fdout, -1); |
session_set_fds(s, ptyfd, fdout, -1); |
} else { |
} else { |
server_loop(pid, ptyfd, fdout, -1); |
server_loop(pid, ptyfd, fdout, -1); |
/* server_loop _has_ closed ptyfd and fdout. */ |
/* server_loop _has_ closed ptyfd and fdout. */ |
session_pty_cleanup(s); |
|
} |
} |
} |
} |
|
|
const char * |
/* |
get_remote_name_or_ip(void) |
* 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 (forced_command) { |
if (utmp_len > 0) |
original_command = command; |
remote = get_canonical_hostname(); |
command = forced_command; |
if (utmp_len == 0 || strlen(remote) > utmp_len) |
debug("Forced command '%.900s'", command); |
remote = get_remote_ipaddr(); |
} |
return remote; |
|
|
if (s->ttyfd != -1) |
|
do_exec_pty(s, command); |
|
else |
|
do_exec_no_pty(s, command); |
|
|
|
original_command = NULL; |
} |
} |
|
|
|
|
/* administrative, login(1)-like work */ |
/* administrative, login(1)-like work */ |
void |
void |
do_login(Session *s, const char *command) |
do_login(Session *s, const char *command) |
{ |
{ |
FILE *f; |
|
char *time_string; |
char *time_string; |
char buf[256]; |
|
char hostname[MAXHOSTNAMELEN]; |
char hostname[MAXHOSTNAMELEN]; |
socklen_t fromlen; |
socklen_t fromlen; |
struct sockaddr_storage from; |
struct sockaddr_storage from; |
struct stat st; |
|
time_t last_login_time; |
time_t last_login_time; |
struct passwd * pw = s->pw; |
struct passwd * pw = s->pw; |
pid_t pid = getpid(); |
pid_t pid = getpid(); |
|
|
} |
} |
|
|
/* Get the time and hostname when the user last logged in. */ |
/* Get the time and hostname when the user last logged in. */ |
hostname[0] = '\0'; |
if (options.print_lastlog) { |
last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, |
hostname[0] = '\0'; |
hostname, sizeof(hostname)); |
last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, |
|
hostname, sizeof(hostname)); |
|
} |
|
|
/* Record that there was a login on that tty from the remote host. */ |
/* Record that there was a login on that tty from the remote host. */ |
record_login(pid, s->tty, pw->pw_name, pw->pw_uid, |
record_login(pid, s->tty, pw->pw_name, pw->pw_uid, |
get_remote_name_or_ip(), (struct sockaddr *)&from); |
get_remote_name_or_ip(utmp_len, options.reverse_mapping_check), |
|
(struct sockaddr *)&from); |
|
|
/* Done if .hushlogin exists or a command given. */ |
if (check_quietlogin(s, command)) |
if (command != NULL) |
|
return; |
return; |
snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); |
|
#ifdef HAVE_LOGIN_CAP |
if (options.print_lastlog && last_login_time != 0) { |
if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) |
|
#else |
|
if (stat(buf, &st) >= 0) |
|
#endif |
|
return; |
|
if (last_login_time != 0) { |
|
time_string = ctime(&last_login_time); |
time_string = ctime(&last_login_time); |
if (strchr(time_string, '\n')) |
if (strchr(time_string, '\n')) |
*strchr(time_string, '\n') = 0; |
*strchr(time_string, '\n') = 0; |
|
|
else |
else |
printf("Last login: %s from %s\r\n", time_string, hostname); |
printf("Last login: %s from %s\r\n", time_string, hostname); |
} |
} |
|
|
|
do_motd(); |
|
} |
|
|
|
/* |
|
* Display the message of the day. |
|
*/ |
|
void |
|
do_motd(void) |
|
{ |
|
FILE *f; |
|
char buf[256]; |
|
|
if (options.print_motd) { |
if (options.print_motd) { |
#ifdef HAVE_LOGIN_CAP |
#ifdef HAVE_LOGIN_CAP |
f = fopen(login_getcapstr(lc, "welcome", "/etc/motd", |
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 |
* Sets the value of the given variable in the environment. If the variable |
* already exists, its value is overriden. |
* already exists, its value is overriden. |
*/ |
*/ |
void |
static void |
child_set_env(char ***envp, unsigned int *envsizep, const char *name, |
child_set_env(char ***envp, u_int *envsizep, const char *name, |
const char *value) |
const char *value) |
{ |
{ |
unsigned int i, namelen; |
u_int i, namelen; |
char **env; |
char **env; |
|
|
/* |
/* |
|
|
* Otherwise, it must consist of empty lines, comments (line starts with '#') |
* Otherwise, it must consist of empty lines, comments (line starts with '#') |
* and assignments of the form name=value. No other forms are allowed. |
* and assignments of the form name=value. No other forms are allowed. |
*/ |
*/ |
void |
static void |
read_environment_file(char ***env, unsigned int *envsize, |
read_environment_file(char ***env, u_int *envsize, |
const char *filename) |
const char *filename) |
{ |
{ |
FILE *f; |
FILE *f; |
|
|
* ids, and executing the command or shell. |
* ids, and executing the command or shell. |
*/ |
*/ |
void |
void |
do_child(const char *command, struct passwd * pw, const char *term, |
do_child(Session *s, const char *command) |
const char *display, const char *auth_proto, |
|
const char *auth_data, const char *ttyname) |
|
{ |
{ |
const char *shell, *hostname = NULL, *cp = NULL; |
const char *shell, *hostname = NULL, *cp = NULL; |
|
struct passwd *pw = s->pw; |
char buf[256]; |
char buf[256]; |
char cmd[1024]; |
char cmd[1024]; |
FILE *f = NULL; |
FILE *f = NULL; |
unsigned int envsize, i; |
u_int envsize, i; |
char **env; |
char **env; |
extern char **environ; |
extern char **environ; |
struct stat st; |
struct stat st; |
char *argv[10]; |
char *argv[10]; |
|
int do_xauth; |
|
|
|
do_xauth = |
|
s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL; |
|
|
|
/* remove hostkey from the child's memory */ |
|
destroy_sensitive_data(); |
|
|
/* login(1) is only called if we execute the login shell */ |
/* login(1) is only called if we execute the login shell */ |
if (options.use_login && command != NULL) |
if (options.use_login && command != NULL) |
options.use_login = 0; |
options.use_login = 0; |
|
|
(LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) { |
(LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) { |
perror("unable to set user context"); |
perror("unable to set user context"); |
exit(1); |
exit(1); |
|
|
} |
} |
#else |
#else |
if (setlogin(pw->pw_name) < 0) |
if (setlogin(pw->pw_name) < 0) |
|
|
endgrent(); |
endgrent(); |
|
|
/* Permanently switch to the desired uid. */ |
/* Permanently switch to the desired uid. */ |
permanently_set_uid(pw->pw_uid); |
permanently_set_uid(pw); |
#endif |
#endif |
} |
} |
if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) |
if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) |
|
|
shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); |
shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); |
#endif |
#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. */ |
/* Initialize the environment. */ |
envsize = 100; |
envsize = 100; |
env = xmalloc(envsize * sizeof(char *)); |
env = xmalloc(envsize * sizeof(char *)); |
|
|
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); |
child_set_env(&env, &envsize, "SSH_CLIENT", buf); |
|
|
if (ttyname) |
if (s->ttyfd != -1) |
child_set_env(&env, &envsize, "SSH_TTY", ttyname); |
child_set_env(&env, &envsize, "SSH_TTY", s->tty); |
if (term) |
if (s->term) |
child_set_env(&env, &envsize, "TERM", term); |
child_set_env(&env, &envsize, "TERM", s->term); |
if (display) |
if (s->display) |
child_set_env(&env, &envsize, "DISPLAY", display); |
child_set_env(&env, &envsize, "DISPLAY", s->display); |
if (original_command) |
if (original_command) |
child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", |
child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", |
original_command); |
original_command); |
|
|
#ifdef KRB4 |
#ifdef KRB4 |
{ |
if (s->authctxt->krb4_ticket_file) |
extern char *ticket; |
child_set_env(&env, &envsize, "KRBTKFILE", |
|
s->authctxt->krb4_ticket_file); |
if (ticket) |
#endif |
child_set_env(&env, &envsize, "KRBTKFILE", ticket); |
#ifdef KRB5 |
} |
if (s->authctxt->krb5_ticket_file) |
#endif /* KRB4 */ |
child_set_env(&env, &envsize, "KRB5CCNAME", |
|
s->authctxt->krb5_ticket_file); |
if (xauthfile) |
#endif |
child_set_env(&env, &envsize, "XAUTHORITY", xauthfile); |
|
if (auth_get_socket_name() != NULL) |
if (auth_get_socket_name() != NULL) |
child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, |
child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, |
auth_get_socket_name()); |
auth_get_socket_name()); |
|
|
} |
} |
/* we have to stash the hostname before we close our socket. */ |
/* we have to stash the hostname before we close our socket. */ |
if (options.use_login) |
if (options.use_login) |
hostname = get_remote_name_or_ip(); |
hostname = get_remote_name_or_ip(utmp_len, |
|
options.reverse_mapping_check); |
/* |
/* |
* Close the connection descriptors; note that this is the child, and |
* Close the connection descriptors; note that this is the child, and |
* the server will still have the socket open, and it is important |
* the server will still have the socket open, and it is important |
|
|
*/ |
*/ |
environ = env; |
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 */ |
|
|
/* |
/* |
* Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first |
* Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first |
* in this order). |
* in this order). |
*/ |
*/ |
if (!options.use_login) { |
if (!options.use_login) { |
if (stat(SSH_USER_RC, &st) >= 0) { |
/* 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) |
if (debug_flag) |
fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC); |
fprintf(stderr, "Running %s\n", cmd); |
|
f = popen(cmd, "w"); |
f = popen("/bin/sh " SSH_USER_RC, "w"); |
|
if (f) { |
if (f) { |
if (auth_proto != NULL && auth_data != NULL) |
if (do_xauth) |
fprintf(f, "%s %s\n", auth_proto, auth_data); |
fprintf(f, "%s %s\n", s->auth_proto, |
|
s->auth_data); |
pclose(f); |
pclose(f); |
} else |
} else |
fprintf(stderr, "Could not run %s\n", SSH_USER_RC); |
fprintf(stderr, "Could not run %s\n", |
} else if (stat(SSH_SYSTEM_RC, &st) >= 0) { |
_PATH_SSH_USER_RC); |
|
} else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) { |
if (debug_flag) |
if (debug_flag) |
fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC); |
fprintf(stderr, "Running %s %s\n", _PATH_BSHELL, |
|
_PATH_SSH_SYSTEM_RC); |
f = popen("/bin/sh " SSH_SYSTEM_RC, "w"); |
f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w"); |
if (f) { |
if (f) { |
if (auth_proto != NULL && auth_data != NULL) |
if (do_xauth) |
fprintf(f, "%s %s\n", auth_proto, auth_data); |
fprintf(f, "%s %s\n", s->auth_proto, |
|
s->auth_data); |
pclose(f); |
pclose(f); |
} else |
} else |
fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC); |
fprintf(stderr, "Could not run %s\n", |
} else if (options.xauth_location != NULL) { |
_PATH_SSH_SYSTEM_RC); |
|
} else if (do_xauth && options.xauth_location != NULL) { |
/* Add authority data to .Xauthority if appropriate. */ |
/* Add authority data to .Xauthority if appropriate. */ |
if (auth_proto != NULL && auth_data != NULL) { |
char *screen = strchr(s->display, ':'); |
char *screen = strchr(display, ':'); |
|
if (debug_flag) { |
if (debug_flag) { |
|
fprintf(stderr, |
|
"Running %.100s add " |
|
"%.100s %.100s %.100s\n", |
|
options.xauth_location, s->display, |
|
s->auth_proto, s->auth_data); |
|
if (screen != NULL) |
fprintf(stderr, |
fprintf(stderr, |
"Running %.100s add %.100s %.100s %.100s\n", |
"Adding %.*s/unix%s %s %s\n", |
options.xauth_location, display, |
(int)(screen - s->display), |
auth_proto, auth_data); |
s->display, screen, |
if (screen != NULL) |
s->auth_proto, s->auth_data); |
fprintf(stderr, |
|
"Adding %.*s/unix%s %s %s\n", |
|
(int)(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", |
|
(int)(screen-display), display, |
|
screen, auth_proto, auth_data); |
|
pclose(f); |
|
} else { |
|
fprintf(stderr, "Could not run %s\n", |
|
cmd); |
|
} |
|
} |
} |
|
snprintf(cmd, sizeof cmd, "%s -q -", |
|
options.xauth_location); |
|
f = popen(cmd, "w"); |
|
if (f) { |
|
fprintf(f, "add %s %s %s\n", s->display, |
|
s->auth_proto, s->auth_data); |
|
if (screen != NULL) |
|
fprintf(f, "add %.*s/unix%s %s %s\n", |
|
(int)(screen - s->display), |
|
s->display, screen, |
|
s->auth_proto, |
|
s->auth_data); |
|
pclose(f); |
|
} else { |
|
fprintf(stderr, "Could not run %s\n", |
|
cmd); |
|
} |
} |
} |
/* Get the last component of the shell name. */ |
/* Get the last component of the shell name. */ |
cp = strrchr(shell, '/'); |
cp = strrchr(shell, '/'); |
|
|
else |
else |
cp = shell; |
cp = shell; |
} |
} |
|
|
|
/* restore SIGPIPE for child */ |
|
signal(SIGPIPE, SIG_DFL); |
|
|
/* |
/* |
* If we have no command, execute the shell. In this case, the 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 |
* name to be passed in argv[0] is preceded by '-' to indicate that |
|
|
if (!options.use_login) { |
if (!options.use_login) { |
char buf[256]; |
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 '-'. */ |
/* Start the shell. Set initial character to '-'. */ |
buf[0] = '-'; |
buf[0] = '-'; |
strncpy(buf + 1, cp, sizeof(buf) - 1); |
strncpy(buf + 1, cp, sizeof(buf) - 1); |
|
|
/* Launch login(1). */ |
/* Launch login(1). */ |
|
|
execl("/usr/bin/login", "login", "-h", hostname, |
execl("/usr/bin/login", "login", "-h", hostname, |
"-p", "-f", "--", pw->pw_name, NULL); |
"-p", "-f", "--", pw->pw_name, (char *)NULL); |
|
|
/* Login couldn't be executed, die. */ |
/* Login couldn't be executed, die. */ |
|
|
|
|
debug("session_new: 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].used = 0; |
sessions[i].self = i; |
|
} |
} |
did_init = 1; |
did_init = 1; |
} |
} |
for(i = 0; i < MAX_SESSIONS; i++) { |
for(i = 0; i < MAX_SESSIONS; i++) { |
Session *s = &sessions[i]; |
Session *s = &sessions[i]; |
if (! s->used) { |
if (! s->used) { |
s->pid = 0; |
memset(s, 0, sizeof(*s)); |
s->extended = 0; |
|
s->chanid = -1; |
s->chanid = -1; |
s->ptyfd = -1; |
s->ptyfd = -1; |
s->ttyfd = -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->used = 1; |
s->pw = NULL; |
s->self = i; |
debug("session_new: session %d", i); |
debug("session_new: session %d", i); |
return s; |
return s; |
} |
} |
|
|
return NULL; |
return NULL; |
} |
} |
|
|
void |
static void |
session_dump(void) |
session_dump(void) |
{ |
{ |
int i; |
int i; |
|
|
} |
} |
|
|
int |
int |
session_open(int chanid) |
session_open(Authctxt *authctxt, int chanid) |
{ |
{ |
Session *s = session_new(); |
Session *s = session_new(); |
debug("session_open: channel %d", chanid); |
debug("session_open: channel %d", chanid); |
|
|
error("no more sessions"); |
error("no more sessions"); |
return 0; |
return 0; |
} |
} |
s->pw = auth_get_user(); |
s->authctxt = authctxt; |
|
s->pw = authctxt->pw; |
if (s->pw == NULL) |
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); |
debug("session_open: session %d: link with channel %d", s->self, chanid); |
s->chanid = chanid; |
s->chanid = chanid; |
return 1; |
return 1; |
} |
} |
|
|
Session * |
static Session * |
session_by_channel(int id) |
session_by_channel(int id) |
{ |
{ |
int i; |
int i; |
|
|
return NULL; |
return NULL; |
} |
} |
|
|
Session * |
static Session * |
session_by_pid(pid_t pid) |
session_by_pid(pid_t pid) |
{ |
{ |
int i; |
int i; |
|
|
return NULL; |
return NULL; |
} |
} |
|
|
int |
static int |
session_window_change_req(Session *s) |
session_window_change_req(Session *s) |
{ |
{ |
s->col = packet_get_int(); |
s->col = packet_get_int(); |
|
|
return 1; |
return 1; |
} |
} |
|
|
int |
static int |
session_pty_req(Session *s) |
session_pty_req(Session *s) |
{ |
{ |
unsigned int len; |
u_int len; |
char *term_modes; /* encoded terminal modes */ |
int n_bytes; |
|
|
if (no_pty_flag) |
if (no_pty_flag) { |
|
debug("Allocating a pty not permitted for this authentication."); |
return 0; |
return 0; |
if (s->ttyfd != -1) |
} |
|
if (s->ttyfd != -1) { |
|
packet_disconnect("Protocol error: you already have a pty."); |
return 0; |
return 0; |
|
} |
|
|
s->term = packet_get_string(&len); |
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->xpixel = packet_get_int(); |
s->ypixel = packet_get_int(); |
s->ypixel = packet_get_int(); |
term_modes = packet_get_string(&len); |
|
packet_done(); |
|
|
|
if (strcmp(s->term, "") == 0) { |
if (strcmp(s->term, "") == 0) { |
xfree(s->term); |
xfree(s->term); |
s->term = NULL; |
s->term = NULL; |
} |
} |
|
|
/* Allocate a pty and open it. */ |
/* Allocate a pty and open it. */ |
|
debug("Allocating pty."); |
if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) { |
if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) { |
xfree(s->term); |
if (s->term) |
|
xfree(s->term); |
s->term = NULL; |
s->term = NULL; |
s->ptyfd = -1; |
s->ptyfd = -1; |
s->ttyfd = -1; |
s->ttyfd = -1; |
error("session_pty_req: session %d alloc failed", s->self); |
error("session_pty_req: session %d alloc failed", s->self); |
xfree(term_modes); |
|
return 0; |
return 0; |
} |
} |
debug("session_pty_req: session %d alloc %s", s->self, s->tty); |
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 |
* Add a cleanup function to clear the utmp entry and record logout |
* time in case we call fatal() (e.g., the connection gets closed). |
* time in case we call fatal() (e.g., the connection gets closed). |
*/ |
*/ |
fatal_add_cleanup(pty_cleanup_proc, (void *)s); |
fatal_add_cleanup(session_pty_cleanup, (void *)s); |
pty_setowner(s->pw, s->tty); |
pty_setowner(s->pw, s->tty); |
/* Get window size from the packet. */ |
|
|
/* Set window size from the packet. */ |
pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); |
pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); |
|
|
|
packet_done(); |
session_proctitle(s); |
session_proctitle(s); |
|
|
/* XXX parse and set terminal modes */ |
|
xfree(term_modes); |
|
return 1; |
return 1; |
} |
} |
|
|
int |
static int |
session_subsystem_req(Session *s) |
session_subsystem_req(Session *s) |
{ |
{ |
unsigned int len; |
u_int len; |
int success = 0; |
int success = 0; |
char *subsys = packet_get_string(&len); |
char *subsys = packet_get_string(&len); |
int i; |
int i; |
|
|
for (i = 0; i < options.num_subsystems; i++) { |
for (i = 0; i < options.num_subsystems; i++) { |
if(strcmp(subsys, options.subsystem_name[i]) == 0) { |
if(strcmp(subsys, options.subsystem_name[i]) == 0) { |
debug("subsystem: exec() %s", options.subsystem_command[i]); |
debug("subsystem: exec() %s", options.subsystem_command[i]); |
do_exec_no_pty(s, options.subsystem_command[i], s->pw); |
s->is_subsystem = 1; |
|
do_exec(s, options.subsystem_command[i]); |
success = 1; |
success = 1; |
} |
} |
} |
} |
|
|
return success; |
return success; |
} |
} |
|
|
int |
static int |
session_x11_req(Session *s) |
session_x11_req(Session *s) |
{ |
{ |
int fd; |
int success; |
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."); |
|
|
|
s->single_connection = packet_get_char(); |
s->single_connection = packet_get_char(); |
s->auth_proto = packet_get_string(NULL); |
s->auth_proto = packet_get_string(NULL); |
s->auth_data = packet_get_string(NULL); |
s->auth_data = packet_get_string(NULL); |
s->screen = packet_get_int(); |
s->screen = packet_get_int(); |
packet_done(); |
packet_done(); |
|
|
s->display = x11_create_display_inet(s->screen, options.x11_display_offset); |
success = session_setup_x11fwd(s); |
if (s->display == NULL) { |
if (!success) { |
xfree(s->auth_proto); |
xfree(s->auth_proto); |
xfree(s->auth_data); |
xfree(s->auth_data); |
return 0; |
s->auth_proto = NULL; |
|
s->auth_data = NULL; |
} |
} |
xauthfile = xmalloc(MAXPATHLEN); |
return success; |
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; |
|
xfree(s->auth_proto); |
|
xfree(s->auth_data); |
|
/* XXXX remove listening channels */ |
|
return 0; |
|
} |
|
strlcat(xauthfile, "/cookies", MAXPATHLEN); |
|
fd = open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); |
|
if (fd >= 0) |
|
close(fd); |
|
restore_uid(); |
|
fatal_add_cleanup(xauthfile_cleanup_proc, s); |
|
return 1; |
|
} |
} |
|
|
int |
static int |
session_shell_req(Session *s) |
session_shell_req(Session *s) |
{ |
{ |
/* if forced_command == NULL, the shell is execed */ |
|
char *shell = forced_command; |
|
packet_done(); |
packet_done(); |
s->extended = 1; |
do_exec(s, NULL); |
if (s->ttyfd == -1) |
|
do_exec_no_pty(s, shell, s->pw); |
|
else |
|
do_exec_pty(s, shell, s->pw); |
|
return 1; |
return 1; |
} |
} |
|
|
int |
static int |
session_exec_req(Session *s) |
session_exec_req(Session *s) |
{ |
{ |
unsigned int len; |
u_int len; |
char *command = packet_get_string(&len); |
char *command = packet_get_string(&len); |
packet_done(); |
packet_done(); |
if (forced_command) { |
do_exec(s, command); |
original_command = 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); |
|
return 1; |
return 1; |
} |
} |
|
|
|
static int |
|
session_auth_agent_req(Session *s) |
|
{ |
|
static int called = 0; |
|
packet_done(); |
|
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); |
|
} |
|
} |
|
|
void |
void |
session_input_channel_req(int id, void *arg) |
session_input_channel_req(int id, void *arg) |
{ |
{ |
unsigned int len; |
u_int len; |
int reply; |
int reply; |
int success = 0; |
int success = 0; |
char *rtype; |
char *rtype; |
|
|
s->self, id, rtype, reply); |
s->self, id, rtype, reply); |
|
|
/* |
/* |
* a session is in LARVAL state until a shell |
* a session is in LARVAL state until a shell, a command |
* or programm is executed |
* or a subsystem is executed |
*/ |
*/ |
if (c->type == SSH_CHANNEL_LARVAL) { |
if (c->type == SSH_CHANNEL_LARVAL) { |
if (strcmp(rtype, "shell") == 0) { |
if (strcmp(rtype, "shell") == 0) { |
|
|
success = session_pty_req(s); |
success = session_pty_req(s); |
} else if (strcmp(rtype, "x11-req") == 0) { |
} else if (strcmp(rtype, "x11-req") == 0) { |
success = session_x11_req(s); |
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) { |
} else if (strcmp(rtype, "subsystem") == 0) { |
success = session_subsystem_req(s); |
success = session_subsystem_req(s); |
} |
} |
|
|
1); |
1); |
} |
} |
|
|
void |
/* |
session_pty_cleanup(Session *s) |
* Function to perform pty cleanup. Also called if we get aborted abnormally |
|
* (e.g., due to a dropped connection). |
|
*/ |
|
static void |
|
session_pty_cleanup(void *session) |
{ |
{ |
if (s == NULL || s->ttyfd == -1) |
Session *s = session; |
|
|
|
if (s == NULL) { |
|
error("session_pty_cleanup: no session"); |
return; |
return; |
|
} |
|
if (s->ttyfd == -1) |
|
return; |
|
|
debug("session_pty_cleanup: session %i release %s", s->self, s->tty); |
debug("session_pty_cleanup: session %d release %s", s->self, s->tty); |
|
|
/* Cancel the cleanup function. */ |
|
fatal_remove_cleanup(pty_cleanup_proc, (void *)s); |
|
|
|
/* Record that the user has logged out. */ |
/* Record that the user has logged out. */ |
record_logout(s->pid, s->tty); |
if (s->pid != 0) |
|
record_logout(s->pid, s->tty); |
|
|
/* Release the pseudo-tty. */ |
/* Release the pseudo-tty. */ |
pty_release(s->tty); |
pty_release(s->tty); |
|
|
error("close(s->ptymaster): %s", strerror(errno)); |
error("close(s->ptymaster): %s", strerror(errno)); |
} |
} |
|
|
void |
static void |
session_exit_message(Session *s, int status) |
session_exit_message(Session *s, int status) |
{ |
{ |
Channel *c; |
Channel *c; |
|
|
fatal("session_close: no session"); |
fatal("session_close: no session"); |
c = channel_lookup(s->chanid); |
c = channel_lookup(s->chanid); |
if (c == NULL) |
if (c == NULL) |
fatal("session_close: session %d: no channel %d", |
fatal("session_exit_message: session %d: no channel %d", |
s->self, s->chanid); |
s->self, s->chanid); |
debug("session_exit_message: session %d channel %d pid %d", |
debug("session_exit_message: session %d channel %d pid %d", |
s->self, s->chanid, s->pid); |
s->self, s->chanid, s->pid); |
|
|
s->chanid = -1; |
s->chanid = -1; |
} |
} |
|
|
void |
static 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 %d", s->self, s->pid); |
|
if (s->ttyfd != -1) { |
|
fatal_remove_cleanup(session_pty_cleanup, (void *)s); |
|
session_pty_cleanup(s); |
|
} |
if (s->term) |
if (s->term) |
xfree(s->term); |
xfree(s->term); |
if (s->display) |
if (s->display) |
|
|
if (s->auth_proto) |
if (s->auth_proto) |
xfree(s->auth_proto); |
xfree(s->auth_proto); |
s->used = 0; |
s->used = 0; |
} |
|
|
|
void |
|
session_close(Session *s) |
|
{ |
|
session_pty_cleanup(s); |
|
session_free(s); |
|
session_proctitle(s); |
session_proctitle(s); |
} |
} |
|
|
|
|
{ |
{ |
Session *s = session_by_pid(pid); |
Session *s = session_by_pid(pid); |
if (s == NULL) { |
if (s == NULL) { |
debug("session_close_by_pid: no session for pid %d", s->pid); |
debug("session_close_by_pid: no session for pid %d", pid); |
return; |
return; |
} |
} |
if (s->chanid != -1) |
if (s->chanid != -1) |
|
|
session_close(s); |
session_close(s); |
} |
} |
|
|
|
int |
|
session_have_children(void) |
|
{ |
|
int i; |
|
|
|
for(i = 0; i < MAX_SESSIONS; i++) { |
|
Session *s = &sessions[i]; |
|
if (s->used && s->pid != -1) { |
|
debug("session_have_children: id %d pid %d", i, s->pid); |
|
return 1; |
|
} |
|
} |
|
debug("session_have_children: no more children"); |
|
return 0; |
|
} |
|
|
/* |
/* |
* this is called when a channel dies before |
* this is called when a channel dies before |
* the session 'child' itself dies |
* the session 'child' itself dies |
|
|
} |
} |
} |
} |
|
|
char * |
static char * |
session_tty_list(void) |
session_tty_list(void) |
{ |
{ |
static char buf[1024]; |
static char buf[1024]; |
|
|
setproctitle("%s@%s", s->pw->pw_name, session_tty_list()); |
setproctitle("%s@%s", s->pw->pw_name, session_tty_list()); |
} |
} |
|
|
void |
int |
do_authenticated2(void) |
session_setup_x11fwd(Session *s) |
{ |
{ |
struct passwd *pw; |
struct stat st; |
|
|
/* |
if (no_x11_forwarding_flag) { |
* Cancel the alarm we set to limit the time taken for |
packet_send_debug("X11 forwarding disabled in user configuration file."); |
* authentication. |
return 0; |
*/ |
|
alarm(0); |
|
if (startup_pipe != -1) { |
|
close(startup_pipe); |
|
startup_pipe = -1; |
|
} |
} |
#ifdef HAVE_LOGIN_CAP |
if (!options.x11_forwarding) { |
pw = auth_get_user(); |
debug("X11 forwarding disabled in server configuration file."); |
if ((lc = login_getclass(pw->pw_class)) == NULL) { |
return 0; |
error("unable to get login class"); |
|
return; |
|
} |
} |
#endif |
if (!options.xauth_location || |
server_loop2(); |
(stat(options.xauth_location, &st) == -1)) { |
if (xauthfile) |
packet_send_debug("No xauth program; cannot forward with spoofing."); |
xauthfile_cleanup_proc(NULL); |
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; |
|
} |
|
s->display = x11_create_display_inet(s->screen, options.x11_display_offset); |
|
if (s->display == NULL) { |
|
debug("x11_create_display_inet failed."); |
|
return 0; |
|
} |
|
return 1; |
|
} |
|
|
|
static void |
|
do_authenticated2(Authctxt *authctxt) |
|
{ |
|
server_loop2(authctxt); |
} |
} |