Skip to content

Commit 2c9c1fc

Browse files
lawrinlawrin
authored andcommitted
New boolean connection option is introduced - OPT_ENABLE_CLEARTEXT_PLUGIN. It's purpose is to enable libmysql cleartext plugin that is required since versions 5.5.27 and 5.6.7. Client cleartext plugin is required for PAM authentication.
The patch contains the testcase for the new option. In the mysql_connection.cpp there have been 3 arrays introduced to map connection option name to the sql::mysql::MySQL_Connection_Options enum member and function template that makes mysql_options calls using those arrays. Options divided into 3 arrays to enforce their correct option type. Also options method of the MySQL_NativeConnectionWrapper class(and in the interface it implements) has been overloaded to accept int, bool and SQLString values.
1 parent 44dc07f commit 2c9c1fc

File tree

9 files changed

+216
-52
lines changed

9 files changed

+216
-52
lines changed

CHANGES

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2323
*/
2424

25+
GA 1.1.3 -
26+
- Added boolean connection option OPT_ENABLE_CLEARTEXT_PLUGIN allowing to
27+
enable cleartext libmysql plugin. That plugin is required for some
28+
authentication methods, e.g. PAM. (Bug#16520952)
29+
2530
GA 1.1.2 - 2013-01-16
2631
- Expired password support. Connection options
2732
OPT_CAN_HANDLE_EXPIRED_PASSWORDS(bool, application can deal with expired
@@ -35,6 +40,9 @@ GA 1.1.2 - 2013-01-16
3540
CLIENT_MULTI_STATEMENTS connection option still has to be selected.
3641
getUpdateCount will return data for the last executed statement.
3742

43+
- ABI change - MySQL_Connection class object size changed. If you don't use
44+
that class directly you shouldn't be affected by that.
45+
3846
- Built against libmysql 5.6.10
3947

4048
GA 1.1.1 - 2012-08-06

driver/mysql_connection.cpp

Lines changed: 84 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ static const String2IntMap flagsOptions[]=
204204
/* {{{ readFlag(::sql::SQLString, int= 0) -I- */
205205
/** Check if connection option pointed by map iterator defines a connection
206206
flag */
207-
static bool read_flag(ConnectOptionsMap::const_iterator &cit, int &flags)
207+
static bool read_connection_flag(ConnectOptionsMap::const_iterator &cit, int &flags)
208208
{
209209
const bool * value;
210210

@@ -225,9 +225,55 @@ static bool read_flag(ConnectOptionsMap::const_iterator &cit, int &flags)
225225
}
226226
return false;
227227
}
228-
229228
/* }}} */
230229

230+
/* Array for mapping of boolean connection options to mysql_options call */
231+
static const String2IntMap booleanOptions[]=
232+
{
233+
{"OPT_REPORT_DATA_TRUNCATION", MYSQL_REPORT_DATA_TRUNCATION},
234+
{"OPT_ENABLE_CLEARTEXT_PLUGIN", MYSQL_ENABLE_CLEARTEXT_PLUGIN}
235+
};
236+
/* Array for mapping of integer connection options to mysql_options call */
237+
static const String2IntMap intOptions[]=
238+
{
239+
{"OPT_CONNECT_TIMEOUT", MYSQL_OPT_CONNECT_TIMEOUT},
240+
{"OPT_READ_TIMEOUT", MYSQL_OPT_READ_TIMEOUT},
241+
{"OPT_WRITE_TIMEOUT", MYSQL_OPT_WRITE_TIMEOUT}
242+
};
243+
/* Array for mapping of string connection options to mysql_options call */
244+
static const String2IntMap stringOptions[]=
245+
{
246+
{"preInit", MYSQL_INIT_COMMAND}
247+
};
248+
249+
template<class T>
250+
bool process_connection_option(ConnectOptionsMap::const_iterator &option,
251+
const String2IntMap options_map[],
252+
size_t map_size,
253+
boost::shared_ptr< NativeAPI::NativeConnectionWrapper > &proxy)
254+
{
255+
const T * value;
256+
257+
for (unsigned int i = 0; i < map_size; ++i) {
258+
259+
if (!option->first.compare(options_map[i].key)) {
260+
261+
if (!(value = boost::get<T>(&option->second))) {
262+
sql::SQLString err("Option ");
263+
err.append(option->first).append(" is not of expected type");
264+
throw sql::InvalidArgumentException(err);
265+
}
266+
proxy->options(static_cast<sql::mysql::MySQL_Connection_Options>(options_map[i].value),
267+
*value);
268+
269+
return true;
270+
}
271+
}
272+
273+
return false;
274+
}
275+
276+
231277
/*
232278
We support :
233279
- hostName
@@ -262,9 +308,17 @@ static bool read_flag(ConnectOptionsMap::const_iterator &cit, int &flags)
262308
- OPT_RECONNECT
263309
- OPT_CHARSET_NAME
264310
- OPT_REPORT_DATA_TRUNCATION
311+
- OPT_CAN_HANDLE_EXPIRED_PASSWORDS
312+
- OPT_ENABLE_CLEARTEXT_PLUGIN
313+
- preInit
314+
- postInit
315+
316+
To add new connection option that maps to a myql_options call, only add its
317+
mapping to sql::mysql::MySQL_Connection_Options value to one of arrays above
318+
- booleanOptions, intOptions, stringOptions. You might need to add new member
319+
to the sql::mysql::MySQL_Connection_Options enum
265320
*/
266321

267-
268322
/* {{{ MySQL_Connection::init() -I- */
269323
void MySQL_Connection::init(ConnectOptionsMap & properties)
270324
{
@@ -305,6 +359,8 @@ void MySQL_Connection::init(ConnectOptionsMap & properties)
305359
}
306360
}
307361

