23 #define LDAP_DEPRECATED 1
31 #include <drizzled/plugin/authentication.h>
32 #include <drizzled/identifier.h>
33 #include <drizzled/util/convert.h>
37 #include <boost/program_options.hpp>
39 namespace po= boost::program_options;
41 using namespace drizzled;
47 const std::string DEFAULT_URI=
"ldap://localhost/";
49 std::string bind_password;
51 std::string password_attribute;
52 std::string DEFAULT_PASSWORD_ATTRIBUTE=
"userPassword";
53 std::string mysql_password_attribute;
54 const std::string DEFAULT_MYSQL_PASSWORD_ATTRIBUTE=
"drizzleMysqlUserPassword";
55 static const int DEFAULT_CACHE_TIMEOUT= 600;
57 static cachetimeout_constraint cache_timeout= 0;
72 bool initialize(
void);
84 string& getError(
void);
95 typedef std::pair<PasswordType, std::string> PasswordEntry;
96 typedef std::pair<std::string, PasswordEntry> UserEntry;
97 typedef std::map<std::string, PasswordEntry> UserCache;
109 void lookupUser(
const string& user);
122 bool verifyMySQLHash(
const PasswordEntry &password,
123 const string &scramble_bytes,
124 const string &scrambled_password);
126 time_t next_cache_expiration;
130 pthread_rwlock_t lock;
133 AuthLDAP::AuthLDAP(
string name_arg):
134 plugin::Authentication(name_arg),
135 next_cache_expiration(),
142 AuthLDAP::~AuthLDAP()
144 pthread_rwlock_destroy(&lock);
151 int return_code= pthread_rwlock_init(&lock, NULL);
152 if (return_code != 0)
154 error=
"pthread_rwlock_init failed";
163 int return_code= ldap_initialize(&ldap, (
char *)uri.c_str());
164 if (return_code != LDAP_SUCCESS)
166 error=
"ldap_initialize failed: ";
167 error+= ldap_err2string(return_code);
172 return_code= ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
173 if (return_code != LDAP_SUCCESS)
177 error=
"ldap_set_option failed: ";
178 error+= ldap_err2string(return_code);
182 if (not bind_dn.empty())
184 return_code= ldap_simple_bind_s(ldap, (
char *)bind_dn.c_str(), (
char *)bind_password.c_str());
185 if (return_code != LDAP_SUCCESS)
189 error=
"ldap_simple_bind_s failed: ";
190 error+= ldap_err2string(return_code);
206 if (cache_timeout > 0)
208 struct timeval current_time;
209 gettimeofday(¤t_time, NULL);
210 if (current_time.tv_sec > next_cache_expiration)
212 pthread_rwlock_wrlock(&lock);
214 if (current_time.tv_sec > next_cache_expiration)
217 next_cache_expiration= current_time.tv_sec + cache_timeout;
219 pthread_rwlock_unlock(&lock);
223 pthread_rwlock_rdlock(&lock);
225 AuthLDAP::UserCache::const_iterator user= users.find(sctx.username());
226 if (user == users.end())
228 pthread_rwlock_unlock(&lock);
230 pthread_rwlock_wrlock(&lock);
233 user= users.find(sctx.username());
234 if (user == users.end())
237 pthread_rwlock_unlock(&lock);
239 pthread_rwlock_rdlock(&lock);
242 user= users.find(sctx.username());
243 if (user == users.end())
245 pthread_rwlock_unlock(&lock);
250 if (user->second.first == NOT_FOUND)
252 pthread_rwlock_unlock(&lock);
256 if (sctx.getPasswordType() == identifier::User::MYSQL_HASH)
258 bool allow=
verifyMySQLHash(user->second, sctx.getPasswordContext(), password);
259 pthread_rwlock_unlock(&lock);
263 if (user->second.first == PLAIN_TEXT && password == user->second.second)
265 pthread_rwlock_unlock(&lock);
269 pthread_rwlock_unlock(&lock);
275 string filter(
"(uid=" + user +
")");
276 const char *attributes[3]=
278 (
char *)password_attribute.c_str(),
279 (
char *)mysql_password_attribute.c_str(),
283 bool try_reconnect=
true;
291 errmsg_printf(error::ERROR, _(
"Reconnect failed: %s\n"),
297 int return_code= ldap_search_ext_s(ldap,
298 (
char *)base_dn.c_str(),
301 const_cast<char **
>(attributes),
308 if (return_code != LDAP_SUCCESS)
310 errmsg_printf(error::ERROR, _(
"ldap_search_ext_s failed: %s\n"),
311 ldap_err2string(return_code));
316 try_reconnect=
false;
328 LDAPMessage *entry= ldap_first_entry(ldap, result);
329 AuthLDAP::PasswordEntry new_password;
331 new_password= AuthLDAP::PasswordEntry(NOT_FOUND,
"");
334 char **values= ldap_get_values(ldap, entry, (
char *)mysql_password_attribute.c_str());
337 values= ldap_get_values(ldap, entry, (
char *)password_attribute.c_str());
339 new_password= AuthLDAP::PasswordEntry(NOT_FOUND,
"");
342 new_password= AuthLDAP::PasswordEntry(PLAIN_TEXT, values[0]);
343 ldap_value_free(values);
348 new_password= AuthLDAP::PasswordEntry(MYSQL_HASH, values[0]);
349 ldap_value_free(values);
353 users.insert(AuthLDAP::UserEntry(user, new_password));
357 const string &scramble_bytes,
358 const string &scrambled_password)
360 if (scramble_bytes.size() != SHA1_DIGEST_LENGTH ||
361 scrambled_password.size() != SHA1_DIGEST_LENGTH)
367 uint8_t local_scrambled_password[SHA1_DIGEST_LENGTH];
368 uint8_t temp_hash[SHA1_DIGEST_LENGTH];
369 uint8_t scrambled_password_check[SHA1_DIGEST_LENGTH];
371 if (password.first == MYSQL_HASH)
374 drizzled_hex_to_string(reinterpret_cast<char*>(local_scrambled_password),
375 password.second.c_str(), SHA1_DIGEST_LENGTH * 2);
381 SHA1Update(&ctx, reinterpret_cast<const uint8_t *>(password.second.c_str()),
382 password.second.size());
383 SHA1Final(temp_hash, &ctx);
386 SHA1Update(&ctx, temp_hash, SHA1_DIGEST_LENGTH);
387 SHA1Final(local_scrambled_password, &ctx);
392 SHA1Update(&ctx, reinterpret_cast<const uint8_t*>(scramble_bytes.c_str()),
394 SHA1Update(&ctx, local_scrambled_password, SHA1_DIGEST_LENGTH);
395 SHA1Final(temp_hash, &ctx);
399 for (
int x= 0; x < SHA1_DIGEST_LENGTH; x++)
400 temp_hash[x]= temp_hash[x] ^ scrambled_password[x];
404 SHA1Update(&ctx, temp_hash, SHA1_DIGEST_LENGTH);
405 SHA1Final(scrambled_password_check, &ctx);
408 return memcmp(local_scrambled_password, scrambled_password_check, SHA1_DIGEST_LENGTH) == 0;
416 errmsg_printf(error::ERROR, _(
"Could not load auth ldap: %s\n"),
430 context.add(auth_ldap);
436 context(
"uri", po::value<string>(&uri)->default_value(DEFAULT_URI),
437 N_(
"URI of the LDAP server to contact"));
438 context(
"bind-dn", po::value<string>(&bind_dn)->default_value(
""),
439 N_(
"DN to use when binding to the LDAP server"));
440 context(
"bind-password", po::value<string>(&bind_password)->default_value(
""),
441 N_(
"Password to use when binding the DN"));
442 context(
"base-dn", po::value<string>(&base_dn)->default_value(
""),
443 N_(
"DN to use when searching"));
444 context(
"password-attribute", po::value<string>(&password_attribute)->default_value(DEFAULT_PASSWORD_ATTRIBUTE),
445 N_(
"Attribute in LDAP with plain text password"));
446 context(
"mysql-password-attribute", po::value<string>(&mysql_password_attribute)->default_value(DEFAULT_MYSQL_PASSWORD_ATTRIBUTE),
447 N_(
"Attribute in LDAP with MySQL hashed password"));
448 context(
"cache-timeout", po::value<cachetimeout_constraint>(&cache_timeout)->default_value(DEFAULT_CACHE_TIMEOUT),
449 N_(
"How often to empty the users cache, 0 to disable"));
454 DRIZZLE_DECLARE_PLUGIN
459 "Eric Day, Henrik Ingo, Edward Konetzko",
460 N_(
"Authentication against an LDAP server"),
464 auth_ldap::init_options
466 DRIZZLE_DECLARE_PLUGIN_END;