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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 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 "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 strcmp(pmtab->p_device, "/dev/console") == 0) { 111 while (checkut_line(pmtab->p_device)) 112 sleep(15); 113 } 114 115 if ((pmtab->p_device == NULL) || (*(pmtab->p_device) == '\0')) { 116 devname = find_ttyname(0); 117 if ((devname == NULL) || (*devname == '\0')) { 118 log("ttyname cannot find the device on fd 0"); 119 exit(1); 120 } 121 pmtab->p_device = devname; 122 #ifdef DEBUG 123 debug("ttymon_express: devname = %s", devname); 124 #endif 125 /* 126 * become session leader 127 * fd 0 is closed and reopened just to make sure 128 * controlling tty is set up right 129 */ 130 (void) setsid(); 131 (void) close(0); 132 revokedevaccess(pmtab->p_device, 0, 0, 0); 133 if (open(pmtab->p_device, O_RDWR) < 0) { 134 log("open %s failed: %s", pmtab->p_device, 135 strerror(errno)); 136 exit(1); 137 } 138 if ((pmtab->p_modules != NULL) && 139 (*(pmtab->p_modules) != '\0')) { 140 if (push_linedisc(0, pmtab->p_modules, 141 pmtab->p_device) == -1) 142 exit(1); 143 } 144 if (initial_termio(0, pmtab) == -1) 145 exit(1); 146 di_devperm_logout((const char *)pmtab->p_device); 147 } else { 148 (void) setsid(); 149 (void) close(0); 150 Retry = FALSE; 151 open_device(pmtab); 152 if (Retry) /* open failed */ 153 exit(1); 154 } 155 tmchild(pmtab); 156 exit(1); /*NOTREACHED*/ 157 } 158 159 /* 160 * parse_arg - parse cmd line arguments 161 */ 162 static int 163 parse_args(int argc, char **argv, struct pmtab *pmtab) 164 { 165 static char p_server[] = "/usr/bin/login"; 166 extern char *lastname(); 167 extern void getty_account(); 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 pmtab->p_ttylabel = "9600"; 194 ttymon_options(argc, argv, pmtab); 195 } 196 if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0')) 197 getty_account(pmtab->p_device); /* utmp accounting */ 198 return (0); 199 } 200 201 202 /* 203 * ttymon_options - scan and check args for ttymon express 204 */ 205 206 static void 207 ttymon_options(int argc, char **argv, struct pmtab *pmtab) 208 { 209 int c; /* option letter */ 210 char *timeout; 211 int gflag = 0; /* -g seen */ 212 int size = 0; 213 char tbuf[BUFSIZ]; 214 215 extern char *optarg; 216 extern int optind; 217 extern void copystr(); 218 extern char *strsave(); 219 extern char *getword(); 220 221 while ((c = getopt(argc, argv, "T:gd:ht:p:m:l:")) != -1) { 222 switch (c) { 223 case 'g': 224 gflag = 1; 225 break; 226 case 'd': 227 pmtab->p_device = optarg; 228 break; 229 case 'h': 230 pmtab->p_ttyflags &= ~H_FLAG; 231 break; 232 233 case 'T': 234 pmtab->p_termtype = optarg; 235 break; 236 /* 237 * case 'b': 238 * pmtab->p_ttyflags |= B_FLAG; 239 * pmtab->p_ttyflags |= R_FLAG; 240 * break; 241 */ 242 case 't': 243 timeout = optarg; 244 while (*optarg) { 245 if (!isdigit(*optarg++)) { 246 log("Invalid argument for " 247 "\"-t\" -- number expected."); 248 usage(); 249 } 250 } 251 pmtab->p_timeout = atoi(timeout); 252 break; 253 case 'p': 254 copystr(tbuf, optarg); 255 pmtab->p_prompt = strsave(getword(tbuf, &size, TRUE)); 256 break; 257 case 'm': 258 pmtab->p_modules = optarg; 259 if (vml(pmtab->p_modules) != 0) 260 usage(); 261 break; 262 case 'l': 263 pmtab->p_ttylabel = optarg; 264 break; 265 case '?': 266 usage(); 267 break; /*NOTREACHED*/ 268 } 269 } 270 if (optind < argc) 271 usage(); 272 273 if (!gflag) 274 usage(); 275 } 276 277 /* 278 * usage - print out a usage message 279 */ 280 281 static void 282 usage() 283 { 284 char *umsg = "Usage: ttymon\n ttymon -g [-h] [-d device] " 285 "[-l ttylabel] [-t timeout] [-p prompt] [-m modules]\n"; 286 287 if (isatty(STDERR_FILENO)) 288 (void) fprintf(stderr, "%s", umsg); 289 else 290 cons_printf(umsg); 291 exit(1); 292 } 293 294 /* 295 * getty_options - this is cut from getty.c 296 * - it scan getty cmd args 297 * - modification is made to stuff args in pmtab 298 */ 299 static void 300 getty_options(argc, argv, pmtab) 301 int argc; 302 char **argv; 303 struct pmtab *pmtab; 304 { 305 char *ptr; 306 307 /* 308 * the pre-4.0 getty's hang_up_line() is a no-op. 309 * For compatibility, H_FLAG cannot be set for this "getty". 310 */ 311 pmtab->p_ttyflags &= ~(H_FLAG); 312 313 while (--argc && **++argv == '-') { 314 for (ptr = *argv + 1; *ptr; ptr++) 315 switch (*ptr) { 316 case 'h': 317 break; 318 case 't': 319 if (isdigit(*++ptr)) { 320 (void) sscanf(ptr, "%d", &(pmtab->p_timeout)); 321 while (isdigit(*++ptr)); 322 ptr--; 323 } else if (--argc) { 324 if (isdigit(*(ptr = *++argv))) 325 (void) sscanf(ptr, "%d", 326 &(pmtab->p_timeout)); 327 else { 328 log("getty: timeout argument <%s> " 329 "invalid", *argv); 330 exit(1); 331 } 332 } 333 break; 334 335 case 'c': 336 log("Use \"sttydefs -l\" to check /etc/ttydefs."); 337 exit(0); 338 default: 339 break; 340 } 341 } 342 343 if (argc < 1) { 344 log("getty: no terminal line specified."); 345 exit(1); 346 } else { 347 (void) strcat(devbuf, "/dev/"); 348 (void) strcat(devbuf, *argv); 349 pmtab->p_device = devbuf; 350 } 351 352 if (--argc > 0) { 353 pmtab->p_ttylabel = *++argv; 354 } 355 356 /* 357 * every thing after this will be ignored 358 * i.e. termtype and linedisc are ignored 359 */ 360 } 361 362 /* 363 * find_ttyname(fd) - find the name of device associated with fd. 364 * - it first tries utmpx to see if an entry exists 365 * - with my pid and ut_line is defined. If ut_line 366 * - is defined, it will see if the major and minor 367 * - number of fd and devname from utmpx match. 368 * - If utmpx search fails, ttyname(fd) will be called. 369 */ 370 static char * 371 find_ttyname(fd) 372 int fd; 373 { 374 pid_t ownpid; 375 struct utmpx *u; 376 static struct stat statf, statu; 377 static char buf[BUFSIZ]; 378 379 ownpid = getpid(); 380 setutxent(); 381 while ((u = getutxent()) != NULL) { 382 if (u->ut_pid == ownpid) { 383 if (strlen(u->ut_line) != 0) { 384 if (*(u->ut_line) != '/') { 385 (void) strcpy(buf, "/dev/"); 386 (void) strncat(buf, u->ut_line, 387 sizeof (u->ut_line)); 388 } else { 389 (void) strncat(buf, u->ut_line, 390 sizeof (u->ut_line)); 391 } 392 } 393 else 394 u = NULL; 395 break; 396 } 397 } 398 endutxent(); 399 if ((u != NULL) && 400 (fstat(fd, &statf) == 0) && 401 (stat(buf, &statu) == 0) && 402 (statf.st_dev == statu.st_dev) && 403 (statf.st_rdev == statu.st_rdev)) { 404 #ifdef DEBUG 405 debug("ttymon_express: find device name from utmpx."); 406 #endif 407 return (buf); 408 } else { 409 #ifdef DEBUG 410 debug("ttymon_express: calling ttyname to find device name."); 411 #endif 412 return (ttyname(fd)); 413 } 414 } 415 416 /* 417 * Revoke all access to a device node and make sure that there are 418 * no interposed streams devices attached. Must be called before a 419 * device is actually opened. 420 * When fdetach is called, the underlying device node is revealed; it 421 * will have the previous owner and that owner can re-attach; so we 422 * retry until we win. 423 * Ignore non-existent devices. 424 */ 425 void 426 revokedevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode) 427 { 428 do { 429 if (chown(dev, uid, gid) == -1) 430 return; 431 } while (fdetach(dev) == 0); 432 433 /* Remove ACLs */ 434 if (acl(dev, GETACLCNT, 0, NULL) > MIN_ACL_ENTRIES) { 435 aclent_t acls[3]; 436 437 acls[0].a_type = USER_OBJ; 438 acls[0].a_id = uid; 439 acls[0].a_perm = 0; 440 441 acls[1].a_type = GROUP_OBJ; 442 acls[1].a_id = gid; 443 acls[1].a_perm = 0; 444 445 acls[2].a_type = OTHER_OBJ; 446 acls[2].a_id = 0; 447 acls[2].a_perm = 0; 448 449 (void) acl(dev, SETACL, 3, acls); 450 } 451 452 (void) chmod(dev, mode); 453 } 454