[channels.c channels.h clientloop.c clientloop.h includes.h readconf.c]
     [readconf.h scp.1 sftp.1 ssh.1 ssh.c ssh_config.5]
     implement session multiplexing in the client (the server has supported
     this since 2.0); ok markus@
This commit is contained in:
Damien Miller 2004-06-15 10:34:08 +10:00
Родитель 05202ffe21
Коммит 0e220dbfbc
15 изменённых файлов: 652 добавлений и 144 удалений

Просмотреть файл

@ -24,6 +24,11 @@
[ssh.1 ssh_config.5 sshd_config.5]
List supported ciphers in man pages, tidy up ssh -c;
"looks fine" jmc@, ok markus@
- djm@cvs.openbsd.org 2004/06/13 15:03:02
[channels.c channels.h clientloop.c clientloop.h includes.h readconf.c]
[readconf.h scp.1 sftp.1 ssh.1 ssh.c ssh_config.5]
implement session multiplexing in the client (the server has supported
this since 2.0); ok markus@
20040603
- (dtucker) [auth-pam.c] Don't use pam_* namespace for sshd's PAM functions.
@ -1208,4 +1213,4 @@
- (djm) Trim deprecated options from INSTALL. Mention UsePAM
- (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu
$Id: ChangeLog,v 1.3381 2004/06/15 00:30:39 djm Exp $
$Id: ChangeLog,v 1.3382 2004/06/15 00:34:08 djm Exp $

Просмотреть файл

@ -39,7 +39,7 @@
*/
#include "includes.h"
RCSID("$OpenBSD: channels.c,v 1.203 2004/05/26 23:02:39 markus Exp $");
RCSID("$OpenBSD: channels.c,v 1.204 2004/06/13 15:03:02 djm Exp $");
#include "ssh.h"
#include "ssh1.h"
@ -172,6 +172,7 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd,
c->rfd = rfd;
c->wfd = wfd;
c->sock = (rfd == wfd) ? rfd : -1;
c->ctl_fd = -1; /* XXX: set elsewhere */
c->efd = efd;
c->extended_usage = extusage;
@ -263,6 +264,7 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd,
c->single_connection = 0;
c->detach_user = NULL;
c->confirm = NULL;
c->confirm_ctx = NULL;
c->input_filter = NULL;
debug("channel %d: new [%s]", found, remote_name);
return c;
@ -304,10 +306,11 @@ channel_close_fd(int *fdp)
static void
channel_close_fds(Channel *c)
{
debug3("channel %d: close_fds r %d w %d e %d",
c->self, c->rfd, c->wfd, c->efd);
debug3("channel %d: close_fds r %d w %d e %d c %d",
c->self, c->rfd, c->wfd, c->efd, c->ctl_fd);
channel_close_fd(&c->sock);
channel_close_fd(&c->ctl_fd);
channel_close_fd(&c->rfd);
channel_close_fd(&c->wfd);
channel_close_fd(&c->efd);
@ -333,6 +336,8 @@ channel_free(Channel *c)
if (c->sock != -1)
shutdown(c->sock, SHUT_RDWR);
if (c->ctl_fd != -1)
shutdown(c->ctl_fd, SHUT_RDWR);
channel_close_fds(c);
buffer_free(&c->input);
buffer_free(&c->output);
@ -550,12 +555,13 @@ channel_open_message(void)
case SSH_CHANNEL_X11_OPEN:
case SSH_CHANNEL_INPUT_DRAINING:
case SSH_CHANNEL_OUTPUT_DRAINING:
snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n",
snprintf(buf, sizeof buf,
" #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cfd %d)\r\n",
c->self, c->remote_name,
c->type, c->remote_id,
c->istate, buffer_len(&c->input),
c->ostate, buffer_len(&c->output),
c->rfd, c->wfd);
c->rfd, c->wfd, c->ctl_fd);
buffer_append(&buffer, buf, strlen(buf));
continue;
default:
@ -596,14 +602,14 @@ channel_request_start(int id, char *service, int wantconfirm)
logit("channel_request_start: %d: unknown channel id", id);
return;
}
debug2("channel %d: request %s", id, service) ;
debug2("channel %d: request %s confirm %d", id, service, wantconfirm);
packet_start(SSH2_MSG_CHANNEL_REQUEST);
packet_put_int(c->remote_id);
packet_put_cstring(service);
packet_put_char(wantconfirm);
}
void
channel_register_confirm(int id, channel_callback_fn *fn)
channel_register_confirm(int id, channel_callback_fn *fn, void *ctx)
{
Channel *c = channel_lookup(id);
@ -612,6 +618,7 @@ channel_register_confirm(int id, channel_callback_fn *fn)
return;
}
c->confirm = fn;
c->confirm_ctx = ctx;
}
void
channel_register_cleanup(int id, channel_callback_fn *fn)
@ -729,6 +736,10 @@ channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset)
buffer_len(&c->extended) < c->remote_window)
FD_SET(c->efd, readset);
}
/* XXX: What about efd? races? */
if (compat20 && c->ctl_fd != -1 &&
c->istate == CHAN_INPUT_OPEN && c->ostate == CHAN_OUTPUT_OPEN)
FD_SET(c->ctl_fd, readset);
}
static void
@ -1482,6 +1493,33 @@ channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset)
return 1;
}
static int
channel_handle_ctl(Channel *c, fd_set * readset, fd_set * writeset)
{
char buf[16];
int len;
/* Monitor control fd to detect if the slave client exits */
if (c->ctl_fd != -1 && FD_ISSET(c->ctl_fd, readset)) {
len = read(c->ctl_fd, buf, sizeof(buf));
if (len < 0 && (errno == EINTR || errno == EAGAIN))
return 1;
if (len <= 0) {
debug2("channel %d: ctl read<=0", c->self);
if (c->type != SSH_CHANNEL_OPEN) {
debug2("channel %d: not open", c->self);
chan_mark_dead(c);
return -1;
} else {
chan_read_failed(c);
chan_write_failed(c);
}
return -1;
} else
fatal("%s: unexpected data on ctl fd", __func__);
}
return 1;
}
static int
channel_check_window(Channel *c)
{
if (c->type == SSH_CHANNEL_OPEN &&
@ -1511,6 +1549,7 @@ channel_post_open(Channel *c, fd_set * readset, fd_set * writeset)
if (!compat20)
return;
channel_handle_efd(c, readset, writeset);
channel_handle_ctl(c, readset, writeset);
channel_check_window(c);
}
@ -2011,7 +2050,7 @@ channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt)
c->remote_maxpacket = packet_get_int();
if (c->confirm) {
debug2("callback start");
c->confirm(c->self, NULL);
c->confirm(c->self, c->confirm_ctx);
debug2("callback done");
}
debug2("channel %d: open confirm rwindow %u rmax %u", c->self,
@ -2531,6 +2570,27 @@ channel_connect_to(const char *host, u_short port)
return connect_to(host, port);
}
void
channel_send_window_changes(void)
{
int i;
struct winsize ws;
for (i = 0; i < channels_alloc; i++) {
if (channels[i] == NULL ||
channels[i]->type != SSH_CHANNEL_OPEN)
continue;
if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0)
continue;
channel_request_start(i, "window-change", 0);
packet_put_int(ws.ws_col);
packet_put_int(ws.ws_row);
packet_put_int(ws.ws_xpixel);
packet_put_int(ws.ws_ypixel);
packet_send();
}
}
/* -- X11 forwarding */
/*

Просмотреть файл

@ -1,4 +1,4 @@
/* $OpenBSD: channels.h,v 1.72 2004/05/21 11:33:11 djm Exp $ */
/* $OpenBSD: channels.h,v 1.73 2004/06/13 15:03:02 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -76,6 +76,7 @@ struct Channel {
int wfd; /* write fd */
int efd; /* extended fd */
int sock; /* sock fd */
int ctl_fd; /* control fd (client sharing) */
int isatty; /* rfd is a tty */
int wfd_isatty; /* wfd is a tty */
int force_drain; /* force close on iEOF */
@ -105,6 +106,7 @@ struct Channel {
/* callback */
channel_callback_fn *confirm;
channel_callback_fn *detach_user;
void *confirm_ctx;
/* filter */
channel_filter_fn *input_filter;
@ -161,10 +163,11 @@ void channel_stop_listening(void);
void channel_send_open(int);
void channel_request_start(int, char *, int);
void channel_register_cleanup(int, channel_callback_fn *);
void channel_register_confirm(int, channel_callback_fn *);
void channel_register_confirm(int, channel_callback_fn *, void *);
void channel_register_filter(int, channel_filter_fn *);
void channel_cancel_cleanup(int);
int channel_close_fd(int *);
void channel_send_window_changes(void);
/* protocol handler */

Просмотреть файл

@ -59,7 +59,7 @@
*/
#include "includes.h"
RCSID("$OpenBSD: clientloop.c,v 1.122 2004/05/22 06:32:12 djm Exp $");
RCSID("$OpenBSD: clientloop.c,v 1.123 2004/06/13 15:03:02 djm Exp $");
#include "ssh.h"
#include "ssh1.h"
@ -81,6 +81,9 @@ RCSID("$OpenBSD: clientloop.c,v 1.122 2004/05/22 06:32:12 djm Exp $");
#include "atomicio.h"
#include "sshpty.h"
#include "misc.h"
#include "monitor_fdpass.h"
#include "match.h"
#include "msg.h"
/* import options */
extern Options options;
@ -91,6 +94,9 @@ extern int stdin_null_flag;
/* Flag indicating that no shell has been requested */
extern int no_shell_flag;
/* Control socket */
extern int control_fd;
/*
* Name of the host we are connecting to. This is the name given on the
* command line, or the HostName specified for the user-supplied name in a
@ -131,9 +137,19 @@ static int server_alive_timeouts = 0;
static void client_init_dispatch(void);
int session_ident = -1;
struct confirm_ctx {
int want_tty;
int want_subsys;
Buffer cmd;
char *term;
struct termios tio;
};
/*XXX*/
extern Kex *xxx_kex;
void ssh_process_session2_setup(int, int, int, Buffer *);
/* Restores stdin to blocking mode. */
static void
@ -291,19 +307,13 @@ client_check_window_change(void)
/** XXX race */
received_window_change_signal = 0;
if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
return;
debug2("client_check_window_change: changed");
if (compat20) {
channel_request_start(session_ident, "window-change", 0);
packet_put_int(ws.ws_col);
packet_put_int(ws.ws_row);
packet_put_int(ws.ws_xpixel);
packet_put_int(ws.ws_ypixel);
packet_send();
channel_send_window_changes();
} else {
if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
return;
packet_start(SSH_CMSG_WINDOW_SIZE);
packet_put_int(ws.ws_row);
packet_put_int(ws.ws_col);
@ -335,7 +345,6 @@ server_alive_check(void)
* Waits until the client can do something (some data becomes available on
* one of the file descriptors).
*/
static void
client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
int *maxfdp, int *nallocp, int rekeying)
@ -381,6 +390,9 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
if (packet_have_data_to_write())
FD_SET(connection_out, *writesetp);
if (control_fd != -1)
FD_SET(control_fd, *readsetp);
/*
* Wait for something to happen. This will suspend the process until
* some selected descriptor can be read, written, or has some other
@ -499,6 +511,176 @@ client_process_net_input(fd_set * readset)
}
}
static void
client_subsystem_reply(int type, u_int32_t seq, void *ctxt)
{
int id;
Channel *c;
id = packet_get_int();
packet_check_eom();
if ((c = channel_lookup(id)) == NULL) {
error("%s: no channel for id %d", __func__, id);
return;
}
if (type == SSH2_MSG_CHANNEL_SUCCESS)
debug2("Request suceeded on channel %d", id);
else if (type == SSH2_MSG_CHANNEL_FAILURE) {
error("Request failed on channel %d", id);
channel_free(c);
}
}
static void
client_extra_session2_setup(int id, void *arg)
{
struct confirm_ctx *cctx = arg;
Channel *c;
if (cctx == NULL)
fatal("%s: cctx == NULL", __func__);
if ((c = channel_lookup(id)) == NULL)
fatal("%s: no channel for id %d", __func__, id);
client_session2_setup(id, cctx->want_tty, cctx->want_subsys,
cctx->term, &cctx->tio, c->rfd, &cctx->cmd,
client_subsystem_reply);
c->confirm_ctx = NULL;
buffer_free(&cctx->cmd);
free(cctx->term);
free(cctx);
}
static void
client_process_control(fd_set * readset)
{
Buffer m;
Channel *c;
int client_fd, new_fd[3], ver;
socklen_t addrlen;
struct sockaddr_storage addr;
struct confirm_ctx *cctx;
char *cmd;
u_int len;
uid_t euid;
gid_t egid;
/*
* Accept connection on control socket
*/
if (control_fd == -1 || !FD_ISSET(control_fd, readset))
return;
memset(&addr, 0, sizeof(addr));
addrlen = sizeof(addr);
if ((client_fd = accept(control_fd,
(struct sockaddr*)&addr, &addrlen)) == -1) {
error("%s accept: %s", __func__, strerror(errno));
return;
}
if (getpeereid(client_fd, &euid, &egid) < 0) {
error("%s getpeereid failed: %s", __func__, strerror(errno));
close(client_fd);
return;
}
if ((euid != 0) && (getuid() != euid)) {
error("control mode uid mismatch: peer euid %u != uid %u",
(u_int) euid, (u_int) getuid());
close(client_fd);
return;
}
/* XXX: implement use of ssh-askpass to confirm additional channels */
unset_nonblock(client_fd);
buffer_init(&m);
buffer_put_int(&m, getpid());
if (ssh_msg_send(client_fd, /* version */0, &m) == -1) {
error("%s: client msg_send failed", __func__);
close(client_fd);
return;
}
buffer_clear(&m);
if (ssh_msg_recv(client_fd, &m) == -1) {
error("%s: client msg_recv failed", __func__);
close(client_fd);
return;
}
if ((ver = buffer_get_char(&m)) != 0) {
error("%s: wrong client version %d", __func__, ver);
buffer_free(&m);
close(client_fd);
return;
}
cctx = xmalloc(sizeof(*cctx));
memset(cctx, 0, sizeof(*cctx));
cctx->want_tty = buffer_get_int(&m);
cctx->want_subsys = buffer_get_int(&m);
cctx->term = buffer_get_string(&m, &len);
cmd = buffer_get_string(&m, &len);
buffer_init(&cctx->cmd);
buffer_append(&cctx->cmd, cmd, strlen(cmd));
debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__,
cctx->want_tty, cctx->want_subsys, cmd);
/* Gather fds from client */
new_fd[0] = mm_receive_fd(client_fd);
new_fd[1] = mm_receive_fd(client_fd);
new_fd[2] = mm_receive_fd(client_fd);
debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__,
new_fd[0], new_fd[1], new_fd[2]);
/* Try to pick up ttymodes from client before it goes raw */
if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1)
error("%s: tcgetattr: %s", __func__, strerror(errno));
buffer_clear(&m);
if (ssh_msg_send(client_fd, /* version */0, &m) == -1) {
error("%s: client msg_send failed", __func__);
close(client_fd);
close(new_fd[0]);
close(new_fd[1]);
close(new_fd[2]);
return;
}
buffer_free(&m);
/* enable nonblocking unless tty */
if (!isatty(new_fd[0]))
set_nonblock(new_fd[0]);
if (!isatty(new_fd[1]))
set_nonblock(new_fd[1]);
if (!isatty(new_fd[2]))
set_nonblock(new_fd[2]);
set_nonblock(client_fd);
c = channel_new("session", SSH_CHANNEL_OPENING,
new_fd[0], new_fd[1], new_fd[2],
CHAN_SES_WINDOW_DEFAULT, CHAN_SES_PACKET_DEFAULT,
CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0);
/* XXX */
c->ctl_fd = client_fd;
debug3("%s: channel_new: %d", __func__, c->self);
channel_send_open(c->self);
channel_register_confirm(c->self, client_extra_session2_setup, cctx);
}
static void
process_cmdline(void)
{
@ -901,9 +1083,6 @@ simple_escape_filter(Channel *c, char *buf, int len)
static void
client_channel_closed(int id, void *arg)
{
if (id != session_ident)
error("client_channel_closed: id %d != session_ident %d",
id, session_ident);
channel_cancel_cleanup(id);
session_closed = 1;
leave_raw_mode();
@ -937,6 +1116,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
connection_in = packet_get_connection_in();
connection_out = packet_get_connection_out();
max_fd = MAX(connection_in, connection_out);
if (control_fd != -1)
max_fd = MAX(max_fd, control_fd);
if (!compat20) {
/* enable nonblocking unless tty */
@ -1054,6 +1235,9 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
/* Buffer input from the connection. */
client_process_net_input(readset);
/* Accept control connections. */
client_process_control(readset);
if (quit_pending)
break;
@ -1385,7 +1569,7 @@ static void
client_input_channel_req(int type, u_int32_t seq, void *ctxt)
{
Channel *c = NULL;
int id, reply, success = 0;
int exitval, id, reply, success = 0;
char *rtype;
id = packet_get_int();
@ -1395,18 +1579,21 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt)
debug("client_input_channel_req: channel %d rtype %s reply %d",
id, rtype, reply);
if (session_ident == -1) {
error("client_input_channel_req: no channel %d", session_ident);
} else if (id != session_ident) {
error("client_input_channel_req: channel %d: wrong channel: %d",
session_ident, id);
}
c = channel_lookup(id);
if (c == NULL) {
error("client_input_channel_req: channel %d: unknown channel", id);
} else if (strcmp(rtype, "exit-status") == 0) {
success = 1;
exit_status = packet_get_int();
exitval = packet_get_int();
if (id == session_ident) {
success = 1;
exit_status = exitval;
} else if (c->ctl_fd == -1) {
error("client_input_channel_req: unexpected channel %d",
session_ident);
} else {
atomicio(vwrite, c->ctl_fd, &exitval, sizeof(exitval));
success = 1;
}
packet_check_eom();
}
if (reply) {
@ -1437,6 +1624,98 @@ client_input_global_request(int type, u_int32_t seq, void *ctxt)
xfree(rtype);
}
void
client_session2_setup(int id, int want_tty, int want_subsystem,
const char *term, struct termios *tiop, int in_fd, Buffer *cmd,
dispatch_fn *subsys_repl)
{
int len;
debug2("%s: id %d", __func__, id);
if (want_tty) {
struct winsize ws;
struct termios tio;
/* Store window size in the packet. */
if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0)
memset(&ws, 0, sizeof(ws));
channel_request_start(id, "pty-req", 0);
packet_put_cstring(term != NULL ? term : "");
packet_put_int(ws.ws_col);
packet_put_int(ws.ws_row);
packet_put_int(ws.ws_xpixel);
packet_put_int(ws.ws_ypixel);
tio = get_saved_tio();
tty_make_modes(-1, tiop != NULL ? tiop : &tio);
packet_send();
/* XXX wait for reply */
}
/* Transfer any environment variables from client to server */
if (options.num_send_env != 0) {
int i, j, matched;
extern char **environ;
char *name, *val;
debug("Sending environment.");
for (i = 0; environ && environ[i] != NULL; i++) {
/* Split */
name = xstrdup(environ[i]);
if ((val = strchr(name, '=')) == NULL) {
free(name);
continue;
}
*val++ = '\0';
matched = 0;
for (j = 0; j < options.num_send_env; j++) {
if (match_pattern(name, options.send_env[j])) {
matched = 1;
break;
}
}
if (!matched) {
debug3("Ignored env %s", name);
free(name);
continue;
}
debug("Sending env %s = %s", name, val);
channel_request_start(id, "env", 0);
packet_put_cstring(name);
packet_put_cstring(val);
packet_send();
free(name);
}
}
len = buffer_len(cmd);
if (len > 0) {
if (len > 900)
len = 900;
if (want_subsystem) {
debug("Sending subsystem: %.*s", len, (u_char*)buffer_ptr(cmd));
channel_request_start(id, "subsystem", subsys_repl != NULL);
if (subsys_repl != NULL) {
/* register callback for reply */
/* XXX we assume that client_loop has already been called */
dispatch_set(SSH2_MSG_CHANNEL_FAILURE, subsys_repl);
dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, subsys_repl);
}
} else {
debug("Sending command: %.*s", len, (u_char*)buffer_ptr(cmd));
channel_request_start(id, "exec", 0);
}
packet_put_string(buffer_ptr(cmd), buffer_len(cmd));
packet_send();
} else {
channel_request_start(id, "shell", 0);
packet_send();
}
}
static void
client_init_dispatch_20(void)
{
@ -1503,5 +1782,7 @@ cleanup_exit(int i)
{
leave_raw_mode();
leave_non_blocking();
if (options.control_path != NULL && control_fd != -1)
unlink(options.control_path);
_exit(i);
}

Просмотреть файл

@ -1,4 +1,4 @@
/* $OpenBSD: clientloop.h,v 1.8 2003/12/16 15:49:51 markus Exp $ */
/* $OpenBSD: clientloop.h,v 1.9 2004/06/13 15:03:02 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -38,3 +38,5 @@
/* Client side main loop for the interactive session. */
int client_loop(int, int, int);
void client_global_request_reply_fwd(int, u_int32_t, void *);
void client_session2_setup(int, int, int, const char *, struct termios *,
int, Buffer *, dispatch_fn *);

Просмотреть файл

@ -25,7 +25,7 @@
#ifndef _DEFINES_H
#define _DEFINES_H
/* $Id: defines.h,v 1.115 2004/04/14 07:24:30 dtucker Exp $ */
/* $Id: defines.h,v 1.116 2004/06/15 00:34:08 djm Exp $ */
/* Constants */
@ -462,6 +462,9 @@ struct winsize {
(struct cmsghdr *)NULL)
#endif /* CMSG_FIRSTHDR */
#ifndef offsetof
# define offsetof(type, member) ((size_t) &((type *)0)->member)
#endif
/* Function replacement / compatibility hacks */

Просмотреть файл

@ -1,4 +1,4 @@
/* $OpenBSD: includes.h,v 1.17 2002/01/26 16:44:22 stevesk Exp $ */
/* $OpenBSD: includes.h,v 1.18 2004/06/13 15:03:02 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -33,6 +33,7 @@ static /**/const char *const rcsid[] = { (char *)rcsid, "\100(#)" msg }
#include <grp.h>
#include <time.h>
#include <dirent.h>
#include <stddef.h>
#ifdef HAVE_LIMITS_H
# include <limits.h> /* For PATH_MAX */

Просмотреть файл

@ -12,7 +12,7 @@
*/
#include "includes.h"
RCSID("$OpenBSD: readconf.c,v 1.131 2004/05/27 00:50:13 dtucker Exp $");
RCSID("$OpenBSD: readconf.c,v 1.132 2004/06/13 15:03:02 djm Exp $");
#include "ssh.h"
#include "xmalloc.h"
@ -106,7 +106,7 @@ typedef enum {
oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
oAddressFamily, oGssAuthentication, oGssDelegateCreds,
oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
oSendEnv,
oSendEnv, oControlPath, oControlMaster,
oDeprecated, oUnsupported
} OpCodes;
@ -195,6 +195,8 @@ static struct {
{ "serveraliveinterval", oServerAliveInterval },
{ "serveralivecountmax", oServerAliveCountMax },
{ "sendenv", oSendEnv },
{ "controlpath", oControlPath },
{ "controlmaster", oControlMaster },
{ NULL, oBadOption }
};
@ -764,6 +766,14 @@ parse_int:
}
break;
case oControlPath:
charptr = &options->control_path;
goto parse_string;
case oControlMaster:
intptr = &options->control_master;
goto parse_flag;
case oDeprecated:
debug("%s line %d: Deprecated option \"%s\"",
filename, linenum, keyword);
@ -905,6 +915,8 @@ initialize_options(Options * options)
options->server_alive_interval = -1;
options->server_alive_count_max = -1;
options->num_send_env = 0;
options->control_path = NULL;
options->control_master = -1;
}
/*
@ -1025,6 +1037,8 @@ fill_default_options(Options * options)
options->server_alive_interval = 0;
if (options->server_alive_count_max == -1)
options->server_alive_count_max = 3;
if (options->control_master == -1)
options->control_master = 0;
/* options->proxy_command should not be set by default */
/* options->user will be set in the main program if appropriate */
/* options->hostname will be set in the main program if appropriate */

