]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
agetty: improve login(1) argv[]
authorKarel Zak <kzak@redhat.com>
Thu, 4 Aug 2011 16:37:25 +0000 (18:37 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 8 Aug 2011 10:07:07 +0000 (12:07 +0200)
* fix regression: missing username should not be reported (EPERM) if
  -n/--skip-login is given and username is NULL.

* don't compose login options *string* if we can use argv *array* (the
  string is necessary only for --login-options).

* don't overwrite --login-options by --autologin

  The old code silently ignores login-options and "login -f <username>"
  is always used.

  The new code uses:
    a) "login -f <username>"    by default
    b) "login <login-options>"  for --login-options + --autologin

  where for b) the username from "--autologin <username>" is used to
  replace \u magic string in <login-options>.

* the \u could be used more than once in one login argv string, for
  example: agetty --login-options "-o user=\\u,name=\\u --foo"

* the space in --login-options is correctly ignored, for example
  agetty --login-options " hello world ".

Reviewed-by: Voelker, Bernhard <bernhard.voelker@siemens-enterprise.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
term-utils/agetty.8
term-utils/agetty.c

index 4ccb28a14d68caccdbd651438f9a1d0d0e4a884e..c43f4561cb4459a0151552ea27f109bc0bd9b19f 100644 (file)
@@ -87,9 +87,11 @@ whatever init(8) may have set, and is inherited by login and the shell.
 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.
+Log the specified user automatically in without asking for a login name and
+password. The \-f \fIusername\fP option is added to the \fB/bin/login\fP
+command line by default. The \-\-login-options option changes this default
+behaviour and then only \\u is replaced by the \fIusername\fP and no other
+option is added to the login command line.
 .TP
 \-c, \-\-noreset
 Don't reset terminal cflags (control modes). See \fItermios(3)\fP for more
@@ -160,9 +162,10 @@ is run as root.
 .TP
 \-o, \-\-login\-options \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.
+by the login name. The default \fB/bin/login\fP command line
+is "/bin/login -- <username>".
+
+Please read the SECURITY NOTICE below if you want to use this.
 .TP
 \-p, \-\-login\-pause
 Wait for any key before dropping to the login prompt.  Can be combined
index 6a77df4d8e2cbb43f8d7bb5eeefd90c17b791dde..084c64af46ddfb0349d4c809caa4969d753a0687 100644 (file)
 
 /* Login prompt. */
 #define LOGIN          "login: "
-#define ARRAY_SIZE_MAX 16              /* Numbers of args for login beside "-- \\u" */
+#define LOGIN_ARGV_MAX 16              /* Numbers of args for login */
 
 /* Some shorthands for control characters. */
 #define CTL(x)         (x ^ 0100)      /* Assumes ASCII dialect */
@@ -262,9 +262,8 @@ static void log_err(const char *, ...) __attribute__((__noreturn__))
 static void log_warn (const char *, ...)
                                __attribute__((__format__(printf, 1, 2)));
 static ssize_t append(char *dest, size_t len, const char  *sep, const char *src);
-static void checkname (const char* nm);
-static void replacename (char** arr, const char* nm);
-static void mkarray (char** arr, char* str);
+static void check_username (const char* nm);
+static void login_options_to_argv(char *argv[], int *argc, char *str, char *username);
 
 /* Fake hostname for ut_host specified on command line. */
 static char *fakehost;