362+
#define PROCESS_CONN_OPTION(option_type, options_map) process_connection_option< option_type >(it, options_map, sizeof(options_map)/sizeof(String2IntMap), proxy)
363+
308364
for (it = properties.begin(); it != properties.end(); ++it) {
309365
if (!it->first.compare("userName")) {
310366
if ((p_s = boost::get< sql::SQLString >(&it->second))) {
@@ -431,24 +487,6 @@ void MySQL_Connection::init(ConnectOptionsMap & properties)
431487
} else {
432488
throw sql::InvalidArgumentException("No bool value passed for metadataUseInfoSchema");
433489
}
434-
}else if (!it->first.compare("OPT_CONNECT_TIMEOUT")) {
435-
if (!(p_i = boost::get< int >(&it->second))) {
436-
throw sql::InvalidArgumentException("No long long value passed for OPT_CONNECT_TIMEOUT");
437-
}
438-
long l_tmp = static_cast<long>(*p_i);
439-
proxy->options(MYSQL_OPT_CONNECT_TIMEOUT, (const char *) &l_tmp);
440-
} else if (!it->first.compare("OPT_READ_TIMEOUT")) {
441-
if (!(p_i = boost::get< int >(&it->second))) {
442-
throw sql::InvalidArgumentException("No long long value passed for OPT_READ_TIMEOUT");
443-
}
444-
long l_tmp = static_cast<long>(*p_i);
445-
proxy->options(MYSQL_OPT_READ_TIMEOUT, (const char *) &l_tmp);
446-
} else if (!it->first.compare("OPT_WRITE_TIMEOUT")) {
447-
if (!(p_i = boost::get< int >(&it->second))) {
448-
throw sql::InvalidArgumentException("No long long value passed for OPT_WRITE_TIMEOUT");
449-
}
450-
long l_tmp = static_cast<long>(*p_i);
451-
proxy->options(MYSQL_OPT_WRITE_TIMEOUT, (const char *) &l_tmp);
452490
} else if (!it->first.compare("OPT_RECONNECT")) {
453491
if (!(p_b = boost::get<bool>(&it->second))) {
454492
throw sql::InvalidArgumentException("No bool value passed for OPT_RECONNECT");
@@ -460,11 +498,6 @@ void MySQL_Connection::init(ConnectOptionsMap & properties)
460498
throw sql::InvalidArgumentException("No SQLString value passed for OPT_CHARSET_NAME");
461499
}
462500
defaultCharset = *p_s;
463-
} else if (!it->first.compare("OPT_REPORT_DATA_TRUNCATION")) {
464-
if (!(p_b = boost::get< bool >(&it->second))) {
465-
throw sql::InvalidArgumentException("No bool value passed for OPT_REPORT_DATA_TRUNCATION");
466-
}
467-
proxy->options(MYSQL_REPORT_DATA_TRUNCATION, (const char *) p_b);
468501
} else if (!it->first.compare("OPT_NAMED_PIPE")) {
469502
/* Not sure it is really needed */
470503
uri.setProtocol(NativeAPI::PROTOCOL_PIPE);
@@ -484,26 +517,36 @@ void MySQL_Connection::init(ConnectOptionsMap & properties)
484517
/* We do not care here about server version */
485518
proxy->options(MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, (const char*)p_b);
486519
}
487-
488-
} else if (!it->first.compare("preInit")) {
489-
if ((p_s = boost::get< sql::SQLString >(&it->second))) {
490-
proxy->options(MYSQL_INIT_COMMAND, p_s->c_str());
491-
} else {
492-
throw sql::InvalidArgumentException("No string value passed for preInit");
493-
}
494-
}else if (!it->first.compare("postInit")) {
520+
} else if (!it->first.compare("postInit")) {
495521
if ((p_s = boost::get< sql::SQLString >(&it->second))) {
496522
postInit= *p_s;
497523
} else {
498524
throw sql::InvalidArgumentException("No string value passed for postInit");
499525
}
500-
} else if (read_flag(it, flags)) {
526+
527+
/* If you need to add new integer connection option that should result in
528+
calling mysql_optiong - add its mapping to the intOptions array
529+
*/
530+
} else if (PROCESS_CONN_OPTION(int, intOptions)) {
531+
// Nothing to do here
532+
533+
/* For boolean coonection option - add mapping to booleanOptions array */
534+
} else if (PROCESS_CONN_OPTION(bool, booleanOptions)) {
535+
// Nothing to do here
536+
537+
/* For string coonection option - add mapping to stringOptions array */
538+
} else if (PROCESS_CONN_OPTION(sql::SQLString, stringOptions)) {
539+
// Nothing to do here
540+
} else if (read_connection_flag(it, flags)) {
501541
// Nothing to do here
502542
} else {
503543
// TODO: Shouldn't we really create a warning here? as soon as we are able to
504544
// create a warning
505545
}
506-
}
546+
547+
} /* End of cycle on connection options map */
548+
549+
#undef PROCESS_CONNSTR_OPTION
507550

