Annotation of embedaddon/sudo/src/utmp.c, revision 1.1.1.2
1.1 misho 1: /*
2: * Copyright (c) 2011 Todd C. Miller <Todd.Miller@courtesan.com>
3: *
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.
7: *
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.
15: */
16:
17: #include <config.h>
18:
19: #include <sys/types.h>
20: #include <sys/param.h>
21: #include <sys/time.h>
22: #include <sys/wait.h>
23: #include <stdio.h>
24: #ifdef STDC_HEADERS
25: # include <stdlib.h>
26: # include <stddef.h>
27: #else
28: # ifdef HAVE_STDLIB_H
29: # include <stdlib.h>
30: # endif
31: #endif /* STDC_HEADERS */
32: #ifdef HAVE_STRING_H
33: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
34: # include <memory.h>
35: # endif
36: # include <string.h>
37: #endif /* HAVE_STRING_H */
38: #ifdef HAVE_STRINGS_H
39: # include <strings.h>
40: #endif /* HAVE_STRINGS_H */
41: #ifdef HAVE_UNISTD_H
42: # include <unistd.h>
43: #endif /* HAVE_UNISTD_H */
44: #if TIME_WITH_SYS_TIME
45: # include <time.h>
46: #endif
47: #ifdef HAVE_UTMPX_H
48: # include <utmpx.h>
49: #else
50: # include <utmp.h>
51: #endif /* HAVE_UTMPX_H */
52: #ifdef HAVE_GETTTYENT
53: # include <ttyent.h>
54: #endif
55: #include <fcntl.h>
56:
57: #include "sudo.h"
58: #include "sudo_exec.h"
59:
60: /*
61: * Simplify handling of utmp vs. utmpx
62: */
63: #if !defined(HAVE_GETUTXID) && defined(HAVE_GETUTID)
64: # define getutxline(u) getutline(u)
65: # define pututxline(u) pututline(u)
1.1.1.2 ! misho 66: # define setutxent() setutent()
! 67: # define endutxent() endutent()
1.1 misho 68: #endif /* !HAVE_GETUTXID && HAVE_GETUTID */
69:
70: #ifdef HAVE_GETUTXID
71: typedef struct utmpx sudo_utmp_t;
72: #else
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
77: # endif
78: #endif
79:
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
84: # undef __e_exit
85: # define __e_exit e_exit
86: #endif
87:
88: #if defined(HAVE_GETUTXID) || defined(HAVE_GETUTID)
89: /*
90: * Create ut_id from the new ut_line and the old ut_id.
91: */
92: static void
93: utmp_setid(sudo_utmp_t *old, sudo_utmp_t *new)
94: {
95: const char *line = new->ut_line;
96: size_t idlen;
1.1.1.2 ! misho 97: debug_decl(utmp_setid, SUDO_DEBUG_UTMP)
1.1 misho 98:
99: /* Skip over "tty" in the id if old entry did too. */
100: if (old != NULL) {
101: if (strncmp(line, "tty", 3) == 0) {
102: idlen = MIN(sizeof(old->ut_id), 3);
103: if (strncmp(old->ut_id, "tty", idlen) != 0)
104: line += 3;
105: }
106: }
107:
108: /* Store as much as will fit, skipping parts of the beginning as needed. */
109: idlen = strlen(line);
110: if (idlen > sizeof(new->ut_id)) {
111: line += idlen - sizeof(new->ut_id);
112: idlen = sizeof(new->ut_id);
113: }
114: strncpy(new->ut_id, line, idlen);
1.1.1.2 ! misho 115:
! 116: debug_return;
1.1 misho 117: }
118: #endif /* HAVE_GETUTXID || HAVE_GETUTID */
119:
120: /*
121: * Store time in utmp structure.
122: */
123: static void
124: utmp_settime(sudo_utmp_t *ut)
125: {
126: struct timeval tv;
1.1.1.2 ! misho 127: debug_decl(utmp_settime, SUDO_DEBUG_UTMP)
1.1 misho 128:
129: gettimeofday(&tv, NULL);
130:
131: #if defined(HAVE_STRUCT_UTMP_UT_TV) || defined(HAVE_STRUCT_UTMPX_UT_TV)
132: ut->ut_tv.tv_sec = tv.tv_sec;
133: ut->ut_tv.tv_usec = tv.tv_usec;
134: #else
135: ut->ut_time = tv.tv_sec;
136: #endif
1.1.1.2 ! misho 137:
! 138: debug_return;
1.1 misho 139: }
140:
141: /*
142: * Fill in a utmp entry, using an old entry as a template if there is one.
143: */
144: static void
145: utmp_fill(const char *line, const char *user, sudo_utmp_t *ut_old,
146: sudo_utmp_t *ut_new)
147: {
1.1.1.2 ! misho 148: debug_decl(utmp_file, SUDO_DEBUG_UTMP)
! 149:
1.1 misho 150: if (ut_old == NULL) {
151: memset(ut_new, 0, sizeof(*ut_new));
152: if (user == NULL) {
153: strncpy(ut_new->ut_user, user_details.username,
154: sizeof(ut_new->ut_user));
155: }
156: } else if (ut_old != ut_new) {
157: memcpy(ut_new, ut_old, sizeof(*ut_new));
158: }
159: if (user != NULL)
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_UTMPX_UT_ID) || defined(HAVE_STRUCT_UTMP_UT_ID)
163: utmp_setid(ut_old, ut_new);
164: #endif
165: #if defined(HAVE_STRUCT_UTMPX_UT_PID) || defined(HAVE_STRUCT_UTMP_UT_PID)
166: ut_new->ut_pid = getpid();
167: #endif
168: utmp_settime(ut_new);
169: #if defined(HAVE_STRUCT_UTMPX_UT_TYPE) || defined(HAVE_STRUCT_UTMP_UT_TYPE)
170: ut_new->ut_type = USER_PROCESS;
171: #endif
1.1.1.2 ! misho 172: debug_return;
1.1 misho 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}utent()/{get,put}getutxent().
180: *
181: * Legacy: sparse file indexed by ttyslot() * sizeof(struct utmp)
182: */
183: #if defined(HAVE_GETUTXID) || defined(HAVE_GETUTID)
1.1.1.2 ! misho 184: bool
1.1 misho 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;
1.1.1.2 ! misho 189: bool rval = false;
! 190: debug_decl(utmp_login, SUDO_DEBUG_UTMP)
1.1 misho 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: setutxent();
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 = getutxline(&utbuf);
204: }
205: utmp_fill(to_line, user, ut_old, &utbuf);
206: if (pututxline(&utbuf) != NULL)
1.1.1.2 ! misho 207: rval = true;
1.1 misho 208: endutxent();
209:
1.1.1.2 ! misho 210: debug_return_bool(rval);
1.1 misho 211: }
212:
1.1.1.2 ! misho 213: bool
1.1 misho 214: utmp_logout(const char *line, int status)
215: {
1.1.1.2 ! misho 216: bool rval = false;
1.1 misho 217: sudo_utmp_t *ut, utbuf;
1.1.1.2 ! misho 218: debug_decl(utmp_logout, SUDO_DEBUG_UTMP)
1.1 misho 219:
220: /* Strip off /dev/ prefix from line as needed. */
221: if (strncmp(line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
222: line += sizeof(_PATH_DEV) - 1;
223:
224: memset(&utbuf, 0, sizeof(utbuf));
225: strncpy(utbuf.ut_line, line, sizeof(utbuf.ut_line));
226: if ((ut = getutxline(&utbuf)) != NULL) {
227: memset(ut->ut_user, 0, sizeof(ut->ut_user));
228: # if defined(HAVE_STRUCT_UTMPX_UT_TYPE) || defined(HAVE_STRUCT_UTMP_UT_TYPE)
229: ut->ut_type = DEAD_PROCESS;
230: # endif
231: # if defined(HAVE_STRUCT_UTMPX_UT_EXIT) || defined(HAVE_STRUCT_UTMP_UT_EXIT)
232: ut->ut_exit.__e_exit = WEXITSTATUS(status);
233: ut->ut_exit.__e_termination = WIFEXITED(status) ? WEXITSTATUS(status) : 0;
234: # endif
235: utmp_settime(ut);
236: if (pututxline(ut) != NULL)
1.1.1.2 ! misho 237: rval = true;
1.1 misho 238: }
1.1.1.2 ! misho 239: debug_return_bool(rval);
1.1 misho 240: }
241:
242: #else /* !HAVE_GETUTXID && !HAVE_GETUTID */
243:
244: /*
245: * Find the slot for the specified line (tty name and file descriptor).
246: * Returns a slot suitable for seeking into utmp on success or <= 0 on error.
247: * If getttyent() is available we can use that to compute the slot.
248: */
249: # ifdef HAVE_GETTTYENT
250: static int
251: utmp_slot(const char *line, int ttyfd)
252: {
253: int slot = 1;
254: struct ttyent *tty;
1.1.1.2 ! misho 255: debug_decl(utmp_slot, SUDO_DEBUG_UTMP)
1.1 misho 256:
257: setttyent();
258: while ((tty = getttyent()) != NULL) {
259: if (strcmp(line, tty->ty_name) == 0)
260: break;
261: slot++;
262: }
263: endttyent();
1.1.1.2 ! misho 264: debug_return_int(tty ? slot : 0);
1.1 misho 265: }
266: # else
267: static int
268: utmp_slot(const char *line, int ttyfd)
269: {
270: int sfd, slot;
1.1.1.2 ! misho 271: debug_decl(utmp_slot, SUDO_DEBUG_UTMP)
1.1 misho 272:
273: /*
274: * Temporarily point stdin to the tty since ttyslot()
275: * doesn't take an argument.
276: */
277: if ((sfd = dup(STDIN_FILENO)) == -1)
278: error(1, _("unable to save stdin"));
279: if (dup2(ttyfd, STDIN_FILENO) == -1)
280: error(1, _("unable to dup2 stdin"));
281: slot = ttyslot();
282: if (dup2(sfd, STDIN_FILENO) == -1)
283: error(1, _("unable to restore stdin"));
284: close(sfd);
285:
1.1.1.2 ! misho 286: debug_return_int(slot);
1.1 misho 287: }
288: # endif /* HAVE_GETTTYENT */
289:
1.1.1.2 ! misho 290: bool
1.1 misho 291: utmp_login(const char *from_line, const char *to_line, int ttyfd,
292: const char *user)
293: {
294: sudo_utmp_t utbuf, *ut_old = NULL;
1.1.1.2 ! misho 295: bool rval = false;
! 296: int slot;
1.1 misho 297: FILE *fp;
1.1.1.2 ! misho 298: debug_decl(utmp_login, SUDO_DEBUG_UTMP)
1.1 misho 299:
300: /* Strip off /dev/ prefix from line as needed. */
301: if (strncmp(to_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
302: to_line += sizeof(_PATH_DEV) - 1;
303:
304: /* Find slot for new entry. */
305: slot = utmp_slot(to_line, ttyfd);
306: if (slot <= 0)
307: goto done;
308:
309: if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
310: goto done;
311:
312: if (from_line != NULL) {
313: if (strncmp(from_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
314: from_line += sizeof(_PATH_DEV) - 1;
315:
316: /* Lookup old line. */
317: while (fread(&utbuf, sizeof(utbuf), 1, fp) == 1) {
318: # ifdef HAVE_STRUCT_UTMP_UT_ID
319: if (utbuf.ut_type != LOGIN_PROCESS && utbuf.ut_type != USER_PROCESS)
320: continue;
321: # endif
322: if (utbuf.ut_user[0] &&
323: !strncmp(utbuf.ut_line, from_line, sizeof(utbuf.ut_line))) {
324: ut_old = &utbuf;
325: break;
326: }
327: }
328: }
329: utmp_fill(to_line, user, ut_old, &utbuf);
330: if (fseek(fp, slot * (long)sizeof(utbuf), SEEK_SET) == 0) {
331: if (fwrite(&utbuf, sizeof(utbuf), 1, fp) == 1)
1.1.1.2 ! misho 332: rval = true;
1.1 misho 333: }
334: fclose(fp);
335:
336: done:
1.1.1.2 ! misho 337: debug_return_bool(rval);
1.1 misho 338: }
339:
1.1.1.2 ! misho 340: bool
1.1 misho 341: utmp_logout(const char *line, int status)
342: {
343: sudo_utmp_t utbuf;
1.1.1.2 ! misho 344: bool rval = false;
1.1 misho 345: FILE *fp;
1.1.1.2 ! misho 346: debug_decl(utmp_logout, SUDO_DEBUG_UTMP)
1.1 misho 347:
348: if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
1.1.1.2 ! misho 349: debug_return_int(rval);
1.1 misho 350:
351: /* Strip off /dev/ prefix from line as needed. */
352: if (strncmp(line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
353: line += sizeof(_PATH_DEV) - 1;
354:
355: while (fread(&utbuf, sizeof(utbuf), 1, fp) == 1) {
356: if (!strncmp(utbuf.ut_line, line, sizeof(utbuf.ut_line))) {
357: memset(utbuf.ut_user, 0, sizeof(utbuf.ut_user));
358: # if defined(HAVE_STRUCT_UTMP_UT_TYPE)
359: utbuf.ut_type = DEAD_PROCESS;
360: # endif
361: utmp_settime(&utbuf);
362: /* Back up and overwrite record. */
363: if (fseek(fp, 0L - (long)sizeof(utbuf), SEEK_CUR) == 0) {
364: if (fwrite(&utbuf, sizeof(utbuf), 1, fp) == 1)
1.1.1.2 ! misho 365: rval = true;
1.1 misho 366: }
367: break;
368: }
369: }
370: fclose(fp);
371:
1.1.1.2 ! misho 372: debug_return_bool(rval);
1.1 misho 373: }
374: #endif /* HAVE_GETUTXID || HAVE_GETUTID */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>