diff options
author | Anton Lofgren <alofgren@op5.com> | 2013-10-21 08:18:30 +0200 |
---|---|---|
committer | Lorenz Kästle <lorenz.kaestle@netways.de> | 2022-01-14 15:34:08 +0100 |
commit | cb0b3e245afcdc29e606299c93fc232ddd6d7cef (patch) | |
tree | 69f35f5d31087e4efcd830dd7b1d0b73b4467c2e /plugins/check_ssh.c | |
parent | 9899bc736f45400fa70bdee281f5f5b46490b805 (diff) | |
download | monitoring-plugins-cb0b3e245afcdc29e606299c93fc232ddd6d7cef.tar.gz |
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>
Diffstat (limited to 'plugins/check_ssh.c')
-rw-r--r-- | plugins/check_ssh.c | 122 |
1 files changed, 85 insertions, 37 deletions
diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c index 8ccbd5a7..8a3abb03 100644 --- a/plugins/check_ssh.c +++ b/plugins/check_ssh.c | |||
@@ -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, *saveptr = 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,94 @@ 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 | strip (version_control_string); | ||
282 | if (verbose) | ||
283 | printf ("%s\n", version_control_string); | ||
284 | ssh_proto = version_control_string + 4; | ||
285 | ssh_server = ssh_proto + strspn (ssh_proto, "-0123456789."); | ||
286 | |||
287 | /* If there's a space in the version string, whatever's after the space is a comment | ||
288 | * (which is NOT part of the server name/version)*/ | ||
289 | tmp = strchr(ssh_server, ' '); | ||
290 | if (tmp) { | ||
291 | ssh_server[tmp - ssh_server] = '\0'; | ||
292 | } | ||
293 | if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) { | ||
294 | printf(_("SSH CRITICAL - Invalid protocol version control string %s\n"), version_control_string); | ||
239 | exit (STATE_CRITICAL); | 295 | exit (STATE_CRITICAL); |
240 | } | 296 | } |
241 | else { | 297 | 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 | 298 | ||
262 | if (remote_protocol && strcmp(remote_protocol, ssh_proto)) { | 299 | xasprintf (&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no); |
263 | printf | 300 | send (sd, buffer, strlen (buffer), MSG_DONTWAIT); |
264 | (_("SSH CRITICAL - %s (protocol %s) protocol version mismatch, expected '%s'\n"), | 301 | if (verbose) |
265 | ssh_server, ssh_proto, remote_protocol); | 302 | printf ("%s\n", buffer); |
266 | close(sd); | ||
267 | exit (STATE_CRITICAL); | ||
268 | } | ||
269 | 303 | ||
270 | elapsed_time = (double)deltime(tv) / 1.0e6; | 304 | if (remote_version && strcmp(remote_version, ssh_server)) { |
305 | printf | ||
306 | (_("SSH CRITICAL - %s (protocol %s) version mismatch, expected '%s'\n"), | ||
307 | ssh_server, ssh_proto, remote_version); | ||
308 | close(sd); | ||
309 | exit (STATE_CRITICAL); | ||
310 | } | ||
271 | 311 | ||
312 | if (remote_protocol && strcmp(remote_protocol, ssh_proto)) { | ||
272 | printf | 313 | printf |
273 | (_("SSH OK - %s (protocol %s) | %s\n"), | 314 | (_("SSH CRITICAL - %s (protocol %s) protocol version mismatch, expected '%s'\n"), |
274 | ssh_server, ssh_proto, fperfdata("time", elapsed_time, "s", | 315 | ssh_server, ssh_proto, remote_protocol); |
275 | FALSE, 0, FALSE, 0, TRUE, 0, TRUE, (int)socket_timeout)); | ||
276 | close(sd); | 316 | close(sd); |
277 | exit (STATE_OK); | 317 | exit (STATE_CRITICAL); |
278 | } | 318 | } |
319 | elapsed_time = (double)deltime(tv) / 1.0e6; | ||
320 | |||
321 | printf | ||
322 | (_("SSH OK - %s (protocol %s) | %s\n"), | ||
323 | ssh_server, ssh_proto, fperfdata("time", elapsed_time, "s", | ||
324 | FALSE, 0, FALSE, 0, TRUE, 0, TRUE, (int)socket_timeout)); | ||
325 | close(sd); | ||
326 | exit (STATE_OK); | ||
279 | } | 327 | } |
280 | 328 | ||
281 | 329 | ||