Annotation of embedaddon/sudo/src/utmp.c, revision 1.1.1.1
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)
66: # define setutxent setutent(u)
67: # define endutxent endutent(u)
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;
97:
98: /* Skip over "tty" in the id if old entry did too. */
99: if (old != NULL) {
100: if (strncmp(line, "tty", 3) == 0) {
101: idlen = MIN(sizeof(old->ut_id), 3);
102: if (strncmp(old->ut_id, "tty", idlen) != 0)
103: line += 3;
104: }
105: }
106:
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);
112: }
113: strncpy(new->ut_id, line, idlen);
114: }
115: #endif /* HAVE_GETUTXID || HAVE_GETUTID */
116:
117: /*
118: * Store time in utmp structure.
119: */
120: static void
121: utmp_settime(sudo_utmp_t *ut)
122: {
123: struct timeval tv;
124:
125: gettimeofday(&tv, NULL);
126:
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;
130: #else
131: ut->ut_time = tv.tv_sec;
132: #endif
133: }
134:
135: /*
136: * Fill in a utmp entry, using an old entry as a template if there is one.
137: */
138: static void
139: utmp_fill(const char *line, const char *user, sudo_utmp_t *ut_old,
140: sudo_utmp_t *ut_new)
141: {
142: if (ut_old == NULL) {
143: memset(ut_new, 0, sizeof(*ut_new));
144: if (user == NULL) {
145: strncpy(ut_new->ut_user, user_details.username,
146: sizeof(ut_new->ut_user));
147: }
148: } else if (ut_old != ut_new) {
149: memcpy(ut_new, ut_old, sizeof(*ut_new));
150: }
151: if (user != NULL)
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);
156: #endif
157: #if defined(HAVE_STRUCT_UTMPX_UT_PID) || defined(HAVE_STRUCT_UTMP_UT_PID)
158: ut_new->ut_pid = getpid();
159: #endif
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;
163: #endif
164: }
165:
166: /*
167: * There are two basic utmp file types:
168: *
169: * POSIX: sequential access with new entries appended to the end.
170: * Manipulated via {get,put}utent()/{get,put}getutxent().
171: *
172: * Legacy: sparse file indexed by ttyslot() * sizeof(struct utmp)
173: */
174: #if defined(HAVE_GETUTXID) || defined(HAVE_GETUTID)
175: int
176: utmp_login(const char *from_line, const char *to_line, int ttyfd,
177: const char *user)
178: {
179: sudo_utmp_t utbuf, *ut_old = NULL;
180: int rval = FALSE;
181:
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;
185: setutxent();
186: if (from_line != NULL) {
187: if (strncmp(from_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
188: from_line += sizeof(_PATH_DEV) - 1;
189:
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);
194: }
195: utmp_fill(to_line, user, ut_old, &utbuf);
196: if (pututxline(&utbuf) != NULL)
197: rval = TRUE;
198: endutxent();
199:
200: return rval;
201: }
202:
203: int
204: utmp_logout(const char *line, int status)
205: {
206: int rval = FALSE;
207: sudo_utmp_t *ut, utbuf;
208:
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;
212:
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;
219: # endif
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;
223: # endif
224: utmp_settime(ut);
225: if (pututxline(ut) != NULL)
226: rval = TRUE;
227: }
228: return rval;
229: }
230:
231: #else /* !HAVE_GETUTXID && !HAVE_GETUTID */
232:
233: /*
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.
237: */
238: # ifdef HAVE_GETTTYENT
239: static int
240: utmp_slot(const char *line, int ttyfd)
241: {
242: int slot = 1;
243: struct ttyent *tty;
244:
245: setttyent();
246: while ((tty = getttyent()) != NULL) {
247: if (strcmp(line, tty->ty_name) == 0)
248: break;
249: slot++;
250: }
251: endttyent();
252: return tty ? slot : 0;
253: }
254: # else
255: static int
256: utmp_slot(const char *line, int ttyfd)
257: {
258: int sfd, slot;
259:
260: /*
261: * Temporarily point stdin to the tty since ttyslot()
262: * doesn't take an argument.
263: */
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"));
268: slot = ttyslot();
269: if (dup2(sfd, STDIN_FILENO) == -1)
270: error(1, _("unable to restore stdin"));
271: close(sfd);
272:
273: return slot;
274: }
275: # endif /* HAVE_GETTTYENT */
276:
277: int
278: utmp_login(const char *from_line, const char *to_line, int ttyfd,
279: const char *user)
280: {
281: sudo_utmp_t utbuf, *ut_old = NULL;
282: int slot, rval = FALSE;
283: FILE *fp;
284:
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;
288:
289: /* Find slot for new entry. */
290: slot = utmp_slot(to_line, ttyfd);
291: if (slot <= 0)
292: goto done;
293:
294: if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
295: goto done;
296:
297: if (from_line != NULL) {
298: if (strncmp(from_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
299: from_line += sizeof(_PATH_DEV) - 1;
300:
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)
305: continue;
306: # endif
307: if (utbuf.ut_user[0] &&
308: !strncmp(utbuf.ut_line, from_line, sizeof(utbuf.ut_line))) {
309: ut_old = &utbuf;
310: break;
311: }
312: }
313: }
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)
317: rval = TRUE;
318: }
319: fclose(fp);
320:
321: done:
322: return rval;
323: }
324:
325: int
326: utmp_logout(const char *line, int status)
327: {
328: sudo_utmp_t utbuf;
329: int rval = FALSE;
330: FILE *fp;
331:
332: if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
333: return rval;
334:
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;
338:
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;
344: # endif
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)
349: rval = TRUE;
350: }
351: break;
352: }
353: }
354: fclose(fp);
355:
356: return rval;
357: }
358: #endif /* HAVE_GETUTXID || HAVE_GETUTID */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>