If your error value is already a legal row, a string compare will treat the error as that row.

musl's crypt() returns * when it cannot hash a password. Unix stores * to mean the account has no password. OpenSSH compared the two.

crypt() is supposed to return a hash, or NULL when it gives up. A lot of callers never check the NULL, so they crash.

In 2012 musl stopped returning NULL. Failure became one character that "can never match a real password." They picked *.

That character was taken. A * in the password field is the old Unix lock: not a hash, so nothing should match.

Bill Joy put one on the daemon account in a 1980 BSD passwd file. It is still how you spell "this account does not take a password."

OpenSSH will still run the compare. A 2016 timing fix (CVE-2016-6210) stopped answering instantly when a user had no hash, because the speed told you the user existed.

No salt of its own? Borrow another account's, hash anyway, then say no. The note says invalid users.

The same path runs for a real account whose field is *. A slower no, aimed at the lock.

PAM never makes that call. It sees the lock and does not ask crypt().

OpenSSH's own tests run on OpenBSD and glibc. musl does not test sshd.

Alpine's openssh-server is musl without PAM by default. That is the build where the two stars are the same string.

musl also refuses a password past a length cap, so one huge attempt cannot pin a CPU. Refusal is failure, and failure is *.

It is not every Alpine box. Password authentication has to still be on, some other account has to hold a real hash, and the attempt has to be one crypt() refuses.

Then the account that was not supposed to take a password takes one.

Alpine had picked that lock on purpose. Empty root passwords in 2019 (CVE-2019-5021) got locked with !.

Without PAM, ! also kills key login, and the cloud images need keys. Alpine 3.16 switched root to *.

Passwords off, keys on. The commit says so. The compare did not care.

  • A sentinel has to sit outside the values you actually store. "Looks fake" is not outside.
  • "Can never match a real hash" is the wrong set. You also store non-hashes, on purpose.
  • Replacing NULL with a string does not fix a missing check. It feeds the check.
  • A comment that says "invalid users" is not a branch. Read who calls it.
  • Test the libc you ship. The other project's suite will not.

glibc, anything going through PAM, Dropbear, and OpenBSD are not on this path. OpenSSH 10.6p2 fixes the one that is.

If that package is not on the box yet, turn password authentication off.

Failure and "locked" were the same bytes. == waved them in.

sshole

Discussion on Hacker News

This post was generated automatically from public Hacker News data.