Skip to content

Conversation

@alejandro-colomar
Copy link
Collaborator

@alejandro-colomar alejandro-colomar commented Dec 9, 2024

Closes: #1149
Link: https://www.wired.com/story/null-license-plate-landed-one-hacker-ticket-hell/
Link: https://xkcd.com/327/
Link: https://www.youtube.com/watch?v=hNoS2BU6bbQ
Link: https://lwn.net/Articles/1001215/
Link: https://dwheeler.com/essays/fixing-unix-linux-filenames.html
Link: #121
Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=42874
Link: https://lists.debian.org/debian-devel/2024/11/msg00250.html
Link: https://lists.debian.org/debian-devel/2024/12/msg00012.html
Link: https://lwn.net/Articles/1000485/
Cc: @ikerexxe
Cc: @hallyn
Cc: @thesamesam
Cc: @jubalh
Cc: @zeha
Cc: @rbalint
Cc: @Zugschlus

While at it:

src/: Report errors in user or groups names more consistently

  • Don't print the user name; if it's bad, it might be dangerous.
  • Print the string "user" or "group" before the error message.
  • Print the errno string to be consistent.

v1b
  • Rebase
$ git range-diff master..gh/nobadnames shadow/master..nobadnames 
1:  be021c88 ! 1:  7fea8a83 Little Bobby Tables
    @@ Commit message
     
      ## lib/chkname.c ##
     @@
    - #include "chkname.h"
    + #include "string/strcmp/streq.h"
      
      
     -int allow_bad_names = false;
