aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bind_config.c107
-rw-r--r--src/channels.c16
-rw-r--r--src/config.c31
-rw-r--r--src/misc.c30
-rw-r--r--src/poll.c12
5 files changed, 157 insertions, 39 deletions
diff --git a/src/bind_config.c b/src/bind_config.c
index ace5a0ac..4c891c95 100644
--- a/src/bind_config.c
+++ b/src/bind_config.c
@@ -189,18 +189,29 @@ ssh_bind_config_parse_line(ssh_bind bind,
const char *line,
unsigned int count,
uint32_t *parser_flags,
- uint8_t *seen);
-
-static void local_parse_file(ssh_bind bind,
- const char *filename,
- uint32_t *parser_flags,
- uint8_t *seen)
+ uint8_t *seen,
+ unsigned int depth);
+
+#define LIBSSH_BIND_CONF_MAX_DEPTH 16
+static void
+local_parse_file(ssh_bind bind,
+ const char *filename,
+ uint32_t *parser_flags,
+ uint8_t *seen,
+ unsigned int depth)
{
FILE *f;
char line[MAX_LINE_SIZE] = {0};
unsigned int count = 0;
int rv;
+ if (depth > LIBSSH_BIND_CONF_MAX_DEPTH) {
+ ssh_set_error(bind, SSH_FATAL,
+ "ERROR - Too many levels of configuration includes "
+ "when processing file '%s'", filename);
+ return;
+ }
+
f = fopen(filename, "r");
if (f == NULL) {
SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
@@ -213,7 +224,7 @@ static void local_parse_file(ssh_bind bind,
while (fgets(line, sizeof(line), f)) {
count++;
- rv = ssh_bind_config_parse_line(bind, line, count, parser_flags, seen);
+ rv = ssh_bind_config_parse_line(bind, line, count, parser_flags, seen, depth);
if (rv < 0) {
fclose(f);
return;
@@ -228,7 +239,8 @@ static void local_parse_file(ssh_bind bind,
static void local_parse_glob(ssh_bind bind,
const char *fileglob,
uint32_t *parser_flags,
- uint8_t *seen)
+ uint8_t *seen,
+ unsigned int depth)
{
glob_t globbuf = {
.gl_flags = 0,
@@ -248,7 +260,7 @@ static void local_parse_glob(ssh_bind bind,
}
for (i = 0; i < globbuf.gl_pathc; i++) {
- local_parse_file(bind, globbuf.gl_pathv[i], parser_flags, seen);
+ local_parse_file(bind, globbuf.gl_pathv[i], parser_flags, seen, depth);
}
globfree(&globbuf);
@@ -274,7 +286,8 @@ ssh_bind_config_parse_line(ssh_bind bind,
const char *line,
unsigned int count,
uint32_t *parser_flags,
- uint8_t *seen)
+ uint8_t *seen,
+ unsigned int depth)
{
enum ssh_bind_config_opcode_e opcode;
const char *p = NULL;
@@ -288,7 +301,12 @@ ssh_bind_config_parse_line(ssh_bind bind,
return -1;
}
- if ((line == NULL) || (parser_flags == NULL)) {
+ /* Ignore empty lines */
+ if (line == NULL || *line == '\0') {
+ return 0;
+ }
+
+ if (parser_flags == NULL) {
ssh_set_error_invalid(bind);
return -1;
}
@@ -333,9 +351,9 @@ ssh_bind_config_parse_line(ssh_bind bind,
p = ssh_config_get_str_tok(&s, NULL);
if (p && (*parser_flags & PARSING)) {
#if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER)
- local_parse_glob(bind, p, parser_flags, seen);
+ local_parse_glob(bind, p, parser_flags, seen, depth + 1);
#else
- local_parse_file(bind, p, parser_flags, seen);
+ local_parse_file(bind, p, parser_flags, seen, depth + 1);
#endif /* HAVE_GLOB */
}
break;
@@ -628,7 +646,7 @@ int ssh_bind_config_parse_file(ssh_bind bind, const char *filename)
parser_flags = PARSING;
while (fgets(line, sizeof(line), f)) {
count++;
- rv = ssh_bind_config_parse_line(bind, line, count, &parser_flags, seen);
+ rv = ssh_bind_config_parse_line(bind, line, count, &parser_flags, seen, 0);
if (rv) {
fclose(f);
return -1;
@@ -638,3 +656,64 @@ int ssh_bind_config_parse_file(ssh_bind bind, const char *filename)
fclose(f);
return 0;
}
+
+/* @brief Parse configuration string and set the options to the given bind session
+ *
+ * @params[in] bind The ssh bind session
+ * @params[in] input Null terminated string containing the configuration
+ *
+ * @returns SSH_OK on successful parsing the configuration string,
+ * SSH_ERROR on error
+ */
+int ssh_bind_config_parse_string(ssh_bind bind, const char *input)
+{
+ char line[MAX_LINE_SIZE] = {0};
+ const char *c = input, *line_start = input;
+ unsigned int line_num = 0, line_len;
+ uint32_t parser_flags;
+ int rv;
+
+ /* This local table is used during the parsing of the current file (and
+ * files included recursively in this file) to prevent an option to be
+ * redefined, i.e. the first value set is kept. But this DO NOT prevent the
+ * option to be redefined later by another file. */
+ uint8_t seen[BIND_CFG_MAX] = {0};
+
+ SSH_LOG(SSH_LOG_DEBUG, "Reading bind configuration data from string:");
+ SSH_LOG(SSH_LOG_DEBUG, "START\n%s\nEND", input);
+
+ parser_flags = PARSING;
+ while (1) {
+ line_num++;
+ line_start = c;
+ c = strchr(line_start, '\n');
+ if (c == NULL) {
+ /* if there is no newline in the end of the string */
+ c = strchr(line_start, '\0');
+ }
+ if (c == NULL) {
+ /* should not happen, would mean a string without trailing '\0' */
+ SSH_LOG(SSH_LOG_WARN, "No trailing '\\0' in config string");
+ return SSH_ERROR;
+ }
+ line_len = c - line_start;
+ if (line_len > MAX_LINE_SIZE - 1) {
+ SSH_LOG(SSH_LOG_WARN, "Line %u too long: %u characters",
+ line_num, line_len);
+ return SSH_ERROR;
+ }
+ memcpy(line, line_start, line_len);
+ line[line_len] = '\0';
+ SSH_LOG(SSH_LOG_DEBUG, "Line %u: %s", line_num, line);
+ rv = ssh_bind_config_parse_line(bind, line, line_num, &parser_flags, seen, 0);
+ if (rv < 0) {
+ return SSH_ERROR;
+ }
+ if (*c == '\0') {
+ break;
+ }
+ c++;
+ }
+
+ return SSH_OK;
+}
diff --git a/src/channels.c b/src/channels.c
index 8d812477..0dc4260d 100644
--- a/src/channels.c
+++ b/src/channels.c
@@ -245,6 +245,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail){
"SSH2_MSG_CHANNEL_OPEN_FAILURE received in incorrect channel "
"state %d",
channel->state);
+ SAFE_FREE(error);
goto error;
}
@@ -878,7 +879,7 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
#else
SSH_LOG(SSH_LOG_WARNING, "Unhandled channel request %s", request);
#endif
-
+
SAFE_FREE(request);
return SSH_PACKET_USED;
@@ -3086,6 +3087,8 @@ int ssh_channel_read_nonblocking(ssh_channel channel,
*
* @return The number of bytes available for reading, 0 if nothing
* is available or SSH_ERROR on error.
+ * When a channel is freed the function returns
+ * SSH_ERROR immediately.
*
* @warning When the channel is in EOF state, the function returns SSH_EOF.
*
@@ -3094,7 +3097,7 @@ int ssh_channel_read_nonblocking(ssh_channel channel,
int ssh_channel_poll(ssh_channel channel, int is_stderr){
ssh_buffer stdbuf;
- if(channel == NULL) {
+ if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) {
return SSH_ERROR;
}
@@ -3140,6 +3143,7 @@ int ssh_channel_poll(ssh_channel channel, int is_stderr){
* SSH_ERROR on error.
*
* @warning When the channel is in EOF state, the function returns SSH_EOF.
+ * When a channel is freed the function returns SSH_ERROR immediately.
*
* @see ssh_channel_is_eof()
*/
@@ -3151,7 +3155,7 @@ int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr)
size_t len;
int rc;
- if (channel == NULL) {
+ if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) {
return SSH_ERROR;
}
@@ -3233,6 +3237,8 @@ static int ssh_channel_exit_status_termination(void *c){
* (yet), or SSH_ERROR on error.
* @warning This function may block until a timeout (or never)
* if the other side is not willing to close the channel.
+ * When a channel is freed the function returns
+ * SSH_ERROR immediately.
*
* If you're looking for an async handling of this register a callback for the
* exit status.
@@ -3241,7 +3247,7 @@ static int ssh_channel_exit_status_termination(void *c){
*/
int ssh_channel_get_exit_status(ssh_channel channel) {
int rc;
- if(channel == NULL) {
+ if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) {
return SSH_ERROR;
}
rc = ssh_handle_packets_termination(channel->session,
@@ -3590,7 +3596,7 @@ error:
* forward the content of a socket to the channel. You still have to
* use channel_read and channel_write for this.
*/
-int ssh_channel_open_x11(ssh_channel channel,
+int ssh_channel_open_x11(ssh_channel channel,
const char *orig_addr, int orig_port) {
ssh_session session;
ssh_buffer payload = NULL;
diff --git a/src/config.c b/src/config.c
index a082e994..7ff270ff 100644
--- a/src/config.c
+++ b/src/config.c
@@ -191,7 +191,7 @@ static struct ssh_config_match_keyword_table_s ssh_config_match_keyword_table[]
};
static int ssh_config_parse_line(ssh_session session, const char *line,
- unsigned int count, int *parsing);
+ unsigned int count, int *parsing, unsigned int depth);
static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
int i;
@@ -205,16 +205,25 @@ static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
return SOC_UNKNOWN;
}
+#define LIBSSH_CONF_MAX_DEPTH 16
static void
local_parse_file(ssh_session session,
const char *filename,
- int *parsing)
+ int *parsing,
+ unsigned int depth)
{
FILE *f;
char line[MAX_LINE_SIZE] = {0};
unsigned int count = 0;
int rv;
+ if (depth > LIBSSH_CONF_MAX_DEPTH) {
+ ssh_set_error(session, SSH_FATAL,
+ "ERROR - Too many levels of configuration includes "
+ "when processing file '%s'", filename);
+ return;
+ }
+
f = fopen(filename, "r");
if (f == NULL) {
SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
@@ -225,7 +234,7 @@ local_parse_file(ssh_session session,
SSH_LOG(SSH_LOG_PACKET, "Reading additional configuration data from %s", filename);
while (fgets(line, sizeof(line), f)) {
count++;
- rv = ssh_config_parse_line(session, line, count, parsing);
+ rv = ssh_config_parse_line(session, line, count, parsing, depth);
if (rv < 0) {
fclose(f);
return;
@@ -239,7 +248,8 @@ local_parse_file(ssh_session session,
#if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER)
static void local_parse_glob(ssh_session session,
const char *fileglob,
- int *parsing)
+ int *parsing,
+ unsigned int depth)
{
glob_t globbuf = {
.gl_flags = 0,
@@ -259,7 +269,7 @@ static void local_parse_glob(ssh_session session,
}
for (i = 0; i < globbuf.gl_pathc; i++) {
- local_parse_file(session, globbuf.gl_pathv[i], parsing);
+ local_parse_file(session, globbuf.gl_pathv[i], parsing, depth);
}
globfree(&globbuf);
@@ -513,7 +523,8 @@ static int
ssh_config_parse_line(ssh_session session,
const char *line,
unsigned int count,
- int *parsing)
+ int *parsing,
+ unsigned int depth)
{
enum ssh_config_opcode_e opcode;
const char *p = NULL, *p2 = NULL;
@@ -573,9 +584,9 @@ ssh_config_parse_line(ssh_session session,
p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) {
#if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER)
- local_parse_glob(session, p, parsing);
+ local_parse_glob(session, p, parsing, depth + 1);
#else
- local_parse_file(session, p, parsing);
+ local_parse_file(session, p, parsing, depth + 1);
#endif /* HAVE_GLOB */
}
break;
@@ -1163,7 +1174,7 @@ int ssh_config_parse_file(ssh_session session, const char *filename)
parsing = 1;
while (fgets(line, sizeof(line), f)) {
count++;
- rv = ssh_config_parse_line(session, line, count, &parsing);
+ rv = ssh_config_parse_line(session, line, count, &parsing, 0);
if (rv < 0) {
fclose(f);
return -1;
@@ -1215,7 +1226,7 @@ int ssh_config_parse_string(ssh_session session, const char *input)
memcpy(line, line_start, line_len);
line[line_len] = '\0';
SSH_LOG(SSH_LOG_DEBUG, "Line %u: %s", line_num, line);
- rv = ssh_config_parse_line(session, line, line_num, &parsing);
+ rv = ssh_config_parse_line(session, line, line_num, &parsing, 0);
if (rv < 0) {
return SSH_ERROR;
}
diff --git a/src/misc.c b/src/misc.c
index 6472d583..97c3a0ef 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -1156,7 +1156,15 @@ char *ssh_path_expand_escape(ssh_session session, const char *s) {
case '%':
goto escape;
case 'd':
- x = strdup(session->opts.sshdir);
+ if (session->opts.sshdir) {
+ x = strdup(session->opts.sshdir);
+ } else {
+ ssh_set_error(session, SSH_FATAL,
+ "Cannot expand sshdir");
+ free(buf);
+ free(r);
+ return NULL;
+ }
break;
case 'u':
x = ssh_get_local_username();
@@ -1167,10 +1175,26 @@ char *ssh_path_expand_escape(ssh_session session, const char *s) {
}
break;
case 'h':
- x = strdup(session->opts.host);
+ if (session->opts.host) {
+ x = strdup(session->opts.host);
+ } else {
+ ssh_set_error(session, SSH_FATAL,
+ "Cannot expand host");
+ free(buf);
+ free(r);
+ return NULL;
+ }
break;
case 'r':
- x = strdup(session->opts.username);
+ if (session->opts.username) {
+ x = strdup(session->opts.username);
+ } else {
+ ssh_set_error(session, SSH_FATAL,
+ "Cannot expand username");
+ free(buf);
+ free(r);
+ return NULL;
+ }
break;
case 'p':
if (session->opts.port < 65536) {
diff --git a/src/poll.c b/src/poll.c
index 46206949..82c9b18b 100644
--- a/src/poll.c
+++ b/src/poll.c
@@ -246,19 +246,17 @@ static int bsd_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
}
#endif
- if (fds[i].events & (POLLIN | POLLRDNORM)) {
- FD_SET (fds[i].fd, &readfds);
- }
+ // we use the readfds to get POLLHUP and POLLERR, which are provided even when not requested
+ FD_SET (fds[i].fd, &readfds);
+
if (fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) {
FD_SET (fds[i].fd, &writefds);
}
if (fds[i].events & (POLLPRI | POLLRDBAND)) {
FD_SET (fds[i].fd, &exceptfds);
}
- if (fds[i].fd > max_fd &&
- (fds[i].events & (POLLIN | POLLOUT | POLLPRI |
- POLLRDNORM | POLLRDBAND |
- POLLWRNORM | POLLWRBAND))) {
+
+ if (fds[i].fd > max_fd) {
max_fd = fds[i].fd;
rc = 0;
}