from cinap — add -t flag, unconditional TLS. Reference: /n/atom/patch/applied2013/smtp-t Date: Tue Jun 18 03:06:10 CES 2013 Signed-off-by: quanstro@quanstro.net --- /sys/man/8/smtp Tue Jun 18 03:05:34 2013 +++ /sys/man/8/smtp Tue Jun 18 03:05:34 2013 @@ -6,7 +6,7 @@ .ti -0.5i .B upas/smtp [ -.B -aAdfips +.B -aAdfipst ] [ .B -b .I busted-mx @@ -118,6 +118,9 @@ .B -s if the server supports the ESMTP extension to use TLS encryption, turn it on for this session. See RFC3207 for details. +.TP +.B -t +preemtively establish TLS connection before SMTP handshake (SMTPS). .TP .B -u specify a user name to be used in authentication. The default name is --- /sys/src/cmd/upas/smtp/smtp.c Tue Jun 18 03:05:34 2013 +++ /sys/src/cmd/upas/smtp/smtp.c Tue Jun 18 03:05:34 2013 @@ -6,6 +6,7 @@ #include static char* connect(char*, Mx*); +static char* wraptls(void); static char* dotls(char*); static char* doauth(char*); @@ -52,6 +53,7 @@ char *farend; /* system we are trying to send to */ char *user; /* user we are authenticating as, if authenticating */ char hostdomain[256]; +Mx *tmmx; /* global for timeout */ Biobuf bin; Biobuf bout; @@ -64,41 +66,13 @@ Mx *mx; mx = va_arg(fmt->args, Mx*); - if(mx == nil || mx->host[0] == 0 && mx->ip[0] == 0) + if(mx == nil || mx->host[0] == 0) return fmtstrcpy(fmt, ""); - if((fmt->flags & FmtSharp) == 0) - fmtstrcpy(fmt, "("); - if(mx->valid == 0) - fmtstrcpy(fmt, "!"); - fmtprint(fmt, "%s:%s;pref=%d", mx->host, mx->ip, mx->pref); - if((fmt->flags & FmtSharp) == 0) - fmtstrcpy(fmt, ")"); - return 0; + else + return fmtprint(fmt, "(%s:%s)", mx->host, mx->ip); } #pragma varargck type "D" Mx* -int -Lfmt(Fmt *fmt) -{ - int i; - Mxtab *mx; - - mx = va_arg(fmt->args, Mxtab*); - if(mx == nil || mx->nmx == 0) - return fmtstrcpy(fmt, ""); - fmtstrcpy(fmt, "("); - for(i = 0; inmx; i++){ - if(i == mx->pmx) - fmtprint(fmt, "*"); - fmtprint(fmt, "%#D", mx->mx+i); - if(++i == mx->nmx) - break; - fmtprint(fmt, " "); - } - return fmtstrcpy(fmt, ")"); -} -#pragma varargck type "L" Mxtab* - char* deliverytype(void) { @@ -110,7 +84,7 @@ void usage(void) { - fprint(2, "usage: smtp [-aAdfips] [-b busted-mx] [-g gw] [-h host] " + fprint(2, "usage: smtp [-aAdfipst] [-b busted-mx] [-g gw] [-h host] " "[-u user] [.domain] net!host[!service] sender rcpt-list\n"); exits(Giveup); } @@ -118,7 +92,7 @@ int timeout(void *, char *msg) { - syslog(0, "smtp.fail", "%s interrupt: %s: %s", deliverytype(), farend, msg); + syslog(0, "smtp.fail", "%s interrupt: %s: %s %D", deliverytype(), farend, msg, tmmx); if(strstr(msg, "alarm")){ fprint(2, "smtp timeout: connection to %s timed out\n", farend); if(quitting) @@ -128,7 +102,7 @@ if(strstr(msg, "closed pipe")){ fprint(2, "smtp timeout: connection closed to %s\n", farend); if(quitting){ - syslog(0, "smtp.fail", "%s closed pipe to %s", deliverytype(), farend); + syslog(0, "smtp.fail", "%s closed pipe to %s %D", deliverytype(), farend, tmmx); _exits(quitrv); } /* call _exits() to prevent Bio from trying to flush closed pipe */ @@ -152,7 +126,7 @@ main(int argc, char **argv) { char *phase, *addr, *rv, *trv, *host, *domain; - char **errs, hellodomain[256]; + char **errs, *p, *e, hellodomain[256], allrx[512]; int i, ok, rcvrs, bustedmx; String *from, *fromm, *sender; Mx mx; @@ -161,7 +135,6 @@ quotefmtinstall(); mailfmtinstall(); /* 2047 encoding */ fmtinstall('D', Dfmt); - fmtinstall('L', Lfmt); errs = malloc(argc*sizeof(char*)); reply = s_new(); host = 0; @@ -201,6 +174,9 @@ case 's': trysecure = 1; break; + case 't': + trysecure = 2; + break; case 'u': user = EARGF(usage()); break; @@ -257,6 +233,7 @@ /* * send the mail */ + rcvrs = 0; phase = ""; USED(phase); /* just in case */ if(filter){ @@ -273,10 +250,11 @@ if((rv = connect(addr, &mx)) != 0) exits(rv); + tmmx = &mx; /* 10 minutes to get through the initial handshake */ atnotify(timeout, 1); alarm(10*alarmscale); - if((rv = hello(hellodomain, 0)) != 0){ + if((rv = hello(hellodomain, trysecure > 1)) != 0){ phase = "hello"; goto error; } @@ -287,7 +265,6 @@ } ok = 0; - rcvrs = 0; /* if any rcvrs are ok, we try to send the message */ phase = "rcptto"; for(i = 0; i < argc; i++){ @@ -339,9 +316,18 @@ * here when all rcvrs failed */ error: + alarm(0); removenewline(s_to_c(reply)); - syslog(0, "smtp.fail", "%s to %s %D %s failed: %s", - deliverytype(), addr, &mx, phase, s_to_c(reply)); + if(rcvrs > 0){ + p = allrx; + e = allrx + sizeof allrx; + seprint(p, e, "to "); + for(i = 0; i < rcvrs - 1; i++) + p = seprint(p, e, "%s,", argv[i]); + seprint(p, e, "%s ", argv[i]); + } + syslog(0, "smtp.fail", "%s %s at %s %D %s failed: %s", + deliverytype(), allrx, addr, &mx, phase, s_to_c(reply)); fprint(2, "%s connect to %s %D %s:\n%s\n", thedate(), addr, &mx, phase, s_to_c(reply)); if(!filter) quit(rv); @@ -352,18 +338,17 @@ * connect to the remote host */ static char * -connect(char* net, Mx *x) +connect(char* net, Mx *mx) { - char buf[1024]; + char buf[256]; int fd; - Mxtab mx; - memset(x, 0, sizeof *x); - fd = mxdial0(net, ddomain, gdomain, &mx); + fd = mxdial(net, ddomain, gdomain, mx); + if(fd < 0){ rerrstr(buf, sizeof buf); - Bprint(&berr, "smtp: %s (%s) %L\n", buf, net, &mx); - syslog(0, "smtp.fail", "%s %s (%s) %L", deliverytype(), buf, net, &mx); + Bprint(&berr, "smtp: %s (%s) %D\n", buf, net, mx); + syslog(0, "smtp.fail", "%s %s (%s) %D", deliverytype(), buf, net, mx); if(strstr(buf, "illegal") || strstr(buf, "unknown") || strstr(buf, "can't translate")) @@ -371,9 +356,6 @@ else return Retry; } - *x = mx.mx[mx.pmx]; - /* mxtabfree(&mx); but for .netdir */ - Binit(&bin, fd, OREAD); fd = dup(fd, -1); Binit(&bout, fd, OWRITE); @@ -383,13 +365,8 @@ static char smtpthumbs[] = "/sys/lib/tls/smtp"; static char smtpexclthumbs[] = "/sys/lib/tls/smtp.exclude"; -/* - * exchange names with remote host, attempt to - * enable encryption and optionally authenticate. - * not fatal if we can't. - */ -static char * -dotls(char *me) +static char* +wraptls(void) { char *h; int fd; @@ -397,24 +374,21 @@ TLSconn *c; Thumbprint *goodcerts; - c = mallocz(sizeof(*c), 1); /* Note: not freed on success */ + c = mallocz(sizeof(*c), 1); if(c == nil) return Giveup; - dBprint("STARTTLS\r\n"); - if(getreply() != 2) - return Giveup; - fd = tlsClient(Bfildes(&bout), c); if(fd < 0){ syslog(0, "smtp", "tlsClient to %q: %r", ddomain); + free(c); return Giveup; } goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs); if(goodcerts == nil){ - free(c); - close(fd); syslog(0, "smtp", "bad thumbprints in %s", smtpthumbs); + close(fd); + free(c); return Giveup; /* how to recover? TLS is started */ } @@ -422,8 +396,6 @@ sha1(c->cert, c->certlen, hash, nil); if(!okThumbprint(hash, goodcerts)){ /* TODO? if not excluded, add hash to thumb list */ - free(c); - close(fd); h = malloc(2*sizeof hash + 1); if(h != nil){ enc16(h, 2*sizeof hash + 1, hash, sizeof hash); @@ -433,9 +405,12 @@ h, ddomain); free(h); } + close(fd); + free(c); return Giveup; /* how to recover? TLS is started */ } freeThumbprints(goodcerts); + free(c); Bterm(&bin); Bterm(&bout); @@ -448,6 +423,27 @@ Binit(&bout, fd, OWRITE); syslog(0, "smtp", "started TLS to %q", ddomain); + return nil; +} + +/* + * exchange names with remote host, attempt to + * enable encryption and optionally authenticate. + * not fatal if we can't. + */ +static char * +dotls(char *me) +{ + char *err; + + dBprint("STARTTLS\r\n"); + if (getreply() != 2) + return Giveup; + + err = wraptls(); + if (err != nil) + return err; + return(hello(me, 1)); } @@ -464,8 +460,6 @@ return Retry; p = s_to_c(reply) + 4; l = dec64((uchar*)ch, sizeof ch, p, strlen(p)); - if(l == -1) - return Retry; ch[l] = 0; e = abuf + sizeof abuf;