Skip to content

Commit a5aa96e

Browse files
author
Michal Jankowski
committed
Bug#32631511: LDAP SASL AUTHENTICATION: OPENLDAP DOES NOT RECOGNIZE THE DN NAMED <ROOT>
Problems: 1. Server plugin not working with OpenLDAP and GSSAPI. 2. Client plugin using default realm out of the Kerberos config instead of the user's one. 3. Client plugin not allowing configuration with multiple LDAP servers. Fix: 1. Corrected search filter in server plugin to work with OpenLDAP and GSSAPI. 2. Use proper realm in client plugin. 3. Use Krb5 API config functions to enable multi server setup. Move the config read to place where the user's realm is known. Tests: 1. Added MTR tests with OpenLDAP server and GSSAPI authentication. Change-Id: I1d1ea23d5e44c90288ecf39403a338ece2167409
1 parent db5b9ce commit a5aa96e

File tree

6 files changed

+186
-169
lines changed

6 files changed

+186
-169
lines changed

libmysql/authentication_ldap/auth_ldap_kerberos.cc

+100-106
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ Kerberos::Kerberos()
4141
Kerberos::~Kerberos() { cleanup(); }
4242

4343
void Kerberos::get_ldap_host(std::string &host) {
44-
if (initialize()) host = m_ldap_server_host;
44+
assert(m_initialized);
45+
host = m_ldap_server_host;
4546
}
4647

