static inline void errno_cnt_dump(FILE *fp, char *name, errno_cnt_t *ecp) { int i; if (errno_cnt_empty(ecp)) { return; } fprintf(fp, "%s: ", name); for (i = 0; i <= ELAST; i++) { char *name; if ((*ecp)[i] == 0) { continue; } if ((name = errno_name(i))) { fprintf(fp, "%s", name); } else { fprintf(fp, "%d", i); } fprintf(fp, "=%lu ", (*ecp)[i]); } fprintf(fp, "\n"); }
/** * @brief Return -1 if the connection is invalid, 0 if the operation should be retried, or a positive number indicating the * number of bytes processed. */ int tls_continue(TLS *tls, int result, int syserror) { int holder = 0; unsigned long tlserror = 0; chr_t *message = MEMORYBUF(1024); // Check that the daemon hasn't initiated a shutdown. if (!status()) return -1; // Data was processed, so there is no need to retry the operation. else if (result > 0) return result; // Switch statement will process neutral/negative result codes. switch ((holder = SSL_get_error_d(tls, result))) { // This result is expected when no more data is expected, such as when the end-of-file terminator ir reached. case SSL_ERROR_ZERO_RETURN: // This indicates a non-error occurred, such as a timeout lapse, or shutdown/close notifcation is reccieved. case SSL_ERROR_NONE: result = -1; break; // This indicates the operation should be retried, possibly because of a renegotiation, or other out-of-band // interrupted the operation. case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: result = 0; break; // A TLS error ocurred, check the error stack to find out more. case SSL_ERROR_SSL: ERR_error_string_n_d((tlserror = ERR_get_error_d()), message, 1024); log_pedantic("A TLS error occurred. { error = %lu / message = %s", tlserror, message); result = -1; break; // Indicates the call returned because of a transport error. Check errno for more information. case SSL_ERROR_SYSCALL: log_pedantic("A TCP error occurred. { errno = %i / error = %s / message = %s }", syserror, errno_name(syserror), strerror_r(syserror, message, 1024)); result = -1; break; default: log_pedantic("An unexpected TLS error result was encountered. { error = %i }", holder); result = 0; break; } return result; }
/** * @brief Create a TLS session for a file descriptor, and accept the client TLS/SSL handshake. * @see SSL_accept() * @see BIO_new_socket() * @param server a server object which contains the underlying SSL context. * @param sockd the file descriptor of the TCP connection to be made SSL-ready. * @param flags passed to BIO_new_socket(), determines whether the socket is shut down when the BIO is freed. */ TLS * tls_server_alloc(void *server, int sockd, int flags) { SSL *tls; BIO *bio; server_t *local = server; int_t error = 0, result = 0, counter = 0; // Clear the error state, so we get accurate indications of a problem. errno = 0; ERR_clear_error_d(); #ifdef MAGMA_PEDANTIC if (!local) { log_pedantic("Passed a NULL server pointer."); } else if (!local->tls.context) { log_pedantic("Passed a NULL SSL context pointer."); } else if (sockd < 0) { log_pedantic("Passed an invalid socket. { sockd = %i }", sockd); } #endif if (!local || !local->tls.context || sockd < 0) { return NULL; } else if (!(tls = SSL_new_d(local->tls.context)) || !(bio = BIO_new_socket_d(sockd, flags))) { log_pedantic("TLS/BIO allocation error. { error = %s }", ssl_error_string(MEMORYBUF(256), 256)); if (tls) { SSL_free_d(tls); } return NULL; } SSL_set_bio_d(tls, bio, bio); SSL_set_accept_state_d(tls); // If the result code indicates a handshake error, but the TCP connection is still alive, we retry the handshake. do { // Attempt the server connection setup. if ((result = SSL_accept_d(tls)) <= 0 && status()) { switch ((error = SSL_get_error_d(tls, result))) { // Log these errors with extra information. case (SSL_ERROR_SSL): log_pedantic("TLS accept error. { accept = %i / error = SSL_ERROR_SSL, message = %s }", result, ssl_error_string(MEMORYBUF(512), 512)); break; case (SSL_ERROR_SYSCALL): log_pedantic("TLS accept error. { accept = %i / error = SSL_ERROR_SYSCALL / errno = %i / message = %s }", result, errno, errno_name(errno)); break; // A zero return indicates a socket shutdown. The latter should never happen. case (SSL_ERROR_ZERO_RETURN): case (SSL_ERROR_NONE): break; default: log_pedantic("TLS accept error. { accept = %i / error = %i }", result, error); break; } } } while (result < 0 && counter++ < 10); if (result != 1) { SSL_free_d(tls); return NULL; } return tls; }