diff options
Diffstat (limited to 'plugins')
| -rw-r--r-- | plugins/check_smtp.c | 676 | ||||
| -rw-r--r-- | plugins/check_smtp.d/config.h | 16 | ||||
| -rw-r--r-- | plugins/netutils.h | 20 | ||||
| -rw-r--r-- | 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 @@ | |||
| 28 | * | 28 | * |
| 29 | *****************************************************************************/ | 29 | *****************************************************************************/ |
| 30 | 30 | ||
| 31 | const char *progname = "check_smtp"; | ||
| 32 | const char *copyright = "2000-2024"; | ||
| 33 | const 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 | ||
| 45 | const char *progname = "check_smtp"; | ||
| 46 | const char *copyright = "2000-2024"; | ||
| 47 | const 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 | |||
| 791 | int recvline(char *buf, size_t bufsize, check_smtp_config config, int socket_descriptor, | 899 | int 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, | |||
| 114 | void np_net_ssl_cleanup(void); | 114 | void np_net_ssl_cleanup(void); |
| 115 | int np_net_ssl_write(const void *buf, int num); | 115 | int np_net_ssl_write(const void *buf, int num); |
| 116 | int np_net_ssl_read(void *buf, int num); | 116 | int np_net_ssl_read(void *buf, int num); |
| 117 | |||
| 118 | typedef 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 | |||
| 125 | typedef struct { | ||
| 126 | double remaining_seconds; | ||
| 127 | retrieve_expiration_date_errors errors; | ||
| 128 | } retrieve_expiration_time_result; | ||
| 129 | |||
| 130 | typedef struct { | ||
| 131 | mp_state_enum result_state; | ||
| 132 | double remaining_seconds; | ||
| 133 | retrieve_expiration_date_errors errors; | ||
| 134 | } net_ssl_check_cert_result; | ||
| 135 | net_ssl_check_cert_result np_net_ssl_check_cert2(int days_till_exp_warn, int days_till_exp_crit); | ||
| 136 | |||
| 117 | mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); | 137 | mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); |
| 118 | mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); | 138 | mp_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 | ||
| 315 | retrieve_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 | |||
| 413 | net_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 | |||
| 315 | mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) { | 447 | mp_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; |
