]> granicus.if.org Git - shadow/blob - libmisc/utmp.c
* NEWS, libmisc/chowntty.c: Fix a race condition that could lead to
[shadow] / libmisc / utmp.c
1 /*
2  * Copyright (c) 1989 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 1999, Marek Michałkiewicz
4  * Copyright (c) 2001 - 2005, Tomasz Kłoczko
5  * Copyright (c) 2008       , Nicolas François
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the copyright holders or contributors may not be used to
17  *    endorse or promote products derived from this software without
18  *    specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <config.h>
34
35 #include "defines.h"
36 #include "prototypes.h"
37
38 #include <utmp.h>
39
40 #if HAVE_UTMPX_H
41 #include <utmpx.h>
42 #endif
43
44 #include <fcntl.h>
45 #include <stdio.h>
46
47 #ident "$Id$"
48
49 #if HAVE_UTMPX_H
50 struct utmpx utxent;
51 #endif
52 struct utmp utent;
53
54 #define NO_UTENT \
55         _("No utmp entry.  You must exec \"login\" from the lowest level \"sh\"")
56 #define NO_TTY \
57         _("Unable to determine your tty name.")
58
59 /*
60  * checkutmp - see if utmp file is correct for this process
61  *
62  *      System V is very picky about the contents of the utmp file
63  *      and requires that a slot for the current process exist.
64  *      The utmp file is scanned for an entry with the same process
65  *      ID.  If no entry exists the process exits with a message.
66  *
67  *      The "picky" flag is for network and other logins that may
68  *      use special flags.  It allows the pid checks to be overridden.
69  *      This means that getty should never invoke login with any
70  *      command line flags.
71  */
72
73 #if defined(__linux__)          /* XXX */
74
75 void checkutmp (bool picky)
76 {
77         char *line;
78         struct utmp *ut;
79         pid_t pid = getpid ();
80
81         setutent ();
82
83         /* First, try to find a valid utmp entry for this process.  */
84         while ((ut = getutent ()) != NULL)
85                 if (   (ut->ut_pid == pid)
86                     && ('\0' != ut->ut_line[0])
87                     && ('\0' != ut->ut_id[0])
88                     && (   (LOGIN_PROCESS == ut->ut_type)
89                         || (USER_PROCESS  == ut->ut_type))) {
90                         break;
91                 }
92
93         /* If there is one, just use it, otherwise create a new one.  */
94         if (NULL != ut) {
95                 utent = *ut;
96         } else {
97                 if (picky) {
98                         (void) puts (NO_UTENT);
99                         exit (EXIT_FAILURE);
100                 }
101                 line = ttyname (0);
102                 if (NULL == line) {
103                         (void) puts (NO_TTY);
104                         exit (EXIT_FAILURE);
105                 }
106                 if (strncmp (line, "/dev/", 5) == 0) {
107                         line += 5;
108                 }
109                 memset ((void *) &utent, 0, sizeof utent);
110                 utent.ut_type = LOGIN_PROCESS;
111                 utent.ut_pid = pid;
112                 strncpy (utent.ut_line, line, sizeof utent.ut_line);
113                 /* XXX - assumes /dev/tty?? */
114                 strncpy (utent.ut_id, utent.ut_line + 3, sizeof utent.ut_id);
115                 strcpy (utent.ut_user, "LOGIN");
116                 utent.ut_time = time (NULL);
117         }
118 }
119
120 #elif defined(LOGIN_PROCESS)
121
122 void checkutmp (bool picky)
123 {
124         char *line;
125         struct utmp *ut;
126
127 #if HAVE_UTMPX_H
128         struct utmpx *utx;
129 #endif
130         pid_t pid = getpid ();
131
132 #if HAVE_UTMPX_H
133         setutxent ();
134 #endif
135         setutent ();
136
137         if (picky) {
138 #if HAVE_UTMPX_H
139                 while ((utx = getutxent ()) != NULL) {
140                         if (utx->ut_pid == pid) {
141                                 break;
142                         }
143                 }
144
145                 if (NULL != utx) {
146                         utxent = *utx;
147                 }
148 #endif
149                 while ((ut = getutent ()) != NULL) {
150                         if (ut->ut_pid == pid) {
151                                 break;
152                         }
153                 }
154
155                 if (NULL != ut) {
156                         utent = *ut;
157                 }
158
159 #if HAVE_UTMPX_H
160                 endutxent ();
161 #endif
162                 endutent ();
163
164                 if (NULL == ut) {
165                         (void) puts (NO_UTENT);
166                         exit (EXIT_FAILURE);
167                 }
168 #ifndef UNIXPC
169
170                 /*
171                  * If there is no ut_line value in this record, fill
172                  * it in by getting the TTY name and stuffing it in
173                  * the structure.  The UNIX/PC is broken in this regard
174                  * and needs help ...
175                  */
176
177                 if (utent.ut_line[0] == '\0')
178 #endif                          /* !UNIXPC */
179                 {
180                         line = ttyname (0);
181                         if (NULL == line) {
182                                 (void) puts (NO_TTY);
183                                 exit (EXIT_FAILURE);
184                         }
185                         if (strncmp (line, "/dev/", 5) == 0) {
186                                 line += 5;
187                         }
188                         strncpy (utent.ut_line, line, sizeof utent.ut_line);
189 #if HAVE_UTMPX_H
190                         strncpy (utxent.ut_line, line, sizeof utxent.ut_line);
191 #endif
192                 }
193         } else {
194                 line = ttyname (0);
195                 if (NULL == line) {
196                         (void) puts (NO_TTY);
197                         exit (EXIT_FAILURE);
198                 }
199                 if (strncmp (line, "/dev/", 5) == 0) {
200                         line += 5;
201                 }
202
203                 strncpy (utent.ut_line, line, sizeof utent.ut_line);
204                 ut = getutline (&utent);
205                 if (NULL != ut) {
206                         strncpy (utent.ut_id, ut->ut_id, sizeof ut->ut_id);
207                 }
208
209                 strcpy (utent.ut_user, "LOGIN");
210                 utent.ut_pid = getpid ();
211                 utent.ut_type = LOGIN_PROCESS;
212                 utent.ut_time = time (NULL);
213 #if HAVE_UTMPX_H
214                 strncpy (utxent.ut_line, line, sizeof utxent.ut_line);
215                 utx = getutxline (&utxent);
216                 if (NULL != utx) {
217                         strncpy (utxent.ut_id, utx->ut_id, sizeof utxent.ut_id);
218                 }
219
220                 strcpy (utxent.ut_user, "LOGIN");
221                 utxent.ut_pid = utent.ut_pid;
222                 utxent.ut_type = utent.ut_type;
223                 if (sizeof (utxent.ut_tv) == sizeof (struct timeval)) {
224                         gettimeofday ((struct timeval *) &utxent.ut_tv, NULL);
225                 } else {
226                         struct timeval tv;
227
228                         gettimeofday (&tv, NULL);
229                         utxent.ut_tv.tv_sec = tv.tv_sec;
230                         utxent.ut_tv.tv_usec = tv.tv_usec;
231                 }
232                 utent.ut_time = utxent.ut_tv.tv_sec;
233 #endif
234         }
235 }
236
237 #endif
238
239
240 /*
241  * Some systems already have updwtmp() and possibly updwtmpx().  Others
242  * don't, so we re-implement these functions if necessary.  --marekm
243  */
244
245 #ifndef HAVE_UPDWTMP
246 static void updwtmp (const char *filename, const struct utmp *ut)
247 {
248         int fd;
249
250         fd = open (filename, O_APPEND | O_WRONLY, 0);
251         if (fd >= 0) {
252                 write (fd, (const char *) ut, sizeof (*ut));
253                 close (fd);
254         }
255 }
256 #endif                          /* ! HAVE_UPDWTMP */
257
258 #ifdef HAVE_UTMPX_H
259 #ifndef HAVE_UPDWTMPX
260 static void updwtmpx (const char *filename, const struct utmpx *utx)
261 {
262         int fd;
263
264         fd = open (filename, O_APPEND | O_WRONLY, 0);
265         if (fd >= 0) {
266                 write (fd, (const char *) utx, sizeof (*utx));
267                 close (fd);
268         }
269 }
270 #endif                          /* ! HAVE_UPDWTMPX */
271 #endif                          /* ! HAVE_UTMPX_H */
272
273
274 /*
275  * setutmp - put a USER_PROCESS entry in the utmp file
276  *
277  *      setutmp changes the type of the current utmp entry to
278  *      USER_PROCESS.  the wtmp file will be updated as well.
279  */
280
281 #if defined(__linux__)          /* XXX */
282
283 int setutmp (const char *name, const char unused(*line), const char unused(*host))
284 {
285         int err = 0;
286         utent.ut_type = USER_PROCESS;
287         strncpy (utent.ut_user, name, sizeof utent.ut_user);
288         utent.ut_time = time (NULL);
289         /* other fields already filled in by checkutmp above */
290         setutent ();
291         if (pututline (&utent) == NULL) {
292                 err = 1;
293         }
294         endutent ();
295         updwtmp (_WTMP_FILE, &utent);
296
297         return err;
298 }
299
300 #elif HAVE_UTMPX_H
301
302 int setutmp (const char *name, const char *line, const char *host)
303 {
304         struct utmp *utmp, utline;
305         struct utmpx *utmpx, utxline;
306         pid_t pid = getpid ();
307         bool found_utmpx = false;
308         bool found_utmp = false;
309         int err = 0;
310
311         /*
312          * The canonical device name doesn't include "/dev/"; skip it
313          * if it is already there.
314          */
315
316         if (strncmp (line, "/dev/", 5) == 0) {
317                 line += 5;
318         }
319
320         /*
321          * Update utmpx.  We create an empty entry in case there is
322          * no matching entry in the utmpx file.
323          */
324
325         setutxent ();
326         setutent ();
327
328         while ((utmpx = getutxent ()) != NULL) {
329                 if (utmpx->ut_pid == pid) {
330                         found_utmpx = true;
331                         break;
332                 }
333         }
334         while ((utmp = getutent ()) != NULL) {
335                 if (utmp->ut_pid == pid) {
336                         found_utmp = true;
337                         break;
338                 }
339         }
340
341         /*
342          * If the entry matching `pid' cannot be found, create a new
343          * entry with the device name in it.
344          */
345
346         if (!found_utmpx) {
347                 memset ((void *) &utxline, 0, sizeof utxline);
348                 strncpy (utxline.ut_line, line, sizeof utxline.ut_line);
349                 utxline.ut_pid = getpid ();
350         } else {
351                 utxline = *utmpx;
352                 if (strncmp (utxline.ut_line, "/dev/", 5) == 0) {
353                         memmove (utxline.ut_line, utxline.ut_line + 5,
354                                  sizeof utxline.ut_line - 5);
355                         utxline.ut_line[sizeof utxline.ut_line - 5] = '\0';
356                 }
357         }
358         if (!found_utmp) {
359                 memset ((void *) &utline, 0, sizeof utline);
360                 strncpy (utline.ut_line, utxline.ut_line,
361                          sizeof utline.ut_line);
362                 utline.ut_pid = utxline.ut_pid;
363         } else {
364                 utline = *utmp;
365                 if (strncmp (utline.ut_line, "/dev/", 5) == 0) {
366                         memmove (utline.ut_line, utline.ut_line + 5,
367                                  sizeof utline.ut_line - 5);
368                         utline.ut_line[sizeof utline.ut_line - 5] = '\0';
369                 }
370         }
371
372         /*
373          * Fill in the fields in the utmpx entry and write it out.  Do
374          * the utmp entry at the same time to make sure things don't
375          * get messed up.
376          */
377
378         strncpy (utxline.ut_user, name, sizeof utxline.ut_user);
379         strncpy (utline.ut_user, name, sizeof utline.ut_user);
380
381         utline.ut_type = utxline.ut_type = USER_PROCESS;
382
383         if (sizeof (utxline.ut_tv) == sizeof (struct timeval)) {
384                 gettimeofday ((struct timeval *) &utxline.ut_tv, NULL);
385         } else {
386                 struct timeval tv;
387
388                 gettimeofday (&tv, NULL);
389                 utxline.ut_tv.tv_sec = tv.tv_sec;
390                 utxline.ut_tv.tv_usec = tv.tv_usec;
391         }
392         utline.ut_time = utxline.ut_tv.tv_sec;
393
394         strncpy (utxline.ut_host, (NULL != host) ? host : "",
395                  sizeof utxline.ut_host);
396
397         if (   (pututxline (&utxline) == NULL)
398             || (pututline (&utline) == NULL)) {
399                 err = 1;
400         }
401
402         updwtmpx (_WTMP_FILE "x", &utxline);
403         updwtmp (_WTMP_FILE, &utline);
404
405         utxent = utxline;
406         utent = utline;
407
408         return err;
409 }
410
411 #endif