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