Wednesday, August 15, 2012

MySQL LDAP Authentication Plugin

MySQL LDAP Authentication Plugin:
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!

PlanetMySQL Voting:
Vote UP /
Vote DOWN

DIGITAL JUICE

No comments:

Post a Comment

Thank's!