*** 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 <openssl/ssl.h>
+ #include <openssl/x509.h>
+ #include <openssl/err.h>
+ #include <openssl/rand.h>
+ #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
  }