X-Git-Url: http://git.madism.org/?p=apps%2Fmadmutt.git;a=blobdiff_plain;f=lib-sys%2Fevtloop.c;h=11546b22be1644cb47a97fb5cbf38157f9b0b141;hp=ed9eca53e8d8c2248abff5ad4bcb0461ef6c8975;hb=f3076bd918cea51487a5b28dff9c69578f50b25e;hpb=bb97a3c3f2ed50a557ead340ba64413d6f014cee diff --git a/lib-sys/evtloop.c b/lib-sys/evtloop.c index ed9eca5..11546b2 100644 --- a/lib-sys/evtloop.c +++ b/lib-sys/evtloop.c @@ -29,6 +29,7 @@ #endif #include "evtloop.h" #include "mutt.h" +#include "mutt_ssl.li" static int epollfd = -1; @@ -69,6 +70,14 @@ int el_job_setmode(job_t *w, el_mode mode) } } +void job_wipe(job_t *w) +{ + if (w->xcred) + gnutls_certificate_free_credentials(w->xcred); + if (w->session) + gnutls_deinit(w->session); +} + int el_job_release(job_t *w, el_status reason) { w->state = EL_LLP_FINI; @@ -76,12 +85,48 @@ int el_job_release(job_t *w, el_status reason) w->m->finalize(w, reason); } if (w->fd >= 0) { + if (w->session) + gnutls_bye(w->session, GNUTLS_SHUT_RDWR); close(w->fd); } job_delete(&w); return -1; } +static int el_job_tlsing(job_t *w, int starttls) +{ + int err = gnutls_handshake(w->session); + if (err < 0 && !gnutls_error_is_fatal(err)) { + int wr = gnutls_record_get_direction(w->session); + return el_job_setemode(w, wr ? EL_WRITING : EL_READING); + } + if (err < 0) + return el_job_release(w, EL_RDHUP); + +#if 0 + if (!tls_check_certificate (conn)) + return -1; +#endif + + /* set Security Strength Factor (SSF) for SASL */ + /* NB: gnutls_cipher_get_key_size() returns key length in bytes */ + w->ssf = gnutls_cipher_get_key_size(gnutls_cipher_get(w->session)) * 8; + w->state = EL_LLP_READY; + if (starttls) + return el_job_setemode(w, w->mode); + return w->m->on_event(w, EL_EVT_RUNNING); +} + +static int el_job_starttlsing(job_t *w) +{ + return el_job_tlsing(w, true); +} + +static int el_job_connecting_ssl(job_t *w) +{ + return el_job_tlsing(w, false); +} + static int el_job_connecting(job_t *w) { int err = 0; @@ -90,12 +135,49 @@ static int el_job_connecting(job_t *w) if (getsockopt(w->fd, SOL_SOCKET, SO_ERROR, (void *)&err, &len) || err) return el_job_release(w, EL_ERROR); + if (w->session) { + w->llp = &el_job_connecting_ssl; + return w->llp(w); + } w->state = EL_LLP_READY; return w->m->on_event(w, EL_EVT_RUNNING); } +static int tls_negotiate(job_t *w) +{ + static int protocol_priority[] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 }; + + if (gnutls_certificate_allocate_credentials(&w->xcred) < 0) + return -1; + + /* ignore errors, maybe file doesn't exist yet */ + gnutls_certificate_set_x509_trust_file(w->xcred, mod_ssl.cert_file, + GNUTLS_X509_FMT_PEM); + + if (mod_ssl.ca_certificates_file) { + gnutls_certificate_set_x509_trust_file(w->xcred, + mod_ssl.ca_certificates_file, GNUTLS_X509_FMT_PEM); + } + gnutls_init(&w->session, GNUTLS_CLIENT); + + /* set socket */ + gnutls_transport_set_ptr(w->session, (gnutls_transport_ptr)(intptr_t)w->fd); + + /* disable TLS/SSL protocols as needed */ + if (!mod_ssl.use_sslv3) { + protocol_priority[1] = 0; + } + + /* We use default priorities (see gnutls documentation), + except for protocol version */ + gnutls_set_default_priority(w->session); + gnutls_protocol_set_priority(w->session, protocol_priority); + gnutls_credentials_set(w->session, GNUTLS_CRD_CERTIFICATE, w->xcred); + return 0; +} + int el_job_connect(job_t *w, struct sockaddr *addr, socklen_t len, - int type, int proto) + int type, int proto, int ssl) { int res, sock = socket(addr->sa_family, type, proto); @@ -111,6 +193,9 @@ int el_job_connect(job_t *w, struct sockaddr *addr, socklen_t len, goto error; w->fd = sock; + if (ssl && tls_negotiate(w) < 0) + goto error; + w->llp = &el_job_connecting; return el_job_setmode(w, EL_WRITING); @@ -119,6 +204,15 @@ int el_job_connect(job_t *w, struct sockaddr *addr, socklen_t len, return el_job_release(w, EL_ERROR); } +int el_job_starttls(job_t *w) +{ + if (tls_negotiate(w) < 0) + return el_job_release(w, EL_RDHUP); + w->state = EL_LLP_INIT; + w->llp = &el_job_starttlsing; + return w->llp(w); +} + ssize_t el_job_read(job_t *w, buffer_t *buf) { ssize_t nr; @@ -194,13 +288,13 @@ int el_dispatch(int timeout) if (event & EPOLLRDHUP) { IGNORE(el_job_release(w, EL_RDHUP)); } else if (w->mode != w->emode) { - w->m->on_event(w, EL_EVT_INOUT ^ w->emode); + IGNORE(w->m->on_event(w, EL_EVT_INOUT ^ w->emode)); } else { if (event & EPOLLIN) evt |= EL_EVT_IN; if (event & EPOLLOUT) evt |= EL_EVT_OUT; - w->m->on_event(w, evt); + IGNORE(w->m->on_event(w, evt)); } break;