@@ -278,19 +277,18 @@ FILE *dbf;
 
 int main(int argc, char **argv)
 {
-       char *logname = NULL;                   /* login name, given to /bin/login */
-       char  logcmd[NAME_MAX+1];
-       char *logarr[ARRAY_SIZE_MAX + 2];       /* arguments plus "-- \\u" */
+       char *username = NULL;                  /* login name, given to /bin/login */
        struct chardata chardata;               /* will be set by get_logname() */
        struct termios termios;                 /* terminal mode bits */
        static struct options options = {
                .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 */
        };
+       char *login_argv[LOGIN_ARGV_MAX + 1];
+       int login_argc = 0;
        struct sigaction sa, sa_hup, sa_quit, sa_int;
        sigset_t set;
 
@@ -315,6 +313,8 @@ int main(int argc, char **argv)
        /* Parse command-line arguments. */
        parse_args(argc, argv, &options);
 
+       login_argv[login_argc++] = options.login;       /* set login program name */
+
        /* Update the utmp file. */
 #ifdef SYSV_STYLE
        update_utmp(&options);
@@ -379,16 +379,15 @@ int main(int argc, char **argv)
        chardata = init_chardata;
        if ((options.flags & F_NOPROMPT) == 0) {
                if (options.autolog) {
-                       /* Do the auto login */
+                       /* 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";
+                       username = options.autolog;
                } else {
                        /* Read the login name. */
                        debug("reading login name\n");
-                       while ((logname =
+                       while ((username =
                                get_logname(&options, &termios, &chardata)) == 0)
                                if ((options.flags & F_VCONSOLE) == 0)
                                        next_speed(&options, &termios);
@@ -410,13 +409,25 @@ int main(int argc, char **argv)
        sigaction(SIGQUIT, &sa_quit, NULL);
        sigaction(SIGINT, &sa_int, NULL);
 
-       *logcmd = '\0';
-       append(logcmd, sizeof(logcmd), NULL, options.login);
-       append(logcmd, sizeof(logcmd), " ", options.logopt);
+       if (username)
+               check_username(username);
+
+       if (options.logopt) {
+               /*
+                * The --login-options completely overwrites the default
+                * way how agetty composes login(1) command line.
+                */
+               login_options_to_argv(login_argv, &login_argc,
+                                     options.logopt, username);
+       } else if (username) {
+               if (options.autolog)
+                       login_argv[login_argc++] = "-f";
+               else
+                       login_argv[login_argc++] = "--";
+               login_argv[login_argc++] = username;
+       }
 
-       checkname(logname);
-       mkarray(logarr, logcmd);
-       replacename(logarr, logname);
+       login_argv[login_argc] = NULL;  /* last login argv */
 
        if (options.chroot) {
                if (chroot(options.chroot) < 0)
@@ -435,8 +446,87 @@ int main(int argc, char **argv)
        }
 
        /* Let the login program take care of password validation. */
-       execv(options.login, logarr);
-       log_err(_("%s: can't exec %s: %m"), options.tty, options.login);
+       execv(options.login, login_argv);
+       log_err(_("%s: can't exec %s: %m"), options.tty, login_argv[0]);
+}
+
+/*
+ * Returns : @str if \u not found
+ *         : @username if @str equal to "\u"
+ *         : newly allocated string if \u mixed with something other
+ */
+static char *replace_u(char *str, char *username)
+{
+       char *entry = NULL, *p = str;
+       size_t usz = username ? strlen(username) : 0;
+
+       while (*p) {
+               size_t sz;
+               char *tp, *old = entry;
+
+               if (memcmp(p, "\\u", 2)) {
+                       p++;
+                       continue;       /* no \u */
+               }
+               sz = strlen(str);
+
+               if (p == str && sz == 2)
+                       /* 'str' contains only '\u' */
+                       return username;
+
+               tp = entry = malloc(sz + usz);
+               if (!tp)
+                       log_err(_("failed to allocate memory: %m"));
+
+               if (p != str) {
+                       /* copy chars befor \u */
+                       memcpy(tp, str, p - str);
+                       tp += p - str;
+               }
+               if (usz) {
+                       /* copy username */
+                       memcpy(tp, username, usz);
+                       tp += usz;
+               }
+               if (*(p + 2))
+                       /* copy chars after \u + \0 */
+                       memcpy(tp, p + 2, sz - (p - str) - 1);
+               else
+                       *tp = '\0';
+
+               p = tp;
+               str = entry;
+               free(old);
+       }
+
+       return entry ? entry : str;
+}
+
+static void login_options_to_argv(char *argv[], int *argc,
+                                 char *str, char *username)
+{
+       char *p;
+       int i = *argc;
+
+       while (str && isspace(*str))
+               str++;
+       p = str;
+
+       while (p && *p && i < LOGIN_ARGV_MAX) {
+               if (isspace(*p)) {
+                       *p = '\0';
+                       while (isspace(*++p))
+                               ;
+                       if (*p) {
+                               argv[i++] = replace_u(str, username);
+                               str = p;
+                       }
+               } else
+                       p++;
+       }
+       if (str && *str && i < LOGIN_ARGV_MAX)
+               argv[i++] = replace_u(str, username);
+       *argc = i;
 }
 
 /* Parse command-line arguments. */
@@ -1833,12 +1923,13 @@ static ssize_t append(char *dest, size_t len, const char  *sep, const char *src)
 
        return dsz + ssz + sz;
 }
+
 /*
  * 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)
+static void check_username(const char* nm)
 {
        const char *p = nm;
        if (!nm)
@@ -1855,48 +1946,3 @@ err:
        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(_("failed to allocate memory: %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;
-}