├── Makefile ├── README ├── VERSION ├── debian ├── changelog ├── compat ├── control ├── copyright ├── rules ├── timeoutd.docs ├── timeoutd.init.d ├── timeoutd.install └── timeoutd.manpages ├── dump_wtmp.c ├── timeoutd.8 ├── timeoutd.c ├── timeouts └── timeouts.5 /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-fomit-frame-pointer -O2 -s -g -Wall 2 | timeoutd: timeoutd.c Makefile 3 | #$(CC) $(CFLAGS) -o timeoutd timeoutd.c 4 | $(CC) $(CFLAGS) -o timeoutd.o -c timeoutd.c -DTIMEOUTDX11 5 | $(CC) $(CFLAGS) -o timeoutd -L/usr/X11R6/lib timeoutd.o -lXss -lXext 6 | 7 | 8 | install: 9 | install -o root -g system -m 2111 timeoutd /usr/etc/timeoutd 10 | install -o man -g info -m 444 timeoutd.8 /usr/man/man8 11 | install -o man -g info -m 444 timeouts.5 /usr/man/man5 12 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | TIMEOUTD 1.5 by Shane Alderton 2 | 3 | Timeoutd is a programme which allows you to control the following 4 | characteristics on a user by user and/or group by group basis for 5 | each tty on your system: 6 | 7 | - maximum idle time 8 | - maximum time per session 9 | - maximum time per day 10 | - times when people can/can't login on specific ttys 11 | 12 | To build timeoutd, you should make any changes to the makefile for 13 | your preferred compilation options, then simply: 14 | 15 | make 16 | 17 | The next step is to install a timeouts file in /usr/etc specifying 18 | the parameters for each line/user/group combination. You can use 19 | the sample file provided in the distribution as a starting point 20 | after reading the timeoutd.8 and timeouts.5 man pages. 21 | 22 | Once you have installed the timeouts file in /usr/etc, you can type: 23 | 24 | make install 25 | 26 | to install the timeoutd binaries and man pages. 27 | 28 | Then it is just a matter of running /usr/etc/timeoutd. You may want 29 | to add a line to your /etc/rc or /etc/rc.local (or whatever) to run 30 | timeoutd at boot time. 31 | 32 | If you wish, you can also modify your login programme to have timeoutd 33 | run at login time to check whether each user is allowed to login or not. 34 | Otherwise, users who are not allowed to login will be logged off within 35 | 1 minute of logging in. 36 | 37 | Another (albeit less certain) way of doing this is to put the following 38 | line in /etc/profile near the top of the file: 39 | 40 | /usr/etc/timeoutd `whoami` `basename \`tty\`` || exit 41 | 42 | 43 | Please sends bugs, comments, suggestions to: 44 | shanea@bigpond.net.au (Shane Alderton) 45 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.5 2 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | timeoutd (1.5-10.1ubuntu1~1.gbp45a983) UNRELEASED; urgency=low 2 | 3 | ** SNAPSHOT build @45a9836343ec6efa904b51360713c52fb623d6d8 ** 4 | 5 | * UNRELEASED 6 | * Fixed local X session handling 7 | 8 | -- Shawn Willden Sun, 04 Jan 2009 23:17:57 -0700 9 | 10 | timeoutd (1.5-10.1) unstable; urgency=low 11 | 12 | * Non-maintainer upload. 13 | * Added LSB formatted dependency info in init.d script (closes: #468919) 14 | * Removed redundant debian/init.d file 15 | 16 | -- Peter Eisentraut Wed, 02 Apr 2008 02:15:25 +0200 17 | 18 | timeoutd (1.5-10) unstable; urgency=low 19 | 20 | * Updating build depencies due to xlibs-dev: Closes: #346924 21 | * Thanks Mark Hindley for patches: Closes: #347780 22 | o Don't crash when X server is not contactable 23 | o Recognise remote X sessions 24 | o XCloseDisplay to free resources 25 | o Rename shutdown() to prevent conflict with netdb.h 26 | 27 | -- Dennis Stampfer Fri, 20 Jan 2006 20:39:25 +0100 28 | 29 | timeoutd (1.5-9) unstable; urgency=low 30 | 31 | * Removed "-m486" because it breaks builds on other archs. 32 | Closes: #231522 33 | 34 | -- Dennis Stampfer Mon, 1 Mar 2004 20:56:13 +0100 35 | 36 | timeoutd (1.5-8) unstable; urgency=low 37 | 38 | * timeoutd now complains in logs when segfaulting 39 | * Improofed SSH-Handling 40 | - Wait a few secs to read message before logging out 41 | * No more warnings when compiling 42 | * Now depends on xlibs 43 | - timeoutd is able to querry X for idle time. Closes: #222287 44 | * Improofed description 45 | * Corrected debian/copyright 46 | * Conforms to Standards version 3.6.1 47 | 48 | -- Dennis Stampfer Fri, 2 Jan 2004 00:11:09 +0100 49 | 50 | timeoutd (1.5-7) unstable; urgency=low 51 | 52 | * Upstream author's adress outdated. Changed address. 53 | * Restrictions are now possible for users running X. 54 | * telnet sessions are handled like tty. Closes: #194917 55 | * ssh restrictions are possible from now on. 56 | * Conforms to Standards version 3.6.0 57 | 58 | -- Dennis Stampfer Sun, 01 Jun 2003 16:27:00 +0200 59 | 60 | timeoutd (1.5-6) unstable; urgency=low 61 | 62 | * timeoutd runs now also when a X-Session is running. Closes: #191575 63 | * Conforms to Standards version 3.5.9.0 64 | 65 | -- Dennis Stampfer Sat, 10 May 2003 15:28:35 +0200 66 | 67 | timeoutd (1.5-5) unstable; urgency=low 68 | 69 | * New Maintainer. Closes: #158333 70 | * Conforms to Standards version 3.5.8.0 71 | * Improved description. 72 | 73 | -- Dennis Stampfer Wed, 4 Dec 2002 18:32:37 +0100 74 | 75 | timeoutd (1.5-4) unstable; urgency=low 76 | 77 | * QA upload. 78 | * Package is orphaned (see #158333); set maintainer to Debian QA Group. 79 | * timeoutd.c: 80 | - Determine utmp field sizes with sizeof instead of using traditional 81 | values. Closes: #52456. 82 | - Open terminal device with O_NONBLOCK. Closes: #30980. 83 | - Do `chdir("/dev")' to avoid `device busy' errors when the filesystem 84 | from which we were started is unmounted. 85 | - Fix some more warnings. 86 | * timeouts: Add format summary. Closes: #126543. 87 | * Drop dump_wtmp -- last(1) is so much better. Closes: #22775. 88 | * Why was timeoutd setgid root? It must be run as root anyway to kill 89 | other users' processes... 90 | * Add build dependencies. Closes: #95039. 91 | * Switch from debstd to debhelper. 92 | * debian/copyright: 93 | - Note that the upstream URL is outdated and that the author's e-mail 94 | bounces. Closes: #131085. 95 | - Refer to /usr/share/common-licenses/GPL. 96 | * debian/init.d: Update from current /etc/init.d/skeleton. Uses a delay 97 | between starting and stopping. Closes: #30979. 98 | * debian/README.debian: Everything already said in copyright; remove. 99 | * debian/changelog: Remove obsolete Emacs local variables. 100 | * Conforms to Standards version 3.5.6. 101 | 102 | -- Matej Vela Thu, 29 Aug 2002 04:26:45 +0200 103 | 104 | timeoutd (1.5-3) unstable; urgency=low 105 | 106 | * Non-Maintainer upload 107 | * rebuild to move /usr/doc to /usr/share/doc (Closes: #91677) 108 | * indented Description 109 | * updated FSF address in copyright 110 | * marked init.d script as conffile 111 | * added isp flags to dpkg-gencontrol to create Section: and Priority: 112 | fields 113 | * tweaked init.d script to provide a force-reload 114 | * upped standards version 115 | 116 | -- Stephen Stafford Sat, 21 Apr 2001 22:26:37 +0100 117 | 118 | timeoutd (1.5-2) unstable; urgency=low 119 | 120 | * recompiled for libc6 121 | * now includes dump_wtmp binary 122 | * cleaned up some compiler warnings 123 | * added reload and restart args to /etc/init.d/timeoutd 124 | 125 | -- Craig Sanders Sat, 8 Nov 1997 13:18:47 +1100 126 | 127 | timeoutd (1.5-1) unstable; urgency=low 128 | 129 | * Initial Release. 130 | 131 | -- Craig Sanders Sat, 22 Feb 1997 15:20:11 +1100 132 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 4 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: timeoutd 2 | Section: admin 3 | Priority: extra 4 | Maintainer: Dennis Stampfer 5 | Standards-Version: 3.6.2 6 | Build-Depends: debhelper (>= 4), libx11-dev, libxss-dev 7 | 8 | Package: timeoutd 9 | Architecture: any 10 | Depends: ${shlibs:Depends} 11 | Conflicts: suidmanager (<< 0.50) 12 | Description: Flexible user timeout daemon with X11 support 13 | timeoutd enforces the time restrictions specified for each or all users. 14 | . 15 | timeoutd scans /var/run/utmp every minute and checks /etc/timeouts for 16 | an entry which matches a restricted user, based on: 17 | . 18 | - The current day and time 19 | - The tty that the user is currently logged in on 20 | - The user's login ID 21 | - Any primary or secondary groups the user is in 22 | timeoutd can restrict local users, X11-users and users via telnet/SSH 23 | for a maximum of their session, max. day, idle or no login at all. 24 | . 25 | timeoutd is also able to restrict users running X. 26 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | This package was debianized by Craig Sanders on 2 | Sat, 22 Feb 1997 15:20:11 +1100. 3 | 4 | It is maintained by Dennis Stampfer since 5 | Wed, 4 Dec 2002 18:32:37 +0100 6 | 7 | It was downloaded from ion.apana.org.au. Unfortunately, this address is 8 | outdated. It is not developed upstream anymore. 9 | 10 | Author: Shane Alderton 11 | 12 | Copyright 1997 Shane Alderton 13 | 14 | Maintainers worked on timeoutd for Debian: 15 | 1997 Craig Sanders 16 | 2001 Stephen Stafford 17 | 2002 Matej Vela 18 | 2002-2006 Dennis Stampfer 19 | 20 | This program is free software; you can redistribute it and/or modify 21 | it under the terms of the GNU General Public License as published by 22 | the Free Software Foundation; either version 2 of the License, or 23 | (at your option) any later version. 24 | 25 | This program is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 | GNU General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public License 31 | along with this program; if not, write to the Free Software 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 33 | 34 | On Debian systems, the complete text of the GNU General Public License 35 | can be found in /usr/share/common-licenses/GPL. 36 | 37 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # Sample debian/rules that uses debhelper. 3 | # This file is public domain software, originally written by Joey Hess. 4 | 5 | # Uncomment this to turn on verbose mode. 6 | #export DH_VERBOSE=1 7 | 8 | build: 9 | $(MAKE) timeoutd 10 | 11 | clean: 12 | dh_testdir 13 | dh_testroot 14 | 15 | # Add here commands to clean up after the build process. 16 | rm -f timeoutd timeoutd.o 17 | 18 | dh_clean 19 | 20 | install: build 21 | dh_testdir 22 | dh_testroot 23 | dh_clean -k 24 | dh_installdirs 25 | 26 | # Add here commands to install the package into debian/ 27 | dh_install 28 | 29 | # Build architecture-independent files here. 30 | binary-indep: build install 31 | # We have nothing to do by default. 32 | 33 | # Build architecture-dependent files here. 34 | binary-arch: build install 35 | dh_testdir 36 | dh_testroot 37 | dh_installchangelogs 38 | dh_installdocs 39 | # dh_installexamples 40 | # dh_installmenu 41 | # dh_installdebconf 42 | # dh_installlogrotate 43 | # dh_installemacsen 44 | # dh_installpam 45 | # dh_installmime 46 | dh_installinit 47 | # dh_installcron 48 | # dh_installinfo 49 | # dh_undocumented 50 | dh_installman 51 | # dh_link 52 | dh_strip 53 | dh_compress 54 | dh_fixperms 55 | # dh_makeshlibs 56 | dh_installdeb 57 | # dh_perl 58 | dh_shlibdeps 59 | dh_gencontrol 60 | dh_md5sums 61 | dh_builddeb 62 | 63 | binary: binary-indep binary-arch 64 | .PHONY: build clean binary-indep binary-arch binary install 65 | -------------------------------------------------------------------------------- /debian/timeoutd.docs: -------------------------------------------------------------------------------- 1 | README 2 | -------------------------------------------------------------------------------- /debian/timeoutd.init.d: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: timeoutd 4 | # Required-Start: $remote_fs $syslog 5 | # Required-Stop: $remote_fs $syslog 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: start and stop the user timeout daemon 9 | ### END INIT INFO 10 | # 11 | # Based on skeleton 1.9.1 by Miquel van Smoorenburg . 12 | 13 | DAEMON=/usr/sbin/timeoutd 14 | NAME=timeoutd 15 | DESC="user timeout daemon" 16 | 17 | test -x $DAEMON || exit 0 18 | 19 | set -e 20 | 21 | case "$1" in 22 | start) 23 | echo -n "Starting $DESC: $NAME" 24 | start-stop-daemon --start --oknodo --quiet --exec $DAEMON 25 | echo "." 26 | ;; 27 | stop) 28 | echo -n "Stopping $DESC: $NAME" 29 | start-stop-daemon --stop --oknodo --quiet --exec $DAEMON 30 | echo "." 31 | ;; 32 | reload|force-reload) 33 | echo -n "Reloading $DESC configuration..." 34 | start-stop-daemon --stop --signal 1 --quiet --exec $DAEMON 35 | echo "done." 36 | ;; 37 | restart) 38 | echo -n "Restarting $DESC: $NAME" 39 | start-stop-daemon --stop --oknodo --quiet --exec $DAEMON 40 | sleep 1 41 | start-stop-daemon --start --quiet --exec $DAEMON 42 | echo "." 43 | ;; 44 | *) 45 | N=/etc/init.d/$NAME 46 | echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2 47 | exit 1 48 | ;; 49 | esac 50 | 51 | exit 0 52 | -------------------------------------------------------------------------------- /debian/timeoutd.install: -------------------------------------------------------------------------------- 1 | timeoutd usr/sbin 2 | timeouts etc 3 | -------------------------------------------------------------------------------- /debian/timeoutd.manpages: -------------------------------------------------------------------------------- 1 | timeoutd.8 timeouts.5 2 | -------------------------------------------------------------------------------- /dump_wtmp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | main() 6 | { 7 | FILE *fp; 8 | struct utmp ut; 9 | struct tm *tm; 10 | char user[9]; 11 | char host[17]; 12 | char line[13]; 13 | 14 | if ((fp = fopen(UTMP_FILE, "r")) == NULL) 15 | { 16 | printf("Could not open wtmp file!"); 17 | exit(1); 18 | } 19 | 20 | /* Go to end of file minus one structure */ 21 | fseek(fp, -1L * sizeof(struct utmp), SEEK_END); 22 | 23 | while (fread(&ut, sizeof(struct utmp), 1, fp) == 1) 24 | { 25 | tm = localtime(&ut.ut_time); 26 | 27 | /* 28 | if (tm->tm_year != now.tm_year || tm->tm_yday != now.tm_yday) 29 | break; 30 | */ 31 | 32 | printf("%02d:%02d type=", tm->tm_hour,tm->tm_min); 33 | switch (ut.ut_type) 34 | { 35 | #ifndef SUNOS 36 | case RUN_LVL: printf("RUN_LVL"); 37 | break; 38 | case BOOT_TIME: printf("BOOT_TIME"); 39 | break; 40 | case NEW_TIME: printf("NEW_TIME"); 41 | break; 42 | case OLD_TIME: printf("OLD_TIME"); 43 | break; 44 | case INIT_PROCESS: printf("INIT_PROCESS"); 45 | break; 46 | case LOGIN_PROCESS: printf("LOGIN_PROCESS"); 47 | break; 48 | case USER_PROCESS: printf("USER_PROCESS"); 49 | break; 50 | case DEAD_PROCESS: printf("DEAD_PROCESS"); 51 | break; 52 | #endif 53 | default: printf("UNKNOWN!(type=%d)", ut.ut_type); 54 | } 55 | strncpy(user, ut.ut_user, 8); 56 | user[8] = 0; 57 | strncpy(host, ut.ut_host, 16); 58 | host[16] = 0; 59 | strncpy(line, ut.ut_line, 12); 60 | line[12] = 0; 61 | printf(" line=%s host=%s user=%s\n", line, host, user); 62 | 63 | 64 | /* Position the file pointer 2 structures back */ 65 | if (fseek(fp, -2 * sizeof(struct utmp), SEEK_CUR) < 0) break; 66 | } 67 | fclose(fp); 68 | } 69 | -------------------------------------------------------------------------------- /timeoutd.8: -------------------------------------------------------------------------------- 1 | .TH TIMEOUTD 8 2 | .SH NAME 3 | timeoutd \- Enforce idle and session time restrictions 4 | .SH SYNOPSIS 5 | .B /usr/sbin/timeoutd [ user tty ] 6 | .SH DESCRIPTION 7 | .B timeoutd 8 | enforces the time restrictions specified in 9 | .IR /etc/timeouts . 10 | When invoked in daemon mode (without any parameters) timeoutd backgrounds 11 | itself, then scans \fB/var/run/utmp\fR every minute and checks \fB/etc/timeouts\fR 12 | for an entry which matches that user, based on: 13 | .IP "\- The current day and time" 14 | .IP "\- The tty that the user is currently logged in on" 15 | .IP "\- The user's login ID" 16 | .IP "\- Any primary or secondary groups the user is in" 17 | .PP 18 | If a match is found, the limits specified for that entry are enforced by 19 | sending a 20 | .B SIGHUP 21 | (Hangup signal) to the user's login process, followed 22 | after 5 seconds by a 23 | .B SIGKILL 24 | (Sure kill signal) to ensure the user is 25 | logged out. 26 | .PP 27 | Where possible, 28 | .B timeoutd 29 | will send a warning to the user 30 | every minute for 5 minutes (or other time specified in 31 | .IR /etc/timeouts ) 32 | before logging them out. Warnings are not sent for exceeded idle limits, 33 | as this would count as activity on the terminal. 34 | .PP 35 | Timeoutd currently allows limits to be set on idle time as well as amount 36 | of time logged in per session and per day. 37 | .PP 38 | When calculating idle time, any activity on the terminal, either incoming 39 | (such as typing) or outgoing (such as information displayed on the screen) 40 | is counted as activity. This is to prevent log-offs during file transfers. 41 | .PP 42 | Under Linux, 43 | .B timeoutd 44 | detects when a serial line is in SLIP mode and disables 45 | idle time limit checking (as the last read/write times for the tty are 46 | not updated). 47 | .PP 48 | Debug information, error messages and notification of users who have been 49 | timed out are all recorded via syslog (facility=DAEMON). 50 | .PP 51 | .B timeoutd 52 | can also be invoked by login to check whether a user is allowed 53 | to login at that time, or whether they have exceeded their daily time limit. 54 | When invoked in this way, by passing a username and tty (without the leading 55 | /dev) on the command line, 56 | .B timeoutd 57 | returns one of the following exit codes: 58 | .IP "0 User is allowed to login 59 | .IP "1 Fatal error 60 | .IP "5 Incorrect command line format 61 | .IP "10 User has exceeded maximum daily connect time 62 | .IP "20 User not permitted to login at this time on this tty 63 | .IP "30 Internal error checking user name (probably invalid user name) 64 | .SH FILES 65 | .IP "/etc/timeouts \- lists valid login times and idle/session time restrictions 66 | .IP "/var/run/utmp \- current login sessions 67 | .IP "/var/log/wtmp \- for calculating total logged in time for current day 68 | .SH BUGS 69 | Sessions which end in the current day but started before midnight 70 | will not be considered when calculating total daily logged in time for a 71 | user on that day. This will not, however, affect checking of the 72 | session limit, which should limit such problems. It does 73 | mean that a user could conceivably exceed their maximum daily time 74 | by one extra session if they log on just before midnight. 75 | .SH "SEE ALSO" 76 | .BR timeouts "(5) 77 | .SH "WRITTEN BY" 78 | Orginally written by Shane Alderton , updated by 79 | Dennis Stampfer . 80 | -------------------------------------------------------------------------------- /timeoutd.c: -------------------------------------------------------------------------------- 1 | /* 2 | "@(#) timeoutd.c 1.6 by Shane Alderton" 3 | based on: 4 | "@(#) autologout.c by David Dickson" 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | 20 | Thanks to: 21 | David Dickson for writing the original autologout.c 22 | programme upon which this programme was based. 23 | 24 | */ 25 | /* #define DEBUG _DEBUG_ */ 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #ifdef TIMEOUTDX11 47 | #include 48 | #include 49 | #include 50 | 51 | #define TIMEOUTD_XSESSION_NONE 0 52 | #define TIMEOUTD_XSESSION_LOCAL 1 53 | #define TIMEOUTD_XSESSION_REMOTE 2 54 | #endif 55 | 56 | #define OPENLOG_FLAGS LOG_CONS|LOG_PID 57 | #define SYSLOG_DEBUG LOG_DEBUG 58 | 59 | /* For those systems (SUNOS) which don't define this: */ 60 | #ifndef WTMP_FILE 61 | #define WTMP_FILE "/usr/adm/wtmp" 62 | #endif 63 | 64 | #ifdef SUNOS 65 | #define ut_pid ut_time 66 | #define ut_user ut_name 67 | #define SEEK_CUR 1 68 | #define SEEK_END 2 69 | #define SURE_KILL 1 70 | 71 | FILE *utfile = NULL; 72 | #define NEED_UTMP_UTILS 73 | #define NEED_STRSEP 74 | #endif 75 | 76 | 77 | #ifdef NEED_UTMP_UTILS 78 | void setutent() 79 | { 80 | if (utfile == NULL) 81 | { 82 | if ((utfile = fopen("/etc/utmp", "r")) == NULL) 83 | { 84 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 85 | syslog(LOG_ERR, "Could not open /etc/utmp"); 86 | closelog(); 87 | exit(1); 88 | } 89 | } 90 | else fseek(utfile, 0L, 0); 91 | } 92 | 93 | struct utmp *getutent() /* returns next utmp file entry */ 94 | { 95 | static struct utmp uent; 96 | 97 | while (fread(&uent, sizeof(struct utmp), 1, utfile) == 1) 98 | { 99 | if (uent.ut_line[0] != 0 && uent.ut_name[0] != 0) 100 | return &uent; 101 | } 102 | return (struct utmp *) NULL; 103 | } 104 | #endif 105 | 106 | #ifndef linux 107 | #define N_TTY 1 108 | #define N_SLIP 2 109 | #endif 110 | 111 | #ifndef CONFIG 112 | #define CONFIG "/etc/timeouts" 113 | #endif 114 | 115 | #define MAXLINES 512 116 | #define max(a,b) ((a)>(b)?(a):(b)) 117 | 118 | #define ACTIVE 1 119 | #define IDLEMAX 2 120 | #define SESSMAX 3 121 | #define DAYMAX 4 122 | #define NOLOGIN 5 123 | /*#define XSESSION 6*/ 124 | #define IDLEMSG 0 125 | #define SESSMSG 1 126 | #define DAYMSG 2 127 | #define NOLOGINMSG 3 128 | 129 | #define KWAIT 5 /* Time to wait after sending a kill signal */ 130 | 131 | char *limit_names[] = {"idle", "session", "daily", "nologin"}; 132 | 133 | char *daynames[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA", "WK", "AL", NULL}; 134 | char daynums[] = { 1 , 2 , 4 , 8 , 16 , 32 , 64 , 62 , 127, 0}; 135 | 136 | struct utmp *utmpp; /* pointer to utmp file entry */ 137 | char *ctime(); /* returns pointer to time string */ 138 | struct utmp *getutent(); /* returns next utmp file entry */ 139 | void shut_down(); 140 | void read_config(); 141 | void reread_config(); 142 | void reapchild(); 143 | void free_wtmp(); 144 | void check_idle(); 145 | void read_wtmp(); 146 | void bailout(); 147 | char chk_timeout(); 148 | void logoff_msg(); 149 | void killit(); 150 | int getdisc(); 151 | int chk_xsession(); /* seppy: is it a X-Session? */ 152 | void killit_xsession(); /* seppy: kill the X-Session*/ 153 | int chk_ssh(pid_t pid); /* seppy: check if user is logged in via ssh (we have to 154 | handle that different... ;( */ 155 | char *getusr(pid_t pid); /*seppy: get the owner of a running process */ 156 | void segfault(); /* seppy: catch segfault and log them */ 157 | int chk_xterm(); /* seppy: is it a xterm? */ 158 | pid_t getcpid(); /* seppy: get the child's pid. Needed for ssh */ 159 | 160 | #ifdef TIMEOUTDX11 161 | Time get_xidle(); /* seppy: how long is user idle? (user,display)*/ 162 | #endif 163 | 164 | 165 | struct ut_list { 166 | struct utmp elem; 167 | struct ut_list *next; 168 | }; 169 | 170 | struct ut_list *wtmplist = (struct ut_list *) NULL; 171 | struct ut_list *ut_list_p; 172 | 173 | struct time_ent { 174 | int days; 175 | int starttime; 176 | int endtime; 177 | }; 178 | 179 | struct config_ent { 180 | struct time_ent *times; 181 | char *ttys; 182 | char *users; 183 | char *groups; 184 | char login_allowed; 185 | int idlemax; 186 | int sessmax; 187 | int daymax; 188 | int warntime; 189 | char *messages[10]; 190 | }; 191 | 192 | struct config_ent *config[MAXLINES + 1]; 193 | char errmsg[256]; 194 | char dev[sizeof(utmpp->ut_line)]; 195 | unsigned char limit_type; 196 | int configline = 0; 197 | int pending_reread = 0; 198 | int allow_reread = 0; 199 | time_t time_now; 200 | struct tm now; 201 | int now_hhmm; 202 | int daytime = 0; /* Amount of time a user has been on in current day */ 203 | char path[255]; /*seppy*/ 204 | FILE *proc_file;/*seppy*/ 205 | char comm[16]; /*seppy; to save the command of a pid*/ 206 | 207 | #ifdef NEED_STRCASECMP 208 | int strcasecmp(char *s1, char *s2) 209 | { 210 | while (*s1 && *s2) 211 | { 212 | if (tolower(*s1) < tolower(*s2)) 213 | return -1; 214 | else if (tolower(*s1) > tolower(*s2)) 215 | return 1; 216 | s1++; 217 | s2++; 218 | } 219 | if (*s1) 220 | return -1; 221 | if (*s2) 222 | return 1; 223 | return 0; 224 | } 225 | #endif 226 | 227 | #ifdef NEED_STRSEP 228 | char *strsep (stringp, delim) 229 | char **stringp; 230 | char *delim; 231 | { 232 | char *retp = *stringp; 233 | char *p; 234 | 235 | if (!**stringp) return NULL; 236 | 237 | while (**stringp) 238 | { 239 | p = delim; 240 | while (*p) 241 | { 242 | if (*p == **stringp) 243 | { 244 | **stringp = '\0'; 245 | (*stringp)++; 246 | return retp; 247 | } 248 | p++; 249 | } 250 | (*stringp)++; 251 | } 252 | return retp; 253 | } 254 | #endif 255 | 256 | int main(argc, argv) 257 | int argc; 258 | char *argv[]; 259 | { 260 | signal(SIGTERM, shut_down); 261 | signal(SIGHUP, reread_config); 262 | signal(SIGCHLD, reapchild); 263 | signal(SIGINT, SIG_IGN); 264 | signal(SIGQUIT, SIG_IGN); 265 | signal(SIGSEGV, segfault); 266 | 267 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 268 | 269 | /* The only valid invocations are "timeoutd" or "timeoutd user tty" */ 270 | if (argc != 1 && argc != 3) 271 | { 272 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 273 | syslog(LOG_ERR, "Incorrect invocation of timeoutd (argc=%d) by UID %d.", argc, getuid()); 274 | closelog(); 275 | exit(5); 276 | } 277 | 278 | /* read config file into memory */ 279 | read_config(); 280 | 281 | /* Change into the root filesystem to avoid "device busy" errors when the 282 | * filesystem from which we were started is unmounted. /dev is convenient as 283 | * ut_line fields are relative to it. 284 | */ 285 | if (chdir("/dev")) 286 | { 287 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 288 | syslog(LOG_ERR, "Could not change working directory to /dev!"); 289 | closelog(); 290 | exit(1); 291 | } 292 | 293 | /* Handle the "timeoutd user tty" invocation */ 294 | /* This is a bit of a shameless hack, but, well, it works. */ 295 | if (argc == 3) 296 | { 297 | #ifdef DEBUG 298 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 299 | syslog(SYSLOG_DEBUG, "Running in user check mode. Checking user %s on %s.", argv[1], argv[2]); 300 | closelog(); 301 | #endif 302 | strncpy(dev, argv[2], sizeof(dev) - 1); 303 | dev[sizeof(dev) - 1] = '\0'; 304 | time_now = time((time_t *)0); /* get current time */ 305 | now = *(localtime(&time_now)); /* Break it into bits */ 306 | now_hhmm = now.tm_hour * 100 + now.tm_min; 307 | allow_reread = 0; 308 | read_wtmp(); /* Read in today's wtmp entries */ 309 | switch(chk_timeout(argv[1], dev, "", 0, 0)) 310 | { 311 | case DAYMAX: 312 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 313 | syslog(LOG_NOTICE, 314 | "User %s on %s exceeded maximum daily limit (%d minutes). Login check failed.", 315 | argv[1], argv[2], config[configline]->daymax); 316 | closelog(); 317 | /* 318 | printf("\r\nLogin not permitted. You have exceeded your maximum daily limit.\r\n"); 319 | printf("Please try again tomorrow.\r\n"); 320 | */ 321 | logoff_msg(1); 322 | exit(10); 323 | case NOLOGIN: 324 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 325 | syslog(LOG_NOTICE, 326 | "User %s not allowed to login on %s at this time. Login check failed.", 327 | argv[1], argv[2]); 328 | closelog(); 329 | /* 330 | printf("\r\nLogin not permitted at this time. Please try again later.\r\n"); 331 | */ 332 | logoff_msg(1); 333 | exit(20); 334 | case ACTIVE: 335 | #ifdef DEBUG 336 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 337 | syslog(SYSLOG_DEBUG, "User %s on %s passed login check.", argv[1], argv[2]); 338 | closelog(); 339 | #endif 340 | free_wtmp(); 341 | exit(0); 342 | default: 343 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 344 | syslog(LOG_ERR, "Internal error checking user %s on %s - unexpected return from chk_timeout", 345 | argv[1], argv[2]); 346 | closelog(); 347 | exit(30); 348 | } 349 | } 350 | 351 | /* If running in daemon mode (no parameters) */ 352 | if (fork()) /* the parent process */ 353 | exit(0); /* exits */ 354 | 355 | close(0); 356 | close(1); 357 | close(2); 358 | setsid(); 359 | 360 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 361 | syslog(LOG_NOTICE, "Daemon started."); 362 | closelog(); 363 | 364 | /* the child processes all utmp file entries: */ 365 | while (1) 366 | { 367 | /* Record time at which we woke up & started checking */ 368 | time_now = time((time_t *)0); /* get current time */ 369 | now = *(localtime(&time_now)); /* Break it into bits */ 370 | now_hhmm = now.tm_hour * 100 + now.tm_min; 371 | allow_reread = 0; 372 | read_wtmp(); /* Read in today's wtmp entries */ 373 | setutent(); 374 | #ifdef DEBUG 375 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 376 | syslog(SYSLOG_DEBUG, "Time to check utmp for exceeded limits."); 377 | closelog(); 378 | #endif 379 | while ((utmpp = getutent()) != (struct utmp *) NULL) 380 | check_idle(); 381 | free_wtmp(); /* Free up memory used by today's wtmp entries */ 382 | allow_reread = 1; 383 | if (pending_reread) 384 | reread_config(SIGHUP); 385 | #ifdef DEBUG 386 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 387 | syslog(SYSLOG_DEBUG, "Finished checking utmp... sleeping for 1 minute."); 388 | closelog(); 389 | #endif 390 | sleep(60); 391 | } 392 | } 393 | 394 | /* Read in today's wtmp entries */ 395 | 396 | void read_wtmp() 397 | { 398 | FILE *fp; 399 | struct utmp ut; 400 | struct tm *tm; 401 | 402 | #ifdef DEBUG 403 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 404 | syslog(SYSLOG_DEBUG, "Reading today's wtmp entries."); 405 | closelog(); 406 | #endif 407 | 408 | if ((fp = fopen(WTMP_FILE, "r")) == NULL) 409 | bailout("Could not open wtmp file!", 1); 410 | 411 | #ifdef DEBUG 412 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 413 | syslog(SYSLOG_DEBUG, "Seek to end of wtmp"); 414 | closelog(); 415 | #endif 416 | /* Go to end of file minus one structure */ 417 | fseek(fp, -1L * sizeof(struct utmp), SEEK_END); 418 | 419 | while (fread(&ut, sizeof(struct utmp), 1, fp) == 1) 420 | { 421 | tm = localtime(&ut.ut_time); 422 | 423 | if (tm->tm_year != now.tm_year || tm->tm_yday != now.tm_yday) 424 | break; 425 | 426 | #ifndef SUNOS 427 | if (ut.ut_type == USER_PROCESS || 428 | ut.ut_type == DEAD_PROCESS || 429 | ut.ut_type == UT_UNKNOWN || /* SA 19940703 */ 430 | ut.ut_type == LOGIN_PROCESS || 431 | ut.ut_type == BOOT_TIME) 432 | #endif 433 | { 434 | if ((ut_list_p = (struct ut_list *) malloc(sizeof(struct ut_list))) == NULL) 435 | bailout("Out of memory in read_wtmp.", 1); 436 | ut_list_p->elem = ut; 437 | ut_list_p->next = wtmplist; 438 | wtmplist = ut_list_p; 439 | } 440 | 441 | /* Position the file pointer 2 structures back */ 442 | if (fseek(fp, -2 * sizeof(struct utmp), SEEK_CUR) < 0) break; 443 | } 444 | fclose(fp); 445 | #ifdef DEBUG 446 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 447 | syslog(SYSLOG_DEBUG, "Finished reading today's wtmp entries."); 448 | closelog(); 449 | #endif 450 | } 451 | 452 | /* Free up memory used by today's wtmp entries */ 453 | 454 | void free_wtmp() 455 | { 456 | #ifdef DEBUG 457 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 458 | syslog(SYSLOG_DEBUG, "Freeing list of today's wtmp entries."); 459 | closelog(); 460 | #endif 461 | 462 | while (wtmplist) 463 | { 464 | #ifdef DEBUG_WTMP 465 | struct tm *tm; 466 | tm = localtime(&(wtmplist->elem.ut_time)); 467 | printf("%d:%d %s %s %s\n", 468 | tm->tm_hour,tm->tm_min, wtmplist->elem.ut_line, 469 | wtmplist->elem.ut_user, 470 | #ifndef SUNOS 471 | wtmplist->elem.ut_type == LOGIN_PROCESS?"login":wtmplist->elem.ut_type == BOOT_TIME?"reboot":"logoff" 472 | #else 473 | "" 474 | #endif 475 | ); 476 | #endif 477 | ut_list_p = wtmplist; 478 | wtmplist = wtmplist->next; 479 | free(ut_list_p); 480 | } 481 | #ifdef DEBUG 482 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 483 | syslog(SYSLOG_DEBUG, "Finished freeing list of today's wtmp entries."); 484 | closelog(); 485 | #endif 486 | } 487 | 488 | void store_times(t, time_str) 489 | struct time_ent **t; 490 | char *time_str; 491 | { 492 | int i = 0; 493 | int ar_size = 2; 494 | char *p; 495 | struct time_ent *te; 496 | 497 | while (time_str[i]) 498 | if (time_str[i++] == ',') 499 | ar_size++; 500 | 501 | if ((*t = (struct time_ent *) malloc (ar_size * sizeof(struct time_ent))) == NULL) 502 | bailout("Out of memory", 1); 503 | te = *t; 504 | 505 | p = strtok(time_str, ","); 506 | /* For each day/timerange set, */ 507 | while (p) 508 | { 509 | /* Store valid days */ 510 | te->days = 0; 511 | while (isalpha(*p)) 512 | { 513 | if (!p[1] || !isalpha(p[1])) 514 | { 515 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 516 | syslog(LOG_ERR, "Malformed day name (%c%c) in time field of config file (%s). Entry ignored.", p[0], p[1], CONFIG); 517 | closelog(); 518 | (*t)->days = 0; 519 | return; 520 | } 521 | *p = toupper(*p); 522 | p[1] = toupper(p[1]); 523 | 524 | i = 0; 525 | while (daynames[i]) 526 | { 527 | if (!strncmp(daynames[i], p, 2)) 528 | { 529 | te->days |= daynums[i]; 530 | break; 531 | } 532 | i++; 533 | } 534 | if (!daynames[i]) 535 | { 536 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 537 | syslog(LOG_ERR, "Malformed day name (%c%c) in time field of config file (%s). Entry ignored.", p[0], p[1], CONFIG); 538 | closelog(); 539 | (*t)->days = 0; 540 | return; 541 | } 542 | p += 2; 543 | } 544 | 545 | /* Store start and end times */ 546 | if (*p) 547 | { 548 | if (strlen(p) != 9 || p[4] != '-') 549 | { 550 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 551 | syslog(LOG_ERR, "Malformed time (%s) in time field of config file (%s). Entry ignored.", p, CONFIG); 552 | closelog(); 553 | (*t)->days = 0; 554 | return; 555 | } 556 | te->starttime = atoi(p); 557 | te->endtime = atoi(p+5); 558 | if ((te->starttime == 0 && strncmp(p, "0000-", 5)) || (te->endtime == 0 && strcmp(p+5, "0000"))) 559 | { 560 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 561 | syslog(LOG_ERR, "Invalid range (%s) in time field of config file (%s). Entry ignored.", p, CONFIG); 562 | closelog(); 563 | (*t)->days = 0; 564 | return; 565 | } 566 | } 567 | else 568 | { 569 | te->starttime = 0; 570 | te->endtime = 2359; 571 | } 572 | p = strtok(NULL, ","); 573 | te++; 574 | } 575 | te->days = 0; 576 | } 577 | 578 | void alloc_cp(a, b) 579 | char **a; 580 | char *b; 581 | { 582 | if ((*a = (char *) malloc(strlen(b)+1)) == NULL) 583 | bailout("Out of memory", 1); 584 | else strcpy(*a, b); 585 | } 586 | 587 | void read_config() 588 | { 589 | FILE *config_file; 590 | char *p; 591 | char *lstart; 592 | int i = 0; 593 | #ifdef DEBUG 594 | int j = 0; 595 | #endif 596 | char line[256]; 597 | char *tok; 598 | int linenum = 0; 599 | 600 | if ((config_file = fopen(CONFIG, "r")) == NULL) 601 | bailout("Cannot open config file", 1); 602 | 603 | while (fgets(line, 256, config_file) != NULL) 604 | { 605 | linenum++; 606 | p = line; 607 | while (*p && (*p == ' ' || *p == '\t')) 608 | p++; 609 | lstart = p; 610 | while (*p && *p != '#' && *p != '\n') 611 | p++; 612 | *p = '\0'; 613 | if (*lstart) 614 | { 615 | if (i == MAXLINES) 616 | bailout("Too many lines in timeouts config file.", 1); 617 | if ((config[i] = (struct config_ent *) malloc(sizeof(struct config_ent))) == NULL) 618 | bailout("Out of memory", 1); 619 | config[i]->times = NULL; 620 | config[i]->ttys = NULL; 621 | config[i]->users = NULL; 622 | config[i]->groups = NULL; 623 | config[i]->login_allowed = 1; 624 | config[i]->idlemax = -1; 625 | config[i]->sessmax = -1; 626 | config[i]->daymax = -1; 627 | config[i]->warntime = 5; 628 | config[i]->messages[IDLEMSG] = NULL; 629 | config[i]->messages[SESSMSG] = NULL; 630 | config[i]->messages[DAYMSG] = NULL; 631 | config[i]->messages[NOLOGINMSG] = NULL; 632 | if ((tok = strsep(&lstart, ":")) != NULL) store_times(&config[i]->times, tok); 633 | if ((tok = strsep(&lstart, ":")) != NULL) alloc_cp(&config[i]->ttys, tok); 634 | if ((tok = strsep(&lstart, ":")) != NULL) alloc_cp(&config[i]->users, tok); 635 | if ((tok = strsep(&lstart, ":")) != NULL) alloc_cp(&config[i]->groups, tok); 636 | tok = strsep(&lstart, ":"); 637 | if (tok != NULL && !strncasecmp(tok, "NOLOGIN", 7)) 638 | { 639 | config[i]->login_allowed=0; 640 | if (tok[7] == ';') alloc_cp(&config[i]->messages[NOLOGINMSG], tok+8); 641 | else if ((tok = strsep(&lstart, ":")) != NULL) alloc_cp(&config[i]->messages[NOLOGINMSG], tok); 642 | } 643 | else 644 | if (tok != NULL && !strcasecmp(tok, "LOGIN")) config[i]->login_allowed=1; 645 | else 646 | { 647 | if (tok != NULL) 648 | { 649 | config[i]->idlemax = atoi(tok); 650 | if ((p = strchr(tok, ';')) != NULL) alloc_cp(&config[i]->messages[IDLEMSG], p+1); 651 | } 652 | if ((tok = strsep(&lstart, ":")) != NULL) 653 | { 654 | config[i]->sessmax = atoi(tok); 655 | if ((p = strchr(tok, ';')) != NULL) alloc_cp(&config[i]->messages[SESSMSG], p+1); 656 | } 657 | if ((tok = strsep(&lstart, ":")) != NULL) 658 | { 659 | config[i]->daymax = atoi(tok); 660 | if ((p = strchr(tok, ';')) != NULL) alloc_cp(&config[i]->messages[DAYMSG], p+1); 661 | } 662 | if ((tok = strsep(&lstart, ":")) != NULL) 663 | { 664 | config[i]->warntime = atoi(tok); 665 | } 666 | } 667 | if (!config[i]->times || !config[i]->ttys || 668 | !config[i]->users || !config[i]->groups) 669 | { 670 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 671 | syslog(LOG_ERR, 672 | "Error on line %d of config file (%s). Line ignored.", 673 | linenum, CONFIG); 674 | closelog(); 675 | } 676 | else i++; 677 | } 678 | } 679 | config[i] = NULL; 680 | 681 | if (fclose(config_file) == EOF) 682 | bailout("Cannot close config file", 1); 683 | 684 | #ifdef DEBUG 685 | i = 0; 686 | while (config[i]) 687 | { 688 | printf("line %d: ", i); 689 | j = 0; 690 | while (config[i]->times[j].days) 691 | printf("%d(%d-%d):", config[i]->times[j].days, 692 | config[i]->times[j].starttime, 693 | config[i]->times[j].endtime),j++; 694 | printf("%s:%s:%s:%s:%d;%s:%d;%s:%d;%s:%d\n", 695 | config[i]->ttys, 696 | config[i]->users, 697 | config[i]->groups, 698 | config[i]->login_allowed?"LOGIN":"NOLOGIN", 699 | config[i]->idlemax, 700 | config[i]->messages[IDLEMSG] == NULL?"builtin":config[i]->messages[IDLEMSG], 701 | config[i]->sessmax, 702 | config[i]->messages[SESSMSG] == NULL?"builtin":config[i]->messages[SESSMSG], 703 | config[i]->daymax, 704 | config[i]->messages[DAYMSG] == NULL?"builtin":config[i]->messages[DAYMSG], 705 | config[i]->warntime 706 | ),i++; 707 | } 708 | printf("End debug output.\n"); 709 | #endif /* DEBUG */ 710 | } 711 | 712 | char chktimes(te) 713 | struct time_ent *te; 714 | { 715 | while (te->days) 716 | { 717 | if (daynums[now.tm_wday] & te->days && /* Date within range */ 718 | ((te->starttime <= te->endtime && /* Time within range */ 719 | now_hhmm >= te->starttime && 720 | now_hhmm <= te->endtime) 721 | || 722 | (te->starttime > te->endtime && 723 | (now_hhmm >= te->starttime || 724 | now_hhmm <= te->endtime)) 725 | ) 726 | ) 727 | return 1; 728 | te++; 729 | } 730 | return 0; 731 | } 732 | 733 | char chkmatch(element, in_set) 734 | char *element; 735 | char *in_set; 736 | { 737 | char *t; 738 | char *set = (char *) malloc(strlen(in_set) + 1); 739 | 740 | if (set == NULL) bailout("Out of memory", 1); 741 | else strcpy(set, in_set); 742 | 743 | t = strtok(set, " ,"); 744 | while (t) 745 | { 746 | if (t[strlen(t)-1] == '*') 747 | { 748 | if (!strncmp(t, element, strlen(t) - 1)) 749 | { 750 | free(set); 751 | return 1; 752 | } 753 | } 754 | else if (!strcmp(t, element)) 755 | { 756 | free(set); 757 | return 1; 758 | } 759 | t = strtok(NULL, " ,"); 760 | } 761 | free(set); 762 | return 0; 763 | } 764 | 765 | /* Return the number of minutes which user has been logged in for on 766 | * any of the ttys specified in config[configline] during the current day. 767 | */ 768 | 769 | void get_day_time(user) 770 | char *user; 771 | { 772 | struct ut_list *login_p; 773 | struct ut_list *logout_p; 774 | struct ut_list *prev_p; 775 | 776 | daytime = 0; 777 | login_p = wtmplist; 778 | while (login_p) 779 | { 780 | /* For each login on a matching tty find its logout */ 781 | if ( 782 | #ifndef SUNOS 783 | login_p->elem.ut_type == USER_PROCESS && 784 | #endif 785 | !strncmp(login_p->elem.ut_user, user, 8) && 786 | chkmatch(login_p->elem.ut_line, config[configline]->ttys)) 787 | { 788 | #ifdef DEBUG_WTMP 789 | struct tm *tm; 790 | tm = localtime(&(login_p->elem.ut_time)); 791 | fprintf(stderr, "%d:%d %s %s %s\n", 792 | tm->tm_hour,tm->tm_min, login_p->elem.ut_line, 793 | login_p->elem.ut_user, 794 | "login"); 795 | #endif 796 | prev_p = logout_p = login_p->next; 797 | while (logout_p) 798 | { 799 | /* 800 | * SA19931128 801 | * If there has been a crash, then be reasonably fair and use the 802 | * last recorded login/logout as the user's logout time. This will 803 | * potentially allow them slightly more online time than usual, 804 | * but is better than marking them as logged in for the time the machine 805 | * was down. 806 | */ 807 | #ifndef SUNOS 808 | if (logout_p->elem.ut_type == BOOT_TIME) 809 | { 810 | logout_p = prev_p; 811 | break; 812 | } 813 | #endif 814 | if (/*logout_p->elem.ut_type == DEAD_PROCESS &&*/ 815 | !strcmp(login_p->elem.ut_line, logout_p->elem.ut_line)) 816 | break; 817 | prev_p = logout_p; 818 | logout_p = logout_p->next; 819 | } 820 | 821 | #ifdef DEBUG_WTMP 822 | if (logout_p) 823 | { 824 | tm = localtime(&(logout_p->elem.ut_time)); 825 | fprintf(stderr, "%d:%d %s %s %s\n", 826 | tm->tm_hour,tm->tm_min, logout_p->elem.ut_line, 827 | logout_p->elem.ut_user, "logout"); 828 | fprintf(stderr, "%s %d minutes\n", user, ((logout_p?logout_p->elem.ut_time:time_now) - login_p->elem.ut_time)/60); 829 | } 830 | #endif 831 | daytime += (logout_p?logout_p->elem.ut_time:time_now) - login_p->elem.ut_time; 832 | } 833 | login_p = login_p->next; 834 | } 835 | daytime /= 60; 836 | #ifdef DEBUG 837 | fprintf(stderr, "%s has been logged in for %d minutes today.\n", user, daytime); 838 | #endif 839 | return; 840 | } 841 | 842 | void warnpending(tty, time_remaining, user, host) 843 | char *tty; 844 | int time_remaining; 845 | char *user; 846 | char *host; 847 | { 848 | int fd; 849 | FILE *ttyf; 850 | char cmdbuf[1024]; 851 | 852 | #ifdef DEBUG 853 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 854 | syslog(SYSLOG_DEBUG, "Warning %s@%s on %s of pending logoff in %d minutes.", 855 | user, host, tty, time_remaining); 856 | closelog(); 857 | #endif 858 | 859 | if(chk_xsession(tty, host)) { 860 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 861 | syslog(SYSLOG_DEBUG, "Warning %s running X on %s for pending logout! (%d min%s left)", user, tty, time_remaining, time_remaining==1?"":"s"); 862 | closelog(); 863 | 864 | /* then send the message using xmessage */ 865 | /* well, this is not really clean: */ 866 | sprintf(cmdbuf, "su %s -c \"xmessage -display %s -center 'WARNING: You will be logged out in %d minute%s when your %s limit expires.'&\"", user, host, time_remaining, time_remaining==1?"":"s", limit_names[limit_type]); 867 | system(cmdbuf); 868 | /*#ifdef DEBUG*/ 869 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 870 | syslog(LOG_DEBUG, "cmdbuf=%s", cmdbuf); 871 | closelog(); 872 | /*#endif*/ 873 | sleep(KWAIT); /* and give the user some time to read the message ;) */ 874 | return; 875 | } 876 | 877 | if ((fd = open(tty, O_WRONLY|O_NOCTTY|O_NONBLOCK)) < 0 || 878 | (ttyf = fdopen(fd, "w")) == NULL) 879 | { 880 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 881 | syslog(LOG_ERR, "Could not open %s to warn of impending logoff.\n",tty); 882 | closelog(); 883 | return; 884 | } 885 | fprintf(ttyf, "\r\nWARNING:\r\nYou will be logged out in %d minute%s when your %s limit expires.\r\n", 886 | time_remaining, time_remaining==1?"":"s", limit_names[limit_type]); 887 | fclose(ttyf); 888 | } 889 | 890 | char chk_timeout(user, dev, host, idle, session) 891 | char *user; 892 | char *dev; 893 | char *host; 894 | int idle; 895 | int session; 896 | { 897 | struct passwd *pw; 898 | struct group *gr; 899 | struct group *secgr; 900 | char timematch = 0; 901 | char ttymatch = 0; 902 | char usermatch = 0; 903 | char groupmatch = 0; 904 | char *tty = dev; 905 | char **p; 906 | int disc; 907 | 908 | configline = 0; 909 | 910 | /* Find primary group for specified user */ 911 | if ((pw = getpwnam(user)) == NULL) 912 | { 913 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 914 | syslog(LOG_ERR, "Could not get password entry for %s.", user); 915 | closelog(); 916 | return 0; 917 | } 918 | if ((gr = getgrgid(pw->pw_gid)) == NULL) 919 | { 920 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 921 | syslog(LOG_ERR, "Could not get group name for %s.", user); 922 | closelog(); 923 | return 0; 924 | } 925 | 926 | #ifdef DEBUG 927 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 928 | syslog(SYSLOG_DEBUG, "Checking user %s group %s tty %s.", user, gr->gr_name, tty); 929 | closelog(); 930 | #endif 931 | 932 | /* Check to see if current user matches any entry based on tty/user/group */ 933 | while (config[configline]) 934 | { 935 | timematch = chktimes(config[configline]->times); 936 | ttymatch = chkmatch(tty, config[configline]->ttys); 937 | usermatch = chkmatch(user, config[configline]->users); 938 | groupmatch = chkmatch(gr->gr_name, config[configline]->groups); 939 | /* If the primary group doesn't match this entry, check secondaries */ 940 | setgrent(); 941 | while (!groupmatch && (secgr = getgrent()) != NULL) 942 | { 943 | p = secgr->gr_mem; 944 | while (*p && !groupmatch) 945 | { 946 | /* 947 | printf("Group %s member %s\n", secgr->gr_name, *p); 948 | */ 949 | if (!strcmp(*p, user)) 950 | groupmatch = chkmatch(secgr->gr_name, config[configline]->groups); 951 | p++; 952 | } 953 | /* 954 | free(gr); 955 | */ 956 | } 957 | /* 958 | endgrent(); 959 | */ 960 | /* If so, then check their idle, daily and session times in turn */ 961 | if (timematch && ttymatch && usermatch && groupmatch) 962 | { 963 | get_day_time(user); 964 | #ifdef DEBUG 965 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 966 | syslog(SYSLOG_DEBUG, "Matched entry %d", configline); 967 | syslog(SYSLOG_DEBUG, "Idle=%d (max=%d) Sess=%d (max=%d) Daily=%d (max=%d) warntime=%d", idle, config[configline]->idlemax, session, config[configline]->sessmax, daytime, config[configline]->daymax, config[configline]->warntime); 968 | closelog(); 969 | #endif 970 | disc = getdisc(dev, host); 971 | 972 | limit_type = NOLOGINMSG; 973 | if (!config[configline]->login_allowed) 974 | return NOLOGIN; 975 | 976 | limit_type = IDLEMSG; 977 | if (disc == N_TTY && config[configline]->idlemax > 0 && idle >= config[configline]->idlemax) 978 | return IDLEMAX; 979 | 980 | limit_type = SESSMSG; 981 | if (config[configline]->sessmax > 0 && session >= config[configline]->sessmax) 982 | return SESSMAX; 983 | 984 | limit_type = DAYMSG; 985 | if (config[configline]->daymax > 0 && daytime >= config[configline]->daymax) 986 | return DAYMAX; 987 | 988 | /* If none of those have been exceeded, then warn users of upcoming logouts */ 989 | limit_type = DAYMSG; 990 | if (config[configline]->daymax > 0 && daytime >= config[configline]->daymax - config[configline]->warntime) 991 | warnpending(dev, config[configline]->daymax - daytime, user, host); 992 | else 993 | { 994 | limit_type = SESSMSG; 995 | if (config[configline]->sessmax > 0 && session >= config[configline]->sessmax - config[configline]->warntime) 996 | warnpending(dev, config[configline]->sessmax - session, user, host); 997 | } 998 | 999 | /* Otherwise, leave the poor net addict alone */ 1000 | return ACTIVE; 1001 | } 1002 | configline++; 1003 | } 1004 | 1005 | /* If they do not match any entries, then they can stay on forever */ 1006 | return ACTIVE; 1007 | } 1008 | 1009 | void check_idle() /* Check for exceeded time limits & logoff exceeders */ 1010 | { 1011 | char user[sizeof(utmpp->ut_user)]; 1012 | char host[sizeof(utmpp->ut_host)]; 1013 | struct stat status, *pstat; 1014 | time_t idle, sesstime, time(); 1015 | short aktconfigline = -1; /* -1 if user is in config; >0 if he's not in config, * is handled in an other way */ 1016 | 1017 | pstat = &status; /* point to status structure */ 1018 | #ifndef SUNOS 1019 | sprintf(path, "/proc/%d", utmpp->ut_pid); 1020 | if (utmpp->ut_type != USER_PROCESS || !utmpp->ut_user[0] || /* if not user process */ 1021 | stat(path, pstat)) /* or if proc doesn't exist */ 1022 | return; /* skip the utmp entry */ 1023 | #endif 1024 | strncpy(user, utmpp->ut_user, sizeof(user) - 1); /* get user name */ 1025 | user[sizeof(user) - 1] = '\0'; /* null terminate user name string */ 1026 | 1027 | 1028 | /* Only check user if he is mentioned in the config */ 1029 | 1030 | if(!config[0]) 1031 | return; /* no entries in config */ 1032 | while(config[++aktconfigline] && aktconfigline >= 0) 1033 | if(strcmp(config[aktconfigline]->users, user) == 0 || config[aktconfigline]->users[0] == '*') { 1034 | aktconfigline = -2; /* we found user or * in config, so he/they has/have restrictions */ 1035 | break; 1036 | } 1037 | 1038 | if(aktconfigline > 0) { /* > 0 if user is not in config */ 1039 | #ifdef DEBUG 1040 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1041 | syslog(SYSLOG_DEBUG, "User %s or * not in config -> No restrictions. Not checking %s on %s", user, user, dev); 1042 | closelog(); 1043 | #endif 1044 | return; /* now, we return because the user beeing checked is not in config, so he has no restrictions */ 1045 | } 1046 | 1047 | 1048 | strncpy(host, utmpp->ut_host, sizeof(host) - 1); /* get host name */ 1049 | host[sizeof(host) - 1] = '\0'; 1050 | strncpy(dev, utmpp->ut_line, sizeof(dev) - 1); /* get device name */ 1051 | dev[sizeof(dev) - 1] = '\0'; 1052 | sprintf(path, "/dev/%s", dev); 1053 | if (stat(path, pstat) && !chk_xsession(dev, host) == TIMEOUTD_XSESSION_LOCAL) /* if can't get status for 1054 | port && if it's not a local Xsession */ 1055 | { 1056 | sprintf(errmsg, "Can't get status of user %s's terminal (%s)\n", 1057 | user, dev); 1058 | /* bailout(errmsg, 1); MOH: is there a reason to exit here? */ 1059 | return; 1060 | } 1061 | /* idle time is the lesser of: 1062 | * current time less last access time OR 1063 | * current time less last modified time 1064 | */ 1065 | #ifdef TIMEOUTDX11 1066 | if(chk_xsession(dev, host) && !chk_xterm(dev, host)) { /* check idle for Xsession, but not for xterm */ 1067 | idle = get_xidle(user, host) / 1000 / 60; /* get_xidle returns millisecs, we need mins */ 1068 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1069 | syslog(SYSLOG_DEBUG, "get_xidle(%s,%s) returned %d mins idle for %s.", dev, host, (int)idle, user); 1070 | closelog(); 1071 | } 1072 | else if (chk_xterm(dev, host)) return; 1073 | else 1074 | #endif 1075 | idle = (time_now - max(pstat->st_atime, pstat->st_mtime)) / 60; 1076 | 1077 | sesstime = (time_now - utmpp->ut_time) / 60; 1078 | switch(chk_timeout(user, dev, host, idle, sesstime)) 1079 | { 1080 | case ACTIVE: 1081 | #ifdef DEBUG 1082 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1083 | syslog(SYSLOG_DEBUG, "User %s is active.", user); 1084 | closelog(); 1085 | #endif 1086 | break; 1087 | case IDLEMAX: 1088 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1089 | syslog(LOG_NOTICE, 1090 | "User %s exceeded idle limit (idle for %ld minutes, max=%d).\n", 1091 | user, idle, config[configline]->idlemax); 1092 | closelog(); 1093 | killit(utmpp->ut_pid, user, dev, host); 1094 | break; 1095 | case SESSMAX: 1096 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1097 | syslog(LOG_NOTICE, 1098 | "User %s exceeded maximum session limit at %s (on for %ld minutes, max=%d).\n", 1099 | user, dev, sesstime, config[configline]->sessmax); 1100 | closelog(); 1101 | killit(utmpp->ut_pid, user, dev, host); 1102 | break; 1103 | case DAYMAX: 1104 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1105 | syslog(LOG_NOTICE, 1106 | "User %s exceeded maximum daily limit (on for %d minutes, max=%d).\n", 1107 | user, daytime, config[configline]->daymax); 1108 | closelog(); 1109 | killit(utmpp->ut_pid, user, dev, host); 1110 | break; 1111 | case NOLOGIN: 1112 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1113 | #ifdef DEBUG 1114 | syslog(LOG_NOTICE, "NOLOGIN period reached for user %s@%s. (pid %d)", user, host, utmpp->ut_pid); 1115 | #else 1116 | syslog(LOG_NOTICE, "NOLOGIN period reached for user %s %s", user, host); 1117 | #endif 1118 | closelog(); 1119 | killit(utmpp->ut_pid, user, dev, host); 1120 | break; 1121 | default: 1122 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1123 | syslog(LOG_ERR, "Internal error - unexpected return from chk_timeout"); 1124 | closelog(); 1125 | } 1126 | } 1127 | 1128 | void bailout(message, status) /* display error message and exit */ 1129 | int status; /* exit status */ 1130 | char *message; /* pointer to the error message */ 1131 | { 1132 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1133 | syslog(LOG_ERR, "Exiting - %s", message); 1134 | closelog(); 1135 | exit(status); 1136 | } 1137 | 1138 | void shut_down(signum) 1139 | int signum; 1140 | { 1141 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1142 | syslog(LOG_NOTICE, "Received SIGTERM.. exiting."); 1143 | closelog(); 1144 | exit(0); 1145 | } 1146 | 1147 | void segfault(signum) 1148 | int signum; 1149 | { 1150 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1151 | syslog(LOG_NOTICE, "Received SIGSEGV.. Something went wrong! Exiting!"); 1152 | closelog(); 1153 | exit(0); 1154 | } 1155 | 1156 | void logoff_msg(tty) 1157 | int tty; 1158 | { 1159 | FILE *msgfile = NULL; 1160 | char msgbuf[1024]; 1161 | int cnt; 1162 | 1163 | if (config[configline]->messages[limit_type]) 1164 | msgfile = fopen(config[configline]->messages[limit_type], "r"); 1165 | 1166 | if (msgfile) 1167 | { 1168 | while ((cnt = read(tty, msgbuf, 1024)) > 0) 1169 | write(tty, msgbuf, cnt); 1170 | fclose(msgfile); 1171 | } 1172 | else 1173 | { 1174 | if (limit_type == NOLOGINMSG) 1175 | sprintf(msgbuf, "\r\n\r\nLogins not allowed at this time. Please try again later.\r\n"); 1176 | else 1177 | sprintf(msgbuf, "\r\n\r\nYou have exceeded your %s time limit. Logging you off now.\r\n\r\n", limit_names[limit_type]); 1178 | write(tty, msgbuf, strlen(msgbuf)); 1179 | } 1180 | } 1181 | 1182 | /* terminate process using SIGHUP, then SIGKILL */ 1183 | void killit(pid, user, dev, host) 1184 | int pid; 1185 | char *user; 1186 | char *dev; 1187 | char *host; 1188 | { 1189 | int tty; 1190 | pid_t cpid; 1191 | #ifdef SUNOS 1192 | struct passwd *pw; 1193 | #endif 1194 | 1195 | if(chk_xsession(dev, host) && !chk_xterm(dev, host)) { 1196 | killit_xsession(utmpp->ut_pid, user, host); 1197 | return; 1198 | } 1199 | /* Tell user which limit they have exceeded and that they will be logged off */ 1200 | if ((tty = open(dev, O_WRONLY|O_NOCTTY|O_NONBLOCK)) < 0) 1201 | { 1202 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1203 | syslog(LOG_ERR, "Could not write logoff message to %s.", dev); 1204 | closelog(); 1205 | return; 1206 | } 1207 | 1208 | 1209 | /* check if the pid is sshd. If so, get PID of the child process (another ssh, owned by the user). 1210 | Test reverse if this child process is also ssh and owned by the user we want to log out. 1211 | (because we don't want to slay another user ;) */ 1212 | cpid = getcpid(pid); 1213 | #ifdef DEBUG 1214 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1215 | syslog(LOG_NOTICE, "I am at killit() pid=%d user=%s child=%d line %d", pid, user, cpid, __LINE__); 1216 | closelog(); 1217 | #endif 1218 | 1219 | if(chk_ssh(pid) && chk_ssh(cpid) && !strcmp(getusr(cpid), user)) { 1220 | #ifdef DEBUG 1221 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1222 | syslog(LOG_NOTICE, "User %s (pid:%d, cpid:%d) logged in via ssh from %s.", user, pid, cpid, host); 1223 | closelog(); 1224 | #endif 1225 | pid = cpid; 1226 | } 1227 | 1228 | logoff_msg(tty); 1229 | sleep (KWAIT); /*make sure msg does not get lost, again (esp. ssh)*/ 1230 | close(tty); 1231 | 1232 | #ifdef DEBUG 1233 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1234 | syslog(LOG_NOTICE, "Would normally kill pid %d user %s on %s",pid,user,dev); 1235 | closelog(); 1236 | return; 1237 | #endif 1238 | 1239 | if (fork()) /* the parent process */ 1240 | return; /* returns */ 1241 | 1242 | /* Wait a little while in case the above message gets lost during logout */ 1243 | #ifdef SURE_KILL 1244 | signal(SIGHUP, SIG_IGN); 1245 | if ((pw = getpwnam(user)) == NULL) 1246 | { 1247 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1248 | syslog(LOG_ERR, "Could not log user %s off line %s - unable to determine uid.", user, dev); 1249 | closelog(); 1250 | } 1251 | if (setuid(pw->pw_uid)) 1252 | { 1253 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1254 | syslog(LOG_ERR, "Could not log user %s off line %s - unable to setuid(%d).", user, dev, pw->pw_uid); 1255 | closelog(); 1256 | } 1257 | kill(-1, SIGHUP); 1258 | sleep(KWAIT); 1259 | kill(-1, SIGKILL); 1260 | #else 1261 | kill(pid, SIGHUP); /* first send "hangup" signal */ 1262 | sleep(KWAIT); 1263 | if (!kill(pid, 0)) { /* SIGHUP might be ignored */ 1264 | kill(pid, SIGKILL); /* then send sure "kill" signal */ 1265 | sleep(KWAIT); 1266 | if (!kill(pid, 0)) 1267 | { 1268 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1269 | syslog(LOG_ERR, "Could not log user %s off line %s.", user, dev); 1270 | closelog(); 1271 | } 1272 | } 1273 | #endif 1274 | exit(0); 1275 | } 1276 | 1277 | void reread_config(signum) 1278 | int signum; 1279 | { 1280 | int i = 0; 1281 | 1282 | if (!allow_reread) 1283 | pending_reread = 1; 1284 | else 1285 | { 1286 | pending_reread = 0; 1287 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1288 | syslog(LOG_NOTICE, "Re-reading configuration file."); 1289 | closelog(); 1290 | while (config[i]) 1291 | { 1292 | free(config[i]->times); 1293 | free(config[i]->ttys); 1294 | free(config[i]->users); 1295 | free(config[i]->groups); 1296 | if (config[i]->messages[IDLEMSG]) free(config[i]->messages[IDLEMSG]); 1297 | if (config[i]->messages[DAYMSG]) free(config[i]->messages[DAYMSG]); 1298 | if (config[i]->messages[SESSMSG]) free(config[i]->messages[SESSMSG]); 1299 | if (config[i]->messages[NOLOGINMSG]) free(config[i]->messages[NOLOGINMSG]); 1300 | free(config[i]); 1301 | i++; 1302 | } 1303 | read_config(); 1304 | } 1305 | signal(SIGHUP, reread_config); 1306 | } 1307 | 1308 | void reapchild(signum) 1309 | int signum; 1310 | { 1311 | int st; 1312 | 1313 | wait(&st); 1314 | signal(SIGCHLD, reapchild); 1315 | } 1316 | 1317 | int getdisc(d, host) 1318 | char *d; 1319 | char *host; 1320 | { 1321 | int fd; 1322 | int disc; 1323 | 1324 | #ifdef linux 1325 | if(chk_xsession(d, host) || chk_xterm(d, host)) 1326 | return N_TTY; 1327 | 1328 | if ((fd = open(d, O_RDONLY|O_NONBLOCK|O_NOCTTY)) < 0) 1329 | { 1330 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1331 | syslog(LOG_WARNING, "Could not open %s for checking line discipline - idle limits will be enforced.", d); 1332 | closelog(); 1333 | return N_TTY; 1334 | } 1335 | 1336 | if (ioctl(fd, TIOCGETD, &disc) < 0) 1337 | { 1338 | close(fd); 1339 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1340 | syslog(LOG_WARNING, "Could not get line discipline for %s - idle limits will be enforced.", d); 1341 | closelog(); 1342 | return N_TTY; 1343 | } 1344 | 1345 | close(fd); 1346 | 1347 | #ifdef DEBUG 1348 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1349 | syslog(SYSLOG_DEBUG, "TTY %s: Discipline=%s.",d,disc==N_SLIP?"SLIP":disc==N_TTY?"TTY":disc==N_PPP?"PPP":disc==N_MOUSE?"MOUSE":"UNKNOWN"); 1350 | closelog(); 1351 | #endif 1352 | 1353 | return disc; 1354 | #else 1355 | return N_TTY; 1356 | #endif 1357 | } 1358 | 1359 | int chk_xsession(dev, host) /* returns TIMEOUTD_XSESSION_{REMOTE,LOCAL,NONE} when dev and host seem to be a xSession. */ 1360 | char *dev,*host; 1361 | { 1362 | if( strncmp(host, ":0", 1) == 0 ) { 1363 | /* Look here, how we check if it's a Xsession but no telnet or whatever. 1364 | * The problem is that a xterm running on :0 has the device pts/?. But if we ignore 1365 | * all pts/?, ssh users won't be restricted. 1366 | * So, if (tty="pts/?" OR tty=":*") AND host = ":*", we have a Xsession: 1367 | * 1368 | * seppy@schleptop:~$ w 1369 | * 20:06:33 up 18 min, 6 users, load average: 0.14, 0.16, 0.12 1370 | * USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT 1371 | * dennis :0 - 19:48 ?xdm? 0.00s ? - 1372 | * dennis pts/1 :0.0 20:00 4:12 0.03s 0.03s bash 1373 | * dennis pts/2 :0.0 20:01 0.00s 0.18s 0.16s ssh localhost 1374 | * dennis pts/3 localhost 20:01 0.00s 0.01s 0.00s w 1375 | */ 1376 | #ifdef DEBUG 1377 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1378 | syslog(LOG_DEBUG, "LOCAL Xsession detected. device=%s host=%s", dev, host); 1379 | closelog(); 1380 | #endif 1381 | return TIMEOUTD_XSESSION_LOCAL; 1382 | } 1383 | else if (strstr(dev, ":") && strlen(host) > 1 && gethostbyname(host)) { 1384 | /* What about remote XDMCP sessions? 1385 | * USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT 1386 | * mark pts/3 mercury Sat11 0.00s 10.99s 0.04s w 1387 | * rebecca ap:10 ap 10:32 0.00s 0.00s 1.28s x-session-manager 1388 | */ 1389 | 1390 | #ifdef DEBUG 1391 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1392 | syslog(LOG_DEBUG, "REMOTE Xsession detected. device=%s host=%s", dev, host); 1393 | closelog(); 1394 | #endif 1395 | return TIMEOUTD_XSESSION_REMOTE; 1396 | } 1397 | else { 1398 | #ifdef DEBUG 1399 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1400 | syslog(LOG_DEBUG, "NO xsession detected. device=%s host=%s", dev, host); 1401 | closelog(); 1402 | #endif 1403 | return TIMEOUTD_XSESSION_NONE; 1404 | } 1405 | } 1406 | 1407 | /* We have to handle Xterms(pts/?) and Xsessions (:0) different: 1408 | - Check Xsession for idle, but not a XTERM 1409 | - Send message for pending logoff to X, but not to XTERM 1410 | -> Don't check XTERM at all 1411 | - but: check ssh (pts/?) but no XTERM (again) 1412 | */ 1413 | int chk_xterm(dev, host) /* returns 1 when dev and host seem to be a xTERM. */ 1414 | char *dev,*host; 1415 | { 1416 | if(strncmp(dev, "pts/0", 3) == 0 && strncmp(host, ":0", 1) == 0 ) { 1417 | #ifdef DEBUG 1418 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1419 | syslog(LOG_DEBUG, "XTERM detected. device=%s host=%s Ignoring.", dev, host); 1420 | closelog(); 1421 | #endif 1422 | return 1; 1423 | } 1424 | else 1425 | return 0; 1426 | } /* chk_xterm(dev,host) */ 1427 | 1428 | 1429 | void killit_xsession(pid, user, host) /* returns 1 when host seems to be a xSession. */ 1430 | int pid; 1431 | char *host, *user; 1432 | { 1433 | char msgbuf[1024], cmdbuf[1024]; 1434 | /* first, get the message into msgbuf */ 1435 | if (limit_type == NOLOGINMSG) { 1436 | sprintf(msgbuf, "Logins not allowed at this time. Please try again later."); 1437 | } else { 1438 | sprintf(msgbuf, "You have exceeded your %s time limit. Logging you off now.", limit_names[limit_type]); 1439 | } 1440 | 1441 | /* then send the message using xmessage */ 1442 | /* well, this is not really clean: */ 1443 | sprintf(cmdbuf, "su %s -c \"xmessage -display %s -center '%s'&\"", user, host, msgbuf); 1444 | system(cmdbuf); 1445 | #ifdef DEBUG 1446 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1447 | syslog(LOG_DEBUG, "cmdbuf=%s", cmdbuf); 1448 | closelog(); 1449 | #endif 1450 | sleep(KWAIT); /* and give the user some time to read the message ;) */ 1451 | 1452 | 1453 | #ifndef DEBUG 1454 | /* kill pid here */ 1455 | kill(pid, SIGTERM); /* otherwise, X crashes */ 1456 | sleep(KWAIT); 1457 | if (!kill(pid, 0)) { /* SIGHUP might be ignored */ 1458 | kill(pid, SIGKILL); /* then send sure "kill" signal */ 1459 | sleep(KWAIT); 1460 | if (!kill(pid, 0)) 1461 | { 1462 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1463 | syslog(LOG_ERR, "Could not log user %s off line %s. (running X)", user, host); 1464 | closelog(); 1465 | } 1466 | } 1467 | #else 1468 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1469 | syslog(LOG_ERR, "Would normally logoff user %s running X (kill PID %d)", user, pid); 1470 | closelog(); 1471 | #endif 1472 | } 1473 | 1474 | 1475 | 1476 | int chk_ssh(pid)/* seppy; returns true if pid is sshd, otherwise it returns false */ 1477 | pid_t pid; 1478 | { 1479 | sprintf(path, "/proc/%d/stat", pid); 1480 | proc_file = fopen(path, "r"); 1481 | if(!proc_file) { 1482 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1483 | syslog(LOG_WARNING, "chk_ssh(): PID %d does not exist. Something went wrong. Ignoring.", pid); 1484 | closelog(); 1485 | return 0; 1486 | } 1487 | 1488 | fscanf (proc_file, "%*d (%[^)]", comm); 1489 | fclose(proc_file); 1490 | 1491 | if(!strcmp(comm, "sshd")) 1492 | return 1; 1493 | else 1494 | return 0; 1495 | } 1496 | 1497 | char *getusr(pid) /*seppy; returns the name of the user owning process with the Process ID pid */ 1498 | pid_t pid; 1499 | { 1500 | char uid[99]; 1501 | sprintf(path, "/proc/%d/status", pid); 1502 | proc_file = fopen(path, "r"); 1503 | if(!proc_file) { 1504 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1505 | syslog(LOG_NOTICE, "getusr(): PID %d does not exist. Ignoring.", pid); 1506 | closelog(); 1507 | return "unknown"; 1508 | } 1509 | while(!fscanf(proc_file, "Uid: %s", uid)) 1510 | fgets(uid, 98, proc_file); 1511 | fclose(proc_file); 1512 | return getpwuid(atoi(uid))->pw_name; 1513 | } 1514 | 1515 | #ifdef TIMEOUTDX11 1516 | Time get_xidle(user, display) /*seppy; returns millicecs since last input event */ 1517 | char *user; 1518 | char *display; 1519 | { 1520 | Display* dpy; 1521 | static XScreenSaverInfo* mitInfo = 0; 1522 | struct passwd *pwEntry; 1523 | char homedir[50]; /*50 should be enough*/ 1524 | char oldhomedir[50]; 1525 | uid_t oldeuid; 1526 | Time retval = 0; 1527 | 1528 | pwEntry = getpwnam(user); 1529 | if(!pwEntry) { 1530 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1531 | syslog(LOG_ERR, "Could not get passwd-entry for user %s", user); 1532 | closelog(); 1533 | } 1534 | 1535 | #ifdef DEBUG 1536 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1537 | syslog(LOG_DEBUG, "su-ing to %s(%d) and connecting to X", user, pwEntry->pw_uid); 1538 | closelog(); 1539 | #endif 1540 | 1541 | /*change into the user running x. we need that to connect to X*/ 1542 | /*setregid(1000, 1000); We don't need this*/ 1543 | 1544 | /*save old, to come back*/ 1545 | oldeuid = geteuid(); 1546 | sprintf(oldhomedir, "HOME=%s", getenv("HOME")); 1547 | 1548 | /*become user*/ 1549 | if(seteuid(pwEntry->pw_uid) == -1) { 1550 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1551 | syslog(LOG_ERR, "Could not seteuid(%d).", pwEntry->pw_uid); 1552 | closelog(); 1553 | } 1554 | 1555 | sprintf(homedir, "HOME=%s", pwEntry->pw_dir); 1556 | putenv(homedir); 1557 | 1558 | /* First, check if there is a xserver.. */ 1559 | if ((dpy = XOpenDisplay (display)) == NULL) { /* = intended */ 1560 | openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON); 1561 | syslog(LOG_NOTICE, "Could not connect to %s to query idle-time for %s. Ignoring.", display, user); 1562 | closelog(); 1563 | } else { 1564 | if (!mitInfo) 1565 | mitInfo = XScreenSaverAllocInfo (); 1566 | XScreenSaverQueryInfo (dpy, DefaultRootWindow (dpy), mitInfo); 1567 | retval = mitInfo->idle; 1568 | XCloseDisplay(dpy); 1569 | } 1570 | /*go back again*/ 1571 | putenv(oldhomedir); 1572 | setuid(oldeuid); 1573 | 1574 | return retval; 1575 | } /* get_xidle(user) */ 1576 | #endif 1577 | 1578 | 1579 | /* seppy; getchild() 1580 | returns the pid of the first child-process found. 1581 | - 1 if a error occured, 1582 | - 0 if none found 1583 | 1584 | We need this because utmp returns a process owned by 1585 | root when a user is connected via ssh. If we kill its 1586 | child (owned by the user) he/she gets logged off */ 1587 | pid_t getcpid(ppid) 1588 | pid_t ppid; 1589 | { 1590 | DIR *proc; 1591 | FILE *proc_file; 1592 | struct dirent *cont; 1593 | char akt_pid[99]; 1594 | char path[256]; 1595 | 1596 | proc = opendir("/proc/"); 1597 | if(proc == NULL) { 1598 | printf("error opening directory\n"); 1599 | return -1; /* error */ 1600 | } 1601 | 1602 | while((cont = readdir(proc)) != NULL) 1603 | if(cont->d_type == 4 && isdigit(cont->d_name[0])) { /* check only PIDs */ 1604 | sprintf(path, "/proc/%s/status", cont->d_name); 1605 | proc_file = fopen(path, "r"); 1606 | if(!proc_file) 1607 | printf("error opening proc status file %s\n", path); 1608 | 1609 | while(!fscanf(proc_file, "PPid: %s", akt_pid)) 1610 | fgets(akt_pid, 10, proc_file); 1611 | 1612 | if(atoi(akt_pid) == ppid) 1613 | return (pid_t)atoi(cont->d_name); /* return pid of child */ 1614 | } /* if(cont->d_type == 4) */ 1615 | 1616 | return 0; /* no child found */ 1617 | } /* getchild(ppid) */ 1618 | -------------------------------------------------------------------------------- /timeouts: -------------------------------------------------------------------------------- 1 | # /etc/timeouts: user login/idle/session time limits. See timeouts(5). 2 | # 3 | # Format: TIMES:TTYS:USERS:GROUPS:MAXIDLE:MAXSESS:MAXDAY:WARN 4 | # or: TIMES:TTYS:USERS:GROUPS:LOGINSTATUS 5 | # 6 | # Some examples: 7 | # 8 | # dopey is not allowed to login 9 | #Al:*:dopey:*:NOLOGIN 10 | # 11 | # cas gets unlimited use 12 | #Al:*:cas:*:0:0:0:0 13 | # 14 | # fred is allowed 20 minutes idle, 240 mins per session, and 480 mins per day 15 | # on ttyS3 16 | #Al:ttyS3:fred:*:20:240:480:10 17 | # 18 | # everyone else is allowed only 120min/session, 240/day 19 | #Al:ttyS3:*:*:20:120:240:5 20 | -------------------------------------------------------------------------------- /timeouts.5: -------------------------------------------------------------------------------- 1 | .TH TIMEOUTS 5 2 | .SH NAME 3 | timeouts \- user login/idle/session time limits 4 | .SH DESCRIPTION 5 | The timeouts file is used by timeoutd (8) to impose limits on what times 6 | particular users or groups of users can login on particular 7 | terminals, how long a user can be idle (no activity on the terminal), 8 | how long a user can be logged in for in a single session and how much 9 | time a user can spend on a set of terminals each day. 10 | .PP 11 | The timeouts file is a plain ASCII file. Blank lines, or lines where 12 | the first non blank character is a hash (#) will be ignored. All other 13 | lines should be of the format: 14 | .PP 15 | TIMES:TTYS:USERS:GROUPS:MAXIDLE:MAXSESS:MAXDAY:WARN 16 | .PP 17 | OR 18 | .PP 19 | TIMES:TTYS:USERS:GROUPS:LOGINSTATUS 20 | .PP 21 | \fBTIMES\fR is a comma separated list of times for which the entry is valid. 22 | The entry will be ignored completely outside these times. 23 | The format for each element of the times field is: DD[DD...][SSSS-EEEE] 24 | Where: 25 | .IP 26 | DD is one of Su Mo Tu We Th Fr Sa Wk Al 27 | .br 28 | (Al = SuMoTuWeThFrSa Wk = MoTuWeThFr) 29 | .IP 30 | SSSS and EEEE are start and end times in 24 hour notation. 31 | .PP 32 | \fBTTYS\fR is a comma separated list of ttys (without the leading /dev/) 33 | for which the entry is valid. A trailing asterisk (*) will result in 34 | any tty which matches up to the asterisk being accepted. An asterisk 35 | by itself matches all tttys. 36 | .PP 37 | \fBUSERS\fR is a comma separated list of users, with pattern matching 38 | as for TTYS. 39 | .PP 40 | \fBGROUPS\fR is a comma separated list of groups, with pattern matching 41 | as for TTYS. 42 | .PP 43 | \fBMAXIDLE\fR is the number of minutes which a user may remain idle 44 | without being logged off. Idle time is defined as time during which 45 | no activity (no output to the tty or input from the tty) is detected. 46 | This is not checked under Linux if the tty is in SLIP mode. 47 | .PP 48 | \fBMAXSESS\fR is the maximum number of minutes that a user 49 | can be logged in for in a single session if they match that entry. 50 | .PP 51 | \fBMAXDAY\fR is the maximum number of minutes per day that a user 52 | can be logged in for if they match that entry. 53 | .PP 54 | \fBWARN\fR provides a facility for notifying a user that they are 55 | about to be logged off due to exceeding MAXSESS or MAXDAY. WARN is 56 | measured in minutes with a default value of 5. The user will receive 57 | a warning every minute for WARN minutes before being logged off. 58 | .PP 59 | \fBLOGINSTATUS\fR is one of either LOGIN or NOLOGIN and is used to 60 | limit the times during which certain people or groups of people can 61 | use specific terminals. 62 | .PP 63 | When searching through the timeouts file, timeoutd will use the first 64 | entry for which the TIMES:TTYS:USERS:GROUPS fields all match the 65 | user who is being checked. 66 | .PP 67 | When calculating the number of minutes for which a user has been logged 68 | on in the given day, timeoutd will consider logged in time on all 69 | ttys covered by the TTYS field for the matching entry. 70 | .PP 71 | .SH EXAMPLES 72 | .IP Al:*:*:*:10 73 | Would match all all users in any group regardless of which tty they are 74 | logged in on and allow an idle time of 10 minutes, with no daily or 75 | session time limits. 76 | .IP SaSu:ttyS*:*:subs:5:90:180:3 77 | Would match all users in group subs logged on to any dialin line (assuming 78 | all serial lines are dialins) over the weekend and allow them 5 minutes 79 | idle time, 90 minutes per session and 180 minutes per day, with a 3 80 | minute warning period before logoff will occur. 81 | .IP Wk:ttyS2,ttyS4:*:subs,other:10::60:5 82 | Would match all users in groups subs or other logged on to ttyS2 or ttyS4 83 | on a weekday 84 | and allow them 10 minutes idle time, no session limit and a 60 minute 85 | daily limit with a 5 minute warning period. Note that this provides 86 | for 60 minutes per day across both ttyS2 and ttyS4, NOT 60 minutes on ttyS2 87 | and 60 minutes on ttyS4. 88 | .IP Wk2000-0700:ttyS*:*:*:NOLOGIN 89 | Would match all dialled in users (if all ttyS lines were modems) and 90 | prevent them logging in before 7am or after 8pm on weekdays. 91 | .SH FILES 92 | /etc/timeouts 93 | .SH BUGS 94 | See timeoutd(8) 95 | .SH "SEE ALSO" 96 | .BR timeoutd "(8) 97 | .SH "WRITTEN BY" 98 | Shane Alderton 99 | --------------------------------------------------------------------------------