1*84d9c625SLionel Sambuc /* $NetBSD: lock.c,v 1.33 2013/10/18 20:47:06 christos Exp $ */
2f3c74513SThomas Cort
3f3c74513SThomas Cort /*
4f3c74513SThomas Cort * Copyright (c) 1980, 1987, 1993
5f3c74513SThomas Cort * The Regents of the University of California. All rights reserved.
6f3c74513SThomas Cort *
7f3c74513SThomas Cort * This code is derived from software contributed to Berkeley by
8f3c74513SThomas Cort * Bob Toxen.
9f3c74513SThomas Cort *
10f3c74513SThomas Cort * Redistribution and use in source and binary forms, with or without
11f3c74513SThomas Cort * modification, are permitted provided that the following conditions
12f3c74513SThomas Cort * are met:
13f3c74513SThomas Cort * 1. Redistributions of source code must retain the above copyright
14f3c74513SThomas Cort * notice, this list of conditions and the following disclaimer.
15f3c74513SThomas Cort * 2. Redistributions in binary form must reproduce the above copyright
16f3c74513SThomas Cort * notice, this list of conditions and the following disclaimer in the
17f3c74513SThomas Cort * documentation and/or other materials provided with the distribution.
18f3c74513SThomas Cort * 3. Neither the name of the University nor the names of its contributors
19f3c74513SThomas Cort * may be used to endorse or promote products derived from this software
20f3c74513SThomas Cort * without specific prior written permission.
21f3c74513SThomas Cort *
22f3c74513SThomas Cort * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23f3c74513SThomas Cort * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24f3c74513SThomas Cort * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25f3c74513SThomas Cort * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26f3c74513SThomas Cort * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27f3c74513SThomas Cort * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28f3c74513SThomas Cort * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29f3c74513SThomas Cort * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30f3c74513SThomas Cort * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31f3c74513SThomas Cort * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32f3c74513SThomas Cort * SUCH DAMAGE.
33f3c74513SThomas Cort */
34f3c74513SThomas Cort
35f3c74513SThomas Cort #include <sys/cdefs.h>
36f3c74513SThomas Cort #ifndef lint
37f3c74513SThomas Cort __COPYRIGHT("@(#) Copyright (c) 1980, 1987, 1993\
38f3c74513SThomas Cort The Regents of the University of California. All rights reserved.");
39f3c74513SThomas Cort #endif /* not lint */
40f3c74513SThomas Cort
41f3c74513SThomas Cort #ifndef lint
42f3c74513SThomas Cort #if 0
43f3c74513SThomas Cort static char sccsid[] = "@(#)lock.c 8.1 (Berkeley) 6/6/93";
44f3c74513SThomas Cort #endif
45*84d9c625SLionel Sambuc __RCSID("$NetBSD: lock.c,v 1.33 2013/10/18 20:47:06 christos Exp $");
46f3c74513SThomas Cort #endif /* not lint */
47f3c74513SThomas Cort
48f3c74513SThomas Cort /*
49f3c74513SThomas Cort * Lock a terminal up until the given key is entered, until the root
50f3c74513SThomas Cort * password is entered, or the given interval times out.
51f3c74513SThomas Cort *
52f3c74513SThomas Cort * Timeout interval is by default TIMEOUT, it can be changed with
53f3c74513SThomas Cort * an argument of the form -time where time is in minutes
54f3c74513SThomas Cort */
55f3c74513SThomas Cort
56f3c74513SThomas Cort #include <sys/param.h>
57f3c74513SThomas Cort #include <sys/stat.h>
58f3c74513SThomas Cort #include <sys/time.h>
59f3c74513SThomas Cort #include <signal.h>
60f3c74513SThomas Cort
61f3c74513SThomas Cort #include <err.h>
62f3c74513SThomas Cort #include <pwd.h>
63f3c74513SThomas Cort #include <errno.h>
64f3c74513SThomas Cort #include <stdio.h>
65f3c74513SThomas Cort #include <stdlib.h>
66f3c74513SThomas Cort #include <string.h>
67f3c74513SThomas Cort #include <termios.h>
68f3c74513SThomas Cort #include <time.h>
69f3c74513SThomas Cort #include <unistd.h>
70f3c74513SThomas Cort
71f3c74513SThomas Cort #ifdef USE_PAM
72f3c74513SThomas Cort #include <security/pam_appl.h>
73f3c74513SThomas Cort #include <security/openpam.h> /* for openpam_ttyconv() */
74f3c74513SThomas Cort #endif
75f3c74513SThomas Cort
76f3c74513SThomas Cort #ifdef SKEY
77f3c74513SThomas Cort #include <skey.h>
78f3c74513SThomas Cort #endif
79f3c74513SThomas Cort
80f3c74513SThomas Cort
81f3c74513SThomas Cort #define TIMEOUT 15
82f3c74513SThomas Cort
83f3c74513SThomas Cort int main(int, char **);
84f3c74513SThomas Cort
85f3c74513SThomas Cort static void bye(int) __dead;
86f3c74513SThomas Cort static void hi(int);
87f3c74513SThomas Cort static void quit(int) __dead;
88f3c74513SThomas Cort #ifdef SKEY
89f3c74513SThomas Cort static int skey_auth(const char *);
90f3c74513SThomas Cort #endif
91f3c74513SThomas Cort
92f3c74513SThomas Cort static struct timeval timeout;
93f3c74513SThomas Cort static struct timeval zerotime;
94f3c74513SThomas Cort static struct termios tty, ntty;
95f3c74513SThomas Cort static int notimeout; /* no timeout at all */
96f3c74513SThomas Cort static long nexttime; /* keep the timeout time */
97f3c74513SThomas Cort
98f3c74513SThomas Cort int
main(int argc,char ** argv)99f3c74513SThomas Cort main(int argc, char **argv)
100f3c74513SThomas Cort {
101f3c74513SThomas Cort struct passwd *pw;
102f3c74513SThomas Cort struct timeval timval;
103f3c74513SThomas Cort struct itimerval ntimer, otimer;
104f3c74513SThomas Cort struct tm *timp;
105f3c74513SThomas Cort time_t curtime;
106f3c74513SThomas Cort int ch, usemine;
107f3c74513SThomas Cort long sectimeout;
108*84d9c625SLionel Sambuc char *ap, *ttynam;
109f3c74513SThomas Cort const char *tzn;
110f3c74513SThomas Cort uid_t uid = getuid();
111f3c74513SThomas Cort char hostname[MAXHOSTNAMELEN + 1], s[BUFSIZ], s1[BUFSIZ];
112f3c74513SThomas Cort #ifdef USE_PAM
113f3c74513SThomas Cort pam_handle_t *pamh = NULL;
114f3c74513SThomas Cort static const struct pam_conv pamc = { &openpam_ttyconv, NULL };
115f3c74513SThomas Cort int pam_err;
116*84d9c625SLionel Sambuc #else
117*84d9c625SLionel Sambuc char *mypw = NULL;
118f3c74513SThomas Cort #endif
119f3c74513SThomas Cort
120f3c74513SThomas Cort if ((pw = getpwuid(getuid())) == NULL)
121f3c74513SThomas Cort errx(1, "unknown uid %lu.", (u_long)uid);
122f3c74513SThomas Cort
123f3c74513SThomas Cort notimeout = 0;
124f3c74513SThomas Cort sectimeout = TIMEOUT;
125f3c74513SThomas Cort usemine = 0;
126f3c74513SThomas Cort
127f3c74513SThomas Cort while ((ch = getopt(argc, argv, "npt:")) != -1)
128f3c74513SThomas Cort switch ((char)ch) {
129f3c74513SThomas Cort case 'n':
130f3c74513SThomas Cort notimeout = 1;
131f3c74513SThomas Cort break;
132f3c74513SThomas Cort case 't':
133f3c74513SThomas Cort errno = 0;
134f3c74513SThomas Cort if (((sectimeout = strtol(optarg, &ap, 0)) == LONG_MAX
135f3c74513SThomas Cort || sectimeout == LONG_MIN)
136f3c74513SThomas Cort && errno == ERANGE)
137f3c74513SThomas Cort err(1, "illegal timeout value: %s", optarg);
138f3c74513SThomas Cort if (optarg == ap || *ap || sectimeout <= 0)
139f3c74513SThomas Cort errx(1, "illegal timeout value: %s", optarg);
140f3c74513SThomas Cort if (sectimeout >= INT_MAX / 60)
141f3c74513SThomas Cort errx(1, "too large timeout value: %ld",
142f3c74513SThomas Cort sectimeout);
143f3c74513SThomas Cort break;
144f3c74513SThomas Cort case 'p':
145f3c74513SThomas Cort usemine = 1;
146f3c74513SThomas Cort #ifndef USE_PAM
147f3c74513SThomas Cort mypw = strdup(pw->pw_passwd);
148f3c74513SThomas Cort if (!mypw)
149f3c74513SThomas Cort err(1, "strdup");
150f3c74513SThomas Cort #endif
151f3c74513SThomas Cort break;
152f3c74513SThomas Cort case '?':
153f3c74513SThomas Cort default:
154f3c74513SThomas Cort (void)fprintf(stderr,
155f3c74513SThomas Cort "usage: %s [-np] [-t timeout]\n", getprogname());
156f3c74513SThomas Cort exit(1);
157f3c74513SThomas Cort }
158f3c74513SThomas Cort
159f3c74513SThomas Cort #if defined(USE_PAM) || defined(SKEY)
160f3c74513SThomas Cort if (! usemine) { /* -p with PAM or S/key needs privs */
161f3c74513SThomas Cort #endif
162f3c74513SThomas Cort if (setuid(uid) == -1) /* discard privs */
163f3c74513SThomas Cort err(1, "setuid failed");
164f3c74513SThomas Cort #if defined(USE_PAM) || defined(SKEY)
165f3c74513SThomas Cort }
166f3c74513SThomas Cort #endif
167f3c74513SThomas Cort
168f3c74513SThomas Cort timeout.tv_sec = (int)sectimeout * 60;
169f3c74513SThomas Cort
170f3c74513SThomas Cort if (tcgetattr(STDIN_FILENO, &tty) < 0) /* get information for header */
171f3c74513SThomas Cort err(1, "tcgetattr failed");
172f3c74513SThomas Cort gethostname(hostname, sizeof(hostname));
173f3c74513SThomas Cort hostname[sizeof(hostname) - 1] = '\0';
174f3c74513SThomas Cort if (!(ttynam = ttyname(STDIN_FILENO)))
175f3c74513SThomas Cort err(1, "ttyname failed");
176f3c74513SThomas Cort if (gettimeofday(&timval, NULL) == -1)
177f3c74513SThomas Cort err(1, "gettimeofday failed");
178f3c74513SThomas Cort curtime = timval.tv_sec;
179f3c74513SThomas Cort nexttime = timval.tv_sec + ((int)sectimeout * 60);
180f3c74513SThomas Cort timp = localtime(&curtime);
181f3c74513SThomas Cort ap = asctime(timp);
182f3c74513SThomas Cort #ifdef __SVR4
183f3c74513SThomas Cort tzn = tzname[0];
184f3c74513SThomas Cort #else
185f3c74513SThomas Cort tzn = timp->tm_zone;
186f3c74513SThomas Cort #endif
187f3c74513SThomas Cort
188f3c74513SThomas Cort if (signal(SIGINT, quit) == SIG_ERR)
189f3c74513SThomas Cort err(1, "signal failed");
190f3c74513SThomas Cort if (signal(SIGQUIT, quit) == SIG_ERR)
191f3c74513SThomas Cort err(1, "signal failed");
192f3c74513SThomas Cort ntty = tty; ntty.c_lflag &= ~ECHO;
193f3c74513SThomas Cort if (tcsetattr(STDIN_FILENO, TCSADRAIN, &ntty) == -1)
194f3c74513SThomas Cort err(1, "tcsetattr");
195f3c74513SThomas Cort
196f3c74513SThomas Cort if (!usemine) {
197f3c74513SThomas Cort /* get key and check again */
198f3c74513SThomas Cort (void)printf("Key: ");
199f3c74513SThomas Cort if (!fgets(s, sizeof(s), stdin) || *s == '\n')
200f3c74513SThomas Cort quit(0);
201f3c74513SThomas Cort (void)printf("\nAgain: ");
202f3c74513SThomas Cort /*
203f3c74513SThomas Cort * Don't need EOF test here, if we get EOF, then s1 != s
204f3c74513SThomas Cort * and the right things will happen.
205f3c74513SThomas Cort */
206f3c74513SThomas Cort (void)fgets(s1, sizeof(s1), stdin);
207f3c74513SThomas Cort (void)putchar('\n');
208f3c74513SThomas Cort if (strcmp(s1, s)) {
209f3c74513SThomas Cort (void)printf("\alock: passwords didn't match.\n");
210f3c74513SThomas Cort (void)tcsetattr(STDIN_FILENO, TCSADRAIN, &tty);
211f3c74513SThomas Cort exit(1);
212f3c74513SThomas Cort }
213f3c74513SThomas Cort s[0] = '\0';
214*84d9c625SLionel Sambuc #ifndef USE_PAM
215f3c74513SThomas Cort mypw = s1;
216*84d9c625SLionel Sambuc #endif
217f3c74513SThomas Cort }
218f3c74513SThomas Cort #ifdef USE_PAM
219f3c74513SThomas Cort if (usemine) {
220f3c74513SThomas Cort pam_err = pam_start("lock", pw->pw_name, &pamc, &pamh);
221f3c74513SThomas Cort if (pam_err != PAM_SUCCESS)
222f3c74513SThomas Cort err(1, "pam_start: %s", pam_strerror(NULL, pam_err));
223f3c74513SThomas Cort }
224f3c74513SThomas Cort #endif
225f3c74513SThomas Cort
226f3c74513SThomas Cort /* set signal handlers */
227f3c74513SThomas Cort if (signal(SIGINT, hi) == SIG_ERR)
228f3c74513SThomas Cort err(1, "signal failed");
229f3c74513SThomas Cort if (signal(SIGQUIT, hi) == SIG_ERR)
230f3c74513SThomas Cort err(1, "signal failed");
231f3c74513SThomas Cort if (signal(SIGTSTP, hi) == SIG_ERR)
232f3c74513SThomas Cort err(1, "signal failed");
233f3c74513SThomas Cort
234f3c74513SThomas Cort if (notimeout) {
235f3c74513SThomas Cort if (signal(SIGALRM, hi) == SIG_ERR)
236f3c74513SThomas Cort err(1, "signal failed");
237f3c74513SThomas Cort (void)printf("lock: %s on %s. no timeout.\n"
238f3c74513SThomas Cort "time now is %.20s%s%s",
239f3c74513SThomas Cort ttynam, hostname, ap, tzn, ap + 19);
240f3c74513SThomas Cort }
241f3c74513SThomas Cort else {
242f3c74513SThomas Cort if (signal(SIGALRM, bye) == SIG_ERR)
243f3c74513SThomas Cort err(1, "signal failed");
244f3c74513SThomas Cort
245f3c74513SThomas Cort ntimer.it_interval = zerotime;
246f3c74513SThomas Cort ntimer.it_value = timeout;
247f3c74513SThomas Cort if (setitimer(ITIMER_REAL, &ntimer, &otimer) == -1)
248f3c74513SThomas Cort err(1, "setitimer failed");
249f3c74513SThomas Cort
250f3c74513SThomas Cort /* header info */
251f3c74513SThomas Cort (void)printf("lock: %s on %s. timeout in %ld minutes\n"
252f3c74513SThomas Cort "time now is %.20s%s%s",
253f3c74513SThomas Cort ttynam, hostname, sectimeout, ap, tzn, ap + 19);
254f3c74513SThomas Cort }
255f3c74513SThomas Cort
256f3c74513SThomas Cort for (;;) {
257f3c74513SThomas Cort #ifdef USE_PAM
258f3c74513SThomas Cort if (usemine) {
259f3c74513SThomas Cort pam_err = pam_authenticate(pamh, 0);
260f3c74513SThomas Cort if (pam_err == PAM_SUCCESS)
261f3c74513SThomas Cort break;
262f3c74513SThomas Cort goto tryagain;
263f3c74513SThomas Cort }
264f3c74513SThomas Cort #endif
265f3c74513SThomas Cort (void)printf("Key: ");
266f3c74513SThomas Cort if (!fgets(s, sizeof(s), stdin)) {
267f3c74513SThomas Cort clearerr(stdin);
268f3c74513SThomas Cort hi(0);
269f3c74513SThomas Cort goto tryagain;
270f3c74513SThomas Cort }
271f3c74513SThomas Cort #ifndef USE_PAM
272f3c74513SThomas Cort if (usemine) {
273f3c74513SThomas Cort s[strlen(s) - 1] = '\0';
274f3c74513SThomas Cort #ifdef SKEY
275f3c74513SThomas Cort if (strcasecmp(s, "s/key") == 0) {
276f3c74513SThomas Cort if (skey_auth(pw->pw_name))
277f3c74513SThomas Cort break;
278f3c74513SThomas Cort }
279f3c74513SThomas Cort #endif
280f3c74513SThomas Cort if (!strcmp(mypw, crypt(s, mypw)))
281f3c74513SThomas Cort break;
282f3c74513SThomas Cort }
283f3c74513SThomas Cort else
284f3c74513SThomas Cort #endif
285f3c74513SThomas Cort if (!strcmp(s, s1))
286f3c74513SThomas Cort break;
287f3c74513SThomas Cort (void)printf("\a\n");
288f3c74513SThomas Cort tryagain:
289f3c74513SThomas Cort if (tcsetattr(STDIN_FILENO, TCSADRAIN, &ntty) == -1
290f3c74513SThomas Cort && errno != EINTR)
291f3c74513SThomas Cort err(1, "tcsetattr failed");
292f3c74513SThomas Cort }
293f3c74513SThomas Cort #ifdef USE_PAM
294f3c74513SThomas Cort if (usemine) {
295f3c74513SThomas Cort (void)pam_end(pamh, pam_err);
296f3c74513SThomas Cort }
297f3c74513SThomas Cort #endif
298f3c74513SThomas Cort quit(0);
299f3c74513SThomas Cort /* NOTREACHED */
300f3c74513SThomas Cort return 0;
301f3c74513SThomas Cort }
302f3c74513SThomas Cort
303f3c74513SThomas Cort #ifdef SKEY
304f3c74513SThomas Cort /*
305f3c74513SThomas Cort * We can't use libskey's skey_authenticate() since it
306f3c74513SThomas Cort * handles signals in a way that's inappropriate
307f3c74513SThomas Cort * for our needs. Instead we roll our own.
308f3c74513SThomas Cort */
309f3c74513SThomas Cort static int
skey_auth(const char * user)310f3c74513SThomas Cort skey_auth(const char *user)
311f3c74513SThomas Cort {
312f3c74513SThomas Cort char s[128];
313f3c74513SThomas Cort const char *ask;
314f3c74513SThomas Cort int ret = 0;
315f3c74513SThomas Cort
316f3c74513SThomas Cort if (!skey_haskey(user) && (ask = skey_keyinfo(user))) {
317f3c74513SThomas Cort (void)printf("\n[%s]\nResponse: ", ask);
318f3c74513SThomas Cort if (!fgets(s, sizeof(s), stdin) || *s == '\n')
319f3c74513SThomas Cort clearerr(stdin);
320f3c74513SThomas Cort else {
321f3c74513SThomas Cort s[strlen(s) - 1] = '\0';
322f3c74513SThomas Cort if (skey_passcheck(user, s) != -1)
323f3c74513SThomas Cort ret = 1;
324f3c74513SThomas Cort }
325f3c74513SThomas Cort } else
326f3c74513SThomas Cort (void)printf("Sorry, you have no s/key.\n");
327f3c74513SThomas Cort return ret;
328f3c74513SThomas Cort }
329f3c74513SThomas Cort #endif
330f3c74513SThomas Cort
331f3c74513SThomas Cort static void
hi(int dummy)332f3c74513SThomas Cort hi(int dummy)
333f3c74513SThomas Cort {
334f3c74513SThomas Cort struct timeval timval;
335f3c74513SThomas Cort
336f3c74513SThomas Cort if (notimeout)
337f3c74513SThomas Cort (void)printf("lock: type in the unlock key.\n");
338f3c74513SThomas Cort else {
339f3c74513SThomas Cort if (gettimeofday(&timval, NULL) == -1)
340f3c74513SThomas Cort err(1, "gettimeofday failed");
341f3c74513SThomas Cort (void)printf("lock: type in the unlock key. "
342f3c74513SThomas Cort "timeout in %lld:%lld minutes\n",
343f3c74513SThomas Cort (long long)(nexttime - timval.tv_sec) / 60,
344f3c74513SThomas Cort (long long)(nexttime - timval.tv_sec) % 60);
345f3c74513SThomas Cort }
346f3c74513SThomas Cort }
347f3c74513SThomas Cort
348f3c74513SThomas Cort static void
quit(int dummy)349f3c74513SThomas Cort quit(int dummy)
350f3c74513SThomas Cort {
351f3c74513SThomas Cort (void)putchar('\n');
352f3c74513SThomas Cort (void)tcsetattr(STDIN_FILENO, TCSADRAIN, &tty);
353f3c74513SThomas Cort exit(0);
354f3c74513SThomas Cort }
355f3c74513SThomas Cort
356f3c74513SThomas Cort static void
bye(int dummy)357f3c74513SThomas Cort bye(int dummy)
358f3c74513SThomas Cort {
359f3c74513SThomas Cort (void)tcsetattr(STDIN_FILENO, TCSADRAIN, &tty);
360f3c74513SThomas Cort (void)printf("lock: timeout\n");
361f3c74513SThomas Cort exit(1);
362f3c74513SThomas Cort }
363