*** check_smtp.c Fri Mar 7 02:15:40 2003 --- /usr/local/projects/nagios-plugins-1.3.1/plugins//check_smtp.c Wed Sep 29 15:14:33 2004 *************** *** 37,46 **** --- 37,64 ---- #include "config.h" #include "common.h" #include "netutils.h" #include "utils.h" + #ifdef HAVE_SSL + #include + #include + #include + #include + #endif + + #ifdef HAVE_SSL + int check_cert = FALSE; + int days_till_exp; + SSL_CTX *ctx; + SSL *ssl; + X509 *server_cert; + int connect_SSL (void); + int check_certificate (X509 **); + #endif + + const char *progname = "check_smtp"; #define SMTP_PORT 25 #define SMTP_EXPECT "220" #define SMTP_HELO "HELO " *************** *** 57,66 **** --- 75,85 ---- */ #define SMTP_DUMMYCMD "MAIL " #define SMTP_USE_DUMMYCMD 1 #define SMTP_QUIT "QUIT\r\n" + #define SMTP_STARTTLS "STARTTLS\r\n" int process_arguments (int, char **); int validate_arguments (void); void print_help (void); void print_usage (void); *************** *** 72,88 **** int warning_time = 0; int check_warning_time = FALSE; int critical_time = 0; int check_critical_time = FALSE; int verbose = FALSE; int main (int argc, char **argv) { ! int sd; int result = STATE_UNKNOWN; - char buffer[MAX_INPUT_BUFFER] = ""; char *from_str = NULL; char *helocmd = NULL; if (process_arguments (argc, argv) != OK) usage ("Invalid command arguments supplied\n"); --- 91,116 ---- int warning_time = 0; int check_warning_time = FALSE; int critical_time = 0; int check_critical_time = FALSE; int verbose = FALSE; + int use_ssl = FALSE; + int sd; + char timestamp[17] = ""; + //char *buffer = ""; + char buffer[MAX_INPUT_BUFFER] = ""; + enum { + TCP_PROTOCOL = 1, + UDP_PROTOCOL = 2, + MAXBUF = 1024 + }; int main (int argc, char **argv) { ! int result = STATE_UNKNOWN; char *from_str = NULL; char *helocmd = NULL; if (process_arguments (argc, argv) != OK) usage ("Invalid command arguments supplied\n"); *************** *** 114,130 **** /* we connected, so close connection before exiting */ if (result == STATE_OK) { /* watch for the SMTP connection string and */ /* return a WARNING status if we couldn't read any data */ ! if (recv (sd, buffer, MAX_INPUT_BUFFER - 1, 0) == -1) { ! printf ("recv() failed\n"); result = STATE_WARNING; } else { /* strip the buffer of carriage returns */ ! strip (buffer); /* make sure we find the response we are looking for */ if (!strstr (buffer, server_expect)) { if (server_port == SMTP_PORT) printf ("Invalid SMTP response received from host\n"); else --- 142,158 ---- /* we connected, so close connection before exiting */ if (result == STATE_OK) { /* watch for the SMTP connection string and */ /* return a WARNING status if we couldn't read any data */ ! if (recv(sd, buffer, MAX_INPUT_BUFFER-1, 0) == -1) { ! printf ("my_recv() failed\n"); result = STATE_WARNING; } else { /* strip the buffer of carriage returns */ ! strip (buffer); /* make sure we find the response we are looking for */ if (!strstr (buffer, server_expect)) { if (server_port == SMTP_PORT) printf ("Invalid SMTP response received from host\n"); else *************** *** 132,162 **** server_port); result = STATE_WARNING; } } /* send the HELO command */ send(sd, helocmd, strlen(helocmd), 0); /* allow for response to helo command to reach us */ ! recv(sd, buffer, MAX_INPUT_BUFFER-1, 0); ! #ifdef SMTP_USE_DUMMYCMD send(sd, from_str, strlen(from_str), 0); /* allow for response to DUMMYCMD to reach us */ ! recv(sd, buffer, MAX_INPUT_BUFFER-1, 0); if (verbose == TRUE) printf("DUMMYCMD: %s\n%s\n",from_str,buffer); #endif /* SMTP_USE_DUMMYCMD */ ! /* tell the server we're done */ send (sd, SMTP_QUIT, strlen (SMTP_QUIT), 0); /* finally close the connection */ ! close (sd); } /* reset the alarm */ alarm (0); --- 160,228 ---- server_port); result = STATE_WARNING; } } + #ifdef HAVE_SSL + if(use_ssl) { + /* send the STARTTLS command */ + send(sd, SMTP_STARTTLS, strlen(SMTP_STARTTLS), 0); + + recv(sd, buffer, MAX_INPUT_BUFFER-1, 0); // wait for it + if(connect_STARTTLS() != OK) { + printf ("ERROR: Cannot create SSL context.\n"); + return STATE_CRITICAL; + } + if ( check_cert ) { + if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) { + result = check_certificate (&server_cert); + X509_free(server_cert); + } + else { + printf("ERROR: Cannot retrieve server certificate.\n"); + result = STATE_CRITICAL; + } + my_close (); + return result; + } + } + #endif + /* send the HELO command */ + #ifdef HAVE_SSL + if (use_ssl) + SSL_write(ssl, helocmd, strlen (helocmd)); + else + #endif send(sd, helocmd, strlen(helocmd), 0); /* allow for response to helo command to reach us */ ! my_recv(); ! #if defined(SMTP_USE_DUMMYCMD) && defined(HAVE_SSL) ! if (use_ssl) ! SSL_write(ssl,from_str, strlen(from_str)); ! else ! #endif #ifdef SMTP_USE_DUMMYCMD send(sd, from_str, strlen(from_str), 0); /* allow for response to DUMMYCMD to reach us */ ! my_recv(); if (verbose == TRUE) printf("DUMMYCMD: %s\n%s\n",from_str,buffer); #endif /* SMTP_USE_DUMMYCMD */ ! #ifdef HAVE_SSL ! if (use_ssl) ! SSL_write(ssl,SMTP_QUIT,strlen (SMTP_QUIT)); ! else ! #endif /* tell the server we're done */ send (sd, SMTP_QUIT, strlen (SMTP_QUIT), 0); /* finally close the connection */ ! my_close (); } /* reset the alarm */ alarm (0); *************** *** 197,206 **** --- 263,274 ---- {"port", required_argument, 0, 'p'}, {"from", required_argument, 0, 'f'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, + {"starttls",no_argument,0,'S'}, + {"certificate",required_argument,0,'C'}, {0, 0, 0, 0} }; #endif if (argc < 2) *************** *** 216,229 **** } while (1) { #ifdef HAVE_GETOPT_H c = ! getopt_long (argc, argv, "+hVvt:p:f:e:c:w:H:", long_options, &option_index); #else ! c = getopt (argc, argv, "+?hVvt:p:f:e:c:w:H:"); #endif if (c == -1 || c == EOF) break; switch (c) { --- 284,297 ---- } while (1) { #ifdef HAVE_GETOPT_H c = ! getopt_long (argc, argv, "+hVvt:p:f:e:c:w:H:SC:", long_options, &option_index); #else ! c = getopt (argc, argv, "+?hVvt:p:f:e:c:w:H:SC:"); #endif if (c == -1 || c == EOF) break; switch (c) { *************** *** 276,285 **** --- 344,369 ---- } else { usage ("Time interval must be a nonnegative integer\n"); } break; + case 'S': + /* starttls */ + use_ssl = TRUE; + break; + case 'C': /* Check SSL cert validity */ + #ifdef HAVE_SSL + if (!is_intnonneg (optarg)) + usage2 ("invalid certificate expiration period", optarg); + days_till_exp = atoi (optarg); + check_cert = TRUE; + use_ssl = TRUE; + #else + terminate (STATE_UNKNOWN, + "SSL support not available. Install OpenSSL and recompile."); + #endif + break; case 'V': /* version */ print_revision (progname, "$Revision: 1.9.2.2 $"); exit (STATE_OK); case 'h': /* help */ print_help (); *************** *** 344,353 **** --- 428,443 ---- " Seconds necessary to result in a warning status\n" " -c, --critical=INTEGER\n" " Seconds necessary to result in a critical status\n" " -t, --timeout=INTEGER\n" " Seconds before connection attempt times out (default: %d)\n" + #ifdef HAVE_SSL + " -S, --starttls\n" + " Use starttls for connection\n" + " -C, --certificate\n" + " Check certificate\n" + #endif " -v, --verbose\n" " Print extra information (command-line use only)\n" " -h, --help\n" " Print detailed help screen\n" " -V, --version\n" *************** *** 365,370 **** --- 455,615 ---- { printf ("Usage: %s -H host [-e expect] [-p port] [-f from addr] [-w warn] [-c crit] [-t timeout] [-v]\n" " %s --help\n" " %s --version\n", progname, progname, progname); + } + + #ifdef HAVE_SSL + int + connect_STARTTLS (void) + { + SSL_METHOD *meth; + + /* Initialize SSL context */ + SSLeay_add_ssl_algorithms (); + meth = SSLv2_client_method (); + SSL_load_error_strings (); + if ((ctx = SSL_CTX_new (meth)) == NULL) + { + printf ("ERROR: Cannot create SSL context.\n"); + return STATE_CRITICAL; + } + /* Do the SSL handshake */ + if ((ssl = SSL_new (ctx)) != NULL) + { + SSL_set_fd (ssl, sd); + /* original version checked for -1 + I look for success instead (1) */ + if (SSL_connect (ssl) == 1) + return OK; + ERR_print_errors_fp (stderr); + } + else + { + printf ("ERROR: Cannot initiate SSL handshake.\n"); + } + /* this causes a seg fault + not sure why, being sloppy + and commenting it out */ + // SSL_free (ssl); + + + SSL_CTX_free (ctx); + my_close (); + + return STATE_CRITICAL; + } + #endif + + #ifdef HAVE_SSL + int + check_certificate (X509 ** certificate) + { + ASN1_STRING *tm; + int offset; + struct tm stamp; + int days_left; + + + /* Retrieve timestamp of certificate */ + tm = X509_get_notAfter (*certificate); + + /* Generate tm structure to process timestamp */ + if (tm->type == V_ASN1_UTCTIME) { + if (tm->length < 10) { + printf ("ERROR: Wrong time format in certificate.\n"); + return STATE_CRITICAL; + } + else { + stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0'); + if (stamp.tm_year < 50) + stamp.tm_year += 100; + offset = 0; + } + } + else { + if (tm->length < 12) { + printf ("ERROR: Wrong time format in certificate.\n"); + return STATE_CRITICAL; + } + else { + stamp.tm_year = + (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 + + (tm->data[2] - '0') * 10 + (tm->data[3] - '0'); + stamp.tm_year -= 1900; + offset = 2; + } + } + stamp.tm_mon = + (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1; + stamp.tm_mday = + (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0'); + stamp.tm_hour = + (tm->data[6 + offset] - '0') * 10 + (tm->data[7 + offset] - '0'); + stamp.tm_min = + (tm->data[8 + offset] - '0') * 10 + (tm->data[9 + offset] - '0'); + stamp.tm_sec = 0; + stamp.tm_isdst = -1; + + days_left = (mktime (&stamp) - time (NULL)) / 86400; + snprintf + (timestamp, 16, "%02d/%02d/%04d %02d:%02d", + stamp.tm_mon + 1, + stamp.tm_mday, stamp.tm_year + 1900, stamp.tm_hour, stamp.tm_min); + + if (days_left > 0 && days_left <= days_till_exp) { + printf ("Certificate expires in %d day(s) (%s).\n", days_left, timestamp); + return STATE_WARNING; + } + if (days_left < 0) { + printf ("Certificate expired on %s.\n", timestamp); + return STATE_CRITICAL; + } + + if (days_left == 0) { + printf ("Certificate expires today (%s).\n", timestamp); + return STATE_WARNING; + } + + printf ("Certificate will expire on %s.\n", timestamp); + + return STATE_OK; + } + #endif + + int + my_recv (void) + { + int i; + + #ifdef HAVE_SSL + if (use_ssl) { + i = SSL_read (ssl, buffer, MAXBUF - 1); + } + else { + #endif + i = read (sd, buffer, MAXBUF - 1); + #ifdef HAVE_SSL + } + #endif + + return i; + } + + int + my_close (void) + { + #ifdef HAVE_SSL + if (use_ssl == TRUE) { + SSL_shutdown (ssl); + SSL_free (ssl); + SSL_CTX_free (ctx); + return 0; + } + else { + #endif + return close (sd); + #ifdef HAVE_SSL + } + #endif }