xref: /openbsd-src/usr.bin/lock/lock.c (revision f6462fb5afa7997dc3ec8c412166460be4dfaaf2)
1*f6462fb5Sschwarze /*	$OpenBSD: lock.c,v 1.46 2019/07/24 20:23:09 schwarze Exp $	*/
21bbed971Sderaadt /*	$NetBSD: lock.c,v 1.8 1996/05/07 18:32:31 jtc Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1980, 1987, 1993
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * This code is derived from software contributed to Berkeley by
9df930be7Sderaadt  * Bob Toxen.
10df930be7Sderaadt  *
11df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
12df930be7Sderaadt  * modification, are permitted provided that the following conditions
13df930be7Sderaadt  * are met:
14df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
15df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
16df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
17df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
18df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
19f75387cbSmillert  * 3. Neither the name of the University nor the names of its contributors
20df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
21df930be7Sderaadt  *    without specific prior written permission.
22df930be7Sderaadt  *
23df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33df930be7Sderaadt  * SUCH DAMAGE.
34df930be7Sderaadt  */
35df930be7Sderaadt 
36df930be7Sderaadt /*
371e4c0f81Sjca  * Lock a terminal up until the given key or user password is entered,
381e4c0f81Sjca  * or the given interval times out.
39df930be7Sderaadt  */
40df930be7Sderaadt 
41df930be7Sderaadt #include <sys/stat.h>
42df930be7Sderaadt #include <sys/time.h>
43df930be7Sderaadt #include <signal.h>
44df930be7Sderaadt 
45df930be7Sderaadt #include <ctype.h>
46df930be7Sderaadt #include <err.h>
47df930be7Sderaadt #include <pwd.h>
48ed8b5f35Smillert #include <readpassphrase.h>
49df930be7Sderaadt #include <stdio.h>
501bbed971Sderaadt #include <stdlib.h>
51df930be7Sderaadt #include <string.h>
52df930be7Sderaadt #include <termios.h>
531bbed971Sderaadt #include <unistd.h>
54b9fc9a72Sderaadt #include <limits.h>
55df930be7Sderaadt 
56ed8b5f35Smillert #include <login_cap.h>
57ed8b5f35Smillert #include <bsd_auth.h>
584e0e1dc4Smillert 
59c72b5b24Smillert void bye(int);
60c72b5b24Smillert void hi(int);
618a6602a7Scheloha void usage(void);
62df930be7Sderaadt 
63*f6462fb5Sschwarze int	no_timeout = 0;			/* lock terminal forever */
64df930be7Sderaadt 
65266ade72Smillert int
main(int argc,char * argv[])66d0c2d48dSderaadt main(int argc, char *argv[])
67df930be7Sderaadt {
68b9fc9a72Sderaadt 	char hostname[HOST_NAME_MAX+1], s[BUFSIZ], s1[BUFSIZ], date[256];
69757e8834Stedu 	char hash[_PASSWORD_LEN];
70d0c2d48dSderaadt 	char *p, *style, *nstyle, *ttynam;
71df930be7Sderaadt 	struct itimerval ntimer, otimer;
72b02d8f05Stedu 	struct timeval timeout;
73c52c9307Smartynas 	int ch, sectimeout, usemine, cnt, tries = 10, backoff = 3;
74769f22eaSderaadt 	const char *errstr;
75d0c2d48dSderaadt 	struct passwd *pw;
76df930be7Sderaadt 	struct tm *timp;
77df930be7Sderaadt 	time_t curtime;
78ed8b5f35Smillert 	login_cap_t *lc;
79df930be7Sderaadt 
80*f6462fb5Sschwarze 	sectimeout = 0;
81ed8b5f35Smillert 	style = NULL;
82df930be7Sderaadt 	usemine = 0;
83b02d8f05Stedu 	memset(&timeout, 0, sizeof(timeout));
84df930be7Sderaadt 
8591211a55Stedu 	if (pledge("stdio rpath wpath getpw tty proc exec", NULL) == -1)
860bd1216cSderaadt 		err(1, "pledge");
87060ad421Sderaadt 
88df930be7Sderaadt 	if (!(pw = getpwuid(getuid())))
89b0ca7b2aSderaadt 		errx(1, "unknown uid %u.", getuid());
90df930be7Sderaadt 
91ed8b5f35Smillert 	lc = login_getclass(pw->pw_class);
92c52c9307Smartynas 	if (lc != NULL) {
93c52c9307Smartynas 		/*
94c52c9307Smartynas 		 * We allow "login-tries" attempts to login but start
95c52c9307Smartynas 		 * slowing down after "login-backoff" attempts.
96c52c9307Smartynas 		 */
97a8b197cdStedu 		tries = login_getcapnum(lc, "login-tries", 10, 10);
98a8b197cdStedu 		backoff = login_getcapnum(lc, "login-backoff", 3, 3);
99c52c9307Smartynas 	}
100ed8b5f35Smillert 
1010654e9b7Sjca 	while ((ch = getopt(argc, argv, "a:npt:")) != -1) {
102ed8b5f35Smillert 		switch (ch) {
103ed8b5f35Smillert 		case 'a':
104ed8b5f35Smillert 			if (lc) {
105ed8b5f35Smillert 				style = login_getstyle(lc, optarg, "auth-lock");
106ed8b5f35Smillert 				if (style == NULL)
107ed8b5f35Smillert 					errx(1,
108ed8b5f35Smillert 					    "invalid authentication style: %s",
109ed8b5f35Smillert 					    optarg);
110ed8b5f35Smillert 			}
111ed8b5f35Smillert 			usemine = 1;
112ed8b5f35Smillert 			break;
113df930be7Sderaadt 		case 't':
114a8b197cdStedu 			sectimeout = strtonum(optarg, 1, INT_MAX, &errstr);
115769f22eaSderaadt 			if (errstr)
116769f22eaSderaadt 				errx(1, "timeout %s: %s", errstr, optarg);
117df930be7Sderaadt 			break;
118df930be7Sderaadt 		case 'p':
119df930be7Sderaadt 			usemine = 1;
120df930be7Sderaadt 			break;
1210654e9b7Sjca 		case 'n':
122*f6462fb5Sschwarze 			no_timeout = 1;
1230654e9b7Sjca 			break;
124df930be7Sderaadt 		default:
1258a6602a7Scheloha 			usage();
126df930be7Sderaadt 		}
1276bc13b30Stedu 	}
128*f6462fb5Sschwarze 	if (sectimeout == 0)
129*f6462fb5Sschwarze 		no_timeout = 1;
130df930be7Sderaadt 
131df930be7Sderaadt 	gethostname(hostname, sizeof(hostname));
13283536322Stobias 	if (usemine && lc == NULL)
13383536322Stobias 		errx(1, "login class not found");
134ed8b5f35Smillert 	if (!(ttynam = ttyname(STDIN_FILENO)))
135df930be7Sderaadt 		errx(1, "not a terminal?");
136ed8b5f35Smillert 	curtime = time(NULL);
137df930be7Sderaadt 	timp = localtime(&curtime);
138ed8b5f35Smillert 	strftime(date, sizeof(date), "%c", timp);
139df930be7Sderaadt 
140ed8b5f35Smillert 	if (!usemine) {
141df930be7Sderaadt 		/* get key and check again */
142ed8b5f35Smillert 		if (!readpassphrase("Key: ", s, sizeof(s), RPP_ECHO_OFF) ||
143ed8b5f35Smillert 		    *s == '\0')
144ed8b5f35Smillert 			exit(0);
145df930be7Sderaadt 		/*
146df930be7Sderaadt 		 * Don't need EOF test here, if we get EOF, then s1 != s
147df930be7Sderaadt 		 * and the right things will happen.
148df930be7Sderaadt 		 */
149a8b197cdStedu 		readpassphrase("Again: ", s1, sizeof(s1), RPP_ECHO_OFF);
150df930be7Sderaadt 		if (strcmp(s1, s)) {
151ed8b5f35Smillert 			warnx("\apasswords didn't match.");
152df930be7Sderaadt 			exit(1);
153df930be7Sderaadt 		}
154757e8834Stedu 		crypt_newhash(s, "bcrypt", hash, sizeof(hash));
1553a7efd93Smestre 		explicit_bzero(s, sizeof(s));
156757e8834Stedu 		explicit_bzero(s1, sizeof(s1));
157df930be7Sderaadt 	}
158df930be7Sderaadt 
159df930be7Sderaadt 	/* set signal handlers */
160a8b197cdStedu 	signal(SIGINT, hi);
161a8b197cdStedu 	signal(SIGQUIT, hi);
162a8b197cdStedu 	signal(SIGTSTP, hi);
163a8b197cdStedu 	signal(SIGALRM, bye);
164df930be7Sderaadt 
1652d9f0fe4Scheloha 	if (!no_timeout) {
1662d9f0fe4Scheloha 		timeout.tv_sec = (time_t)sectimeout * 60;
167b02d8f05Stedu 		memset(&ntimer, 0, sizeof(ntimer));
168df930be7Sderaadt 		ntimer.it_value = timeout;
169df930be7Sderaadt 		setitimer(ITIMER_REAL, &ntimer, &otimer);
1702d9f0fe4Scheloha 	}
171df930be7Sderaadt 
172df930be7Sderaadt 	/* header info */
173dde5a603Sdownsj 	if (no_timeout) {
174a8b197cdStedu 		fprintf(stderr,
175ed8b5f35Smillert 		    "%s: %s on %s. no timeout\ntime now is %s\n",
1764b9dbb2eStedu 		    getprogname(), ttynam, hostname, date);
177dde5a603Sdownsj 	} else {
178a8b197cdStedu 		fprintf(stderr,
179ed8b5f35Smillert 		    "%s: %s on %s. timeout in %d minutes\ntime now is %s\n",
1804b9dbb2eStedu 		    getprogname(), ttynam, hostname, sectimeout, date);
181dde5a603Sdownsj 	}
182df930be7Sderaadt 
183c52c9307Smartynas 	for (cnt = 0;;) {
184c24b2f3dStedu 		if (!readpassphrase("Key: ", s, sizeof(s), RPP_ECHO_OFF))
185c24b2f3dStedu 			continue;
186c24b2f3dStedu 		if (strlen(s) == 0) {
187266ade72Smillert 			hi(0);
188df930be7Sderaadt 			continue;
189df930be7Sderaadt 		}
190df930be7Sderaadt 		if (usemine) {
191df930be7Sderaadt 			/*
192ed8b5f35Smillert 			 * If user entered 's/key' or the style specified via
193ed8b5f35Smillert 			 * the '-a' argument, auth_userokay() will prompt
194ed8b5f35Smillert 			 * for a new password.  Otherwise, use what we have.
195df930be7Sderaadt 			 */
196ed8b5f35Smillert 			if ((strcmp(s, "s/key") == 0 &&
197ed8b5f35Smillert 			    (nstyle = login_getstyle(lc, "skey", "auth-lock")))
198ed8b5f35Smillert 			    || ((nstyle = style) && strcmp(s, nstyle) == 0))
199ed8b5f35Smillert 				p = NULL;
200ed8b5f35Smillert 			else
201ed8b5f35Smillert 				p = s;
2023a7efd93Smestre 			if (auth_userokay(pw->pw_name, nstyle, "auth-lock",
2033a7efd93Smestre 			    p)) {
2043a7efd93Smestre 				explicit_bzero(s, sizeof(s));
205ed8b5f35Smillert 				break;
2063a7efd93Smestre 			}
207757e8834Stedu 		} else if (crypt_checkpass(s, hash) == 0) {
2083a7efd93Smestre 			explicit_bzero(s, sizeof(s));
2092e4bb4d9Stb 			explicit_bzero(hash, sizeof(hash));
210ed8b5f35Smillert 			break;
2113a7efd93Smestre 		}
212a8b197cdStedu 		putc('\a', stderr);
213c52c9307Smartynas 		cnt %= tries;
214c52c9307Smartynas 		if (++cnt > backoff) {
215c52c9307Smartynas 			sigset_t set, oset;
216c52c9307Smartynas 			sigfillset(&set);
217c52c9307Smartynas 			sigprocmask(SIG_BLOCK, &set, &oset);
218c52c9307Smartynas 			sleep((u_int)((cnt - backoff) * tries / 2));
219c52c9307Smartynas 			sigprocmask(SIG_SETMASK, &oset, NULL);
220c52c9307Smartynas 		}
221ed8b5f35Smillert 	}
222df930be7Sderaadt 
223ed8b5f35Smillert 	exit(0);
224df930be7Sderaadt }
225df930be7Sderaadt 
226df930be7Sderaadt void
hi(int signo)227318c2520Sderaadt hi(int signo)
228df930be7Sderaadt {
229c9c0bf19Stedu 	struct itimerval left;
230df930be7Sderaadt 
231a8b197cdStedu 	dprintf(STDERR_FILENO, "%s: type in the unlock key.",
2324b9dbb2eStedu 	    getprogname());
233c9c0bf19Stedu 	if (!no_timeout) {
234a8b197cdStedu 		getitimer(ITIMER_REAL, &left);
235a8b197cdStedu 		dprintf(STDERR_FILENO, " timeout in %lld:%02d minutes",
236c9c0bf19Stedu 		    (long long)(left.it_value.tv_sec / 60),
237c9c0bf19Stedu 		    (int)(left.it_value.tv_sec % 60));
238dde5a603Sdownsj 	}
239a8b197cdStedu 	dprintf(STDERR_FILENO, "\n");
240df930be7Sderaadt }
241df930be7Sderaadt 
242df930be7Sderaadt void
bye(int signo)243318c2520Sderaadt bye(int signo)
244df930be7Sderaadt {
245ed8b5f35Smillert 
246ed8b5f35Smillert 	if (!no_timeout)
24782aaa63dSmillert 		warnx("timeout");
24882aaa63dSmillert 	_exit(1);
249dde5a603Sdownsj }
2508a6602a7Scheloha 
2518a6602a7Scheloha void
usage(void)2528a6602a7Scheloha usage(void)
2538a6602a7Scheloha {
254*f6462fb5Sschwarze 	fprintf(stderr, "usage: %s [-np] [-a style] [-t timeout]\n",
2558a6602a7Scheloha 	    getprogname());
2568a6602a7Scheloha 	exit(1);
2578a6602a7Scheloha }
258