Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/bearssl/tools/sslio.c
39535 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
31
#ifdef _WIN32
32
#include <winsock2.h>
33
#include <ws2tcpip.h>
34
#else
35
#include <sys/types.h>
36
#include <sys/socket.h>
37
#include <netdb.h>
38
#include <netinet/in.h>
39
#include <arpa/inet.h>
40
#include <unistd.h>
41
#include <fcntl.h>
42
#include <poll.h>
43
44
#define SOCKET int
45
#define INVALID_SOCKET (-1)
46
#endif
47
48
#include "brssl.h"
49
50
static void
51
dump_blob(const char *name, const void *data, size_t len)
52
{
53
const unsigned char *buf;
54
size_t u;
55
56
buf = data;
57
fprintf(stderr, "%s (len = %lu)", name, (unsigned long)len);
58
for (u = 0; u < len; u ++) {
59
if ((u & 15) == 0) {
60
fprintf(stderr, "\n%08lX ", (unsigned long)u);
61
} else if ((u & 7) == 0) {
62
fprintf(stderr, " ");
63
}
64
fprintf(stderr, " %02x", buf[u]);
65
}
66
fprintf(stderr, "\n");
67
}
68
69
/*
70
* Inspect the provided data in case it is a "command" to trigger a
71
* special behaviour. If the command is recognised, then it is executed
72
* and this function returns 1. Otherwise, this function returns 0.
73
*/
74
static int
75
run_command(br_ssl_engine_context *cc, unsigned char *buf, size_t len)
76
{
77
/*
78
* A single static slot for saving session parameters.
79
*/
80
static br_ssl_session_parameters slot;
81
static int slot_used = 0;
82
83
size_t u;
84
85
if (len < 2 || len > 3) {
86
return 0;
87
}
88
if (len == 3 && (buf[1] != '\r' || buf[2] != '\n')) {
89
return 0;
90
}
91
if (len == 2 && buf[1] != '\n') {
92
return 0;
93
}
94
switch (buf[0]) {
95
case 'Q':
96
fprintf(stderr, "closing...\n");
97
br_ssl_engine_close(cc);
98
return 1;
99
case 'R':
100
if (br_ssl_engine_renegotiate(cc)) {
101
fprintf(stderr, "renegotiating...\n");
102
} else {
103
fprintf(stderr, "not renegotiating.\n");
104
}
105
return 1;
106
case 'F':
107
/*
108
* Session forget is nominally client-only. But the
109
* session parameters are in the engine structure, which
110
* is the first field of the client context, so the cast
111
* still works properly. On the server, this forgetting
112
* has no effect.
113
*/
114
fprintf(stderr, "forgetting session...\n");
115
br_ssl_client_forget_session((br_ssl_client_context *)cc);
116
return 1;
117
case 'S':
118
fprintf(stderr, "saving session parameters...\n");
119
br_ssl_engine_get_session_parameters(cc, &slot);
120
fprintf(stderr, " id = ");
121
for (u = 0; u < slot.session_id_len; u ++) {
122
fprintf(stderr, "%02X", slot.session_id[u]);
123
}
124
fprintf(stderr, "\n");
125
slot_used = 1;
126
return 1;
127
case 'P':
128
if (slot_used) {
129
fprintf(stderr, "restoring session parameters...\n");
130
fprintf(stderr, " id = ");
131
for (u = 0; u < slot.session_id_len; u ++) {
132
fprintf(stderr, "%02X", slot.session_id[u]);
133
}
134
fprintf(stderr, "\n");
135
br_ssl_engine_set_session_parameters(cc, &slot);
136
return 1;
137
}
138
return 0;
139
default:
140
return 0;
141
}
142
}
143
144
#ifdef _WIN32
145
146
typedef struct {
147
unsigned char buf[1024];
148
size_t ptr, len;
149
} in_buffer;
150
151
static int
152
in_return_bytes(in_buffer *bb, unsigned char *buf, size_t len)
153
{
154
if (bb->ptr < bb->len) {
155
size_t clen;
156
157
if (buf == NULL) {
158
return 1;
159
}
160
clen = bb->len - bb->ptr;
161
if (clen > len) {
162
clen = len;
163
}
164
memcpy(buf, bb->buf + bb->ptr, clen);
165
bb->ptr += clen;
166
if (bb->ptr == bb->len) {
167
bb->ptr = bb->len = 0;
168
}
169
return (int)clen;
170
}
171
return 0;
172
}
173
174
/*
175
* A buffered version of in_read(), using a buffer to return only
176
* full lines when feasible.
177
*/
178
static int
179
in_read_buffered(HANDLE h_in, in_buffer *bb, unsigned char *buf, size_t len)
180
{
181
int n;
182
183
if (len == 0) {
184
return 0;
185
}
186
n = in_return_bytes(bb, buf, len);
187
if (n != 0) {
188
return n;
189
}
190
for (;;) {
191
INPUT_RECORD inrec;
192
DWORD v;
193
194
if (!PeekConsoleInput(h_in, &inrec, 1, &v)) {
195
fprintf(stderr, "ERROR: PeekConsoleInput()"
196
" failed with 0x%08lX\n",
197
(unsigned long)GetLastError());
198
return -1;
199
}
200
if (v == 0) {
201
return 0;
202
}
203
if (!ReadConsoleInput(h_in, &inrec, 1, &v)) {
204
fprintf(stderr, "ERROR: ReadConsoleInput()"
205
" failed with 0x%08lX\n",
206
(unsigned long)GetLastError());
207
return -1;
208
}
209
if (v == 0) {
210
return 0;
211
}
212
if (inrec.EventType == KEY_EVENT
213
&& inrec.Event.KeyEvent.bKeyDown)
214
{
215
int c;
216
217
c = inrec.Event.KeyEvent.uChar.AsciiChar;
218
if (c == '\n' || c == '\r' || c == '\t'
219
|| (c >= 32 && c != 127))
220
{
221
if (c == '\r') {
222
c = '\n';
223
}
224
bb->buf[bb->ptr ++] = (unsigned char)c;
225
printf("%c", c);
226
fflush(stdout);
227
bb->len = bb->ptr;
228
if (bb->len == sizeof bb->buf || c == '\n') {
229
bb->ptr = 0;
230
return in_return_bytes(bb, buf, len);
231
}
232
}
233
}
234
}
235
}
236
237
static int
238
in_avail_buffered(HANDLE h_in, in_buffer *bb)
239
{
240
return in_read_buffered(h_in, bb, NULL, 1);
241
}
242
243
#endif
244
245
/* see brssl.h */
246
int
247
run_ssl_engine(br_ssl_engine_context *cc, unsigned long fd, unsigned flags)
248
{
249
int hsdetails;
250
int retcode;
251
int verbose;
252
int trace;
253
#ifdef _WIN32
254
WSAEVENT fd_event;
255
int can_send, can_recv;
256
HANDLE h_in, h_out;
257
in_buffer bb;
258
#endif
259
260
hsdetails = 0;
261
retcode = 0;
262
verbose = (flags & RUN_ENGINE_VERBOSE) != 0;
263
trace = (flags & RUN_ENGINE_TRACE) != 0;
264
265
/*
266
* Print algorithm details.
267
*/
268
if (verbose) {
269
const char *rngname;
270
271
fprintf(stderr, "Algorithms:\n");
272
br_prng_seeder_system(&rngname);
273
fprintf(stderr, " RNG: %s\n", rngname);
274
if (cc->iaes_cbcenc != 0) {
275
fprintf(stderr, " AES/CBC (enc): %s\n",
276
get_algo_name(cc->iaes_cbcenc, 0));
277
}
278
if (cc->iaes_cbcdec != 0) {
279
fprintf(stderr, " AES/CBC (dec): %s\n",
280
get_algo_name(cc->iaes_cbcdec, 0));
281
}
282
if (cc->iaes_ctr != 0) {
283
fprintf(stderr, " AES/CTR: %s\n",
284
get_algo_name(cc->iaes_ctr, 0));
285
}
286
if (cc->iaes_ctrcbc != 0) {
287
fprintf(stderr, " AES/CCM: %s\n",
288
get_algo_name(cc->iaes_ctrcbc, 0));
289
}
290
if (cc->ides_cbcenc != 0) {
291
fprintf(stderr, " DES/CBC (enc): %s\n",
292
get_algo_name(cc->ides_cbcenc, 0));
293
}
294
if (cc->ides_cbcdec != 0) {
295
fprintf(stderr, " DES/CBC (dec): %s\n",
296
get_algo_name(cc->ides_cbcdec, 0));
297
}
298
if (cc->ighash != 0) {
299
fprintf(stderr, " GHASH (GCM): %s\n",
300
get_algo_name(cc->ighash, 0));
301
}
302
if (cc->ichacha != 0) {
303
fprintf(stderr, " ChaCha20: %s\n",
304
get_algo_name(cc->ichacha, 0));
305
}
306
if (cc->ipoly != 0) {
307
fprintf(stderr, " Poly1305: %s\n",
308
get_algo_name(cc->ipoly, 0));
309
}
310
if (cc->iec != 0) {
311
fprintf(stderr, " EC: %s\n",
312
get_algo_name(cc->iec, 0));
313
}
314
if (cc->iecdsa != 0) {
315
fprintf(stderr, " ECDSA: %s\n",
316
get_algo_name(cc->iecdsa, 0));
317
}
318
if (cc->irsavrfy != 0) {
319
fprintf(stderr, " RSA (vrfy): %s\n",
320
get_algo_name(cc->irsavrfy, 0));
321
}
322
}
323
324
#ifdef _WIN32
325
fd_event = WSA_INVALID_EVENT;
326
can_send = 0;
327
can_recv = 0;
328
bb.ptr = bb.len = 0;
329
#endif
330
331
/*
332
* On Unix systems, we need to follow three descriptors:
333
* standard input (0), standard output (1), and the socket
334
* itself (for both read and write). This is done with a poll()
335
* call.
336
*
337
* On Windows systems, we use WSAEventSelect() to associate
338
* an event handle with the network activity, and we use
339
* WaitForMultipleObjectsEx() on that handle and the standard
340
* input handle, when appropriate. Standard output is assumed
341
* to be always writeable, and standard input to be the console;
342
* this does not work well (or at all) with redirections (to
343
* pipes or files) but it should be enough for a debug tool
344
* (TODO: make something that handles redirections as well).
345
*/
346
347
#ifdef _WIN32
348
fd_event = WSACreateEvent();
349
if (fd_event == WSA_INVALID_EVENT) {
350
fprintf(stderr, "ERROR: WSACreateEvent() failed with %d\n",
351
WSAGetLastError());
352
retcode = -2;
353
goto engine_exit;
354
}
355
WSAEventSelect(fd, fd_event, FD_READ | FD_WRITE | FD_CLOSE);
356
h_in = GetStdHandle(STD_INPUT_HANDLE);
357
h_out = GetStdHandle(STD_OUTPUT_HANDLE);
358
SetConsoleMode(h_in, ENABLE_ECHO_INPUT
359
| ENABLE_LINE_INPUT
360
| ENABLE_PROCESSED_INPUT
361
| ENABLE_PROCESSED_OUTPUT
362
| ENABLE_WRAP_AT_EOL_OUTPUT);
363
#else
364
/*
365
* Make sure that stdin and stdout are non-blocking.
366
*/
367
fcntl(0, F_SETFL, O_NONBLOCK);
368
fcntl(1, F_SETFL, O_NONBLOCK);
369
#endif
370
371
/*
372
* Perform the loop.
373
*/
374
for (;;) {
375
unsigned st;
376
int sendrec, recvrec, sendapp, recvapp;
377
#ifdef _WIN32
378
HANDLE pfd[2];
379
DWORD wt;
380
#else
381
struct pollfd pfd[3];
382
int n;
383
#endif
384
size_t u, k_fd, k_in, k_out;
385
int sendrec_ok, recvrec_ok, sendapp_ok, recvapp_ok;
386
387
/*
388
* Get current engine state.
389
*/
390
st = br_ssl_engine_current_state(cc);
391
if (st == BR_SSL_CLOSED) {
392
int err;
393
394
err = br_ssl_engine_last_error(cc);
395
if (err == BR_ERR_OK) {
396
if (verbose) {
397
fprintf(stderr,
398
"SSL closed normally\n");
399
}
400
retcode = 0;
401
goto engine_exit;
402
} else {
403
fprintf(stderr, "ERROR: SSL error %d", err);
404
retcode = err;
405
if (err >= BR_ERR_SEND_FATAL_ALERT) {
406
err -= BR_ERR_SEND_FATAL_ALERT;
407
fprintf(stderr,
408
" (sent alert %d)\n", err);
409
} else if (err >= BR_ERR_RECV_FATAL_ALERT) {
410
err -= BR_ERR_RECV_FATAL_ALERT;
411
fprintf(stderr,
412
" (received alert %d)\n", err);
413
} else {
414
const char *ename;
415
416
ename = find_error_name(err, NULL);
417
if (ename == NULL) {
418
ename = "unknown";
419
}
420
fprintf(stderr, " (%s)\n", ename);
421
}
422
goto engine_exit;
423
}
424
}
425
426
/*
427
* Compute descriptors that must be polled, depending
428
* on engine state.
429
*/
430
sendrec = ((st & BR_SSL_SENDREC) != 0);
431
recvrec = ((st & BR_SSL_RECVREC) != 0);
432
sendapp = ((st & BR_SSL_SENDAPP) != 0);
433
recvapp = ((st & BR_SSL_RECVAPP) != 0);
434
if (verbose && sendapp && !hsdetails) {
435
char csn[80];
436
const char *pname;
437
438
fprintf(stderr, "Handshake completed\n");
439
fprintf(stderr, " version: ");
440
switch (cc->session.version) {
441
case BR_SSL30:
442
fprintf(stderr, "SSL 3.0");
443
break;
444
case BR_TLS10:
445
fprintf(stderr, "TLS 1.0");
446
break;
447
case BR_TLS11:
448
fprintf(stderr, "TLS 1.1");
449
break;
450
case BR_TLS12:
451
fprintf(stderr, "TLS 1.2");
452
break;
453
default:
454
fprintf(stderr, "unknown (0x%04X)",
455
(unsigned)cc->session.version);
456
break;
457
}
458
fprintf(stderr, "\n");
459
get_suite_name_ext(
460
cc->session.cipher_suite, csn, sizeof csn);
461
fprintf(stderr, " cipher suite: %s\n", csn);
462
if (uses_ecdhe(cc->session.cipher_suite)) {
463
get_curve_name_ext(
464
br_ssl_engine_get_ecdhe_curve(cc),
465
csn, sizeof csn);
466
fprintf(stderr,
467
" ECDHE curve: %s\n", csn);
468
}
469
fprintf(stderr, " secure renegotiation: %s\n",
470
cc->reneg == 1 ? "no" : "yes");
471
pname = br_ssl_engine_get_selected_protocol(cc);
472
if (pname != NULL) {
473
fprintf(stderr,
474
" protocol name (ALPN): %s\n",
475
pname);
476
}
477
hsdetails = 1;
478
}
479
480
k_fd = (size_t)-1;
481
k_in = (size_t)-1;
482
k_out = (size_t)-1;
483
484
u = 0;
485
#ifdef _WIN32
486
/*
487
* If we recorded that we can send or receive data, and we
488
* want to do exactly that, then we don't wait; we just do
489
* it.
490
*/
491
recvapp_ok = 0;
492
sendrec_ok = 0;
493
recvrec_ok = 0;
494
sendapp_ok = 0;
495
496
if (sendrec && can_send) {
497
sendrec_ok = 1;
498
} else if (recvrec && can_recv) {
499
recvrec_ok = 1;
500
} else if (recvapp) {
501
recvapp_ok = 1;
502
} else if (sendapp && in_avail_buffered(h_in, &bb)) {
503
sendapp_ok = 1;
504
} else {
505
/*
506
* If we cannot do I/O right away, then we must
507
* wait for some event, and try again.
508
*/
509
pfd[u] = (HANDLE)fd_event;
510
k_fd = u;
511
u ++;
512
if (sendapp) {
513
pfd[u] = h_in;
514
k_in = u;
515
u ++;
516
}
517
wt = WaitForMultipleObjectsEx(u, pfd,
518
FALSE, INFINITE, FALSE);
519
if (wt == WAIT_FAILED) {
520
fprintf(stderr, "ERROR:"
521
" WaitForMultipleObjectsEx()"
522
" failed with 0x%08lX",
523
(unsigned long)GetLastError());
524
retcode = -2;
525
goto engine_exit;
526
}
527
if (wt == k_fd) {
528
WSANETWORKEVENTS e;
529
530
if (WSAEnumNetworkEvents(fd, fd_event, &e)) {
531
fprintf(stderr, "ERROR:"
532
" WSAEnumNetworkEvents()"
533
" failed with %d\n",
534
WSAGetLastError());
535
retcode = -2;
536
goto engine_exit;
537
}
538
if (e.lNetworkEvents & (FD_WRITE | FD_CLOSE)) {
539
can_send = 1;
540
}
541
if (e.lNetworkEvents & (FD_READ | FD_CLOSE)) {
542
can_recv = 1;
543
}
544
}
545
continue;
546
}
547
#else
548
if (sendrec || recvrec) {
549
pfd[u].fd = fd;
550
pfd[u].revents = 0;
551
pfd[u].events = 0;
552
if (sendrec) {
553
pfd[u].events |= POLLOUT;
554
}
555
if (recvrec) {
556
pfd[u].events |= POLLIN;
557
}
558
k_fd = u;
559
u ++;
560
}
561
if (sendapp) {
562
pfd[u].fd = 0;
563
pfd[u].revents = 0;
564
pfd[u].events = POLLIN;
565
k_in = u;
566
u ++;
567
}
568
if (recvapp) {
569
pfd[u].fd = 1;
570
pfd[u].revents = 0;
571
pfd[u].events = POLLOUT;
572
k_out = u;
573
u ++;
574
}
575
n = poll(pfd, u, -1);
576
if (n < 0) {
577
if (errno == EINTR) {
578
continue;
579
}
580
perror("ERROR: poll()");
581
retcode = -2;
582
goto engine_exit;
583
}
584
if (n == 0) {
585
continue;
586
}
587
588
/*
589
* We transform closures/errors into read+write accesses
590
* so as to force the read() or write() call that will
591
* detect the situation.
592
*/
593
while (u -- > 0) {
594
if (pfd[u].revents & (POLLERR | POLLHUP)) {
595
pfd[u].revents |= POLLIN | POLLOUT;
596
}
597
}
598
599
recvapp_ok = recvapp && (pfd[k_out].revents & POLLOUT) != 0;
600
sendrec_ok = sendrec && (pfd[k_fd].revents & POLLOUT) != 0;
601
recvrec_ok = recvrec && (pfd[k_fd].revents & POLLIN) != 0;
602
sendapp_ok = sendapp && (pfd[k_in].revents & POLLIN) != 0;
603
#endif
604
605
/*
606
* We give preference to outgoing data, on stdout and on
607
* the socket.
608
*/
609
if (recvapp_ok) {
610
unsigned char *buf;
611
size_t len;
612
#ifdef _WIN32
613
DWORD wlen;
614
#else
615
ssize_t wlen;
616
#endif
617
618
buf = br_ssl_engine_recvapp_buf(cc, &len);
619
#ifdef _WIN32
620
if (!WriteFile(h_out, buf, len, &wlen, NULL)) {
621
if (verbose) {
622
fprintf(stderr, "stdout closed...\n");
623
}
624
retcode = -2;
625
goto engine_exit;
626
}
627
#else
628
wlen = write(1, buf, len);
629
if (wlen <= 0) {
630
if (verbose) {
631
fprintf(stderr, "stdout closed...\n");
632
}
633
retcode = -2;
634
goto engine_exit;
635
}
636
#endif
637
br_ssl_engine_recvapp_ack(cc, wlen);
638
continue;
639
}
640
if (sendrec_ok) {
641
unsigned char *buf;
642
size_t len;
643
int wlen;
644
645
buf = br_ssl_engine_sendrec_buf(cc, &len);
646
wlen = send(fd, buf, len, 0);
647
if (wlen <= 0) {
648
#ifdef _WIN32
649
int err;
650
651
err = WSAGetLastError();
652
if (err == EWOULDBLOCK
653
|| err == WSAEWOULDBLOCK)
654
{
655
can_send = 0;
656
continue;
657
}
658
#else
659
if (errno == EINTR || errno == EWOULDBLOCK) {
660
continue;
661
}
662
#endif
663
if (verbose) {
664
fprintf(stderr, "socket closed...\n");
665
}
666
retcode = -1;
667
goto engine_exit;
668
}
669
if (trace) {
670
dump_blob("Outgoing bytes", buf, wlen);
671
}
672
br_ssl_engine_sendrec_ack(cc, wlen);
673
continue;
674
}
675
if (recvrec_ok) {
676
unsigned char *buf;
677
size_t len;
678
int rlen;
679
680
buf = br_ssl_engine_recvrec_buf(cc, &len);
681
rlen = recv(fd, buf, len, 0);
682
if (rlen == 0) {
683
if (verbose) {
684
fprintf(stderr, "socket closed...\n");
685
}
686
retcode = -1;
687
goto engine_exit;
688
}
689
if (rlen < 0) {
690
#ifdef _WIN32
691
int err;
692
693
err = WSAGetLastError();
694
if (err == EWOULDBLOCK
695
|| err == WSAEWOULDBLOCK)
696
{
697
can_recv = 0;
698
continue;
699
}
700
#else
701
if (errno == EINTR || errno == EWOULDBLOCK) {
702
continue;
703
}
704
#endif
705
if (verbose) {
706
fprintf(stderr, "socket broke...\n");
707
}
708
retcode = -1;
709
goto engine_exit;
710
}
711
if (trace) {
712
dump_blob("Incoming bytes", buf, rlen);
713
}
714
br_ssl_engine_recvrec_ack(cc, rlen);
715
continue;
716
}
717
if (sendapp_ok) {
718
unsigned char *buf;
719
size_t len;
720
#ifdef _WIN32
721
int rlen;
722
#else
723
ssize_t rlen;
724
#endif
725
726
buf = br_ssl_engine_sendapp_buf(cc, &len);
727
#ifdef _WIN32
728
rlen = in_read_buffered(h_in, &bb, buf, len);
729
#else
730
rlen = read(0, buf, len);
731
#endif
732
if (rlen <= 0) {
733
if (verbose) {
734
fprintf(stderr, "stdin closed...\n");
735
}
736
br_ssl_engine_close(cc);
737
} else if (!run_command(cc, buf, rlen)) {
738
br_ssl_engine_sendapp_ack(cc, rlen);
739
}
740
br_ssl_engine_flush(cc, 0);
741
continue;
742
}
743
744
/* We should never reach that point. */
745
fprintf(stderr, "ERROR: poll() misbehaves\n");
746
retcode = -2;
747
goto engine_exit;
748
}
749
750
/*
751
* Release allocated structures.
752
*/
753
engine_exit:
754
#ifdef _WIN32
755
if (fd_event != WSA_INVALID_EVENT) {
756
WSACloseEvent(fd_event);
757
}
758
#endif
759
return retcode;
760
}
761
762