summaryrefslogtreecommitdiffstats
path: root/plugins/check_ssh.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_ssh.c')
-rw-r--r--plugins/check_ssh.c210
1 files changed, 144 insertions, 66 deletions
diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c
index 42a88cf9..9d0d7cde 100644
--- a/plugins/check_ssh.c
+++ b/plugins/check_ssh.c
@@ -28,6 +28,9 @@
28 * 28 *
29 *****************************************************************************/ 29 *****************************************************************************/
30 30
31#include "output.h"
32#include "perfdata.h"
33#include "states.h"
31const char *progname = "check_ssh"; 34const char *progname = "check_ssh";
32const char *copyright = "2000-2024"; 35const char *copyright = "2000-2024";
33const char *email = "devel@monitoring-plugins.org"; 36const char *email = "devel@monitoring-plugins.org";
@@ -35,26 +38,26 @@ const char *email = "devel@monitoring-plugins.org";
35#include "./common.h" 38#include "./common.h"
36#include "./netutils.h" 39#include "./netutils.h"
37#include "utils.h" 40#include "utils.h"
41#include "./check_ssh.d/config.h"
38 42
39#ifndef MSG_DONTWAIT 43#ifndef MSG_DONTWAIT
40# define MSG_DONTWAIT 0 44# define MSG_DONTWAIT 0
41#endif 45#endif
42 46
43#define SSH_DFL_PORT 22 47#define BUFF_SZ 256
44#define BUFF_SZ 256
45 48
46static int port = -1;
47static char *server_name = NULL;
48static char *remote_version = NULL;
49static char *remote_protocol = NULL;
50static bool verbose = false; 49static bool verbose = false;
51 50
52static int process_arguments(int /*argc*/, char ** /*argv*/); 51typedef struct process_arguments_wrapper {
53static int validate_arguments(void); 52 int errorcode;
53 check_ssh_config config;
54} process_arguments_wrapper;
55
56static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
54static void print_help(void); 57static void print_help(void);
55void print_usage(void); 58void print_usage(void);
56 59
57static int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_protocol); 60static int ssh_connect(mp_check *overall, char *haddr, int hport, char *remote_version, char *remote_protocol);
58 61
59int main(int argc, char **argv) { 62int main(int argc, char **argv) {
60 setlocale(LC_ALL, ""); 63 setlocale(LC_ALL, "");
@@ -64,24 +67,35 @@ int main(int argc, char **argv) {
64 /* Parse extra opts if any */ 67 /* Parse extra opts if any */
65 argv = np_extra_opts(&argc, argv, progname); 68 argv = np_extra_opts(&argc, argv, progname);
66 69
67 if (process_arguments(argc, argv) == ERROR) 70 process_arguments_wrapper tmp_config = process_arguments(argc, argv);
71
72 if (tmp_config.errorcode == ERROR) {
68 usage4(_("Could not parse arguments")); 73 usage4(_("Could not parse arguments"));
74 }
75
76 check_ssh_config config = tmp_config.config;
77
78 mp_check overall = mp_check_init();
79 if (config.output_format_is_set) {
80 mp_set_format(config.output_format);
81 }
69 82
70 /* initialize alarm signal handling */ 83 /* initialize alarm signal handling */
71 signal(SIGALRM, socket_timeout_alarm_handler); 84 signal(SIGALRM, socket_timeout_alarm_handler);
72
73 alarm(socket_timeout); 85 alarm(socket_timeout);
74 86
75 /* ssh_connect exits if error is found */ 87 /* ssh_connect exits if error is found */
76 int result = ssh_connect(server_name, port, remote_version, remote_protocol); 88 ssh_connect(&overall, config.server_name, config.port, config.remote_version, config.remote_protocol);
77 89
78 alarm(0); 90 alarm(0);
79 91
80 return (result); 92 mp_exit(overall);
81} 93}
82 94
95#define output_format_index CHAR_MAX + 1
96
83/* process command-line arguments */ 97/* process command-line arguments */
84int process_arguments(int argc, char **argv) { 98process_arguments_wrapper process_arguments(int argc, char **argv) {
85 static struct option longopts[] = {{"help", no_argument, 0, 'h'}, 99 static struct option longopts[] = {{"help", no_argument, 0, 'h'},
86 {"version", no_argument, 0, 'V'}, 100 {"version", no_argument, 0, 'V'},
87 {"host", required_argument, 0, 'H'}, /* backward compatibility */ 101 {"host", required_argument, 0, 'H'}, /* backward compatibility */
@@ -93,22 +107,33 @@ int process_arguments(int argc, char **argv) {
93 {"verbose", no_argument, 0, 'v'}, 107 {"verbose", no_argument, 0, 'v'},
94 {"remote-version", required_argument, 0, 'r'}, 108 {"remote-version", required_argument, 0, 'r'},
95 {"remote-protocol", required_argument, 0, 'P'}, 109 {"remote-protocol", required_argument, 0, 'P'},
110 {"output-format", required_argument, 0, output_format_index},
96 {0, 0, 0, 0}}; 111 {0, 0, 0, 0}};
97 112
98 if (argc < 2) 113 process_arguments_wrapper result = {
99 return ERROR; 114 .config = check_ssh_config_init(),
115 .errorcode = OK,
116 };
117
118 if (argc < 2) {
119 result.errorcode = ERROR;
120 return result;
121 }
100 122
101 for (int i = 1; i < argc; i++) 123 for (int i = 1; i < argc; i++) {
102 if (strcmp("-to", argv[i]) == 0) 124 if (strcmp("-to", argv[i]) == 0) {
103 strcpy(argv[i], "-t"); 125 strcpy(argv[i], "-t");
126 }
127 }
104 128
105 int option_char; 129 int option_char;
106 while (true) { 130 while (true) {
107 int option = 0; 131 int option = 0;
108 option_char = getopt_long(argc, argv, "+Vhv46t:r:H:p:P:", longopts, &option); 132 option_char = getopt_long(argc, argv, "+Vhv46t:r:H:p:P:", longopts, &option);
109 133
110 if (option_char == -1 || option_char == EOF) 134 if (option_char == -1 || option_char == EOF) {
111 break; 135 break;
136 }
112 137
113 switch (option_char) { 138 switch (option_char) {
114 case '?': /* help */ 139 case '?': /* help */
@@ -123,10 +148,11 @@ int process_arguments(int argc, char **argv) {
123 verbose = true; 148 verbose = true;
124 break; 149 break;
125 case 't': /* timeout period */ 150 case 't': /* timeout period */
126 if (!is_integer(optarg)) 151 if (!is_intpos(optarg)) {
127 usage2(_("Timeout interval must be a positive integer"), optarg); 152 usage2(_("Timeout interval must be a positive integer"), optarg);
128 else 153 } else {
129 socket_timeout = atoi(optarg); 154 socket_timeout = (unsigned int)atoi(optarg);
155 }
130 break; 156 break;
131 case '4': 157 case '4':
132 address_family = AF_INET; 158 address_family = AF_INET;
@@ -139,50 +165,61 @@ int process_arguments(int argc, char **argv) {
139#endif 165#endif
140 break; 166 break;
141 case 'r': /* remote version */ 167 case 'r': /* remote version */
142 remote_version = optarg; 168 result.config.remote_version = optarg;
143 break; 169 break;
144 case 'P': /* remote version */ 170 case 'P': /* remote version */
145 remote_protocol = optarg; 171 result.config.remote_protocol = optarg;
146 break; 172 break;
147 case 'H': /* host */ 173 case 'H': /* host */
148 if (!is_host(optarg)) 174 if (!is_host(optarg)) {
149 usage2(_("Invalid hostname/address"), optarg); 175 usage2(_("Invalid hostname/address"), optarg);
150 server_name = optarg; 176 }
177 result.config.server_name = optarg;
151 break; 178 break;
152 case 'p': /* port */ 179 case 'p': /* port */
153 if (is_intpos(optarg)) { 180 if (is_intpos(optarg)) {
154 port = atoi(optarg); 181 result.config.port = atoi(optarg);
155 } else { 182 } else {
156 usage2(_("Port number must be a positive integer"), optarg); 183 usage2(_("Port number must be a positive integer"), optarg);
157 } 184 }
185 break;
186 case output_format_index: {
187 parsed_output_format parser = mp_parse_output_format(optarg);
188 if (!parser.parsing_success) {
189 // TODO List all available formats here, maybe add anothoer usage function
190 printf("Invalid output format: %s\n", optarg);
191 exit(STATE_UNKNOWN);
192 }
193
194 result.config.output_format_is_set = true;
195 result.config.output_format = parser.output_format;
196 break;
197 }
158 } 198 }
159 } 199 }
160 200
161 option_char = optind; 201 option_char = optind;
162 if (server_name == NULL && option_char < argc) { 202 if (result.config.server_name == NULL && option_char < argc) {
163 if (is_host(argv[option_char])) { 203 if (is_host(argv[option_char])) {
164 server_name = argv[option_char++]; 204 result.config.server_name = argv[option_char++];
165 } 205 }
166 } 206 }
167 207
168 if (port == -1 && option_char < argc) { 208 if (result.config.port == -1 && option_char < argc) {
169 if (is_intpos(argv[option_char])) { 209 if (is_intpos(argv[option_char])) {
170 port = atoi(argv[option_char++]); 210 result.config.port = atoi(argv[option_char++]);
171 } else { 211 } else {
172 print_usage(); 212 print_usage();
173 exit(STATE_UNKNOWN); 213 exit(STATE_UNKNOWN);
174 } 214 }
175 } 215 }
176 216
177 return validate_arguments(); 217 if (result.config.server_name == NULL) {
178} 218 result.errorcode = ERROR;
219 return result;
220 }
179 221
180int validate_arguments(void) { 222 return result;
181 if (server_name == NULL)
182 return ERROR;
183 if (port == -1) /* funky, but allows -p to override stray integer in args */
184 port = SSH_DFL_PORT;
185 return OK;
186} 223}
187 224
188/************************************************************************ 225/************************************************************************
@@ -191,28 +228,34 @@ int validate_arguments(void) {
191 * 228 *
192 *-----------------------------------------------------------------------*/ 229 *-----------------------------------------------------------------------*/
193 230
194int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_protocol) { 231int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_version, char *desired_remote_protocol) {
195 struct timeval tv; 232 struct timeval tv;
196 gettimeofday(&tv, NULL); 233 gettimeofday(&tv, NULL);
197 234
198 int socket; 235 int socket;
199 int result = my_tcp_connect(haddr, hport, &socket); 236 int result = my_tcp_connect(haddr, hport, &socket);
200 237
201 if (result != STATE_OK) 238 mp_subcheck connection_sc = mp_subcheck_init();
239 if (result != STATE_OK) {
240 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
241 xasprintf(&connection_sc.output, "Failed to establish TCP connection to Host %s and Port %d", haddr, hport);
242 mp_add_subcheck_to_check(overall, connection_sc);
202 return result; 243 return result;
244 }
203 245
204 char *output = (char *)calloc(BUFF_SZ + 1, sizeof(char)); 246 char *output = (char *)calloc(BUFF_SZ + 1, sizeof(char));
205 char *buffer = NULL; 247 char *buffer = NULL;
206 ssize_t recv_ret = 0; 248 size_t recv_ret = 0;
207 char *version_control_string = NULL; 249 char *version_control_string = NULL;
208 ssize_t byte_offset = 0; 250 size_t byte_offset = 0;
209 while ((version_control_string == NULL) && (recv_ret = recv(socket, output + byte_offset, BUFF_SZ - byte_offset, 0) > 0)) { 251 while ((version_control_string == NULL) &&
252 (recv_ret = recv(socket, output + byte_offset, (unsigned long)(BUFF_SZ - byte_offset), 0) > 0)) {
210 253
211 if (strchr(output, '\n')) { /* we've got at least one full line, start parsing*/ 254 if (strchr(output, '\n')) { /* we've got at least one full line, start parsing*/
212 byte_offset = 0; 255 byte_offset = 0;
213 256
214 char *index = NULL; 257 char *index = NULL;
215 int len = 0; 258 unsigned long len = 0;
216 while ((index = strchr(output + byte_offset, '\n')) != NULL) { 259 while ((index = strchr(output + byte_offset, '\n')) != NULL) {
217 /*Partition the buffer so that this line is a separate string, 260 /*Partition the buffer so that this line is a separate string,
218 * by replacing the newline with NUL*/ 261 * by replacing the newline with NUL*/
@@ -243,14 +286,23 @@ int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_proto
243 } 286 }
244 287
245 if (recv_ret < 0) { 288 if (recv_ret < 0) {
246 printf("SSH CRITICAL - %s", strerror(errno)); 289 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
247 exit(STATE_CRITICAL); 290 xasprintf(&connection_sc.output, "%s", "SSH CRITICAL - %s", strerror(errno));
291 mp_add_subcheck_to_check(overall, connection_sc);
292 return OK;
248 } 293 }
249 294
250 if (version_control_string == NULL) { 295 if (version_control_string == NULL) {
251 printf("SSH CRITICAL - No version control string received"); 296 connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL);
252 exit(STATE_CRITICAL); 297 xasprintf(&connection_sc.output, "%s", "SSH CRITICAL - No version control string received");
298 mp_add_subcheck_to_check(overall, connection_sc);
299 return OK;
253 } 300 }
301
302 connection_sc = mp_set_subcheck_state(connection_sc, STATE_OK);
303 xasprintf(&connection_sc.output, "%s", "Initial connection succeeded");
304 mp_add_subcheck_to_check(overall, connection_sc);
305
254 /* 306 /*
255 * "When the connection has been established, both sides MUST send an 307 * "When the connection has been established, both sides MUST send an
256 * identification string. This identification string MUST be 308 * identification string. This identification string MUST be
@@ -259,8 +311,9 @@ int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_proto
259 * - RFC 4253:4.2 311 * - RFC 4253:4.2
260 */ 312 */
261 strip(version_control_string); 313 strip(version_control_string);
262 if (verbose) 314 if (verbose) {
263 printf("%s\n", version_control_string); 315 printf("%s\n", version_control_string);
316 }
264 317
265 char *ssh_proto = version_control_string + 4; 318 char *ssh_proto = version_control_string + 4;
266 319
@@ -288,41 +341,65 @@ int ssh_connect(char *haddr, int hport, char *remote_version, char *remote_proto
288 if (tmp) { 341 if (tmp) {
289 ssh_server[tmp - ssh_server] = '\0'; 342 ssh_server[tmp - ssh_server] = '\0';
290 } 343 }
344
345 mp_subcheck protocol_validity_sc = mp_subcheck_init();
291 if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) { 346 if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) {
292 printf(_("SSH CRITICAL - Invalid protocol version control string %s\n"), version_control_string); 347 protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_CRITICAL);
293 exit(STATE_CRITICAL); 348 xasprintf(&protocol_validity_sc.output, "Invalid protocol version control string %s", version_control_string);
349 mp_add_subcheck_to_check(overall, protocol_validity_sc);
350 return OK;
294 } 351 }
352
353 protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_OK);
354 xasprintf(&protocol_validity_sc.output, "Valid protocol version control string %s", version_control_string);
355 mp_add_subcheck_to_check(overall, protocol_validity_sc);
356
295 ssh_proto[strspn(ssh_proto, "0123456789. ")] = 0; 357 ssh_proto[strspn(ssh_proto, "0123456789. ")] = 0;
296 358
297 static char *rev_no = VERSION; 359 static char *rev_no = VERSION;
298 xasprintf(&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no); 360 xasprintf(&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no);
299 send(socket, buffer, strlen(buffer), MSG_DONTWAIT); 361 send(socket, buffer, strlen(buffer), MSG_DONTWAIT);
300 if (verbose) 362 if (verbose) {
301 printf("%s\n", buffer); 363 printf("%s\n", buffer);
364 }
302 365
303 if (remote_version && strcmp(remote_version, ssh_server)) { 366 if (desired_remote_version && strcmp(desired_remote_version, ssh_server)) {
304 printf(_("SSH CRITICAL - %s (protocol %s) version mismatch, expected '%s'\n"), ssh_server, ssh_proto, remote_version); 367 mp_subcheck remote_version_sc = mp_subcheck_init();
368 remote_version_sc = mp_set_subcheck_state(remote_version_sc, STATE_CRITICAL);
369 xasprintf(&remote_version_sc.output, _("%s (protocol %s) version mismatch, expected '%s'"), ssh_server, ssh_proto,
370 desired_remote_version);
305 close(socket); 371 close(socket);
306 exit(STATE_CRITICAL); 372 mp_add_subcheck_to_check(overall, remote_version_sc);
373 return OK;
307 } 374 }
308 375
309 double elapsed_time = (double)deltime(tv) / 1.0e6; 376 double elapsed_time = (double)deltime(tv) / 1.0e6;
310 if (remote_protocol && strcmp(remote_protocol, ssh_proto)) { 377 mp_perfdata time_pd = perfdata_init();
311 printf(_("SSH CRITICAL - %s (protocol %s) protocol version mismatch, expected '%s' | %s\n"), ssh_server, ssh_proto, remote_protocol, 378 time_pd.value = mp_create_pd_value(elapsed_time);
312 fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, true, (int)socket_timeout)); 379 time_pd.label = "time";
313 close(socket); 380 time_pd.max_present = true;
314 exit(STATE_CRITICAL); 381 time_pd.max = mp_create_pd_value(socket_timeout);
382
383 mp_subcheck protocol_version_sc = mp_subcheck_init();
384 mp_add_perfdata_to_subcheck(&protocol_version_sc, time_pd);
385
386 if (desired_remote_protocol && strcmp(desired_remote_protocol, ssh_proto)) {
387 protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_CRITICAL);
388 xasprintf(&protocol_version_sc.output, _("%s (protocol %s) protocol version mismatch, expected '%s'"), ssh_server, ssh_proto,
389 desired_remote_protocol);
390 } else {
391 protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_OK);
392 xasprintf(&protocol_version_sc.output, "SSH server version: %s (protocol version: %s)", ssh_server, ssh_proto);
315 } 393 }
316 394
317 printf(_("SSH OK - %s (protocol %s) | %s\n"), ssh_server, ssh_proto, 395 mp_add_subcheck_to_check(overall, protocol_version_sc);
318 fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, true, (int)socket_timeout));
319 close(socket); 396 close(socket);
320 exit(STATE_OK); 397 return OK;
321} 398}
322 399
323void print_help(void) { 400void print_help(void) {
324 char *myport; 401 char *myport;
325 xasprintf(&myport, "%d", SSH_DFL_PORT); 402 xasprintf(&myport, "%d", default_ssh_port);
326 403
327 print_revision(progname, NP_VERSION); 404 print_revision(progname, NP_VERSION);
328 405
@@ -349,6 +426,7 @@ void print_help(void) {
349 426
350 printf(" %s\n", "-P, --remote-protocol=STRING"); 427 printf(" %s\n", "-P, --remote-protocol=STRING");
351 printf(" %s\n", _("Alert if protocol doesn't match expected protocol version (ex: 2.0)")); 428 printf(" %s\n", _("Alert if protocol doesn't match expected protocol version (ex: 2.0)"));
429 printf(UT_OUTPUT_FORMAT);
352 430
353 printf(UT_VERBOSE); 431 printf(UT_VERBOSE);
354 432
@@ -357,5 +435,5 @@ void print_help(void) {
357 435
358void print_usage(void) { 436void print_usage(void) {
359 printf("%s\n", _("Usage:")); 437 printf("%s\n", _("Usage:"));
360 printf("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] <host>\n", progname); 438 printf("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] --hostname <host>\n", progname);
361} 439}