As a continuation of previous post, now, I will show how to make a mysql plugin for ldap authentication.
Get the mysql-server source code at http://dev.mysql.com/downloads/mysql/ (http://dev.mysql.com/get/Downloads/MySQL-5.5/mysql-5.5.27.tar.gz/from/http://cdn.mysql.com/)
Installing necessary packages
yum groupinstall 'Development Tools' yum install cmake ncurses-devel
Download source code, build and start MySQL Server
wget http://dev.mysql.com/get/Downloads/MySQL-5.5/mysql-5.5.27.tar.gz/from/http://cdn.mysql.com/ tar -xzf mysql-5.5.27.tar.gz cd mysql-5.5.25 # Preconfiguration setup groupadd mysql useradd -r -g mysql mysql # Beginning of source-build specific instructions cmake . make make install # Postinstallation setup chown -R mysql . chgrp -R mysql . ./scripts/mysql_install_db --user=mysql chown -R root . chown -R mysql data cp support-files/mysql.server /etc/init.d/mysql.server # Start mysql server /etc/init.d/mysql.server start
Goal 1) The first version of the plugin must should allow user authentication with password.
Create auth_ldap folder within mysql-5.5.25/plugin
mkdir plugin/auth_ldap
Create a new file in plugin/auth_ldap/auth_ldap.c
/* Author: Ignacio Ocampo <nafiux@gmail.com> Website: http://www.nafiux.com/blog Version: 1.0.0 Description: Simple auth plugin */ #include <mysql/plugin_auth.h> #include <mysql/client_plugin.h> #include <mysql.h> #include <stdio.h> #include <unistd.h> #include <syslog.h> #include <string.h> static void auth_ldap_log(int priority, char *msg) { openlog("auth_ldap", LOG_PID|LOG_CONS, LOG_USER); syslog(LOG_INFO, msg); closelog(); } /* principal function */ static int auth_ldap_server (MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) { unsigned char *pkt; int pkt_len; /* read the password as null-terminated string, fail on error */ if ((pkt_len= vio->read_packet(vio, &pkt)) < 0) return CR_ERROR; /* fail on empty password */ if (!pkt_len || *pkt == '\0') { auth_ldap_log(LOG_INFO, "fail empty password"); info->password_used= PASSWORD_USED_NO; return CR_ERROR; } /* accept any nonempty password */ info->password_used= PASSWORD_USED_YES; auth_ldap_log(LOG_INFO, "accept any nonempty password"); return CR_OK; } static struct st_mysql_auth auth_ldap_handler = { MYSQL_AUTHENTICATION_INTERFACE_VERSION, "auth_ldap", /* required client-side plugin name */ auth_ldap_server /* server-side plugin main function */ }; mysql_declare_plugin(auth_ldap) { MYSQL_AUTHENTICATION_PLUGIN, &auth_ldap_handler, /* type-specific descriptor */ "auth_ldap", /* plugin name */ "Ignacio Ocampo", /* author */ "LDAP authentication plugin", /* description */ PLUGIN_LICENSE_GPL, /* license type */ NULL, /* no init function */ NULL, /* no deinit function */ 0x0100, /* version = 1.0 */ NULL, /* no status variables */ NULL, /* no system variables */ NULL, /* no reserved information */ 0 /* no flags */ } mysql_declare_plugin_end; static int auth_ldap_client (MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { int res; /* send password as null-terminated string in clear text */ res= vio->write_packet(vio, (const unsigned char *) mysql->passwd, strlen(mysql->passwd) + 1); return res ? CR_ERROR : CR_OK; } mysql_declare_client_plugin(AUTHENTICATION) "auth_ldap", /* plugin name */ "Ignacio Ocampo", /* author */ "LDAP authentication plugin", /* description */ {1,0,0}, /* version = 1.0.0 */ "GPL", /* license type */ NULL, /* for internal use */ NULL, /* no init function */ NULL, /* no deinit function */ NULL, /* no option-handling function */ auth_ldap_client /* main function */ mysql_end_client_plugin;
To compile the plugin, we can use the macros defined for cmake in MySQL source code.
Create a new file in plugin/auth_ldap/CMakeLists.txt:
MYSQL_ADD_PLUGIN(auth_ldap auth_ldap.c MODULE_ONLY MODULE_OUTPUT_NAME "auth_ldap")
To compile the plugin, run de make command at the root of source code of MySQL (in the same directory where you ran to top):
make
Plugin installation:
cp plugin/auth_ldap/auth_ldap.so /usr/local/mysql/lib/plugin/auth_ldap.so chgrp mysql /usr/local/mysql/lib/plugin/auth_ldap.so /usr/local/mysql/bin/mysql -h 127.0.0.1 -u root mysql> INSTALL PLUGIN auth_ldap SONAME 'auth_ldap.so';
We will implement proxy user support in authentication plugin (see http://dev.mysql.com/doc/refman/5.5/en/writing-authentication-plugins.html#writing-authentication-plugins-proxy-users)
The idea is authenticate any user with LDAP, however, it user must be mapped to a mysql local user, to do it:
Create the Database and Proxy to authenticate any external user through out auth_ldap plugin.
As you can see, the local user “dev” serves as proxy for any user and can access the database “dev_example1″
mysql> CREATE DATABASE dev_example1; mysql> CREATE USER 'dev'@'localhost'; mysql> GRANT ALL ON dev_example1.* TO 'dev'@'locahost'; mysql> USE mysql; mysql> DELETE FROM user WHERE User=''; mysql> CREATE USER ''@'' IDENTIFIED WITH auth_ldap; mysql> GRANT PROXY ON 'dev'@'localhost' TO ''@''; mysql> SELECT user, host, plugin FROM user; +------+-----------------------+-----------+ | user | host | plugin | +------+-----------------------+-----------+ | root | localhost | | | root | localhost.localdomain | | | root | 127.0.0.1 | | | root | ::1 | | | dev | % | | | | | auth_ldap | +------+-----------------------+-----------+ 6 rows in set (0,00 sec)
Test our plugin:
/usr/local/mysql/bin/mysql -h 127.0.0.1 --user=foo ERROR 1045 (28000): Access denied for user 'foo'@'localhost' (using password: NO) /usr/local/mysql/bin/mysql -h 127.0.0.1 --user=foo --password=bar Welcome to the MySQL monitor. Commands end with ; or \g.
Check the log:
tail /var/log/messages Aug 11 12:00:50 localhost auth_ldap[3383]: fail empty password Aug 11 12:00:55 localhost auth_ldap[3383]: accept any nonempty password
Goal 1 completed!
Goal 2) Implement ldap authentication and mapping with local user
/* Author: Ignacio Ocampo <nafiux@gmail.com> Website: http://www.nafiux.com/blog Version: 1.0.2 Description: Auth plugin with LDAP */ #include <mysql/plugin_auth.h> #include <mysql/client_plugin.h> #include <mysql.h> #include <stdio.h> #include <unistd.h> #include <syslog.h> #include <string.h> #include <ldap.h> #define LDAP_SERVER "ldap://nafiux.com:389" #define LDAP_PATH "ou=People,dc=nafiux,dc=com" static void auth_ldap_log(int priority, char *msg) { openlog("auth_ldap", LOG_PID|LOG_CONS, LOG_USER); syslog(LOG_INFO, msg); closelog(); } /* ldap login function */ static int auth_ldap_login(char *username, unsigned char *password) { LDAP *ld; int rc; char dn[200]; char buffer[200]; /* Open LDAP Connection*/ if( ldap_initialize( &ld, LDAP_SERVER ) ) { auth_ldap_log(LOG_ERR, "ldap_initialize"); return( 1 ); } /* User authentication (bind) */ sprintf(dn, "cn=%s,%s", username, LDAP_PATH); rc = ldap_simple_bind_s( ld, dn, password ); if( rc != LDAP_SUCCESS ) { sprintf(buffer, "Authentication failed: %s", dn); auth_ldap_log(LOG_INFO, buffer); return( 1 ); } sprintf(buffer, "Authentication successful: %s", dn); auth_ldap_log(LOG_INFO, buffer); return( 0 ); } /* principal function */ static int auth_ldap_server (MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) { unsigned char *pkt; int pkt_len; /* read the password as null-terminated string, fail on error */ if ((pkt_len= vio->read_packet(vio, &pkt)) < 0) return CR_ERROR; /* fail on empty password */ if (!pkt_len || *pkt == '\0') { auth_ldap_log(LOG_INFO, "fail empty password"); info->password_used= PASSWORD_USED_NO; return CR_ERROR; } /* accept any nonempty password */ info->password_used= PASSWORD_USED_YES; //auth_ldap_log(LOG_INFO, "accept any nonempty password"); if(auth_ldap_login(info->user_name, pkt)) return CR_ERROR; strcpy(info->authenticated_as, "dev"); // local user strcpy(info->external_user, info->user_name); return CR_OK; } static struct st_mysql_auth auth_ldap_handler = { MYSQL_AUTHENTICATION_INTERFACE_VERSION, "auth_ldap", /* required client-side plugin name */ auth_ldap_server /* server-side plugin main function */ }; mysql_declare_plugin(auth_ldap) { MYSQL_AUTHENTICATION_PLUGIN, &auth_ldap_handler, /* type-specific descriptor */ "auth_ldap", /* plugin name */ "Ignacio Ocampo", /* author */ "LDAP authentication plugin", /* description */ PLUGIN_LICENSE_GPL, /* license type */ NULL, /* no init function */ NULL, /* no deinit function */ 0x0100, /* version = 1.0 */ NULL, /* no status variables */ NULL, /* no system variables */ NULL, /* no reserved information */ 0 /* no flags */ } mysql_declare_plugin_end; static int auth_ldap_client (MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { int res; /* send password as null-terminated string in clear text */ res= vio->write_packet(vio, (const unsigned char *) mysql->passwd, strlen(mysql->passwd) + 1); return res ? CR_ERROR : CR_OK; } mysql_declare_client_plugin(AUTHENTICATION) "auth_ldap", /* plugin name */ "Ignacio Ocampo", /* author */ "LDAP authentication plugin", /* description */ {1,0,0}, /* version = 1.0.0 */ "GPL", /* license type */ NULL, /* for internal use */ NULL, /* no init function */ NULL, /* no deinit function */ NULL, /* no option-handling function */ auth_ldap_client /* main function */ mysql_end_client_plugin;
Befor compile the plugin, you need to update the plugin/auth_ldap/CMakeLists.txt (add the LINK_LIBRARIES “ldap” option)
MYSQL_ADD_PLUGIN(auth_ldap auth_ldap.c MODULE_ONLY MODULE_OUTPUT_NAME "auth_ldap" LINK_LIBRARIES "ldap")
Install openldap-devel package
yum install openldap-devel
Make, copy plugin and restart server (see the previous steps).
Finally, I try the plugin completely.
/usr/local/mysql/bin/mysql -h 127.0.0.1 -u foo --password=bar ERROR 1045 (28000): Access denied for user 'foo'@'localhost' (using password: YES) /usr/local/mysql/bin/mysql -h 127.0.0.1 -u nafiux --password=REAL_LDAP_PASSWORD Welcome to the MySQL monitor. Commands end with ; or \g.
Check the log:
tail /var/log/messages Aug 11 14:46:43 localhost auth_ldap[28666]: Authentication failed: cn=foo,ou=People,dc=nafiux,dc=com Aug 11 14:47:04 localhost auth_ldap[28666]: Authentication successful: cn=nafiux,ou=People,dc=nafiux,dc=com
Check the available databases and user session information:
/usr/local/mysql/bin/mysql -h 127.0.0.1 -u nafiux --password=REAL_LDAP_PASSWORD Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 20 Server version: 5.5.27 Source distribution Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> SHOW DATABASES; +--------------------+ | Database | +--------------------+ | information_schema | | dev_example1 | | test | +--------------------+ 3 rows in set (0,00 sec) mysql> SELECT USER(), CURRENT_USER(), @@proxy_user, @@external_user; +------------------+----------------+--------------+-----------------+ | USER() | CURRENT_USER() | @@proxy_user | @@external_user | +------------------+----------------+--------------+-----------------+ | nafiux@localhost | dev@localhost | ''@'' | ''@'' | +------------------+----------------+--------------+-----------------+ 1 row in set (0,00 sec)
Goal 2 completed!