Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions common/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -790,12 +790,12 @@ func ValidateConfig() error {
if (Configuration.ListeningType == ListeningSecurely || Configuration.ListeningType == ListeningBoth || Configuration.ListeningType == ListeningSecureUnix) &&
Configuration.NodeType == CSS {
if len(Configuration.ServerCertificate) > 0 && strings.HasPrefix(Configuration.ServerCertificate, "/") {
if _, err := ValidateFilePathWithExtension(Configuration.ServerCertificate, Configuration.PersistenceRootPath, certExtensions); err != nil {
if _, err := ValidateFilePathWithExtension(Configuration.ServerCertificate, "", certExtensions); err != nil {
return &configError{fmt.Sprintf("Invalid ServerCertificate path: %s", err)}
}
}
if len(Configuration.ServerKey) > 0 && strings.HasPrefix(Configuration.ServerKey, "/") {
if _, err := ValidateFilePathWithExtension(Configuration.ServerKey, Configuration.PersistenceRootPath, keyExtensions); err != nil {
if _, err := ValidateFilePathWithExtension(Configuration.ServerKey, "", keyExtensions); err != nil {
return &configError{fmt.Sprintf("Invalid ServerKey path: %s", err)}
}
}
Expand All @@ -804,17 +804,17 @@ func ValidateConfig() error {
// Validate MQTT certificate paths (when using MQTT with SSL)
if mqtt && Configuration.MQTTUseSSL {
if len(Configuration.MQTTCACertificate) > 0 && strings.HasPrefix(Configuration.MQTTCACertificate, "/") {
if _, err := ValidateFilePathWithExtension(Configuration.MQTTCACertificate, Configuration.PersistenceRootPath, certExtensions); err != nil {
if _, err := ValidateFilePathWithExtension(Configuration.MQTTCACertificate, "", certExtensions); err != nil {
return &configError{fmt.Sprintf("Invalid MQTTCACertificate path: %s", err)}
}
}
if len(Configuration.MQTTSSLCert) > 0 && strings.HasPrefix(Configuration.MQTTSSLCert, "/") {
if _, err := ValidateFilePathWithExtension(Configuration.MQTTSSLCert, Configuration.PersistenceRootPath, certExtensions); err != nil {
if _, err := ValidateFilePathWithExtension(Configuration.MQTTSSLCert, "", certExtensions); err != nil {
return &configError{fmt.Sprintf("Invalid MQTTSSLCert path: %s", err)}
}
}
if len(Configuration.MQTTSSLKey) > 0 && strings.HasPrefix(Configuration.MQTTSSLKey, "/") {
if _, err := ValidateFilePathWithExtension(Configuration.MQTTSSLKey, Configuration.PersistenceRootPath, keyExtensions); err != nil {
if _, err := ValidateFilePathWithExtension(Configuration.MQTTSSLKey, "", keyExtensions); err != nil {
return &configError{fmt.Sprintf("Invalid MQTTSSLKey path: %s", err)}
}
}
Expand All @@ -823,7 +823,7 @@ func ValidateConfig() error {
// Validate MongoDB certificate path (when using MongoDB with SSL)
if Configuration.StorageProvider == Mongo && Configuration.MongoUseSSL {
if len(Configuration.MongoCACertificate) > 0 && strings.HasPrefix(Configuration.MongoCACertificate, "/") {
if _, err := ValidateFilePathWithExtension(Configuration.MongoCACertificate, Configuration.PersistenceRootPath, certExtensions); err != nil {
if _, err := ValidateFilePathWithExtension(Configuration.MongoCACertificate, "", certExtensions); err != nil {
return &configError{fmt.Sprintf("Invalid MongoCACertificate path: %s", err)}
}
}
Expand Down
18 changes: 10 additions & 8 deletions common/pathvalidation.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,17 @@ func ValidateFilePath(path, baseDir string) (string, error) {
return "", fmt.Errorf("invalid file path: failed to get absolute path: %w", err)
}

absBase, err := filepath.Abs(baseDir)
if err != nil {
return "", fmt.Errorf("invalid file path: failed to get absolute base directory: %w", err)
}
if baseDir != "" {
absBase, err := filepath.Abs(baseDir)
if err != nil {
return "", fmt.Errorf("invalid file path: failed to get absolute base directory: %w", err)
}

// Ensure the path is within the base directory (CWE-22: Path Traversal)
// The path must either be exactly the base directory or start with base directory + separator
if absPath != absBase && !strings.HasPrefix(absPath, absBase+string(os.PathSeparator)) {
return "", fmt.Errorf("invalid file path: path traversal detected: %s is outside allowed directory %s", absPath, absBase)
// Ensure the path is within the base directory (CWE-22: Path Traversal)
// The path must either be exactly the base directory or start with base directory + separator
if absPath != absBase && !strings.HasPrefix(absPath, absBase+string(os.PathSeparator)) {
return "", fmt.Errorf("invalid file path: path traversal detected: %s is outside allowed directory %s", absPath, absBase)
}
}

return absPath, nil
Expand Down
5 changes: 2 additions & 3 deletions core/base/certificates.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"math/big"
"net"
"os"
"path/filepath"
"time"

"github.com/open-horizon/edge-utilities/logger"
Expand Down Expand Up @@ -55,8 +54,8 @@ func setupCertificates() error {
if len(keyExtensions) == 0 {
keyExtensions = []string{".pem", ".key"}
}
validatedCert, certPathErr := common.ValidateFilePathWithExtension(common.Configuration.ServerCertificate, filepath.Dir(common.Configuration.ServerCertificate), certExtensions)
validatedKey, keyPathErr := common.ValidateFilePathWithExtension(common.Configuration.ServerKey, filepath.Dir(common.Configuration.ServerKey), keyExtensions)
validatedCert, certPathErr := common.ValidateFilePathWithExtension(common.Configuration.ServerCertificate, "", certExtensions)
validatedKey, keyPathErr := common.ValidateFilePathWithExtension(common.Configuration.ServerKey, "", keyExtensions)
if certPathErr == nil && keyPathErr == nil {
_, err = tls.LoadX509KeyPair(validatedCert, validatedKey)
if err == nil {
Expand Down
6 changes: 3 additions & 3 deletions core/communications/mqttCommunication.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ func newTLSConfig() *tls.Config {
if len(certExtensions) == 0 {
certExtensions = []string{".pem", ".crt", ".cert"}
}
validatedPath, pathErr := common.ValidateFilePathWithExtension(caCert, common.Configuration.PersistenceRootPath, certExtensions)
validatedPath, pathErr := common.ValidateFilePathWithExtension(caCert, "", certExtensions)
if pathErr != nil {
// If path validation fails, treat as certificate content rather than file path
pemCerts = []byte(common.Configuration.MQTTCACertificate)
Expand Down Expand Up @@ -429,8 +429,8 @@ func newTLSConfig() *tls.Config {
if len(keyExtensions) == 0 {
keyExtensions = []string{".pem", ".key"}
}
validatedCert, certPathErr := common.ValidateFilePathWithExtension(cert, common.Configuration.PersistenceRootPath, certExtensions)
validatedKey, keyPathErr := common.ValidateFilePathWithExtension(key, common.Configuration.PersistenceRootPath, keyExtensions)
validatedCert, certPathErr := common.ValidateFilePathWithExtension(cert, "", certExtensions)
validatedKey, keyPathErr := common.ValidateFilePathWithExtension(key, "", keyExtensions)

var clientCert tls.Certificate
var err error
Expand Down
22 changes: 11 additions & 11 deletions core/storage/mongoStorage.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,15 +137,15 @@ func (store *MongoStorage) Init() common.SyncServiceError {
// Use SecureString for password
password := common.NewSecureString(common.Configuration.MongoPassword)
defer password.Clear()

credential := options.Credential{
AuthMechanism: common.Configuration.MongoAuthMechanism,
AuthSource: common.Configuration.MongoAuthDbName,
Username: common.Configuration.MongoUsername,
Password: password.String(), // Only convert when needed
}
clientOptions = clientOptions.SetAuth(credential)

// Clear credential password after connection is established
defer func() {
credential.Password = ""
Expand All @@ -164,14 +164,14 @@ func (store *MongoStorage) Init() common.SyncServiceError {
} else {
caFile = common.Configuration.PersistenceRootPath + common.Configuration.MongoCACertificate
}

// Validate certificate file path to prevent path traversal attacks (CWE-22)
var serverCaCert []byte
certExtensions := common.Configuration.AllowedCertificateExtensions
if len(certExtensions) == 0 {
certExtensions = []string{".pem", ".crt", ".cert"}
}
validatedPath, pathErr := common.ValidateFilePathWithExtension(caFile, common.Configuration.PersistenceRootPath, certExtensions)
validatedPath, pathErr := common.ValidateFilePathWithExtension(caFile, "", certExtensions)
if pathErr != nil {
// If path validation fails, treat as certificate content rather than file path
serverCaCert = []byte(common.Configuration.MongoCACertificate)
Expand All @@ -198,22 +198,22 @@ func (store *MongoStorage) Init() common.SyncServiceError {
// SECURITY WARNING: Certificate validation is DISABLED!
// This should NEVER be used in production environments.
// This configuration makes the connection vulnerable to MITM attacks.

if log.IsLogging(logger.ERROR) {
log.Error("SECURITY CRITICAL: MongoDB certificate validation is DISABLED! Connection is vulnerable to MITM attacks. Set MongoAllowInvalidCertificates=false for production.")
}

// Prevent use on CSS (Cloud Sync Service) nodes
if common.Configuration.NodeType == common.CSS {
message := "MongoAllowInvalidCertificates cannot be enabled on CSS nodes for security reasons"
return &Error{message}
}

// Log every connection attempt with disabled validation
if trace.IsLogging(logger.WARNING) {
trace.Warning("Connecting to MongoDB with certificate validation DISABLED")
}

tlsConfig.InsecureSkipVerify = true
}

Expand Down Expand Up @@ -1259,7 +1259,7 @@ func (store *MongoStorage) RemoveObjectTempData(orgID string, objectType string,

id := createTempObjectCollectionID(orgID, objectType, objectID, chunkNumber)
if trace.IsLogging(logger.TRACE) {
trace.Trace("RemoveObjectTempData for org - %s, type - %s, id - %s, chunkNum - %d", orgID, objectType, objectID, chunkNumber)
trace.Trace("RemoveObjectTempData for org - %s, type - %s, id - %s, chunkNum - %d", orgID, objectType, objectID, chunkNumber)
}
fileHandle, _ := store.retrieveObjectTempData(id)

Expand Down Expand Up @@ -1299,7 +1299,7 @@ func (store *MongoStorage) RetrieveObjectTempData(orgID string, objectType strin

id := createTempObjectCollectionID(orgID, objectType, objectID, chunkNumber)
if trace.IsLogging(logger.TRACE) {
trace.Trace("RetrieveObjectTempData for org - %s, type - %s, id - %s, chunkNum - %d", orgID, objectType, objectID, chunkNumber)
trace.Trace("RetrieveObjectTempData for org - %s, type - %s, id - %s, chunkNum - %d", orgID, objectType, objectID, chunkNumber)
}
fileHandle, err := store.retrieveObjectTempData(id)
if err != nil {
Expand All @@ -1316,7 +1316,7 @@ func (store *MongoStorage) RetrieveObjectTempData(orgID string, objectType strin
}

if trace.IsLogging(logger.TRACE) {
trace.Trace("returning %d file readers in the MultiReader from RetrieveObjectTempData", len(readers))
trace.Trace("returning %d file readers in the MultiReader from RetrieveObjectTempData", len(readers))
}
rr := io.MultiReader(readers...)
return rr, nil
Expand Down
Loading