aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Frameworks/SPMySQLFramework/MySQL Client Libraries/Patches/001-cpp-dependency.diff8
-rw-r--r--Frameworks/SPMySQLFramework/MySQL Client Libraries/Patches/002-new-types.diff9
-rw-r--r--Frameworks/SPMySQLFramework/MySQL Client Libraries/Patches/003-callback-password-auth.diff111
-rw-r--r--Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql.h12
-rw-r--r--Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql_version.h4
-rw-r--r--Frameworks/SPMySQLFramework/MySQL Client Libraries/lib/libmysqlclient.abin7936384 -> 7932592 bytes
-rw-r--r--Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj5
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h1
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h18
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m120
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnectionDelegate.h9
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLUtilities.h12
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLUtilities.m66
-rwxr-xr-xFrameworks/SPMySQLFramework/build-mysql-client.sh2
-rw-r--r--Interfaces/English.lproj/ConnectionView.xib53
-rw-r--r--Source/SPConnectionController.h23
-rw-r--r--Source/SPConnectionController.m185
-rw-r--r--Source/SPDatabaseDocument.h4
-rw-r--r--Source/SPDatabaseDocument.m75
-rw-r--r--Source/SPDatabaseStructure.m8
-rw-r--r--Source/SPKeychain.m9
21 files changed, 123 insertions, 611 deletions
diff --git a/Frameworks/SPMySQLFramework/MySQL Client Libraries/Patches/001-cpp-dependency.diff b/Frameworks/SPMySQLFramework/MySQL Client Libraries/Patches/001-cpp-dependency.diff
index e3ce848a..06c20001 100644
--- a/Frameworks/SPMySQLFramework/MySQL Client Libraries/Patches/001-cpp-dependency.diff
+++ b/Frameworks/SPMySQLFramework/MySQL Client Libraries/Patches/001-cpp-dependency.diff
@@ -1,11 +1,3 @@
-This patch is neccesary to remove a linker error when trying to link SPMySQL with libmysqlclient.a.
-
-To apply:
- cd mysql-source-root
- patch -p1 < this-file
-
-(patch created with `diff -Naur`)
-
--- mysql-5.5.56-dist/extra/yassl/taocrypt/include/runtime.hpp 2017-04-27 09:12:30.000000000 +0200
+++ mysql-5.5.56/extra/yassl/taocrypt/include/runtime.hpp 2017-05-20 23:27:14.000000000 +0200
@@ -53,8 +53,8 @@
diff --git a/Frameworks/SPMySQLFramework/MySQL Client Libraries/Patches/002-new-types.diff b/Frameworks/SPMySQLFramework/MySQL Client Libraries/Patches/002-new-types.diff
index 47aa9c6d..bb42f9d9 100644
--- a/Frameworks/SPMySQLFramework/MySQL Client Libraries/Patches/002-new-types.diff
+++ b/Frameworks/SPMySQLFramework/MySQL Client Libraries/Patches/002-new-types.diff
@@ -1,12 +1,3 @@
-This patch backports field types that were added after MySQL 5.5,
-but are technically still compatible to the old client libs.
-
-To apply:
- cd mysql-source-root
- patch -p1 < this-file
-
-(patch created with `diff -Naur`)
-
--- mysql-5.5.56-dist/include/mysql_com.h 2017-04-27 09:12:30.000000000 +0200
+++ mysql-5.5.56/include/mysql_com.h 2017-05-21 01:46:44.000000000 +0200
@@ -349,7 +349,11 @@
diff --git a/Frameworks/SPMySQLFramework/MySQL Client Libraries/Patches/003-callback-password-auth.diff b/Frameworks/SPMySQLFramework/MySQL Client Libraries/Patches/003-callback-password-auth.diff
deleted file mode 100644
index 249df42e..00000000
--- a/Frameworks/SPMySQLFramework/MySQL Client Libraries/Patches/003-callback-password-auth.diff
+++ /dev/null
@@ -1,111 +0,0 @@
-This patch changes the way libmysqlclient receives the connection password.
-Usually it will get the password by trying in order:
- 1) The passwd that is passed as a parameter to mysql_real_connect()
- 2) The password that was set on MYSQL->options.password
- 3) The contents of the environment variable MYSQL_PWD (compile time setting)
- 4) An empty string
-
-If a connection could be made (not yet authenticathed) the password will be stored
-in MYSQL->passwd for the whole lifetime of the struct.
-
-We don't want that for two reasons:
- 1) That way the password stays in plaintext memory for possibly a long time (and
- may even get swapped to disk)
- 2) MySQL uses plugins for auth (negotiated with the server) and some of them may
- transmit the password in plaintext over an unsecure connection.
- Since we have no control over that we would have to decide beforehand if that
- COULD happen and flat out always deny or allow Keychain access (since e.g.
- the AVAILABILITY of the cleartext plugin can be controlled by an envvar).
-
-So with this patch we change the flow of information:
-Now mysql doesn't receive the password up front, but instead it has to ask the user (ie. SPMySQL)
-to get the password precisely then when it needs it and mysql will also tell us
-which auth plugin it negotiated with the server, so we can decide on a per situation
-basis whether to request manual input or fetch it from Keychain.
-
-To apply:
- cd mysql-source-root
- patch -p1 < this-file
-
-(patch created with `diff -Naur`)
-
-diff -Naur mysql-5.5.59-p002/include/mysql.h mysql-5.5.59-p003/include/mysql.h
---- mysql-5.5.59-p002/include/mysql.h 2017-11-27 13:03:17.000000000 +0100
-+++ mysql-5.5.59-p003/include/mysql.h 2018-02-16 00:23:19.000000000 +0100
-@@ -288,6 +288,18 @@
- /* needed for embedded server - no net buffer to store the 'info' */
- char *info_buffer;
- void *extension;
-+
-+ /* SPMySQL patch:
-+ * Set this to a callback function that will be invoked when mysql wants to do authentication.
-+ * @param mysql The MYSQL struct
-+ * @param plugin The name of the auth plugin that will be used (usually either
-+ * "mysql_native_password", "mysql_old_password" or "mysql_clear_password")
-+ * @param with_password A block function you must invoke, during which mysql can use the password you provide via the passwd parameter.
-+ * After the block you should immediately clear the password from memory again.
-+ */
-+ void (*passwd_callback)(struct st_mysql *mysql, const char *plugin, void (^with_password)(const char *passwd));
-+ /* SPMySQL patch: This is used with passwd_callback to bridge back to OOP land */
-+ void *sp_context;
- } MYSQL;
-
-
-diff -Naur mysql-5.5.59-p002/sql-common/client.c mysql-5.5.59-p003/sql-common/client.c
---- mysql-5.5.59-p002/sql-common/client.c 2017-11-27 13:03:17.000000000 +0100
-+++ mysql-5.5.59-p003/sql-common/client.c 2018-02-16 00:27:37.000000000 +0100
-@@ -2952,7 +2952,7 @@
- auth_plugin_t *auth_plugin;
- MCPVIO_EXT mpvio;
- ulong pkt_length;
-- int res;
-+ __block int res;
-
- DBUG_ENTER ("run_plugin_auth");
- /* determine the default/initial plugin to use */
-@@ -2996,7 +2996,29 @@
- mpvio.db= db;
- mpvio.plugin= auth_plugin;
-
-- res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);
-+ /*
-+ * SPMySQL Patch to inverse the password flow
-+ */
-+ if(mysql->passwd_callback)
-+ {
-+ res = CR_ERROR; //fallback, if block is never invoked
-+ mysql->passwd_callback(mysql, auth_plugin_name, ^(const char *passwd) {
-+ char *saved_passwd = mysql->passwd;
-+ mysql->passwd = (char *)(passwd ? passwd : ""); // see mysql_change_user
-+ res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);
-+ mysql->passwd = saved_passwd;
-+ });
-+ }
-+ else
-+ {
-+ set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
-+ unknown_sqlstate,
-+ ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
-+ auth_plugin_name,
-+ "passwd_callback not set!");
-+ DBUG_RETURN (1);
-+ }
-+
- DBUG_PRINT ("info", ("authenticate_user returned %s",
- res == CR_OK ? "CR_OK" :
- res == CR_ERROR ? "CR_ERROR" :
-@@ -3069,7 +3091,13 @@
- DBUG_RETURN(1);
-
- mpvio.plugin= auth_plugin;
-- res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);
-+ res = CR_ERROR; //fallback, if block is never invoked
-+ mysql->passwd_callback(mysql, auth_plugin_name, ^(const char *passwd) {
-+ char *saved_passwd = mysql->passwd;
-+ mysql->passwd = (char *)(passwd ? passwd : ""); // see mysql_change_user
-+ res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);
-+ mysql->passwd = saved_passwd;
-+ });
-
- DBUG_PRINT ("info", ("second authenticate_user returned %s",
- res == CR_OK ? "CR_OK" :
diff --git a/Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql.h b/Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql.h
index 353267aa..3a27ab41 100644
--- a/Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql.h
+++ b/Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql.h
@@ -288,18 +288,6 @@ typedef struct st_mysql
/* needed for embedded server - no net buffer to store the 'info' */
char *info_buffer;
void *extension;
-
- /* SPMySQL patch:
- * Set this to a callback function that will be invoked when mysql wants to do authentication.
- * @param mysql The MYSQL struct
- * @param plugin The name of the auth plugin that will be used (usually either
- * "mysql_native_password", "mysql_old_password" or "mysql_clear_password")
- * @param with_password A block function you must invoke, during which mysql can use the password you provide via the passwd parameter.
- * After the block you should immediately clear the password from memory again.
- */
- void (*passwd_callback)(struct st_mysql *mysql, const char *plugin, void (^with_password)(const char *passwd));
- /* SPMySQL patch: This is used with passwd_callback to bridge back to OOP land */
- void *sp_context;
} MYSQL;
diff --git a/Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql_version.h b/Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql_version.h
index 77ea7e07..8c18116a 100644
--- a/Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql_version.h
+++ b/Frameworks/SPMySQLFramework/MySQL Client Libraries/include/mysql_version.h
@@ -11,11 +11,11 @@
#include <custom_conf.h>
#else
#define PROTOCOL_VERSION 10
-#define MYSQL_SERVER_VERSION "5.5.59"
+#define MYSQL_SERVER_VERSION "5.5.56"
#define MYSQL_BASE_VERSION "mysqld-5.5"
#define MYSQL_SERVER_SUFFIX_DEF ""
#define FRM_VER 6
-#define MYSQL_VERSION_ID 50559
+#define MYSQL_VERSION_ID 50556
#define MYSQL_PORT 3306
#define MYSQL_PORT_DEFAULT 0
#define MYSQL_UNIX_ADDR "/tmp/mysql.sock"
diff --git a/Frameworks/SPMySQLFramework/MySQL Client Libraries/lib/libmysqlclient.a b/Frameworks/SPMySQLFramework/MySQL Client Libraries/lib/libmysqlclient.a
index fa616267..0fccae22 100644
--- a/Frameworks/SPMySQLFramework/MySQL Client Libraries/lib/libmysqlclient.a
+++ b/Frameworks/SPMySQLFramework/MySQL Client Libraries/lib/libmysqlclient.a
Binary files differ
diff --git a/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj b/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj
index 476adffe..aa7989fc 100644
--- a/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj
+++ b/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj
@@ -13,7 +13,6 @@
507FF1E51BC0D82300104523 /* DataConversion_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 507FF1811BC0C64100104523 /* DataConversion_Tests.m */; };
507FF23B1BC0E8CA00104523 /* SPMySQL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* SPMySQL.framework */; };
507FF23D1BC157B500104523 /* SPMySQLStringAdditions_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 507FF23C1BC157B500104523 /* SPMySQLStringAdditions_Tests.m */; };
- 508FDC95204329580062A9CA /* SPMySQLUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 508FDC94204329580062A9CA /* SPMySQLUtilities.m */; };
580A331E14D75CF7000D6933 /* SPMySQLGeometryData.h in Headers */ = {isa = PBXBuildFile; fileRef = 580A331C14D75CF7000D6933 /* SPMySQLGeometryData.h */; settings = {ATTRIBUTES = (Public, ); }; };
580A331F14D75CF7000D6933 /* SPMySQLGeometryData.m in Sources */ = {isa = PBXBuildFile; fileRef = 580A331D14D75CF7000D6933 /* SPMySQLGeometryData.m */; };
583C734A17A489CC0056B284 /* SPMySQLStreamingResultStoreDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 583C734917A489CC0056B284 /* SPMySQLStreamingResultStoreDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -104,7 +103,6 @@
507FF1D51BC0D7D300104523 /* SPMySQL Unit Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SPMySQL Unit Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
507FF1D81BC0D7D300104523 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
507FF23C1BC157B500104523 /* SPMySQLStringAdditions_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPMySQLStringAdditions_Tests.m; sourceTree = "<group>"; };
- 508FDC94204329580062A9CA /* SPMySQLUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SPMySQLUtilities.m; path = Source/SPMySQLUtilities.m; sourceTree = "<group>"; };
580A331C14D75CF7000D6933 /* SPMySQLGeometryData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SPMySQLGeometryData.h; path = Source/SPMySQLGeometryData.h; sourceTree = "<group>"; };
580A331D14D75CF7000D6933 /* SPMySQLGeometryData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SPMySQLGeometryData.m; path = Source/SPMySQLGeometryData.m; sourceTree = "<group>"; };
583C734917A489CC0056B284 /* SPMySQLStreamingResultStoreDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SPMySQLStreamingResultStoreDelegate.h; path = Source/SPMySQLStreamingResultStoreDelegate.h; sourceTree = "<group>"; };
@@ -275,7 +273,6 @@
32C88DFF0371C24200C91783 /* Other Sources */ = {
isa = PBXGroup;
children = (
- 508FDC94204329580062A9CA /* SPMySQLUtilities.m */,
584294E314CB8002000F8438 /* SPMySQLConstants.h */,
58C006C714E0B18A00AC489A /* SPMySQLUtilities.h */,
32DBCF5E0370ADEE00C91783 /* SPMySQLFramework_Prefix.pch */,
@@ -558,7 +555,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 508FDC96204329580062A9CA /* SPMySQLUtilities.m in Sources */,
507FF23D1BC157B500104523 /* SPMySQLStringAdditions_Tests.m in Sources */,
507FF1E51BC0D82300104523 /* DataConversion_Tests.m in Sources */,
);
@@ -586,7 +582,6 @@
58C00BD214E7459600AC489A /* Databases & Tables.m in Sources */,
586A99FC14F02E21007F82BF /* SPMySQLStreamingResult.m in Sources */,
586AA16814F30C5F007F82BF /* Convenience Methods.m in Sources */,
- 508FDC95204329580062A9CA /* SPMySQLUtilities.m in Sources */,
584D812F15057ECD00F24774 /* SPMySQLKeepAliveTimer.m in Sources */,
584D82561509775000F24774 /* Copying.m in Sources */,
58D2A4D216EDF1C6002EB401 /* SPMySQLEmptyResult.m in Sources */,
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h b/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
index b2fa9f5d..8fdf4e7e 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
@@ -41,7 +41,6 @@
- (BOOL)_connect;
- (MYSQL *)_makeRawMySQLConnectionWithEncoding:(NSString *)encodingName isMasterConnection:(BOOL)isMaster;
-- (void)_mysqlConnection:(MYSQL *)connection wantsPassword:(void (^)(const char *passwd))inBlock withPlugin:(const char *)pluginName;
- (BOOL)_reconnectAllowingRetries:(BOOL)canRetry;
- (BOOL)_reconnectAfterBackgroundConnectionLoss;
- (BOOL)_waitForNetworkConnectionWithTimeout:(double)timeoutSeconds;
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h
index 0faa3e02..343d0d36 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h
@@ -61,7 +61,6 @@
pthread_t reconnectingThread;
uint64_t initialConnectTime;
unsigned long mysqlConnectionThreadId;
- BOOL allowCleartextPlugin;
// Connection proxy
NSObject <SPMySQLConnectionProxy> *proxy;
@@ -176,23 +175,6 @@
- (void)addClientFlags:(SPMySQLClientFlags)opts;
- (void)removeClientFlags:(SPMySQLClientFlags)opts;
-/**
- * This tells the mysql client whether the cleartext auth plugin is permitted.
- *
- * If enabled, and requested by the server, the password will simply be transmitted
- * in plaintext, instead of hashing it on the client first, thus this plugin is
- * rather risky. It is mostly used when the server has to forward the password to another
- * auth backend (which it can't do with the one-way hash).
- *
- * If it is not enabled, but requested by the server the connection will fail with
- * error "plugin not enabled (2059)"
- *
- * WARNING: There are 2 ways to enable this plugin: Via this property or by setting
- * the envvar LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN to 1. Sadly ANY ONE of those two is
- * sufficient to enable the plugin.
- */
-@property (readwrite, assign, nonatomic) BOOL allowCleartextPlugin;
-
#pragma mark -
#pragma mark Connection and disconnection
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
index d4146f58..d50375e2 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
@@ -33,9 +33,6 @@
#include <mach/mach_time.h>
#include <pthread.h>
#include <SystemConfiguration/SCNetworkReachability.h>
-#include <errno.h>
-#include <string.h>
-#import "SPMySQLUtilities.h"
// Thread flag constant
static pthread_key_t mySQLThreadInitFlagKey;
@@ -52,7 +49,6 @@ const SPMySQLClientFlags SPMySQLConnectionOptions =
// List of permissible ciphers to use for SSL connections
const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RSA-AES128-SHA:AES128-SHA:AES256-RMD:AES128-RMD:DES-CBC3-RMD:DHE-RSA-AES256-RMD:DHE-RSA-AES128-RMD:DHE-RSA-DES-CBC3-RMD:RC4-SHA:RC4-MD5:DES-CBC3-SHA:DES-CBC-SHA:EDH-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC-SHA";
-static void PasswordCallback(MYSQL *mysql, const char *plugin, void (^with_password)(const char *passwd));
@implementation SPMySQLConnection
@@ -78,7 +74,6 @@ static void PasswordCallback(MYSQL *mysql, const char *plugin, void (^with_passw
@synthesize delegateQueryLogging;
@synthesize lastQueryWasCancelled;
@synthesize clientFlags = clientFlags;
-@synthesize allowCleartextPlugin = allowCleartextPlugin;
#pragma mark -
#pragma mark Getters and Setters
@@ -475,6 +470,14 @@ static void PasswordCallback(MYSQL *mysql, const char *plugin, void (^with_passw
const char *__crashreporter_info__ = NULL;
asm(".desc ___crashreporter_info__, 0x10");
+static uint64_t _elapsedMicroSecondsSinceAbsoluteTime(uint64_t comparisonTime)
+{
+ uint64_t elapsedTime_t = mach_absolute_time() - comparisonTime;
+ Nanoseconds elapsedTime = AbsoluteToNanoseconds(*(AbsoluteTime *)&(elapsedTime_t));
+
+ return (UnsignedWideToUInt64(elapsedTime) / 1000ULL);
+}
+
@implementation SPMySQLConnection (PrivateAPI)
/**
@@ -486,8 +489,8 @@ asm(".desc ___crashreporter_info__, 0x10");
// If a connection is already active in some form, throw an exception
if (state != SPMySQLDisconnected && state != SPMySQLConnectionLostInBackground) {
@synchronized (self) {
- double diff = _elapsedSecondsSinceAbsoluteTime(initialConnectTime);
- asprintf(&__crashreporter_info__, "Attempted to connect a connection that is not disconnected (SPMySQLConnectionState=%d).\nIf state==2: Previous connection made %lfs ago from: %s", state, diff, [_debugLastConnectedEvent cStringUsingEncoding:NSUTF8StringEncoding]);
+ uint64_t diff = _elapsedMicroSecondsSinceAbsoluteTime(initialConnectTime);
+ asprintf(&__crashreporter_info__, "Attempted to connect a connection that is not disconnected (SPMySQLConnectionState=%d).\nIf state==2: Previous connection made %lluµs ago from: %s", state, diff, [_debugLastConnectedEvent cStringUsingEncoding:NSUTF8StringEncoding]);
__builtin_trap();
}
[NSException raise:NSInternalInconsistencyException format:@"Attempted to connect a connection that is not disconnected (SPMySQLConnectionState=%d).", state];
@@ -606,17 +609,30 @@ asm(".desc ___crashreporter_info__, 0x10");
NSStringEncoding connectEncodingNS = [SPMySQLConnection stringEncodingForMySQLCharset:[encodingName UTF8String]];
mysql_options(theConnection, MYSQL_SET_CHARSET_NAME, [encodingName UTF8String]);
- my_bool cleartextAllowed = [self allowCleartextPlugin] ? TRUE : FALSE;
- mysql_options(theConnection, MYSQL_ENABLE_CLEARTEXT_PLUGIN, &cleartextAllowed);
-
// Set up the connection variables in the format MySQL needs, from the class-wide variables
const char *theHost = NULL;
const char *theUsername = "";
+ const char *thePassword = NULL;
const char *theSocket = NULL;
if (host) theHost = [host UTF8String]; //mysql calls getaddrinfo on the hostname. Apples code uses -UTF8String in that situation.
if (username) theUsername = _cStringForStringWithEncoding(username, connectEncodingNS, NULL); //during connect this is in MYSQL_SET_CHARSET_NAME encoding
+ // If a password was supplied, use it; otherwise ask the delegate if appropriate.
+ //
+ // Note that password has no charset in mysql: If a user password is set to 'ü' on a latin1 connection
+ // and you later try to connect on an UTF-8 terminal (or vice versa) it will fail. The MySQL (5.5) manual wrongly states that
+ // MYSQL_SET_CHARSET_NAME has influence over that, but it does not and could not, since the password is hashed by the client
+ // before transmitting it to the server and the (5.5) client has no charset support, effectively treating password as
+ // a NUL-terminated byte array.
+ // There is one exception, though: The "mysql_clear_password" auth plugin sends the password in plaintext and the server side
+ // MAY choose to do a charset conversion as appropriate before handing it to whatever backend is used.
+ // Since we don't know which auth plugin server and client will agree upon, we'll do as the manual says...
+ if (password) {
+ thePassword = _cStringForStringWithEncoding(password, connectEncodingNS, NULL);
+ } else if ([delegate respondsToSelector:@selector(keychainPasswordForConnection:)]) {
+ thePassword = _cStringForStringWithEncoding([delegate keychainPasswordForConnection:self], connectEncodingNS, NULL);
+ }
// If set to use a socket and a socket was supplied, use it; otherwise, search for a socket to use
if (useSocket) {
@@ -674,11 +690,7 @@ asm(".desc ___crashreporter_info__, 0x10");
}
}
- // we will provide the password via this callback. the mysql_real_connect parameter is a dummy and won't work
- theConnection->passwd_callback = &PasswordCallback;
- theConnection->sp_context = self;
-
- MYSQL *connectionStatus = mysql_real_connect(theConnection, theHost, theUsername, "", NULL, (unsigned int)port, theSocket, [self clientFlags]);
+ MYSQL *connectionStatus = mysql_real_connect(theConnection, theHost, theUsername, thePassword, NULL, (unsigned int)port, theSocket, [self clientFlags]);
// If the connection failed, return NULL
if (theConnection != connectionStatus) {
@@ -721,78 +733,6 @@ asm(".desc ___crashreporter_info__, 0x10");
return theConnection;
}
-- (void)_mysqlConnection:(MYSQL *)connection wantsPassword:(void (^)(const char *passwd))inBlock withPlugin:(const char *)pluginName
-{
- NSString *passwd = nil;
-
- // If a password was supplied, use it; otherwise ask the delegate if appropriate.
- if (password) {
- passwd = password;
- }
- else if ([delegate respondsToSelector:@selector(passwordForConnection:authPlugin:)]) {
- // It's not clear what charset the plugin name is in:
- // In the 5.5 libmysqlclient:
- // * For the compiled-in plugins this will simply be the byte sequence as it was in the source code
- // * The server requests a plugin in the first packet it sends and gives its own charset (mysql->server_language)
- // * However client for the most part ignores the plugin name
- // * and it completely ignores the server_language
- // * When the client sends its reply (in send_client_reply_packet()) it will send the plugin name together with
- // the desired charset+collation (but doesn't apply any charset conversion logic to the values)
- // In the JDBC client it works like this:
- // * The plugin name in the first packet from the server will always be interpreted as "ASCII"
- // * The plugin name in the client response will be encoded in the client's initial charset
- // TODO We will just use latin1 for now, as it is the safest fallback
- NSString *plugin = [NSString stringWithCString:pluginName encoding:NSISOLatin1StringEncoding];
- passwd = [delegate passwordForConnection:self authPlugin:plugin];
- }
-
- // shortcut for nil/empty passwords
- if(![passwd length]) {
- // nil means abort, "" is a valid password:
- // only invoke the block when the password is @"", otherwise we'll skip it which will make mysql abort the connection
- if(passwd) inBlock("");
- return;
- }
-
- // Note (libmysqlclient 5.5):
- // mysql_character_set_name() is only initialized after mysql has read the first packet from the server.
- // Before that it will always be latin1, regardless of what was set with mysql_options().
- // That does not mean, that client and server have agreed on a charset already, though!
- NSStringEncoding connectEncodingNS = [SPMySQLConnection stringEncodingForMySQLCharset:mysql_character_set_name(connection)];
-
- // Note that password has no charset in mysql: If a user password is set to 'ü' on a latin1 connection
- // and you later try to connect on an UTF-8 terminal (or vice versa) it will fail. The MySQL (5.5) manual wrongly states that
- // MYSQL_SET_CHARSET_NAME has influence over that, but it does not and could not, since the password is hashed by the client
- // before transmitting it to the server and the (5.5) client has very limited charset support,
- // effectively treating password as a NUL-terminated byte array.
- // There is one exception, though: The "mysql_clear_password" auth plugin sends the password in plaintext and the server side
- // MAY choose to do a charset conversion as appropriate before handing it to whatever backend is used.
- // Since we don't know which auth plugin server and client will agree upon, we'll do as the manual says...
- NSInteger cLength = [passwd lengthOfBytesUsingEncoding:connectEncodingNS];
-
- if(!cLength || cLength == NSIntegerMax) {
- NSLog(@"%s: -lengthOfBytesUsingEncoding: returned 0 or NSIntegerMax for encoding %lu (mysql: %s)", __PRETTY_FUNCTION__, connectEncodingNS, connection->options.charset_name);
- return;
- }
-
- char *cBuffer = malloc(++cLength);
-
- if(!cBuffer) {
- NSLog(@"%s: malloc(%ld) failed: %s", __PRETTY_FUNCTION__, (long)cLength, strerror(errno));
- return;
- }
-
- if([passwd getCString:cBuffer maxLength:cLength encoding:connectEncodingNS]) {
- inBlock(cBuffer);
- }
- else {
- NSLog(@"%s: -getCString:maxLength:encoding: failed for password!", __PRETTY_FUNCTION__);
- }
-
- SPMySQLSafeEraseMemory(cBuffer, cLength); //clear password from memory
- free(cBuffer);
-}
-
/**
* Perform a reconnection task, either once-only or looping as requested. If looping is
* permitted and this method fails, it will ask how to proceed and loop depending on
@@ -1222,9 +1162,3 @@ asm(".desc ___crashreporter_info__, 0x10");
}
@end
-
-void PasswordCallback(MYSQL *mysql, const char *plugin, void (^with_password)(const char *))
-{
- assert(mysql && mysql->sp_context);
- [(SPMySQLConnection *)mysql->sp_context _mysqlConnection:mysql wantsPassword:with_password withPlugin:plugin];
-}
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnectionDelegate.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnectionDelegate.h
index f32be742..1e238c12 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnectionDelegate.h
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnectionDelegate.h
@@ -62,18 +62,15 @@
- (void)showErrorWithTitle:(NSString *)title message:(NSString *)message;
/**
- * Requests the password for the connection.
+ * Requests the keychain password for the connection.
* When a connection is being made to a server, it is best not to
* set the password on the class; instead, it should be kept within
- * the secure store (Keychain), and the other connection details (user, host)
+ * the secure store, and the other connection details (user, host)
* can be used to look it up and supplied on demand.
*
- * NOTE: This will be called on the thread SPMySQL is running on (which *MAY* be a background thread)!
- *
* @param connection The connection instance to supply the password for
- * @param pluginName The auth plugin libmysqlclients wants to use the password with
*/
-- (NSString *)passwordForConnection:(id)connection authPlugin:(NSString *)pluginName;
+- (NSString *)keychainPasswordForConnection:(id)connection;
/**
* Notifies the delegate that no underlying connection is available,
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLUtilities.h b/Frameworks/SPMySQLFramework/Source/SPMySQLUtilities.h
index 852ca685..50f9d504 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLUtilities.h
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLUtilities.h
@@ -1,5 +1,5 @@
//
-// SPMySQLUtilities.h
+// Locking.h
// SPMySQLFramework
//
// Created by Rowan Beentje (rowan.beent.je) on February 6, 2012
@@ -30,16 +30,6 @@
#include <mach/mach_time.h>
-/**
- * This function provides a memset() variant that cannot be optimized
- * away by a compiler's dead store optimization.
- *
- * It will write cLength '\0' bytes to the buffer pointed to by cBuffer.
- *
- * @return See the documentation for memset_s in libc
- */
-errno_t SPMySQLSafeEraseMemory(void *cBuffer, size_t cLength);
-
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLUtilities.m b/Frameworks/SPMySQLFramework/Source/SPMySQLUtilities.m
deleted file mode 100644
index 3d147143..00000000
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLUtilities.m
+++ /dev/null
@@ -1,66 +0,0 @@
-//
-// SPMySQLUtilities.c
-// SPMySQLFramework
-//
-// Created by Max Lohrmann on 25.02.2018
-// Copyright (c) 2018 Max Lohrmann. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person
-// obtaining a copy of this software and associated documentation
-// files (the "Software"), to deal in the Software without
-// restriction, including without limitation the rights to use,
-// copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the
-// Software is furnished to do so, subject to the following
-// conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-// OTHER DEALINGS IN THE SOFTWARE.
-//
-// More info at <https://github.com/sequelpro/sequelpro>
-
-#include <errno.h>
-#define __STDC_WANT_LIB_EXT1__ 1
-#include <string.h>
-#include <dlfcn.h>
-#include <dispatch/dispatch.h>
-
-#include "SPMySQLUtilities.h"
-
-static errno_t LegacyMemsetS(void *ptr, rsize_t ignored, int value, rsize_t count);
-
-errno_t SPMySQLSafeEraseMemory(void *cBuffer, size_t cLength) {
- // memset_s is 10.9+ only - if we added a link time dependency, SP wouldn't launch on older targets
- static errno_t (*memsetPtr)(void *, rsize_t, int, rsize_t);
- static dispatch_once_t findMemsetToken;
- dispatch_once(&findMemsetToken, ^{
- memsetPtr = dlsym(RTLD_DEFAULT, "memset_s");
- if(!memsetPtr) memsetPtr = &LegacyMemsetS;
- });
-
- return (*memsetPtr)(cBuffer, cLength, '\0', cLength);
-}
-
-/**
- * This function tries to emulate the important (to us) parts
- * of memset_s on pre 10.9 systems.
- *
- * The implementation is taken from the original memset_s proposal:
- * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1381.pdf
- */
-errno_t LegacyMemsetS(void *s, rsize_t smax __attribute__((unused)), int c, rsize_t n)
-{
- volatile unsigned char * addr = (volatile unsigned char *)s;
- while(n--) *addr++ = (unsigned char)c;
-
- return 0;
-}
diff --git a/Frameworks/SPMySQLFramework/build-mysql-client.sh b/Frameworks/SPMySQLFramework/build-mysql-client.sh
index ca43eab2..5475b0a0 100755
--- a/Frameworks/SPMySQLFramework/build-mysql-client.sh
+++ b/Frameworks/SPMySQLFramework/build-mysql-client.sh
@@ -47,7 +47,7 @@ CLEAN='NO'
MIN_OS_X_VERSION='10.6'
ARCHITECTURES='-arch i386 -arch x86_64'
-CONFIGURE_OPTIONS='-DBUILD_CONFIG=mysql_release -DENABLED_LOCAL_INFILE=1 -DWITH_SSL=bundled -DWITH_MYSQLD_LDFLAGS="-all-static --disable-shared" -DWITHOUT_SERVER=1 -DWITH_ZLIB=system -DWITH_UNIT_TESTS=0'
+CONFIGURE_OPTIONS='-DBUILD_CONFIG=mysql_release -DENABLED_LOCAL_INFILE=1 -DWITH_SSL=bundled -DWITH_MYSQLD_LDFLAGS="-all-static --disable-shared" -DWITHOUT_SERVER=1 -DWITH_ZLIB=system'
OUTPUT_DIR='SPMySQLFiles.build'
ESC=`printf '\033'`
diff --git a/Interfaces/English.lproj/ConnectionView.xib b/Interfaces/English.lproj/ConnectionView.xib
index b47ae48a..05ced791 100644
--- a/Interfaces/English.lproj/ConnectionView.xib
+++ b/Interfaces/English.lproj/ConnectionView.xib
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="13F1911" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="13F1507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment identifier="macosx"/>
<development version="5100" identifier="xcode"/>
@@ -23,9 +23,6 @@
<outlet property="helpButton" destination="4829" id="5458"/>
<outlet property="progressIndicator" destination="5422" id="5426"/>
<outlet property="progressIndicatorText" destination="5423" id="5425"/>
- <outlet property="requestPasswordAccessoryView" destination="eXu-WW-1SA" id="67S-wG-21A"/>
- <outlet property="requestPasswordPasswordField" destination="iX8-1c-QGP" id="f2E-RB-7D1"/>
- <outlet property="requestPasswordPluginNameField" destination="qYD-Yv-BoC" id="4Ps-j5-3U9"/>
<outlet property="saveFavoriteButton" destination="5869" id="5874"/>
<outlet property="socketColorField" destination="5896" id="5897"/>
<outlet property="socketConnectionFormContainer" destination="5162" id="5163"/>
@@ -2593,52 +2590,6 @@ DQ
</textField>
</subviews>
</customView>
- <customView id="eXu-WW-1SA" userLabel="Request Password Accessory View">
- <rect key="frame" x="-5" y="0.0" width="334" height="47"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <subviews>
- <textField toolTip="The name of the authentication plugin MySQL wants to use" horizontalHuggingPriority="251" verticalHuggingPriority="750" id="kJN-ff-ksF">
- <rect key="frame" x="-2" y="30" width="105" height="17"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Plugin:" id="r9R-qm-igR">
- <font key="font" metaFont="system"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- </textField>
- <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="qYD-Yv-BoC">
- <rect key="frame" x="107" y="30" width="229" height="17"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="$pluginName" id="6ge-00-IRy">
- <font key="font" metaFont="systemBold"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- </textField>
- <secureTextField verticalHuggingPriority="750" id="iX8-1c-QGP">
- <rect key="frame" x="109" y="0.0" width="225" height="22"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" usesSingleLineMode="YES" id="de0-rh-heM">
- <font key="font" metaFont="system"/>
- <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
- <allowedInputSourceLocales>
- <string>NSAllRomanInputSourcesLocaleIdentifier</string>
- </allowedInputSourceLocales>
- </secureTextFieldCell>
- </secureTextField>
- <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="YdZ-UM-eof">
- <rect key="frame" x="-2" y="2" width="105" height="17"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Password:" id="afg-rd-MnW">
- <font key="font" metaFont="system"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- </textField>
- </subviews>
- <point key="canvasLocation" x="104" y="923.5"/>
- </customView>
</objects>
<resources>
<image name="button_action" width="30" height="22"/>
diff --git a/Source/SPConnectionController.h b/Source/SPConnectionController.h
index 44b0a89f..2da8f58a 100644
--- a/Source/SPConnectionController.h
+++ b/Source/SPConnectionController.h
@@ -69,8 +69,6 @@
BOOL isConnecting;
BOOL isEditingConnection;
BOOL isTestingConnection;
- NSString *agreedInsecurePlugin;
- NSString *insecureOverridePassword;
// Standard details
NSInteger previousType;
@@ -166,10 +164,6 @@
IBOutlet NSMenuItem *favoritesSortByMenuItem;
IBOutlet NSView *exportPanelAccessoryView;
IBOutlet NSView *editButtonsView;
-
- IBOutlet NSView *requestPasswordAccessoryView;
- IBOutlet NSTextField *requestPasswordPluginNameField;
- IBOutlet NSSecureTextField *requestPasswordPasswordField;
BOOL isEditingItemName;
BOOL reverseFavoritesSort;
@@ -214,24 +208,11 @@
@property (readwrite, assign) NSInteger sshKeyLocationEnabled;
@property (readwrite, retain) NSString *sshKeyLocation;
@property (readwrite, retain) NSString *sshPort;
-@property (readwrite, copy, nonatomic) NSString *connectionKeychainID;
@property (readwrite, retain) NSString *connectionKeychainItemName;
@property (readwrite, retain) NSString *connectionKeychainItemAccount;
@property (readwrite, retain) NSString *connectionSSHKeychainItemName;
@property (readwrite, retain) NSString *connectionSSHKeychainItemAccount;
@property (readwrite, assign) BOOL useCompression;
-/**
- * If the user was prompted to allow a connection with an insecure auth plugin,
- * the name of that plugin will be stored here (not persisted) so that we
- * don't have to ask again when duplicating a connection/reconnecting.
- */
-@property (readwrite, copy, nonatomic) NSString *agreedInsecurePlugin;
-/**
- * If the user has given a password that is not the keychain password in
- * the insecure auth plugin request, we will store it in memory and keep the
- * other properties unchanged, since they are connected to GUI and/or backing stores
- */
-@property (readwrite, copy, nonatomic) NSString *insecureOverridePassword;
#ifdef SP_CODA
@property (readwrite, assign) SPDatabaseDocument *dbDocument;
@@ -240,10 +221,6 @@
@property (readonly, assign) BOOL isConnecting;
@property (readonly, assign) BOOL isEditingConnection;
-- (NSString *)keychainPassword;
-- (NSString *)keychainPasswordForSSH;
-- (NSString *)actualPasswordForAuthPlugin:(NSString *)pluginName;
-
// Connection processes
- (IBAction)initiateConnection:(id)sender;
- (IBAction)cancelConnection:(id)sender;
diff --git a/Source/SPConnectionController.m b/Source/SPConnectionController.m
index 8df37973..018ecf93 100644
--- a/Source/SPConnectionController.m
+++ b/Source/SPConnectionController.m
@@ -114,9 +114,6 @@ static BOOL FindLinesInFile(NSData *fileData,const void *first,size_t first_len,
- (void)_documentWillClose:(NSNotification *)notification;
-- (void)_beginRequestPasswordForInsecurePlugin:(NSString *)pluginName;
-- (void)_insecurePasswordAlertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo;
-
static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2, void *key);
#endif
@@ -166,14 +163,11 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2,
@synthesize sshKeyLocation;
@synthesize sshPort;
@synthesize useCompression;
-@synthesize agreedInsecurePlugin = agreedInsecurePlugin;
-@synthesize insecureOverridePassword = insecureOverridePassword;
#ifdef SP_CODA
@synthesize dbDocument;
#endif
-@synthesize connectionKeychainID = connectionKeychainID;
@synthesize connectionKeychainItemName;
@synthesize connectionKeychainItemAccount;
@synthesize connectionSSHKeychainItemName;
@@ -182,155 +176,6 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2,
@synthesize isConnecting;
@synthesize isEditingConnection;
-- (NSString *)keychainPassword
-{
- NSString *kcItemName = [self connectionKeychainItemName];
- // If no keychain item is available, return an empty password
- if (!kcItemName) return nil;
-
- // Otherwise, pull the password from the keychain using the details from this connection
- NSString *kcPassword = [keychain getPasswordForName:kcItemName account:[self connectionKeychainItemAccount]];
-
- return kcPassword;
-}
-
-- (NSString *)keychainPasswordForSSH
-{
- if (![self connectionKeychainItemName]) return nil;
-
- // Otherwise, pull the password from the keychain using the details from this connection
- NSString *kcSSHPassword = [keychain getPasswordForName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount];
-
- return kcSSHPassword;
-}
-
-/**
- * This method is responsible for handing the user password to SPMySQL when it needs it.
- * It will receive the name of the auth plugin mysql plans to use and with
- * that it has to decide whether to fetch the password from keychain, or present
- * additional prompts to the user (e.g. in case the auth plugin is insecure).
- *
- * Note: The 5.5 libmysqlclient on the first attempt ignores the requested plugin
- * and tries to guess one instead. Only if that fails it will try to use
- * the server's suggested one. Thus this method may be invoked multiple times
- * during a single connection attempt.
- *
- * @return The actual user password to give to the mysql auth plugin.
- * This may be empty (no password).
- * This may be nil, which will be interpreted as "cancelled by user"
- *
- * This MUST be called on the UI thread!
- */
-- (NSString *)actualPasswordForAuthPlugin:(NSString *)pluginName
-{
- NSArray *securePluginNames = @[
- @"mysql_native_password", //default on 4.1+
- //@"sha256_password", // not supported by the 5.5 client, only secure when used with TLS server cert checks!
- // over SSL: password is transmitted in plaintext
- // over plaintext: the mysql client uses assymetric crypto to encrypt the password and
- // send the encrypted plaintext to the server (this requires the OpenSSL
- // libs, so doesn't work with our client builds)
- ];
- //INSECURE:
- // mysql_clear_password; Plaintext password
- // mysql_old_password; Deprecated, used with pre-4.1 servers. No longer supported in 5.7.5+ clients
- //UNSUPPORTED:
- // authentication_windows_client; Kerberos/NTLM auth. Not supported on UNIX by libmysqlclient
- // authentication_ldap_sasl_client; MySQL Enterprise
- // auth_test_plugin; Developer example only
-
- // TODO incorporate SSL/TLS state in decision (cleartext over trusted SSL should be fine)
- // This is not possible right now, because someone with access to the Mac could just swap the SSL CA cert
- // and server ip to something he controls. So if Sequel Pro would just check that SSL was in use, it could
- // still be attacked to easily give the password away.
- // Instead we have to link all critical connection parameters (host+port+ssh-tunnel+ssl server or ca cert hash)
- // to the password in keychain and make sure none of them changed before giving the password to libmysqlclient.
-
- if(![securePluginNames containsObject:pluginName]) {
- if(![pluginName isEqualToString:agreedInsecurePlugin]) {
- // since the user will probably take longer to answer the dialog than the connection timeout is
- // (and because the UI sheet is async anyway),
- // we will do a little trick and disconnect mysql right now and retry, once we actually have the password
- [self performSelector:@selector(_beginRequestPasswordForInsecurePlugin:) withObject:pluginName afterDelay:0.0];
- cancellingConnection = YES;
- return nil;
- }
- }
-
- NSString *pass;
- // override password always wins
- if ((pass = [self insecureOverridePassword])) {
- ;
- }
- // Only set the password if there is no Keychain item set and the connection is not being tested.
- // The connection will otherwise ask the delegate for passwords in the Keychain.
- else if ((!connectionKeychainItemName || isTestingConnection) && (pass = [self password])) {
- ;
- }
- else {
- pass = [self keychainPassword];
- }
-
- return (pass ? pass : @""); //returning nil would mean cancelled
-}
-
-- (void)_beginRequestPasswordForInsecurePlugin:(NSString *)pluginName
-{
- // if the user presses "Disconnect" in the dialog OR
- // if this was only a test connection OR
- // if the connection failed anyway
- // -> agreedInsecurePlugin will be cleared again via -_restoreConnectionInterface
- [self setAgreedInsecurePlugin:pluginName];
-
- //show modal warning dialog
- NSAlert *alert = [[NSAlert alloc] init]; //released in alert callback
- [alert setAlertStyle:NSAlertStyleCritical];
- [alert setMessageText:NSLocalizedString(@"Transmit password insecurely?",@"Connection dialog : password security error alert : title")];
- [alert setInformativeText:NSLocalizedString(@"The MySQL server has requested the password to be transmitted in an insecure manner. This could be indicative of an attack on the server or your network connection!\n\nIf you still want to continue anyway, manually reenter your connection password.",@"Connection dialog : password security error alert : detail message")];
- [alert setAccessoryView:requestPasswordAccessoryView];
-
- [requestPasswordPluginNameField setStringValue:pluginName];
-
- NSButton *connectAnywayButton = [alert addButtonWithTitle:NSLocalizedString(@"Connect Anyway",@"Connection dialog : password security error alert : confirm button")];
- [connectAnywayButton setTag:NSAlertDefaultReturn];
-
- NSButton *disconnectButton = [alert addButtonWithTitle:NSLocalizedString(@"Disconnect",@"Connection dialog : password security error alert : cancel button")];
- [disconnectButton setTag:NSAlertAlternateReturn];
-
- [alert beginSheetModalForWindow:[dbDocument parentWindow]
- modalDelegate:self
- didEndSelector:@selector(_insecurePasswordAlertDidEnd:returnCode:contextInfo:)
- contextInfo:NULL];
-}
-
-- (void)_insecurePasswordAlertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
-{
- NSString *pass = nil;
- if(returnCode == NSAlertDefaultReturn) {
- pass = [NSString stringWithString:[requestPasswordPasswordField stringValue]];
- }
- [requestPasswordPasswordField setStringValue:@""];
-
- [alert autorelease];
-
- cancellingConnection = NO;
-
- //cancelled by user?
- if(!pass) {
- [self _restoreConnectionInterface];
- return;
- }
-
- // if the manually given password matches the keychain password there is no need to keep it in memory,
- // otherwise we use the override password ivar in order to not affect the normal logic
- if (!connectionKeychainItemName || ![pass isEqualToString:[self keychainPassword]]) {
- [self setInsecureOverridePassword:pass];
- }
-
- [[pass retain] release]; //free it asap
- [self initiateMySQLConnection]; //reconnect
-}
-
#pragma mark -
#pragma mark Connection processes
@@ -892,13 +737,11 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2,
#ifndef SP_CODA
// Clear the keychain referral items as appropriate
- [self setConnectionKeychainID:nil];
+ if (connectionKeychainID) SPClear(connectionKeychainID);
if (connectionKeychainItemName) SPClear(connectionKeychainItemName);
if (connectionKeychainItemAccount) SPClear(connectionKeychainItemAccount);
if (connectionSSHKeychainItemName) SPClear(connectionSSHKeychainItemName);
if (connectionSSHKeychainItemAccount) SPClear(connectionSSHKeychainItemAccount);
- [self setAgreedInsecurePlugin:nil];
- [self setInsecureOverridePassword:nil];
SPTreeNode *node = [self selectedFavoriteNode];
if ([node isGroup]) node = nil;
@@ -961,7 +804,7 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2,
}
// Store the selected favorite ID for use with the document on connection
- if ([fav objectForKey:SPFavoriteIDKey]) [self setConnectionKeychainID:[[fav objectForKey:SPFavoriteIDKey] stringValue]];
+ if ([fav objectForKey:SPFavoriteIDKey]) connectionKeychainID = [[[fav objectForKey:SPFavoriteIDKey] stringValue] retain];
// And the same for the SSH password
connectionSSHKeychainItemName = [[keychain nameForSSHForFavoriteName:[fav objectForKey:SPFavoriteNameKey] id:[fav objectForKey:SPFavoriteIDKey]] retain];
@@ -1898,11 +1741,7 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2,
// Reset the window title
[dbDocument updateWindowTitle:self];
[[dbDocument parentTabViewItem] setLabel:[dbDocument displayName]];
-
- //only store that if the connection attempt was successful
- [self setAgreedInsecurePlugin:nil];
- [self setInsecureOverridePassword:nil];
-
+
// Stop the current tab's progress indicator
[dbDocument setIsProcessing:NO];
@@ -2221,6 +2060,12 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2,
}
}
+ // Only set the password if there is no Keychain item set and the connection is not being tested.
+ // The connection will otherwise ask the delegate for passwords in the Keychain.
+ if ((!connectionKeychainItemName || isTestingConnection) && [self password]) {
+ [mySQLConnection setPassword:[self password]];
+ }
+
// Enable SSL if set
if ([self useSSL]) {
[mySQLConnection setUseSSL:YES];
@@ -2262,11 +2107,6 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2,
[mySQLConnection setUseKeepAlive:[[prefs objectForKey:SPUseKeepAlive] boolValue]];
[mySQLConnection setKeepAliveInterval:[[prefs objectForKey:SPKeepAliveInterval] floatValue]];
- // We can always enable the cleartext plugin (this does not yet mean it will actually be used),
- // since mysql will ask us for the password in -actualPasswordForAuthPlugin: at which point
- // we get to decide what to do with the actual plugin.
- [mySQLConnection setAllowCleartextPlugin:YES];
-
// Connect
[mySQLConnection connect];
@@ -2532,6 +2372,8 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2,
for (NSUInteger i = 0; i < [toolbarItems count]; i++) [[toolbarItems objectAtIndex:i] setEnabled:YES];
#endif
+ if (connectionKeychainID) [dbDocument setKeychainID:connectionKeychainID];
+
// Pass the connection to the table document, allowing it to set
// up the other classes and the rest of the interface.
[dbDocument setConnection:mySQLConnection];
@@ -3325,6 +3167,7 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2,
#endif
// Keychain references
+ connectionKeychainID = nil;
connectionKeychainItemName = nil;
connectionKeychainItemAccount = nil;
connectionSSHKeychainItemName = nil;
@@ -3778,13 +3621,11 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2,
SPClear(nibObjectsToRelease);
- [self setConnectionKeychainID:nil];
+ if (connectionKeychainID) SPClear(connectionKeychainID);
if (connectionKeychainItemName) SPClear(connectionKeychainItemName);
if (connectionKeychainItemAccount) SPClear(connectionKeychainItemAccount);
if (connectionSSHKeychainItemName) SPClear(connectionSSHKeychainItemName);
if (connectionSSHKeychainItemAccount) SPClear(connectionSSHKeychainItemAccount);
- [self setAgreedInsecurePlugin:nil];
- [self setInsecureOverridePassword:nil];
#ifndef SP_CODA
if (currentFavorite) SPClear(currentFavorite);
diff --git a/Source/SPDatabaseDocument.h b/Source/SPDatabaseDocument.h
index c89f7847..de7dd55e 100644
--- a/Source/SPDatabaseDocument.h
+++ b/Source/SPDatabaseDocument.h
@@ -262,6 +262,8 @@
NSMutableArray *runningActivitiesArray;
#endif
+ NSString *keyChainID;
+
#ifndef SP_CODA /* ivars */
NSThread *printThread;
@@ -340,6 +342,7 @@
#endif
- (void)setConnection:(SPMySQLConnection *)theConnection;
- (SPMySQLConnection *)getConnection;
+- (void)setKeychainID:(NSString *)theID;
// Database methods
- (IBAction)setDatabases:(id)sender;
@@ -448,6 +451,7 @@
- (NSString *)port;
- (NSString *)mySQLVersion;
- (NSString *)user;
+- (NSString *)keyChainID;
- (NSString *)connectionID;
#ifndef SP_CODA /* method decls */
- (NSString *)tabTitleForTooltip;
diff --git a/Source/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m
index fe555241..2340b97e 100644
--- a/Source/SPDatabaseDocument.m
+++ b/Source/SPDatabaseDocument.m
@@ -119,6 +119,7 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
- (void) closeAndDisconnect;
+- (NSString *)keychainPasswordForConnection:(SPMySQLConnection *)connection;
- (NSString *)keychainPasswordForSSHConnection:(SPMySQLConnection *)connection;
@end
@@ -217,6 +218,8 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
alterDatabaseCharsetHelper = nil; //init in awakeFromNib
addDatabaseCharsetHelper = nil;
+ keyChainID = nil;
+
#ifndef SP_CODA /* init ivars */
statusValues = nil;
printThread = nil;
@@ -610,6 +613,14 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
return mySQLConnection;
}
+/**
+ * Sets this connection's Keychain ID.
+ */
+- (void)setKeychainID:(NSString *)theId
+{
+ keyChainID = [[NSString stringWithString:theId] retain];
+}
+
#pragma mark -
#pragma mark Database methods
@@ -2852,6 +2863,11 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
return thePort;
}
+- (NSString *)keyChainID
+{
+ return keyChainID;
+}
+
- (BOOL)isSaveInBundle
{
return _isSavedInBundle;
@@ -4658,8 +4674,7 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
}
[connection setObject:connectionType forKey:@"type"];
- NSString *kcid = [connectionController connectionKeychainID];
- if ([kcid length]) [connection setObject:kcid forKey:@"kcid"];
+ if ([[self keyChainID] length]) [connection setObject:[self keyChainID] forKey:@"kcid"];
[connection setObject:[self name] forKey:@"name"];
[connection setObject:[self host] forKey:@"host"];
[connection setObject:[self user] forKey:@"user"];
@@ -4671,7 +4686,7 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
[connection setObject:[self database] forKey:@"database"];
if (includePasswords) {
- NSString *pw = [connectionController keychainPassword];
+ NSString *pw = [self keychainPasswordForConnection:nil];
if (!pw) pw = [connectionController password];
if (pw) [connection setObject:pw forKey:@"password"];
@@ -4860,10 +4875,9 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
[connectionController setSslCACertFileLocation:[connection objectForKey:@"sslCACertFileLocation"]];
// Set the keychain details if available
- NSString *kcid = (NSString *)[connection objectForKey:@"kcid"];
- if ([kcid length]) {
- [connectionController setConnectionKeychainID:kcid];
- [connectionController setConnectionKeychainItemName:[keychain nameForFavoriteName:[connectionController name] id:kcid]];
+ if ([connection objectForKey:@"kcid"] && [(NSString *)[connection objectForKey:@"kcid"] length]) {
+ [self setKeychainID:[connection objectForKey:@"kcid"]];
+ [connectionController setConnectionKeychainItemName:[keychain nameForFavoriteName:[connectionController name] id:[self keyChainID]]];
[connectionController setConnectionKeychainItemAccount:[keychain accountForUser:[connectionController user] host:[connectionController host] database:[connection objectForKey:@"database"]]];
}
@@ -4871,8 +4885,9 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
if ([connection objectForKey:@"password"])
[connectionController setPassword:[connection objectForKey:@"password"]];
else {
- NSString *pw = [connectionController keychainPassword];
- if (pw) [connectionController setPassword:pw];
+ NSString *pw = [self keychainPasswordForConnection:nil];
+ if (pw)
+ [connectionController setPassword:pw];
}
// Set the socket details, whether or not the type is a socket
@@ -4895,8 +4910,8 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
if ([connection objectForKey:@"ssh_password"])
[connectionController setSshPassword:[connection objectForKey:@"ssh_password"]];
else {
- if ([kcid length]) {
- [connectionController setConnectionSSHKeychainItemName:[keychain nameForSSHForFavoriteName:[connectionController name] id:kcid]];
+ if ([connection objectForKey:@"kcid"] && [(NSString *)[connection objectForKey:@"kcid"] length]) {
+ [connectionController setConnectionSSHKeychainItemName:[keychain nameForSSHForFavoriteName:[connectionController name] id:[self keyChainID]]];
[connectionController setConnectionSSHKeychainItemAccount:[keychain accountForSSHUser:[connectionController sshUser] sshHost:[connectionController sshHost]]];
}
NSString *sshpw = [self keychainPasswordForSSHConnection:nil];
@@ -7133,11 +7148,21 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
}
/**
- * Invoked when the current connection needs a password.
+ * Invoked when the current connection needs a password from the Keychain.
*/
-- (NSString *)passwordForConnection:(SPMySQLConnection *)connection authPlugin:(NSString *)pluginName
+- (NSString *)keychainPasswordForConnection:(SPMySQLConnection *)connection
{
- return [[connectionController onMainThread] actualPasswordForAuthPlugin:pluginName];
+ // If no keychain item is available, return an empty password
+ if (![connectionController connectionKeychainItemName]) return nil;
+
+ // Otherwise, pull the password from the keychain using the details from this connection
+ SPKeychain *keychain = [[SPKeychain alloc] init];
+
+ NSString *password = [keychain getPasswordForName:[connectionController connectionKeychainItemName] account:[connectionController connectionKeychainItemAccount]];
+
+ [keychain release];
+
+ return password;
}
/**
@@ -7148,10 +7173,25 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
- (NSString *)keychainPasswordForSSHConnection:(SPMySQLConnection *)connection
{
// If no keychain item is available, return an empty password
- NSString *password = [connectionController keychainPasswordForSSH];
- if (!password) return @"";
+ if (![connectionController connectionKeychainItemName]) return @"";
- return password;
+ // Otherwise, pull the password from the keychain using the details from this connection
+ SPKeychain *keychain = [[SPKeychain alloc] init];
+
+ NSString *connectionSSHKeychainItemName = [[keychain nameForSSHForFavoriteName:[connectionController name] id:[self keyChainID]] retain];
+ NSString *connectionSSHKeychainItemAccount = [[keychain accountForSSHUser:[connectionController sshUser] sshHost:[connectionController sshHost]] retain];
+ NSString *sshPassword = [keychain getPasswordForName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount];
+
+ if (!sshPassword || ![sshPassword length]) {
+ sshPassword = @"";
+ }
+
+ if (connectionSSHKeychainItemName) [connectionSSHKeychainItemName release];
+ if (connectionSSHKeychainItemAccount) [connectionSSHKeychainItemAccount release];
+
+ [keychain release];
+
+ return sshPassword;
}
/**
@@ -7714,6 +7754,7 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
if (spfPreferences) SPClear(spfPreferences);
if (spfSession) SPClear(spfSession);
if (spfDocData) SPClear(spfDocData);
+ if (keyChainID) SPClear(keyChainID);
if (mainToolbar) SPClear(mainToolbar);
if (titleAccessoryView) SPClear(titleAccessoryView);
if (taskProgressWindow) SPClear(taskProgressWindow);
diff --git a/Source/SPDatabaseStructure.m b/Source/SPDatabaseStructure.m
index 99a274b0..9289b13b 100644
--- a/Source/SPDatabaseStructure.m
+++ b/Source/SPDatabaseStructure.m
@@ -258,7 +258,6 @@
goto cleanup_thread_and_pool;
}
-#if 0
// For future usage - currently unused
// If the affected item name and type - for example, table type and table name - were supplied, extract it.
NSString *affectedItem = nil;
@@ -270,7 +269,6 @@
else
affectedItem = nil;
}
-#endif
// Delete all stored data for the database to be updated, leaving the structure key
[queriedStructure removeObjectForKey:db_id];
@@ -444,11 +442,11 @@ cleanup_thread_and_pool:
#pragma mark SPMySQLConnection delegate methods
/**
- * Forward password requests to the database object.
+ * Forward keychain password requests to the database object.
*/
-- (NSString *)passwordForConnection:(id)connection authPlugin:(NSString *)pluginName
+- (NSString *)keychainPasswordForConnection:(id)connection
{
- return [delegate passwordForConnection:connection authPlugin:pluginName];
+ return [delegate keychainPasswordForConnection:connection];
}
#pragma mark -
diff --git a/Source/SPKeychain.m b/Source/SPKeychain.m
index 61b1f5a8..94b561c5 100644
--- a/Source/SPKeychain.m
+++ b/Source/SPKeychain.m
@@ -43,6 +43,15 @@
if (!(self = [super init])) {
return nil;
}
+
+ NSString *cleartext = [NSProcessInfo processInfo].environment[@"LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN"];
+ if (cleartext != nil) {
+ NSLog(@"LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN is set. Disabling keychain access. See Issue #2437");
+
+ [self release];
+ return nil;
+ }
+
return self;
}