summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/check_ssh.c178
-rw-r--r--plugins/t/check_ssh.t120
2 files changed, 225 insertions, 73 deletions
diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c
index 8ccbd5a7..05150655 100644
--- a/plugins/check_ssh.c
+++ b/plugins/check_ssh.c
@@ -1,31 +1,31 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2*
3* Monitoring check_ssh plugin 3* Monitoring check_ssh plugin
4* 4*
5* License: GPL 5* License: GPL
6* Copyright (c) 2000-2007 Monitoring Plugins Development Team 6* Copyright (c) 2000-2007 Monitoring Plugins Development Team
7* 7*
8* Description: 8* Description:
9* 9*
10* This file contains the check_ssh plugin 10* This file contains the check_ssh plugin
11* 11*
12* Try to connect to an SSH server at specified server and port 12* Try to connect to an SSH server at specified server and port
13* 13*
14* 14*
15* This program is free software: you can redistribute it and/or modify 15* This program is free software: you can redistribute it and/or modify
16* it under the terms of the GNU General Public License as published by 16* it under the terms of the GNU General Public License as published by
17* the Free Software Foundation, either version 3 of the License, or 17* the Free Software Foundation, either version 3 of the License, or
18* (at your option) any later version. 18* (at your option) any later version.
19* 19*
20* This program is distributed in the hope that it will be useful, 20* This program is distributed in the hope that it will be useful,
21* but WITHOUT ANY WARRANTY; without even the implied warranty of 21* but WITHOUT ANY WARRANTY; without even the implied warranty of
22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23* GNU General Public License for more details. 23* GNU General Public License for more details.
24* 24*
25* You should have received a copy of the GNU General Public License 25* You should have received a copy of the GNU General Public License
26* along with this program. If not, see <http://www.gnu.org/licenses/>. 26* along with this program. If not, see <http://www.gnu.org/licenses/>.
27* 27*
28* 28*
29*****************************************************************************/ 29*****************************************************************************/
30 30
31const char *progname = "check_ssh"; 31const char *progname = "check_ssh";
@@ -106,7 +106,7 @@ process_arguments (int argc, char **argv)
106 {"timeout", required_argument, 0, 't'}, 106 {"timeout", required_argument, 0, 't'},
107 {"verbose", no_argument, 0, 'v'}, 107 {"verbose", no_argument, 0, 'v'},
108 {"remote-version", required_argument, 0, 'r'}, 108 {"remote-version", required_argument, 0, 'r'},
109 {"remote-protcol", required_argument, 0, 'P'}, 109 {"remote-protocol", required_argument, 0, 'P'},
110 {0, 0, 0, 0} 110 {0, 0, 0, 0}
111 }; 111 };
112 112
@@ -215,8 +215,13 @@ ssh_connect (char *haddr, int hport, char *remote_version, char *remote_protocol
215{ 215{
216 int sd; 216 int sd;
217 int result; 217 int result;
218 int len = 0;
219 ssize_t byte_offset = 0;
220 ssize_t recv_ret = 0;
221 char *version_control_string = NULL;
218 char *output = NULL; 222 char *output = NULL;
219 char *buffer = NULL; 223 char *buffer = NULL;
224 char *tmp= NULL;
220 char *ssh_proto = NULL; 225 char *ssh_proto = NULL;
221 char *ssh_server = NULL; 226 char *ssh_server = NULL;
222 static char *rev_no = VERSION; 227 static char *rev_no = VERSION;
@@ -231,51 +236,118 @@ ssh_connect (char *haddr, int hport, char *remote_version, char *remote_protocol
231 return result; 236 return result;
232 237
233 output = (char *) malloc (BUFF_SZ + 1); 238 output = (char *) malloc (BUFF_SZ + 1);
234 memset (output, 0, BUFF_SZ + 1); 239 memset(output, 0, BUFF_SZ+1);
235 recv (sd, output, BUFF_SZ, 0); 240 while (!version_control_string && (recv_ret = recv(sd, output+byte_offset, BUFF_SZ - byte_offset, 0)) > 0) {
236 if (strncmp (output, "SSH", 3)) { 241 if (strchr(output, '\n')) { /* we've got at least one full line, start parsing*/
237 printf (_("Server answer: %s"), output); 242 byte_offset = 0;
238 close(sd); 243 while (strchr(output+byte_offset, '\n') != NULL) {
244 /*Partition the buffer so that this line is a separate string,
245 * by replacing the newline with NUL*/
246 output[(strchr(output+byte_offset, '\n')-output)]= '\0';
247 len = strlen(output+byte_offset);
248 if (len >= 4) {
249 /*if the string starts with SSH-, this _should_ be a valid version control string*/
250 if (strncmp (output+byte_offset, "SSH-", 4) == 0) {
251 version_control_string = output+byte_offset;
252 break;
253 }
254 }
255
256 /*the start of the next line (if one exists) will be after the current one (+ NUL)*/
257 byte_offset+=len+1;
258 }
259 if(!version_control_string) {
260 /* move unconsumed data to beginning of buffer, null rest */
261 memmove((void *)output, (void *)output+byte_offset+1, BUFF_SZ - len+1);
262 memset(output+byte_offset+1, 0, BUFF_SZ-byte_offset+1);
263
264 /*start reading from end of current line chunk on next recv*/
265 byte_offset = strlen(output);
266 }
267 }
268 else {
269 byte_offset += recv_ret;
270 }
271 }
272 tmp = NULL;
273 if (recv_ret < 0) {
274 printf("SSH CRITICAL - %s", strerror(errno));
275 exit(STATE_CRITICAL);
276 }
277 if (!version_control_string) {
278 printf("SSH CRITICAL - No version control string received");
279 exit(STATE_CRITICAL);
280 }
281 /*
282 * "When the connection has been established, both sides MUST send an
283 * identification string. This identification string MUST be
284 *
285 * SSH-protoversion-softwareversion SP comments CR LF"
286 * - RFC 4253:4.2
287 */
288 strip (version_control_string);
289 if (verbose)
290 printf ("%s\n", version_control_string);
291 ssh_proto = version_control_string + 4;
292
293 /*
294 * We assume the protoversion is of the form Major.Minor, although
295 * this is not _strictly_ required. See
296 *
297 * "Both the 'protoversion' and 'softwareversion' strings MUST consist of
298 * printable US-ASCII characters, with the exception of whitespace
299 * characters and the minus sign (-)"
300 * - RFC 4253:4.2
301 * and,
302 *
303 * "As stated earlier, the 'protoversion' specified for this protocol is
304 * "2.0". Earlier versions of this protocol have not been formally
305 * documented, but it is widely known that they use 'protoversion' of
306 * "1.x" (e.g., "1.5" or "1.3")."
307 * - RFC 4253:5
308 */
309 ssh_server = ssh_proto + strspn (ssh_proto, "0123456789.") + 1; /* (+1 for the '-' separating protoversion from softwareversion) */
310
311 /* If there's a space in the version string, whatever's after the space is a comment
312 * (which is NOT part of the server name/version)*/
313 tmp = strchr(ssh_server, ' ');
314 if (tmp) {
315 ssh_server[tmp - ssh_server] = '\0';
316 }
317 if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) {
318 printf(_("SSH CRITICAL - Invalid protocol version control string %s\n"), version_control_string);
239 exit (STATE_CRITICAL); 319 exit (STATE_CRITICAL);
240 } 320 }
241 else { 321 ssh_proto[strspn (ssh_proto, "0123456789. ")] = 0;
242 strip (output);
243 if (verbose)
244 printf ("%s\n", output);
245 ssh_proto = output + 4;
246 ssh_server = ssh_proto + strspn (ssh_proto, "-0123456789. ");
247 ssh_proto[strspn (ssh_proto, "0123456789. ")] = 0;
248
249 xasprintf (&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no);
250 send (sd, buffer, strlen (buffer), MSG_DONTWAIT);
251 if (verbose)
252 printf ("%s\n", buffer);
253
254 if (remote_version && strcmp(remote_version, ssh_server)) {
255 printf
256 (_("SSH CRITICAL - %s (protocol %s) version mismatch, expected '%s'\n"),
257 ssh_server, ssh_proto, remote_version);
258 close(sd);
259 exit (STATE_CRITICAL);
260 }
261 322
262 if (remote_protocol && strcmp(remote_protocol, ssh_proto)) { 323 xasprintf (&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no);
263 printf 324 send (sd, buffer, strlen (buffer), MSG_DONTWAIT);
264 (_("SSH CRITICAL - %s (protocol %s) protocol version mismatch, expected '%s'\n"), 325 if (verbose)
265 ssh_server, ssh_proto, remote_protocol); 326 printf ("%s\n", buffer);
266 close(sd);
267 exit (STATE_CRITICAL);
268 }
269 327
270 elapsed_time = (double)deltime(tv) / 1.0e6; 328 if (remote_version && strcmp(remote_version, ssh_server)) {
329 printf
330 (_("SSH CRITICAL - %s (protocol %s) version mismatch, expected '%s'\n"),
331 ssh_server, ssh_proto, remote_version);
332 close(sd);
333 exit (STATE_CRITICAL);
334 }
271 335
336 if (remote_protocol && strcmp(remote_protocol, ssh_proto)) {
272 printf 337 printf
273 (_("SSH OK - %s (protocol %s) | %s\n"), 338 (_("SSH CRITICAL - %s (protocol %s) protocol version mismatch, expected '%s'\n"),
274 ssh_server, ssh_proto, fperfdata("time", elapsed_time, "s", 339 ssh_server, ssh_proto, remote_protocol);
275 FALSE, 0, FALSE, 0, TRUE, 0, TRUE, (int)socket_timeout));
276 close(sd); 340 close(sd);
277 exit (STATE_OK); 341 exit (STATE_CRITICAL);
278 } 342 }
343 elapsed_time = (double)deltime(tv) / 1.0e6;
344
345 printf
346 (_("SSH OK - %s (protocol %s) | %s\n"),
347 ssh_server, ssh_proto, fperfdata("time", elapsed_time, "s",
348 FALSE, 0, FALSE, 0, TRUE, 0, TRUE, (int)socket_timeout));
349 close(sd);
350 exit (STATE_OK);
279} 351}
280 352
281 353
@@ -293,7 +365,7 @@ print_help (void)
293 365
294 printf ("%s\n", _("Try to connect to an SSH server at specified server and port")); 366 printf ("%s\n", _("Try to connect to an SSH server at specified server and port"));
295 367
296 printf ("\n\n"); 368 printf ("\n\n");
297 369
298 print_usage (); 370 print_usage ();
299 371
@@ -307,10 +379,10 @@ print_help (void)
307 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 379 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
308 380
309 printf (" %s\n", "-r, --remote-version=STRING"); 381 printf (" %s\n", "-r, --remote-version=STRING");
310 printf (" %s\n", _("Alert if string doesn't match expected server version (ex: OpenSSH_3.9p1)")); 382 printf (" %s\n", _("Alert if string doesn't match expected server version (ex: OpenSSH_3.9p1)"));
311 383
312 printf (" %s\n", "-P, --remote-protocol=STRING"); 384 printf (" %s\n", "-P, --remote-protocol=STRING");
313 printf (" %s\n", _("Alert if protocol doesn't match expected protocol version (ex: 2.0)")); 385 printf (" %s\n", _("Alert if protocol doesn't match expected protocol version (ex: 2.0)"));
314 386
315 printf (UT_VERBOSE); 387 printf (UT_VERBOSE);
316 388
@@ -322,7 +394,7 @@ print_help (void)
322void 394void
323print_usage (void) 395print_usage (void)
324{ 396{
325 printf ("%s\n", _("Usage:")); 397 printf ("%s\n", _("Usage:"));
326 printf ("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] <host>\n", progname); 398 printf ("%s [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] <host>\n", progname);
327} 399}
328 400
diff --git a/plugins/t/check_ssh.t b/plugins/t/check_ssh.t
index a5cd23ce..7df62651 100644
--- a/plugins/t/check_ssh.t
+++ b/plugins/t/check_ssh.t
@@ -8,34 +8,114 @@ use strict;
8use Test::More; 8use Test::More;
9use NPTest; 9use NPTest;
10 10
11my $res;
12
11# Required parameters 13# Required parameters
12my $ssh_host = getTestParameter("NP_SSH_HOST", "A host providing SSH service", "localhost"); 14my $ssh_host = getTestParameter("NP_SSH_HOST",
13my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE", "The hostname of system not responsive to network requests", "10.0.0.1" ); 15 "A host providing SSH service",
14my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost" ); 16 "localhost");
17
18my $host_nonresponsive = getTestParameter("NP_HOST_NONRESPONSIVE",
19 "The hostname of system not responsive to network requests",
20 "10.0.0.1" );
21
22my $hostname_invalid = getTestParameter("NP_HOSTNAME_INVALID",
23 "An invalid (not known to DNS) hostname",
24 "nosuchhost" );
25
26
27plan tests => 14 + 6;
28
29SKIP: {
30 skip "SSH_HOST must be defined", 6 unless $ssh_host;
31 my $result = NPTest->testCmd(
32 "./check_ssh -H $ssh_host"
33 );
34 cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)");
35 like($result->output, '/^SSH OK - /', "Status text if command returned none (OK)");
36
37
38 $result = NPTest->testCmd(
39 "./check_ssh -H $host_nonresponsive -t 2"
40 );
41 cmp_ok($result->return_code, '==', 2, "Exit with return code 0 (OK)");
42 like($result->output, '/^CRITICAL - Socket timeout after 2 seconds/', "Status text if command returned none (OK)");
43
44
45
46 $result = NPTest->testCmd(
47 "./check_ssh -H $hostname_invalid -t 2"
48 );
49 cmp_ok($result->return_code, '==', 3, "Exit with return code 0 (OK)");
50 like($result->output, '/^check_ssh: Invalid hostname/', "Status text if command returned none (OK)");
51
15 52
53}
54SKIP: {
16 55
17plan skip_all => "SSH_HOST must be defined" unless $ssh_host; 56 skip "No netcat available", 14 unless (system("which nc > /dev/null") == 0);
18plan tests => 6;
19 57
58 my $nc_flags = "-l 5003 -i 1";
59 #A valid protocol version control string has the form
60 # SSH-protoversion-softwareversion SP comments CR LF
61 #
62 # where `comments` is optional, protoversion is the SSH protocol version and
63 # softwareversion is an arbitrary string representing the server software version
64 open(NC, "echo 'SSH-2.0-nagiosplug.ssh.0.1' | nc ${nc_flags}|");
65 sleep 1;
66 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" );
67 cmp_ok( $res->return_code, '==', 0, "Got SSH protocol version control string");
68 like( $res->output, '/^SSH OK - nagiosplug.ssh.0.1 \(protocol 2.0\)/', "Output OK");
69 close NC;
20 70
21my $result = NPTest->testCmd( 71 open(NC, "echo 'SSH-2.0-3.2.9.1' | nc ${nc_flags}|");
22 "./check_ssh -H $ssh_host" 72 sleep 1;
23 ); 73 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" );
24cmp_ok($result->return_code, '==', 0, "Exit with return code 0 (OK)"); 74 cmp_ok( $res->return_code, "==", 0, "Got SSH protocol version control string with non-alpha softwareversion string");
25like($result->output, '/^SSH OK - /', "Status text if command returned none (OK)"); 75 like( $res->output, '/^SSH OK - 3.2.9.1 \(protocol 2.0\)/', "Output OK for non-alpha softwareversion string");
76 close NC;
26 77
78 open(NC, "echo 'SSH-2.0-nagiosplug.ssh.0.1 this is a comment' | nc ${nc_flags} |");
79 sleep 1;
80 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003 -r nagiosplug.ssh.0.1" );
81 cmp_ok( $res->return_code, '==', 0, "Got SSH protocol version control string, and parsed comment appropriately");
82 like( $res->output, '/^SSH OK - nagiosplug.ssh.0.1 \(protocol 2.0\)/', "Output OK");
83 close NC;
27 84
28$result = NPTest->testCmd( 85 open(NC, "echo 'SSH-' | nc ${nc_flags}|");
29 "./check_ssh -H $host_nonresponsive -t 2" 86 sleep 1;
30 ); 87 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" );
31cmp_ok($result->return_code, '==', 2, "Exit with return code 0 (OK)"); 88 cmp_ok( $res->return_code, '==', 2, "Got invalid SSH protocol version control string");
32like($result->output, '/^CRITICAL - Socket timeout after 2 seconds/', "Status text if command returned none (OK)"); 89 like( $res->output, '/^SSH CRITICAL/', "Output OK");
90 close NC;
33 91
92 open(NC, "echo '' | nc ${nc_flags}|");
93 sleep 1;
94 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" );
95 cmp_ok( $res->return_code, '==', 2, "No version control string received");
96 like( $res->output, '/^SSH CRITICAL - No version control string received/', "Output OK");
97 close NC;
34 98
99 open(NC, "echo 'Not a version control string' | nc ${nc_flags}|");
100 sleep 1;
101 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" );
102 cmp_ok( $res->return_code, '==', 2, "No version control string received");
103 like( $res->output, '/^SSH CRITICAL - No version control string received/', "Output OK");
104 close NC;
35 105
36$result = NPTest->testCmd(
37 "./check_ssh -H $hostname_invalid -t 2"
38 );
39cmp_ok($result->return_code, '==', 3, "Exit with return code 0 (OK)");
40like($result->output, '/^check_ssh: Invalid hostname/', "Status text if command returned none (OK)");
41 106
107 #RFC 4253 permits servers to send any number of data lines prior to sending the protocol version control string
108 open(NC, "{ echo 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; sleep 1;
109 echo 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'; sleep 1;
110 echo 'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC'; sleep 1;
111 echo 'DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD'; sleep 1;
112 printf 'EEEEEEEEEEEEEEEEEE'; sleep 1;
113 printf 'EEEEEEEEEEEEEEEEEE\n'; sleep 1;
114 echo 'Some\nPrepended\nData\nLines\n'; sleep 1;
115 echo 'SSH-2.0-nagiosplug.ssh.0.2';} | nc ${nc_flags}|");
116 sleep 1;
117 $res = NPTest->testCmd( "./check_ssh -H localhost -p 5003" );
118 cmp_ok( $res->return_code, '==', 0, "Got delayed SSH protocol version control string");
119 like( $res->output, '/^SSH OK - nagiosplug.ssh.0.2 \(protocol 2.0\)/', "Output OK");
120 close NC;
121}