MySQL LDAP Authentication Plugin (Clear password client plugin)

Based on my last post MySQL LDAP Authentication Plugin, I received feedback from MySql Joro Blog by Oracle.

They told me:

Insted of writing (and having to deply) your own client plugin you probably can reuse the cleartext client side plugin, specially because it’s available in a number of mysql clients already. Check sql-common/client.c on MySQL 5.5+ for details.

This is very useful because you only need to put the plugin in server side, and in the client side you only need to check if the clear password plugin is enabled.

Now, I present the updated code with the only server side plugin, and I reused the cleartext client side plugin from MySql, it’s more short and very focused in LDAP authentication:

/*
Author: Ignacio Ocampo <nafiux@gmail.com>
Website: http://www.nafiux.com/blog
Version: 1.0.3
Description:
  Server auth LDAP plugin with client cleartext built-in 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>
#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,
  "mysql_clear_password",           /* 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;

Make the plugin

make

And copy to plugin folder

cp plugin/auth_ldap/auth_ldap.so /usr/local/mysql/lib/plugin/auth_ldap.so

Test it

/usr/local/mysql/bin/mysql -h 127.0.0.1 -u nafiux --password=anypassword
ERROR 2059 (HY000): Authentication plugin 'mysql_clear_password' cannot be loaded: plugin not enabled

If you get this error it’s because in sql-common/client_plugin.c, in the load_env_plugins function is a validation:

static void load_env_plugins(MYSQL *mysql)
{
  char *plugs, *free_env, *s= getenv("LIBMYSQL_PLUGINS");
  char *enable_cleartext_plugin= getenv("LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN");

  if (enable_cleartext_plugin && strchr("1Yy", enable_cleartext_plugin[0]))
    libmysql_cleartext_plugin_enabled= 1;

  /* no plugins to load */
  if(!s)
    return;

To solve this, you need to define the environment variable LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN as:

export LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN=1

It only works on user session, if you want to do this permanently edit your ~/.bash_profile, for all users edit /etc/profile

Re-try:

[root@localhost mysql-5.5.27]# /usr/local/mysql/bin/mysql -h 127.0.0.1 -u nafiux --password=anypassword
ERROR 1045 (28000): Access denied for user 'nafiux'@'localhost' (using password: YES)
[root@localhost mysql-5.5.27]# /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 9
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>