Skip to content

Commit 27991a9

Browse files
committed
BUG21656282: Connection fails using unicode passwords with C extension
The connector fails when using a password with unicode passwords with the C extension. This issue only occurs using Python 2. This patch fixes this issue by encoding the password when using Python 2. A test was added for regression.
1 parent c1089f2 commit 27991a9

File tree

2 files changed

+77
-6
lines changed

2 files changed

+77
-6
lines changed

src/mysql_capi.c

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,10 +1025,9 @@ MySQL_commit(MySQL *self)
10251025
PyObject*
10261026
MySQL_connect(MySQL *self, PyObject *args, PyObject *kwds)
10271027
{
1028-
char *host= NULL, *user= NULL, *password= NULL, *database= NULL,
1029-
*unix_socket= NULL;
1028+
char *host= NULL, *user= NULL, *database= NULL, *unix_socket= NULL;
10301029
char *ssl_ca= NULL, *ssl_cert= NULL, *ssl_key= NULL;
1031-
PyObject *charset_name, *compress, *ssl_verify_cert;
1030+
PyObject *charset_name, *compress, *ssl_verify_cert, *password;
10321031
const char* auth_plugin;
10331032
unsigned long client_flags= 0;
10341033
unsigned int port= 3306, tmp_uint;
@@ -1046,7 +1045,11 @@ MySQL_connect(MySQL *self, PyObject *args, PyObject *kwds)
10461045
NULL
10471046
};
10481047

1048+
#ifdef PY3
10491049
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|zzzzkzkzzzO!O!", kwlist,
1050+
#else
1051+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|zzOzkzkzzzO!O!", kwlist,
1052+
#endif
10501053
&host, &user, &password, &database,
10511054
&port, &unix_socket,
10521055
&client_flags,
@@ -1179,9 +1182,24 @@ MySQL_connect(MySQL *self, PyObject *args, PyObject *kwds)
11791182
mysql_options(&self->session, MYSQL_OPT_LOCAL_INFILE, (unsigned int*)&abool);
11801183
}
11811184

1182-
res= mysql_real_connect(&self->session,
1183-
host, user, password, database,
1184-
port, unix_socket, client_flags);
1185+
#ifdef PY3
1186+
res= mysql_real_connect(&self->session, host, user, password, database,
1187+
port, unix_socket, client_flags);
1188+
#else
1189+
char* c_password;
1190+
if (PyUnicode_Check(password))
1191+
{
1192+
PyObject* u_password= PyUnicode_AsUTF8String(password);
1193+
c_password= PyString_AsString(u_password);
1194+
Py_DECREF(u_password);
1195+
}
1196+
else
1197+
{
1198+
c_password= PyString_AsString(password);
1199+
}
1200+
res= mysql_real_connect(&self->session, host, user, c_password, database,
1201+
port, unix_socket, client_flags);
1202+
#endif
11851203

11861204
Py_END_ALLOW_THREADS
11871205

tests/test_bugs.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4143,3 +4143,56 @@ def test_set(self):
41434143
results.append(result.fetchall())
41444144
self.assertEqual(exp, results)
41454145
cur.close()
4146+
4147+
4148+
@unittest.skipIf(not CMySQLConnection, ERR_NO_CEXT)
4149+
class BugOra21656282(tests.MySQLConnectorTests):
4150+
"""BUG#21656282: CONNECT FAILURE WITH C-EXT WHEN PASSWORD CONTAINS UNICODE
4151+
CHARACTER
4152+
"""
4153+
def setUp(self):
4154+
config = tests.get_mysql_config()
4155+
self.cnx = connection.MySQLConnection(**config)
4156+
self.host = 'localhost' if config['unix_socket'] and os.name != 'nt' \
4157+
else config['host']
4158+
self.user = 'unicode_user'
4159+
self.password = u'步'
4160+
4161+
# Use utf8mb4 character set
4162+
self.cnx.cmd_query("SET character_set_server='utf8mb4'")
4163+
4164+
# Drop user if exists
4165+
self._drop_user(self.host, self.user)
4166+
4167+
# Create the user with unicode password
4168+
create_user = (u"CREATE USER '{user}'@'{host}' IDENTIFIED BY "
4169+
u"'{password}'")
4170+
self.cnx.cmd_query(create_user.format(user=self.user, host=self.host,
4171+
password=self.password))
4172+
4173+
# Grant all to new user on database
4174+
grant = "GRANT ALL ON {database}.* TO '{user}'@'{host}'"
4175+
self.cnx.cmd_query(grant.format(database=config['database'],
4176+
user=self.user, host=self.host))
4177+
4178+
def tearDown(self):
4179+
self._drop_user(self.host, self.user)
4180+
4181+
def _drop_user(self, host, user):
4182+
try:
4183+
drop_user = "DROP USER '{user}'@'{host}'"
4184+
self.cnx.cmd_query(drop_user.format(user=user, host=host))
4185+
except errors.DatabaseError:
4186+
# It's OK when drop user fails
4187+
pass
4188+
4189+
def test_unicode_password(self):
4190+
config = tests.get_mysql_config()
4191+
config['user'] = self.user
4192+
config['password'] = self.password
4193+
try:
4194+
cnx = CMySQLConnection(**config)
4195+
except:
4196+
self.fail('Failed using password with unicode characters')
4197+
else:
4198+
cnx.close()

0 commit comments

Comments
 (0)