Browse Source

Plan 9 from Bell Labs 2013-08-22

David du Colombier 10 years ago
parent
commit
acf19f7cc0

+ 6 - 1
sys/man/8/smtp

@@ -6,7 +6,7 @@ smtp, smtpd \-  mail transport
 .ti -0.5i
 .B upas/smtp
 [
-.B -aAdfips
+.B -aAdfiops
 ] [
 .B -b
 .I busted-mx
@@ -108,6 +108,11 @@ under
 .BR -a ,
 authenticate even if we can't start TLS.
 .TP
+.B -o
+under
+.BR -s ,
+use TLS even if we don't know the remote system.
+.TP
 .B -p
 ping: just verify that the users named in the
 .I rcpt-list

+ 45 - 20
sys/src/cmd/upas/smtp/mxdial.c

@@ -4,28 +4,29 @@
 
 enum
 {
-	Nmx=	16,
+	Nmx=		16,
 	Maxstring=	256,
+	Maxipstr=	8*5,		/* ipv6 */
 };
 
 typedef struct Mx	Mx;
 struct Mx
 {
-	char host[256];
-	char ip[24];
-	int pref;
+	char	host[Maxstring];
+	char	ip[Maxipstr];
+	int	pref;
 };
 
 char	*bustedmxs[Maxbustedmx];
 Ndb *db;
 
+static Mx mx[Nmx];
+
+static int	callmx(DS*, char*, char*);
+static int	compar(void*, void*);
+static void	expand_meta(DS *ds);
 static int	mxlookup(DS*, char*);
 static int	mxlookup1(DS*, char*);
-static int	compar(void*, void*);
-static int	callmx(DS*, char*, char*);
-static void expand_meta(DS *ds);
-
-static Mx mx[Nmx];
 
 int
 mxdial(char *addr, char *ddomain, char *gdomain)
@@ -80,6 +81,12 @@ timedwrite(int fd, void *buf, long len, long ms)
 	return n;
 }
 
+static int
+isloopback(char *ip)
+{
+	return strcmp(ip, "127.0.0.1") == 0 || strcmp(ip, "::1") == 0;
+}
+
 /*
  *  take an address and return all the mx entries for it,
  *  most preferred first
@@ -88,6 +95,7 @@ static int
 callmx(DS *ds, char *dest, char *domain)
 {
 	int fd, i, nmx;
+	char *ip;
 	char addr[Maxstring];
 
 	/* get a list of mx entries */
@@ -102,14 +110,21 @@ callmx(DS *ds, char *dest, char *domain)
 		return dial(dest, 0, 0, 0);
 	}
 
-	/* refuse to honor loopback addresses given by dns */
-	for(i = 0; i < nmx; i++)
-		if(strcmp(mx[i].ip, "127.0.0.1") == 0){
+	/* refuse to honor loopback addresses given by dns.  catch \n too. */
+	for(i = 0; i < nmx; i++) {
+		ip = mx[i].ip;
+		if(strchr(ip, '\n') != nil){
+			if(debug)
+				fprint(2, "mxlookup ip contains newline\n");
+			werrstr("illegal: newline in mail server ip");
+			return -1;
+		}else if(isloopback(ip)){
 			if(debug)
 				fprint(2, "mxlookup returns loopback\n");
-			werrstr("illegal: domain lists 127.0.0.1 as mail server");
+			werrstr("illegal: domain lists %s as mail server", ip);
 			return -1;
 		}
+	}
 
 	/* sort by preference */
 	if(nmx > 1)
@@ -122,6 +137,11 @@ callmx(DS *ds, char *dest, char *domain)
 				fprint(2, "mxdial skipping busted mx %s\n",
 					mx[i].host);
 			continue;
+		}else if(isloopback(mx[i].host)){
+			if(debug)
+				fprint(2, "host ip %s is loopback\n",
+					mx[i].host);
+			continue;
 		}
 		snprint(addr, sizeof(addr), "%s/%s!%s!%s", ds->netdir, ds->proto,
 			mx[i].host, ds->service);
