/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ttymon.h" #include "tmextern.h" #include "tmstruct.h" static char devbuf[BUFSIZ]; static char *devname; static int parse_args(); static void ttymon_options(); static void getty_options(); static void usage(); static char *find_ttyname(); extern void tmchild(); extern int vml(); void revokedevaccess(char *, uid_t, gid_t, mode_t); /* cannot include libdevinfo.h */ extern int di_devperm_logout(const char *); /* * ttymon_express - This is call when ttymon is invoked with args * or invoked as getty * - This special version of ttymon will monitor * one port only * - It is intended to be used when some process * wants to have a login session on the fly */ void ttymon_express(int argc, char **argv) { struct pmtab *pmtab; struct sigaction sigact; extern int Retry; extern void open_device(); extern void read_ttydefs(); extern int checkut_line(); #ifdef DEBUG extern FILE *Debugfp; extern void opendebug(); #endif #ifdef DEBUG opendebug(TRUE); #endif sigact.sa_flags = 0; sigact.sa_handler = SIG_IGN; (void) sigemptyset(&sigact.sa_mask); (void) sigaction(SIGINT, &sigact, NULL); if ((pmtab = ALLOC_PMTAB) == PNULL) { log("ttymon_express: ALLOC_PMTAB failed"); exit(1); } if (parse_args(argc, argv, pmtab) != 0) { log("ttymon_express: parse_args failed"); exit(1); } read_ttydefs(NULL, FALSE); if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0') && strcmp(pmtab->p_device, "/dev/console") == 0) { while (checkut_line(pmtab->p_device)) sleep(15); } if ((pmtab->p_device == NULL) || (*(pmtab->p_device) == '\0')) { devname = find_ttyname(0); if ((devname == NULL) || (*devname == '\0')) { log("ttyname cannot find the device on fd 0"); exit(1); } pmtab->p_device = devname; #ifdef DEBUG debug("ttymon_express: devname = %s", devname); #endif /* * become session leader * fd 0 is closed and reopened just to make sure * controlling tty is set up right */ (void) setsid(); (void) close(0); revokedevaccess(pmtab->p_device, 0, 0, 0); if (open(pmtab->p_device, O_RDWR) < 0) { log("open %s failed: %s", pmtab->p_device, strerror(errno)); exit(1); } if ((pmtab->p_modules != NULL) && (*(pmtab->p_modules) != '\0')) { if (push_linedisc(0, pmtab->p_modules, pmtab->p_device) == -1) exit(1); } if (initial_termio(0, pmtab) == -1) exit(1); di_devperm_logout((const char *)pmtab->p_device); } else { (void) setsid(); (void) close(0); Retry = FALSE; open_device(pmtab); if (Retry) /* open failed */ exit(1); } tmchild(pmtab); exit(1); /*NOTREACHED*/ } /* * parse_arg - parse cmd line arguments */ static int parse_args(int argc, char **argv, struct pmtab *pmtab) { static char p_server[] = "/usr/bin/login"; extern char *lastname(); extern void getty_account(); /* initialize fields to some default first */ pmtab->p_tag = ""; pmtab->p_flags = 0; pmtab->p_identity = "root"; pmtab->p_res1 = "reserved"; pmtab->p_res2 = "reserved"; pmtab->p_res3 = "reserved"; pmtab->p_uid = 0; pmtab->p_gid = 0; pmtab->p_dir = "/"; pmtab->p_ttyflags = 0; pmtab->p_count = 0; pmtab->p_server = p_server; pmtab->p_timeout = 0; pmtab->p_modules = ""; pmtab->p_prompt = "login: "; pmtab->p_dmsg = ""; pmtab->p_termtype = ""; pmtab->p_device = ""; pmtab->p_status = GETTY; if (strcmp(lastname(argv[0]), "getty") == 0) { pmtab->p_ttylabel = "300"; getty_options(argc, argv, pmtab); } else { pmtab->p_ttylabel = "9600"; ttymon_options(argc, argv, pmtab); } if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0')) getty_account(pmtab->p_device); /* utmp accounting */ return (0); } /* * ttymon_options - scan and check args for ttymon express */ static void ttymon_options(int argc, char **argv, struct pmtab *pmtab) { int c; /* option letter */ char *timeout; int gflag = 0; /* -g seen */ int size = 0; char tbuf[BUFSIZ]; extern char *optarg; extern int optind; extern void copystr(); extern char *strsave(); extern char *getword(); while ((c = getopt(argc, argv, "T:gd:ht:p:m:l:")) != -1) { switch (c) { case 'g': gflag = 1; break; case 'd': pmtab->p_device = optarg; break; case 'h': pmtab->p_ttyflags &= ~H_FLAG; break; case 'T': pmtab->p_termtype = optarg; break; /* * case 'b': * pmtab->p_ttyflags |= B_FLAG; * pmtab->p_ttyflags |= R_FLAG; * break; */ case 't': timeout = optarg; while (*optarg) { if (!isdigit(*optarg++)) { log("Invalid argument for " "\"-t\" -- number expected."); usage(); } } pmtab->p_timeout = atoi(timeout); break; case 'p': copystr(tbuf, optarg); pmtab->p_prompt = strsave(getword(tbuf, &size, TRUE)); break; case 'm': pmtab->p_modules = optarg; if (vml(pmtab->p_modules) != 0) usage(); break; case 'l': pmtab->p_ttylabel = optarg; break; case '?': usage(); break; /*NOTREACHED*/ } } if (optind < argc) usage(); if (!gflag) usage(); } /* * usage - print out a usage message */ static void usage() { char *umsg = "Usage: ttymon\n ttymon -g [-h] [-d device] " "[-l ttylabel] [-t timeout] [-p prompt] [-m modules]\n"; if (isatty(STDERR_FILENO)) (void) fprintf(stderr, "%s", umsg); else cons_printf(umsg); exit(1); } /* * getty_options - this is cut from getty.c * - it scan getty cmd args * - modification is made to stuff args in pmtab */ static void getty_options(argc, argv, pmtab) int argc; char **argv; struct pmtab *pmtab; { char *ptr; /* * the pre-4.0 getty's hang_up_line() is a no-op. * For compatibility, H_FLAG cannot be set for this "getty". */ pmtab->p_ttyflags &= ~(H_FLAG); while (--argc && **++argv == '-') { for (ptr = *argv + 1; *ptr; ptr++) switch (*ptr) { case 'h': break; case 't': if (isdigit(*++ptr)) { (void) sscanf(ptr, "%d", &(pmtab->p_timeout)); while (isdigit(*++ptr)); ptr--; } else if (--argc) { if (isdigit(*(ptr = *++argv))) (void) sscanf(ptr, "%d", &(pmtab->p_timeout)); else { log("getty: timeout argument <%s> " "invalid", *argv); exit(1); } } break; case 'c': log("Use \"sttydefs -l\" to check /etc/ttydefs."); exit(0); default: break; } } if (argc < 1) { log("getty: no terminal line specified."); exit(1); } else { (void) strcat(devbuf, "/dev/"); (void) strcat(devbuf, *argv); pmtab->p_device = devbuf; } if (--argc > 0) { pmtab->p_ttylabel = *++argv; } /* * every thing after this will be ignored * i.e. termtype and linedisc are ignored */ } /* * find_ttyname(fd) - find the name of device associated with fd. * - it first tries utmpx to see if an entry exists * - with my pid and ut_line is defined. If ut_line * - is defined, it will see if the major and minor * - number of fd and devname from utmpx match. * - If utmpx search fails, ttyname(fd) will be called. */ static char * find_ttyname(fd) int fd; { pid_t ownpid; struct utmpx *u; static struct stat statf, statu; static char buf[BUFSIZ]; ownpid = getpid(); setutxent(); while ((u = getutxent()) != NULL) { if (u->ut_pid == ownpid) { if (strlen(u->ut_line) != 0) { if (*(u->ut_line) != '/') { (void) strcpy(buf, "/dev/"); (void) strncat(buf, u->ut_line, sizeof (u->ut_line)); } else { (void) strncat(buf, u->ut_line, sizeof (u->ut_line)); } } else u = NULL; break; } } endutxent(); if ((u != NULL) && (fstat(fd, &statf) == 0) && (stat(buf, &statu) == 0) && (statf.st_dev == statu.st_dev) && (statf.st_rdev == statu.st_rdev)) { #ifdef DEBUG debug("ttymon_express: find device name from utmpx."); #endif return (buf); } else { #ifdef DEBUG debug("ttymon_express: calling ttyname to find device name."); #endif return (ttyname(fd)); } } /* * Revoke all access to a device node and make sure that there are * no interposed streams devices attached. Must be called before a * device is actually opened. * When fdetach is called, the underlying device node is revealed; it * will have the previous owner and that owner can re-attach; so we * retry until we win. * Ignore non-existent devices. */ void revokedevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode) { do { if (chown(dev, uid, gid) == -1) return; } while (fdetach(dev) == 0); /* Remove ACLs */ if (acl(dev, GETACLCNT, 0, NULL) > MIN_ACL_ENTRIES) { aclent_t acls[3]; acls[0].a_type = USER_OBJ; acls[0].a_id = uid; acls[0].a_perm = 0; acls[1].a_type = GROUP_OBJ; acls[1].a_id = gid; acls[1].a_perm = 0; acls[2].a_type = OTHER_OBJ; acls[2].a_id = 0; acls[2].a_perm = 0; (void) acl(dev, SETACL, 3, acls); } (void) chmod(dev, mode); }