From 78e98c40d35c0b3bc933886ce11cbf2d0cf44c99 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Tue, 7 Jan 2020 08:50:18 -0700 Subject: basicauth: Accept placeholders; move base64 decoding to provision See https://caddy.community/t/v2-basicauth-bug/6738?u=matt --- modules/caddyhttp/caddyauth/basicauth.go | 35 +++++++++++++++++++++++++++----- modules/caddyhttp/caddyauth/caddyfile.go | 18 ++-------------- 2 files changed, 32 insertions(+), 21 deletions(-) (limited to 'modules/caddyhttp') diff --git a/modules/caddyhttp/caddyauth/basicauth.go b/modules/caddyhttp/caddyauth/basicauth.go index 74aa407..e0868c2 100644 --- a/modules/caddyhttp/caddyauth/basicauth.go +++ b/modules/caddyhttp/caddyauth/basicauth.go @@ -15,6 +15,7 @@ package caddyauth import ( + "encoding/base64" "encoding/json" "fmt" "net/http" @@ -66,12 +67,34 @@ func (hba *HTTPBasicAuth) Provision(ctx caddy.Context) error { return fmt.Errorf("hash is required") } + repl := caddy.NewReplacer() + // load account list hba.Accounts = make(map[string]Account) - for _, acct := range hba.AccountList { + for i, acct := range hba.AccountList { if _, ok := hba.Accounts[acct.Username]; ok { - return fmt.Errorf("username is not unique: %s", acct.Username) + return fmt.Errorf("account %d: username is not unique: %s", i, acct.Username) } + + acct.Username = repl.ReplaceAll(acct.Username, "") + acct.Password = repl.ReplaceAll(string(acct.Password), "") + acct.Salt = repl.ReplaceAll(string(acct.Salt), "") + + if acct.Username == "" || acct.Password == "" { + return fmt.Errorf("account %d: username and password are required", i) + } + + acct.password, err = base64.StdEncoding.DecodeString(acct.Password) + if err != nil { + return fmt.Errorf("base64-decoding password: %v", err) + } + if acct.Salt != "" { + acct.salt, err = base64.StdEncoding.DecodeString(acct.Salt) + if err != nil { + return fmt.Errorf("base64-decoding salt: %v", err) + } + } + hba.Accounts[acct.Username] = acct } hba.AccountList = nil // allow GC to deallocate @@ -104,7 +127,7 @@ func (hba HTTPBasicAuth) Authenticate(w http.ResponseWriter, req *http.Request) // don't return early if account does not exist; we want // to try to avoid side-channels that leak existence - same, err := hba.Hash.Compare(account.Password, plaintextPassword, account.Salt) + same, err := hba.Hash.Compare(account.password, plaintextPassword, account.salt) if err != nil { return User{}, false, err } @@ -134,11 +157,13 @@ type Account struct { Username string `json:"username"` // The user's hashed password, base64-encoded. - Password []byte `json:"password"` + Password string `json:"password"` // The user's password salt, base64-encoded; for // algorithms where external salt is needed. - Salt []byte `json:"salt,omitempty"` + Salt string `json:"salt,omitempty"` + + password, salt []byte } // Interface guards diff --git a/modules/caddyhttp/caddyauth/caddyfile.go b/modules/caddyhttp/caddyauth/caddyfile.go index 8a33e6f..31acd61 100644 --- a/modules/caddyhttp/caddyauth/caddyfile.go +++ b/modules/caddyhttp/caddyauth/caddyfile.go @@ -15,8 +15,6 @@ package caddyauth import ( - "encoding/base64" - "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig" "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" @@ -76,22 +74,10 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) return nil, h.Err("username and password cannot be empty or missing") } - pwd, err := base64.StdEncoding.DecodeString(b64Pwd) - if err != nil { - return nil, h.Errf("decoding password: %v", err) - } - var salt []byte - if b64Salt != "" { - salt, err = base64.StdEncoding.DecodeString(b64Salt) - if err != nil { - return nil, h.Errf("decoding salt: %v", err) - } - } - ba.AccountList = append(ba.AccountList, Account{ Username: username, - Password: pwd, - Salt: salt, + Password: b64Pwd, + Salt: b64Salt, }) } } -- cgit v1.2.3