yo_resp/dispatch/auth.rs
1//! `AUTH`, and the two lines a command refused for want of it is answered with.
2//!
3//! The passwords themselves are not here. They belong to users and users belong
4//! to the `acl` module, which is where `requirepass` lives too: setting it is a
5//! way of writing one rule on the user called `default`. What is left here is
6//! the command, because `AUTH` is a command like any other and the rest of the
7//! ACL is not.
8//!
9//! # Who starts out let in
10//!
11//! A connection carries a flag saying whether it has authenticated, and the
12//! flag is decided when the connection is accepted rather than when it sends
13//! its first command. A connection accepted while no password is set is let in
14//! at once, because the default user is `nopass` and there is nothing to ask
15//! it for, and it stays let in if a password is set later. A connection
16//! accepted while a password is set has to send `AUTH` first.
17//!
18//! That is a real server's rule and it is worth being clear about, because it
19//! is not the rule anybody would guess. `CONFIG SET requirepass` does not lock
20//! out the clients that are already connected, including the one that just set
21//! it, and it does lock out every client that connects after it.
22//!
23//! `RESET` puts the connection back to how it was accepted, and that includes
24//! this: a connection that authenticated and then sent `RESET` has to
25//! authenticate again on a server with a password, and does not on a server
26//! without one. It also puts the connection back on the default user, whatever
27//! it had authenticated as.
28
29use yo_common::{Code, Error, Result};
30
31use super::acl;
32use super::args::{self, Args, is};
33use super::{Server, Session};
34use crate::reply::Out;
35
36/// The one line a command refused for want of a password is answered with.
37///
38/// The whole line and not the part after the code, because it goes two places:
39/// straight into the reply, and spliced into the `EXECABORT` an `EXEC` gets, and
40/// the reference puts the code in both.
41pub(super) const NOAUTH: &str = "NOAUTH Authentication required.";
42
43/// The line `HELLO` gets instead, which says what to do about it.
44///
45/// A client that speaks RESP3 has to send `HELLO` before it can send `AUTH`, or
46/// it would be speaking RESP2 by the time it authenticated, so the reference
47/// spends a sentence here pointing at the option that solves it.
48pub(super) const HELLO_NOAUTH: &str = "NOAUTH HELLO must be called with the client already authenticated, otherwise the HELLO <proto> AUTH <user> <pass> option can be used to authenticate the client and select the RESP protocol version at the same time";
49
50/// `AUTH password` or `AUTH username password`.
51pub(super) fn execute(
52 server: &Server,
53 session: &mut Session,
54 args: Args<'_>,
55 out: &mut Out,
56) -> Result<()> {
57 // Arity is a minimum of two, so a third argument is where this stops being
58 // a command it knows and the reference calls that a syntax error rather
59 // than a wrong number of arguments.
60 if args.len() > 3 {
61 return Err(args::syntax());
62 }
63 let (user, password) = if args.len() == 3 {
64 (args.get(1), args.get(2))
65 } else {
66 (acl::DEFAULT, args.get(1))
67 };
68 if args.len() == 2 && !server.guarded() && is(user, acl::DEFAULT) {
69 // The one message here that is not about the password being wrong. A
70 // client that sends a one argument `AUTH` to a server with no password
71 // has almost certainly connected to the wrong server, so the reference
72 // says so at length rather than letting it through.
73 return Err(Error::new(
74 Code::Invalid,
75 "AUTH <password> called without any password configured for the default user. Are you sure your configuration is correct?",
76 ));
77 }
78 if !acl::authenticate(server, session, user, password, args, out) {
79 // Written straight into the buffer, because the code in front of it is
80 // what a client branches on and this is the only place in the engine
81 // that sends it. The same reason `NOPROTO` is written where it is
82 // decided.
83 out.error(b"WRONGPASS invalid username-password pair or user is disabled.");
84 return Ok(());
85 }
86 out.ok();
87 Ok(())
88}