Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/logsrvd/sendlog.c
3901 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2019-2023, 2025-2026 Todd C. Miller <[email protected]>
5
*
6
* Permission to use, copy, modify, and distribute this software for any
7
* purpose with or without fee is hereby granted, provided that the above
8
* copyright notice and this permission notice appear in all copies.
9
*
10
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
*/
18
19
#include <config.h>
20
21
#include <sys/stat.h>
22
#include <sys/types.h>
23
#include <sys/socket.h>
24
#include <sys/time.h>
25
#include <netinet/in.h>
26
#include <netinet/tcp.h>
27
#include <arpa/inet.h>
28
29
#include <errno.h>
30
#include <fcntl.h>
31
#include <limits.h>
32
#include <netdb.h>
33
#ifdef HAVE_STDBOOL_H
34
# include <stdbool.h>
35
#else
36
# include <compat/stdbool.h>
37
#endif /* HAVE_STDBOOL_H */
38
#if defined(HAVE_STDINT_H)
39
# include <stdint.h>
40
#elif defined(HAVE_INTTYPES_H)
41
# include <inttypes.h>
42
#endif
43
#include <stdio.h>
44
#include <stdlib.h>
45
#include <string.h>
46
#include <time.h>
47
#include <unistd.h>
48
#ifndef HAVE_GETADDRINFO
49
# include <compat/getaddrinfo.h>
50
#endif
51
#ifdef HAVE_GETOPT_LONG
52
# include <getopt.h>
53
# else
54
# include <compat/getopt.h>
55
#endif /* HAVE_GETOPT_LONG */
56
57
#include <sudo_compat.h>
58
#include <sudo_conf.h>
59
#include <sudo_debug.h>
60
#include <sudo_event.h>
61
#include <sudo_eventlog.h>
62
#include <sudo_fatal.h>
63
#include <sudo_gettext.h>
64
#include <sudo_iolog.h>
65
#include <sudo_util.h>
66
67
#include "sendlog.h"
68
#include <hostcheck.h>
69
70
#if defined(HAVE_OPENSSL)
71
# define TLS_HANDSHAKE_TIMEO_SEC 10
72
#endif
73
74
TAILQ_HEAD(connection_list, client_closure);
75
static struct connection_list connections = TAILQ_HEAD_INITIALIZER(connections);
76
77
static struct peer_info server_info = { "localhost" };
78
static char *iolog_dir;
79
static bool testrun = false;
80
static int nr_of_conns = 1;
81
static int finished_transmissions = 0;
82
83
#if defined(HAVE_OPENSSL)
84
static SSL_CTX *ssl_ctx = NULL;
85
static const char *ca_bundle = NULL;
86
static const char *cert = NULL;
87
static const char *key = NULL;
88
static bool verify_server = true;
89
#endif
90
91
/* Server callback may redirect to client callback for TLS. */
92
static void client_msg_cb(int fd, int what, void *v);
93
static void server_msg_cb(int fd, int what, void *v);
94
95
static void
96
display_usage(FILE *fp)
97
{
98
#if defined(HAVE_OPENSSL)
99
fprintf(fp, "usage: %s [-AnV] [-b ca_bundle] [-c cert_file] [-h host] "
100
"[-i iolog-id] [-k key_file] [-p port] "
101
#else
102
fprintf(fp, "usage: %s [-AnV] [-h host] [-i iolog-id] [-p port] "
103
#endif
104
"[-r restart-point] [-R reject-reason] [-s stop-point] [-t number] /path/to/iolog\n",
105
getprogname());
106
}
107
108
sudo_noreturn static void
109
usage(void)
110
{
111
display_usage(stderr);
112
exit(EXIT_FAILURE);
113
}
114
115
sudo_noreturn static void
116
help(void)
117
{
118
printf("%s - %s\n\n", getprogname(),
119
_("send sudo I/O log to remote server"));
120
display_usage(stdout);
121
printf("\n%s\n", _("Options:"));
122
printf(" --help %s\n",
123
_("display help message and exit"));
124
printf(" -A, --accept %s\n",
125
_("only send an accept event (no I/O)"));
126
#if defined(HAVE_OPENSSL)
127
printf(" -b, --ca-bundle %s\n",
128
_("certificate bundle file to verify server's cert against"));
129
printf(" -c, --cert %s\n",
130
_("certificate file for TLS handshake"));
131
#endif
132
printf(" -h, --host %s\n",
133
_("host to send logs to"));
134
printf(" -i, --iolog_id %s\n",
135
_("remote ID of I/O log to be resumed"));
136
#if defined(HAVE_OPENSSL)
137
printf(" -k, --key %s\n",
138
_("private key file"));
139
printf(" -n, --no-verify %s\n",
140
_("do not verify server certificate"));
141
#endif
142
printf(" -p, --port %s\n",
143
_("port to use when connecting to host"));
144
printf(" -r, --restart %s\n",
145
_("restart previous I/O log transfer"));
146
printf(" -R, --reject %s\n",
147
_("reject the command with the given reason"));
148
printf(" -s, --stop-after %s\n",
149
_("stop transfer after reaching this time"));
150
printf(" -t, --test %s\n",
151
_("test audit server by sending selected I/O log n times in parallel"));
152
printf(" -V, --version %s\n",
153
_("display version information and exit"));
154
putchar('\n');
155
exit(EXIT_SUCCESS);
156
}
157
158
/*
159
* Connect to specified host:port
160
* If host has multiple addresses, the first one that connects is used.
161
* Returns open socket or -1 on error.
162
*/
163
static int
164
connect_server(struct peer_info *server, const char *port)
165
{
166
struct addrinfo hints, *res, *res0;
167
const char *addr, *cause = "getaddrinfo";
168
int error, sock, save_errno;
169
debug_decl(connect_server, SUDO_DEBUG_UTIL);
170
171
memset(&hints, 0, sizeof(hints));
172
hints.ai_family = AF_UNSPEC;
173
hints.ai_socktype = SOCK_STREAM;
174
error = getaddrinfo(server->name, port, &hints, &res0);
175
if (error != 0) {
176
sudo_warnx(U_("unable to look up %s:%s: %s"), server->name, port,
177
gai_strerror(error));
178
debug_return_int(-1);
179
}
180
181
sock = -1;
182
for (res = res0; res; res = res->ai_next) {
183
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
184
if (sock == -1) {
185
cause = "socket";
186
continue;
187
}
188
if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
189
cause = "connect";
190
save_errno = errno;
191
close(sock);
192
errno = save_errno;
193
sock = -1;
194
continue;
195
}
196
if (server->ipaddr[0] == '\0') {
197
switch (res->ai_family) {
198
case AF_INET:
199
addr = (char *)&((struct sockaddr_in *)res->ai_addr)->sin_addr;
200
break;
201
case AF_INET6:
202
addr = (char *)&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
203
break;
204
default:
205
cause = "ai_family";
206
save_errno = EAFNOSUPPORT;
207
close(sock);
208
errno = save_errno;
209
sock = -1;
210
continue;
211
}
212
if (inet_ntop(res->ai_family, addr, server->ipaddr,
213
sizeof(server->ipaddr)) == NULL) {
214
sudo_warnx("%s", U_("unable to get server IP addr"));
215
}
216
}
217
break; /* success */
218
}
219
freeaddrinfo(res0);
220
221
if (sock != -1) {
222
int flags = fcntl(sock, F_GETFL, 0);
223
if (flags == -1 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
224
cause = "fcntl(O_NONBLOCK)";
225
save_errno = errno;
226
close(sock);
227
errno = save_errno;
228
sock = -1;
229
}
230
}
231
if (sock == -1)
232
sudo_warn("%s", cause);
233
234
debug_return_int(sock);
235
}
236
237
/*
238
* Get a buffer from the free list if possible, else allocate a new one.
239
*/
240
static struct connection_buffer *
241
get_free_buf(size_t len, struct client_closure *closure)
242
{
243
struct connection_buffer *buf;
244
debug_decl(get_free_buf, SUDO_DEBUG_UTIL);
245
246
buf = TAILQ_FIRST(&closure->free_bufs);
247
if (buf != NULL) {
248
TAILQ_REMOVE(&closure->free_bufs, buf, entries);
249
} else {
250
if ((buf = calloc(1, sizeof(*buf))) == NULL) {
251
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
252
debug_return_ptr(NULL);
253
}
254
}
255
256
if (len > buf->size) {
257
free(buf->data);
258
buf->size = sudo_pow2_roundup(len);
259
if (buf->size < len || (buf->data = malloc(buf->size)) == NULL) {
260
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
261
free(buf);
262
buf = NULL;
263
}
264
}
265
266
debug_return_ptr(buf);
267
}
268
269
/*
270
* Read the next I/O buffer as described by closure->timing.
271
*/
272
static bool
273
read_io_buf(struct client_closure *closure)
274
{
275
struct timing_closure *timing = &closure->timing;
276
const char *errstr = NULL;
277
size_t nread;
278
debug_decl(read_io_buf, SUDO_DEBUG_UTIL);
279
280
if (!closure->iolog_files[timing->event].enabled) {
281
errno = ENOENT;
282
sudo_warn("%s/%s", iolog_dir, iolog_fd_to_name(timing->event));
283
debug_return_bool(false);
284
}
285
286
/* Expand buf as needed. */
287
if (timing->u.nbytes > closure->bufsize) {
288
const size_t new_size = sudo_pow2_roundup(timing->u.nbytes);
289
if (new_size < timing->u.nbytes) {
290
/* overflow */
291
errno = ENOMEM;
292
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
293
timing->u.nbytes = 0;
294
debug_return_bool(false);
295
}
296
free(closure->buf);
297
if ((closure->buf = malloc(new_size)) == NULL) {
298
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
299
closure->bufsize = 0;
300
timing->u.nbytes = 0;
301
debug_return_bool(false);
302
}
303
closure->bufsize = new_size;
304
}
305
306
nread = (size_t)iolog_read(&closure->iolog_files[timing->event],
307
closure->buf, timing->u.nbytes, &errstr);
308
if (nread != timing->u.nbytes) {
309
if (nread != (size_t)-1)
310
errstr = strerror(EINVAL);
311
sudo_warnx(U_("unable to read %s/%s: %s"), iolog_dir,
312
iolog_fd_to_name(timing->event), errstr);
313
debug_return_bool(false);
314
}
315
debug_return_bool(true);
316
}
317
318
/*
319
* Format a ClientMessage and store the wire format message in buf.
320
* Returns true on success, false on failure.
321
*/
322
static bool
323
fmt_client_message(struct client_closure *closure, ClientMessage *msg)
324
{
325
struct connection_buffer *buf = NULL;
326
uint32_t msg_len;
327
bool ret = false;
328
size_t len;
329
debug_decl(fmt_client_message, SUDO_DEBUG_UTIL);
330
331
len = client_message__get_packed_size(msg);
332
if (len > MESSAGE_SIZE_MAX) {
333
sudo_warnx(U_("client message too large: %zu"), len);
334
goto done;
335
}
336
/* Wire message size is used for length encoding, precedes message. */
337
msg_len = htonl((uint32_t)len);
338
len += sizeof(msg_len);
339
340
if (!TAILQ_EMPTY(&closure->write_bufs)) {
341
buf = TAILQ_FIRST(&closure->write_bufs);
342
if (len > buf->size - buf->len) {
343
/* Too small. */
344
buf = NULL;
345
}
346
}
347
if (buf == NULL) {
348
if ((buf = get_free_buf(len, closure)) == NULL) {
349
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
350
goto done;
351
}
352
TAILQ_INSERT_TAIL(&closure->write_bufs, buf, entries);
353
}
354
355
memcpy(buf->data + buf->len, &msg_len, sizeof(msg_len));
356
client_message__pack(msg, buf->data + buf->len + sizeof(msg_len));
357
buf->len += len;
358
359
ret = true;
360
361
done:
362
debug_return_bool(ret);
363
}
364
365
static bool
366
fmt_client_hello(struct client_closure *closure)
367
{
368
ClientMessage client_msg = CLIENT_MESSAGE__INIT;
369
ClientHello hello_msg = CLIENT_HELLO__INIT;
370
bool ret = false;
371
debug_decl(fmt_client_hello, SUDO_DEBUG_UTIL);
372
373
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: sending ClientHello", __func__);
374
hello_msg.client_id = (char *)"Sudo Sendlog " PACKAGE_VERSION;
375
376
/* Schedule ClientMessage */
377
client_msg.u.hello_msg = &hello_msg;
378
client_msg.type_case = CLIENT_MESSAGE__TYPE_HELLO_MSG;
379
ret = fmt_client_message(closure, &client_msg);
380
if (ret) {
381
if (sudo_ev_add(closure->evbase, closure->read_ev, NULL, false) == -1)
382
ret = false;
383
if (sudo_ev_add(closure->evbase, closure->write_ev, NULL, false) == -1)
384
ret = false;
385
}
386
387
debug_return_bool(ret);
388
}
389
390
#if defined(HAVE_OPENSSL)
391
/* Wrapper for fmt_client_hello() called via tls_connect_cb() */
392
static bool
393
tls_start_fn(struct tls_client_closure *tls_client)
394
{
395
return fmt_client_hello(tls_client->parent_closure);
396
}
397
398
/* Called on TLS connection error. */
399
static void
400
tls_connect_error_fn(struct tls_client_closure *tls_client)
401
{
402
sudo_ev_loopbreak(tls_client->evbase);
403
}
404
#endif /* HAVE_OPENSSL */
405
406
static void
407
free_info_messages(InfoMessage **info_msgs, size_t n_info_msgs)
408
{
409
debug_decl(free_info_messages, SUDO_DEBUG_UTIL);
410
411
if (info_msgs != NULL) {
412
while (n_info_msgs) {
413
if (info_msgs[--n_info_msgs]->value_case == INFO_MESSAGE__VALUE_STRLISTVAL) {
414
/* Only strlistval was dynamically allocated */
415
free(info_msgs[n_info_msgs]->u.strlistval->strings);
416
free(info_msgs[n_info_msgs]->u.strlistval);
417
}
418
free(info_msgs[n_info_msgs]);
419
}
420
free(info_msgs);
421
}
422
423
debug_return;
424
}
425
426
/*
427
* Convert a NULL-terminated string vector (argv, envp) to a
428
* StringList with an associated size.
429
* Performs a shallow copy of the strings (copies pointers).
430
*/
431
static InfoMessage__StringList *
432
vec_to_stringlist(char * const *vec)
433
{
434
InfoMessage__StringList *strlist;
435
size_t len;
436
debug_decl(vec_to_stringlist, SUDO_DEBUG_UTIL);
437
438
strlist = malloc(sizeof(*strlist));
439
if (strlist == NULL)
440
goto done;
441
info_message__string_list__init(strlist);
442
443
/* Convert vec into a StringList. */
444
for (len = 0; vec[len] != NULL; len++) {
445
continue;
446
}
447
strlist->strings = reallocarray(NULL, len, sizeof(char *));
448
if (strlist->strings == NULL) {
449
free(strlist);
450
strlist = NULL;
451
goto done;
452
}
453
strlist->n_strings = len;
454
for (len = 0; vec[len] != NULL; len++) {
455
strlist->strings[len] = vec[len];
456
}
457
458
done:
459
debug_return_ptr(strlist);
460
}
461
462
/*
463
* Split command + args separated by whitespace into a StringList.
464
* Returns a StringList containing command and args, reusing the contents
465
* of "command", which is modified.
466
*/
467
static InfoMessage__StringList *
468
command_to_stringlist(char *command)
469
{
470
InfoMessage__StringList *strlist;
471
char *cp;
472
size_t len;
473
debug_decl(command_to_stringlist, SUDO_DEBUG_UTIL);
474
475
strlist = malloc(sizeof(*strlist));
476
if (strlist == NULL)
477
debug_return_ptr(NULL);
478
info_message__string_list__init(strlist);
479
480
for (cp = command, len = 0;;) {
481
len++;
482
if ((cp = strchr(cp, ' ')) == NULL)
483
break;
484
cp++;
485
}
486
strlist->strings = reallocarray(NULL, len, sizeof(char *));
487
if (strlist->strings == NULL) {
488
free(strlist);
489
debug_return_ptr(NULL);
490
}
491
strlist->n_strings = len;
492
493
for (cp = command, len = 0;;) {
494
strlist->strings[len++] = cp;
495
if ((cp = strchr(cp, ' ')) == NULL)
496
break;
497
*cp++ = '\0';
498
}
499
500
debug_return_ptr(strlist);
501
}
502
503
/*
504
* Build runargv StringList using either argv or command in evlog.
505
* Truncated command in evlog after first space as a side effect.
506
*/
507
static InfoMessage__StringList *
508
fmt_runargv(const struct eventlog *evlog)
509
{
510
InfoMessage__StringList *runargv;
511
debug_decl(fmt_runargv, SUDO_DEBUG_UTIL);
512
513
/* We may have runargv from the log.json file. */
514
if (evlog->runargv != NULL && evlog->runargv[0] != NULL) {
515
/* Convert evlog->runargv into a StringList. */
516
runargv = vec_to_stringlist(evlog->runargv);
517
if (runargv != NULL) {
518
/* Make sure command doesn't include arguments. */
519
char *cp = strchr(evlog->command, ' ');
520
if (cp != NULL)
521
*cp = '\0';
522
}
523
} else {
524
/* No log.json file, split command into a StringList. */
525
runargv = command_to_stringlist(evlog->command);
526
}
527
528
debug_return_ptr(runargv);
529
}
530
531
/*
532
* Build runenv StringList from env in evlog, if present.
533
*/
534
static InfoMessage__StringList *
535
fmt_runenv(const struct eventlog *evlog)
536
{
537
debug_decl(fmt_runenv, SUDO_DEBUG_UTIL);
538
539
/* Only present in log.json. */
540
if (evlog->runenv == NULL || evlog->runenv[0] == NULL)
541
debug_return_ptr(NULL);
542
543
debug_return_ptr(vec_to_stringlist(evlog->runenv));
544
}
545
546
/*
547
* Build submitenv StringList from env in evlog, if present.
548
*/
549
static InfoMessage__StringList *
550
fmt_submitenv(const struct eventlog *evlog)
551
{
552
debug_decl(fmt_submitenv, SUDO_DEBUG_UTIL);
553
554
/* Only present in log.json. */
555
if (evlog->submitenv == NULL || evlog->submitenv[0] == NULL)
556
debug_return_ptr(NULL);
557
558
debug_return_ptr(vec_to_stringlist(evlog->submitenv));
559
}
560
561
static InfoMessage **
562
fmt_info_messages(const struct eventlog *evlog, char *hostname,
563
size_t *n_info_msgs)
564
{
565
InfoMessage__StringList *runargv = NULL;
566
InfoMessage__StringList *runenv = NULL;
567
InfoMessage__StringList *submitenv = NULL;
568
InfoMessage **info_msgs = NULL;
569
size_t info_msgs_size, n = 0;
570
debug_decl(fmt_info_messages, SUDO_DEBUG_UTIL);
571
572
runargv = fmt_runargv(evlog);
573
if (runargv == NULL)
574
goto oom;
575
576
/* runenv and submitenv are only present in log.json */
577
runenv = fmt_runenv(evlog);
578
submitenv = fmt_submitenv(evlog);
579
580
/* The sudo I/O log info file has limited info. */
581
info_msgs_size = 15;
582
info_msgs = calloc(info_msgs_size, sizeof(InfoMessage *));
583
if (info_msgs == NULL)
584
goto oom;
585
for (n = 0; n < info_msgs_size; n++) {
586
info_msgs[n] = malloc(sizeof(InfoMessage));
587
if (info_msgs[n] == NULL)
588
goto oom;
589
info_message__init(info_msgs[n]);
590
}
591
592
#define fill_str(_n, _v) do { \
593
info_msgs[n]->key = (char *)(_n); \
594
info_msgs[n]->u.strval = (_v); \
595
info_msgs[n]->value_case = INFO_MESSAGE__VALUE_STRVAL; \
596
n++; \
597
} while (0)
598
599
#define fill_strlist(_n, _v) do { \
600
info_msgs[n]->key = (char *)(_n); \
601
info_msgs[n]->u.strlistval = (_v); \
602
info_msgs[n]->value_case = INFO_MESSAGE__VALUE_STRLISTVAL; \
603
n++; \
604
} while (0)
605
606
#define fill_num(_n, _v) do { \
607
info_msgs[n]->key = (char *)(_n); \
608
info_msgs[n]->u.numval = (_v); \
609
info_msgs[n]->value_case = INFO_MESSAGE__VALUE_NUMVAL; \
610
n++; \
611
} while (0)
612
613
/*
614
* Fill in info_msgs. For legacy I/O log files, only command, runargv,
615
* runuser, submitcwd, submithost, submituser, ttyname may be present.
616
*/
617
n = 0;
618
fill_num("columns", evlog->columns);
619
fill_str("command", evlog->command);
620
fill_num("lines", evlog->lines);
621
fill_strlist("runargv", runargv);
622
runargv = NULL;
623
if (submitenv != NULL) {
624
fill_strlist("submitenv", submitenv);
625
submitenv = NULL;
626
}
627
if (runenv != NULL) {
628
fill_strlist("runenv", runenv);
629
runenv = NULL;
630
}
631
if (evlog->rungid != (gid_t)-1) {
632
fill_num("rungid", evlog->rungid);
633
}
634
if (evlog->rungroup != NULL) {
635
fill_str("rungroup", evlog->rungroup);
636
}
637
if (evlog->runuid != (uid_t)-1) {
638
fill_num("runuid", evlog->runuid);
639
}
640
fill_str("runuser", evlog->runuser);
641
if (evlog->source != NULL) {
642
fill_str("source", evlog->source);
643
}
644
fill_str("submitcwd", evlog->cwd);
645
fill_str("submithost", hostname);
646
fill_str("submituser", evlog->submituser);
647
fill_str("ttyname", evlog->ttyname);
648
649
/* Update n_info_msgs. */
650
*n_info_msgs = n;
651
652
/* Avoid leaking unused info_msg structs. */
653
while (n < info_msgs_size) {
654
free(info_msgs[n++]);
655
}
656
657
debug_return_ptr(info_msgs);
658
659
oom:
660
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
661
free_info_messages(info_msgs, n);
662
if (runargv != NULL) {
663
free(runargv->strings);
664
free(runargv);
665
}
666
if (runenv != NULL) {
667
free(runenv->strings);
668
free(runenv);
669
}
670
if (submitenv != NULL) {
671
free(submitenv->strings);
672
free(submitenv);
673
}
674
*n_info_msgs = 0;
675
debug_return_ptr(NULL);
676
}
677
678
/*
679
* Build and format a RejectMessage wrapped in a ClientMessage.
680
* Stores the wire format message in the closure's write buffer.
681
* Returns true on success, false on failure.
682
*/
683
static bool
684
fmt_reject_message(struct client_closure *closure)
685
{
686
ClientMessage client_msg = CLIENT_MESSAGE__INIT;
687
RejectMessage reject_msg = REJECT_MESSAGE__INIT;
688
TimeSpec ts = TIME_SPEC__INIT;
689
size_t n_info_msgs;
690
bool ret = false;
691
char *hostname;
692
debug_decl(fmt_reject_message, SUDO_DEBUG_UTIL);
693
694
/*
695
* Fill in RejectMessage and add it to ClientMessage.
696
*/
697
if ((hostname = sudo_gethostname()) == NULL) {
698
sudo_warn("gethostname");
699
debug_return_bool(false);
700
}
701
702
/* Sudo I/O logs only store start time in seconds. */
703
ts.tv_sec = (int64_t)closure->evlog->event_time.tv_sec;
704
ts.tv_nsec = (int32_t)closure->evlog->event_time.tv_nsec;
705
reject_msg.submit_time = &ts;
706
707
/* Why the command was rejected. */
708
reject_msg.reason = closure->reject_reason;
709
710
reject_msg.info_msgs = fmt_info_messages(closure->evlog, hostname,
711
&n_info_msgs);
712
if (reject_msg.info_msgs == NULL)
713
goto done;
714
715
/* Update n_info_msgs. */
716
reject_msg.n_info_msgs = n_info_msgs;
717
718
sudo_debug_printf(SUDO_DEBUG_INFO,
719
"%s: sending RejectMessage, array length %zu", __func__, n_info_msgs);
720
721
/* Schedule ClientMessage */
722
client_msg.u.reject_msg = &reject_msg;
723
client_msg.type_case = CLIENT_MESSAGE__TYPE_REJECT_MSG;
724
ret = fmt_client_message(closure, &client_msg);
725
if (ret) {
726
if (sudo_ev_add(closure->evbase, closure->write_ev, NULL, false) == -1)
727
ret = false;
728
}
729
730
done:
731
free_info_messages(reject_msg.info_msgs, n_info_msgs);
732
free(hostname);
733
734
debug_return_bool(ret);
735
}
736
737
/*
738
* Build and format an AcceptMessage wrapped in a ClientMessage.
739
* Stores the wire format message in the closure's write buffer.
740
* Returns true on success, false on failure.
741
*/
742
static bool
743
fmt_accept_message(struct client_closure *closure)
744
{
745
ClientMessage client_msg = CLIENT_MESSAGE__INIT;
746
AcceptMessage accept_msg = ACCEPT_MESSAGE__INIT;
747
TimeSpec ts = TIME_SPEC__INIT;
748
bool ret = false;
749
char *hostname;
750
debug_decl(fmt_accept_message, SUDO_DEBUG_UTIL);
751
752
/*
753
* Fill in AcceptMessage and add it to ClientMessage.
754
*/
755
if ((hostname = sudo_gethostname()) == NULL) {
756
sudo_warn("gethostname");
757
debug_return_bool(false);
758
}
759
ts.tv_sec = (int64_t)closure->evlog->event_time.tv_sec;
760
ts.tv_nsec = (int32_t)closure->evlog->event_time.tv_nsec;
761
accept_msg.submit_time = &ts;
762
763
/* Client will send IoBuffer messages. */
764
accept_msg.expect_iobufs = !closure->accept_only;
765
766
accept_msg.info_msgs = fmt_info_messages(closure->evlog, hostname,
767
&accept_msg.n_info_msgs);
768
if (accept_msg.info_msgs == NULL)
769
goto done;
770
771
sudo_debug_printf(SUDO_DEBUG_INFO,
772
"%s: sending AcceptMessage, array length %zu", __func__,
773
accept_msg.n_info_msgs);
774
775
/* Schedule ClientMessage */
776
client_msg.u.accept_msg = &accept_msg;
777
client_msg.type_case = CLIENT_MESSAGE__TYPE_ACCEPT_MSG;
778
ret = fmt_client_message(closure, &client_msg);
779
if (ret) {
780
if (sudo_ev_add(closure->evbase, closure->write_ev, NULL, false) == -1)
781
ret = false;
782
}
783
784
done:
785
free_info_messages(accept_msg.info_msgs, accept_msg.n_info_msgs);
786
free(hostname);
787
788
debug_return_bool(ret);
789
}
790
791
/*
792
* Build and format a RestartMessage wrapped in a ClientMessage.
793
* Stores the wire format message in the closure's write buffer.
794
* Returns true on success, false on failure.
795
*/
796
static bool
797
fmt_restart_message(struct client_closure *closure)
798
{
799
ClientMessage client_msg = CLIENT_MESSAGE__INIT;
800
RestartMessage restart_msg = RESTART_MESSAGE__INIT;
801
TimeSpec ts = TIME_SPEC__INIT;
802
bool ret = false;
803
debug_decl(fmt_restart_message, SUDO_DEBUG_UTIL);
804
805
sudo_debug_printf(SUDO_DEBUG_INFO,
806
"%s: sending RestartMessage, [%lld, %ld]", __func__,
807
(long long)closure->restart.tv_sec, closure->restart.tv_nsec);
808
809
ts.tv_sec = (int64_t)closure->restart.tv_sec;
810
ts.tv_nsec = (int32_t)closure->restart.tv_nsec;
811
restart_msg.resume_point = &ts;
812
restart_msg.log_id = (char *)closure->iolog_id;
813
814
/* Schedule ClientMessage */
815
client_msg.u.restart_msg = &restart_msg;
816
client_msg.type_case = CLIENT_MESSAGE__TYPE_RESTART_MSG;
817
ret = fmt_client_message(closure, &client_msg);
818
if (ret) {
819
if (sudo_ev_add(closure->evbase, closure->write_ev, NULL, false) == -1)
820
ret = false;
821
}
822
823
debug_return_bool(ret);
824
}
825
826
/*
827
* Build and format an ExitMessage wrapped in a ClientMessage.
828
* Stores the wire format message in the closure's write buffer list.
829
* Returns true on success, false on failure.
830
*/
831
static bool
832
fmt_exit_message(struct client_closure *closure)
833
{
834
ClientMessage client_msg = CLIENT_MESSAGE__INIT;
835
ExitMessage exit_msg = EXIT_MESSAGE__INIT;
836
TimeSpec run_time = TIME_SPEC__INIT;
837
struct eventlog *evlog = closure->evlog;
838
bool ret = false;
839
debug_decl(fmt_exit_message, SUDO_DEBUG_UTIL);
840
841
if (evlog->exit_value != -1)
842
exit_msg.exit_value = evlog->exit_value;
843
if (sudo_timespecisset(&evlog->run_time)) {
844
run_time.tv_sec = (int64_t)evlog->run_time.tv_sec;
845
run_time.tv_nsec = (int32_t)evlog->run_time.tv_nsec;
846
exit_msg.run_time = &run_time;
847
}
848
if (evlog->signal_name != NULL) {
849
exit_msg.signal = evlog->signal_name;
850
exit_msg.dumped_core = evlog->dumped_core;
851
}
852
853
if (evlog->signal_name != NULL) {
854
sudo_debug_printf(SUDO_DEBUG_INFO,
855
"%s: sending ExitMessage, signal %s, run_time [%lld, %ld]",
856
__func__, evlog->signal_name, (long long)evlog->run_time.tv_sec,
857
evlog->run_time.tv_nsec);
858
} else {
859
sudo_debug_printf(SUDO_DEBUG_INFO,
860
"%s: sending ExitMessage, exit value %d, run_time [%lld, %ld]",
861
__func__, evlog->exit_value, (long long)evlog->run_time.tv_sec,
862
evlog->run_time.tv_nsec);
863
}
864
865
/* Send ClientMessage */
866
client_msg.u.exit_msg = &exit_msg;
867
client_msg.type_case = CLIENT_MESSAGE__TYPE_EXIT_MSG;
868
if (!fmt_client_message(closure, &client_msg))
869
goto done;
870
871
ret = true;
872
873
done:
874
debug_return_bool(ret);
875
}
876
877
/*
878
* Build and format an IoBuffer wrapped in a ClientMessage.
879
* Stores the wire format message in the closure's write buffer list.
880
* Returns true on success, false on failure.
881
*/
882
static bool
883
fmt_io_buf(int type, struct client_closure *closure)
884
{
885
ClientMessage client_msg = CLIENT_MESSAGE__INIT;
886
IoBuffer iobuf_msg = IO_BUFFER__INIT;
887
TimeSpec delay = TIME_SPEC__INIT;
888
bool ret = false;
889
debug_decl(fmt_io_buf, SUDO_DEBUG_UTIL);
890
891
if (!read_io_buf(closure))
892
goto done;
893
894
/* Fill in IoBuffer. */
895
/* TODO: split buffer if it is too large */
896
delay.tv_sec = (int64_t)closure->timing.delay.tv_sec;
897
delay.tv_nsec = (int32_t)closure->timing.delay.tv_nsec;
898
iobuf_msg.delay = &delay;
899
iobuf_msg.data.data = (void *)closure->buf;
900
iobuf_msg.data.len = closure->timing.u.nbytes;
901
902
sudo_debug_printf(SUDO_DEBUG_INFO,
903
"%s: sending IoBuffer length %zu, type %d, size %zu", __func__,
904
iobuf_msg.data.len, type, io_buffer__get_packed_size(&iobuf_msg));
905
906
/* Send ClientMessage, it doesn't matter which IoBuffer we set. */
907
client_msg.u.ttyout_buf = &iobuf_msg;
908
client_msg.type_case = type;
909
if (!fmt_client_message(closure, &client_msg))
910
goto done;
911
912
ret = true;
913
914
done:
915
debug_return_bool(ret);
916
}
917
918
/*
919
* Build and format a ChangeWindowSize message wrapped in a ClientMessage.
920
* Stores the wire format message in the closure's write buffer list.
921
* Returns true on success, false on failure.
922
*/
923
static bool
924
fmt_winsize(struct client_closure *closure)
925
{
926
ClientMessage client_msg = CLIENT_MESSAGE__INIT;
927
ChangeWindowSize winsize_msg = CHANGE_WINDOW_SIZE__INIT;
928
TimeSpec delay = TIME_SPEC__INIT;
929
struct timing_closure *timing = &closure->timing;
930
bool ret = false;
931
debug_decl(fmt_winsize, SUDO_DEBUG_UTIL);
932
933
/* Fill in ChangeWindowSize message. */
934
delay.tv_sec = (int64_t)timing->delay.tv_sec;
935
delay.tv_nsec = (int32_t)timing->delay.tv_nsec;
936
winsize_msg.delay = &delay;
937
winsize_msg.rows = timing->u.winsize.lines;
938
winsize_msg.cols = timing->u.winsize.cols;
939
940
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: sending ChangeWindowSize, %dx%d",
941
__func__, winsize_msg.rows, winsize_msg.cols);
942
943
/* Send ClientMessage */
944
client_msg.u.winsize_event = &winsize_msg;
945
client_msg.type_case = CLIENT_MESSAGE__TYPE_WINSIZE_EVENT;
946
if (!fmt_client_message(closure, &client_msg))
947
goto done;
948
949
ret = true;
950
951
done:
952
debug_return_bool(ret);
953
}
954
955
/*
956
* Build and format a CommandSuspend message wrapped in a ClientMessage.
957
* Stores the wire format message in the closure's write buffer list.
958
* Returns true on success, false on failure.
959
*/
960
static bool
961
fmt_suspend(struct client_closure *closure)
962
{
963
ClientMessage client_msg = CLIENT_MESSAGE__INIT;
964
CommandSuspend suspend_msg = COMMAND_SUSPEND__INIT;
965
TimeSpec delay = TIME_SPEC__INIT;
966
struct timing_closure *timing = &closure->timing;
967
bool ret = false;
968
debug_decl(fmt_suspend, SUDO_DEBUG_UTIL);
969
970
/* Fill in CommandSuspend message. */
971
delay.tv_sec = (int64_t)timing->delay.tv_sec;
972
delay.tv_nsec = (int32_t)timing->delay.tv_nsec;
973
suspend_msg.delay = &delay;
974
if (sig2str(timing->u.signo, closure->buf) == -1)
975
goto done;
976
suspend_msg.signal = closure->buf;
977
978
sudo_debug_printf(SUDO_DEBUG_INFO,
979
"%s: sending CommandSuspend, SIG%s", __func__, suspend_msg.signal);
980
981
/* Send ClientMessage */
982
client_msg.u.suspend_event = &suspend_msg;
983
client_msg.type_case = CLIENT_MESSAGE__TYPE_SUSPEND_EVENT;
984
if (!fmt_client_message(closure, &client_msg))
985
goto done;
986
987
ret = true;
988
989
done:
990
debug_return_bool(ret);
991
}
992
993
/*
994
* Read the next entry for the I/O log timing file and format a ClientMessage.
995
* Stores the wire format message in the closure's write buffer list.
996
* Returns true on success, false on failure.
997
*/
998
static bool
999
fmt_next_iolog(struct client_closure *closure)
1000
{
1001
struct timing_closure *timing = &closure->timing;
1002
bool ret = false;
1003
debug_decl(fmt_next_iolog, SUDO_DEBUG_UTIL);
1004
1005
for (;;) {
1006
const int timing_status = iolog_read_timing_record(
1007
&closure->iolog_files[IOFD_TIMING], timing);
1008
switch (timing_status) {
1009
case 0:
1010
/* OK */
1011
break;
1012
case 1:
1013
/* no more IO buffers */
1014
closure->state = SEND_EXIT;
1015
debug_return_bool(fmt_exit_message(closure));
1016
case -1:
1017
default:
1018
debug_return_bool(false);
1019
}
1020
1021
/* Track elapsed time for comparison with commit points. */
1022
sudo_timespecadd(&closure->elapsed, &timing->delay, &closure->elapsed);
1023
1024
/* If there is a stopping point, make sure we haven't reached it. */
1025
if (sudo_timespecisset(&closure->stop_after)) {
1026
if (sudo_timespeccmp(&closure->elapsed, &closure->stop_after, >)) {
1027
/* Reached limit, force premature end. */
1028
sudo_timespecsub(&closure->elapsed, &timing->delay,
1029
&closure->elapsed);
1030
debug_return_bool(false);
1031
}
1032
}
1033
1034
/* If we have a restart point, ignore records until we hit it. */
1035
if (sudo_timespecisset(&closure->restart)) {
1036
if (sudo_timespeccmp(&closure->restart, &closure->elapsed, >=))
1037
continue;
1038
sudo_timespecclear(&closure->restart); /* caught up */
1039
}
1040
1041
switch (timing->event) {
1042
case IO_EVENT_STDIN:
1043
ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_STDIN_BUF, closure);
1044
break;
1045
case IO_EVENT_STDOUT:
1046
ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_STDOUT_BUF, closure);
1047
break;
1048
case IO_EVENT_STDERR:
1049
ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_STDERR_BUF, closure);
1050
break;
1051
case IO_EVENT_TTYIN:
1052
ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_TTYIN_BUF, closure);
1053
break;
1054
case IO_EVENT_TTYOUT:
1055
ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_TTYOUT_BUF, closure);
1056
break;
1057
case IO_EVENT_WINSIZE:
1058
ret = fmt_winsize(closure);
1059
break;
1060
case IO_EVENT_SUSPEND:
1061
ret = fmt_suspend(closure);
1062
break;
1063
default:
1064
sudo_warnx(U_("unexpected I/O event %d"), timing->event);
1065
break;
1066
}
1067
1068
/* Keep filling write buffer as long as we only have one of them. */
1069
if (!ret)
1070
break;
1071
if (TAILQ_NEXT(TAILQ_FIRST(&closure->write_bufs), entries) != NULL)
1072
break;
1073
}
1074
1075
debug_return_bool(ret);
1076
}
1077
1078
/*
1079
* Additional work to do after a ClientMessage was sent to the server.
1080
* Advances state and formats the next ClientMessage (if any).
1081
*/
1082
static bool
1083
client_message_completion(struct client_closure *closure)
1084
{
1085
debug_decl(client_message_completion, SUDO_DEBUG_UTIL);
1086
1087
switch (closure->state) {
1088
case RECV_HELLO:
1089
/* Wait for ServerHello, nothing to write until then. */
1090
sudo_ev_del(closure->evbase, closure->write_ev);
1091
break;
1092
case SEND_ACCEPT:
1093
if (closure->accept_only) {
1094
closure->state = SEND_EXIT;
1095
debug_return_bool(fmt_exit_message(closure));
1096
}
1097
FALLTHROUGH;
1098
case SEND_RESTART:
1099
closure->state = SEND_IO;
1100
FALLTHROUGH;
1101
case SEND_IO:
1102
/* fmt_next_iolog() will advance state on EOF. */
1103
if (!fmt_next_iolog(closure))
1104
debug_return_bool(false);
1105
break;
1106
case SEND_REJECT:
1107
/* Done writing, wait for server to close connection. */
1108
sudo_ev_del(closure->evbase, closure->write_ev);
1109
closure->state = FINISHED;
1110
break;
1111
case SEND_EXIT:
1112
/* Done writing, wait for final commit point if sending I/O. */
1113
sudo_ev_del(closure->evbase, closure->write_ev);
1114
closure->state = closure->accept_only ? FINISHED : CLOSING;
1115
break;
1116
default:
1117
sudo_warnx(U_("%s: unexpected state %d"), __func__, closure->state);
1118
debug_return_bool(false);
1119
}
1120
debug_return_bool(true);
1121
}
1122
1123
/*
1124
* Respond to a ServerHello message from the server.
1125
* Returns true on success, false on error.
1126
*/
1127
static bool
1128
handle_server_hello(const ServerHello *msg, struct client_closure *closure)
1129
{
1130
size_t n;
1131
debug_decl(handle_server_hello, SUDO_DEBUG_UTIL);
1132
1133
if (closure->state != RECV_HELLO) {
1134
sudo_warnx(U_("%s: unexpected state %d"), __func__, closure->state);
1135
debug_return_bool(false);
1136
}
1137
1138
/* Check that ServerHello is valid. */
1139
if (msg == NULL || msg->server_id == NULL || msg->server_id[0] == '\0') {
1140
sudo_warnx("%s", U_("invalid ServerHello"));
1141
debug_return_bool(false);
1142
}
1143
1144
if (!testrun) {
1145
printf("Server ID: %s\n", msg->server_id);
1146
/* TODO: handle redirect */
1147
if (msg->redirect != NULL && msg->redirect[0] != '\0')
1148
printf("Redirect: %s\n", msg->redirect);
1149
for (n = 0; n < msg->n_servers; n++) {
1150
printf("Server %u: %s\n", (unsigned int)n + 1, msg->servers[n]);
1151
}
1152
}
1153
1154
debug_return_bool(true);
1155
}
1156
1157
/*
1158
* Respond to a commit_point ServerMessage from the server.
1159
* Returns true on success, false on error.
1160
*/
1161
static bool
1162
handle_commit_point(const TimeSpec *commit_point,
1163
struct client_closure *closure)
1164
{
1165
debug_decl(handle_commit_point, SUDO_DEBUG_UTIL);
1166
1167
/* Only valid after we have sent an IO buffer. */
1168
if (closure->state < SEND_IO) {
1169
sudo_warnx(U_("%s: unexpected state %d"), __func__, closure->state);
1170
debug_return_bool(false);
1171
}
1172
1173
/* Check that ServerMessage's commit_point is valid. */
1174
if (commit_point == NULL) {
1175
sudo_warnx(U_("%s: invalid ServerMessage, missing commit_point"),
1176
server_info.name);
1177
debug_return_bool(false);
1178
}
1179
1180
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: commit point: [%lld, %d]",
1181
__func__, (long long)commit_point->tv_sec, commit_point->tv_nsec);
1182
closure->committed.tv_sec = (time_t)commit_point->tv_sec;
1183
closure->committed.tv_nsec = (long)commit_point->tv_nsec;
1184
1185
debug_return_bool(true);
1186
}
1187
1188
/*
1189
* Respond to a log_id ServerMessage from the relay.
1190
* Always returns true.
1191
*/
1192
static bool
1193
handle_log_id(const char *id, struct client_closure *closure)
1194
{
1195
debug_decl(handle_log_id, SUDO_DEBUG_UTIL);
1196
1197
if (!testrun)
1198
printf("Remote log ID: %s\n", id);
1199
1200
debug_return_bool(true);
1201
}
1202
1203
/*
1204
* Respond to a ServerError message from the server.
1205
* Always returns false.
1206
*/
1207
static bool
1208
handle_server_error(const char *errmsg, struct client_closure *closure)
1209
{
1210
debug_decl(handle_server_error, SUDO_DEBUG_UTIL);
1211
1212
sudo_warnx(U_("error message received from server: %s"), errmsg);
1213
debug_return_bool(false);
1214
}
1215
1216
/*
1217
* Respond to a ServerAbort message from the server.
1218
* Always returns false.
1219
*/
1220
static bool
1221
handle_server_abort(const char *errmsg, struct client_closure *closure)
1222
{
1223
debug_decl(handle_server_abort, SUDO_DEBUG_UTIL);
1224
1225
sudo_warnx(U_("abort message received from server: %s"), errmsg);
1226
debug_return_bool(false);
1227
}
1228
1229
/*
1230
* Respond to a ServerMessage from the server.
1231
* Returns true on success, false on error.
1232
*/
1233
static bool
1234
handle_server_message(const uint8_t *buf, size_t len,
1235
struct client_closure *closure)
1236
{
1237
ServerMessage *msg;
1238
bool ret = false;
1239
debug_decl(handle_server_message, SUDO_DEBUG_UTIL);
1240
1241
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: unpacking ServerMessage", __func__);
1242
msg = server_message__unpack(NULL, len, buf);
1243
if (msg == NULL) {
1244
sudo_warnx(U_("unable to unpack %s size %zu"), "ServerMessage", len);
1245
debug_return_bool(false);
1246
}
1247
1248
switch (msg->type_case) {
1249
case SERVER_MESSAGE__TYPE_HELLO:
1250
if ((ret = handle_server_hello(msg->u.hello, closure))) {
1251
if (sudo_timespecisset(&closure->restart)) {
1252
closure->state = SEND_RESTART;
1253
ret = fmt_restart_message(closure);
1254
} else if (closure->reject_reason != NULL) {
1255
closure->state = SEND_REJECT;
1256
ret = fmt_reject_message(closure);
1257
} else {
1258
closure->state = SEND_ACCEPT;
1259
ret = fmt_accept_message(closure);
1260
}
1261
}
1262
break;
1263
case SERVER_MESSAGE__TYPE_COMMIT_POINT:
1264
ret = handle_commit_point(msg->u.commit_point, closure);
1265
if (sudo_timespeccmp(&closure->elapsed, &closure->committed, ==)) {
1266
sudo_ev_del(closure->evbase, closure->read_ev);
1267
closure->state = FINISHED;
1268
if (++finished_transmissions == nr_of_conns)
1269
sudo_ev_loopexit(closure->evbase);
1270
}
1271
break;
1272
case SERVER_MESSAGE__TYPE_LOG_ID:
1273
ret = handle_log_id(msg->u.log_id, closure);
1274
break;
1275
case SERVER_MESSAGE__TYPE_ERROR:
1276
ret = handle_server_error(msg->u.error, closure);
1277
closure->state = ERROR;
1278
break;
1279
case SERVER_MESSAGE__TYPE_ABORT:
1280
ret = handle_server_abort(msg->u.abort, closure);
1281
closure->state = ERROR;
1282
break;
1283
default:
1284
sudo_warnx(U_("%s: unexpected type_case value %d"),
1285
__func__, msg->type_case);
1286
break;
1287
}
1288
1289
server_message__free_unpacked(msg, NULL);
1290
debug_return_bool(ret);
1291
}
1292
1293
/*
1294
* Read and unpack a ServerMessage (read callback).
1295
*/
1296
static void
1297
server_msg_cb(int fd, int what, void *v)
1298
{
1299
struct client_closure *closure = v;
1300
struct connection_buffer *buf = &closure->read_buf;
1301
size_t nread;
1302
uint32_t msg_len;
1303
debug_decl(server_msg_cb, SUDO_DEBUG_UTIL);
1304
1305
/* For TLS we may need to read as part of SSL_write_ex(). */
1306
if (closure->write_instead_of_read) {
1307
closure->write_instead_of_read = false;
1308
client_msg_cb(fd, what, v);
1309
debug_return;
1310
}
1311
1312
if (what == SUDO_EV_TIMEOUT) {
1313
sudo_warnx("%s", U_("timeout reading from server"));
1314
goto bad;
1315
}
1316
1317
#if defined(HAVE_OPENSSL)
1318
if (cert != NULL) {
1319
SSL *ssl = closure->tls_client.ssl;
1320
int result;
1321
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: reading ServerMessage (TLS)", __func__);
1322
result = SSL_read_ex(ssl, buf->data + buf->len, buf->size - buf->len,
1323
&nread);
1324
if (result <= 0) {
1325
unsigned long errcode;
1326
const char *errstr;
1327
1328
switch (SSL_get_error(ssl, result)) {
1329
case SSL_ERROR_ZERO_RETURN:
1330
/* ssl connection shutdown cleanly */
1331
nread = 0;
1332
break;
1333
case SSL_ERROR_WANT_READ:
1334
/* ssl wants to read more, read event is always active */
1335
sudo_debug_printf(SUDO_DEBUG_NOTICE|SUDO_DEBUG_LINENO,
1336
"SSL_read_ex returns SSL_ERROR_WANT_READ");
1337
debug_return;
1338
case SSL_ERROR_WANT_WRITE:
1339
/* ssl wants to write, schedule a write if not pending */
1340
sudo_debug_printf(SUDO_DEBUG_NOTICE|SUDO_DEBUG_LINENO,
1341
"SSL_read_ex returns SSL_ERROR_WANT_WRITE");
1342
if (!sudo_ev_pending(closure->write_ev, SUDO_EV_WRITE, NULL)) {
1343
/* Enable a temporary write event. */
1344
if (sudo_ev_add(closure->evbase, closure->write_ev, NULL, false) == -1) {
1345
sudo_warnx("%s", U_("unable to add event to queue"));
1346
goto bad;
1347
}
1348
closure->temporary_write_event = true;
1349
}
1350
/* Redirect write event to finish SSL_read_ex() */
1351
closure->read_instead_of_write = true;
1352
debug_return;
1353
case SSL_ERROR_SSL:
1354
/*
1355
* For TLS 1.3, if the cert verify function on the server
1356
* returns an error, OpenSSL will send an internal error
1357
* alert when we read ServerHello. Convert to a more useful
1358
* message and hope that no actual internal error occurs.
1359
*/
1360
errcode = ERR_get_error();
1361
#if !defined(HAVE_WOLFSSL)
1362
if (closure->state == RECV_HELLO &&
1363
ERR_GET_REASON(errcode) == SSL_R_TLSV1_ALERT_INTERNAL_ERROR) {
1364
errstr = U_("host name does not match certificate");
1365
} else
1366
#endif
1367
{
1368
errstr = ERR_reason_error_string(errcode);
1369
}
1370
sudo_warnx("%s", errstr ? errstr : strerror(errno));
1371
goto bad;
1372
case SSL_ERROR_SYSCALL:
1373
sudo_warn("SSL_read_ex");
1374
goto bad;
1375
default:
1376
errstr = ERR_reason_error_string(ERR_get_error());
1377
sudo_warnx("SSL_read_ex: %s",
1378
errstr ? errstr : strerror(errno));
1379
goto bad;
1380
}
1381
}
1382
} else
1383
#endif
1384
{
1385
ssize_t n;
1386
1387
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: reading ServerMessage", __func__);
1388
n = read(fd, buf->data + buf->len, buf->size - buf->len);
1389
if (n < 0) {
1390
if (errno == EAGAIN || errno == EINTR)
1391
debug_return;
1392
sudo_warn("read");
1393
goto bad;
1394
}
1395
nread = (size_t)n;
1396
}
1397
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: received %zd bytes from server",
1398
__func__, nread);
1399
if (nread == 0) {
1400
if (closure->state != FINISHED)
1401
sudo_warnx("%s", U_("premature EOF"));
1402
goto bad;
1403
}
1404
if (nread > SIZE_MAX - buf->len) {
1405
sudo_warnx(U_("internal error, %s overflow"), __func__);
1406
goto bad;
1407
}
1408
buf->len += nread;
1409
1410
while (buf->len - buf->off >= sizeof(msg_len)) {
1411
/* Read wire message size (uint32_t in network byte order). */
1412
memcpy(&msg_len, buf->data + buf->off, sizeof(msg_len));
1413
msg_len = ntohl(msg_len);
1414
1415
if (msg_len > MESSAGE_SIZE_MAX) {
1416
sudo_warnx(U_("server message too large: %u"), msg_len);
1417
goto bad;
1418
}
1419
1420
if (msg_len + sizeof(msg_len) > buf->len - buf->off) {
1421
/* Incomplete message, we'll read the rest next time. */
1422
if (!expand_buf(buf, msg_len + sizeof(msg_len)))
1423
goto bad;
1424
debug_return;
1425
}
1426
1427
/* Parse ServerMessage, could be zero bytes. */
1428
sudo_debug_printf(SUDO_DEBUG_INFO,
1429
"%s: parsing ServerMessage, size %u", __func__, msg_len);
1430
buf->off += sizeof(msg_len);
1431
if (!handle_server_message(buf->data + buf->off, msg_len, closure))
1432
goto bad;
1433
buf->off += msg_len;
1434
}
1435
if (buf->len != buf->off) {
1436
memmove(buf->data, buf->data + buf->off, buf->len - buf->off);
1437
}
1438
buf->len -= buf->off;
1439
buf->off = 0;
1440
debug_return;
1441
bad:
1442
sudo_ev_del(closure->evbase, closure->read_ev);
1443
debug_return;
1444
}
1445
1446
/*
1447
* Send a ClientMessage to the server (write callback).
1448
*/
1449
static void
1450
client_msg_cb(int fd, int what, void *v)
1451
{
1452
struct client_closure *closure = v;
1453
struct connection_buffer *buf;
1454
size_t nwritten;
1455
debug_decl(client_msg_cb, SUDO_DEBUG_UTIL);
1456
1457
if ((buf = TAILQ_FIRST(&closure->write_bufs)) == NULL) {
1458
sudo_warnx(U_("missing write buffer for client %s"), "localhost");
1459
goto bad;
1460
}
1461
1462
/* For TLS we may need to write as part of SSL_read_ex(). */
1463
if (closure->read_instead_of_write) {
1464
closure->read_instead_of_write = false;
1465
/* Delete write event if it was only due to SSL_read_ex(). */
1466
if (closure->temporary_write_event) {
1467
closure->temporary_write_event = false;
1468
sudo_ev_del(closure->evbase, closure->write_ev);
1469
}
1470
server_msg_cb(fd, what, v);
1471
debug_return;
1472
}
1473
1474
if (what == SUDO_EV_TIMEOUT) {
1475
sudo_warnx("%s", U_("timeout writing to server"));
1476
goto bad;
1477
}
1478
1479
sudo_debug_printf(SUDO_DEBUG_INFO,
1480
"%s: sending %zu bytes to server", __func__, buf->len - buf->off);
1481
1482
#if defined(HAVE_OPENSSL)
1483
if (cert != NULL) {
1484
SSL *ssl = closure->tls_client.ssl;
1485
const int result = SSL_write_ex(ssl, buf->data + buf->off,
1486
buf->len - buf->off, &nwritten);
1487
if (result <= 0) {
1488
const char *errstr;
1489
1490
switch (SSL_get_error(ssl, result)) {
1491
case SSL_ERROR_ZERO_RETURN:
1492
/* ssl connection shutdown */
1493
goto bad;
1494
case SSL_ERROR_WANT_READ:
1495
/* ssl wants to read, read event always active */
1496
sudo_debug_printf(SUDO_DEBUG_NOTICE|SUDO_DEBUG_LINENO,
1497
"SSL_write_ex returns SSL_ERROR_WANT_READ");
1498
/* Redirect read event to finish SSL_write_ex() */
1499
closure->write_instead_of_read = true;
1500
debug_return;
1501
case SSL_ERROR_WANT_WRITE:
1502
/* ssl wants to write more, write event remains active */
1503
sudo_debug_printf(SUDO_DEBUG_NOTICE|SUDO_DEBUG_LINENO,
1504
"SSL_write_ex returns SSL_ERROR_WANT_WRITE");
1505
debug_return;
1506
case SSL_ERROR_SYSCALL:
1507
sudo_warn("SSL_write_ex");
1508
goto bad;
1509
default:
1510
errstr = ERR_reason_error_string(ERR_get_error());
1511
sudo_warnx("SSL_write_ex: %s",
1512
errstr ? errstr : strerror(errno));
1513
goto bad;
1514
}
1515
}
1516
} else
1517
#endif
1518
{
1519
const ssize_t n = write(fd, buf->data + buf->off, buf->len - buf->off);
1520
if (n < 0) {
1521
if (errno == EAGAIN || errno == EINTR)
1522
debug_return;
1523
sudo_warn("write");
1524
goto bad;
1525
}
1526
nwritten = (size_t)n;
1527
}
1528
if (nwritten > SIZE_MAX - buf->off) {
1529
sudo_warnx(U_("internal error, %s overflow"), __func__);
1530
goto bad;
1531
}
1532
buf->off += nwritten;
1533
1534
if (buf->off == buf->len) {
1535
/* sent entire message */
1536
sudo_debug_printf(SUDO_DEBUG_INFO,
1537
"%s: finished sending %zu bytes to server", __func__, buf->len);
1538
buf->off = 0;
1539
buf->len = 0;
1540
TAILQ_REMOVE(&closure->write_bufs, buf, entries);
1541
TAILQ_INSERT_TAIL(&closure->free_bufs, buf, entries);
1542
if (TAILQ_EMPTY(&closure->write_bufs)) {
1543
/* Write queue empty, check state. */
1544
if (!client_message_completion(closure))
1545
goto bad;
1546
}
1547
}
1548
debug_return;
1549
1550
bad:
1551
sudo_ev_del(closure->evbase, closure->read_ev);
1552
sudo_ev_del(closure->evbase, closure->write_ev);
1553
debug_return;
1554
}
1555
1556
/*
1557
* Parse a timespec on the command line of the form
1558
* seconds[,nanoseconds]
1559
*/
1560
static bool
1561
parse_timespec(struct timespec *ts, char *strval)
1562
{
1563
const char *errstr;
1564
char *nsecstr;
1565
debug_decl(parse_timespec, SUDO_DEBUG_UTIL);
1566
1567
if ((nsecstr = strchr(strval, ',')) != NULL)
1568
*nsecstr++ = '\0';
1569
1570
ts->tv_nsec = 0;
1571
ts->tv_sec = (time_t)sudo_strtonum(strval, 0, TIME_T_MAX, &errstr);
1572
if (errstr != NULL) {
1573
sudo_warnx(U_("%s: %s"), strval, U_(errstr));
1574
debug_return_bool(false);
1575
}
1576
1577
if (nsecstr != NULL) {
1578
ts->tv_nsec = (long)sudo_strtonum(nsecstr, 0, LONG_MAX, &errstr);
1579
if (errstr != NULL) {
1580
sudo_warnx(U_("%s: %s"), nsecstr, U_(errstr));
1581
debug_return_bool(false);
1582
}
1583
}
1584
1585
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: parsed timespec [%lld, %ld]",
1586
__func__, (long long)ts->tv_sec, ts->tv_nsec);
1587
debug_return_bool(true);
1588
}
1589
1590
/*
1591
* Free client closure contents.
1592
*/
1593
static void
1594
client_closure_free(struct client_closure *closure)
1595
{
1596
struct connection_buffer *buf;
1597
debug_decl(connection_closure_free, SUDO_DEBUG_UTIL);
1598
1599
if (closure != NULL) {
1600
TAILQ_REMOVE(&connections, closure, entries);
1601
#if defined(HAVE_OPENSSL)
1602
if (closure->tls_client.ssl != NULL) {
1603
if (SSL_shutdown(closure->tls_client.ssl) == 0)
1604
SSL_shutdown(closure->tls_client.ssl);
1605
SSL_free(closure->tls_client.ssl);
1606
}
1607
sudo_ev_free(closure->tls_client.tls_connect_ev);
1608
#endif
1609
sudo_ev_free(closure->read_ev);
1610
sudo_ev_free(closure->write_ev);
1611
free(closure->read_buf.data);
1612
free(closure->buf);
1613
while ((buf = TAILQ_FIRST(&closure->write_bufs)) != NULL) {
1614
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
1615
"discarding write buffer %p, len %zu", buf, buf->len - buf->off);
1616
TAILQ_REMOVE(&closure->write_bufs, buf, entries);
1617
free(buf->data);
1618
free(buf);
1619
}
1620
while ((buf = TAILQ_FIRST(&closure->free_bufs)) != NULL) {
1621
TAILQ_REMOVE(&closure->free_bufs, buf, entries);
1622
free(buf->data);
1623
free(buf);
1624
}
1625
shutdown(closure->sock, SHUT_RDWR);
1626
close(closure->sock);
1627
free(closure);
1628
}
1629
1630
debug_return;
1631
}
1632
1633
/*
1634
* Initialize a new client closure
1635
*/
1636
static struct client_closure *
1637
client_closure_alloc(int sock, struct sudo_event_base *base,
1638
struct timespec *restart, struct timespec *stop_after, const char *iolog_id,
1639
char *reject_reason, bool accept_only, struct eventlog *evlog)
1640
{
1641
struct connection_buffer *buf;
1642
struct client_closure *closure;
1643
debug_decl(client_closure_alloc, SUDO_DEBUG_UTIL);
1644
1645
if ((closure = calloc(1, sizeof(*closure))) == NULL)
1646
debug_return_ptr(NULL);
1647
1648
closure->sock = sock;
1649
closure->evbase = base;
1650
TAILQ_INIT(&closure->write_bufs);
1651
TAILQ_INIT(&closure->free_bufs);
1652
1653
TAILQ_INSERT_TAIL(&connections, closure, entries);
1654
1655
closure->state = RECV_HELLO;
1656
closure->accept_only = accept_only;
1657
closure->reject_reason = reject_reason;
1658
closure->evlog = evlog;
1659
1660
closure->restart.tv_sec = restart->tv_sec;
1661
closure->restart.tv_nsec = restart->tv_nsec;
1662
closure->stop_after.tv_sec = stop_after->tv_sec;
1663
closure->stop_after.tv_nsec = stop_after->tv_nsec;
1664
1665
closure->iolog_id = iolog_id;
1666
1667
closure->read_buf.size = 8 * 1024;
1668
closure->read_buf.data = malloc(closure->read_buf.size);
1669
if (closure->read_buf.data == NULL)
1670
goto bad;
1671
1672
closure->read_ev = sudo_ev_alloc(sock, SUDO_EV_READ|SUDO_EV_PERSIST,
1673
server_msg_cb, closure);
1674
if (closure->read_ev == NULL)
1675
goto bad;
1676
1677
buf = get_free_buf(64 * 1024, closure);
1678
if (buf == NULL)
1679
goto bad;
1680
TAILQ_INSERT_TAIL(&closure->free_bufs, buf, entries);
1681
1682
closure->write_ev = sudo_ev_alloc(sock, SUDO_EV_WRITE|SUDO_EV_PERSIST,
1683
client_msg_cb, closure);
1684
if (closure->write_ev == NULL)
1685
goto bad;
1686
1687
#if defined(HAVE_OPENSSL)
1688
if (cert != NULL) {
1689
closure->tls_client.tls_connect_ev = sudo_ev_alloc(sock, SUDO_EV_WRITE,
1690
tls_connect_cb, &closure->tls_client);
1691
if (closure->tls_client.tls_connect_ev == NULL)
1692
goto bad;
1693
closure->tls_client.evbase = base;
1694
closure->tls_client.parent_closure = closure;
1695
closure->tls_client.peer_name = &server_info;
1696
closure->tls_client.connect_timeout.tv_sec = TLS_HANDSHAKE_TIMEO_SEC;
1697
closure->tls_client.start_fn = tls_start_fn;
1698
closure->tls_client.connect_error_fn = tls_connect_error_fn;
1699
}
1700
#endif
1701
1702
debug_return_ptr(closure);
1703
bad:
1704
client_closure_free(closure);
1705
debug_return_ptr(NULL);
1706
}
1707
1708
#if defined(HAVE_OPENSSL)
1709
static const char short_opts[] = "Ah:i:np:r:R:s:t:b:c:k:V";
1710
#else
1711
static const char short_opts[] = "Ah:i:Ip:r:R:t:s:V";
1712
#endif
1713
static struct option long_opts[] = {
1714
{ "accept", no_argument, NULL, 'A' },
1715
{ "help", no_argument, NULL, 1 },
1716
{ "host", required_argument, NULL, 'h' },
1717
{ "iolog-id", required_argument, NULL, 'i' },
1718
{ "port", required_argument, NULL, 'p' },
1719
{ "restart", required_argument, NULL, 'r' },
1720
{ "reject", required_argument, NULL, 'R' },
1721
{ "stop-after", required_argument, NULL, 's' },
1722
{ "test", optional_argument, NULL, 't' },
1723
#if defined(HAVE_OPENSSL)
1724
{ "ca-bundle", required_argument, NULL, 'b' },
1725
{ "cert", required_argument, NULL, 'c' },
1726
{ "key", required_argument, NULL, 'k' },
1727
{ "no-verify", no_argument, NULL, 'n' },
1728
#endif
1729
{ "version", no_argument, NULL, 'V' },
1730
{ NULL, no_argument, NULL, 0 },
1731
};
1732
1733
sudo_dso_public int main(int argc, char *argv[]);
1734
1735
int
1736
main(int argc, char *argv[])
1737
{
1738
struct client_closure *closure = NULL;
1739
struct sudo_event_base *evbase;
1740
struct eventlog *evlog;
1741
const char *port = NULL;
1742
struct timespec restart = { 0, 0 };
1743
struct timespec stop_after = { 0, 0 };
1744
bool accept_only = false;
1745
char *reject_reason = NULL;
1746
const char *iolog_id = NULL;
1747
const char *errstr;
1748
int ch, sock, iolog_dir_fd, finished;
1749
debug_decl_vars(main, SUDO_DEBUG_MAIN);
1750
1751
#if defined(SUDO_DEVEL) && defined(__OpenBSD__)
1752
{
1753
extern char *malloc_options;
1754
malloc_options = "S";
1755
}
1756
#endif
1757
1758
signal(SIGPIPE, SIG_IGN);
1759
1760
initprogname(argc > 0 ? argv[0] : "sudo_sendlog");
1761
setlocale(LC_ALL, "");
1762
bindtextdomain("sudo", LOCALEDIR); /* XXX - add logsrvd domain */
1763
textdomain("sudo");
1764
1765
/* Read sudo.conf and initialize the debug subsystem. */
1766
if (sudo_conf_read(NULL, SUDO_CONF_DEBUG) == -1)
1767
return EXIT_FAILURE;
1768
sudo_debug_register(getprogname(), NULL, NULL,
1769
sudo_conf_debug_files(getprogname()), -1);
1770
1771
if (protobuf_c_version_number() < 1003000)
1772
sudo_fatalx("%s", U_("Protobuf-C version 1.3 or higher required"));
1773
1774
while ((ch = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
1775
switch (ch) {
1776
case 'A':
1777
accept_only = true;
1778
break;
1779
case 'h':
1780
server_info.name = optarg;
1781
break;
1782
case 'i':
1783
iolog_id = optarg;
1784
break;
1785
case 'p':
1786
port = optarg;
1787
break;
1788
case 'R':
1789
reject_reason = optarg;
1790
break;
1791
case 'r':
1792
if (!parse_timespec(&restart, optarg))
1793
goto bad;
1794
break;
1795
case 's':
1796
if (!parse_timespec(&stop_after, optarg))
1797
goto bad;
1798
break;
1799
case 't':
1800
nr_of_conns = (int)sudo_strtonum(optarg, 1, INT_MAX, &errstr);
1801
if (errstr != NULL) {
1802
sudo_warnx(U_("%s: %s"), optarg, U_(errstr));
1803
goto bad;
1804
}
1805
testrun = true;
1806
break;
1807
case 1:
1808
help();
1809
/* NOTREACHED */
1810
#if defined(HAVE_OPENSSL)
1811
case 'b':
1812
ca_bundle = optarg;
1813
break;
1814
case 'c':
1815
cert = optarg;
1816
break;
1817
case 'k':
1818
key = optarg;
1819
break;
1820
case 'n':
1821
verify_server = false;
1822
break;
1823
#endif
1824
case 'V':
1825
(void)printf(_("%s version %s\n"), getprogname(),
1826
PACKAGE_VERSION);
1827
return 0;
1828
default:
1829
usage();
1830
/* NOTREACHED */
1831
}
1832
}
1833
argc -= optind;
1834
argv += optind;
1835
1836
#if defined(HAVE_OPENSSL)
1837
/* if no key file is given explicitly, try to load the key from the cert */
1838
if (cert != NULL) {
1839
if (key == NULL)
1840
key = cert;
1841
if (port == NULL)
1842
port = DEFAULT_PORT_TLS;
1843
}
1844
#endif
1845
if (port == NULL)
1846
port = DEFAULT_PORT;
1847
1848
if (sudo_timespecisset(&restart) != (iolog_id != NULL)) {
1849
sudo_warnx("%s", U_("both restart point and iolog ID must be specified"));
1850
usage();
1851
}
1852
if (sudo_timespecisset(&restart) && (accept_only || reject_reason)) {
1853
sudo_warnx("%s", U_("a restart point may not be set when no I/O is sent"));
1854
usage();
1855
}
1856
1857
/* Remaining arg should be to I/O log dir to send. */
1858
if (argc != 1)
1859
usage();
1860
iolog_dir = argv[0];
1861
if ((iolog_dir_fd = open(iolog_dir, O_RDONLY|O_DIRECTORY|O_NOFOLLOW)) == -1) {
1862
sudo_warn("%s", iolog_dir);
1863
goto bad;
1864
}
1865
1866
/* Parse I/O log info file. */
1867
if ((evlog = iolog_parse_loginfo(iolog_dir_fd, iolog_dir)) == NULL)
1868
goto bad;
1869
1870
if ((evbase = sudo_ev_base_alloc()) == NULL)
1871
sudo_fatal(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1872
1873
if (testrun)
1874
printf("connecting clients...\n");
1875
1876
for (int i = 0; i < nr_of_conns; i++) {
1877
sock = connect_server(&server_info, port);
1878
if (sock == -1)
1879
goto bad;
1880
1881
if (!testrun)
1882
printf("Connected to %s:%s\n", server_info.name, port);
1883
1884
closure = client_closure_alloc(sock, evbase, &restart, &stop_after,
1885
iolog_id, reject_reason, accept_only, evlog);
1886
if (closure == NULL)
1887
goto bad;
1888
1889
/* Open the I/O log files and seek to restart point if there is one. */
1890
if (!iolog_open_all(iolog_dir_fd, iolog_dir, closure->iolog_files, "r"))
1891
goto bad;
1892
if (sudo_timespecisset(&closure->restart)) {
1893
if (!iolog_seekto(iolog_dir_fd, iolog_dir, closure->iolog_files,
1894
&closure->elapsed, &closure->restart))
1895
goto bad;
1896
}
1897
1898
#if defined(HAVE_OPENSSL)
1899
if (cert != NULL) {
1900
if (!tls_client_setup(closure->sock, ca_bundle, cert, key, NULL,
1901
NULL, NULL, true, verify_server, &closure->tls_client))
1902
goto bad;
1903
} else
1904
#endif
1905
{
1906
/* No TLS, send ClientHello */
1907
if (!fmt_client_hello(closure))
1908
goto bad;
1909
}
1910
}
1911
1912
if (testrun)
1913
puts("sending logs...");
1914
1915
struct timespec t_start, t_end, t_result;
1916
sudo_gettime_real(&t_start);
1917
1918
sudo_ev_dispatch(evbase);
1919
sudo_ev_base_free(evbase);
1920
1921
sudo_gettime_real(&t_end);
1922
sudo_timespecsub(&t_end, &t_start, &t_result);
1923
1924
finished = 0;
1925
while ((closure = TAILQ_FIRST(&connections)) != NULL) {
1926
if (closure->state == FINISHED) {
1927
finished++;
1928
} else {
1929
sudo_warnx(U_("exited prematurely with state %d"), closure->state);
1930
sudo_warnx(U_("elapsed time sent to server [%lld, %ld]"),
1931
(long long)closure->elapsed.tv_sec, closure->elapsed.tv_nsec);
1932
sudo_warnx(U_("commit point received from server [%lld, %ld]"),
1933
(long long)closure->committed.tv_sec, closure->committed.tv_nsec);
1934
}
1935
client_closure_free(closure);
1936
}
1937
eventlog_free(evlog);
1938
#if defined(HAVE_OPENSSL)
1939
SSL_CTX_free(ssl_ctx);
1940
#endif
1941
1942
if (finished != 0) {
1943
printf("%d I/O log%s transmitted successfully in %lld.%.9ld seconds\n",
1944
finished, nr_of_conns > 1 ? "s" : "",
1945
(long long)t_result.tv_sec, t_result.tv_nsec);
1946
debug_return_int(EXIT_SUCCESS);
1947
}
1948
1949
bad:
1950
debug_return_int(EXIT_FAILURE);
1951
}
1952
1953