Просмотреть файл

@ -1,4 +1,4 @@
/* $OpenBSD: readconf.h,v 1.62 2004/04/27 09:46:37 djm Exp $ */
/* $OpenBSD: readconf.h,v 1.63 2004/06/13 15:03:02 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -108,6 +108,9 @@ typedef struct {
int num_send_env;
char *send_env[MAX_SEND_ENV];
char *control_path;
int control_master;
} Options;

4
scp.1
Просмотреть файл

@ -9,7 +9,7 @@
.\"
.\" Created: Sun May 7 00:14:37 1995 ylo
.\"
.\" $OpenBSD: scp.1,v 1.35 2004/05/04 18:36:07 jmc Exp $
.\" $OpenBSD: scp.1,v 1.36 2004/06/13 15:03:02 djm Exp $
.\"
.Dd September 25, 1999
.Dt SCP 1
@ -128,6 +128,8 @@ For full details of the options listed below, and their possible values, see
.It CompressionLevel
.It ConnectionAttempts
.It ConnectTimeout
.It ControlMaster
.It ControlPath
.It GlobalKnownHostsFile
.It GSSAPIAuthentication
.It GSSAPIDelegateCredentials

4
sftp.1
Просмотреть файл

@ -1,4 +1,4 @@
.\" $OpenBSD: sftp.1,v 1.54 2004/05/02 23:02:17 dtucker Exp $
.\" $OpenBSD: sftp.1,v 1.55 2004/06/13 15:03:02 djm Exp $
.\"
.\" Copyright (c) 2001 Damien Miller. All rights reserved.
.\"
@ -154,6 +154,8 @@ For full details of the options listed below, and their possible values, see
.It CompressionLevel
.It ConnectionAttempts
.It ConnectTimeout
.It ControlMaster
.It ControlPath
.It GlobalKnownHostsFile
.It GSSAPIAuthentication
.It GSSAPIDelegateCredentials

Просмотреть файл

@ -39,7 +39,7 @@
#include "pathnames.h"
#include "log.h"
RCSID("$Id: ssh-rand-helper.c,v 1.16 2003/11/21 12:56:47 djm Exp $");
RCSID("$Id: ssh-rand-helper.c,v 1.17 2004/06/15 00:34:08 djm Exp $");
/* Number of bytes we write out */
#define OUTPUT_SEED_SIZE 48
@ -69,10 +69,6 @@ extern char *__progname;
char *__progname;
#endif
#ifndef offsetof
# define offsetof(type, member) ((size_t) &((type *)0)->member)
#endif
#define WHITESPACE " \t\n"
#ifndef RUSAGE_SELF

