version 1.186.2.3, 2006/11/08 00:44:05 |
version 1.187, 2005/10/10 10:23:08 |
|
|
/* $OpenBSD$ */ |
|
/* |
/* |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
* All rights reserved |
* All rights reserved |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
*/ |
|
|
#include <sys/types.h> |
#include "includes.h" |
#include <sys/wait.h> |
RCSID("$OpenBSD$"); |
#include <sys/un.h> |
|
#include <sys/stat.h> |
|
#include <sys/socket.h> |
|
#include <sys/param.h> |
|
|
|
#include <errno.h> |
|
#include <grp.h> |
|
#include <paths.h> |
|
#include <pwd.h> |
|
#include <signal.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
|
|
#include "xmalloc.h" |
|
#include "ssh.h" |
#include "ssh.h" |
#include "ssh1.h" |
#include "ssh1.h" |
#include "ssh2.h" |
#include "ssh2.h" |
|
#include "xmalloc.h" |
#include "sshpty.h" |
#include "sshpty.h" |
#include "packet.h" |
#include "packet.h" |
#include "buffer.h" |
#include "buffer.h" |
|
|
#include "uidswap.h" |
#include "uidswap.h" |
#include "compat.h" |
#include "compat.h" |
#include "channels.h" |
#include "channels.h" |
#include "key.h" |
#include "bufaux.h" |
#include "cipher.h" |
|
#include "kex.h" |
|
#include "hostfile.h" |
|
#include "auth.h" |
#include "auth.h" |
#include "auth-options.h" |
#include "auth-options.h" |
#include "pathnames.h" |
#include "pathnames.h" |
|
|
#include "serverloop.h" |
#include "serverloop.h" |
#include "canohost.h" |
#include "canohost.h" |
#include "session.h" |
#include "session.h" |
#ifdef GSSAPI |
#include "kex.h" |
#include "ssh-gss.h" |
|
#endif |
|
#include "monitor_wrap.h" |
#include "monitor_wrap.h" |
|
|
#ifdef KRB5 |
#ifdef KRB5 |
#include <kafs.h> |
#include <kafs.h> |
#endif |
#endif |
|
|
|
#ifdef GSSAPI |
|
#include "ssh-gss.h" |
|
#endif |
|
|
/* func */ |
/* func */ |
|
|
Session *session_new(void); |
Session *session_new(void); |
|
|
sunaddr.sun_family = AF_UNIX; |
sunaddr.sun_family = AF_UNIX; |
strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); |
strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); |
|
|
if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) |
if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) |
packet_disconnect("bind: %.100s", strerror(errno)); |
packet_disconnect("bind: %.100s", strerror(errno)); |
|
|
/* Restore the privileged uid. */ |
/* Restore the privileged uid. */ |
|
|
{ |
{ |
setproctitle("%s", authctxt->pw->pw_name); |
setproctitle("%s", authctxt->pw->pw_name); |
|
|
|
/* |
|
* Cancel the alarm we set to limit the time taken for |
|
* authentication. |
|
*/ |
|
alarm(0); |
|
if (startup_pipe != -1) { |
|
close(startup_pipe); |
|
startup_pipe = -1; |
|
} |
/* setup the channel layer */ |
/* setup the channel layer */ |
if (!no_port_forwarding_flag && options.allow_tcp_forwarding) |
if (!no_port_forwarding_flag && options.allow_tcp_forwarding) |
channel_permit_all_opens(); |
channel_permit_all_opens(); |
|
|
break; |
break; |
} |
} |
debug("Received TCP/IP port forwarding request."); |
debug("Received TCP/IP port forwarding request."); |
if (channel_input_port_forward_request(s->pw->pw_uid == 0, |
channel_input_port_forward_request(s->pw->pw_uid == 0, options.gateway_ports); |
options.gateway_ports) < 0) { |
|
debug("Port forwarding failed."); |
|
break; |
|
} |
|
success = 1; |
success = 1; |
break; |
break; |
|
|
|
|
{ |
{ |
pid_t pid; |
pid_t pid; |
|
|
|
#ifdef USE_PIPES |
|
int pin[2], pout[2], perr[2]; |
|
/* Allocate pipes for communicating with the program. */ |
|
if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0) |
|
packet_disconnect("Could not create pipes: %.100s", |
|
strerror(errno)); |
|
#else /* USE_PIPES */ |
int inout[2], err[2]; |
int inout[2], err[2]; |
/* Uses socket pairs to communicate with the program. */ |
/* Uses socket pairs to communicate with the program. */ |
if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || |
if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || |
socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) |
socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) |
packet_disconnect("Could not create socket pairs: %.100s", |
packet_disconnect("Could not create socket pairs: %.100s", |
strerror(errno)); |
strerror(errno)); |
|
#endif /* USE_PIPES */ |
if (s == NULL) |
if (s == NULL) |
fatal("do_exec_no_pty: no session"); |
fatal("do_exec_no_pty: no session"); |
|
|
|
|
if (setsid() < 0) |
if (setsid() < 0) |
error("setsid failed: %.100s", strerror(errno)); |
error("setsid failed: %.100s", strerror(errno)); |
|
|
|
#ifdef USE_PIPES |
/* |
/* |
|
* Redirect stdin. We close the parent side of the socket |
|
* pair, and make the child side the standard input. |
|
*/ |
|
close(pin[1]); |
|
if (dup2(pin[0], 0) < 0) |
|
perror("dup2 stdin"); |
|
close(pin[0]); |
|
|
|
/* Redirect stdout. */ |
|
close(pout[0]); |
|
if (dup2(pout[1], 1) < 0) |
|
perror("dup2 stdout"); |
|
close(pout[1]); |
|
|
|
/* Redirect stderr. */ |
|
close(perr[0]); |
|
if (dup2(perr[1], 2) < 0) |
|
perror("dup2 stderr"); |
|
close(perr[1]); |
|
#else /* USE_PIPES */ |
|
/* |
* Redirect stdin, stdout, and stderr. Stdin and stdout will |
* Redirect stdin, stdout, and stderr. Stdin and stdout will |
* use the same socket, as some programs (particularly rdist) |
* use the same socket, as some programs (particularly rdist) |
* seem to depend on it. |
* seem to depend on it. |
|
|
perror("dup2 stdout"); |
perror("dup2 stdout"); |
if (dup2(err[0], 2) < 0) /* stderr */ |
if (dup2(err[0], 2) < 0) /* stderr */ |
perror("dup2 stderr"); |
perror("dup2 stderr"); |
|
#endif /* USE_PIPES */ |
|
|
/* Do processing for the child (exec command etc). */ |
/* Do processing for the child (exec command etc). */ |
do_child(s, command); |
do_child(s, command); |
|
|
s->pid = pid; |
s->pid = pid; |
/* Set interactive/non-interactive mode. */ |
/* Set interactive/non-interactive mode. */ |
packet_set_interactive(s->display != NULL); |
packet_set_interactive(s->display != NULL); |
|
#ifdef USE_PIPES |
|
/* We are the parent. Close the child sides of the pipes. */ |
|
close(pin[0]); |
|
close(pout[1]); |
|
close(perr[1]); |
|
|
|
if (compat20) { |
|
if (s->is_subsystem) { |
|
close(perr[0]); |
|
perr[0] = -1; |
|
} |
|
session_set_fds(s, pin[1], pout[0], perr[0]); |
|
} else { |
|
/* Enter the interactive session. */ |
|
server_loop(pid, pin[1], pout[0], perr[0]); |
|
/* 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. */ |
/* We are the parent. Close the child sides of the socket pairs. */ |
close(inout[0]); |
close(inout[0]); |
close(err[0]); |
close(err[0]); |
|
|
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]. */ |
} |
} |
|
#endif /* USE_PIPES */ |
} |
} |
|
|
/* |
/* |
|
|
void |
void |
do_exec(Session *s, const char *command) |
do_exec(Session *s, const char *command) |
{ |
{ |
if (options.adm_forced_command) { |
if (forced_command) { |
original_command = command; |
original_command = command; |
command = options.adm_forced_command; |
|
debug("Forced command (config) '%.900s'", command); |
|
} else if (forced_command) { |
|
original_command = command; |
|
command = forced_command; |
command = forced_command; |
debug("Forced command (key option) '%.900s'", command); |
debug("Forced command '%.900s'", command); |
} |
} |
|
|
#ifdef GSSAPI |
#ifdef GSSAPI |
|
|
fromlen = sizeof(from); |
fromlen = sizeof(from); |
if (packet_connection_is_on_socket()) { |
if (packet_connection_is_on_socket()) { |
if (getpeername(packet_get_connection_in(), |
if (getpeername(packet_get_connection_in(), |
(struct sockaddr *)&from, &fromlen) < 0) { |
(struct sockaddr *) & from, &fromlen) < 0) { |
debug("getpeername: %.100s", strerror(errno)); |
debug("getpeername: %.100s", strerror(errno)); |
cleanup_exit(255); |
cleanup_exit(255); |
} |
} |
|
|
if (envsize >= 1000) |
if (envsize >= 1000) |
fatal("child_set_env: too many env vars"); |
fatal("child_set_env: too many env vars"); |
envsize += 50; |
envsize += 50; |
env = (*envp) = xrealloc(env, envsize, sizeof(char *)); |
env = (*envp) = xrealloc(env, envsize * sizeof(char *)); |
*envsizep = envsize; |
*envsizep = envsize; |
} |
} |
/* Need to set the NULL pointer at end of array beyond the new slot. */ |
/* Need to set the NULL pointer at end of array beyond the new slot. */ |
|
|
|
|
/* Initialize the environment. */ |
/* Initialize the environment. */ |
envsize = 100; |
envsize = 100; |
env = xcalloc(envsize, sizeof(char *)); |
env = xmalloc(envsize * sizeof(char *)); |
env[0] = NULL; |
env[0] = NULL; |
|
|
#ifdef GSSAPI |
#ifdef GSSAPI |
|
|
endpwent(); |
endpwent(); |
|
|
/* |
/* |
* Close any extra open file descriptors so that we don't have them |
* Close any extra open file descriptors so that we don\'t have them |
* hanging around in clients. Note that we want to do this after |
* hanging around in clients. Note that we want to do this after |
* initgroups, because at least on Solaris 2.3 it leaves file |
* initgroups, because at least on Solaris 2.3 it leaves file |
* descriptors open. |
* descriptors open. |
|
|
} |
} |
#endif |
#endif |
|
|
/* Change current directory to the user's home directory. */ |
/* Change current directory to the user\'s home directory. */ |
if (chdir(pw->pw_dir) < 0) { |
if (chdir(pw->pw_dir) < 0) { |
fprintf(stderr, "Could not chdir to home directory %s: %s\n", |
fprintf(stderr, "Could not chdir to home directory %s: %s\n", |
pw->pw_dir, strerror(errno)); |
pw->pw_dir, strerror(errno)); |
|
|
do_rc_files(s, shell); |
do_rc_files(s, shell); |
|
|
/* restore SIGPIPE for child */ |
/* restore SIGPIPE for child */ |
signal(SIGPIPE, SIG_DFL); |
signal(SIGPIPE, SIG_DFL); |
|
|
if (options.use_login) { |
if (options.use_login) { |
launch_login(pw, hostname); |
launch_login(pw, hostname); |
|
|
struct stat st; |
struct stat st; |
u_int len; |
u_int len; |
int success = 0; |
int success = 0; |
char *prog, *cmd, *subsys = packet_get_string(&len); |
char *cmd, *subsys = packet_get_string(&len); |
u_int i; |
u_int i; |
|
|
packet_check_eom(); |
packet_check_eom(); |
|
|
|
|
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) { |
prog = options.subsystem_command[i]; |
cmd = options.subsystem_command[i]; |
cmd = options.subsystem_args[i]; |
if (stat(cmd, &st) < 0) { |
if (stat(prog, &st) < 0) { |
error("subsystem: cannot stat %s: %s", cmd, |
error("subsystem: cannot stat %s: %s", prog, |
|
strerror(errno)); |
strerror(errno)); |
break; |
break; |
} |
} |
|
|
|
|
if (s->auth_proto != NULL || s->auth_data != NULL) { |
if (s->auth_proto != NULL || s->auth_data != NULL) { |
error("session_x11_req: session %d: " |
error("session_x11_req: session %d: " |
"x11 forwarding already active", s->self); |
"x11 fowarding already active", s->self); |
return 0; |
return 0; |
} |
} |
s->single_connection = packet_get_char(); |
s->single_connection = packet_get_char(); |
|
|
for (i = 0; i < options.num_accept_env; i++) { |
for (i = 0; i < options.num_accept_env; i++) { |
if (match_pattern(name, options.accept_env[i])) { |
if (match_pattern(name, options.accept_env[i])) { |
debug2("Setting env %d: %s=%s", s->num_env, name, val); |
debug2("Setting env %d: %s=%s", s->num_env, name, val); |
s->env = xrealloc(s->env, s->num_env + 1, |
s->env = xrealloc(s->env, sizeof(*s->env) * |
sizeof(*s->env)); |
(s->num_env + 1)); |
s->env[s->num_env].name = name; |
s->env[s->num_env].name = name; |
s->env[s->num_env].val = val; |
s->env[s->num_env].val = val; |
s->num_env++; |
s->num_env++; |
|
|
{ |
{ |
Channel *c; |
Channel *c; |
|
|
if ((c = channel_by_id(id)) == NULL) { |
if ((c = channel_lookup(id)) == NULL) { |
debug("session_close_x11: x11 channel %d missing", id); |
debug("session_close_x11: x11 channel %d missing", id); |
} else { |
} else { |
/* Detach X11 listener */ |
/* Detach X11 listener */ |
|
|
|
|
/* disconnect channel */ |
/* disconnect channel */ |
debug("session_exit_message: release channel %d", s->chanid); |
debug("session_exit_message: release channel %d", s->chanid); |
|
s->pid = 0; |
|
|
/* |
/* |
* Adjust cleanup callback attachment to send close messages when |
* Adjust cleanup callback attachment to send close messages when |
* the channel gets EOF. The session will be then be closed |
* the channel gets EOF. The session will be then be closed |
* by session_close_by_channel when the childs close their fds. |
* by session_close_by_channel when the childs close their fds. |
*/ |
*/ |
channel_register_cleanup(c->self, session_close_by_channel, 1); |
channel_register_cleanup(c->self, session_close_by_channel, 1); |
|
|
if (s->auth_proto) |
if (s->auth_proto) |
xfree(s->auth_proto); |
xfree(s->auth_proto); |
s->used = 0; |
s->used = 0; |
if (s->env != NULL) { |
for (i = 0; i < s->num_env; i++) { |
for (i = 0; i < s->num_env; i++) { |
xfree(s->env[i].name); |
xfree(s->env[i].name); |
xfree(s->env[i].val); |
xfree(s->env[i].val); |
|
} |
|
xfree(s->env); |
|
} |
} |
|
if (s->env != NULL) |
|
xfree(s->env); |
session_proctitle(s); |
session_proctitle(s); |
} |
} |
|
|
|
|
session_exit_message(s, status); |
session_exit_message(s, status); |
if (s->ttyfd != -1) |
if (s->ttyfd != -1) |
session_pty_cleanup(s); |
session_pty_cleanup(s); |
s->pid = 0; |
|
} |
} |
|
|
/* |
/* |
|
|
return; |
return; |
called = 1; |
called = 1; |
|
|
if (authctxt == NULL || !authctxt->authenticated) |
if (authctxt == NULL) |
return; |
return; |
#ifdef KRB5 |
#ifdef KRB5 |
if (options.kerberos_ticket_cleanup && |
if (options.kerberos_ticket_cleanup && |