monitoring-plugins/plugins/check_ssh.c

463 lines
14 KiB
C
Raw Permalink Normal View History

/*****************************************************************************
2024-10-31 10:55:04 -04:00
*
* Monitoring check_ssh plugin
*
* License: GPL
* Copyright (c) 2000-2024 Monitoring Plugins Development Team
*
* Description:
*
* This file contains the check_ssh plugin
*
* Try to connect to an SSH server at specified server and port
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*****************************************************************************/
#include "output.h"
#include "perfdata.h"
#include "states.h"
const char *progname = "check_ssh";
2024-10-31 09:47:49 -04:00
const char *copyright = "2000-2024";
const char *email = "devel@monitoring-plugins.org";
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
#include "./common.h"
#include "./netutils.h"
#include "utils.h"
#include "./check_ssh.d/config.h"
#ifndef MSG_DONTWAIT
2024-10-31 10:55:04 -04:00
# define MSG_DONTWAIT 0
#endif
2025-02-25 07:03:27 -05:00
#define BUFF_SZ 256
static bool verbose = false;
typedef struct process_arguments_wrapper {
int errorcode;
check_ssh_config config;
} process_arguments_wrapper;
static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
static void print_help(void);
2024-10-31 10:55:04 -04:00
void print_usage(void);
2025-08-01 08:35:23 -04:00
static int ssh_connect(mp_check *overall, char *haddr, int hport, char *remote_version,
char *remote_protocol);
2024-10-31 10:55:04 -04:00
int main(int argc, char **argv) {
#ifdef __OpenBSD__
/* - rpath is required to read --extra-opts (given up later)
* - inet is required for sockets
* - unix is required for Unix domain sockets
* - dns is required for name lookups */
pledge("stdio rpath inet unix dns", NULL);
#endif // __OpenBSD__
2024-10-31 10:55:04 -04:00
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
/* Parse extra opts if any */
2024-10-31 10:55:04 -04:00
argv = np_extra_opts(&argc, argv, progname);
process_arguments_wrapper tmp_config = process_arguments(argc, argv);
if (tmp_config.errorcode == ERROR) {
2024-10-31 10:55:04 -04:00
usage4(_("Could not parse arguments"));
2025-02-22 16:14:17 -05:00
}
#ifdef __OpenBSD__
pledge("stdio inet unix dns", NULL);
#endif // __OpenBSD__
check_ssh_config config = tmp_config.config;
mp_check overall = mp_check_init();
if (config.output_format_is_set) {
mp_set_format(config.output_format);
}
/* initialize alarm signal handling */
signal(SIGALRM, socket_timeout_alarm_handler);
alarm(socket_timeout);
/* ssh_connect exits if error is found */
2025-08-01 08:35:23 -04:00
ssh_connect(&overall, config.server_name, config.port, config.remote_version,
config.remote_protocol);
2024-10-31 10:55:04 -04:00
alarm(0);
mp_exit(overall);
}
#define output_format_index CHAR_MAX + 1
/* process command-line arguments */
process_arguments_wrapper process_arguments(int argc, char **argv) {
2025-08-01 08:35:23 -04:00
static struct option longopts[] = {
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
{"host", required_argument, 0, 'H'}, /* backward compatibility */
{"hostname", required_argument, 0, 'H'},
{"port", required_argument, 0, 'p'},
{"use-ipv4", no_argument, 0, '4'},
{"use-ipv6", no_argument, 0, '6'},
{"timeout", required_argument, 0, 't'},
{"verbose", no_argument, 0, 'v'},
{"remote-version", required_argument, 0, 'r'},
{"remote-protocol", required_argument, 0, 'P'},
{"output-format", required_argument, 0, output_format_index},
{0, 0, 0, 0}};
process_arguments_wrapper result = {
.config = check_ssh_config_init(),
.errorcode = OK,
};
2025-02-22 16:14:17 -05:00
if (argc < 2) {
result.errorcode = ERROR;
return result;
2025-02-22 16:14:17 -05:00
}
2025-02-22 16:14:17 -05:00
for (int i = 1; i < argc; i++) {
if (strcmp("-to", argv[i]) == 0) {
2024-10-31 11:00:41 -04:00
strcpy(argv[i], "-t");
2025-02-22 16:14:17 -05:00
}
}
2024-10-31 11:00:41 -04:00
int option_char;
while (true) {
int option = 0;
option_char = getopt_long(argc, argv, "+Vhv46t:r:H:p:P:", longopts, &option);
2025-02-22 16:14:17 -05:00
if (option_char == -1 || option_char == EOF) {
break;
2025-02-22 16:14:17 -05:00
}
2024-10-31 11:00:41 -04:00
switch (option_char) {
2024-10-31 10:55:04 -04:00
case '?': /* help */
usage5();
case 'V': /* version */
print_revision(progname, NP_VERSION);
exit(STATE_UNKNOWN);
case 'h': /* help */
print_help();
exit(STATE_UNKNOWN);
case 'v': /* verbose */
2023-10-18 14:24:13 -04:00
verbose = true;
break;
2024-10-31 10:55:04 -04:00
case 't': /* timeout period */
if (!is_intpos(optarg)) {
2024-10-31 10:55:04 -04:00
usage2(_("Timeout interval must be a positive integer"), optarg);
2025-02-22 16:14:17 -05:00
} else {
2025-02-25 07:03:27 -05:00
socket_timeout = (unsigned int)atoi(optarg);
2025-02-22 16:14:17 -05:00
}
break;
case '4':
address_family = AF_INET;
break;
case '6':
address_family = AF_INET6;
break;
2024-10-31 10:55:04 -04:00
case 'r': /* remote version */
result.config.remote_version = optarg;
break;
2024-10-31 10:55:04 -04:00
case 'P': /* remote version */
result.config.remote_protocol = optarg;
break;
2024-10-31 10:55:04 -04:00
case 'H': /* host */
2025-02-22 16:14:17 -05:00
if (!is_host(optarg)) {
2024-10-31 10:55:04 -04:00
usage2(_("Invalid hostname/address"), optarg);
2025-02-22 16:14:17 -05:00
}
result.config.server_name = optarg;
break;
2024-10-31 10:55:04 -04:00
case 'p': /* port */
if (is_intpos(optarg)) {
result.config.port = atoi(optarg);
2024-10-31 10:55:04 -04:00
} else {
usage2(_("Port number must be a positive integer"), optarg);
}
2025-02-25 07:02:30 -05:00
break;
case output_format_index: {
parsed_output_format parser = mp_parse_output_format(optarg);
if (!parser.parsing_success) {
// TODO List all available formats here, maybe add anothoer usage function
printf("Invalid output format: %s\n", optarg);
exit(STATE_UNKNOWN);
}
result.config.output_format_is_set = true;
result.config.output_format = parser.output_format;
break;
}
}
}
2024-10-31 11:00:41 -04:00
option_char = optind;
if (result.config.server_name == NULL && option_char < argc) {
2024-10-31 11:00:41 -04:00
if (is_host(argv[option_char])) {
result.config.server_name = argv[option_char++];
}
}
if (result.config.port == -1 && option_char < argc) {
2024-10-31 11:00:41 -04:00
if (is_intpos(argv[option_char])) {
result.config.port = atoi(argv[option_char++]);
2024-10-31 10:55:04 -04:00
} else {
print_usage();
exit(STATE_UNKNOWN);
}
}
if (result.config.server_name == NULL) {
result.errorcode = ERROR;
return result;
2025-02-22 16:14:17 -05:00
}
return result;
}
/************************************************************************
2024-10-31 10:55:04 -04:00
*
* Try to connect to SSH server at specified server and port
*
*-----------------------------------------------------------------------*/
2025-08-01 08:35:23 -04:00
int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_version,
char *desired_remote_protocol) {
struct timeval tv;
gettimeofday(&tv, NULL);
2024-10-31 11:00:41 -04:00
int socket;
int result = my_tcp_connect(haddr, hport, &socket);
mp_subcheck connection_sc = mp_subcheck_init();
2025-02-22 16:14:17 -05:00
if (result != STATE_OK) {
connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
2025-08-01 08:35:23 -04:00
xasprintf(&connection_sc.output,
"Failed to establish TCP connection to Host %s and Port %d", haddr, hport);
mp_add_subcheck_to_check(overall, connection_sc);
return result;
2025-02-22 16:14:17 -05:00
}
2024-10-31 10:55:04 -04:00
char *output = (char *)calloc(BUFF_SZ + 1, sizeof(char));
2024-10-31 11:00:41 -04:00
char *buffer = NULL;
ssize_t recv_ret = 0;
2024-10-31 11:00:41 -04:00
char *version_control_string = NULL;
2025-02-25 05:40:32 -05:00
size_t byte_offset = 0;
2025-02-25 07:03:27 -05:00
while ((version_control_string == NULL) &&
2025-08-01 08:35:23 -04:00
(recv_ret = recv(socket, output + byte_offset, (unsigned long)(BUFF_SZ - byte_offset),
0) > 0)) {
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
if (strchr(output, '\n')) { /* we've got at least one full line, start parsing*/
byte_offset = 0;
char *index = NULL;
2024-10-31 10:55:04 -04:00
while ((index = strchr(output + byte_offset, '\n')) != NULL) {
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
/*Partition the buffer so that this line is a separate string,
* by replacing the newline with NUL*/
output[(index - output)] = '\0';
size_t len = strlen(output + byte_offset);
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
2024-10-31 10:55:04 -04:00
if ((len >= 4) && (strncmp(output + byte_offset, "SSH-", 4) == 0)) {
2025-08-01 08:35:23 -04:00
/*if the string starts with SSH-, this _should_ be a valid version control
* string*/
2024-10-31 10:55:04 -04:00
version_control_string = output + byte_offset;
break;
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
}
/*the start of the next line (if one exists) will be after the current one (+ NUL)*/
byte_offset += (len + 1);
}
2024-10-31 10:55:04 -04:00
if (version_control_string == NULL) {
check_ssh: Fix buffer overflow A buffer overflow was occurring when the server responded with: Exceeded MaxStartups\r\n glibc would then abort() with the following output: *** buffer overflow detected ***: terminated It was the memset() that was overflowing the buffer. But the memmove() needed fixing too. First off, there was an off-by-one error in both the memmove() and memset(). byte_offset was already set to the start of the data _past_ the newline (i.e. len + 1). For the memmove(), incrementing that by 1 again lost the first character of the additional output. For the memset(), this causes a buffer overflow. Second, the memset() has multiple issues. The comment claims that it was NULing (sic "null") the "rest". However, it has no idea how long the "rest" is, at this point. It was NULing BUFF_SZ - byte_offset + 1. After fixing the off-by-one / buffer overflow, it would be NULing BUFF_SZ - byte_offset. But that doesn't make any sense. The length of the first line has no relation to the length of the second line. For a quick-and-dirty test, add something like this just inside the while loop: memcpy(output, "Exceeded MaxStartups\r\nnext blah1 blah2 blah3 blah4\0", sizeof("Exceeded MaxStartups\r\nnext blah1 blah2 blah3 blah4\0")); And, after the memmove(), add: printf("output='%s'\n", output); If you fix the memset() buffer overflow, it will output: output='ext blah1 blah2 blah3 ' As you can see, the first character is lost. If you then fix the memmove(), it will output: output='next blah1 blah2 blah3' Note that this is still losing the "blah4". After moving the memset() after byte_offset is set to the new strlen() of output, then it works correctly: output='next blah1 blah2 blah3 blah4' Signed-off-by: Richard Laager <rlaager@wiktel.com>
2025-07-11 19:19:31 -04:00
/* move unconsumed data to beginning of buffer */
memmove((void *)output, (void *)(output + byte_offset), BUFF_SZ - byte_offset);
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
/*start reading from end of current line chunk on next recv*/
byte_offset = strlen(output);
check_ssh: Fix buffer overflow A buffer overflow was occurring when the server responded with: Exceeded MaxStartups\r\n glibc would then abort() with the following output: *** buffer overflow detected ***: terminated It was the memset() that was overflowing the buffer. But the memmove() needed fixing too. First off, there was an off-by-one error in both the memmove() and memset(). byte_offset was already set to the start of the data _past_ the newline (i.e. len + 1). For the memmove(), incrementing that by 1 again lost the first character of the additional output. For the memset(), this causes a buffer overflow. Second, the memset() has multiple issues. The comment claims that it was NULing (sic "null") the "rest". However, it has no idea how long the "rest" is, at this point. It was NULing BUFF_SZ - byte_offset + 1. After fixing the off-by-one / buffer overflow, it would be NULing BUFF_SZ - byte_offset. But that doesn't make any sense. The length of the first line has no relation to the length of the second line. For a quick-and-dirty test, add something like this just inside the while loop: memcpy(output, "Exceeded MaxStartups\r\nnext blah1 blah2 blah3 blah4\0", sizeof("Exceeded MaxStartups\r\nnext blah1 blah2 blah3 blah4\0")); And, after the memmove(), add: printf("output='%s'\n", output); If you fix the memset() buffer overflow, it will output: output='ext blah1 blah2 blah3 ' As you can see, the first character is lost. If you then fix the memmove(), it will output: output='next blah1 blah2 blah3' Note that this is still losing the "blah4". After moving the memset() after byte_offset is set to the new strlen() of output, then it works correctly: output='next blah1 blah2 blah3 blah4' Signed-off-by: Richard Laager <rlaager@wiktel.com>
2025-07-11 19:19:31 -04:00
/* NUL the rest of the buffer */
memset(output + byte_offset, 0, BUFF_SZ - byte_offset);
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
}
} else {
byte_offset += (size_t)recv_ret;
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
}
}
if (recv_ret < 0) {
connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
2025-08-01 08:34:29 -04:00
xasprintf(&connection_sc.output, "%s - %s", "SSH CRITICAL - ", strerror(errno));
mp_add_subcheck_to_check(overall, connection_sc);
return OK;
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
}
if (version_control_string == NULL) {
connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
xasprintf(&connection_sc.output, "%s", "SSH CRITICAL - No version control string received");
mp_add_subcheck_to_check(overall, connection_sc);
return OK;
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
}
connection_sc = mp_set_subcheck_state(connection_sc, STATE_OK);
2025-02-25 05:40:45 -05:00
xasprintf(&connection_sc.output, "%s", "Initial connection succeeded");
mp_add_subcheck_to_check(overall, connection_sc);
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
/*
* "When the connection has been established, both sides MUST send an
* identification string. This identification string MUST be
*
* SSH-protoversion-softwareversion SP comments CR LF"
* - RFC 4253:4.2
*/
2024-10-31 10:55:04 -04:00
strip(version_control_string);
2025-02-22 16:14:17 -05:00
if (verbose) {
2024-10-31 10:55:04 -04:00
printf("%s\n", version_control_string);
2025-02-22 16:14:17 -05:00
}
2024-10-31 11:00:41 -04:00
char *ssh_proto = version_control_string + 4;
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
/*
* We assume the protoversion is of the form Major.Minor, although
* this is not _strictly_ required. See
*
* "Both the 'protoversion' and 'softwareversion' strings MUST consist of
* printable US-ASCII characters, with the exception of whitespace
* characters and the minus sign (-)"
* - RFC 4253:4.2
* and,
*
* "As stated earlier, the 'protoversion' specified for this protocol is
* "2.0". Earlier versions of this protocol have not been formally
* documented, but it is widely known that they use 'protoversion' of
* "1.x" (e.g., "1.5" or "1.3")."
* - RFC 4253:5
*/
2025-08-01 08:35:23 -04:00
char *ssh_server = ssh_proto + strspn(ssh_proto, "0123456789.") +
1; /* (+1 for the '-' separating protoversion from softwareversion) */
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
/* If there's a space in the version string, whatever's after the space is a comment
* (which is NOT part of the server name/version)*/
char *tmp = strchr(ssh_server, ' ');
if (tmp) {
ssh_server[tmp - ssh_server] = '\0';
}
mp_subcheck protocol_validity_sc = mp_subcheck_init();
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) {
protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_CRITICAL);
2025-08-01 08:35:23 -04:00
xasprintf(&protocol_validity_sc.output, "Invalid protocol version control string %s",
version_control_string);
mp_add_subcheck_to_check(overall, protocol_validity_sc);
return OK;
}
protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_OK);
2025-08-01 08:35:23 -04:00
xasprintf(&protocol_validity_sc.output, "Valid protocol version control string %s",
version_control_string);
mp_add_subcheck_to_check(overall, protocol_validity_sc);
2024-10-31 10:55:04 -04:00
ssh_proto[strspn(ssh_proto, "0123456789. ")] = 0;
2024-10-31 11:00:41 -04:00
static char *rev_no = VERSION;
2024-10-31 10:55:04 -04:00
xasprintf(&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no);
2024-10-31 11:00:41 -04:00
send(socket, buffer, strlen(buffer), MSG_DONTWAIT);
2025-02-22 16:14:17 -05:00
if (verbose) {
2024-10-31 10:55:04 -04:00
printf("%s\n", buffer);
2025-02-22 16:14:17 -05:00
}
if (desired_remote_version && strcmp(desired_remote_version, ssh_server)) {
mp_subcheck remote_version_sc = mp_subcheck_init();
remote_version_sc = mp_set_subcheck_state(remote_version_sc, STATE_CRITICAL);
2025-08-01 08:35:23 -04:00
xasprintf(&remote_version_sc.output, _("%s (protocol %s) version mismatch, expected '%s'"),
ssh_server, ssh_proto, desired_remote_version);
2024-10-31 11:00:41 -04:00
close(socket);
mp_add_subcheck_to_check(overall, remote_version_sc);
return OK;
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
}
double elapsed_time = (double)deltime(tv) / 1.0e6;
mp_perfdata time_pd = perfdata_init();
time_pd.value = mp_create_pd_value(elapsed_time);
time_pd.label = "time";
time_pd.max_present = true;
time_pd.max = mp_create_pd_value(socket_timeout);
mp_subcheck protocol_version_sc = mp_subcheck_init();
mp_add_perfdata_to_subcheck(&protocol_version_sc, time_pd);
if (desired_remote_protocol && strcmp(desired_remote_protocol, ssh_proto)) {
protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_CRITICAL);
2025-08-01 08:35:23 -04:00
xasprintf(&protocol_version_sc.output,
_("%s (protocol %s) protocol version mismatch, expected '%s'"), ssh_server,
ssh_proto, desired_remote_protocol);
} else {
protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_OK);
2025-08-01 08:35:23 -04:00
xasprintf(&protocol_version_sc.output, "SSH server version: %s (protocol version: %s)",
ssh_server, ssh_proto);
}
check_ssh: patches from op5 (#1738) * check_ssh: properly parse a delayed version control string This resolves an issue with SSH servers which do not respond with their version control string as the first thing in the SSH protocol version exchange phase after connection establishment. This patch also makes sure that we disregard a potential comment in the version exchange string to avoid nonsense mismatches. In the future, we might want to add the capability to match against a user specified comment. In addition, the patch largely improves the communication towards the server, which adds better protocol adherence. Of course, new test cases are added to support the trigger and guard against regressions of the bugs solved by this patch. This fixes op5#7945 (https://bugs.op5.com/view.php?id=7945) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh.t: Fix a few typos Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Handle non-alpha software versions This patch fixes a bug where we would reject version control strings that do not contain letters, because the assumption is made that they always do. This is not required by the RFC however, and there exist implementations that do not contain letters. I've also added a few references to the RFC to make the process of parsing the control string more apparent. This fixes op5#8716 (https://bugs.op5.com/view.php?id=8716) Signed-off-by: Anton Lofgren <alofgren@op5.com> * check_ssh: Fix a typo in "remote-protocol parameter remote-protcol -> remote-protocol Signed-off-by: Anton Lofgren <alofgren@op5.com> * Remove unused variable * Formating fixes * Update translations * Remove merge conflict artefact from previous merge * Set fixed include paths * Improve code style to be slightly more readable * Update test cases for different netcat behaviour and reduce sleep time --------- Signed-off-by: Anton Lofgren <alofgren@op5.com> Co-authored-by: Anton Lofgren <alofgren@op5.com>
2024-03-26 19:35:16 -04:00
mp_add_subcheck_to_check(overall, protocol_version_sc);
2024-10-31 11:00:41 -04:00
close(socket);
return OK;
}
2024-10-31 10:55:04 -04:00
void print_help(void) {
char *myport;
xasprintf(&myport, "%d", default_ssh_port);
2024-10-31 10:55:04 -04:00
print_revision(progname, NP_VERSION);
2024-10-31 10:55:04 -04:00
printf("Copyright (c) 1999 Remi Paulmier <remi@sinfomic.fr>\n");
printf(COPYRIGHT, copyright, email);
2024-10-31 10:55:04 -04:00
printf("%s\n", _("Try to connect to an SSH server at specified server and port"));
2024-10-31 10:55:04 -04:00
printf("\n\n");
2024-10-31 10:55:04 -04:00
print_usage();
2024-10-31 10:55:04 -04:00
printf(UT_HELP_VRSN);
printf(UT_EXTRA_OPTS);
2024-10-31 10:55:04 -04:00
printf(UT_HOST_PORT, 'p', myport);
2024-10-31 10:55:04 -04:00
printf(UT_IPv46);
2024-10-31 10:55:04 -04:00
printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
2024-10-31 10:55:04 -04:00
printf(" %s\n", "-r, --remote-version=STRING");
2025-08-01 08:35:23 -04:00
printf(" %s\n",
_("Alert if string doesn't match expected server version (ex: OpenSSH_3.9p1)"));
2024-10-31 10:55:04 -04:00
printf(" %s\n", "-P, --remote-protocol=STRING");
printf(" %s\n", _("Alert if protocol doesn't match expected protocol version (ex: 2.0)"));
printf(UT_OUTPUT_FORMAT);
2024-10-31 10:55:04 -04:00
printf(UT_VERBOSE);
2024-10-31 10:55:04 -04:00
printf(UT_SUPPORT);
}
2024-10-31 10:55:04 -04:00
void print_usage(void) {
printf("%s\n", _("Usage:"));
2025-08-01 08:35:23 -04:00
printf("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] --hostname <host>\n",
progname);
}