aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m50
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnectionDelegate.h3
-rw-r--r--Source/SPDatabaseDocument.m22
-rw-r--r--Source/SPDatabaseStructure.m6
4 files changed, 54 insertions, 27 deletions
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
index 7738f665..d96a3b85 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
@@ -730,22 +730,27 @@ static uint64_t _elapsedMicroSecondsSinceAbsoluteTime(uint64_t comparisonTime)
- (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 a password was supplied, use it; otherwise ask the delegate if appropriate.
if (password) {
passwd = password;
- } else if ([delegate respondsToSelector:@selector(keychainPasswordForConnection:)]) {
- passwd = [delegate keychainPasswordForConnection:self]; //TODO pass pluginName to client
+ }
+ else if ([delegate respondsToSelector:@selector(keychainPasswordForConnection: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 keychainPasswordForConnection:self authPlugin:plugin];
}
// shortcut for empty/nil password
@@ -754,7 +759,20 @@ static uint64_t _elapsedMicroSecondsSinceAbsoluteTime(uint64_t comparisonTime)
return;
}
- NSStringEncoding connectEncodingNS = [SPMySQLConnection stringEncodingForMySQLCharset:connection->options.charset_name];
+ // 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) {
@@ -1218,7 +1236,7 @@ static uint64_t _elapsedMicroSecondsSinceAbsoluteTime(uint64_t comparisonTime)
@end
-void PasswordCallback(MYSQL *mysql, const char *plugin, void (^with_password)(const char *passwd))
+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];
@@ -1234,7 +1252,7 @@ void PasswordCallback(MYSQL *mysql, const char *plugin, void (^with_password)(co
errno_t LegacyMemsetS(void *s, rsize_t smax, int c, rsize_t n)
{
volatile unsigned char * addr = (volatile unsigned char *)s;
- while(n--) *addr++ = c;
+ while(n--) *addr++ = (unsigned char)c;
return 0;
}
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnectionDelegate.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnectionDelegate.h
index 1e238c12..69a6ebac 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnectionDelegate.h
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnectionDelegate.h
@@ -69,8 +69,9 @@
* can be used to look it up and supplied on demand.
*
* @param connection The connection instance to supply the password for
+ * @param pluginName The auth plugin libmysqlclients wants to use the password with
*/
-- (NSString *)keychainPasswordForConnection:(id)connection;
+- (NSString *)keychainPasswordForConnection:(id)connection authPlugin:(NSString *)pluginName;
/**
* Notifies the delegate that no underlying connection is available,
diff --git a/Source/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m
index 2340b97e..7b1d51de 100644
--- a/Source/SPDatabaseDocument.m
+++ b/Source/SPDatabaseDocument.m
@@ -119,7 +119,7 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
- (void) closeAndDisconnect;
-- (NSString *)keychainPasswordForConnection:(SPMySQLConnection *)connection;
+- (NSString *)keychainPassword;
- (NSString *)keychainPasswordForSSHConnection:(SPMySQLConnection *)connection;
@end
@@ -4686,7 +4686,7 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
[connection setObject:[self database] forKey:@"database"];
if (includePasswords) {
- NSString *pw = [self keychainPasswordForConnection:nil];
+ NSString *pw = [self keychainPassword];
if (!pw) pw = [connectionController password];
if (pw) [connection setObject:pw forKey:@"password"];
@@ -4885,9 +4885,8 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
if ([connection objectForKey:@"password"])
[connectionController setPassword:[connection objectForKey:@"password"]];
else {
- NSString *pw = [self keychainPasswordForConnection:nil];
- if (pw)
- [connectionController setPassword:pw];
+ NSString *pw = [self keychainPassword];
+ if (pw) [connectionController setPassword:pw];
}
// Set the socket details, whether or not the type is a socket
@@ -7150,15 +7149,22 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
/**
* Invoked when the current connection needs a password from the Keychain.
*/
-- (NSString *)keychainPasswordForConnection:(SPMySQLConnection *)connection
+- (NSString *)keychainPasswordForConnection:(SPMySQLConnection *)connection authPlugin:(NSString *)pluginName
{
+ //TODO check plugin name to see whether we want to fetch it from keychain
+ return [self keychainPassword];
+}
+
+- (NSString *)keychainPassword
+{
+ NSString *kcItemName = [connectionController connectionKeychainItemName];
// If no keychain item is available, return an empty password
- if (![connectionController connectionKeychainItemName]) return nil;
+ if (!kcItemName) 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]];
+ NSString *password = [keychain getPasswordForName:kcItemName account:[connectionController connectionKeychainItemAccount]];
[keychain release];
diff --git a/Source/SPDatabaseStructure.m b/Source/SPDatabaseStructure.m
index 9289b13b..1eb4352a 100644
--- a/Source/SPDatabaseStructure.m
+++ b/Source/SPDatabaseStructure.m
@@ -258,6 +258,7 @@
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;
@@ -269,6 +270,7 @@
else
affectedItem = nil;
}
+#endif
// Delete all stored data for the database to be updated, leaving the structure key
[queriedStructure removeObjectForKey:db_id];
@@ -444,9 +446,9 @@ cleanup_thread_and_pool:
/**
* Forward keychain password requests to the database object.
*/
-- (NSString *)keychainPasswordForConnection:(id)connection
+- (NSString *)keychainPasswordForConnection:(id)connection authPlugin:(NSString *)pluginName
{
- return [delegate keychainPasswordForConnection:connection];
+ return [delegate keychainPasswordForConnection:connection authPlugin:pluginName];
}
#pragma mark -