Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/logsrvd/logsrvd_queue.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2021-2025 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 <netinet/in.h>
25
#include <netinet/tcp.h>
26
#include <arpa/inet.h>
27
28
#include <errno.h>
29
#include <fcntl.h>
30
#include <limits.h>
31
#ifdef HAVE_STDBOOL_H
32
# include <stdbool.h>
33
#else
34
# include <compat/stdbool.h>
35
#endif /* HAVE_STDBOOL_H */
36
#if defined(HAVE_STDINT_H)
37
# include <stdint.h>
38
#elif defined(HAVE_INTTYPES_H)
39
# include <inttypes.h>
40
#endif
41
#include <stdio.h>
42
#include <stdlib.h>
43
#include <string.h>
44
#include <dirent.h>
45
#include <time.h>
46
#include <unistd.h>
47
48
#include <sudo_compat.h>
49
#include <sudo_conf.h>
50
#include <sudo_debug.h>
51
#include <sudo_event.h>
52
#include <sudo_eventlog.h>
53
#include <sudo_fatal.h>
54
#include <sudo_gettext.h>
55
#include <sudo_iolog.h>
56
#include <sudo_queue.h>
57
#include <sudo_util.h>
58
59
#include <logsrvd.h>
60
61
/*
62
* Queue of finished journal files to be relayed.
63
*/
64
struct outgoing_journal {
65
TAILQ_ENTRY(outgoing_journal) entries;
66
char *journal_path;
67
};
68
TAILQ_HEAD(outgoing_journal_queue, outgoing_journal);
69
70
static struct outgoing_journal_queue outgoing_journal_queue =
71
TAILQ_HEAD_INITIALIZER(outgoing_journal_queue);
72
73
static struct sudo_event *outgoing_queue_event;
74
75
/*
76
* Callback that runs when the outgoing queue retry timer fires.
77
* Tries to relay the first entry in the outgoing queue.
78
*/
79
static void
80
outgoing_queue_cb(int unused, int what, void *v)
81
{
82
struct connection_closure *closure;
83
struct outgoing_journal *oj, *next;
84
struct sudo_event_base *evbase = v;
85
bool success = false;
86
debug_decl(outgoing_queue_cb, SUDO_DEBUG_UTIL);
87
88
/* Must have at least one relay server. */
89
if (TAILQ_EMPTY(logsrvd_conf_relay_address()))
90
debug_return;
91
92
/* Process first journal. */
93
TAILQ_FOREACH_SAFE(oj, &outgoing_journal_queue, entries, next) {
94
FILE *fp;
95
int fd;
96
97
fd = open(oj->journal_path, O_RDWR);
98
if (fd == -1) {
99
if (errno == ENOENT) {
100
TAILQ_REMOVE(&outgoing_journal_queue, oj, entries);
101
free(oj->journal_path);
102
free(oj);
103
}
104
continue;
105
}
106
if (!sudo_lock_file(fd, SUDO_TLOCK)) {
107
sudo_warn(U_("unable to lock %s"), oj->journal_path);
108
close(fd);
109
continue;
110
}
111
fp = fdopen(fd, "r");
112
if (fp == NULL) {
113
sudo_warn(U_("unable to open %s"), oj->journal_path);
114
close(fd);
115
break;
116
}
117
118
/* Allocate a connection closure and fill in journal vars. */
119
closure = connection_closure_alloc(fd, false, true, evbase);
120
if (closure == NULL) {
121
fclose(fp);
122
break;
123
}
124
closure->journal = fp;
125
closure->journal_path = oj->journal_path;
126
127
/* Done with oj now, closure owns journal_path. */
128
TAILQ_REMOVE(&outgoing_journal_queue, oj, entries);
129
free(oj);
130
131
success = connect_relay(closure);
132
if (!success) {
133
sudo_warnx("%s", U_("unable to connect to relay"));
134
connection_close(closure);
135
}
136
break;
137
}
138
}
139
140
/*
141
* Schedule the outgoing_queue_event, creating it as necessary.
142
* The event will fire after the specified timeout elapses.
143
*/
144
bool
145
logsrvd_queue_enable(time_t timeout, struct sudo_event_base *evbase)
146
{
147
debug_decl(logsrvd_queue_enable, SUDO_DEBUG_UTIL);
148
149
if (!TAILQ_EMPTY(&outgoing_journal_queue)) {
150
struct timespec tv = { timeout, 0 };
151
152
if (outgoing_queue_event == NULL) {
153
outgoing_queue_event = sudo_ev_alloc(-1, SUDO_EV_TIMEOUT,
154
outgoing_queue_cb, evbase);
155
if (outgoing_queue_event == NULL) {
156
sudo_warnx(U_("%s: %s"), __func__,
157
U_("unable to allocate memory"));
158
debug_return_bool(false);
159
}
160
}
161
if (sudo_ev_add(evbase, outgoing_queue_event, &tv, false) == -1) {
162
sudo_warnx("%s", U_("unable to add event to queue"));
163
debug_return_bool(false);
164
}
165
}
166
debug_return_bool(true);
167
}
168
169
/*
170
* Allocate a queue item based on the connection and push it on
171
* the outgoing queue.
172
* Consumes journal_path from the closure.
173
*/
174
bool
175
logsrvd_queue_insert(struct connection_closure *closure)
176
{
177
struct outgoing_journal *oj;
178
debug_decl(logsrvd_queue_insert, SUDO_DEBUG_UTIL);
179
180
if (closure->journal_path == NULL) {
181
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
182
"missing journal_path for closure %p", closure);
183
debug_return_bool(false);
184
}
185
186
if ((oj = malloc(sizeof(*oj))) == NULL) {
187
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
188
debug_return_bool(false);
189
}
190
oj->journal_path = closure->journal_path;
191
closure->journal_path = NULL;
192
TAILQ_INSERT_TAIL(&outgoing_journal_queue, oj, entries);
193
194
if (!logsrvd_queue_enable(logsrvd_conf_relay_retry_interval(),
195
closure->evbase))
196
debug_return_bool(false);
197
198
debug_return_bool(true);
199
}
200
201
/*
202
* Scan the outgoing queue at startup and populate the
203
* outgoing_journal_queue.
204
*/
205
bool
206
logsrvd_queue_scan(struct sudo_event_base *evbase)
207
{
208
const char uuid_template[] = "123e4567-e89b-12d3-a456-426655440000";
209
char path[PATH_MAX];
210
struct dirent *dent;
211
int dirlen;
212
DIR *dirp;
213
debug_decl(logsrvd_queue_scan, SUDO_DEBUG_UTIL);
214
215
/* Must have at least one relay server. */
216
if (TAILQ_EMPTY(logsrvd_conf_relay_address()))
217
debug_return_bool(true);
218
219
dirlen = snprintf(path, sizeof(path), "%s/outgoing/%s",
220
logsrvd_conf_relay_dir(), uuid_template);
221
if (dirlen < ssizeof(uuid_template) || dirlen >= ssizeof(path)) {
222
errno = ENAMETOOLONG;
223
sudo_warn("%s/outgoing/%s", logsrvd_conf_relay_dir(), uuid_template);
224
debug_return_bool(false);
225
}
226
dirlen -= (int)sizeof(uuid_template) - 1;
227
path[dirlen] = '\0';
228
229
dirp = opendir(path);
230
if (dirp == NULL) {
231
sudo_warn("opendir %s", path);
232
debug_return_bool(false);
233
}
234
while ((dent = readdir(dirp)) != NULL) {
235
unsigned char uuid[16];
236
struct outgoing_journal *oj;
237
238
/* Skip anything that is not a uuid. */
239
if (sudo_uuid_from_string(dent->d_name, uuid) != 0)
240
continue;
241
242
/* Add to queue. */
243
path[dirlen] = '\0';
244
if (strlcat(path, dent->d_name, sizeof(path)) >= sizeof(path))
245
continue;
246
if ((oj = malloc(sizeof(*oj))) == NULL)
247
goto oom;
248
if ((oj->journal_path = strdup(path)) == NULL) {
249
free(oj);
250
goto oom;
251
}
252
TAILQ_INSERT_TAIL(&outgoing_journal_queue, oj, entries);
253
}
254
closedir(dirp);
255
256
/* Process the queue immediately. */
257
if (!logsrvd_queue_enable(0, evbase))
258
debug_return_bool(false);
259
260
debug_return_bool(true);
261
oom:
262
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
263
closedir(dirp);
264
debug_return_bool(false);
265
}
266
267
/*
268
* Dump outgoing queue in response to SIGUSR1.
269
*/
270
void
271
logsrvd_queue_dump(void)
272
{
273
struct outgoing_journal *oj;
274
debug_decl(logsrvd_queue_dump, SUDO_DEBUG_UTIL);
275
276
if (TAILQ_EMPTY(&outgoing_journal_queue))
277
debug_return;
278
279
sudo_debug_printf(SUDO_DEBUG_INFO, "outgoing journal queue:");
280
TAILQ_FOREACH(oj, &outgoing_journal_queue, entries) {
281
sudo_debug_printf(SUDO_DEBUG_INFO, " %s", oj->journal_path);
282
}
283
}
284
285