upgrade to 0.9.4

This commit is contained in:
xiaoweiwei 2020-07-27 17:48:00 +08:00
parent d1fc6f2a8f
commit 5ae3ce762b
22 changed files with 47 additions and 5956 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,169 +0,0 @@
From 82c375b7c99141a5495e62060e0b7f9c97981e7e Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Fri, 25 Oct 2019 13:24:28 +0200
Subject: CVE-2019-14889: scp: Log SCP warnings received from the server
Fixes T181
Previously, warnings received from the server were ignored. With this
change the warning message sent by the server will be logged.
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit c75d417d06867fd792b788e6281334621c2cd335)
---
src/scp.c | 75 ++++++++++-----------------------------------------------------
1 file changed, 11 insertions(+), 64 deletions(-)
diff --git a/src/scp.c b/src/scp.c
index 5de0e6ff..166f3d2f 100644
--- a/src/scp.c
+++ b/src/scp.c
@@ -113,7 +113,6 @@ int ssh_scp_init(ssh_scp scp)
{
int rc;
char execbuffer[1024] = {0};
- uint8_t code;
if (scp == NULL) {
return SSH_ERROR;
@@ -157,19 +156,8 @@ int ssh_scp_init(ssh_scp scp)
}
if (scp->mode == SSH_SCP_WRITE) {
- rc = ssh_channel_read(scp->channel, &code, 1, 0);
- if (rc <= 0) {
- ssh_set_error(scp->session, SSH_FATAL,
- "Error reading status code: %s",
- ssh_get_error(scp->session));
- scp->state = SSH_SCP_ERROR;
- return SSH_ERROR;
- }
-
- if (code != 0) {
- ssh_set_error(scp->session, SSH_FATAL,
- "scp status code %ud not valid", code);
- scp->state = SSH_SCP_ERROR;
+ rc = ssh_scp_response(scp, NULL);
+ if (rc != 0) {
return SSH_ERROR;
}
} else {
@@ -277,7 +265,6 @@ int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode)
{
char buffer[1024] = {0};
int rc;
- uint8_t code;
char *dir = NULL;
char *perms = NULL;
@@ -303,19 +290,8 @@ int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode)
return SSH_ERROR;
}
- rc = ssh_channel_read(scp->channel, &code, 1, 0);
- if (rc <= 0) {
- ssh_set_error(scp->session, SSH_FATAL,
- "Error reading status code: %s",
- ssh_get_error(scp->session));
- scp->state = SSH_SCP_ERROR;
- return SSH_ERROR;
- }
-
- if (code != 0) {
- ssh_set_error(scp->session, SSH_FATAL, "scp status code %ud not valid",
- code);
- scp->state = SSH_SCP_ERROR;
+ rc = ssh_scp_response(scp, NULL);
+ if (rc != 0) {
return SSH_ERROR;
}
@@ -334,7 +310,6 @@ int ssh_scp_leave_directory(ssh_scp scp)
{
char buffer[] = "E\n";
int rc;
- uint8_t code;
if (scp == NULL) {
return SSH_ERROR;
@@ -352,18 +327,8 @@ int ssh_scp_leave_directory(ssh_scp scp)
return SSH_ERROR;
}
- rc = ssh_channel_read(scp->channel, &code, 1, 0);
- if (rc <= 0) {
- ssh_set_error(scp->session, SSH_FATAL, "Error reading status code: %s",
- ssh_get_error(scp->session));
- scp->state = SSH_SCP_ERROR;
- return SSH_ERROR;
- }
-
- if (code != 0) {
- ssh_set_error(scp->session, SSH_FATAL, "scp status code %ud not valid",
- code);
- scp->state = SSH_SCP_ERROR;
+ rc = ssh_scp_response(scp, NULL);
+ if (rc != 0) {
return SSH_ERROR;
}
@@ -395,7 +360,6 @@ int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size,
int rc;
char *file = NULL;
char *perms = NULL;
- uint8_t code;
if (scp == NULL) {
return SSH_ERROR;
@@ -422,19 +386,8 @@ int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size,
return SSH_ERROR;
}
- rc = ssh_channel_read(scp->channel, &code, 1, 0);
- if (rc <= 0) {
- ssh_set_error(scp->session, SSH_FATAL,
- "Error reading status code: %s",
- ssh_get_error(scp->session));
- scp->state = SSH_SCP_ERROR;
- return SSH_ERROR;
- }
-
- if (code != 0) {
- ssh_set_error(scp->session, SSH_FATAL,
- "scp status code %ud not valid", code);
- scp->state = SSH_SCP_ERROR;
+ rc = ssh_scp_response(scp, NULL);
+ if (rc != 0) {
return SSH_ERROR;
}
@@ -498,7 +451,7 @@ int ssh_scp_response(ssh_scp scp, char **response)
if (code > 2) {
ssh_set_error(scp->session, SSH_FATAL,
- "SCP: invalid status code %ud received", code);
+ "SCP: invalid status code %u received", code);
scp->state = SSH_SCP_ERROR;
return SSH_ERROR;
}
@@ -585,14 +538,8 @@ int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len)
* and handle */
rc = ssh_channel_poll(scp->channel, 0);
if (rc > 0) {
- rc = ssh_channel_read(scp->channel, &code, 1, 0);
- if (rc == SSH_ERROR) {
- return SSH_ERROR;
- }
-
- if (code == 1 || code == 2) {
- ssh_set_error(scp->session, SSH_REQUEST_DENIED,
- "SCP: Error: status code %i received", code);
+ rc = ssh_scp_response(scp, NULL);
+ if (rc != 0) {
return SSH_ERROR;
}
}
--
cgit v1.2.1

View File

@ -1,238 +0,0 @@
From 2ba1dea5493fb2f5a5be2dd263ce46ccb5f8ec76 Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Tue, 22 Oct 2019 16:08:24 +0200
Subject: CVE-2019-14889: misc: Add function to quote file names
The added function quote file names strings to be used in a shell.
Special cases are treated for the charactes '\'' and '!'.
Fixes T181
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit c4ad1aba9860e02fe03ef3f58a047964e9e765fc)
---
include/libssh/misc.h | 8 +++
src/misc.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 192 insertions(+)
diff --git a/include/libssh/misc.h b/include/libssh/misc.h
index bc50cff8..0531d4f3 100644
--- a/include/libssh/misc.h
+++ b/include/libssh/misc.h
@@ -50,6 +50,12 @@ struct ssh_timestamp {
long useconds;
};
+enum ssh_quote_state_e {
+ NO_QUOTE,
+ SINGLE_QUOTE,
+ DOUBLE_QUOTE
+};
+
struct ssh_list *ssh_list_new(void);
void ssh_list_free(struct ssh_list *list);
struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list);
@@ -81,4 +87,6 @@ int ssh_timeout_update(struct ssh_timestamp *ts, int timeout);
int ssh_match_group(const char *group, const char *object);
+int ssh_quote_file_name(const char *file_name, char *buf, size_t buf_len);
+
#endif /* MISC_H_ */
diff --git a/src/misc.c b/src/misc.c
index 18c9745e..b042b46d 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -1108,4 +1108,188 @@ char *strndup(const char *s, size_t n)
}
#endif /* ! HAVE_STRNDUP */
+/**
+ * @internal
+ *
+ * @brief Quote file name to be used on shell.
+ *
+ * Try to put the given file name between single quotes. There are special
+ * cases:
+ *
+ * - When the '\'' char is found in the file name, it is double quoted
+ * - example:
+ * input: a'b
+ * output: 'a'"'"'b'
+ * - When the '!' char is found in the file name, it is replaced by an unquoted
+ * verbatim char "\!"
+ * - example:
+ * input: a!b
+ * output 'a'\!'b'
+ *
+ * @param[in] file_name File name string to be quoted before used on shell
+ * @param[out] buf Buffer to receive the final quoted file name. Must
+ * have room for the final quoted string. The maximum
+ * output length would be (3 * strlen(file_name) + 1)
+ * since in the worst case each character would be
+ * replaced by 3 characters, plus the terminating '\0'.
+ * @param[in] buf_len The size of the provided output buffer
+ *
+ * @returns SSH_ERROR on error; length of the resulting string not counting the
+ * string terminator '\0'
+ * */
+int ssh_quote_file_name(const char *file_name, char *buf, size_t buf_len)
+{
+ const char *src = NULL;
+ char *dst = NULL;
+ size_t required_buf_len;
+
+ enum ssh_quote_state_e state = NO_QUOTE;
+
+ if (file_name == NULL || buf == NULL || buf_len == 0) {
+ SSH_LOG(SSH_LOG_WARNING, "Invalid parameter");
+ return SSH_ERROR;
+ }
+
+ /* Only allow file names smaller than 32kb. */
+ if (strlen(file_name) > 32 * 1024) {
+ SSH_LOG(SSH_LOG_WARNING, "File name too long");
+ return SSH_ERROR;
+ }
+
+ /* Paranoia check */
+ required_buf_len = (size_t)3 * strlen(file_name) + 1;
+ if (required_buf_len > buf_len) {
+ SSH_LOG(SSH_LOG_WARNING, "Buffer too small");
+ return SSH_ERROR;
+ }
+
+ src = file_name;
+ dst = buf;
+
+ while ((*src != '\0')) {
+ switch (*src) {
+
+ /* The '\'' char is double quoted */
+
+ case '\'':
+ switch (state) {
+ case NO_QUOTE:
+ /* Start a new double quoted string. The '\'' char will be
+ * copied to the beginning of it at the end of the loop. */
+ *dst++ = '"';
+ break;
+ case SINGLE_QUOTE:
+ /* Close the current single quoted string and start a new double
+ * quoted string. The '\'' char will be copied to the beginning
+ * of it at the end of the loop. */
+ *dst++ = '\'';
+ *dst++ = '"';
+ break;
+ case DOUBLE_QUOTE:
+ /* If already in the double quoted string, keep copying the
+ * sequence of chars. */
+ break;
+ default:
+ /* Should never be reached */
+ goto error;
+ }
+
+ /* When the '\'' char is found, the resulting state will be
+ * DOUBLE_QUOTE in any case*/
+ state = DOUBLE_QUOTE;
+ break;
+
+ /* The '!' char is replaced by unquoted "\!" */
+
+ case '!':
+ switch (state) {
+ case NO_QUOTE:
+ /* The '!' char is interpreted in some shells (e.g. CSH) even
+ * when is quoted with single quotes. Replace it with unquoted
+ * "\!" which is correctly interpreted as the '!' character. */
+ *dst++ = '\\';
+ break;
+ case SINGLE_QUOTE:
+ /* Close the current quoted string and replace '!' for unquoted
+ * "\!" */
+ *dst++ = '\'';
+ *dst++ = '\\';
+ break;
+ case DOUBLE_QUOTE:
+ /* Close current quoted string and replace "!" for unquoted
+ * "\!" */
+ *dst++ = '"';
+ *dst++ = '\\';
+ break;
+ default:
+ /* Should never be reached */
+ goto error;
+ }
+
+ /* When the '!' char is found, the resulting state will be NO_QUOTE
+ * in any case*/
+ state = NO_QUOTE;
+ break;
+
+ /* Ordinary chars are single quoted */
+
+ default:
+ switch (state) {
+ case NO_QUOTE:
+ /* Start a new single quoted string */
+ *dst++ = '\'';
+ break;
+ case SINGLE_QUOTE:
+ /* If already in the single quoted string, keep copying the
+ * sequence of chars. */
+ break;
+ case DOUBLE_QUOTE:
+ /* Close current double quoted string and start a new single
+ * quoted string. */
+ *dst++ = '"';
+ *dst++ = '\'';
+ break;
+ default:
+ /* Should never be reached */
+ goto error;
+ }
+
+ /* When an ordinary char is found, the resulting state will be
+ * SINGLE_QUOTE in any case*/
+ state = SINGLE_QUOTE;
+ break;
+ }
+
+ /* Copy the current char to output */
+ *dst++ = *src++;
+ }
+
+ /* Close the quoted string when necessary */
+
+ switch (state) {
+ case NO_QUOTE:
+ /* No open string */
+ break;
+ case SINGLE_QUOTE:
+ /* Close current single quoted string */
+ *dst++ = '\'';
+ break;
+ case DOUBLE_QUOTE:
+ /* Close current double quoted string */
+ *dst++ = '"';
+ break;
+ default:
+ /* Should never be reached */
+ goto error;
+ }
+
+ /* Put the string terminator */
+ *dst = '\0';
+
+ return dst - buf;
+
+error:
+ return SSH_ERROR;
+}
+
/** @} */
--
cgit v1.2.1

View File

@ -1,33 +0,0 @@
From 391c78de9d0f7baec3a44d86a76f4e1324eb9529 Mon Sep 17 00:00:00 2001
From: Andreas Schneider <asn@cryptomilk.org>
Date: Fri, 6 Dec 2019 09:40:30 +0100
Subject: CVE-2019-14889: scp: Don't allow file path longer than 32kb
Signed-off-by: Andreas Schneider <asn@cryptomilk.org>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
(cherry picked from commit 0b5ee397260b6e08dffa2c1ce515a153aaeda765)
---
src/scp.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/scp.c b/src/scp.c
index 166f3d2f..4b00aa5f 100644
--- a/src/scp.c
+++ b/src/scp.c
@@ -80,6 +80,12 @@ ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location)
goto error;
}
+ if (strlen(location) > 32 * 1024) {
+ ssh_set_error(session, SSH_FATAL,
+ "Location path is too long");
+ goto error;
+ }
+
scp->location = strdup(location);
if (scp->location == NULL) {
ssh_set_error(session, SSH_FATAL,
--
cgit v1.2.1

View File

@ -1,128 +0,0 @@
From b0edec4e8d01ad73b0d26ad4070d7e1a1e86dfc8 Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Thu, 31 Oct 2019 18:10:27 +0100
Subject: CVE-2019-14889: scp: Quote location to be used on shell
Single quote file paths to be used on commands to be executed on remote
shell.
Fixes T181
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 3830c7ae6eec751b7618d3fc159cb5bb3c8806a6)
---
src/scp.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 56 insertions(+), 6 deletions(-)
diff --git a/src/scp.c b/src/scp.c
index 4b00aa5f..652551e3 100644
--- a/src/scp.c
+++ b/src/scp.c
@@ -29,6 +29,7 @@
#include "libssh/priv.h"
#include "libssh/scp.h"
+#include "libssh/misc.h"
/**
* @defgroup libssh_scp The SSH scp functions
@@ -119,6 +120,9 @@ int ssh_scp_init(ssh_scp scp)
{
int rc;
char execbuffer[1024] = {0};
+ char *quoted_location = NULL;
+ size_t quoted_location_len = 0;
+ size_t scp_location_len;
if (scp == NULL) {
return SSH_ERROR;
@@ -130,33 +134,79 @@ int ssh_scp_init(ssh_scp scp)
return SSH_ERROR;
}
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Initializing scp session %s %son location '%s'",
+ if (scp->location == NULL) {
+ ssh_set_error(scp->session, SSH_FATAL,
+ "Invalid scp context: location is NULL");
+ return SSH_ERROR;
+ }
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Initializing scp session %s %son location '%s'",
scp->mode == SSH_SCP_WRITE?"write":"read",
- scp->recursive?"recursive ":"",
+ scp->recursive ? "recursive " : "",
scp->location);
scp->channel = ssh_channel_new(scp->session);
if (scp->channel == NULL) {
+ ssh_set_error(scp->session, SSH_FATAL,
+ "Channel creation failed for scp");
scp->state = SSH_SCP_ERROR;
return SSH_ERROR;
}
rc = ssh_channel_open_session(scp->channel);
if (rc == SSH_ERROR) {
+ ssh_set_error(scp->session, SSH_FATAL,
+ "Failed to open channel for scp");
+ scp->state = SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+
+ /* In the worst case, each character would be replaced by 3 plus the string
+ * terminator '\0' */
+ scp_location_len = strlen(scp->location);
+ quoted_location_len = ((size_t)3 * scp_location_len) + 1;
+ /* Paranoia check */
+ if (quoted_location_len < scp_location_len) {
+ ssh_set_error(scp->session, SSH_FATAL,
+ "Buffer overflow detected");
+ scp->state = SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+
+ quoted_location = (char *)calloc(1, quoted_location_len);
+ if (quoted_location == NULL) {
+ ssh_set_error(scp->session, SSH_FATAL,
+ "Failed to allocate memory for quoted location");
+ scp->state = SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+
+ rc = ssh_quote_file_name(scp->location, quoted_location,
+ quoted_location_len);
+ if (rc <= 0) {
+ ssh_set_error(scp->session, SSH_FATAL,
+ "Failed to single quote command location");
+ SAFE_FREE(quoted_location);
scp->state = SSH_SCP_ERROR;
return SSH_ERROR;
}
if (scp->mode == SSH_SCP_WRITE) {
snprintf(execbuffer, sizeof(execbuffer), "scp -t %s %s",
- scp->recursive ? "-r":"", scp->location);
+ scp->recursive ? "-r" : "", quoted_location);
} else {
snprintf(execbuffer, sizeof(execbuffer), "scp -f %s %s",
- scp->recursive ? "-r":"", scp->location);
+ scp->recursive ? "-r" : "", quoted_location);
}
- if (ssh_channel_request_exec(scp->channel, execbuffer) == SSH_ERROR) {
+ SAFE_FREE(quoted_location);
+
+ SSH_LOG(SSH_LOG_DEBUG, "Executing command: %s", execbuffer);
+
+ rc = ssh_channel_request_exec(scp->channel, execbuffer);
+ if (rc == SSH_ERROR){
+ ssh_set_error(scp->session, SSH_FATAL,
+ "Failed executing command: %s", execbuffer);
scp->state = SSH_SCP_ERROR;
return SSH_ERROR;
}
--
cgit v1.2.1

View File

@ -1,36 +0,0 @@
From 26a8b6535159e3f7fb4a6204373b195f09e3bc20 Mon Sep 17 00:00:00 2001
From: Andreas Schneider <asn@cryptomilk.org>
Date: Tue, 11 Feb 2020 11:52:33 +0100
Subject: [PATCH] CVE-2020-1730: Fix a possible segfault when zeroing AES-CTR
key
Fixes T213
Signed-off-by: Andreas Schneider <asn@cryptomilk.org>
Reviewed-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
---
src/libcrypto.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/libcrypto.c b/src/libcrypto.c
index b24a18f..2d692cd 100644
--- a/src/libcrypto.c
+++ b/src/libcrypto.c
@@ -638,8 +638,12 @@ static void aes_ctr_encrypt(struct ssh_cipher_struct *cipher, void *in, void *ou
}
static void aes_ctr_cleanup(struct ssh_cipher_struct *cipher){
- explicit_bzero(cipher->aes_key, sizeof(*cipher->aes_key));
- SAFE_FREE(cipher->aes_key);
+ if (cipher != NULL) {
+ if (cipher->aes_key != NULL) {
+ explicit_bzero(cipher->aes_key, sizeof(*cipher->aes_key));
+ }
+ SAFE_FREE(cipher->aes_key);
+ }
}
#endif /* HAVE_OPENSSL_EVP_AES_CTR */
--
1.8.3.1

File diff suppressed because it is too large Load Diff

View File

@ -1,58 +0,0 @@
From 6ae097069ad8e4658f14870c4d23409b88139810 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Tue, 10 Dec 2019 18:09:51 +0800
Subject: [PATCH] backport-fixes-the-oss-fuzz-bug
---
src/buffer.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/src/buffer.c b/src/buffer.c
index da6e587..08529ee 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1112,6 +1112,7 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer,
goto cleanup;
}
+ rc = SSH_ERROR;
switch (*p) {
case 'b':
o.byte = va_arg(ap, uint8_t *);
@@ -1121,20 +1122,26 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer,
case 'w':
o.word = va_arg(ap, uint16_t *);
rlen = ssh_buffer_get_data(buffer, o.word, sizeof(uint16_t));
- *o.word = ntohs(*o.word);
- rc = rlen==2 ? SSH_OK : SSH_ERROR;
+ if (rlen == 2) {
+ *o.word = ntohs(*o.word);
+ rc = SSH_OK;
+ }
break;
case 'd':
o.dword = va_arg(ap, uint32_t *);
rlen = ssh_buffer_get_u32(buffer, o.dword);
- *o.dword = ntohl(*o.dword);
- rc = rlen==4 ? SSH_OK : SSH_ERROR;
+ if (rlen == 4) {
+ *o.dword = ntohl(*o.dword);
+ rc = SSH_OK;
+ }
break;
case 'q':
o.qword = va_arg(ap, uint64_t*);
rlen = ssh_buffer_get_u64(buffer, o.qword);
- *o.qword = ntohll(*o.qword);
- rc = rlen==8 ? SSH_OK : SSH_ERROR;
+ if (rlen == 8) {
+ *o.qword = ntohll(*o.qword);
+ rc = SSH_OK;
+ }
break;
case 'S':
o.string = va_arg(ap, ssh_string *);
--
2.19.1

Binary file not shown.

View File

@ -1,16 +0,0 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEEjf9T4Y8qvI2PPJIjfuD8TcwBTj0FAlukpGkACgkQfuD8TcwB
Tj3qsA//bPS3hYBbKIChg1+o2s/lAbkjV6mv5LR9gyTljjUAikFNf/AN/yrNLD/H
0sAAD8S2Mj5t4+daUronpX9IJPZtimFB3WoBl+S9J9ybyzpgsspTNv0KZt/O9Vt+
QamOYkMXDtDcqUCHxIzURKiIc6ATsobiUx6EhWOSa8fFsnW6golCJtHzHi5fKsPF
x92J5gZ4jUehZJEiX/LmqFCblLK5qV8g/F+TauWg9jL5m0SNuR0gfDxi3VNV+yeG
gCtneHNrg/Jq9PwI71dIAQ+EDxYARBrLRe7zNSJgZHNuHttyVZaObgO/tFGzAwfj
g+9cuBTHvkKbgM0CodT9ftmdXU8Gt2/3yugfP/FSHUKCy9YgOM5Yo+T8lhAw3Pnt
5ZienZztJwBabui7rWeebhaBSFNuaFUhp1V5HOBT1YjKWlr3yqSGs2PmYYA7Ioeq
ulcyUsNZFXj7hALCxhyBfcwz+USWBpjuxZz5gK5uXbwWcxZUkiRTCXprKiN8jUn/
1/wteO4inm3dpKM3oMuxsk6c64JZnbXkD9vPEP7Fv48nPVVcqs+jk5RPK7iOBUgd
bglc6F05cnUzFz78Lj/FIgEqdYV/vGtxxpwOCRPBDhDWvjbDltN7GkcKQ7ItNd9L
UpMir12LL1Lo32IWxH457dKSCut2/+wGGLcXjUMMhhs/6UDqerg=
=uDol
-----END PGP SIGNATURE-----

View File

@ -0,0 +1,11 @@
--- a/include/libssh/libssh.h 2020-04-15 13:38:32.899177005 +0200
+++ b/include/libssh/libssh.h 2020-04-15 13:38:57.406454427 +0200
@@ -79,7 +79,7 @@
/* libssh version */
#define LIBSSH_VERSION_MAJOR 0
#define LIBSSH_VERSION_MINOR 9
-#define LIBSSH_VERSION_MICRO 3
+#define LIBSSH_VERSION_MICRO 4
#define LIBSSH_VERSION_INT SSH_VERSION_INT(LIBSSH_VERSION_MAJOR, \
LIBSSH_VERSION_MINOR, \

BIN
libssh-0.9.4.tar.xz Normal file

Binary file not shown.

16
libssh-0.9.4.tar.xz.asc Normal file
View File

@ -0,0 +1,16 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEjf9T4Y8qvI2PPJIjfuD8TcwBTj0FAl6O0BgACgkQfuD8TcwB
Tj0dCQ/+J0pjZU6uu7h6gkc4BbRciCpYDIv66Lw9iCc2bQmLLhPrukWjz6/PDV+U
iL/1dlwxG8rOlXdtCEFGyDvm0y4E8NaQCcgjU9jA8nsXo+SyyJAeWT7BeI3m2hPi
tjbLAjQVHCW1jIite1dJeoPIPg15LChc08t+HWVI3pwQviwlJWTPmHgMaT3uwa1X
fD66hjgB2UFo5eYnbION3L/jpA0vsI4o4F5CFPEhgbz3H6KmrgQbKLPM3H/103zU
XjtHEw7gy/85OmjpcskMrUVAMbw9EZ5ESFOrKyuQaFBY57L//tAdUaEloxsMKt+5
nmYunmlGmDLT6rHfjSg5X1S+NsQaXhGelc0TLVgvlzs4kR+QbApR1ewKTcsYlVwr
jYG+PuAiROqc18xM/fQYh8UqohluDBmUpEDmVOEKT2tg/S7R5RJtOxdmcZPsLO+W
EOoP+OeUvQqNlzqu6kBRI4v2lwVU4QwDzKCNRzwQHJOH+azH/3FRJBDF1ZAQvgxy
w/NqlpFO6P76e0SLzBjHCDyqwbAzfq4WK3f5oE0RAA5RlndWusovTAWaYrAbVaoz
emkt/guiHHsbLy6S2ELJu4BI9TGGtDMJoo1ScMMQzFqijUISCBgK/+6mUVUlMli0
lTH6VE+MvpElADE+IYSXWOLHrspTxVa/jVun3iYE8Nexn6G0XE0=
=xSu8
-----END PGP SIGNATURE-----

View File

@ -1,130 +0,0 @@
From b166ac4749c78f475b1708f0345e6ca2749c5d6d Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Mon, 10 Sep 2018 17:37:42 +0200
Subject: CVE-2018-10933: Introduced new auth states
Introduced the states SSH_AUTH_STATE_PUBKEY_OFFER_SENT and
SSH_AUTH_STATE_PUBKEY_AUTH_SENT to know when SSH2_MSG_USERAUTH_PK_OK and
SSH2_MSG_USERAUTH_SUCCESS should be expected.
Fixes T101
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
---
include/libssh/auth.h | 4 ++++
src/auth.c | 44 +++++++++++++++++++++++++++-----------------
2 files changed, 31 insertions(+), 17 deletions(-)
diff --git a/include/libssh/auth.h b/include/libssh/auth.h
index 3913f219..8daab47d 100644
--- a/include/libssh/auth.h
+++ b/include/libssh/auth.h
@@ -76,6 +76,10 @@ enum ssh_auth_state_e {
SSH_AUTH_STATE_GSSAPI_TOKEN,
/** We have sent the MIC and expecting to be authenticated */
SSH_AUTH_STATE_GSSAPI_MIC_SENT,
+ /** We have offered a pubkey to check if it is supported */
+ SSH_AUTH_STATE_PUBKEY_OFFER_SENT,
+ /** We have sent pubkey and signature expecting to be authenticated */
+ SSH_AUTH_STATE_PUBKEY_AUTH_SENT,
};
/** @internal
diff --git a/src/auth.c b/src/auth.c
index 97b6a6e1..41b76aa6 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -85,6 +85,8 @@ static int ssh_auth_response_termination(void *user) {
case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT:
case SSH_AUTH_STATE_GSSAPI_TOKEN:
case SSH_AUTH_STATE_GSSAPI_MIC_SENT:
+ case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
+ case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
return 0;
default:
return 1;
@@ -167,6 +169,8 @@ static int ssh_userauth_get_response(ssh_session session) {
case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT:
case SSH_AUTH_STATE_GSSAPI_TOKEN:
case SSH_AUTH_STATE_GSSAPI_MIC_SENT:
+ case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
+ case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
case SSH_AUTH_STATE_NONE:
/* not reached */
rc = SSH_AUTH_ERROR;
@@ -312,24 +316,30 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_success) {
SSH_PACKET_CALLBACK(ssh_packet_userauth_pk_ok) {
int rc;
- SSH_LOG(SSH_LOG_TRACE, "Received SSH_USERAUTH_PK_OK/INFO_REQUEST/GSSAPI_RESPONSE");
-
- if (session->auth.state == SSH_AUTH_STATE_KBDINT_SENT) {
- /* Assuming we are in keyboard-interactive context */
SSH_LOG(SSH_LOG_TRACE,
- "keyboard-interactive context, assuming SSH_USERAUTH_INFO_REQUEST");
- rc = ssh_packet_userauth_info_request(session,type,packet,user);
+ "Received SSH_USERAUTH_PK_OK/INFO_REQUEST/GSSAPI_RESPONSE");
+
+ if (session->auth.state == SSH_AUTH_STATE_KBDINT_SENT) {
+ /* Assuming we are in keyboard-interactive context */
+ SSH_LOG(SSH_LOG_TRACE,
+ "keyboard-interactive context, "
+ "assuming SSH_USERAUTH_INFO_REQUEST");
+ rc = ssh_packet_userauth_info_request(session,type,packet,user);
#ifdef WITH_GSSAPI
- } else if (session->auth.state == SSH_AUTH_STATE_GSSAPI_REQUEST_SENT) {
- rc = ssh_packet_userauth_gssapi_response(session, type, packet, user);
+ } else if (session->auth.state == SSH_AUTH_STATE_GSSAPI_REQUEST_SENT) {
+ rc = ssh_packet_userauth_gssapi_response(session, type, packet, user);
#endif
- } else {
- session->auth.state = SSH_AUTH_STATE_PK_OK;
- SSH_LOG(SSH_LOG_TRACE, "Assuming SSH_USERAUTH_PK_OK");
- rc = SSH_PACKET_USED;
- }
+ } else if (session->auth.state == SSH_AUTH_STATE_PUBKEY_OFFER_SENT) {
+ session->auth.state = SSH_AUTH_STATE_PK_OK;
+ SSH_LOG(SSH_LOG_TRACE, "Assuming SSH_USERAUTH_PK_OK");
+ rc = SSH_PACKET_USED;
+ } else {
+ session->auth.state = SSH_AUTH_STATE_ERROR;
+ SSH_LOG(SSH_LOG_TRACE, "SSH_USERAUTH_PK_OK received in wrong state");
+ rc = SSH_PACKET_USED;
+ }
- return rc;
+ return rc;
}
/**
@@ -553,7 +563,7 @@ int ssh_userauth_try_publickey(ssh_session session,
ssh_string_free(pubkey_s);
session->auth.current_method = SSH_AUTH_METHOD_PUBLICKEY;
- session->auth.state = SSH_AUTH_STATE_NONE;
+ session->auth.state = SSH_AUTH_STATE_PUBKEY_OFFER_SENT;
session->pending_call_state = SSH_PENDING_CALL_AUTH_OFFER_PUBKEY;
rc = ssh_packet_send(session);
if (rc == SSH_ERROR) {
@@ -701,7 +711,7 @@ int ssh_userauth_publickey(ssh_session session,
}
session->auth.current_method = SSH_AUTH_METHOD_PUBLICKEY;
- session->auth.state = SSH_AUTH_STATE_NONE;
+ session->auth.state = SSH_AUTH_STATE_PUBKEY_AUTH_SENT;
session->pending_call_state = SSH_PENDING_CALL_AUTH_PUBKEY;
rc = ssh_packet_send(session);
if (rc == SSH_ERROR) {
@@ -797,7 +807,7 @@ static int ssh_userauth_agent_publickey(ssh_session session,
}
session->auth.current_method = SSH_AUTH_METHOD_PUBLICKEY;
- session->auth.state = SSH_AUTH_STATE_NONE;
+ session->auth.state = SSH_AUTH_STATE_PUBKEY_AUTH_SENT;
session->pending_call_state = SSH_PENDING_CALL_AUTH_AGENT;
rc = ssh_packet_send(session);
if (rc == SSH_ERROR) {
--
cgit v1.2.1

View File

@ -1,62 +0,0 @@
From fcfba0d8aa15a0142e57513f271eb7fa4bbabc9a Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Mon, 10 Sep 2018 17:38:22 +0200
Subject: CVE-2018-10933: Introduce SSH_AUTH_STATE_PASSWORD_AUTH_SENT
The introduced auth state allows to identify when authentication using
password was tried.
Fixes T101
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
---
include/libssh/auth.h | 2 ++
src/auth.c | 4 +++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/include/libssh/auth.h b/include/libssh/auth.h
index 8daab47d..899282c1 100644
--- a/include/libssh/auth.h
+++ b/include/libssh/auth.h
@@ -80,6 +80,8 @@ enum ssh_auth_state_e {
SSH_AUTH_STATE_PUBKEY_OFFER_SENT,
/** We have sent pubkey and signature expecting to be authenticated */
SSH_AUTH_STATE_PUBKEY_AUTH_SENT,
+ /** We have sent a password expecting to be authenticated */
+ SSH_AUTH_STATE_PASSWORD_AUTH_SENT,
};
/** @internal
diff --git a/src/auth.c b/src/auth.c
index 41b76aa6..2b06d2e1 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -87,6 +87,7 @@ static int ssh_auth_response_termination(void *user) {
case SSH_AUTH_STATE_GSSAPI_MIC_SENT:
case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
+ case SSH_AUTH_STATE_PASSWORD_AUTH_SENT:
return 0;
default:
return 1;
@@ -171,6 +172,7 @@ static int ssh_userauth_get_response(ssh_session session) {
case SSH_AUTH_STATE_GSSAPI_MIC_SENT:
case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
+ case SSH_AUTH_STATE_PASSWORD_AUTH_SENT:
case SSH_AUTH_STATE_NONE:
/* not reached */
rc = SSH_AUTH_ERROR;
@@ -1268,7 +1270,7 @@ int ssh_userauth_password(ssh_session session,
}
session->auth.current_method = SSH_AUTH_METHOD_PASSWORD;
- session->auth.state = SSH_AUTH_STATE_NONE;
+ session->auth.state = SSH_AUTH_STATE_PASSWORD_AUTH_SENT;
session->pending_call_state = SSH_PENDING_CALL_AUTH_PASSWORD;
rc = ssh_packet_send(session);
if (rc == SSH_ERROR) {
--
cgit v1.2.1

View File

@ -1,62 +0,0 @@
From 7819621fc2a07d2d4649b36ca77850610741cfec Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Wed, 19 Sep 2018 17:01:15 +0200
Subject: CVE-2018-10933: Introduce SSH_AUTH_STATE_AUTH_NONE_SENT
The introduced auth state allows to identify when a request without
authentication information was sent.
Fixes T101
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
---
include/libssh/auth.h | 2 ++
src/auth.c | 4 +++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/include/libssh/auth.h b/include/libssh/auth.h
index 899282c1..90b377d4 100644
--- a/include/libssh/auth.h
+++ b/include/libssh/auth.h
@@ -82,6 +82,8 @@ enum ssh_auth_state_e {
SSH_AUTH_STATE_PUBKEY_AUTH_SENT,
/** We have sent a password expecting to be authenticated */
SSH_AUTH_STATE_PASSWORD_AUTH_SENT,
+ /** We have sent a request without auth information (method 'none') */
+ SSH_AUTH_STATE_AUTH_NONE_SENT,
};
/** @internal
diff --git a/src/auth.c b/src/auth.c
index 2b06d2e1..993e6dfe 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -88,6 +88,7 @@ static int ssh_auth_response_termination(void *user) {
case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
case SSH_AUTH_STATE_PASSWORD_AUTH_SENT:
+ case SSH_AUTH_STATE_AUTH_NONE_SENT:
return 0;
default:
return 1;
@@ -173,6 +174,7 @@ static int ssh_userauth_get_response(ssh_session session) {
case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
case SSH_AUTH_STATE_PASSWORD_AUTH_SENT:
+ case SSH_AUTH_STATE_AUTH_NONE_SENT:
case SSH_AUTH_STATE_NONE:
/* not reached */
rc = SSH_AUTH_ERROR;
@@ -428,7 +430,7 @@ int ssh_userauth_none(ssh_session session, const char *username) {
}
session->auth.current_method = SSH_AUTH_METHOD_NONE;
- session->auth.state = SSH_AUTH_STATE_NONE;
+ session->auth.state = SSH_AUTH_STATE_AUTH_NONE_SENT;
session->pending_call_state = SSH_PENDING_CALL_AUTH_NONE;
rc = ssh_packet_send(session);
if (rc == SSH_ERROR) {
--
cgit v1.2.1

View File

@ -1,34 +0,0 @@
From 72bce5ece7edc2fd8023185f9d47b9fc86ef4663 Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Wed, 5 Sep 2018 12:14:07 +0200
Subject: CVE-2018-10933: Set correct state after sending MIC
After sending the client token, the auth state is set as
SSH_AUTH_STATE_GSSAPI_MIC_SENT. Then this can be expected to be the
state when a USERAUTH_FAILURE or USERAUTH_SUCCESS arrives.
Fixes T101
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
---
src/gssapi.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/gssapi.c b/src/gssapi.c
index 51b69e7a..77df0b59 100644
--- a/src/gssapi.c
+++ b/src/gssapi.c
@@ -960,8 +960,8 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client){
}
if (maj_stat == GSS_S_COMPLETE) {
- session->auth.state = SSH_AUTH_STATE_NONE;
ssh_gssapi_send_mic(session);
+ session->auth.state = SSH_AUTH_STATE_GSSAPI_MIC_SENT;
}
return SSH_PACKET_USED;
--
cgit v1.2.1

View File

@ -1,39 +0,0 @@
From adeaa69cc535827ec8acfbaf3ff91224b5a595a7 Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Fri, 7 Sep 2018 17:00:40 +0200
Subject: CVE-2018-10933: Check channel state when OPEN_CONFIRMATION arrives
When a SSH2_MSG_OPEN_CONFIRMATION arrives, the channel state is checked
to be in SSH_CHANNEL_STATE_OPENING.
Fixes T101
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
---
src/channels.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/channels.c b/src/channels.c
index 103009a8..b26f6bd4 100644
--- a/src/channels.c
+++ b/src/channels.c
@@ -171,6 +171,15 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){
"Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d",
channel->local_channel,
channel->remote_channel);
+
+ if (channel->state != SSH_CHANNEL_STATE_OPENING) {
+ SSH_LOG(SSH_LOG_RARE,
+ "SSH2_MSG_CHANNEL_OPEN_CONFIRMATION received in incorrect "
+ "channel state %d",
+ channel->state);
+ goto error;
+ }
+
SSH_LOG(SSH_LOG_PROTOCOL,
"Remote window : %lu, maxpacket : %lu",
(long unsigned int) channel->remote_window,
--
cgit v1.2.1

View File

@ -1,49 +0,0 @@
From f8c452cbef228b105dcb757d7554c3388a4dbea5 Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Fri, 7 Sep 2018 17:12:01 +0200
Subject: CVE-2018-10933: Check channel state when OPEN_FAILURE arrives
When a SSH2_MSG_OPEN_FAILURE arrives, the channel state is checked
to be in SSH_CHANNEL_STATE_OPENING.
Fixes T101
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
---
src/channels.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/channels.c b/src/channels.c
index b26f6bd4..0e61e5cd 100644
--- a/src/channels.c
+++ b/src/channels.c
@@ -220,6 +220,14 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail){
return SSH_PACKET_USED;
}
+ if (channel->state != SSH_CHANNEL_STATE_OPENING) {
+ SSH_LOG(SSH_LOG_RARE,
+ "SSH2_MSG_CHANNEL_OPEN_FAILURE received in incorrect channel "
+ "state %d",
+ channel->state);
+ goto error;
+ }
+
ssh_set_error(session, SSH_REQUEST_DENIED,
"Channel opening failure: channel %u error (%lu) %s",
channel->local_channel,
@@ -228,6 +236,10 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail){
SAFE_FREE(error);
channel->state=SSH_CHANNEL_STATE_OPEN_DENIED;
return SSH_PACKET_USED;
+
+error:
+ ssh_set_error(session, SSH_FATAL, "Invalid packet");
+ return SSH_PACKET_USED;
}
static int ssh_channel_open_termination(void *c){
--
cgit v1.2.1

View File

@ -1,871 +0,0 @@
From 203818608ac8a83d68098f008306c3a568ac4cac Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Tue, 28 Aug 2018 18:13:03 +0200
Subject: CVE-2018-10933: Introduced packet filtering
The packet filter checks required states for the incoming packets and
reject them if they arrived in the wrong state.
Fixes T101
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
---
include/libssh/packet.h | 6 +
src/packet.c | 809 +++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 813 insertions(+), 2 deletions(-)
diff --git a/include/libssh/packet.h b/include/libssh/packet.h
index a3bcb9a8..fbe09700 100644
--- a/include/libssh/packet.h
+++ b/include/libssh/packet.h
@@ -43,6 +43,12 @@ enum ssh_packet_state_e {
PACKET_STATE_PROCESSING
};
+enum ssh_packet_filter_result_e {
+ SSH_PACKET_UNKNOWN,
+ SSH_PACKET_ALLOWED,
+ SSH_PACKET_DENIED
+};
+
int ssh_packet_send(ssh_session session);
SSH_PACKET_CALLBACK(ssh_packet_unimplemented);
diff --git a/src/packet.c b/src/packet.c
index aa2f17f0..0b070fd4 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -128,6 +128,797 @@ static ssh_packet_callback default_packet_handlers[]= {
ssh_packet_channel_failure, // SSH2_MSG_CHANNEL_FAILURE 100
};
+/** @internal
+ * @brief check if the received packet is allowed for the current session state
+ * @param session current ssh_session
+ * @returns SSH_PACKET_ALLOWED if the packet is allowed; SSH_PACKET_DENIED
+ * if the packet arrived in wrong state; SSH_PACKET_UNKNOWN if the packet type
+ * is unknown
+ */
+static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session session)
+{
+ enum ssh_packet_filter_result_e rc;
+
+#ifdef DEBUG_PACKET
+ SSH_LOG(SSH_LOG_PACKET, "Filtering packet type %d",
+ session->in_packet.type);
+#endif
+
+ switch(session->in_packet.type) {
+ case SSH2_MSG_DISCONNECT: // 1
+ /*
+ * States required:
+ * - None
+ *
+ * Transitions:
+ * - session->socket->state = SSH_SOCKET_CLOSED
+ * - session->session_state = SSH_SESSION_STATE_ERROR
+ * */
+
+ /* Always allowed */
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_IGNORE: // 2
+ /*
+ * States required:
+ * - None
+ *
+ * Transitions:
+ * - None
+ * */
+
+ /* Always allowed */
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_UNIMPLEMENTED: // 3
+ /*
+ * States required:
+ * - None
+ *
+ * Transitions:
+ * - None
+ * */
+
+ /* Always allowed */
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_DEBUG: // 4
+ /*
+ * States required:
+ * - None
+ *
+ * Transitions:
+ * - None
+ * */
+
+ /* Always allowed */
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_SERVICE_REQUEST: // 5
+ /* Server only */
+
+ /*
+ * States required:
+ * - session->session_state == SSH_SESSION_STATE_AUTHENTICATING
+ * or session->session_state == SSH_SESSION_STATE_AUTHENTICATED
+ * - session->dh_handshake_state == DH_STATE_FINISHED
+ *
+ * Transitions:
+ * - None
+ * */
+
+ /* If this is a client, reject the message */
+ if (session->client) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if ((session->session_state != SSH_SESSION_STATE_AUTHENTICATING) &&
+ (session->session_state != SSH_SESSION_STATE_AUTHENTICATED))
+ {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if (session->dh_handshake_state != DH_STATE_FINISHED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_SERVICE_ACCEPT: // 6
+ /*
+ * States required:
+ * - session->session_state == SSH_SESSION_STATE_AUTHENTICATING
+ * or session->session_state == SSH_SESSION_STATE_AUTHENTICATED
+ * - session->dh_handshake_state == DH_STATE_FINISHED
+ * - session->auth.service_state == SSH_AUTH_SERVICE_SENT
+ *
+ * Transitions:
+ * - auth.service_state = SSH_AUTH_SERVICE_ACCEPTED
+ * */
+
+ if ((session->session_state != SSH_SESSION_STATE_AUTHENTICATING) &&
+ (session->session_state != SSH_SESSION_STATE_AUTHENTICATED))
+ {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if (session->dh_handshake_state != DH_STATE_FINISHED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ /* TODO check if only auth service can be requested */
+ if (session->auth.service_state != SSH_AUTH_SERVICE_SENT) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_EXT_INFO: // 7
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATING
+ * - dh_handshake_state == DH_STATE_FINISHED
+ *
+ * Transitions:
+ * - None
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if (session->dh_handshake_state != DH_STATE_FINISHED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_KEXINIT: // 20
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+ * or session_state == SSH_SESSION_STATE_INITIAL_KEX
+ * - dh_handshake_state == DH_STATE_INIT
+ * or dh_handshake_state == DH_STATE_FINISHED (re-exchange)
+ *
+ * Transitions:
+ * - session->dh_handshake_state = DH_STATE_INIT
+ * - session->session_state = SSH_SESSION_STATE_KEXINIT_RECEIVED
+ *
+ * On server:
+ * - session->session_state = SSH_SESSION_STATE_DH
+ * */
+
+ if ((session->session_state != SSH_SESSION_STATE_AUTHENTICATED) &&
+ (session->session_state != SSH_SESSION_STATE_INITIAL_KEX))
+ {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if ((session->dh_handshake_state != DH_STATE_INIT) &&
+ (session->dh_handshake_state != DH_STATE_FINISHED))
+ {
+ rc = SSH_PACKET_DENIED;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_NEWKEYS: // 21
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_DH
+ * - dh_handshake_state == DH_STATE_NEWKEYS_SENT
+ *
+ * Transitions:
+ * - session->dh_handshake_state = DH_STATE_FINISHED
+ * - session->session_state = SSH_SESSION_STATE_AUTHENTICATING
+ * if session->flags & SSH_SESSION_FLAG_AUTHENTICATED
+ * - session->session_state = SSH_SESSION_STATE_AUTHENTICATED
+ * */
+
+ /* If DH has not been started, reject message */
+ if (session->session_state != SSH_SESSION_STATE_DH) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ /* Only allowed if dh_handshake_state is in NEWKEYS_SENT state */
+ if (session->dh_handshake_state != DH_STATE_NEWKEYS_SENT) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_KEXDH_INIT: // 30
+ // SSH2_MSG_KEX_ECDH_INIT: // 30
+ // SSH2_MSG_ECMQV_INIT: // 30
+ // SSH2_MSG_KEX_DH_GEX_REQUEST_OLD: // 30
+
+ /* Server only */
+
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_DH
+ * - dh_handshake_state == DH_STATE_INIT
+ *
+ * Transitions:
+ * - session->dh_handshake_state = DH_STATE_INIT_SENT
+ * then calls dh_handshake_server which triggers:
+ * - session->dh_handhsake_state = DH_STATE_NEWKEYS_SENT
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_DH) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ /* Only allowed if dh_handshake_state is in initial state */
+ if (session->dh_handshake_state != DH_STATE_INIT) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_KEXDH_REPLY: // 31
+ // SSH2_MSG_KEX_ECDH_REPLY: // 31
+ // SSH2_MSG_ECMQV_REPLY: // 31
+ // SSH2_MSG_KEX_DH_GEX_GROUP: // 31
+
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_DH
+ * - dh_handshake_state == DH_STATE_INIT_SENT
+ *
+ * Transitions:
+ * - session->dh_handhsake_state = DH_STATE_NEWKEYS_SENT
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_DH) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if (session->dh_handshake_state != DH_STATE_INIT_SENT) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_KEX_DH_GEX_INIT: // 32
+ /* TODO Not filtered */
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_KEX_DH_GEX_REPLY: // 33
+ /* TODO Not filtered */
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_KEX_DH_GEX_REQUEST: // 34
+ /* TODO Not filtered */
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_USERAUTH_REQUEST: // 50
+ /* Server only */
+
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATING
+ * - dh_hanshake_state == DH_STATE_FINISHED
+ *
+ * Transitions:
+ * - if authentication was successful:
+ * - session_state = SSH_SESSION_STATE_AUTHENTICATED
+ * */
+
+ /* If this is a client, reject the message */
+ if (session->client) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if (session->dh_handshake_state != DH_STATE_FINISHED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_USERAUTH_FAILURE: // 51
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATING
+ * - dh_hanshake_state == DH_STATE_FINISHED
+ * - session->auth.state == SSH_AUTH_STATE_KBDINT_SENT
+ * or session->auth.state == SSH_AUTH_STATE_PUBKEY_OFFER_SENT
+ * or session->auth.state == SSH_AUTH_STATE_PUBKEY_AUTH_SENT
+ * or session->auth.state == SSH_AUTH_STATE_PASSWORD_AUTH_SENT
+ * or session->auth.state == SSH_AUTH_STATE_GSSAPI_MIC_SENT
+ *
+ * Transitions:
+ * - if unpacking failed:
+ * - session->auth.state = SSH_AUTH_ERROR
+ * - if failure was partial:
+ * - session->auth.state = SSH_AUTH_PARTIAL
+ * - else:
+ * - session->auth.state = SSH_AUTH_STATE_FAILED
+ * */
+
+ /* If this is a server, reject the message */
+ if (session->server) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if (session->dh_handshake_state != DH_STATE_FINISHED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_USERAUTH_SUCCESS: // 52
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATING
+ * - dh_hanshake_state == DH_STATE_FINISHED
+ * - session->auth.state == SSH_AUTH_STATE_KBDINT_SENT
+ * or session->auth.state == SSH_AUTH_STATE_PUBKEY_AUTH_SENT
+ * or session->auth.state == SSH_AUTH_STATE_PASSWORD_AUTH_SENT
+ * or session->auth.state == SSH_AUTH_STATE_GSSAPI_MIC_SENT
+ * or session->auth.state == SSH_AUTH_STATE_AUTH_NONE_SENT
+ *
+ * Transitions:
+ * - session->auth.state = SSH_AUTH_STATE_SUCCESS
+ * - session->session_state = SSH_SESSION_STATE_AUTHENTICATED
+ * - session->flags |= SSH_SESSION_FLAG_AUTHENTICATED
+ * - sessions->auth.current_method = SSH_AUTH_METHOD_UNKNOWN
+ * */
+
+ /* If this is a server, reject the message */
+ if (session->server) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if (session->dh_handshake_state != DH_STATE_FINISHED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if ((session->auth.state != SSH_AUTH_STATE_KBDINT_SENT) &&
+ (session->auth.state != SSH_AUTH_STATE_PUBKEY_AUTH_SENT) &&
+ (session->auth.state != SSH_AUTH_STATE_PASSWORD_AUTH_SENT) &&
+ (session->auth.state != SSH_AUTH_STATE_GSSAPI_MIC_SENT) &&
+ (session->auth.state != SSH_AUTH_STATE_AUTH_NONE_SENT))
+ {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_USERAUTH_BANNER: // 53
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATING
+ *
+ * Transitions:
+ * - None
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_USERAUTH_PK_OK: // 60
+ // SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // 60
+ // SSH2_MSG_USERAUTH_INFO_REQUEST: // 60
+ // SSH2_MSG_USERAUTH_GSSAPI_RESPONSE: // 60
+
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATING
+ * - session->auth.state == SSH_AUTH_STATE_KBDINT_SENT
+ * or
+ * session->auth.state == SSH_AUTH_STATE_GSSAPI_REQUEST_SENT
+ * or
+ * session->auth.state == SSH_AUTH_STATE_PUBKEY_OFFER_SENT
+ *
+ * Transitions:
+ * Depending on the current state, the message is treated
+ * differently:
+ * - session->auth.state == SSH_AUTH_STATE_KBDINT_SENT
+ * - session->auth.state = SSH_AUTH_STATE_INFO
+ * - session->auth.state == SSH_AUTH_STATE_GSSAPI_REQUEST_SENT
+ * - session->auth.state = SSH_AUTH_STATE_GSSAPI_TOKEN
+ * - session->auth.state == SSH_AUTH_STATE_PUBKEY_OFFER_SENT
+ * - session->auth.state = SSH_AUTH_STATE_PK_OK
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if ((session->auth.state != SSH_AUTH_STATE_KBDINT_SENT) &&
+ (session->auth.state != SSH_AUTH_STATE_PUBKEY_OFFER_SENT) &&
+ (session->auth.state != SSH_AUTH_STATE_GSSAPI_REQUEST_SENT))
+ {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_USERAUTH_INFO_RESPONSE: // 61
+ // SSH2_MSG_USERAUTH_GSSAPI_TOKEN: // 61
+
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATING
+ * - session_state->auth.state == SSH_SESSION_STATE_GSSAPI_TOKEN
+ * or
+ * session_state->auth.state == SSH_SESSION_STATE_INFO
+ *
+ * Transitions:
+ * - None
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if ((session->auth.state != SSH_AUTH_STATE_INFO) &&
+ (session->auth.state != SSH_AUTH_STATE_GSSAPI_TOKEN))
+ {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE: // 63
+ /* TODO Not filtered */
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_USERAUTH_GSSAPI_ERROR: // 64
+ /* TODO Not filtered */
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_USERAUTH_GSSAPI_ERRTOK: // 65
+ /* TODO Not filtered */
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_USERAUTH_GSSAPI_MIC: // 66
+ /* Server only */
+
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATING
+ * - session->gssapi->state == SSH_GSSAPI_STATE_RCV_MIC
+ *
+ * Transitions:
+ * Depending on the result of the verification, the states are
+ * changed:
+ * - SSH_AUTH_SUCCESS:
+ * - session->session_state = SSH_SESSION_STATE_AUTHENTICATED
+ * - session->flags != SSH_SESSION_FLAG_AUTHENTICATED
+ * - SSH_AUTH_PARTIAL:
+ * - None
+ * - any other case:
+ * - None
+ * */
+
+ /* If this is a client, reject the message */
+ if (session->client) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if (session->dh_handshake_state != DH_STATE_FINISHED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_GLOBAL_REQUEST: // 80
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+ *
+ * Transitions:
+ * - None
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_REQUEST_SUCCESS: // 81
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+ * - session->global_req_state == SSH_CHANNEL_REQ_STATE_PENDING
+ *
+ * Transitions:
+ * - session->global_req_state == SSH_CHANNEL_REQ_STATE_ACCEPTED
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if (session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_REQUEST_FAILURE: // 82
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+ * - session->global_req_state == SSH_CHANNEL_REQ_STATE_PENDING
+ *
+ * Transitions:
+ * - session->global_req_state == SSH_CHANNEL_REQ_STATE_DENIED
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ if (session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_CHANNEL_OPEN: // 90
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+ *
+ * Transitions:
+ * - None
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: // 91
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+ *
+ * Transitions:
+ * - channel->state = SSH_CHANNEL_STATE_OPEN
+ * - channel->flags &= ~SSH_CHANNEL_FLAG_NOT_BOUND
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_CHANNEL_OPEN_FAILURE: // 92
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+ *
+ * Transitions:
+ * - channel->state = SSH_CHANNEL_STATE_OPEN_DENIED
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_CHANNEL_WINDOW_ADJUST: // 93
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+ *
+ * Transitions:
+ * - None
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_CHANNEL_DATA: // 94
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+ *
+ * Transitions:
+ * - None
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_CHANNEL_EXTENDED_DATA: // 95
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+ *
+ * Transitions:
+ * - None
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_CHANNEL_EOF: // 96
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+ *
+ * Transitions:
+ * - None
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_CHANNEL_CLOSE: // 97
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+ *
+ * Transitions:
+ * - channel->state = SSH_CHANNEL_STATE_CLOSED
+ * - channel->flags |= SSH_CHANNEL_FLAG_CLOSED_REMOTE
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_CHANNEL_REQUEST: // 98
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+ *
+ * Transitions:
+ * - Depends on the request
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_CHANNEL_SUCCESS: // 99
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+ * - channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
+ *
+ * Transitions:
+ * - channel->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ case SSH2_MSG_CHANNEL_FAILURE: // 100
+ /*
+ * States required:
+ * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+ * - channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
+ *
+ * Transitions:
+ * - channel->request_state = SSH_CHANNEL_REQ_STATE_DENIED
+ * */
+
+ if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
+ rc = SSH_PACKET_ALLOWED;
+ break;
+ default:
+ /* Unknown message, do not filter */
+ rc = SSH_PACKET_UNKNOWN;
+ goto end;
+ }
+
+end:
+#ifdef DEBUG_PACKET
+ if (rc == SSH_PACKET_DENIED) {
+ SSH_LOG(SSH_LOG_PACKET, "REJECTED packet type %d: ",
+ session->in_packet.type);
+ }
+
+ if (rc == SSH_PACKET_UNKNOWN) {
+ SSH_LOG(SSH_LOG_PACKET, "UNKNOWN packet type %d",
+ session->in_packet.type);
+ }
+#endif
+
+ return rc;
+}
+
/* in nonblocking mode, socket_read will read as much as it can, and return */
/* SSH_OK if it has read at least len bytes, otherwise, SSH_AGAIN. */
/* in blocking mode, it will read at least len bytes and will block until it's ok. */
@@ -158,6 +949,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
uint32_t packet_len, compsize, payloadsize;
uint8_t padding;
size_t processed = 0; /* number of byte processed from the callback */
+ enum ssh_packet_filter_result_e filter_result;
if(session->current_crypto != NULL) {
current_macsize = hmac_digest_len(session->current_crypto->in_hmac);
@@ -345,8 +1137,21 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
"packet: read type %hhd [len=%d,padding=%hhd,comp=%d,payload=%d]",
session->in_packet.type, packet_len, padding, compsize, payloadsize);
- /* Execute callbacks */
- ssh_packet_process(session, session->in_packet.type);
+ /* Check if the packet is expected */
+ filter_result = ssh_packet_incoming_filter(session);
+
+ switch(filter_result) {
+ case SSH_PACKET_ALLOWED:
+ /* Execute callbacks */
+ ssh_packet_process(session, session->in_packet.type);
+ break;
+ case SSH_PACKET_DENIED:
+ goto error;
+ case SSH_PACKET_UNKNOWN:
+ ssh_packet_send_unimplemented(session, session->recv_seq - 1);
+ break;
+ }
+
session->packet_state = PACKET_STATE_INIT;
if (processed < receivedlen) {
/* Handle a potential packet left in socket buffer */
--
cgit v1.2.1

View File

@ -1,543 +0,0 @@
From 09a7638575b3c07ad227763e1f03f7553b045e79 Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Fri, 7 Sep 2018 15:26:49 +0200
Subject: CVE-2018-10933: Add tests for packet filtering
Created the test torture_packet_filter.c which tests if packets are
being correctly filtered.
Fixes T101
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
---
tests/unittests/CMakeLists.txt | 3 +
tests/unittests/torture_packet_filter.c | 502 ++++++++++++++++++++++++++++++++
2 files changed, 505 insertions(+)
create mode 100644 tests/unittests/torture_packet_filter.c
diff --git a/tests/unittests/CMakeLists.txt b/tests/unittests/CMakeLists.txt
index 397ebd05..13887ff1 100644
--- a/tests/unittests/CMakeLists.txt
+++ b/tests/unittests/CMakeLists.txt
@@ -35,6 +35,9 @@ target_compile_options(torture_knownhosts_parsing PRIVATE ${DEFAULT_C_COMPILE_FL
add_cmocka_test(torture_hashes torture_hashes.c ${TEST_TARGET_LIBRARIES})
target_compile_options(torture_hashes PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
+add_cmocka_test(torture_packet_filter torture_packet_filter.c ${TORTURE_LIBRARY})
+target_compile_options(torture_packet_filter PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
+
if (CMAKE_USE_PTHREADS_INIT)
add_cmocka_test(torture_rand torture_rand.c ${TEST_TARGET_LIBRARIES})
target_compile_options(torture_rand PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
diff --git a/tests/unittests/torture_packet_filter.c b/tests/unittests/torture_packet_filter.c
new file mode 100644
index 00000000..72cbc4cd
--- /dev/null
+++ b/tests/unittests/torture_packet_filter.c
@@ -0,0 +1,502 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2018 by Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*
+ * This test checks if the messages accepted by the packet filter were intented
+ * to be accepted.
+ *
+ * The process consists in 2 steps:
+ * - Try the filter with a message type in an arbitrary state
+ * - If the message is accepted by the filter, check if the message is in the
+ * set of accepted states.
+ *
+ * Only the values selected by the flag (COMPARE_*) are considered.
+ * */
+
+#include "config.h"
+
+#define LIBSSH_STATIC
+
+#include "torture.h"
+#include "libssh/priv.h"
+#include "libssh/libssh.h"
+#include "libssh/session.h"
+#include "libssh/auth.h"
+#include "libssh/ssh2.h"
+#include "libssh/packet.h"
+
+#include "packet.c"
+
+#define COMPARE_SESSION_STATE 1
+#define COMPARE_ROLE (1 << 1)
+#define COMPARE_DH_STATE (1 << 2)
+#define COMPARE_AUTH_STATE (1 << 3)
+#define COMPARE_GLOBAL_REQ_STATE (1 << 4)
+#define COMPARE_CURRENT_METHOD (1 << 5)
+
+#define SESSION_STATE_COUNT 11
+#define DH_STATE_COUNT 4
+#define AUTH_STATE_COUNT 15
+#define GLOBAL_REQ_STATE_COUNT 5
+#define MESSAGE_COUNT 100 // from 1 to 100
+
+#define ROLE_CLIENT 0
+#define ROLE_SERVER 1
+
+/*
+ * This is the list of currently unfiltered message types.
+ * Only unrecognized types should be in this list.
+ * */
+static uint8_t unfiltered[] = {
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 22, 23, 24, 25, 26, 27, 28, 29,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 54, 55, 56, 57, 58, 59,
+ 62,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 83, 84, 85, 86, 87, 88, 89,
+};
+
+typedef struct global_state_st {
+ /* If the bit in this flag is zero, the corresponding state is not
+ * considered, working as a wildcard (meaning any value is accepted) */
+ uint32_t flags;
+ uint8_t role;
+ enum ssh_session_state_e session;
+ enum ssh_dh_state_e dh;
+ enum ssh_auth_state_e auth;
+ enum ssh_channel_request_state_e global_req;
+} global_state;
+
+static int cmp_state(const void *e1, const void *e2)
+{
+ global_state *s1 = (global_state *) e1;
+ global_state *s2 = (global_state *) e2;
+
+ /* Compare role (client == 0 or server == 1)*/
+ if (s1->role < s2->role) {
+ return -1;
+ }
+ else if (s1->role > s2->role) {
+ return 1;
+ }
+
+ /* Compare session state */
+ if (s1->session < s2->session) {
+ return -1;
+ }
+ else if (s1->session > s2->session) {
+ return 1;
+ }
+
+ /* Compare DH state */
+ if (s1->dh < s2->dh) {
+ return -1;
+ }
+ else if (s1->dh > s2->dh) {
+ return 1;
+ }
+
+ /* Compare auth */
+ if (s1->auth < s2->auth) {
+ return -1;
+ }
+ else if (s1->auth > s2->auth) {
+ return 1;
+ }
+
+ /* Compare global_req */
+ if (s1->global_req < s2->global_req) {
+ return -1;
+ }
+ else if (s1->global_req > s2->global_req) {
+ return 1;
+ }
+
+ /* If all equal, they are equal */
+ return 0;
+}
+
+static int cmp_state_search(const void *key, const void *array_element)
+{
+ global_state *s1 = (global_state *) key;
+ global_state *s2 = (global_state *) array_element;
+
+ int result = 0;
+
+ if (s2->flags & COMPARE_ROLE) {
+ /* Compare role (client == 0 or server == 1)*/
+ if (s1->role < s2->role) {
+ return -1;
+ }
+ else if (s1->role > s2->role) {
+ return 1;
+ }
+ }
+
+ if (s2->flags & COMPARE_SESSION_STATE) {
+ /* Compare session state */
+ if (s1->session < s2->session) {
+ result = -1;
+ goto end;
+ }
+ else if (s1->session > s2->session) {
+ result = 1;
+ goto end;
+ }
+ }
+
+ if (s2->flags & COMPARE_DH_STATE) {
+ /* Compare DH state */
+ if (s1->dh < s2->dh) {
+ result = -1;
+ goto end;
+ }
+ else if (s1->dh > s2->dh) {
+ result = 1;
+ goto end;
+ }
+ }
+
+ if (s2->flags & COMPARE_AUTH_STATE) {
+ /* Compare auth */
+ if (s1->auth < s2->auth) {
+ result = -1;
+ goto end;
+ }
+ else if (s1->auth > s2->auth) {
+ result = 1;
+ goto end;
+ }
+ }
+
+ if (s2->flags & COMPARE_GLOBAL_REQ_STATE) {
+ /* Compare global_req */
+ if (s1->global_req < s2->global_req) {
+ result = -1;
+ goto end;
+ }
+ else if (s1->global_req > s2->global_req) {
+ result = 1;
+ goto end;
+ }
+ }
+
+end:
+ return result;
+}
+
+static int is_state_accepted(global_state *tested, global_state *accepted,
+ int accepted_len)
+{
+ global_state *found = NULL;
+
+ found = bsearch(tested, accepted, accepted_len, sizeof(global_state),
+ cmp_state_search);
+
+ if (found != NULL) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int cmp_uint8(const void *i, const void *j)
+{
+ uint8_t e1 = *((uint8_t *)i);
+ uint8_t e2 = *((uint8_t *)j);
+
+ if (e1 < e2) {
+ return -1;
+ }
+ else if (e1 > e2) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int check_unfiltered(uint8_t msg_type)
+{
+ uint8_t *found;
+
+ found = bsearch(&msg_type, unfiltered, sizeof(unfiltered)/sizeof(uint8_t),
+ sizeof(uint8_t), cmp_uint8);
+
+ if (found != NULL) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void torture_packet_filter_check_unfiltered(void **state)
+{
+ ssh_session session;
+
+ int role_c;
+ int auth_c;
+ int session_c;
+ int dh_c;
+ int global_req_c;
+
+ uint8_t msg_type;
+
+ enum ssh_packet_filter_result_e rc;
+ int in_unfiltered;
+
+ session = ssh_new();
+
+ for (msg_type = 1; msg_type <= MESSAGE_COUNT; msg_type++) {
+ session->in_packet.type = msg_type;
+ for (role_c = 0; role_c < 2; role_c++) {
+ session->server = role_c;
+ for (session_c = 0; session_c < SESSION_STATE_COUNT; session_c++) {
+ session->session_state = session_c;
+ for (dh_c = 0; dh_c < DH_STATE_COUNT; dh_c++) {
+ session->dh_handshake_state = dh_c;
+ for (auth_c = 0; auth_c < AUTH_STATE_COUNT; auth_c++) {
+ session->auth.state = auth_c;
+ for (global_req_c = 0;
+ global_req_c < GLOBAL_REQ_STATE_COUNT;
+ global_req_c++)
+ {
+ session->global_req_state = global_req_c;
+
+ rc = ssh_packet_incoming_filter(session);
+
+ if (rc == SSH_PACKET_UNKNOWN) {
+ in_unfiltered = check_unfiltered(msg_type);
+
+ if (!in_unfiltered) {
+ fprintf(stderr, "Message type %d UNFILTERED "
+ "in state: role %d, session %d, dh %d, auth %d\n",
+ msg_type, role_c, session_c, dh_c, auth_c);
+ }
+ assert_int_equal(in_unfiltered, 1);
+ }
+ else {
+ in_unfiltered = check_unfiltered(msg_type);
+
+ if (in_unfiltered) {
+ fprintf(stderr, "Message type %d NOT UNFILTERED "
+ "in state: role %d, session %d, dh %d, auth %d\n",
+ msg_type, role_c, session_c, dh_c, auth_c);
+ }
+ assert_int_equal(in_unfiltered, 0);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ssh_free(session);
+}
+
+static int check_message_in_all_states(global_state accepted[],
+ int accepted_count, uint8_t msg_type)
+{
+ ssh_session session;
+
+ int role_c;
+ int auth_c;
+ int session_c;
+ int dh_c;
+ int global_req_c;
+
+ enum ssh_packet_filter_result_e rc;
+ int in_accepted;
+
+ global_state key;
+
+ session = ssh_new();
+
+ /* Sort the accepted array so that the elements can be searched using
+ * bsearch */
+ qsort(accepted, accepted_count, sizeof(global_state), cmp_state);
+
+ session->in_packet.type = msg_type;
+
+ for (role_c = 0; role_c < 2; role_c++) {
+ session->server = role_c;
+ key.role = role_c;
+ for (session_c = 0; session_c < SESSION_STATE_COUNT; session_c++) {
+ session->session_state = session_c;
+ key.session = session_c;
+ for (dh_c = 0; dh_c < DH_STATE_COUNT; dh_c++) {
+ session->dh_handshake_state = dh_c;
+ key.dh = dh_c;
+ for (auth_c = 0; auth_c < AUTH_STATE_COUNT; auth_c++) {
+ session->auth.state = auth_c;
+ key.auth = auth_c;
+ for (global_req_c = 0;
+ global_req_c < GLOBAL_REQ_STATE_COUNT;
+ global_req_c++)
+ {
+ session->global_req_state = global_req_c;
+ key.global_req = global_req_c;
+
+ rc = ssh_packet_incoming_filter(session);
+
+ if (rc == SSH_PACKET_ALLOWED) {
+ in_accepted = is_state_accepted(&key, accepted,
+ accepted_count);
+
+ if (!in_accepted) {
+ fprintf(stderr, "Message type %d ALLOWED "
+ "in state: role %d, session %d, dh %d, auth %d\n",
+ msg_type, role_c, session_c, dh_c, auth_c);
+ }
+ assert_int_equal(in_accepted, 1);
+ }
+ else if (rc == SSH_PACKET_DENIED) {
+ in_accepted = is_state_accepted(&key, accepted, accepted_count);
+
+ if (in_accepted) {
+ fprintf(stderr, "Message type %d DENIED "
+ "in state: role %d, session %d, dh %d, auth %d\n",
+ msg_type, role_c, session_c, dh_c, auth_c);
+ }
+ assert_int_equal(in_accepted, 0);
+ }
+ else {
+ fprintf(stderr, "Message type %d UNFILTERED "
+ "in state: role %d, session %d, dh %d, auth %d\n",
+ msg_type, role_c, session_c, dh_c, auth_c);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ssh_free(session);
+ return 0;
+}
+
+static void torture_packet_filter_check_auth_success(void **state)
+{
+ int rc;
+
+ global_state accepted[] = {
+ {
+ .flags = (COMPARE_SESSION_STATE |
+ COMPARE_ROLE |
+ COMPARE_AUTH_STATE |
+ COMPARE_DH_STATE),
+ .role = ROLE_CLIENT,
+ .session = SSH_SESSION_STATE_AUTHENTICATING,
+ .dh = DH_STATE_FINISHED,
+ .auth = SSH_AUTH_STATE_PUBKEY_AUTH_SENT,
+ },
+ {
+ .flags = (COMPARE_SESSION_STATE |
+ COMPARE_ROLE |
+ COMPARE_AUTH_STATE |
+ COMPARE_DH_STATE),
+ .role = ROLE_CLIENT,
+ .session = SSH_SESSION_STATE_AUTHENTICATING,
+ .dh = DH_STATE_FINISHED,
+ .auth = SSH_AUTH_STATE_PASSWORD_AUTH_SENT,
+ },
+ {
+ .flags = (COMPARE_SESSION_STATE |
+ COMPARE_ROLE |
+ COMPARE_AUTH_STATE |
+ COMPARE_DH_STATE),
+ .role = ROLE_CLIENT,
+ .session = SSH_SESSION_STATE_AUTHENTICATING,
+ .dh = DH_STATE_FINISHED,
+ .auth = SSH_AUTH_STATE_GSSAPI_MIC_SENT,
+ },
+ {
+ .flags = (COMPARE_SESSION_STATE |
+ COMPARE_ROLE |
+ COMPARE_AUTH_STATE |
+ COMPARE_DH_STATE),
+ .role = ROLE_CLIENT,
+ .session = SSH_SESSION_STATE_AUTHENTICATING,
+ .dh = DH_STATE_FINISHED,
+ .auth = SSH_AUTH_STATE_KBDINT_SENT,
+ },
+ {
+ .flags = (COMPARE_SESSION_STATE |
+ COMPARE_ROLE |
+ COMPARE_AUTH_STATE |
+ COMPARE_DH_STATE |
+ COMPARE_CURRENT_METHOD),
+ .role = ROLE_CLIENT,
+ .session = SSH_SESSION_STATE_AUTHENTICATING,
+ .dh = DH_STATE_FINISHED,
+ .auth = SSH_AUTH_STATE_AUTH_NONE_SENT,
+ }
+ };
+
+ int accepted_count = 5;
+
+ /* Unused */
+ (void) state;
+
+ rc = check_message_in_all_states(accepted, accepted_count,
+ SSH2_MSG_USERAUTH_SUCCESS);
+
+ assert_int_equal(rc, 0);
+}
+
+static void torture_packet_filter_check_channel_open(void **state)
+{
+ int rc;
+
+ /* The only condition to accept a CHANNEL_OPEN is to be authenticated */
+ global_state accepted[] = {
+ {
+ .flags = COMPARE_SESSION_STATE,
+ .session = SSH_SESSION_STATE_AUTHENTICATED,
+ }
+ };
+
+ int accepted_count = 1;
+
+ /* Unused */
+ (void) state;
+
+ rc = check_message_in_all_states(accepted, accepted_count,
+ SSH2_MSG_CHANNEL_OPEN);
+
+ assert_int_equal(rc, 0);
+}
+
+int torture_run_tests(void)
+{
+ int rc;
+ struct CMUnitTest tests[] = {
+ cmocka_unit_test(torture_packet_filter_check_auth_success),
+ cmocka_unit_test(torture_packet_filter_check_channel_open),
+ cmocka_unit_test(torture_packet_filter_check_unfiltered),
+ };
+
+ ssh_init();
+ torture_filter_tests(tests);
+ rc = cmocka_run_group_tests(tests, NULL, NULL);
+ ssh_finalize();
+ return rc;
+}
--
cgit v1.2.1

View File

@ -1,34 +1,21 @@
Name: libssh
Version: 0.8.3
Release: 8
Version: 0.9.4
Release: 1
Summary: A library implementing the SSH protocol
License: LGPLv2+
URL: https://www.libssh.org
Source0: https://www.libssh.org/files/0.8/%{name}-%{version}.tar.xz
Source1: https://www.libssh.org/files/0.8/%{name}-%{version}.tar.xz.asc
Source2: https://cryptomilk.org/gpgkey-8DFF53E18F2ABC8D8F3C92237EE0FC4DCC014E3D.gpg#/%{name}.keyring
URL: http://www.libssh.org
Patch1: libssh-0.8.3-fix-covscan-errors.patch
Patch2: libssh-0.8.3-fixes-the-oss-fuzz-bug.patch
Source0: https://www.libssh.org/files/0.9/%{name}-%{version}.tar.xz
Source1: https://www.libssh.org/files/0.9/%{name}-%{version}.tar.xz.asc
Source2: https://cryptomilk.org/gpgkey-8DFF53E18F2ABC8D8F3C92237EE0FC4DCC014E3D.gpg#/%{name}.keyring
#patches6000-patches6007 come from https://git.libssh.org/
Patch6000: libssh-stable-0p8-CVE-2018-10933-part1.patch
Patch6001: libssh-stable-0p8-CVE-2018-10933-part2.patch
Patch6002: libssh-stable-0p8-CVE-2018-10933-part3.patch
Patch6003: libssh-stable-0p8-CVE-2018-10933-part4.patch
Patch6004: libssh-stable-0p8-CVE-2018-10933-part5.patch
Patch6005: libssh-stable-0p8-CVE-2018-10933-part6.patch
Patch6006: libssh-stable-0p8-CVE-2018-10933-part7.patch
Patch6007: libssh-stable-0p8-CVE-2018-10933-part8.patch
Patch6008: 0001-CVE-2019-14889.patch
Patch6009: 0002-CVE-2019-14889.patch
Patch6010: 0003-CVE-2019-14889.patch
Patch6011: 0004-CVE-2019-14889.patch
Patch6012: 0005-CVE-2019-14889.patch
Patch6013: CVE-2020-1730.patch
Patch1: libssh-0.9.4-fix-version.patch
BuildRequires: cmake libcmocka-devel krb5-devel zlib-devel pkgconfig
BuildRequires: doxygen gcc-c++ gnupg2 openssl-devel
BuildRequires: cmake gcc-c++ gnupg2 openssl-devel pkgconfig zlib-devel
BuildRequires: krb5-devel libcmocka-devel openssh-clients openssh-server
BuildRequires: nmap-ncat
Recommends: crypto-policies
Provides: libssh_threads.so.4()(64bit)
@ -61,15 +48,15 @@ fi
pushd obj
%cmake .. \
-DUNIT_TESTING=ON
-DUNIT_TESTING=ON \
%make_build VERBOSE=1
make docs
popd
%install
make DESTDIR=%{buildroot} install/fast -C obj
install -d -m755 %{buildroot}%{_sysconfdir}/libssh
pushd %{buildroot}%{_libdir}
for i in libssh.so*;
@ -100,19 +87,19 @@ popd
%defattr(-,root,root)
%{_includedir}/libssh/
%{_libdir}/cmake/libssh/
%{_libdir}/pkgconfig/*.pc
%{_libdir}/pkgconfig/libssh.pc
%{_libdir}/*.so
%files help
%defattr(-,root,root)
%doc README ChangeLog obj/doc/html
%doc ChangeLog README
%changelog
* Fri Apr 17 2020 openEuler Buildteam <buildteam@openeuler.org> - 0.8.3-8
- Type:cves
- ID:CVE-2020-1730
* Mon Apr 20 2020 openEuler Buildteam <buildteam@openeuler.org> - 0.9.4-1
- Type:bugfix
- Id:NA
- SUG:NA
- DESC:fix CVE-2020-1730
- DESC:update to 0.9.4
* Sun Jan 12 2020 openEuler Buildteam <buildteam@openeuler.org> - 0.8.3-7
- Type:bugfix