Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/bearssl/samples/server_basic.c
39507 views
1
/*
2
* Copyright (c) 2016 Thomas Pornin <[email protected]>
3
*
4
* Permission is hereby granted, free of charge, to any person obtaining
5
* a copy of this software and associated documentation files (the
6
* "Software"), to deal in the Software without restriction, including
7
* without limitation the rights to use, copy, modify, merge, publish,
8
* distribute, sublicense, and/or sell copies of the Software, and to
9
* permit persons to whom the Software is furnished to do so, subject to
10
* the following conditions:
11
*
12
* The above copyright notice and this permission notice shall be
13
* included in all copies or substantial portions of the Software.
14
*
15
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
* SOFTWARE.
23
*/
24
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
#include <stdint.h>
29
#include <errno.h>
30
#include <signal.h>
31
32
#include <sys/types.h>
33
#include <sys/socket.h>
34
#include <netdb.h>
35
#include <netinet/in.h>
36
#include <arpa/inet.h>
37
#include <unistd.h>
38
39
#include "bearssl.h"
40
41
/*
42
* This sample code can use three possible certificate chains:
43
* -- A full-RSA chain (server key is RSA, certificates are signed with RSA)
44
* -- A full-EC chain (server key is EC, certificates are signed with ECDSA)
45
* -- A mixed chain (server key is EC, certificates are signed with RSA)
46
*
47
* The macros below define which chain is selected. This impacts the list
48
* of supported cipher suites.
49
*
50
* Other macros, which can be defined (with a non-zero value):
51
*
52
* SERVER_PROFILE_MIN_FS
53
* Select a "minimal" profile with forward security (ECDHE cipher
54
* suite).
55
*
56
* SERVER_PROFILE_MIN_NOFS
57
* Select a "minimal" profile without forward security (RSA or ECDH
58
* cipher suite, but not ECDHE).
59
*
60
* SERVER_CHACHA20
61
* If SERVER_PROFILE_MIN_FS is selected, then this macro selects
62
* a cipher suite with ChaCha20+Poly1305; otherwise, AES/GCM is
63
* used. This macro has no effect otherwise, since there is no
64
* non-forward secure cipher suite that uses ChaCha20+Poly1305.
65
*/
66
67
#if !(SERVER_RSA || SERVER_EC || SERVER_MIXED)
68
#define SERVER_RSA 1
69
#define SERVER_EC 0
70
#define SERVER_MIXED 0
71
#endif
72
73
#if SERVER_RSA
74
#include "chain-rsa.h"
75
#include "key-rsa.h"
76
#define SKEY RSA
77
#elif SERVER_EC
78
#include "chain-ec.h"
79
#include "key-ec.h"
80
#define SKEY EC
81
#elif SERVER_MIXED
82
#include "chain-ec+rsa.h"
83
#include "key-ec.h"
84
#define SKEY EC
85
#else
86
#error Must use one of RSA, EC or MIXED chains.
87
#endif
88
89
/*
90
* Create a server socket bound to the specified host and port. If 'host'
91
* is NULL, this will bind "generically" (all addresses).
92
*
93
* Returned value is the server socket descriptor, or -1 on error.
94
*/
95
static int
96
host_bind(const char *host, const char *port)
97
{
98
struct addrinfo hints, *si, *p;
99
int fd;
100
int err;
101
102
memset(&hints, 0, sizeof hints);
103
hints.ai_family = PF_UNSPEC;
104
hints.ai_socktype = SOCK_STREAM;
105
err = getaddrinfo(host, port, &hints, &si);
106
if (err != 0) {
107
fprintf(stderr, "ERROR: getaddrinfo(): %s\n",
108
gai_strerror(err));
109
return -1;
110
}
111
fd = -1;
112
for (p = si; p != NULL; p = p->ai_next) {
113
struct sockaddr *sa;
114
struct sockaddr_in sa4;
115
struct sockaddr_in6 sa6;
116
size_t sa_len;
117
void *addr;
118
char tmp[INET6_ADDRSTRLEN + 50];
119
int opt;
120
121
sa = (struct sockaddr *)p->ai_addr;
122
if (sa->sa_family == AF_INET) {
123
sa4 = *(struct sockaddr_in *)sa;
124
sa = (struct sockaddr *)&sa4;
125
sa_len = sizeof sa4;
126
addr = &sa4.sin_addr;
127
if (host == NULL) {
128
sa4.sin_addr.s_addr = INADDR_ANY;
129
}
130
} else if (sa->sa_family == AF_INET6) {
131
sa6 = *(struct sockaddr_in6 *)sa;
132
sa = (struct sockaddr *)&sa6;
133
sa_len = sizeof sa6;
134
addr = &sa6.sin6_addr;
135
if (host == NULL) {
136
sa6.sin6_addr = in6addr_any;
137
}
138
} else {
139
addr = NULL;
140
sa_len = p->ai_addrlen;
141
}
142
if (addr != NULL) {
143
inet_ntop(p->ai_family, addr, tmp, sizeof tmp);
144
} else {
145
sprintf(tmp, "<unknown family: %d>",
146
(int)sa->sa_family);
147
}
148
fprintf(stderr, "binding to: %s\n", tmp);
149
fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
150
if (fd < 0) {
151
perror("socket()");
152
continue;
153
}
154
opt = 1;
155
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);
156
opt = 0;
157
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof opt);
158
if (bind(fd, sa, sa_len) < 0) {
159
perror("bind()");
160
close(fd);
161
continue;
162
}
163
break;
164
}
165
if (p == NULL) {
166
freeaddrinfo(si);
167
fprintf(stderr, "ERROR: failed to bind\n");
168
return -1;
169
}
170
freeaddrinfo(si);
171
if (listen(fd, 5) < 0) {
172
perror("listen()");
173
close(fd);
174
return -1;
175
}
176
fprintf(stderr, "bound.\n");
177
return fd;
178
}
179
180
/*
181
* Accept a single client on the provided server socket. This is blocking.
182
* On error, this returns -1.
183
*/
184
static int
185
accept_client(int server_fd)
186
{
187
int fd;
188
struct sockaddr sa;
189
socklen_t sa_len;
190
char tmp[INET6_ADDRSTRLEN + 50];
191
const char *name;
192
193
sa_len = sizeof sa;
194
fd = accept(server_fd, &sa, &sa_len);
195
if (fd < 0) {
196
perror("accept()");
197
return -1;
198
}
199
name = NULL;
200
switch (sa.sa_family) {
201
case AF_INET:
202
name = inet_ntop(AF_INET,
203
&((struct sockaddr_in *)&sa)->sin_addr,
204
tmp, sizeof tmp);
205
break;
206
case AF_INET6:
207
name = inet_ntop(AF_INET6,
208
&((struct sockaddr_in6 *)&sa)->sin6_addr,
209
tmp, sizeof tmp);
210
break;
211
}
212
if (name == NULL) {
213
sprintf(tmp, "<unknown: %lu>", (unsigned long)sa.sa_family);
214
name = tmp;
215
}
216
fprintf(stderr, "accepting connection from: %s\n", name);
217
return fd;
218
}
219
220
/*
221
* Low-level data read callback for the simplified SSL I/O API.
222
*/
223
static int
224
sock_read(void *ctx, unsigned char *buf, size_t len)
225
{
226
for (;;) {
227
ssize_t rlen;
228
229
rlen = read(*(int *)ctx, buf, len);
230
if (rlen <= 0) {
231
if (rlen < 0 && errno == EINTR) {
232
continue;
233
}
234
return -1;
235
}
236
return (int)rlen;
237
}
238
}
239
240
/*
241
* Low-level data write callback for the simplified SSL I/O API.
242
*/
243
static int
244
sock_write(void *ctx, const unsigned char *buf, size_t len)
245
{
246
for (;;) {
247
ssize_t wlen;
248
249
wlen = write(*(int *)ctx, buf, len);
250
if (wlen <= 0) {
251
if (wlen < 0 && errno == EINTR) {
252
continue;
253
}
254
return -1;
255
}
256
return (int)wlen;
257
}
258
}
259
260
/*
261
* Sample HTTP response to send.
262
*/
263
static const char *HTTP_RES =
264
"HTTP/1.0 200 OK\r\n"
265
"Content-Length: 46\r\n"
266
"Connection: close\r\n"
267
"Content-Type: text/html; charset=iso-8859-1\r\n"
268
"\r\n"
269
"<html>\r\n"
270
"<body>\r\n"
271
"<p>Test!</p>\r\n"
272
"</body>\r\n"
273
"</html>\r\n";
274
275
/*
276
* Main program: this is a simple program that expects 1 argument: a
277
* port number. This will start a simple network server on that port,
278
* that expects incoming SSL clients. It handles only one client at a
279
* time (handling several would require threads, sub-processes, or
280
* multiplexing with select()/poll(), all of which being possible).
281
*
282
* For each client, the server will wait for two successive newline
283
* characters (ignoring CR characters, so CR+LF is accepted), then
284
* produce a sample static HTTP response. This is very crude, but
285
* sufficient for explanatory purposes.
286
*/
287
int
288
main(int argc, char *argv[])
289
{
290
const char *port;
291
int fd;
292
293
if (argc != 2) {
294
return EXIT_FAILURE;
295
}
296
port = argv[1];
297
298
/*
299
* Ignore SIGPIPE to avoid crashing in case of abrupt socket close.
300
*/
301
signal(SIGPIPE, SIG_IGN);
302
303
/*
304
* Open the server socket.
305
*/
306
fd = host_bind(NULL, port);
307
if (fd < 0) {
308
return EXIT_FAILURE;
309
}
310
311
/*
312
* Process each client, one at a time.
313
*/
314
for (;;) {
315
int cfd;
316
br_ssl_server_context sc;
317
unsigned char iobuf[BR_SSL_BUFSIZE_BIDI];
318
br_sslio_context ioc;
319
int lcwn, err;
320
321
cfd = accept_client(fd);
322
if (cfd < 0) {
323
return EXIT_FAILURE;
324
}
325
326
/*
327
* Initialise the context with the cipher suites and
328
* algorithms. This depends on the server key type
329
* (and, for EC keys, the signature algorithm used by
330
* the CA to sign the server's certificate).
331
*
332
* Depending on the defined macros, we may select one of
333
* the "minimal" profiles. Key exchange algorithm depends
334
* on the key type:
335
* RSA key: RSA or ECDHE_RSA
336
* EC key, cert signed with ECDSA: ECDH_ECDSA or ECDHE_ECDSA
337
* EC key, cert signed with RSA: ECDH_RSA or ECDHE_ECDSA
338
*/
339
#if SERVER_RSA
340
#if SERVER_PROFILE_MIN_FS
341
#if SERVER_CHACHA20
342
br_ssl_server_init_mine2c(&sc, CHAIN, CHAIN_LEN, &SKEY);
343
#else
344
br_ssl_server_init_mine2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
345
#endif
346
#elif SERVER_PROFILE_MIN_NOFS
347
br_ssl_server_init_minr2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
348
#else
349
br_ssl_server_init_full_rsa(&sc, CHAIN, CHAIN_LEN, &SKEY);
350
#endif
351
#elif SERVER_EC
352
#if SERVER_PROFILE_MIN_FS
353
#if SERVER_CHACHA20
354
br_ssl_server_init_minf2c(&sc, CHAIN, CHAIN_LEN, &SKEY);
355
#else
356
br_ssl_server_init_minf2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
357
#endif
358
#elif SERVER_PROFILE_MIN_NOFS
359
br_ssl_server_init_minv2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
360
#else
361
br_ssl_server_init_full_ec(&sc, CHAIN, CHAIN_LEN,
362
BR_KEYTYPE_EC, &SKEY);
363
#endif
364
#else /* SERVER_MIXED */
365
#if SERVER_PROFILE_MIN_FS
366
#if SERVER_CHACHA20
367
br_ssl_server_init_minf2c(&sc, CHAIN, CHAIN_LEN, &SKEY);
368
#else
369
br_ssl_server_init_minf2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
370
#endif
371
#elif SERVER_PROFILE_MIN_NOFS
372
br_ssl_server_init_minu2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
373
#else
374
br_ssl_server_init_full_ec(&sc, CHAIN, CHAIN_LEN,
375
BR_KEYTYPE_RSA, &SKEY);
376
#endif
377
#endif
378
/*
379
* Set the I/O buffer to the provided array. We
380
* allocated a buffer large enough for full-duplex
381
* behaviour with all allowed sizes of SSL records,
382
* hence we set the last argument to 1 (which means
383
* "split the buffer into separate input and output
384
* areas").
385
*/
386
br_ssl_engine_set_buffer(&sc.eng, iobuf, sizeof iobuf, 1);
387
388
/*
389
* Reset the server context, for a new handshake.
390
*/
391
br_ssl_server_reset(&sc);
392
393
/*
394
* Initialise the simplified I/O wrapper context.
395
*/
396
br_sslio_init(&ioc, &sc.eng, sock_read, &cfd, sock_write, &cfd);
397
398
/*
399
* Read bytes until two successive LF (or CR+LF) are received.
400
*/
401
lcwn = 0;
402
for (;;) {
403
unsigned char x;
404
405
if (br_sslio_read(&ioc, &x, 1) < 0) {
406
goto client_drop;
407
}
408
if (x == 0x0D) {
409
continue;
410
}
411
if (x == 0x0A) {
412
if (lcwn) {
413
break;
414
}
415
lcwn = 1;
416
} else {
417
lcwn = 0;
418
}
419
}
420
421
/*
422
* Write a response and close the connection.
423
*/
424
br_sslio_write_all(&ioc, HTTP_RES, strlen(HTTP_RES));
425
br_sslio_close(&ioc);
426
427
client_drop:
428
err = br_ssl_engine_last_error(&sc.eng);
429
if (err == 0) {
430
fprintf(stderr, "SSL closed (correctly).\n");
431
} else {
432
fprintf(stderr, "SSL error: %d\n", err);
433
}
434
close(cfd);
435
}
436
}
437
438