1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <fcntl.h> 36 #include <errno.h> 37 #include <ctype.h> 38 #include <string.h> 39 #include <signal.h> 40 #include <sys/stat.h> 41 #include <utmpx.h> 42 #include <pwd.h> 43 #include <dirent.h> 44 #include <sys/param.h> 45 #include <sys/acl.h> 46 #include <sys/stat.h> 47 #include <sys/types.h> 48 #include <sys/console.h> 49 #include "ttymon.h" 50 #include "tmextern.h" 51 #include "tmstruct.h" 52 53 static char devbuf[BUFSIZ]; 54 static char *devname; 55 56 static int parse_args(); 57 static void ttymon_options(); 58 static void getty_options(); 59 static void usage(); 60 static char *find_ttyname(); 61 62 extern void tmchild(); 63 extern int vml(); 64 65 void revokedevaccess(char *, uid_t, gid_t, mode_t); 66 /* cannot include libdevinfo.h */ 67 extern int di_devperm_logout(const char *); 68 69 /* 70 * ttymon_express - This is call when ttymon is invoked with args 71 * or invoked as getty 72 * - This special version of ttymon will monitor 73 * one port only 74 * - It is intended to be used when some process 75 * wants to have a login session on the fly 76 */ 77 void 78 ttymon_express(int argc, char **argv) 79 { 80 struct pmtab *pmtab; 81 struct sigaction sigact; 82 extern int Retry; 83 extern void open_device(); 84 extern void read_ttydefs(); 85 extern int checkut_line(); 86 #ifdef DEBUG 87 extern FILE *Debugfp; 88 extern void opendebug(); 89 #endif 90 91 #ifdef DEBUG 92 opendebug(TRUE); 93 #endif 94 95 sigact.sa_flags = 0; 96 sigact.sa_handler = SIG_IGN; 97 (void) sigemptyset(&sigact.sa_mask); 98 (void) sigaction(SIGINT, &sigact, NULL); 99 100 if ((pmtab = ALLOC_PMTAB) == PNULL) { 101 log("ttymon_express: ALLOC_PMTAB failed"); 102 exit(1); 103 } 104 105 if (parse_args(argc, argv, pmtab) != 0) { 106 log("ttymon_express: parse_args failed"); 107 exit(1); 108 } 109 110 read_ttydefs(NULL, FALSE); 111 112 if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0') && 113 strcmp(pmtab->p_device, "/dev/console") == 0) { 114 while (checkut_line(pmtab->p_device)) 115 sleep(15); 116 } 117 118 if ((pmtab->p_device == NULL) || (*(pmtab->p_device) == '\0')) { 119 devname = find_ttyname(0); 120 if ((devname == NULL) || (*devname == '\0')) { 121 log("ttyname cannot find the device on fd 0"); 122 exit(1); 123 } 124 pmtab->p_device = devname; 125 #ifdef DEBUG 126 debug("ttymon_express: devname = %s", devname); 127 #endif 128 /* 129 * become session leader 130 * fd 0 is closed and reopened just to make sure 131 * controlling tty is set up right 132 */ 133 (void) setsid(); 134 (void) close(0); 135 revokedevaccess(pmtab->p_device, 0, 0, 0); 136 if (open(pmtab->p_device, O_RDWR) < 0) { 137 log("open %s failed: %s", pmtab->p_device, 138 strerror(errno)); 139 exit(1); 140 } 141 if ((pmtab->p_modules != NULL) && 142 (*(pmtab->p_modules) != '\0')) { 143 if (push_linedisc(0, pmtab->p_modules, 144 pmtab->p_device) == -1) 145 exit(1); 146 } 147 if (initial_termio(0, pmtab) == -1) 148 exit(1); 149 di_devperm_logout((const char *)pmtab->p_device); 150 } else { 151 (void) setsid(); 152 (void) close(0); 153 Retry = FALSE; 154 open_device(pmtab); 155 if (Retry) /* open failed */ 156 exit(1); 157 } 158 tmchild(pmtab); 159 exit(1); /*NOTREACHED*/ 160 } 161 162 /* 163 * parse_arg - parse cmd line arguments 164 */ 165 static int 166 parse_args(int argc, char **argv, struct pmtab *pmtab) 167 { 168 static char p_server[] = "/usr/bin/login"; 169 extern char *lastname(); 170 extern void getty_account(); 171 static char termbuf[MAX_TERM_TYPE_LEN]; 172 static struct cons_getterm cnterm = {sizeof (termbuf), termbuf}; 173 174 /* initialize fields to some default first */ 175 pmtab->p_tag = ""; 176 pmtab->p_flags = 0; 177 pmtab->p_identity = "root"; 178 pmtab->p_res1 = "reserved"; 179 pmtab->p_res2 = "reserved"; 180 pmtab->p_res3 = "reserved"; 181 pmtab->p_uid = 0; 182 pmtab->p_gid = 0; 183 pmtab->p_dir = "/"; 184 pmtab->p_ttyflags = 0; 185 pmtab->p_count = 0; 186 pmtab->p_server = p_server; 187 pmtab->p_timeout = 0; 188 pmtab->p_modules = ""; 189 pmtab->p_prompt = "login: "; 190 pmtab->p_dmsg = ""; 191 pmtab->p_termtype = ""; 192 pmtab->p_device = ""; 193 pmtab->p_status = GETTY; 194 if (strcmp(lastname(argv[0]), "getty") == 0) { 195 pmtab->p_ttylabel = "300"; 196 getty_options(argc, argv, pmtab); 197 } else { 198 int cn_fd; 199 200 pmtab->p_ttylabel = "9600"; 201 ttymon_options(argc, argv, pmtab); 202 203 /* 204 * The following code is only reached if -g was specified. 205 * It attempts to determine a suitable terminal type for 206 * the console login process. 207 * 208 * If -d /dev/console also specified, we send an ioctl 209 * to the console device to query the TERM type. 210 * 211 * If any of the tests, system calls, or ioctls fail 212 * then pmtab->p_termtype retains its default value 213 * of "". otherwise it is set to a term type value 214 * that was returned. 215 */ 216 if ((strlen(pmtab->p_termtype) == 0) && 217 (strcmp(pmtab->p_device, "/dev/console") == 0) && 218 ((cn_fd = open("/dev/console", O_RDONLY)) != -1)) { 219 220 if (ioctl(cn_fd, CONS_GETTERM, &cnterm) != -1) 221 pmtab->p_termtype = cnterm.cn_term_type; 222 (void) close(cn_fd); 223 } 224 } 225 226 if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0')) 227 getty_account(pmtab->p_device); /* utmp accounting */ 228 return (0); 229 } 230 231 232 /* 233 * ttymon_options - scan and check args for ttymon express 234 */ 235 236 static void 237 ttymon_options(int argc, char **argv, struct pmtab *pmtab) 238 { 239 int c; /* option letter */ 240 char *timeout; 241 int gflag = 0; /* -g seen */ 242 int size = 0; 243 char tbuf[BUFSIZ]; 244 245 extern char *optarg; 246 extern int optind; 247 extern void copystr(); 248 extern char *strsave(); 249 extern char *getword(); 250 251 while ((c = getopt(argc, argv, "T:gd:ht:p:m:l:")) != -1) { 252 switch (c) { 253 case 'g': 254 gflag = 1; 255 break; 256 case 'd': 257 pmtab->p_device = optarg; 258 break; 259 case 'h': 260 pmtab->p_ttyflags &= ~H_FLAG; 261 break; 262 263 case 'T': 264 pmtab->p_termtype = optarg; 265 break; 266 /* 267 * case 'b': 268 * pmtab->p_ttyflags |= B_FLAG; 269 * pmtab->p_ttyflags |= R_FLAG; 270 * break; 271 */ 272 case 't': 273 timeout = optarg; 274 while (*optarg) { 275 if (!isdigit(*optarg++)) { 276 log("Invalid argument for " 277 "\"-t\" -- number expected."); 278 usage(); 279 } 280 } 281 pmtab->p_timeout = atoi(timeout); 282 break; 283 case 'p': 284 copystr(tbuf, optarg); 285 pmtab->p_prompt = strsave(getword(tbuf, &size, TRUE)); 286 break; 287 case 'm': 288 pmtab->p_modules = optarg; 289 if (vml(pmtab->p_modules) != 0) 290 usage(); 291 break; 292 case 'l': 293 pmtab->p_ttylabel = optarg; 294 break; 295 case '?': 296 usage(); 297 break; /*NOTREACHED*/ 298 } 299 } 300 if (optind < argc) 301 usage(); 302 303 if (!gflag) 304 usage(); 305 } 306 307 /* 308 * usage - print out a usage message 309 */ 310 311 static void 312 usage() 313 { 314 char *umsg = "Usage: ttymon\n ttymon -g [-h] [-d device] " 315 "[-l ttylabel] [-t timeout] [-p prompt] [-m modules]\n"; 316 317 if (isatty(STDERR_FILENO)) 318 (void) fprintf(stderr, "%s", umsg); 319 else 320 cons_printf(umsg); 321 exit(1); 322 } 323 324 /* 325 * getty_options - this is cut from getty.c 326 * - it scan getty cmd args 327 * - modification is made to stuff args in pmtab 328 */ 329 static void 330 getty_options(argc, argv, pmtab) 331 int argc; 332 char **argv; 333 struct pmtab *pmtab; 334 { 335 char *ptr; 336 337 /* 338 * the pre-4.0 getty's hang_up_line() is a no-op. 339 * For compatibility, H_FLAG cannot be set for this "getty". 340 */ 341 pmtab->p_ttyflags &= ~(H_FLAG); 342 343 while (--argc && **++argv == '-') { 344 for (ptr = *argv + 1; *ptr; ptr++) 345 switch (*ptr) { 346 case 'h': 347 break; 348 case 't': 349 if (isdigit(*++ptr)) { 350 (void) sscanf(ptr, "%d", &(pmtab->p_timeout)); 351 while (isdigit(*++ptr)); 352 ptr--; 353 } else if (--argc) { 354 if (isdigit(*(ptr = *++argv))) 355 (void) sscanf(ptr, "%d", 356 &(pmtab->p_timeout)); 357 else { 358 log("getty: timeout argument <%s> " 359 "invalid", *argv); 360 exit(1); 361 } 362 } 363 break; 364 365 case 'c': 366 log("Use \"sttydefs -l\" to check /etc/ttydefs."); 367 exit(0); 368 default: 369 break; 370 } 371 } 372 373 if (argc < 1) { 374 log("getty: no terminal line specified."); 375 exit(1); 376 } else { 377 (void) strcat(devbuf, "/dev/"); 378 (void) strcat(devbuf, *argv); 379 pmtab->p_device = devbuf; 380 } 381 382 if (--argc > 0) { 383 pmtab->p_ttylabel = *++argv; 384 } 385 386 /* 387 * every thing after this will be ignored 388 * i.e. termtype and linedisc are ignored 389 */ 390 } 391 392 /* 393 * find_ttyname(fd) - find the name of device associated with fd. 394 * - it first tries utmpx to see if an entry exists 395 * - with my pid and ut_line is defined. If ut_line 396 * - is defined, it will see if the major and minor 397 * - number of fd and devname from utmpx match. 398 * - If utmpx search fails, ttyname(fd) will be called. 399 */ 400 static char * 401 find_ttyname(fd) 402 int fd; 403 { 404 pid_t ownpid; 405 struct utmpx *u; 406 static struct stat statf, statu; 407 static char buf[BUFSIZ]; 408 409 ownpid = getpid(); 410 setutxent(); 411 while ((u = getutxent()) != NULL) { 412 if (u->ut_pid == ownpid) { 413 if (strlen(u->ut_line) != 0) { 414 if (*(u->ut_line) != '/') { 415 (void) strcpy(buf, "/dev/"); 416 (void) strncat(buf, u->ut_line, 417 sizeof (u->ut_line)); 418 } else { 419 (void) strncat(buf, u->ut_line, 420 sizeof (u->ut_line)); 421 } 422 } 423 else 424 u = NULL; 425 break; 426 } 427 } 428 endutxent(); 429 if ((u != NULL) && 430 (fstat(fd, &statf) == 0) && 431 (stat(buf, &statu) == 0) && 432 (statf.st_dev == statu.st_dev) && 433 (statf.st_rdev == statu.st_rdev)) { 434 #ifdef DEBUG 435 debug("ttymon_express: find device name from utmpx."); 436 #endif 437 return (buf); 438 } else { 439 #ifdef DEBUG 440 debug("ttymon_express: calling ttyname to find device name."); 441 #endif 442 return (ttyname(fd)); 443 } 444 } 445 446 /* 447 * Revoke all access to a device node and make sure that there are 448 * no interposed streams devices attached. Must be called before a 449 * device is actually opened. 450 * When fdetach is called, the underlying device node is revealed; it 451 * will have the previous owner and that owner can re-attach; so we 452 * retry until we win. 453 * Ignore non-existent devices. 454 */ 455 void 456 revokedevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode) 457 { 458 do { 459 if (chown(dev, uid, gid) == -1) 460 return; 461 } while (fdetach(dev) == 0); 462 463 /* Remove ACLs */ 464 465 (void) acl_strip(dev, uid, gid, mode); 466 } 467