2:  df6999b5 = 2:  a33b3c02 src/: Report errors in user or groups names more consistently
v2
$ git range-diff master..gh/nobadnames chkname..nobadnames 
1:  7fea8a83 ! 1:  51a73833 Little Bobby Tables
    @@ Commit message
     
      ## lib/chkname.c ##
     @@
    - #include "string/strcmp/streq.h"
    +  *   false - bad name
    +  * errors:
    +  *   EINVAL       Invalid name
    +- *   EILSEQ       Invalid name character sequence (acceptable with --badname)
    ++ *   EILSEQ       Invalid name character sequence
    +  *   EOVERFLOW    Name longer than maximum size
    +  */
    + 
    +@@
    + #include "string/strcmp/strprefix.h"
      
      
     -int allow_bad_names = false;
    @@ lib/chkname.c
      size_t
      login_name_max_size(void)
      {
    -@@ lib/chkname.c: login_name_max_size(void)
    - static bool
    - is_valid_name(const char *name)
    - {
    +@@ lib/chkname.c: is_valid_name(const char *name)
    +   if (streq(name, "")
    +    || streq(name, ".")
    +    || streq(name, "..")
    +-   || strpbrk(name, ",: ")
    +    || strprefix(name, "-")
    +-   || strchriscntrl(name)
    +    || strisdigit(name))
    +   {
    +           errno = EINVAL;
    +           return false;
    +   }
    + 
     -  if (allow_bad_names) {
     -          return true;
     -  }
    @@ src/newusers.c: static int add_user (const char *name, uid_t uid, gid_t gid)
      
     -  /* Check if this is a valid user name */
        if (!is_valid_user_name(name)) {
    --          if (errno == EINVAL) {
    +-          if (errno == EILSEQ) {
     -                  fprintf(stderr,
     -                          _("%s: invalid user name '%s': use --badname to ignore\n"),
     -                          Prog, name);
    @@ src/pwck.c: static void check_pw_file (int *errors, bool *changed)
     -           */
     -
                if (!is_valid_user_name(pwd->pw_name)) {
    --                  if (errno == EINVAL) {
    +-                  if (errno == EILSEQ) {
     -                          printf(_("invalid user name '%s': use --badname to ignore\n"),
     -                                 pwd->pw_name);
     -                  } else {
    @@ src/useradd.c: static void process_flags (int argc, char **argv)
      
                user_name = argv[optind];
                if (!is_valid_user_name(user_name)) {
    --                  if (errno == EINVAL) {
    +-                  if (errno == EILSEQ) {
     -                          fprintf(stderr,
     -                                  _("%s: invalid user name '%s': use --badname to ignore\n"),
     -                                  Prog, user_name);
    @@ src/usermod.c: process_flags(int argc, char **argv)
                                /*@notreached@*/break;
                        case 'l':
                                if (!is_valid_user_name(optarg)) {
    --                                  if (errno == EINVAL) {
    +-                                  if (errno == EILSEQ) {
     -                                          fprintf(stderr,
     -                                                  _("%s: invalid user name '%s': use --badname to ignore\n"),
     -                                                  Prog, optarg);
2:  a33b3c02 = 2:  d45dc827 src/: Report errors in user or groups names more consistently
v2b
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  51a73833 = 1:  f423a89e Little Bobby Tables
2:  d45dc827 = 2:  eee679fe src/: Report errors in user or groups names more consistently
v2c
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  f423a89e ! 1:  c892957c Little Bobby Tables
    @@ lib/chkname.c: is_valid_name(const char *name)
        if (streq(name, "")
         || streq(name, ".")
         || streq(name, "..")
    --   || strpbrk(name, ",: ")
    +-   || strpbrk(name, ",: /")
         || strprefix(name, "-")
     -   || strchriscntrl(name)
         || strisdigit(name))
2:  eee679fe = 2:  1cd7313f src/: Report errors in user or groups names more consistently
v2d
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  c892957c ! 1:  398a1bbe Little Bobby Tables
    @@ lib/chkname.c
       */
      
     @@
    - #include "string/strcmp/strprefix.h"
    + #endif
      
      
     -int allow_bad_names = false;
2:  1cd7313f = 2:  769bf0a9 src/: Report errors in user or groups names more consistently
v2e
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  398a1bbe = 1:  8869cc1b Little Bobby Tables
2:  769bf0a9 = 2:  f43bbf53 src/: Report errors in user or groups names more consistently
v2f
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  8869cc1b ! 1:  2d442bdf Little Bobby Tables
    @@ src/newusers.c: static void process_flags (int argc, char **argv)
     
      ## src/pwck.c ##
     @@ src/pwck.c: static void close_files (bool changed);
    - static void check_pw_file (int *errors, bool *changed);
    - static void check_spw_file (int *errors, bool *changed);
    + static void check_pw_file (bool *errors, bool *changed);
    + static void check_spw_file (bool *errors, bool *changed);
      
     -extern int allow_bad_names;
      
    @@ src/pwck.c: static void process_flags (int argc, char **argv)
                case 'h':
                        usage (E_SUCCESS);
                        /*@notreached@*/break;
    -@@ src/pwck.c: static void check_pw_file (int *errors, bool *changed)
    +@@ src/pwck.c: static void check_pw_file (bool *errors, bool *changed)
                        }
                }
      
    @@ src/pwck.c: static void check_pw_file (int *errors, bool *changed)
     -                                 pwd->pw_name);
     -                  }
     +                  printf(_("invalid user name '%s'\n"), pwd->pw_name);
    -                   *errors += 1;
    +                   *errors = true;
                }
      
     
2:  f43bbf53 ! 2:  93844d78 src/: Report errors in user or groups names more consistently
    @@ src/grpck.c
      
      #include "chkname.h"
      #include "commonio.h"
    -@@ src/grpck.c: static void check_grp_file (int *errors, bool *changed)
    +@@ src/grpck.c: static void check_grp_file (bool *errors, bool *changed)
                 */
                if (!is_valid_group_name (grp->gr_name)) {
    -                   *errors += 1;
    +                   *errors = true;
     -                  printf (_("invalid group name '%s'\n"), grp->gr_name);
     +                  printf(_("group: %s\n"), strerror(errno));
                }
    @@ src/newgrp.c
      #include <pwd.h>
      #include <stdio.h>
     +#include <string.h>
    - #include <assert.h>
    + #include <sys/types.h>
      
      #include "agetpass.h"
     @@ src/newgrp.c: int main (int argc, char **argv)
    @@ src/pwck.c
      
      #include "chkname.h"
      #include "commonio.h"
    -@@ src/pwck.c: static void check_pw_file (int *errors, bool *changed)
    +@@ src/pwck.c: static void check_pw_file (bool *errors, bool *changed)
                }
      
                if (!is_valid_user_name(pwd->pw_name)) {
     -                  printf(_("invalid user name '%s'\n"), pwd->pw_name);
     +                  printf(_("user: %s\n"), strerror(errno));
    -                   *errors += 1;
    +                   *errors = true;
                }
      
     
v2g
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  2d442bdf ! 1:  70dfe564 Little Bobby Tables
    @@ lib/chkname.c: is_valid_name(const char *name)
        if (streq(name, "")
         || streq(name, ".")
         || streq(name, "..")
    --   || strpbrk(name, ",: /")
    +-   || strpbrk(name, ",: /\"")
         || strprefix(name, "-")
     -   || strchriscntrl(name)
         || strisdigit(name))
2:  93844d78 = 2:  0368ae60 src/: Report errors in user or groups names more consistently
v2h
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  70dfe564 ! 1:  b05047d1 Little Bobby Tables
    @@ lib/chkname.c
      login_name_max_size(void)
      {
     @@ lib/chkname.c: is_valid_name(const char *name)
    -   if (streq(name, "")
    -    || streq(name, ".")
    -    || streq(name, "..")
    --   || strpbrk(name, ",: /\"")
    +    || strcaseeq(name, "all")
    +    || strcaseeq(name, "except")
         || strprefix(name, "-")
    +-   || strpbrk(name, ",: /@\"")
     -   || strchriscntrl(name)
         || strisdigit(name))
        {
2:  0368ae60 = 2:  49762400 src/: Report errors in user or groups names more consistently
v2i
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  b05047d1 ! 1:  df70e6cf Little Bobby Tables
    @@ lib/chkname.c: is_valid_name(const char *name)
         || strcaseeq(name, "all")
         || strcaseeq(name, "except")
         || strprefix(name, "-")
    --   || strpbrk(name, ",: /@\"")
    +-   || strpbrk(name, " \"#,/:@")
     -   || strchriscntrl(name)
         || strisdigit(name))
        {
2:  49762400 = 2:  c3130aac src/: Report errors in user or groups names more consistently
v2j
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  df70e6cf ! 1:  795af449 Little Bobby Tables
    @@ lib/chkname.c: is_valid_name(const char *name)
         || strcaseeq(name, "all")
         || strcaseeq(name, "except")
         || strprefix(name, "-")
    --   || strpbrk(name, " \"#,/:@")
    +-   || strpbrk(name, " !\"#&*+,/:;@|")
     -   || strchriscntrl(name)
         || strisdigit(name))
        {
2:  c3130aac = 2:  9359d770 src/: Report errors in user or groups names more consistently
v2k
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  795af449 = 1:  7b364d0e Little Bobby Tables
2:  9359d770 = 2:  e93633b6 src/: Report errors in user or groups names more consistently
v2l
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  7b364d0e = 1:  62fb6405 Little Bobby Tables
2:  e93633b6 = 2:  62c72ef4 src/: Report errors in user or groups names more consistently
v2m
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  62fb6405 = 1:  3a704c43 Little Bobby Tables
2:  62c72ef4 = 2:  396e1568 src/: Report errors in user or groups names more consistently
v2n
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  3a704c43 = 1:  af77c74e Little Bobby Tables
2:  396e1568 = 2:  17081c77 src/: Report errors in user or groups names more consistently
v2o
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  af77c74e ! 1:  82922b8f Little Bobby Tables
    @@ lib/chkname.c: is_valid_name(const char *name)
         || strcaseeq(name, "all")
         || strcaseeq(name, "except")
         || strprefix(name, "-")
    --   || strpbrk(name, " !\"#&*+,/:;@|")
    +-   || strpbrk(name, " !\"#&*+,/:;@|~")
     -   || strchriscntrl(name)
         || strisdigit(name))
        {
2:  17081c77 = 2:  d83e3b5e src/: Report errors in user or groups names more consistently
v2p
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  82922b8f ! 1:  b70622ba Little Bobby Tables
    @@ src/useradd.c: static void process_flags (int argc, char **argv)
     +                          Prog, user_name);
      #ifdef WITH_AUDIT
                        audit_logger (AUDIT_ADD_USER, Prog,
    -                                 "adding user",
    +                                 "add-user",
     
      ## src/usermod.c ##
     @@ src/usermod.c: static void update_faillog (void);
2:  d83e3b5e ! 2:  e2917149 src/: Report errors in user or groups names more consistently
    @@ src/groupadd.c: static void
     -                  Prog, group_name);
     -
     +          fprintf(stderr, _("%s: group: %s\n"), Prog, strerror(errno));
    -           exit(E_BAD_ARG);
    +           fail_exit (E_BAD_ARG);
        }
      
     
    @@ src/useradd.c: static void process_flags (int argc, char **argv)
     +                  fprintf(stderr, _("%s: user: %s\n"), Prog, strerror(errno));
      #ifdef WITH_AUDIT
                        audit_logger (AUDIT_ADD_USER, Prog,
    -                                 "adding user",
    +                                 "add-user",
     
      ## src/usermod.c ##
     @@
v2q
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  b70622ba = 1:  4fbb7e90 Little Bobby Tables
2:  e2917149 = 2:  dcf74a50 src/: Report errors in user or groups names more consistently
v2r
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  4fbb7e90 = 1:  01b39e19 Little Bobby Tables
2:  dcf74a50 = 2:  613e2f0f src/: Report errors in user or groups names more consistently
v2s
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  01b39e19 = 1:  89a22951 Little Bobby Tables
2:  613e2f0f = 2:  6a190643 src/: Report errors in user or groups names more consistently
v2t
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  89a22951 = 1:  237e9384 Little Bobby Tables
2:  6a190643 = 2:  8c508e59 src/: Report errors in user or groups names more consistently
v2u
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  237e9384 = 1:  8101abc5 Little Bobby Tables
2:  8c508e59 = 2:  f0bc22ee src/: Report errors in user or groups names more consistently
v2v
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  8101abc5 = 1:  f3d3a454 Little Bobby Tables
2:  f0bc22ee = 2:  3b49d22c src/: Report errors in user or groups names more consistently
v2w
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  f3d3a454 = 1:  c942beaf Little Bobby Tables
2:  3b49d22c = 2:  3e66a50b src/: Report errors in user or groups names more consistently
v2x
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  c942beaf = 1:  0ed7af7b Little Bobby Tables
2:  3e66a50b = 2:  50f10abd src/: Report errors in user or groups names more consistently
v2y
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  0ed7af7b = 1:  00e395c0 Little Bobby Tables
2:  50f10abd = 2:  83b8edb4 src/: Report errors in user or groups names more consistently
v2z
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  00e395c0 = 1:  fca682b1 Little Bobby Tables
2:  83b8edb4 = 2:  8577a427 src/: Report errors in user or groups names more consistently
v3
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  fca682b1 = 1:  620379ef Little Bobby Tables
2:  8577a427 = 2:  aa95c3e4 src/: Report errors in user or groups names more consistently
v3b
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  620379ef = 1:  e7afd805 Little Bobby Tables
2:  aa95c3e4 = 2:  c1c96c61 src/: Report errors in user or groups names more consistently
v3c
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  e7afd805 = 1:  78f9cec3 Little Bobby Tables
2:  c1c96c61 ! 2:  ffc662a1 src/: Report errors in user or groups names more consistently
    @@ src/groupmod.c: check_new_name(void)
      ## src/grpck.c ##
     @@
      
    - #include <config.h>
    + #include "config.h"
      
     +#include <errno.h>
      #include <fcntl.h>
v3d
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  78f9cec3 = 1:  f9370cd8 Little Bobby Tables
2:  ffc662a1 = 2:  1b152e3c src/: Report errors in user or groups names more consistently
v3e
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  f9370cd8 = 1:  54578549 Little Bobby Tables
2:  1b152e3c ! 2:  c5afad58 src/: Report errors in user or groups names more consistently
    @@ src/chfn.c
      #include <fcntl.h>
      #include <pwd.h>
      #include <signal.h>
    - #include <stdio.h>
    -+#include <string.h>
    - #include <sys/types.h>
    - #include <getopt.h>
    - 
     @@ src/chfn.c: int main (int argc, char **argv)
         */
        if (optind < argc) {
    @@ src/useradd.c: static void process_flags (int argc, char **argv)
     
      ## src/usermod.c ##
     @@
    - #endif                            /* USE_PAM */
      #endif                            /* ACCT_TOOLS_SETUID */
    + #include <paths.h>
      #include <stdio.h>
     +#include <string.h>
      #include <strings.h>
v3f
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  54578549 ! 1:  4533314b Little Bobby Tables
    @@ man/usermod.8.xml
          <option>-c</option>, <option>--comment</option>&nbsp;<replaceable>COMMENT</replaceable>
     
      ## src/newusers.c ##
    -@@ src/newusers.c: static void check_perms (void);
    - static void open_files (void);
    - static void close_files (void);
    +@@ src/newusers.c: static void check_perms (struct option_flags *flags);
    + static void open_files (bool process_selinux);
    + static void close_files (struct option_flags *flags);
      
     -extern int allow_bad_names;
      
    @@ src/newusers.c: static int add_user (const char *name, uid_t uid, gid_t gid)
                return -1;
        }
      
    -@@ src/newusers.c: static void process_flags (int argc, char **argv)
    +@@ src/newusers.c: static void process_flags (int argc, char **argv, struct option_flags *flags)
      #endif                            /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
      #endif                            /* !USE_PAM */
        static struct option long_options[] = {
    @@ src/newusers.c: static void process_flags (int argc, char **argv)
      #ifndef USE_PAM
                {"crypt-method", required_argument, NULL, 'c'},
      #endif                            /* !USE_PAM */
    -@@ src/newusers.c: static void process_flags (int argc, char **argv)
    +@@ src/newusers.c: static void process_flags (int argc, char **argv, struct option_flags *flags)
      #endif
                                 long_options, NULL)) != -1) {
                switch (c) {
    @@ src/newusers.c: static void process_flags (int argc, char **argv)
                        crypt_method = optarg;
     
      ## src/pwck.c ##
    -@@ src/pwck.c: static void close_files (bool changed);
    - static void check_pw_file (bool *errors, bool *changed);
    +@@ src/pwck.c: static void check_pw_file (bool *errors, bool *changed,
    +                            struct option_flags *flags);
      static void check_spw_file (bool *errors, bool *changed);
      
     -extern int allow_bad_names;
    @@ src/pwck.c: usage (int status)
        (void) fputs (_("  -h, --help                    display this help message and exit\n"), usageout);
        (void) fputs (_("  -q, --quiet                   report errors only\n"), usageout);
        (void) fputs (_("  -r, --read-only               display errors and warnings\n"
    -@@ src/pwck.c: static void process_flags (int argc, char **argv)
    +@@ src/pwck.c: static void process_flags (int argc, char **argv, struct option_flags *flags)
      {
        int c;
        static struct option long_options[] = {
    @@ src/pwck.c: static void process_flags (int argc, char **argv)
                {"help",      no_argument,       NULL, 'h'},
                {"quiet",     no_argument,       NULL, 'q'},
                {"read-only", no_argument,       NULL, 'r'},
    -@@ src/pwck.c: static void process_flags (int argc, char **argv)
    +@@ src/pwck.c: static void process_flags (int argc, char **argv, struct option_flags *flags)
        while ((c = getopt_long (argc, argv, "behqrR:s",
                                 long_options, NULL)) != -1) {
                switch (c) {
    @@ src/pwck.c: static void process_flags (int argc, char **argv)
                case 'h':
                        usage (E_SUCCESS);
                        /*@notreached@*/break;
    -@@ src/pwck.c: static void check_pw_file (bool *errors, bool *changed)
    +@@ src/pwck.c: static void check_pw_file (bool *errors, bool *changed, struct option_flags *fla
                        }
                }
      
    @@ src/useradd.c: static void usage (int status)
        (void) fputs (_("  -b, --base-dir BASE_DIR       base directory for the home directory of the\n"
                        "                                new account\n"), usageout);
      #ifdef WITH_BTRFS
    -@@ src/useradd.c: static void process_flags (int argc, char **argv)
    +@@ src/useradd.c: static void process_flags (int argc, char **argv, struct option_flags *flags)
      #ifdef WITH_BTRFS
                        {"btrfs-subvolume-home", no_argument, NULL, 200},
      #endif
    @@ src/useradd.c: static void process_flags (int argc, char **argv)
                        {"comment",        required_argument, NULL, 'c'},
                        {"home-dir",       required_argument, NULL, 'd'},
                        {"defaults",       no_argument,       NULL, 'D'},
    -@@ src/useradd.c: static void process_flags (int argc, char **argv)
    +@@ src/useradd.c: static void process_flags (int argc, char **argv, struct option_flags *flags)
                        case 200:
                                subvolflg = true;
                                break;
    @@ src/useradd.c: static void process_flags (int argc, char **argv)
                        case 'c':
                                if (!VALID (optarg)) {
                                        fprintf (stderr,
    -@@ src/useradd.c: static void process_flags (int argc, char **argv)
    +@@ src/useradd.c: static void process_flags (int argc, char **argv, struct option_flags *flags)
      
                user_name = argv[optind];
                if (!is_valid_user_name(user_name)) {
    @@ src/usermod.c: usage (int status)
        (void) fputs (_("  -c, --comment COMMENT         new value of the GECOS field\n"), usageout);
        (void) fputs (_("  -d, --home HOME_DIR           new home directory for the user account\n"), usageout);
        (void) fputs (_("  -e, --expiredate EXPIRE_DATE  set account expiration date to EXPIRE_DATE\n"), usageout);
    -@@ src/usermod.c: process_flags(int argc, char **argv)
    +@@ src/usermod.c: process_flags(int argc, char **argv, struct option_flags *flags)
                int c;
                static struct option long_options[] = {
                        {"append",       no_argument,       NULL, 'a'},
    @@ src/usermod.c: process_flags(int argc, char **argv)
                        {"comment",      required_argument, NULL, 'c'},
                        {"home",         required_argument, NULL, 'd'},
                        {"expiredate",   required_argument, NULL, 'e'},
    -@@ src/usermod.c: process_flags(int argc, char **argv)
    +@@ src/usermod.c: process_flags(int argc, char **argv, struct option_flags *flags)
                        case 'a':
                                aflg = true;
                                break;
    @@ src/usermod.c: process_flags(int argc, char **argv)
                        case 'c':
                                if (!VALID (optarg)) {
                                        fprintf (stderr,
    -@@ src/usermod.c: process_flags(int argc, char **argv)
    +@@ src/usermod.c: process_flags(int argc, char **argv, struct option_flags *flags)
                                /*@notreached@*/break;
                        case 'l':
                                if (!is_valid_user_name(optarg)) {
2:  c5afad58 ! 2:  ffc6a324 src/: Report errors in user or groups names more consistently
    @@ src/chfn.c: int main (int argc, char **argv)
                if (!is_valid_user_name (argv[optind])) {
     -                  fprintf (stderr, _("%s: Provided user name is not a valid name\n"), Prog);
     +                  fprintf(stderr, _("%s: user: %s\n"), Prog, strerror(errno));
    -                   fail_exit (E_NOPERM);
    +                   fail_exit (E_NOPERM, process_selinux);
                }
                user = argv[optind];
     
    @@ src/chsh.c: int main (int argc, char **argv)
                if (!is_valid_user_name (argv[optind])) {
     -                  fprintf (stderr, _("%s: Provided user name is not a valid name\n"), Prog);
     +                  fprintf(stderr, _("%s: user: %s\n"), Prog, strerror(errno));
    -                   fail_exit (1);
    +                   fail_exit (1, process_selinux);
                }
                user = argv[optind];
     
    @@ src/grpck.c
      #include <fcntl.h>
     +#include <getopt.h>
      #include <grp.h>
    + #include <paths.h>
      #include <pwd.h>
      #include <stdio.h>
     -#include <getopt.h>
    @@ src/grpck.c
      
      #include "chkname.h"
      #include "commonio.h"
    -@@ src/grpck.c: static void check_grp_file (bool *errors, bool *changed)
    +@@ src/grpck.c: static void check_grp_file (bool *errors, bool *changed, struct option_flags *fl
                 */
                if (!is_valid_group_name (grp->gr_name)) {
                        *errors = true;
    @@ src/passwd.c: main(int argc, char **argv)
                if (!is_valid_user_name (argv[optind])) {
     -                  fprintf (stderr, _("%s: Provided user name is not a valid name\n"), Prog);
     +                  fprintf(stderr, _("%s: user: %s\n"), Prog, strerror(errno));
    -                   fail_exit (E_NOPERM);
    +                   fail_exit (E_NOPERM, process_selinux);
                }
                name = argv[optind];
     
    @@ src/pwck.c
      
      #include "chkname.h"
      #include "commonio.h"
    -@@ src/pwck.c: static void check_pw_file (bool *errors, bool *changed)
    +@@ src/pwck.c: static void check_pw_file (bool *errors, bool *changed, struct option_flags *fla
                }
      
                if (!is_valid_user_name(pwd->pw_name)) {
    @@ src/pwck.c: static void check_pw_file (bool *errors, bool *changed)
      
     
      ## src/useradd.c ##
    -@@ src/useradd.c: static void process_flags (int argc, char **argv)
    +@@ src/useradd.c: static void process_flags (int argc, char **argv, struct option_flags *flags)
      
                user_name = argv[optind];
                if (!is_valid_user_name(user_name)) {
    @@ src/usermod.c
      #include <strings.h>
      #include <sys/stat.h>
      #include <sys/types.h>
    -@@ src/usermod.c: process_flags(int argc, char **argv)
    +@@ src/usermod.c: process_flags(int argc, char **argv, struct option_flags *flags)
                                /*@notreached@*/break;
                        case 'l':
                                if (!is_valid_user_name(optarg)) {
v3g
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  4533314b ! 1:  ea0c4dc2 Little Bobby Tables
    @@ lib/chkname.c
      login_name_max_size(void)
      {
     @@ lib/chkname.c: is_valid_name(const char *name)
    -    || strcaseeq(name, "all")
    -    || strcaseeq(name, "except")
    +    || strcaseeq(name, "all")  // access.conf(5)
    +    || strcaseeq(name, "except")  // access.conf(5)
         || strprefix(name, "-")
     -   || strpbrk(name, " !\"#&*+,/:;@|~")
     -   || strchriscntrl(name)
2:  ffc6a324 = 2:  10f5ba25 src/: Report errors in user or groups names more consistently
v3h
  • Rebase
$ git range-diff gh/nobadnames^^..gh/nobadnames chkname..nobadnames 
1:  ea0c4dc2 ! 1:  8ff4b426 Little Bobby Tables
    @@ lib/chkname.c
      login_name_max_size(void)
      {
     @@ lib/chkname.c: is_valid_name(const char *name)
    -    || strcaseeq(name, "all")  // access.conf(5)
         || strcaseeq(name, "except")  // access.conf(5)
    +    || strcaseeq(name, "none")    // access.conf(5)
         || strprefix(name, "-")
     -   || strpbrk(name, " !\"#&*+,/:;@|~")
     -   || strchriscntrl(name)
2:  10f5ba25 = 2:  d32e3419 src/: Report errors in user or groups names more consistently
v3i
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  8ff4b4260 = 1:  84a1d7141 Little Bobby Tables
2:  d32e3419b = 2:  8fe59f265 src/: Report errors in user or groups names more consistently
v4
  • Rebase
$ git range-diff alx/chkname..gh/nobadnames chkname..nobadnames 
1:  84a1d7141 ! 1:  748add817 Little Bobby Tables
    @@ lib/chkname.c
     @@ lib/chkname.c: is_valid_name(const char *name)
         || strcaseeq(name, "except")  // access.conf(5)
         || strcaseeq(name, "none")    // access.conf(5)
    -    || strprefix(name, "-")
    +    || strspn(name, "-")
     -   || strpbrk(name, " !\"#&*+,/:;@|~")
     -   || strchriscntrl(name)
         || strisdigit(name))
2:  8fe59f265 = 2:  d96decf8d src/: Report errors in user or groups names more consistently

@alejandro-colomar alejandro-colomar changed the title lib/, man/, src/: Do not allow bad names Little Bobby Tables Dec 9, 2024
@alejandro-colomar alejandro-colomar force-pushed the nobadnames branch 2 times, most recently from ca3d0f2 to df6999b Compare December 9, 2024 22:44
@hallyn
Copy link
Member

hallyn commented Dec 10, 2024

I'd prefer aliasing --allow-badnames to --allow-unsafe-names. (But keeping the old name so anyone using it doesn't get their scripts broken).

Calling it allow-unsafe-names gives the clear message that we don't advise doing it. But I don't think it's our place to try to force people to stop.

If shadow itself is breaking on such names, then we should fix those places in shadow. The conern is that other userspace will break on the unsafe names. If someone makes a very tight full system where they're convinced unsafe names are fine for them, that's their prerogative.

@zeha
Copy link
Contributor

zeha commented Dec 10, 2024

I'd prefer aliasing --allow-badnames to --allow-unsafe-names. (But keeping the old name so anyone using it doesn't get their scripts broken).

Calling it allow-unsafe-names gives the clear message that we don't advise doing it. But I don't think it's our place to try to force people to stop.

If shadow itself is breaking on such names, then we should fix those places in shadow.

I agree whatever breaks in shadow should be fixed in shadow. It might be debatable what will be called broken though.

  • useradd -m --badname ../foo? (this makes a user called ../foo with a homedir of /home/../foo, resulting in /foo existing)
  • useradd -m --badname "$(printf '\n')"? (this makes a user with a zero-length name; cannot be deleted with deluser "$(printf '\n')")

The conern is that other userspace will break on the unsafe names. If someone makes a very tight full system where they're convinced unsafe names are fine for them, that's their prerogative.

I agree, but I see it as not pushing the rest of userspace forward to a world where more names will be allowed in general.

@alejandro-colomar
Copy link
Collaborator Author

If shadow itself is breaking on such names, then we should fix those places in shadow.

That's easier said than done. If one insists on using characters such as a newline, can we really "work"?

root@359f6c0bddb0:/# cp /etc/passwd /tmp/passwd
root@359f6c0bddb0:/# useradd --badname '\n'
root@359f6c0bddb0:/# diff -u /tmp/passwd /etc/passwd
--- /tmp/passwd	2024-12-10 22:56:06.914745615 +0000
+++ /etc/passwd	2024-12-10 22:56:57.662149407 +0000
@@ -16,3 +16,4 @@
 irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
 _apt:x:42:65534::/nonexistent:/usr/sbin/nologin
 nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
+\n:x:1000:1000::/home/\n:/bin/sh
root@359f6c0bddb0:/# useradd --badname '^M'
root@359f6c0bddb0:/# diff -u /tmp/passwd /etc/passwd
--- /tmp/passwd	2024-12-10 22:56:06.914745615 +0000
+++ /etc/passwd	2024-12-10 22:57:43.377648075 +0000
@@ -16,3 +16,5 @@
 irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
 _apt:x:42:65534::/nonexistent:/usr/sbin/nologin
 nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
+\n:x:1000:1000::/home/\n:/bin/sh
:/bin/sh1001::/home/
root@359f6c0bddb0:/# useradd --badname ':,'
useradd: failure while writing changes to /etc/passwd
root@359f6c0bddb0:/# useradd --badname ':'
useradd: failure while writing changes to /etc/passwd
root@359f6c0bddb0:/# useradd --badname ','
root@359f6c0bddb0:/# diff -u /tmp/passwd /etc/passwd
--- /tmp/passwd	2024-12-10 22:56:06.914745615 +0000
+++ /etc/passwd	2024-12-10 22:58:45.041004746 +0000
@@ -16,3 +16,6 @@
 irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
 _apt:x:42:65534::/nonexistent:/usr/sbin/nologin
 nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
+\n:x:1000:1000::/home/\n:/bin/sh
:/bin/sh1001::/home/
+,:x:1002:1002::/home/,:/bin/sh

Some names are bad, and some names are really bad.  '--badname' should
only allow the mildly bad ones, which we can handle.  Some names are too
bad, and it's not possible to deal with them.  Reject them
unconditionally.

Acked-by: Chris Hofstaedtler <[email protected]>
Acked-by: Tobias Stoeckmann <[email protected]>
Cc: Marc 'Zugschlus' Haber <[email protected]>
Cc: Iker Pedrosa <[email protected]>
Cc: Serge Hallyn <[email protected]>
Signed-off-by: Alejandro Colomar <[email protected]>
This function returns true if the input character is a character from
the POSIX portable filename character set.

Link: <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap03.html#tag_03_265>
Signed-off-by: Alejandro Colomar <[email protected]>
In the first case, we can do the transformation because a few lines
above, we explicitly reject a name starting with a '-'.

In the second case, we're obviously using ispfchar() instead of its
pattern.

Signed-off-by: Alejandro Colomar <[email protected]>
-  Don't print the user name; if it's bad, it might be dangerous.
-  Print the string "user" or "group" before the error message.
-  Print the errno string to be consistent.

Signed-off-by: Alejandro Colomar <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Should we remove --badname?

3 participants