]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
agetty: add an autologin feature
authorWerner Fink <werner@suse.de>
Mon, 9 May 2011 13:52:39 +0000 (15:52 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 17 May 2011 09:13:06 +0000 (11:13 +0200)
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>
term-utils/agetty.8
term-utils/agetty.c

index 4abd8197f50a7837053c5b6cfd0673e18be59844..f998699b29e279bfd7e31134ecfdb9f88c6766c9 100644 (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
index 5f2df0837b7cef822671bc8f680da1c85368ee92..64b2fd1ff610dbf9e58216ddcb69f032121ea007 100644 (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 */
 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;
+}