1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1980, 1987, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Bob Toxen. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #ifndef lint 36 static const char copyright[] = 37 "@(#) Copyright (c) 1980, 1987, 1993\n\ 38 The Regents of the University of California. All rights reserved.\n"; 39 #endif /* not lint */ 40 41 #ifndef lint 42 #if 0 43 static char sccsid[] = "@(#)lock.c 8.1 (Berkeley) 6/6/93"; 44 #endif 45 #endif /* not lint */ 46 #include <sys/cdefs.h> 47 /* 48 * Lock a terminal up until the given key is entered or the given 49 * interval times out. 50 * 51 * Timeout interval is by default TIMEOUT, it can be changed with 52 * an argument of the form -time where time is in minutes 53 */ 54 55 #include <sys/param.h> 56 #include <sys/stat.h> 57 #include <sys/signal.h> 58 #include <sys/consio.h> 59 60 #include <err.h> 61 #include <ctype.h> 62 #include <errno.h> 63 #include <paths.h> 64 #include <pwd.h> 65 #include <stdint.h> 66 #include <stdio.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include <syslog.h> 70 #include <termios.h> 71 #include <time.h> 72 #include <unistd.h> 73 74 #include <security/pam_appl.h> 75 #include <security/openpam.h> /* for openpam_ttyconv() */ 76 77 #define TIMEOUT 15 78 79 static void quit(int); 80 static void bye(int); 81 static void hi(int); 82 static void usage(void) __dead2; 83 84 static struct timeval timeout; 85 static struct timeval zerotime; 86 static struct termios tty, ntty; 87 static long nexttime; /* keep the timeout time */ 88 static int no_timeout; /* lock terminal forever */ 89 static int vtyunlock; /* Unlock flag and code. */ 90 91 /*ARGSUSED*/ 92 int 93 main(int argc, char **argv) 94 { 95 static const struct pam_conv pamc = { &openpam_ttyconv, NULL }; 96 pam_handle_t *pamh; 97 struct passwd *pw; 98 struct itimerval ntimer, otimer; 99 struct tm *timp; 100 time_t timval; 101 int ch, failures, pam_err, sectimeout, usemine, vtylock; 102 char *ap, *ttynam, *tzn; 103 char hostname[MAXHOSTNAMELEN], s[BUFSIZ], s1[BUFSIZ]; 104 105 openlog("lock", 0, LOG_AUTH); 106 107 pam_err = PAM_SYSTEM_ERR; /* pacify GCC */ 108 109 sectimeout = TIMEOUT; 110 pamh = NULL; 111 pw = NULL; 112 usemine = 0; 113 no_timeout = 0; 114 vtylock = 0; 115 while ((ch = getopt(argc, argv, "npt:v")) != -1) 116 switch((char)ch) { 117 case 't': 118 if ((sectimeout = atoi(optarg)) <= 0) 119 errx(1, "illegal timeout value"); 120 break; 121 case 'p': 122 usemine = 1; 123 if (!(pw = getpwuid(getuid()))) 124 errx(1, "unknown uid %d", getuid()); 125 break; 126 case 'n': 127 no_timeout = 1; 128 break; 129 case 'v': 130 vtylock = 1; 131 break; 132 case '?': 133 default: 134 usage(); 135 } 136 timeout.tv_sec = sectimeout * 60; 137 138 if (!usemine) { /* -p with PAM or S/key needs privs */ 139 /* discard privs */ 140 if (setuid(getuid()) != 0) 141 errx(1, "setuid failed"); 142 } 143 144 if (tcgetattr(0, &tty)) /* get information for header */ 145 exit(1); 146 gethostname(hostname, sizeof(hostname)); 147 if (!(ttynam = ttyname(0))) 148 errx(1, "not a terminal?"); 149 if (strncmp(ttynam, _PATH_DEV, strlen(_PATH_DEV)) == 0) 150 ttynam += strlen(_PATH_DEV); 151 timval = time(NULL); 152 nexttime = timval + (sectimeout * 60); 153 timp = localtime(&timval); 154 ap = asctime(timp); 155 tzn = timp->tm_zone; 156 157 (void)signal(SIGINT, quit); 158 (void)signal(SIGQUIT, quit); 159 ntty = tty; ntty.c_lflag &= ~ECHO; 160 (void)tcsetattr(0, TCSADRAIN|TCSASOFT, &ntty); 161 162 if (usemine) { 163 pam_err = pam_start("lock", pw->pw_name, &pamc, &pamh); 164 if (pam_err != PAM_SUCCESS) 165 err(1, "pam_start: %s", pam_strerror(NULL, pam_err)); 166 } else { 167 /* get key and check again */ 168 (void)printf("Key: "); 169 if (!fgets(s, sizeof(s), stdin) || *s == '\n') 170 quit(0); 171 (void)printf("\nAgain: "); 172 /* 173 * Don't need EOF test here, if we get EOF, then s1 != s 174 * and the right things will happen. 175 */ 176 (void)fgets(s1, sizeof(s1), stdin); 177 (void)putchar('\n'); 178 if (strcmp(s1, s)) { 179 (void)printf("\07lock: passwords didn't match.\n"); 180 (void)tcsetattr(0, TCSADRAIN|TCSASOFT, &tty); 181 exit(1); 182 } 183 s[0] = '\0'; 184 } 185 186 /* set signal handlers */ 187 (void)signal(SIGINT, hi); 188 (void)signal(SIGQUIT, hi); 189 (void)signal(SIGTSTP, hi); 190 (void)signal(SIGALRM, bye); 191 192 ntimer.it_interval = zerotime; 193 ntimer.it_value = timeout; 194 if (!no_timeout) 195 setitimer(ITIMER_REAL, &ntimer, &otimer); 196 if (vtylock) { 197 /* 198 * If this failed, we want to err out; warn isn't good 199 * enough, since we don't want the user to think that 200 * everything is nice and locked because they got a 201 * "Key:" prompt. 202 */ 203 if (ioctl(0, VT_LOCKSWITCH, &vtylock) == -1) { 204 (void)tcsetattr(0, TCSADRAIN|TCSASOFT, &tty); 205 err(1, "locking vty"); 206 } 207 vtyunlock = 0x2; 208 } 209 210 /* header info */ 211 if (pw != NULL) 212 (void)printf("lock: %s using %s on %s.", pw->pw_name, 213 ttynam, hostname); 214 else 215 (void)printf("lock: %s on %s.", ttynam, hostname); 216 if (no_timeout) 217 (void)printf(" no timeout."); 218 else 219 (void)printf(" timeout in %d minute%s.", sectimeout, 220 sectimeout != 1 ? "s" : ""); 221 if (vtylock) 222 (void)printf(" vty locked."); 223 (void)printf("\ntime now is %.20s%s%s", ap, tzn, ap + 19); 224 225 failures = 0; 226 227 for (;;) { 228 if (usemine) { 229 pam_err = pam_authenticate(pamh, 0); 230 if (pam_err == PAM_SUCCESS) 231 break; 232 233 if (pam_err != PAM_AUTH_ERR && 234 pam_err != PAM_USER_UNKNOWN && 235 pam_err != PAM_MAXTRIES) { 236 syslog(LOG_ERR, "pam_authenticate: %s", 237 pam_strerror(pamh, pam_err)); 238 } 239 240 goto tryagain; 241 } 242 (void)printf("Key: "); 243 if (!fgets(s, sizeof(s), stdin)) { 244 clearerr(stdin); 245 hi(0); 246 goto tryagain; 247 } 248 if (!strcmp(s, s1)) 249 break; 250 (void)printf("\07\n"); 251 failures++; 252 if (getuid() == 0) 253 syslog(LOG_NOTICE, "%d ROOT UNLOCK FAILURE%s (%s on %s)", 254 failures, failures > 1 ? "S": "", ttynam, hostname); 255 tryagain: 256 if (tcgetattr(0, &ntty) && (errno != EINTR)) 257 exit(1); 258 sleep(1); /* to discourage guessing */ 259 } 260 if (getuid() == 0) 261 syslog(LOG_NOTICE, "ROOT UNLOCK ON hostname %s port %s", 262 hostname, ttynam); 263 if (usemine) 264 (void)pam_end(pamh, pam_err); 265 quit(0); 266 return(0); /* not reached */ 267 } 268 269 270 static void 271 usage(void) 272 { 273 (void)fprintf(stderr, "usage: lock [-npv] [-t timeout]\n"); 274 exit(1); 275 } 276 277 static void 278 hi(int signo __unused) 279 { 280 time_t timval; 281 282 timval = time(NULL); 283 (void)printf("lock: type in the unlock key. "); 284 if (no_timeout) { 285 (void)putchar('\n'); 286 } else { 287 (void)printf("timeout in %jd:%jd minutes\n", 288 (intmax_t)(nexttime - timval) / 60, 289 (intmax_t)(nexttime - timval) % 60); 290 } 291 } 292 293 static void 294 quit(int signo __unused) 295 { 296 (void)putchar('\n'); 297 (void)tcsetattr(0, TCSADRAIN|TCSASOFT, &tty); 298 if (vtyunlock) 299 (void)ioctl(0, VT_LOCKSWITCH, &vtyunlock); 300 exit(0); 301 } 302 303 static void 304 bye(int signo __unused) 305 { 306 if (!no_timeout) { 307 (void)tcsetattr(0, TCSADRAIN|TCSASOFT, &tty); 308 if (vtyunlock) 309 (void)ioctl(0, VT_LOCKSWITCH, &vtyunlock); 310 (void)printf("lock: timeout\n"); 311 exit(1); 312 } 313 } 314