508551
/* libmysql shouldn't think it is too smart */
509552
if (tcpProtocol(uri) && !uri.Host().compare(util::LOCALHOST)) {
@@ -561,13 +604,12 @@ void MySQL_Connection::init(ConnectOptionsMap & properties)
561604
&& client_doesnt_support_exp_pwd) {
562605

563606
native_error= deCL_CANT_HANDLE_EXP_PWD;
564-
error_message= "Your password has expired. Your instance of"
607+
error_message= "Your password has expired, but your instance of"
565608
" Connector/C++ is not linked against mysql client library that"
566-
" allows to change it. resetting of an expired password. To"
567-
" resolve this, you either need to change the password with"
568-
" mysql client that can do it or rebuild your instance of"
569-
" Connector/C++ against mysql client library that supports"
570-
" resetting of an expired password.";
609+
" allows to reset it. To resolve this you either need to change"
610+
" the password with mysql client that is capable to do that,"
611+
" or rebuild your instance of Connector/C++ against mysql client"
612+
" library that supports resetting of an expired password.";
571613
} else {
572614
error_message= proxy->error();
573615
}

driver/nativeapi/mysql_native_connection_wrapper.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
2+
Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
33
44
The MySQL Connector/C++ is licensed under the terms of the GPLv2
55
<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
@@ -215,6 +215,35 @@ MySQL_NativeConnectionWrapper::options(::sql::mysql::MySQL_Connection_Options op
215215
/* }}} */
216216

217217

218+
/* {{{ MySQL_NativeConnectionWrapper::options(SQLString &) */
219+
int
220+
MySQL_NativeConnectionWrapper::options(::sql::mysql::MySQL_Connection_Options option,
221+
const ::sql::SQLString &str)
222+
{
223+
return api->options(mysql, static_cast< mysql_option >(option), str.c_str());
224+
}
225+
/* }}} */
226+
227+
228+
/* {{{ MySQL_NativeConnectionWrapper::options(bool &) */
229+
int
230+
MySQL_NativeConnectionWrapper::options(::sql::mysql::MySQL_Connection_Options option,
231+
const bool &option_val)
232+
{
233+
my_bool dummy= option_val ? '\1' : '\0';
234+
return api->options(mysql, static_cast< mysql_option >(option), &dummy);
235+
}
236+
237+
238+
/* {{{ MySQL_NativeConnectionWrapper::options(int &) */
239+
int
240+
MySQL_NativeConnectionWrapper::options(::sql::mysql::MySQL_Connection_Options option,
241+
const int &option_val)
242+
{
243+
return api->options(mysql, static_cast< mysql_option >(option), &option_val);
244+
}
245+
246+
218247
/* {{{ MySQL_NativeConnectionWrapper::query() */
219248
int
220249
MySQL_NativeConnectionWrapper::query(const SQLString & stmt_str)

driver/nativeapi/mysql_native_connection_wrapper.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
2+
Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
33
44
The MySQL Connector/C++ is licensed under the terms of the GPLv2
55
<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
@@ -102,6 +102,10 @@ class MySQL_NativeConnectionWrapper : public NativeConnectionWrapper
102102
int next_result();
103103

104104
int options(::sql::mysql::MySQL_Connection_Options, const void * );
105+
int options(::sql::mysql::MySQL_Connection_Options,
106+
const ::sql::SQLString &);
107+
int options(::sql::mysql::MySQL_Connection_Options, const bool &);
108+
int options(::sql::mysql::MySQL_Connection_Options, const int &);
105109

106110
int query(const ::sql::SQLString &);
107111

driver/nativeapi/native_connection_wrapper.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
2+
Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
33
44
The MySQL Connector/C++ is licensed under the terms of the GPLv2
55
<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
@@ -98,6 +98,12 @@ class NativeConnectionWrapper : public boost::noncopyable
9898
virtual int next_result() = 0;
9999

100100
virtual int options(::sql::mysql::MySQL_Connection_Options, const void *) = 0;
101+
virtual int options(::sql::mysql::MySQL_Connection_Options,
102+
const ::sql::SQLString &) = 0;
103+
virtual int options(::sql::mysql::MySQL_Connection_Options,
104+
const bool &) = 0;
105+
virtual int options(::sql::mysql::MySQL_Connection_Options,
106+
const int &) = 0;
101107

102108
virtual int query(const SQLString &) = 0;
103109

test/unit/bugs/bugs.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,8 @@ void bugs::expired_pwd()
270270
{
271271
stmt->executeUpdate("DROP USER ccpp_expired_pwd");
272272
}
273-
catch (sql::SQLException &) {
273+
catch (sql::SQLException &)
274+
{
274275
// Catching exception if user did not exist
275276
}
276277

test/unit/classes/connection.cpp

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
2+
Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
33
44
The MySQL Connector/C++ is licensed under the terms of the GPLv2
55
<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
@@ -2169,5 +2169,78 @@ void connection::loadSameLibraryTwice()
21692169
}
21702170
#endif
21712171

2172+
2173+
/* Test of the OPT_ENABLE_CLEARTEXT_PLUGIN connection of the text
2174+
The test idea - we try to create fake PAM authorized user and try to connect
2175+
using that user first without, and then with the new option selected. In
2176+
first case the error has to be that cleartext plugin could not be loaded, and
2177+
in second case the error has to be different */
2178+
void connection::enableClearTextAuth()
2179+
{
2180+
int serverVersion= getMySQLVersion(con);
2181+
if ( serverVersion < 55027 || serverVersion > 56000 && serverVersion < 56007)
2182+
{
2183+
SKIP("The server does not support tested functionality(cleartext plugin enabling)");
2184+
}
2185+
2186+
try
2187+
{
2188+
stmt->executeUpdate("DROP USER 't_ct_user'@'%'");
2189+
}
2190+
catch (sql::SQLException &)
2191+
{
2192+
// Catching exception if user did not exist
2193+
}
2194+
2195+
try
2196+
{
2197+
stmt->executeUpdate("GRANT ALL ON 't_ct_plugin' TO 't_ct_user' IDENTIFIED WITH "
2198+
"'authentication_pam'");
2199+
}
2200+
catch (sql::SQLException &)
2201+
{
2202+
SKIP("The authentication_pam plugin not loaded");
2203+
}
2204+
2205+
sql::ConnectOptionsMap opts;
2206+
testsuite::Connection c2;
2207+
2208+
opts["userName"]= sql::SQLString("t_ct_user");
2209+
opts["password"]= sql::SQLString("foo");
2210+
2211+
/*
2212+
Expecting error CR_AUTH_PLUGIN_CANNOT_LOAD_ERROR
2213+
without option ENABLE_CLEARTEXT_PLUGIN
2214+
*/
2215+
try
2216+
{
2217+
c2.reset(getConnection(&opts));
2218+
}
2219+
catch (sql::SQLException &e)
2220+
{
2221+
/* We should have dropped the created user here if assertion fails -
2222+
TODO: Add sort of dropSchemaObject for created users in tearDown */
2223+
ASSERT_EQUALS(2059, e.getErrorCode()/*CR_AUTH_PLUGIN_CANNOT_LOAD_ERROR*/);
2224+
}
2225+
2226+
/*
2227+
Expecting error other then CR_AUTH_PLUGIN_CANNOT_LOAD_ERROR
2228+
as option ENABLE_CLEARTEXT_PLUGIN is used
2229+
*/
2230+
opts["OPT_ENABLE_CLEARTEXT_PLUGIN"]= true;
2231+
2232+
try
2233+
{
2234+
c2.reset(getConnection(&opts));
2235+
}
2236+
catch (sql::SQLException &e)
2237+
{
2238+
ASSERT(e.getErrorCode() != 2059);
2239+
}
2240+
2241+
stmt->executeUpdate("DROP USER 't_ct_user'@'%'");
2242+
}
2243+
2244+
21722245
} /* namespace connection */
21732246
} /* namespace testsuite */

0 commit comments

Comments
 (0)