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