diff options
author | Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> | 2025-09-15 01:57:40 +0200 |
---|---|---|
committer | Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> | 2025-09-15 01:57:40 +0200 |
commit | f5f4a021a2760d553e3e4cdedd291eb815750369 (patch) | |
tree | d41b56d231c7e0c7b5b92aa4e90c20d95bf50cfb /plugins | |
parent | c15d12cbd5008de74dccd6f716e418feffed5560 (diff) | |
download | monitoring-plugins-f5f4a021a2760d553e3e4cdedd291eb815750369.tar.gz |
Add new cert check function
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/check_curl.c | 3 | ||||
-rw-r--r-- | plugins/netutils.h | 3 | ||||
-rw-r--r-- | plugins/sslutils.c | 135 |
3 files changed, 139 insertions, 2 deletions
diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 680ecef7..b1021045 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c | |||
@@ -569,8 +569,9 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state | |||
569 | sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); | 569 | sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); |
570 | } | 570 | } |
571 | } else if (errcode == REG_NOMATCH) { | 571 | } else if (errcode == REG_NOMATCH) { |
572 | xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output); | ||
573 | // got no match | 572 | // got no match |
573 | xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output); | ||
574 | |||
574 | if (config.invert_regex) { | 575 | if (config.invert_regex) { |
575 | sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); | 576 | sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); |
576 | } else { | 577 | } else { |
diff --git a/plugins/netutils.h b/plugins/netutils.h index c53b3cef..6adb8e01 100644 --- a/plugins/netutils.h +++ b/plugins/netutils.h | |||
@@ -32,6 +32,7 @@ | |||
32 | #define _NETUTILS_H_ | 32 | #define _NETUTILS_H_ |
33 | 33 | ||
34 | #include "common.h" | 34 | #include "common.h" |
35 | #include "output.h" | ||
35 | #include "states.h" | 36 | #include "states.h" |
36 | #include "utils.h" | 37 | #include "utils.h" |
37 | #include <netinet/in.h> | 38 | #include <netinet/in.h> |
@@ -114,6 +115,6 @@ void np_net_ssl_cleanup(); | |||
114 | int np_net_ssl_write(const void *buf, int num); | 115 | int np_net_ssl_write(const void *buf, int num); |
115 | int np_net_ssl_read(void *buf, int num); | 116 | int np_net_ssl_read(void *buf, int num); |
116 | mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); | 117 | mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); |
118 | mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); | ||
117 | #endif /* HAVE_SSL */ | 119 | #endif /* HAVE_SSL */ |
118 | |||
119 | #endif /* _NETUTILS_H_ */ | 120 | #endif /* _NETUTILS_H_ */ |
diff --git a/plugins/sslutils.c b/plugins/sslutils.c index bea1307f..3ce6afed 100644 --- a/plugins/sslutils.c +++ b/plugins/sslutils.c | |||
@@ -26,6 +26,7 @@ | |||
26 | * | 26 | * |
27 | *****************************************************************************/ | 27 | *****************************************************************************/ |
28 | 28 | ||
29 | #include "output.h" | ||
29 | #define MAX_CN_LENGTH 256 | 30 | #define MAX_CN_LENGTH 256 |
30 | #include "common.h" | 31 | #include "common.h" |
31 | #include "netutils.h" | 32 | #include "netutils.h" |
@@ -322,4 +323,138 @@ mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_cr | |||
322 | # endif /* USE_OPENSSL */ | 323 | # endif /* USE_OPENSSL */ |
323 | } | 324 | } |
324 | 325 | ||
326 | mp_subcheck mp_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, | ||
327 | int days_till_exp_crit) { | ||
328 | mp_subcheck sc_cert = mp_subcheck_init(); | ||
329 | # ifdef USE_OPENSSL | ||
330 | if (!certificate) { | ||
331 | xasprintf(&sc_cert.output, _("No server certificate present to inspect")); | ||
332 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
333 | return sc_cert; | ||
334 | } | ||
335 | |||
336 | /* Extract CN from certificate subject */ | ||
337 | X509_NAME *subj = X509_get_subject_name(certificate); | ||
338 | |||
339 | if (!subj) { | ||
340 | xasprintf(&sc_cert.output, _("Cannot retrieve certificate subject")); | ||
341 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
342 | return sc_cert; | ||
343 | } | ||
344 | |||
345 | char commonName[MAX_CN_LENGTH] = ""; | ||
346 | int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, commonName, sizeof(commonName)); | ||
347 | if (cnlen == -1) { | ||
348 | strcpy(commonName, _("Unknown CN")); | ||
349 | } | ||
350 | |||
351 | /* Retrieve timestamp of certificate */ | ||
352 | ASN1_STRING *expiry_timestamp = X509_get_notAfter(certificate); | ||
353 | |||
354 | int offset = 0; | ||
355 | struct tm stamp = {}; | ||
356 | /* Generate tm structure to process timestamp */ | ||
357 | if (expiry_timestamp->type == V_ASN1_UTCTIME) { | ||
358 | if (expiry_timestamp->length < 10) { | ||
359 | xasprintf(&sc_cert.output, _("Wrong time format in certificate")); | ||
360 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
361 | return sc_cert; | ||
362 | } | ||
363 | |||
364 | stamp.tm_year = (expiry_timestamp->data[0] - '0') * 10 + (expiry_timestamp->data[1] - '0'); | ||
365 | if (stamp.tm_year < 50) { | ||
366 | stamp.tm_year += 100; | ||
367 | } | ||
368 | |||
369 | offset = 0; | ||
370 | } else { | ||
371 | if (expiry_timestamp->length < 12) { | ||
372 | xasprintf(&sc_cert.output, _("Wrong time format in certificate")); | ||
373 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
374 | return sc_cert; | ||
375 | } | ||
376 | stamp.tm_year = (expiry_timestamp->data[0] - '0') * 1000 + | ||
377 | (expiry_timestamp->data[1] - '0') * 100 + | ||
378 | (expiry_timestamp->data[2] - '0') * 10 + (expiry_timestamp->data[3] - '0'); | ||
379 | stamp.tm_year -= 1900; | ||
380 | offset = 2; | ||
381 | } | ||
382 | |||
383 | stamp.tm_mon = (expiry_timestamp->data[2 + offset] - '0') * 10 + | ||
384 | (expiry_timestamp->data[3 + offset] - '0') - 1; | ||
385 | stamp.tm_mday = (expiry_timestamp->data[4 + offset] - '0') * 10 + | ||
386 | (expiry_timestamp->data[5 + offset] - '0'); | ||
387 | stamp.tm_hour = (expiry_timestamp->data[6 + offset] - '0') * 10 + | ||
388 | (expiry_timestamp->data[7 + offset] - '0'); | ||
389 | stamp.tm_min = (expiry_timestamp->data[8 + offset] - '0') * 10 + | ||
390 | (expiry_timestamp->data[9 + offset] - '0'); | ||
391 | stamp.tm_sec = (expiry_timestamp->data[10 + offset] - '0') * 10 + | ||
392 | (expiry_timestamp->data[11 + offset] - '0'); | ||
393 | stamp.tm_isdst = -1; | ||
394 | |||
395 | time_t tm_t = timegm(&stamp); | ||
396 | double time_left = difftime(tm_t, time(NULL)); | ||
397 | int days_left = (int)(time_left / 86400); | ||
398 | char *timeZone = getenv("TZ"); | ||
399 | setenv("TZ", "GMT", 1); | ||
400 | tzset(); | ||
401 | |||
402 | char timestamp[50] = ""; | ||
403 | strftime(timestamp, 50, "%c %z", localtime(&tm_t)); | ||
404 | if (timeZone) { | ||
405 | setenv("TZ", timeZone, 1); | ||
406 | } else { | ||
407 | unsetenv("TZ"); | ||
408 | } | ||
409 | |||
410 | tzset(); | ||
411 | |||
412 | int time_remaining; | ||
413 | if (days_left > 0 && days_left <= days_till_exp_warn) { | ||
414 | xasprintf(&sc_cert.output, _("Certificate '%s' expires in %d day(s) (%s)"), commonName, | ||
415 | days_left, timestamp); | ||
416 | if (days_left > days_till_exp_crit) { | ||
417 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); | ||
418 | } else { | ||
419 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
420 | } | ||
421 | } else if (days_left == 0 && time_left > 0) { | ||
422 | if (time_left >= 3600) { | ||
423 | time_remaining = (int)time_left / 3600; | ||
424 | } else { | ||
425 | time_remaining = (int)time_left / 60; | ||
426 | } | ||
427 | |||
428 | xasprintf(&sc_cert.output, _("Certificate '%s' expires in %u %s (%s)"), commonName, | ||
429 | time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); | ||
430 | |||
431 | if (days_left > days_till_exp_crit) { | ||
432 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); | ||
433 | } else { | ||
434 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
435 | } | ||
436 | } else if (time_left < 0) { | ||
437 | xasprintf(&sc_cert.output, _("Certificate '%s' expired on %s"), commonName, timestamp); | ||
438 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
439 | } else if (days_left == 0) { | ||
440 | xasprintf(&sc_cert.output, _("Certificate '%s' just expired (%s)"), commonName, | ||
441 | timestamp); | ||
442 | if (days_left > days_till_exp_crit) { | ||
443 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); | ||
444 | } else { | ||
445 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); | ||
446 | } | ||
447 | } else { | ||
448 | xasprintf(&sc_cert.output, _("Certificate '%s' will expire on %s"), commonName, | ||
449 | timestamp); | ||
450 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_OK); | ||
451 | } | ||
452 | X509_free(certificate); | ||
453 | return sc_cert; | ||
454 | # else /* ifndef USE_OPENSSL */ | ||
455 | xasprintf(&sc_cert.output, _("Plugin does not support checking certificates")); | ||
456 | sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); | ||
457 | return sc_cert; | ||
458 | # endif /* USE_OPENSSL */ | ||
459 | } | ||
325 | #endif /* HAVE_SSL */ | 460 | #endif /* HAVE_SSL */ |