xref: /netbsd-src/usr.bin/lock/lock.c (revision 39bbc68a3b8731308f39852383f5430560168a91)
1*39bbc68aSsevan /*	$NetBSD: lock.c,v 1.34 2016/09/05 00:40:29 sevan Exp $	*/
231ac284aSjtc 
361f28255Scgd /*
431ac284aSjtc  * Copyright (c) 1980, 1987, 1993
531ac284aSjtc  *	The Regents of the University of California.  All rights reserved.
631ac284aSjtc  *
731ac284aSjtc  * This code is derived from software contributed to Berkeley by
831ac284aSjtc  * Bob Toxen.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
1889aaa1bbSagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
350c797888Slukem #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
3798e5374cSlukem __COPYRIGHT("@(#) Copyright (c) 1980, 1987, 1993\
3898e5374cSlukem  The Regents of the University of California.  All rights reserved.");
3961f28255Scgd #endif /* not lint */
4061f28255Scgd 
4161f28255Scgd #ifndef lint
4231ac284aSjtc #if 0
4331ac284aSjtc static char sccsid[] = "@(#)lock.c	8.1 (Berkeley) 6/6/93";
4431ac284aSjtc #endif
45*39bbc68aSsevan __RCSID("$NetBSD: lock.c,v 1.34 2016/09/05 00:40:29 sevan Exp $");
4661f28255Scgd #endif /* not lint */
4761f28255Scgd 
4861f28255Scgd /*
4961f28255Scgd  * Lock a terminal up until the given key is entered, until the root
5061f28255Scgd  * password is entered, or the given interval times out.
5161f28255Scgd  *
5261f28255Scgd  * Timeout interval is by default TIMEOUT, it can be changed with
5361f28255Scgd  * an argument of the form -time where time is in minutes
5461f28255Scgd  */
5561f28255Scgd 
5661f28255Scgd #include <sys/param.h>
5761f28255Scgd #include <sys/stat.h>
5861f28255Scgd #include <sys/time.h>
594a241ea5Sjtc #include <signal.h>
60bd23a100Smycroft 
61bd23a100Smycroft #include <err.h>
6261f28255Scgd #include <pwd.h>
63eee22daeSchristos #include <errno.h>
6461f28255Scgd #include <stdio.h>
65273e6cc6Sjtc #include <stdlib.h>
6661f28255Scgd #include <string.h>
67bd23a100Smycroft #include <termios.h>
68bed4261eSkleink #include <time.h>
69273e6cc6Sjtc #include <unistd.h>
7073d8c423Slukem 
7173d8c423Slukem #ifdef USE_PAM
7273d8c423Slukem #include <security/pam_appl.h>
7373d8c423Slukem #include <security/openpam.h>	/* for openpam_ttyconv() */
7473d8c423Slukem #endif
7573d8c423Slukem 
760c797888Slukem #ifdef SKEY
770c797888Slukem #include <skey.h>
780c797888Slukem #endif
7961f28255Scgd 
8073d8c423Slukem 
8161f28255Scgd #define	TIMEOUT	15
8261f28255Scgd 
838b0f9554Sperry static void	bye(int) __dead;
843506cd1fSchristos static void	hi(int);
858b0f9554Sperry static void	quit(int) __dead;
860c797888Slukem #ifdef SKEY
873506cd1fSchristos static int	skey_auth(const char *);
880c797888Slukem #endif
8961f28255Scgd 
903506cd1fSchristos static struct timeval	timeout;
913506cd1fSchristos static struct timeval	zerotime;
923506cd1fSchristos static struct termios	tty, ntty;
933506cd1fSchristos static int	notimeout;			/* no timeout at all */
943506cd1fSchristos static long	nexttime;			/* keep the timeout time */
9561f28255Scgd 
960c797888Slukem int
main(int argc,char ** argv)973506cd1fSchristos main(int argc, char **argv)
9861f28255Scgd {
9961f28255Scgd 	struct passwd *pw;
10061f28255Scgd 	struct timeval timval;
10161f28255Scgd 	struct itimerval ntimer, otimer;
10261f28255Scgd 	struct tm *timp;
103ed7cdf53Scgd 	time_t curtime;
1043506cd1fSchristos 	int ch, usemine;
1053506cd1fSchristos 	long sectimeout;
1066e28978dSchristos 	char *ap, *ttynam;
10766427701Smycroft 	const char *tzn;
1083506cd1fSchristos 	uid_t uid = getuid();
1092beab49aSmrg 	char hostname[MAXHOSTNAMELEN + 1], s[BUFSIZ], s1[BUFSIZ];
11073d8c423Slukem #ifdef USE_PAM
11173d8c423Slukem 	pam_handle_t *pamh = NULL;
11273d8c423Slukem 	static const struct pam_conv pamc = { &openpam_ttyconv, NULL };
11373d8c423Slukem 	int pam_err;
1146e28978dSchristos #else
1156e28978dSchristos 	char *mypw = NULL;
11673d8c423Slukem #endif
11761f28255Scgd 
1183506cd1fSchristos 	if ((pw = getpwuid(getuid())) == NULL)
1193506cd1fSchristos 		errx(1, "unknown uid %lu.", (u_long)uid);
120b4d921abSmrg 
1219a312a99Stron 	notimeout = 0;
12261f28255Scgd 	sectimeout = TIMEOUT;
12361f28255Scgd 	usemine = 0;
12458732c3cSderaadt 
1259a312a99Stron 	while ((ch = getopt(argc, argv, "npt:")) != -1)
12661f28255Scgd 		switch ((char)ch) {
1279a312a99Stron 		case 'n':
1289a312a99Stron 			notimeout = 1;
1299a312a99Stron 			break;
13061f28255Scgd 		case 't':
1311ee9d337Sliamjfoy 			errno = 0;
1323506cd1fSchristos 			if (((sectimeout = strtol(optarg, &ap, 0)) == LONG_MAX
1333506cd1fSchristos 			    || sectimeout == LONG_MIN)
1343506cd1fSchristos 			    && errno == ERANGE)
1353506cd1fSchristos 				err(1, "illegal timeout value: %s", optarg);
1363506cd1fSchristos 			if (optarg == ap || *ap || sectimeout <= 0)
137bd23a100Smycroft 				errx(1, "illegal timeout value: %s", optarg);
1383506cd1fSchristos 			if (sectimeout >= INT_MAX / 60)
1393506cd1fSchristos 				errx(1, "too large timeout value: %ld",
1403506cd1fSchristos 				    sectimeout);
14161f28255Scgd 			break;
14261f28255Scgd 		case 'p':
14361f28255Scgd 			usemine = 1;
14473d8c423Slukem #ifndef USE_PAM
14561f28255Scgd 			mypw = strdup(pw->pw_passwd);
1465f2d0b66Sitojun 			if (!mypw)
1475f2d0b66Sitojun 				err(1, "strdup");
14873d8c423Slukem #endif
14961f28255Scgd 			break;
15061f28255Scgd 		case '?':
15161f28255Scgd 		default:
15261f28255Scgd 			(void)fprintf(stderr,
1538ff81120Swiz 			    "usage: %s [-np] [-t timeout]\n", getprogname());
15461f28255Scgd 			exit(1);
15561f28255Scgd 		}
15673d8c423Slukem 
15773d8c423Slukem #if defined(USE_PAM) || defined(SKEY)
15873d8c423Slukem 	if (! usemine) {	/* -p with PAM or S/key needs privs */
15973d8c423Slukem #endif
16073d8c423Slukem 	if (setuid(uid) == -1)	/* discard privs */
16173d8c423Slukem 		err(1, "setuid failed");
16273d8c423Slukem #if defined(USE_PAM) || defined(SKEY)
16373d8c423Slukem 	}
16473d8c423Slukem #endif
16573d8c423Slukem 
1663506cd1fSchristos 	timeout.tv_sec = (int)sectimeout * 60;
16761f28255Scgd 
1683506cd1fSchristos 	if (tcgetattr(STDIN_FILENO, &tty) < 0)	/* get information for header */
1693506cd1fSchristos 		err(1, "tcgetattr failed");
17061f28255Scgd 	gethostname(hostname, sizeof(hostname));
171b4d921abSmrg 	hostname[sizeof(hostname) - 1] = '\0';
1723506cd1fSchristos 	if (!(ttynam = ttyname(STDIN_FILENO)))
1733506cd1fSchristos 		err(1, "ttyname failed");
1743506cd1fSchristos 	if (gettimeofday(&timval, NULL) == -1)
1753506cd1fSchristos 		err(1, "gettimeofday failed");
176ed7cdf53Scgd 	curtime = timval.tv_sec;
1773506cd1fSchristos 	nexttime = timval.tv_sec + ((int)sectimeout * 60);
178ed7cdf53Scgd 	timp = localtime(&curtime);
17961f28255Scgd 	ap = asctime(timp);
180e2d09d7fSchristos #ifdef __SVR4
181e2d09d7fSchristos 	tzn = tzname[0];
182e2d09d7fSchristos #else
18361f28255Scgd 	tzn = timp->tm_zone;
184e2d09d7fSchristos #endif
18561f28255Scgd 
1863506cd1fSchristos 	if (signal(SIGINT, quit) == SIG_ERR)
1873506cd1fSchristos 		err(1, "signal failed");
1883506cd1fSchristos 	if (signal(SIGQUIT, quit) == SIG_ERR)
1893506cd1fSchristos 		err(1, "signal failed");
190bd23a100Smycroft 	ntty = tty; ntty.c_lflag &= ~ECHO;
1913506cd1fSchristos 	if (tcsetattr(STDIN_FILENO, TCSADRAIN, &ntty) == -1)
1923506cd1fSchristos 		err(1, "tcsetattr");
19361f28255Scgd 
19473d8c423Slukem 	if (!usemine) {
19561f28255Scgd 		/* get key and check again */
19661f28255Scgd 		(void)printf("Key: ");
19761f28255Scgd 		if (!fgets(s, sizeof(s), stdin) || *s == '\n')
1980c797888Slukem 			quit(0);
19961f28255Scgd 		(void)printf("\nAgain: ");
20061f28255Scgd 		/*
20161f28255Scgd 		 * Don't need EOF test here, if we get EOF, then s1 != s
20261f28255Scgd 		 * and the right things will happen.
20361f28255Scgd 		 */
20461f28255Scgd 		(void)fgets(s1, sizeof(s1), stdin);
20561f28255Scgd 		(void)putchar('\n');
20661f28255Scgd 		if (strcmp(s1, s)) {
207bd23a100Smycroft 			(void)printf("\alock: passwords didn't match.\n");
2083506cd1fSchristos 			(void)tcsetattr(STDIN_FILENO, TCSADRAIN, &tty);
20961f28255Scgd 			exit(1);
21061f28255Scgd 		}
211f890b048Spk 		s[0] = '\0';
2126e28978dSchristos #ifndef USE_PAM
21361f28255Scgd 		mypw = s1;
2146e28978dSchristos #endif
21561f28255Scgd 	}
21673d8c423Slukem #ifdef USE_PAM
21773d8c423Slukem 	if (usemine) {
21873d8c423Slukem 		pam_err = pam_start("lock", pw->pw_name, &pamc, &pamh);
21973d8c423Slukem 		if (pam_err != PAM_SUCCESS)
22073d8c423Slukem 			err(1, "pam_start: %s", pam_strerror(NULL, pam_err));
22173d8c423Slukem 	}
22273d8c423Slukem #endif
22361f28255Scgd 
22461f28255Scgd 	/* set signal handlers */
2253506cd1fSchristos 	if (signal(SIGINT, hi) == SIG_ERR)
2263506cd1fSchristos 		err(1, "signal failed");
2273506cd1fSchristos 	if (signal(SIGQUIT, hi) == SIG_ERR)
2283506cd1fSchristos 		err(1, "signal failed");
2293506cd1fSchristos 	if (signal(SIGTSTP, hi) == SIG_ERR)
2303506cd1fSchristos 		err(1, "signal failed");
2319a312a99Stron 
2329a312a99Stron 	if (notimeout) {
2333506cd1fSchristos 		if (signal(SIGALRM, hi) == SIG_ERR)
2343506cd1fSchristos 			err(1, "signal failed");
2353506cd1fSchristos 		(void)printf("lock: %s on %s.  no timeout.\n"
2363506cd1fSchristos 		    "time now is %.20s%s%s",
2379a312a99Stron 		    ttynam, hostname, ap, tzn, ap + 19);
2389a312a99Stron 	}
2399a312a99Stron 	else {
2403506cd1fSchristos 		if (signal(SIGALRM, bye) == SIG_ERR)
2413506cd1fSchristos 			err(1, "signal failed");
24261f28255Scgd 
24361f28255Scgd 		ntimer.it_interval = zerotime;
24461f28255Scgd 		ntimer.it_value = timeout;
2453506cd1fSchristos 		if (setitimer(ITIMER_REAL, &ntimer, &otimer) == -1)
2463506cd1fSchristos 			err(1, "setitimer failed");
24761f28255Scgd 
24861f28255Scgd 		/* header info */
2493506cd1fSchristos 		(void)printf("lock: %s on %s. timeout in %ld minutes\n"
2503506cd1fSchristos 		    "time now is %.20s%s%s",
25161f28255Scgd 		    ttynam, hostname, sectimeout, ap, tzn, ap + 19);
2529a312a99Stron 	}
25361f28255Scgd 
25461f28255Scgd 	for (;;) {
25573d8c423Slukem #ifdef USE_PAM
25673d8c423Slukem 		if (usemine) {
25773d8c423Slukem 			pam_err = pam_authenticate(pamh, 0);
25873d8c423Slukem 			if (pam_err == PAM_SUCCESS)
25973d8c423Slukem 				break;
26073d8c423Slukem 			goto tryagain;
26173d8c423Slukem 		}
26273d8c423Slukem #endif
26361f28255Scgd 		(void)printf("Key: ");
26461f28255Scgd 		if (!fgets(s, sizeof(s), stdin)) {
26561f28255Scgd 			clearerr(stdin);
2660c797888Slukem 			hi(0);
267eee22daeSchristos 			goto tryagain;
26861f28255Scgd 		}
269da88a5a4Schristos #ifndef USE_PAM
27061f28255Scgd 		if (usemine) {
27161f28255Scgd 			s[strlen(s) - 1] = '\0';
27258732c3cSderaadt #ifdef SKEY
27358732c3cSderaadt 			if (strcasecmp(s, "s/key") == 0) {
27458732c3cSderaadt 				if (skey_auth(pw->pw_name))
27558732c3cSderaadt 					break;
27658732c3cSderaadt 			}
27758732c3cSderaadt #endif
27861f28255Scgd 			if (!strcmp(mypw, crypt(s, mypw)))
27961f28255Scgd 				break;
28061f28255Scgd 		}
281da88a5a4Schristos 		else
282da88a5a4Schristos #endif
283da88a5a4Schristos 			if (!strcmp(s, s1))
28461f28255Scgd 				break;
285bd23a100Smycroft 		(void)printf("\a\n");
286eee22daeSchristos  tryagain:
2873506cd1fSchristos 		if (tcsetattr(STDIN_FILENO, TCSADRAIN, &ntty) == -1
2883506cd1fSchristos 		    && errno != EINTR)
2893506cd1fSchristos 			err(1, "tcsetattr failed");
29061f28255Scgd 	}
29173d8c423Slukem #ifdef USE_PAM
29273d8c423Slukem 	if (usemine) {
29373d8c423Slukem 		(void)pam_end(pamh, pam_err);
29473d8c423Slukem 	}
29573d8c423Slukem #endif
2960c797888Slukem 	quit(0);
2970c797888Slukem 	/* NOTREACHED */
2983506cd1fSchristos 	return 0;
29961f28255Scgd }
30061f28255Scgd 
30158732c3cSderaadt #ifdef SKEY
30258732c3cSderaadt /*
30358732c3cSderaadt  * We can't use libskey's skey_authenticate() since it
30458732c3cSderaadt  * handles signals in a way that's inappropriate
30558732c3cSderaadt  * for our needs. Instead we roll our own.
30658732c3cSderaadt  */
3073506cd1fSchristos static int
skey_auth(const char * user)3083506cd1fSchristos skey_auth(const char *user)
30958732c3cSderaadt {
310ce931cccSmartin 	char s[128];
311ce931cccSmartin 	const char *ask;
31258732c3cSderaadt 	int ret = 0;
31358732c3cSderaadt 
31458732c3cSderaadt 	if (!skey_haskey(user) && (ask = skey_keyinfo(user))) {
3153506cd1fSchristos 		(void)printf("\n[%s]\nResponse: ", ask);
31658732c3cSderaadt 		if (!fgets(s, sizeof(s), stdin) || *s == '\n')
31758732c3cSderaadt 			clearerr(stdin);
31858732c3cSderaadt 		else {
31958732c3cSderaadt 			s[strlen(s) - 1] = '\0';
32058732c3cSderaadt 			if (skey_passcheck(user, s) != -1)
32158732c3cSderaadt 				ret = 1;
32258732c3cSderaadt 		}
32358732c3cSderaadt 	} else
3243506cd1fSchristos 		(void)printf("Sorry, you have no s/key.\n");
32558732c3cSderaadt 	return ret;
32658732c3cSderaadt }
32758732c3cSderaadt #endif
32858732c3cSderaadt 
3293506cd1fSchristos static void
hi(int dummy)330d34c2845Smatt hi(int dummy)
33161f28255Scgd {
33261f28255Scgd 	struct timeval timval;
33361f28255Scgd 
3349a312a99Stron 	if (notimeout)
3359a312a99Stron 		(void)printf("lock: type in the unlock key.\n");
3363506cd1fSchristos 	else {
3373506cd1fSchristos 		if (gettimeofday(&timval, NULL) == -1)
3383506cd1fSchristos 			err(1, "gettimeofday failed");
3393506cd1fSchristos 		(void)printf("lock: type in the unlock key. "
34099db4631Schristos 		    "timeout in %lld:%lld minutes\n",
34199db4631Schristos 		    (long long)(nexttime - timval.tv_sec) / 60,
34299db4631Schristos 		    (long long)(nexttime - timval.tv_sec) % 60);
3433506cd1fSchristos 	}
34461f28255Scgd }
34561f28255Scgd 
3463506cd1fSchristos static void
quit(int dummy)3473506cd1fSchristos quit(int dummy)
34861f28255Scgd {
34961f28255Scgd 	(void)putchar('\n');
3503506cd1fSchristos 	(void)tcsetattr(STDIN_FILENO, TCSADRAIN, &tty);
35161f28255Scgd 	exit(0);
35261f28255Scgd }
35361f28255Scgd 
3543506cd1fSchristos static void
bye(int dummy)3553506cd1fSchristos bye(int dummy)
35661f28255Scgd {
3573506cd1fSchristos 	(void)tcsetattr(STDIN_FILENO, TCSADRAIN, &tty);
35861f28255Scgd 	(void)printf("lock: timeout\n");
35961f28255Scgd 	exit(1);
36061f28255Scgd }
361