aboutsummaryrefslogtreecommitdiffstats
path: root/Frameworks/SPMySQLFramework/Source
diff options
context:
space:
mode:
Diffstat (limited to 'Frameworks/SPMySQLFramework/Source')
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h1
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m85
2 files changed, 69 insertions, 17 deletions
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h b/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
index 8fdf4e7e..b2fa9f5d 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
@@ -41,6 +41,7 @@
- (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.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
index d50375e2..f6755f60 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
@@ -33,6 +33,10 @@
#include <mach/mach_time.h>
#include <pthread.h>
#include <SystemConfiguration/SCNetworkReachability.h>
+#include <errno.h>
+#define __STDC_WANT_LIB_EXT1__ 1
+#include <string.h>
+#include <stdlib.h>
// Thread flag constant
static pthread_key_t mySQLThreadInitFlagKey;
@@ -49,6 +53,7 @@ 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
@@ -612,27 +617,11 @@ static uint64_t _elapsedMicroSecondsSinceAbsoluteTime(uint64_t comparisonTime)
// 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) {
@@ -690,7 +679,11 @@ static uint64_t _elapsedMicroSecondsSinceAbsoluteTime(uint64_t comparisonTime)
}
}
- MYSQL *connectionStatus = mysql_real_connect(theConnection, theHost, theUsername, thePassword, NULL, (unsigned int)port, theSocket, [self clientFlags]);
+ // 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]);
// If the connection failed, return NULL
if (theConnection != connectionStatus) {
@@ -733,6 +726,58 @@ static uint64_t _elapsedMicroSecondsSinceAbsoluteTime(uint64_t comparisonTime)
return theConnection;
}
+- (void)_mysqlConnection:(MYSQL *)connection wantsPassword:(void (^)(const char *passwd))inBlock withPlugin:(const char *)pluginName
+{
+ // 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...
+ NSString *passwd = nil;
+
+ if (password) {
+ passwd = password;
+ } else if ([delegate respondsToSelector:@selector(keychainPasswordForConnection:)]) {
+ passwd = [delegate keychainPasswordForConnection:self]; //TODO pass pluginName to client
+ }
+
+ // shortcut for empty/nil password
+ if(![passwd length]) {
+ inBlock(NULL);
+ return;
+ }
+
+ NSStringEncoding connectEncodingNS = [SPMySQLConnection stringEncodingForMySQLCharset:connection->options.charset_name];
+ 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__);
+ }
+
+ memset_s(cBuffer, cLength, '\0', 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
@@ -1162,3 +1207,9 @@ static uint64_t _elapsedMicroSecondsSinceAbsoluteTime(uint64_t comparisonTime)
}
@end
+
+void PasswordCallback(MYSQL *mysql, const char *plugin, void (^with_password)(const char *passwd))
+{
+ assert(mysql && mysql->sp_context);
+ [(SPMySQLConnection *)mysql->sp_context _mysqlConnection:mysql wantsPassword:with_password withPlugin:plugin];
+}