[Nagiosplug-devel] PATCH: SMTP auth support in nagios plugin check_smtp.c

Holger Weiss holger at CIS.FU-Berlin.DE
Tue Nov 1 10:35:15 CET 2005


* Lubomir Host <rajo at platon.sk> [2005-10-31 22:22]:
> Here is a patch that adds the ability to confirm that your SMTP AUTH
> mechanism is working on your smtp server.

I was going to do the same thing this week, thanks for saving me the
work! ;-)  FWIW, I've attached my backport of your patch to the 1.4.2
release of check_smtp in case it's useful for anyone.

Holger

-- 
PGP fingerprint:  F1F0 9071 8084 A426 DD59  9839 59D3 F3A1 B8B5 D3DE
-------------- next part --------------
--- check_smtp.c.orig	2005-04-07 06:33:33.000000000 +0200
+++ check_smtp.c	2005-11-01 19:09:29.182255669 +0100
@@ -60,9 +60,10 @@
 	SMTP_PORT	= 25
 };
 const char *SMTP_EXPECT = "220";
-const char *SMTP_HELO = "HELO ";
+const char *SMTP_HELO = "EHLO ";
 const char *SMTP_QUIT	= "QUIT\r\n";
 const char *SMTP_STARTTLS = "STARTTLS\r\n";
+const char *SMTP_AUTH_LOGIN = "AUTH LOGIN\r\n";
 
 int process_arguments (int, char **);
 int validate_arguments (void);
@@ -95,6 +96,9 @@
 int response_size=0;
 char **commands = NULL;
 char **responses = NULL;
+char *authtype = NULL;
+char *authuser = NULL;
+char *authpass = NULL;
 int warning_time = 0;
 int check_warning_time = FALSE;
 int critical_time = 0;
@@ -109,6 +113,47 @@
   MAXBUF = 1024
 };
 
+/* written by lauri alanko */
+static char *
+base64 (const char *bin, size_t len)
+{
+
+	char *buf = (char *) malloc ((len + 2) / 3 * 4 + 1);
+	size_t i = 0, j = 0;
+
+	char BASE64_END = '=';
+	char base64_table[64];
+	strncpy (base64_table, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);
+
+	while (j < len - 2) {
+		buf[i++] = base64_table[bin[j] >> 2];
+		buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
+		buf[i++] = base64_table[((bin[j + 1] & 15) << 2) | (bin[j + 2] >> 6)];
+		buf[i++] = base64_table[bin[j + 2] & 63];
+		j += 3;
+	}
+
+	switch (len - j) {
+	case 1:
+		buf[i++] = base64_table[bin[j] >> 2];
+		buf[i++] = base64_table[(bin[j] & 3) << 4];
+		buf[i++] = BASE64_END;
+		buf[i++] = BASE64_END;
+		break;
+	case 2:
+		buf[i++] = base64_table[bin[j] >> 2];
+		buf[i++] = base64_table[((bin[j] & 3) << 4) | (bin[j + 1] >> 4)];
+		buf[i++] = base64_table[(bin[j + 1] & 15) << 2];
+		buf[i++] = BASE64_END;
+		break;
+	case 0:
+		break;
+	}
+
+	buf[i] = '\0';
+	return buf;
+}
+
 int
 main (int argc, char **argv)
 {
@@ -119,6 +164,7 @@
 	int result = STATE_UNKNOWN;
 	char *cmd_str = NULL;
 	char *helocmd = NULL;
+	char *error_msg = NULL;
 	struct timeval tv;
 
 	setlocale (LC_ALL, "");
@@ -198,6 +244,21 @@
 		    printf (_("CRITICAL - Cannot create SSL context.\n"));
 		    return STATE_CRITICAL;
 		  }
