agetty: add an autologin feature

Add an autologin feature to agetty, that is that a user can be
automatically logged in.  For this the options of for the
login program has to used.  Make it possible to pass-through
options to the login program which requires a security check.

Signed-off-by: Werner Fink <werner@suse.de>
This commit is contained in:
Werner Fink 2011-05-09 15:52:39 +02:00 committed by Karel Zak
parent 1683f6bf20
commit eb8e1f9f0c
2 changed files with 222 additions and 26 deletions

View File

@ -4,6 +4,7 @@ agetty \- alternative Linux getty
.SH SYNOPSIS
.BR "agetty " [\-8chiLmnsUw]
.RI "[-a " user ]
.RI "[-f " issue_file ]
.RI "[-H " login_host ]
.RI "[-I " init ]
@ -85,6 +86,11 @@ whatever init(8) may have set, and is inherited by login and the shell.
\-8, \-\-8bits
Assume that the tty is 8-bit clean, hence disable parity detection.
.TP
\-a, \-\-autologin \fIusername\fP
Log the specified user automatically in without asking for a login
name and password. Check the -f option from
\fB/bin/login\fP for this.
.TP
\-c, \-\-noreset
Don't reset terminal cflags (control modes). See \fItermios(3)\fP for more
details.
@ -152,6 +158,16 @@ space parity, 7 bit characters, and ASCII CR (13) end-of-line character.
Beware that the program that \fBagetty\fR starts (usually /bin/login)
is run as root.
.TP
\-o, \-\-logopts \fI"login_options"\fP
Options that are passed to the login program. \\u is replaced
by the login name. Defaults to "-- \\u", which is suitable for
\fB/bin/login\fP. Please read the SECURITY NOTICE below if
you want to use this.
.TP
\-p, \-\-loginpause
Wait for any key before dropping to the login prompt. Can be combined
with \fB\-\-autologin\fP to save memory by lazily spawning shells.
.TP
\-R, \-\-hangup
Do call vhangup() for a virtually hangup of the specified terminal.
.TP
@ -207,6 +223,19 @@ dis-connection and turn on auto-answer after 1 ring.)
.ti +5
/sbin/agetty \-w \-I 'ATE0Q1&D2&C1S0=1\\015' 115200 ttyS1
.SH SECURITY NOTICE
If you use the \fB\-\-login\fP and \fB\-\-logopts\fP options, be aware
that a malicious user may try to enter lognames with embedded options,
which then get passed to the used login program. Agetty does check
for a leading - and makes sure the logname gets passed as one parameter
(so embedded spaces will not create yet another parameter), but depending
on how the login binary parses the command line that might not be sufficient.
Check that the used login program can not be abused this way.
.PP
Some programs use -- to indicate that the rest of the commandline should
not be interpreted as options. Use this feature if available by passing -- before
the username gets passed by \\u.
.SH ISSUE ESCAPES
The issue-file (\fI/etc/issue\fP or the file set with the \-f option)
may contain certain escape codes to display the system name, date and

View File

