prevent unicode-confusion in password by applying PRECIS, and username/email address by applying unicode NFC normalization

an é (e with accent) can also be written as e+\u0301. the first form is NFC,
the second NFD. when logging in, we transform usernames (email addresses) to
NFC. so both forms will be accepted. if a client is using NFD, they can log
in too.

for passwords, we apply the PRECIS "opaquestring", which (despite the name)
transforms the value too: unicode spaces are replaced with ascii spaces. the
string is also normalized to NFC. PRECIS may reject confusing passwords when
you set a password.
This commit is contained in:
Mechiel Lukkien
2024-03-08 23:29:15 +01:00
parent 8e6fe7459b
commit c57aeac7f0
99 changed files with 59625 additions and 114 deletions

View File

@ -334,6 +334,9 @@ func startNoSwitchboard(t *testing.T) *testconn {
return startArgs(t, false, false, true, false, "mjl")
}
const password0 = "te\u0301st \u00a0\u2002\u200a" // NFD and various unicode spaces.
const password1 = "tést " // PRECIS normalized, with NFC.
func startArgs(t *testing.T, first, isTLS, allowLoginWithoutTLS, setPassword bool, accname string) *testconn {
limitersInit() // Reset rate limiters.
@ -346,7 +349,7 @@ func startArgs(t *testing.T, first, isTLS, allowLoginWithoutTLS, setPassword boo
acc, err := store.OpenAccount(pkglog, accname)
tcheck(t, err, "open account")
if setPassword {
err = acc.SetPassword(pkglog, "testtest")
err = acc.SetPassword(pkglog, password0)
tcheck(t, err, "set password")
}
switchStop := func() {}
@ -405,20 +408,20 @@ func TestLogin(t *testing.T) {
tc.transactf("bad", "login too many args")
tc.transactf("bad", "login") // no args
tc.transactf("no", "login mjl@mox.example badpass")
tc.transactf("no", "login mjl testtest") // must use email, not account
tc.transactf("no", `login mjl "%s"`, password0) // must use email, not account
tc.transactf("no", "login mjl@mox.example test")
tc.transactf("no", "login mjl@mox.example testtesttest")
tc.transactf("no", `login "mjl@mox.example" "testtesttest"`)
tc.transactf("no", "login \"m\xf8x@mox.example\" \"testtesttest\"")
tc.transactf("ok", "login mjl@mox.example testtest")
tc.transactf("ok", `login mjl@mox.example "%s"`, password0)
tc.close()
tc = start(t)
tc.transactf("ok", `login "mjl@mox.example" "testtest"`)
tc.transactf("ok", `login "mjl@mox.example" "%s"`, password0)
tc.close()
tc = start(t)
tc.transactf("ok", `login "\"\"@mox.example" "testtest"`)
tc.transactf("ok", `login "\"\"@mox.example" "%s"`, password0)
defer tc.close()
tc.transactf("bad", "logout badarg")
@ -447,7 +450,7 @@ func TestState(t *testing.T) {
}
// Some commands not allowed when authenticated.
tc.transactf("ok", "login mjl@mox.example testtest")
tc.transactf("ok", `login mjl@mox.example "%s"`, password0)
for _, cmd := range append(append([]string{}, notAuthenticated...), selected...) {
tc.transactf("no", "%s", cmd)
}
@ -472,7 +475,7 @@ func TestLiterals(t *testing.T) {
tc := start(t)
defer tc.close()
tc.client.Login("mjl@mox.example", "testtest")
tc.client.Login("mjl@mox.example", password0)
tc.client.Create("tmpbox")
tc.transactf("ok", "rename {6+}\r\ntmpbox {7+}\r\nntmpbox")
@ -495,7 +498,7 @@ func TestLiterals(t *testing.T) {
func TestScenario(t *testing.T) {
tc := start(t)
defer tc.close()
tc.transactf("ok", "login mjl@mox.example testtest")
tc.transactf("ok", `login mjl@mox.example "%s"`, password0)
tc.transactf("bad", " missingcommand")
@ -573,7 +576,7 @@ func TestScenario(t *testing.T) {
func TestMailbox(t *testing.T) {
tc := start(t)
defer tc.close()
tc.client.Login("mjl@mox.example", "testtest")
tc.client.Login("mjl@mox.example", password0)
invalid := []string{
"e\u0301", // é but as e + acute, not unicode-normalized
@ -595,11 +598,11 @@ func TestMailbox(t *testing.T) {
func TestMailboxDeleted(t *testing.T) {
tc := start(t)
defer tc.close()
tc.client.Login("mjl@mox.example", "testtest")
tc.client.Login("mjl@mox.example", password0)
tc2 := startNoSwitchboard(t)
defer tc2.close()
tc2.client.Login("mjl@mox.example", "testtest")
tc2.client.Login("mjl@mox.example", password0)
tc.client.Create("testbox")
tc2.client.Select("testbox")
@ -631,7 +634,7 @@ func TestMailboxDeleted(t *testing.T) {
func TestID(t *testing.T) {
tc := start(t)
defer tc.close()
tc.client.Login("mjl@mox.example", "testtest")
tc.client.Login("mjl@mox.example", password0)
tc.transactf("ok", "id nil")
tc.xuntagged(imapclient.UntaggedID{"name": "mox", "version": moxvar.Version})
@ -645,7 +648,7 @@ func TestID(t *testing.T) {
func TestSequence(t *testing.T) {
tc := start(t)
defer tc.close()
tc.client.Login("mjl@mox.example", "testtest")
tc.client.Login("mjl@mox.example", password0)
tc.client.Select("inbox")
tc.transactf("bad", "fetch * all") // ../rfc/9051:7018
@ -673,13 +676,13 @@ func TestSequence(t *testing.T) {
func DisabledTestReference(t *testing.T) {
tc := start(t)
defer tc.close()
tc.client.Login("mjl@mox.example", "testtest")
tc.client.Login("mjl@mox.example", password0)
tc.client.Select("inbox")
tc.client.Append("inbox", nil, nil, []byte(exampleMsg))
tc2 := startNoSwitchboard(t)
defer tc2.close()
tc2.client.Login("mjl@mox.example", "testtest")
tc2.client.Login("mjl@mox.example", password0)
tc2.client.Select("inbox")
tc.client.StoreFlagsSet("1", true, `\Deleted`)
@ -687,7 +690,7 @@ func DisabledTestReference(t *testing.T) {
tc3 := startNoSwitchboard(t)
defer tc3.close()
tc3.client.Login("mjl@mox.example", "testtest")
tc3.client.Login("mjl@mox.example", password0)
tc3.transactf("ok", `list "" "inbox" return (status (messages))`)
tc3.xuntagged(imapclient.UntaggedList{Separator: '/', Mailbox: "Inbox"}, imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[string]int64{"MESSAGES": 0}})