Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/bearssl/samples/client_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
* Connect to the specified host and port. The connected socket is
43
* returned, or -1 on error.
44
*/
45
static int
46
host_connect(const char *host, const char *port)
47
{
48
struct addrinfo hints, *si, *p;
49
int fd;
50
int err;
51
52
memset(&hints, 0, sizeof hints);
53
hints.ai_family = PF_UNSPEC;
54
hints.ai_socktype = SOCK_STREAM;
55
err = getaddrinfo(host, port, &hints, &si);
56
if (err != 0) {
57
fprintf(stderr, "ERROR: getaddrinfo(): %s\n",
58
gai_strerror(err));
59
return -1;
60
}
61
fd = -1;
62
for (p = si; p != NULL; p = p->ai_next) {
63
struct sockaddr *sa;
64
void *addr;
65
char tmp[INET6_ADDRSTRLEN + 50];
66
67
sa = (struct sockaddr *)p->ai_addr;
68
if (sa->sa_family == AF_INET) {
69
addr = &((struct sockaddr_in *)sa)->sin_addr;
70
} else if (sa->sa_family == AF_INET6) {
71
addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
72
} else {
73
addr = NULL;
74
}
75
if (addr != NULL) {
76
inet_ntop(p->ai_family, addr, tmp, sizeof tmp);
77
} else {
78
sprintf(tmp, "<unknown family: %d>",
79
(int)sa->sa_family);
80
}
81
fprintf(stderr, "connecting to: %s\n", tmp);
82
fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
83
if (fd < 0) {
84
perror("socket()");
85
continue;
86
}
87
if (connect(fd, p->ai_addr, p->ai_addrlen) < 0) {
88
perror("connect()");
89
close(fd);
90
continue;
91
}
92
break;
93
}
94
if (p == NULL) {
95
freeaddrinfo(si);
96
fprintf(stderr, "ERROR: failed to connect\n");
97
return -1;
98
}
99
freeaddrinfo(si);
100
fprintf(stderr, "connected.\n");
101
return fd;
102
}
103
104
/*
105
* Low-level data read callback for the simplified SSL I/O API.
106
*/
107
static int
108
sock_read(void *ctx, unsigned char *buf, size_t len)
109
{
110
for (;;) {
111
ssize_t rlen;
112
113
rlen = read(*(int *)ctx, buf, len);
114
if (rlen <= 0) {
115
if (rlen < 0 && errno == EINTR) {
116
continue;
117
}
118
return -1;
119
}
120
return (int)rlen;
121
}
122
}
123
124
/*
125
* Low-level data write callback for the simplified SSL I/O API.
126
*/
127
static int
128
sock_write(void *ctx, const unsigned char *buf, size_t len)
129
{
130
for (;;) {
131
ssize_t wlen;
132
133
wlen = write(*(int *)ctx, buf, len);
134
if (wlen <= 0) {
135
if (wlen < 0 && errno == EINTR) {
136
continue;
137
}
138
return -1;
139
}
140
return (int)wlen;
141
}
142
}
143
144
/*
145
* The hardcoded trust anchors. These are the two DN + public key that
146
* correspond to the self-signed certificates cert-root-rsa.pem and
147
* cert-root-ec.pem.
148
*
149
* C code for hardcoded trust anchors can be generated with the "brssl"
150
* command-line tool (with the "ta" command).
151
*/
152
153
static const unsigned char TA0_DN[] = {
154
0x30, 0x1C, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
155
0x02, 0x43, 0x41, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x03,
156
0x13, 0x04, 0x52, 0x6F, 0x6F, 0x74
157
};
158
159
static const unsigned char TA0_RSA_N[] = {
160
0xB6, 0xD9, 0x34, 0xD4, 0x50, 0xFD, 0xB3, 0xAF, 0x7A, 0x73, 0xF1, 0xCE,
161
0x38, 0xBF, 0x5D, 0x6F, 0x45, 0xE1, 0xFD, 0x4E, 0xB1, 0x98, 0xC6, 0x60,
162
0x83, 0x26, 0xD2, 0x17, 0xD1, 0xC5, 0xB7, 0x9A, 0xA3, 0xC1, 0xDE, 0x63,
163
0x39, 0x97, 0x9C, 0xF0, 0x5E, 0x5C, 0xC8, 0x1C, 0x17, 0xB9, 0x88, 0x19,
164
0x6D, 0xF0, 0xB6, 0x2E, 0x30, 0x50, 0xA1, 0x54, 0x6E, 0x93, 0xC0, 0xDB,
165
0xCF, 0x30, 0xCB, 0x9F, 0x1E, 0x27, 0x79, 0xF1, 0xC3, 0x99, 0x52, 0x35,
166
0xAA, 0x3D, 0xB6, 0xDF, 0xB0, 0xAD, 0x7C, 0xCB, 0x49, 0xCD, 0xC0, 0xED,
167
0xE7, 0x66, 0x10, 0x2A, 0xE9, 0xCE, 0x28, 0x1F, 0x21, 0x50, 0xFA, 0x77,
168
0x4C, 0x2D, 0xDA, 0xEF, 0x3C, 0x58, 0xEB, 0x4E, 0xBF, 0xCE, 0xE9, 0xFB,
169
0x1A, 0xDA, 0xA3, 0x83, 0xA3, 0xCD, 0xA3, 0xCA, 0x93, 0x80, 0xDC, 0xDA,
170
0xF3, 0x17, 0xCC, 0x7A, 0xAB, 0x33, 0x80, 0x9C, 0xB2, 0xD4, 0x7F, 0x46,
171
0x3F, 0xC5, 0x3C, 0xDC, 0x61, 0x94, 0xB7, 0x27, 0x29, 0x6E, 0x2A, 0xBC,
172
0x5B, 0x09, 0x36, 0xD4, 0xC6, 0x3B, 0x0D, 0xEB, 0xBE, 0xCE, 0xDB, 0x1D,
173
0x1C, 0xBC, 0x10, 0x6A, 0x71, 0x71, 0xB3, 0xF2, 0xCA, 0x28, 0x9A, 0x77,
174
0xF2, 0x8A, 0xEC, 0x42, 0xEF, 0xB1, 0x4A, 0x8E, 0xE2, 0xF2, 0x1A, 0x32,
175
0x2A, 0xCD, 0xC0, 0xA6, 0x46, 0x2C, 0x9A, 0xC2, 0x85, 0x37, 0x91, 0x7F,
176
0x46, 0xA1, 0x93, 0x81, 0xA1, 0x74, 0x66, 0xDF, 0xBA, 0xB3, 0x39, 0x20,
177
0x91, 0x93, 0xFA, 0x1D, 0xA1, 0xA8, 0x85, 0xE7, 0xE4, 0xF9, 0x07, 0xF6,
178
0x10, 0xF6, 0xA8, 0x27, 0x01, 0xB6, 0x7F, 0x12, 0xC3, 0x40, 0xC3, 0xC9,
179
0xE2, 0xB0, 0xAB, 0x49, 0x18, 0x3A, 0x64, 0xB6, 0x59, 0xB7, 0x95, 0xB5,
180
0x96, 0x36, 0xDF, 0x22, 0x69, 0xAA, 0x72, 0x6A, 0x54, 0x4E, 0x27, 0x29,
181
0xA3, 0x0E, 0x97, 0x15
182
};
183
184
static const unsigned char TA0_RSA_E[] = {
185
0x01, 0x00, 0x01
186
};
187
188
static const unsigned char TA1_DN[] = {
189
0x30, 0x1C, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
190
0x02, 0x43, 0x41, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x03,
191
0x13, 0x04, 0x52, 0x6F, 0x6F, 0x74
192
};
193
194
static const unsigned char TA1_EC_Q[] = {
195
0x04, 0x71, 0x74, 0xBA, 0xAB, 0xB9, 0x30, 0x2E, 0x81, 0xD5, 0xE5, 0x57,
196
0xF9, 0xF3, 0x20, 0x68, 0x0C, 0x9C, 0xF9, 0x64, 0xDB, 0xB4, 0x20, 0x0D,
197
0x6D, 0xEA, 0x40, 0xD0, 0x4A, 0x6E, 0x42, 0xFD, 0xB6, 0x9A, 0x68, 0x25,
198
0x44, 0xF6, 0xDF, 0x7B, 0xC4, 0xFC, 0xDE, 0xDD, 0x7B, 0xBB, 0xC5, 0xDB,
199
0x7C, 0x76, 0x3F, 0x41, 0x66, 0x40, 0x6E, 0xDB, 0xA7, 0x87, 0xC2, 0xE5,
200
0xD8, 0xC5, 0xF3, 0x7F, 0x8D
201
};
202
203
static const br_x509_trust_anchor TAs[2] = {
204
{
205
{ (unsigned char *)TA0_DN, sizeof TA0_DN },
206
BR_X509_TA_CA,
207
{
208
BR_KEYTYPE_RSA,
209
{ .rsa = {
210
(unsigned char *)TA0_RSA_N, sizeof TA0_RSA_N,
211
(unsigned char *)TA0_RSA_E, sizeof TA0_RSA_E,
212
} }
213
}
214
},
215
{
216
{ (unsigned char *)TA1_DN, sizeof TA1_DN },
217
BR_X509_TA_CA,
218
{
219
BR_KEYTYPE_EC,
220
{ .ec = {
221
BR_EC_secp256r1,
222
(unsigned char *)TA1_EC_Q, sizeof TA1_EC_Q,
223
} }
224
}
225
}
226
};
227
228
#define TAs_NUM 2
229
230
/*
231
* Main program: this is a simple program that expects 2 or 3 arguments.
232
* The first two arguments are a hostname and a port; the program will
233
* open a SSL connection with that server and port. It will then send
234
* a simple HTTP GET request, using the third argument as target path
235
* ("/" is used as path if no third argument was provided). The HTTP
236
* response, complete with header and contents, is received and written
237
* on stdout.
238
*/
239
int
240
main(int argc, char *argv[])
241
{
242
const char *host, *port, *path;
243
int fd;
244
br_ssl_client_context sc;
245
br_x509_minimal_context xc;
246
unsigned char iobuf[BR_SSL_BUFSIZE_BIDI];
247
br_sslio_context ioc;
248
249
/*
250
* Parse command-line argument: host, port, and path. The path
251
* is optional; if absent, "/" is used.
252
*/
253
if (argc < 3 || argc > 4) {
254
return EXIT_FAILURE;
255
}
256
host = argv[1];
257
port = argv[2];
258
if (argc == 4) {
259
path = argv[3];
260
} else {
261
path = "/";
262
}
263
264
/*
265
* Ignore SIGPIPE to avoid crashing in case of abrupt socket close.
266
*/
267
signal(SIGPIPE, SIG_IGN);
268
269
/*
270
* Open the socket to the target server.
271
*/
272
fd = host_connect(host, port);
273
if (fd < 0) {
274
return EXIT_FAILURE;
275
}
276
277
/*
278
* Initialise the client context:
279
* -- Use the "full" profile (all supported algorithms).
280
* -- The provided X.509 validation engine is initialised, with
281
* the hardcoded trust anchor.
282
*/
283
br_ssl_client_init_full(&sc, &xc, TAs, TAs_NUM);
284
285
/*
286
* Set the I/O buffer to the provided array. We allocated a
287
* buffer large enough for full-duplex behaviour with all
288
* allowed sizes of SSL records, hence we set the last argument
289
* to 1 (which means "split the buffer into separate input and
290
* output areas").
291
*/
292
br_ssl_engine_set_buffer(&sc.eng, iobuf, sizeof iobuf, 1);
293
294
/*
295
* Reset the client context, for a new handshake. We provide the
296
* target host name: it will be used for the SNI extension. The
297
* last parameter is 0: we are not trying to resume a session.
298
*/
299
br_ssl_client_reset(&sc, host, 0);
300
301
/*
302
* Initialise the simplified I/O wrapper context, to use our
303
* SSL client context, and the two callbacks for socket I/O.
304
*/
305
br_sslio_init(&ioc, &sc.eng, sock_read, &fd, sock_write, &fd);
306
307
/*
308
* Note that while the context has, at that point, already
309
* assembled the ClientHello to send, nothing happened on the
310
* network yet. Real I/O will occur only with the next call.
311
*
312
* We write our simple HTTP request. We could test each call
313
* for an error (-1), but this is not strictly necessary, since
314
* the error state "sticks": if the context fails for any reason
315
* (e.g. bad server certificate), then it will remain in failed
316
* state and all subsequent calls will return -1 as well.
317
*/
318
br_sslio_write_all(&ioc, "GET ", 4);
319
br_sslio_write_all(&ioc, path, strlen(path));
320
br_sslio_write_all(&ioc, " HTTP/1.0\r\nHost: ", 17);
321
br_sslio_write_all(&ioc, host, strlen(host));
322
br_sslio_write_all(&ioc, "\r\n\r\n", 4);
323
324
/*
325
* SSL is a buffered protocol: we make sure that all our request
326
* bytes are sent onto the wire.
327
*/
328
br_sslio_flush(&ioc);
329
330
/*
331
* Read the server's response. We use here a small 512-byte buffer,
332
* but most of the buffering occurs in the client context: the
333
* server will send full records (up to 16384 bytes worth of data
334
* each), and the client context buffers one full record at a time.
335
*/
336
for (;;) {
337
int rlen;
338
unsigned char tmp[512];
339
340
rlen = br_sslio_read(&ioc, tmp, sizeof tmp);
341
if (rlen < 0) {
342
break;
343
}
344
fwrite(tmp, 1, rlen, stdout);
345
}
346
347
/*
348
* Close the socket.
349
*/
350
close(fd);
351
352
/*
353
* Check whether we closed properly or not. If the engine is
354
* closed, then its error status allows to distinguish between
355
* a normal closure and a SSL error.
356
*
357
* If the engine is NOT closed, then this means that the
358
* underlying network socket was closed or failed in some way.
359
* Note that many Web servers out there do not properly close
360
* their SSL connections (they don't send a close_notify alert),
361
* which will be reported here as "socket closed without proper
362
* SSL termination".
363
*/
364
if (br_ssl_engine_current_state(&sc.eng) == BR_SSL_CLOSED) {
365
int err;
366
367
err = br_ssl_engine_last_error(&sc.eng);
368
if (err == 0) {
369
fprintf(stderr, "closed.\n");
370
return EXIT_SUCCESS;
371
} else {
372
fprintf(stderr, "SSL error %d\n", err);
373
return EXIT_FAILURE;
374
}
375
} else {
376
fprintf(stderr,
377
"socket closed without proper SSL termination\n");
378
return EXIT_FAILURE;
379
}
380
}
381
382