@@ -170,8 +190,9 @@ static int
 mxlookup1(DS *ds, char *domain)
 {
 	int i, n, fd, nmx;
-	char buf[1024], dnsname[Maxstring];
+	char buf[Maxdomain], dnsname[Maxstring];
 	char *fields[4];
+	Mx *mxp;
 
 	snprint(dnsname, sizeof dnsname, "%s/dns", ds->netdir);
 
@@ -202,6 +223,7 @@ mxlookup1(DS *ds, char *domain)
 		 */
 		seek(fd, 0, 0);
 		while(nmx < Nmx && (n = read(fd, buf, sizeof buf-1)) > 0){
+			mxp = &mx[nmx];
 			buf[n] = 0;
 			if(debug)
 				fprint(2, "dns mx: %s\n", buf);
@@ -212,8 +234,9 @@ mxlookup1(DS *ds, char *domain)
 			if(strchr(domain, '.') == 0)
 				strcpy(domain, fields[0]);
 
-			strncpy(mx[nmx].host, fields[3], sizeof(mx[n].host)-1);
-			mx[nmx].pref = atoi(fields[2]);
+			strncpy(mxp->host, fields[3], sizeof mxp->host - 1);
+			mxp->host[sizeof mxp->host - 1] = '\0';
+			mxp->pref = atoi(fields[2]);
 			nmx++;
 		}
 		if(debug)
@@ -238,9 +261,10 @@ mxlookup1(DS *ds, char *domain)
 	 * look up all ip addresses
 	 */
 	for(i = 0; i < nmx; i++){
+		mxp = &mx[i];
 		seek(fd, 0, 0);
-		snprint(buf, sizeof buf, "%s ip", mx[i].host);
-		mx[i].ip[0] = 0;
+		snprint(buf, sizeof buf, "%s ip", mxp->host);
+		mxp->ip[0] = 0;
 		/*
 		 * don't hang indefinitely in the write to /net/dns.
 		 */
@@ -252,13 +276,14 @@ mxlookup1(DS *ds, char *domain)
 		buf[n] = 0;
 		if(getfields(buf, fields, 4, 1, " \t") < 3)
 			goto no;
-		strncpy(mx[i].ip, fields[2], sizeof(mx[i].ip)-1);
+		strncpy(mxp->ip, fields[2], sizeof mxp->ip - 1);
+		mxp->ip[sizeof mxp->ip - 1] = '\0';
 		continue;
 
 	no:
 		/* remove mx[i] and go around again */
 		nmx--;
-		mx[i] = mx[nmx];
+		*mxp = mx[nmx];
 		i--;
 	}
 	return nmx;

+ 2 - 1
sys/src/cmd/upas/smtp/rmtdns.c

@@ -1,11 +1,12 @@
 #include	"common.h"
+#include	"smtp.h"
 #include	<ndb.h>
 
 int
 rmtdns(char *net, char *path)
 {
 	int fd, n, nb, r;
-	char *domain, *cp, buf[1024];
+	char *domain, *cp, buf[Maxdomain + 5];
 
 	if(net == 0 || path == 0)
 		return 0;

+ 41 - 26
sys/src/cmd/upas/smtp/smtp.c

@@ -44,9 +44,10 @@ int	ping;
 int	quitting;	/* when error occurs in quit */
 int	tryauth;	/* Try to authenticate, if supported */
 int	trysecure;	/* Try to use TLS if the other side supports it */
+int	okunksecure;	/* okay to use TLS to unknown servers */
 
 char	*quitrv;	/* deferred return value when in quit */
-char	ddomain[1024];	/* domain name of destination machine */
+char	ddomain[Maxdomain]; /* domain name of destination machine */
 char	*gdomain;	/* domain name of gateway */
 char	*uneaten;	/* first character after rfc822 headers */
 char	*farend;	/* system we are trying to send to */
@@ -144,6 +145,9 @@ main(int argc, char **argv)
 	case 'i':
 		insecure = 1;
 		break;
+	case 'o':
+		okunksecure = 1;
+		break;
 	case 'p':
 		alarmscale = 10*1000;	/* tens of seconds */
 		ping = 1;
@@ -313,6 +317,37 @@ connect(char* net)
 static char smtpthumbs[] =	"/sys/lib/tls/smtp";
 static char smtpexclthumbs[] =	"/sys/lib/tls/smtp.exclude";
 
+static char *
+ckthumbs(TLSconn *c)
+{
+	Thumbprint *goodcerts;
+	char *h, *err;
+	uchar hash[SHA1dlen];
+
+	err = nil;
+	goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs);
+	if (goodcerts == nil) {
+		if (!okunksecure)
+			syslog(0, "smtp", "bad thumbprints in %s", smtpthumbs);
+		return Giveup;		/* how to recover? TLS is started */
+	}
+
+	/* compute sha1 hash of remote's certificate, see if we know it */
+	sha1(c->cert, c->certlen, hash, nil);
+	if (!okThumbprint(hash, goodcerts) && !okunksecure) {
+		h = malloc(2*sizeof hash + 1);
+		if (h != nil) {
+			enc16(h, 2*sizeof hash + 1, hash, sizeof hash);
+			syslog(0, "smtp", "remote cert. has bad thumbprint: "
+				"x509 sha1=%s server=%q", h, ddomain);
+			free(h);
+		}
+		err = Giveup;		/* how to recover? TLS is started */
+	}
+	freeThumbprints(goodcerts);
+	return err;
+}
+
 /*
  *  exchange names with remote host, attempt to
  *  enable encryption and optionally authenticate.
@@ -322,10 +357,8 @@ static char *
 dotls(char *me)
 {
 	TLSconn *c;
-	Thumbprint *goodcerts;
-	char *h;
+	char *err;
 	int fd;
-	uchar hash[SHA1dlen];
 
 	c = mallocz(sizeof(*c), 1);	/* Note: not freed on success */
 	if (c == nil)
@@ -340,32 +373,14 @@ dotls(char *me)
 		syslog(0, "smtp", "tlsClient to %q: %r", ddomain);
 		return Giveup;
 	}
