sh: Add test for exit status of for loop without items.
[dragonfly.git] / usr.bin / lock / lock.c
1 /*
2  * Copyright (c) 1980, 1987, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Bob Toxen.
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. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * @(#) Copyright (c) 1980, 1987, 1993 The Regents of the University of California.  All rights reserved.
37  * @(#)lock.c   8.1 (Berkeley) 6/6/93
38  * $FreeBSD: src/usr.bin/lock/lock.c,v 1.8.2.1 2002/09/15 22:32:56 dd Exp $
39  * $DragonFly: src/usr.bin/lock/lock.c,v 1.5 2007/11/25 18:10:07 swildner Exp $
40  */
41
42 /*
43  * Lock a terminal up until the given key is entered or the given
44  * interval times out.
45  *
46  * Timeout interval is by default TIMEOUT, it can be changed with
47  * an argument of the form -time where time is in minutes
48  */
49
50 #include <sys/param.h>
51 #include <sys/time.h>
52 #include <sys/consio.h>
53
54 #include <err.h>
55 #include <pwd.h>
56 #include <sgtty.h>
57 #include <signal.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <syslog.h>
62 #include <unistd.h>
63
64 #define TIMEOUT 15
65
66 static void     quit(int);
67 static void     bye(int);
68 static void     hi(int);
69 static void     usage(void);
70
71 struct timeval  timeout;
72 struct timeval  zerotime;
73 struct sgttyb   tty, ntty;
74 long    nexttime;                       /* keep the timeout time */
75 int     no_timeout;                     /* lock terminal forever */
76 int     vtyunlock;                      /* Unlock flag and code. */
77
78 /*ARGSUSED*/
79 int
80 main(int argc, char **argv)
81 {
82         struct passwd *pw;
83         struct timeval timval;
84         time_t timval_sec;
85         struct itimerval ntimer, otimer;
86         struct tm *timp;
87         int ch, failures, sectimeout, usemine, vtylock;
88         long tmp;
89         char *ap, *ep, *mypw, *ttynam, *tzn;
90         char hostname[MAXHOSTNAMELEN + 1], s[BUFSIZ], s1[BUFSIZ];
91
92         openlog("lock", LOG_ODELAY, LOG_AUTH);
93
94         sectimeout = TIMEOUT;
95         mypw = NULL;
96         usemine = 0;
97         no_timeout = 0;
98         vtylock = 0;
99         while ((ch = getopt(argc, argv, "npt:v")) != -1)
100                 switch(ch) {
101                 case 't':
102                         tmp = strtol(optarg, &ep, 10);
103                         if (*ep != '\0' || tmp > INT_MAX || tmp < INT_MIN)      
104                                 errx(1, "illegal timeout value");
105                         sectimeout = (int)tmp;
106                         break;
107                 case 'p':
108                         usemine = 1;
109                         if (!(pw = getpwuid(getuid())))
110                                 errx(1, "unknown uid %d", getuid());
111                         mypw = strdup(pw->pw_passwd);
112                         break;
113                 case 'n':
114                         no_timeout = 1;
115                         break;
116                 case 'v':
117                         vtylock = 1;
118                         break;
119                 case '?':
120                 default:
121                         usage();
122                 }
123         timeout.tv_sec = sectimeout * 60;
124
125         setuid(getuid());               /* discard privs */
126
127         if (ioctl(STDIN_FILENO, TIOCGETP, &tty))        /* get information for header */
128                 err(1, "ioctl(TIOCGETP) failed");
129
130         gethostname(hostname, sizeof(hostname));
131         if (!(ttynam = ttyname(0)))
132                 errx(1, "not a terminal?");
133         if (gettimeofday(&timval, NULL))
134                 err(1, "gettimeofday");
135         nexttime = timval.tv_sec + (sectimeout * 60);
136         timval_sec = timval.tv_sec;
137         timp = localtime(&timval_sec);
138         ap = asctime(timp);
139         tzn = timp->tm_zone;
140
141         signal(SIGINT, quit);
142         signal(SIGQUIT, quit);
143         ntty = tty; ntty.sg_flags &= ~ECHO;
144         if (ioctl(STDIN_FILENO, TIOCSETP, &ntty))
145                 err(1, "ioctl(TIOSETP) failed");
146
147         if (!mypw) {
148                 /* get key and check again */
149                 printf("Key: ");
150                 if (!fgets(s, sizeof(s), stdin) || *s == '\n')
151                         quit(0);
152                 printf("\nAgain: ");
153                 /*
154                  * Don't need EOF test here, if we get EOF, then s1 != s
155                  * and the right things will happen.
156                  */
157                 fgets(s1, sizeof(s1), stdin);
158                 putchar('\n');
159                 if (strcmp(s1, s)) {
160                         printf("\07lock: passwords didn't match.\n");
161                         ioctl(STDIN_FILENO, TIOCSETP, &tty);
162                         exit(1);
163                 }
164                 s[0] = '\0';
165                 mypw = s1;
166         }
167
168         /* set signal handlers */
169         signal(SIGINT, hi);
170         signal(SIGQUIT, hi);
171         signal(SIGTSTP, hi);
172         signal(SIGALRM, bye);
173
174         ntimer.it_interval = zerotime;
175         ntimer.it_value = timeout;
176         if (!no_timeout)
177                 setitimer(ITIMER_REAL, &ntimer, &otimer);
178         if (vtylock) {
179                 /*
180                  * If this failed, we want to err out; warn isn't good
181                  * enough, since we don't want the user to think that
182                  * everything is nice and locked because they got a
183                  * "Key:" prompt.
184                  */
185                 if (ioctl(STDIN_FILENO, VT_LOCKSWITCH, &vtylock) == -1) {
186                         ioctl(STDIN_FILENO, TIOCSETP, &tty);
187                         err(1, "locking vty");
188                 }
189                 vtyunlock = 0x2;
190         }
191
192         /* header info */
193         printf("lock: %s on %s.", ttynam, hostname);
194         if (no_timeout)
195                 printf(" no timeout.");
196         else
197                 printf(" timeout in %d minute%s.", sectimeout,
198                     sectimeout != 1 ? "s" : "");
199         if (vtylock)
200                 printf(" vty locked.");
201         printf("\ntime now is %.20s%s%s", ap, tzn, ap + 19);
202
203         failures = 0;
204
205         for (;;) {
206                 printf("Key: ");
207                 if (!fgets(s, sizeof(s), stdin)) {
208                         clearerr(stdin);
209                         hi(0);
210                         continue;
211                 }
212                 if (usemine) {
213                         s[strlen(s) - 1] = '\0';
214                         if (!strcmp(mypw, crypt(s, mypw)))
215                                 break;
216                 }
217                 else if (!strcmp(s, s1))
218                         break;
219                 printf("\07\n");
220                 failures++;
221                 if (getuid() == 0)
222                     syslog(LOG_NOTICE, "%d ROOT UNLOCK FAILURE%s (%s on %s)",
223                         failures, failures > 1 ? "S": "", ttynam, hostname);
224                 if (ioctl(STDIN_FILENO, TIOCGETP, &ntty))
225                         exit(1);
226                 sleep(1);               /* to discourage guessing */
227         }
228         if (getuid() == 0)
229                 syslog(LOG_NOTICE, "ROOT UNLOCK ON hostname %s port %s",
230                     hostname, ttynam);
231         quit(0);
232         return(0); /* not reached */
233 }
234
235
236 static void
237 usage(void)
238 {
239         fprintf(stderr, "usage: lock [-npv] [-t timeout]\n");
240         exit(1);
241 }
242
243 static void
244 hi(int signo __unused)
245 {
246         struct timeval timval;
247
248         if (!gettimeofday(&timval, NULL)) {
249                 printf("lock: type in the unlock key. ");
250                 if (no_timeout) {
251                         putchar('\n');
252                 } else {
253                         printf("timeout in %ld:%ld minutes\n",
254                             (nexttime - timval.tv_sec) / 60,
255                             (nexttime - timval.tv_sec) % 60);
256                 }
257         }
258 }
259
260 static void
261 quit(int signo __unused)
262 {
263         putchar('\n');
264         ioctl(STDIN_FILENO, TIOCSETP, &tty);
265         if (vtyunlock)
266                 ioctl(STDIN_FILENO, VT_LOCKSWITCH, &vtyunlock);
267         exit(0);
268 }
269
270 static void
271 bye(int signo __unused)
272 {
273         if (!no_timeout) {
274                 ioctl(STDIN_FILENO, TIOCSETP, &tty);
275                 if (vtyunlock)
276                         ioctl(STDIN_FILENO, VT_LOCKSWITCH, &vtyunlock);
277                 printf("lock: timeout\n");
278                 exit(1);
279         }
280 }