On Tue, Feb 17, 2015 at 09:10:05AM +0100, Reyk Floeter wrote: > But workaround is a harsh word - this is the way you were supposed to > use SSL_write(). It is adapted from relayd and was turned into > tls_write().
I came from the other direction. I had no idea about SSL API and expected to use libtls as a simple replacement for plain TCP. The partial write and moving buffer semantics are not documented within libtls, so I tried the naive way. > I even wonder why you didn't pick this up in your > evbuffer TLS implementation for syslogd; looks a bit reinvented. My goal was to keep my TLS buffer imlementation as close as possible to libevent. So I can merge it back into libevent. If we don't want this, I can still clean it up. > > - set SSL_MODE_ENABLE_PARTIAL_WRITE in libtls > > - remove the clt_buf workaround in httpd > > - ignore ftp client > > > > This approach sounds sane and I would love to have tls_write(3) behave > just like write(2). Yes, but after unlock. > - What is the impact of adding the flags by default? I think it makes sense for non-blocking operations in httpd and syslogd. They do propper buffer handling anyway. The ftp client is blocking and just wants to write the HTTP header. There short writes are uncomfortable. > - What is the reason for OpenSSL's defaults? > > There is an old thread with some dicussion about it: > http://marc.info/?l=openssl-dev&m=118766345824094&w=2 I found another thread from 2006. http://marc.info/?t=115101262200001&r=1&w=2 I have the impression Bodo Moeller knows something about it. | If SSL_write() has started writing a first record, but delayed other | data to later records, then it may have to return -1 to indicate | a "WANT_WRITE" or "WANT_READ" condition. My interpretation is, OpenSSL cannot indicate a short write in a want read condition. You must not change the buffer content or decrease its length. This buffer move check is a hint that you must be careful. Anyway, libtls is locked. We can either release a broken syslogd over TLS implementation or commit this diff. It has the smallest impact of everything I have tried. ok or better idea? bluhm Index: usr.sbin/syslogd/evbuffer_tls.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/syslogd/evbuffer_tls.c,v retrieving revision 1.2 diff -u -p -r1.2 evbuffer_tls.c --- usr.sbin/syslogd/evbuffer_tls.c 30 Jan 2015 14:00:55 -0000 1.2 +++ usr.sbin/syslogd/evbuffer_tls.c 19 Feb 2015 16:47:44 -0000 @@ -186,6 +186,7 @@ buffertls_writecb(int fd, short event, v if (res <= 0) goto error; } + buftls->bt_flags &= ~BT_WRITE_AGAIN; event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls); if (EVBUFFER_LENGTH(bufev->output) != 0) @@ -202,6 +203,7 @@ buffertls_writecb(int fd, short event, v return; reschedule: + buftls->bt_flags |= BT_WRITE_AGAIN; if (EVBUFFER_LENGTH(bufev->output) != 0) bufferevent_add(&bufev->ev_write, bufev->timeout_write); return; @@ -276,6 +278,7 @@ buffertls_set(struct buffertls *buftls, event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls); buftls->bt_bufev = bufev; buftls->bt_ctx = ctx; + buftls->bt_flags = 0; } void Index: usr.sbin/syslogd/evbuffer_tls.h =================================================================== RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/syslogd/evbuffer_tls.h,v retrieving revision 1.1 diff -u -p -r1.1 evbuffer_tls.h --- usr.sbin/syslogd/evbuffer_tls.h 18 Jan 2015 19:37:59 -0000 1.1 +++ usr.sbin/syslogd/evbuffer_tls.h 19 Feb 2015 16:47:44 -0000 @@ -28,6 +28,8 @@ struct buffertls { struct bufferevent *bt_bufev; struct tls *bt_ctx; const char *bt_hostname; + int bt_flags; +#define BT_WRITE_AGAIN 0x1 }; void buffertls_set(struct buffertls *, struct bufferevent *, struct tls *, Index: usr.sbin/syslogd/syslogd.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/syslogd/syslogd.c,v retrieving revision 1.156 diff -u -p -r1.156 syslogd.c --- usr.sbin/syslogd/syslogd.c 14 Feb 2015 09:02:15 -0000 1.156 +++ usr.sbin/syslogd/syslogd.c 19 Feb 2015 16:47:44 -0000 @@ -1265,8 +1265,22 @@ fprintlog(struct filed *f, int flags, ch } break; - case F_FORWTCP: case F_FORWTLS: + if (f->f_un.f_forw.f_buftls.bt_flags & BT_WRITE_AGAIN) { + /* + * After an OpenSSL SSL_ERROR_WANT_WRITE you must not + * modify the buffer pointer or length until the next + * successful write. Otherwise there will be an + * error SSL3_WRITE_PENDING:bad write retry. + * XXX This should be handled in the buffertls layer. + */ + dprintf(" %s (dropped tls write again)\n", + f->f_un.f_forw.f_loghost); + f->f_un.f_forw.f_dropped++; + break; + } + /* FALLTHROUGH */ + case F_FORWTCP: dprintf(" %s", f->f_un.f_forw.f_loghost); if (EVBUFFER_LENGTH(f->f_un.f_forw.f_bufev->output) >= MAX_TCPBUF) {