+
+		  /* resend the EHLO command
+		   *
+		   * RFC 3207, 4.2 says: ``The client MUST discard any knowledge
+		   * obtained from the server, such as the list of SMTP service
+		   * extensions, which was not obtained from the TLS negotiation
+		   * itself.  The client SHOULD send an EHLO command as the
+		   * first command after a successful TLS negotiation.''
+		   *
+		   * For this reason, at least Exim will not allow an AUTH LOGIN
+		   * command if we haven't resent EHLO.
+		   */
+		  SSL_write(ssl, helocmd, strlen(helocmd));
+		  myrecv();
+
 		  if ( check_cert ) {
 		    if ((server_cert = SSL_get_peer_certificate (ssl)) != NULL) {
 		      result = check_certificate (&server_cert);
@@ -256,7 +317,7 @@
 					regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
 					printf (_("Could Not Compile Regular Expression"));
 					return ERROR;
-				}
+				} 
 				excode = regexec (&preg, buffer, 10, pmatch, eflags);
 				if (excode == 0) {
 					result = STATE_OK;
@@ -280,6 +341,107 @@
 			n++;
 		}
 
+		if (authtype != NULL) {
+			if (strcmp (authtype, "LOGIN") == 0) {
+				char *abuf;
+				int ret;
+				do {
+					if (authuser == NULL) {
+						result = STATE_CRITICAL;
+						error_msg = _("no authuser specified, ");
+						break;
+					}
+					if (authpass == NULL) {
+						result = STATE_CRITICAL;
+						error_msg = _("no authpass specified, ");
+						break;
+					}
+
+					/* send AUTH LOGIN */
+#ifdef HAVE_SSL
+					if (use_ssl)
+						SSL_write(ssl, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN));
+					else
+#endif
+					send(sd, SMTP_AUTH_LOGIN, strlen(SMTP_AUTH_LOGIN), 0);
+					if (verbose)
+						printf (_("sent %s\n"), "AUTH LOGIN");
+
+					if((ret = myrecv()) < 0){
+						error_msg = _("recv() failed after AUTH LOGIN, \n");
+						result = STATE_WARNING;
+						break;
+					}
+					buffer[ret] = 0;
+					if (verbose)
+						printf (_("received %s\n"), buffer);
+
+					if (strncmp (buffer, "334", 3) != 0) {
+						result = STATE_CRITICAL;
+						error_msg = _("invalid response received after AUTH LOGIN, ");
+						break;
+					}
+
+					/* encode authuser with base64 */
+					abuf = base64 (authuser, strlen(authuser));
+					strcat (abuf, "\r\n");
+#ifdef HAVE_SSL
+					if (use_ssl)
+						SSL_write(ssl, abuf, strlen(abuf));
+					else
+#endif
+					send(sd, abuf, strlen(abuf), 0);
+					if (verbose)
+						printf (_("sent %s\n"), abuf);
+
+					if ((ret = myrecv()) == -1) {
+						result = STATE_CRITICAL;
+						error_msg = _("recv() failed after sending authuser, ");
+						break;
+					}
+					buffer[ret] = 0;
+					if (verbose) {
+						printf (_("received %s\n"), buffer);
+					}
+					if (strncmp (buffer, "334", 3) != 0) {
+						result = STATE_CRITICAL;
+						error_msg = _("invalid response received after authuser, ");
+						break;
+					}
+					/* encode authpass with base64 */
+					abuf = base64 (authpass, strlen(authpass));
+					strcat (abuf, "\r\n");
+#ifdef HAVE_SSL
+					if (use_ssl)
+						SSL_write(ssl, abuf, strlen(abuf));
+					else
+#endif
+					send(sd, abuf, strlen(abuf), 0);
+					if (verbose) {
+						printf (_("sent %s\n"), abuf);
+					}
+					if ((ret = myrecv()) == -1) {
+						result = STATE_CRITICAL;
+						error_msg = _("recv() failed after sending authpass, ");
+						break;
+					}
+					buffer[ret] = 0;
+					if (verbose) {
+						printf (_("received %s\n"), buffer);
+					}
+					if (strncmp (buffer, "235", 3) != 0) {
+						result = STATE_CRITICAL;
+						error_msg = _("invalid response received after authpass, ");
+						break;
+					}
+					break;
+				} while (0);
+			} else {
+				result = STATE_CRITICAL;
+				error_msg = _("only authtype LOGIN is supported, ");
+			}
+		}
+
 		/* tell the server we're done */
 #ifdef HAVE_SSL
 		if (use_ssl)
