/
harnessTls.c
190 lines (156 loc) · 7.37 KB
/
harnessTls.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/***********************************************************************************************************************************
Tls Test Harness
***********************************************************************************************************************************/
#include <arpa/inet.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <openssl/conf.h>
#include <openssl/ssl.h>
#include "common/crypto/common.h"
#include "common/error.h"
#include "common/io/socket/session.h"
#include "common/io/tls/session.intern.h"
#include "common/type/buffer.h"
#include "common/wait.h"
#include "common/harnessTest.h"
#include "common/harnessTls.h"
/***********************************************************************************************************************************
Test defaults
***********************************************************************************************************************************/
#define TLS_TEST_HOST "tls.test.pgbackrest.org"
static int testServerSocket = 0;
static SSL_CTX *testServerContext = NULL;
static TlsSession *testServerSession = NULL;
/***********************************************************************************************************************************
Initialize TLS and listen on the specified port for TLS connections
***********************************************************************************************************************************/
void
harnessTlsServerInit(unsigned int port, const char *serverCert, const char *serverKey)
{
// Add test hosts
if (testContainer())
{
if (system("echo \"127.0.0.1 " TLS_TEST_HOST "\" | sudo tee -a /etc/hosts > /dev/null") != 0)
THROW(AssertError, "unable to add test host to /etc/hosts");
}
// Initialize ssl and create a context
cryptoInit();
const SSL_METHOD *method = SSLv23_method();
cryptoError(method == NULL, "unable to load TLS method");
testServerContext = SSL_CTX_new(method);
cryptoError(testServerContext == NULL, "unable to create TLS context");
// Configure the context by setting key and cert
cryptoError(
SSL_CTX_use_certificate_file(testServerContext, serverCert, SSL_FILETYPE_PEM) <= 0, "unable to load server certificate");
cryptoError(
SSL_CTX_use_PrivateKey_file(testServerContext, serverKey, SSL_FILETYPE_PEM) <= 0, "unable to load server private key");
// Create the socket
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons((uint16_t)port);
address.sin_addr.s_addr = htonl(INADDR_ANY);
if ((testServerSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
THROW_SYS_ERROR(AssertError, "unable to create socket");
// Set the address as reusable so we can bind again in the same process for testing
int reuseAddr = 1;
setsockopt(testServerSocket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr));
// Bind the address. It might take a bit to bind if another process was recently using it so retry a few times.
Wait *wait = waitNew(2000);
int result;
do
{
result = bind(testServerSocket, (struct sockaddr *)&address, sizeof(address));
}
while (result < 0 && waitMore(wait));
if (result < 0)
THROW_SYS_ERROR(AssertError, "unable to bind socket");
// Listen for client connections
if (listen(testServerSocket, 1) < 0)
THROW_SYS_ERROR(AssertError, "unable to listen on socket");
}
/**********************************************************************************************************************************/
void
harnessTlsServerInitDefault(void)
{
if (testContainer())
harnessTlsServerInit(harnessTlsTestPort(), TLS_CERT_TEST_CERT, TLS_CERT_TEST_KEY);
else
{
harnessTlsServerInit(
harnessTlsTestPort(),
strPtr(strNewFmt("%s/" TEST_CERTIFICATE_PREFIX ".crt", testRepoPath())),
strPtr(strNewFmt("%s/" TEST_CERTIFICATE_PREFIX ".key", testRepoPath())));
}
}
/***********************************************************************************************************************************
Expect an exact string from the client
***********************************************************************************************************************************/
void
harnessTlsServerExpect(const char *expected)
{
Buffer *buffer = bufNew(strlen(expected));
ioRead(tlsSessionIoRead(testServerSession), buffer);
// Treat and ? characters as wildcards so variable elements (e.g. auth hashes) can be ignored
String *actual = strNewBuf(buffer);
for (unsigned int actualIdx = 0; actualIdx < strSize(actual); actualIdx++)
{
if (expected[actualIdx] == '?')
((char *)strPtr(actual))[actualIdx] = '?';
}
// Error if actual does not match expected
if (!strEqZ(actual, expected))
THROW_FMT(AssertError, "server expected '%s' but got '%s'", expected, strPtr(actual));
}
/***********************************************************************************************************************************
Send a reply to the client
***********************************************************************************************************************************/
void
harnessTlsServerReply(const char *reply)
{
ioWrite(tlsSessionIoWrite(testServerSession), BUF((unsigned char *)reply, strlen(reply)));
ioWriteFlush(tlsSessionIoWrite(testServerSession));
}
/***********************************************************************************************************************************
Accept a TLS connection from the client
***********************************************************************************************************************************/
void
harnessTlsServerAccept(void)
{
struct sockaddr_in addr;
unsigned int len = sizeof(addr);
int testClientSocket = accept(testServerSocket, (struct sockaddr *)&addr, &len);
if (testClientSocket < 0)
THROW_SYS_ERROR(AssertError, "unable to accept socket");
SSL *testClientSSL = SSL_new(testServerContext);
testServerSession = tlsSessionNew(
testClientSSL, sckSessionNew(sckSessionTypeServer, testClientSocket, STRDEF("client"), 0, 5000), 5000);
}
/***********************************************************************************************************************************
Close the connection
***********************************************************************************************************************************/
void
harnessTlsServerClose(void)
{
tlsSessionClose(testServerSession, true);
tlsSessionFree(testServerSession);
testServerSession = NULL;
}
/**********************************************************************************************************************************/
void
harnessTlsServerAbort(void)
{
tlsSessionClose(testServerSession, false);
tlsSessionFree(testServerSession);
testServerSession = NULL;
}
/**********************************************************************************************************************************/
const String *harnessTlsTestHost(void)
{
return strNew(testContainer() ? TLS_TEST_HOST : "localhost");
}
/**********************************************************************************************************************************/
unsigned int harnessTlsTestPort(void)
{
return 44443 + testIdx();
}