Module: monitoring-plugins
 Branch: master
 Commit: 6bc9e518b247e85a39479a0ac6685e68c3a61b40
 Author: Lorenz Kästle <[email protected]>
   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 = "[email protected]";
-
 #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 = "[email protected]";
+
 #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;


Reply via email to