Imported Upstream version 1.8.4p4
[debian/sudo] / src / utmp.c
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     debug_decl(utmp_setid, SUDO_DEBUG_UTMP)
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);
115
116     debug_return;
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;
127     debug_decl(utmp_settime, SUDO_DEBUG_UTMP)
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
137
138     debug_return;
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 {
148     debug_decl(utmp_file, SUDO_DEBUG_UTMP)
149
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
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}utent()/{get,put}getutxent().
180  *
181  *  Legacy: sparse file indexed by ttyslot() * sizeof(struct utmp)
182  */
183 #if 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 rval = 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     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)
207         rval = true;
208     endutxent();
209
210     debug_return_bool(rval);
211 }
212
213 bool
214 utmp_logout(const char *line, int status)
215 {
216     bool rval = false;
217     sudo_utmp_t *ut, utbuf;
218     debug_decl(utmp_logout, SUDO_DEBUG_UTMP)
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)
237             rval = true;
238     }
239     debug_return_bool(rval);
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;
255     debug_decl(utmp_slot, SUDO_DEBUG_UTMP)
256
257     setttyent();
258     while ((tty = getttyent()) != NULL) {
259         if (strcmp(line, tty->ty_name) == 0)
260             break;
261         slot++;
262     }
263     endttyent();
264     debug_return_int(tty ? slot : 0);
265 }
266 # else
267 static int
268 utmp_slot(const char *line, int ttyfd)
269 {
270     int sfd, slot;
271     debug_decl(utmp_slot, SUDO_DEBUG_UTMP)
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
286     debug_return_int(slot);
287 }
288 # endif /* HAVE_GETTTYENT */
289
290 bool
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;
295     bool rval = false;
296     int slot;
297     FILE *fp;
298     debug_decl(utmp_login, SUDO_DEBUG_UTMP)
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)
332             rval = true;
333     }
334     fclose(fp);
335
336 done:
337     debug_return_bool(rval);
338 }
339
340 bool
341 utmp_logout(const char *line, int status)
342 {
343     sudo_utmp_t utbuf;
344     bool rval = false;
345     FILE *fp;
346     debug_decl(utmp_logout, SUDO_DEBUG_UTMP)
347
348     if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
349         debug_return_int(rval);
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)
365                     rval = true;
366             }
367             break;
368         }
369     }
370     fclose(fp);
371
372     debug_return_bool(rval);
373 }
374 #endif /* HAVE_GETUTXID || HAVE_GETUTID */