26
ssh.1
Просмотреть файл

@ -34,7 +34,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $OpenBSD: ssh.1,v 1.189 2004/06/13 14:01:42 dtucker Exp $
.\" $OpenBSD: ssh.1,v 1.190 2004/06/13 15:03:02 djm Exp $
.Dd September 25, 1999
.Dt SSH 1
.Os
@ -43,7 +43,7 @@
.Nd OpenSSH SSH client (remote login program)
.Sh SYNOPSIS
.Nm ssh
.Op Fl 1246AaCfgkNnqsTtVvXxY
.Op Fl 1246AaCfgkMNnqSsTtVvXxY
.Op Fl b Ar bind_address
.Op Fl c Ar cipher_spec
.Op Fl D Ar port
@ -605,6 +605,17 @@ be specified in order of preference.
See the
.Cm MACs
keyword for more information.
.It Fl M
Places the
.Nm
client into
.Dq master
mode for connection sharing.
Refer to the description of
.Cm ControlMaster
in
.Xr ssh_config 5
for details.
.It Fl N
Do not execute a remote command.
This is useful for just forwarding ports
@ -649,6 +660,8 @@ For full details of the options listed below, and their possible values, see
.It CompressionLevel
.It ConnectionAttempts
.It ConnectTimeout
.It ControlMaster
.It ControlPath
.It DynamicForward
.It EscapeChar
.It ForwardAgent
@ -724,6 +737,15 @@ IPv6 addresses can be specified with an alternative syntax:
.Ar hostport .
.Xc
.Sm on
.It Fl S
Places the
.Nm
client into slave mode for connection sharing.
Refer to the description of
.Cm ControlMaster
in
.Xr ssh_config 5
for details.
.It Fl s
May be used to request invocation of a subsystem on the remote system.
Subsystems are a feature of the SSH2 protocol which facilitate the use

