Restrict idents as other servers do

This commit is contained in:
Daniel Oaks 2019-02-03 18:49:42 +10:00
parent 9f25a42c3d
commit 8cd5db1194
5 changed files with 49 additions and 4 deletions

View File

@ -623,12 +623,15 @@ func (client *Client) HasUsername() bool {
return client.username != "" && client.username != "*" return client.username != "" && client.username != "*"
} }
// SetNames sets the client's ident and realname.
func (client *Client) SetNames(username, realname string) error { func (client *Client) SetNames(username, realname string) error {
usernameCasefolded, err := CasefoldName(username) // do this before casefolding to ensure these are actually ascii
if err != nil { if !isIdent(username) {
return errInvalidUsername return errInvalidUsername
} }
usernameCasefolded := strings.ToLower(username) // only ascii is supported in idents anyway
client.stateMutex.Lock() client.stateMutex.Lock()
defer client.stateMutex.Unlock() defer client.stateMutex.Unlock()

View File

@ -2195,8 +2195,7 @@ func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
err := client.SetNames(msg.Params[0], msg.Params[3]) err := client.SetNames(msg.Params[0], msg.Params[3])
if err == errInvalidUsername { if err == errInvalidUsername {
rb.Add(nil, "", "ERROR", client.t("Malformed username")) rb.Add(nil, server.name, ERR_INVALIDUSERNAME, client.t("Malformed username"))
return true
} }
return false return false

View File

@ -143,6 +143,7 @@ const (
ERR_YOUREBANNEDCREEP = "465" ERR_YOUREBANNEDCREEP = "465"
ERR_YOUWILLBEBANNED = "466" ERR_YOUWILLBEBANNED = "466"
ERR_KEYSET = "467" ERR_KEYSET = "467"
ERR_INVALIDUSERNAME = "468"
ERR_CHANNELISFULL = "471" ERR_CHANNELISFULL = "471"
ERR_UNKNOWNMODE = "472" ERR_UNKNOWNMODE = "472"
ERR_INVITEONLYCHAN = "473" ERR_INVITEONLYCHAN = "473"

View File

@ -128,6 +128,32 @@ func isBoring(name string) bool {
return true return true
} }
// returns true if the given name is a valid ident, using a mix of Insp and
// Chary's ident restrictions.
func isIdent(name string) bool {
if len(name) < 1 {
return false
}
for i := 0; i < len(name); i++ {
chr := name[i]
if (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') {
continue // alphanumerics
}
if i == 0 {
return false // first char must be alnum
}
switch chr {
case '[', '\\', ']', '^', '_', '{', '|', '}', '-', '.', '`':
continue // allowed chars
default:
return false // disallowed chars
}
}
return true
}
// Skeleton produces a canonicalized identifier that tries to catch // Skeleton produces a canonicalized identifier that tries to catch
// homoglyphic / confusable identifiers. It's a tweaked version of the TR39 // homoglyphic / confusable identifiers. It's a tweaked version of the TR39
// skeleton algorithm. We apply the skeleton algorithm first and only then casefold, // skeleton algorithm. We apply the skeleton algorithm first and only then casefold,

View File

@ -140,6 +140,22 @@ func TestIsBoring(t *testing.T) {
assertBoring("Νικηφόρος", false) assertBoring("Νικηφόρος", false)
} }
func TestIsIdent(t *testing.T) {
assertIdent := func(str string, expected bool) {
if isIdent(str) != expected {
t.Errorf("expected [%s] to have identness [%t], but got [%t]", str, expected, !expected)
}
}
assertIdent("warning", true)
assertIdent("sid3225", true)
assertIdent("dan.oak25", true)
assertIdent("dan.oak[25]", true)
assertIdent("phi@#$%ip", false)
assertIdent("Νικηφόρος", false)
assertIdent("-dan56", false)
}
func TestSkeleton(t *testing.T) { func TestSkeleton(t *testing.T) {
skeleton := func(str string) string { skeleton := func(str string) string {
skel, err := Skeleton(str) skel, err := Skeleton(str)