[nagios-plugins] check_ssh using libssh to verify host ...

Git Repository <[email protected]> Fri, 4 Oct 2013 00:10:06 +0200 (CEST)
Newsgroups gmane.network.nagios.plugins.cvs
Message-ID <[email protected]>
    Module: nagios-plugins
    Branch: pu
    Commit: 776f03beaad1595502c3ccbf5532568e64c90c9d
    Author: hjanuschka <[email protected]>
 Committer: Thomas Guyot-Sionnest <[email protected]>
      Date: Tue Dec  4 13:29:40 2012 +0100
       URL: https://www.nagios-plugins.org/repositories/nagios-plugins.git/?/commit/?id=776f03b

     check_ssh using libssh to verify host fingerprint and openssh version

    if libssh is not found, the fingerprint / openssh-version-detection functions are not compiled in.
    for backward compatibility the parameters of original check_ssh stay untouched
    for checking the fingerprint && || the openssh version use -f and or -o

    to check against known_hosts file use -f known_host

    sample calls:
        #checks if SSH fingerprint is stored in local known hosts
        ./check_ssh -f known_hosts  localhost

        #checks if SSH fingerprint is stored in local known hosts, and version is >= 5.5
        ./check_ssh -f known_hosts  -o 5.5 localhost

        #checks if SSH fingerprint is equal the print supplied with -f
        ./check_ssh -f  a2:47:e8:83:d9:8b:f5:c4:19:23:47:0d:e0:bf:a8:26  localhost

        #checks if SSH fingerprint is equal the print supplied with -f and version is >= 5.5
        ./check_ssh -f  a2:47:e8:83:d9:8b:f5:c4:19:23:47:0d:e0:bf:a8:26  -o 5.5 localhost

        #check for SSH version
        ./check_ssh -o 5.5 localhost

---

 configure.in        |   14 ++++
 plugins/Makefile.am |    2 +-
 plugins/check_ssh.c |  206 ++++++++++++++++++++++++++++++++++++++-------------
 3 files changed, 170 insertions(+), 52 deletions(-)

diff --git a/configure.in b/configure.in
index 1d4ed00..a951d39 100644
--- a/configure.in
+++ b/configure.in
@@ -204,6 +204,20 @@ if test "$enable_extra_opts" = "yes" ; then
 	fi
 fi
 
+
+dnl Check for ssh libraries
+
+AC_CHECK_LIB(ssh,ssh_connect)
+if test "$ac_cv_lib_ssh_ssh_connect" = "yes"; then
+	SSHLIB="-lssh"
+  AC_SUBST(SSHLIB)
+  AC_DEFINE(HAVE_SSH,1,[Define if SSL libraries are found])
+else
+    AC_MSG_WARN([Skipping check_ssh plugin])
+    AC_MSG_WARN([install ssh libs to compile this plugin (see REQUIREMENTS).])
+fi
+
+
 dnl Check for PostgreSQL libraries
 _SAVEDLIBS="$LIBS"
 _SAVEDCPPFLAGS="$CPPFLAGS"
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 0eb0255..7cea40c 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -96,7 +96,7 @@ check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS)
 check_real_LDADD = $(NETLIBS)
 check_snmp_LDADD = $(BASEOBJS)
 check_smtp_LDADD = $(SSLOBJS) $(NETLIBS) $(SSLLIBS)
-check_ssh_LDADD = $(NETLIBS)
+check_ssh_LDADD = $(NETLIBS) $(SSHLIB)
 check_swap_LDADD = $(MATHLIBS) $(BASEOBJS) popen.o
 check_tcp_LDADD = $(SSLOBJS) $(NETLIBS) $(SSLLIBS)
 check_time_LDADD = $(NETLIBS)
diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c
index 6e8a5fc..c3526c9 100644
--- a/plugins/check_ssh.c
+++ b/plugins/check_ssh.c
@@ -36,6 +36,10 @@ const char *email = "[email protected]";
 #include "netutils.h"
 #include "utils.h"
 
