ReviewOS

mail-os/mail

No command to change an existing account's password

#10
Open glennmichael123 opened this 8 days ago ·
8 days ago

There is no way to change an existing account's password. create and verify are the only actions on either command surface, so the only route to a password change is editing the database by hand.

Reproducing

On v0.3.12, probing both surfaces:

mail-server user:local create   -> Error: username is required        (recognised)
mail-server user:local verify   -> Error: username is required        (recognised)
mail-server user:local password -> Error: Too many arguments provided (not recognised)
mail-server user:local reset    -> Error: Too many arguments provided
mail-server user:local update   -> Error: Too many arguments provided

mail-server user create         -> Error: email address is required   (recognised)
mail-server user verify         -> Error: username is required        (recognised)
mail-server user password       -> Error: Too many arguments provided
mail-server user reset          -> Error: Too many arguments provided
mail-server user update         -> Error: Too many arguments provided

--help on user:local prints nothing, so the available actions are not discoverable either.

Why create cannot stand in for it

users.username is UNIQUE, so create on an existing account is rejected rather than upserting. An operator who wants to change a password has three options, and all of them are worse than a command:

  1. Delete the row and re-create it. imap_mailboxes has no foreign key to users and PRAGMA foreign_keys is 0, so the folder rows and the maildir do survive — but that is a property of the current schema rather than a guarantee, and nothing tells the operator it is safe. It reads like it will destroy the mailbox.
  2. Write a hash directly into the database, which means reproducing the argon2 parameters correctly by hand.
  3. Create a throwaway user, copy its password_hash onto the real row, delete the throwaway. This is the only one that uses the server's own hasher without touching the account row, and it takes four commands plus two raw SQL statements.

What this causes in practice

On a production install this led to someone doing a database-level password restore across three accounts at once. It rewrote the hashes for all three and locked those users out of mail they had been using, with no record of what was intended. Recovering it meant diffing the live database against a pre-change backup to work out which rows had moved.

A single supported command would have made that a non-event.

create and verify take the password as a positional argument, so every password-setting command is recorded in shell history. On the same install, a live mailbox password was sitting in /root/.bash_history in plaintext, months after the account was created, on a host that also accepts public SMTP.

Worth fixing alongside, since a new password command would inherit the same problem.

Proposal

mail-server user:local password <username> [password]
mail-server user password <username> [password]

With the password optional:

  • omitted, prompt on a TTY with no echo, or read one line from stdin when piped
  • provided as argv, keep it working for scripts, since that is what automation needs

The same treatment would suit create and verify. Reading from stdin is what lets an operator do pwgen | mail-server user:local password user@example.com - without the value ever reaching the shell's history.

Two things that would help regardless of whether the command lands:

  • make --help on user and user:local list their actions, so the surface is discoverable
  • have an unrecognised action say so, rather than Error: Too many arguments provided, which reads as a syntax problem with the arguments rather than an unknown subcommand

Sign in to comment on this issue.