|
|
@ -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
|
|
|
|