X-Git-Url: http://git.madism.org/?p=apps%2Fmadmutt.git;a=blobdiff_plain;f=lib-sys%2Fevtloop.c;h=11546b22be1644cb47a97fb5cbf38157f9b0b141;hp=76d6b3311685a3853463fabe8eca020bb1c6856a;hb=f3076bd918cea51487a5b28dff9c69578f50b25e;hpb=7489084cbae92559260659c8606eea91d70e8f3b diff --git a/lib-sys/evtloop.c b/lib-sys/evtloop.c index 76d6b33..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; @@ -127,26 +221,47 @@ ssize_t el_job_read(job_t *w, buffer_t *buf) if (w->session) { nr = gnutls_record_recv(w->session, buf->data + buf->len, BUFSIZ); - if (nr < 0 && gnutls_error_is_fatal(nr)) - return el_job_release(w, EL_RDHUP); - if (nr <= 0) { + if (nr < 0 && !gnutls_error_is_fatal(nr)) { int wr = gnutls_record_get_direction(w->session); return el_job_setemode(w, wr ? EL_WRITING : EL_READING); } EL_JOB_CHECK(el_job_setemode(w, w->mode)); } else { nr = read(w->fd, buf->data + buf->len, BUFSIZ); - if (nr < 0) { - if (errno == EINTR || errno == EAGAIN) - return 0; - } - if (nr <= 0) - return el_job_release(w, EL_RDHUP); + if (nr < 0 && (errno == EINTR || errno == EAGAIN)) + return 0; } + if (nr <= 0) + return el_job_release(w, EL_RDHUP); buffer_extend(buf, nr); return nr; } +ssize_t el_job_write(job_t *w, buffer_t *buf) +{ + ssize_t nr; + + if (buf->len == 0) + return 0; + + if (w->session) { + nr = gnutls_record_send(w->session, buf->data, buf->len); + if (nr < 0 && !gnutls_error_is_fatal(nr)) { + int wr = gnutls_record_get_direction(w->session); + return el_job_setemode(w, wr ? EL_WRITING : EL_READING); + } + EL_JOB_CHECK(el_job_setemode(w, w->mode)); + } else { + nr = write(w->fd, buf->data, buf->len); + if (nr < 0 && (errno == EINTR || errno == EAGAIN)) + return 0; + } + if (nr <= 0) + return el_job_release(w, EL_RDHUP); + buffer_splice(buf, 0, nr, NULL, 0); + return nr; +} + int el_dispatch(int timeout) { struct epoll_event events[FD_SETSIZE]; @@ -173,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;