-	goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs);
-	if (goodcerts == nil) {
-		free(c);
-		close(fd);
-		syslog(0, "smtp", "bad thumbprints in %s", smtpthumbs);
-		return Giveup;		/* how to recover? TLS is started */
-	}
 
-	/* compute sha1 hash of remote's certificate, see if we know it */
-	sha1(c->cert, c->certlen, hash, nil);
-	if (!okThumbprint(hash, goodcerts)) {
-		/* TODO? if not excluded, add hash to thumb list */
+	err = ckthumbs(c);
+	if (err && !okunksecure) {
 		free(c);
 		close(fd);
-		h = malloc(2*sizeof hash + 1);
-		if (h != nil) {
-			enc16(h, 2*sizeof hash + 1, hash, sizeof hash);
-			// fprint(2, "x509 sha1=%s", h);
-			syslog(0, "smtp",
-		"remote cert. has bad thumbprint: x509 sha1=%s server=%q",
-				h, ddomain);
-			free(h);
-		}
-		return Giveup;		/* how to recover? TLS is started */
+		return err;		/* how to recover? TLS is started */
 	}
-	freeThumbprints(goodcerts);
+
 	Bterm(&bin);
 	Bterm(&bout);
 

+ 10 - 9
sys/src/cmd/upas/smtp/smtp.h

@@ -1,5 +1,6 @@
 enum {
 	Maxbustedmx = 100,
+	Maxdomain = 1024,
 };
 
 typedef struct Node Node;
@@ -46,22 +47,22 @@ extern int	debug;
 extern int	messageid;
 extern char	*bustedmxs[Maxbustedmx];
 
-Node*	anonymous(Node*);
 Node*	address(Node*);
+Node*	anonymous(Node*);
 int	badfieldname(Node*);
 Node*	bang(Node*, Node*);
-Node*	colon(Node*, Node*);
 int	cistrcmp(char*, char*);
+Node*	colon(Node*, Node*);
+void	dial_string_parse(char*, DS*);
+void	freefield(Field*);
+void	freenode(Node*);
 Node*	link2(Node*, Node*);
 Node*	link3(Node*, Node*, Node*);
-void	freenode(Node*);
+int	mxdial(char*, char*, char*);
 void	newfield(Node*, int);
-void	freefield(Field*);
+Node*	whiten(Node*);
+void	yycleanup(void);
 void	yyinit(char*, int);
-int	yyparse(void);
 int	yylex(void);
+int	yyparse(void);
 String*	yywhite(void);
-Node*	whiten(Node*);
-void	yycleanup(void);
-int	mxdial(char*, char*, char*);
-void	dial_string_parse(char*, DS*);