Changeset View
Changeset View
Standalone View
Standalone View
channels.c
/* $OpenBSD: channels.c,v 1.386 2018/10/04 01:04:52 djm Exp $ */ | /* $OpenBSD: channels.c,v 1.394 2019/07/07 01:05:00 dtucker Exp $ */ | ||||
/* | /* | ||||
* Author: Tatu Ylonen <ylo@cs.hut.fi> | * Author: Tatu Ylonen <ylo@cs.hut.fi> | ||||
* 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 file contains functions for generic socket connection forwarding. | * This file contains functions for generic socket connection forwarding. | ||||
* There is also code for initiating connection forwarding for X11 connections, | * There is also code for initiating connection forwarding for X11 connections, | ||||
* arbitrary tcp/ip connections, and the authentication agent connection. | * arbitrary tcp/ip connections, and the authentication agent connection. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
#include <arpa/inet.h> | #include <arpa/inet.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <limits.h> | #include <limits.h> | ||||
#include <netdb.h> | #include <netdb.h> | ||||
#include <stdarg.h> | #include <stdarg.h> | ||||
#ifdef HAVE_STDINT_H | #ifdef HAVE_STDINT_H | ||||
#include <stdint.h> | # include <stdint.h> | ||||
#endif | #endif | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <termios.h> | #include <termios.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include "openbsd-compat/sys-queue.h" | #include "openbsd-compat/sys-queue.h" | ||||
▲ Show 20 Lines • Show All 151 Lines • ▼ Show 20 Lines | |||||
/* -- channel core */ | /* -- channel core */ | ||||
void | void | ||||
channel_init_channels(struct ssh *ssh) | channel_init_channels(struct ssh *ssh) | ||||
{ | { | ||||
struct ssh_channels *sc; | struct ssh_channels *sc; | ||||
if ((sc = calloc(1, sizeof(*sc))) == NULL || | if ((sc = calloc(1, sizeof(*sc))) == NULL) | ||||
(sc->channel_pre = calloc(SSH_CHANNEL_MAX_TYPE, | |||||
sizeof(*sc->channel_pre))) == NULL || | |||||
(sc->channel_post = calloc(SSH_CHANNEL_MAX_TYPE, | |||||
sizeof(*sc->channel_post))) == NULL) | |||||
fatal("%s: allocation failed", __func__); | fatal("%s: allocation failed", __func__); | ||||
sc->channels_alloc = 10; | sc->channels_alloc = 10; | ||||
sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels)); | sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels)); | ||||
sc->IPv4or6 = AF_UNSPEC; | sc->IPv4or6 = AF_UNSPEC; | ||||
channel_handler_init(sc); | channel_handler_init(sc); | ||||
ssh->chanctxt = sc; | ssh->chanctxt = sc; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 249 Lines • ▼ Show 20 Lines | permission_set_get(struct ssh *ssh, int where) | ||||
case FORWARD_REMOTE: | case FORWARD_REMOTE: | ||||
return &sc->remote_perms; | return &sc->remote_perms; | ||||
break; | break; | ||||
default: | default: | ||||
fatal("%s: invalid forwarding direction %d", __func__, where); | fatal("%s: invalid forwarding direction %d", __func__, where); | ||||
} | } | ||||
} | } | ||||
/* Reutrns pointers to the specified forwarding list and its element count */ | /* Returns pointers to the specified forwarding list and its element count */ | ||||
static void | static void | ||||
permission_set_get_array(struct ssh *ssh, int who, int where, | permission_set_get_array(struct ssh *ssh, int who, int where, | ||||
struct permission ***permpp, u_int **npermpp) | struct permission ***permpp, u_int **npermpp) | ||||
{ | { | ||||
struct permission_set *pset = permission_set_get(ssh, where); | struct permission_set *pset = permission_set_get(ssh, where); | ||||
switch (who) { | switch (who) { | ||||
case FORWARD_USER: | case FORWARD_USER: | ||||
▲ Show 20 Lines • Show All 129 Lines • ▼ Show 20 Lines | channel_free(struct ssh *ssh, Channel *c) | ||||
explicit_bzero(c, sizeof(*c)); | explicit_bzero(c, sizeof(*c)); | ||||
free(c); | free(c); | ||||
} | } | ||||
void | void | ||||
channel_free_all(struct ssh *ssh) | channel_free_all(struct ssh *ssh) | ||||
{ | { | ||||
u_int i; | u_int i; | ||||
struct ssh_channels *sc = ssh->chanctxt; | |||||
for (i = 0; i < ssh->chanctxt->channels_alloc; i++) | for (i = 0; i < sc->channels_alloc; i++) | ||||
if (ssh->chanctxt->channels[i] != NULL) | if (sc->channels[i] != NULL) | ||||
channel_free(ssh, ssh->chanctxt->channels[i]); | channel_free(ssh, sc->channels[i]); | ||||
free(sc->channels); | |||||
sc->channels = NULL; | |||||
sc->channels_alloc = 0; | |||||
sc->channel_max_fd = 0; | |||||
free(sc->x11_saved_display); | |||||
sc->x11_saved_display = NULL; | |||||
free(sc->x11_saved_proto); | |||||
sc->x11_saved_proto = NULL; | |||||
free(sc->x11_saved_data); | |||||
sc->x11_saved_data = NULL; | |||||
sc->x11_saved_data_len = 0; | |||||
free(sc->x11_fake_data); | |||||
sc->x11_fake_data = NULL; | |||||
sc->x11_fake_data_len = 0; | |||||
} | } | ||||
/* | /* | ||||
* Closes the sockets/fds of all channels. This is used to close extra file | * Closes the sockets/fds of all channels. This is used to close extra file | ||||
* descriptors after a fork. | * descriptors after a fork. | ||||
*/ | */ | ||||
void | void | ||||
channel_close_all(struct ssh *ssh) | channel_close_all(struct ssh *ssh) | ||||
▲ Show 20 Lines • Show All 992 Lines • ▼ Show 20 Lines | channel_post_x11_listener(struct ssh *ssh, Channel *c, | ||||
newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); | newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); | ||||
if (c->single_connection) { | if (c->single_connection) { | ||||
oerrno = errno; | oerrno = errno; | ||||
debug2("single_connection: closing X11 listener."); | debug2("single_connection: closing X11 listener."); | ||||
channel_close_fd(ssh, &c->sock); | channel_close_fd(ssh, &c->sock); | ||||
chan_mark_dead(ssh, c); | chan_mark_dead(ssh, c); | ||||
errno = oerrno; | errno = oerrno; | ||||
} | } | ||||
if (newsock < 0) { | if (newsock == -1) { | ||||
if (errno != EINTR && errno != EWOULDBLOCK && | if (errno != EINTR && errno != EWOULDBLOCK && | ||||
errno != ECONNABORTED) | errno != ECONNABORTED) | ||||
error("accept: %.100s", strerror(errno)); | error("accept: %.100s", strerror(errno)); | ||||
if (errno == EMFILE || errno == ENFILE) | if (errno == EMFILE || errno == ENFILE) | ||||
c->notbefore = monotime() + 1; | c->notbefore = monotime() + 1; | ||||
return; | return; | ||||
} | } | ||||
set_nodelay(newsock); | set_nodelay(newsock); | ||||
▲ Show 20 Lines • Show All 126 Lines • ▼ Show 20 Lines | if (c->type == SSH_CHANNEL_RPORT_LISTENER) { | ||||
rtype = "dynamic-tcpip"; | rtype = "dynamic-tcpip"; | ||||
} else { | } else { | ||||
nextstate = SSH_CHANNEL_OPENING; | nextstate = SSH_CHANNEL_OPENING; | ||||
rtype = "direct-tcpip"; | rtype = "direct-tcpip"; | ||||
} | } | ||||
addrlen = sizeof(addr); | addrlen = sizeof(addr); | ||||
newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); | newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); | ||||
if (newsock < 0) { | if (newsock == -1) { | ||||
if (errno != EINTR && errno != EWOULDBLOCK && | if (errno != EINTR && errno != EWOULDBLOCK && | ||||
errno != ECONNABORTED) | errno != ECONNABORTED) | ||||
error("accept: %.100s", strerror(errno)); | error("accept: %.100s", strerror(errno)); | ||||
if (errno == EMFILE || errno == ENFILE) | if (errno == EMFILE || errno == ENFILE) | ||||
c->notbefore = monotime() + 1; | c->notbefore = monotime() + 1; | ||||
return; | return; | ||||
} | } | ||||
if (c->host_port != PORT_STREAMLOCAL) | if (c->host_port != PORT_STREAMLOCAL) | ||||
Show All 22 Lines | channel_post_auth_listener(struct ssh *ssh, Channel *c, | ||||
struct sockaddr_storage addr; | struct sockaddr_storage addr; | ||||
socklen_t addrlen; | socklen_t addrlen; | ||||
if (!FD_ISSET(c->sock, readset)) | if (!FD_ISSET(c->sock, readset)) | ||||
return; | return; | ||||
addrlen = sizeof(addr); | addrlen = sizeof(addr); | ||||
newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); | newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); | ||||
if (newsock < 0) { | if (newsock == -1) { | ||||
error("accept from auth socket: %.100s", strerror(errno)); | error("accept from auth socket: %.100s", strerror(errno)); | ||||
if (errno == EMFILE || errno == ENFILE) | if (errno == EMFILE || errno == ENFILE) | ||||
c->notbefore = monotime() + 1; | c->notbefore = monotime() + 1; | ||||
return; | return; | ||||
} | } | ||||
nc = channel_new(ssh, "accepted auth socket", | nc = channel_new(ssh, "accepted auth socket", | ||||
SSH_CHANNEL_OPENING, newsock, newsock, -1, | SSH_CHANNEL_OPENING, newsock, newsock, -1, | ||||
c->local_window_max, c->local_maxpacket, | c->local_window_max, c->local_maxpacket, | ||||
Show All 11 Lines | channel_post_connecting(struct ssh *ssh, Channel *c, | ||||
socklen_t sz = sizeof(err); | socklen_t sz = sizeof(err); | ||||
if (!FD_ISSET(c->sock, writeset)) | if (!FD_ISSET(c->sock, writeset)) | ||||
return; | return; | ||||
if (!c->have_remote_id) | if (!c->have_remote_id) | ||||
fatal(":%s: channel %d: no remote id", __func__, c->self); | fatal(":%s: channel %d: no remote id", __func__, c->self); | ||||
/* for rdynamic the OPEN_CONFIRMATION has been sent already */ | /* for rdynamic the OPEN_CONFIRMATION has been sent already */ | ||||
isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH); | isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH); | ||||
if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { | if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) == -1) { | ||||
err = errno; | err = errno; | ||||
error("getsockopt SO_ERROR failed"); | error("getsockopt SO_ERROR failed"); | ||||
} | } | ||||
if (err == 0) { | if (err == 0) { | ||||
debug("channel %d: connected to %s port %d", | debug("channel %d: connected to %s port %d", | ||||
c->self, c->connect_ctx.host, c->connect_ctx.port); | c->self, c->connect_ctx.host, c->connect_ctx.port); | ||||
channel_connect_ctx_free(&c->connect_ctx); | channel_connect_ctx_free(&c->connect_ctx); | ||||
c->type = SSH_CHANNEL_OPEN; | c->type = SSH_CHANNEL_OPEN; | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | channel_handle_rfd(struct ssh *ssh, Channel *c, | ||||
force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; | force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; | ||||
if (c->rfd == -1 || (!force && !FD_ISSET(c->rfd, readset))) | if (c->rfd == -1 || (!force && !FD_ISSET(c->rfd, readset))) | ||||
return 1; | return 1; | ||||
errno = 0; | errno = 0; | ||||
len = read(c->rfd, buf, sizeof(buf)); | len = read(c->rfd, buf, sizeof(buf)); | ||||
if (len < 0 && (errno == EINTR || | if (len == -1 && (errno == EINTR || | ||||
((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) | ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) | ||||
return 1; | return 1; | ||||
#ifndef PTY_ZEROREAD | #ifndef PTY_ZEROREAD | ||||
if (len <= 0) { | if (len <= 0) { | ||||
#else | #else | ||||
if ((!c->isatty && len <= 0) || | if ((!c->isatty && len <= 0) || | ||||
(c->isatty && (len < 0 || (len == 0 && errno != 0)))) { | (c->isatty && (len < 0 || (len == 0 && errno != 0)))) { | ||||
#endif | #endif | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | if (c->output_filter != NULL) { | ||||
buf = data = sshbuf_mutable_ptr(c->output); | buf = data = sshbuf_mutable_ptr(c->output); | ||||
dlen = sshbuf_len(c->output); | dlen = sshbuf_len(c->output); | ||||
} | } | ||||
if (c->datagram) { | if (c->datagram) { | ||||
/* ignore truncated writes, datagrams might get lost */ | /* ignore truncated writes, datagrams might get lost */ | ||||
len = write(c->wfd, buf, dlen); | len = write(c->wfd, buf, dlen); | ||||
free(data); | free(data); | ||||
if (len < 0 && (errno == EINTR || errno == EAGAIN || | if (len == -1 && (errno == EINTR || errno == EAGAIN || | ||||
errno == EWOULDBLOCK)) | errno == EWOULDBLOCK)) | ||||
return 1; | return 1; | ||||
if (len <= 0) | if (len <= 0) | ||||
goto write_fail; | goto write_fail; | ||||
goto out; | goto out; | ||||
} | } | ||||
#ifdef _AIX | #ifdef _AIX | ||||
/* XXX: Later AIX versions can't push as much data to tty */ | /* XXX: Later AIX versions can't push as much data to tty */ | ||||
if (c->wfd_isatty) | if (c->wfd_isatty) | ||||
dlen = MIN(dlen, 8*1024); | dlen = MIN(dlen, 8*1024); | ||||
#endif | #endif | ||||
len = write(c->wfd, buf, dlen); | len = write(c->wfd, buf, dlen); | ||||
if (len < 0 && | if (len == -1 && | ||||
(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) | (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) | ||||
return 1; | return 1; | ||||
if (len <= 0) { | if (len <= 0) { | ||||
write_fail: | write_fail: | ||||
if (c->type != SSH_CHANNEL_OPEN) { | if (c->type != SSH_CHANNEL_OPEN) { | ||||
debug2("channel %d: not open", c->self); | debug2("channel %d: not open", c->self); | ||||
chan_mark_dead(ssh, c); | chan_mark_dead(ssh, c); | ||||
return -1; | return -1; | ||||
Show All 37 Lines | channel_handle_efd_write(struct ssh *ssh, Channel *c, | ||||
ssize_t len; | ssize_t len; | ||||
if (!FD_ISSET(c->efd, writeset) || sshbuf_len(c->extended) == 0) | if (!FD_ISSET(c->efd, writeset) || sshbuf_len(c->extended) == 0) | ||||
return 1; | return 1; | ||||
len = write(c->efd, sshbuf_ptr(c->extended), | len = write(c->efd, sshbuf_ptr(c->extended), | ||||
sshbuf_len(c->extended)); | sshbuf_len(c->extended)); | ||||
debug2("channel %d: written %zd to efd %d", c->self, len, c->efd); | debug2("channel %d: written %zd to efd %d", c->self, len, c->efd); | ||||
if (len < 0 && (errno == EINTR || errno == EAGAIN || | if (len == -1 && (errno == EINTR || errno == EAGAIN || | ||||
errno == EWOULDBLOCK)) | errno == EWOULDBLOCK)) | ||||
return 1; | return 1; | ||||
if (len <= 0) { | if (len <= 0) { | ||||
debug2("channel %d: closing write-efd %d", c->self, c->efd); | debug2("channel %d: closing write-efd %d", c->self, c->efd); | ||||
channel_close_fd(ssh, &c->efd); | channel_close_fd(ssh, &c->efd); | ||||
} else { | } else { | ||||
if ((r = sshbuf_consume(c->extended, len)) != 0) { | if ((r = sshbuf_consume(c->extended, len)) != 0) { | ||||
fatal("%s: channel %d: consume: %s", | fatal("%s: channel %d: consume: %s", | ||||
__func__, c->self, ssh_err(r)); | __func__, c->self, ssh_err(r)); | ||||
} | } | ||||
c->local_consumed += len; | c->local_consumed += len; | ||||
} | } | ||||
return 1; | return 1; | ||||
} | } | ||||
static int | static int | ||||
channel_handle_efd_read(struct ssh *ssh, Channel *c, | channel_handle_efd_read(struct ssh *ssh, Channel *c, | ||||
fd_set *readset, fd_set *writeset) | fd_set *readset, fd_set *writeset) | ||||
{ | { | ||||
char buf[CHAN_RBUF]; | char buf[CHAN_RBUF]; | ||||
int r; | |||||
ssize_t len; | ssize_t len; | ||||
int r, force; | |||||
if (!c->detach_close && !FD_ISSET(c->efd, readset)) | force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; | ||||
if (c->efd == -1 || (!force && !FD_ISSET(c->efd, readset))) | |||||
return 1; | return 1; | ||||
len = read(c->efd, buf, sizeof(buf)); | len = read(c->efd, buf, sizeof(buf)); | ||||
debug2("channel %d: read %zd from efd %d", c->self, len, c->efd); | debug2("channel %d: read %zd from efd %d", c->self, len, c->efd); | ||||
if (len < 0 && (errno == EINTR || ((errno == EAGAIN || | if (len == -1 && (errno == EINTR || ((errno == EAGAIN || | ||||
errno == EWOULDBLOCK) && !c->detach_close))) | errno == EWOULDBLOCK) && !force))) | ||||
return 1; | return 1; | ||||
if (len <= 0) { | if (len <= 0) { | ||||
debug2("channel %d: closing read-efd %d", | debug2("channel %d: closing read-efd %d", | ||||
c->self, c->efd); | c->self, c->efd); | ||||
channel_close_fd(ssh, &c->efd); | channel_close_fd(ssh, &c->efd); | ||||
} else { | } else { | ||||
if (c->extended_usage == CHAN_EXTENDED_IGNORE) { | if (c->extended_usage == CHAN_EXTENDED_IGNORE) { | ||||
debug3("channel %d: discard efd", | debug3("channel %d: discard efd", | ||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | read_mux(struct ssh *ssh, Channel *c, u_int need) | ||||
char buf[CHAN_RBUF]; | char buf[CHAN_RBUF]; | ||||
ssize_t len; | ssize_t len; | ||||
u_int rlen; | u_int rlen; | ||||
int r; | int r; | ||||
if (sshbuf_len(c->input) < need) { | if (sshbuf_len(c->input) < need) { | ||||
rlen = need - sshbuf_len(c->input); | rlen = need - sshbuf_len(c->input); | ||||
len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF)); | len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF)); | ||||
if (len < 0 && (errno == EINTR || errno == EAGAIN)) | if (len == -1 && (errno == EINTR || errno == EAGAIN)) | ||||
return sshbuf_len(c->input); | return sshbuf_len(c->input); | ||||
if (len <= 0) { | if (len <= 0) { | ||||
debug2("channel %d: ctl read<=0 rfd %d len %zd", | debug2("channel %d: ctl read<=0 rfd %d len %zd", | ||||
c->self, c->rfd, len); | c->self, c->rfd, len); | ||||
chan_read_failed(ssh, c); | chan_read_failed(ssh, c); | ||||
return 0; | return 0; | ||||
} else if ((r = sshbuf_put(c->input, buf, len)) != 0) { | } else if ((r = sshbuf_put(c->input, buf, len)) != 0) { | ||||
fatal("%s: channel %d: append: %s", | fatal("%s: channel %d: append: %s", | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | channel_post_mux_client_write(struct ssh *ssh, Channel *c, | ||||
ssize_t len; | ssize_t len; | ||||
int r; | int r; | ||||
if (c->wfd == -1 || !FD_ISSET(c->wfd, writeset) || | if (c->wfd == -1 || !FD_ISSET(c->wfd, writeset) || | ||||
sshbuf_len(c->output) == 0) | sshbuf_len(c->output) == 0) | ||||
return; | return; | ||||
len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output)); | len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output)); | ||||
if (len < 0 && (errno == EINTR || errno == EAGAIN)) | if (len == -1 && (errno == EINTR || errno == EAGAIN)) | ||||
return; | return; | ||||
if (len <= 0) { | if (len <= 0) { | ||||
chan_mark_dead(ssh, c); | chan_mark_dead(ssh, c); | ||||
return; | return; | ||||
} | } | ||||
if ((r = sshbuf_consume(c->output, len)) != 0) | if ((r = sshbuf_consume(c->output, len)) != 0) | ||||
fatal("%s: channel %d: consume: %s", __func__, | fatal("%s: channel %d: consume: %s", __func__, | ||||
c->self, ssh_err(r)); | c->self, ssh_err(r)); | ||||
Show All 31 Lines | channel_post_mux_listener(struct ssh *ssh, Channel *c, | ||||
if ((newsock = accept(c->sock, (struct sockaddr*)&addr, | if ((newsock = accept(c->sock, (struct sockaddr*)&addr, | ||||
&addrlen)) == -1) { | &addrlen)) == -1) { | ||||
error("%s accept: %s", __func__, strerror(errno)); | error("%s accept: %s", __func__, strerror(errno)); | ||||
if (errno == EMFILE || errno == ENFILE) | if (errno == EMFILE || errno == ENFILE) | ||||
c->notbefore = monotime() + 1; | c->notbefore = monotime() + 1; | ||||
return; | return; | ||||
} | } | ||||
if (getpeereid(newsock, &euid, &egid) < 0) { | if (getpeereid(newsock, &euid, &egid) == -1) { | ||||
error("%s getpeereid failed: %s", __func__, | error("%s getpeereid failed: %s", __func__, | ||||
strerror(errno)); | strerror(errno)); | ||||
close(newsock); | close(newsock); | ||||
return; | return; | ||||
} | } | ||||
if ((euid != 0) && (getuid() != euid)) { | if ((euid != 0) && (getuid() != euid)) { | ||||
error("multiplex uid mismatch: peer euid %u != uid %u", | error("multiplex uid mismatch: peer euid %u != uid %u", | ||||
(u_int)euid, (u_int)getuid()); | (u_int)euid, (u_int)getuid()); | ||||
▲ Show 20 Lines • Show All 665 Lines • ▼ Show 20 Lines | channel_input_data(int type, u_int32_t seq, struct ssh *ssh) | ||||
/* Ignore any data for non-open channels (might happen on close) */ | /* Ignore any data for non-open channels (might happen on close) */ | ||||
if (c->type != SSH_CHANNEL_OPEN && | if (c->type != SSH_CHANNEL_OPEN && | ||||
c->type != SSH_CHANNEL_RDYNAMIC_OPEN && | c->type != SSH_CHANNEL_RDYNAMIC_OPEN && | ||||
c->type != SSH_CHANNEL_RDYNAMIC_FINISH && | c->type != SSH_CHANNEL_RDYNAMIC_FINISH && | ||||
c->type != SSH_CHANNEL_X11_OPEN) | c->type != SSH_CHANNEL_X11_OPEN) | ||||
return 0; | return 0; | ||||
/* Get the data. */ | /* Get the data. */ | ||||
if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0) | if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 || | ||||
(r = sshpkt_get_end(ssh)) != 0) | |||||
fatal("%s: channel %d: get data: %s", __func__, | fatal("%s: channel %d: get data: %s", __func__, | ||||
c->self, ssh_err(r)); | c->self, ssh_err(r)); | ||||
ssh_packet_check_eom(ssh); | |||||
win_len = data_len; | win_len = data_len; | ||||
if (c->datagram) | if (c->datagram) | ||||
win_len += 4; /* string length header */ | win_len += 4; /* string length header */ | ||||
/* | /* | ||||
* The sending side reduces its window as it sends data, so we | * The sending side reduces its window as it sends data, so we | ||||
* must 'fake' consumption of the data in order to ensure that window | * must 'fake' consumption of the data in order to ensure that window | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | if ((r = sshpkt_get_u32(ssh, &tcode)) != 0) { | ||||
ssh_packet_disconnect(ssh, "Invalid extended_data message"); | ssh_packet_disconnect(ssh, "Invalid extended_data message"); | ||||
} | } | ||||
if (c->efd == -1 || | if (c->efd == -1 || | ||||
c->extended_usage != CHAN_EXTENDED_WRITE || | c->extended_usage != CHAN_EXTENDED_WRITE || | ||||
tcode != SSH2_EXTENDED_DATA_STDERR) { | tcode != SSH2_EXTENDED_DATA_STDERR) { | ||||
logit("channel %d: bad ext data", c->self); | logit("channel %d: bad ext data", c->self); | ||||
return 0; | return 0; | ||||
} | } | ||||
if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0) { | if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 || | ||||
(r = sshpkt_get_end(ssh)) != 0) { | |||||
error("%s: parse data: %s", __func__, ssh_err(r)); | error("%s: parse data: %s", __func__, ssh_err(r)); | ||||
ssh_packet_disconnect(ssh, "Invalid extended_data message"); | ssh_packet_disconnect(ssh, "Invalid extended_data message"); | ||||
} | } | ||||
ssh_packet_check_eom(ssh); | |||||
if (data_len > c->local_window) { | if (data_len > c->local_window) { | ||||
logit("channel %d: rcvd too much extended_data %zu, win %u", | logit("channel %d: rcvd too much extended_data %zu, win %u", | ||||
c->self, data_len, c->local_window); | c->self, data_len, c->local_window); | ||||
return 0; | return 0; | ||||
} | } | ||||
debug2("channel %d: rcvd ext data %zu", c->self, data_len); | debug2("channel %d: rcvd ext data %zu", c->self, data_len); | ||||
/* XXX sshpkt_getb? */ | /* XXX sshpkt_getb? */ | ||||
if ((r = sshbuf_put(c->extended, data, data_len)) != 0) | if ((r = sshbuf_put(c->extended, data, data_len)) != 0) | ||||
error("%s: append: %s", __func__, ssh_err(r)); | error("%s: append: %s", __func__, ssh_err(r)); | ||||
c->local_window -= data_len; | c->local_window -= data_len; | ||||
return 0; | return 0; | ||||
} | } | ||||
int | int | ||||
channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh) | channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh) | ||||
{ | { | ||||
Channel *c = channel_from_packet_id(ssh, __func__, "ieof"); | Channel *c = channel_from_packet_id(ssh, __func__, "ieof"); | ||||
int r; | |||||
ssh_packet_check_eom(ssh); | if ((r = sshpkt_get_end(ssh)) != 0) { | ||||
error("%s: parse data: %s", __func__, ssh_err(r)); | |||||
ssh_packet_disconnect(ssh, "Invalid ieof message"); | |||||
} | |||||
if (channel_proxy_upstream(c, type, seq, ssh)) | if (channel_proxy_upstream(c, type, seq, ssh)) | ||||
return 0; | return 0; | ||||
chan_rcvd_ieof(ssh, c); | chan_rcvd_ieof(ssh, c); | ||||
/* XXX force input close */ | /* XXX force input close */ | ||||
if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { | if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { | ||||
debug("channel %d: FORCE input drain", c->self); | debug("channel %d: FORCE input drain", c->self); | ||||
c->istate = CHAN_INPUT_WAIT_DRAIN; | c->istate = CHAN_INPUT_WAIT_DRAIN; | ||||
if (sshbuf_len(c->input) == 0) | if (sshbuf_len(c->input) == 0) | ||||
chan_ibuf_empty(ssh, c); | chan_ibuf_empty(ssh, c); | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
int | int | ||||
channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh) | channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh) | ||||
{ | { | ||||
Channel *c = channel_from_packet_id(ssh, __func__, "oclose"); | Channel *c = channel_from_packet_id(ssh, __func__, "oclose"); | ||||
int r; | |||||
if (channel_proxy_upstream(c, type, seq, ssh)) | if (channel_proxy_upstream(c, type, seq, ssh)) | ||||
return 0; | return 0; | ||||
ssh_packet_check_eom(ssh); | if ((r = sshpkt_get_end(ssh)) != 0) { | ||||
error("%s: parse data: %s", __func__, ssh_err(r)); | |||||
ssh_packet_disconnect(ssh, "Invalid oclose message"); | |||||
} | |||||
chan_rcvd_oclose(ssh, c); | chan_rcvd_oclose(ssh, c); | ||||
return 0; | return 0; | ||||
} | } | ||||
int | int | ||||
channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh) | channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh) | ||||
{ | { | ||||
Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation"); | Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation"); | ||||
u_int32_t remote_window, remote_maxpacket; | u_int32_t remote_window, remote_maxpacket; | ||||
int r; | int r; | ||||
if (channel_proxy_upstream(c, type, seq, ssh)) | if (channel_proxy_upstream(c, type, seq, ssh)) | ||||
return 0; | return 0; | ||||
if (c->type != SSH_CHANNEL_OPENING) | if (c->type != SSH_CHANNEL_OPENING) | ||||
packet_disconnect("Received open confirmation for " | ssh_packet_disconnect(ssh, "Received open confirmation for " | ||||
"non-opening channel %d.", c->self); | "non-opening channel %d.", c->self); | ||||
/* | /* | ||||
* Record the remote channel number and mark that the channel | * Record the remote channel number and mark that the channel | ||||
* is now open. | * is now open. | ||||
*/ | */ | ||||
if ((r = sshpkt_get_u32(ssh, &c->remote_id)) != 0 || | if ((r = sshpkt_get_u32(ssh, &c->remote_id)) != 0 || | ||||
(r = sshpkt_get_u32(ssh, &remote_window)) != 0 || | (r = sshpkt_get_u32(ssh, &remote_window)) != 0 || | ||||
(r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0) { | (r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0 || | ||||
(r = sshpkt_get_end(ssh)) != 0) { | |||||
error("%s: window/maxpacket: %s", __func__, ssh_err(r)); | error("%s: window/maxpacket: %s", __func__, ssh_err(r)); | ||||
packet_disconnect("Invalid open confirmation message"); | ssh_packet_disconnect(ssh, "Invalid open confirmation message"); | ||||
} | } | ||||
ssh_packet_check_eom(ssh); | |||||
c->have_remote_id = 1; | c->have_remote_id = 1; | ||||
c->remote_window = remote_window; | c->remote_window = remote_window; | ||||
c->remote_maxpacket = remote_maxpacket; | c->remote_maxpacket = remote_maxpacket; | ||||
c->type = SSH_CHANNEL_OPEN; | c->type = SSH_CHANNEL_OPEN; | ||||
if (c->open_confirm) { | if (c->open_confirm) { | ||||
debug2("%s: channel %d: callback start", __func__, c->self); | debug2("%s: channel %d: callback start", __func__, c->self); | ||||
c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx); | c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx); | ||||
Show All 26 Lines | channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh) | ||||
Channel *c = channel_from_packet_id(ssh, __func__, "open failure"); | Channel *c = channel_from_packet_id(ssh, __func__, "open failure"); | ||||
u_int32_t reason; | u_int32_t reason; | ||||
char *msg = NULL; | char *msg = NULL; | ||||
int r; | int r; | ||||
if (channel_proxy_upstream(c, type, seq, ssh)) | if (channel_proxy_upstream(c, type, seq, ssh)) | ||||
return 0; | return 0; | ||||
if (c->type != SSH_CHANNEL_OPENING) | if (c->type != SSH_CHANNEL_OPENING) | ||||
packet_disconnect("Received open failure for " | ssh_packet_disconnect(ssh, "Received open failure for " | ||||
"non-opening channel %d.", c->self); | "non-opening channel %d.", c->self); | ||||
if ((r = sshpkt_get_u32(ssh, &reason)) != 0) { | if ((r = sshpkt_get_u32(ssh, &reason)) != 0) { | ||||
error("%s: reason: %s", __func__, ssh_err(r)); | error("%s: reason: %s", __func__, ssh_err(r)); | ||||
packet_disconnect("Invalid open failure message"); | ssh_packet_disconnect(ssh, "Invalid open failure message"); | ||||
} | } | ||||
/* skip language */ | /* skip language */ | ||||
if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 || | if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 || | ||||
(r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0) { | (r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0 || | ||||
(r = sshpkt_get_end(ssh)) != 0) { | |||||
error("%s: message/lang: %s", __func__, ssh_err(r)); | error("%s: message/lang: %s", __func__, ssh_err(r)); | ||||
packet_disconnect("Invalid open failure message"); | ssh_packet_disconnect(ssh, "Invalid open failure message"); | ||||
} | } | ||||
ssh_packet_check_eom(ssh); | |||||
logit("channel %d: open failed: %s%s%s", c->self, | logit("channel %d: open failed: %s%s%s", c->self, | ||||
reason2txt(reason), msg ? ": ": "", msg ? msg : ""); | reason2txt(reason), msg ? ": ": "", msg ? msg : ""); | ||||
free(msg); | free(msg); | ||||
if (c->open_confirm) { | if (c->open_confirm) { | ||||
debug2("%s: channel %d: callback start", __func__, c->self); | debug2("%s: channel %d: callback start", __func__, c->self); | ||||
c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx); | c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx); | ||||
debug2("%s: channel %d: callback done", __func__, c->self); | debug2("%s: channel %d: callback done", __func__, c->self); | ||||
} | } | ||||
Show All 13 Lines | channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh) | ||||
if ((c = channel_lookup(ssh, id)) == NULL) { | if ((c = channel_lookup(ssh, id)) == NULL) { | ||||
logit("Received window adjust for non-open channel %d.", id); | logit("Received window adjust for non-open channel %d.", id); | ||||
return 0; | return 0; | ||||
} | } | ||||
if (channel_proxy_upstream(c, type, seq, ssh)) | if (channel_proxy_upstream(c, type, seq, ssh)) | ||||
return 0; | return 0; | ||||
if ((r = sshpkt_get_u32(ssh, &adjust)) != 0) { | if ((r = sshpkt_get_u32(ssh, &adjust)) != 0 || | ||||
(r = sshpkt_get_end(ssh)) != 0) { | |||||
error("%s: adjust: %s", __func__, ssh_err(r)); | error("%s: adjust: %s", __func__, ssh_err(r)); | ||||
packet_disconnect("Invalid window adjust message"); | ssh_packet_disconnect(ssh, "Invalid window adjust message"); | ||||
} | } | ||||
ssh_packet_check_eom(ssh); | |||||
debug2("channel %d: rcvd adjust %u", c->self, adjust); | debug2("channel %d: rcvd adjust %u", c->self, adjust); | ||||
if ((new_rwin = c->remote_window + adjust) < c->remote_window) { | if ((new_rwin = c->remote_window + adjust) < c->remote_window) { | ||||
fatal("channel %d: adjust %u overflows remote window %u", | fatal("channel %d: adjust %u overflows remote window %u", | ||||
c->self, adjust, c->remote_window); | c->self, adjust, c->remote_window); | ||||
} | } | ||||
c->remote_window = new_rwin; | c->remote_window = new_rwin; | ||||
return 0; | return 0; | ||||
} | } | ||||
int | int | ||||
channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh) | channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh) | ||||
{ | { | ||||
int id = channel_parse_id(ssh, __func__, "status confirm"); | int id = channel_parse_id(ssh, __func__, "status confirm"); | ||||
Channel *c; | Channel *c; | ||||
struct channel_confirm *cc; | struct channel_confirm *cc; | ||||
/* Reset keepalive timeout */ | /* Reset keepalive timeout */ | ||||
packet_set_alive_timeouts(0); | ssh_packet_set_alive_timeouts(ssh, 0); | ||||
debug2("%s: type %d id %d", __func__, type, id); | debug2("%s: type %d id %d", __func__, type, id); | ||||
if ((c = channel_lookup(ssh, id)) == NULL) { | if ((c = channel_lookup(ssh, id)) == NULL) { | ||||
logit("%s: %d: unknown", __func__, id); | logit("%s: %d: unknown", __func__, id); | ||||
return 0; | return 0; | ||||
} | } | ||||
if (channel_proxy_upstream(c, type, seq, ssh)) | if (channel_proxy_upstream(c, type, seq, ssh)) | ||||
return 0; | return 0; | ||||
ssh_packet_check_eom(ssh); | if (sshpkt_get_end(ssh) != 0) | ||||
ssh_packet_disconnect(ssh, "Invalid status confirm message"); | |||||
if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) | if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) | ||||
return 0; | return 0; | ||||
cc->cb(ssh, type, c, cc->ctx); | cc->cb(ssh, type, c, cc->ctx); | ||||
TAILQ_REMOVE(&c->status_confirms, cc, entry); | TAILQ_REMOVE(&c->status_confirms, cc, entry); | ||||
explicit_bzero(cc, sizeof(*cc)); | explicit_bzero(cc, sizeof(*cc)); | ||||
free(cc); | free(cc); | ||||
return 0; | return 0; | ||||
} | } | ||||
Show All 18 Lines | |||||
* Special-case listen_addrs are: | * Special-case listen_addrs are: | ||||
* | * | ||||
* "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR | * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR | ||||
* "" (empty string), "*" -> wildcard v4/v6 | * "" (empty string), "*" -> wildcard v4/v6 | ||||
* "localhost" -> loopback v4/v6 | * "localhost" -> loopback v4/v6 | ||||
* "127.0.0.1" / "::1" -> accepted even if gateway_ports isn't set | * "127.0.0.1" / "::1" -> accepted even if gateway_ports isn't set | ||||
*/ | */ | ||||
static const char * | static const char * | ||||
channel_fwd_bind_addr(const char *listen_addr, int *wildcardp, | channel_fwd_bind_addr(struct ssh *ssh, const char *listen_addr, int *wildcardp, | ||||
int is_client, struct ForwardOptions *fwd_opts) | int is_client, struct ForwardOptions *fwd_opts) | ||||
{ | { | ||||
const char *addr = NULL; | const char *addr = NULL; | ||||
int wildcard = 0; | int wildcard = 0; | ||||
if (listen_addr == NULL) { | if (listen_addr == NULL) { | ||||
/* No address specified: default to gateway_ports setting */ | /* No address specified: default to gateway_ports setting */ | ||||
if (fwd_opts->gateway_ports) | if (fwd_opts->gateway_ports) | ||||
wildcard = 1; | wildcard = 1; | ||||
} else if (fwd_opts->gateway_ports || is_client) { | } else if (fwd_opts->gateway_ports || is_client) { | ||||
if (((datafellows & SSH_OLD_FORWARD_ADDR) && | if (((datafellows & SSH_OLD_FORWARD_ADDR) && | ||||
strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || | strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || | ||||
*listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || | *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || | ||||
(!is_client && fwd_opts->gateway_ports == 1)) { | (!is_client && fwd_opts->gateway_ports == 1)) { | ||||
wildcard = 1; | wildcard = 1; | ||||
/* | /* | ||||
* Notify client if they requested a specific listen | * Notify client if they requested a specific listen | ||||
* address and it was overridden. | * address and it was overridden. | ||||
*/ | */ | ||||
if (*listen_addr != '\0' && | if (*listen_addr != '\0' && | ||||
strcmp(listen_addr, "0.0.0.0") != 0 && | strcmp(listen_addr, "0.0.0.0") != 0 && | ||||
strcmp(listen_addr, "*") != 0) { | strcmp(listen_addr, "*") != 0) { | ||||
packet_send_debug("Forwarding listen address " | ssh_packet_send_debug(ssh, | ||||
"Forwarding listen address " | |||||
"\"%s\" overridden by server " | "\"%s\" overridden by server " | ||||
"GatewayPorts", listen_addr); | "GatewayPorts", listen_addr); | ||||
} | } | ||||
} else if (strcmp(listen_addr, "localhost") != 0 || | } else if (strcmp(listen_addr, "localhost") != 0 || | ||||
strcmp(listen_addr, "127.0.0.1") == 0 || | strcmp(listen_addr, "127.0.0.1") == 0 || | ||||
strcmp(listen_addr, "::1") == 0) { | strcmp(listen_addr, "::1") == 0) { | ||||
/* Accept localhost address when GatewayPorts=yes */ | /* Accept localhost address when GatewayPorts=yes */ | ||||
addr = listen_addr; | addr = listen_addr; | ||||
Show All 37 Lines | if (is_client && fwd->connect_path != NULL) { | ||||
} | } | ||||
if (strlen(host) >= NI_MAXHOST) { | if (strlen(host) >= NI_MAXHOST) { | ||||
error("Forward host name too long."); | error("Forward host name too long."); | ||||
return 0; | return 0; | ||||
} | } | ||||
} | } | ||||
/* Determine the bind address, cf. channel_fwd_bind_addr() comment */ | /* Determine the bind address, cf. channel_fwd_bind_addr() comment */ | ||||
addr = channel_fwd_bind_addr(fwd->listen_host, &wildcard, | addr = channel_fwd_bind_addr(ssh, fwd->listen_host, &wildcard, | ||||
is_client, fwd_opts); | is_client, fwd_opts); | ||||
debug3("%s: type %d wildcard %d addr %s", __func__, | debug3("%s: type %d wildcard %d addr %s", __func__, | ||||
type, wildcard, (addr == NULL) ? "NULL" : addr); | type, wildcard, (addr == NULL) ? "NULL" : addr); | ||||
/* | /* | ||||
* getaddrinfo returns a loopback address if the hostname is | * getaddrinfo returns a loopback address if the hostname is | ||||
* set to NULL and hints.ai_flags is not AI_PASSIVE | * set to NULL and hints.ai_flags is not AI_PASSIVE | ||||
*/ | */ | ||||
memset(&hints, 0, sizeof(hints)); | memset(&hints, 0, sizeof(hints)); | ||||
hints.ai_family = ssh->chanctxt->IPv4or6; | hints.ai_family = ssh->chanctxt->IPv4or6; | ||||
hints.ai_flags = wildcard ? AI_PASSIVE : 0; | hints.ai_flags = wildcard ? AI_PASSIVE : 0; | ||||
hints.ai_socktype = SOCK_STREAM; | hints.ai_socktype = SOCK_STREAM; | ||||
snprintf(strport, sizeof strport, "%d", fwd->listen_port); | snprintf(strport, sizeof strport, "%d", fwd->listen_port); | ||||
if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { | if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { | ||||
if (addr == NULL) { | if (addr == NULL) { | ||||
/* This really shouldn't happen */ | /* This really shouldn't happen */ | ||||
packet_disconnect("getaddrinfo: fatal error: %s", | ssh_packet_disconnect(ssh, "getaddrinfo: fatal error: %s", | ||||
ssh_gai_strerror(r)); | ssh_gai_strerror(r)); | ||||
} else { | } else { | ||||
error("%s: getaddrinfo(%.64s): %s", __func__, addr, | error("%s: getaddrinfo(%.64s): %s", __func__, addr, | ||||
ssh_gai_strerror(r)); | ssh_gai_strerror(r)); | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
if (allocated_listen_port != NULL) | if (allocated_listen_port != NULL) | ||||
Show All 23 Lines | for (ai = aitop; ai; ai = ai->ai_next) { | ||||
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), | if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), | ||||
strport, sizeof(strport), | strport, sizeof(strport), | ||||
NI_NUMERICHOST|NI_NUMERICSERV) != 0) { | NI_NUMERICHOST|NI_NUMERICSERV) != 0) { | ||||
error("%s: getnameinfo failed", __func__); | error("%s: getnameinfo failed", __func__); | ||||
continue; | continue; | ||||
} | } | ||||
/* Create a port to listen for the host. */ | /* Create a port to listen for the host. */ | ||||
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | ||||
if (sock < 0) { | if (sock == -1) { | ||||
/* this is no error since kernel may not support ipv6 */ | /* this is no error since kernel may not support ipv6 */ | ||||
verbose("socket [%s]:%s: %.100s", ntop, strport, | verbose("socket [%s]:%s: %.100s", ntop, strport, | ||||
strerror(errno)); | strerror(errno)); | ||||
continue; | continue; | ||||
} | } | ||||
set_reuseaddr(sock); | set_reuseaddr(sock); | ||||
if (ai->ai_family == AF_INET6) | if (ai->ai_family == AF_INET6) | ||||
sock_set_v6only(sock); | sock_set_v6only(sock); | ||||
debug("Local forwarding listening on %s port %s.", | debug("Local forwarding listening on %s port %s.", | ||||
ntop, strport); | ntop, strport); | ||||
/* Bind the socket to the address. */ | /* Bind the socket to the address. */ | ||||
if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { | if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { | ||||
/* | /* | ||||
* address can be in if use ipv6 address is | * address can be in if use ipv6 address is | ||||
* already bound | * already bound | ||||
*/ | */ | ||||
if (!ai->ai_next) | if (!ai->ai_next) | ||||
error("bind [%s]:%s: %.100s", | error("bind [%s]:%s: %.100s", | ||||
ntop, strport, strerror(errno)); | ntop, strport, strerror(errno)); | ||||
else | else | ||||
verbose("bind [%s]:%s: %.100s", | verbose("bind [%s]:%s: %.100s", | ||||
ntop, strport, strerror(errno)); | ntop, strport, strerror(errno)); | ||||
close(sock); | close(sock); | ||||
continue; | continue; | ||||
} | } | ||||
/* Start listening for connections on the socket. */ | /* Start listening for connections on the socket. */ | ||||
if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { | if (listen(sock, SSH_LISTEN_BACKLOG) == -1) { | ||||
error("listen: %.100s", strerror(errno)); | error("listen: %.100s", strerror(errno)); | ||||
error("listen [%s]:%s: %.100s", ntop, strport, | error("listen [%s]:%s: %.100s", ntop, strport, | ||||
strerror(errno)); | strerror(errno)); | ||||
close(sock); | close(sock); | ||||
continue; | continue; | ||||
} | } | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 161 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
channel_cancel_lport_listener_tcpip(struct ssh *ssh, | channel_cancel_lport_listener_tcpip(struct ssh *ssh, | ||||
const char *lhost, u_short lport, int cport, | const char *lhost, u_short lport, int cport, | ||||
struct ForwardOptions *fwd_opts) | struct ForwardOptions *fwd_opts) | ||||
{ | { | ||||
u_int i; | u_int i; | ||||
int found = 0; | int found = 0; | ||||
const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, fwd_opts); | const char *addr = channel_fwd_bind_addr(ssh, lhost, NULL, 1, fwd_opts); | ||||
for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { | for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { | ||||
Channel *c = ssh->chanctxt->channels[i]; | Channel *c = ssh->chanctxt->channels[i]; | ||||
if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER) | if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER) | ||||
continue; | continue; | ||||
if (c->listening_port != lport) | if (c->listening_port != lport) | ||||
continue; | continue; | ||||
if (cport == CHANNEL_CANCEL_PORT_STATIC) { | if (cport == CHANNEL_CANCEL_PORT_STATIC) { | ||||
▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* protocol v2 remote port fwd, used by sshd */ | /* protocol v2 remote port fwd, used by sshd */ | ||||
int | int | ||||
channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd, | channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd, | ||||
int *allocated_listen_port, struct ForwardOptions *fwd_opts) | int *allocated_listen_port, struct ForwardOptions *fwd_opts) | ||||
{ | { | ||||
if (!check_rfwd_permission(ssh, fwd)) { | if (!check_rfwd_permission(ssh, fwd)) { | ||||
packet_send_debug("port forwarding refused"); | ssh_packet_send_debug(ssh, "port forwarding refused"); | ||||
if (fwd->listen_path != NULL) | |||||
/* XXX always allowed, see remote_open_match() */ | |||||
logit("Received request from %.100s port %d to " | |||||
"remote forward to path \"%.100s\", " | |||||
"but the request was denied.", | |||||
ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), | |||||
fwd->listen_path); | |||||
else if(fwd->listen_host != NULL) | |||||
logit("Received request from %.100s port %d to " | |||||
"remote forward to host %.100s port %d, " | |||||
"but the request was denied.", | |||||
ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), | |||||
fwd->listen_host, fwd->listen_port ); | |||||
else | |||||
logit("Received request from %.100s port %d to remote " | |||||
"forward, but the request was denied.", | |||||
ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); | |||||
return 0; | return 0; | ||||
} | } | ||||
if (fwd->listen_path != NULL) { | if (fwd->listen_path != NULL) { | ||||
return channel_setup_fwd_listener_streamlocal(ssh, | return channel_setup_fwd_listener_streamlocal(ssh, | ||||
SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); | SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); | ||||
} else { | } else { | ||||
return channel_setup_fwd_listener_tcpip(ssh, | return channel_setup_fwd_listener_tcpip(ssh, | ||||
SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port, | SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port, | ||||
▲ Show 20 Lines • Show All 579 Lines • ▼ Show 20 Lines | for (i = 0; i < pset->num_permitted_admin; i++) { | ||||
if (open_match(perm, host, port)) { | if (open_match(perm, host, port)) { | ||||
permit_adm = 1; | permit_adm = 1; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (!permit || !permit_adm) { | if (!permit || !permit_adm) { | ||||
logit("Received request to connect to host %.100s port %d, " | logit("Received request from %.100s port %d to connect to " | ||||
"but the request was denied.", host, port); | "host %.100s port %d, but the request was denied.", | ||||
ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), host, port); | |||||
if (reason != NULL) | if (reason != NULL) | ||||
*reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; | *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; | ||||
return NULL; | return NULL; | ||||
} | } | ||||
memset(&cctx, 0, sizeof(cctx)); | memset(&cctx, 0, sizeof(cctx)); | ||||
sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, | sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, | ||||
&cctx, reason, errmsg); | &cctx, reason, errmsg); | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | channel_send_window_changes(struct ssh *ssh) | ||||
struct winsize ws; | struct winsize ws; | ||||
int r; | int r; | ||||
u_int i; | u_int i; | ||||
for (i = 0; i < sc->channels_alloc; i++) { | for (i = 0; i < sc->channels_alloc; i++) { | ||||
if (sc->channels[i] == NULL || !sc->channels[i]->client_tty || | if (sc->channels[i] == NULL || !sc->channels[i]->client_tty || | ||||
sc->channels[i]->type != SSH_CHANNEL_OPEN) | sc->channels[i]->type != SSH_CHANNEL_OPEN) | ||||
continue; | continue; | ||||
if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) < 0) | if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) == -1) | ||||
continue; | continue; | ||||
channel_request_start(ssh, i, "window-change", 0); | channel_request_start(ssh, i, "window-change", 0); | ||||
if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 || | if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 || | ||||
(r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 || | (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 || | ||||
(r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 || | (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 || | ||||
(r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 || | (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 || | ||||
(r = sshpkt_send(ssh)) != 0) | (r = sshpkt_send(ssh)) != 0) | ||||
fatal("%s: channel %u: send window-change: %s", | fatal("%s: channel %u: send window-change: %s", | ||||
▲ Show 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | if ((gaierr = getaddrinfo(NULL, strport, | ||||
return -1; | return -1; | ||||
} | } | ||||
for (ai = aitop; ai; ai = ai->ai_next) { | for (ai = aitop; ai; ai = ai->ai_next) { | ||||
if (ai->ai_family != AF_INET && | if (ai->ai_family != AF_INET && | ||||
ai->ai_family != AF_INET6) | ai->ai_family != AF_INET6) | ||||
continue; | continue; | ||||
sock = socket(ai->ai_family, ai->ai_socktype, | sock = socket(ai->ai_family, ai->ai_socktype, | ||||
ai->ai_protocol); | ai->ai_protocol); | ||||
if (sock < 0) { | if (sock == -1) { | ||||
if ((errno != EINVAL) && (errno != EAFNOSUPPORT) | if ((errno != EINVAL) && (errno != EAFNOSUPPORT) | ||||
#ifdef EPFNOSUPPORT | #ifdef EPFNOSUPPORT | ||||
&& (errno != EPFNOSUPPORT) | && (errno != EPFNOSUPPORT) | ||||
#endif | #endif | ||||
) { | ) { | ||||
error("socket: %.100s", strerror(errno)); | error("socket: %.100s", strerror(errno)); | ||||
freeaddrinfo(aitop); | freeaddrinfo(aitop); | ||||
return -1; | return -1; | ||||
} else { | } else { | ||||
debug("x11_create_display_inet: Socket family %d not supported", | debug("x11_create_display_inet: Socket family %d not supported", | ||||
ai->ai_family); | ai->ai_family); | ||||
continue; | continue; | ||||
} | } | ||||
} | } | ||||
if (ai->ai_family == AF_INET6) | if (ai->ai_family == AF_INET6) | ||||
sock_set_v6only(sock); | sock_set_v6only(sock); | ||||
if (x11_use_localhost) | if (x11_use_localhost) | ||||
set_reuseaddr(sock); | set_reuseaddr(sock); | ||||
if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { | if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { | ||||
debug2("%s: bind port %d: %.100s", __func__, | debug2("%s: bind port %d: %.100s", __func__, | ||||
port, strerror(errno)); | port, strerror(errno)); | ||||
close(sock); | close(sock); | ||||
for (n = 0; n < num_socks; n++) | for (n = 0; n < num_socks; n++) | ||||
close(socks[n]); | close(socks[n]); | ||||
num_socks = 0; | num_socks = 0; | ||||
break; | break; | ||||
} | } | ||||
socks[num_socks++] = sock; | socks[num_socks++] = sock; | ||||
if (num_socks == NUM_SOCKS) | if (num_socks == NUM_SOCKS) | ||||
break; | break; | ||||
} | } | ||||
freeaddrinfo(aitop); | freeaddrinfo(aitop); | ||||
if (num_socks > 0) | if (num_socks > 0) | ||||
break; | break; | ||||
} | } | ||||
if (display_number >= MAX_DISPLAYS) { | if (display_number >= MAX_DISPLAYS) { | ||||
error("Failed to allocate internet-domain X11 display socket."); | error("Failed to allocate internet-domain X11 display socket."); | ||||
return -1; | return -1; | ||||
} | } | ||||
/* Start listening for connections on the socket. */ | /* Start listening for connections on the socket. */ | ||||
for (n = 0; n < num_socks; n++) { | for (n = 0; n < num_socks; n++) { | ||||
sock = socks[n]; | sock = socks[n]; | ||||
if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { | if (listen(sock, SSH_LISTEN_BACKLOG) == -1) { | ||||
error("listen: %.100s", strerror(errno)); | error("listen: %.100s", strerror(errno)); | ||||
close(sock); | close(sock); | ||||
return -1; | return -1; | ||||
} | } | ||||
} | } | ||||
/* Allocate a channel for each socket. */ | /* Allocate a channel for each socket. */ | ||||
*chanids = xcalloc(num_socks + 1, sizeof(**chanids)); | *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); | ||||
Show All 15 Lines | |||||
static int | static int | ||||
connect_local_xsocket_path(const char *pathname) | connect_local_xsocket_path(const char *pathname) | ||||
{ | { | ||||
int sock; | int sock; | ||||
struct sockaddr_un addr; | struct sockaddr_un addr; | ||||
sock = socket(AF_UNIX, SOCK_STREAM, 0); | sock = socket(AF_UNIX, SOCK_STREAM, 0); | ||||
if (sock < 0) | if (sock == -1) | ||||
error("socket: %.100s", strerror(errno)); | error("socket: %.100s", strerror(errno)); | ||||
memset(&addr, 0, sizeof(addr)); | memset(&addr, 0, sizeof(addr)); | ||||
addr.sun_family = AF_UNIX; | addr.sun_family = AF_UNIX; | ||||
strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); | strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); | ||||
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) | if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) | ||||
return sock; | return sock; | ||||
close(sock); | close(sock); | ||||
error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); | error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); | ||||
▲ Show 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | #endif | ||||
if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { | if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { | ||||
error("%.100s: unknown host. (%s)", buf, | error("%.100s: unknown host. (%s)", buf, | ||||
ssh_gai_strerror(gaierr)); | ssh_gai_strerror(gaierr)); | ||||
return -1; | return -1; | ||||
} | } | ||||
for (ai = aitop; ai; ai = ai->ai_next) { | for (ai = aitop; ai; ai = ai->ai_next) { | ||||
/* Create a socket. */ | /* Create a socket. */ | ||||
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | ||||
if (sock < 0) { | if (sock == -1) { | ||||
debug2("socket: %.100s", strerror(errno)); | debug2("socket: %.100s", strerror(errno)); | ||||
continue; | continue; | ||||
} | } | ||||
/* Connect it to the display. */ | /* Connect it to the display. */ | ||||
if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { | if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) { | ||||
debug2("connect %.100s port %u: %.100s", buf, | debug2("connect %.100s port %u: %.100s", buf, | ||||
6000 + display_number, strerror(errno)); | 6000 + display_number, strerror(errno)); | ||||
close(sock); | close(sock); | ||||
continue; | continue; | ||||
} | } | ||||
/* Success */ | /* Success */ | ||||
break; | break; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 76 Lines • Show Last 20 Lines |