📄 tortls.c
字号:
}
if (err == TOR_TLS_WANTWRITE || err == TOR_TLS_WANTREAD) {
tls->wantwrite_n = n;
}
return err;
}
/** Perform initial handshake on <b>tls</b>. When finished, returns
* TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD,
* or TOR_TLS_WANTWRITE.
*/
int
tor_tls_handshake(tor_tls_t *tls)
{
int r;
tor_assert(tls);
tor_assert(tls->ssl);
tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE);
check_no_tls_errors();
if (tls->isServer) {
r = SSL_accept(tls->ssl);
} else {
r = SSL_connect(tls->ssl);
}
r = tor_tls_get_error(tls,r,0, "handshaking", LOG_INFO);
if (ERR_peek_error() != 0) {
tls_log_errors(tls, tls->isServer ? LOG_INFO : LOG_WARN,
"handshaking");
return TOR_TLS_ERROR_MISC;
}
if (r == TOR_TLS_DONE) {
tls->state = TOR_TLS_ST_OPEN;
if (tls->isServer) {
SSL_set_info_callback(tls->ssl, NULL);
SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb);
/* There doesn't seem to be a clear OpenSSL API to clear mode flags. */
tls->ssl->mode &= ~SSL_MODE_NO_AUTO_CHAIN;
#ifdef V2_HANDSHAKE_SERVER
if (tor_tls_client_is_using_v2_ciphers(tls->ssl, ADDR(tls))) {
/* This check is redundant, but back when we did it in the callback,
* we might have not been able to look up the tor_tls_t if the code
* was buggy. Fixing that. */
if (!tls->wasV2Handshake) {
log_warn(LD_BUG, "For some reason, wasV2Handshake didn't"
" get set. Fixing that.");
}
tls->wasV2Handshake = 1;
log_debug(LD_NET, "Completed V2 TLS handshake with client; waiting "
"for renegotiation.");
} else {
tls->wasV2Handshake = 0;
}
#endif
} else {
#ifdef V2_HANDSHAKE_CLIENT
/* If we got no ID cert, we're a v2 handshake. */
X509 *cert = SSL_get_peer_certificate(tls->ssl);
STACK_OF(X509) *chain = SSL_get_peer_cert_chain(tls->ssl);
int n_certs = sk_X509_num(chain);
if (n_certs > 1 || (n_certs == 1 && cert != sk_X509_value(chain, 0)))
tls->wasV2Handshake = 0;
else {
log_debug(LD_NET, "Server sent back a single certificate; looks like "
"a v2 handshake on %p.", tls);
tls->wasV2Handshake = 1;
}
if (cert)
X509_free(cert);
#endif
SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST);
}
}
return r;
}
/** Client only: Renegotiate a TLS session. When finished, returns
* TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, or
* TOR_TLS_WANTWRITE.
*/
int
tor_tls_renegotiate(tor_tls_t *tls)
{
int r;
tor_assert(tls);
/* We could do server-initiated renegotiation too, but that would be tricky.
* Instead of "SSL_renegotiate, then SSL_do_handshake until done" */
tor_assert(!tls->isServer);
if (tls->state != TOR_TLS_ST_RENEGOTIATE) {
int r = SSL_renegotiate(tls->ssl);
if (r <= 0) {
return tor_tls_get_error(tls, r, 0, "renegotiating", LOG_WARN);
}
tls->state = TOR_TLS_ST_RENEGOTIATE;
}
r = SSL_do_handshake(tls->ssl);
if (r == 1) {
tls->state = TOR_TLS_ST_OPEN;
return TOR_TLS_DONE;
} else
return tor_tls_get_error(tls, r, 0, "renegotiating handshake", LOG_INFO);
}
/** Shut down an open tls connection <b>tls</b>. When finished, returns
* TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD,
* or TOR_TLS_WANTWRITE.
*/
int
tor_tls_shutdown(tor_tls_t *tls)
{
int r, err;
char buf[128];
tor_assert(tls);
tor_assert(tls->ssl);
while (1) {
if (tls->state == TOR_TLS_ST_SENTCLOSE) {
/* If we've already called shutdown once to send a close message,
* we read until the other side has closed too.
*/
do {
r = SSL_read(tls->ssl, buf, 128);
} while (r>0);
err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading to shut down",
LOG_INFO);
if (err == _TOR_TLS_ZERORETURN) {
tls->state = TOR_TLS_ST_GOTCLOSE;
/* fall through... */
} else {
return err;
}
}
r = SSL_shutdown(tls->ssl);
if (r == 1) {
/* If shutdown returns 1, the connection is entirely closed. */
tls->state = TOR_TLS_ST_CLOSED;
return TOR_TLS_DONE;
}
err = tor_tls_get_error(tls, r, CATCH_SYSCALL|CATCH_ZERO, "shutting down",
LOG_INFO);
if (err == _TOR_TLS_SYSCALL) {
/* The underlying TCP connection closed while we were shutting down. */
tls->state = TOR_TLS_ST_CLOSED;
return TOR_TLS_DONE;
} else if (err == _TOR_TLS_ZERORETURN) {
/* The TLS connection says that it sent a shutdown record, but
* isn't done shutting down yet. Make sure that this hasn't
* happened before, then go back to the start of the function
* and try to read.
*/
if (tls->state == TOR_TLS_ST_GOTCLOSE ||
tls->state == TOR_TLS_ST_SENTCLOSE) {
log(LOG_WARN, LD_NET,
"TLS returned \"half-closed\" value while already half-closed");
return TOR_TLS_ERROR_MISC;
}
tls->state = TOR_TLS_ST_SENTCLOSE;
/* fall through ... */
} else {
return err;
}
} /* end loop */
}
/** Return true iff this TLS connection is authenticated.
*/
int
tor_tls_peer_has_cert(tor_tls_t *tls)
{
X509 *cert;
cert = SSL_get_peer_certificate(tls->ssl);
tls_log_errors(tls, LOG_WARN, "getting peer certificate");
if (!cert)
return 0;
X509_free(cert);
return 1;
}
/** Warn that a certificate lifetime extends through a certain range. */
static void
log_cert_lifetime(X509 *cert, const char *problem)
{
BIO *bio = NULL;
BUF_MEM *buf;
char *s1=NULL, *s2=NULL;
char mytime[33];
time_t now = time(NULL);
struct tm tm;
if (problem)
log_warn(LD_GENERAL,
"Certificate %s: is your system clock set incorrectly?",
problem);
if (!(bio = BIO_new(BIO_s_mem()))) {
log_warn(LD_GENERAL, "Couldn't allocate BIO!"); goto end;
}
if (!(ASN1_TIME_print(bio, X509_get_notBefore(cert)))) {
tls_log_errors(NULL, LOG_WARN, "printing certificate lifetime");
goto end;
}
BIO_get_mem_ptr(bio, &buf);
s1 = tor_strndup(buf->data, buf->length);
(void)BIO_reset(bio);
if (!(ASN1_TIME_print(bio, X509_get_notAfter(cert)))) {
tls_log_errors(NULL, LOG_WARN, "printing certificate lifetime");
goto end;
}
BIO_get_mem_ptr(bio, &buf);
s2 = tor_strndup(buf->data, buf->length);
strftime(mytime, 32, "%b %d %H:%M:%S %Y GMT", tor_gmtime_r(&now, &tm));
log_warn(LD_GENERAL,
"(certificate lifetime runs from %s through %s. Your time is %s.)",
s1,s2,mytime);
end:
/* Not expected to get invoked */
tls_log_errors(NULL, LOG_WARN, "getting certificate lifetime");
if (bio)
BIO_free(bio);
if (s1)
tor_free(s1);
if (s2)
tor_free(s2);
}
/** Helper function: try to extract a link certificate and an identity
* certificate from <b>tls</b>, and store them in *<b>cert_out</b> and
* *<b>id_cert_out</b> respectively. Log all messages at level
* <b>severity</b>.
*
* Note that a reference is added to cert_out, so it needs to be
* freed. id_cert_out doesn't. */
static void
try_to_extract_certs_from_tls(int severity, tor_tls_t *tls,
X509 **cert_out, X509 **id_cert_out)
{
X509 *cert = NULL, *id_cert = NULL;
STACK_OF(X509) *chain = NULL;
int num_in_chain, i;
*cert_out = *id_cert_out = NULL;
if (!(cert = SSL_get_peer_certificate(tls->ssl)))
return;
*cert_out = cert;
if (!(chain = SSL_get_peer_cert_chain(tls->ssl)))
return;
num_in_chain = sk_X509_num(chain);
/* 1 means we're receiving (server-side), and it's just the id_cert.
* 2 means we're connecting (client-side), and it's both the link
* cert and the id_cert.
*/
if (num_in_chain < 1) {
log_fn(severity,LD_PROTOCOL,
"Unexpected number of certificates in chain (%d)",
num_in_chain);
return;
}
for (i=0; i<num_in_chain; ++i) {
id_cert = sk_X509_value(chain, i);
if (X509_cmp(id_cert, cert) != 0)
break;
}
*id_cert_out = id_cert;
}
/** If the provided tls connection is authenticated and has a
* certificate chain that is currently valid and signed, then set
* *<b>identity_key</b> to the identity certificate's key and return
* 0. Else, return -1 and log complaints with log-level <b>severity</b>.
*/
int
tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key)
{
X509 *cert = NULL, *id_cert = NULL;
EVP_PKEY *id_pkey = NULL;
RSA *rsa;
int r = -1;
*identity_key = NULL;
try_to_extract_certs_from_tls(severity, tls, &cert, &id_cert);
if (!cert)
goto done;
if (!id_cert) {
log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found");
goto done;
}
if (!(id_pkey = X509_get_pubkey(id_cert)) ||
X509_verify(cert, id_pkey) <= 0) {
log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0");
tls_log_errors(tls, severity,"verifying certificate");
goto done;
}
rsa = EVP_PKEY_get1_RSA(id_pkey);
if (!rsa)
goto done;
*identity_key = _crypto_new_pk_env_rsa(rsa);
r = 0;
done:
if (cert)
X509_free(cert);
if (id_pkey)
EVP_PKEY_free(id_pkey);
/* This should never get invoked, but let's make sure in case OpenSSL
* acts unexpectedly. */
tls_log_errors(tls, LOG_WARN, "finishing tor_tls_verify");
return r;
}
/** Check whether the certificate set on the connection <b>tls</b> is
* expired or not-yet-valid, give or take <b>tolerance</b>
* seconds. Return 0 for valid, -1 for failure.
*
* NOTE: you should call tor_tls_verify before tor_tls_check_lifetime.
*/
int
tor_tls_check_lifetime(tor_tls_t *tls, int tolerance)
{
time_t now, t;
X509 *cert;
int r = -1;
now = time(NULL);
if (!(cert = SSL_get_peer_certificate(tls->ssl)))
goto done;
t = now + tolerance;
if (X509_cmp_time(X509_get_notBefore(cert), &t) > 0) {
log_cert_lifetime(cert, "not yet valid");
goto done;
}
t = now - tolerance;
if (X509_cmp_time(X509_get_notAfter(cert), &t) < 0) {
log_cert_lifetime(cert, "already expired");
goto done;
}
r = 0;
done:
if (cert)
X509_free(cert);
/* Not expected to get invoked */
tls_log_errors(tls, LOG_WARN, "checking certificate lifetime");
return r;
}
/** Return the number of bytes available for reading from <b>tls</b>.
*/
int
tor_tls_get_pending_bytes(tor_tls_t *tls)
{
tor_assert(tls);
return SSL_pending(tls->ssl);
}
/** If <b>tls</b> requires that the next write be of a particular size,
* return that size. Otherwise, return 0. */
size_t
tor_tls_get_forced_write_size(tor_tls_t *tls)
{
return tls->wantwrite_n;
}
/** Sets n_read and n_written to the number of bytes read and written,
* respectively, on the raw socket used by <b>tls</b> since the last time this
* function was called on <b>tls</b>. */
void
tor_tls_get_n_raw_bytes(tor_tls_t *tls, size_t *n_read, size_t *n_written)
{
BIO *wbio, *tmpbio;
unsigned long r, w;
r = BIO_number_read(SSL_get_rbio(tls->ssl));
/* We want the number of bytes actually for real written. Unfortunately,
* sometimes OpenSSL replaces the wbio on tls->ssl with a buffering bio,
* which makes the answer turn out wrong. Let's cope with that. Note
* that this approach will fail if we ever replace tls->ssl's BIOs with
* buffering bios for reasons of our own. As an alternative, we could
* save the original BIO for tls->ssl in the tor_tls_t structure, but
* that would be tempting fate. */
wbio = SSL_get_wbio(tls->ssl);
if (wbio->method == BIO_f_buffer() && (tmpbio = BIO_next(wbio)) != NULL)
wbio = tmpbio;
w = BIO_number_written(wbio);
/* We are ok with letting these unsigned ints go "negative" here:
* If we wrapped around, this should still give us the right answer, unless
* we wrapped around by more than ULONG_MAX since the last time we called
* this function.
*/
*n_read = (size_t)(r - tls->last_read_count);
*n_written = (size_t)(w - tls->last_write_count);
if (*n_read > INT_MAX || *n_written > INT_MAX) {
log_warn(LD_BUG, "Preposterously large value in tor_tls_get_n_raw_bytes. "
"r=%lu, last_read=%lu, w=%lu, last_written=%lu",
r, tls->last_read_count, w, tls->last_write_count);
}
tls->last_read_count = r;
tls->last_write_count = w;
}
/** Implement check_no_tls_errors: If there are any pending OpenSSL
* errors, log an error message. */
void
_check_no_tls_errors(const char *fname, int line)
{
if (ERR_peek_error() == 0)
return;
log(LOG_WARN, LD_CRYPTO, "Unhandled OpenSSL errors found at %s:%d: ",
tor_fix_source_file(fname), line);
tls_log_errors(NULL, LOG_WARN, NULL);
}
/** Return true iff the initial TLS connection at <b>tls</b> did not use a v2
* TLS handshake. Output is undefined if the handshake isn't finished. */
int
tor_tls_used_v1_handshake(tor_tls_t *tls)
{
if (tls->isServer) {
#ifdef V2_HANDSHAKE_SERVER
return ! tls->wasV2Handshake;
#endif
} else {
#ifdef V2_HANDSHAKE_CLIENT
return ! tls->wasV2Handshake;
#endif
}
return 1;
}
/** DOCDOC */
void
tor_tls_get_buffer_sizes(tor_tls_t *tls,
int *rbuf_capacity, int *rbuf_bytes,
int *wbuf_capacity, int *wbuf_bytes)
{
*rbuf_capacity = tls->ssl->s3->rbuf.len;
*wbuf_capacity = tls->ssl->s3->wbuf.len;
*rbuf_bytes = tls->ssl->s3->rbuf.left;
*wbuf_bytes = tls->ssl->s3->wbuf.left;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -