Skip to main content

yo_resp/dispatch/
acl.rs

1//! Users, the rules that describe what each of them may do, and the gate that
2//! holds every command to them.
3//!
4//! # What an ACL actually is
5//!
6//! A user is a name, a switch saying whether it is usable at all, a list of
7//! password hashes, and one or more selectors. A selector is the interesting
8//! part: a set of commands, a set of key patterns and a set of channel patterns.
9//! A command is allowed if any one selector allows the command and every key and
10//! channel it touches. Selectors are tried in order and the first that says yes
11//! wins, which is how a user can be given two unrelated jobs without either of
12//! them leaking into the other. Without them the only way to say "read anything
13//! under `cache:` and write anything under `job:`" is to allow reads and writes
14//! over both.
15//!
16//! Every user starts with one selector, called the root selector here and in
17//! Redis, which is the one the bare rules land in: `ACL SETUSER u +get ~k:*`
18//! writes into it, and `ACL SETUSER u (+get ~k:*)` adds another one beside it.
19//!
20//! # Why the rules are kept as text as well as bits
21//!
22//! Command permission is a bitmap, one bit a command, because the gate reads it
23//! on every command a guarded server runs and a set of names would mean a lookup
24//! there. But `ACL LIST` and `ACL GETUSER` have to hand back something that can
25//! be fed to `ACL SETUSER` and produce the same user, and a bitmap cannot say
26//! whether `+@read -get` or the eleven names that leaves was what the operator
27//! wrote. Worse, the two are not the same user: the first allows a command added
28//! to the `@read` category later and the second does not.
29//!
30//! So a selector keeps both. The bitmap is what the gate reads and the rule list
31//! is what describes it, and every rule that touches commands appends itself to
32//! the list after removing whatever it overrides. That is Redis's design and it
33//! is the reason `ACL GETUSER` on a user built with categories reads back in
34//! categories. The removal is the fiddly half and it is spelled out at
35//! [`Selector::note`].
36//!
37//! # The one thing here that is not Redis's
38//!
39//! Redis gives every subcommand of a container command its own identity and its
40//! own bit, so `+config|get` sets a bit and `+config|nope` is refused because
41//! there is no such command. There is no subcommand table here yet, which is
42//! D-114, so both go through the mechanism Redis keeps for the other case: a
43//! list of first arguments a command is allowed with. The gate then reaches the
44//! same answer for every rule an operator would actually write, and the
45//! difference is that `+config|nope` is accepted here and refused there. It is
46//! written down as D-137 rather than papered over, and it goes away with the
47//! subcommand table.
48//!
49//! # Where the gate sits
50//!
51//! Right after the password and the transaction refusal and right before the
52//! memory limit, which is where `processCommand` puts it. That ordering is
53//! visible: a command a user is not allowed to run is refused before the server
54//! decides whether it is out of memory, and a command with the wrong number of
55//! arguments is told that rather than told it is not allowed.
56
57use std::sync::Mutex;
58use std::sync::atomic::Ordering::{Acquire, Relaxed, Release};
59use std::sync::atomic::{AtomicBool, AtomicU64};
60
61use yo_common::{Code, Error, Result, glob_matches, sha256};
62
63use super::args::{self, Args, is};
64use super::keyspec::{self, Access};
65use super::table::{self, Spec};
66use super::{Server, Session};
67use crate::reply::Out;
68
69/// The user every connection is on until it says otherwise.
70pub(super) const DEFAULT: &[u8] = b"default";
71
72/// The categories `ACL CAT` lists, in the order it lists them.
73///
74/// The first twenty two are Redis's own list in Redis's own order, which is the
75/// order they are declared in `server.h` and not alphabetical. The nine after
76/// them are the module surfaces this engine has built in. A real server with
77/// those modules loaded reports them here too, so the shape is right even though
78/// a bare server answers twenty two.
79const CATEGORIES: [&str; 31] = [
80    "keyspace",
81    "read",
82    "write",
83    "set",
84    "sortedset",
85    "list",
86    "hash",
87    "string",
88    "array",
89    "bitmap",
90    "hyperloglog",
91    "geo",
92    "stream",
93    "pubsub",
94    "admin",
95    "fast",
96    "slow",
97    "blocking",
98    "dangerous",
99    "connection",
100    "transaction",
101    "scripting",
102    "bloom",
103    "cms",
104    "cuckoo",
105    "graph",
106    "json",
107    "search",
108    "tdigest",
109    "timeseries",
110    "topk",
111];
112
113/// How many words of bitmap it takes to give every command a bit.
114const WORDS: usize = table::count().div_ceil(64);
115
116/// The read bit on a key pattern, which is what `%R~` sets.
117const READ: u8 = 1;
118/// The write bit, which is what `%W~` sets.
119const WRITE: u8 = 2;
120
121/// A key pattern and what it may be used for.
122#[derive(Debug, Clone, PartialEq, Eq)]
123struct Pattern {
124    /// [`READ`], [`WRITE`] or both.
125    flags: u8,
126    /// The glob itself, without the sigil.
127    glob: Vec<u8>,
128}
129
130impl Pattern {
131    /// The pattern written the way `ACL LIST` writes it.
132    ///
133    /// A pattern good for both is written bare, because `%RW~x` and `~x` are the
134    /// same thing and the bare form is the one everybody types. A pattern good
135    /// for one keeps its sigil, because there is no other way to say it.
136    fn describe(&self, into: &mut Vec<u8>) {
137        match self.flags {
138            READ => into.extend_from_slice(b"%R~"),
139            WRITE => into.extend_from_slice(b"%W~"),
140            _ => into.push(b'~'),
141        }
142        into.extend_from_slice(&self.glob);
143    }
144}
145
146/// One set of commands, keys and channels, which is what a permission check runs
147/// against.
148#[derive(Debug, Clone)]
149struct Selector {
150    /// One bit a command, indexed by its row in the table.
151    allowed: [u64; WORDS],
152    /// Whether every command is allowed without looking, which `+@all` sets and
153    /// taking any command away clears.
154    all_commands: bool,
155    /// Whether a command that does not exist yet would be allowed.
156    ///
157    /// Redis keeps this as a reserved bit past the end of the bitmap, set by
158    /// `+@all` and cleared by `-@all` and by nothing else, so that `+@all -get`
159    /// and `-@all +everything-but-get` can be told apart. They describe back
160    /// differently and they behave differently the day a command is added.
161    future: bool,
162    /// Whether every key is allowed.
163    all_keys: bool,
164    /// Whether every channel is allowed.
165    all_channels: bool,
166    /// The command rules in the order they were given, space separated.
167    ///
168    /// Not the bitmap written out. See the module note for why the two are both
169    /// kept.
170    rules: Vec<u8>,
171    /// The first arguments a command is allowed with, when it is not allowed
172    /// outright. Sorted by command index.
173    firstargs: Vec<(u16, Vec<Vec<u8>>)>,
174    /// The first arguments a container command is refused with, when it is
175    /// allowed outright. Sorted by command index.
176    ///
177    /// This is how `+config -config|get` is held. A server with a row a
178    /// subcommand has does it by clearing that row's bit, and the day yo has one
179    /// too this goes away with the rest of D-114.
180    deniedfirst: Vec<(u16, Vec<Vec<u8>>)>,
181    /// The key patterns, in the order they were added.
182    patterns: Vec<Pattern>,
183    /// The channel patterns, in the order they were added.
184    channels: Vec<Vec<u8>>,
185}
186
187impl Selector {
188    /// A selector that allows nothing, which is where every user starts.
189    fn new() -> Selector {
190        Selector {
191            allowed: [0; WORDS],
192            all_commands: false,
193            future: false,
194            all_keys: false,
195            all_channels: false,
196            rules: Vec::new(),
197            firstargs: Vec::new(),
198            deniedfirst: Vec::new(),
199            patterns: Vec::new(),
200            channels: Vec::new(),
201        }
202    }
203
204    /// Whether the command at `i` is allowed outright.
205    fn bit(&self, i: usize) -> bool {
206        i < table::count() && self.allowed[i / 64] & (1 << (i % 64)) != 0
207    }
208
209    /// Allow or refuse the command at `i`, and forget any first arguments it had.
210    ///
211    /// Taking anything away clears `all_commands`, because that flag is only
212    /// there to let the gate skip the bitmap and it can no longer do that.
213    fn set(&mut self, i: usize, allow: bool) {
214        if i >= table::count() {
215            return;
216        }
217        if allow {
218            self.allowed[i / 64] |= 1 << (i % 64);
219        } else {
220            self.allowed[i / 64] &= !(1 << (i % 64));
221            self.all_commands = false;
222        }
223        self.firstargs.retain(|(at, _)| usize::from(*at) != i);
224        self.deniedfirst.retain(|(at, _)| usize::from(*at) != i);
225    }
226
227    /// Record a command rule, dropping whatever it overrides.
228    ///
229    /// The rule that goes on the end is the one that wins, so anything it makes
230    /// irrelevant has to come off first or the list would grow forever and
231    /// describe the user wrongly. Two things are irrelevant: the same rule
232    /// written before, and, when this rule names a whole command, any earlier
233    /// rule about one of its subcommands. `+get` after `-get|foo` leaves `+get`
234    /// alone, because the subcommand rule cannot survive its parent being
235    /// decided again.
236    ///
237    /// Note that a rule is matched on the name and not on the sign, so `+get`
238    /// removes an earlier `-get` rather than sitting after it.
239    fn note(&mut self, rule: &[u8], allow: bool) {
240        let mut kept: Vec<u8> = Vec::with_capacity(self.rules.len() + rule.len() + 2);
241        for old in self.rules.split(|b| *b == b' ') {
242            if old.is_empty() {
243                continue;
244            }
245            // The sign is not part of the name and is not compared.
246            let name = &old[1..];
247            let same = name == rule;
248            let child =
249                name.len() > rule.len() && name.starts_with(rule) && name[rule.len()] == b'|';
250            if same || child {
251                continue;
252            }
253            if !kept.is_empty() {
254                kept.push(b' ');
255            }
256            kept.extend_from_slice(old);
257        }
258        if !kept.is_empty() {
259            kept.push(b' ');
260        }
261        kept.push(if allow { b'+' } else { b'-' });
262        kept.extend_from_slice(rule);
263        self.rules = kept;
264    }
265
266    /// Allow this command only when its first argument is `first`.
267    fn allow_first(&mut self, i: u16, first: &[u8]) {
268        let lower = first.to_ascii_lowercase();
269        match self.firstargs.binary_search_by_key(&i, |(at, _)| *at) {
270            Ok(at) => {
271                let list = &mut self.firstargs[at].1;
272                if !list.contains(&lower) {
273                    list.push(lower);
274                }
275            }
276            Err(at) => self.firstargs.insert(at, (i, vec![lower])),
277        }
278    }
279
280    /// The first arguments the command at `i` is allowed with.
281    fn firsts(&self, i: u16) -> &[Vec<u8>] {
282        match self.firstargs.binary_search_by_key(&i, |(at, _)| *at) {
283            Ok(at) => &self.firstargs[at].1,
284            Err(_) => &[],
285        }
286    }
287
288    /// Refuse this command when its first argument is `first`.
289    ///
290    /// This clears `all_commands` for the same reason [`Selector::set`] does:
291    /// the flag is only there so the gate can skip the bitmap, and once one
292    /// subcommand is spoken for it can no longer skip anything.
293    fn deny_first(&mut self, i: u16, first: &[u8]) {
294        let lower = first.to_ascii_lowercase();
295        self.all_commands = false;
296        match self.deniedfirst.binary_search_by_key(&i, |(at, _)| *at) {
297            Ok(at) => {
298                let list = &mut self.deniedfirst[at].1;
299                if !list.contains(&lower) {
300                    list.push(lower);
301                }
302            }
303            Err(at) => self.deniedfirst.insert(at, (i, vec![lower])),
304        }
305    }
306
307    /// The first arguments the command at `i` is refused with.
308    fn denied(&self, i: u16) -> &[Vec<u8>] {
309        match self.deniedfirst.binary_search_by_key(&i, |(at, _)| *at) {
310            Ok(at) => &self.deniedfirst[at].1,
311            Err(_) => &[],
312        }
313    }
314
315    /// Everything a rule about commands could have set, back to nothing.
316    fn reset_commands(&mut self, all: bool) {
317        self.allowed = [if all { u64::MAX } else { 0 }; WORDS];
318        self.all_commands = all;
319        self.future = all;
320        self.rules.clear();
321        self.firstargs.clear();
322        self.deniedfirst.clear();
323    }
324
325    /// The command rules written the way `ACL SETUSER` would take them back.
326    ///
327    /// Always starts with `+@all` or `-@all` and then repeats the rule list,
328    /// which is exactly what was fed in after the last time it was cleared. That
329    /// is why this needs no cleverness: the list is already the answer.
330    fn describe_commands(&self) -> Vec<u8> {
331        let mut out = Vec::with_capacity(self.rules.len() + 8);
332        out.extend_from_slice(if self.future { b"+@all" } else { b"-@all" });
333        if !self.rules.is_empty() {
334            out.push(b' ');
335            out.extend_from_slice(&self.rules);
336        }
337        out
338    }
339
340    /// The key patterns, space separated, or `~*`.
341    fn describe_keys(&self) -> Vec<u8> {
342        let mut out = Vec::new();
343        if self.all_keys {
344            out.extend_from_slice(b"~*");
345            return out;
346        }
347        for pattern in &self.patterns {
348            if !out.is_empty() {
349                out.push(b' ');
350            }
351            pattern.describe(&mut out);
352        }
353        out
354    }
355
356    /// The channel patterns, space separated, or `&*`.
357    fn describe_channels(&self) -> Vec<u8> {
358        let mut out = Vec::new();
359        if self.all_channels {
360            out.extend_from_slice(b"&*");
361            return out;
362        }
363        for channel in &self.channels {
364            if !out.is_empty() {
365                out.push(b' ');
366            }
367            out.push(b'&');
368            out.extend_from_slice(channel);
369        }
370        out
371    }
372
373    /// The whole selector as `ACL LIST` writes it inside a user's line.
374    ///
375    /// Keys first, then channels, then commands. The `resetchannels` in front of
376    /// the channel list is not decoration: without it a line read back on a
377    /// server whose `acl-pubsub-default` is `allchannels` would start from every
378    /// channel allowed and the `&x` rules would add nothing.
379    fn describe(&self) -> Vec<u8> {
380        let mut out = self.describe_keys();
381        if !out.is_empty() {
382            out.push(b' ');
383        }
384        if self.all_channels {
385            out.extend_from_slice(b"&* ");
386        } else {
387            out.extend_from_slice(b"resetchannels ");
388            for channel in &self.channels {
389                out.push(b'&');
390                out.extend_from_slice(channel);
391                out.push(b' ');
392            }
393        }
394        out.extend_from_slice(&self.describe_commands());
395        out
396    }
397}
398
399/// What one of these is allowed to say went wrong with a rule.
400///
401/// The sentences are Redis's, word for word, because an operator reading one has
402/// almost certainly found it in Redis's documentation first.
403#[derive(Debug, Clone, Copy, PartialEq, Eq)]
404enum Bad {
405    /// The rule is not one, or is malformed.
406    Syntax,
407    /// A command or category nobody has heard of.
408    Unknown,
409    /// A key pattern after `~*`.
410    AfterAllKeys,
411    /// A channel pattern after `&*`.
412    AfterAllChannels,
413    /// `<password` for a password the user has not got.
414    NoSuchPassword,
415    /// A `#` hash that is not sixty four hexadecimal characters.
416    Hash,
417    /// `+get|set|other`, which Redis has never supported.
418    NestedFirstArg,
419    /// A `(` with no `)`, which is reported differently from all the rest.
420    Unmatched,
421}
422
423impl Bad {
424    /// The sentence after `Error in ACL SETUSER modifier '<rule>': `.
425    fn text(self) -> &'static str {
426        match self {
427            Bad::Syntax | Bad::Unmatched => "Syntax error",
428            Bad::Unknown => "Unknown command or category name in ACL",
429            Bad::AfterAllKeys => {
430                "Adding a pattern after the * pattern (or the 'allkeys' flag) is not valid and does not have any effect. Try 'resetkeys' to start with an empty list of patterns"
431            }
432            Bad::AfterAllChannels => {
433                "Adding a pattern after the * pattern (or the 'allchannels' flag) is not valid and does not have any effect. Try 'resetchannels' to start with an empty list of channels"
434            }
435            Bad::NoSuchPassword => {
436                "The password you are trying to remove from the user does not exist"
437            }
438            Bad::Hash => {
439                "The password hash must be exactly 64 characters and contain only lowercase hexadecimal characters"
440            }
441            Bad::NestedFirstArg => "Allowing first-arg of a subcommand is not supported",
442        }
443    }
444}
445
446/// One account, and everything it is allowed to do.
447#[derive(Debug, Clone)]
448pub(crate) struct User {
449    /// The name, which is also the key it is filed under.
450    name: Vec<u8>,
451    /// Whether it may authenticate at all.
452    enabled: bool,
453    /// Whether any password gets in, which is what `nopass` means.
454    nopass: bool,
455    /// Whether `DEBUG` payload sanitising is skipped for this user.
456    ///
457    /// Nothing reads it here yet, and it is kept and reported because it is on
458    /// every `ACL LIST` line a real server writes and a config file round trip
459    /// that dropped it would quietly change the user.
460    skip_sanitize: bool,
461    /// The SHA-256 hashes of the passwords that get in, lower case hex.
462    passwords: Vec<[u8; 64]>,
463    /// The root selector first and any others after it.
464    selectors: Vec<Selector>,
465}
466
467impl User {
468    /// A new user, off, with no password and allowed nothing.
469    fn new(name: &[u8]) -> User {
470        User {
471            name: name.to_vec(),
472            enabled: false,
473            nopass: false,
474            skip_sanitize: false,
475            passwords: Vec::new(),
476            selectors: vec![Selector::new()],
477        }
478    }
479
480    /// The user a server with no `aclfile` starts with, which can do anything.
481    fn default_user() -> User {
482        let mut user = User::new(DEFAULT);
483        user.enabled = true;
484        user.nopass = true;
485        let root = &mut user.selectors[0];
486        root.reset_commands(true);
487        root.all_keys = true;
488        root.all_channels = true;
489        user
490    }
491
492    /// The flag words `ACL LIST` and `ACL GETUSER` print, in Redis's order.
493    fn flags(&self) -> Vec<&'static str> {
494        let mut out = Vec::with_capacity(3);
495        out.push(if self.enabled { "on" } else { "off" });
496        if self.nopass {
497            out.push("nopass");
498        }
499        // Exactly one of the two is always set, because a user is created with
500        // the sanitising one on and the rules only ever swap them.
501        out.push(if self.skip_sanitize {
502            "skip-sanitize-payload"
503        } else {
504            "sanitize-payload"
505        });
506        out
507    }
508
509    /// The whole user as one `ACL LIST` line, without the leading `user `.
510    fn describe(&self) -> Vec<u8> {
511        let mut out = Vec::with_capacity(64);
512        out.extend_from_slice(&self.name);
513        for flag in self.flags() {
514            out.push(b' ');
515            out.extend_from_slice(flag.as_bytes());
516        }
517        for hash in &self.passwords {
518            out.extend_from_slice(b" #");
519            out.extend_from_slice(hash);
520        }
521        for (at, selector) in self.selectors.iter().enumerate() {
522            out.push(b' ');
523            if at == 0 {
524                out.extend_from_slice(&selector.describe());
525            } else {
526                out.push(b'(');
527                out.extend_from_slice(&selector.describe());
528                out.push(b')');
529            }
530        }
531        out
532    }
533
534    /// Whether nothing this user could be asked to do would be refused.
535    ///
536    /// One selector that allows every command, every key and every channel, and
537    /// no other selectors, because a second one could only ever allow more and
538    /// the first already allows everything. That is the shape the default user
539    /// has on a server nobody has written an ACL for.
540    fn unrestricted(&self) -> bool {
541        self.selectors.len() == 1
542            && self.selectors[0].all_commands
543            && self.selectors[0].all_keys
544            && self.selectors[0].all_channels
545    }
546
547    /// Whether this password gets in.
548    ///
549    /// Every hash is compared whatever the first one said, so the number of
550    /// passwords a user has is not readable from how long a wrong guess took.
551    /// The hashing has already thrown away everything about the guess that a
552    /// timing difference could leak, so this is belt and braces, and it costs
553    /// one compare of thirty two bytes a password.
554    fn admits(&self, password: &[u8]) -> bool {
555        if !self.enabled {
556            return false;
557        }
558        if self.nopass {
559            return true;
560        }
561        let guess = sha256::hex(password);
562        let mut hit = false;
563        for hash in &self.passwords {
564            hit |= same(hash, &guess);
565        }
566        hit
567    }
568}
569
570/// Whether two byte strings are equal, in time that does not depend on where
571/// they stop being equal.
572fn same(a: &[u8], b: &[u8]) -> bool {
573    let mut diff = u8::from(a.len() != b.len());
574    for i in 0..a.len().max(b.len()) {
575        diff |= a.get(i).copied().unwrap_or(0) ^ b.get(i).copied().unwrap_or(0xff);
576    }
577    diff == 0
578}
579
580/// Every user on the server, and a counter saying when one last changed.
581///
582/// One lock over the lot, because every path that writes here is an operator
583/// typing a command and every path that reads is either the same or a
584/// connection authenticating. The command gate does not come through here at
585/// all: it holds a clone of the user it is running as, refreshed when the
586/// counter moves, which is what keeps a guarded server's hot path free of this
587/// lock. See [`Session::acl_user`].
588#[derive(Debug)]
589pub(crate) struct Users {
590    /// The users, sorted by name, which is the order `ACL LIST` reports.
591    ///
592    /// Redis keeps them in a radix tree and walks it in order, so the sorting is
593    /// not a nicety, it is the observable order of two commands.
594    table: Mutex<Vec<User>>,
595    /// Bumped whenever anything in the table changes.
596    generation: AtomicU64,
597    /// Whether a connection has to authenticate before it can do anything.
598    guarded: AtomicBool,
599    /// Whether any user here could be refused any command.
600    ///
601    /// The gate reads this and nothing else on a server nobody has written an
602    /// ACL for, which is every server that only ever set `requirepass` and every
603    /// server that did not even do that. Setting a password does not make it
604    /// true: the default user still has every permission, so no command can be
605    /// refused once a connection is past the password.
606    restricted: AtomicBool,
607}
608
609impl Default for Users {
610    fn default() -> Users {
611        Users {
612            table: Mutex::new(vec![User::default_user()]),
613            generation: AtomicU64::new(1),
614            guarded: AtomicBool::new(false),
615            restricted: AtomicBool::new(false),
616        }
617    }
618}
619
620impl Users {
621    /// Run `f` over the table, and say that it changed if `f` says so.
622    ///
623    /// The two summaries are recomputed here rather than at every call site,
624    /// which is the point of routing every write through one function: there is
625    /// exactly one place that can leave them disagreeing with the table.
626    fn with<T>(&self, f: impl FnOnce(&mut Vec<User>) -> (bool, T)) -> T {
627        let mut held = self.table.lock().unwrap_or_else(|e| e.into_inner());
628        let (changed, out) = f(&mut held);
629        if changed {
630            let guarded = held
631                .binary_search_by(|u| u.name.as_slice().cmp(DEFAULT))
632                .is_ok_and(|at| !held[at].nopass || !held[at].enabled);
633            self.guarded.store(guarded, Relaxed);
634            self.restricted
635                .store(held.iter().any(|u| !u.unrestricted()), Relaxed);
636            // Released after the change and acquired before a reader looks at
637            // its copy, so a session that sees a new number sees the table that
638            // goes with it.
639            self.generation.fetch_add(1, Release);
640        }
641        out
642    }
643
644    /// A copy of the user called `name`, if there is one.
645    fn get(&self, name: &[u8]) -> Option<User> {
646        self.with(|table| {
647            let found = table
648                .binary_search_by(|u| u.name.as_slice().cmp(name))
649                .ok()
650                .map(|at| table[at].clone());
651            (false, found)
652        })
653    }
654
655    /// The number the session's copy of a user is stamped with.
656    fn generation(&self) -> u64 {
657        self.generation.load(Acquire)
658    }
659}
660
661impl Server {
662    /// Whether this server asks connections for a password.
663    ///
664    /// True when the default user has one, which is exactly what `requirepass`
665    /// means: a server whose default user is `nopass` lets an unauthenticated
666    /// connection straight through as that user, and a server whose default user
667    /// has a password does not.
668    #[must_use]
669    pub(crate) fn guarded(&self) -> bool {
670        self.acl.guarded.load(Relaxed)
671    }
672
673    /// Whether the gate has to ask the ACL anything at all.
674    #[must_use]
675    pub(crate) fn restricted(&self) -> bool {
676        self.acl.restricted.load(Relaxed)
677    }
678
679    /// Set or clear the default user's password, where an empty one clears it.
680    ///
681    /// This is the whole of `CONFIG SET requirepass`, and on a real server it is
682    /// the whole of it too: the config option is a way of writing one rule on
683    /// one user. Setting it drops any password the default user already had,
684    /// which is what the reference does and is worth knowing, because it means
685    /// `requirepass` and `ACL SETUSER default >pw` fight rather than add up.
686    pub fn set_password(&self, password: &[u8]) {
687        self.acl.with(|table| {
688            let Ok(at) = table.binary_search_by(|u| u.name.as_slice().cmp(DEFAULT)) else {
689                return (false, ());
690            };
691            let user = &mut table[at];
692            user.passwords.clear();
693            if password.is_empty() {
694                user.nopass = true;
695            } else {
696                user.nopass = false;
697                user.passwords.push(sha256::hex(password));
698            }
699            (true, ())
700        });
701        self.plain.set(password);
702    }
703
704    /// Hand the plain `requirepass` to `f`, which is how `CONFIG GET` writes it.
705    pub(crate) fn with_password<T>(&self, f: impl FnOnce(&[u8]) -> T) -> T {
706        self.plain.with(f)
707    }
708
709    /// The users, for the `ACL` command and for authentication.
710    pub(crate) fn users(&self) -> &Users {
711        &self.acl
712    }
713}
714
715/// The plain `requirepass`, kept beside the hash because `CONFIG GET` reports it.
716///
717/// A real server does the same and it is worth being explicit about why, because
718/// it looks like the hashing was pointless. It is not: the hash is what an ACL
719/// user's password is, and the config file and `ACL LIST` and `ACL GETUSER` all
720/// name it rather than this. This copy exists for one command, `CONFIG GET
721/// requirepass`, which a real server answers with the password itself, and a
722/// server that answered a hash there would break every tool that reads its own
723/// config back.
724#[derive(Debug, Default)]
725pub(crate) struct Plain {
726    /// Empty when there is no `requirepass`.
727    secret: Mutex<Vec<u8>>,
728}
729
730impl Plain {
731    /// Remember the password `CONFIG SET requirepass` was given.
732    fn set(&self, password: &[u8]) {
733        let mut held = self.secret.lock().unwrap_or_else(|e| e.into_inner());
734        held.clear();
735        held.extend_from_slice(password);
736    }
737
738    /// Hand it to `f`, borrowed rather than copied so it lives in one place.
739    fn with<T>(&self, f: impl FnOnce(&[u8]) -> T) -> T {
740        let held = self.secret.lock().unwrap_or_else(|e| e.into_inner());
741        f(&held)
742    }
743}
744
745/// Let this connection in as `user` if the password is right, and say whether
746/// it did.
747///
748/// Answers the same thing for a user that does not exist and a user whose
749/// password was wrong, on purpose: the difference between the two is a list of
750/// user names.
751///
752/// The generation is read before the copy is taken rather than after. A write
753/// that lands in between then leaves the session stamped with a number that is
754/// too small, so its next command takes a fresh copy for nothing, and the other
755/// order would leave it stamped with a number that is too large and holding a
756/// user whose rules had already changed.
757pub(super) fn authenticate(
758    server: &Server,
759    session: &mut Session,
760    user: &[u8],
761    password: &[u8],
762) -> bool {
763    let stamp = server.acl.generation();
764    let Some(found) = server.acl.get(user) else {
765        return false;
766    };
767    if !found.admits(password) {
768        return false;
769    }
770    session.become_user(stamp, found);
771    session.admit(true);
772    true
773}
774
775// ------------------------------------------------------------------ the rules
776
777/// Apply one rule to `user`, which is `ACLSetUser` written out.
778///
779/// The user level rules are here and everything else falls through to the
780/// selector. That split is Redis's and it is why `ACL SETUSER u (on)` is a
781/// syntax error: `on` is a fact about the account and a selector is a set of
782/// permissions, so there is nowhere in a selector to put it.
783fn set_user(user: &mut User, rule: &[u8]) -> std::result::Result<(), Bad> {
784    if rule.is_empty() {
785        return Ok(());
786    }
787    if word(rule, b"on") {
788        user.enabled = true;
789    } else if word(rule, b"off") {
790        user.enabled = false;
791    } else if word(rule, b"skip-sanitize-payload") {
792        user.skip_sanitize = true;
793    } else if word(rule, b"sanitize-payload") {
794        user.skip_sanitize = false;
795    } else if word(rule, b"nopass") {
796        user.nopass = true;
797        user.passwords.clear();
798    } else if word(rule, b"resetpass") {
799        user.nopass = false;
800        user.passwords.clear();
801    } else if rule[0] == b'>' || rule[0] == b'#' {
802        let hash = hash_of(rule)?;
803        if !user.passwords.contains(&hash) {
804            user.passwords.push(hash);
805        }
806        // A user with a password is not a `nopass` user, whatever it was.
807        user.nopass = false;
808    } else if rule[0] == b'<' || rule[0] == b'!' {
809        let hash = hash_of(rule)?;
810        let before = user.passwords.len();
811        user.passwords.retain(|held| *held != hash);
812        if user.passwords.len() == before {
813            return Err(Bad::NoSuchPassword);
814        }
815    } else if rule[0] == b'(' && rule[rule.len() - 1] == b')' {
816        let mut selector = Selector::new();
817        for word in split(&rule[1..rule.len() - 1]) {
818            set_selector(&mut selector, &word)?;
819        }
820        user.selectors.push(selector);
821    } else if rule[0] == b'(' {
822        return Err(Bad::Unmatched);
823    } else if word(rule, b"clearselectors") {
824        user.selectors.truncate(1);
825    } else if word(rule, b"reset") {
826        let name = std::mem::take(&mut user.name);
827        *user = User::new(&name);
828    } else {
829        return set_selector(&mut user.selectors[0], rule);
830    }
831    Ok(())
832}
833
834/// The hash a `>`, `#`, `<` or `!` rule names.
835///
836/// The first form of each pair is a password to hash and the second is a hash
837/// already, and the only difference between them is whether the sixty four
838/// characters are checked or produced.
839fn hash_of(rule: &[u8]) -> std::result::Result<[u8; 64], Bad> {
840    let rest = &rule[1..];
841    if rule[0] == b'>' || rule[0] == b'<' {
842        return Ok(sha256::hex(rest));
843    }
844    let ok = rest.len() == 64
845        && rest
846            .iter()
847            .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(b));
848    if !ok {
849        return Err(Bad::Hash);
850    }
851    let mut hash = [0u8; 64];
852    hash.copy_from_slice(rest);
853    Ok(hash)
854}
855
856/// Apply one rule to a selector, which is `ACLSetSelector` written out.
857fn set_selector(selector: &mut Selector, rule: &[u8]) -> std::result::Result<(), Bad> {
858    if word(rule, b"allkeys") || rule == b"~*" {
859        selector.all_keys = true;
860        selector.patterns.clear();
861    } else if word(rule, b"resetkeys") {
862        selector.all_keys = false;
863        selector.patterns.clear();
864    } else if word(rule, b"allchannels") || rule == b"&*" {
865        selector.all_channels = true;
866        selector.channels.clear();
867    } else if word(rule, b"resetchannels") {
868        selector.all_channels = false;
869        selector.channels.clear();
870    } else if word(rule, b"allcommands") || rule == b"+@all" {
871        selector.reset_commands(true);
872    } else if word(rule, b"nocommands") || rule == b"-@all" {
873        selector.reset_commands(false);
874    } else if rule[0] == b'~' || rule[0] == b'%' {
875        add_pattern(selector, rule)?;
876    } else if rule[0] == b'&' {
877        if selector.all_channels {
878            return Err(Bad::AfterAllChannels);
879        }
880        let glob = &rule[1..];
881        if glob.contains(&b' ') {
882            return Err(Bad::Syntax);
883        }
884        if !selector.channels.iter().any(|held| held == glob) {
885            selector.channels.push(glob.to_vec());
886        }
887    } else if rule[0] == b'+' && rule.get(1) != Some(&b'@') {
888        add_command(selector, &rule[1..], true)?;
889    } else if rule[0] == b'-' && rule.get(1) != Some(&b'@') {
890        add_command(selector, &rule[1..], false)?;
891    } else if rule[0] == b'+' || rule[0] == b'-' {
892        let allow = rule[0] == b'+';
893        add_category(selector, &rule[2..], allow)?;
894        // Recorded with the sigil, because `@read` is what has to be written
895        // back and a bare `read` would read as a command name.
896        let mut name = Vec::with_capacity(rule.len() - 1);
897        name.push(b'@');
898        name.extend_from_slice(&rule[2..].to_ascii_lowercase());
899        selector.note(&name, allow);
900    } else {
901        return Err(Bad::Syntax);
902    }
903    Ok(())
904}
905
906/// A `~pattern` or `%RW~pattern` rule.
907fn add_pattern(selector: &mut Selector, rule: &[u8]) -> std::result::Result<(), Bad> {
908    if selector.all_keys {
909        return Err(Bad::AfterAllKeys);
910    }
911    let mut flags = 0u8;
912    let mut at = 1;
913    if rule[0] == b'%' {
914        // The letters run until the `~`, each may appear once, and there has to
915        // be at least one of them. `%~x` and `%RR~x` are both syntax errors. A
916        // rule that ends before the `~` is not: a bare `%R` is a pattern with an
917        // empty glob, which a real server takes and describes back as `%R~`.
918        let mut ok = true;
919        while at < rule.len() {
920            let letter = rule[at].to_ascii_uppercase();
921            if letter == b'R' && flags & READ == 0 {
922                flags |= READ;
923            } else if letter == b'W' && flags & WRITE == 0 {
924                flags |= WRITE;
925            } else if rule[at] == b'~' {
926                at += 1;
927                break;
928            } else {
929                ok = false;
930                break;
931            }
932            at += 1;
933        }
934        if flags == 0 || !ok {
935            return Err(Bad::Syntax);
936        }
937    } else {
938        flags = READ | WRITE;
939    }
940    let glob = &rule[at..];
941    if glob.contains(&b' ') {
942        return Err(Bad::Syntax);
943    }
944    // The same pattern given twice with different letters is one pattern good
945    // for both, which is why `%R~x %W~x` reads back as `~x`.
946    if let Some(held) = selector.patterns.iter_mut().find(|p| p.glob == glob) {
947        held.flags |= flags;
948    } else {
949        selector.patterns.push(Pattern {
950            flags,
951            glob: glob.to_vec(),
952        });
953    }
954    Ok(())
955}
956
957/// A `+command` or `+command|first` rule.
958fn add_command(selector: &mut Selector, name: &[u8], allow: bool) -> std::result::Result<(), Bad> {
959    let Some(bar) = name.iter().rposition(|b| *b == b'|') else {
960        let Some(spec) = table::lookup(name) else {
961            return Err(Bad::Unknown);
962        };
963        selector.set(table::index_of(spec), allow);
964        selector.note(&name.to_ascii_lowercase(), allow);
965        return Ok(());
966    };
967    let (head, first) = (&name[..bar], &name[bar + 1..]);
968    // The command has to exist even though the first argument cannot be checked,
969    // so `+nosuch|get` is refused and `+get|nosuch` is not.
970    let Some(spec) = table::lookup(head) else {
971        return Err(Bad::Unknown);
972    };
973    if head.contains(&b'|') {
974        return Err(Bad::NestedFirstArg);
975    }
976    if first.is_empty() {
977        return Err(Bad::Syntax);
978    }
979    let at = u16::try_from(table::index_of(spec)).unwrap_or(u16::MAX);
980    if allow {
981        // Nothing to do when the command is already allowed outright, which is
982        // what makes `+get +get|set` the same user as `+get`.
983        if !selector.bit(usize::from(at)) {
984            selector.allow_first(at, first);
985        }
986    } else {
987        // Taking one subcommand away only means anything for a command that has
988        // subcommands. A real server refuses `-get|nope` because it looks the
989        // whole name up in the table and GET has no such row, so this refuses it
990        // too, and for the same answer.
991        if !super::CONTAINERS.contains(&spec.name) {
992            return Err(Bad::Unknown);
993        }
994        selector.deny_first(at, first);
995    }
996    selector.note(&name.to_ascii_lowercase(), allow);
997    Ok(())
998}
999
1000/// A `+@category` or `-@category` rule.
1001fn add_category(selector: &mut Selector, name: &[u8], allow: bool) -> std::result::Result<(), Bad> {
1002    let Some(wanted) = category(name) else {
1003        return Err(Bad::Unknown);
1004    };
1005    for (at, spec) in table::COMMANDS.iter().enumerate() {
1006        if spec.acl.iter().any(|held| &held[1..] == wanted) {
1007            selector.set(at, allow);
1008        }
1009    }
1010    // And then the subcommands that are in the category without their container
1011    // being in it, one first argument at a time. `-@admin` has to reach CONFIG
1012    // GET and leave CONFIG HELP alone, and this is the only thing that knows the
1013    // two are different. See the note on `table::SUBCATS`.
1014    for (container, sub, cats) in table::SUBCATS {
1015        if !cats.iter().any(|held| &held[1..] == wanted) {
1016            continue;
1017        }
1018        let Some(spec) = table::lookup(container.as_bytes()) else {
1019            continue;
1020        };
1021        let at = u16::try_from(table::index_of(spec)).unwrap_or(u16::MAX);
1022        if !allow {
1023            selector.deny_first(at, sub.as_bytes());
1024        } else if !selector.bit(usize::from(at)) {
1025            selector.allow_first(at, sub.as_bytes());
1026        }
1027    }
1028    Ok(())
1029}
1030
1031/// The category called `name`, whatever case it was written in.
1032fn category(name: &[u8]) -> Option<&'static str> {
1033    CATEGORIES
1034        .iter()
1035        .find(|held| name.eq_ignore_ascii_case(held.as_bytes()))
1036        .copied()
1037}
1038
1039/// Whether `rule` is the keyword `word`, case insensitively.
1040fn word(rule: &[u8], keyword: &[u8]) -> bool {
1041    rule.eq_ignore_ascii_case(keyword)
1042}
1043
1044/// Split a selector's inside into rules on runs of spaces.
1045///
1046/// A selector arrives as one argument, `(+get ~k:*)`, so the words inside it
1047/// have to be taken apart here. Redis uses its config file splitter, which
1048/// understands quotes; this does not, because a key pattern with a space in it
1049/// is refused by the rule above anyway and a quoted rule has nowhere to be
1050/// useful.
1051fn split(inside: &[u8]) -> Vec<Vec<u8>> {
1052    inside
1053        .split(|b| *b == b' ')
1054        .filter(|part| !part.is_empty())
1055        .map(<[u8]>::to_vec)
1056        .collect()
1057}
1058
1059// ------------------------------------------------------------- the permissions
1060
1061/// Why a command was refused, ranked the way Redis ranks them.
1062///
1063/// The rank decides which of several selectors' complaints is reported: a user
1064/// with two selectors that both say no is told about the most specific refusal,
1065/// on the grounds that the selector which got as far as looking at a key was the
1066/// one the operator meant to use.
1067#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1068pub(crate) enum Denied {
1069    /// Not allowed to run the command at all.
1070    Command,
1071    /// Allowed the command, not the key at this argument.
1072    Key(usize),
1073    /// Allowed the command, not the channel at this argument.
1074    Channel(usize),
1075}
1076
1077impl Denied {
1078    /// Redis's numeric ranking, which is what two refusals are compared on.
1079    fn rank(self) -> u8 {
1080        match self {
1081            Denied::Command => 1,
1082            Denied::Key(_) => 2,
1083            Denied::Channel(_) => 4,
1084        }
1085    }
1086
1087    /// The argument the refusal is about, or nought for the command itself.
1088    fn at(self) -> usize {
1089        match self {
1090            Denied::Command => 0,
1091            Denied::Key(at) | Denied::Channel(at) => at,
1092        }
1093    }
1094}
1095
1096/// Where a command's channels are, since they are not in the key specs.
1097///
1098/// Eight commands and no more, which is Redis's own table. The flags in Redis
1099/// are four and only two of them are checked, so what is kept here is the one
1100/// bit that matters, whether the argument is a pattern being subscribed to, plus
1101/// whether the command is checked at all: unsubscribing is always allowed,
1102/// because a client that has lost permission to a channel still has to be able
1103/// to stop listening to it.
1104struct Channels {
1105    /// The first argument that is a channel.
1106    first: usize,
1107    /// How many there are, or `None` for all the rest.
1108    count: Option<usize>,
1109    /// Whether these are patterns, which are matched literally rather than as
1110    /// globs. `PSUBSCRIBE news.*` needs the ACL to hold `&news.*` exactly, not
1111    /// something that matches it, because otherwise `&news.sport` would let a
1112    /// client subscribe to `news.*` and hear everything.
1113    pattern: bool,
1114}
1115
1116/// The channel arguments of `name`, if it has any that are checked.
1117fn channels_of(name: &str) -> Option<Channels> {
1118    let spec = match name {
1119        "subscribe" | "ssubscribe" => Channels {
1120            first: 1,
1121            count: None,
1122            pattern: false,
1123        },
1124        "psubscribe" => Channels {
1125            first: 1,
1126            count: None,
1127            pattern: true,
1128        },
1129        "publish" | "spublish" => Channels {
1130            first: 1,
1131            count: Some(1),
1132            pattern: false,
1133        },
1134        _ => return None,
1135    };
1136    Some(spec)
1137}
1138
1139/// Whether `selector` may reach `key` for `need`.
1140fn key_ok(selector: &Selector, key: &[u8], need: Access) -> bool {
1141    if selector.all_keys {
1142        return true;
1143    }
1144    selector
1145        .patterns
1146        .iter()
1147        .any(|p| Access::from_bits(p.flags).covers(need) && glob_matches(&p.glob, key))
1148}
1149
1150/// Whether `selector` may reach `channel`.
1151fn channel_ok(selector: &Selector, channel: &[u8], pattern: bool) -> bool {
1152    if selector.all_channels {
1153        return true;
1154    }
1155    selector.channels.iter().any(|held| {
1156        if pattern {
1157            held == channel
1158        } else {
1159            glob_matches(held, channel)
1160        }
1161    })
1162}
1163
1164/// Whether one selector allows this whole command, and what it objected to.
1165fn selector_ok(
1166    selector: &Selector,
1167    spec: &'static Spec,
1168    args: Args<'_>,
1169    base: usize,
1170) -> std::result::Result<(), Denied> {
1171    if !selector.all_commands && !spec.flags.contains(&"no_auth") {
1172        let at = table::index_of(spec);
1173        let index = u16::try_from(at).unwrap_or(u16::MAX);
1174        let sub = (args.len() > base + 1).then(|| args.get(base + 1));
1175        let named = |list: &[Vec<u8>]| {
1176            sub.is_some_and(|word| list.iter().any(|held| word.eq_ignore_ascii_case(held)))
1177        };
1178        if selector.bit(at) {
1179            // Allowed outright, unless this is the one subcommand that was taken
1180            // back off it.
1181            if named(selector.denied(index)) {
1182                return Err(Denied::Command);
1183            }
1184        } else if !named(selector.firsts(index)) {
1185            return Err(Denied::Command);
1186        }
1187    }
1188
1189    if !selector.all_keys && keyspec::takes_keys(spec, args, base) {
1190        let mut refused = None;
1191        keyspec::find(spec, args, base, &mut |run| {
1192            if refused.is_some() {
1193                return;
1194            }
1195            let need = run.need();
1196            for i in 0..run.count {
1197                let at = run.first + i * run.step;
1198                if at < args.len() && !key_ok(selector, args.get(at), need) {
1199                    refused = Some(Denied::Key(at));
1200                    return;
1201                }
1202            }
1203        });
1204        if let Some(why) = refused {
1205            return Err(why);
1206        }
1207    }
1208
1209    if !selector.all_channels
1210        && let Some(where_) = channels_of(spec.name)
1211    {
1212        let first = base + where_.first;
1213        let stop = where_
1214            .count
1215            .map_or(args.len(), |n| (first + n).min(args.len()));
1216        for at in first..stop {
1217            if !channel_ok(selector, args.get(at), where_.pattern) {
1218                return Err(Denied::Channel(at));
1219            }
1220        }
1221    }
1222    Ok(())
1223}
1224
1225/// Whether `user` may run this command, and what it objected to.
1226///
1227/// Every selector is tried and the first that says yes wins. When none does, the
1228/// refusal reported is the highest ranked one, and on a tie the one about the
1229/// argument furthest along, which is Redis's rule and is what makes a two
1230/// selector user complain about the key rather than the command.
1231pub(crate) fn permits(
1232    user: &User,
1233    spec: &'static Spec,
1234    args: Args<'_>,
1235    base: usize,
1236) -> std::result::Result<(), Denied> {
1237    // The whole of the cost on a server whose users can do anything, which is
1238    // every server nobody has written an ACL for.
1239    if let Some(root) = user.selectors.first()
1240        && root.all_commands
1241        && root.all_keys
1242        && root.all_channels
1243    {
1244        return Ok(());
1245    }
1246    let mut worst = Denied::Command;
1247    for selector in &user.selectors {
1248        match selector_ok(selector, spec, args, base) {
1249            Ok(()) => return Ok(()),
1250            Err(why) => {
1251                if why.rank() > worst.rank()
1252                    || (why.rank() == worst.rank() && why.at() > worst.at())
1253                {
1254                    worst = why;
1255                }
1256            }
1257        }
1258    }
1259    Err(worst)
1260}
1261
1262/// The sentence a refusal is reported with.
1263///
1264/// `verbose` is the difference between the gate and `ACL DRYRUN`. The gate is
1265/// answering a client that has just been told no and naming the key it asked
1266/// about would tell it which keys exist, so the terse form says only that a key
1267/// was the problem. `ACL DRYRUN` is answering an operator who asked the
1268/// question on purpose and wants to know which key, so it names it.
1269pub(crate) fn refusal(
1270    why: Denied,
1271    user: &[u8],
1272    spec: &'static Spec,
1273    args: Args<'_>,
1274    base: usize,
1275    verbose: bool,
1276) -> String {
1277    let name = String::from_utf8_lossy(user);
1278    match why {
1279        Denied::Command => {
1280            let sub = container_name(spec, args, base);
1281            format!("User {name} has no permissions to run the '{sub}' command")
1282        }
1283        Denied::Key(at) if verbose => {
1284            let key = String::from_utf8_lossy(args.get(at));
1285            format!("User {name} has no permissions to access the '{key}' key")
1286        }
1287        Denied::Key(_) => "No permissions to access a key".to_string(),
1288        Denied::Channel(at) if verbose => {
1289            let channel = String::from_utf8_lossy(args.get(at));
1290            format!("User {name} has no permissions to access the '{channel}' channel")
1291        }
1292        Denied::Channel(_) => "No permissions to access a channel".to_string(),
1293    }
1294}
1295
1296/// The command's name as a refusal spells it, which is `acl|list` and not `acl`.
1297fn container_name(spec: &'static Spec, args: Args<'_>, base: usize) -> String {
1298    if super::CONTAINERS.contains(&spec.name) && args.len() > base + 1 {
1299        let sub = String::from_utf8_lossy(args.get(base + 1)).to_lowercase();
1300        return format!("{}|{sub}", spec.name);
1301    }
1302    spec.name.to_string()
1303}
1304
1305/// Who a connection is, and the copy of that user its commands are checked
1306/// against.
1307///
1308/// Boxed on the session, because it is three allocations and a connection on a
1309/// server with no ACL never reads any of them past the first command.
1310#[derive(Debug)]
1311pub(crate) struct Identity {
1312    /// The name, which is what `ACL WHOAMI` and `CLIENT INFO` report.
1313    name: Vec<u8>,
1314    /// The generation the copy below was taken at.
1315    stamp: u64,
1316    /// The copy itself.
1317    user: User,
1318}
1319
1320impl Default for Identity {
1321    /// A connection starts as the default user, with a copy stamped nought so
1322    /// that the first command it sends fetches the real one.
1323    fn default() -> Identity {
1324        Identity {
1325            name: DEFAULT.to_vec(),
1326            stamp: 0,
1327            user: User::default_user(),
1328        }
1329    }
1330}
1331
1332impl Session {
1333    /// The name this connection is authenticated as.
1334    pub(crate) fn acl_name(&self) -> &[u8] {
1335        &self.acl.name
1336    }
1337
1338    /// Say that this connection has authenticated as `user`.
1339    ///
1340    /// Written to the row as well as here, because `CLIENT LIST` reports the
1341    /// user of every connection and runs on whichever thread the client asking
1342    /// is on, which is very often not this one.
1343    pub(super) fn become_user(&mut self, stamp: u64, user: User) {
1344        yo_alloc::allow(|| {
1345            self.acl.name.clear();
1346            self.acl.name.extend_from_slice(&user.name);
1347        });
1348        self.acl.stamp = stamp;
1349        self.acl.user = user;
1350        self.sock.set_text(|text| &mut text.user, &self.acl.name);
1351    }
1352
1353    /// Put the connection back on the default user, which is what `RESET` does.
1354    pub(super) fn forget_user(&mut self) {
1355        *self.acl = Identity::default();
1356        // Empty rather than the name, which is how the row spells the default
1357        // user, so a connection that never authenticated costs nothing.
1358        self.sock.set_text(|text| &mut text.user, b"");
1359    }
1360
1361    /// The generation the copy was taken at.
1362    fn acl_stamp(&self) -> u64 {
1363        self.acl.stamp
1364    }
1365
1366    /// Take a fresh copy, or keep the one there is if the user has gone.
1367    fn acl_refresh(&mut self, now: u64, fresh: Option<User>) {
1368        self.acl.stamp = now;
1369        if let Some(user) = fresh {
1370            self.acl.user = user;
1371        }
1372    }
1373
1374    /// The copy itself.
1375    fn acl_cached(&self) -> &User {
1376        &self.acl.user
1377    }
1378}
1379
1380/// What the connection is running as, refreshed if the table has moved on.
1381///
1382/// A session keeps a copy of its user rather than a handle into the table,
1383/// because the alternative is taking the table's lock on every command on every
1384/// connection. The copy is stamped with the generation it was taken at and
1385/// replaced when that number moves, so a `SETUSER` that tightens a user's
1386/// permissions reaches every connection already authenticated as it, on that
1387/// connection's next command. That is what a real server does, and it is the
1388/// half of `SETUSER` that would be easy to get wrong: the point of tightening a
1389/// user is usually the connection that is already open.
1390///
1391/// A user deleted out from under a connection leaves the copy in place, which is
1392/// also Redis's behaviour: `ACL DELUSER` closes those connections rather than
1393/// leaving them running as a user that no longer exists.
1394fn current<'a>(server: &Server, session: &'a mut Session) -> &'a User {
1395    let now = server.acl.generation();
1396    if session.acl_stamp() != now {
1397        let fresh = server.acl.get(session.acl_name());
1398        session.acl_refresh(now, fresh);
1399    }
1400    session.acl_cached()
1401}
1402
1403/// The gate, which is every command on a server that has an ACL worth checking.
1404///
1405/// `None` means the command may run. The refusal is written by the caller
1406/// rather than here, because it is one of the errors that kills an open
1407/// transaction and the caller is what knows about transactions.
1408pub(super) fn gate(
1409    server: &Server,
1410    session: &mut Session,
1411    spec: &'static Spec,
1412    args: Args<'_>,
1413) -> Option<String> {
1414    let user = current(server, session);
1415    let why = permits(user, spec, args, 0).err()?;
1416    // The code is in the line rather than in front of it, because the line goes
1417    // two places: straight into the reply, and spliced into the `EXECABORT` an
1418    // `EXEC` gets. The same reason `NOAUTH` is a whole line.
1419    let said = refusal(why, &user.name, spec, args, 0, false);
1420    Some(yo_alloc::allow(|| format!("NOPERM {said}")))
1421}
1422
1423// ------------------------------------------------------------- the command
1424
1425/// The subcommands and how many arguments each takes, counting `ACL` itself.
1426///
1427/// A real server gives every one of these a row in the command table with its
1428/// own arity, and enforces it before the body is reached, which is why `ACL
1429/// WHOAMI x` is a wrong number of arguments and `ACL LOG 1 2` is an unknown
1430/// subcommand: the second one passes its own arity check and then falls off the
1431/// end of the parse. There is no subcommand table here yet, so the arities live
1432/// in this list and the same two sentences come out. It folds into D-114.
1433///
1434/// `LOAD`, `SAVE` and `LOG` are not here because they are not here at all yet,
1435/// and a row for one of them would mean answering a precise arity error for a
1436/// subcommand that is about to be called unknown.
1437const ARITIES: [(&[u8], i32); 9] = [
1438    (b"cat", -2),
1439    (b"deluser", -3),
1440    (b"dryrun", -4),
1441    (b"genpass", -2),
1442    (b"getuser", 3),
1443    (b"help", 2),
1444    (b"list", 2),
1445    (b"setuser", -3),
1446    (b"users", 2),
1447    // `whoami` is not here because its arity is exactly two, which is the same
1448    // check the fallthrough below makes, and a row for it would be dead weight.
1449];
1450
1451/// `ACL <subcommand> ...`.
1452pub(super) fn execute(
1453    server: &Server,
1454    session: &mut Session,
1455    args: Args<'_>,
1456    out: &mut Out,
1457) -> Result<()> {
1458    let sub = args.get(1);
1459    // The arity of the subcommand before anything else, which is where a real
1460    // server makes this decision: the row is found and checked in
1461    // `processCommand`, so a wrong count is refused before the body runs.
1462    if let Some((name, arity)) = ARITIES
1463        .iter()
1464        .find(|(name, _)| sub.eq_ignore_ascii_case(name))
1465    {
1466        let n = args.len() as i32;
1467        if (*arity > 0 && n != *arity) || (*arity < 0 && n < -*arity) {
1468            let name = std::str::from_utf8(name).unwrap_or("acl");
1469            return Err(args::wrong_arity_sub("acl", name));
1470        }
1471    }
1472
1473    if is(sub, b"whoami") {
1474        if args.len() != 2 {
1475            return Err(args::wrong_arity_sub("acl", "whoami"));
1476        }
1477        out.bulk(session.acl_name());
1478    } else if is(sub, b"cat") {
1479        cat(args, out)?;
1480    } else if is(sub, b"list") {
1481        yo_alloc::allow(|| {
1482            server.users().with(|table| {
1483                out.array(table.len());
1484                for user in table.iter() {
1485                    let mut line = b"user ".to_vec();
1486                    line.extend_from_slice(&user.describe());
1487                    out.bulk(&line);
1488                }
1489                (false, ())
1490            });
1491        });
1492    } else if is(sub, b"users") {
1493        server.users().with(|table| {
1494            out.array(table.len());
1495            for user in table.iter() {
1496                out.bulk(&user.name);
1497            }
1498            (false, ())
1499        });
1500    } else if is(sub, b"getuser") {
1501        yo_alloc::allow(|| getuser(server, args, out));
1502    } else if is(sub, b"setuser") {
1503        return yo_alloc::allow(|| setuser(server, args, out));
1504    } else if is(sub, b"deluser") {
1505        return deluser(server, args, out);
1506    } else if is(sub, b"genpass") {
1507        genpass(args, out)?;
1508    } else if is(sub, b"dryrun") {
1509        return yo_alloc::allow(|| dryrun(server, args, out));
1510    } else if is(sub, b"help") {
1511        super::server::help(out, HELP);
1512    } else {
1513        return Err(args::unknown_subcommand(sub, "ACL"));
1514    }
1515    Ok(())
1516}
1517
1518/// `ACL CAT` and `ACL CAT <category>`.
1519///
1520/// With no argument this is the list of categories, and with one it is the
1521/// commands in it, in table order. Redis walks a hash table there and so reports
1522/// an order that is neither sorted nor stable across versions, so matching it
1523/// exactly is not a thing to aim at; what a client can rely on is the set, and
1524/// this reports the same set for every category the two servers share.
1525fn cat(args: Args<'_>, out: &mut Out) -> Result<()> {
1526    if args.len() == 2 {
1527        out.array(CATEGORIES.len());
1528        for name in CATEGORIES {
1529            out.bulk(name.as_bytes());
1530        }
1531        return Ok(());
1532    }
1533    if args.len() > 3 {
1534        return Err(args::subcommand_syntax(args.get(1), "ACL"));
1535    }
1536    let Some(wanted) = category(args.get(2)) else {
1537        return Err(yo_alloc::allow(|| {
1538            Error::fmt(
1539                Code::Invalid,
1540                format_args!(
1541                    "Unknown category '{}'",
1542                    String::from_utf8_lossy(args.get(2))
1543                ),
1544            )
1545        }));
1546    };
1547    // The header goes on afterwards, because how many commands are in a
1548    // category is not a thing the table can be asked without walking it.
1549    let start = out.len();
1550    let mut n = 0;
1551    for spec in table::COMMANDS {
1552        if spec.acl.iter().any(|held| &held[1..] == wanted) {
1553            out.bulk(spec.name.as_bytes());
1554            n += 1;
1555        }
1556    }
1557    out.close_array(start, n);
1558    Ok(())
1559}
1560
1561/// `ACL GETUSER <username>`.
1562fn getuser(server: &Server, args: Args<'_>, out: &mut Out) {
1563    let Some(user) = server.users().get(args.get(2)) else {
1564        out.nil();
1565        return;
1566    };
1567    // Six fields: the two about the account, the root selector's three repeated
1568    // at the top level for the clients that were written before selectors
1569    // existed, and the selectors themselves.
1570    out.map(6);
1571    out.bulk(b"flags");
1572    // Only the flags that belong to the account. The selector flags are named in
1573    // the same table and an older server did list them here, but 8.10.1 does not,
1574    // and a client that wants to know whether a user can reach every key reads
1575    // the keys field rather than counting words in this set.
1576    let flags = user.flags();
1577    let root = &user.selectors[0];
1578    out.set(flags.len());
1579    for flag in &flags {
1580        out.bulk(flag.as_bytes());
1581    }
1582    out.bulk(b"passwords");
1583    out.array(user.passwords.len());
1584    for hash in &user.passwords {
1585        out.bulk(hash);
1586    }
1587    describe_selector(root, out);
1588    out.bulk(b"selectors");
1589    out.array(user.selectors.len() - 1);
1590    for selector in &user.selectors[1..] {
1591        out.map(3);
1592        describe_selector(selector, out);
1593    }
1594}
1595
1596/// The three fields a selector contributes to `ACL GETUSER`.
1597fn describe_selector(selector: &Selector, out: &mut Out) {
1598    out.bulk(b"commands");
1599    out.bulk(&selector.describe_commands());
1600    out.bulk(b"keys");
1601    out.bulk(&selector.describe_keys());
1602    out.bulk(b"channels");
1603    out.bulk(&selector.describe_channels());
1604}
1605
1606/// `ACL SETUSER <username> [rule ...]`.
1607///
1608/// Every rule is applied to a copy and the copy replaces the user only if all of
1609/// them worked, so a `SETUSER` that fails halfway leaves nothing behind. That is
1610/// worth more than it sounds: the failure case is an operator tightening a
1611/// user's permissions and mistyping one rule, and a server that applied the
1612/// first half would have left the user with the new restrictions and none of the
1613/// new grants.
1614fn setuser(server: &Server, args: Args<'_>, out: &mut Out) -> Result<()> {
1615    let name = args.get(2);
1616    if name.contains(&b' ') || name.contains(&0) {
1617        return Err(Error::new(
1618            Code::Invalid,
1619            "Usernames can't contain spaces or null characters",
1620        ));
1621    }
1622    let rules = merge(args, 3)?;
1623    let outcome = server.users().with(|table| {
1624        let at = table.binary_search_by(|u| u.name.as_slice().cmp(name));
1625        let mut staged = match at {
1626            Ok(at) => table[at].clone(),
1627            Err(_) => User::new(name),
1628        };
1629        for rule in &rules {
1630            if let Err(why) = set_user(&mut staged, rule) {
1631                return (false, Err((rule.clone(), why)));
1632            }
1633        }
1634        match at {
1635            Ok(at) => table[at] = staged,
1636            Err(at) => table.insert(at, staged),
1637        }
1638        (true, Ok(()))
1639    });
1640    match outcome {
1641        Ok(()) => {
1642            out.ok();
1643            Ok(())
1644        }
1645        Err((rule, why)) => Err(Error::fmt(
1646            Code::Invalid,
1647            format_args!(
1648                "Error in ACL SETUSER modifier '{}': {}",
1649                String::from_utf8_lossy(&rule),
1650                why.text()
1651            ),
1652        )),
1653    }
1654}
1655
1656/// Join the arguments from `from` on, gluing a selector back together.
1657///
1658/// A selector is one rule and a client sends it as several arguments, because
1659/// `(+get ~k:*)` has a space in it and the wire has no way to say that was meant
1660/// as one word. So a `(` that does not end in `)` swallows the arguments after
1661/// it until one does. An opening bracket that is never closed is the one rule
1662/// error reported in its own sentence rather than as a modifier error, because
1663/// there is no single modifier to blame.
1664fn merge(args: Args<'_>, from: usize) -> Result<Vec<Vec<u8>>> {
1665    let mut out: Vec<Vec<u8>> = Vec::with_capacity(args.len() - from);
1666    let mut open: Option<usize> = None;
1667    for i in from..args.len() {
1668        let word = args.get(i);
1669        if open.is_none() && word.first() == Some(&b'(') && word.last() != Some(&b')') {
1670            open = Some(i);
1671            out.push(word.to_vec());
1672            continue;
1673        }
1674        if open.is_some() {
1675            let held = out.last_mut().expect("an open bracket left a rule behind");
1676            held.push(b' ');
1677            held.extend_from_slice(word);
1678            if word.last() == Some(&b')') {
1679                open = None;
1680            }
1681            continue;
1682        }
1683        out.push(word.to_vec());
1684    }
1685    if let Some(at) = open {
1686        return Err(Error::fmt(
1687            Code::Invalid,
1688            format_args!(
1689                "Unmatched parenthesis in acl selector starting at '{}'.",
1690                String::from_utf8_lossy(args.get(at))
1691            ),
1692        ));
1693    }
1694    Ok(out)
1695}
1696
1697/// `ACL DELUSER <username> [<username> ...]`.
1698///
1699/// The default user is checked for over the whole list before anything is
1700/// deleted, so `ACL DELUSER alice default` deletes neither. Redis does the same
1701/// and the reason is the same as `SETUSER`'s: a half done change to who may
1702/// reach a server is worse than no change.
1703fn deluser(server: &Server, args: Args<'_>, out: &mut Out) -> Result<()> {
1704    for i in 2..args.len() {
1705        if args.get(i) == DEFAULT {
1706            return Err(Error::new(
1707                Code::Invalid,
1708                "The 'default' user cannot be removed",
1709            ));
1710        }
1711    }
1712    let gone = server.users().with(|table| {
1713        let mut gone = 0;
1714        for i in 2..args.len() {
1715            if let Ok(at) = table.binary_search_by(|u| u.name.as_slice().cmp(args.get(i))) {
1716                table.remove(at);
1717                gone += 1;
1718            }
1719        }
1720        (gone > 0, gone)
1721    });
1722    out.int(gone);
1723    Ok(())
1724}
1725
1726/// `ACL GENPASS [<bits>]`.
1727///
1728/// The bytes come from the operating system rather than from the engine's own
1729/// generator, which is seeded and reproducible on purpose. See
1730/// [`yo_common::entropy`].
1731fn genpass(args: Args<'_>, out: &mut Out) -> Result<()> {
1732    if args.len() > 3 {
1733        return Err(args::subcommand_syntax(args.get(1), "ACL"));
1734    }
1735    let bits = if args.len() == 3 { args.int(2)? } else { 256 };
1736    if bits <= 0 || bits > 4096 {
1737        return Err(Error::new(
1738            Code::Invalid,
1739            "ACL GENPASS argument must be the number of bits for the output password, a positive number up to 4096",
1740        ));
1741    }
1742    // One hex character is four bits, rounded up, so `GENPASS 10` is three
1743    // characters and holds twelve bits rather than ten. That is the reference's
1744    // arithmetic and it errs towards more entropy than was asked for.
1745    let chars = ((bits + 3) / 4) as usize;
1746    let mut raw = [0u8; 4096 / 8 + 1];
1747    let bytes = chars.div_ceil(2);
1748    yo_common::entropy::fill(&mut raw[..bytes]);
1749    const DIGITS: &[u8; 16] = b"0123456789abcdef";
1750    let mut hex = [0u8; 1024];
1751    for (i, slot) in hex[..chars].iter_mut().enumerate() {
1752        let byte = raw[i / 2];
1753        *slot = DIGITS[usize::from(if i % 2 == 0 { byte >> 4 } else { byte & 0xf })];
1754    }
1755    out.bulk(&hex[..chars]);
1756    Ok(())
1757}
1758
1759/// `ACL DRYRUN <username> <command> [<arg> ...]`.
1760///
1761/// The same question the gate asks, asked out loud. The answer is a bulk string
1762/// rather than an error even when it is a refusal, because the command
1763/// succeeded: it was asked whether something would be allowed and it found out.
1764fn dryrun(server: &Server, args: Args<'_>, out: &mut Out) -> Result<()> {
1765    let Some(user) = server.users().get(args.get(2)) else {
1766        return Err(Error::fmt(
1767            Code::Invalid,
1768            format_args!("User '{}' not found", String::from_utf8_lossy(args.get(2))),
1769        ));
1770    };
1771    let Some(spec) = table::lookup(args.get(3)) else {
1772        return Err(Error::fmt(
1773            Code::Invalid,
1774            format_args!(
1775                "Command '{}' not found",
1776                String::from_utf8_lossy(args.get(3))
1777            ),
1778        ));
1779    };
1780    if !table::arity_ok(spec, args.len() - 3) {
1781        return Err(args::wrong_arity(spec.name));
1782    }
1783    match permits(&user, spec, args, 3) {
1784        Ok(()) => out.ok(),
1785        Err(why) => {
1786            let text = refusal(why, &user.name, spec, args, 3, true);
1787            out.bulk(text.as_bytes());
1788        }
1789    }
1790    Ok(())
1791}
1792
1793/// What `ACL HELP` says.
1794///
1795/// The subcommands that are here, which is all of Redis's but `LOAD`, `SAVE` and
1796/// `LOG`. Those three want an ACL file and a log of refusals, which is the next
1797/// change rather than this one, and a client reading this to find out what it
1798/// can send should not be told about them by a server that would answer them
1799/// with an unknown subcommand.
1800const HELP: &[&str] = &[
1801    "ACL <subcommand> [<arg> [value] [opt] ...]. Subcommands are:",
1802    "CAT [<category>]",
1803    "    List all commands that belong to <category>, or all command categories",
1804    "    when no category is specified.",
1805    "DELUSER <username> [<username> ...]",
1806    "    Delete a list of users.",
1807    "DRYRUN <username> <command> [<arg> ...]",
1808    "    Returns whether the user can execute the given command without executing the command.",
1809    "GETUSER <username>",
1810    "    Get the user's details.",
1811    "GENPASS [<bits>]",
1812    "    Generate a secure 256-bit user password. The optional `bits` argument can",
1813    "    be used to specify a different size.",
1814    "LIST",
1815    "    Show users details in config file format.",
1816    "SETUSER <username> <attribute> [<attribute> ...]",
1817    "    Create or modify a user with the specified attributes.",
1818    "USERS",
1819    "    List all the registered usernames.",
1820    "WHOAMI",
1821    "    Return the current connection username.",
1822    "HELP",
1823    "    Print this help.",
1824];
1825
1826#[cfg(test)]
1827mod tests {
1828    use super::*;
1829    use crate::proto::Limits;
1830    use crate::request::{Argv, Step};
1831
1832    /// A user built by applying rules in order, or the first rule that failed.
1833    fn built(rules: &[&str]) -> std::result::Result<User, Bad> {
1834        let mut user = User::new(b"u");
1835        for rule in rules {
1836            set_user(&mut user, rule.as_bytes())?;
1837        }
1838        Ok(user)
1839    }
1840
1841    /// The user's line the way `ACL LIST` writes it.
1842    fn listed(rules: &[&str]) -> String {
1843        let user = built(rules).expect("every rule here is a good one");
1844        String::from_utf8(user.describe()).expect("the description is text")
1845    }
1846
1847    /// Whether this user could run this command, said as a word so a failing
1848    /// assertion reads like the question it was asked.
1849    fn allows(rules: &[&str], words: &[&str]) -> bool {
1850        let user = built(rules).expect("every rule here is a good one");
1851        let mut buf = format!("*{}\r\n", words.len()).into_bytes();
1852        for word in words {
1853            buf.extend_from_slice(format!("${}\r\n{word}\r\n", word.len()).as_bytes());
1854        }
1855        let mut argv = Argv::new();
1856        let Ok(Step::Command { .. }) = argv.decode(&buf, &Limits::default()) else {
1857            panic!("the test wrote a command that does not decode");
1858        };
1859        let args = Args::new(&argv, &buf);
1860        let spec = table::lookup(args.name()).expect("a command this server has");
1861        permits(&user, spec, args, 0).is_ok()
1862    }
1863
1864    #[test]
1865    fn a_key_permission_with_no_pattern_after_it_is_an_empty_pattern() {
1866        // 8.10.1 takes a bare `%R` and writes it back with the `~` it never got.
1867        assert_eq!(
1868            listed(&["%R"]),
1869            "u off sanitize-payload %R~ resetchannels -@all"
1870        );
1871        assert_eq!(
1872            listed(&["%RW"]),
1873            "u off sanitize-payload ~ resetchannels -@all"
1874        );
1875    }
1876
1877    #[test]
1878    fn a_key_permission_that_is_not_r_or_w_once_each_is_a_syntax_error() {
1879        assert_eq!(built(&["%"]).err(), Some(Bad::Syntax));
1880        assert_eq!(built(&["%~k:*"]).err(), Some(Bad::Syntax));
1881        assert_eq!(built(&["%RR~k:*"]).err(), Some(Bad::Syntax));
1882        assert_eq!(built(&["%X~k:*"]).err(), Some(Bad::Syntax));
1883    }
1884
1885    #[test]
1886    fn a_subcommand_can_be_taken_back_off_a_container_that_was_allowed() {
1887        assert_eq!(
1888            listed(&["+config", "-config|get"]),
1889            "u off sanitize-payload resetchannels -@all +config -config|get"
1890        );
1891        assert!(allows(
1892            &["+config", "-config|get"],
1893            &["config", "set", "maxmemory", "0"]
1894        ));
1895        assert!(!allows(
1896            &["+config", "-config|get"],
1897            &["config", "get", "maxmemory"]
1898        ));
1899        // And allowing the container again forgets the exception, the same way
1900        // allowing it forgets a first argument it was limited to.
1901        assert_eq!(
1902            listed(&["+config", "-config|get", "+config"]),
1903            "u off sanitize-payload resetchannels -@all +config"
1904        );
1905        assert!(allows(
1906            &["+config", "-config|get", "+config"],
1907            &["config", "get", "maxmemory"]
1908        ));
1909    }
1910
1911    #[test]
1912    fn a_first_argument_can_only_be_taken_off_a_command_that_has_subcommands() {
1913        // GET has no subcommands, so a real server looks up `get|nope`, finds
1914        // nothing, and says so. Allowing one is a different mechanism and works.
1915        assert_eq!(built(&["-get|nope"]).err(), Some(Bad::Unknown));
1916        assert_eq!(built(&["-select|0"]).err(), Some(Bad::Unknown));
1917        assert!(built(&["+get|nope"]).is_ok());
1918    }
1919
1920    #[test]
1921    fn a_category_a_subcommand_holds_reaches_that_subcommand_and_no_further() {
1922        // CONFIG is only `@slow` in the table because 8.10.1 puts `@admin` on
1923        // `config|get` and `config|set` rather than on `config`, so a rule about
1924        // `@admin` has to take CONFIG GET away and leave CONFIG HELP behind.
1925        let deny = ["~*", "+@all", "-@admin"];
1926        assert!(!allows(&deny, &["config", "get", "maxmemory"]));
1927        assert!(allows(&deny, &["config", "help"]));
1928        assert!(!allows(&deny, &["client", "kill", "id", "4"]));
1929        assert!(allows(&deny, &["client", "setname", "x"]));
1930        assert!(allows(&deny, &["acl", "whoami"]));
1931        assert!(!allows(&deny, &["acl", "setuser", "u"]));
1932        // And the other way round, from a user that starts with nothing.
1933        let grant = ["~*", "-@all", "+@admin"];
1934        assert!(allows(&grant, &["config", "get", "maxmemory"]));
1935        assert!(!allows(&grant, &["config", "help"]));
1936        assert!(!allows(&grant, &["get", "k"]));
1937        // None of which changes what the container is listed as being in, or
1938        // `COMMAND INFO config` and `ACL CAT admin` would both start lying.
1939        let config = table::lookup(b"config").expect("a command this server has");
1940        assert_eq!(config.acl, ["@slow"]);
1941    }
1942
1943    #[test]
1944    fn the_flags_a_user_reports_are_the_ones_about_the_account() {
1945        // Not `allkeys`, which an older server did report here and 8.10.1 does
1946        // not. A client that wants to know reads the keys field.
1947        let user = built(&["on", "~*", "&*", "+@all"]).expect("good rules");
1948        assert_eq!(user.flags(), ["on", "sanitize-payload"]);
1949    }
1950}