Skip to content

Commit 8e68332

Browse files
committed
Prevent setting an empty username with 'USER [ * * :foo'
This used to be valid because '[' is stripped from usernames This uses the ERR_INVALIDUSERNAME numeric I am about to introduce to modern.ircdocs.horse: ircdocs/modern-irc#217
1 parent f757057 commit 8e68332

File tree

7 files changed

+34
-10
lines changed

7 files changed

+34
-10
lines changed

sable_ircd/src/command/client_command.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@ impl ClientCommand {
225225
LookupError::NoSuchChannelName(name) => Some(make_numeric!(NoSuchChannel, &name)),
226226
_ => None,
227227
},
228-
CommandError::InvalidNick(name) => Some(make_numeric!(ErroneousNickname, &name)),
228+
CommandError::InvalidNickname(name) => Some(make_numeric!(ErroneousNickname, &name)),
229+
CommandError::InvalidUsername(_name) => Some(make_numeric!(InvalidUsername)),
229230
CommandError::InvalidChannelName(name) => {
230231
Some(make_numeric!(InvalidChannelName, &name))
231232
}

sable_ircd/src/command/error.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ pub enum CommandError {
2727
/// A required object wasn't found in the network state
2828
LookupError(LookupError),
2929
/// A nickname parameter wasn't a valid nick
30-
InvalidNick(String),
30+
InvalidNickname(String),
31+
/// A username parameter wasn't a valid username
32+
InvalidUsername(String),
3133
/// A channel name parameter wasn't a valid channel name
3234
InvalidChannelName(String),
3335
/// A services command was executed, but services aren't currently running
@@ -120,7 +122,13 @@ impl From<LookupError> for CommandError {
120122

121123
impl From<InvalidNicknameError> for CommandError {
122124
fn from(e: InvalidNicknameError) -> Self {
123-
Self::InvalidNick(e.0)
125+
Self::InvalidNickname(e.0)
126+
}
127+
}
128+
129+
impl From<InvalidUsernameError> for CommandError {
130+
fn from(e: InvalidUsernameError) -> Self {
131+
Self::InvalidUsername(e.0)
124132
}
125133
}
126134

sable_ircd/src/command/handlers/services/mod.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,12 @@ impl<'a> Command for ServicesCommand<'a> {
105105
}
106106
}
107107
}
108-
CommandError::InvalidNick(name) => {
108+
CommandError::InvalidNickname(name) => {
109109
self.notice(format_args!("Invalid nickname {}", name));
110110
}
111+
CommandError::InvalidUsername(name) => {
112+
self.notice(format_args!("Invalid username {}", name));
113+
}
111114
CommandError::InvalidChannelName(name) => {
112115
self.notice(format_args!("Invalid channel name {}", name));
113116
}

sable_ircd/src/command/handlers/user.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ fn handle_user(
55
server: &ClientServer,
66
source: PreClientSource,
77
cmd: &dyn Command,
8-
username: &str,
8+
username: Username,
99
_unused1: &str,
1010
_unused2: &str,
1111
realname: &str,
1212
) -> CommandResult {
1313
// Ignore these results; they'll only fail if USER was already successfully processed
1414
// from this pre-client. If that happens we silently ignore the new values.
15-
source.user.set(Username::new_coerce(username)).ok();
15+
source.user.set(username).ok();
1616
source.realname.set(realname.to_owned()).ok();
1717

1818
if source.can_register() {

sable_ircd/src/command/plumbing/argument_type.rs

+6
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ impl<'a> PositionalArgument<'a> for Nickname {
5252
}
5353
}
5454

55+
impl<'a> PositionalArgument<'a> for Username {
56+
fn parse_str(_ctx: &'a dyn Command, value: &'a str) -> Result<Self, CommandError> {
57+
Ok(Username::new_coerce(value)?)
58+
}
59+
}
60+
5561
impl<'a> PositionalArgument<'a> for state::ChannelRoleName {
5662
fn parse_str(_ctx: &'a dyn Command, value: &'a str) -> Result<Self, CommandError> {
5763
value

sable_ircd/src/messages/numeric.rs

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ define_messages! {
6464
451(NotRegistered) => { () => ":You have not registered" },
6565
461(NotEnoughParameters) => { (command: &str) => "{command} :Not enough parameters" },
6666
462(AlreadyRegistered) => { () => ":You are already connected and cannot handshake again" },
67+
468(InvalidUsername) => { () => ":Your username is not valid" },
6768
472(UnknownMode) => { (c: char) => "{c} :Unknown mode character" },
6869
479(InvalidChannelName) => { (name: &str) => "{name} :Illegal channel name" },
6970
482(ChanOpPrivsNeeded) => { (chan: &ChannelName) => "{chan} :You're not a channel operator" },

sable_network/src/validated.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ define_validated! {
5959
}
6060

6161
Username(ArrayString<{ Username::LENGTH }>) {
62-
Ok(())
62+
if value.len() == 0 {
63+
Self::error(value)
64+
} else {
65+
Ok(())
66+
}
6367
}
6468

6569
Hostname(ArrayString<{ Hostname::LENGTH }>) {
@@ -129,13 +133,14 @@ impl Username {
129133
pub const LENGTH: usize = 10;
130134

131135
/// Coerce the provided value into a valid `Username`, by truncating to the
132-
/// permitted length and removing any invalid characters.
133-
pub fn new_coerce(s: &str) -> Self {
136+
/// permitted length, removing any invalid characters, and checking it is not empty.
137+
pub fn new_coerce(s: &str) -> <Self as Validated>::Result {
134138
let mut s = s.to_string();
135139
s.retain(|c| c != '[');
136140
s.truncate(s.floor_char_boundary(Self::LENGTH));
137141
// expect() is safe here; we've already truncated to the max length
138-
Self(ArrayString::try_from(s.as_str()).expect("Failed to convert string"))
142+
let val = ArrayString::try_from(s.as_str()).expect("Failed to convert string");
143+
Self::validate(&val).map(|()| Self(val))
139144
}
140145
}
141146

0 commit comments

Comments
 (0)