2
* Copyright (c) 2011 Todd C. Miller <Todd.Miller@courtesan.com>
4
* Permission to use, copy, modify, and distribute this software for any
5
* purpose with or without fee is hereby granted, provided that the above
6
* copyright notice and this permission notice appear in all copies.
8
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
#include <sys/types.h>
20
#include <sys/param.h>
31
#endif /* STDC_HEADERS */
33
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
37
#endif /* HAVE_STRING_H */
40
#endif /* HAVE_STRINGS_H */
43
#endif /* HAVE_UNISTD_H */
44
#if TIME_WITH_SYS_TIME
51
#endif /* HAVE_UTMPX_H */
58
#include "sudo_exec.h"
61
* Simplify handling of utmp vs. utmpx
63
#if !defined(HAVE_GETUTXID) && defined(HAVE_GETUTID)
64
# define getutxline(u) getutline(u)
65
# define pututxline(u) pututline(u)
66
# define setutxent setutent(u)
67
# define endutxent endutent(u)
68
#endif /* !HAVE_GETUTXID && HAVE_GETUTID */
71
typedef struct utmpx sudo_utmp_t;
73
typedef struct utmp sudo_utmp_t;
74
/* Older systems have ut_name, not us_user */
75
# if !defined(HAVE_STRUCT_UTMP_UT_USER) && !defined(ut_user)
76
# define ut_user ut_name
80
/* HP-UX has __e_termination and __e_exit, others lack the __ */
81
#if defined(HAVE_STRUCT_UTMPX_UT_EXIT_E_TERMINATION) || defined(HAVE_STRUCT_UTMP_UT_EXIT_E_TERMINATION)
82
# undef __e_termination
83
# define __e_termination e_termination
85
# define __e_exit e_exit
88
#if defined(HAVE_GETUTXID) || defined(HAVE_GETUTID)
90
* Create ut_id from the new ut_line and the old ut_id.
93
utmp_setid(sudo_utmp_t *old, sudo_utmp_t *new)
95
const char *line = new->ut_line;
98
/* Skip over "tty" in the id if old entry did too. */
100
if (strncmp(line, "tty", 3) == 0) {
101
idlen = MIN(sizeof(old->ut_id), 3);
102
if (strncmp(old->ut_id, "tty", idlen) != 0)
107
/* Store as much as will fit, skipping parts of the beginning as needed. */
108
idlen = strlen(line);
109
if (idlen > sizeof(new->ut_id)) {
110
line += idlen - sizeof(new->ut_id);
111
idlen = sizeof(new->ut_id);
113
strncpy(new->ut_id, line, idlen);
115
#endif /* HAVE_GETUTXID || HAVE_GETUTID */
118
* Store time in utmp structure.
121
utmp_settime(sudo_utmp_t *ut)
125
gettimeofday(&tv, NULL);
127
#if defined(HAVE_STRUCT_UTMP_UT_TV) || defined(HAVE_STRUCT_UTMPX_UT_TV)
128
ut->ut_tv.tv_sec = tv.tv_sec;
129
ut->ut_tv.tv_usec = tv.tv_usec;
131
ut->ut_time = tv.tv_sec;
136
* Fill in a utmp entry, using an old entry as a template if there is one.
139
utmp_fill(const char *line, const char *user, sudo_utmp_t *ut_old,
142
if (ut_old == NULL) {
143
memset(ut_new, 0, sizeof(*ut_new));
145
strncpy(ut_new->ut_user, user_details.username,
146
sizeof(ut_new->ut_user));
148
} else if (ut_old != ut_new) {
149
memcpy(ut_new, ut_old, sizeof(*ut_new));
152
strncpy(ut_new->ut_user, user, sizeof(ut_new->ut_user));
153
strncpy(ut_new->ut_line, line, sizeof(ut_new->ut_line));
154
#if defined(HAVE_STRUCT_UTMPX_UT_ID) || defined(HAVE_STRUCT_UTMP_UT_ID)
155
utmp_setid(ut_old, ut_new);
157
#if defined(HAVE_STRUCT_UTMPX_UT_PID) || defined(HAVE_STRUCT_UTMP_UT_PID)
158
ut_new->ut_pid = getpid();
160
utmp_settime(ut_new);
161
#if defined(HAVE_STRUCT_UTMPX_UT_TYPE) || defined(HAVE_STRUCT_UTMP_UT_TYPE)
162
ut_new->ut_type = USER_PROCESS;
167
* There are two basic utmp file types:
169
* POSIX: sequential access with new entries appended to the end.
170
* Manipulated via {get,put}utent()/{get,put}getutxent().
172
* Legacy: sparse file indexed by ttyslot() * sizeof(struct utmp)
174
#if defined(HAVE_GETUTXID) || defined(HAVE_GETUTID)
176
utmp_login(const char *from_line, const char *to_line, int ttyfd,
179
sudo_utmp_t utbuf, *ut_old = NULL;
182
/* Strip off /dev/ prefix from line as needed. */
183
if (strncmp(to_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
184
to_line += sizeof(_PATH_DEV) - 1;
186
if (from_line != NULL) {
187
if (strncmp(from_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
188
from_line += sizeof(_PATH_DEV) - 1;
190
/* Lookup old line. */
191
memset(&utbuf, 0, sizeof(utbuf));
192
strncpy(utbuf.ut_line, from_line, sizeof(utbuf.ut_line));
193
ut_old = getutxline(&utbuf);
195
utmp_fill(to_line, user, ut_old, &utbuf);
196
if (pututxline(&utbuf) != NULL)
204
utmp_logout(const char *line, int status)
207
sudo_utmp_t *ut, utbuf;
209
/* Strip off /dev/ prefix from line as needed. */
210
if (strncmp(line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
211
line += sizeof(_PATH_DEV) - 1;
213
memset(&utbuf, 0, sizeof(utbuf));
214
strncpy(utbuf.ut_line, line, sizeof(utbuf.ut_line));
215
if ((ut = getutxline(&utbuf)) != NULL) {
216
memset(ut->ut_user, 0, sizeof(ut->ut_user));
217
# if defined(HAVE_STRUCT_UTMPX_UT_TYPE) || defined(HAVE_STRUCT_UTMP_UT_TYPE)
218
ut->ut_type = DEAD_PROCESS;
220
# if defined(HAVE_STRUCT_UTMPX_UT_EXIT) || defined(HAVE_STRUCT_UTMP_UT_EXIT)
221
ut->ut_exit.__e_exit = WEXITSTATUS(status);
222
ut->ut_exit.__e_termination = WIFEXITED(status) ? WEXITSTATUS(status) : 0;
225
if (pututxline(ut) != NULL)
231
#else /* !HAVE_GETUTXID && !HAVE_GETUTID */
234
* Find the slot for the specified line (tty name and file descriptor).
235
* Returns a slot suitable for seeking into utmp on success or <= 0 on error.
236
* If getttyent() is available we can use that to compute the slot.
238
# ifdef HAVE_GETTTYENT
240
utmp_slot(const char *line, int ttyfd)
246
while ((tty = getttyent()) != NULL) {
247
if (strcmp(line, tty->ty_name) == 0)
252
return tty ? slot : 0;
256
utmp_slot(const char *line, int ttyfd)
261
* Temporarily point stdin to the tty since ttyslot()
262
* doesn't take an argument.
264
if ((sfd = dup(STDIN_FILENO)) == -1)
265
error(1, _("unable to save stdin"));
266
if (dup2(ttyfd, STDIN_FILENO) == -1)
267
error(1, _("unable to dup2 stdin"));
269
if (dup2(sfd, STDIN_FILENO) == -1)
270
error(1, _("unable to restore stdin"));
275
# endif /* HAVE_GETTTYENT */
278
utmp_login(const char *from_line, const char *to_line, int ttyfd,
281
sudo_utmp_t utbuf, *ut_old = NULL;
282
int slot, rval = FALSE;
285
/* Strip off /dev/ prefix from line as needed. */
286
if (strncmp(to_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
287
to_line += sizeof(_PATH_DEV) - 1;
289
/* Find slot for new entry. */
290
slot = utmp_slot(to_line, ttyfd);
294
if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
297
if (from_line != NULL) {
298
if (strncmp(from_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
299
from_line += sizeof(_PATH_DEV) - 1;
301
/* Lookup old line. */
302
while (fread(&utbuf, sizeof(utbuf), 1, fp) == 1) {
303
# ifdef HAVE_STRUCT_UTMP_UT_ID
304
if (utbuf.ut_type != LOGIN_PROCESS && utbuf.ut_type != USER_PROCESS)
307
if (utbuf.ut_user[0] &&
308
!strncmp(utbuf.ut_line, from_line, sizeof(utbuf.ut_line))) {
314
utmp_fill(to_line, user, ut_old, &utbuf);
315
if (fseek(fp, slot * (long)sizeof(utbuf), SEEK_SET) == 0) {
316
if (fwrite(&utbuf, sizeof(utbuf), 1, fp) == 1)
326
utmp_logout(const char *line, int status)
332
if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
335
/* Strip off /dev/ prefix from line as needed. */
336
if (strncmp(line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
337
line += sizeof(_PATH_DEV) - 1;
339
while (fread(&utbuf, sizeof(utbuf), 1, fp) == 1) {
340
if (!strncmp(utbuf.ut_line, line, sizeof(utbuf.ut_line))) {
341
memset(utbuf.ut_user, 0, sizeof(utbuf.ut_user));
342
# if defined(HAVE_STRUCT_UTMP_UT_TYPE)
343
utbuf.ut_type = DEAD_PROCESS;
345
utmp_settime(&utbuf);
346
/* Back up and overwrite record. */
347
if (fseek(fp, 0L - (long)sizeof(utbuf), SEEK_CUR) == 0) {
348
if (fwrite(&utbuf, sizeof(utbuf), 1, fp) == 1)
358
#endif /* HAVE_GETUTXID || HAVE_GETUTID */