Skip to content

Commit

Permalink
fix #2099
Browse files Browse the repository at this point in the history
Add optional support for rfc1459 and rfc1459-strict casemappings
  • Loading branch information
slingamn committed May 28, 2024
1 parent 5ee32cd commit 7772b55
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 6 deletions.
5 changes: 3 additions & 2 deletions default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,10 @@ server:
# the recommended default is 'ascii' (traditional ASCII-only identifiers).
# the other options are 'precis', which allows UTF8 identifiers that are "sane"
# (according to UFC 8265), with additional mitigations for homoglyph attacks,
# and 'permissive', which allows identifiers containing unusual characters like
# 'permissive', which allows identifiers containing unusual characters like
# emoji, at the cost of increased vulnerability to homoglyph attacks and potential
# client compatibility problems. we recommend leaving this value at its default;
# client compatibility problems, and the legacy mappings 'rfc1459' and
# 'rfc1459-strict'. we recommend leaving this value at its default;
# however, note that changing it once the network is already up and running is
# problematic.
casemapping: "ascii"
Expand Down
15 changes: 14 additions & 1 deletion irc/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,10 @@ func (cm *Casemapping) UnmarshalYAML(unmarshal func(interface{}) error) (err err
result = CasemappingPRECIS
case "permissive", "fun":
result = CasemappingPermissive
case "rfc1459":
result = CasemappingRFC1459
case "rfc1459-strict":
result = CasemappingRFC1459Strict
default:
return fmt.Errorf("invalid casemapping value: %s", orig)
}
Expand Down Expand Up @@ -1622,7 +1626,16 @@ func (config *Config) generateISupport() (err error) {
isupport.Initialize()
isupport.Add("AWAYLEN", strconv.Itoa(config.Limits.AwayLen))
isupport.Add("BOT", "B")
isupport.Add("CASEMAPPING", "ascii")
var casemappingToken string
switch config.Server.Casemapping {
default:
casemappingToken = "ascii" // this is published for ascii, precis, or permissive
case CasemappingRFC1459:
casemappingToken = "rfc1459"
case CasemappingRFC1459Strict:
casemappingToken = "rfc1459-strict"
}
isupport.Add("CASEMAPPING", casemappingToken)
isupport.Add("CHANLIMIT", fmt.Sprintf("%s:%d", chanTypes, config.Channels.MaxChannelsPerClient))
isupport.Add("CHANMODES", chanmodesToken)
if config.History.Enabled && config.History.ChathistoryMax > 0 {
Expand Down
27 changes: 26 additions & 1 deletion irc/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ const (
// confusables detection: standard skeleton algorithm (which may be ineffective
// over the larger set of permitted identifiers)
CasemappingPermissive
// rfc1459 is a legacy mapping as defined here: https://modern.ircdocs.horse/#casemapping-parameter
CasemappingRFC1459
// rfc1459-strict is a legacy mapping as defined here: https://modern.ircdocs.horse/#casemapping-parameter
CasemappingRFC1459Strict
)

// XXX this is a global variable without explicit synchronization.
Expand Down Expand Up @@ -110,6 +114,10 @@ func casefoldWithSetting(str string, setting Casemapping) (string, error) {
return foldASCII(str)
case CasemappingPermissive:
return foldPermissive(str)
case CasemappingRFC1459:
return foldRFC1459(str, false)
case CasemappingRFC1459Strict:
return foldRFC1459(str, true)
}
}

Expand Down Expand Up @@ -214,7 +222,7 @@ func Skeleton(name string) (string, error) {
switch globalCasemappingSetting {
default:
return realSkeleton(name)
case CasemappingASCII:
case CasemappingASCII, CasemappingRFC1459, CasemappingRFC1459Strict:
// identity function is fine because we independently case-normalize in Casefold
return name, nil
}
Expand Down Expand Up @@ -302,6 +310,23 @@ func foldASCII(str string) (result string, err error) {
return strings.ToLower(str), nil
}

var (
rfc1459Replacer = strings.NewReplacer("[", "{", "]", "}", "\\", "|", "~", "^")
rfc1459StrictReplacer = strings.NewReplacer("[", "{", "]", "}", "\\", "|")
)

func foldRFC1459(str string, strict bool) (result string, err error) {
asciiFold, err := foldASCII(str)
if err != nil {
return "", err
}
replacer := rfc1459Replacer
if strict {
replacer = rfc1459StrictReplacer
}
return replacer.Replace(asciiFold), nil
}

func IsPrintableASCII(str string) bool {
for i := 0; i < len(str); i++ {
// allow space here because it's technically printable;
Expand Down
28 changes: 28 additions & 0 deletions irc/strings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,3 +279,31 @@ func TestFoldASCIIInvalid(t *testing.T) {
t.Errorf("control characters should be invalid in identifiers")
}
}

func TestFoldRFC1459(t *testing.T) {
folder := func(str string) (string, error) {
return foldRFC1459(str, false)
}
tester := func(first, second string, equal bool) {
validFoldTester(first, second, equal, folder, t)
}
tester("shivaram", "SHIVARAM", true)
tester("shivaram[a]", "shivaram{a}", true)
tester("shivaram\\a]", "shivaram{a}", false)
tester("shivaram\\a]", "shivaram|a}", true)
tester("shivaram~a]", "shivaram^a}", true)
}

func TestFoldRFC1459Strict(t *testing.T) {
folder := func(str string) (string, error) {
return foldRFC1459(str, true)
}
tester := func(first, second string, equal bool) {
validFoldTester(first, second, equal, folder, t)
}
tester("shivaram", "SHIVARAM", true)
tester("shivaram[a]", "shivaram{a}", true)
tester("shivaram\\a]", "shivaram{a}", false)
tester("shivaram\\a]", "shivaram|a}", true)
tester("shivaram~a]", "shivaram^a}", false)
}
5 changes: 3 additions & 2 deletions traditional.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,10 @@ server:
# the recommended default is 'ascii' (traditional ASCII-only identifiers).
# the other options are 'precis', which allows UTF8 identifiers that are "sane"
# (according to UFC 8265), with additional mitigations for homoglyph attacks,
# and 'permissive', which allows identifiers containing unusual characters like
# 'permissive', which allows identifiers containing unusual characters like
# emoji, at the cost of increased vulnerability to homoglyph attacks and potential
# client compatibility problems. we recommend leaving this value at its default;
# client compatibility problems, and the legacy mappings 'rfc1459' and
# 'rfc1459-strict'. we recommend leaving this value at its default;
# however, note that changing it once the network is already up and running is
# problematic.
casemapping: "ascii"
Expand Down

0 comments on commit 7772b55

Please sign in to comment.