4748
bool Kerberos::initialize() {
@@ -57,11 +58,6 @@ bool Kerberos::initialize() {
5758
log(res_kerberos);
5859
goto EXIT;
5960
}
60-
if (!get_kerberos_config()) {
61-
log_error(
62-
"Failed to get required details from Kerberos configuration file.");
63-
goto EXIT;
64-
}
6561
m_initialized = true;
6662
log_dbg("Kerberos object initialized successfully.");
6763

@@ -89,6 +85,16 @@ void Kerberos::cleanup() {
8985
m_initialized = false;
9086
}
9187

88+
void Kerberos::set_user_and_password(const char *user, const char *password) {
89+
assert(user);
90+
assert(password);
91+
92+
m_user = user;
93+
m_password = password;
94+
auto pos = m_user.find('@');
95+
m_realm = pos == std::string::npos ? "" : std::string(m_user, pos + 1);
96+
}
97+
9298
bool Kerberos::open_default_cache() {
9399
if (m_krb_credentials_cache != nullptr) return true;
94100
krb5_error_code res_kerberos =
@@ -250,117 +256,116 @@ bool Kerberos::obtain_store_credentials() {
250256
return success;
251257
}
252258

253-
bool Kerberos::get_kerberos_config() {
254-
log_dbg("Getting kerberos configuration.");
255-
/*
256-
Kerberos profile category/sub-category names.
257-
*/
259+
void Kerberos::get_ldap_server_from_kdc() {
260+
assert(m_initialized);
261+
258262
static const char realms_heading[] = "realms";
259-
static const char host_default[] = "";
260-
static const char apps_heading[] = "appdefaults";
261-
static const char mysql_apps[] = "mysql";
262-
static const char ldap_host_option[] = "ldap_server_host";
263-
static const char ldap_destroy_option[] = "ldap_destroy_tgt";
264263
static const char kdc_option[] = "kdc";
265264

266265
krb5_error_code res_kerberos = 0;
267266
_profile_t *profile = nullptr;
268267
char *host_value = nullptr;
269-
char *default_realm = nullptr;
270-
271-
/*
272-
Get default realm.
273-
*/
274-
res_kerberos = krb5.krb5_get_default_realm()(m_context, &default_realm);
275-
if (res_kerberos) {
276-
log_error("Failed to get default realm from Kerberos configuration.");
277-
goto EXIT;
278-
}
279268

280269
res_kerberos = krb5.krb5_get_profile()(m_context, &profile);
281270
if (res_kerberos) {
282271
log_error("Failed to get Kerberos configuration profile.");
283-
goto EXIT;
272+
return;
284273
}
285274

286-
/*
287-
1. Getting ldap server host from mysql app section.
288-
2. If failed to get from mysql app section, get from realm section.
289-
realm section should have kdc server info as without kdc info, kerberos
290-
authentication will not work. Authentication process will stop and consider
291-
failed if failed to get LDAP server host.
292-
*/
293275
res_kerberos =
294-
krb5.profile_get_string()(profile, apps_heading, mysql_apps,
295-
ldap_host_option, host_default, &host_value);
296-
if (res_kerberos || !strcmp(host_value, "")) {
297-
if (host_value) {
298-
krb5.profile_release_string()(host_value);
299-
host_value = nullptr;
300-
}
301-
res_kerberos =
302-
krb5.profile_get_string()(profile, realms_heading, default_realm,
303-
kdc_option, host_default, &host_value);
304-
if (res_kerberos) {
305-
if (host_value) {
306-
krb5.profile_release_string()(host_value);
307-
host_value = nullptr;
308-
}
309-
log_error("get_kerberos_config: failed to get ldap server host.");
310-
goto EXIT;
311-
}
312-
}
276+
krb5.profile_get_string()(profile, realms_heading, m_realm.c_str(),
277+
kdc_option, nullptr, &host_value);
278+
if (res_kerberos || host_value == nullptr)
279+
log_warning("Failed to get LDAP server host as KDC from [realms] section.");
280+
else
281+
m_ldap_server_host = host_value;
282+
283+
// Cleanup
313284
if (host_value) {
314285
m_ldap_server_host = host_value;
315-
log_info("Kerberos configuration KDC : ", m_ldap_server_host.c_str());
316-
size_t pos = m_ldap_server_host.npos;
317-
/* IPV6 */
318-
if (m_ldap_server_host[0] == '[') {
319-
pos = m_ldap_server_host.find("]");
320-
if (pos != m_ldap_server_host.npos &&
321-
(m_ldap_server_host.length() > (pos + 1)) &&
322-
(m_ldap_server_host[pos + 1] == ':')) {
323-
m_ldap_server_host = m_ldap_server_host.substr(1, pos - 1);
324-
}
325-
}
326-
/* IPV4 */
327-
else {
328-
pos = m_ldap_server_host.find(":");
329-
if (pos != m_ldap_server_host.npos) {
330-
m_ldap_server_host.erase(pos);
331-
}
332-
}
333-
log_info("Processed Kerberos KDC: ", m_ldap_server_host.c_str());
286+
krb5.profile_release_string()(host_value);
287+
host_value = nullptr;
334288
}
289+
if (profile) {
290+
krb5.profile_release()(profile);
291+
profile = nullptr;
292+
}
293+
}
294+
295+
bool Kerberos::get_kerberos_config() {
296+
assert(m_initialized);
297+
298+
static const char mysql_apps[] = "mysql";
299+
static const char ldap_host_option[] = "ldap_server_host";
300+
static const char ldap_destroy_option[] = "ldap_destroy_tgt";
301+
krb5_principal principal(nullptr);
302+
char *host_value = nullptr;
303+
bool result = true;
304+
305+
log_dbg("Getting kerberos configuration.");
306+
m_ldap_server_host = "";
335307

336308
/*
337-
Get the LDAP destroy TGT from MySQL app section.
338-
If failed to get destroy TGT option, default option value will be false.
339-
This value is consistent with kerberos authentication usage as TGT was
340-
supposed to be used till it expires.
309+
1. Get ldap server host from [appdefaults] section, mysql application,
310+
ldap_server_host option.
311+
2. If 1. failed, get from [realms] section, current realm, kdc option.
312+
3. If 2. failed, return failure.
341313
*/
342-
res_kerberos = krb5.profile_get_boolean()(profile, apps_heading, mysql_apps,
343-
ldap_destroy_option, m_destroy_tgt,
344-
(int *)&m_destroy_tgt);
314+
auto res_kerberos =
315+
krb5.krb5_parse_name()(m_context, m_user.c_str(), &principal);
345316
if (res_kerberos) {
346-
log_info(
347-
"get_kerberos_config: failed to get destroy TGT flag, default is set.");
317+
log_error("Failed to parse Kerberos client principal.");
318+
result = false;
319+
goto EXIT;
348320
}
321+
krb5.krb5_appdefault_string()(m_context, mysql_apps, &principal->realm,
322+
ldap_host_option, "", &host_value);
323+
if (host_value == nullptr || host_value[0] == 0) {
324+
log_warning("Failed to get LDAP server host from [appdefaults] section.");
325+
get_ldap_server_from_kdc();
326+
} else
327+
m_ldap_server_host = host_value;
349328

350-
EXIT:
351-
if (host_value) {
352-
krb5.profile_release_string()(host_value);
353-
host_value = nullptr;
329+
if (m_ldap_server_host.empty()) {
330+
log_error("Failed to get LDAP server host");
331+
result = false;
332+
goto EXIT;
354333
}
355-
if (default_realm) {
356-
krb5.krb5_free_default_realm()(m_context, default_realm);
357-
default_realm = nullptr;
334+
335+
log_dbg("LDAP server host raw value: ", m_ldap_server_host.c_str());
336+
337+
/* IPV6 */
338+
if (m_ldap_server_host[0] == '[') {
339+
auto pos = m_ldap_server_host.find("]");
340+
if (pos != m_ldap_server_host.npos &&
341+
(m_ldap_server_host.length() > (pos + 1)) &&
342+
(m_ldap_server_host[pos + 1] == ':')) {
343+
m_ldap_server_host = m_ldap_server_host.substr(1, pos - 1);
344+
}
358345
}
359-
if (profile) {
360-
krb5.profile_release()(profile);
361-
profile = nullptr;
346+
/* IPV4 */
347+
else {
348+
auto pos = m_ldap_server_host.find(":");
349+
if (pos != m_ldap_server_host.npos) {
350+
m_ldap_server_host.erase(pos);
351+
}
362352
}
363-
return res_kerberos == 0;
353+
log_info("Processed LDAP server host: ", m_ldap_server_host.c_str());
354+
355+
/*
356+
Get the LDAP destroy TGT option from [appdefaults] section, mysql
357+
application. If failed to get destroy TGT option, default option value will
358+
be false. This value is consistent with kerberos authentication usage as TGT
359+
was supposed to be used till it expires.
360+
*/
361+
krb5.krb5_appdefault_boolean()(m_context, mysql_apps, &principal->realm,
362+
ldap_destroy_option, 0,
363+
reinterpret_cast<int *>(&m_destroy_tgt));
364+
365+
EXIT:
366+
if (principal) krb5.krb5_free_principal()(m_context, principal);
367+
if (host_value) krb5.krb5_free_string()(m_context, host_value);
368+
return result;
364369
}
365370

366371
bool Kerberos::credentials_valid() {
@@ -370,7 +375,6 @@ bool Kerberos::credentials_valid() {
370375
krb5_timestamp krb_current_time = 0;
371376
bool credentials_retrieve = false;
372377
krb5_creds matching_credential;
373-
char *realm = nullptr;
374378

375379
memset(&matching_credential, 0, sizeof(matching_credential));
376380
memset(&credentials, 0, sizeof(credentials));
@@ -390,15 +394,9 @@ bool Kerberos::credentials_valid() {
390394
log_error("Failed to parse Kerberos client principal.");
391395
goto EXIT;
392396
}
393-
res_kerberos = krb5.krb5_get_default_realm()(m_context, &realm);
394-
if (res_kerberos) {
395-
log_error("Failed to get default Kerberos realm.");
396-
goto EXIT;
397-
}
398-
log_info("Default Kerberos realm is '", realm, "'.");
399-
res_kerberos =
400-
krb5.krb5_build_principal()(m_context, &matching_credential.server,
401-
strlen(realm), realm, "krbtgt", realm, NULL);
397+
res_kerberos = krb5.krb5_build_principal()(
398+
m_context, &matching_credential.server, m_realm.length(), m_realm.c_str(),
399+
"krbtgt", m_realm.c_str(), NULL);
402400
if (res_kerberos) {
403401
log_error("Failed to build Kerberos TGT principal.");
404402
goto EXIT;
@@ -436,10 +434,6 @@ bool Kerberos::credentials_valid() {
436434

437435
EXIT:
438436
if (res_kerberos) log(res_kerberos);
439-
if (realm) {
440-
krb5.krb5_free_default_realm()(m_context, realm);
441-
realm = nullptr;
442-
}
443437
if (matching_credential.server) {
444438
krb5.krb5_free_principal()(m_context, matching_credential.server);
445439
}

0 commit comments

Comments
 (0)