[monitoring-plugins] check_smtp: modern output + some tls cert helper ...
Lorenz Kästle
git at monitoring-plugins.org
Sun Nov 9 12:30:12 CET 2025
Module: monitoring-plugins
Branch: master
Commit: 6bc9e518b247e85a39479a0ac6685e68c3a61b40
Author: Lorenz Kästle <12514511+RincewindsHat at users.noreply.github.com>
Date: Sat Nov 8 00:19:25 2025 +0100
URL: https://www.monitoring-plugins.org/repositories/monitoring-plugins/commit/?id=6bc9e518
check_smtp: modern output + some tls cert helper functions
---
plugins/check_smtp.c | 676 ++++++++++++++++++++++++------------------
plugins/check_smtp.d/config.h | 16 +-
plugins/netutils.h | 20 ++
plugins/sslutils.c | 132 +++++++++
4 files changed, 550 insertions(+), 294 deletions(-)
diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c
index 83ad575c..f2c7f05c 100644
--- a/plugins/check_smtp.c
+++ b/plugins/check_smtp.c
@@ -28,20 +28,24 @@
*
*****************************************************************************/
-const char *progname = "check_smtp";
-const char *copyright = "2000-2024";
-const char *email = "devel at monitoring-plugins.org";
-
#include "common.h"
#include "netutils.h"
+#include "output.h"
+#include "perfdata.h"
+#include "thresholds.h"
#include "utils.h"
#include "base64.h"
#include "regex.h"
#include <ctype.h>
+#include <string.h>
#include "check_smtp.d/config.h"
#include "../lib/states.h"
+const char *progname = "check_smtp";
+const char *copyright = "2000-2024";
+const char *email = "devel at monitoring-plugins.org";
+
#define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n"
#define SMTP_HELO "HELO "
#define SMTP_EHLO "EHLO "
@@ -161,323 +165,414 @@ int main(int argc, char **argv) {
gettimeofday(&start_time, NULL);
int socket_descriptor = 0;
+
/* try to connect to the host at the given port number */
- mp_state_enum result =
+ mp_state_enum tcp_result =
my_tcp_connect(config.server_address, config.server_port, &socket_descriptor);
- char *error_msg = "";
+ mp_check overall = mp_check_init();
+ mp_subcheck sc_tcp_connect = mp_subcheck_init();
char buffer[MAX_INPUT_BUFFER];
bool ssl_established = false;
- if (result == STATE_OK) { /* we connected */
- /* If requested, send PROXY header */
- if (config.use_proxy_prefix) {
- if (verbose) {
- printf("Sending header %s\n", PROXY_PREFIX);
- }
- my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established);
+
+ if (tcp_result != STATE_OK) {
+ // Connect failed
+ sc_tcp_connect = mp_set_subcheck_state(sc_tcp_connect, STATE_CRITICAL);
+ xasprintf(&sc_tcp_connect.output, "TCP connect to '%s' failed", config.server_address);
+ mp_add_subcheck_to_check(&overall, sc_tcp_connect);
+ mp_exit(overall);
+ }
+
+ /* we connected */
+ /* If requested, send PROXY header */
+ if (config.use_proxy_prefix) {
+ if (verbose) {
+ printf("Sending header %s\n", PROXY_PREFIX);
}
+ my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established);
+ }
#ifdef HAVE_SSL
- if (config.use_ssl) {
- result = np_net_ssl_init_with_hostname(socket_descriptor,
- (config.use_sni ? config.server_address : NULL));
- if (result != STATE_OK) {
- printf(_("CRITICAL - Cannot create SSL context.\n"));
- close(socket_descriptor);
- np_net_ssl_cleanup();
- exit(STATE_CRITICAL);
- }
- ssl_established = true;
+ if (config.use_ssl) {
+ int tls_result = np_net_ssl_init_with_hostname(
+ socket_descriptor, (config.use_sni ? config.server_address : NULL));
+
+ mp_subcheck sc_tls_connection = mp_subcheck_init();
+
+ if (tls_result != STATE_OK) {
+ close(socket_descriptor);
+ np_net_ssl_cleanup();
+
+ sc_tls_connection = mp_set_subcheck_state(sc_tls_connection, STATE_CRITICAL);
+ xasprintf(&sc_tls_connection.output, "cannot create TLS context");
+ mp_add_subcheck_to_check(&overall, sc_tls_connection);
+ mp_exit(overall);
}
+
+ sc_tls_connection = mp_set_subcheck_state(sc_tls_connection, STATE_OK);
+ xasprintf(&sc_tls_connection.output, "TLS context established");
+ mp_add_subcheck_to_check(&overall, sc_tls_connection);
+ ssl_established = true;
+ }
#endif
- /* watch for the SMTP connection string and */
- /* return a WARNING status if we couldn't read any data */
- if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
- printf(_("recv() failed\n"));
- exit(STATE_WARNING);
+ /* watch for the SMTP connection string and */
+ /* return a WARNING status if we couldn't read any data */
+ if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
+ mp_subcheck sc_read_data = mp_subcheck_init();
+ sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING);
+ xasprintf(&sc_read_data.output, "recv() failed");
+ mp_add_subcheck_to_check(&overall, sc_read_data);
+ mp_exit(overall);
+ }
+
+ char *server_response = NULL;
+ /* save connect return (220 hostname ..) for later use */
+ xasprintf(&server_response, "%s", buffer);
+
+ /* send the HELO/EHLO command */
+ my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established);
+
+ /* allow for response to helo command to reach us */
+ if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
+ mp_subcheck sc_read_data = mp_subcheck_init();
+ sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING);
+ xasprintf(&sc_read_data.output, "recv() failed");
+ mp_add_subcheck_to_check(&overall, sc_read_data);
+ mp_exit(overall);
+ }
+
+ bool supports_tls = false;
+ if (config.use_ehlo || config.use_lhlo) {
+ if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) {
+ supports_tls = true;
}
+ }
- char *server_response = NULL;
- /* save connect return (220 hostname ..) for later use */
- xasprintf(&server_response, "%s", buffer);
+ if (config.use_starttls && !supports_tls) {
+ smtp_quit(config, buffer, socket_descriptor, ssl_established);
- /* send the HELO/EHLO command */
- my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established);
+ mp_subcheck sc_read_data = mp_subcheck_init();
+ sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING);
+ xasprintf(&sc_read_data.output, "StartTLS not supported by server");
+ mp_add_subcheck_to_check(&overall, sc_read_data);
+ mp_exit(overall);
+ }
+
+#ifdef HAVE_SSL
+ if (config.use_starttls) {
+ /* send the STARTTLS command */
+ send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0);
+
+ mp_subcheck sc_starttls_init = mp_subcheck_init();
+ recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
+ ssl_established); /* wait for it */
+ if (!strstr(buffer, SMTP_EXPECT)) {
+ smtp_quit(config, buffer, socket_descriptor, ssl_established);
+
+ xasprintf(&sc_starttls_init.output, "StartTLS not supported by server");
+ sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_UNKNOWN);
+ mp_add_subcheck_to_check(&overall, sc_starttls_init);
+ mp_exit(overall);
+ }
+
+ mp_state_enum starttls_result = np_net_ssl_init_with_hostname(
+ socket_descriptor, (config.use_sni ? config.server_address : NULL));
+ if (starttls_result != STATE_OK) {
+ close(socket_descriptor);
+ np_net_ssl_cleanup();
+
+ sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_CRITICAL);
+ xasprintf(&sc_starttls_init.output, "failed to create StartTLS context");
+ mp_add_subcheck_to_check(&overall, sc_starttls_init);
+ mp_exit(overall);
+ }
+ sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_OK);
+ xasprintf(&sc_starttls_init.output, "created StartTLS context");
+ mp_add_subcheck_to_check(&overall, sc_starttls_init);
+
+ ssl_established = true;
+
+ /*
+ * Resend the EHLO command.
+ *
+ * RFC 3207 (4.2) says: ``The client MUST discard any knowledge
+ * obtained from the server, such as the list of SMTP service
+ * extensions, which was not obtained from the TLS negotiation
+ * itself. The client SHOULD send an EHLO command as the first
+ * command after a successful TLS negotiation.'' For this
+ * reason, some MTAs will not allow an AUTH LOGIN command before
+ * we resent EHLO via TLS.
+ */
+ if (my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established) <=
+ 0) {
+ my_close(socket_descriptor);
+
+ mp_subcheck sc_ehlo = mp_subcheck_init();
+ sc_ehlo = mp_set_subcheck_state(sc_ehlo, STATE_UNKNOWN);
+ xasprintf(&sc_ehlo.output, "cannot send EHLO command via StartTLS");
+ mp_add_subcheck_to_check(&overall, sc_ehlo);
+ mp_exit(overall);
+ }
+
+ if (verbose) {
+ printf(_("sent %s"), helocmd);
+ }
- /* allow for response to helo command to reach us */
if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
- printf(_("recv() failed\n"));
- exit(STATE_WARNING);
+ my_close(socket_descriptor);
+
+ mp_subcheck sc_ehlo = mp_subcheck_init();
+ sc_ehlo = mp_set_subcheck_state(sc_ehlo, STATE_UNKNOWN);
+ xasprintf(&sc_ehlo.output, "cannot read EHLO response via StartTLS");
+ mp_add_subcheck_to_check(&overall, sc_ehlo);
+ mp_exit(overall);
}
- bool supports_tls = false;
- if (config.use_ehlo || config.use_lhlo) {
- if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) {
- supports_tls = true;
- }
+ if (verbose) {
+ printf("%s", buffer);
}
+ }
- if (config.use_starttls && !supports_tls) {
- printf(_("WARNING - TLS not supported by server\n"));
+# ifdef USE_OPENSSL
+ if (ssl_established) {
+ net_ssl_check_cert_result cert_check_result =
+ np_net_ssl_check_cert2(config.days_till_exp_warn, config.days_till_exp_crit);
+
+ mp_subcheck sc_cert_check = mp_subcheck_init();
+
+ switch (cert_check_result.errors) {
+ case ALL_OK: {
+ xasprintf(&sc_cert_check.output, "Certificate expiration. Remaining time %g days",
+ cert_check_result.remaining_seconds / 86400);
+ sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
+ } break;
+ case NO_SERVER_CERTIFICATE_PRESENT: {
+ xasprintf(&sc_cert_check.output, "no server certificate present");
+ sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
+ } break;
+ case UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT: {
+ xasprintf(&sc_cert_check.output, "can not retrieve certificate subject");
+ sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
+ } break;
+ case WRONG_TIME_FORMAT_IN_CERTIFICATE: {
+ xasprintf(&sc_cert_check.output, "wrong time format in certificate");
+ sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
+ } break;
+ };
+
+ mp_add_subcheck_to_check(&overall, sc_cert_check);
+
+ if (config.check_cert) {
smtp_quit(config, buffer, socket_descriptor, ssl_established);
- exit(STATE_WARNING);
+ my_close(socket_descriptor);
+ mp_exit(overall);
}
+ }
+# endif /* USE_OPENSSL */
-#ifdef HAVE_SSL
- if (config.use_starttls) {
- /* send the STARTTLS command */
- send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0);
-
- recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
- ssl_established); /* wait for it */
- if (!strstr(buffer, SMTP_EXPECT)) {
- printf(_("Server does not support STARTTLS\n"));
- smtp_quit(config, buffer, socket_descriptor, ssl_established);
- exit(STATE_UNKNOWN);
- }
+#endif
- result = np_net_ssl_init_with_hostname(socket_descriptor,
- (config.use_sni ? config.server_address : NULL));
- if (result != STATE_OK) {
- printf(_("CRITICAL - Cannot create SSL context.\n"));
- close(socket_descriptor);
- np_net_ssl_cleanup();
- exit(STATE_CRITICAL);
- }
+ if (verbose) {
+ printf("%s", buffer);
+ }
- ssl_established = true;
-
- /*
- * Resend the EHLO command.
- *
- * RFC 3207 (4.2) says: ``The client MUST discard any knowledge
- * obtained from the server, such as the list of SMTP service
- * extensions, which was not obtained from the TLS negotiation
- * itself. The client SHOULD send an EHLO command as the first
- * command after a successful TLS negotiation.'' For this
- * reason, some MTAs will not allow an AUTH LOGIN command before
- * we resent EHLO via TLS.
- */
- if (my_send(config, helocmd, strlen(helocmd), socket_descriptor, ssl_established) <=
- 0) {
- printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS."));
- my_close(socket_descriptor);
- exit(STATE_UNKNOWN);
- }
+ /* save buffer for later use */
+ xasprintf(&server_response, "%s%s", server_response, buffer);
+ /* strip the buffer of carriage returns */
+ strip(server_response);
- if (verbose) {
- printf(_("sent %s"), helocmd);
- }
+ /* make sure we find the droids we are looking for */
+ mp_subcheck sc_expect_response = mp_subcheck_init();
- if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <=
- 0) {
- printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS."));
- my_close(socket_descriptor);
- exit(STATE_UNKNOWN);
- }
+ if (!strstr(server_response, config.server_expect)) {
+ sc_expect_response = mp_set_subcheck_state(sc_expect_response, STATE_WARNING);
+ if (config.server_port == SMTP_PORT) {
+ xasprintf(&sc_expect_response.output, _("invalid SMTP response received from host: %s"),
+ server_response);
+ } else {
+ xasprintf(&sc_expect_response.output,
+ _("invalid SMTP response received from host on port %d: %s"),
+ config.server_port, server_response);
+ }
+ exit(STATE_WARNING);
+ } else {
+ xasprintf(&sc_expect_response.output, "received valid SMTP response '%s' from host: '%s'",
+ config.server_expect, server_response);
+ sc_expect_response = mp_set_subcheck_state(sc_expect_response, STATE_OK);
+ }
- if (verbose) {
- printf("%s", buffer);
- }
+ mp_add_subcheck_to_check(&overall, sc_expect_response);
-# ifdef USE_OPENSSL
- if (config.check_cert) {
- result =
- np_net_ssl_check_cert(config.days_till_exp_warn, config.days_till_exp_crit);
- smtp_quit(config, buffer, socket_descriptor, ssl_established);
- my_close(socket_descriptor);
- exit(result);
- }
-# endif /* USE_OPENSSL */
+ if (config.send_mail_from) {
+ my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
+ if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 1 &&
+ verbose) {
+ printf("%s", buffer);
}
-#endif
+ }
- if (verbose) {
+ size_t counter = 0;
+ while (counter < config.ncommands) {
+ xasprintf(&cmd_str, "%s%s", config.commands[counter], "\r\n");
+ my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
+ if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 1 &&
+ verbose) {
printf("%s", buffer);
}
- /* save buffer for later use */
- xasprintf(&server_response, "%s%s", server_response, buffer);
- /* strip the buffer of carriage returns */
- strip(server_response);
+ strip(buffer);
- /* make sure we find the droids we are looking for */
- if (!strstr(server_response, config.server_expect)) {
- if (config.server_port == SMTP_PORT) {
- printf(_("Invalid SMTP response received from host: %s\n"), server_response);
- } else {
- printf(_("Invalid SMTP response received from host on port %d: %s\n"),
- config.server_port, server_response);
+ if (counter < config.nresponses) {
+ int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
+ regex_t preg;
+ int errcode = regcomp(&preg, config.responses[counter], cflags);
+ char errbuf[MAX_INPUT_BUFFER];
+ if (errcode != 0) {
+ regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
+ printf(_("Could Not Compile Regular Expression"));
+ exit(STATE_UNKNOWN);
}
- exit(STATE_WARNING);
- }
- if (config.send_mail_from) {
- my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
- if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >=
- 1 &&
- verbose) {
- printf("%s", buffer);
+ regmatch_t pmatch[10];
+ int eflags = 0;
+ int excode = regexec(&preg, buffer, 10, pmatch, eflags);
+ mp_subcheck sc_expected_responses = mp_subcheck_init();
+ if (excode == 0) {
+ xasprintf(&sc_expected_responses.output, "valid response '%s' to command '%s'",
+ buffer, config.commands[counter]);
+ sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_OK);
+ } else if (excode == REG_NOMATCH) {
+ sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_WARNING);
+ xasprintf(&sc_expected_responses.output, "invalid response '%s' to command '%s'",
+ buffer, config.commands[counter]);
+ } else {
+ regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
+ xasprintf(&sc_expected_responses.output, "regexec execute error: %s", errbuf);
+ sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_UNKNOWN);
}
}
+ counter++;
+ }
- int counter = 0;
- while (counter < config.ncommands) {
- xasprintf(&cmd_str, "%s%s", config.commands[counter], "\r\n");
- my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
- if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >=
- 1 &&
- verbose) {
- printf("%s", buffer);
- }
- strip(buffer);
- if (counter < config.nresponses) {
- int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
- regex_t preg;
- int errcode = regcomp(&preg, config.responses[counter], cflags);
- char errbuf[MAX_INPUT_BUFFER];
- if (errcode != 0) {
- regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
- printf(_("Could Not Compile Regular Expression"));
- exit(STATE_UNKNOWN);
+ if (config.authtype != NULL) {
+ mp_subcheck sc_auth = mp_subcheck_init();
+
+ if (strcmp(config.authtype, "LOGIN") == 0) {
+ char *abuf;
+ int ret;
+ do {
+ /* send AUTH LOGIN */
+ my_send(config, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), socket_descriptor,
+ ssl_established);
+
+ if (verbose) {
+ printf(_("sent %s\n"), "AUTH LOGIN");
}
- regmatch_t pmatch[10];
- int eflags = 0;
- int excode = regexec(&preg, buffer, 10, pmatch, eflags);
- if (excode == 0) {
- result = STATE_OK;
- } else if (excode == REG_NOMATCH) {
- result = STATE_WARNING;
- printf(_("SMTP %s - Invalid response '%s' to command '%s'\n"),
- state_text(result), buffer, config.commands[counter]);
- } else {
- regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
- printf(_("Execute Error: %s\n"), errbuf);
- result = STATE_UNKNOWN;
+ if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
+ ssl_established)) <= 0) {
+ xasprintf(&sc_auth.output, _("recv() failed after AUTH LOGIN"));
+ sc_auth = mp_set_subcheck_state(sc_auth, STATE_WARNING);
+ break;
}
- }
- counter++;
- }
- if (config.authtype != NULL) {
- if (strcmp(config.authtype, "LOGIN") == 0) {
- char *abuf;
- int ret;
- do {
- if (config.authuser == NULL) {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("no authuser specified, "));
- break;
- }
- if (config.authpass == NULL) {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("no authpass specified, "));
- break;
- }
-
- /* send AUTH LOGIN */
- my_send(config, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), socket_descriptor,
- ssl_established);
- if (verbose) {
- printf(_("sent %s\n"), "AUTH LOGIN");
- }
-
- if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
- ssl_established)) <= 0) {
- xasprintf(&error_msg, _("recv() failed after AUTH LOGIN, "));
- result = STATE_WARNING;
- break;
- }
- if (verbose) {
- printf(_("received %s\n"), buffer);
- }
-
- if (strncmp(buffer, "334", 3) != 0) {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("invalid response received after AUTH LOGIN, "));
- break;
- }
-
- /* encode authuser with base64 */
- base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf);
- xasprintf(&abuf, "%s\r\n", abuf);
- my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
- if (verbose) {
- printf(_("sent %s\n"), abuf);
- }
-
- if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
- ssl_established)) <= 0) {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("recv() failed after sending authuser, "));
- break;
- }
- if (verbose) {
- printf(_("received %s\n"), buffer);
- }
- if (strncmp(buffer, "334", 3) != 0) {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("invalid response received after authuser, "));
- break;
- }
- /* encode authpass with base64 */
- base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf);
- xasprintf(&abuf, "%s\r\n", abuf);
- my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
- if (verbose) {
- printf(_("sent %s\n"), abuf);
- }
- if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
- ssl_established)) <= 0) {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("recv() failed after sending authpass, "));
- break;
- }
- if (verbose) {
- printf(_("received %s\n"), buffer);
- }
- if (strncmp(buffer, "235", 3) != 0) {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("invalid response received after authpass, "));
- break;
- }
+ if (verbose) {
+ printf(_("received %s\n"), buffer);
+ }
+
+ if (strncmp(buffer, "334", 3) != 0) {
+ xasprintf(&sc_auth.output, "invalid response received after AUTH LOGIN");
+ sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
break;
- } while (false);
- } else {
- result = STATE_CRITICAL;
- xasprintf(&error_msg, _("only authtype LOGIN is supported, "));
- }
- }
+ }
- /* tell the server we're done */
- smtp_quit(config, buffer, socket_descriptor, ssl_established);
+ /* encode authuser with base64 */
+ base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf);
+ xasprintf(&abuf, "%s\r\n", abuf);
+ my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
+ if (verbose) {
+ printf(_("sent %s\n"), abuf);
+ }
- /* finally close the connection */
- close(socket_descriptor);
+ if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
+ ssl_established)) <= 0) {
+ xasprintf(&sc_auth.output, "recv() failed after sending authuser");
+ sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
+ break;
+ }
+
+ if (verbose) {
+ printf(_("received %s\n"), buffer);
+ }
+
+ if (strncmp(buffer, "334", 3) != 0) {
+ xasprintf(&sc_auth.output, "invalid response received after authuser");
+ sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
+ break;
+ }
+
+ /* encode authpass with base64 */
+ base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf);
+ xasprintf(&abuf, "%s\r\n", abuf);
+ my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
+
+ if (verbose) {
+ printf(_("sent %s\n"), abuf);
+ }
+
+ if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
+ ssl_established)) <= 0) {
+ xasprintf(&sc_auth.output, "recv() failed after sending authpass");
+ sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
+ break;
+ }
+
+ if (verbose) {
+ printf(_("received %s\n"), buffer);
+ }
+
+ if (strncmp(buffer, "235", 3) != 0) {
+ xasprintf(&sc_auth.output, "invalid response received after authpass");
+ sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
+ break;
+ }
+ break;
+ } while (false);
+ } else {
+ sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
+ xasprintf(&sc_auth.output, "only authtype LOGIN is supported");
+ }
+
+ mp_add_subcheck_to_check(&overall, sc_auth);
}
+ /* tell the server we're done */
+ smtp_quit(config, buffer, socket_descriptor, ssl_established);
+
+ /* finally close the connection */
+ close(socket_descriptor);
+
/* reset the alarm */
alarm(0);
long microsec = deltime(start_time);
double elapsed_time = (double)microsec / 1.0e6;
- if (result == STATE_OK) {
- if (config.check_critical_time && elapsed_time > config.critical_time) {
- result = STATE_CRITICAL;
- } else if (config.check_warning_time && elapsed_time > config.warning_time) {
- result = STATE_WARNING;
- }
- }
+ mp_perfdata pd_elapsed_time = perfdata_init();
+ pd_elapsed_time = mp_set_pd_value(pd_elapsed_time, elapsed_time);
+ pd_elapsed_time.label = "time";
+ pd_elapsed_time.uom = "s";
- printf(_("SMTP %s - %s%.3f sec. response time%s%s|%s\n"), state_text(result), error_msg,
- elapsed_time, verbose ? ", " : "", verbose ? buffer : "",
- fperfdata("time", elapsed_time, "s", config.check_warning_time, config.warning_time,
- config.check_critical_time, config.critical_time, true, 0, false, 0));
+ pd_elapsed_time = mp_pd_set_thresholds(pd_elapsed_time, config.connection_time);
- exit(result);
+ mp_subcheck sc_connection_time = mp_subcheck_init();
+ xasprintf(&sc_connection_time.output, "connection time: %.3gs", elapsed_time);
+ sc_connection_time =
+ mp_set_subcheck_state(sc_connection_time, mp_get_pd_status(pd_elapsed_time));
+ mp_add_subcheck_to_check(&overall, sc_connection_time);
+
+ mp_exit(overall);
}
/* process command-line arguments */
@@ -535,8 +630,8 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
}
}
- int command_size = 0;
- int response_size = 0;
+ unsigned long command_size = 0;
+ unsigned long response_size = 0;
bool implicit_tls = false;
int server_port_option = 0;
while (true) {
@@ -591,7 +686,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
result.config.commands =
realloc(result.config.commands, sizeof(char *) * command_size);
if (result.config.commands == NULL) {
- die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"),
+ die(STATE_UNKNOWN, _("Could not realloc() units [%lu]\n"),
result.config.ncommands);
}
}
@@ -605,7 +700,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
result.config.responses =
realloc(result.config.responses, sizeof(char *) * response_size);
if (result.config.responses == NULL) {
- die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"),
+ die(STATE_UNKNOWN, _("Could not realloc() units [%lu]\n"),
result.config.nresponses);
}
}
@@ -613,22 +708,22 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
strncpy(result.config.responses[result.config.nresponses], optarg, 255);
result.config.nresponses++;
break;
- case 'c': /* critical time threshold */
- if (!is_nonnegative(optarg)) {
- usage4(_("Critical time must be a positive"));
- } else {
- result.config.critical_time = strtod(optarg, NULL);
- result.config.check_critical_time = true;
+ case 'c': /* critical time threshold */ {
+ mp_range_parsed tmp = mp_parse_range_string(optarg);
+ if (tmp.error != MP_PARSING_SUCCES) {
+ die(STATE_UNKNOWN, "failed to parse critical time threshold");
}
- break;
- case 'w': /* warning time threshold */
- if (!is_nonnegative(optarg)) {
- usage4(_("Warning time must be a positive"));
- } else {
- result.config.warning_time = strtod(optarg, NULL);
- result.config.check_warning_time = true;
+ result.config.connection_time =
+ mp_thresholds_set_warn(result.config.connection_time, tmp.range);
+ } break;
+ case 'w': /* warning time threshold */ {
+ mp_range_parsed tmp = mp_parse_range_string(optarg);
+ if (tmp.error != MP_PARSING_SUCCES) {
+ die(STATE_UNKNOWN, "failed to parse warning time threshold");
}
- break;
+ result.config.connection_time =
+ mp_thresholds_set_crit(result.config.connection_time, tmp.range);
+ } break;
case 'v': /* verbose */
verbose++;
break;
@@ -742,6 +837,19 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
result.config.server_port = server_port_option;
}
+ if (result.config.authtype) {
+ if (strcmp(result.config.authtype, "LOGIN") == 0) {
+ if (result.config.authuser == NULL) {
+ usage4("no authuser specified");
+ }
+ if (result.config.authpass == NULL) {
+ usage4("no authpass specified");
+ }
+ } else {
+ usage4("only authtype LOGIN is supported");
+ }
+ }
+
return result;
}
@@ -791,7 +899,7 @@ char *smtp_quit(check_smtp_config config, char buffer[MAX_INPUT_BUFFER], int soc
int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_descriptor,
bool ssl_established) {
int result;
- int counter;
+ size_t counter;
for (counter = result = 0; counter < bufsize - 1; counter++) {
if ((result = my_recv(config, &buf[counter], 1, socket_descriptor, ssl_established)) != 1) {
@@ -799,7 +907,7 @@ int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_des
}
if (buf[counter] == '\n') {
buf[++counter] = '\0';
- return counter;
+ return (int)counter;
}
}
return (result == 1 || counter == 0) ? -2 : result; /* -2 if out of space */
diff --git a/plugins/check_smtp.d/config.h b/plugins/check_smtp.d/config.h
index 0a6511ef..bc433093 100644
--- a/plugins/check_smtp.d/config.h
+++ b/plugins/check_smtp.d/config.h
@@ -1,6 +1,7 @@
#pragma once
#include "../../config.h"
+#include "thresholds.h"
#include <stddef.h>
#include <string.h>
@@ -18,20 +19,18 @@ typedef struct {
char *server_expect;
bool ignore_send_quit_failure;
- double warning_time;
- bool check_warning_time;
- double critical_time;
- bool check_critical_time;
+ mp_thresholds connection_time;
+
bool use_ehlo;
bool use_lhlo;
char *from_arg;
bool send_mail_from;
- int ncommands;
+ unsigned long ncommands;
char **commands;
- int nresponses;
+ unsigned long nresponses;
char **responses;
char *authtype;
@@ -58,10 +57,7 @@ check_smtp_config check_smtp_config_init() {
.server_expect = SMTP_EXPECT,
.ignore_send_quit_failure = false,
- .warning_time = 0,
- .check_warning_time = false,
- .critical_time = 0,
- .check_critical_time = false,
+ .connection_time = mp_thresholds_init(),
.use_ehlo = false,
.use_lhlo = false,
diff --git a/plugins/netutils.h b/plugins/netutils.h
index c4461113..dbd22398 100644
--- a/plugins/netutils.h
+++ b/plugins/netutils.h
@@ -114,6 +114,26 @@ int np_net_ssl_init_with_hostname_version_and_cert(int socket, char *host_name,
void np_net_ssl_cleanup(void);
int np_net_ssl_write(const void *buf, int num);
int np_net_ssl_read(void *buf, int num);
+
+typedef enum {
+ ALL_OK,
+ NO_SERVER_CERTIFICATE_PRESENT,
+ UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT,
+ WRONG_TIME_FORMAT_IN_CERTIFICATE,
+} retrieve_expiration_date_errors;
+
+typedef struct {
+ double remaining_seconds;
+ retrieve_expiration_date_errors errors;
+} retrieve_expiration_time_result;
+
+typedef struct {
+ mp_state_enum result_state;
+ double remaining_seconds;
+ retrieve_expiration_date_errors errors;
+} net_ssl_check_cert_result;
+net_ssl_check_cert_result np_net_ssl_check_cert2(int days_till_exp_warn, int days_till_exp_crit);
+
mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
#endif /* HAVE_SSL */
diff --git a/plugins/sslutils.c b/plugins/sslutils.c
index 0e6d7525..c1d15534 100644
--- a/plugins/sslutils.c
+++ b/plugins/sslutils.c
@@ -312,6 +312,138 @@ mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_
# endif /* USE_OPENSSL */
}
+retrieve_expiration_time_result np_net_ssl_get_cert_expiration(X509 *certificate) {
+# ifdef USE_OPENSSL
+ retrieve_expiration_time_result result = {
+ .errors = ALL_OK,
+ .remaining_seconds = {},
+ };
+
+ if (!certificate) {
+ // printf("%s\n", _("CRITICAL - No server certificate present to inspect."));
+ result.errors = NO_SERVER_CERTIFICATE_PRESENT;
+ return result;
+ }
+
+ /* Extract CN from certificate subject */
+ X509_NAME *subj = X509_get_subject_name(certificate);
+
+ if (!subj) {
+ // printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject."));
+ result.errors = UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT;
+ return result;
+ }
+
+ char cn[MAX_CN_LENGTH] = "";
+ int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn));
+ if (cnlen == -1) {
+ strcpy(cn, _("Unknown CN"));
+ }
+
+ /* Retrieve timestamp of certificate */
+ ASN1_STRING *expiration_timestamp = X509_get_notAfter(certificate);
+
+ int offset = 0;
+ struct tm stamp = {};
+ /* Generate tm structure to process timestamp */
+ if (expiration_timestamp->type == V_ASN1_UTCTIME) {
+ if (expiration_timestamp->length < 10) {
+ result.errors = WRONG_TIME_FORMAT_IN_CERTIFICATE;
+ return result;
+ }
+
+ stamp.tm_year =
+ (expiration_timestamp->data[0] - '0') * 10 + (expiration_timestamp->data[1] - '0');
+ if (stamp.tm_year < 50) {
+ stamp.tm_year += 100;
+ }
+ offset = 0;
+ } else {
+ if (expiration_timestamp->length < 12) {
+ result.errors = WRONG_TIME_FORMAT_IN_CERTIFICATE;
+ return result;
+ }
+
+ stamp.tm_year = (expiration_timestamp->data[0] - '0') * 1000 +
+ (expiration_timestamp->data[1] - '0') * 100 +
+ (expiration_timestamp->data[2] - '0') * 10 +
+ (expiration_timestamp->data[3] - '0');
+ stamp.tm_year -= 1900;
+ offset = 2;
+ }
+ stamp.tm_mon = (expiration_timestamp->data[2 + offset] - '0') * 10 +
+ (expiration_timestamp->data[3 + offset] - '0') - 1;
+ stamp.tm_mday = (expiration_timestamp->data[4 + offset] - '0') * 10 +
+ (expiration_timestamp->data[5 + offset] - '0');
+ stamp.tm_hour = (expiration_timestamp->data[6 + offset] - '0') * 10 +
+ (expiration_timestamp->data[7 + offset] - '0');
+ stamp.tm_min = (expiration_timestamp->data[8 + offset] - '0') * 10 +
+ (expiration_timestamp->data[9 + offset] - '0');
+ stamp.tm_sec = (expiration_timestamp->data[10 + offset] - '0') * 10 +
+ (expiration_timestamp->data[11 + offset] - '0');
+ stamp.tm_isdst = -1;
+
+ time_t tm_t = timegm(&stamp);
+ double time_left = difftime(tm_t, time(NULL));
+ result.remaining_seconds = time_left;
+
+ char *timezone = getenv("TZ");
+ setenv("TZ", "GMT", 1);
+ tzset();
+
+ char timestamp[50] = "";
+ strftime(timestamp, 50, "%c %z", localtime(&tm_t));
+ if (timezone) {
+ setenv("TZ", timezone, 1);
+ } else {
+ unsetenv("TZ");
+ }
+
+ tzset();
+
+ X509_free(certificate);
+
+ return result;
+# else /* ifndef USE_OPENSSL */
+ printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
+ return STATE_WARNING;
+# endif /* USE_OPENSSL */
+}
+
+net_ssl_check_cert_result np_net_ssl_check_cert2(int days_till_exp_warn, int days_till_exp_crit) {
+# ifdef USE_OPENSSL
+ X509 *certificate = NULL;
+ certificate = SSL_get_peer_certificate(s);
+
+ retrieve_expiration_time_result expiration_date = np_net_ssl_get_cert_expiration(certificate);
+
+ net_ssl_check_cert_result result = {
+ .result_state = STATE_UNKNOWN,
+ .remaining_seconds = expiration_date.remaining_seconds,
+ .errors = expiration_date.errors,
+ };
+
+ if (expiration_date.errors == ALL_OK) {
+ // got a valid expiration date
+ unsigned int remaining_days = result.remaining_seconds / 86400;
+
+ if (remaining_days < days_till_exp_crit) {
+ result.result_state = STATE_CRITICAL;
+ } else if (remaining_days < days_till_exp_warn) {
+ result.result_state = STATE_WARNING;
+ } else {
+ result.result_state = STATE_OK;
+ }
+ }
+
+ return result;
+
+# else /* ifndef USE_OPENSSL */
+ printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
+ return STATE_WARNING;
+# endif /* USE_OPENSSL */
+}
+
mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) {
# ifdef USE_OPENSSL
X509 *certificate = NULL;
More information about the Commits
mailing list