@@ -305,13 +467,15 @@
 			result = STATE_WARNING;
 	}
 
-	printf (_("SMTP %s - %.3f sec. response time%s%s|%s\n"),
-	        state_text (result), elapsed_time,
-          verbose?", ":"", verbose?buffer:"",
-	        fperfdata ("time", elapsed_time, "s",
-	                  (int)check_warning_time, warning_time,
-	                  (int)check_critical_time, critical_time,
-	                  TRUE, 0, FALSE, 0));
+	printf (_("SMTP %s - %s%.3f sec. response time%s%s|%s\n"),
+			state_text (result),
+			(error_msg == NULL ? "" : error_msg),
+			elapsed_time,
+			verbose?", ":"", verbose?buffer:"",
+			fperfdata ("time", elapsed_time, "s",
+				(int)check_warning_time, warning_time,
+				(int)check_critical_time, critical_time,
+				TRUE, 0, FALSE, 0));
 
 	return result;
 }
@@ -336,6 +500,9 @@
 		{"command", required_argument, 0, 'C'},
 		{"response", required_argument, 0, 'R'},
 		{"nocommand", required_argument, 0, 'n'},
+		{"authtype", required_argument, 0, 'A'},
+		{"authuser", required_argument, 0, 'U'},
+		{"authpass", required_argument, 0, 'P'},
 		{"verbose", no_argument, 0, 'v'},
 		{"version", no_argument, 0, 'V'},
 		{"use-ipv4", no_argument, 0, '4'},
@@ -359,7 +526,7 @@
 	}
 
 	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:A:U:P:",
 		                 longopts, &option);
 
 		if (c == -1 || c == EOF)
@@ -384,6 +551,15 @@
 			from_arg = optarg;
 			smtp_use_dummycmd = 1;
 			break;
+		case 'A':
+			authtype = optarg;
+			break;
+		case 'U':
+			authuser = optarg;
+			break;
+		case 'P':
+			authpass = optarg;
+			break;
 		case 'e':									/* server expect string on 220  */
 			server_expect = optarg;
 			break;
@@ -548,6 +724,15 @@
     Use STARTTLS for the connection.\n"));
 #endif
 
+	printf("\
+ -A, --authtype=STRING\n\
+   SMTP AUTH type to check (default none, only LOGIN supported)\n\
+ -U, --authuser=STRING\n\
+   SMTP AUTH username\n\
+ -P, --authpass=STRING\n\
+   SMTP AUTH password\n\
+			");
+
 	printf (_(UT_WARN_CRIT));
 
 	printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
@@ -570,6 +755,7 @@
 {
 	printf ("\
 Usage: %s -H host [-p port] [-e expect] [-C command] [-f from addr]\n\
+                  [-A authtype -U authuser -P authpass]\n\
                   [-w warn] [-c crit] [-t timeout] [-S] [-D days] [-n] [-v] [-4|-6]\n", progname);
 }
 
@@ -693,11 +879,11 @@
 
 #ifdef HAVE_SSL
   if (use_ssl) {
-    i = SSL_read (ssl, buffer, MAXBUF - 1);
+    i = SSL_read (ssl, buffer, MAX_INPUT_BUFFER - 1);
   }
   else {
 #endif
-    i = read (sd, buffer, MAXBUF - 1);
+    i = read (sd, buffer, MAX_INPUT_BUFFER - 1);
 #ifdef HAVE_SSL
   }
 #endif


More information about the Devel mailing list