Move all source files to Sources/ and rename WireGuardKit targets

Signed-off-by: Andrej Mihajlov <and@mullvad.net>
This commit is contained in:
Andrej Mihajlov
2020-12-02 12:27:39 +01:00
parent 9c38a1b897
commit ec57408570
209 changed files with 54 additions and 58 deletions
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
import NetworkExtension
import WireGuardKit
enum PacketTunnelProviderError: String, Error {
case savedProtocolConfigurationIsInvalid
case dnsResolutionFailure
case couldNotStartBackend
case couldNotDetermineFileDescriptor
case couldNotSetNetworkSettings
}
extension NETunnelProviderProtocol {
convenience init?(tunnelConfiguration: TunnelConfiguration, previouslyFrom old: NEVPNProtocol? = nil) {
self.init()
guard let name = tunnelConfiguration.name else { return nil }
guard let appId = Bundle.main.bundleIdentifier else { return nil }
providerBundleIdentifier = "\(appId).network-extension"
passwordReference = Keychain.makeReference(containing: tunnelConfiguration.asWgQuickConfig(), called: name, previouslyReferencedBy: old?.passwordReference)
if passwordReference == nil {
return nil
}
#if os(macOS)
providerConfiguration = ["UID": getuid()]
#endif
let endpoints = tunnelConfiguration.peers.compactMap { $0.endpoint }
if endpoints.count == 1 {
serverAddress = endpoints[0].stringRepresentation
} else if endpoints.isEmpty {
serverAddress = "Unspecified"
} else {
serverAddress = "Multiple endpoints"
}
}
func asTunnelConfiguration(called name: String? = nil) -> TunnelConfiguration? {
if let passwordReference = passwordReference,
let config = Keychain.openReference(called: passwordReference) {
return try? TunnelConfiguration(fromWgQuickConfig: config, called: name)
}
if let oldConfig = providerConfiguration?["WgQuickConfig"] as? String {
return try? TunnelConfiguration(fromWgQuickConfig: oldConfig, called: name)
}
return nil
}
func destroyConfigurationReference() {
guard let ref = passwordReference else { return }
Keychain.deleteReference(called: ref)
}
func verifyConfigurationReference() -> Bool {
guard let ref = passwordReference else { return false }
return Keychain.verifyReference(called: ref)
}
@discardableResult
func migrateConfigurationIfNeeded(called name: String) -> Bool {
/* This is how we did things before we switched to putting items
* in the keychain. But it's still useful to keep the migration
* around so that .mobileconfig files are easier.
*/
if let oldConfig = providerConfiguration?["WgQuickConfig"] as? String {
#if os(macOS)
providerConfiguration = ["UID": getuid()]
#elseif os(iOS)
providerConfiguration = nil
#else
#error("Unimplemented")
#endif
guard passwordReference == nil else { return true }
wg_log(.debug, message: "Migrating tunnel configuration '\(name)'")
passwordReference = Keychain.makeReference(containing: oldConfig, called: name)
return true
}
#if os(macOS)
if passwordReference != nil && providerConfiguration?["UID"] == nil && verifyConfigurationReference() {
providerConfiguration = ["UID": getuid()]
return true
}
#endif
return false
}
}
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
import Foundation
extension String {
func splitToArray(separator: Character = ",", trimmingCharacters: CharacterSet? = nil) -> [String] {
return split(separator: separator)
.map {
if let charSet = trimmingCharacters {
return $0.trimmingCharacters(in: charSet)
} else {
return String($0)
}
}
}
}
extension Optional where Wrapped == String {
func splitToArray(separator: Character = ",", trimmingCharacters: CharacterSet? = nil) -> [String] {
switch self {
case .none:
return []
case .some(let wrapped):
return wrapped.splitToArray(separator: separator, trimmingCharacters: trimmingCharacters)
}
}
}
@@ -0,0 +1,248 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
import Foundation
import WireGuardKit
extension TunnelConfiguration {
enum ParserState {
case inInterfaceSection
case inPeerSection
case notInASection
}
enum ParseError: Error {
case invalidLine(String.SubSequence)
case noInterface
case multipleInterfaces
case interfaceHasNoPrivateKey
case interfaceHasInvalidPrivateKey(String)
case interfaceHasInvalidListenPort(String)
case interfaceHasInvalidAddress(String)
case interfaceHasInvalidDNS(String)
case interfaceHasInvalidMTU(String)
case interfaceHasUnrecognizedKey(String)
case peerHasNoPublicKey
case peerHasInvalidPublicKey(String)
case peerHasInvalidPreSharedKey(String)
case peerHasInvalidAllowedIP(String)
case peerHasInvalidEndpoint(String)
case peerHasInvalidPersistentKeepAlive(String)
case peerHasInvalidTransferBytes(String)
case peerHasInvalidLastHandshakeTime(String)
case peerHasUnrecognizedKey(String)
case multiplePeersWithSamePublicKey
case multipleEntriesForKey(String)
}
convenience init(fromWgQuickConfig wgQuickConfig: String, called name: String? = nil) throws {
var interfaceConfiguration: InterfaceConfiguration?
var peerConfigurations = [PeerConfiguration]()
let lines = wgQuickConfig.split { $0.isNewline }
var parserState = ParserState.notInASection
var attributes = [String: String]()
for (lineIndex, line) in lines.enumerated() {
var trimmedLine: String
if let commentRange = line.range(of: "#") {
trimmedLine = String(line[..<commentRange.lowerBound])
} else {
trimmedLine = String(line)
}
trimmedLine = trimmedLine.trimmingCharacters(in: .whitespacesAndNewlines)
let lowercasedLine = trimmedLine.lowercased()
if !trimmedLine.isEmpty {
if let equalsIndex = trimmedLine.firstIndex(of: "=") {
// Line contains an attribute
let keyWithCase = trimmedLine[..<equalsIndex].trimmingCharacters(in: .whitespacesAndNewlines)
let key = keyWithCase.lowercased()
let value = trimmedLine[trimmedLine.index(equalsIndex, offsetBy: 1)...].trimmingCharacters(in: .whitespacesAndNewlines)
let keysWithMultipleEntriesAllowed: Set<String> = ["address", "allowedips", "dns"]
if let presentValue = attributes[key] {
if keysWithMultipleEntriesAllowed.contains(key) {
attributes[key] = presentValue + "," + value
} else {
throw ParseError.multipleEntriesForKey(keyWithCase)
}
} else {
attributes[key] = value
}
let interfaceSectionKeys: Set<String> = ["privatekey", "listenport", "address", "dns", "mtu"]
let peerSectionKeys: Set<String> = ["publickey", "presharedkey", "allowedips", "endpoint", "persistentkeepalive"]
if parserState == .inInterfaceSection {
guard interfaceSectionKeys.contains(key) else {
throw ParseError.interfaceHasUnrecognizedKey(keyWithCase)
}
} else if parserState == .inPeerSection {
guard peerSectionKeys.contains(key) else {
throw ParseError.peerHasUnrecognizedKey(keyWithCase)
}
}
} else if lowercasedLine != "[interface]" && lowercasedLine != "[peer]" {
throw ParseError.invalidLine(line)
}
}
let isLastLine = lineIndex == lines.count - 1
if isLastLine || lowercasedLine == "[interface]" || lowercasedLine == "[peer]" {
// Previous section has ended; process the attributes collected so far
if parserState == .inInterfaceSection {
let interface = try TunnelConfiguration.collate(interfaceAttributes: attributes)
guard interfaceConfiguration == nil else { throw ParseError.multipleInterfaces }
interfaceConfiguration = interface
} else if parserState == .inPeerSection {
let peer = try TunnelConfiguration.collate(peerAttributes: attributes)
peerConfigurations.append(peer)
}
}
if lowercasedLine == "[interface]" {
parserState = .inInterfaceSection
attributes.removeAll()
} else if lowercasedLine == "[peer]" {
parserState = .inPeerSection
attributes.removeAll()
}
}
let peerPublicKeysArray = peerConfigurations.map { $0.publicKey }
let peerPublicKeysSet = Set<PublicKey>(peerPublicKeysArray)
if peerPublicKeysArray.count != peerPublicKeysSet.count {
throw ParseError.multiplePeersWithSamePublicKey
}
if let interfaceConfiguration = interfaceConfiguration {
self.init(name: name, interface: interfaceConfiguration, peers: peerConfigurations)
} else {
throw ParseError.noInterface
}
}
func asWgQuickConfig() -> String {
var output = "[Interface]\n"
output.append("PrivateKey = \(interface.privateKey.base64Key)\n")
if let listenPort = interface.listenPort {
output.append("ListenPort = \(listenPort)\n")
}
if !interface.addresses.isEmpty {
let addressString = interface.addresses.map { $0.stringRepresentation }.joined(separator: ", ")
output.append("Address = \(addressString)\n")
}
if !interface.dns.isEmpty {
let dnsString = interface.dns.map { $0.stringRepresentation }.joined(separator: ", ")
output.append("DNS = \(dnsString)\n")
}
if let mtu = interface.mtu {
output.append("MTU = \(mtu)\n")
}
for peer in peers {
output.append("\n[Peer]\n")
output.append("PublicKey = \(peer.publicKey.base64Key)\n")
if let preSharedKey = peer.preSharedKey?.base64Key {
output.append("PresharedKey = \(preSharedKey)\n")
}
if !peer.allowedIPs.isEmpty {
let allowedIPsString = peer.allowedIPs.map { $0.stringRepresentation }.joined(separator: ", ")
output.append("AllowedIPs = \(allowedIPsString)\n")
}
if let endpoint = peer.endpoint {
output.append("Endpoint = \(endpoint.stringRepresentation)\n")
}
if let persistentKeepAlive = peer.persistentKeepAlive {
output.append("PersistentKeepalive = \(persistentKeepAlive)\n")
}
}
return output
}
private static func collate(interfaceAttributes attributes: [String: String]) throws -> InterfaceConfiguration {
guard let privateKeyString = attributes["privatekey"] else {
throw ParseError.interfaceHasNoPrivateKey
}
guard let privateKey = PrivateKey(base64Key: privateKeyString) else {
throw ParseError.interfaceHasInvalidPrivateKey(privateKeyString)
}
var interface = InterfaceConfiguration(privateKey: privateKey)
if let listenPortString = attributes["listenport"] {
guard let listenPort = UInt16(listenPortString) else {
throw ParseError.interfaceHasInvalidListenPort(listenPortString)
}
interface.listenPort = listenPort
}
if let addressesString = attributes["address"] {
var addresses = [IPAddressRange]()
for addressString in addressesString.splitToArray(trimmingCharacters: .whitespacesAndNewlines) {
guard let address = IPAddressRange(from: addressString) else {
throw ParseError.interfaceHasInvalidAddress(addressString)
}
addresses.append(address)
}
interface.addresses = addresses
}
if let dnsString = attributes["dns"] {
var dnsServers = [DNSServer]()
for dnsServerString in dnsString.splitToArray(trimmingCharacters: .whitespacesAndNewlines) {
guard let dnsServer = DNSServer(from: dnsServerString) else {
throw ParseError.interfaceHasInvalidDNS(dnsServerString)
}
dnsServers.append(dnsServer)
}
interface.dns = dnsServers
}
if let mtuString = attributes["mtu"] {
guard let mtu = UInt16(mtuString) else {
throw ParseError.interfaceHasInvalidMTU(mtuString)
}
interface.mtu = mtu
}
return interface
}
private static func collate(peerAttributes attributes: [String: String]) throws -> PeerConfiguration {
guard let publicKeyString = attributes["publickey"] else {
throw ParseError.peerHasNoPublicKey
}
guard let publicKey = PublicKey(base64Key: publicKeyString) else {
throw ParseError.peerHasInvalidPublicKey(publicKeyString)
}
var peer = PeerConfiguration(publicKey: publicKey)
if let preSharedKeyString = attributes["presharedkey"] {
guard let preSharedKey = PreSharedKey(base64Key: preSharedKeyString) else {
throw ParseError.peerHasInvalidPreSharedKey(preSharedKeyString)
}
peer.preSharedKey = preSharedKey
}
if let allowedIPsString = attributes["allowedips"] {
var allowedIPs = [IPAddressRange]()
for allowedIPString in allowedIPsString.splitToArray(trimmingCharacters: .whitespacesAndNewlines) {
guard let allowedIP = IPAddressRange(from: allowedIPString) else {
throw ParseError.peerHasInvalidAllowedIP(allowedIPString)
}
allowedIPs.append(allowedIP)
}
peer.allowedIPs = allowedIPs
}
if let endpointString = attributes["endpoint"] {
guard let endpoint = Endpoint(from: endpointString) else {
throw ParseError.peerHasInvalidEndpoint(endpointString)
}
peer.endpoint = endpoint
}
if let persistentKeepAliveString = attributes["persistentkeepalive"] {
guard let persistentKeepAlive = UInt16(persistentKeepAliveString) else {
throw ParseError.peerHasInvalidPersistentKeepAlive(persistentKeepAliveString)
}
peer.persistentKeepAlive = persistentKeepAlive
}
return peer
}
}