Skip to content

Commit 7a9b09f

Browse files
DDvOt8m
authored andcommitted
BIO_s_connect(): Enable BIO_gets()
Fixes openssl#16028 Reviewed-by: Tomas Mraz <[email protected]> (Merged from openssl#16030)
1 parent bef9b48 commit 7a9b09f

File tree

3 files changed

+144
-27
lines changed

3 files changed

+144
-27
lines changed

crypto/bio/bss_conn.c

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ typedef struct bio_connect_st {
4242
static int conn_write(BIO *h, const char *buf, int num);
4343
static int conn_read(BIO *h, char *buf, int size);
4444
static int conn_puts(BIO *h, const char *str);
45+
static int conn_gets(BIO *h, char *buf, int size);
4546
static long conn_ctrl(BIO *h, int cmd, long arg1, void *arg2);
4647
static int conn_new(BIO *h);
4748
static int conn_free(BIO *data);
@@ -68,7 +69,7 @@ static const BIO_METHOD methods_connectp = {
6869
bread_conv,
6970
conn_read,
7071
conn_puts,
71-
NULL, /* conn_gets, */
72+
conn_gets,
7273
conn_ctrl,
7374
conn_new,
7475
conn_free,
@@ -595,6 +596,56 @@ static int conn_puts(BIO *bp, const char *str)
595596
return ret;
596597
}
597598

599+
int conn_gets(BIO *bio, char *buf, int size)
600+
{
601+
BIO_CONNECT *data;
602+
char *ptr = buf;
603+
int ret = 0;
604+
605+
if (buf == NULL) {
606+
ERR_raise(ERR_LIB_BIO, ERR_R_PASSED_NULL_PARAMETER);
607+
return -1;
608+
}
609+
if (size <= 0) {
610+
ERR_raise(ERR_LIB_BIO, BIO_R_INVALID_ARGUMENT);
611+
return -1;
612+
}
613+
*buf = '\0';
614+
615+
if (bio == NULL || bio->ptr == NULL) {
616+
ERR_raise(ERR_LIB_BIO, ERR_R_PASSED_NULL_PARAMETER);
617+
return -1;
618+
}
619+
data = (BIO_CONNECT *)bio->ptr;
620+
if (data->state != BIO_CONN_S_OK) {
621+
ret = conn_state(bio, data);
622+
if (ret <= 0)
623+
return ret;
624+
}
625+
626+
clear_socket_error();
627+
while (size-- > 1) {
628+
# ifndef OPENSSL_NO_KTLS
629+
if (BIO_get_ktls_recv(bio))
630+
ret = ktls_read_record(bio->num, ptr, 1);
631+
else
632+
# endif
633+
ret = readsocket(bio->num, ptr, 1);
634+
BIO_clear_retry_flags(bio);
635+
if (ret <= 0) {
636+
if (BIO_sock_should_retry(ret))
637+
BIO_set_retry_read(bio);
638+
else if (ret == 0)
639+
bio->flags |= BIO_FLAGS_IN_EOF;
640+
break;
641+
}
642+
if (*ptr++ == '\n')
643+
break;
644+
}
645+
*ptr = '\0';
646+
return ret > 0 || (bio->flags & BIO_FLAGS_IN_EOF) != 0 ? ptr - buf : ret;
647+
}
648+
598649
BIO *BIO_new_connect(const char *str)
599650
{
600651
BIO *ret;

doc/man3/BIO_s_connect.pod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ on the underlying connection. If no connection is established
4444
and the port and hostname (see below) is set up properly then
4545
a connection is established first.
4646

47-
Connect BIOs support BIO_puts() but not BIO_gets().
47+
Connect BIOs support BIO_puts() and BIO_gets().
4848

4949
If the close flag is set on a connect BIO then any active
5050
connection is shutdown and the socket closed when the BIO
@@ -199,6 +199,8 @@ BIO_set_conn_int_port(), BIO_get_conn_int_port(), BIO_set_conn_ip(), and BIO_get
199199
were removed in OpenSSL 1.1.0.
200200
Use BIO_set_conn_address() and BIO_get_conn_address() instead.
201201

202+
Connect BIOs support BIO_gets() since OpenSSL 3.1.
203+
202204
=head1 COPYRIGHT
203205

204206
Copyright 2000-2021 The OpenSSL Project Authors. All Rights Reserved.

test/http_test.c

Lines changed: 89 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,21 @@ static X509 *x509 = NULL;
2121

2222
typedef struct {
2323
BIO *out;
24+
const char *content_type;
25+
const char *txt;
2426
char version;
2527
int keep_alive;
2628
} server_args;
2729

2830
/*-
2931
* Pretty trivial HTTP mock server:
30-
* For POST, copy request headers+body from mem BIO 'in' as response to 'out'.
31-
* For GET, redirect to RPATH, else respond with 'rsp' of ASN1 type 'it'.
32-
* Respond with HTTP version 1.'version' and 'keep_alive' (unless implicit).
32+
* For POST, copy request headers+body from mem BIO |in| as response to |out|.
33+
* For GET, redirect to RPATH unless already there, else use |content_type| and
34+
* respond with |txt| if not NULL, else with |rsp| of ASN1 type |it|.
35+
* Response hdr has HTTP version 1.|version| and |keep_alive| (unless implicit).
3336
*/
3437
static int mock_http_server(BIO *in, BIO *out, char version, int keep_alive,
38+
const char *content_type, const char *txt,
3539
ASN1_VALUE *rsp, const ASN1_ITEM *it)
3640
{
3741
const char *req, *path;
@@ -40,7 +44,7 @@ static int mock_http_server(BIO *in, BIO *out, char version, int keep_alive,
4044
int is_get = count >= 4 && strncmp(hdr, "GET ", 4) == 0;
4145
int len;
4246

43-
/* first line should contain "<GET or POST> <path> HTTP/1.x" */
47+
/* first line should contain "(GET|POST) <path> HTTP/1.x" */
4448
if (is_get)
4549
hdr += 4;
4650
else if (TEST_true(count >= 5 && strncmp(hdr, "POST ", 5) == 0))
@@ -79,11 +83,15 @@ static int mock_http_server(BIO *in, BIO *out, char version, int keep_alive,
7983
version == '0' ? "keep-alive" : "close") <= 0)
8084
return 0;
8185
if (is_get) { /* construct new header and body */
82-
if ((len = ASN1_item_i2d(rsp, NULL, it)) <= 0)
86+
if (txt != NULL)
87+
len = strlen(txt);
88+
else if ((len = ASN1_item_i2d(rsp, NULL, it)) <= 0)
8389
return 0;
84-
if (BIO_printf(out, "Content-Type: application/x-x509-ca-cert\r\n"
85-
"Content-Length: %d\r\n\r\n", len) <= 0)
90+
if (BIO_printf(out, "Content-Type: %s\r\n"
91+
"Content-Length: %d\r\n\r\n", content_type, len) <= 0)
8692
return 0;
93+
if (txt != NULL)
94+
return BIO_puts(out, txt);
8795
return ASN1_item_i2d_bio(it, out, rsp);
8896
} else {
8997
len = strlen("Connection: ");
@@ -106,47 +114,91 @@ static long http_bio_cb_ex(BIO *bio, int oper, const char *argp, size_t len,
106114

107115
if (oper == (BIO_CB_CTRL | BIO_CB_RETURN) && cmd == BIO_CTRL_FLUSH)
108116
ret = mock_http_server(bio, args->out, args->version, args->keep_alive,
117+
args->content_type, args->txt,
109118
(ASN1_VALUE *)x509, x509_it);
110119
return ret;
111120
}
112121

113-
static int test_http_x509(int do_get)
122+
#define text1 "test\n"
123+
#define text2 "more\n"
124+
#define REAL_SERVER_URL "http://httpbin.org/"
125+
#define DOCTYPE_HTML "<!DOCTYPE html>\n"
126+
127+
static int test_http_method(int do_get, int do_txt)
114128
{
115-
X509 *rcert = NULL;
116129
BIO *wbio = BIO_new(BIO_s_mem());
117130
BIO *rbio = BIO_new(BIO_s_mem());
118-
server_args mock_args = { NULL, '0', 0 };
119-
BIO *rsp, *req = ASN1_item_i2d_mem_bio(x509_it, (ASN1_VALUE *)x509);
131+
server_args mock_args = { NULL, NULL, NULL, '0', 0 };
132+
BIO *req, *rsp;
120133
STACK_OF(CONF_VALUE) *headers = NULL;
121-
const char content_type[] = "application/x-x509-ca-cert";
134+
const char *content_type;
122135
int res = 0;
123-
136+
int real_server = do_txt && 0; /* remove "&& 0" for using real server */
137+
138+
if (do_txt) {
139+
content_type = "text/plain";
140+
req = BIO_new(BIO_s_mem());
141+
if (req == NULL
142+
|| BIO_puts(req, text1) != sizeof(text1) - 1
143+
|| BIO_puts(req, text2) != sizeof(text2) - 1) {
144+
BIO_free(req);
145+
req = NULL;
146+
}
147+
mock_args.txt = text1;
148+
} else {
149+
content_type = "application/x-x509-ca-cert";
150+
req = ASN1_item_i2d_mem_bio(x509_it, (ASN1_VALUE *)x509);
151+
mock_args.txt = NULL;
152+
}
124153
if (wbio == NULL || rbio == NULL || req == NULL)
125154
goto err;
155+
126156
mock_args.out = rbio;
157+
mock_args.content_type = content_type;
127158
BIO_set_callback_ex(wbio, http_bio_cb_ex);
128159
BIO_set_callback_arg(wbio, (char *)&mock_args);
129160

130161
rsp = do_get ?
131-
OSSL_HTTP_get("/will-be-redirected",
162+
OSSL_HTTP_get(real_server ? REAL_SERVER_URL :
163+
do_txt ? RPATH : "/will-be-redirected",
132164
NULL /* proxy */, NULL /* no_proxy */,
133-
wbio, rbio, NULL /* bio_update_fn */, NULL /* arg */,
134-
0 /* buf_size */, headers, content_type,
135-
1 /* expect_asn1 */,
165+
real_server ? NULL : wbio,
166+
real_server ? NULL : rbio,
167+
NULL /* bio_update_fn */, NULL /* arg */,
168+
0 /* buf_size */, headers,
169+
real_server ? "text/html; charset=utf-8": content_type,
170+
!do_txt /* expect_asn1 */,
136171
OSSL_HTTP_DEFAULT_MAX_RESP_LEN, 0 /* timeout */)
137172
: OSSL_HTTP_transfer(NULL, NULL /* host */, NULL /* port */, RPATH,
138173
0 /* use_ssl */,NULL /* proxy */, NULL /* no_pr */,
139174
wbio, rbio, NULL /* bio_fn */, NULL /* arg */,
140175
0 /* buf_size */, headers, content_type,
141-
req, content_type, 1 /* expect_asn1 */,
176+
req, content_type, !do_txt /* expect_asn1 */,
142177
OSSL_HTTP_DEFAULT_MAX_RESP_LEN, 0 /* timeout */,
143178
0 /* keep_alive */);
144-
rcert = d2i_X509_bio(rsp, NULL);
145-
BIO_free(rsp);
146-
res = TEST_ptr(rcert) && TEST_int_eq(X509_cmp(x509, rcert), 0);
179+
if (rsp != NULL) {
180+
if (do_get && real_server) {
181+
char rtext[sizeof(DOCTYPE_HTML)];
182+
183+
res = TEST_int_eq(BIO_gets(rsp, rtext, sizeof(rtext)),
184+
sizeof(DOCTYPE_HTML) - 1)
185+
&& TEST_str_eq(rtext, DOCTYPE_HTML);
186+
} else if (do_txt) {
187+
char rtext[sizeof(text1) + 1 /* more space than needed */];
188+
189+
res = TEST_int_eq(BIO_gets(rsp, rtext, sizeof(rtext)),
190+
sizeof(text1) - 1)
191+
&& TEST_str_eq(rtext, text1);
192+
} else {
193+
X509 *rcert = d2i_X509_bio(rsp, NULL);
194+
195+
res = TEST_ptr(rcert) && TEST_int_eq(X509_cmp(x509, rcert), 0);
196+
X509_free(rcert);
197+
}
198+
BIO_free(rsp);
199+
}
147200

148201
err:
149-
X509_free(rcert);
150202
BIO_free(req);
151203
BIO_free(wbio);
152204
BIO_free(rbio);
@@ -159,8 +211,8 @@ static int test_http_keep_alive(char version, int keep_alive, int kept_alive)
159211
BIO *wbio = BIO_new(BIO_s_mem());
160212
BIO *rbio = BIO_new(BIO_s_mem());
161213
BIO *rsp;
162-
server_args mock_args = { NULL, '0', 0 };
163214
const char *const content_type = "application/x-x509-ca-cert";
215+
server_args mock_args = { NULL, content_type, NULL, '0', 0 };
164216
OSSL_HTTP_REQ_CTX *rctx = NULL;
165217
int i, res = 0;
166218

@@ -306,14 +358,24 @@ static int test_http_url_invalid_path(void)
306358
return test_http_url_invalid("https://[FF01::101]pkix");
307359
}
308360

361+
static int test_http_get_txt(void)
362+
{
363+
return test_http_method(1 /* GET */, 1);
364+
}
365+
366+
static int test_http_post_txt(void)
367+
{
368+
return test_http_method(0 /* POST */, 1);
369+
}
370+
309371
static int test_http_get_x509(void)
310372
{
311-
return test_http_x509(1);
373+
return test_http_method(1 /* GET */, 0); /* includes redirection */
312374
}
313375

314376
static int test_http_post_x509(void)
315377
{
316-
return test_http_x509(0);
378+
return test_http_method(0 /* POST */, 0);
317379
}
318380

319381
static int test_http_keep_alive_0_no_no(void)
@@ -380,6 +442,8 @@ int setup_tests(void)
380442
ADD_TEST(test_http_url_invalid_prefix);
381443
ADD_TEST(test_http_url_invalid_port);
382444
ADD_TEST(test_http_url_invalid_path);
445+
ADD_TEST(test_http_get_txt);
446+
ADD_TEST(test_http_post_txt);
383447
ADD_TEST(test_http_get_x509);
384448
ADD_TEST(test_http_post_x509);
385449
ADD_TEST(test_http_keep_alive_0_no_no);

0 commit comments

Comments
 (0)