]> granicus.if.org Git - shadow/blob - src/logoutd.c
* src/useradd.c: When exiting because of a failure, warn if an
[shadow] / src / logoutd.c
1 /*
2  * Copyright (c) 1991 - 1993, Julianne Frances Haugh
3  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
4  * Copyright (c) 2001 - 2006, Tomasz Kłoczko
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the copyright holders or contributors may not be used to
16  *    endorse or promote products derived from this software without
17  *    specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
23  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include <config.h>
33
34 #ident "$Id$"
35
36 #include <fcntl.h>
37 #include <signal.h>
38 #include <stdio.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include "defines.h"
42 #include "prototypes.h"
43 /*
44  * Global variables
45  */
46 char *Prog;
47
48 #ifndef DEFAULT_HUP_MESG
49 #define DEFAULT_HUP_MESG _("login time exceeded\n\n")
50 #endif
51
52 #ifndef HUP_MESG_FILE
53 #define HUP_MESG_FILE "/etc/logoutd.mesg"
54 #endif
55
56 /* local function prototypes */
57 #ifdef USE_UTMPX
58 static int check_login (const struct utmpx *ut);
59 #else                           /* !USE_UTMPX */
60 static int check_login (const struct utmp *ut);
61 #endif                          /* !USE_UTMPX */
62 static void send_mesg_to_tty (int tty_fd);
63
64 /*
65  * check_login - check if user (struct utmpx/utmp) allowed to stay logged in
66  */
67 #ifdef USE_UTMPX
68 static int check_login (const struct utmpx *ut)
69 #else                           /* !USE_UTMPX */
70 static int check_login (const struct utmp *ut)
71 #endif                          /* !USE_UTMPX */
72 {
73         char user[sizeof (ut->ut_user) + 1];
74         time_t now;
75
76         /*
77          * ut_user may not have the terminating NUL.
78          */
79         strncpy (user, ut->ut_user, sizeof (ut->ut_user));
80         user[sizeof (ut->ut_user)] = '\0';
81
82         (void) time (&now);
83
84         /*
85          * Check if they are allowed to be logged in right now.
86          */
87         if (!isttytime (user, ut->ut_line, now)) {
88                 return 0;
89         }
90         return 1;
91 }
92
93
94 static void send_mesg_to_tty (int tty_fd)
95 {
96         TERMIO oldt, newt;
97         FILE *mesg_file, *tty_file;
98         int c;
99         bool is_tty;
100
101         tty_file = fdopen (tty_fd, "w");
102         if (NULL == tty_file) {
103                 return;
104         }
105
106         is_tty = (GTTY (tty_fd, &oldt) == 0);
107         if (is_tty) {
108                 /* Suggested by Ivan Nejgebauar <ian@unsux.ns.ac.yu>:
109                    set OPOST before writing the message. */
110                 newt = oldt;
111                 newt.c_oflag |= OPOST;
112                 STTY (tty_fd, &newt);
113         }
114
115         mesg_file = fopen (HUP_MESG_FILE, "r");
116         if (NULL != mesg_file) {
117                 while ((c = getc (mesg_file)) != EOF) {
118                         if (c == '\n') {
119                                 putc ('\r', tty_file);
120                         }
121                         putc (c, tty_file);
122                 }
123                 fclose (mesg_file);
124         } else {
125                 fputs (DEFAULT_HUP_MESG, tty_file);
126         }
127         fflush (tty_file);
128         fclose (tty_file);
129
130         if (is_tty) {
131                 STTY (tty_fd, &oldt);
132         }
133 }
134
135
136 /*
137  * logoutd - logout daemon to enforce /etc/porttime file policy
138  *
139  *      logoutd is started at system boot time and enforces the login
140  *      time and port restrictions specified in /etc/porttime. The
141  *      utmpx/utmp file is periodically scanned and offending users are logged
142  *      off from the system.
143  */
144 int main (int argc, char **argv)
145 {
146         int i;
147         int status;
148         pid_t pid;
149
150 #ifdef USE_UTMPX
151         struct utmpx *ut;
152 #else                           /* !USE_UTMPX */
153         struct utmp *ut;
154 #endif                          /* !USE_UTMPX */
155         char user[sizeof (ut->ut_user) + 1];    /* terminating NUL */
156         char tty_name[sizeof (ut->ut_line) + 6];        /* /dev/ + NUL */
157         int tty_fd;
158
159         if (1 != argc) {
160                 (void) fputs (_("Usage: logoutd\n"), stderr);
161         }
162
163         (void) setlocale (LC_ALL, "");
164         (void) bindtextdomain (PACKAGE, LOCALEDIR);
165         (void) textdomain (PACKAGE);
166
167 #ifndef DEBUG
168         for (i = 0; close (i) == 0; i++);
169
170         setpgrp ();
171
172         /*
173          * Put this process in the background.
174          */
175         pid = fork ();
176         if (pid > 0) {
177                 /* parent */
178                 exit (EXIT_SUCCESS);
179         } else if (pid < 0) {
180                 /* error */
181                 perror ("fork");
182                 exit (EXIT_FAILURE);
183         }
184 #endif                          /* !DEBUG */
185
186         /*
187          * Start syslogging everything
188          */
189         Prog = Basename (argv[0]);
190
191         OPENLOG ("logoutd");
192
193         /*
194          * Scan the utmpx/utmp file once per minute looking for users that
195          * are not supposed to still be logged in.
196          */
197         while (true) {
198
199                 /* 
200                  * Attempt to re-open the utmpx/utmp file. The file is only
201                  * open while it is being used.
202                  */
203 #ifdef USE_UTMPX
204                 setutxent ();
205 #else                           /* !USE_UTMPX */
206                 setutent ();
207 #endif                          /* !USE_UTMPX */
208
209                 /*
210                  * Read all of the entries in the utmpx/utmp file. The entries
211                  * for login sessions will be checked to see if the user
212                  * is permitted to be signed on at this time.
213                  */
214 #ifdef USE_UTMPX
215                 while ((ut = getutxent ()) != NULL)
216 #else                           /* !USE_UTMPX */
217                 while ((ut = getutent ()) != NULL)
218 #endif                          /* !USE_UTMPX */
219                 {
220                         if (ut->ut_type != USER_PROCESS) {
221                                 continue;
222                         }
223                         if (ut->ut_user[0] == '\0') {
224                                 continue;
225                         }
226                         if (check_login (ut)) {
227                                 continue;
228                         }
229
230                         /*
231                          * Put the rest of this in a child process. This
232                          * keeps the scan from waiting on other ports to die.
233                          */
234
235                         pid = fork ();
236                         if (pid > 0) {
237                                 /* parent */
238                                 continue;
239                         } else if (pid < 0) {
240                                 /* failed - give up until the next scan */
241                                 break;
242                         }
243                         /* child */
244
245                         if (strncmp (ut->ut_line, "/dev/", 5) != 0) {
246                                 strcpy (tty_name, "/dev/");
247                         } else {
248                                 tty_name[0] = '\0';
249                         }
250
251                         strcat (tty_name, ut->ut_line);
252 #ifndef O_NOCTTY
253 #define O_NOCTTY 0
254 #endif
255                         tty_fd =
256                             open (tty_name, O_WRONLY | O_NDELAY | O_NOCTTY);
257                         if (tty_fd != -1) {
258                                 send_mesg_to_tty (tty_fd);
259                                 close (tty_fd);
260                                 sleep (10);
261                         }
262
263                         if (ut->ut_pid > 1) {
264                                 kill (-ut->ut_pid, SIGHUP);
265                                 sleep (10);
266                                 kill (-ut->ut_pid, SIGKILL);
267                         }
268
269                         strncpy (user, ut->ut_user, sizeof (user) - 1);
270                         user[sizeof (user) - 1] = '\0';
271
272                         SYSLOG ((LOG_NOTICE,
273                                  "logged off user '%s' on '%s'", user,
274                                  tty_name));
275
276                         /*
277                          * This child has done all it can, drop dead.
278                          */
279                         exit (EXIT_SUCCESS);
280                 }
281
282 #ifdef USE_UTMPX
283                 endutxent ();
284 #else                           /* !USE_UTMPX */
285                 endutent ();
286 #endif                          /* !USE_UTMPX */
287
288 #ifndef DEBUG
289                 sleep (60);
290 #endif
291                 /*
292                  * Reap any dead babies ...
293                  */
294                 while (wait (&status) != -1);
295         }
296
297         return EXIT_FAILURE;
298 }
299