On Wed, Jan 14, 2015 at 06:08:16PM -0500, Wietse Venema wrote: > I have prelimiary code almost working with little code.
You likely did not patch posttls-finger(1). Patch below my signature, sample output: $ ./bin/posttls-finger -F ~/capath/bundle.pem -cw -lsecure "[smtp.ntlworld.com]:465" posttls-finger: smtp.ntlworld.com[62.254.26.221]:465: Matched subjectAltName: smtp.ntlworld.com posttls-finger: smtp.ntlworld.com[62.254.26.221]:465 CommonName smtp.ntlworld.com posttls-finger: smtp.ntlworld.com[62.254.26.221]:465: subject_CN=smtp.ntlworld.com, issuer_CN=VeriSign Class 3 Secure Server CA - G3, fingerprint=C7:05:F7:92:FB:51:FB:A6:4A:BB:42:C3:92:83:A2:E4:85:11:86:96, pkey_fingerprint=88:FD:3B:85:89:8A:4F:60:01:01:2E:91:EB:11:20:BB:F3:4C:E8:8F posttls-finger: Verified TLS connection established to smtp.ntlworld.com[62.254.26.221]:465: TLSv1.1 with cipher RC4-SHA (128/128 bits) I guess this site's administrators really liked the 1990's, SMTPS with RC4-SHA forever! :-) -- Viktor. diff --git a/src/posttls-finger/posttls-finger.c b/src/posttls-finger/posttls-finger.c index ac4bc87..6841f90 100644 --- a/src/posttls-finger/posttls-finger.c +++ b/src/posttls-finger/posttls-finger.c @@ -228,6 +228,12 @@ /* .IP "\fB-v\fR" /* Enable verose Postfix logging. Specify more than once to increase /* the level of verbose logging. +/* .IP "\fB-w\fR" +/* Enable outgoing TLS wrapper mode, or SMTPS support. This is typically +/* provided on port 465 by servers that are compatible with the ad-hoc +/* SMTP in SSL protocol, rather than the standard STARTTLS protocol. +/* The destination \fIdomain\fR:\fIport\fR should of course provide such +/* a service. /* .IP "[\fBinet:\fR]\fIdomain\fR[:\fIport\fR]" /* Connect via TCP to domain \fIdomain\fR, port \fIport\fR. The default /* port is \fBsmtp\fR (or 24 with LMTP). With SMTP an MX lookup is @@ -421,6 +427,7 @@ typedef struct STATE { VSTRING *buffer; /* Response buffer */ VSTREAM *stream; /* Open connection */ int level; /* TLS security level */ + int wrapper_mode; /* SMTPS support */ #ifdef USE_TLS char *mdalg; /* fingerprint digest algorithm */ char *CAfile; /* Trusted public CAs */ @@ -547,6 +554,34 @@ static char *exception_text(int except) } } +/* greeting - read server's 220 greeting */ + +static int greeting(STATE *state) +{ + VSTREAM *stream = state->stream; + int except; + RESPONSE *resp; + + /* + * Prepare for disaster. + */ + smtp_stream_setup(stream, conn_tmout, 1); + if ((except = vstream_setjmp(stream)) != 0) { + msg_info("%s while reading server greeting", exception_text(except)); + return (1); + } + + /* + * Read and parse the server's SMTP greeting banner. + */ + if (((resp = response(state, 1))->code / 100) != 2) { + msg_info("SMTP service not available: %d %s", resp->code, resp->str); + return (1); + } + + return (0); +} + /* ehlo - send EHLO/LHLO */ static RESPONSE *ehlo(STATE *state) @@ -647,25 +682,27 @@ static int starttls(STATE *state) VSTREAM *stream = state->stream; TLS_CLIENT_START_PROPS tls_props; - /* SMTP stream with deadline timeouts */ - smtp_stream_setup(stream, smtp_tmout, 1); - if ((except = vstream_setjmp(stream)) != 0) { - msg_fatal("%s while sending STARTTLS", exception_text(except)); - return (1); - } - command(state, state->pass == 1, "STARTTLS"); + if (state->wrapper_mode == 0) { + /* SMTP stream with deadline timeouts */ + smtp_stream_setup(stream, smtp_tmout, 1); + if ((except = vstream_setjmp(stream)) != 0) { + msg_fatal("%s while sending STARTTLS", exception_text(except)); + return (1); + } + command(state, state->pass == 1, "STARTTLS"); - resp = response(state, state->pass == 1); - if (resp->code / 100 != 2) { - msg_info("STARTTLS rejected: %d %s", resp->code, resp->str); - return (1); - } + resp = response(state, state->pass == 1); + if (resp->code / 100 != 2) { + msg_info("STARTTLS rejected: %d %s", resp->code, resp->str); + return (1); + } - /* - * Discard any plain-text data that may be piggybacked after the server's - * 220 STARTTLS reply. Should we abort the session instead? - */ - vstream_fpurge(stream, VSTREAM_PURGE_READ); + /* + * Discard any plain-text data that may be piggybacked after the server's + * 220 STARTTLS reply. Should we abort the session instead? + */ + vstream_fpurge(stream, VSTREAM_PURGE_READ); + } #define ADD_EXCLUDE(vstr, str) \ do { \ @@ -718,6 +755,10 @@ static int starttls(STATE *state) state->stream = 0; return (1); } + + if (state->wrapper_mode && greeting(state) != 0) + return (1); + if (state->pass == 1) { ehlo(state); if (!TLS_CERT_IS_PRESENT(state->tls_context)) @@ -743,42 +784,28 @@ static int doproto(STATE *state) int except; int n; char *lines; - char *words; + char *words = 0; char *word; - /* - * Prepare for disaster. - */ - smtp_stream_setup(stream, conn_tmout, 1); - if ((except = vstream_setjmp(stream)) != 0) - msg_fatal("%s while reading server greeting", exception_text(except)); - - /* - * Read and parse the server's SMTP greeting banner. - */ - if (((resp = response(state, 1))->code / 100) != 2) { - msg_info("SMTP service not available: %d %s", resp->code, resp->str); - return (1); - } - - /* - * Send the standard greeting with our hostname - */ - if ((resp = ehlo(state)) == 0) - return (1); + if (! state->wrapper_mode) { + if (greeting(state) != 0) + return (1); + if ((resp = ehlo(state)) == 0) + return (1); - lines = resp->str; - for (n = 0; (words = mystrtok(&lines, "\n")) != 0; ++n) { - if ((word = mystrtok(&words, " \t=")) != 0) { - if (n == 0) - state->helo = mystrdup(word); - if (strcasecmp(word, "STARTTLS") == 0) - break; + lines = resp->str; + for (n = 0; (words = mystrtok(&lines, "\n")) != 0; ++n) { + if ((word = mystrtok(&words, " \t=")) != 0) { + if (n == 0) + state->helo = mystrdup(word); + if (strcasecmp(word, "STARTTLS") == 0) + break; + } } } #ifdef USE_TLS - if (words && state->tls_ctx) + if ((state->wrapper_mode || words) && state->tls_ctx) if (starttls(state)) return (1); #endif @@ -1531,7 +1558,7 @@ static void usage(void) #ifdef USE_TLS fprintf(stderr, "usage: %s %s \\\n\t%s \\\n\t%s \\\n\t%s" " destination [match ...]\n", var_procname, - "[-acCfSv] [-t conn_tmout] [-T cmd_tmout] [-L logopts]", + "[-acCfSvw] [-t conn_tmout] [-T cmd_tmout] [-L logopts]", "[-h host_lookup] [-l level] [-d mdalg] [-g grade] [-p protocols]", "[-A tafile] [-F CAfile.pem] [-P CApath/] [-m count] [-r delay]", "[-o name=value]"); @@ -1594,6 +1621,7 @@ static void parse_options(STATE *state, int argc, char *argv[]) state->pass = 1; state->reconnect = -1; state->max_reconnect = 5; + state->wrapper_mode = 0; #ifdef USE_TLS state->protocols = mystrdup("!SSLv2"); state->grade = mystrdup("medium"); @@ -1603,7 +1631,7 @@ static void parse_options(STATE *state, int argc, char *argv[]) #define OPTS "a:ch:o:St:T:v" #ifdef USE_TLS -#define TLSOPTS "A:Cd:fF:g:l:L:m:p:P:r:" +#define TLSOPTS "A:Cd:fF:g:l:L:m:p:P:r:w" state->mdalg = mystrdup("sha1"); state->CApath = mystrdup(""); @@ -1692,6 +1720,9 @@ static void parse_options(STATE *state, int argc, char *argv[]) case 'r': state->reconnect = atoi(optarg); break; + case 'w': + state->wrapper_mode = 1; + break; #endif } }