From 36e58ae0c3ad7a9d3660722b35d3ed9c97687dd2 Mon Sep 17 00:00:00 2001 From: Ton Voon Date: Sat, 14 Mar 2009 01:17:50 +0000 Subject: Fixed passing of quotes in OID for check_snmp (#1985230 - Jan Wagner, patch by John Barbuto) --- plugins/Makefile.am | 4 +- plugins/check_snmp.c | 295 +++++++++++++++++++++++-------------------------- plugins/t/check_snmp.t | 49 ++++++-- 3 files changed, 182 insertions(+), 166 deletions(-) (limited to 'plugins') diff --git a/plugins/Makefile.am b/plugins/Makefile.am index f5272e17..4e1f2101 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -93,7 +93,7 @@ check_ping_LDADD = $(NETLIBS) popen.o check_procs_LDADD = $(BASEOBJS) check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS) check_real_LDADD = $(NETLIBS) -check_snmp_LDADD = $(BASEOBJS) popen.o +check_snmp_LDADD = $(BASEOBJS) check_smtp_LDADD = $(SSLOBJS) $(NETLIBS) $(SSLLIBS) check_ssh_LDADD = $(NETLIBS) check_swap_LDADD = $(MATHLIBS) $(BASEOBJS) popen.o @@ -135,7 +135,7 @@ check_ping_DEPENDENCIES = check_ping.c $(NETOBJS) popen.o $(DEPLIBS) check_procs_DEPENDENCIES = check_procs.c $(BASEOBJS) popen.o $(DEPLIBS) check_radius_DEPENDENCIES = check_radius.c $(NETOBJS) $(DEPLIBS) check_real_DEPENDENCIES = check_real.c $(NETOBJS) $(DEPLIBS) -check_snmp_DEPENDENCIES = check_snmp.c $(BASEOBJS) popen.o $(DEPLIBS) +check_snmp_DEPENDENCIES = check_snmp.c $(BASEOBJS) $(DEPLIBS) check_smtp_DEPENDENCIES = check_smtp.c $(SSLOBJS) $(NETOBJS) $(DEPLIBS) check_ssh_DEPENDENCIES = check_ssh.c $(NETOBJS) $(DEPLIBS) check_swap_DEPENDENCIES = check_swap.c $(BASEOBJS) popen.o $(DEPLIBS) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index a8e08fa1..1b1eb2e8 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -34,7 +34,7 @@ const char *email = "nagiosplug-devel@lists.sourceforge.net"; #include "common.h" #include "utils.h" -#include "popen.h" +#include "utils_cmd.h" #define DEFAULT_COMMUNITY "public" #define DEFAULT_PORT "161" @@ -91,14 +91,14 @@ regex_t preg; regmatch_t pmatch[10]; char timestamp[10] = ""; char errbuf[MAX_INPUT_BUFFER] = ""; -char perfstr[MAX_INPUT_BUFFER] = ""; +char perfstr[MAX_INPUT_BUFFER] = "| "; int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; int eflags = 0; int errcode, excode; char *server_address = NULL; char *community = NULL; -char *authpriv = NULL; +char **authpriv = NULL; char *proto = NULL; char *seclevel = NULL; char *secname = NULL; @@ -106,10 +106,11 @@ char *authproto = NULL; char *privproto = NULL; char *authpasswd = NULL; char *privpasswd = NULL; -char *oid; +char **oids = NULL; char *label; char *units; char *port; +char *snmpcmd; char string_value[MAX_INPUT_BUFFER] = ""; char **labels = NULL; char **unitv = NULL; @@ -117,6 +118,8 @@ size_t nlabels = 0; size_t labels_size = 8; size_t nunits = 0; size_t unitv_size = 8; +int numoids = 0; +int numauthpriv = 0; int verbose = FALSE; int usesnmpgetnext = FALSE; unsigned long long lower_warn_lim[MAX_OIDS]; @@ -139,18 +142,16 @@ main (int argc, char **argv) { int i = 0; int iresult = STATE_UNKNOWN; - int found = 0; - int result = STATE_DEPENDENT; - char input_buffer[MAX_INPUT_BUFFER]; - char *command_line = NULL; + int result = STATE_UNKNOWN; + char **command_line = NULL; char *cl_hidden_auth = NULL; + char *oidname = NULL; char *response = NULL; char *outbuff; - char *output; char *ptr = NULL; - char *p2 = NULL; char *show = NULL; char type[8] = ""; + output chld_out, chld_err; setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); @@ -162,12 +163,10 @@ main (int argc, char **argv) eval_method[i] = CHECK_UNDEF; i = 0; - oid = strdup (""); label = strdup ("SNMP"); units = strdup (""); port = strdup (DEFAULT_PORT); outbuff = strdup (""); - output = strdup (""); delimiter = strdup (" = "); output_delim = strdup (DEFAULT_OUTPUT_DELIMITER); /* miblist = strdup (DEFAULT_MIBLIST); */ @@ -180,91 +179,73 @@ main (int argc, char **argv) if (process_arguments (argc, argv) == ERROR) usage4 (_("Could not parse arguments")); - /* create the command line to execute */ - if(usesnmpgetnext == TRUE) { - asprintf(&command_line, "%s -t %d -r %d -m %s -v %s %s %s:%s %s", - PATH_TO_SNMPGETNEXT, timeout_interval, retries, miblist, proto, - authpriv, server_address, port, oid); - asprintf(&cl_hidden_auth, "%s -t %d -r %d -m %s -v %s %s %s:%s %s", - PATH_TO_SNMPGETNEXT, timeout_interval, retries, miblist, proto, - "[authpriv]", server_address, port, oid); + /* Create the command array to execute */ + if(usesnmpgetnext == TRUE) { + snmpcmd = strdup (PATH_TO_SNMPGETNEXT); }else{ - - asprintf (&command_line, "%s -t %d -r %d -m %s -v %s %s %s:%s %s", - PATH_TO_SNMPGET, timeout_interval, retries, miblist, proto, - authpriv, server_address, port, oid); - asprintf(&cl_hidden_auth, "%s -t %d -r %d -m %s -v %s %s %s:%s %s", - PATH_TO_SNMPGET, timeout_interval, retries, miblist, proto, - "[authpriv]", server_address, port, oid); + snmpcmd = strdup (PATH_TO_SNMPGET); + } + + /* 9 arguments to pass before authpriv options + 1 for host and numoids. Add one for terminating NULL */ + command_line = calloc (9 + numauthpriv + 1 + numoids + 1, sizeof (char *)); + command_line[0] = snmpcmd; + command_line[1] = strdup ("-t"); + asprintf (&command_line[2], "%d", timeout_interval); + command_line[3] = strdup ("-r"); + asprintf (&command_line[4], "%d", retries); + command_line[5] = strdup ("-m"); + command_line[6] = strdup (miblist); + command_line[7] = "-v"; + command_line[8] = strdup (proto); + + for (i = 0; i < numauthpriv; i++) { + command_line[9 + i] = authpriv[i]; } - if (verbose) - printf ("%s\n", command_line); - + asprintf (&command_line[9 + numauthpriv], "%s:%s", server_address, port); - /* run the command */ - child_process = spopen (command_line); - if (child_process == NULL) { - printf (_("Could not open pipe: %s\n"), cl_hidden_auth); - exit (STATE_UNKNOWN); - } + /* This is just for display purposes, so it can remain a string */ + asprintf(&cl_hidden_auth, "%s -t %d -r %d -m %s -v %s %s %s:%s", + snmpcmd, timeout_interval, retries, miblist, proto, "[authpriv]", + server_address, port); -#if 0 /* Removed May 29, 2007 */ - child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); - if (child_stderr == NULL) { - printf (_("Could not open stderr for %s\n"), cl_hidden_auth); + for (i = 0; i < numoids; i++) { + command_line[9 + numauthpriv + 1 + i] = oids[i]; + asprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]); } -#endif - while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) - asprintf (&output, "%s%s", output, input_buffer); + command_line[9 + numauthpriv + 1 + numoids] = NULL; if (verbose) - printf ("%s\n", output); + printf ("%s\n", cl_hidden_auth); - ptr = output; + /* Run the command */ + result = cmd_run_array (command_line, &chld_out, &chld_err, 0); - strncat(perfstr, "| ", sizeof(perfstr)-strlen(perfstr)-1); - while (ptr) { - char *foo, *ptr2; - unsigned int copylen; - - foo = strstr (ptr, delimiter); - copylen = foo-ptr; - if (copylen > sizeof(perfstr)-strlen(perfstr)-1) - copylen = sizeof(perfstr)-strlen(perfstr)-1; - ptr2 = ptr; - ptr = foo; - - if (ptr == NULL) - break; - - ptr += strlen (delimiter); - ptr += strspn (ptr, " "); + if (chld_err.lines > 0) { + printf (_("External command error: %s\n"), chld_err.line[0]); + for (i = 1; i < chld_err.lines; i++) { + printf ("%s\n", chld_err.line[i]); + } + exit (STATE_UNKNOWN); + } - found++; + /* Return UNKNOWN or worse if no output is returned */ + if (chld_out.lines == 0) + die (max_state_alt (result, STATE_UNKNOWN), _("%s problem - No data received from host\nCMD: %s\n"), + label, + cl_hidden_auth); - if (ptr[0] == '"') { - ptr++; - response = strpcpy (response, ptr, "\""); - ptr = strpbrk (ptr, "\""); - ptr += strspn (ptr, "\"\n"); - } - else { - response = strpcpy (response, ptr, "\n"); - ptr = strpbrk (ptr, "\n"); - ptr += strspn (ptr, "\n"); - while - (strstr (ptr, delimiter) && - strstr (ptr, "\n") && strstr (ptr, "\n") < strstr (ptr, delimiter)) { - response = strpcat (response, ptr, "\n"); - ptr = strpbrk (ptr, "\n"); - } - if (ptr && strstr (ptr, delimiter) == NULL) { - asprintf (&response, "%s%s", response, ptr); - ptr = NULL; - } + if (verbose) { + for (i = 0; i < chld_out.lines; i++) { + printf ("%s\n", chld_out.line[i]); } + } + + for (i = 0; i < chld_out.lines; i++) { + ptr = chld_out.line[i]; + oidname = strpcpy (oidname, ptr, delimiter); + response = strstr (ptr, delimiter); /* We strip out the datatype indicator for PHBs */ @@ -289,7 +270,6 @@ main (int argc, char **argv) show = strstr (response, "STRING: ") + 8; else show = response; - p2 = show; iresult = STATE_DEPENDENT; @@ -306,10 +286,10 @@ main (int argc, char **argv) eval_method[i] & WARN_LE || eval_method[i] & WARN_EQ || eval_method[i] & WARN_NE) { - p2 = strpbrk (p2, "0123456789"); - if (p2 == NULL) + ptr = strpbrk (show, "0123456789"); + if (ptr == NULL) die (STATE_UNKNOWN,_("No valid data returned")); - response_value[i] = strtoul (p2, NULL, 10); + response_value[i] = strtoul (ptr, NULL, 10); iresult = check_num (i); asprintf (&show, "%llu", response_value[i]); } @@ -364,10 +344,8 @@ main (int argc, char **argv) if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL) asprintf (&outbuff, "%s %s", outbuff, unitv[i]); - i++; - if (is_numeric(show)) { - strncat(perfstr, ptr2, copylen); + strncat(perfstr, oidname, sizeof(perfstr)-strlen(perfstr)-1); strncat(perfstr, "=", sizeof(perfstr)-strlen(perfstr)-1); strncat(perfstr, show, sizeof(perfstr)-strlen(perfstr)-1); @@ -375,29 +353,6 @@ main (int argc, char **argv) strncat(perfstr, type, sizeof(perfstr)-strlen(perfstr)-1); strncat(perfstr, " ", sizeof(perfstr)-strlen(perfstr)-1); } - - } /* end while (ptr) */ - - if (found == 0) - die (STATE_UNKNOWN, - _("%s problem - No data received from host\nCMD: %s\n"), - label, - cl_hidden_auth); - -#if 0 /* Removed May 29, 2007 */ - /* WARNING if output found on stderr */ - if (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) - result = max_state (result, STATE_WARNING); - - /* close stderr */ - (void) fclose (child_stderr); -#endif - - /* close the pipe */ - if (spclose (child_process)) { - if (result == STATE_OK) - result = STATE_UNKNOWN; - asprintf (&outbuff, "%s (%s)", outbuff, _("snmpget returned an error status")); } /* if (nunits == 1 || i == 1) */ @@ -563,12 +518,12 @@ process_arguments (int argc, char **argv) */ needmibs = TRUE; } - - for (ptr = optarg; (ptr = index (ptr, ',')); ptr++) - ptr[0] = ' '; /* relpace comma with space */ - for (ptr = optarg; (ptr = index (ptr, ' ')); ptr++) - j++; /* count OIDs */ - asprintf (&oid, "%s %s", (oid?oid:""), optarg); + oids = calloc(MAX_OIDS, sizeof (char *)); + for (ptr = strtok(optarg, ", "); ptr != NULL; ptr = strtok(NULL, ", ")) { + oids[j] = strdup(ptr); + j++; + } + numoids = j; if (c == 'E' || c == 'e') { jj++; ii++; @@ -675,8 +630,6 @@ process_arguments (int argc, char **argv) if (community == NULL) community = strdup (DEFAULT_COMMUNITY); - - return validate_arguments (); } @@ -713,47 +666,79 @@ validate_arguments () } } + /* Check server_address is given */ + if (server_address == NULL) + die(STATE_UNKNOWN, _("No host specified\n")); - /* Need better checks to verify seclevel and authproto choices */ - - if (seclevel == NULL) - asprintf (&seclevel, "noAuthNoPriv"); - - - if (authproto == NULL ) - asprintf(&authproto, DEFAULT_AUTH_PROTOCOL); - - if (privproto == NULL ) - asprintf(&privproto, DEFAULT_PRIV_PROTOCOL); + /* Check oid is given */ + if (numoids == 0) + die(STATE_UNKNOWN, _("No OIDs specified\n")); - if (proto == NULL || (strcmp(proto,DEFAULT_PROTOCOL) == 0) ) { /* default protocol version */ + if (proto == NULL) asprintf(&proto, DEFAULT_PROTOCOL); - asprintf(&authpriv, "%s%s", "-c ", community); - } - else if ( strcmp (proto, "2c") == 0 ) { /* snmpv2c args */ - asprintf(&authpriv, "%s%s", "-c ", community); + + if ((strcmp(proto,"1") == 0) || (strcmp(proto, "2c")==0)) { /* snmpv1 or snmpv2c */ + numauthpriv = 2; + authpriv = calloc (numauthpriv, sizeof (char *)); + authpriv[0] = strdup ("-c"); + authpriv[1] = strdup (community); } else if ( strcmp (proto, "3") == 0 ) { /* snmpv3 args */ - asprintf(&proto, "%s", "3"); - - if ( (strcmp(seclevel, "noAuthNoPriv") == 0) || seclevel == NULL ) { - asprintf(&authpriv, "%s", "-l noAuthNoPriv" ); - } - else if ( strcmp(seclevel, "authNoPriv") == 0 ) { - if ( secname == NULL || authpasswd == NULL) { - printf (_("Missing secname (%s) or authpassword (%s) ! \n"),secname, authpasswd ); - print_usage (); - exit (STATE_UNKNOWN); + if (seclevel == NULL) + asprintf(&seclevel, "noAuthNoPriv"); + + if (strcmp(seclevel, "noAuthNoPriv") == 0) { + numauthpriv = 2; + authpriv = calloc (numauthpriv, sizeof (char *)); + authpriv[0] = strdup ("-l"); + authpriv[1] = strdup ("noAuthNoPriv"); + } else { + if (! ( (strcmp(seclevel, "authNoPriv")==0) || (strcmp(seclevel, "authPriv")==0) ) ) { + usage2 (_("Invalid seclevel"), seclevel); } - asprintf(&authpriv, "-l authNoPriv -a %s -u %s -A %s ", authproto, secname, authpasswd); - } - else if ( strcmp(seclevel, "authPriv") == 0 ) { - if ( secname == NULL || authpasswd == NULL || privpasswd == NULL ) { - printf (_("Missing secname (%s), authpassword (%s), or privpasswd (%s)! \n"),secname, authpasswd,privpasswd ); - print_usage (); - exit (STATE_UNKNOWN); + + if (authproto == NULL ) + asprintf(&authproto, DEFAULT_AUTH_PROTOCOL); + + if (secname == NULL) + die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); + + if (authpasswd == NULL) + die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd"); + + if ( strcmp(seclevel, "authNoPriv") == 0 ) { + numauthpriv = 8; + authpriv = calloc (numauthpriv, sizeof (char *)); + authpriv[0] = strdup ("-l"); + authpriv[1] = strdup ("authNoPriv"); + authpriv[2] = strdup ("-a"); + authpriv[3] = strdup (authproto); + authpriv[4] = strdup ("-u"); + authpriv[5] = strdup (secname); + authpriv[6] = strdup ("-A"); + authpriv[7] = strdup (authpasswd); + } else if ( strcmp(seclevel, "authPriv") == 0 ) { + if (privproto == NULL ) + asprintf(&privproto, DEFAULT_PRIV_PROTOCOL); + + if (privpasswd == NULL) + die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd"); + + numauthpriv = 12; + authpriv = calloc (numauthpriv, sizeof (char *)); + authpriv[0] = strdup ("-l"); + authpriv[1] = strdup ("authPriv"); + authpriv[2] = strdup ("-a"); + authpriv[3] = strdup (authproto); + authpriv[4] = strdup ("-u"); + authpriv[5] = strdup (secname); + authpriv[6] = strdup ("-A"); + authpriv[7] = strdup (authpasswd); + authpriv[8] = strdup ("-x"); + authpriv[9] = strdup (privproto); + authpriv[10] = strdup ("-X"); + authpriv[11] = strdup (privpasswd); } - asprintf(&authpriv, "-l authPriv -a %s -u %s -A %s -x %s -X %s ", authproto, secname, authpasswd, privproto, privpasswd); } } diff --git a/plugins/t/check_snmp.t b/plugins/t/check_snmp.t index 369314ec..4820aace 100644 --- a/plugins/t/check_snmp.t +++ b/plugins/t/check_snmp.t @@ -8,9 +8,8 @@ use strict; use Test::More; use NPTest; -use vars qw($tests); -BEGIN {$tests = 14; plan tests => $tests} - +my $tests = 32; +plan tests => $tests; my $res; SKIP: { @@ -20,7 +19,7 @@ SKIP: { "A host providing an SNMP Service"); my $snmp_community = getTestParameter( "snmp_community", "NP_SNMP_COMMUNITY", "public", - "The SNMP Community string for SNMP Testing" ); + "The SNMP Community string for SNMP Testing (assumes snmp v1)" ); my $host_nonresponsive = getTestParameter( "host_nonresponsive", "NP_HOST_NONRESPONSIVE", "10.0.0.1", "The hostname of system not responsive to network requests" ); @@ -28,12 +27,32 @@ SKIP: { my $hostname_invalid = getTestParameter( "hostname_invalid", "NP_HOSTNAME_INVALID", "nosuchhost", "An invalid (not known to DNS) hostname" ); + $res = NPTest->testCmd( "./check_snmp -t 1" ); + is( $res->return_code, 3, "No host name" ); + is( $res->output, "No host specified" ); + + $res = NPTest->testCmd( "./check_snmp -H fakehostname" ); + is( $res->return_code, 3, "No OIDs specified" ); + is( $res->output, "No OIDs specified" ); + + $res = NPTest->testCmd( "./check_snmp -H fakehost -o oids -P 3 --seclevel=rubbish" ); + is( $res->return_code, 3, "Invalid seclevel" ); + like( $res->output, "/check_snmp: Invalid seclevel - rubbish/" ); + + $res = NPTest->testCmd( "./check_snmp -H fakehost -o oids -P 3c" ); + is( $res->return_code, 3, "Invalid protocol" ); + like( $res->output, "/check_snmp: Invalid SNMP version - 3c/" ); + SKIP: { - skip "no snmp host defined", 10 if ( ! $host_snmp ); + skip "no snmp host defined", 20 if ( ! $host_snmp ); $res = NPTest->testCmd( "./check_snmp -H $host_snmp -C $snmp_community -o system.sysUpTime.0 -w 1: -c 1:"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying uptime" ); - like($res->output, '/^SNMP OK - \d+/', "String contains SNMP OK"); + like($res->output, '/^SNMP OK - (\d+)/', "String contains SNMP OK"); + $res->output =~ /^SNMP OK - (\d+)/; + my $value = $1; + cmp_ok( $value, ">", 0, "Got a time value" ); + like($res->perf_output, "/sysUpTime.*$1/", "Got perfdata with value '$1' in it"); $res = NPTest->testCmd( "./check_snmp -H $host_snmp -C $snmp_community -o system.sysDescr.0"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying sysDescr" ); @@ -49,21 +68,33 @@ SKIP: { $res = NPTest->testCmd( "./check_snmp -H $host_snmp -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w :0 -c 0"); cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL when querying hrSWRunIndex.1 and crit-th doesn't apply" ); - like($res->output, '/^SNMP CRITICAL - \*1\*\s.*$/', "String matches SNMP CRITICAL and ouput format"); + like($res->output, '/^SNMP CRITICAL - \*1\*\s.*$/', "String matches SNMP CRITICAL and output format"); + + $res = NPTest->testCmd( "./check_snmp -H $host_snmp -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2 -c 1:2"); + cmp_ok( $res->return_code, '==', 0, "Checking two OIDs at once" ); + like($res->output, "/^SNMP OK - 2 1/", "Got two values back" ); + like( $res->perf_output, "/ifIndex.2=2/", "Got 1st perf data" ); + like( $res->perf_output, "/ifIndex.1=1/", "Got 2nd perf data" ); + + $res = NPTest->testCmd( "./check_snmp -H $host_snmp -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2,1:2 -c 2:2,2:2"); + cmp_ok( $res->return_code, '==', 2, "Checking critical threshold is passed if any one value crosses" ); + like($res->output, "/^SNMP CRITICAL - 2 *1*/", "Got two values back" ); + like( $res->perf_output, "/ifIndex.2=2/", "Got 1st perf data" ); + like( $res->perf_output, "/ifIndex.1=1/", "Got 2nd perf data" ); } SKIP: { skip "no non responsive host defined", 2 if ( ! $host_nonresponsive ); $res = NPTest->testCmd( "./check_snmp -H $host_nonresponsive -C $snmp_community -o system.sysUpTime.0 -w 1: -c 1:"); cmp_ok( $res->return_code, '==', 3, "Exit UNKNOWN with non responsive host" ); - like($res->output, '/SNMP problem - /', "String matches SNMP Problem"); + like($res->output, '/External command error: Timeout: No Response from /', "String matches timeout problem"); } SKIP: { skip "no non invalid host defined", 2 if ( ! $hostname_invalid ); $res = NPTest->testCmd( "./check_snmp -H $hostname_invalid -C $snmp_community -o system.sysUpTime.0 -w 1: -c 1:"); cmp_ok( $res->return_code, '==', 3, "Exit UNKNOWN with non responsive host" ); - like($res->output, '/SNMP problem - /', "String matches SNMP Problem"); + like($res->output, '/External command error: .*nosuchhost/', "String matches invalid host"); } } -- cgit v1.2.3-74-g34f1