From 037f08ed62018a32930238429829e7e6a892bf35 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Wed, 1 May 2019 10:37:39 +0200 Subject: [PATCH] Retry auth once without local options Hack around picky server implementations. Fixes #95 --- CHANGELOG.md | 6 ++ .../Core/SessionProxy+Authenticator.swift | 62 +++++++++++-------- TunnelKit/Sources/Core/SessionProxy.swift | 13 ++++ 3 files changed, 54 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfb60fa..7f9ff43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed + +- Authentication failure due to local options. [#95](https://github.com/keeshux/tunnelkit/issues/95) + ## 1.7.0 (2019-04-28) ### Changed diff --git a/TunnelKit/Sources/Core/SessionProxy+Authenticator.swift b/TunnelKit/Sources/Core/SessionProxy+Authenticator.swift index 149741b..b5a7a50 100644 --- a/TunnelKit/Sources/Core/SessionProxy+Authenticator.swift +++ b/TunnelKit/Sources/Core/SessionProxy+Authenticator.swift @@ -66,6 +66,8 @@ extension SessionProxy { let password: ZeroingData? + var withLocalOptions: Bool + init(_ username: String?, _ password: String?) throws { preMaster = try SecureRandom.safeData(length: CoreConfiguration.preMasterLength) random1 = try SecureRandom.safeData(length: CoreConfiguration.randomLength) @@ -80,6 +82,8 @@ extension SessionProxy { self.password = nil } + withLocalOptions = true + controlBuffer = Z() } @@ -95,35 +99,39 @@ extension SessionProxy { raw.append(random2) // options string - var opts = [ - "V4", - "dev-type tun" - ] - ////// - if let comp = options.compressionFraming { - switch comp { - case .compLZO: - opts.append("comp-lzo") - - case .compress: - opts.append("compress") - - default: - break + let optsString: String + if withLocalOptions { + var opts = [ + "V4", + "dev-type tun" + ] + if let comp = options.compressionFraming { + switch comp { + case .compLZO: + opts.append("comp-lzo") + + case .compress: + opts.append("compress") + + default: + break + } } + if let direction = options.tlsWrap?.key.direction?.rawValue { + opts.append("keydir \(direction)") + } + opts.append("cipher \(options.cipher?.rawValue ?? "BF-CBC")") + opts.append("auth \(options.fallbackDigest.rawValue)") + opts.append("keysize \(options.fallbackCipher.keySize)") + if let strategy = options.tlsWrap?.strategy { + opts.append("tls-\(strategy)") + } + opts.append("key-method 2") + opts.append("tls-client") + optsString = opts.joined(separator: ",") + } else { + optsString = "V0 UNDEF" } - if let direction = options.tlsWrap?.key.direction?.rawValue { - opts.append("keydir \(direction)") - } - opts.append("cipher \(options.cipher?.rawValue ?? "BF-CBC")") - opts.append("auth \(options.fallbackDigest.rawValue)") - opts.append("keysize \(options.fallbackCipher.keySize)") - if let strategy = options.tlsWrap?.strategy { - opts.append("tls-\(strategy)") - } - opts.append("key-method 2") - opts.append("tls-client") - let optsString = opts.joined(separator: ",") log.debug("TLS.auth: Local options: \(optsString)") raw.appendSized(Z(optsString, nullTerminated: true)) diff --git a/TunnelKit/Sources/Core/SessionProxy.swift b/TunnelKit/Sources/Core/SessionProxy.swift index 4e40387..a655291 100644 --- a/TunnelKit/Sources/Core/SessionProxy.swift +++ b/TunnelKit/Sources/Core/SessionProxy.swift @@ -105,6 +105,8 @@ public class SessionProxy { private let queue: DispatchQueue private var tlsObserver: NSObjectProtocol? + + private var withLocalOptions: Bool private var keys: [UInt8: SessionKey] @@ -190,6 +192,7 @@ public class SessionProxy { self.configuration = configuration self.cachesURL = cachesURL + withLocalOptions = true keys = [:] oldKeys = [] negotiationKeyIdx = 0 @@ -676,6 +679,7 @@ public class SessionProxy { do { authenticator = try Authenticator(credentials?.username, pushReply?.options.authToken ?? credentials?.password) + authenticator?.withLocalOptions = withLocalOptions try authenticator?.putAuth(into: negotiationKey.tls, options: configuration) } catch let e { deferStop(.shutdown, e) @@ -908,6 +912,15 @@ public class SessionProxy { // Ruby: handle_ctrl_msg private func handleControlMessage(_ message: String) { guard !message.hasPrefix("AUTH_FAILED") else { + + // XXX: retry without client options + if authenticator?.withLocalOptions ?? false { + log.warning("Authentication failure, retrying without local options") + withLocalOptions = false + deferStop(.reconnect, SessionError.badCredentials) + return + } + deferStop(.shutdown, SessionError.badCredentials) return }