#include <sys/param.h>
#include <sys/time.h>
#include <sys/consio.h>
#include <err.h>
#include <pwd.h>
#include <errno.h>
#include <stdint.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <termios.h>
#include <unistd.h>
#define TIMEOUT 15
static void quit(int);
static void bye(int);
static void hi(int);
static void usage(void);
static struct timeval timeout;
static struct timeval zerotime;
static struct termios tty, ntty;
static long nexttime;
static int no_timeout;
static int vtyunlock;
int
main(int argc, char **argv)
{
struct passwd *pw;
time_t timval;
struct itimerval ntimer, otimer;
struct tm *timp;
int ch, failures, sectimeout, usemine, vtylock;
long tmp;
char *ap, *ep, *mypw, *cryptpw, *ttynam, *tzn;
char hostname[MAXHOSTNAMELEN + 1], s[BUFSIZ], s1[BUFSIZ];
openlog("lock", 0, LOG_AUTH);
sectimeout = TIMEOUT;
pw = NULL;
mypw = NULL;
usemine = 0;
no_timeout = 0;
vtylock = 0;
while ((ch = getopt(argc, argv, "npt:v")) != -1)
switch(ch) {
case 't':
tmp = strtol(optarg, &ep, 10);
if (*ep != '\0' || tmp > INT_MAX || tmp < INT_MIN)
errx(1, "illegal timeout value");
sectimeout = (int)tmp;
break;
case 'p':
usemine = 1;
if (!(pw = getpwuid(getuid())))
errx(1, "unknown uid %d", getuid());
mypw = strdup(pw->pw_passwd);
break;
case 'n':
no_timeout = 1;
break;
case 'v':
vtylock = 1;
break;
case '?':
default:
usage();
}
timeout.tv_sec = sectimeout * 60;
if (setuid(getuid()) != 0)
errx(1, "setuid failed");
if (tcgetattr(STDIN_FILENO, &tty))
err(1, "tcgetattr failed");
gethostname(hostname, sizeof(hostname));
if (!(ttynam = ttyname(STDIN_FILENO)))
errx(1, "not a terminal?");
timval = time(NULL);
nexttime = timval + (sectimeout * 60);
timp = localtime(&timval);
ap = asctime(timp);
tzn = timp->tm_zone;
signal(SIGINT, quit);
signal(SIGQUIT, quit);
ntty = tty; ntty.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO, TCSADRAIN|TCSASOFT, &ntty);
if (!mypw) {
printf("Key: ");
if (!fgets(s, sizeof(s), stdin) || *s == '\n')
quit(0);
printf("\nAgain: ");
fgets(s1, sizeof(s1), stdin);
putchar('\n');
if (strcmp(s1, s)) {
printf("\07lock: passwords didn't match.\n");
tcsetattr(STDIN_FILENO, TCSADRAIN|TCSASOFT, &tty);
exit(1);
}
s[0] = '\0';
mypw = s1;
}
signal(SIGINT, hi);
signal(SIGQUIT, hi);
signal(SIGTSTP, hi);
signal(SIGALRM, bye);
ntimer.it_interval = zerotime;
ntimer.it_value = timeout;
if (!no_timeout)
setitimer(ITIMER_REAL, &ntimer, &otimer);
if (vtylock) {
if (ioctl(STDIN_FILENO, VT_LOCKSWITCH, &vtylock) == -1) {
tcsetattr(0, TCSADRAIN|TCSASOFT, &tty);
err(1, "locking vty");
}
vtyunlock = 0x2;
}
if (pw != NULL)
printf("lock: %s using %s on %s.", pw->pw_name,
ttynam, hostname);
else
printf("lock: %s on %s.", ttynam, hostname);
if (no_timeout)
printf(" no timeout.");
else
printf(" timeout in %d minute%s.", sectimeout,
sectimeout != 1 ? "s" : "");
if (vtylock)
printf(" vty locked.");
printf("\ntime now is %.20s%s%s", ap, tzn, ap + 19);
failures = 0;
for (;;) {
printf("Key: ");
if (!fgets(s, sizeof(s), stdin)) {
clearerr(stdin);
hi(0);
goto tryagain;
}
if (usemine) {
s[strlen(s) - 1] = '\0';
cryptpw = crypt(s, mypw);
if (cryptpw == NULL || !strcmp(mypw, cryptpw))
break;
}
else if (!strcmp(s, s1))
break;
printf("\07\n");
failures++;
if (getuid() == 0)
syslog(LOG_NOTICE, "%d ROOT UNLOCK FAILURE%s (%s on %s)",
failures, failures > 1 ? "S": "", ttynam, hostname);
tryagain:
if (tcgetattr(0, &ntty) && (errno != EINTR))
exit(1);
sleep(1);
}
if (getuid() == 0)
syslog(LOG_NOTICE, "ROOT UNLOCK ON hostname %s port %s",
hostname, ttynam);
quit(0);
return(0);
}
static void
usage(void)
{
fprintf(stderr, "usage: lock [-npv] [-t timeout]\n");
exit(1);
}
static void
hi(int signo __unused)
{
time_t timval;
if ((timval = time(NULL)) != (time_t)-1) {
printf("lock: type in the unlock key. ");
if (no_timeout) {
putchar('\n');
} else {
printf("timeout in %jd:%jd minutes\n",
(intmax_t)(nexttime - timval) / 60,
(intmax_t)(nexttime - timval) % 60);
}
}
}
static void
quit(int signo __unused)
{
putchar('\n');
tcsetattr(STDIN_FILENO, TCSADRAIN|TCSASOFT, &tty);
if (vtyunlock)
ioctl(STDIN_FILENO, VT_LOCKSWITCH, &vtyunlock);
exit(0);
}
static void
bye(int signo __unused)
{
if (!no_timeout) {
tcsetattr(STDIN_FILENO, TCSADRAIN|TCSASOFT, &tty);
if (vtyunlock)
ioctl(STDIN_FILENO, VT_LOCKSWITCH, &vtyunlock);
printf("lock: timeout\n");
exit(1);
}
}