From eb8e1f9f0cfb8ebb44ca50962e0623257c4d6436 Mon Sep 17 00:00:00 2001 From: Werner Fink Date: Mon, 9 May 2011 15:52:39 +0200 Subject: 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 --- term-utils/agetty.8 | 29 +++++++ term-utils/agetty.c | 219 +++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 222 insertions(+), 26 deletions(-) (limited to 'term-utils') diff --git a/term-utils/agetty.8 b/term-utils/agetty.8 index 4abd8197f..f998699b2 100644 --- a/term-utils/agetty.8 +++ b/term-utils/agetty.8 @@ -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 diff --git a/term-utils/agetty.c b/term-utils/agetty.c index 5f2df0837..64b2fd1ff 100644 --- a/term-utils/agetty.c +++ b/term-utils/agetty.c @@ -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; +} -- cgit v1.2.3-55-g7522