title: What are the real rules for linux usernames on CentOS 6 and RHEL 6?
body: <p>I'm writing some web UI pages that can be used to create Linux user accounts.  This web UI will be used on CentOS 6 (which is derived from RHEL 6).  I'm finding inconsistent and incomplete information about what constitutes a valid Linux user name.  I went to the source, examining a Linux shadow-utils source package but I did not ensure that the version I was looking at is in fact the same as that which is part of CentOS 6.</p>
  
  <p>Below is the code fragment I currently use, which includes copy/paste of the comments from the shadow-utils package version 4.1.4.3, plus some of my own notes, and a Java regular expression search to follow my understanding from looking at shadow-utils source.</p>
  
  <p>The referenced "is_valid_name()" check in chkname.c is apparently not what is used from the useradd command on Linux, since the comments (and the C-code source) do not allow names beginning with a number.  However, useradd does allow one to create an account like "1234".</p>
  
  <p>I'd appreciate assistance adjusting from what I have now to what would be more correct, as well as info about how useradd.c is implemented with some slightly different is_valid_name function.</p>
  
  <p>Thanks!
  Alan</p>
  
  <pre><code>/**
   * Define constants for use in isNameLinuxCompatible(...) method.
   *
   * The source for the Linux compatible user name rule is is_valid_name(...) a function in the "shadow" package
   * for Linux.  The source file for that function has a comment as follows:
   *      User/group names must match [a-z_][a-z0-9_-]*[$]
   * That expression is a little loose/sloppy since
   * (1) the trailing $ sign is optional, and
   * (2) uppercase A-Z is also ok (and case is significant, 'A' != 'a').
   *
   * We deal with (1) by using the [$]? form where the ? means zero or more characters (aka "greedy").
   * We deal with (2) by using the CASE_INSENSITIVE option.
   *
   * Another way to express this is:
   *  1st character:                      a-z_         required at least one char
   *  chars other than first and last:    a-z0-9_-     optional
   *  last character:                     $            optional
   * Max length is 31.  Min length is 1.
   *
   * NOTE: The initial ^ and final $ below are important since we need the entire string to satisfy the rule,
   * from beginning to end.
   *
   * See http://download.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html for reference info on pattern matching.
   */
  
  private static final String  LINUX_USERNAME_REGEX     = "^[a-z_][a-z0-9_-]*[$]?$";
  private static final Pattern LINUX_USERNAME_PATTERN   = Pattern.compile(LINUX_USERNAME_REGEX, Pattern.CASE_INSENSITIVE);
  private static final int     LINUX_USERNAME_MINLENGTH = 1;
  private static final int     LINUX_USERNAME_MAXLENGTH = 31;
  
  /**
   * See if username is compatible with standard Linux rules for usernames, in terms of length and
   * in terms of content.
   *
   * @param username the name to be checked for validity
   * @return true if Linux compatible, else false
   */
  public boolean isNameLinuxCompatible (final String username) {
      boolean nameOK = false;
      if (username != null) {
          int len = username.length();
          if ((len &gt;= LINUX_USERNAME_MINLENGTH) &amp;&amp; (len &lt;= LINUX_USERNAME_MAXLENGTH)) {
              Matcher m = LINUX_USERNAME_PATTERN.matcher(username);
              nameOK = m.find();
          }
      }
      return (nameOK);
  }
  </code></pre>