/* SPDX-License-Identifier: MIT * * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved. */ package conf import ( "errors" "os" "path/filepath" "strings" "golang.zx2c4.com/wireguard/windows/conf/dpapi" ) const configFileSuffix = ".conf.dpapi" const configFileUnencryptedSuffix = ".conf" func ListConfigNames() ([]string, error) { configFileDir, err := tunnelConfigurationsDirectory() if err != nil { return nil, err } files, err := os.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.Type().IsRegular() { continue } info, err := file.Info() if err != nil { continue } if info.Mode().Perm()&0444 == 0 { continue } name = strings.TrimSuffix(name, configFileSuffix) if !TunnelNameIsValid(name) { continue } configs[i] = name i++ } return configs[:i], nil } func LoadFromName(name string) (*Config, error) { configFileDir, err := tunnelConfigurationsDirectory() 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 := os.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 FromWgQuickWithUnknownEncoding(string(bytes), name) } func PathIsEncrypted(path string) bool { return strings.HasSuffix(filepath.Base(path), configFileSuffix) } 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) } if !TunnelNameIsValid(name) { return "", errors.New("Tunnel name is not valid") } return name, nil } func (config *Config) Save(overwrite bool) error { if !TunnelNameIsValid(config.Name) { return errors.New("Tunnel name is not valid") } configFileDir, err := tunnelConfigurationsDirectory() 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 } return writeLockedDownFile(filename, overwrite, bytes) } func (config *Config) Path() (string, error) { if !TunnelNameIsValid(config.Name) { return "", errors.New("Tunnel name is not valid") } configFileDir, err := tunnelConfigurationsDirectory() if err != nil { return "", err } return filepath.Join(configFileDir, config.Name+configFileSuffix), nil } func DeleteName(name string) error { if !TunnelNameIsValid(name) { return errors.New("Tunnel name is not valid") } configFileDir, err := tunnelConfigurationsDirectory() if err != nil { return err } return os.Remove(filepath.Join(configFileDir, name+configFileSuffix)) } func (config *Config) Delete() error { return DeleteName(config.Name) }