aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/conf/store.go
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2019-02-25 18:45:32 +0100
committerJason A. Donenfeld <Jason@zx2c4.com>2019-02-28 08:05:02 +0100
commit840f33de326233d5fee1144334db41bf5c82a8fa (patch)
tree43070181e30db403dfad69f3e67a566ba589df4e /conf/store.go
parentInitial scaffolding (diff)
downloadwireguard-windows-840f33de326233d5fee1144334db41bf5c82a8fa.tar.xz
wireguard-windows-840f33de326233d5fee1144334db41bf5c82a8fa.zip
conf: introduce configuration management
Diffstat (limited to 'conf/store.go')
-rw-r--r--conf/store.go199
1 files changed, 199 insertions, 0 deletions
diff --git a/conf/store.go b/conf/store.go
new file mode 100644
index 00000000..7c110865
--- /dev/null
+++ b/conf/store.go
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package conf
+
+import (
+ "errors"
+ "golang.zx2c4.com/wireguard/windows/conf/dpapi"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+const configFileSuffix = ".conf.dpapi"
+const configFileUnencryptedSuffix = ".conf"
+
+func ListConfigNames() ([]string, error) {
+ configFileDir, err := resolveConfigFileDir()
+ if err != nil {
+ return nil, err
+ }
+ files, err := ioutil.ReadDir(configFileDir)
+ if err != nil {
+ return nil, err
+ }
+ configs := make([]string, len(files))
+ i := 0
+ for _, file := range files {
+ name := filepath.Base(file.Name())
+ if len(name) <= len(configFileSuffix) || !strings.HasSuffix(name, configFileSuffix) {
+ continue
+ }
+ if !file.Mode().IsRegular() || file.Mode().Perm()&0444 == 0 {
+ continue
+ }
+ configs[i] = strings.TrimSuffix(name, configFileSuffix)
+ i++
+ }
+ return configs[:i], nil
+}
+
+func MigrateUnencryptedConfigs() (int, []error) {
+ configFileDir, err := resolveConfigFileDir()
+ if err != nil {
+ return 0, []error{err}
+ }
+ files, err := ioutil.ReadDir(configFileDir)
+ if err != nil {
+ return 0, []error{err}
+ }
+ errs := make([]error, len(files))
+ i := 0
+ e := 0
+ for _, file := range files {
+ path := filepath.Join(configFileDir, file.Name())
+ name := filepath.Base(file.Name())
+ if len(name) <= len(configFileUnencryptedSuffix) || !strings.HasSuffix(name, configFileUnencryptedSuffix) {
+ continue
+ }
+ if !file.Mode().IsRegular() || file.Mode().Perm()&0444 == 0 {
+ continue
+ }
+
+ // We don't use ioutil's ReadFile, because we actually want RDWR, so that we can take advantage
+ // of Windows file locking for ensuring the file is finished being written.
+ f, err := os.OpenFile(path, os.O_RDWR, 0)
+ if err != nil {
+ errs[e] = err
+ e++
+ continue
+ }
+ bytes, err := ioutil.ReadAll(f)
+ f.Close()
+ if err != nil {
+ errs[e] = err
+ e++
+ continue
+ }
+ _, err = FromWgQuick(string(bytes), "input")
+ if err != nil {
+ errs[e] = err
+ e++
+ continue
+ }
+
+ bytes, err = dpapi.Encrypt(bytes, strings.TrimSuffix(name, configFileUnencryptedSuffix))
+ if err != nil {
+ errs[e] = err
+ e++
+ continue
+ }
+ dstFile := strings.TrimSuffix(path, configFileUnencryptedSuffix) + configFileSuffix
+ if _, err = os.Stat(dstFile); err != nil && !os.IsNotExist(err) {
+ errs[e] = errors.New("Unable to migrate to " + dstFile + " as it already exists")
+ e++
+ continue
+ }
+ err = ioutil.WriteFile(dstFile, bytes, 0600)
+ if err != nil {
+ errs[e] = err
+ e++
+ continue
+ }
+ err = os.Remove(path)
+ if err != nil && os.Remove(dstFile) == nil {
+ errs[e] = err
+ e++
+ continue
+ }
+ i++
+ }
+ return i, errs[:e]
+}
+
+func LoadFromName(name string) (*Config, error) {
+ configFileDir, err := resolveConfigFileDir()
+ if err != nil {
+ return nil, err
+ }
+ return LoadFromPath(filepath.Join(configFileDir, name+configFileSuffix))
+}
+
+func LoadFromPath(path string) (*Config, error) {
+ name, err := NameFromPath(path)
+ if err != nil {
+ return nil, err
+ }
+ bytes, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+ if strings.HasSuffix(path, configFileSuffix) {
+ bytes, err = dpapi.Decrypt(bytes, name)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return FromWgQuick(string(bytes), name)
+}
+
+func NameFromPath(path string) (string, error) {
+ name := filepath.Base(path)
+ if !((len(name) > len(configFileSuffix) && strings.HasSuffix(name, configFileSuffix)) ||
+ (len(name) > len(configFileUnencryptedSuffix) && strings.HasSuffix(name, configFileUnencryptedSuffix))) {
+ return "", errors.New("Path must end in either " + configFileSuffix + " or " + configFileUnencryptedSuffix)
+ }
+ if strings.HasSuffix(path, configFileSuffix) {
+ name = strings.TrimSuffix(name, configFileSuffix)
+ } else {
+ name = strings.TrimSuffix(name, configFileUnencryptedSuffix)
+ }
+ return name, nil
+}
+
+func (config *Config) Save() error {
+ configFileDir, err := resolveConfigFileDir()
+ if err != nil {
+ return err
+ }
+ filename := filepath.Join(configFileDir, config.Name+configFileSuffix)
+ bytes := []byte(config.ToWgQuick())
+ bytes, err = dpapi.Encrypt(bytes, config.Name)
+ if err != nil {
+ return err
+ }
+ err = ioutil.WriteFile(filename+".tmp", bytes, 0600)
+ if err != nil {
+ return err
+ }
+ err = os.Rename(filename+".tmp", filename)
+ if err != nil {
+ os.Remove(filename + ".tmp")
+ return err
+ }
+ return nil
+}
+
+func (config *Config) Path() (string, error) {
+ configFileDir, err := resolveConfigFileDir()
+ if err != nil {
+ return "", err
+ }
+ return filepath.Join(configFileDir, config.Name+configFileSuffix), nil
+}
+
+func DeleteName(name string) error {
+ configFileDir, err := resolveConfigFileDir()
+ if err != nil {
+ return err
+ }
+ return os.Remove(filepath.Join(configFileDir, name+configFileSuffix))
+}
+
+func (config *Config) Delete() error {
+ return DeleteName(config.Name)
+}