Subject: buffer overrun in bind db

From: Alexey Antipov <[email-masked]>
Date: Thu, 31 Jan 2002 16:55:02 +0300

Zmailer version: 2.99.56-pre1
OS: FreeBSD 4.5-RELEASE i386, FreeBSD 3.4-RELEASE i386

Router subsystem crashes while processing messages with very long 'To:'
header with '\n' chars in.  This long headers may appear when quotation
symbol missed (square bracket). Whole part after left square bracket may
treated as host name (more longer than MAXNAME). This hostname passed to
search_res for resolving. After error occured this hostname copied into
staticaly allocated fixed size buffer h_errhost. Router segmentation
violation.

Maybe such combination of static buffer and strcpy can appear in other
parts of zmailer source tree.

Real-life letter that cause router crash may be found at 
http://www.lipetsk.ru/~rabbit/10866-42168

Simple script to reproduce bug
=====================================================================
#!/usr/bin/perl -w
use strict;
use Net::SMTP ();
use Net::Domain ();
use Socket;

my $host = $ARGV[0] || 'localhost';

my $smtp = Net::SMTP->new ($host, Timeout => 20, Debug => 0);

my $peerhost = gethostbyaddr ($smtp->peeraddr(), AF_INET) || "[" . inet_ntoa ($smtp->peeraddr ()) . "]";
my $myhost = Net::Domain::hostfqdn ();

my $from = "nobody@" . $myhost;
my $to = "postmaster@" . $peerhost;
$from = $to;

my $x = 'x' x 2000;
my $xx = "\t" . $x . "\n";
my $data = 'To: <x@[' . "\n" . $xx x 10;

$smtp->mail($from);
$smtp->to ($to);
$smtp->data ();
$smtp->datasend ($data);
$smtp->quit ();
=====================================================================

Sample FreeBSD patch:
=====================================================================
--- router/libdb/bind.c.orig	Fri Jan 25 21:04:52 2002
+++ router/libdb/bind.c	Fri Jan 25 21:07:18 2002
@@ -351,7 +351,7 @@
 	  fprintf(stderr,
 		  "search_res: CNAME chain length exceeded (%s)\n",
 		  host);
-	  strcpy(h_errhost, host);
+	  strlcpy(h_errhost, host, sizeof (h_errhost));
 	  h_errno = TRY_AGAIN;
 	  return NULL;
 	}
@@ -359,7 +359,7 @@
 			   (void *)&buf, sizeof(buf));
 	if (qlen < 0) {
 		fprintf(stderr, "search_res: res_mkquery (%s) failed\n", host);
-		strcpy(h_errhost, host);
+		strlcpy(h_errhost, host, sizeof (h_errhost));
 		h_errno = NO_RECOVERY;
 		return NULL;
 	}
@@ -371,7 +371,7 @@
 	    if (D_bind || _res.options & RES_DEBUG)
 	      fprintf(stderr,
 		      "search_res: res_send (%s) failed\n", host);
-	    strcpy(h_errhost, host);
+	    strlcpy(h_errhost, host, sizeof (h_errhost));
 	    h_errno = TRY_AGAIN;
 	    return NULL;
 	  }
@@ -393,7 +393,7 @@
 				h_errno = HOST_NOT_FOUND;
 				return NULL;
 			case SERVFAIL:
-				strcpy(h_errhost, host);
+				strlcpy(h_errhost, host, sizeof (h_errhost));
 				h_errno = TRY_AGAIN;
 				return NULL;
 			case NOERROR:
@@ -403,7 +403,7 @@
 			case FORMERR:
 			case NOTIMP:
 			case REFUSED:
-				strcpy(h_errhost, host);
+				strlcpy(h_errhost, host, sizeof (h_errhost));
 				h_errno = NO_RECOVERY;
 				return NULL;
 		}
@@ -590,7 +590,7 @@
 		fprintf(stderr,
 			"search_res: CNAME chain length exceeded (%s)\n",
 			host);
-		strcpy(h_errhost, host);	/* use strcat on purpose */
+		strlcpy(h_errhost, host, sizeof (h_errhost));	/* use strcat on purpose */
 		h_errno = TRY_AGAIN;
 		return NULL;
 	}
@@ -609,7 +609,7 @@
 		if (rval != NULL)
 			return rval;
 	}
-	strcpy(h_errhost, host);
+	strlcpy(h_errhost, host, sizeof (h_errhost));
 	return NULL;
 }
 
@@ -636,7 +636,7 @@
 		fprintf(stderr,
 			"search_res: CNAME chain length exceeded (%s)\n",
 			host);
-		strcpy(h_errhost, host);	/* use strcat on purpose */
+		strlcpy(h_errhost, host, sizeof (h_errhost));	/* use strcat on purpose */
 		h_errno = TRY_AGAIN;
 		return NULL;
 	}
@@ -647,7 +647,7 @@
 		if (D_bind || _res.options & RES_DEBUG)
 			fprintf(stderr,
 				"search_res: res_mkquery (%s) failed\n", host);
-		strcpy(h_errhost, host);
+		strlcpy(h_errhost, host, sizeof (h_errhost));
 		h_errno = NO_RECOVERY;
 		return NULL;
 	}
@@ -659,7 +659,7 @@
 	    if (D_bind || _res.options & RES_DEBUG)
 	      fprintf(stderr,
 		      "search_res: res_send (%s) failed\n", host);
-	    strcpy(h_errhost, host);
+	    strlcpy(h_errhost, host, sizeof (h_errhost));
 	    h_errno = TRY_AGAIN;
 	    return NULL;
 	  }
=====================================================================

-
To unsubscribe from this list: send the line "unsubscribe zmailer" in
the body of a message to [email-masked]

---

From: Matti Aarnio <[email-masked]>
Date: Thu, 31 Jan 2002 16:24:16 +0200

> parts of zmailer source tree.

   Not very many of those are left anymore.

   ...   hmm..   better not to hang myself,  'grep strcpy */*.c' shows
   quite many instances, which all need to be verified...

> http://www.lipetsk.ru/~rabbit/10866-42168

   ....
> Sample FreeBSD patch:

   The   strlcpy()  function appears to be FreeBSD specific.
   I implemented this a bit differently, which isn't fastest possible,
   but it is in exception path anyway.  (And DNS lookups are SLOW..)

....

-- 
/Matti Aarnio	<[email-masked]>
-
To unsubscribe from this list: send the line "unsubscribe zmailer" in
the body of a message to [email-masked]