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 */
59 #include "sudo_exec.h"
62 * Simplify handling of utmp vs. utmpx
64 #if !defined(HAVE_GETUTXID) && defined(HAVE_GETUTID)
65 # define getutxline(u) getutline(u)
66 # define pututxline(u) pututline(u)
67 # define setutxent() setutent()
68 # define endutxent() endutent()
69 #endif /* !HAVE_GETUTXID && HAVE_GETUTID */
72 typedef struct utmpx sudo_utmp_t;
74 typedef struct utmp sudo_utmp_t;
75 /* Older systems have ut_name, not us_user */
76 # if !defined(HAVE_STRUCT_UTMP_UT_USER) && !defined(ut_user)
77 # define ut_user ut_name
81 /* HP-UX has __e_termination and __e_exit, others lack the __ */
82 #if defined(HAVE_STRUCT_UTMPX_UT_EXIT_E_TERMINATION) || defined(HAVE_STRUCT_UTMP_UT_EXIT_E_TERMINATION)
83 # undef __e_termination
84 # define __e_termination e_termination
86 # define __e_exit e_exit
89 #if defined(HAVE_GETUTXID) || defined(HAVE_GETUTID)
91 * Create ut_id from the new ut_line and the old ut_id.
94 utmp_setid(sudo_utmp_t *old, sudo_utmp_t *new)
96 const char *line = new->ut_line;
98 debug_decl(utmp_setid, SUDO_DEBUG_UTMP)
100 /* Skip over "tty" in the id if old entry did too. */
102 if (strncmp(line, "tty", 3) == 0) {
103 idlen = MIN(sizeof(old->ut_id), 3);
104 if (strncmp(old->ut_id, "tty", idlen) != 0)
109 /* Store as much as will fit, skipping parts of the beginning as needed. */
110 idlen = strlen(line);
111 if (idlen > sizeof(new->ut_id)) {
112 line += idlen - sizeof(new->ut_id);
113 idlen = sizeof(new->ut_id);
115 strncpy(new->ut_id, line, idlen);
119 #endif /* HAVE_GETUTXID || HAVE_GETUTID */
122 * Store time in utmp structure.
125 utmp_settime(sudo_utmp_t *ut)
128 debug_decl(utmp_settime, SUDO_DEBUG_UTMP)
130 gettimeofday(&tv, NULL);
132 #if defined(HAVE_STRUCT_UTMP_UT_TV) || defined(HAVE_STRUCT_UTMPX_UT_TV)
133 ut->ut_tv.tv_sec = tv.tv_sec;
134 ut->ut_tv.tv_usec = tv.tv_usec;
136 ut->ut_time = tv.tv_sec;
143 * Fill in a utmp entry, using an old entry as a template if there is one.
146 utmp_fill(const char *line, const char *user, sudo_utmp_t *ut_old,
149 debug_decl(utmp_file, SUDO_DEBUG_UTMP)
151 if (ut_old == NULL) {
152 memset(ut_new, 0, sizeof(*ut_new));
154 strncpy(ut_new->ut_user, user_details.username,
155 sizeof(ut_new->ut_user));
157 } else if (ut_old != ut_new) {
158 memcpy(ut_new, ut_old, sizeof(*ut_new));
161 strncpy(ut_new->ut_user, user, sizeof(ut_new->ut_user));
162 strncpy(ut_new->ut_line, line, sizeof(ut_new->ut_line));
163 #if defined(HAVE_STRUCT_UTMPX_UT_ID) || defined(HAVE_STRUCT_UTMP_UT_ID)
164 utmp_setid(ut_old, ut_new);
166 #if defined(HAVE_STRUCT_UTMPX_UT_PID) || defined(HAVE_STRUCT_UTMP_UT_PID)
167 ut_new->ut_pid = getpid();
169 utmp_settime(ut_new);
170 #if defined(HAVE_STRUCT_UTMPX_UT_TYPE) || defined(HAVE_STRUCT_UTMP_UT_TYPE)
171 ut_new->ut_type = USER_PROCESS;
177 * There are two basic utmp file types:
179 * POSIX: sequential access with new entries appended to the end.
180 * Manipulated via {get,put}utent()/{get,put}getutxent().
182 * Legacy: sparse file indexed by ttyslot() * sizeof(struct utmp)
184 #if defined(HAVE_GETUTXID) || defined(HAVE_GETUTID)
186 utmp_login(const char *from_line, const char *to_line, int ttyfd,
189 sudo_utmp_t utbuf, *ut_old = NULL;
191 debug_decl(utmp_login, SUDO_DEBUG_UTMP)
193 /* Strip off /dev/ prefix from line as needed. */
194 if (strncmp(to_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
195 to_line += sizeof(_PATH_DEV) - 1;
197 if (from_line != NULL) {
198 if (strncmp(from_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
199 from_line += sizeof(_PATH_DEV) - 1;
201 /* Lookup old line. */
202 memset(&utbuf, 0, sizeof(utbuf));
203 strncpy(utbuf.ut_line, from_line, sizeof(utbuf.ut_line));
204 ut_old = getutxline(&utbuf);
206 utmp_fill(to_line, user, ut_old, &utbuf);
207 if (pututxline(&utbuf) != NULL)
211 debug_return_bool(rval);
215 utmp_logout(const char *line, int status)
218 sudo_utmp_t *ut, utbuf;
219 debug_decl(utmp_logout, SUDO_DEBUG_UTMP)
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;
225 memset(&utbuf, 0, sizeof(utbuf));
226 strncpy(utbuf.ut_line, line, sizeof(utbuf.ut_line));
227 if ((ut = getutxline(&utbuf)) != NULL) {
228 memset(ut->ut_user, 0, sizeof(ut->ut_user));
229 # if defined(HAVE_STRUCT_UTMPX_UT_TYPE) || defined(HAVE_STRUCT_UTMP_UT_TYPE)
230 ut->ut_type = DEAD_PROCESS;
232 # if defined(HAVE_STRUCT_UTMPX_UT_EXIT) || defined(HAVE_STRUCT_UTMP_UT_EXIT)
233 ut->ut_exit.__e_exit = WEXITSTATUS(status);
234 ut->ut_exit.__e_termination = WIFEXITED(status) ? WEXITSTATUS(status) : 0;
237 if (pututxline(ut) != NULL)
240 debug_return_bool(rval);
243 #else /* !HAVE_GETUTXID && !HAVE_GETUTID */
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.
250 # ifdef HAVE_GETTTYENT
252 utmp_slot(const char *line, int ttyfd)
256 debug_decl(utmp_slot, SUDO_DEBUG_UTMP)
259 while ((tty = getttyent()) != NULL) {
260 if (strcmp(line, tty->ty_name) == 0)
265 debug_return_int(tty ? slot : 0);
269 utmp_slot(const char *line, int ttyfd)
272 debug_decl(utmp_slot, SUDO_DEBUG_UTMP)
275 * Temporarily point stdin to the tty since ttyslot()
276 * doesn't take an argument.
278 if ((sfd = dup(STDIN_FILENO)) == -1)
279 error(1, _("unable to save stdin"));
280 if (dup2(ttyfd, STDIN_FILENO) == -1)
281 error(1, _("unable to dup2 stdin"));
283 if (dup2(sfd, STDIN_FILENO) == -1)
284 error(1, _("unable to restore stdin"));
287 debug_return_int(slot);
289 # endif /* HAVE_GETTTYENT */
292 utmp_login(const char *from_line, const char *to_line, int ttyfd,
295 sudo_utmp_t utbuf, *ut_old = NULL;
299 debug_decl(utmp_login, SUDO_DEBUG_UTMP)
301 /* Strip off /dev/ prefix from line as needed. */
302 if (strncmp(to_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
303 to_line += sizeof(_PATH_DEV) - 1;
305 /* Find slot for new entry. */
306 slot = utmp_slot(to_line, ttyfd);
310 if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
313 if (from_line != NULL) {
314 if (strncmp(from_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
315 from_line += sizeof(_PATH_DEV) - 1;
317 /* Lookup old line. */
318 while (fread(&utbuf, sizeof(utbuf), 1, fp) == 1) {
319 # ifdef HAVE_STRUCT_UTMP_UT_ID
320 if (utbuf.ut_type != LOGIN_PROCESS && utbuf.ut_type != USER_PROCESS)
323 if (utbuf.ut_user[0] &&
324 !strncmp(utbuf.ut_line, from_line, sizeof(utbuf.ut_line))) {
330 utmp_fill(to_line, user, ut_old, &utbuf);
332 if (fseeko(fp, slot * (off_t)sizeof(utbuf), SEEK_SET) == 0) {
334 if (fseek(fp, slot * (long)sizeof(utbuf), SEEK_SET) == 0) {
336 if (fwrite(&utbuf, sizeof(utbuf), 1, fp) == 1)
342 debug_return_bool(rval);
346 utmp_logout(const char *line, int status)
351 debug_decl(utmp_logout, SUDO_DEBUG_UTMP)
353 if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
354 debug_return_int(rval);
356 /* Strip off /dev/ prefix from line as needed. */
357 if (strncmp(line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
358 line += sizeof(_PATH_DEV) - 1;
360 while (fread(&utbuf, sizeof(utbuf), 1, fp) == 1) {
361 if (!strncmp(utbuf.ut_line, line, sizeof(utbuf.ut_line))) {
362 memset(utbuf.ut_user, 0, sizeof(utbuf.ut_user));
363 # if defined(HAVE_STRUCT_UTMP_UT_TYPE)
364 utbuf.ut_type = DEAD_PROCESS;
366 utmp_settime(&utbuf);
367 /* Back up and overwrite record. */
369 if (fseeko(fp, (off_t)0 - (off_t)sizeof(utbuf), SEEK_CUR) == 0) {
371 if (fseek(fp, 0L - (long)sizeof(utbuf), SEEK_CUR) == 0) {
373 if (fwrite(&utbuf, sizeof(utbuf), 1, fp) == 1)
381 debug_return_bool(rval);
383 #endif /* HAVE_GETUTXID || HAVE_GETUTID */