From 321075cd563ddbba92a6ebb8e59abefb9da1e8c2 Mon Sep 17 00:00:00 2001 From: "M. Sean Finney" Date: Thu, 13 Oct 2005 10:11:25 +0000 Subject: multiple fixes in check_smtp from debian (bts #285554): - fix for double free via SSL_CTX_free. looks like the previous author knew this was a problem but didn't care enough to fix it ;p. - use defines instead of const chars for SMTP strings. - default to send our fqdn (via gethostbyname lookup on gethostname) in the HELO string, as this is an RFC/SMTP requirement. overridable via cmdline. - use EHLO instead of HELO if using STARTTLS, as it is not SMTP but ESMTP. - verify the server supports STARTTLS before initializing it. - always send QUIT before disconnecting when possible. git-svn-id: https://nagiosplug.svn.sourceforge.net/svnroot/nagiosplug/nagiosplug/trunk@1246 f882894a-f735-0410-b71e-b25c423dba1c diff --git a/plugins/check_smtp.c b/plugins/check_smtp.c index f0bc736..3bb6a32 100644 --- a/plugins/check_smtp.c +++ b/plugins/check_smtp.c @@ -59,10 +59,17 @@ int check_certificate (X509 **); enum { SMTP_PORT = 25 }; -const char *SMTP_EXPECT = "220"; -const char *SMTP_HELO = "HELO "; -const char *SMTP_QUIT = "QUIT\r\n"; -const char *SMTP_STARTTLS = "STARTTLS\r\n"; +#define SMTP_EXPECT "220" +#define SMTP_HELO "HELO " +#define SMTP_EHLO "EHLO " +#define SMTP_QUIT "QUIT\r\n" +#define SMTP_STARTTLS "STARTTLS\r\n" + +#ifndef HOST_MAX_BYTES +#define HOST_MAX_BYTES 255 +#endif + +#define EHLO_SUPPORTS_STARTTLS 1 int process_arguments (int, char **); int validate_arguments (void); @@ -101,6 +108,9 @@ int critical_time = 0; int check_critical_time = FALSE; int verbose = 0; int use_ssl = FALSE; +short use_ehlo = FALSE; +short ssl_established = TRUE; +char *localhostname = NULL; int sd; char buffer[MAX_INPUT_BUFFER]; enum { @@ -112,7 +122,7 @@ enum { int main (int argc, char **argv) { - + short supports_tls=FALSE; int n = 0; double elapsed_time; long microsec; @@ -120,6 +130,7 @@ main (int argc, char **argv) char *cmd_str = NULL; char *helocmd = NULL; struct timeval tv; + struct hostent *hp; setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); @@ -129,12 +140,26 @@ main (int argc, char **argv) usage4 (_("Could not parse arguments")); /* initialize the HELO command with the localhostname */ -#ifndef HOST_MAX_BYTES -#define HOST_MAX_BYTES 255 -#endif - helocmd = malloc (HOST_MAX_BYTES); - gethostname(helocmd, HOST_MAX_BYTES); - asprintf (&helocmd, "%s%s%s", SMTP_HELO, helocmd, "\r\n"); + if(! localhostname){ + localhostname = malloc (HOST_MAX_BYTES); + if(!localhostname){ + printf(_("malloc() failed!\n")); + return STATE_CRITICAL; + } + if(gethostname(localhostname, HOST_MAX_BYTES)){ + printf(_("gethostname() failed!\n")); + return STATE_CRITICAL; + } + hp = gethostbyname(localhostname); + if(!hp) helocmd = localhostname; + else helocmd = hp->h_name; + } else { + helocmd = localhostname; + } + if(use_ehlo) + asprintf (&helocmd, "%s%s%s", SMTP_EHLO, helocmd, "\r\n"); + else + asprintf (&helocmd, "%s%s%s", SMTP_HELO, helocmd, "\r\n"); /* initialize the MAIL command with optional FROM command */ asprintf (&cmd_str, "%sFROM: %s%s", mail_command, from_arg, "\r\n"); @@ -178,11 +203,26 @@ main (int argc, char **argv) } } - /* send the HELO command */ + /* send the HELO/EHLO command */ send(sd, helocmd, strlen(helocmd), 0); /* allow for response to helo command to reach us */ - read (sd, buffer, MAXBUF - 1); + if(read (sd, buffer, MAXBUF - 1) < 0){ + printf (_("recv() failed\n")); + return STATE_WARNING; + } else if(use_ehlo){ + buffer[MAXBUF-1]='\0'; + if(strstr(buffer, "250 STARTTLS") != NULL || + strstr(buffer, "250-STARTTLS") != NULL){ + supports_tls=TRUE; + } + } + + if(use_ssl && ! supports_tls){ + printf(_("WARNING - TLS not supported by server\n")); + send (sd, SMTP_QUIT, strlen (SMTP_QUIT), 0); + return STATE_WARNING; + } #ifdef HAVE_SSL if(use_ssl) { @@ -192,11 +232,14 @@ main (int argc, char **argv) recv(sd,buffer, MAX_INPUT_BUFFER-1, 0); /* wait for it */ if (!strstr (buffer, server_expect)) { printf (_("Server does not support STARTTLS\n")); + send (sd, SMTP_QUIT, strlen (SMTP_QUIT), 0); return STATE_UNKNOWN; } if(connect_STARTTLS() != OK) { printf (_("CRITICAL - Cannot create SSL context.\n")); return STATE_CRITICAL; + } else { + ssl_established = TRUE; } if ( check_cert ) { if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) { @@ -333,6 +376,7 @@ process_arguments (int argc, char **argv) {"timeout", required_argument, 0, 't'}, {"port", required_argument, 0, 'p'}, {"from", required_argument, 0, 'f'}, + {"fqdn", required_argument, 0, 'F'}, {"command", required_argument, 0, 'C'}, {"response", required_argument, 0, 'R'}, {"nocommand", required_argument, 0, 'n'}, @@ -359,7 +403,7 @@ process_arguments (int argc, char **argv) } while (1) { - c = getopt_long (argc, argv, "+hVv46t:p:f:e:c:w:H:C:R:SD:", + c = getopt_long (argc, argv, "+hVv46t:p:f:e:c:w:H:C:R:SD:F:", longopts, &option); if (c == -1 || c == EOF) @@ -380,6 +424,10 @@ process_arguments (int argc, char **argv) else usage4 (_("Port must be a positive integer")); break; + case 'F': + /* localhostname */ + localhostname = strdup(optarg); + break; case 'f': /* from argument */ from_arg = optarg; smtp_use_dummycmd = 1; @@ -439,6 +487,7 @@ process_arguments (int argc, char **argv) case 'S': /* starttls */ use_ssl = TRUE; + use_ehlo = TRUE; break; case 'D': /* Check SSL cert validity */ @@ -581,7 +630,7 @@ connect_STARTTLS (void) /* Initialize SSL context */ SSLeay_add_ssl_algorithms (); - meth = SSLv2_client_method (); + meth = SSLv23_client_method (); SSL_load_error_strings (); if ((ctx = SSL_CTX_new (meth)) == NULL) { @@ -602,11 +651,6 @@ connect_STARTTLS (void) { printf (_("CRITICAL - Cannot initiate SSL handshake.\n")); } - /* this causes a seg faul - not sure why, being sloppy - and commenting it out */ - /* SSL_free (ssl); */ - SSL_CTX_free(ctx); my_close(); return STATE_CRITICAL; @@ -708,7 +752,7 @@ int my_close (void) { #ifdef HAVE_SSL - if (use_ssl == TRUE) { + if (use_ssl == TRUE && ssl_established == TRUE) { SSL_shutdown (ssl); SSL_free (ssl); SSL_CTX_free (ctx); -- cgit v0.10-9-g596f