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