You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
591 lines
17 KiB
591 lines
17 KiB
/* ssl/bio_ssl.c */ |
|
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) |
|
* All rights reserved. |
|
* |
|
* This package is an SSL implementation written |
|
* by Eric Young (eay@cryptsoft.com). |
|
* The implementation was written so as to conform with Netscapes SSL. |
|
* |
|
* This library is free for commercial and non-commercial use as long as |
|
* the following conditions are aheared to. The following conditions |
|
* apply to all code found in this distribution, be it the RC4, RSA, |
|
* lhash, DES, etc., code; not just the SSL code. The SSL documentation |
|
* included with this distribution is covered by the same copyright terms |
|
* except that the holder is Tim Hudson (tjh@cryptsoft.com). |
|
* |
|
* Copyright remains Eric Young's, and as such any Copyright notices in |
|
* the code are not to be removed. |
|
* If this package is used in a product, Eric Young should be given attribution |
|
* as the author of the parts of the library used. |
|
* This can be in the form of a textual message at program startup or |
|
* in documentation (online or textual) provided with the package. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* 1. Redistributions of source code must retain the copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* 3. All advertising materials mentioning features or use of this software |
|
* must display the following acknowledgement: |
|
* "This product includes cryptographic software written by |
|
* Eric Young (eay@cryptsoft.com)" |
|
* The word 'cryptographic' can be left out if the rouines from the library |
|
* being used are not cryptographic related :-). |
|
* 4. If you include any Windows specific code (or a derivative thereof) from |
|
* the apps directory (application code) you must include an acknowledgement: |
|
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
* SUCH DAMAGE. |
|
* |
|
* The licence and distribution terms for any publically available version or |
|
* derivative of this code cannot be changed. i.e. this code cannot simply be |
|
* copied and put under another distribution licence |
|
* [including the GNU Public Licence.] |
|
*/ |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <errno.h> |
|
#include <openssl/crypto.h> |
|
#include <openssl/bio.h> |
|
#include <openssl/err.h> |
|
#include <openssl/ssl.h> |
|
|
|
static int ssl_write(BIO *h, const char *buf, int num); |
|
static int ssl_read(BIO *h, char *buf, int size); |
|
static int ssl_puts(BIO *h, const char *str); |
|
static long ssl_ctrl(BIO *h, int cmd, long arg1, void *arg2); |
|
static int ssl_new(BIO *h); |
|
static int ssl_free(BIO *data); |
|
static long ssl_callback_ctrl(BIO *h, int cmd, bio_info_cb *fp); |
|
typedef struct bio_ssl_st { |
|
SSL *ssl; /* The ssl handle :-) */ |
|
/* re-negotiate every time the total number of bytes is this size */ |
|
int num_renegotiates; |
|
unsigned long renegotiate_count; |
|
unsigned long byte_count; |
|
unsigned long renegotiate_timeout; |
|
unsigned long last_time; |
|
} BIO_SSL; |
|
|
|
static BIO_METHOD methods_sslp = { |
|
BIO_TYPE_SSL, "ssl", |
|
ssl_write, |
|
ssl_read, |
|
ssl_puts, |
|
NULL, /* ssl_gets, */ |
|
ssl_ctrl, |
|
ssl_new, |
|
ssl_free, |
|
ssl_callback_ctrl, |
|
}; |
|
|
|
BIO_METHOD *BIO_f_ssl(void) |
|
{ |
|
return (&methods_sslp); |
|
} |
|
|
|
static int ssl_new(BIO *bi) |
|
{ |
|
BIO_SSL *bs; |
|
|
|
bs = (BIO_SSL *)OPENSSL_malloc(sizeof(BIO_SSL)); |
|
if (bs == NULL) { |
|
BIOerr(BIO_F_SSL_NEW, ERR_R_MALLOC_FAILURE); |
|
return (0); |
|
} |
|
memset(bs, 0, sizeof(BIO_SSL)); |
|
bi->init = 0; |
|
bi->ptr = (char *)bs; |
|
bi->flags = 0; |
|
return (1); |
|
} |
|
|
|
static int ssl_free(BIO *a) |
|
{ |
|
BIO_SSL *bs; |
|
|
|
if (a == NULL) |
|
return (0); |
|
bs = (BIO_SSL *)a->ptr; |
|
if (bs->ssl != NULL) |
|
SSL_shutdown(bs->ssl); |
|
if (a->shutdown) { |
|
if (a->init && (bs->ssl != NULL)) |
|
SSL_free(bs->ssl); |
|
a->init = 0; |
|
a->flags = 0; |
|
} |
|
if (a->ptr != NULL) |
|
OPENSSL_free(a->ptr); |
|
return (1); |
|
} |
|
|
|
static int ssl_read(BIO *b, char *out, int outl) |
|
{ |
|
int ret = 1; |
|
BIO_SSL *sb; |
|
SSL *ssl; |
|
int retry_reason = 0; |
|
int r = 0; |
|
|
|
if (out == NULL) |
|
return (0); |
|
sb = (BIO_SSL *)b->ptr; |
|
ssl = sb->ssl; |
|
|
|
BIO_clear_retry_flags(b); |
|
|
|
#if 0 |
|
if (!SSL_is_init_finished(ssl)) { |
|
/* ret=SSL_do_handshake(ssl); */ |
|
if (ret > 0) { |
|
|
|
outflags = (BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY); |
|
ret = -1; |
|
goto end; |
|
} |
|
} |
|
#endif |
|
/* if (ret > 0) */ |
|
ret = SSL_read(ssl, out, outl); |
|
|
|
switch (SSL_get_error(ssl, ret)) { |
|
case SSL_ERROR_NONE: |
|
if (ret <= 0) |
|
break; |
|
if (sb->renegotiate_count > 0) { |
|
sb->byte_count += ret; |
|
if (sb->byte_count > sb->renegotiate_count) { |
|
sb->byte_count = 0; |
|
sb->num_renegotiates++; |
|
SSL_renegotiate(ssl); |
|
r = 1; |
|
} |
|
} |
|
if ((sb->renegotiate_timeout > 0) && (!r)) { |
|
unsigned long tm; |
|
|
|
tm = (unsigned long)time(NULL); |
|
if (tm > sb->last_time + sb->renegotiate_timeout) { |
|
sb->last_time = tm; |
|
sb->num_renegotiates++; |
|
SSL_renegotiate(ssl); |
|
} |
|
} |
|
|
|
break; |
|
case SSL_ERROR_WANT_READ: |
|
BIO_set_retry_read(b); |
|
break; |
|
case SSL_ERROR_WANT_WRITE: |
|
BIO_set_retry_write(b); |
|
break; |
|
case SSL_ERROR_WANT_X509_LOOKUP: |
|
BIO_set_retry_special(b); |
|
retry_reason = BIO_RR_SSL_X509_LOOKUP; |
|
break; |
|
case SSL_ERROR_WANT_ACCEPT: |
|
BIO_set_retry_special(b); |
|
retry_reason = BIO_RR_ACCEPT; |
|
break; |
|
case SSL_ERROR_WANT_CONNECT: |
|
BIO_set_retry_special(b); |
|
retry_reason = BIO_RR_CONNECT; |
|
break; |
|
case SSL_ERROR_SYSCALL: |
|
case SSL_ERROR_SSL: |
|
case SSL_ERROR_ZERO_RETURN: |
|
default: |
|
break; |
|
} |
|
|
|
b->retry_reason = retry_reason; |
|
return (ret); |
|
} |
|
|
|
static int ssl_write(BIO *b, const char *out, int outl) |
|
{ |
|
int ret, r = 0; |
|
int retry_reason = 0; |
|
SSL *ssl; |
|
BIO_SSL *bs; |
|
|
|
if (out == NULL) |
|
return (0); |
|
bs = (BIO_SSL *)b->ptr; |
|
ssl = bs->ssl; |
|
|
|
BIO_clear_retry_flags(b); |
|
|
|
/* |
|
* ret=SSL_do_handshake(ssl); if (ret > 0) |
|
*/ |
|
ret = SSL_write(ssl, out, outl); |
|
|
|
switch (SSL_get_error(ssl, ret)) { |
|
case SSL_ERROR_NONE: |
|
if (ret <= 0) |
|
break; |
|
if (bs->renegotiate_count > 0) { |
|
bs->byte_count += ret; |
|
if (bs->byte_count > bs->renegotiate_count) { |
|
bs->byte_count = 0; |
|
bs->num_renegotiates++; |
|
SSL_renegotiate(ssl); |
|
r = 1; |
|
} |
|
} |
|
if ((bs->renegotiate_timeout > 0) && (!r)) { |
|
unsigned long tm; |
|
|
|
tm = (unsigned long)time(NULL); |
|
if (tm > bs->last_time + bs->renegotiate_timeout) { |
|
bs->last_time = tm; |
|
bs->num_renegotiates++; |
|
SSL_renegotiate(ssl); |
|
} |
|
} |
|
break; |
|
case SSL_ERROR_WANT_WRITE: |
|
BIO_set_retry_write(b); |
|
break; |
|
case SSL_ERROR_WANT_READ: |
|
BIO_set_retry_read(b); |
|
break; |
|
case SSL_ERROR_WANT_X509_LOOKUP: |
|
BIO_set_retry_special(b); |
|
retry_reason = BIO_RR_SSL_X509_LOOKUP; |
|
break; |
|
case SSL_ERROR_WANT_CONNECT: |
|
BIO_set_retry_special(b); |
|
retry_reason = BIO_RR_CONNECT; |
|
case SSL_ERROR_SYSCALL: |
|
case SSL_ERROR_SSL: |
|
default: |
|
break; |
|
} |
|
|
|
b->retry_reason = retry_reason; |
|
return (ret); |
|
} |
|
|
|
static long ssl_ctrl(BIO *b, int cmd, long num, void *ptr) |
|
{ |
|
SSL **sslp, *ssl; |
|
BIO_SSL *bs; |
|
BIO *dbio, *bio; |
|
long ret = 1; |
|
|
|
bs = (BIO_SSL *)b->ptr; |
|
ssl = bs->ssl; |
|
if ((ssl == NULL) && (cmd != BIO_C_SET_SSL)) |
|
return (0); |
|
switch (cmd) { |
|
case BIO_CTRL_RESET: |
|
SSL_shutdown(ssl); |
|
|
|
if (ssl->handshake_func == ssl->method->ssl_connect) |
|
SSL_set_connect_state(ssl); |
|
else if (ssl->handshake_func == ssl->method->ssl_accept) |
|
SSL_set_accept_state(ssl); |
|
|
|
SSL_clear(ssl); |
|
|
|
if (b->next_bio != NULL) |
|
ret = BIO_ctrl(b->next_bio, cmd, num, ptr); |
|
else if (ssl->rbio != NULL) |
|
ret = BIO_ctrl(ssl->rbio, cmd, num, ptr); |
|
else |
|
ret = 1; |
|
break; |
|
case BIO_CTRL_INFO: |
|
ret = 0; |
|
break; |
|
case BIO_C_SSL_MODE: |
|
if (num) /* client mode */ |
|
SSL_set_connect_state(ssl); |
|
else |
|
SSL_set_accept_state(ssl); |
|
break; |
|
case BIO_C_SET_SSL_RENEGOTIATE_TIMEOUT: |
|
ret = bs->renegotiate_timeout; |
|
if (num < 60) |
|
num = 5; |
|
bs->renegotiate_timeout = (unsigned long)num; |
|
bs->last_time = (unsigned long)time(NULL); |
|
break; |
|
case BIO_C_SET_SSL_RENEGOTIATE_BYTES: |
|
ret = bs->renegotiate_count; |
|
if ((long)num >= 512) |
|
bs->renegotiate_count = (unsigned long)num; |
|
break; |
|
case BIO_C_GET_SSL_NUM_RENEGOTIATES: |
|
ret = bs->num_renegotiates; |
|
break; |
|
case BIO_C_SET_SSL: |
|
if (ssl != NULL) { |
|
ssl_free(b); |
|
if (!ssl_new(b)) |
|
return 0; |
|
} |
|
b->shutdown = (int)num; |
|
ssl = (SSL *)ptr; |
|
((BIO_SSL *)b->ptr)->ssl = ssl; |
|
bio = SSL_get_rbio(ssl); |
|
if (bio != NULL) { |
|
if (b->next_bio != NULL) |
|
BIO_push(bio, b->next_bio); |
|
b->next_bio = bio; |
|
CRYPTO_add(&bio->references, 1, CRYPTO_LOCK_BIO); |
|
} |
|
b->init = 1; |
|
break; |
|
case BIO_C_GET_SSL: |
|
if (ptr != NULL) { |
|
sslp = (SSL **)ptr; |
|
*sslp = ssl; |
|
} else |
|
ret = 0; |
|
break; |
|
case BIO_CTRL_GET_CLOSE: |
|
ret = b->shutdown; |
|
break; |
|
case BIO_CTRL_SET_CLOSE: |
|
b->shutdown = (int)num; |
|
break; |
|
case BIO_CTRL_WPENDING: |
|
ret = BIO_ctrl(ssl->wbio, cmd, num, ptr); |
|
break; |
|
case BIO_CTRL_PENDING: |
|
ret = SSL_pending(ssl); |
|
if (ret == 0) |
|
ret = BIO_pending(ssl->rbio); |
|
break; |
|
case BIO_CTRL_FLUSH: |
|
BIO_clear_retry_flags(b); |
|
ret = BIO_ctrl(ssl->wbio, cmd, num, ptr); |
|
BIO_copy_next_retry(b); |
|
break; |
|
case BIO_CTRL_PUSH: |
|
if ((b->next_bio != NULL) && (b->next_bio != ssl->rbio)) { |
|
SSL_set_bio(ssl, b->next_bio, b->next_bio); |
|
CRYPTO_add(&b->next_bio->references, 1, CRYPTO_LOCK_BIO); |
|
} |
|
break; |
|
case BIO_CTRL_POP: |
|
/* Only detach if we are the BIO explicitly being popped */ |
|
if (b == ptr) { |
|
/* |
|
* Shouldn't happen in practice because the rbio and wbio are the |
|
* same when pushed. |
|
*/ |
|
if (ssl->rbio != ssl->wbio) |
|
BIO_free_all(ssl->wbio); |
|
if (b->next_bio != NULL) |
|
CRYPTO_add(&b->next_bio->references, -1, CRYPTO_LOCK_BIO); |
|
ssl->wbio = NULL; |
|
ssl->rbio = NULL; |
|
} |
|
break; |
|
case BIO_C_DO_STATE_MACHINE: |
|
BIO_clear_retry_flags(b); |
|
|
|
b->retry_reason = 0; |
|
ret = (int)SSL_do_handshake(ssl); |
|
|
|
switch (SSL_get_error(ssl, (int)ret)) { |
|
case SSL_ERROR_WANT_READ: |
|
BIO_set_flags(b, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY); |
|
break; |
|
case SSL_ERROR_WANT_WRITE: |
|
BIO_set_flags(b, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY); |
|
break; |
|
case SSL_ERROR_WANT_CONNECT: |
|
BIO_set_flags(b, BIO_FLAGS_IO_SPECIAL | BIO_FLAGS_SHOULD_RETRY); |
|
b->retry_reason = b->next_bio->retry_reason; |
|
break; |
|
case SSL_ERROR_WANT_X509_LOOKUP: |
|
BIO_set_retry_special(b); |
|
b->retry_reason = BIO_RR_SSL_X509_LOOKUP; |
|
break; |
|
default: |
|
break; |
|
} |
|
break; |
|
case BIO_CTRL_DUP: |
|
dbio = (BIO *)ptr; |
|
if (((BIO_SSL *)dbio->ptr)->ssl != NULL) |
|
SSL_free(((BIO_SSL *)dbio->ptr)->ssl); |
|
((BIO_SSL *)dbio->ptr)->ssl = SSL_dup(ssl); |
|
((BIO_SSL *)dbio->ptr)->renegotiate_count = |
|
((BIO_SSL *)b->ptr)->renegotiate_count; |
|
((BIO_SSL *)dbio->ptr)->byte_count = ((BIO_SSL *)b->ptr)->byte_count; |
|
((BIO_SSL *)dbio->ptr)->renegotiate_timeout = |
|
((BIO_SSL *)b->ptr)->renegotiate_timeout; |
|
((BIO_SSL *)dbio->ptr)->last_time = ((BIO_SSL *)b->ptr)->last_time; |
|
ret = (((BIO_SSL *)dbio->ptr)->ssl != NULL); |
|
break; |
|
case BIO_C_GET_FD: |
|
ret = BIO_ctrl(ssl->rbio, cmd, num, ptr); |
|
break; |
|
case BIO_CTRL_SET_CALLBACK: |
|
{ |
|
#if 0 /* FIXME: Should this be used? -- Richard |
|
* Levitte */ |
|
SSLerr(SSL_F_SSL_CTRL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
|
ret = -1; |
|
#else |
|
ret = 0; |
|
#endif |
|
} |
|
break; |
|
case BIO_CTRL_GET_CALLBACK: |
|
{ |
|
void (**fptr) (const SSL *xssl, int type, int val); |
|
|
|
fptr = (void (**)(const SSL *xssl, int type, int val))ptr; |
|
*fptr = SSL_get_info_callback(ssl); |
|
} |
|
break; |
|
default: |
|
ret = BIO_ctrl(ssl->rbio, cmd, num, ptr); |
|
break; |
|
} |
|
return (ret); |
|
} |
|
|
|
static long ssl_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp) |
|
{ |
|
SSL *ssl; |
|
BIO_SSL *bs; |
|
long ret = 1; |
|
|
|
bs = (BIO_SSL *)b->ptr; |
|
ssl = bs->ssl; |
|
switch (cmd) { |
|
case BIO_CTRL_SET_CALLBACK: |
|
{ |
|
/* |
|
* FIXME: setting this via a completely different prototype seems |
|
* like a crap idea |
|
*/ |
|
SSL_set_info_callback(ssl, (void (*)(const SSL *, int, int))fp); |
|
} |
|
break; |
|
default: |
|
ret = BIO_callback_ctrl(ssl->rbio, cmd, fp); |
|
break; |
|
} |
|
return (ret); |
|
} |
|
|
|
static int ssl_puts(BIO *bp, const char *str) |
|
{ |
|
int n, ret; |
|
|
|
n = strlen(str); |
|
ret = BIO_write(bp, str, n); |
|
return (ret); |
|
} |
|
|
|
BIO *BIO_new_buffer_ssl_connect(SSL_CTX *ctx) |
|
{ |
|
#ifndef OPENSSL_NO_SOCK |
|
BIO *ret = NULL, *buf = NULL, *ssl = NULL; |
|
|
|
if ((buf = BIO_new(BIO_f_buffer())) == NULL) |
|
return (NULL); |
|
if ((ssl = BIO_new_ssl_connect(ctx)) == NULL) |
|
goto err; |
|
if ((ret = BIO_push(buf, ssl)) == NULL) |
|
goto err; |
|
return (ret); |
|
err: |
|
if (buf != NULL) |
|
BIO_free(buf); |
|
if (ssl != NULL) |
|
BIO_free(ssl); |
|
#endif |
|
return (NULL); |
|
} |
|
|
|
BIO *BIO_new_ssl_connect(SSL_CTX *ctx) |
|
{ |
|
#ifndef OPENSSL_NO_SOCK |
|
BIO *ret = NULL, *con = NULL, *ssl = NULL; |
|
|
|
if ((con = BIO_new(BIO_s_connect())) == NULL) |
|
return (NULL); |
|
if ((ssl = BIO_new_ssl(ctx, 1)) == NULL) |
|
goto err; |
|
if ((ret = BIO_push(ssl, con)) == NULL) |
|
goto err; |
|
return (ret); |
|
err: |
|
if (con != NULL) |
|
BIO_free(con); |
|
#endif |
|
return (NULL); |
|
} |
|
|
|
BIO *BIO_new_ssl(SSL_CTX *ctx, int client) |
|
{ |
|
BIO *ret; |
|
SSL *ssl; |
|
|
|
if ((ret = BIO_new(BIO_f_ssl())) == NULL) |
|
return (NULL); |
|
if ((ssl = SSL_new(ctx)) == NULL) { |
|
BIO_free(ret); |
|
return (NULL); |
|
} |
|
if (client) |
|
SSL_set_connect_state(ssl); |
|
else |
|
SSL_set_accept_state(ssl); |
|
|
|
BIO_set_ssl(ret, ssl, BIO_CLOSE); |
|
return (ret); |
|
} |
|
|
|
int BIO_ssl_copy_session_id(BIO *t, BIO *f) |
|
{ |
|
t = BIO_find_type(t, BIO_TYPE_SSL); |
|
f = BIO_find_type(f, BIO_TYPE_SSL); |
|
if ((t == NULL) || (f == NULL)) |
|
return (0); |
|
if ((((BIO_SSL *)t->ptr)->ssl == NULL) || |
|
(((BIO_SSL *)f->ptr)->ssl == NULL)) |
|
return (0); |
|
SSL_copy_session_id(((BIO_SSL *)t->ptr)->ssl, ((BIO_SSL *)f->ptr)->ssl); |
|
return (1); |
|
} |
|
|
|
void BIO_ssl_shutdown(BIO *b) |
|
{ |
|
SSL *s; |
|
|
|
while (b != NULL) { |
|
if (b->method->type == BIO_TYPE_SSL) { |
|
s = ((BIO_SSL *)b->ptr)->ssl; |
|
SSL_shutdown(s); |
|
break; |
|
} |
|
b = b->next_bio; |
|
} |
|
}
|
|
|