Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/src/utmp.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2011-2018 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/time.h>
22
#include <sys/wait.h>
23
#include <string.h>
24
#include <unistd.h>
25
#include <time.h>
26
#if defined(HAVE_UTMPS_H)
27
# include <utmps.h>
28
#elif defined(HAVE_UTMPX_H)
29
# include <utmpx.h>
30
#else
31
# include <utmp.h>
32
#endif /* HAVE_UTMPS_H */
33
#ifdef HAVE_GETTTYENT
34
# include <ttyent.h>
35
#endif
36
#include <fcntl.h>
37
#include <signal.h>
38
39
#include <sudo.h>
40
#include <sudo_exec.h>
41
42
/*
43
* GCC 8 warns about strncpy() where the size field is the size of the buffer.
44
* However, strings in utmp may not be NUL terminated so this usage is correct.
45
*/
46
#if __GNUC_PREREQ__(8, 0)
47
# pragma GCC diagnostic ignored "-Wstringop-truncation"
48
#endif
49
50
/*
51
* Simplify handling of different utmp types.
52
*/
53
#if defined(HAVE_GETUTSID)
54
# define sudo_getutline(u) GETUTSLINE(u)
55
# define sudo_pututline(u) PUTUTSLINE(u)
56
# define sudo_setutent() SETUTSENT()
57
# define sudo_endutent() ENDUTSENT()
58
#elif defined(HAVE_GETUTXID)
59
# define sudo_getutline(u) getutxline(u)
60
# define sudo_pututline(u) pututxline(u)
61
# define sudo_setutent() setutxent()
62
# define sudo_endutent() endutxent()
63
#elif defined(HAVE_GETUTID)
64
# define sudo_getutline(u) getutline(u)
65
# define sudo_pututline(u) pututline(u)
66
# define sudo_setutent() setutent()
67
# define sudo_endutent() endutent()
68
#endif
69
70
#if defined(HAVE_GETUTSID)
71
typedef struct utmps sudo_utmp_t;
72
#elif defined(HAVE_GETUTXID)
73
typedef struct utmpx sudo_utmp_t;
74
#else
75
typedef struct utmp sudo_utmp_t;
76
/* Older systems have ut_name, not ut_user */
77
# if !defined(HAVE_STRUCT_UTMP_UT_USER) && !defined(ut_user)
78
# define ut_user ut_name
79
# endif
80
#endif
81
82
/* HP-UX has __e_termination and __e_exit, others may lack the __ */
83
#if defined(HAVE_STRUCT_UTMP_UT_EXIT_E_TERMINATION)
84
# undef __e_termination
85
# define __e_termination e_termination
86
# undef __e_exit
87
# define __e_exit e_exit
88
#endif
89
90
#if defined(HAVE_STRUCT_UTMP_UT_ID)
91
/*
92
* Create ut_id from the new ut_line and the old ut_id.
93
*/
94
static void
95
utmp_setid(sudo_utmp_t *old, sudo_utmp_t *new)
96
{
97
const char *line = new->ut_line;
98
size_t linelen = strnlen(new->ut_line, sizeof(new->ut_line));
99
debug_decl(utmp_setid, SUDO_DEBUG_UTMP);
100
101
/* Skip over "tty" in the id if old entry did too. */
102
if (old != NULL) {
103
/* cppcheck-suppress uninitdata */
104
if (strncmp(line, "tty", 3) == 0) {
105
const size_t idlen = MIN(sizeof(old->ut_id), 3);
106
if (strncmp(old->ut_id, "tty", idlen) != 0) {
107
line += 3;
108
linelen -= 3;
109
}
110
}
111
}
112
113
/* Store as much as will fit, skipping parts of the beginning as needed. */
114
/* cppcheck-suppress uninitdata */
115
if (linelen > sizeof(new->ut_id)) {
116
line += linelen - sizeof(new->ut_id);
117
linelen = sizeof(new->ut_id);
118
}
119
strncpy(new->ut_id, line, linelen);
120
121
debug_return;
122
}
123
#endif /* HAVE_STRUCT_UTMP_UT_ID */
124
125
/*
126
* Store time in utmp structure.
127
*/
128
static void
129
utmp_settime(sudo_utmp_t *ut)
130
{
131
struct timeval tv;
132
debug_decl(utmp_settime, SUDO_DEBUG_UTMP);
133
134
if (gettimeofday(&tv, NULL) == 0) {
135
#if defined(HAVE_STRUCT_UTMP_UT_TV)
136
ut->ut_tv.tv_sec = tv.tv_sec;
137
ut->ut_tv.tv_usec = tv.tv_usec;
138
#else
139
ut->ut_time = tv.tv_sec;
140
#endif
141
}
142
143
debug_return;
144
}
145
146
/*
147
* Fill in a utmp entry, using an old entry as a template if there is one.
148
*/
149
static void
150
utmp_fill(const char *line, const char *user, sudo_utmp_t *ut_old,
151
sudo_utmp_t *ut_new)
152
{
153
debug_decl(utmp_file, SUDO_DEBUG_UTMP);
154
155
if (ut_old == NULL) {
156
memset(ut_new, 0, sizeof(*ut_new));
157
} else if (ut_old != ut_new) {
158
memcpy(ut_new, ut_old, sizeof(*ut_new));
159
}
160
strncpy(ut_new->ut_user, user, sizeof(ut_new->ut_user));
161
strncpy(ut_new->ut_line, line, sizeof(ut_new->ut_line));
162
#if defined(HAVE_STRUCT_UTMP_UT_ID)
163
utmp_setid(ut_old, ut_new);
164
#endif
165
#if defined(HAVE_STRUCT_UTMP_UT_PID)
166
ut_new->ut_pid = getpid();
167
#endif
168
utmp_settime(ut_new);
169
#if defined(HAVE_STRUCT_UTMP_UT_TYPE)
170
ut_new->ut_type = USER_PROCESS;
171
#endif
172
debug_return;
173
}
174
175
/*
176
* There are two basic utmp file types:
177
*
178
* POSIX: sequential access with new entries appended to the end.
179
* Manipulated via {get,put}[sx]?utent()
180
*
181
* Legacy: sparse file indexed by ttyslot() * sizeof(struct utmp)
182
*/
183
#if defined(HAVE_GETUTSID) || defined(HAVE_GETUTXID) || defined(HAVE_GETUTID)
184
bool
185
utmp_login(const char *from_line, const char *to_line, int ttyfd,
186
const char *user)
187
{
188
sudo_utmp_t utbuf, *ut_old = NULL;
189
bool ret = false;
190
debug_decl(utmp_login, SUDO_DEBUG_UTMP);
191
192
/* Strip off /dev/ prefix from line as needed. */
193
if (strncmp(to_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
194
to_line += sizeof(_PATH_DEV) - 1;
195
sudo_setutent();
196
if (from_line != NULL) {
197
if (strncmp(from_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
198
from_line += sizeof(_PATH_DEV) - 1;
199
200
/* Lookup old line. */
201
memset(&utbuf, 0, sizeof(utbuf));
202
strncpy(utbuf.ut_line, from_line, sizeof(utbuf.ut_line));
203
ut_old = sudo_getutline(&utbuf);
204
sudo_setutent();
205
}
206
utmp_fill(to_line, user, ut_old, &utbuf);
207
if (sudo_pututline(&utbuf) != NULL)
208
ret = true;
209
sudo_endutent();
210
211
debug_return_bool(ret);
212
}
213
214
bool
215
utmp_logout(const char *line, int status)
216
{
217
bool ret = false;
218
sudo_utmp_t *ut, utbuf;
219
debug_decl(utmp_logout, SUDO_DEBUG_UTMP);
220
221
/* Strip off /dev/ prefix from line as needed. */
222
if (strncmp(line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
223
line += sizeof(_PATH_DEV) - 1;
224
225
memset(&utbuf, 0, sizeof(utbuf));
226
strncpy(utbuf.ut_line, line, sizeof(utbuf.ut_line));
227
if ((ut = sudo_getutline(&utbuf)) != NULL) {
228
memset(ut->ut_user, 0, sizeof(ut->ut_user));
229
# if defined(HAVE_STRUCT_UTMP_UT_TYPE)
230
ut->ut_type = DEAD_PROCESS;
231
# endif
232
# if defined(HAVE_STRUCT_UTMP_UT_EXIT)
233
ut->ut_exit.__e_termination = WIFSIGNALED(status) ? WTERMSIG(status) : 0;
234
ut->ut_exit.__e_exit = WIFEXITED(status) ? WEXITSTATUS(status) : 0;
235
# endif
236
utmp_settime(ut);
237
if (sudo_pututline(ut) != NULL)
238
ret = true;
239
}
240
debug_return_bool(ret);
241
}
242
243
#else /* !HAVE_GETUTSID && !HAVE_GETUTXID && !HAVE_GETUTID */
244
245
/*
246
* Find the slot for the specified line (tty name and file descriptor).
247
* Returns a slot suitable for seeking into utmp on success or <= 0 on error.
248
* If getttyent() is available we can use that to compute the slot.
249
*/
250
# ifdef HAVE_GETTTYENT
251
static int
252
utmp_slot(const char *line, int ttyfd)
253
{
254
int slot = 1;
255
struct ttyent *tty;
256
debug_decl(utmp_slot, SUDO_DEBUG_UTMP);
257
258
setttyent();
259
while ((tty = getttyent()) != NULL) {
260
if (strcmp(line, tty->ty_name) == 0)
261
break;
262
slot++;
263
}
264
endttyent();
265
debug_return_int(tty ? slot : 0);
266
}
267
# elif defined(HAVE_TTYSLOT)
268
static int
269
utmp_slot(const char *line, int ttyfd)
270
{
271
int sfd, slot;
272
debug_decl(utmp_slot, SUDO_DEBUG_UTMP);
273
274
/*
275
* Temporarily point stdin to the tty since ttyslot()
276
* doesn't take an argument.
277
*/
278
if ((sfd = dup(STDIN_FILENO)) == -1)
279
sudo_fatal("%s", U_("unable to save stdin"));
280
if (dup2(ttyfd, STDIN_FILENO) == -1)
281
sudo_fatal("%s", U_("unable to dup2 stdin"));
282
slot = ttyslot();
283
if (dup2(sfd, STDIN_FILENO) == -1)
284
sudo_fatal("%s", U_("unable to restore stdin"));
285
close(sfd);
286
287
debug_return_int(slot);
288
}
289
# else /* !HAVE_TTYSLOT */
290
static int
291
utmp_slot(const char *line, int ttyfd)
292
{
293
return -1;
294
}
295
# endif /* HAVE_GETTTYENT */
296
297
bool
298
utmp_login(const char *from_line, const char *to_line, int ttyfd,
299
const char *user)
300
{
301
sudo_utmp_t utbuf, *ut_old = NULL;
302
bool ret = false;
303
int slot;
304
FILE *fp;
305
debug_decl(utmp_login, SUDO_DEBUG_UTMP);
306
307
/* Strip off /dev/ prefix from line as needed. */
308
if (strncmp(to_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
309
to_line += sizeof(_PATH_DEV) - 1;
310
311
/* Find slot for new entry. */
312
slot = utmp_slot(to_line, ttyfd);
313
if (slot <= 0)
314
goto done;
315
316
if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
317
goto done;
318
319
if (from_line != NULL) {
320
if (strncmp(from_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
321
from_line += sizeof(_PATH_DEV) - 1;
322
323
/* Lookup old line. */
324
while (fread(&utbuf, sizeof(utbuf), 1, fp) == 1) {
325
# ifdef HAVE_STRUCT_UTMP_UT_ID
326
if (utbuf.ut_type != LOGIN_PROCESS && utbuf.ut_type != USER_PROCESS)
327
continue;
328
# endif
329
if (utbuf.ut_user[0] &&
330
!strncmp(utbuf.ut_line, from_line, sizeof(utbuf.ut_line))) {
331
ut_old = &utbuf;
332
break;
333
}
334
}
335
}
336
utmp_fill(to_line, user, ut_old, &utbuf);
337
if (fseeko(fp, slot * (off_t)sizeof(utbuf), SEEK_SET) == 0) {
338
if (fwrite(&utbuf, sizeof(utbuf), 1, fp) == 1)
339
ret = true;
340
}
341
fclose(fp);
342
343
done:
344
debug_return_bool(ret);
345
}
346
347
bool
348
utmp_logout(const char *line, int status)
349
{
350
sudo_utmp_t utbuf;
351
bool ret = false;
352
FILE *fp;
353
debug_decl(utmp_logout, SUDO_DEBUG_UTMP);
354
355
if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
356
debug_return_int(ret);
357
358
/* Strip off /dev/ prefix from line as needed. */
359
if (strncmp(line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
360
line += sizeof(_PATH_DEV) - 1;
361
362
while (fread(&utbuf, sizeof(utbuf), 1, fp) == 1) {
363
if (!strncmp(utbuf.ut_line, line, sizeof(utbuf.ut_line))) {
364
memset(utbuf.ut_user, 0, sizeof(utbuf.ut_user));
365
# if defined(HAVE_STRUCT_UTMP_UT_TYPE)
366
utbuf.ut_type = DEAD_PROCESS;
367
# endif
368
utmp_settime(&utbuf);
369
/* Back up and overwrite record. */
370
if (fseeko(fp, (off_t)0 - (off_t)sizeof(utbuf), SEEK_CUR) == 0) {
371
if (fwrite(&utbuf, sizeof(utbuf), 1, fp) == 1)
372
ret = true;
373
}
374
break;
375
}
376
}
377
fclose(fp);
378
379
debug_return_bool(ret);
380
}
381
#endif /* HAVE_GETUTSID || HAVE_GETUTXID || HAVE_GETUTID */
382
383