summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/check_smtp.c676
-rw-r--r--plugins/check_smtp.d/config.h16
-rw-r--r--plugins/netutils.h20
-rw-r--r--plugins/sslutils.c132
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 @@
28 * 28 *
29 *****************************************************************************/ 29 *****************************************************************************/
30 30
31const char *progname = "check_smtp";
32const char *copyright = "2000-2024";
33const char *email = "devel@monitoring-plugins.org";
34
35#include "common.h" 31#include "common.h"
36#include "netutils.h" 32#include "netutils.h"
33#include "output.h"
34#include "perfdata.h"
35#include "thresholds.h"
37#include "utils.h" 36#include "utils.h"
38#include "base64.h" 37#include "base64.h"
39#include "regex.h" 38#include "regex.h"
40 39
41#include <ctype.h> 40#include <ctype.h>
41#include <string.h>
42#include "check_smtp.d/config.h" 42#include "check_smtp.d/config.h"
43#include "../lib/states.h" 43#include "../lib/states.h"
44 44
45const char *progname = "check_smtp";
46const char *copyright = "2000-2024";
47const char *email = "devel@monitoring-plugins.org";
48
45#define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n" 49#define PROXY_PREFIX "PROXY TCP4 0.0.0.0 0.0.0.0 25 25\r\n"
46#define SMTP_HELO "HELO " 50#define SMTP_HELO "HELO "
47#define SMTP_EHLO "EHLO " 51#define SMTP_EHLO "EHLO "
@@ -161,323 +165,414 @@ int main(int argc, char **argv) {
161 gettimeofday(&start_time, NULL); 165 gettimeofday(&start_time, NULL);
162 166
163 int socket_descriptor = 0; 167 int socket_descriptor = 0;
168
164 /* try to connect to the host at the given port number */ 169 /* try to connect to the host at the given port number */
165 mp_state_enum result = 170 mp_state_enum tcp_result =
166 my_tcp_connect(config.server_address, config.server_port, &socket_descriptor); 171 my_tcp_connect(config.server_address, config.server_port, &socket_descriptor);
167 172
168 char *error_msg = ""; 173 mp_check overall = mp_check_init();
174 mp_subcheck sc_tcp_connect = mp_subcheck_init();
169 char buffer[MAX_INPUT_BUFFER]; 175 char buffer[MAX_INPUT_BUFFER];
170 bool ssl_established = false; 176 bool ssl_established = false;
171 if (result == STATE_OK) { /* we connected */ 177
172 /* If requested, send PROXY header */ 178 if (tcp_result != STATE_OK) {
173 if (config.use_proxy_prefix) { 179 // Connect failed
174 if (verbose) { 180 sc_tcp_connect = mp_set_subcheck_state(sc_tcp_connect, STATE_CRITICAL);
175 printf("Sending header %s\n", PROXY_PREFIX); 181 xasprintf(&sc_tcp_connect.output, "TCP connect to '%s' failed", config.server_address);
176 } 182 mp_add_subcheck_to_check(&overall, sc_tcp_connect);
177 my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established); 183 mp_exit(overall);
184 }
185
186 /* we connected */
187 /* If requested, send PROXY header */
188 if (config.use_proxy_prefix) {
189 if (verbose) {
190 printf("Sending header %s\n", PROXY_PREFIX);
178 } 191 }
192 my_send(config, PROXY_PREFIX, strlen(PROXY_PREFIX), socket_descriptor, ssl_established);
193 }
179 194
180#ifdef HAVE_SSL 195#ifdef HAVE_SSL
181 if (config.use_ssl) { 196 if (config.use_ssl) {
182 result = np_net_ssl_init_with_hostname(socket_descriptor, 197 int tls_result = np_net_ssl_init_with_hostname(
183 (config.use_sni ? config.server_address : NULL)); 198 socket_descriptor, (config.use_sni ? config.server_address : NULL));
184 if (result != STATE_OK) { 199
185 printf(_("CRITICAL - Cannot create SSL context.\n")); 200 mp_subcheck sc_tls_connection = mp_subcheck_init();
186 close(socket_descriptor); 201
187 np_net_ssl_cleanup(); 202 if (tls_result != STATE_OK) {
188 exit(STATE_CRITICAL); 203 close(socket_descriptor);
189 } 204 np_net_ssl_cleanup();
190 ssl_established = true; 205
206 sc_tls_connection = mp_set_subcheck_state(sc_tls_connection, STATE_CRITICAL);
207 xasprintf(&sc_tls_connection.output, "cannot create TLS context");
208 mp_add_subcheck_to_check(&overall, sc_tls_connection);
209 mp_exit(overall);
191 } 210 }
211
212 sc_tls_connection = mp_set_subcheck_state(sc_tls_connection, STATE_OK);
213 xasprintf(&sc_tls_connection.output, "TLS context established");
214 mp_add_subcheck_to_check(&overall, sc_tls_connection);
215 ssl_established = true;
216 }
192#endif 217#endif
193 218
194 /* watch for the SMTP connection string and */ 219 /* watch for the SMTP connection string and */
195 /* return a WARNING status if we couldn't read any data */ 220 /* return a WARNING status if we couldn't read any data */
196 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) { 221 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
197 printf(_("recv() failed\n")); 222 mp_subcheck sc_read_data = mp_subcheck_init();
198 exit(STATE_WARNING); 223 sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING);
224 xasprintf(&sc_read_data.output, "recv() failed");
225 mp_add_subcheck_to_check(&overall, sc_read_data);
226 mp_exit(overall);
227 }
228
229 char *server_response = NULL;
230 /* save connect return (220 hostname ..) for later use */
231 xasprintf(&server_response, "%s", buffer);
232
233 /* send the HELO/EHLO command */
234 my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established);
235
236 /* allow for response to helo command to reach us */
237 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
238 mp_subcheck sc_read_data = mp_subcheck_init();
239 sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING);
240 xasprintf(&sc_read_data.output, "recv() failed");
241 mp_add_subcheck_to_check(&overall, sc_read_data);
242 mp_exit(overall);
243 }
244
245 bool supports_tls = false;
246 if (config.use_ehlo || config.use_lhlo) {
247 if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) {
248 supports_tls = true;
199 } 249 }
250 }
200 251
201 char *server_response = NULL; 252 if (config.use_starttls && !supports_tls) {
202 /* save connect return (220 hostname ..) for later use */ 253 smtp_quit(config, buffer, socket_descriptor, ssl_established);
203 xasprintf(&server_response, "%s", buffer);
204 254
205 /* send the HELO/EHLO command */ 255 mp_subcheck sc_read_data = mp_subcheck_init();
206 my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established); 256 sc_read_data = mp_set_subcheck_state(sc_read_data, STATE_WARNING);
257 xasprintf(&sc_read_data.output, "StartTLS not supported by server");
258 mp_add_subcheck_to_check(&overall, sc_read_data);
259 mp_exit(overall);
260 }
261
262#ifdef HAVE_SSL
263 if (config.use_starttls) {
264 /* send the STARTTLS command */
265 send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0);
266
267 mp_subcheck sc_starttls_init = mp_subcheck_init();
268 recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
269 ssl_established); /* wait for it */
270 if (!strstr(buffer, SMTP_EXPECT)) {
271 smtp_quit(config, buffer, socket_descriptor, ssl_established);
272
273 xasprintf(&sc_starttls_init.output, "StartTLS not supported by server");
274 sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_UNKNOWN);
275 mp_add_subcheck_to_check(&overall, sc_starttls_init);
276 mp_exit(overall);
277 }
278
279 mp_state_enum starttls_result = np_net_ssl_init_with_hostname(
280 socket_descriptor, (config.use_sni ? config.server_address : NULL));
281 if (starttls_result != STATE_OK) {
282 close(socket_descriptor);
283 np_net_ssl_cleanup();
284
285 sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_CRITICAL);
286 xasprintf(&sc_starttls_init.output, "failed to create StartTLS context");
287 mp_add_subcheck_to_check(&overall, sc_starttls_init);
288 mp_exit(overall);
289 }
290 sc_starttls_init = mp_set_subcheck_state(sc_starttls_init, STATE_OK);
291 xasprintf(&sc_starttls_init.output, "created StartTLS context");
292 mp_add_subcheck_to_check(&overall, sc_starttls_init);
293
294 ssl_established = true;
295
296 /*
297 * Resend the EHLO command.
298 *
299 * RFC 3207 (4.2) says: ``The client MUST discard any knowledge
300 * obtained from the server, such as the list of SMTP service
301 * extensions, which was not obtained from the TLS negotiation
302 * itself. The client SHOULD send an EHLO command as the first
303 * command after a successful TLS negotiation.'' For this
304 * reason, some MTAs will not allow an AUTH LOGIN command before
305 * we resent EHLO via TLS.
306 */
307 if (my_send(config, helocmd, (int)strlen(helocmd), socket_descriptor, ssl_established) <=
308 0) {
309 my_close(socket_descriptor);
310
311 mp_subcheck sc_ehlo = mp_subcheck_init();
312 sc_ehlo = mp_set_subcheck_state(sc_ehlo, STATE_UNKNOWN);
313 xasprintf(&sc_ehlo.output, "cannot send EHLO command via StartTLS");
314 mp_add_subcheck_to_check(&overall, sc_ehlo);
315 mp_exit(overall);
316 }
317
318 if (verbose) {
319 printf(_("sent %s"), helocmd);
320 }
207 321
208 /* allow for response to helo command to reach us */
209 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) { 322 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 0) {
210 printf(_("recv() failed\n")); 323 my_close(socket_descriptor);
211 exit(STATE_WARNING); 324
325 mp_subcheck sc_ehlo = mp_subcheck_init();
326 sc_ehlo = mp_set_subcheck_state(sc_ehlo, STATE_UNKNOWN);
327 xasprintf(&sc_ehlo.output, "cannot read EHLO response via StartTLS");
328 mp_add_subcheck_to_check(&overall, sc_ehlo);
329 mp_exit(overall);
212 } 330 }
213 331
214 bool supports_tls = false; 332 if (verbose) {
215 if (config.use_ehlo || config.use_lhlo) { 333 printf("%s", buffer);
216 if (strstr(buffer, "250 STARTTLS") != NULL || strstr(buffer, "250-STARTTLS") != NULL) {
217 supports_tls = true;
218 }
219 } 334 }
335 }
220 336
221 if (config.use_starttls && !supports_tls) { 337# ifdef USE_OPENSSL
222 printf(_("WARNING - TLS not supported by server\n")); 338 if (ssl_established) {
339 net_ssl_check_cert_result cert_check_result =
340 np_net_ssl_check_cert2(config.days_till_exp_warn, config.days_till_exp_crit);
341
342 mp_subcheck sc_cert_check = mp_subcheck_init();
343
344 switch (cert_check_result.errors) {
345 case ALL_OK: {
346 xasprintf(&sc_cert_check.output, "Certificate expiration. Remaining time %g days",
347 cert_check_result.remaining_seconds / 86400);
348 sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
349 } break;
350 case NO_SERVER_CERTIFICATE_PRESENT: {
351 xasprintf(&sc_cert_check.output, "no server certificate present");
352 sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
353 } break;
354 case UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT: {
355 xasprintf(&sc_cert_check.output, "can not retrieve certificate subject");
356 sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
357 } break;
358 case WRONG_TIME_FORMAT_IN_CERTIFICATE: {
359 xasprintf(&sc_cert_check.output, "wrong time format in certificate");
360 sc_cert_check = mp_set_subcheck_state(sc_cert_check, cert_check_result.result_state);
361 } break;
362 };
363
364 mp_add_subcheck_to_check(&overall, sc_cert_check);
365
366 if (config.check_cert) {
223 smtp_quit(config, buffer, socket_descriptor, ssl_established); 367 smtp_quit(config, buffer, socket_descriptor, ssl_established);
224 exit(STATE_WARNING); 368 my_close(socket_descriptor);
369 mp_exit(overall);
225 } 370 }
371 }
372# endif /* USE_OPENSSL */
226 373
227#ifdef HAVE_SSL 374#endif
228 if (config.use_starttls) {
229 /* send the STARTTLS command */
230 send(socket_descriptor, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0);
231
232 recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
233 ssl_established); /* wait for it */
234 if (!strstr(buffer, SMTP_EXPECT)) {
235 printf(_("Server does not support STARTTLS\n"));
236 smtp_quit(config, buffer, socket_descriptor, ssl_established);
237 exit(STATE_UNKNOWN);
238 }
239 375
240 result = np_net_ssl_init_with_hostname(socket_descriptor, 376 if (verbose) {
241 (config.use_sni ? config.server_address : NULL)); 377 printf("%s", buffer);
242 if (result != STATE_OK) { 378 }
243 printf(_("CRITICAL - Cannot create SSL context.\n"));
244 close(socket_descriptor);
245 np_net_ssl_cleanup();
246 exit(STATE_CRITICAL);
247 }
248 379
249 ssl_established = true; 380 /* save buffer for later use */
250 381 xasprintf(&server_response, "%s%s", server_response, buffer);
251 /* 382 /* strip the buffer of carriage returns */
252 * Resend the EHLO command. 383 strip(server_response);
253 *
254 * RFC 3207 (4.2) says: ``The client MUST discard any knowledge
255 * obtained from the server, such as the list of SMTP service
256 * extensions, which was not obtained from the TLS negotiation
257 * itself. The client SHOULD send an EHLO command as the first
258 * command after a successful TLS negotiation.'' For this
259 * reason, some MTAs will not allow an AUTH LOGIN command before
260 * we resent EHLO via TLS.
261 */
262 if (my_send(config, helocmd, strlen(helocmd), socket_descriptor, ssl_established) <=
263 0) {
264 printf("%s\n", _("SMTP UNKNOWN - Cannot send EHLO command via TLS."));
265 my_close(socket_descriptor);
266 exit(STATE_UNKNOWN);
267 }
268 384
269 if (verbose) { 385 /* make sure we find the droids we are looking for */
270 printf(_("sent %s"), helocmd); 386 mp_subcheck sc_expect_response = mp_subcheck_init();
271 }
272 387
273 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) <= 388 if (!strstr(server_response, config.server_expect)) {
274 0) { 389 sc_expect_response = mp_set_subcheck_state(sc_expect_response, STATE_WARNING);
275 printf("%s\n", _("SMTP UNKNOWN - Cannot read EHLO response via TLS.")); 390 if (config.server_port == SMTP_PORT) {
276 my_close(socket_descriptor); 391 xasprintf(&sc_expect_response.output, _("invalid SMTP response received from host: %s"),
277 exit(STATE_UNKNOWN); 392 server_response);
278 } 393 } else {
394 xasprintf(&sc_expect_response.output,
395 _("invalid SMTP response received from host on port %d: %s"),
396 config.server_port, server_response);
397 }
398 exit(STATE_WARNING);
399 } else {
400 xasprintf(&sc_expect_response.output, "received valid SMTP response '%s' from host: '%s'",
401 config.server_expect, server_response);
402 sc_expect_response = mp_set_subcheck_state(sc_expect_response, STATE_OK);
403 }
279 404
280 if (verbose) { 405 mp_add_subcheck_to_check(&overall, sc_expect_response);
281 printf("%s", buffer);
282 }
283 406
284# ifdef USE_OPENSSL 407 if (config.send_mail_from) {
285 if (config.check_cert) { 408 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
286 result = 409 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 1 &&
287 np_net_ssl_check_cert(config.days_till_exp_warn, config.days_till_exp_crit); 410 verbose) {
288 smtp_quit(config, buffer, socket_descriptor, ssl_established); 411 printf("%s", buffer);
289 my_close(socket_descriptor);
290 exit(result);
291 }
292# endif /* USE_OPENSSL */
293 } 412 }
294#endif 413 }
295 414
296 if (verbose) { 415 size_t counter = 0;
416 while (counter < config.ncommands) {
417 xasprintf(&cmd_str, "%s%s", config.commands[counter], "\r\n");
418 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established);
419 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 1 &&
420 verbose) {
297 printf("%s", buffer); 421 printf("%s", buffer);
298 } 422 }
299 423
300 /* save buffer for later use */ 424 strip(buffer);
301 xasprintf(&server_response, "%s%s", server_response, buffer);
302 /* strip the buffer of carriage returns */
303 strip(server_response);
304 425
305 /* make sure we find the droids we are looking for */ 426 if (counter < config.nresponses) {
306 if (!strstr(server_response, config.server_expect)) { 427 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
307 if (config.server_port == SMTP_PORT) { 428 regex_t preg;
308 printf(_("Invalid SMTP response received from host: %s\n"), server_response); 429 int errcode = regcomp(&preg, config.responses[counter], cflags);
309 } else { 430 char errbuf[MAX_INPUT_BUFFER];
310 printf(_("Invalid SMTP response received from host on port %d: %s\n"), 431 if (errcode != 0) {
311 config.server_port, server_response); 432 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
433 printf(_("Could Not Compile Regular Expression"));
434 exit(STATE_UNKNOWN);
312 } 435 }
313 exit(STATE_WARNING);
314 }
315 436
316 if (config.send_mail_from) { 437 regmatch_t pmatch[10];
317 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established); 438 int eflags = 0;
318 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 439 int excode = regexec(&preg, buffer, 10, pmatch, eflags);
319 1 && 440 mp_subcheck sc_expected_responses = mp_subcheck_init();
320 verbose) { 441 if (excode == 0) {
321 printf("%s", buffer); 442 xasprintf(&sc_expected_responses.output, "valid response '%s' to command '%s'",
443 buffer, config.commands[counter]);
444 sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_OK);
445 } else if (excode == REG_NOMATCH) {
446 sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_WARNING);
447 xasprintf(&sc_expected_responses.output, "invalid response '%s' to command '%s'",
448 buffer, config.commands[counter]);
449 } else {
450 regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
451 xasprintf(&sc_expected_responses.output, "regexec execute error: %s", errbuf);
452 sc_expected_responses = mp_set_subcheck_state(sc_expected_responses, STATE_UNKNOWN);
322 } 453 }
323 } 454 }
455 counter++;
456 }
324 457
325 int counter = 0; 458 if (config.authtype != NULL) {
326 while (counter < config.ncommands) { 459 mp_subcheck sc_auth = mp_subcheck_init();
327 xasprintf(&cmd_str, "%s%s", config.commands[counter], "\r\n"); 460
328 my_send(config, cmd_str, (int)strlen(cmd_str), socket_descriptor, ssl_established); 461 if (strcmp(config.authtype, "LOGIN") == 0) {
329 if (recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor, ssl_established) >= 462 char *abuf;
330 1 && 463 int ret;
331 verbose) { 464 do {
332 printf("%s", buffer); 465 /* send AUTH LOGIN */
333 } 466 my_send(config, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), socket_descriptor,
334 strip(buffer); 467 ssl_established);
335 if (counter < config.nresponses) { 468
336 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 469 if (verbose) {
337 regex_t preg; 470 printf(_("sent %s\n"), "AUTH LOGIN");
338 int errcode = regcomp(&preg, config.responses[counter], cflags);
339 char errbuf[MAX_INPUT_BUFFER];
340 if (errcode != 0) {
341 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER);
342 printf(_("Could Not Compile Regular Expression"));
343 exit(STATE_UNKNOWN);
344 } 471 }
345 472
346 regmatch_t pmatch[10]; 473 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
347 int eflags = 0; 474 ssl_established)) <= 0) {
348 int excode = regexec(&preg, buffer, 10, pmatch, eflags); 475 xasprintf(&sc_auth.output, _("recv() failed after AUTH LOGIN"));
349 if (excode == 0) { 476 sc_auth = mp_set_subcheck_state(sc_auth, STATE_WARNING);
350 result = STATE_OK; 477 break;
351 } else if (excode == REG_NOMATCH) {
352 result = STATE_WARNING;
353 printf(_("SMTP %s - Invalid response '%s' to command '%s'\n"),
354 state_text(result), buffer, config.commands[counter]);
355 } else {
356 regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
357 printf(_("Execute Error: %s\n"), errbuf);
358 result = STATE_UNKNOWN;
359 } 478 }
360 }
361 counter++;
362 }
363 479
364 if (config.authtype != NULL) { 480 if (verbose) {
365 if (strcmp(config.authtype, "LOGIN") == 0) { 481 printf(_("received %s\n"), buffer);
366 char *abuf; 482 }
367 int ret; 483
368 do { 484 if (strncmp(buffer, "334", 3) != 0) {
369 if (config.authuser == NULL) { 485 xasprintf(&sc_auth.output, "invalid response received after AUTH LOGIN");
370 result = STATE_CRITICAL; 486 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
371 xasprintf(&error_msg, _("no authuser specified, "));
372 break;
373 }
374 if (config.authpass == NULL) {
375 result = STATE_CRITICAL;
376 xasprintf(&error_msg, _("no authpass specified, "));
377 break;
378 }
379
380 /* send AUTH LOGIN */
381 my_send(config, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), socket_descriptor,
382 ssl_established);
383 if (verbose) {
384 printf(_("sent %s\n"), "AUTH LOGIN");
385 }
386
387 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
388 ssl_established)) <= 0) {
389 xasprintf(&error_msg, _("recv() failed after AUTH LOGIN, "));
390 result = STATE_WARNING;
391 break;
392 }
393 if (verbose) {
394 printf(_("received %s\n"), buffer);
395 }
396
397 if (strncmp(buffer, "334", 3) != 0) {
398 result = STATE_CRITICAL;
399 xasprintf(&error_msg, _("invalid response received after AUTH LOGIN, "));
400 break;
401 }
402
403 /* encode authuser with base64 */
404 base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf);
405 xasprintf(&abuf, "%s\r\n", abuf);
406 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
407 if (verbose) {
408 printf(_("sent %s\n"), abuf);
409 }
410
411 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
412 ssl_established)) <= 0) {
413 result = STATE_CRITICAL;
414 xasprintf(&error_msg, _("recv() failed after sending authuser, "));
415 break;
416 }
417 if (verbose) {
418 printf(_("received %s\n"), buffer);
419 }
420 if (strncmp(buffer, "334", 3) != 0) {
421 result = STATE_CRITICAL;
422 xasprintf(&error_msg, _("invalid response received after authuser, "));
423 break;
424 }
425 /* encode authpass with base64 */
426 base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf);
427 xasprintf(&abuf, "%s\r\n", abuf);
428 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
429 if (verbose) {
430 printf(_("sent %s\n"), abuf);
431 }
432 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
433 ssl_established)) <= 0) {
434 result = STATE_CRITICAL;
435 xasprintf(&error_msg, _("recv() failed after sending authpass, "));
436 break;
437 }
438 if (verbose) {
439 printf(_("received %s\n"), buffer);
440 }
441 if (strncmp(buffer, "235", 3) != 0) {
442 result = STATE_CRITICAL;
443 xasprintf(&error_msg, _("invalid response received after authpass, "));
444 break;
445 }
446 break; 487 break;
447 } while (false); 488 }
448 } else {
449 result = STATE_CRITICAL;
450 xasprintf(&error_msg, _("only authtype LOGIN is supported, "));
451 }
452 }
453 489
454 /* tell the server we're done */ 490 /* encode authuser with base64 */
455 smtp_quit(config, buffer, socket_descriptor, ssl_established); 491 base64_encode_alloc(config.authuser, strlen(config.authuser), &abuf);
492 xasprintf(&abuf, "%s\r\n", abuf);
493 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
494 if (verbose) {
495 printf(_("sent %s\n"), abuf);
496 }
456 497
457 /* finally close the connection */ 498 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
458 close(socket_descriptor); 499 ssl_established)) <= 0) {
500 xasprintf(&sc_auth.output, "recv() failed after sending authuser");
501 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
502 break;
503 }
504
505 if (verbose) {
506 printf(_("received %s\n"), buffer);
507 }
508
509 if (strncmp(buffer, "334", 3) != 0) {
510 xasprintf(&sc_auth.output, "invalid response received after authuser");
511 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
512 break;
513 }
514
515 /* encode authpass with base64 */
516 base64_encode_alloc(config.authpass, strlen(config.authpass), &abuf);
517 xasprintf(&abuf, "%s\r\n", abuf);
518 my_send(config, abuf, (int)strlen(abuf), socket_descriptor, ssl_established);
519
520 if (verbose) {
521 printf(_("sent %s\n"), abuf);
522 }
523
524 if ((ret = recvlines(config, buffer, MAX_INPUT_BUFFER, socket_descriptor,
525 ssl_established)) <= 0) {
526 xasprintf(&sc_auth.output, "recv() failed after sending authpass");
527 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
528 break;
529 }
530
531 if (verbose) {
532 printf(_("received %s\n"), buffer);
533 }
534
535 if (strncmp(buffer, "235", 3) != 0) {
536 xasprintf(&sc_auth.output, "invalid response received after authpass");
537 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
538 break;
539 }
540 break;
541 } while (false);
542 } else {
543 sc_auth = mp_set_subcheck_state(sc_auth, STATE_CRITICAL);
544 xasprintf(&sc_auth.output, "only authtype LOGIN is supported");
545 }
546
547 mp_add_subcheck_to_check(&overall, sc_auth);
459 } 548 }
460 549
550 /* tell the server we're done */
551 smtp_quit(config, buffer, socket_descriptor, ssl_established);
552
553 /* finally close the connection */
554 close(socket_descriptor);
555
461 /* reset the alarm */ 556 /* reset the alarm */
462 alarm(0); 557 alarm(0);
463 558
464 long microsec = deltime(start_time); 559 long microsec = deltime(start_time);
465 double elapsed_time = (double)microsec / 1.0e6; 560 double elapsed_time = (double)microsec / 1.0e6;
466 561
467 if (result == STATE_OK) { 562 mp_perfdata pd_elapsed_time = perfdata_init();
468 if (config.check_critical_time && elapsed_time > config.critical_time) { 563 pd_elapsed_time = mp_set_pd_value(pd_elapsed_time, elapsed_time);
469 result = STATE_CRITICAL; 564 pd_elapsed_time.label = "time";
470 } else if (config.check_warning_time && elapsed_time > config.warning_time) { 565 pd_elapsed_time.uom = "s";
471 result = STATE_WARNING;
472 }
473 }
474 566
475 printf(_("SMTP %s - %s%.3f sec. response time%s%s|%s\n"), state_text(result), error_msg, 567 pd_elapsed_time = mp_pd_set_thresholds(pd_elapsed_time, config.connection_time);
476 elapsed_time, verbose ? ", " : "", verbose ? buffer : "",
477 fperfdata("time", elapsed_time, "s", config.check_warning_time, config.warning_time,
478 config.check_critical_time, config.critical_time, true, 0, false, 0));
479 568
480 exit(result); 569 mp_subcheck sc_connection_time = mp_subcheck_init();
570 xasprintf(&sc_connection_time.output, "connection time: %.3gs", elapsed_time);
571 sc_connection_time =
572 mp_set_subcheck_state(sc_connection_time, mp_get_pd_status(pd_elapsed_time));
573 mp_add_subcheck_to_check(&overall, sc_connection_time);
574
575 mp_exit(overall);
481} 576}
482 577
483/* process command-line arguments */ 578/* process command-line arguments */
@@ -535,8 +630,8 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
535 } 630 }
536 } 631 }
537 632
538 int command_size = 0; 633 unsigned long command_size = 0;
539 int response_size = 0; 634 unsigned long response_size = 0;
540 bool implicit_tls = false; 635 bool implicit_tls = false;
541 int server_port_option = 0; 636 int server_port_option = 0;
542 while (true) { 637 while (true) {
@@ -591,7 +686,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
591 result.config.commands = 686 result.config.commands =
592 realloc(result.config.commands, sizeof(char *) * command_size); 687 realloc(result.config.commands, sizeof(char *) * command_size);
593 if (result.config.commands == NULL) { 688 if (result.config.commands == NULL) {
594 die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"), 689 die(STATE_UNKNOWN, _("Could not realloc() units [%lu]\n"),
595 result.config.ncommands); 690 result.config.ncommands);
596 } 691 }
597 } 692 }
@@ -605,7 +700,7 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
605 result.config.responses = 700 result.config.responses =
606 realloc(result.config.responses, sizeof(char *) * response_size); 701 realloc(result.config.responses, sizeof(char *) * response_size);
607 if (result.config.responses == NULL) { 702 if (result.config.responses == NULL) {
608 die(STATE_UNKNOWN, _("Could not realloc() units [%d]\n"), 703 die(STATE_UNKNOWN, _("Could not realloc() units [%lu]\n"),
609 result.config.nresponses); 704 result.config.nresponses);
610 } 705 }
611 } 706 }
@@ -613,22 +708,22 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
613 strncpy(result.config.responses[result.config.nresponses], optarg, 255); 708 strncpy(result.config.responses[result.config.nresponses], optarg, 255);
614 result.config.nresponses++; 709 result.config.nresponses++;
615 break; 710 break;
616 case 'c': /* critical time threshold */ 711 case 'c': /* critical time threshold */ {
617 if (!is_nonnegative(optarg)) { 712 mp_range_parsed tmp = mp_parse_range_string(optarg);
618 usage4(_("Critical time must be a positive")); 713 if (tmp.error != MP_PARSING_SUCCES) {
619 } else { 714 die(STATE_UNKNOWN, "failed to parse critical time threshold");
620 result.config.critical_time = strtod(optarg, NULL);
621 result.config.check_critical_time = true;
622 } 715 }
623 break; 716 result.config.connection_time =
624 case 'w': /* warning time threshold */ 717 mp_thresholds_set_warn(result.config.connection_time, tmp.range);
625 if (!is_nonnegative(optarg)) { 718 } break;
626 usage4(_("Warning time must be a positive")); 719 case 'w': /* warning time threshold */ {
627 } else { 720 mp_range_parsed tmp = mp_parse_range_string(optarg);
628 result.config.warning_time = strtod(optarg, NULL); 721 if (tmp.error != MP_PARSING_SUCCES) {
629 result.config.check_warning_time = true; 722 die(STATE_UNKNOWN, "failed to parse warning time threshold");
630 } 723 }
631 break; 724 result.config.connection_time =
725 mp_thresholds_set_crit(result.config.connection_time, tmp.range);
726 } break;
632 case 'v': /* verbose */ 727 case 'v': /* verbose */
633 verbose++; 728 verbose++;
634 break; 729 break;
@@ -742,6 +837,19 @@ check_smtp_config_wrapper process_arguments(int argc, char **argv) {
742 result.config.server_port = server_port_option; 837 result.config.server_port = server_port_option;
743 } 838 }
744 839
840 if (result.config.authtype) {
841 if (strcmp(result.config.authtype, "LOGIN") == 0) {
842 if (result.config.authuser == NULL) {
843 usage4("no authuser specified");
844 }
845 if (result.config.authpass == NULL) {
846 usage4("no authpass specified");
847 }
848 } else {
849 usage4("only authtype LOGIN is supported");
850 }
851 }
852
745 return result; 853 return result;
746} 854}
747 855
@@ -791,7 +899,7 @@ char *smtp_quit(check_smtp_config config, char buffer[MAX_INPUT_BUFFER], int soc
791int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_descriptor, 899int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_descriptor,
792 bool ssl_established) { 900 bool ssl_established) {
793 int result; 901 int result;
794 int counter; 902 size_t counter;
795 903
796 for (counter = result = 0; counter < bufsize - 1; counter++) { 904 for (counter = result = 0; counter < bufsize - 1; counter++) {
797 if ((result = my_recv(config, &buf[counter], 1, socket_descriptor, ssl_established)) != 1) { 905 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
799 } 907 }
800 if (buf[counter] == '\n') { 908 if (buf[counter] == '\n') {
801 buf[++counter] = '\0'; 909 buf[++counter] = '\0';
802 return counter; 910 return (int)counter;
803 } 911 }
804 } 912 }
805 return (result == 1 || counter == 0) ? -2 : result; /* -2 if out of space */ 913 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 @@
1#pragma once 1#pragma once
2 2
3#include "../../config.h" 3#include "../../config.h"
4#include "thresholds.h"
4#include <stddef.h> 5#include <stddef.h>
5#include <string.h> 6#include <string.h>
6 7
@@ -18,20 +19,18 @@ typedef struct {
18 char *server_expect; 19 char *server_expect;
19 bool ignore_send_quit_failure; 20 bool ignore_send_quit_failure;
20 21
21 double warning_time; 22 mp_thresholds connection_time;
22 bool check_warning_time; 23
23 double critical_time;
24 bool check_critical_time;
25 bool use_ehlo; 24 bool use_ehlo;
26 bool use_lhlo; 25 bool use_lhlo;
27 26
28 char *from_arg; 27 char *from_arg;
29 bool send_mail_from; 28 bool send_mail_from;
30 29
31 int ncommands; 30 unsigned long ncommands;
32 char **commands; 31 char **commands;
33 32
34 int nresponses; 33 unsigned long nresponses;
35 char **responses; 34 char **responses;
36 35
37 char *authtype; 36 char *authtype;
@@ -58,10 +57,7 @@ check_smtp_config check_smtp_config_init() {
58 .server_expect = SMTP_EXPECT, 57 .server_expect = SMTP_EXPECT,
59 .ignore_send_quit_failure = false, 58 .ignore_send_quit_failure = false,
60 59
61 .warning_time = 0, 60 .connection_time = mp_thresholds_init(),
62 .check_warning_time = false,
63 .critical_time = 0,
64 .check_critical_time = false,
65 .use_ehlo = false, 61 .use_ehlo = false,
66 .use_lhlo = false, 62 .use_lhlo = false,
67 63
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,
114void np_net_ssl_cleanup(void); 114void np_net_ssl_cleanup(void);
115int np_net_ssl_write(const void *buf, int num); 115int np_net_ssl_write(const void *buf, int num);
116int np_net_ssl_read(void *buf, int num); 116int np_net_ssl_read(void *buf, int num);
117
118typedef enum {
119 ALL_OK,
120 NO_SERVER_CERTIFICATE_PRESENT,
121 UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT,
122 WRONG_TIME_FORMAT_IN_CERTIFICATE,
123} retrieve_expiration_date_errors;
124
125typedef struct {
126 double remaining_seconds;
127 retrieve_expiration_date_errors errors;
128} retrieve_expiration_time_result;
129
130typedef struct {
131 mp_state_enum result_state;
132 double remaining_seconds;
133 retrieve_expiration_date_errors errors;
134} net_ssl_check_cert_result;
135net_ssl_check_cert_result np_net_ssl_check_cert2(int days_till_exp_warn, int days_till_exp_crit);
136
117mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); 137mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
118mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); 138mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit);
119#endif /* HAVE_SSL */ 139#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_
312# endif /* USE_OPENSSL */ 312# endif /* USE_OPENSSL */
313} 313}
314 314
315retrieve_expiration_time_result np_net_ssl_get_cert_expiration(X509 *certificate) {
316# ifdef USE_OPENSSL
317 retrieve_expiration_time_result result = {
318 .errors = ALL_OK,
319 .remaining_seconds = {},
320 };
321
322 if (!certificate) {
323 // printf("%s\n", _("CRITICAL - No server certificate present to inspect."));
324 result.errors = NO_SERVER_CERTIFICATE_PRESENT;
325 return result;
326 }
327
328 /* Extract CN from certificate subject */
329 X509_NAME *subj = X509_get_subject_name(certificate);
330
331 if (!subj) {
332 // printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject."));
333 result.errors = UNABLE_TO_RETRIEVE_CERTIFICATE_SUBJECT;
334 return result;
335 }
336
337 char cn[MAX_CN_LENGTH] = "";
338 int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn));
339 if (cnlen == -1) {
340 strcpy(cn, _("Unknown CN"));
341 }
342
343 /* Retrieve timestamp of certificate */
344 ASN1_STRING *expiration_timestamp = X509_get_notAfter(certificate);
345
346 int offset = 0;
347 struct tm stamp = {};
348 /* Generate tm structure to process timestamp */
349 if (expiration_timestamp->type == V_ASN1_UTCTIME) {
350 if (expiration_timestamp->length < 10) {
351 result.errors = WRONG_TIME_FORMAT_IN_CERTIFICATE;
352 return result;
353 }
354
355 stamp.tm_year =
356 (expiration_timestamp->data[0] - '0') * 10 + (expiration_timestamp->data[1] - '0');
357 if (stamp.tm_year < 50) {
358 stamp.tm_year += 100;
359 }
360 offset = 0;
361 } else {
362 if (expiration_timestamp->length < 12) {
363 result.errors = WRONG_TIME_FORMAT_IN_CERTIFICATE;
364 return result;
365 }
366
367 stamp.tm_year = (expiration_timestamp->data[0] - '0') * 1000 +
368 (expiration_timestamp->data[1] - '0') * 100 +
369 (expiration_timestamp->data[2] - '0') * 10 +
370 (expiration_timestamp->data[3] - '0');
371 stamp.tm_year -= 1900;
372 offset = 2;
373 }
374 stamp.tm_mon = (expiration_timestamp->data[2 + offset] - '0') * 10 +
375 (expiration_timestamp->data[3 + offset] - '0') - 1;
376 stamp.tm_mday = (expiration_timestamp->data[4 + offset] - '0') * 10 +
377 (expiration_timestamp->data[5 + offset] - '0');
378 stamp.tm_hour = (expiration_timestamp->data[6 + offset] - '0') * 10 +
379 (expiration_timestamp->data[7 + offset] - '0');
380 stamp.tm_min = (expiration_timestamp->data[8 + offset] - '0') * 10 +
381 (expiration_timestamp->data[9 + offset] - '0');
382 stamp.tm_sec = (expiration_timestamp->data[10 + offset] - '0') * 10 +
383 (expiration_timestamp->data[11 + offset] - '0');
384 stamp.tm_isdst = -1;
385
386 time_t tm_t = timegm(&stamp);
387 double time_left = difftime(tm_t, time(NULL));
388 result.remaining_seconds = time_left;
389
390 char *timezone = getenv("TZ");
391 setenv("TZ", "GMT", 1);
392 tzset();
393
394 char timestamp[50] = "";
395 strftime(timestamp, 50, "%c %z", localtime(&tm_t));
396 if (timezone) {
397 setenv("TZ", timezone, 1);
398 } else {
399 unsetenv("TZ");
400 }
401
402 tzset();
403
404 X509_free(certificate);
405
406 return result;
407# else /* ifndef USE_OPENSSL */
408 printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
409 return STATE_WARNING;
410# endif /* USE_OPENSSL */
411}
412
413net_ssl_check_cert_result np_net_ssl_check_cert2(int days_till_exp_warn, int days_till_exp_crit) {
414# ifdef USE_OPENSSL
415 X509 *certificate = NULL;
416 certificate = SSL_get_peer_certificate(s);
417
418 retrieve_expiration_time_result expiration_date = np_net_ssl_get_cert_expiration(certificate);
419
420 net_ssl_check_cert_result result = {
421 .result_state = STATE_UNKNOWN,
422 .remaining_seconds = expiration_date.remaining_seconds,
423 .errors = expiration_date.errors,
424 };
425
426 if (expiration_date.errors == ALL_OK) {
427 // got a valid expiration date
428 unsigned int remaining_days = result.remaining_seconds / 86400;
429
430 if (remaining_days < days_till_exp_crit) {
431 result.result_state = STATE_CRITICAL;
432 } else if (remaining_days < days_till_exp_warn) {
433 result.result_state = STATE_WARNING;
434 } else {
435 result.result_state = STATE_OK;
436 }
437 }
438
439 return result;
440
441# else /* ifndef USE_OPENSSL */
442 printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
443 return STATE_WARNING;
444# endif /* USE_OPENSSL */
445}
446
315mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) { 447mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) {
316# ifdef USE_OPENSSL 448# ifdef USE_OPENSSL
317 X509 *certificate = NULL; 449 X509 *certificate = NULL;