/*++ /* NAME /* tls_bio_ops 3 /* SUMMARY /* TLS network basic I/O management /* SYNOPSIS /* #define TLS_INTERNAL /* #include /* /* int tls_bio_connect(fd, timeout, context) /* int fd; /* int timeout; /* TLS_SESS_STATE *context; /* /* int tls_bio_accept(fd, timeout, context) /* int fd; /* int timeout; /* TLS_SESS_STATE *context; /* /* int tls_bio_shutdown(fd, timeout, context) /* int fd; /* int timeout; /* TLS_SESS_STATE *context; /* /* int tls_bio_read(fd, buf, len, timeout, context) /* int fd; /* void *buf; /* int len; /* int timeout; /* TLS_SESS_STATE *context; /* /* int tls_bio_write(fd, buf, len, timeout, context) /* int fd; /* void *buf; /* int len; /* int timeout; /* TLS_SESS_STATE *context; /* DESCRIPTION /* This module enforces VSTREAM-style timeouts on non-blocking /* I/O while performing TLS handshake or input/output operations. /* /* The Postfix VSTREAM read/write routines invoke the /* tls_bio_read/write routines to send and receive plain-text /* data. In addition, this module provides tls_bio_connect/accept /* routines that trigger the initial TLS handshake. The /* tls_bio_xxx routines invoke the corresponding SSL routines /* that translate the requests into TLS protocol messages. /* /* Whenever an SSL operation indicates that network input (or /* output) needs to happen, the tls_bio_xxx routines wait for /* the network to become readable (or writable) within the /* timeout limit, then retry the SSL operation. This works /* because the network socket is in non-blocking mode. /* /* tls_bio_connect() performs the SSL_connect() operation. /* /* tls_bio_accept() performs the SSL_accept() operation. /* /* tls_bio_shutdown() performs the SSL_shutdown() operation. /* /* tls_bio_read() performs the SSL_read() operation. /* /* tls_bio_write() performs the SSL_write() operation. /* /* Arguments: /* .IP fd /* Network socket. /* .IP buf /* Read/write buffer. /* .IP len /* Read/write request size. /* .IP timeout /* Read/write timeout. /* .IP TLScontext /* TLS session state. /* DIAGNOSTICS /* A result value > 0 means successful completion. /* /* A result value < 0 means that the requested operation did /* not complete due to TLS protocol failure, system call /* failure, or for any reason described under "in addition" /* below. /* /* A result value of 0 from tls_bio_shutdown() means that the /* operation is in progress. A result value of 0 from other /* tls_bio_ops(3) operations means that the remote party either /* closed the network connection or that it sent a TLS shutdown /* request. /* /* Upon return from the tls_bio_ops(3) routines the global /* errno value is non-zero when the requested operation did not /* complete due to system call failure. /* /* In addition, the result value is set to -1, and the global /* errno value is set to ETIMEDOUT, when some network read/write /* operation did not complete within the time limit. /* LICENSE /* .ad /* .fi /* This software is free. You can do with it whatever you want. /* The original author kindly requests that you acknowledge /* the use of his software. /* AUTHOR(S) /* Originally written by: /* Lutz Jaenicke /* BTU Cottbus /* Allgemeine Elektrotechnik /* Universitaetsplatz 3-4 /* D-03044 Cottbus, Germany /* /* Updated by: /* Wietse Venema /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA /* /* Victor Duchovni /* Morgan Stanley /*--*/ /* System library. */ #include #include #ifndef timersub /* res = a - b */ #define timersub(a, b, res) do { \ (res)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ (res)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ if ((res)->tv_usec < 0) { \ (res)->tv_sec--; \ (res)->tv_usec += 1000000; \ } \ } while (0) #endif #ifdef USE_TLS /* Utility library. */ #include #include /* TLS library. */ #define TLS_INTERNAL #include /* tls_bio - perform SSL input/output operation with extreme prejudice */ int tls_bio(int fd, int timeout, TLS_SESS_STATE *TLScontext, int (*hsfunc) (SSL *), int (*rfunc) (SSL *, void *, int), int (*wfunc) (SSL *, const void *, int), void *buf, int num) { const char *myname = "tls_bio"; int status; int err; int enable_deadline; struct timeval time_left; /* amount of time left */ struct timeval time_deadline; /* time of deadline */ struct timeval time_now; /* time after SSL_mumble() call */ /* * Compensation for interface mis-match: With VSTREAMs, timeout <= 0 * means wait forever; with the read/write_wait() calls below, we need to * specify timeout < 0 instead. * * Safety: no time limit means no deadline. */ if (timeout <= 0) { timeout = -1; enable_deadline = 0; } /* * Deadline management is simpler than with VSTREAMs, because we don't * need to decrement a per-stream time limit. We just work within the * budget that is available for this tls_bio() call. */ else { enable_deadline = vstream_fstat(TLScontext->stream, VSTREAM_FLAG_DEADLINE); if (enable_deadline) { GETTIMEOFDAY(&time_deadline); time_deadline.tv_sec += timeout; } } /* * If necessary, retry the SSL handshake or read/write operation after * handling any pending network I/O. */ for (;;) { /* * Flush the per-thread SSL error queue. Otherwise, errors from other * code that also uses TLS may confuse SSL_get_error(3). */ ERR_clear_error(); if (hsfunc) status = hsfunc(TLScontext->con); else if (rfunc) status = rfunc(TLScontext->con, buf, num); else if (wfunc) status = wfunc(TLScontext->con, buf, num); else msg_panic("%s: nothing to do here", myname); err = SSL_get_error(TLScontext->con, status); /* * Correspondence between SSL_ERROR_* error codes and tls_bio_(read, * write, accept, connect, shutdown) return values (for brevity: * retval). * * SSL_ERROR_NONE corresponds with retval > 0. With SSL_(read, write) * this is the number of plaintext bytes sent or received. With * SSL_(accept, connect, shutdown) this means that the operation was * completed successfully. * * SSL_ERROR_WANT_(WRITE, READ) start a new loop iteration, or force * (retval = -1, errno = ETIMEDOUT) when the time limit is exceeded. * * All other SSL_ERROR_* cases correspond with retval <= 0. With * SSL_(read, write, accept, connect) retval == 0 means that the * remote party either closed the network connection or that it * requested TLS shutdown; with SSL_shutdown() retval == 0 means that * our own shutdown request is in progress. With all operations * retval < 0 means that there was an error. In the latter case, * SSL_ERROR_SYSCALL means that error details are returned via the * errno value. * * Find out if we must retry the operation and/or if there is pending * network I/O. * * XXX If we're the first to invoke SSL_shutdown(), then the operation * isn't really complete when the call returns. We could hide that * anomaly here and repeat the call. */ switch (err) { case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: if (enable_deadline) { GETTIMEOFDAY(&time_now); timersub(&time_deadline, &time_now, &time_left); timeout = time_left.tv_sec + (time_left.tv_usec > 0); if (timeout <= 0) { errno = ETIMEDOUT; return (-1); } } if (err == SSL_ERROR_WANT_WRITE) { if (write_wait(fd, timeout) < 0) return (-1); /* timeout error */ } else { if (read_wait(fd, timeout) < 0) return (-1); /* timeout error */ } break; /* * Unhandled cases: SSL_ERROR_WANT_(ACCEPT, CONNECT, X509_LOOKUP) * etc. Historically, Postfix silently treated these as ordinary * I/O errors so we don't really know how common they are. For * now, we just log a warning. */ default: msg_warn("%s: unexpected SSL_ERROR code %d", myname, err); /* FALLTHROUGH */ /* * With tls_timed_read() and tls_timed_write() the caller is the * VSTREAM library module which is unaware of TLS, so we log the * TLS error stack here. In a better world, each VSTREAM I/O * object would provide an error reporting method in addition to * the timed_read and timed_write methods, so that we would not * need to have ad-hoc code like this. */ case SSL_ERROR_SSL: if (rfunc || wfunc) tls_print_errors(); /* FALLTHROUGH */ case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_NONE: errno = 0; /* avoid bogus warnings */ /* FALLTHROUGH */ case SSL_ERROR_SYSCALL: return (status); } } } #endif