1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2017 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #include "config.h"
19 
20 #include <platform/socket.h>
21 
22 #include <gtest/gtest.h>
23 #include "ssl_impl.h"
24 #include "utilities.h"
25 #include "testapp.h"
26 
27 static SSL_CTX *ssl_ctx = nullptr;
28 static SSL *ssl = nullptr;
29 static BIO *bio = nullptr;
30 static BIO *ssl_bio_r = nullptr;
31 static BIO *ssl_bio_w = nullptr;
32 
create_connect_ssl_socket(in_port_t port)33 SOCKET create_connect_ssl_socket(in_port_t port) {
34     SOCKET sfd;
35 
36     std::tie(sfd, ssl_ctx, bio) = cb::net::new_ssl_socket("", port, AF_INET);
37 
38     if (sfd == INVALID_SOCKET) {
39         ADD_FAILURE() << "Failed to connect over ssl to 127.0.0.1:" << port;
40         return INVALID_SOCKET;
41     }
42 
43     // SSL "trickery". To ensure we have full control over send/receive of data.
44     // Switch out the BIO_ssl_connect BIO for a plain memory BIO
45     // Now send/receive is done under our control. byte by byte, large chunks
46     // etc...
47     BIO_get_ssl(bio, &ssl);
48     EXPECT_EQ(nullptr, ssl_bio_r);
49     ssl_bio_r = BIO_new(BIO_s_mem());
50 
51     EXPECT_EQ(nullptr, ssl_bio_w);
52     ssl_bio_w = BIO_new(BIO_s_mem());
53 
54     // Note: previous BIOs attached to 'bio' freed as a result of this call.
55     SSL_set_bio(ssl, ssl_bio_r, ssl_bio_w);
56 
57     return sfd;
58 }
59 
destroy_ssl_socket()60 void destroy_ssl_socket() {
61     BIO_free_all(bio);
62     bio = nullptr;
63     ssl_bio_r = nullptr;
64     ssl_bio_w = nullptr;
65 
66     SSL_CTX_free(ssl_ctx);
67     ssl_ctx = nullptr;
68     if (sock_ssl != INVALID_SOCKET) {
69         cb::net::closesocket(sock_ssl);
70         sock_ssl = INVALID_SOCKET;
71     }
72 }
73 
phase_send_ssl(const void *buf, size_t len)74 ssize_t phase_send_ssl(const void *buf, size_t len) {
75     ssize_t rv = 0, send_rv = 0;
76 
77     long send_len = 0;
78     char* send_buf = NULL;
79     /* push the data through SSL into the BIO */
80     rv = (ssize_t)SSL_write(ssl, (const char*)buf, (int)len);
81     send_len = BIO_get_mem_data(ssl_bio_w, &send_buf);
82 
83     send_rv = socket_send(sock_ssl, send_buf, send_len);
84 
85     if (send_rv > 0) {
86         EXPECT_EQ(send_len, send_rv);
87         (void)BIO_reset(ssl_bio_w);
88     } else {
89         /* flag failure to user */
90         rv = send_rv;
91     }
92 
93     return rv;
94 }
95 
phase_recv_ssl(void* buf, size_t len)96 ssize_t phase_recv_ssl(void* buf, size_t len) {
97     ssize_t rv;
98 
99     /* can we read some data? */
100     while ((rv = SSL_peek(ssl, buf, (int)len)) == -1) {
101         /* nope, keep feeding SSL until we can */
102         rv = socket_recv(sock_ssl, reinterpret_cast<char*>(buf), len);
103 
104         if (rv > 0) {
105             /* write into the BIO what came off the network */
106             BIO_write(ssl_bio_r, buf, rv);
107         } else if (rv == 0) {
108             return rv; /* peer closed */
109         }
110     }
111 
112     /* now pull the data out and return */
113     return SSL_read(ssl, buf, (int)len);
114 }
115