Skip to content

Commit de81a7a

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 ea64621 commit de81a7a

File tree

2 files changed

+78
-6
lines changed

2 files changed

+78
-6
lines changed

src/mysql_capi.c

Lines changed: 25 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;
@@ -1045,7 +1044,11 @@ MySQL_connect(MySQL *self, PyObject *args, PyObject *kwds)
10451044
NULL
10461045
};
10471046

1047+
#ifdef PY3
10481048
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|zzzzkzkzzzO!O!", kwlist,
1049+
#else
1050+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|zzOzkzkzzzO!O!", kwlist,
1051+
#endif
10491052
&host, &user, &password, &database,
10501053
&port, &unix_socket,
10511054
&client_flags,
@@ -1170,9 +1173,25 @@ MySQL_connect(MySQL *self, PyObject *args, PyObject *kwds)
11701173
client_flags= client_flags & ~CLIENT_CONNECT_WITH_DB;
11711174
}
11721175

1173-
res= mysql_real_connect(&self->session,
1174-
host, user, password, database,
1175-
port, unix_socket, client_flags);
1176+
1177+
#ifdef PY3
1178+
res= mysql_real_connect(&self->session, host, user, password, database,
1179+
port, unix_socket, client_flags);
1180+
#else
1181+
char* c_password;
1182+
if (PyUnicode_Check(password))
1183+
{
1184+
PyObject* u_password= PyUnicode_AsUTF8String(password);
1185+
c_password= PyString_AsString(u_password);
1186+
Py_DECREF(u_password);
1187+
}
1188+
else
1189+
{
1190+
c_password= PyString_AsString(password);
1191+
}
1192+
res= mysql_real_connect(&self->session, host, user, c_password, database,
1193+
port, unix_socket, client_flags);
1194+
#endif
11761195

11771196
Py_END_ALLOW_THREADS
11781197

tests/test_bugs.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4110,3 +4110,56 @@ def test_set(self):
41104110
results.append(result.fetchall())
41114111
self.assertEqual(exp, results)
41124112
cur.close()
4113+
4114+
4115+
@unittest.skipIf(not CMySQLConnection, ERR_NO_CEXT)
4116+
class BugOra21656282(tests.MySQLConnectorTests):
4117+
"""BUG#21656282: CONNECT FAILURE WITH C-EXT WHEN PASSWORD CONTAINS UNICODE
4118+
CHARACTER
4119+
"""
4120+
def setUp(self):
4121+
config = tests.get_mysql_config()
4122+
self.cnx = connection.MySQLConnection(**config)
4123+
self.host = 'localhost' if config['unix_socket'] and os.name != 'nt' \
4124+
else config['host']
4125+
self.user = 'unicode_user'
4126+
self.password = u'步'
4127+
4128+
# Use utf8mb4 character set
4129+
self.cnx.cmd_query("SET character_set_server='utf8mb4'")
4130+
4131+
# Drop user if exists
4132+
self._drop_user(self.host, self.user)
4133+
4134+
# Create the user with unicode password
4135+
create_user = (u"CREATE USER '{user}'@'{host}' IDENTIFIED BY "
4136+
u"'{password}'")
4137+
self.cnx.cmd_query(create_user.format(user=self.user, host=self.host,
4138+
password=self.password))
4139+
4140+
# Grant all to new user on database
4141+
grant = "GRANT ALL ON {database}.* TO '{user}'@'{host}'"
4142+
self.cnx.cmd_query(grant.format(database=config['database'],
4143+
user=self.user, host=self.host))
4144+
4145+
def tearDown(self):
4146+
self._drop_user(self.host, self.user)
4147+
4148+
def _drop_user(self, host, user):
4149+
try:
4150+
drop_user = "DROP USER '{user}'@'{host}'"
4151+
self.cnx.cmd_query(drop_user.format(user=user, host=host))
4152+
except errors.DatabaseError:
4153+
# It's OK when drop user fails
4154+
pass
4155+
4156+
def test_unicode_password(self):
4157+
config = tests.get_mysql_config()
4158+
config['user'] = self.user
4159+
config['password'] = self.password
4160+
try:
4161+
cnx = CMySQLConnection(**config)
4162+
except:
4163+
self.fail('Failed using password with unicode characters')
4164+
else:
4165+
cnx.close()

0 commit comments

Comments
 (0)