version 1.42, 2000/10/27 07:32:18 |
version 1.42.2.3, 2001/03/21 19:46:28 |
|
|
* 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 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 "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 |
#ifdef HAVE_LOGIN_CAP |
#include <login_cap.h> |
#include <login_cap.h> |
|
|
void do_exec_pty(Session *s, const char *command, struct passwd * pw); |
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_exec_no_pty(Session *s, const char *command, struct passwd * pw); |
void do_login(Session *s, const char *command); |
void do_login(Session *s, const char *command); |
|
void do_child(Session *s, const char *command); |
|
|
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); |
|
|
|
/* 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; |
|
|
|
|
static char *xauthfile; |
static char *xauthfile; |
|
|
/* original command from peer. */ |
/* original command from peer. */ |
char *original_command = NULL; |
char *original_command = NULL; |
|
|
/* data */ |
/* data */ |
#define MAX_SESSIONS 10 |
#define MAX_SESSIONS 10 |
|
|
char *command; |
char *command; |
int n_bytes; |
int n_bytes; |
int plen; |
int plen; |
unsigned int proto_len, data_len, dlen; |
u_int proto_len, data_len, dlen; |
|
int screen_flag; |
|
|
/* |
/* |
* Cancel the alarm we set to limit the time taken for |
* Cancel the alarm we set to limit the time taken for |
|
|
startup_pipe = -1; |
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->pw = pw; |
|
|
|
if (!no_port_forwarding_flag && options.allow_tcp_forwarding) |
|
channel_permit_all_opens(); |
|
|
#ifdef HAVE_LOGIN_CAP |
#ifdef HAVE_LOGIN_CAP |
if ((lc = login_getclass(pw->pw_class)) == NULL) { |
if ((lc = login_getclass(pw->pw_class)) == NULL) { |
error("unable to get login class"); |
error("unable to get login class"); |
|
|
|
|
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"); |
|
packet_integrity_check(plen, |
|
4 + proto_len + 4 + data_len + 4, type); |
s->screen = packet_get_int(); |
s->screen = packet_get_int(); |
else |
} else { |
|
packet_integrity_check(plen, |
|
4 + proto_len + 4 + data_len, type); |
s->screen = 0; |
s->screen = 0; |
|
} |
s->display = x11_create_display_inet(s->screen, options.x11_display_offset); |
s->display = x11_create_display_inet(s->screen, options.x11_display_offset); |
|
|
if (s->display == NULL) |
if (s->display == NULL) |
|
|
|
|
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); |
|
|
#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]); |
|
|
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 { |
|
|
{ |
{ |
static const char *remote = ""; |
static const char *remote = ""; |
if (utmp_len > 0) |
if (utmp_len > 0) |
remote = get_canonical_hostname(); |
remote = get_canonical_hostname(options.reverse_mapping_check); |
if (utmp_len == 0 || strlen(remote) > utmp_len) |
if (utmp_len == 0 || strlen(remote) > utmp_len) |
remote = get_remote_ipaddr(); |
remote = get_remote_ipaddr(); |
return remote; |
return remote; |
|
|
* already exists, its value is overriden. |
* already exists, its value is overriden. |
*/ |
*/ |
void |
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; |
|
|
/* |
/* |
|
|
* 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 |
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 = s->auth_proto != NULL && s->auth_data != NULL; |
|
|
/* 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) |
|
|
(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) |
|
|
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); |
|
|
* in this order). |
* in this order). |
*/ |
*/ |
if (!options.use_login) { |
if (!options.use_login) { |
if (stat(SSH_USER_RC, &st) >= 0) { |
if (stat(_PATH_SSH_USER_RC, &st) >= 0) { |
if (debug_flag) |
if (debug_flag) |
fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC); |
fprintf(stderr, "Running %s %s\n", _PATH_BSHELL, |
|
_PATH_SSH_USER_RC); |
f = popen("/bin/sh " SSH_USER_RC, "w"); |
f = popen(_PATH_BSHELL " " _PATH_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, '/'); |
|
|
* Check for mail if we have a tty and it was enabled |
* Check for mail if we have a tty and it was enabled |
* in server options. |
* in server options. |
*/ |
*/ |
if (ttyname && options.check_mail) { |
if (s->ttyfd != -1 && options.check_mail) { |
char *mailbox; |
char *mailbox; |
struct stat mailstat; |
struct stat mailstat; |
|
|
mailbox = getenv("MAIL"); |
mailbox = getenv("MAIL"); |
if (mailbox != NULL) { |
if (mailbox != NULL) { |
if (stat(mailbox, &mailstat) != 0 || |
if (stat(mailbox, &mailstat) != 0 || |
|
|
} |
} |
s->pw = auth_get_user(); |
s->pw = auth_get_user(); |
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; |
|
|
int |
int |
session_pty_req(Session *s) |
session_pty_req(Session *s) |
{ |
{ |
unsigned int len; |
u_int len; |
char *term_modes; /* encoded terminal modes */ |
char *term_modes; /* encoded terminal modes */ |
|
|
if (no_pty_flag) |
if (no_pty_flag) |
|
|
int |
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; |
|
|
int |
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) { |
if (forced_command) { |
|
|
return 1; |
return 1; |
} |
} |
|
|
|
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; |
|
|
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); |
} |
} |
|
|
if (s == NULL || s->ttyfd == -1) |
if (s == NULL || s->ttyfd == -1) |
return; |
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. */ |
/* Cancel the cleanup function. */ |
fatal_remove_cleanup(pty_cleanup_proc, (void *)s); |
fatal_remove_cleanup(pty_cleanup_proc, (void *)s); |
|
|
} |
} |
|
|
void |
void |
do_authenticated2(void) |
do_authenticated2(Authctxt *authctxt) |
{ |
{ |
struct passwd *pw; |
|
|
|
/* |
/* |
* Cancel the alarm we set to limit the time taken for |
* Cancel the alarm we set to limit the time taken for |
* authentication. |
* authentication. |
|
|
close(startup_pipe); |
close(startup_pipe); |
startup_pipe = -1; |
startup_pipe = -1; |
} |
} |
|
if (!no_port_forwarding_flag && options.allow_tcp_forwarding) |
|
channel_permit_all_opens(); |
#ifdef HAVE_LOGIN_CAP |
#ifdef HAVE_LOGIN_CAP |
pw = auth_get_user(); |
if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL) { |
if ((lc = login_getclass(pw->pw_class)) == NULL) { |
|
error("unable to get login class"); |
error("unable to get login class"); |
return; |
return; |
} |
} |