diff options
-rw-r--r-- | CHANGES | 10 | ||||
-rw-r--r-- | crypto/bio/bio.h | 36 | ||||
-rw-r--r-- | crypto/bio/bio_err.c | 1 | ||||
-rw-r--r-- | crypto/bio/bio_lib.c | 16 | ||||
-rw-r--r-- | crypto/bio/bss_bio.c | 349 | ||||
-rwxr-xr-x | ms/test.bat | 53 | ||||
-rw-r--r-- | ssl/s3_clnt.c | 1 | ||||
-rw-r--r-- | ssl/ssl_stat.c | 2 | ||||
-rw-r--r-- | ssl/ssltest.c | 377 | ||||
-rw-r--r-- | test/testssl | 37 |
10 files changed, 852 insertions, 30 deletions
@@ -4,6 +4,16 @@ Changes between 0.9.3a and 0.9.4 + *) Add missing case to s3_clnt.c state machine -- one of the new SSL tests + through a BIO pair triggered the default case, i.e. + SSLerr(...,SSL_R_UNKNOWN_STATE). + [Bodo Moeller] + + *) New "BIO pair" concept (crypto/bio/bss_bio.c) so that applications + can use the SSL library even if none of the specific BIOs is + appropriate. + [Bodo Moeller] + *) Fix a bug in i2d_DSAPublicKey() which meant it returned the wrong value for the encoded length. [Jeon KyoungHo <khjeon@sds.samsung.co.kr>] diff --git a/crypto/bio/bio.h b/crypto/bio/bio.h index f75e77b941..5d1654aaef 100644 --- a/crypto/bio/bio.h +++ b/crypto/bio/bio.h @@ -64,6 +64,7 @@ extern "C" { #endif #include <stdio.h> +#include <stdlib.h> #include <openssl/crypto.h> /* These are the 'types' of BIOs */ @@ -317,6 +318,15 @@ typedef struct bio_f_buffer_ctx_struct #define BIO_C_GET_SOCKS 134 #define BIO_C_SET_SOCKS 135 +#define BIO_C_SET_WRITE_BUF_SIZE 136/* for BIO_s_bio */ +#define BIO_C_GET_WRITE_BUF_SIZE 137 +#define BIO_C_MAKE_BIO_PAIR 138 +#define BIO_C_DESTROY_BIO_PAIR 139 +#define BIO_C_GET_WRITE_GUARANTEE 140 +#define BIO_C_GET_READ_REQUEST 141 +#define BIO_C_SHUTDOWN_WR 142 + + #define BIO_set_app_data(s,arg) BIO_set_ex_data(s,0,(char *)arg) #define BIO_get_app_data(s) BIO_get_ex_data(s,0) @@ -431,6 +441,9 @@ int BIO_read_filename(BIO *b,const char *name); #define BIO_get_close(b) (int)BIO_ctrl(b,BIO_CTRL_GET_CLOSE,0,NULL) #define BIO_pending(b) (int)BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL) #define BIO_wpending(b) (int)BIO_ctrl(b,BIO_CTRL_WPENDING,0,NULL) +/* ...pending macros have inappropriate return type */ +size_t BIO_ctrl_pending(BIO *b); +size_t BIO_ctrl_wpending(BIO *b); #define BIO_flush(b) (int)BIO_ctrl(b,BIO_CTRL_FLUSH,0,NULL) #define BIO_get_info_callback(b,cbp) (int)BIO_ctrl(b,BIO_CTRL_GET_CALLBACK,0,(char *)cbp) #define BIO_set_info_callback(b,cb) (int)BIO_ctrl(b,BIO_CTRL_SET_CALLBACK,0,(char *)cb) @@ -438,6 +451,19 @@ int BIO_read_filename(BIO *b,const char *name); /* For the BIO_f_buffer() type */ #define BIO_buffer_get_num_lines(b) BIO_ctrl(b,BIO_CTRL_GET,0,NULL) +/* For BIO_s_bio() */ +#define BIO_set_write_buf_size(b,size) (int)BIO_ctrl(b,BIO_C_SET_WRITE_BUF_SIZE,size,NULL) +#define BIO_get_write_buf_size(b,size) (size_t)BIO_ctrl(b,BIO_C_GET_WRITE_BUF_SIZE,size,NULL) +#define BIO_make_bio_pair(b1,b2) (int)BIO_ctrl(b1,BIO_C_MAKE_BIO_PAIR,0,b2) +#define BIO_destroy_bio_pair(b) (int)BIO_ctrl(b,BIO_C_DESTROY_BIO_PAIR,0,NULL) +/* macros with inappropriate type -- but ...pending macros use int too: */ +#define BIO_get_write_guarantee(b) (int)BIO_ctrl(b,BIO_C_GET_WRITE_GUARANTEE,0,NULL) +#define BIO_get_read_request(b) (int)BIO_ctrl(b,BIO_C_GET_READ_REQUEST,0,NULL) +size_t BIO_ctrl_get_write_guarantee(BIO *b); +size_t BIO_ctrl_get_read_request(BIO *b); + + + #ifdef NO_STDIO #define NO_FP_API #endif @@ -473,7 +499,7 @@ int BIO_read(BIO *b, void *data, int len); int BIO_gets(BIO *bp,char *buf, int size); int BIO_write(BIO *b, const char *data, int len); int BIO_puts(BIO *bp,const char *buf); -long BIO_ctrl(BIO *bp,int cmd,long larg,char *parg); +long BIO_ctrl(BIO *bp,int cmd,long larg,void *parg); char * BIO_ptr_ctrl(BIO *bp,int cmd,long larg); long BIO_int_ctrl(BIO *bp,int cmd,long larg,int iarg); BIO * BIO_push(BIO *b,BIO *append); @@ -538,6 +564,13 @@ BIO *BIO_new_fd(int fd, int close_flag); BIO *BIO_new_connect(char *host_port); BIO *BIO_new_accept(char *host_port); +int BIO_new_bio_pair(BIO **bio1, size_t writebuf1, + BIO **bio2, size_t writebuf2); +/* If successful, returns 1 and in *bio1, *bio2 two BIO pair endpoints. + * Otherwise returns 0 and sets *bio1 and *bio2 to NULL. + * Size 0 uses default value. + */ + void BIO_copy_next_retry(BIO *b); long BIO_ghbn_ctrl(int cmd,int iarg,char *parg); @@ -579,6 +612,7 @@ int BIO_printf(BIO *bio, ...); #define BIO_R_ACCEPT_ERROR 100 #define BIO_R_BAD_FOPEN_MODE 101 #define BIO_R_BAD_HOSTNAME_LOOKUP 102 +#define BIO_R_BROKEN_PIPE 124 #define BIO_R_CONNECT_ERROR 103 #define BIO_R_ERROR_SETTING_NBIO 104 #define BIO_R_ERROR_SETTING_NBIO_ON_ACCEPTED_SOCKET 105 diff --git a/crypto/bio/bio_err.c b/crypto/bio/bio_err.c index badbfc1919..e31bf32aa4 100644 --- a/crypto/bio/bio_err.c +++ b/crypto/bio/bio_err.c @@ -95,6 +95,7 @@ static ERR_STRING_DATA BIO_str_reasons[]= {BIO_R_ACCEPT_ERROR ,"accept error"}, {BIO_R_BAD_FOPEN_MODE ,"bad fopen mode"}, {BIO_R_BAD_HOSTNAME_LOOKUP ,"bad hostname lookup"}, +{BIO_R_BROKEN_PIPE ,"broken pipe"}, {BIO_R_CONNECT_ERROR ,"connect error"}, {BIO_R_ERROR_SETTING_NBIO ,"error setting nbio"}, {BIO_R_ERROR_SETTING_NBIO_ON_ACCEPTED_SOCKET,"error setting nbio on accepted socket"}, diff --git a/crypto/bio/bio_lib.c b/crypto/bio/bio_lib.c index 62d26992af..b72688ea90 100644 --- a/crypto/bio/bio_lib.c +++ b/crypto/bio/bio_lib.c @@ -290,7 +290,7 @@ char *BIO_ptr_ctrl(BIO *b, int cmd, long larg) return(p); } -long BIO_ctrl(BIO *b, int cmd, long larg, char *parg) +long BIO_ctrl(BIO *b, int cmd, long larg, void *parg) { long ret; long (*cb)(); @@ -317,6 +317,20 @@ long BIO_ctrl(BIO *b, int cmd, long larg, char *parg) return(ret); } +/* It is unfortunate to duplicate in functions what the BIO_(w)pending macros + * do; but those macros have inappropriate return type, and for interfacing + * from other programming languages, C macros aren't much of a help anyway. */ +size_t BIO_ctrl_pending(BIO *bio) + { + return BIO_ctrl(bio, BIO_CTRL_PENDING, 0, NULL); + } + +size_t BIO_ctrl_wpending(BIO *bio) + { + return BIO_ctrl(bio, BIO_CTRL_WPENDING, 0, NULL); + } + + /* put the 'bio' on the end of b's list of operators */ BIO *BIO_push(BIO *b, BIO *bio) { diff --git a/crypto/bio/bss_bio.c b/crypto/bio/bss_bio.c index f026d51807..ea9d5c714a 100644 --- a/crypto/bio/bss_bio.c +++ b/crypto/bio/bss_bio.c @@ -1,11 +1,11 @@ /* crypto/bio/bss_bio.c -*- Mode: C; c-file-style: "eay" -*- */ -/* *** Not yet finished (or even tested). *** */ - /* Special method for a BIO where the other endpoint is also a BIO - * of this kind, handled by the same thread. + * of this kind, handled by the same thread (i.e. the "peer" is actually + * ourselves, wearing a different hat). * Such "BIO pairs" are mainly for using the SSL library with I/O interfaces - * for which no specific BIO method is available. */ + * for which no specific BIO method is available. + * See ssl/ssltest.c for some hints on how this can be used. */ #include <assert.h> #include <stdlib.h> @@ -19,7 +19,7 @@ static int bio_new(BIO *bio); static int bio_free(BIO *bio); static int bio_read(BIO *bio, char *buf, int size); static int bio_write(BIO *bio, char *buf, int num); -static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr); +static long bio_ctrl(BIO *bio, int cmd, long num, void *ptr); static int bio_puts(BIO *bio, char *str); static int bio_make_pair(BIO *bio1, BIO *bio2); @@ -74,6 +74,7 @@ static int bio_new(BIO *bio) b->size = 17*1024; /* enough for one TLS record (just a default) */ b->buf = NULL; + bio->ptr = b; return 1; } @@ -103,19 +104,167 @@ static int bio_free(BIO *bio) -static int bio_read(BIO *bio, char *buf, int size) +static int bio_read(BIO *bio, char *buf, int size_) { - /* XXX */ - return -1; + size_t size = size_; + size_t rest; + struct bio_bio_st *b, *peer_b; + + BIO_clear_retry_flags(bio); + + if (!bio->init) + return 0; + + b = bio->ptr; + assert(b != NULL); + assert(b->peer != NULL); + peer_b = b->peer->ptr; + assert(peer_b != NULL); + assert(peer_b->buf != NULL); + + peer_b->request = 0; /* will be set in "retry_read" situation */ + + if (buf == NULL || size == 0) + return 0; + + if (peer_b->len == 0) + { + if (peer_b->closed) + return 0; /* writer has closed, and no data is left */ + else + { + BIO_set_retry_read(bio); /* buffer is empty */ + if (size <= peer_b->size) + peer_b->request = size; + else + peer_b->request = peer_b->size; /* don't ask for more than + * the peer can deliver + * in one write */ + return -1; + } + } + + /* we can read */ + if (peer_b->len < size) + size = peer_b->len; + + /* now read "size" bytes */ + + rest = size; + + assert(rest > 0); + do /* one or two iterations */ + { + size_t chunk; + + assert(rest <= peer_b->len); + if (peer_b->offset + rest <= peer_b->size) + chunk = rest; + else + /* wrap around ring buffer */ + chunk = peer_b->size - peer_b->offset; + assert(peer_b->offset + chunk <= peer_b->size); + + memcpy(buf, peer_b->buf + peer_b->offset, chunk); + + peer_b->len -= chunk; + if (peer_b->len) + { + peer_b->offset += chunk; + assert(peer_b->offset <= peer_b->size); + if (peer_b->offset == peer_b->size) + peer_b->offset = 0; + buf += chunk; + } + else + { + /* buffer now empty, no need to advance "buf" */ + assert(chunk == rest); + peer_b->offset = 0; + } + rest -= chunk; + } + while (rest); + + peer_b->request -= size; + return size; } -static int bio_write(BIO *bio, char *buf, int num) +static int bio_write(BIO *bio, char *buf, int num_) { - /* XXX */ - return -1; - } + size_t num = num_; + size_t rest; + struct bio_bio_st *b; + + BIO_clear_retry_flags(bio); + + if (!bio->init || buf == NULL || num == 0) + return 0; + + b = bio->ptr; + assert(b != NULL); + assert(b->peer != NULL); + assert(b->buf != NULL); + + b->request = 0; + if (b->closed) + { + /* we already closed */ + BIOerr(BIO_F_BIO_WRITE, BIO_R_BROKEN_PIPE); + return -1; + } + + assert(b->len <= b->size); + + if (b->len == b->size) + { + BIO_set_retry_write(bio); /* buffer is full */ + return -1; + } + + /* we can write */ + if (num > b->size - b->len) + num = b->size - b->len; + + /* now write "num" bytes */ + + rest = num; -static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr) + assert(rest > 0); + do /* one or two iterations */ + { + size_t write_offset; + size_t chunk; + + assert(b->len + rest <= b->size); + + write_offset = b->offset + b->len; + if (write_offset >= b->size) + write_offset -= b->size; + /* b->buf[write_offset] is the first byte we can write to. */ + + if (write_offset + rest <= b->size) + chunk = rest; + else + /* wrap around ring buffer */ + chunk = b->size - write_offset; + + memcpy(b->buf + write_offset, buf, chunk); + + b->len += chunk; + + assert(b->len <= b->size); + + rest -= chunk; + buf += chunk; + } + while (rest); + + return num; + } + + +static long bio_ctrl(BIO *bio, int cmd, long num, void *ptr) { long ret; struct bio_bio_st *b = bio->ptr; @@ -124,13 +273,76 @@ static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr) switch (cmd) { - /* XXX Additional commands: */ - /* - Set buffer size */ - /* - make pair */ - /* - destroy pair */ - /* - get number of bytes that the next write will accept */ - /* - get number of bytes requested by peer */ - /* - send "close" */ + /* specific CTRL codes */ + + case BIO_C_SET_WRITE_BUF_SIZE: + if (b->peer) + { + BIOerr(BIO_F_BIO_CTRL, BIO_R_IN_USE); + ret = 0; + } + else + { + size_t new_size = num; + + if (b->size != new_size) + { + if (b->buf) + { + Free(b->buf); + b->buf = NULL; + } + b->size = new_size; + } + ret = 1; + } + break; + + case BIO_C_GET_WRITE_BUF_SIZE: + num = (long) b->size; + + case BIO_C_MAKE_BIO_PAIR: + { + BIO *other_bio = ptr; + + if (bio_make_pair(bio, other_bio)) + ret = 1; + else + ret = 0; + } + break; + + case BIO_C_DESTROY_BIO_PAIR: + /* Effects both BIOs in the pair -- call just once! + * Or let BIO_free(bio1); BIO_free(bio2); do the job. */ + bio_destroy_pair(bio); + ret = 1; + break; + + case BIO_C_GET_WRITE_GUARANTEE: + /* How many bytes can the caller feed to the next write + * withouth having to keep any? */ + if (b->peer == NULL || b->closed) + ret = 0; + else + ret = (long) b->size - b->len; + break; + + case BIO_C_GET_READ_REQUEST: + /* If the peer unsuccesfully tried to read, how many bytes + * were requested? (As with BIO_CTRL_PENDING, that number + * can usually be treated as boolean.) */ + ret = (long) b->request; + break; + + case BIO_C_SHUTDOWN_WR: + /* similar to shutdown(..., SHUT_WR) */ + b->closed = 1; + ret = 1; + break; + + + /* standard CTRL codes follow */ case BIO_CTRL_RESET: if (b->buf != NULL) @@ -169,7 +381,20 @@ static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr) break; case BIO_CTRL_DUP: - /* XXX */ + /* See BIO_dup_chain for circumstances we have to expect. */ + { + BIO *other_bio = ptr; + struct bio_bio_st *other_b; + + assert(other_bio != NULL); + other_b = other_bio->ptr; + assert(other_b != NULL); + + assert(other_b->buf == NULL); /* other_bio is always fresh */ + + other_b->size = b->size; + } + ret = 1; break; @@ -177,6 +402,22 @@ static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr) ret = 1; break; + case BIO_CTRL_EOF: + { + BIO *other_bio = ptr; + + if (other_bio) + { + struct bio_bio_st *other_b = other_bio->ptr; + + assert(other_b != NULL); + ret = other_b->len == 0 && other_b->closed; + } + else + ret = 1; + } + break; + default: ret = 0; } @@ -188,8 +429,6 @@ static int bio_puts(BIO *bio, char *str) return bio_write(bio, str, strlen(str)); } -/* Until bio_make_pair is used, make a dummy function use it for -pedantic */ -void dummy() { bio_make_pair(NULL,NULL); } static int bio_make_pair(BIO *bio1, BIO *bio2) { @@ -273,3 +512,67 @@ static void bio_destroy_pair(BIO *bio) } } } + + +/* Exported convenience functions */ +int BIO_new_bio_pair(BIO **bio1_p, size_t writebuf1, + BIO **bio2_p, size_t writebuf2) + { + BIO *bio1 = NULL, *bio2 = NULL; + long r; + int ret = 0; + + bio1 = BIO_new(BIO_s_bio()); + if (bio1 == NULL) + goto err; + bio2 = BIO_new(BIO_s_bio()); + if (bio2 == NULL) + goto err; + + if (writebuf1) + { + r = BIO_set_write_buf_size(bio1, writebuf1); + if (!r) + goto err; + } + if (writebuf2) + { + r = BIO_set_write_buf_size(bio2, writebuf2); + if (!r) + goto err; + } + + r = BIO_make_bio_pair(bio1, bio2); + if (!r) + goto err; + ret = 1; + + err: + if (ret == 0) + { + if (bio1) + { + BIO_free(bio1); + bio1 = NULL; + } + if (bio2) + { + BIO_free(bio2); + bio2 = NULL; + } + } + + *bio1_p = bio1; + *bio2_p = bio2; + return ret; + } + +size_t BIO_ctrl_get_write_guarantee(BIO *bio) + { + return BIO_ctrl(bio, BIO_C_GET_WRITE_GUARANTEE, 0, NULL); + } + +size_t BIO_ctrl_read_request(BIO *bio) + { + return BIO_ctrl(bio, BIO_C_GET_READ_REQUEST, 0, NULL); + } diff --git a/ms/test.bat b/ms/test.bat index e2779de2a9..a40f7478d7 100755 --- a/ms/test.bat +++ b/ms/test.bat @@ -91,7 +91,7 @@ echo test sslv2 with server authentication ssltest -ssl2 -server_auth -CAfile cert.tmp
if errorlevel 1 goto done
-echo test sslv2 with client authentication
+echo test sslv2 with client authentication
ssltest -ssl2 -client_auth -CAfile cert.tmp
if errorlevel 1 goto done
@@ -107,7 +107,7 @@ echo test sslv3 with server authentication ssltest -ssl3 -server_auth -CAfile cert.tmp
if errorlevel 1 goto done
-echo test sslv3 with client authentication
+echo test sslv3 with client authentication
ssltest -ssl3 -client_auth -CAfile cert.tmp
if errorlevel 1 goto done
@@ -123,7 +123,7 @@ echo test sslv2/sslv3 with server authentication ssltest -server_auth -CAfile cert.tmp
if errorlevel 1 goto done
-echo test sslv2/sslv3 with client authentication
+echo test sslv2/sslv3 with client authentication
ssltest -client_auth -CAfile cert.tmp
if errorlevel 1 goto done
@@ -131,6 +131,53 @@ echo test sslv2/sslv3 with both client and server authentication ssltest -server_auth -client_auth -CAfile cert.tmp
if errorlevel 1 goto done
+echo test sslv2 via BIO pair
+ssltest -bio_pair -ssl2
+if errorlevel 1 goto done
+
+echo test sslv2 with server authentication via BIO pair
+ssltest -bio_pair -ssl2 -server_auth -CAfile cert.tmp
+if errorlevel 1 goto done
+
+echo test sslv2 with client authentication via BIO pair
+ssltest -bio_pair -ssl2 -client_auth -CAfile cert.tmp
+if errorlevel 1 goto done
+
+echo test sslv2 with both client and server authentication via BIO pair
+ssltest -bio_pair -ssl2 -server_auth -client_auth -CAfile cert.tmp
+if errorlevel 1 goto done
+
+echo test sslv3 via BIO pair
+ssltest -bio_pair -ssl3
+if errorlevel 1 goto done
+
+echo test sslv3 with server authentication via BIO pair
+ssltest -bio_pair -ssl3 -server_auth -CAfile cert.tmp
+if errorlevel 1 goto done
+
+echo test sslv3 with client authentication via BIO pair
+ssltest -bio_pair -ssl3 -client_auth -CAfile cert.tmp
+if errorlevel 1 goto done
+
+echo test sslv3 with both client and server authentication via BIO pair
+ssltest -bio_pair -ssl3 -server_auth -client_auth -CAfile cert.tmp
+if errorlevel 1 goto done
+
+echo test sslv2/sslv3 via BIO pair
+ssltest
+if errorlevel 1 goto done
+
+echo test sslv2/sslv3 with server authentication
+ssltest -bio_pair -server_auth -CAfile cert.tmp
+if errorlevel 1 goto done
+
+echo test sslv2/sslv3 with client authentication via BIO pair
+ssltest -bio_pair -client_auth -CAfile cert.tmp
+if errorlevel 1 goto done
+
+echo test sslv2/sslv3 with both client and server authentication via BIO pair
+ssltest -bio_pair -server_auth -client_auth -CAfile cert.tmp
+if errorlevel 1 goto done
del cert.tmp
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c index 38d42c4bf7..ae850c0875 100644 --- a/ssl/s3_clnt.c +++ b/ssl/s3_clnt.c @@ -254,6 +254,7 @@ int ssl3_connect(SSL *s) case SSL3_ST_CW_CERT_A: case SSL3_ST_CW_CERT_B: case SSL3_ST_CW_CERT_C: + case SSL3_ST_CW_CERT_D: ret=ssl3_send_client_certificate(s); if (ret <= 0) goto end; s->state=SSL3_ST_CW_KEY_EXCH_A; diff --git a/ssl/ssl_stat.c b/ssl/ssl_stat.c index 14f58dbdfd..09194888b5 100644 --- a/ssl/ssl_stat.c +++ b/ssl/ssl_stat.c @@ -131,6 +131,8 @@ case SSL3_ST_CR_SRVR_DONE_A: str="SSLv3 read server done A"; break; case SSL3_ST_CR_SRVR_DONE_B: str="SSLv3 read server done B"; break; case SSL3_ST_CW_CERT_A: str="SSLv3 write client certificate A"; break; case SSL3_ST_CW_CERT_B: str="SSLv3 write client certificate B"; break; +case SSL3_ST_CW_CERT_C: str="SSLv3 write client certificate C"; break; +case SSL3_ST_CW_CERT_D: str="SSLv3 write client certificate D"; break; case SSL3_ST_CW_KEY_EXCH_A: str="SSLv3 write client key exchange A"; break; case SSL3_ST_CW_KEY_EXCH_B: str="SSLv3 write client key exchange B"; break; case SSL3_ST_CW_CERT_VRFY_A: str="SSLv3 write certificate verify A"; break; diff --git a/ssl/ssltest.c b/ssl/ssltest.c index c02c26c716..91813dc7da 100644 --- a/ssl/ssltest.c +++ b/ssl/ssltest.c @@ -60,6 +60,7 @@ #include <stdlib.h> #include <string.h> #include <errno.h> +#include <limits.h> #include "openssl/e_os.h" @@ -105,6 +106,7 @@ static int s_nbio=0; #endif +int doit_biopair(SSL *s_ssl,SSL *c_ssl,long bytes); int doit(SSL *s_ssl,SSL *c_ssl,long bytes); static void sv_usage(void) { @@ -132,12 +134,16 @@ static void sv_usage(void) fprintf(stderr," -s_cert arg - Just the server certificate file\n"); fprintf(stderr," -c_cert arg - Just the client certificate file\n"); fprintf(stderr," -cipher arg - The cipher list\n"); + fprintf(stderr," -bio_pair - Use BIO pairs\n"); + fprintf(stderr," -f - Test even cases that can't work\n"); } int main(int argc, char *argv[]) { char *CApath=NULL,*CAfile=NULL; int badop=0; + int bio_pair=0; + int force=0; int tls1=0,ssl2=0,ssl3=0,ret=1; int client_auth=0; int server_auth=0,i; @@ -225,6 +231,14 @@ int main(int argc, char *argv[]) if (--argc < 1) goto bad; CAfile= *(++argv); } + else if (strcmp(*argv,"-bio_pair") == 0) + { + bio_pair = 1; + } + else if (strcmp(*argv,"-f") == 0) + { + force = 1; + } else { fprintf(stderr,"unknown option %s\n",*argv); @@ -241,6 +255,17 @@ bad: goto end; } + if (!ssl2 && !ssl3 && !tls1 && number > 1 && !reuse && !force) + { + fprintf(stderr, "This case cannot work. Use -f switch to perform " + "the test anyway\n" + "(and -d to see what happens, " + "and -bio_pair to really make it happen :-)\n" + "or add one of -ssl2, -ssl3, -tls1, -reuse to " + "avoid protocol mismatch.\n"); + exit(1); + } + /* if (cipher == NULL) cipher=getenv("SSL_CIPHER"); */ SSL_library_init(); @@ -338,7 +363,10 @@ bad: for (i=0; i<number; i++) { if (!reuse) SSL_set_session(c_ssl,NULL); - ret=doit(s_ssl,c_ssl,bytes); + if (bio_pair) + ret=doit_biopair(s_ssl,c_ssl,bytes); + else + ret=doit(s_ssl,c_ssl,bytes); } if (!verbose) @@ -368,6 +396,353 @@ end: EXIT(ret); } +int doit_biopair(SSL *s_ssl, SSL *c_ssl, long count) + { + long cw_num = count, cr_num = count, sw_num = count, sr_num = count; + BIO *s_ssl_bio = NULL, *c_ssl_bio = NULL; + BIO *server = NULL, *server_io = NULL, *client = NULL, *client_io = NULL; + SSL_CIPHER *ciph; + int ret = 1; + + size_t bufsiz = 256; /* small buffer for testing */ + + if (!BIO_new_bio_pair(&server, bufsiz, &server_io, bufsiz)) + goto err; + if (!BIO_new_bio_pair(&client, bufsiz, &client_io, bufsiz)) + goto err; + + s_ssl_bio = BIO_new(BIO_f_ssl()); + if (!s_ssl_bio) + goto err; + + c_ssl_bio = BIO_new(BIO_f_ssl()); + if (!c_ssl_bio) + goto err; + + SSL_set_connect_state(c_ssl); + SSL_set_bio(c_ssl, client, client); + (void)BIO_set_ssl(c_ssl_bio, c_ssl, BIO_NOCLOSE); + + SSL_set_accept_state(s_ssl); + SSL_set_bio(s_ssl, server, server); + (void)BIO_set_ssl(s_ssl_bio, s_ssl, BIO_NOCLOSE); + + do + { + /* c_ssl_bio: SSL filter BIO + * + * client: pseudo-I/O for SSL library + * + * client_io: client's SSL communication; usually to be + * relayed over some I/O facility, but in this + * test program, we're the server, too: + * + * server_io: server's SSL communication + * + * server: pseudo-I/O for SSL library + * + * s_ssl_bio: SSL filter BIO + * + * The client and the server each employ a "BIO pair": + * client + client_io, server + server_io. + * BIO pairs are symmetric. A BIO pair behaves similar + * to a non-blocking socketpair (but both endpoints must + * be handled by the same thread). + * + * Useful functions for querying the state of BIO pair endpoints: + * + * BIO_ctrl_pending(bio) number of bytes we can read now + * BIO_ctrl_get_read_request(bio) number of bytes needed to fulfil + * other side's read attempt + * BIO_ctrl_get_write_gurantee(bio) number of bytes we can write now + * + * ..._read_request is never more than ..._write_guarantee; + * it depends on the application which one you should use. + */ + + /* We have non-blocking behaviour throughout this test program, but + * can be sure that there is *some* progress in each iteration; so + * we don't have to worry about ..._SHOULD_READ or ..._SHOULD_WRITE + * -- we just try everything in each iteration + */ + + { + /* CLIENT */ + + MS_STATIC char cbuf[1024*8]; + int i, r; + + if (debug) + if (SSL_in_init(c_ssl)) + printf("client waiting in SSL_connect - %s\n", + SSL_state_string_long(c_ssl)); + + if (cw_num > 0) + { + /* Write to server. */ + + if (cw_num > (long)sizeof cbuf) + i = sizeof cbuf; + else + i = (int)cw_num; + r = BIO_write(c_ssl_bio, cbuf, i); + if (r == -1) + { + if (!BIO_should_retry(c_ssl_bio)) + { + fprintf(stderr,"ERROR in CLIENT\n"); + goto err; + } + /* BIO_should_retry(...) can just be ignored here. + * The library expects us to call BIO_write with + * the same arguments again, and that's what we will + * do in the next iteration. */ + } + else if (r == 0) + { + fprintf(stderr,"SSL CLIENT STARTUP FAILED\n"); + goto err; + } + else + { + if (debug) + printf("client wrote %d\n", r); + cw_num -= r; + } + } + + if (cr_num > 0) + { + /* Read from server. */ + + r = BIO_read(c_ssl_bio, cbuf, sizeof(cbuf)); + if (r < 0) + { + if (!BIO_should_retry(c_ssl_bio)) + { + fprintf(stderr,"ERROR in CLIENT\n"); + goto err; + } + /* Again, "BIO_should_retry" can be ignored. */ + } + else if (r == 0) + { + fprintf(stderr,"SSL CLIENT STARTUP FAILED\n"); + goto err; + } + else + { + if (debug) + printf("client read %d\n", r); + cr_num -= r; + } + } + } + + { + /* SERVER */ + + MS_STATIC char sbuf[1024*8]; + int i, r; + + if (debug) + if (SSL_in_init(s_ssl)) + printf("server waiting in SSL_accept - %s\n", + SSL_state_string_long(s_ssl)); + + if (sw_num > 0) + { + /* Write to client. */ + + if (sw_num > (long)sizeof sbuf) + i = sizeof sbuf; + else + i = (int)sw_num; + r = BIO_write(s_ssl_bio, sbuf, i); + if (r == -1) + { + if (!BIO_should_retry(s_ssl_bio)) + { + fprintf(stderr,"ERROR in SERVER\n"); + goto err; + } + /* Ignore "BIO_should_retry". */ + } + else if (r == 0) + { + fprintf(stderr,"SSL SERVER STARTUP FAILED\n"); + goto err; + } + else + { + if (debug) + printf("server wrote %d\n", r); + sw_num -= r; + } + } + + if (sr_num > 0) + { + /* Read from client. */ + + r = BIO_read(s_ssl_bio, sbuf, sizeof(sbuf)); + if (r < 0) + { + if (!BIO_should_retry(s_ssl_bio)) + { + fprintf(stderr,"ERROR in SERVER\n"); + goto err; + } + /* blah, blah */ + } + else if (r == 0) + { + fprintf(stderr,"SSL SERVER STARTUP FAILED\n"); + goto err; + } + else + { + if (debug) + printf("server read %d\n", r); + sr_num -= r; + } + } + } + + { + /* "I/O" BETWEEN CLIENT AND SERVER. */ + +#define RELAYBUFSIZ 200 + static char buf[RELAYBUFSIZ]; + + /* RELAYBUF is arbitrary. When writing data over some real + * network, use a buffer of the same size as in the BIO_pipe + * and make that size large (for reading from the network + * small buffers usually won't hurt). + * Here sizes differ for testing. */ + + size_t r1, r2; + size_t num; + int r; + static int prev_progress = 1; + int progress = 0; + + /* client to server */ + do + { + r1 = BIO_ctrl_pending(client_io); + r2 = BIO_ctrl_get_write_guarantee(server_io); + + num = r1; + if (r2 < num) + num = r2; + if (num) + { + if (sizeof buf < num) + num = sizeof buf; + if (INT_MAX < num) /* yeah, right */ + num = INT_MAX; + + r = BIO_read(client_io, buf, (int)num); + if (r != (int)num) /* can't happen */ + { + fprintf(stderr, "ERROR: BIO_read could not read " + "BIO_ctrl_pending() bytes"); + goto err; + } + r = BIO_write(server_io, buf, (int)num); + if (r != (int)num) /* can't happen */ + { + fprintf(stderr, "ERROR: BIO_write could not write " + "BIO_ctrl_get_write_guarantee() bytes"); + goto err; + } + progress = 1; + + if (debug) + printf("C->S relaying: %d bytes\n", (int)num); + } + } + while (r1 && r2); + + /* server to client */ + do + { + r1 = BIO_ctrl_pending(server_io); + r2 = BIO_ctrl_get_write_guarantee(client_io); + + num = r1; + if (r2 < num) + num = r2; + if (num) + { + if (sizeof buf < num) + num = sizeof buf; + if (INT_MAX < num) + num = INT_MAX; + + r = BIO_read(server_io, buf, (int)num); + if (r != (int)num) /* can't happen */ + { + fprintf(stderr, "ERROR: BIO_read could not read " + "BIO_ctrl_pending() bytes"); + goto err; + } + r = BIO_write(client_io, buf, (int)num); + if (r != (int)num) /* can't happen */ + { + fprintf(stderr, "ERROR: BIO_write could not write " + "BIO_ctrl_get_write_guarantee() bytes"); + goto err; + } + progress = 1; + + if (debug) + printf("S->C relaying: %d bytes\n", (int)num); + } + } + while (r1 && r2); + + if (!progress && !prev_progress) + if (cw_num > 0 || cr_num > 0 || sw_num > 0 || sr_num > 0) + /* can't happen */ + { + fprintf(stderr, "ERROR: got stuck\n"); + goto err; + } + prev_progress = progress; + } + } + while (cw_num > 0 || cr_num > 0 || sw_num > 0 || sr_num > 0); + + ciph = SSL_get_current_cipher(c_ssl); + if (verbose) + fprintf(stdout,"DONE via BIO pair, protocol %s, cipher %s, %s\n", + SSL_get_version(c_ssl), + SSL_CIPHER_get_version(ciph), + SSL_CIPHER_get_name(ciph)); + ret = 0; + + err: + ERR_print_errors(bio_err); + + if (server) + BIO_free(server); + if (server_io) + BIO_free(server_io); + if (client) + BIO_free(client); + if (client_io) + BIO_free(client_io); + if (s_ssl_bio) + BIO_free(s_ssl_bio); + if (c_ssl_b |