diff --git a/testdata/httpaccount/domains.conf b/testdata/httpaccount/domains.conf index 515602f..8931695 100644 --- a/testdata/httpaccount/domains.conf +++ b/testdata/httpaccount/domains.conf @@ -1,11 +1,11 @@ Domains: mox.example: nil Accounts: - mjl: + mjl☺: Domain: mox.example FullName: mjl Destinations: - mjl@mox.example: + mjl☺@mox.example: Mailbox: Inbox Rulesets: - diff --git a/testdata/httpaccount/mox.conf b/testdata/httpaccount/mox.conf index e1286db..f92097f 100644 --- a/testdata/httpaccount/mox.conf +++ b/testdata/httpaccount/mox.conf @@ -3,7 +3,7 @@ User: 1000 LogLevel: trace Hostname: mox.example Postmaster: - Account: mjl + Account: mjl☺ Mailbox: postmaster Listeners: local: nil diff --git a/webaccount/account_test.go b/webaccount/account_test.go index e5a2d67..3df521a 100644 --- a/webaccount/account_test.go +++ b/webaccount/account_test.go @@ -79,7 +79,7 @@ func TestAccount(t *testing.T) { mox.ConfigDynamicPath = filepath.Join(filepath.Dir(mox.ConfigStaticPath), "domains.conf") mox.MustLoadConfig(true, false) log := mlog.New("webaccount", nil) - acc, err := store.OpenAccount(log, "mjl") + acc, err := store.OpenAccount(log, "mjl☺") tcheck(t, err, "open account") err = acc.SetPassword(log, "test1234") tcheck(t, err, "set password") @@ -99,14 +99,14 @@ func TestAccount(t *testing.T) { ctx := context.WithValue(ctxbg, requestInfoCtxKey, reqInfo) // Missing login token. - tneedErrorCode(t, "user:error", func() { api.Login(ctx, "", "mjl@mox.example", "test1234") }) + tneedErrorCode(t, "user:error", func() { api.Login(ctx, "", "mjl☺@mox.example", "test1234") }) // Login with loginToken. loginCookie := &http.Cookie{Name: "webaccountlogin"} loginCookie.Value = api.LoginPrep(ctx) reqInfo.Request.Header = http.Header{"Cookie": []string{loginCookie.String()}} - csrfToken := api.Login(ctx, loginCookie.Value, "mjl@mox.example", "test1234") + csrfToken := api.Login(ctx, loginCookie.Value, "mjl☺@mox.example", "test1234") var sessionCookie *http.Cookie for _, c := range respRec.Result().Cookies() { if c.Name == "webaccountsession" { @@ -121,7 +121,7 @@ func TestAccount(t *testing.T) { // Valid loginToken, but bad credentials. loginCookie.Value = api.LoginPrep(ctx) reqInfo.Request.Header = http.Header{"Cookie": []string{loginCookie.String()}} - tneedErrorCode(t, "user:loginFailed", func() { api.Login(ctx, loginCookie.Value, "mjl@mox.example", "badauth") }) + tneedErrorCode(t, "user:loginFailed", func() { api.Login(ctx, loginCookie.Value, "mjl☺@mox.example", "badauth") }) tneedErrorCode(t, "user:loginFailed", func() { api.Login(ctx, loginCookie.Value, "baduser@mox.example", "badauth") }) tneedErrorCode(t, "user:loginFailed", func() { api.Login(ctx, loginCookie.Value, "baduser@baddomain.example", "badauth") }) @@ -211,13 +211,13 @@ func TestAccount(t *testing.T) { // SetPassword needs the token. sessionToken := store.SessionToken(strings.SplitN(sessionCookie.Value, " ", 2)[0]) - reqInfo = requestInfo{"mjl@mox.example", "mjl", sessionToken, respRec, &http.Request{RemoteAddr: "127.0.0.1:1234"}} + reqInfo = requestInfo{"mjl☺@mox.example", "mjl☺", sessionToken, respRec, &http.Request{RemoteAddr: "127.0.0.1:1234"}} ctx = context.WithValue(ctxbg, requestInfoCtxKey, reqInfo) api.SetPassword(ctx, "test1234") fullName, _, dests, _, _ := api.Account(ctx) - api.DestinationSave(ctx, "mjl@mox.example", dests["mjl@mox.example"], dests["mjl@mox.example"]) // todo: save modified value and compare it afterwards + api.DestinationSave(ctx, "mjl☺@mox.example", dests["mjl☺@mox.example"], dests["mjl☺@mox.example"]) // todo: save modified value and compare it afterwards api.AccountSaveFullName(ctx, fullName+" changed") // todo: check if value was changed api.AccountSaveFullName(ctx, fullName) diff --git a/webauth/webauth.go b/webauth/webauth.go index 21bf43b..6af82ee 100644 --- a/webauth/webauth.go +++ b/webauth/webauth.go @@ -41,6 +41,7 @@ import ( "log/slog" "net" "net/http" + "net/url" "strings" "time" @@ -154,9 +155,15 @@ func Check(ctx context.Context, log mlog.Log, sessionAuth SessionAuth, kind stri return "", "", "", false } sessionToken = store.SessionToken(t[0]) - accountName = t[1] var err error + accountName, err = url.QueryUnescape(t[1]) + if err != nil { + time.Sleep(BadAuthDelay) + respondAuthError("user:badAuth", "malformed session account name") + return "", "", "", false + } + loginAddress, err = sessionAuth.use(ctx, log, accountName, sessionToken, csrfToken) if err != nil { time.Sleep(BadAuthDelay) @@ -258,7 +265,8 @@ func Login(ctx context.Context, log mlog.Log, sessionAuth SessionAuth, kind, coo // Add session cookie. http.SetCookie(w, &http.Cookie{ Name: kind + "session", - Value: string(sessionToken) + " " + accountName, + // Cookies values are ascii only, so we keep the account name query escaped. + Value: string(sessionToken) + " " + url.QueryEscape(accountName), Path: cookiePath, Secure: isHTTPS(isForwarded, r), HttpOnly: true,