+#ifdef HAVE_SSH
+#include <libssh/libssh.h>
+#endif
+
 #ifndef MSG_DONTWAIT
 #define MSG_DONTWAIT 0
 #endif
@@ -46,15 +50,17 @@ const char *email = "[email protected]";
 int port = -1;
 char *server_name = NULL;
 char *remote_version = NULL;
+char *remote_fingerprint = NULL;
 int verbose = FALSE;
+char * remote_openssh_version = NULL;
 
 int process_arguments (int, char **);
 int validate_arguments (void);
 void print_help (void);
 void print_usage (void);
 
-int ssh_connect (char *haddr, int hport, char *remote_version);
-
+int ssh_connect_p (char *haddr, int hport, char *remote_version, char *remote_fingerprint);
+int ssh_connect_wo_lib (char *haddr, int hport, char *remote_version);
 
 
 int
@@ -78,14 +84,80 @@ main (int argc, char **argv)
 	alarm (socket_timeout);
 
 	/* ssh_connect exits if error is found */
-	result = ssh_connect (server_name, port, remote_version);
 
+	#ifdef HAVE_SSH
+	if(remote_openssh_version != NULL || remote_fingerprint != NULL) {
+		result = ssh_connect_p (server_name, port, remote_openssh_version, remote_fingerprint);
+	} else {
+		result = ssh_connect_wo_lib(server_name, port, remote_version);
+	}
+	#else
+	result = ssh_connect_wo_lib(server_name, port, remote_version);
+	#endif
 	alarm (0);
 
 	return (result);
 }
 
+int
+ssh_connect_wo_lib (char *haddr, int hport, char *remote_version)
+{
+	int sd;
+	int result;
+	char *output = NULL;
+	char *buffer = NULL;
+	char *ssh_proto = NULL;
+	char *ssh_server = NULL;
+	static char *rev_no = VERSION;
+	struct timeval tv;
+	double elapsed_time;
 
+	gettimeofday(&tv, NULL);
+
+	result = my_tcp_connect (haddr, hport, &sd);
+
+	if (result != STATE_OK)
+		return result;
+
+	output = (char *) malloc (BUFF_SZ + 1);
+	memset (output, 0, BUFF_SZ + 1);
+	recv (sd, output, BUFF_SZ, 0);
+	if (strncmp (output, "SSH", 3)) {
+		printf (_("Server answer: %s"), output);
+		close(sd);
+		exit (STATE_CRITICAL);
+	}
+	else {
+		strip (output);
+		if (verbose)
+			printf ("%s\n", output);
+		ssh_proto = output + 4;
+		ssh_server = ssh_proto + strspn (ssh_proto, "-0123456789. ");
+		ssh_proto[strspn (ssh_proto, "0123456789. ")] = 0;
+
+		xasprintf (&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no);
+		send (sd, buffer, strlen (buffer), MSG_DONTWAIT);
+		if (verbose)
+			printf ("%s\n", buffer);
+
+		if (remote_version && strcmp(remote_version, ssh_server)) {
+			printf
+				(_("SSH WARNING - %s (protocol %s) version mismatch, expected '%s'\n"),
+				 ssh_server, ssh_proto, remote_version);
+			close(sd);
+			exit (STATE_WARNING);
+		}
+
+		elapsed_time = (double)deltime(tv) / 1.0e6;
+
+		printf
+			(_("SSH OK - %s (protocol %s) | %s\n"),
+			 ssh_server, ssh_proto, fperfdata("time", elapsed_time, "s",
+			 FALSE, 0, FALSE, 0, TRUE, 0, TRUE, (int)socket_timeout));
+		close(sd);
+		exit (STATE_OK);
+	}
+}
 
 /* process command-line arguments */
 int
@@ -105,6 +177,8 @@ process_arguments (int argc, char **argv)
 		{"timeout", required_argument, 0, 't'},
 		{"verbose", no_argument, 0, 'v'},
 		{"remote-version", required_argument, 0, 'r'},
