/* * Copyright (c) 1998 David Parsons. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgement: * * This product includes software developed by David Parsons * (orc@pell.chi.il.us) * * 4. My name may not be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY DAVID PARSONS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID * PARSONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ static const char rcsid[] = "$Id$"; /* * plugh: somewhat like a getty, except that it simply plops the user into a shell. * * usage: getty [-cdmq] [-i issue] [-l login] user tty [tag [term-type]] * * -d don't clear the screen before the login prompt. * -m this is a modem-- don't reset the line before prompting. * -q don't display the issue file. * -i name an issue file. * -l use the following program as the login program. * user the user we create a shell for. * tty the tty we're talking to, in /dev * tag which /etc/gettydefs entry to use. * term-type what sort of terminal is this (for clearing the screen) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gettydefs.h" void error(char *fmt, ...); void login(struct passwd *, char *); static struct utsname sysinfo; /* system information for login: prompt */ int tty; /* tty we're talking to */ char *program; /* our program name, for syslog() */ int local = 1; /* if we're on a modem, don't hangup before prompt*/ char *issue = "/etc/issue"; /* issue file before login */ char *user; /* who we are */ int dumb = 0; /* dumb terminal - don't bother to clear */ char *clear = (char*)0; /* clear screen */ char *logintty = (char*)0; /* the tty we're on, for @L escape */ #define CTL(x) (x - '@') #define DEL 0x7f /* if no gettydefs tag is supplied, use this one. */ GDE default_tio = { "default", /* tag */ { BRKINT|IGNPAR|ISTRIP|ICRNL|IXON, /* before */ OPOST|NL0|CR0|TAB0|BS0|VT0|FF0|ONLCR, B9600|CS8|CREAD|CLOCAL, ISIG|ICANON|ECHO|ECHOK, 0, { CTL('C'), CTL('\\'), DEL, CTL('U'), CTL('D'), 0, 0, CTL('Q'), CTL('S'), CTL('Z'), CTL('M'), CTL('R'), CTL('O'), CTL('W'), CTL('V') } }, { BRKINT|IGNPAR|ICRNL|IXON, /* after */ OPOST|NL0|CR0|TAB0|BS0|VT0|FF0|ONLCR, B9600|CS8|CREAD|CLOCAL, ISIG|ICANON|ECHO|ECHOK, 0, { CTL('C'), CTL('\\'), DEL, CTL('U'), CTL('D'), 0, 0, CTL('Q'), CTL('S'), CTL('Z'), CTL('M'), CTL('R'), CTL('O'), CTL('W'), CTL('V') } }, "@S login: ", /* prompt */ "default", /* nexttag */ }; extern int getopt(); extern int optind, opterr; extern char *optarg; extern char **environ; /* * getty, in the flesh */ main(int argc, char **argv) { struct passwd *pwd; char terminal[80]; /* full pathname to the tty device */ char name[20]; /* the user's login name. */ char term_type[80]; /* the terminal type, for putenv() */ GDE *tio; /* the gettydefs entry we're using now */ int opt; /* option, for getopt() and other functions */ char *shell = 0; static char termcap_bfr[2048]; /* a scratchspace for termcaps */ char *arg0; char *p; if (program=strchr(argv[0], '/')) ++program; else program = argv[0]; /* first we pick apart the arguments */ opterr=0; while ((opt=getopt(argc, argv, "cdmqi:l:")) != EOF) { switch (opt) { case 'm': local = 0; break; case 'l': shell = optarg; break; case 'q': issue = (char*)0; break; case 'i': issue = optarg; break; case 'd': dumb = 1; break; } } argc -= (optind-1); argv += (optind-1); /* zap the environment before proceeding */ environ = malloc(1); environ[0] = 0; /* load the gettydefs file, (optionally checking if -c was specified) */ loadgettydefs("/etc/gettydefs"); if (argc < 3) error("usage: plugh [options] user tty [tag [term-type]]"); user = argv[1]; if ((pwd = getpwnam(user)) == 0) error("unknown user <%s>", user); argv++; argc--; if (argc >= 3) { tio = getgettydef(argv[2]); if (tio == (GDE*)0) error("unknown tag <%s>", argv[2]); } else tio = &default_tio; /* If we're setting the terminal type, poke around in /etc/termcap * to find out how to clear the screen */ if (argc > 3) { setenv("TERM", argv[3], 1); if (!dumb) { int ret; char *p = termcap_bfr; if (tgetent(termcap_bfr, argv[3]) != 1) dumb = 1; else clear = tgetstr("cl", &p); } } if (strchr(argv[1], '/') != 0) error("badly formed tty name <%s>", argv[1]); close(0); /* close the standard file descriptors, then open */ close(1); /* the terminal and fill them in */ close(2); sprintf(terminal, "/dev/%s", logintty = argv[1]); if ((tty = open(terminal, O_RDWR)) < 0) error("cannot open tty <%s>", argv[1]); dup(tty); dup(tty); opt = getpid(); /* get a new process group */ ioctl(0, TIOCSPGRP, &opt); if (uname(&sysinfo) != 0) error("uname failed: %s", strerror(errno)); /* set the terminal settings so that the user can actually do * something with their shell */ tcsetattr(tty, TCSADRAIN, &(tio->after)); /* clear the screen on terminals that support it */ if (clear && !dumb) write(tty, clear, strlen(clear)); /* spit out the issue message, if any */ if (issue) cat(issue); /* set up arg0 so that it's got the - to tell the shell that * yes, indeed, we are a login shell */ if (shell == 0) shell = (pwd->pw_shell == 0) ? _PATH_BSHELL : pwd->pw_shell; if ((p = strrchr(shell, '/')) != 0) ++p; else p = shell; arg0 = malloc(sizeof(p)+2); sprintf(arg0, "-%s", p); /* add utmp/wtmp entries */ login(pwd, logintty); /* change our group and id to the user */ initgroups(user,pwd->pw_gid); setgid(pwd->pw_gid); setuid(pwd->pw_uid); /* set appropriate things in our environment */ setenv("LOGNAME", user, 1); setenv("PATH", "/bin:/usr/bin:/usr/local/bin", 1); setenv("HOME", pwd->pw_dir, 1); setenv("SHELL", shell, 1); /* go $HOME */ chdir(pwd->pw_dir); execl(shell, arg0, 0); error("cannot exec shell <%s>", shell); exit(0); } /* main */ /* * cat() dumps a file to the terminal */ cat(char *file) { FILE *f; char c; if (f = fopen(file, "r")) { while ((c=fgetc(f)) != EOF) putchar(c); fclose(f); } } /* cat */ /* * write an error message to the syslog, twiddle our thumbs for a bit, * then quit */ void error(char *fmt, ...) { va_list ptr; openlog(program, LOG_PID, LOG_AUTH); va_start(ptr, fmt); vsyslog(LOG_ERR, fmt, ptr); va_end(ptr); closelog(); sleep(10); exit(1); } /* error */ /* * write entries to utmp and wtmp */ void login(struct passwd *pwd, char *tty) { struct utmp ut; char *ttyabbrev; int wtmp; memset((char *) &ut, 0, sizeof(ut)); ut.ut_type = USER_PROCESS; ut.ut_pid = getpid(); strncpy(ut.ut_line, tty, sizeof(ut.ut_line)); strncpy(ut.ut_id, tty+3, sizeof(ut.ut_id)); time(&ut.ut_time); strncpy(ut.ut_user, pwd->pw_name, sizeof(ut.ut_user)); utmpname(_PATH_UTMP); setutent(); pututline(&ut); endutent(); if ((wtmp = open(_PATH_WTMP, O_APPEND | O_WRONLY)) >= 0) { flock(wtmp, LOCK_EX); write(wtmp, (char *) &ut, sizeof(ut)); flock(wtmp, LOCK_UN); close(wtmp); } } /* login */