mirror of
https://github.com/lxc/incus
synced 2026-08-02 05:26:46 +00:00
incusd: Use constant-time comparison for secrets
Compare cluster join tokens, certificate add tokens, image secrets and operation secrets in constant time to avoid timing side-channels. Signed-off-by: Stéphane Graber <stgraber@stgraber.org>
This commit is contained in:
parent
0c33952a14
commit
9c6002e864
@ -34,6 +34,7 @@ import (
|
||||
"github.com/lxc/incus/v7/shared/api"
|
||||
"github.com/lxc/incus/v7/shared/logger"
|
||||
localtls "github.com/lxc/incus/v7/shared/tls"
|
||||
"github.com/lxc/incus/v7/shared/util"
|
||||
)
|
||||
|
||||
var certificatesCmd = APIEndpoint{
|
||||
@ -342,7 +343,7 @@ func clusterMemberJoinTokenValid(s *state.State, r *http.Request, projectName st
|
||||
continue
|
||||
}
|
||||
|
||||
if opServerName == joinToken.ServerName && opSecret == joinToken.Secret {
|
||||
if opServerName == joinToken.ServerName && util.CompareSecret(opSecret, joinToken.Secret) {
|
||||
foundOp = op
|
||||
break
|
||||
}
|
||||
@ -406,7 +407,7 @@ func certificateTokenValid(s *state.State, r *http.Request, addToken *api.Certif
|
||||
continue
|
||||
}
|
||||
|
||||
if opSecret == addToken.Secret {
|
||||
if util.CompareSecret(opSecret, addToken.Secret) {
|
||||
foundOp = op
|
||||
break
|
||||
}
|
||||
|
||||
@ -3035,7 +3035,7 @@ func imageValidSecret(s *state.State, r *http.Request, projectName string, finge
|
||||
continue
|
||||
}
|
||||
|
||||
if opSecret == secret {
|
||||
if util.CompareSecret(opSecret, secret) {
|
||||
// Check if the operation is currently running (we allow access while expired).
|
||||
if op.Status == api.Running.String() {
|
||||
// Token is single-use, so cancel it now.
|
||||
|
||||
@ -960,7 +960,7 @@ func operationWaitGet(d *Daemon, r *http.Request) response.Response {
|
||||
// First check if the query is for a local operation from this node
|
||||
op, err := operations.OperationGetInternal(id)
|
||||
if err == nil {
|
||||
if secret != "" && op.Metadata()["secret"] != secret {
|
||||
if secret != "" && !util.CompareSecret(op.Metadata()["secret"], secret) {
|
||||
return response.Forbidden(nil)
|
||||
}
|
||||
|
||||
|
||||
@ -1,12 +1,24 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CompareSecret compares an expected secret (typically read from operation metadata
|
||||
// as an untyped value) against a provided one in constant time.
|
||||
func CompareSecret(expected any, provided string) bool {
|
||||
s, ok := expected.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return subtle.ConstantTimeCompare([]byte(s), []byte(provided)) == 1
|
||||
}
|
||||
|
||||
// ParseUint32Range parses a uint32 range in the form "number" or "start-end".
|
||||
// Returns the start number and the size of the range.
|
||||
func ParseUint32Range(value string) (uint32, uint32, error) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user