+		{"fingerprint", required_argument, 0, 'f'},
+		{"remote-openssh-version", required_argument, 0, 'o'},
 		{0, 0, 0, 0}
 	};
 
@@ -116,7 +190,7 @@ process_arguments (int argc, char **argv)
 			strcpy (argv[c], "-t");
 
 	while (1) {
-		c = getopt_long (argc, argv, "+Vhv46t:r:H:p:", longopts, &option);
+		c = getopt_long (argc, argv, "+Vhv46t:r:f:H:o:p:", longopts, &option);
 
 		if (c == -1 || c == EOF)
 			break;
@@ -152,6 +226,12 @@ process_arguments (int argc, char **argv)
 		case 'r':									/* remote version */
 			remote_version = optarg;
 			break;
+		case 'o':
+			remote_openssh_version = optarg;
+		break;
+		case 'f':									/* remote version */
+			remote_fingerprint = optarg;
+			break;
 		case 'H':									/* host */
 			if (is_host (optarg) == FALSE)
 				usage2 (_("Invalid hostname/address"), optarg);
@@ -204,66 +284,82 @@ validate_arguments (void)
 *
 *-----------------------------------------------------------------------*/
 
-
+#ifdef HAVE_SSH
 int
-ssh_connect (char *haddr, int hport, char *remote_version)
+ssh_connect_p (char *haddr, int hport, char *remote_version, char * remote_fingerprint)
 {
-	int sd;
-	int result;
-	char *output = NULL;
-	char *buffer = NULL;
-	char *ssh_proto = NULL;
-	char *ssh_server = NULL;
-	static char *rev_no = VERSION;
 	struct timeval tv;
 	double elapsed_time;
 
+	ssh_session my_ssh_session;
+	int  version;
+	int myversion;
+	int hlen;
+	int rc;
+	int state;
+	int i;
+	unsigned char *hash = NULL;
+	char *  fingerprint;
+	int in_known_host;
+
+	int sshv1,sshv2,sshv3;
+
 	gettimeofday(&tv, NULL);
 
-	result = my_tcp_connect (haddr, hport, &sd);
+	my_ssh_session = ssh_new();
 
-	if (result != STATE_OK)
-		return result;
+	if (my_ssh_session == NULL)
+		return STATE_CRITICAL;
 
-	output = (char *) malloc (BUFF_SZ + 1);
-	memset (output, 0, BUFF_SZ + 1);
-	recv (sd, output, BUFF_SZ, 0);
-	if (strncmp (output, "SSH", 3)) {
-		printf (_("Server answer: %s"), output);
-		close(sd);
+	ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, haddr);
+	ssh_options_set(my_ssh_session, SSH_OPTIONS_PORT, &hport);
+	rc = ssh_connect(my_ssh_session);
+	if (rc != SSH_OK) {
+		printf ("Connect to Server failed\n");
 		exit (STATE_CRITICAL);
 	}
-	else {
-		strip (output);
-		if (verbose)
-			printf ("%s\n", output);
-		ssh_proto = output + 4;
-		ssh_server = ssh_proto + strspn (ssh_proto, "-0123456789. ");
-		ssh_proto[strspn (ssh_proto, "0123456789. ")] = 0;
+	in_known_host=-1;
+	state = ssh_is_server_known(my_ssh_session);
+	hlen = ssh_get_pubkey_hash(my_ssh_session, &hash);
+
+	/* Get the finger print as a string */
+	fingerprint = ssh_get_hexa(hash, hlen);
+	if(remote_fingerprint && strcmp(remote_fingerprint, "known_host") == NULL) {
+		if(state != SSH_SERVER_KNOWN_OK) {
+			printf ("SSH CRITICAL - Fingerprint (%s) checked in known_hosts failed\n", remote_fingerprint,fingerprint);
+			exit(STATE_CRITICAL);
+		} else {
+			in_known_host=1;
+		}
+	}
 
-		xasprintf (&buffer, "SSH-%s-check_ssh_%s\r\n", ssh_proto, rev_no);
-		send (sd, buffer, strlen (buffer), MSG_DONTWAIT);
-		if (verbose)
-			printf ("%s\n", buffer);
+	/* FIXME: This alwats eval to false... */
+	if(remote_fingerprint && strcmp(remote_fingerprint, "known_host") && strcmp(remote_fingerprint, fingerprint)) {
+		printf ("SSH CRITICAL - Fingerprint (%s) mismatched %s\n", remote_fingerprint,fingerprint);
+		free(fingerprint);
+		exit(STATE_CRITICAL);
+	}
 
-		if (remote_version && strcmp(remote_version, ssh_server)) {
-			printf
-				(_("SSH WARNING - %s (protocol %s) version mismatch, expected '%s'\n"),
-				 ssh_server, ssh_proto, remote_version);
-			close(sd);
-			exit (STATE_WARNING);
+	version = ssh_get_openssh_version(my_ssh_session);
+	if(remote_version && sscanf(remote_version, "%d.%d.%d", &sshv1, &sshv2, &sshv3)) {
+		myversion = SSH_VERSION_INT(sshv1, sshv2, sshv3);
+		if(version < myversion) {
+			printf ("SSH WARNING version on server is below %s\n", remote_version);
+			exit(STATE_CRITICAL);
 		}
+	}
 
-		elapsed_time = (double)deltime(tv) / 1.0e6;
+	elapsed_time = (double)deltime(tv) / 1.0e6;
 
-		printf
-			(_("SSH OK - %s (protocol %s) | %s\n"),
-			 ssh_server, ssh_proto, fperfdata("time", elapsed_time, "s",
-			 FALSE, 0, FALSE, 0, TRUE, 0, TRUE, (int)socket_timeout));
-		close(sd);
-		exit (STATE_OK);
-	}
+	printf (_("SSH OK - fingerprint: %s (Version %d) known_host_check:%d | %s\n"),
+	        fingerprint, version,in_known_host, fperfdata("time", elapsed_time, "s",
+	        FALSE, 0, FALSE, 0, TRUE, 0, TRUE, (int)socket_timeout));
+	free(fingerprint);
+	ssh_disconnect(my_ssh_session);
+	ssh_free(my_ssh_session);
+	exit(STATE_OK);
 }
+#endif
 
 
 
@@ -280,7 +376,7 @@ print_help (void)
 
 	printf ("%s\n", _("Try to connect to an SSH server at specified server and port"));
 
-  printf ("\n\n");
+	printf ("\n\n");
 
 	print_usage ();
 
@@ -294,7 +390,15 @@ print_help (void)
 	printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
 
 	printf (" %s\n", "-r, --remote-version=STRING");
-  printf ("    %s\n", _("Warn if string doesn't match expected server version (ex: OpenSSH_3.9p1)"));
+	printf ("    %s\n", _("Warn if string doesn't match expected server version (ex: OpenSSH_3.9p1)"));
+
+#ifdef HAVE_SSH
+	printf (" %s\n", "-o, --remote-openssh-version=STRING");
+	printf ("    %s\n", _("Warn if Remote Openssh version is lower than STRING"));
+#endif
+
+	printf (" %s\n", "-f, --fingerprint=STRING (e.g.: a247e883d98bf5c41923470de0bfa826)");
+	printf ("    %s\n", _("Critical if remote fingerprint is not equal to supplied"));
 
 	printf (UT_VERBOSE);
 
@@ -306,7 +410,7 @@ print_help (void)
 void
 print_usage (void)
 {
-  printf ("%s\n", _("Usage:"));
-	printf ("%s  [-4|-6] [-t <timeout>] [-r <remote version>] [-p <port>] <host>\n", progname);
+	printf ("%s\n", _("Usage:"));
+	printf ("%s  [-4|-6] [-t <timeout>] [-f <fingerprint>] [-r <remote version>] [-o <remote openssh version>] [-p <port>] <host>\n", progname);
 }
 


------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60134791&iu=/4140/ostg.clktrk