280
ssh.c
Просмотреть файл

@ -40,7 +40,7 @@
*/
#include "includes.h"
RCSID("$OpenBSD: ssh.c,v 1.213 2004/05/08 00:01:37 deraadt Exp $");
RCSID("$OpenBSD: ssh.c,v 1.214 2004/06/13 15:03:02 djm Exp $");
#include <openssl/evp.h>
#include <openssl/err.h>
@ -53,21 +53,24 @@ RCSID("$OpenBSD: ssh.c,v 1.213 2004/05/08 00:01:37 deraadt Exp $");
#include "xmalloc.h"
#include "packet.h"
#include "buffer.h"
#include "bufaux.h"
#include "channels.h"
#include "key.h"
#include "authfd.h"
#include "authfile.h"
#include "pathnames.h"
#include "dispatch.h"
#include "clientloop.h"
#include "log.h"
#include "readconf.h"
#include "sshconnect.h"
#include "dispatch.h"
#include "misc.h"
#include "kex.h"
#include "mac.h"
#include "sshpty.h"
#include "match.h"
#include "msg.h"
#include "monitor_fdpass.h"
#ifdef SMARTCARD
#include "scard.h"
@ -141,6 +144,13 @@ static int client_global_request_id = 0;
/* pid of proxycommand child process */
pid_t proxy_command_pid = 0;
/* fd to control socket */
int control_fd = -1;
/* Only used in control client mode */
volatile sig_atomic_t control_client_terminate = 0;
u_int control_server_pid = 0;
/* Prints a help message to the user. This function never returns. */
static void
@ -158,6 +168,7 @@ usage(void)
static int ssh_session(void);
static int ssh_session2(void);
static void load_public_identity_files(void);
static void control_client(const char *path);
/*
* Main program for the ssh client.
@ -228,7 +239,7 @@ main(int ac, char **av)
again:
while ((opt = getopt(ac, av,
"1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:NPR:TVXY")) != -1) {
"1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:MNPR:S:TVXY")) != -1) {
switch (opt) {
case '1':
options.protocol = SSH_PROTO_1;
@ -364,6 +375,9 @@ again:
exit(1);
}
break;
case 'M':
options.control_master = 1;
break;
case 'p':
options.port = a2port(optarg);
if (options.port == 0) {
@ -432,6 +446,13 @@ again:
case 's':
subsystem_flag = 1;
break;
case 'S':
if (options.control_path != NULL)
free(options.control_path);
options.control_path = xstrdup(optarg);
if (options.control_master == -1)
options.control_master = 0;
break;
case 'b':
options.bind_address = optarg;
break;
@ -566,6 +587,13 @@ again:
strcmp(options.proxy_command, "none") == 0)
options.proxy_command = NULL;
if (options.control_path != NULL) {
options.control_path = tilde_expand_filename(
options.control_path, original_real_uid);
}
if (options.control_path != NULL && options.control_master == 0)
control_client(options.control_path); /* This doesn't return */
/* Open a connection to the remote host. */
if (ssh_connect(host, &hostaddr, options.port,
options.address_family, options.connection_attempts,
@ -678,6 +706,9 @@ again:
exit_status = compat20 ? ssh_session2() : ssh_session();
packet_close();
if (options.control_path != NULL && control_fd != -1)
unlink(options.control_path);
/*
* Send SIGHUP to proxy command if used. We don't wait() in
* case it hangs and instead rely on init to reap the child
@ -974,7 +1005,7 @@ ssh_session(void)
}
static void
client_subsystem_reply(int type, u_int32_t seq, void *ctxt)
ssh_subsystem_reply(int type, u_int32_t seq, void *ctxt)
{
int id, len;
@ -1006,40 +1037,50 @@ client_global_request_reply_fwd(int type, u_int32_t seq, void *ctxt)
options.remote_forwards[i].port);
}
static void
ssh_control_listener(void)
{
struct sockaddr_un addr;
mode_t old_umask;
if (options.control_path == NULL || options.control_master != 1)
return;
memset(&addr, '\0', sizeof(addr));
addr.sun_family = AF_UNIX;
addr.sun_len = offsetof(struct sockaddr_un, sun_path) +
strlen(options.control_path) + 1;
if (strlcpy(addr.sun_path, options.control_path,
sizeof(addr.sun_path)) >= sizeof(addr.sun_path))
fatal("ControlPath too long");
if ((control_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
fatal("%s socket(): %s\n", __func__, strerror(errno));
old_umask = umask(0177);
if (bind(control_fd, (struct sockaddr*)&addr, addr.sun_len) == -1) {
control_fd = -1;
if (errno == EINVAL)
fatal("ControlSocket %s already exists",
options.control_path);
else
fatal("%s bind(): %s\n", __func__, strerror(errno));
}
umask(old_umask);
if (listen(control_fd, 64) == -1)
fatal("%s listen(): %s\n", __func__, strerror(errno));
set_nonblock(control_fd);
}
/* request pty/x11/agent/tcpfwd/shell for channel */
static void
ssh_session2_setup(int id, void *arg)
{
int len;
int interactive = 0;
struct termios tio;
debug2("ssh_session2_setup: id %d", id);
if (tty_flag) {
struct winsize ws;
char *cp;
cp = getenv("TERM");
if (!cp)
cp = "";
/* Store window size in the packet. */
if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
memset(&ws, 0, sizeof(ws));
channel_request_start(id, "pty-req", 0);
packet_put_cstring(cp);
packet_put_int(ws.ws_col);
packet_put_int(ws.ws_row);
packet_put_int(ws.ws_xpixel);
packet_put_int(ws.ws_ypixel);
tio = get_saved_tio();
tty_make_modes(/*ignored*/ 0, &tio);
packet_send();
interactive = 1;
/* XXX wait for reply */
}
if (options.forward_x11 &&
getenv("DISPLAY") != NULL) {
int interactive = tty_flag;
if (options.forward_x11 && getenv("DISPLAY") != NULL) {
char *proto, *data;
/* Get reasonable local authentication information. */
x11_get_proto(&proto, &data);
@ -1057,65 +1098,8 @@ ssh_session2_setup(int id, void *arg)
packet_send();
}
/* Transfer any environment variables from client to server */
if (options.num_send_env != 0) {
int i, j, matched;
extern char **environ;
char *name, *val;
debug("Sending environment.");
for (i = 0; environ && environ[i] != NULL; i++) {
/* Split */
name = xstrdup(environ[i]);
if ((val = strchr(name, '=')) == NULL) {
free(name);
continue;
}
*val++ = '\0';
matched = 0;
for (j = 0; j < options.num_send_env; j++) {
if (match_pattern(name, options.send_env[j])) {
matched = 1;
break;
}
}
if (!matched) {
debug3("Ignored env %s", name);
free(name);
continue;
}
debug("Sending env %s = %s", name, val);
channel_request_start(id, "env", 0);
packet_put_cstring(name);
packet_put_cstring(val);
packet_send();
free(name);
}
}
len = buffer_len(&command);
if (len > 0) {
if (len > 900)
len = 900;
if (subsystem_flag) {
debug("Sending subsystem: %.*s", len, (u_char *)buffer_ptr(&command));
channel_request_start(id, "subsystem", /*want reply*/ 1);
/* register callback for reply */
/* XXX we assume that client_loop has already been called */
dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &client_subsystem_reply);
dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &client_subsystem_reply);
} else {
debug("Sending command: %.*s", len, (u_char *)buffer_ptr(&command));
channel_request_start(id, "exec", 0);
}
packet_put_string(buffer_ptr(&command), buffer_len(&command));
packet_send();
} else {
channel_request_start(id, "shell", 0);
packet_send();
}
client_session2_setup(id, tty_flag, subsystem_flag, getenv("TERM"),
NULL, fileno(stdin), &command, &ssh_subsystem_reply);
packet_set_interactive(interactive);
}
@ -1161,7 +1145,7 @@ ssh_session2_open(void)
channel_send_open(c->self);
if (!no_shell_flag)
channel_register_confirm(c->self, ssh_session2_setup);
channel_register_confirm(c->self, ssh_session2_setup, NULL);
return c->self;
}
@ -1173,6 +1157,7 @@ ssh_session2(void)
/* XXX should be pre-session */
ssh_init_forwarding();
ssh_control_listener();
if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN))
id = ssh_session2_open();
@ -1226,3 +1211,110 @@ load_public_identity_files(void)
options.identity_keys[i] = public;
}
}
static void
control_client_sighandler(int signo)
{
control_client_terminate = signo;
}
static void
control_client_sigrelay(int signo)
{
if (control_server_pid > 1)
kill(control_server_pid, signo);
}
static void
control_client(const char *path)
{
struct sockaddr_un addr;
int r, sock, exitval;
Buffer m;
char *cp;
memset(&addr, '\0', sizeof(addr));
addr.sun_family = AF_UNIX;
addr.sun_len = offsetof(struct sockaddr_un, sun_path) +
strlen(path) + 1;
if (strlcpy(addr.sun_path, path,
sizeof(addr.sun_path)) >= sizeof(addr.sun_path))
fatal("ControlPath too long");
if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
fatal("%s socket(): %s", __func__, strerror(errno));
if (connect(sock, (struct sockaddr*)&addr, addr.sun_len) == -1)
fatal("Couldn't connect to %s: %s", path, strerror(errno));
if ((cp = getenv("TERM")) == NULL)
cp = "";
signal(SIGINT, control_client_sighandler);
signal(SIGTERM, control_client_sighandler);
signal(SIGWINCH, control_client_sigrelay);
buffer_init(&m);
/* Get PID of controlee */
if (ssh_msg_recv(sock, &m) == -1)
fatal("%s: msg_recv", __func__);
if (buffer_get_char(&m) != 0)
fatal("%s: wrong version", __func__);
control_server_pid = buffer_get_int(&m);
/* XXX: env passing */
buffer_clear(&m);
buffer_put_int(&m, tty_flag);
buffer_put_int(&m, subsystem_flag);
buffer_put_cstring(&m, cp);
buffer_append(&command, "\0", 1);
buffer_put_cstring(&m, buffer_ptr(&command));
if (ssh_msg_send(sock, /* version */0, &m) == -1)
fatal("%s: msg_send", __func__);
mm_send_fd(sock, STDIN_FILENO);
mm_send_fd(sock, STDOUT_FILENO);
mm_send_fd(sock, STDERR_FILENO);
/* Wait for reply, so master has a chance to gather ttymodes */
buffer_clear(&m);
if (ssh_msg_recv(sock, &m) == -1)
fatal("%s: msg_recv", __func__);
if (buffer_get_char(&m) != 0)
fatal("%s: master returned error", __func__);
buffer_free(&m);
if (tty_flag)
enter_raw_mode();
/* Stick around until the controlee closes the client_fd */
exitval = 0;
for (;!control_client_terminate;) {
r = read(sock, &exitval, sizeof(exitval));
if (r == 0) {
debug2("Received EOF from master");
break;
}
if (r > 0)
debug2("Received exit status from master %d", exitval);
if (r == -1 && errno != EINTR)
fatal("%s: read %s", __func__, strerror(errno));
}
if (control_client_terminate)
debug2("Exiting on signal %d", control_client_terminate);
close(sock);
leave_raw_mode();
if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET)
fprintf(stderr, "Connection to master closed.\r\n");
exit(exitval);
}

Просмотреть файл

@ -34,7 +34,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $OpenBSD: ssh_config.5,v 1.35 2004/06/13 14:01:42 dtucker Exp $
.\" $OpenBSD: ssh_config.5,v 1.36 2004/06/13 15:03:02 djm Exp $
.Dd September 25, 1999
.Dt SSH_CONFIG 5
.Os
@ -256,6 +256,28 @@ will act as a SOCKS server.
Multiple forwardings may be specified, and
additional forwardings can be given on the command line.
Only the superuser can forward privileged ports.
.It Cm ControlMaster
Enables the sharing of multiple sessions over a single network connection.
When set to
.Dq yes
.Nm ssh
will listen for connections on a control socket specified using the
.Cm ControlPath
argument.
Additional sessions can connect to this socket using the same
.Cm ControlPath
with
.Cm ControlMaster
set to
.Dq no
(the default.)
These sessions will reuse the master instance's network connection rather
than initiating new ones.
.It Cm ControlPath
Specify a the path to the control socket used for connection sharing.
See
.Cm ControlMaster
above.
.It Cm EnableSSHKeysign
Setting this option to
.Dq yes