Improve some things about OpenVPN.Configuration

- Treat empty passphrase as no passphrase
- Parse authentication requirement from --auth-user-pass
- Overload ConfigurationParser with String parameter
- Move OpenVPN fallbacks inline with builder

Give a withFallbacks: option to initialize basic fields rather
than leaving them nil.
This commit is contained in:
Davide De Rosa 2022-02-12 08:37:51 +01:00
parent 88544e4877
commit c019cecbe0
3 changed files with 84 additions and 30 deletions

View File

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Added
- Parse OpenVPN authentication requirement from `--auth-user-pass`.
## 4.1.0 (2022-02-09) ## 4.1.0 (2022-02-09)
### Added ### Added

View File

@ -242,6 +242,9 @@ extension OpenVPN {
/// The tunnel MTU. /// The tunnel MTU.
public var mtu: Int? public var mtu: Int?
/// Requires username authentication.
public var authUserPass: Bool?
// MARK: Server // MARK: Server
/// The auth-token returned by the server. /// The auth-token returned by the server.
@ -300,7 +303,18 @@ extension OpenVPN {
/// Policies for redirecting traffic through the VPN gateway. /// Policies for redirecting traffic through the VPN gateway.
public var routingPolicies: [RoutingPolicy]? public var routingPolicies: [RoutingPolicy]?
public init() { /**
Creates a `ConfigurationBuilder`.
- Parameter withFallbacks: If `true`, initializes builder with fallback values rather than nil.
*/
public init(withFallbacks: Bool = false) {
if withFallbacks {
cipher = Fallback.cipher
digest = Fallback.digest
compressionFraming = Fallback.compressionFraming
compressionAlgorithm = Fallback.compressionAlgorithm
}
} }
/** /**
@ -332,6 +346,7 @@ extension OpenVPN {
randomizeEndpoint: randomizeEndpoint, randomizeEndpoint: randomizeEndpoint,
usesPIAPatches: usesPIAPatches, usesPIAPatches: usesPIAPatches,
mtu: mtu, mtu: mtu,
authUserPass: authUserPass,
authToken: authToken, authToken: authToken,
peerId: peerId, peerId: peerId,
ipv4: ipv4, ipv4: ipv4,
@ -348,24 +363,6 @@ extension OpenVPN {
routingPolicies: routingPolicies routingPolicies: routingPolicies
) )
} }
// MARK: Shortcuts
public var fallbackCipher: Cipher {
return cipher ?? Fallback.cipher
}
public var fallbackDigest: Digest {
return digest ?? Fallback.digest
}
public var fallbackCompressionFraming: CompressionFraming {
return compressionFraming ?? Fallback.compressionFraming
}
public var fallbackCompressionAlgorithm: CompressionAlgorithm {
return compressionAlgorithm ?? Fallback.compressionAlgorithm
}
} }
/// The immutable configuration for `OpenVPNSession`. /// The immutable configuration for `OpenVPNSession`.
@ -437,6 +434,9 @@ extension OpenVPN {
/// - Seealso: `ConfigurationBuilder.mtu` /// - Seealso: `ConfigurationBuilder.mtu`
public let mtu: Int? public let mtu: Int?
/// - Seealso: `ConfigurationBuilder.authUserPass`
public let authUserPass: Bool?
/// - Seealso: `ConfigurationBuilder.authToken` /// - Seealso: `ConfigurationBuilder.authToken`
public let authToken: String? public let authToken: String?
@ -502,15 +502,16 @@ extension OpenVPN.Configuration {
/** /**
Returns a `ConfigurationBuilder` to use this configuration as a starting point for a new one. Returns a `ConfigurationBuilder` to use this configuration as a starting point for a new one.
- Parameter withFallbacks: If `true`, initializes builder with fallback values rather than nil.
- Returns: An editable `ConfigurationBuilder` initialized with this configuration. - Returns: An editable `ConfigurationBuilder` initialized with this configuration.
*/ */
public func builder() -> OpenVPN.ConfigurationBuilder { public func builder(withFallbacks: Bool = false) -> OpenVPN.ConfigurationBuilder {
var builder = OpenVPN.ConfigurationBuilder() var builder = OpenVPN.ConfigurationBuilder()
builder.cipher = cipher builder.cipher = cipher ?? (withFallbacks ? OpenVPN.Fallback.cipher : nil)
builder.dataCiphers = dataCiphers builder.dataCiphers = dataCiphers
builder.digest = digest builder.digest = digest ?? (withFallbacks ? OpenVPN.Fallback.digest : nil)
builder.compressionFraming = compressionFraming builder.compressionFraming = compressionFraming ?? (withFallbacks ? OpenVPN.Fallback.compressionFraming : nil)
builder.compressionAlgorithm = compressionAlgorithm builder.compressionAlgorithm = compressionAlgorithm ?? (withFallbacks ? OpenVPN.Fallback.compressionAlgorithm : nil)
builder.ca = ca builder.ca = ca
builder.clientCertificate = clientCertificate builder.clientCertificate = clientCertificate
builder.clientKey = clientKey builder.clientKey = clientKey
@ -527,6 +528,7 @@ extension OpenVPN.Configuration {
builder.randomizeEndpoint = randomizeEndpoint builder.randomizeEndpoint = randomizeEndpoint
builder.usesPIAPatches = usesPIAPatches builder.usesPIAPatches = usesPIAPatches
builder.mtu = mtu builder.mtu = mtu
builder.authUserPass = authUserPass
builder.authToken = authToken builder.authToken = authToken
builder.peerId = peerId builder.peerId = peerId
builder.ipv4 = ipv4 builder.ipv4 = ipv4
@ -549,7 +551,6 @@ extension OpenVPN.Configuration {
// MARK: Encoding // MARK: Encoding
extension OpenVPN.Configuration { extension OpenVPN.Configuration {
public func print() { public func print() {
guard let endpointProtocols = endpointProtocols else { guard let endpointProtocols = endpointProtocols else {
fatalError("No sessionConfiguration.endpointProtocols set") fatalError("No sessionConfiguration.endpointProtocols set")
@ -558,6 +559,7 @@ extension OpenVPN.Configuration {
log.info("\tCipher: \(fallbackCipher)") log.info("\tCipher: \(fallbackCipher)")
log.info("\tDigest: \(fallbackDigest)") log.info("\tDigest: \(fallbackDigest)")
log.info("\tCompression framing: \(fallbackCompressionFraming)") log.info("\tCompression framing: \(fallbackCompressionFraming)")
log.info("\tUsername authentication: \(authUserPass ?? false)")
if let compressionAlgorithm = compressionAlgorithm, compressionAlgorithm != .disabled { if let compressionAlgorithm = compressionAlgorithm, compressionAlgorithm != .disabled {
log.info("\tCompression algorithm: \(compressionAlgorithm)") log.info("\tCompression algorithm: \(compressionAlgorithm)")
} else { } else {

View File

@ -78,6 +78,8 @@ extension OpenVPN {
static let remote = NSRegularExpression("^remote +[^ ]+( +\\d+)?( +(udp[46]?|tcp[46]?))?") static let remote = NSRegularExpression("^remote +[^ ]+( +\\d+)?( +(udp[46]?|tcp[46]?))?")
static let authUserPass = NSRegularExpression("^auth-user-pass")
static let eku = NSRegularExpression("^remote-cert-tls +server") static let eku = NSRegularExpression("^remote-cert-tls +server")
static let remoteRandom = NSRegularExpression("^remote-random") static let remoteRandom = NSRegularExpression("^remote-random")
@ -178,7 +180,7 @@ extension OpenVPN {
} }
/** /**
Parses an .ovpn file from an URL. Parses a configuration from a .ovpn file.
- Parameter url: The URL of the configuration file. - Parameter url: The URL of the configuration file.
- Parameter passphrase: The optional passphrase for encrypted data. - Parameter passphrase: The optional passphrase for encrypted data.
@ -187,8 +189,39 @@ extension OpenVPN {
- Throws: `ConfigurationError` if the configuration file is wrong or incomplete. - Throws: `ConfigurationError` if the configuration file is wrong or incomplete.
*/ */
public static func parsed(fromURL url: URL, passphrase: String? = nil, returnsStripped: Bool = false) throws -> Result { public static func parsed(fromURL url: URL, passphrase: String? = nil, returnsStripped: Bool = false) throws -> Result {
let lines = try String(contentsOf: url).trimmedLines() let contents = try String(contentsOf: url)
return try parsed(fromLines: lines, isClient: true, passphrase: passphrase, originalURL: url, returnsStripped: returnsStripped) return try parsed(
fromContents: contents,
passphrase: passphrase,
originalURL: url,
returnsStripped: returnsStripped
)
}
/**
Parses a configuration from a string.
- Parameter contents: The contents of the configuration file.
- Parameter passphrase: The optional passphrase for encrypted data.
- Parameter originalURL: The optional original URL of the configuration file.
- Parameter returnsStripped: When `true`, stores the stripped file into `Result.strippedLines`. Defaults to `false`.
- Returns: The `Result` outcome of the parsing.
- Throws: `ConfigurationError` if the configuration file is wrong or incomplete.
*/
public static func parsed(
fromContents contents: String,
passphrase: String? = nil,
originalURL: URL? = nil,
returnsStripped: Bool = false
) throws -> Result {
let lines = contents.trimmedLines()
return try parsed(
fromLines: lines,
isClient: true,
passphrase: passphrase,
originalURL: originalURL,
returnsStripped: returnsStripped
)
} }
/** /**
@ -202,7 +235,13 @@ extension OpenVPN {
- Returns: The `Result` outcome of the parsing. - Returns: The `Result` outcome of the parsing.
- Throws: `ConfigurationError` if the configuration file is wrong or incomplete. - Throws: `ConfigurationError` if the configuration file is wrong or incomplete.
*/ */
public static func parsed(fromLines lines: [String], isClient: Bool = false, passphrase: String? = nil, originalURL: URL? = nil, returnsStripped: Bool = false) throws -> Result { public static func parsed(
fromLines lines: [String],
isClient: Bool = false,
passphrase: String? = nil,
originalURL: URL? = nil,
returnsStripped: Bool = false
) throws -> Result {
var optStrippedLines: [String]? = returnsStripped ? [] : nil var optStrippedLines: [String]? = returnsStripped ? [] : nil
var optWarning: ConfigurationError? var optWarning: ConfigurationError?
var unsupportedError: ConfigurationError? var unsupportedError: ConfigurationError?
@ -229,6 +268,7 @@ extension OpenVPN {
var optDefaultProto: SocketType? var optDefaultProto: SocketType?
var optDefaultPort: UInt16? var optDefaultPort: UInt16?
var optRemotes: [(String, UInt16?, SocketType?)] = [] // address, port, socket var optRemotes: [(String, UInt16?, SocketType?)] = [] // address, port, socket
var authUserPass = false
var optChecksEKU: Bool? var optChecksEKU: Bool?
var optRandomizeEndpoint: Bool? var optRandomizeEndpoint: Bool?
var optMTU: Int? var optMTU: Int?
@ -537,6 +577,10 @@ extension OpenVPN {
} }
optMTU = Int(str) optMTU = Int(str)
} }
Regex.authUserPass.enumerateComponents(in: line) { _ in
isHandled = true
authUserPass = true
}
// MARK: Server // MARK: Server
@ -687,10 +731,11 @@ extension OpenVPN {
sessionBuilder.compressionAlgorithm = optCompressionAlgorithm sessionBuilder.compressionAlgorithm = optCompressionAlgorithm
sessionBuilder.ca = optCA sessionBuilder.ca = optCA
sessionBuilder.clientCertificate = optClientCertificate sessionBuilder.clientCertificate = optClientCertificate
sessionBuilder.authUserPass = authUserPass
if let clientKey = optClientKey, clientKey.isEncrypted { if let clientKey = optClientKey, clientKey.isEncrypted {
// FIXME: remove dependency on TLSBox // FIXME: remove dependency on TLSBox
guard let passphrase = passphrase else { guard let passphrase = passphrase, !passphrase.isEmpty else {
throw ConfigurationError.encryptionPassphrase throw ConfigurationError.encryptionPassphrase
} }
do { do {
@ -746,6 +791,7 @@ extension OpenVPN {
sessionBuilder.hostname = nil sessionBuilder.hostname = nil
} }
sessionBuilder.authUserPass = authUserPass
sessionBuilder.checksEKU = optChecksEKU sessionBuilder.checksEKU = optChecksEKU
sessionBuilder.randomizeEndpoint = optRandomizeEndpoint sessionBuilder.randomizeEndpoint = optRandomizeEndpoint
sessionBuilder.mtu = optMTU sessionBuilder.mtu = optMTU