ssize_t tls_write(struct tls *ctx, const void *buf, size_t buflen) { ssize_t rv = -1; int ssl_ret; tls_error_clear(&ctx->error); if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) { if ((rv = tls_handshake(ctx)) != 0) goto out; } if (buflen > INT_MAX) { tls_set_errorx(ctx, "buflen too long"); goto out; } ERR_clear_error(); if ((ssl_ret = SSL_write(ctx->ssl_conn, buf, buflen)) > 0) { rv = (ssize_t)ssl_ret; goto out; } rv = (ssize_t)tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "write"); out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); }
int tls_close(struct tls *ctx) { int ssl_ret; int rv = 0; if (ctx->ssl_conn != NULL) { ssl_ret = SSL_shutdown(ctx->ssl_conn); if (ssl_ret < 0) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "shutdown"); if (rv == TLS_READ_AGAIN || rv == TLS_WRITE_AGAIN) return (rv); } } if (ctx->socket != -1) { if (shutdown(ctx->socket, SHUT_RDWR) != 0) { if (rv == 0 && errno != ENOTCONN && errno != ECONNRESET) { tls_set_error(ctx, "shutdown"); rv = -1; } } if (close(ctx->socket) != 0) { if (rv == 0) { tls_set_error(ctx, "close"); rv = -1; } } ctx->socket = -1; } return (rv); }
int tls_close(struct tls *ctx) { int ssl_ret; int rv = 0; tls_error_clear(&ctx->error); if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) { tls_set_errorx(ctx, "invalid operation for context"); rv = -1; goto out; } if (ctx->state & TLS_SSL_NEEDS_SHUTDOWN) { ERR_clear_error(); ssl_ret = SSL_shutdown(ctx->ssl_conn); if (ssl_ret < 0) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "shutdown"); if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT) goto out; } ctx->state &= ~TLS_SSL_NEEDS_SHUTDOWN; } if (ctx->socket != -1) { if (shutdown(ctx->socket, SHUT_RDWR) != 0) { if (rv == 0 && errno != ENOTCONN && errno != ECONNRESET) { tls_set_error(ctx, "shutdown"); rv = -1; } } if (close(ctx->socket) != 0) { if (rv == 0) { tls_set_error(ctx, "close"); rv = -1; } } ctx->socket = -1; } if ((ctx->state & TLS_EOF_NO_CLOSE_NOTIFY) != 0) { tls_set_errorx(ctx, "EOF without close notify"); rv = -1; } out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); }
static int tls_do_abort(struct tls *ctx) { int ssl_ret, rv; ssl_ret = SSL_shutdown(ctx->ssl_conn); if (ssl_ret < 0) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "shutdown"); if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT) return (rv); } tls_set_errorx(ctx, "unexpected handshake, closing connection"); return -1; }
int tls_handshake_client(struct tls *ctx) { X509 *cert = NULL; int match, ssl_ret; int rv = -1; if ((ctx->flags & TLS_CLIENT) == 0) { tls_set_errorx(ctx, "not a client context"); goto err; } if ((ctx->state & TLS_CONNECTED) == 0) { tls_set_errorx(ctx, "context not connected"); goto err; } ctx->state |= TLS_SSL_NEEDS_SHUTDOWN; ERR_clear_error(); if ((ssl_ret = SSL_connect(ctx->ssl_conn)) != 1) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake"); goto err; } if (ctx->config->verify_name) { cert = SSL_get_peer_certificate(ctx->ssl_conn); if (cert == NULL) { tls_set_errorx(ctx, "no server certificate"); goto err; } if (tls_check_name(ctx, cert, ctx->servername, &match) == -1) goto err; if (!match) { tls_set_errorx(ctx, "name `%s' not present in" " server certificate", ctx->servername); goto err; } } ctx->state |= TLS_HANDSHAKE_COMPLETE; rv = 0; err: X509_free(cert); return (rv); }
int tls_write(struct tls *ctx, const void *buf, size_t buflen, size_t *outlen) { int ssl_ret; if (buflen > INT_MAX) { tls_set_error(ctx, "buflen too long"); return (-1); } ssl_ret = SSL_write(ctx->ssl_conn, buf, buflen); if (ssl_ret > 0) { *outlen = (size_t)ssl_ret; return (0); } return tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "write"); }
int tls_accept_fds(struct tls *ctx, struct tls **cctx, int fd_read, int fd_write) { struct tls *conn_ctx = *cctx; int ret, err; if ((ctx->flags & TLS_SERVER) == 0) { tls_set_error(ctx, "not a server context"); goto err; } if (conn_ctx == NULL) { if ((conn_ctx = tls_server_conn(ctx)) == NULL) { tls_set_error(ctx, "connection context failure"); goto err; } *cctx = conn_ctx; if ((conn_ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { tls_set_error(ctx, "ssl failure"); goto err; } if (SSL_set_rfd(conn_ctx->ssl_conn, fd_read) != 1 || SSL_set_wfd(conn_ctx->ssl_conn, fd_write) != 1) { tls_set_error(ctx, "ssl set fd failure"); goto err; } SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx); } if ((ret = SSL_accept(conn_ctx->ssl_conn)) != 1) { err = tls_ssl_error(ctx, conn_ctx->ssl_conn, ret, "accept"); if (err == TLS_READ_AGAIN || err == TLS_WRITE_AGAIN) { return (err); } goto err; } return (0); err: return (-1); }
int tls_handshake_server(struct tls *ctx) { int ssl_ret; int rv = -1; if ((ctx->flags & TLS_SERVER_CONN) == 0) { tls_set_errorx(ctx, "not a server connection context"); goto err; } ERR_clear_error(); if ((ssl_ret = SSL_accept(ctx->ssl_conn)) != 1) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake"); goto err; } ctx->state |= TLS_HANDSHAKE_COMPLETE; rv = 0; err: return (rv); }
int tls_write(struct tls *ctx, const void *buf, size_t buflen, size_t *outlen) { int ssl_ret; *outlen = 0; if (buflen > INT_MAX) { tls_set_errorx(ctx, "buflen too long"); return (-1); } if (ctx->state & TLS_STATE_ABORT) return tls_do_abort(ctx); ssl_ret = SSL_write(ctx->ssl_conn, buf, buflen); if (ssl_ret > 0) { *outlen = (size_t)ssl_ret; return (0); } return tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "write"); }
int tls_connect_fds(struct tls *ctx, int fd_read, int fd_write, const char *servername) { union { struct in_addr ip4; struct in6_addr ip6; } addrbuf; X509 *cert = NULL; int ret, err; if (ctx->flags & TLS_CONNECTING) goto connecting; if ((ctx->flags & TLS_CLIENT) == 0) { tls_set_error(ctx, "not a client context"); goto err; } if (fd_read < 0 || fd_write < 0) { tls_set_error(ctx, "invalid file descriptors"); return (-1); } if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { tls_set_error(ctx, "ssl context failure"); goto err; } if (tls_configure_ssl(ctx) != 0) goto err; if (ctx->config->verify_name) { if (servername == NULL) { tls_set_error(ctx, "server name not specified"); goto err; } } if (ctx->config->verify_cert) { SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL); if (ctx->config->ca_mem != NULL) { if (ctx->config->ca_len > INT_MAX) { tls_set_error(ctx, "ca too long"); goto err; } if (SSL_CTX_load_verify_mem(ctx->ssl_ctx, ctx->config->ca_mem, ctx->config->ca_len) != 1) { tls_set_error(ctx, "ssl verify memory setup failure"); goto err; } } else if (SSL_CTX_load_verify_locations(ctx->ssl_ctx, ctx->config->ca_file, ctx->config->ca_path) != 1) { tls_set_error(ctx, "ssl verify setup failure"); goto err; } if (ctx->config->verify_depth >= 0) SSL_CTX_set_verify_depth(ctx->ssl_ctx, ctx->config->verify_depth); } if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { tls_set_error(ctx, "ssl connection failure"); goto err; } if (SSL_set_rfd(ctx->ssl_conn, fd_read) != 1 || SSL_set_wfd(ctx->ssl_conn, fd_write) != 1) { tls_set_error(ctx, "ssl file descriptor failure"); goto err; } /* * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not * permitted in "HostName". */ if (servername != NULL && inet_pton(AF_INET, servername, &addrbuf) != 1 && inet_pton(AF_INET6, servername, &addrbuf) != 1) { if (SSL_set_tlsext_host_name(ctx->ssl_conn, servername) == 0) { tls_set_error(ctx, "server name indication failure"); goto err; } } connecting: if ((ret = SSL_connect(ctx->ssl_conn)) != 1) { err = tls_ssl_error(ctx, ret, "connect"); if (err == TLS_READ_AGAIN || err == TLS_WRITE_AGAIN) { ctx->flags |= TLS_CONNECTING; return (err); } goto err; } ctx->flags &= ~TLS_CONNECTING; if (ctx->config->verify_name) { cert = SSL_get_peer_certificate(ctx->ssl_conn); if (cert == NULL) { tls_set_error(ctx, "no server certificate"); goto err; } if ((ret = tls_check_servername(ctx, cert, servername)) != 0) { if (ret != -2) tls_set_error(ctx, "name `%s' not present in" " server certificate", servername); goto err; } } return (0); err: X509_free(cert); return (-1); }