summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBodo Möller <bodo@openssl.org>1999-06-12 01:03:40 +0000
committerBodo Möller <bodo@openssl.org>1999-06-12 01:03:40 +0000
commit95d29597b7cc2ec3653811b1a659094b4831f96b (patch)
tree0959b7b48af2cbf172b0f6a2ab35f86e503c3ef6
parent9bce3070acf81a2890ec7a6c94b97094691b5038 (diff)
BIO pairs.
-rw-r--r--CHANGES10
-rw-r--r--crypto/bio/bio.h36
-rw-r--r--crypto/bio/bio_err.c1
-rw-r--r--crypto/bio/bio_lib.c16
-rw-r--r--crypto/bio/bss_bio.c349
-rwxr-xr-xms/test.bat53
-rw-r--r--ssl/s3_clnt.c1
-rw-r--r--ssl/ssl_stat.c2
-rw-r--r--ssl/ssltest.c377
-rw-r--r--test/testssl37
10 files changed, 852 insertions, 30 deletions
diff --git a/CHANGES b/CHANGES
index 7e9e420e38..8e71a6ab5b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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 "
+