Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/lib/iolog/iolog_loginfo.c
3875 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2009-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 <stdio.h>
22
#include <stdlib.h>
23
#ifdef HAVE_STDBOOL_H
24
# include <stdbool.h>
25
#else
26
# include <compat/stdbool.h>
27
#endif /* HAVE_STDBOOL_H */
28
#include <string.h>
29
#include <unistd.h>
30
#include <fcntl.h>
31
32
#include <sudo_compat.h>
33
#include <sudo_debug.h>
34
#include <sudo_eventlog.h>
35
#include <sudo_fatal.h>
36
#include <sudo_gettext.h>
37
#include <sudo_json.h>
38
#include <sudo_iolog.h>
39
#include <sudo_util.h>
40
41
struct eventlog *
42
iolog_parse_loginfo(int dfd, const char *iolog_dir)
43
{
44
struct eventlog *evlog = NULL;
45
FILE *fp = NULL;
46
int fd = -1;
47
int tmpfd = -1;
48
bool ok, legacy = false;
49
debug_decl(iolog_parse_loginfo, SUDO_DEBUG_UTIL);
50
51
if (dfd == -1) {
52
if ((tmpfd = open(iolog_dir, O_RDONLY|O_DIRECTORY|O_NOFOLLOW)) == -1) {
53
sudo_warn("%s", iolog_dir);
54
goto bad;
55
}
56
dfd = tmpfd;
57
}
58
if ((fd = openat(dfd, "log.json", O_RDONLY|O_NOFOLLOW, 0)) == -1) {
59
fd = openat(dfd, "log", O_RDONLY|O_NOFOLLOW, 0);
60
legacy = true;
61
}
62
if (tmpfd != -1)
63
close(tmpfd);
64
if (fd == -1 || (fp = fdopen(fd, "r")) == NULL) {
65
sudo_warn("%s/log", iolog_dir);
66
goto bad;
67
}
68
fd = -1;
69
70
if ((evlog = calloc(1, sizeof(*evlog))) == NULL) {
71
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
72
goto bad;
73
}
74
evlog->runuid = (uid_t)-1;
75
evlog->rungid = (gid_t)-1;
76
evlog->exit_value = -1;
77
78
ok = legacy ? iolog_parse_loginfo_legacy(fp, iolog_dir, evlog) :
79
iolog_parse_loginfo_json(fp, iolog_dir, evlog);
80
if (ok) {
81
fclose(fp);
82
debug_return_ptr(evlog);
83
}
84
85
bad:
86
if (fd != -1)
87
close(fd);
88
if (fp != NULL)
89
fclose(fp);
90
eventlog_free(evlog);
91
debug_return_ptr(NULL);
92
}
93
94
/*
95
* Write the legacy I/O log file that contains the user and command info.
96
* This file is not compressed.
97
*/
98
static bool
99
iolog_write_info_file_legacy(int dfd, struct eventlog *evlog)
100
{
101
char * const *av;
102
FILE *fp;
103
int error, fd;
104
debug_decl(iolog_info_write_log, SUDO_DEBUG_UTIL);
105
106
fd = iolog_openat(dfd, "log", O_CREAT|O_TRUNC|O_WRONLY|O_NOFOLLOW);
107
if (fd == -1 || (fp = fdopen(fd, "w")) == NULL) {
108
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
109
"unable to %sopen %s/log", fd == -1 ? "" : "fd", evlog->iolog_path);
110
if (fd != -1)
111
close(fd);
112
debug_return_bool(false);
113
}
114
if (fchown(fd, iolog_get_uid(), iolog_get_gid()) != 0) {
115
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
116
"%s: unable to fchown %d:%d %s/log", __func__,
117
(int)iolog_get_uid(), (int)iolog_get_gid(), evlog->iolog_path);
118
}
119
120
fprintf(fp, "%lld:%s:%s:%s:%s:%d:%d\n%s\n",
121
(long long)evlog->event_time.tv_sec,
122
evlog->submituser ? evlog->submituser : "unknown",
123
evlog->runuser ? evlog->runuser : RUNAS_DEFAULT,
124
evlog->rungroup ? evlog->rungroup : "",
125
evlog->ttyname ? evlog->ttyname : "unknown",
126
evlog->lines, evlog->columns,
127
evlog->cwd ? evlog->cwd : "unknown");
128
fputs(evlog->command ? evlog->command : "unknown", fp);
129
for (av = evlog->runargv + 1; *av != NULL; av++) {
130
fputc(' ', fp);
131
fputs(*av, fp);
132
}
133
fputc('\n', fp);
134
fflush(fp);
135
if ((error = ferror(fp))) {
136
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
137
"unable to write to I/O log file %s/log", evlog->iolog_path);
138
}
139
fclose(fp);
140
141
debug_return_bool(!error);
142
}
143
144
/*
145
* Write the "log.json" file that contains the user and command info.
146
* This file is not compressed.
147
*/
148
static bool
149
iolog_write_info_file_json(int dfd, struct eventlog *evlog)
150
{
151
struct json_container jsonc;
152
struct json_value json_value;
153
bool ret = false;
154
FILE *fp = NULL;
155
int fd = -1;
156
debug_decl(iolog_write_info_file_json, SUDO_DEBUG_UTIL);
157
158
if (!sudo_json_init(&jsonc, 4, false, false, false))
159
debug_return_bool(false);
160
161
/* Timestamp */
162
if (!sudo_json_open_object(&jsonc, "timestamp"))
163
goto oom;
164
165
json_value.type = JSON_NUMBER;
166
json_value.u.number = evlog->event_time.tv_sec;
167
if (!sudo_json_add_value(&jsonc, "seconds", &json_value))
168
goto oom;
169
170
json_value.type = JSON_NUMBER;
171
json_value.u.number = evlog->event_time.tv_nsec;
172
if (!sudo_json_add_value(&jsonc, "nanoseconds", &json_value))
173
goto oom;
174
175
if (!sudo_json_close_object(&jsonc))
176
goto oom;
177
178
if (!eventlog_store_json(&jsonc, evlog))
179
goto done;
180
181
fd = iolog_openat(dfd, "log.json", O_CREAT|O_TRUNC|O_WRONLY|O_NOFOLLOW);
182
if (fd == -1 || (fp = fdopen(fd, "w")) == NULL) {
183
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
184
"unable to %sopen %s/log.json", fd == -1 ? "" : "fd",
185
evlog->iolog_path);
186
goto done;
187
}
188
if (fchown(fd, iolog_get_uid(), iolog_get_gid()) != 0) {
189
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
190
"%s: unable to fchown %d:%d %s/log.json", __func__,
191
(int)iolog_get_uid(), (int)iolog_get_gid(), evlog->iolog_path);
192
}
193
fd = -1;
194
195
fprintf(fp, "{%s\n}\n", sudo_json_get_buf(&jsonc));
196
fflush(fp);
197
if (ferror(fp)) {
198
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
199
"unable to write to I/O log file %s/log.json", evlog->iolog_path);
200
goto done;
201
}
202
203
ret = true;
204
goto done;
205
206
oom:
207
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
208
done:
209
sudo_json_free(&jsonc);
210
if (fp != NULL)
211
fclose(fp);
212
if (fd != -1)
213
close(fd);
214
215
debug_return_bool(ret);
216
}
217
218
/*
219
* Write the I/O log and log.json files that contain user and command info.
220
* These files are not compressed.
221
*/
222
bool
223
iolog_write_info_file(int dfd, struct eventlog *evlog)
224
{
225
debug_decl(iolog_write_info_file, SUDO_DEBUG_UTIL);
226
227
if (!iolog_write_info_file_legacy(dfd, evlog))
228
debug_return_bool(false);
229
if (!iolog_write_info_file_json(dfd, evlog))
230
debug_return_bool(false);
231
232
debug_return_bool(true);
233
}
234
235