version 1.45, 2014/04/28 03:09:18 |
version 1.46, 2014/07/15 15:54:14 |
|
|
} |
} |
|
|
static char * |
static char * |
format_forward(u_int ftype, Forward *fwd) |
format_forward(u_int ftype, struct Forward *fwd) |
{ |
{ |
char *ret; |
char *ret; |
|
|
switch (ftype) { |
switch (ftype) { |
case MUX_FWD_LOCAL: |
case MUX_FWD_LOCAL: |
xasprintf(&ret, "local forward %.200s:%d -> %.200s:%d", |
xasprintf(&ret, "local forward %.200s:%d -> %.200s:%d", |
|
(fwd->listen_path != NULL) ? fwd->listen_path : |
(fwd->listen_host == NULL) ? |
(fwd->listen_host == NULL) ? |
(options.gateway_ports ? "*" : "LOCALHOST") : |
(options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : |
fwd->listen_host, fwd->listen_port, |
fwd->listen_host, fwd->listen_port, |
|
(fwd->connect_path != NULL) ? fwd->connect_path : |
fwd->connect_host, fwd->connect_port); |
fwd->connect_host, fwd->connect_port); |
break; |
break; |
case MUX_FWD_DYNAMIC: |
case MUX_FWD_DYNAMIC: |
xasprintf(&ret, "dynamic forward %.200s:%d -> *", |
xasprintf(&ret, "dynamic forward %.200s:%d -> *", |
(fwd->listen_host == NULL) ? |
(fwd->listen_host == NULL) ? |
(options.gateway_ports ? "*" : "LOCALHOST") : |
(options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : |
fwd->listen_host, fwd->listen_port); |
fwd->listen_host, fwd->listen_port); |
break; |
break; |
case MUX_FWD_REMOTE: |
case MUX_FWD_REMOTE: |
xasprintf(&ret, "remote forward %.200s:%d -> %.200s:%d", |
xasprintf(&ret, "remote forward %.200s:%d -> %.200s:%d", |
|
(fwd->listen_path != NULL) ? fwd->listen_path : |
(fwd->listen_host == NULL) ? |
(fwd->listen_host == NULL) ? |
"LOCALHOST" : fwd->listen_host, |
"LOCALHOST" : fwd->listen_host, |
fwd->listen_port, |
fwd->listen_port, |
|
(fwd->connect_path != NULL) ? fwd->connect_path : |
fwd->connect_host, fwd->connect_port); |
fwd->connect_host, fwd->connect_port); |
break; |
break; |
default: |
default: |
|
|
} |
} |
|
|
static int |
static int |
compare_forward(Forward *a, Forward *b) |
compare_forward(struct Forward *a, struct Forward *b) |
{ |
{ |
if (!compare_host(a->listen_host, b->listen_host)) |
if (!compare_host(a->listen_host, b->listen_host)) |
return 0; |
return 0; |
|
if (!compare_host(a->listen_path, b->listen_path)) |
|
return 0; |
if (a->listen_port != b->listen_port) |
if (a->listen_port != b->listen_port) |
return 0; |
return 0; |
if (!compare_host(a->connect_host, b->connect_host)) |
if (!compare_host(a->connect_host, b->connect_host)) |
return 0; |
return 0; |
|
if (!compare_host(a->connect_path, b->connect_path)) |
|
return 0; |
if (a->connect_port != b->connect_port) |
if (a->connect_port != b->connect_port) |
return 0; |
return 0; |
|
|
|
|
{ |
{ |
struct mux_channel_confirm_ctx *fctx = ctxt; |
struct mux_channel_confirm_ctx *fctx = ctxt; |
char *failmsg = NULL; |
char *failmsg = NULL; |
Forward *rfwd; |
struct Forward *rfwd; |
Channel *c; |
Channel *c; |
Buffer out; |
Buffer out; |
|
|
|
|
rfwd = &options.remote_forwards[fctx->fid]; |
rfwd = &options.remote_forwards[fctx->fid]; |
debug("%s: %s for: listen %d, connect %s:%d", __func__, |
debug("%s: %s for: listen %d, connect %s:%d", __func__, |
type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", |
type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", |
rfwd->listen_port, rfwd->connect_host, rfwd->connect_port); |
rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : |
|
rfwd->connect_host, rfwd->connect_port); |
if (type == SSH2_MSG_REQUEST_SUCCESS) { |
if (type == SSH2_MSG_REQUEST_SUCCESS) { |
if (rfwd->listen_port == 0) { |
if (rfwd->listen_port == 0) { |
rfwd->allocated_port = packet_get_int(); |
rfwd->allocated_port = packet_get_int(); |
|
|
} else { |
} else { |
if (rfwd->listen_port == 0) |
if (rfwd->listen_port == 0) |
channel_update_permitted_opens(rfwd->handle, -1); |
channel_update_permitted_opens(rfwd->handle, -1); |
xasprintf(&failmsg, "remote port forwarding failed for " |
if (rfwd->listen_path != NULL) |
"listen port %d", rfwd->listen_port); |
xasprintf(&failmsg, "remote port forwarding failed for " |
|
"listen path %s", rfwd->listen_path); |
|
else |
|
xasprintf(&failmsg, "remote port forwarding failed for " |
|
"listen port %d", rfwd->listen_port); |
} |
} |
fail: |
fail: |
error("%s: %s", __func__, failmsg); |
error("%s: %s", __func__, failmsg); |
|
|
static int |
static int |
process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) |
process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) |
{ |
{ |
Forward fwd; |
struct Forward fwd; |
char *fwd_desc = NULL; |
char *fwd_desc = NULL; |
|
char *listen_addr, *connect_addr; |
u_int ftype; |
u_int ftype; |
u_int lport, cport; |
u_int lport, cport; |
int i, ret = 0, freefwd = 1; |
int i, ret = 0, freefwd = 1; |
|
|
fwd.listen_host = fwd.connect_host = NULL; |
/* XXX - lport/cport check redundant */ |
if (buffer_get_int_ret(&ftype, m) != 0 || |
if (buffer_get_int_ret(&ftype, m) != 0 || |
(fwd.listen_host = buffer_get_string_ret(m, NULL)) == NULL || |
(listen_addr = buffer_get_string_ret(m, NULL)) == NULL || |
buffer_get_int_ret(&lport, m) != 0 || |
buffer_get_int_ret(&lport, m) != 0 || |
(fwd.connect_host = buffer_get_string_ret(m, NULL)) == NULL || |
(connect_addr = buffer_get_string_ret(m, NULL)) == NULL || |
buffer_get_int_ret(&cport, m) != 0 || |
buffer_get_int_ret(&cport, m) != 0 || |
lport > 65535 || cport > 65535) { |
(lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || |
|
(cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { |
error("%s: malformed message", __func__); |
error("%s: malformed message", __func__); |
ret = -1; |
ret = -1; |
goto out; |
goto out; |
} |
} |
fwd.listen_port = lport; |
if (*listen_addr == '\0') { |
fwd.connect_port = cport; |
free(listen_addr); |
if (*fwd.listen_host == '\0') { |
listen_addr = NULL; |
free(fwd.listen_host); |
|
fwd.listen_host = NULL; |
|
} |
} |
if (*fwd.connect_host == '\0') { |
if (*connect_addr == '\0') { |
free(fwd.connect_host); |
free(connect_addr); |
fwd.connect_host = NULL; |
connect_addr = NULL; |
} |
} |
|
|
|
memset(&fwd, 0, sizeof(fwd)); |
|
fwd.listen_port = lport; |
|
if (fwd.listen_port == PORT_STREAMLOCAL) |
|
fwd.listen_path = listen_addr; |
|
else |
|
fwd.listen_host = listen_addr; |
|
fwd.connect_port = cport; |
|
if (fwd.connect_port == PORT_STREAMLOCAL) |
|
fwd.connect_path = connect_addr; |
|
else |
|
fwd.connect_host = connect_addr; |
|
|
debug2("%s: channel %d: request %s", __func__, c->self, |
debug2("%s: channel %d: request %s", __func__, c->self, |
(fwd_desc = format_forward(ftype, &fwd))); |
(fwd_desc = format_forward(ftype, &fwd))); |
|
|
|
|
ftype != MUX_FWD_DYNAMIC) { |
ftype != MUX_FWD_DYNAMIC) { |
logit("%s: invalid forwarding type %u", __func__, ftype); |
logit("%s: invalid forwarding type %u", __func__, ftype); |
invalid: |
invalid: |
free(fwd.listen_host); |
free(listen_addr); |
free(fwd.connect_host); |
free(connect_addr); |
buffer_put_int(r, MUX_S_FAILURE); |
buffer_put_int(r, MUX_S_FAILURE); |
buffer_put_int(r, rid); |
buffer_put_int(r, rid); |
buffer_put_cstring(r, "Invalid forwarding request"); |
buffer_put_cstring(r, "Invalid forwarding request"); |
return 0; |
return 0; |
} |
} |
if (fwd.listen_port >= 65536) { |
if (ftype == MUX_FWD_DYNAMIC && fwd.listen_path) { |
|
logit("%s: streamlocal and dynamic forwards " |
|
"are mutually exclusive", __func__); |
|
goto invalid; |
|
} |
|
if (fwd.listen_port != PORT_STREAMLOCAL && fwd.listen_port >= 65536) { |
logit("%s: invalid listen port %u", __func__, |
logit("%s: invalid listen port %u", __func__, |
fwd.listen_port); |
fwd.listen_port); |
goto invalid; |
goto invalid; |
} |
} |
if (fwd.connect_port >= 65536 || (ftype != MUX_FWD_DYNAMIC && |
if ((fwd.connect_port != PORT_STREAMLOCAL && fwd.connect_port >= 65536) |
ftype != MUX_FWD_REMOTE && fwd.connect_port == 0)) { |
|| (ftype != MUX_FWD_DYNAMIC && ftype != MUX_FWD_REMOTE && fwd.connect_port == 0)) { |
logit("%s: invalid connect port %u", __func__, |
logit("%s: invalid connect port %u", __func__, |
fwd.connect_port); |
fwd.connect_port); |
goto invalid; |
goto invalid; |
} |
} |
if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL) { |
if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL && fwd.connect_path == NULL) { |
logit("%s: missing connect host", __func__); |
logit("%s: missing connect host", __func__); |
goto invalid; |
goto invalid; |
} |
} |
|
|
} |
} |
|
|
if (ftype == MUX_FWD_LOCAL || ftype == MUX_FWD_DYNAMIC) { |
if (ftype == MUX_FWD_LOCAL || ftype == MUX_FWD_DYNAMIC) { |
if (!channel_setup_local_fwd_listener(fwd.listen_host, |
if (!channel_setup_local_fwd_listener(&fwd, |
fwd.listen_port, fwd.connect_host, fwd.connect_port, |
&options.fwd_opts)) { |
options.gateway_ports)) { |
|
fail: |
fail: |
logit("slave-requested %s failed", fwd_desc); |
logit("slave-requested %s failed", fwd_desc); |
buffer_put_int(r, MUX_S_FAILURE); |
buffer_put_int(r, MUX_S_FAILURE); |
|
|
} else { |
} else { |
struct mux_channel_confirm_ctx *fctx; |
struct mux_channel_confirm_ctx *fctx; |
|
|
fwd.handle = channel_request_remote_forwarding(fwd.listen_host, |
fwd.handle = channel_request_remote_forwarding(&fwd); |
fwd.listen_port, fwd.connect_host, fwd.connect_port); |
|
if (fwd.handle < 0) |
if (fwd.handle < 0) |
goto fail; |
goto fail; |
add_remote_forward(&options, &fwd); |
add_remote_forward(&options, &fwd); |
|
|
free(fwd_desc); |
free(fwd_desc); |
if (freefwd) { |
if (freefwd) { |
free(fwd.listen_host); |
free(fwd.listen_host); |
|
free(fwd.listen_path); |
free(fwd.connect_host); |
free(fwd.connect_host); |
|
free(fwd.connect_path); |
} |
} |
return ret; |
return ret; |
} |
} |
|
|
static int |
static int |
process_mux_close_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) |
process_mux_close_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) |
{ |
{ |
Forward fwd, *found_fwd; |
struct Forward fwd, *found_fwd; |
char *fwd_desc = NULL; |
char *fwd_desc = NULL; |
const char *error_reason = NULL; |
const char *error_reason = NULL; |
|
char *listen_addr = NULL, *connect_addr = NULL; |
u_int ftype; |
u_int ftype; |
int i, listen_port, ret = 0; |
int i, ret = 0; |
u_int lport, cport; |
u_int lport, cport; |
|
|
fwd.listen_host = fwd.connect_host = NULL; |
|
if (buffer_get_int_ret(&ftype, m) != 0 || |
if (buffer_get_int_ret(&ftype, m) != 0 || |
(fwd.listen_host = buffer_get_string_ret(m, NULL)) == NULL || |
(listen_addr = buffer_get_string_ret(m, NULL)) == NULL || |
buffer_get_int_ret(&lport, m) != 0 || |
buffer_get_int_ret(&lport, m) != 0 || |
(fwd.connect_host = buffer_get_string_ret(m, NULL)) == NULL || |
(connect_addr = buffer_get_string_ret(m, NULL)) == NULL || |
buffer_get_int_ret(&cport, m) != 0 || |
buffer_get_int_ret(&cport, m) != 0 || |
lport > 65535 || cport > 65535) { |
(lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || |
|
(cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { |
error("%s: malformed message", __func__); |
error("%s: malformed message", __func__); |
ret = -1; |
ret = -1; |
goto out; |
goto out; |
} |
} |
fwd.listen_port = lport; |
|
fwd.connect_port = cport; |
|
|
|
if (*fwd.listen_host == '\0') { |
if (*listen_addr == '\0') { |
free(fwd.listen_host); |
free(listen_addr); |
fwd.listen_host = NULL; |
listen_addr = NULL; |
} |
} |
if (*fwd.connect_host == '\0') { |
if (*connect_addr == '\0') { |
free(fwd.connect_host); |
free(connect_addr); |
fwd.connect_host = NULL; |
connect_addr = NULL; |
} |
} |
|
|
|
memset(&fwd, 0, sizeof(fwd)); |
|
fwd.listen_port = lport; |
|
if (fwd.listen_port == PORT_STREAMLOCAL) |
|
fwd.listen_path = listen_addr; |
|
else |
|
fwd.listen_host = listen_addr; |
|
fwd.connect_port = cport; |
|
if (fwd.connect_port == PORT_STREAMLOCAL) |
|
fwd.connect_path = connect_addr; |
|
else |
|
fwd.connect_host = connect_addr; |
|
|
debug2("%s: channel %d: request cancel %s", __func__, c->self, |
debug2("%s: channel %d: request cancel %s", __func__, c->self, |
(fwd_desc = format_forward(ftype, &fwd))); |
(fwd_desc = format_forward(ftype, &fwd))); |
|
|
|
|
* This shouldn't fail unless we confused the host/port |
* This shouldn't fail unless we confused the host/port |
* between options.remote_forwards and permitted_opens. |
* between options.remote_forwards and permitted_opens. |
* However, for dynamic allocated listen ports we need |
* However, for dynamic allocated listen ports we need |
* to lookup the actual listen port. |
* to use the actual listen port. |
*/ |
*/ |
listen_port = (fwd.listen_port == 0) ? |
if (channel_request_rforward_cancel(found_fwd) == -1) |
found_fwd->allocated_port : fwd.listen_port; |
|
if (channel_request_rforward_cancel(fwd.listen_host, |
|
listen_port) == -1) |
|
error_reason = "port not in permitted opens"; |
error_reason = "port not in permitted opens"; |
} else { /* local and dynamic forwards */ |
} else { /* local and dynamic forwards */ |
/* Ditto */ |
/* Ditto */ |
if (channel_cancel_lport_listener(fwd.listen_host, |
if (channel_cancel_lport_listener(&fwd, fwd.connect_port, |
fwd.listen_port, fwd.connect_port, |
&options.fwd_opts) == -1) |
options.gateway_ports) == -1) |
|
error_reason = "port not found"; |
error_reason = "port not found"; |
} |
} |
|
|
|
|
buffer_put_int(r, rid); |
buffer_put_int(r, rid); |
|
|
free(found_fwd->listen_host); |
free(found_fwd->listen_host); |
|
free(found_fwd->listen_path); |
free(found_fwd->connect_host); |
free(found_fwd->connect_host); |
|
free(found_fwd->connect_path); |
found_fwd->listen_host = found_fwd->connect_host = NULL; |
found_fwd->listen_host = found_fwd->connect_host = NULL; |
|
found_fwd->listen_path = found_fwd->connect_path = NULL; |
found_fwd->listen_port = found_fwd->connect_port = 0; |
found_fwd->listen_port = found_fwd->connect_port = 0; |
} else { |
} else { |
buffer_put_int(r, MUX_S_FAILURE); |
buffer_put_int(r, MUX_S_FAILURE); |
|
|
} |
} |
out: |
out: |
free(fwd_desc); |
free(fwd_desc); |
free(fwd.listen_host); |
free(listen_addr); |
free(fwd.connect_host); |
free(connect_addr); |
|
|
return ret; |
return ret; |
} |
} |
|
|
void |
void |
muxserver_listen(void) |
muxserver_listen(void) |
{ |
{ |
struct sockaddr_un addr; |
|
mode_t old_umask; |
mode_t old_umask; |
char *orig_control_path = options.control_path; |
char *orig_control_path = options.control_path; |
char rbuf[16+1]; |
char rbuf[16+1]; |
|
|
xasprintf(&options.control_path, "%s.%s", orig_control_path, rbuf); |
xasprintf(&options.control_path, "%s.%s", orig_control_path, rbuf); |
debug3("%s: temporary control path %s", __func__, options.control_path); |
debug3("%s: temporary control path %s", __func__, options.control_path); |
|
|
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)) { |
|
error("ControlPath \"%s\" too long for Unix domain socket", |
|
options.control_path); |
|
goto disable_mux_master; |
|
} |
|
|
|
if ((muxserver_sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) |
|
fatal("%s socket(): %s", __func__, strerror(errno)); |
|
|
|
old_umask = umask(0177); |
old_umask = umask(0177); |
if (bind(muxserver_sock, (struct sockaddr *)&addr, addr.sun_len) == -1) { |
muxserver_sock = unix_listener(options.control_path, 64, 0); |
|
umask(old_umask); |
|
if (muxserver_sock < 0) { |
if (errno == EINVAL || errno == EADDRINUSE) { |
if (errno == EINVAL || errno == EADDRINUSE) { |
error("ControlSocket %s already exists, " |
error("ControlSocket %s already exists, " |
"disabling multiplexing", options.control_path); |
"disabling multiplexing", options.control_path); |
|
|
options.control_path = NULL; |
options.control_path = NULL; |
options.control_master = SSHCTL_MASTER_NO; |
options.control_master = SSHCTL_MASTER_NO; |
return; |
return; |
} else |
} else { |
fatal("%s bind(): %s", __func__, strerror(errno)); |
/* unix_listener() logs the error */ |
|
cleanup_exit(255); |
|
} |
} |
} |
umask(old_umask); |
|
|
|
if (listen(muxserver_sock, 64) == -1) |
|
fatal("%s listen(): %s", __func__, strerror(errno)); |
|
|
|
/* Now atomically "move" the mux socket into position */ |
/* Now atomically "move" the mux socket into position */ |
if (link(options.control_path, orig_control_path) != 0) { |
if (link(options.control_path, orig_control_path) != 0) { |
if (errno != EEXIST) { |
if (errno != EEXIST) { |
|
|
} |
} |
|
|
static int |
static int |
mux_client_forward(int fd, int cancel_flag, u_int ftype, Forward *fwd) |
mux_client_forward(int fd, int cancel_flag, u_int ftype, struct Forward *fwd) |
{ |
{ |
Buffer m; |
Buffer m; |
char *e, *fwd_desc; |
char *e, *fwd_desc; |
|
|
buffer_put_int(&m, cancel_flag ? MUX_C_CLOSE_FWD : MUX_C_OPEN_FWD); |
buffer_put_int(&m, cancel_flag ? MUX_C_CLOSE_FWD : MUX_C_OPEN_FWD); |
buffer_put_int(&m, muxclient_request_id); |
buffer_put_int(&m, muxclient_request_id); |
buffer_put_int(&m, ftype); |
buffer_put_int(&m, ftype); |
buffer_put_cstring(&m, |
if (fwd->listen_path != NULL) { |
fwd->listen_host == NULL ? "" : fwd->listen_host); |
buffer_put_cstring(&m, fwd->listen_path); |
|
} else { |
|
buffer_put_cstring(&m, |
|
fwd->listen_host == NULL ? "" : fwd->listen_host); |
|
} |
buffer_put_int(&m, fwd->listen_port); |
buffer_put_int(&m, fwd->listen_port); |
buffer_put_cstring(&m, |
if (fwd->connect_path != NULL) { |
fwd->connect_host == NULL ? "" : fwd->connect_host); |
buffer_put_cstring(&m, fwd->connect_path); |
|
} else { |
|
buffer_put_cstring(&m, |
|
fwd->connect_host == NULL ? "" : fwd->connect_host); |
|
} |
buffer_put_int(&m, fwd->connect_port); |
buffer_put_int(&m, fwd->connect_port); |
|
|
if (mux_client_write_packet(fd, &m) != 0) |
if (mux_client_write_packet(fd, &m) != 0) |