97 lines
2.8 KiB
Go
97 lines
2.8 KiB
Go
package runtimepath
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// CheckKeyLayout 拒绝同时存在新旧密钥,避免误用另一套加密材料。
|
|
func CheckKeyLayout(root string) error {
|
|
legacy, modern := false, false
|
|
for _, name := range BusinessKeyNames() {
|
|
if _, err := os.Lstat(filepath.Join(root, "data", name)); err == nil {
|
|
legacy = true
|
|
} else if !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
if _, err := os.Lstat(groupedKeyPath(root, name)); err == nil {
|
|
modern = true
|
|
if _, err := os.Lstat(filepath.Join(root, "ssl", name)); !os.IsNotExist(err) {
|
|
return errors.New("密钥同时存在于归档目录和旧目录,拒绝自动选择或覆盖")
|
|
}
|
|
} else if !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
if _, err := os.Lstat(filepath.Join(root, "ssl", name)); err == nil {
|
|
modern = true
|
|
} else if !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
}
|
|
if legacy && modern {
|
|
return errors.New("新旧目录同时存在密钥,必须先人工确认迁移,不能自动选择")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DeploymentLayout 识别已迁移部署,旧部署继续只读原路径,不能静默生成替代密钥。
|
|
func DeploymentLayout(root string) bool {
|
|
if _, err := os.Lstat(filepath.Join(root, "data", "credential.key")); err == nil {
|
|
return false
|
|
}
|
|
for _, name := range []string{"data/.config-state.lock", "data/smail-state.db", "ssl/credential.key", "ssl/credentials/credential.key"} {
|
|
info, err := os.Lstat(filepath.Join(root, filepath.FromSlash(name)))
|
|
if err == nil && info.Mode().IsRegular() {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func SecureDirectory(root, name string) (string, error) {
|
|
if !filepath.IsLocal(name) || strings.ContainsAny(name, `/\`) || name == "." {
|
|
return "", errors.New("受保护目录名称无效")
|
|
}
|
|
if err := safeDirectory(root); err != nil {
|
|
return "", err
|
|
}
|
|
directory := filepath.Join(root, name)
|
|
if err := os.Mkdir(directory, 0700); err != nil && !os.IsExist(err) {
|
|
return "", err
|
|
}
|
|
if err := safeDirectory(directory); err != nil {
|
|
return "", err
|
|
}
|
|
return directory, os.Chmod(directory, 0700)
|
|
}
|
|
|
|
func KeyDirectory(root string) string {
|
|
return filepath.Dir(KeyPath(root, "credential.key"))
|
|
}
|
|
|
|
func SSLReference(root, value string) string {
|
|
value = LegacyReference(value)
|
|
if !DeploymentLayout(root) {
|
|
return value
|
|
}
|
|
for _, prefix := range []string{"data/ssl", "data/dkim"} {
|
|
if value == prefix || len(value) > len(prefix) && value[:len(prefix)+1] == prefix+"/" {
|
|
if prefix == "data/dkim" {
|
|
return "ssl/dkim" + value[len(prefix):]
|
|
}
|
|
return "ssl" + value[len(prefix):]
|
|
}
|
|
}
|
|
return value
|
|
}
|
|
|
|
func SSLDirectory(root string) string {
|
|
return filepath.Join(root, filepath.FromSlash(SSLReference(root, "data/ssl")))
|
|
}
|
|
|
|
func DKIMDirectory(root string) string {
|
|
return filepath.Join(root, filepath.FromSlash(SSLReference(root, "data/dkim")))
|
|
}
|