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 */ |