@ -93,7 +93,8 @@
#endif
/* Login prompt. */
#define LOGIN " login: "
#define LOGIN " login: "
#define ARRAY_SIZE_MAX 16 /* Numbers of args for login beside "-- \\u" */
/* Some shorthands for control characters. */
#define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */
@ -131,7 +132,9 @@
struct options {
int flags; /* toggle switches, see below */
int timeout; /* time-out period */
char *autolog; /* login the user automatically */
char *login; /* login program */
char *logopt; /* options for login program */
char *tty; /* name of tty */
char *vcline; /* line of virtual console */
char *term; /* terminal type */
@ -156,6 +159,7 @@ struct options {
#define F_VCONSOLE (1<<12) /* This is a virtual console */
#define F_HANGUP (1<<13) /* Do call vhangup(2) */
#define F_UTF8 (1<<14) /* We can do UTF8 */
#define F_LOGINPAUSE (1<<15) /* Wait for any key before dropping login prompt */
#define serial_tty_option(opt, flag) \
(((opt)->flags & (F_VCONSOLE|(flag))) == (flag))
@ -241,6 +245,9 @@ static speed_t bcode(char *s);
static void usage(FILE * out) __attribute__((__noreturn__));
static void log_err(const char *, ...) __attribute__((__noreturn__)) __attribute__((__format__(printf, 1, 2)));
static void log_warn (const char *, ...) __attribute__((__format__(printf, 1, 2)));
static void checkname (const char* nm);
static void replacename (char** arr, const char* nm);
static void mkarray (char** arr, char* str);
/* Fake hostname for ut_host specified on command line. */
static char *fakehost;
@ -254,20 +261,18 @@ FILE *dbf;
int main(int argc, char **argv)
{
char *logname = NULL; /* login name, given to /bin/login */
struct chardata chardata; /* will be set by get_logname() */
struct termios termios; /* terminal mode bits */
char *logname = NULL; /* login name, given to /bin/login */
char logcmd[NAME_MAX+1];
char *logarr[ARRAY_SIZE_MAX + 2]; /* arguments plus "-- \\u" */
struct chardata chardata; /* will be set by get_logname() */
struct termios termios; /* terminal mode bits */
static struct options options = {
F_ISSUE, /* show /etc/issue (SYSV_STYLE) */
0, /* no timeout */
_PATH_LOGIN, /* default login program */
"tty1", /* default tty line */
0, /* line of virtual console */
DEFAULT_VCTERM, /* terminal type */
"", /* modem init string */
ISSUE, /* default issue file */
0, /* no baud rates known yet */
{ 0 }
.flags = F_ISSUE, /* show /etc/issue (SYSV_STYLE) */
.login = _PATH_LOGIN, /* default login program */
.logopt = "-- \\u", /* escape for user name */
.tty = "tty1", /* default tty line */
.term = DEFAULT_VCTERM, /* terminal type */
.issue = ISSUE /* default issue file */
};
struct sigaction sa, sa_hup, sa_quit, sa_int;
sigset_t set;
@ -315,7 +320,8 @@ int main(int argc, char **argv)
termio_init(&options, &termios);
/* Write the modem init string and DO NOT flush the buffers. */
if (serial_tty_option(&options, F_INITSTRING)) {
if (serial_tty_option(&options, F_INITSTRING) &&
options.initstring && *options.initstring != '\0') {
debug("writing init string\n");
write_all(STDOUT_FILENO, options.initstring,
strlen(options.initstring));
@ -353,12 +359,21 @@ int main(int argc, char **argv)
chardata = init_chardata;
if ((options.flags & F_NOPROMPT) == 0) {
/* Read the login name. */
debug("reading login name\n");
while ((logname =
get_logname(&options, &termios, &chardata)) == 0)
if ((options.flags & F_VCONSOLE) == 0)
next_speed(&options, &termios);
if (options.autolog) {
/* Do the auto login */
debug("doing auto login\n");
do_prompt(&options, &termios);
printf ("%s%s (automatic login)\n", LOGIN, options.autolog);
logname = options.autolog;
options.logopt = "-f \\u";
} else {
/* Read the login name. */
debug("reading login name\n");
while ((logname =
get_logname(&options, &termios, &chardata)) == 0)
if ((options.flags & F_VCONSOLE) == 0)
next_speed(&options, &termios);
}
}
/* Disable timer. */
@ -376,8 +391,16 @@ int main(int argc, char **argv)
sigaction (SIGQUIT, &sa_quit, NULL);
sigaction (SIGINT, &sa_int, NULL);
strncpy(logcmd, options.login, NAME_MAX);
strncat(logcmd, " ", NAME_MAX - strlen(logcmd));
strncat(logcmd, options.logopt, NAME_MAX - strlen(logcmd));
checkname(logname);
mkarray(logarr, logcmd);
replacename(logarr, logname);
/* Let the login program take care of password validation. */
execl(options.login, options.login, "--", logname, NULL);
execv(options.login, logarr);
log_err(_("%s: can't exec %s: %m"), options.tty, options.login);
}
@ -394,6 +417,7 @@ static void parse_args(int argc, char **argv, struct options *op)
};
const struct option longopts[] = {
{ "8bits", no_argument, 0, '8' },
{ "autologin", required_argument, 0, 'a' },
{ "noreset", no_argument, 0, 'c' },
{ "issue-file", required_argument, 0, 'f' },
{ "flow-control", no_argument, 0, 'h' },
@ -404,6 +428,10 @@ static void parse_args(int argc, char **argv, struct options *op)
{ "local-line", no_argument, 0, 'L' },
{ "extract-baud", no_argument, 0, 'm' },
{ "skip-login", no_argument, 0, 'n' },
{ "login-options", required_argument, 0, 'o' },
{ "loginopts", required_argument, 0, 'o' }, /* compat option */
{ "logopts", required_argument, 0, 'o' }, /* compat option */
{ "loginpause", no_argument, 0, 'p' },
{ "hangup", no_argument, 0, 'R' },
{ "keep-baud", no_argument, 0, 's' },
{ "timeout", required_argument, 0, 't' },
@ -414,12 +442,15 @@ static void parse_args(int argc, char **argv, struct options *op)
{ NULL, 0, 0, 0 }
};
while ((c = getopt_long(argc, argv, "8cf:hH:iI:l:LmnsRt:Uw", longopts,
while ((c = getopt_long(argc, argv, "8a:cf:hH:iI:l:Lmno:pRst:Uw", longopts,
NULL)) != -1) {
switch (c) {
case '8':
op->flags |= F_EIGHTBITS;
break;
case 'a':
op->autolog = optarg;
break;
case 'c':
op->flags |= F_KEEPCFLAGS;
break;
@ -452,6 +483,12 @@ static void parse_args(int argc, char **argv, struct options *op)
case 'n':
op->flags |= F_NOPROMPT;
break;
case 'o':
op->logopt = optarg;
break;
case 'p':
op->flags |= F_LOGINPAUSE;
break;
case 'R':
op->flags |= F_HANGUP;
break;
@ -1066,14 +1103,73 @@ static void do_prompt(struct options *op, struct termios *tp)
}
fclose(fd);
}
#endif /* ISSUE */
#endif /* ISSUE */
if (op->flags & F_LOGINPAUSE) {
puts("[press ENTER to login]");
getc(stdin);
}
#ifdef KDGKBLED
if (op->autolog == (char*)0 && (op->flags & F_VCONSOLE)) {
int kb = 0, nl = 0;
struct stat st;
if (stat("/var/run/numlock-on", &st) == 0)
nl = 1;
if (ioctl(STDIN_FILENO, KDGKBLED, &kb) == 0) {
const char *hint = _("Hint: ");
char warn[256];
off_t space = sizeof(warn) - 1, off = 0;
if (nl && (kb & 0x02) == 0) {
const char *msg = _("Num Lock off");
const size_t len = strlen(msg);
strncpy(&warn[0], msg, space);
space -= len;
off += len;
} else if (nl == 0 && (kb & 2) && (kb & 0x20) == 0) {
const char *msg = _("Num Lock on");
const size_t len = strlen(msg);
strncpy(&warn[0], msg, space);
space -= len;
off += len;
}
if ((kb & 0x04) && (kb & 0x40) == 0) {
const char *msg = _("Caps Lock on");
const size_t len = strlen(msg);
if (off) {
strncpy(&warn[off], ", ", space);
space -= 2;
off += 2;
}
strncpy(&warn[off], msg, space);
space -= len;
off += len;
}
if ((kb & 0x01) && (kb & 0x10) == 0) {
const char *msg = _("Scroll Lock on");
const size_t len = strlen(msg);
if (off) {
strncpy(&warn[off], ", ", space);
space -= 2;
off += 2;
}
strncpy(&warn[off], msg, space);
space -= len;
off += len;
}
if (off)
printf ("%s%s\n\n", hint, warn);
}
}
#endif /* KDGKBLED */
{
char hn[MAXHOSTNAMELEN + 1];
if (gethostname(hn, sizeof(hn)) == 0)
write_all(STDOUT_FILENO, hn, strlen(hn));
}
/* Always show login prompt. */
write_all(STDOUT_FILENO, LOGIN, sizeof(LOGIN) - 1);
if (op->autolog == (char*)0) {
/* Always show login prompt. */
write_all(STDOUT_FILENO, LOGIN, sizeof(LOGIN) - 1);
}
}
/* Select next baud rate. */
@ -1360,6 +1456,7 @@ static void __attribute__ ((__noreturn__)) usage(FILE * out)
fprintf(out, _("\nOptions:\n"
" -8, --8bits assume 8-bit tty\n"
" -a, --autologin USER login the specified user automatically\n"
" -c, --noreset do not reset control mode\n"
" -f, --issue-file FILE display issue file\n"
" -h, --flow-control enable hardware flow control\n"
@ -1370,6 +1467,8 @@ static void __attribute__ ((__noreturn__)) usage(FILE * out)
" -L, --local-line force local line\n"
" -m, --extract-baud extract baud rate during connect\n"
" -n, --skip-login do not prompt for login\n"
" -o, --login-options OPTS options that are passed to login\n"
" -p, --loginpause wait for any key before the login\n"
" -R, --hangup do virtually hangup on the tty\n"
" -s, --keep-baud try to keep baud rate after break\n"
" -t, --timeout NUMBER login process timeout\n"
@ -1603,3 +1702,71 @@ static void init_special_char(char* arg, struct options *op)
}
*q = '\0';
}
/*
* Do not allow the user to pass an option as a user name
* To be more safe: Use `--' to make sure the rest is
* interpreted as non-options by the program, if it supports it.
*/
static void checkname(const char* nm)
{
const char *p = nm;
if (!nm)
goto err;
if (strlen (nm) > 42)
goto err;
while (isspace (*p))
p++;
if (*p == '-')
goto err;
return;
err:
errno = EPERM;
log_err ("checkname: %m");
}
static void replacename(char** arr, const char* nm)
{
const char *p;
char *tmp;
while ((p = *arr)) {
const char *p1 = p;
while (*p1) {
if (memcmp (p1, "\\u", 2) == 0) {
tmp = malloc (strlen (p) + strlen (nm));
if (!tmp)
log_err ("replacename: %m");
if (p1 != p)
memcpy (tmp, p, (p1-p));
*(tmp + (p1-p)) = 0;
strcat (tmp, nm);
strcat (tmp, p1+2);
*arr = tmp;
}
p1++;
}
arr++;
}
}
static void mkarray(char** arr, char* str)
{
char* p = str;
char* start = p;
int i = 0;
while (*p && i < (ARRAY_SIZE_MAX)) {
if (isspace (*p)) {
*p = 0;
while (isspace (*++p))
;
if (*p) {
arr[i++] = start;
start = p;
}
} else
p++;
}
arr[i++] = start;
arr[i++] = (char*)0;
}