From: Ken Hornstein Date: Sun, 31 Mar 2013 22:33:46 +0000 (-0400) Subject: Preliminary support for TLS negotiation at the very start of SMTP session. X-Git-Url: https://diplodocus.org/git/nmh/commitdiff_plain/8bf4e8784aa2a0438ba09b1a0eb9207d3baf6bd9 Preliminary support for TLS negotiation at the very start of SMTP session. --- diff --git a/mts/smtp/smtp.c b/mts/smtp/smtp.c index 6fd178bc..b2969762 100755 --- a/mts/smtp/smtp.c +++ b/mts/smtp/smtp.c @@ -126,6 +126,8 @@ static SSL *ssl = NULL; static BIO *sbior = NULL; static BIO *sbiow = NULL; static BIO *io = NULL; + +static int tls_negotiate(void); #endif /* TLS_SUPPORT */ #if defined(CYRUS_SASL) || defined(TLS_SUPPORT) @@ -265,6 +267,25 @@ smtp_init (char *client, char *server, char *port, int watch, int verbose, tls_active = 0; +#ifdef TLS_SUPPORT + /* + * If tls == 2, that means that the user requested "initial" TLS, + * which happens right after the connection has opened. Do that + * negotiation now + */ + + if (tls == 2) { + result = tls_negotiate(); + + /* + * Note: if tls_negotiate() fails it will call sm_end() for us, + * which closes the connection. + */ + if (result != RP_OK) + return result; + } +#endif /* TLS_SUPPORT */ + sm_alarmed = 0; alarm (SM_OPEN); result = smhear (); @@ -302,9 +323,7 @@ smtp_init (char *client, char *server, char *port, int watch, int verbose, * restart the EHLO dialog after TLS negotiation is complete. */ - if (tls) { - BIO *ssl_bio; - + if (tls == 1) { if (! EHLOset("STARTTLS")) { sm_end(NOTOK); return sm_ierror("SMTP server does not support TLS"); @@ -322,86 +341,10 @@ smtp_init (char *client, char *server, char *port, int watch, int verbose, * negotiation. Oblige them. */ - if (! sslctx) { - const SSL_METHOD *method; - - SSL_library_init(); - SSL_load_error_strings(); - - method = TLSv1_client_method(); /* Not sure about this */ - - sslctx = SSL_CTX_new(method); - - if (! sslctx) { - sm_end(NOTOK); - return sm_ierror("Unable to initialize OpenSSL context: %s", - ERR_error_string(ERR_get_error(), NULL)); - } - } - - ssl = SSL_new(sslctx); - - if (! ssl) { - sm_end(NOTOK); - return sm_ierror("Unable to create SSL connection: %s", - ERR_error_string(ERR_get_error(), NULL)); - } - - sbior = BIO_new_socket(fileno(sm_rfp), BIO_NOCLOSE); - sbiow = BIO_new_socket(fileno(sm_wfp), BIO_NOCLOSE); - - if (sbior == NULL || sbiow == NULL) { - sm_end(NOTOK); - return sm_ierror("Unable to create BIO endpoints: %s", - ERR_error_string(ERR_get_error(), NULL)); - } - - SSL_set_bio(ssl, sbior, sbiow); - SSL_set_connect_state(ssl); - - /* - * Set up a BIO to handle buffering for us - */ - - io = BIO_new(BIO_f_buffer()); - - if (! io) { - sm_end(NOTOK); - return sm_ierror("Unable to create a buffer BIO: %s", - ERR_error_string(ERR_get_error(), NULL)); - } - - ssl_bio = BIO_new(BIO_f_ssl()); - - if (! ssl_bio) { - sm_end(NOTOK); - return sm_ierror("Unable to create a SSL BIO: %s", - ERR_error_string(ERR_get_error(), NULL)); - } - - BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE); - BIO_push(io, ssl_bio); - - /* - * Try doing the handshake now - */ - - if (BIO_do_handshake(io) < 1) { - sm_end(NOTOK); - return sm_ierror("Unable to negotiate SSL connection: %s", - ERR_error_string(ERR_get_error(), NULL)); - } + result = tls_negotiate(); - if (sm_debug) { - const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl); - printf("SSL negotiation successful: %s(%d) %s\n", - SSL_CIPHER_get_name(cipher), - SSL_CIPHER_get_bits(cipher, NULL), - SSL_CIPHER_get_version(cipher)); - - } - - tls_active = 1; + if (result != RP_OK) + return result; doingEHLO = 1; result = smtalk (SM_HELO, "EHLO %s", client); @@ -1312,6 +1255,101 @@ sm_fwrite(char *buffer, int len) return ferror(sm_wfp) ? NOTOK : RP_OK; } +#ifdef TLS_SUPPORT +/* + * Negotiate Transport Layer Security + */ + +static int +tls_negotiate(void) +{ + BIO *ssl_bio; + + if (! sslctx) { + SSL_METHOD *method; + + SSL_library_init(); + SSL_load_error_strings(); + + method = TLSv1_client_method(); /* Not sure about this */ + + sslctx = SSL_CTX_new(method); + + if (! sslctx) { + sm_end(NOTOK); + return sm_ierror("Unable to initialize OpenSSL context: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + } + + ssl = SSL_new(sslctx); + + if (! ssl) { + sm_end(NOTOK); + return sm_ierror("Unable to create SSL connection: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + + sbior = BIO_new_socket(fileno(sm_rfp), BIO_NOCLOSE); + sbiow = BIO_new_socket(fileno(sm_wfp), BIO_NOCLOSE); + + if (sbior == NULL || sbiow == NULL) { + sm_end(NOTOK); + return sm_ierror("Unable to create BIO endpoints: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + + SSL_set_bio(ssl, sbior, sbiow); + SSL_set_connect_state(ssl); + + /* + * Set up a BIO to handle buffering for us + */ + + io = BIO_new(BIO_f_buffer()); + + if (! io) { + sm_end(NOTOK); + return sm_ierror("Unable to create a buffer BIO: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + + ssl_bio = BIO_new(BIO_f_ssl()); + + if (! ssl_bio) { + sm_end(NOTOK); + return sm_ierror("Unable to create a SSL BIO: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + + BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE); + BIO_push(io, ssl_bio); + + /* + * Try doing the handshake now + */ + + if (BIO_do_handshake(io) < 1) { + sm_end(NOTOK); + return sm_ierror("Unable to negotiate SSL connection: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + + if (sm_debug) { + const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl); + printf("SSL negotiation successful: %s(%d) %s\n", + SSL_CIPHER_get_name(cipher), + SSL_CIPHER_get_bits(cipher, NULL), + SSL_CIPHER_get_version(cipher)); + + } + + tls_active = 1; + + return RP_OK; +} +#endif /* TLS_SUPPORT */ + /* * Convenience functions to replace occurences of fputs() and fputc() */ diff --git a/uip/post.c b/uip/post.c index 239ecdfc..9c7af29e 100644 --- a/uip/post.c +++ b/uip/post.c @@ -88,6 +88,7 @@ X("user", SASLminc(-4), USERSW) \ X("port server port name/number", 4, PORTSW) \ X("tls", TLSminc(-3), TLSSW) \ + X("initialtls", TLSminc(-10), INITTLSSW) \ X("notls", TLSminc(-5), NTLSSW) \ X("fileproc", -4, FILEPROCSW) \ X("mhlproc", -3, MHLPROCSW) \ @@ -468,7 +469,11 @@ main (int argc, char **argv) continue; case TLSSW: - tls++; + tls = 1; + continue; + + case INITTLSSW: + tls = 2; continue; case NTLSSW: diff --git a/uip/send.c b/uip/send.c index 8a659222..626593ef 100644 --- a/uip/send.c +++ b/uip/send.c @@ -69,6 +69,7 @@ X("attachformat", 7, ATTACHFORMATSW) \ X("port server-port-name/number", 4, PORTSW) \ X("tls", TLSminc(-3), TLSSW) \ + X("initialtls", TLSminc(-10), INITTLSSW) \ X("notls", TLSminc(-5), NTLSSW) \ X("mts smtp|sendmail/smtp|sendmail/pipe", 2, MTSSW) \ X("messageid localname|random", 2, MESSAGEIDSW) \ @@ -245,6 +246,7 @@ main (int argc, char **argv) case SASLSW: case NOSASLSW: case TLSSW: + case INITTLSSW: case NTLSSW: vec[vecp++] = --cp; continue; diff --git a/uip/whatnowsbr.c b/uip/whatnowsbr.c index a084bafc..eb6106a7 100644 --- a/uip/whatnowsbr.c +++ b/uip/whatnowsbr.c @@ -1028,6 +1028,7 @@ check_draft (char *msgnam) X("attachformat", 7, SNDATTACHFORMAT) \ X("port server-port-name/number", 4, PORTSW) \ X("tls", TLSminc(-3), TLSSW) \ + X("initialtls", TLSminc(-10), INITTLSSW) \ X("notls", TLSminc(-5), NTLSSW) \ X("mts smtp|sendmail/smtp|sendmail/pipe", 2, MTSSW) \ X("messageid localname|random", 2, MESSAGEIDSW) \ @@ -1201,6 +1202,7 @@ sendit (char *sp, char **arg, char *file, int pushed) case SASLSW: case NOSASLSW: case TLSSW: + case INITTLSSW: case NTLSSW: vec[vecp++] = --cp; continue; diff --git a/uip/whom.c b/uip/whom.c index 3830e5eb..862fc58c 100644 --- a/uip/whom.c +++ b/uip/whom.c @@ -41,6 +41,7 @@ X("user username", SASLminc(-4), USERSW) \ X("port server port name/number", 4, PORTSW) \ X("tls", TLSminc(-3), TLSSW) \ + X("initialtls", TLSminc(-10), INITTLSSW) \ X("notls", TLSminc(-5), NTLSSW) \ X("mts smtp|sendmail/smtp|sendmail/pipe", 0, MTSSW) \ @@ -104,6 +105,7 @@ main (int argc, char **argv) case SNOOPSW: case SASLSW: case TLSSW: + case INITTLSSW: case NTLSSW: vec[vecp++] = --cp; continue;