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