Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 4 Dec 1999 19:41:04 -0800 (PST)
From:      Kris Kennaway <kris@hub.freebsd.org>
To:        audit@freebsd.org
Subject:   ctm_rmail holes (fwd)
Message-ID:  <Pine.BSF.4.21.9912041930430.3023-100000@hub.freebsd.org>

next in thread | raw e-mail | index | archive | help
I haven't heard any responses about this one yet..can someone give it the
twice-over? I'd kind of like to close this remote buffer overflow :-)

Kris

---------- Forwarded message ----------
Date: Fri, 26 Nov 1999 22:56:42 -0800 (PST)
From: Kris Kennaway <kris@hub.freebsd.org>
To: audit@freebsd.org
Subject: ctm_rmail holes

There are a couple of buffer overflows in ctm_rmail (part of ctm which
automatically decodes and applies deltas received via email) which look
like they could be exploitable by sending a malformed email. OpenBSD fixed
these, but for some reason Theo backed them out a few months ago during a
sync with our code.

Of course, a larger issue with CTM is that it looks like anyone can insert
their code into your source tree just by sending a delta to you, because
it does no authentication whatsoever of the contents except that it
applies cleanly :-(

The attached patch syncs with the OpenBSD security changes, and I've also
fixed a lock file race, and some command-line buffer overflows which
weren't likely to be security problems. However I can't test this, because
I don't have my machine set up to use CTM.

Comments, anyone?

Kris

Index: ctm_rmail.c
===================================================================
RCS file: /home/ncvs/src/usr.sbin/ctm/ctm_rmail/ctm_rmail.c,v
retrieving revision 1.14
diff -u -r1.14 ctm_rmail.c
--- ctm_rmail.c	1998/07/27 22:26:25	1.14
+++ ctm_rmail.c	1999/12/05 03:34:07
@@ -10,6 +10,11 @@
  * Maybe you should write some free software too.
  */
 
+#ifndef lint
+static const char rcsid[] =
+"$FreeBSD$";
+#endif /* not lint */
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <strings.h>
@@ -35,7 +40,8 @@
 void apply_complete(void);
 int read_piece(char *input_file);
 int combine_if_complete(char *delta, int pce, int npieces);
-int combine(char *delta, int npieces, char *dname, char *pname, char *tname);
+int combine(char *delta, int npieces, char *dname, char *pname, char *tname,
+	    int len);
 int decode_line(char *line, char *out_buf);
 int lock_file(char *name);
 
@@ -118,14 +124,14 @@
 /*
  * Construct the file name of a piece of a delta.
  */
-#define mk_piece_name(fn,d,p,n)	\
-    sprintf((fn), "%s/%s+%03d-%03d", piece_dir, (d), (p), (n))
+#define mk_piece_name(fn,l,d,p,n)	\
+    snprintf((fn), l, "%s/%s+%03d-%03d", piece_dir, (d), (p), (n))
 
 /*
  * Construct the file name of an assembled delta.
  */
-#define mk_delta_name(fn,d)	\
-    sprintf((fn), "%s/%s", delta_dir, (d))
+#define mk_delta_name(fn,l,d)	\
+    snprintf((fn), l, "%s/%s", delta_dir, (d))
 
 /*
  * If the next required delta is now present, let ctm lunch on it and any
@@ -149,22 +155,22 @@
      * Grab a lock on the ctm mutex file so that we can be sure we are
      * working alone, not fighting another ctm_rmail!
      */
-    strcpy(fname, delta_dir);
-    strcat(fname, "/.mutex_apply");
+    strlcpy(fname, delta_dir, sizeof(fname));
+    strlcat(fname, "/.mutex_apply", sizeof(fname));
     if ((lfd = lock_file(fname)) < 0)
 	return;
 
     /*
      * Find out which delta ctm needs next.
      */
-    sprintf(fname, "%s/%s", base_dir, CTM_STATUS);
+    snprintf(fname, sizeof(fname), "%s/%s", base_dir, CTM_STATUS);
     if ((fp = fopen(fname, "r")) == NULL)
 	{
 	close(lfd);
 	return;
 	}
 
-    i = fscanf(fp, "%s %d %c", class, &dn, junk);
+    i = fscanf(fp, "%19s %d %c", class, &dn, junk);
     fclose(fp);
     if (i != 2)
 	{
@@ -178,7 +184,7 @@
     here[0] = '\0';
     if (delta_dir[0] != '/')
 	{
-	getcwd(here, sizeof(here)-1);
+	getcwd(here, sizeof(here)-2);
 	i = strlen(here) - 1;
 	if (i >= 0 && here[i] != '/')
 	    {
@@ -192,13 +198,13 @@
      */
     for (;;)
 	{
-	sprintf(delta, "%s.%04d.gz", class, ++dn);
-	mk_delta_name(fname, delta);
+	snprintf(delta, sizeof(delta), "%s.%04d.gz", class, ++dn);
+	mk_delta_name(fname, sizeof(fname), delta);
 
 	if (stat(fname, &sb) < 0)
 	    break;
 
-	sprintf(buf, "(cd %s && ctm %s%s%s%s) 2>&1", base_dir,
+	snprintf(buf, sizeof(buf), "(cd %s && ctm %s%s%s%s) 2>&1", base_dir,
 				set_time ? "-u " : "",
 				apply_verbose ? "-v " : "", here, fname);
 	if ((ctm = popen(buf, "r")) == NULL)
@@ -294,7 +300,7 @@
 	    {
 	    char *s;
 
-	    if (sscanf(line, "CTM_MAIL BEGIN %s %d %d %c",
+	    if (sscanf(line, "CTM_MAIL BEGIN %29s %d %d %c",
 		    delta, &pce, &npieces, junk) != 3)
 		continue;
 
@@ -302,17 +308,17 @@
 		*s = '_';
 
 	    got_one++;
-	    strcpy(tname, piece_dir);
-	    strcat(tname, "/p.XXXXXX");
-	    if ((ofd = mkstemp(tname)) < 0)
+	    strlcpy(tname, piece_dir, sizeof(tname));
+	    strlcat(tname, "/p.XXXXXXXXXX", sizeof(tname));
+	    if ((ofd = mkstemp(tname)) == -1 ||
+		(ofp = fdopen(ofd, "w")) == NULL)
 		{
-		err("*mkstemp: '%s'", tname);
-		status++;
-		continue;
-		}
-	    if ((ofp = fdopen(ofd, "w")) == NULL)
-		{
-		err("cannot open '%s' for writing", tname);
+		if (ofd != -1) {
+		    err("cannot open '%s' for writing", tname);
+		    close(ofd);
+		    }
+		else
+		    err("*mkstemp: '%s'", tname);
 		status++;
 		continue;
 		}
@@ -349,7 +355,7 @@
 		continue;
 		}
 
-	    mk_piece_name(pname, delta, pce, npieces);
+	    mk_piece_name(pname, sizeof(pname), delta, pce, npieces);
 	    if (rename(tname, pname) < 0)
 		{
 		err("*rename: '%s' to '%s'", tname, pname);
@@ -438,8 +444,8 @@
      */
     if (npieces == 1)
 	{
-	mk_delta_name(dname, delta);
-	mk_piece_name(pname, delta, 1, 1);
+	mk_delta_name(dname, sizeof(dname), delta);
+	mk_piece_name(pname, sizeof(pname), delta, 1, 1);
 	if (rename(pname, dname) == 0)
 	    {
 	    err("%s complete", delta);
@@ -451,8 +457,8 @@
      * Grab a lock on the reassembly mutex file so that we can be sure we are
      * working alone, not fighting another ctm_rmail!
      */
-    strcpy(tname, delta_dir);
-    strcat(tname, "/.mutex_build");
+    strlcpy(tname, delta_dir, sizeof(tname));
+    strlcat(tname, "/.mutex_build", sizeof(tname));
     if ((lfd = lock_file(tname)) < 0)
 	return 0;
 
@@ -465,7 +471,7 @@
 	{
 	if (i == pce)
 	    continue;
-	mk_piece_name(pname, delta, i, npieces);
+	mk_piece_name(pname, sizeof(pname), delta, i, npieces);
 	if (stat(pname, &sb) < 0)
 	    {
 	    close(lfd);
@@ -477,7 +483,7 @@
      * Stick them together.  Let combine() use our file name buffers, since
      * we're such good buddies. :-)
      */
-    e = combine(delta, npieces, dname, pname, tname);
+    e = combine(delta, npieces, dname, pname, tname, sizeof(tname));
     close(lfd);
     return e;
     }
@@ -490,23 +496,24 @@
  * happened to by lying around in the calling routine.  Waste not, want not!
  */
 int
-combine(char *delta, int npieces, char *dname, char *pname, char *tname)
+combine(char *delta, int npieces, char *dname, char *pname, char *tname, int len)
     {
     FILE *dfp, *pfp;
     int dfd;
     int i, n, e;
     char buf[BUFSIZ];
 
-    strcpy(tname, delta_dir);
-    strcat(tname, "/d.XXXXXX");
-    if ((dfd = mkstemp(tname)) < 0)
-	{
-	err("*mkstemp: '%s'", tname);
-	return 0;
-	}
-    if ((dfp = fdopen(dfd, "w")) == NULL)
-	{
-	err("cannot open '%s' for writing", tname);
+    strlcpy(tname, delta_dir, len);
+    strlcat(tname, "/d.XXXXXXXXXX", len);
+    if ((dfd = mkstemp(tname)) == -1 ||
+	(dfp = fdopen(dfd, "w")) == NULL)
+	{
+	if (dfd != -1) {
+	    close(dfd);
+	    err("cannot open '%s' for writing", tname);
+	    }
+	else
+	    err("*mktemp: '%s'", tname);
 	return 0;
 	}
 
@@ -515,7 +522,7 @@
      */
     for (i = 1; i <= npieces; i++)
 	{
-	mk_piece_name(pname, delta, i, npieces);
+	mk_piece_name(pname, len, delta, i, npieces);
 	if ((pfp = fopen(pname, "r")) == NULL)
 	    {
 	    err("cannot open '%s' for reading", pname);
@@ -545,7 +552,7 @@
 	return 0;
 	}
 
-    mk_delta_name(dname, delta);
+    mk_delta_name(dname, len, delta);
     if (rename(tname, dname) < 0)
 	{
 	err("*rename: '%s' to '%s'", tname, dname);
@@ -558,7 +565,7 @@
      */
     for (i = 1; i <= npieces; i++)
 	{
-	mk_piece_name(pname, delta, i, npieces);
+	mk_piece_name(pname, len, delta, i, npieces);
 	if (unlink(pname) < 0)
 	    err("*unlink: '%s'", pname);
 	}
@@ -648,15 +655,9 @@
     {
     int lfd;
 
-    if ((lfd = open(name, O_WRONLY|O_CREAT, 0600)) < 0)
+    if ((lfd = open(name, O_WRONLY|O_CREAT|O_EXLOCK, 0600)) < 0)
 	{
 	err("*open: '%s'", name);
-	return -1;
-	}
-    if (flock(lfd, LOCK_EX) < 0)
-	{
-	close(lfd);
-	err("*flock: '%s'", name);
 	return -1;
 	}
     return lfd;



To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-audit" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.BSF.4.21.9912041930430.3023-100000>