Fallo de inyeccion SQL solucionado, ahora se crea un pool de conexiones, codigo reorganizado, ahora se usa GLib como dependencia, otros fallos solucionados
This commit is contained in:
parent
f127871a21
commit
8cddf60b1e
|
@ -11,6 +11,7 @@ AM_SILENT_RULES([yes])
|
||||||
|
|
||||||
# Check for program dependencies
|
# Check for program dependencies
|
||||||
AC_PROG_CC
|
AC_PROG_CC
|
||||||
|
PKG_CHECK_MODULES([glib], [glib-2.0])
|
||||||
|
|
||||||
AC_CONFIG_FILES([
|
AC_CONFIG_FILES([
|
||||||
Makefile
|
Makefile
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
|
|
||||||
chown mysql:mysql /etc/mysql/proxy_auth.sql
|
chown mysql:mysql /etc/mysql/proxy-auth.sql
|
||||||
chmod u=rw,go-rwx /etc/mysql/proxy_auth.sql
|
chmod u=rw,go-rwx /etc/mysql/proxy-auth.sql
|
||||||
|
|
||||||
chown mysql:mysql /etc/mysql/proxy_auth.cnf
|
chown mysql:mysql /etc/mysql/proxy-auth.ini
|
||||||
chmod u=rw,go-rwx /etc/mysql/proxy_auth.cnf
|
chmod u=rw,go-rwx /etc/mysql/proxy-auth.ini
|
||||||
|
|
||||||
#DEBHELPER#
|
#DEBHELPER#
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
FILE=minacum.so
|
||||||
|
SRC=minacum.c
|
||||||
|
#URL=/usr/lib/$(FILE)
|
||||||
|
URL=/usr/lib/mysql/plugin/$(FILE)
|
||||||
|
|
||||||
|
main: $(SRC)
|
||||||
|
rm -f $(FILE)
|
||||||
|
gcc -shared -o $(FILE) $(SRC) -fPIC
|
||||||
|
install:
|
||||||
|
cp $(FILE) $(URL)
|
||||||
|
uninstall:
|
||||||
|
rm -f $(URL)
|
||||||
|
clean:
|
||||||
|
rm -f $(FILE)
|
|
@ -0,0 +1,25 @@
|
||||||
|
NAME=multimax
|
||||||
|
FILE=$(NAME).so
|
||||||
|
SRC=$(NAME).c
|
||||||
|
URL=/usr/lib/mysql/plugin/$(FILE)
|
||||||
|
|
||||||
|
SQL_DROP="DROP FUNCTION IF EXISTS $(NAME);"
|
||||||
|
|
||||||
|
.PHONY: install uninstall clean
|
||||||
|
|
||||||
|
$(FILE): $(SRC)
|
||||||
|
rm -f $@
|
||||||
|
gcc -shared -o $@ $< -fPIC
|
||||||
|
|
||||||
|
install:
|
||||||
|
cp $(FILE) $(URL)
|
||||||
|
echo $(SQL_DROP)
|
||||||
|
echo "CREATE AGGREGATE FUNCTION $(NAME) RETURNS INT SONAME '$(FILE)';"
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -f $(URL)
|
||||||
|
echo $(SQL_DROP)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(FILE)
|
||||||
|
|
|
@ -4,8 +4,13 @@ proxy_auth_LTLIBRARIES = proxy_auth.la
|
||||||
|
|
||||||
proxy_auth_la_CPPFLAGS = \
|
proxy_auth_la_CPPFLAGS = \
|
||||||
-DMYSQL_DYNAMIC_PLUGIN \
|
-DMYSQL_DYNAMIC_PLUGIN \
|
||||||
|
$(glib_CFLAGS) \
|
||||||
-D_CONFIG_DIR=\"$(proxy_auth_datadir)\" \
|
-D_CONFIG_DIR=\"$(proxy_auth_datadir)\" \
|
||||||
-D_SQL_DIR=\"$(proxy_auth_datadir)\"
|
-D_SQL_DIR=\"$(proxy_auth_datadir)\"
|
||||||
|
proxy_auth_la_LIBADD = \
|
||||||
|
$(glib_LIBS) \
|
||||||
|
-lmysqlclient \
|
||||||
|
-lz
|
||||||
proxy_auth_la_CFLAGS = \
|
proxy_auth_la_CFLAGS = \
|
||||||
-Wall -O3 \
|
-Wall -O3 \
|
||||||
-I/usr/include/mysql
|
-I/usr/include/mysql
|
||||||
|
@ -15,12 +20,12 @@ proxy_auth_la_LDFLAGS = \
|
||||||
-avoid-version \
|
-avoid-version \
|
||||||
-export-dynamic \
|
-export-dynamic \
|
||||||
-lmysqlclient
|
-lmysqlclient
|
||||||
proxy_auth_la_SOURCES = proxy_auth.c
|
proxy_auth_la_SOURCES = proxy-auth.c
|
||||||
|
|
||||||
proxy_auth_datadir = $(sysconfdir)/mysql
|
proxy_auth_datadir = $(sysconfdir)/mysql
|
||||||
proxy_auth_data_DATA = \
|
proxy_auth_data_DATA = \
|
||||||
proxy_auth.sql \
|
proxy-auth.sql \
|
||||||
proxy_auth.cnf
|
proxy-auth.ini
|
||||||
|
|
||||||
install-data-hook:
|
install-data-hook:
|
||||||
rm -f $(DESTDIR)$(proxy_authdir)/proxy_auth.la
|
rm -f $(DESTDIR)$(proxy_authdir)/proxy_auth.la
|
||||||
|
|
|
@ -0,0 +1,402 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 - Alejandro T. Colombini
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <mysql.h>
|
||||||
|
#include <mysql/errmsg.h>
|
||||||
|
#include <mysql/plugin_auth.h>
|
||||||
|
#include <glib-unix.h>
|
||||||
|
|
||||||
|
#define CONFIG_FILE _CONFIG_DIR"/proxy_auth.ini"
|
||||||
|
#define SQL_FILE _SQL_DIR"/proxy_auth.sql"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
gboolean initialized;
|
||||||
|
gchar * socket;
|
||||||
|
gchar * host;
|
||||||
|
gchar * user;
|
||||||
|
gchar * pass;
|
||||||
|
gchar * schema;
|
||||||
|
guint port;
|
||||||
|
GRegex * regex;
|
||||||
|
gchar * queryt;
|
||||||
|
gsize queryt_len;
|
||||||
|
gint pool_size;
|
||||||
|
gint max_connections;
|
||||||
|
GAsyncQueue * conn_pool;
|
||||||
|
GMutex mutex;
|
||||||
|
guint last_error;
|
||||||
|
}
|
||||||
|
ProxyAuth;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
MYSQL * conn;
|
||||||
|
gchar * user;
|
||||||
|
guint user_len;
|
||||||
|
gchar * pass;
|
||||||
|
guint pass_len;
|
||||||
|
}
|
||||||
|
RegexData;
|
||||||
|
|
||||||
|
ProxyAuth *
|
||||||
|
proxy_auth_new ()
|
||||||
|
{
|
||||||
|
ProxyAuth * self = g_new (ProxyAuth, 1);
|
||||||
|
self->initialized = FALSE;
|
||||||
|
g_mutex_init (&self->mutex);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
proxy_auth_deinit (ProxyAuth * self)
|
||||||
|
{
|
||||||
|
if (!self->initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_free (self->socket);
|
||||||
|
g_free (self->host);
|
||||||
|
g_free (self->user);
|
||||||
|
g_free (self->pass);
|
||||||
|
g_free (self->schema);
|
||||||
|
g_free (self->queryt);
|
||||||
|
|
||||||
|
if (self->regex)
|
||||||
|
g_regex_unref (self->regex);
|
||||||
|
if (self->conn_pool)
|
||||||
|
g_async_queue_unref (self->conn_pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
proxy_auth_free (ProxyAuth * self)
|
||||||
|
{
|
||||||
|
proxy_auth_deinit (self);
|
||||||
|
g_mutex_clear (&self->mutex);
|
||||||
|
g_free (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
proxy_auth_init (ProxyAuth * self)
|
||||||
|
{
|
||||||
|
gboolean res = FALSE;
|
||||||
|
GError * error = NULL;
|
||||||
|
|
||||||
|
proxy_auth_deinit (self);
|
||||||
|
self->initialized = TRUE;
|
||||||
|
self->socket = NULL;
|
||||||
|
self->host = NULL;
|
||||||
|
self->user = NULL;
|
||||||
|
self->pass = NULL;
|
||||||
|
self->schema = NULL;
|
||||||
|
self->queryt = NULL;
|
||||||
|
self->queryt_len = 0;
|
||||||
|
self->regex = NULL;
|
||||||
|
self->conn_pool = NULL;
|
||||||
|
self->last_error = 0;
|
||||||
|
|
||||||
|
mysql_library_init (0, NULL, NULL);
|
||||||
|
|
||||||
|
// Reading the configuration file
|
||||||
|
|
||||||
|
GKeyFile * key_file = g_key_file_new ();
|
||||||
|
|
||||||
|
if (!g_key_file_load_from_file (key_file, CONFIG_FILE, G_KEY_FILE_NONE, &error))
|
||||||
|
{
|
||||||
|
g_warning ("ProxyAuth: Can't open configuration file: %s", error->message);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->socket = NULL;
|
||||||
|
self->host = g_key_file_get_string (key_file, "db", "host", NULL);
|
||||||
|
|
||||||
|
if (!self->host)
|
||||||
|
self->socket = g_key_file_get_string (key_file, "db", "socket", NULL);
|
||||||
|
|
||||||
|
self->user = g_key_file_get_string (key_file, "db", "user", NULL);
|
||||||
|
self->pass = g_key_file_get_string (key_file, "db", "pass", NULL);
|
||||||
|
self->schema = g_key_file_get_string (key_file, "db", "schema", NULL);
|
||||||
|
self->port = (guint) g_key_file_get_integer (key_file, "db", "port", NULL);
|
||||||
|
self->max_connections = g_key_file_get_integer (key_file, "db", "maxConnections", NULL);
|
||||||
|
|
||||||
|
// Reading the query template
|
||||||
|
|
||||||
|
if (!g_file_get_contents (SQL_FILE, &self->queryt, &self->queryt_len, &error))
|
||||||
|
{
|
||||||
|
g_warning ("ProxyAuth: Can't read the query template: %s", error->message);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the regular expression
|
||||||
|
|
||||||
|
self->regex = g_regex_new ("#\\w+",
|
||||||
|
G_REGEX_OPTIMIZE | G_REGEX_MULTILINE, 0, &error);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
g_warning ("ProxyAuth: Can't create the regex: %s", error->message);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the connection pool
|
||||||
|
|
||||||
|
self->conn_pool = g_async_queue_new_full ((GDestroyNotify) mysql_close);
|
||||||
|
self->pool_size = 0;
|
||||||
|
self->last_error = 0;
|
||||||
|
|
||||||
|
if (self->max_connections < 1)
|
||||||
|
self->max_connections = 1;
|
||||||
|
|
||||||
|
res = TRUE;
|
||||||
|
end:
|
||||||
|
g_clear_error (&error);
|
||||||
|
g_key_file_free (key_file);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
proxy_auth_regex_func (const GMatchInfo * info, GString * res, gpointer data)
|
||||||
|
{
|
||||||
|
RegexData * regex_data = (RegexData *) data;
|
||||||
|
gchar * match = g_match_info_fetch (info, 0);
|
||||||
|
|
||||||
|
gchar * str = NULL;
|
||||||
|
gulong str_len;
|
||||||
|
|
||||||
|
if (!g_strcmp0 (match, "#user"))
|
||||||
|
{
|
||||||
|
str = regex_data->user;
|
||||||
|
str_len = regex_data->user_len;
|
||||||
|
}
|
||||||
|
else if (!g_strcmp0 (match, "#pass"))
|
||||||
|
{
|
||||||
|
str = regex_data->pass;
|
||||||
|
str_len = regex_data->pass_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str)
|
||||||
|
{
|
||||||
|
g_string_append_c (res, '\'');
|
||||||
|
|
||||||
|
// FIXME: mysql_real_escape_string() causes MySQL process crash with signal 11
|
||||||
|
char escaped_str[str_len * 2 + 1];
|
||||||
|
unsigned long scaped_len = mysql_escape_string (escaped_str, str, str_len);
|
||||||
|
g_string_append_len (res, escaped_str, (gssize) scaped_len);
|
||||||
|
|
||||||
|
g_string_append_c (res, '\'');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
g_string_append (res, match);
|
||||||
|
|
||||||
|
g_free (match);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
proxy_auth_reconnect (ProxyAuth * self, MYSQL * conn)
|
||||||
|
{
|
||||||
|
gboolean connected = mysql_real_connect (conn,
|
||||||
|
self->host, self->user, self->pass, self->schema, self->port, self->socket, 0) != NULL;
|
||||||
|
guint conn_error = mysql_errno (conn);
|
||||||
|
|
||||||
|
if (conn_error && self->last_error != conn_error)
|
||||||
|
g_warning ("ProxyAuth: Can't connect to database: %s", mysql_error (conn));
|
||||||
|
else if (mysql_set_character_set (conn, "latin1"))
|
||||||
|
g_warning ("ProxyAuth: Can't set character set: %s", mysql_error (conn));
|
||||||
|
|
||||||
|
self->last_error = conn_error;
|
||||||
|
return connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
proxy_auth_authenticate (ProxyAuth * self, MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_AUTH_INFO * info)
|
||||||
|
{
|
||||||
|
int res = CR_ERROR;
|
||||||
|
gchar * query = NULL;
|
||||||
|
unsigned char * pkt;
|
||||||
|
|
||||||
|
// Check for the username
|
||||||
|
|
||||||
|
guint user_len = info->user_name_length;
|
||||||
|
|
||||||
|
if (info->user_name == NULL
|
||||||
|
&& (user_len = vio->read_packet (vio, &pkt)) < 0)
|
||||||
|
return CR_ERROR;
|
||||||
|
|
||||||
|
if (user_len > MYSQL_USERNAME_LENGTH)
|
||||||
|
return CR_ERROR;
|
||||||
|
|
||||||
|
// Read the password and check if it's valid
|
||||||
|
|
||||||
|
guint pass_len = vio->read_packet (vio, &pkt) - 1;
|
||||||
|
|
||||||
|
if (pass_len < 0)
|
||||||
|
return CR_ERROR;
|
||||||
|
|
||||||
|
if (!pass_len || *pkt == '\0')
|
||||||
|
{
|
||||||
|
info->password_used = PASSWORD_USED_NO;
|
||||||
|
return CR_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
char pass[pass_len + 1];
|
||||||
|
memcpy (pass, pkt, pass_len);
|
||||||
|
pass[pass_len] = '\0';
|
||||||
|
|
||||||
|
info->password_used = PASSWORD_USED_YES;
|
||||||
|
|
||||||
|
// Gets a connection from the pool
|
||||||
|
|
||||||
|
MYSQL * conn = (MYSQL *) g_async_queue_try_pop (self->conn_pool);
|
||||||
|
|
||||||
|
if (!conn)
|
||||||
|
{
|
||||||
|
g_mutex_lock (&self->mutex);
|
||||||
|
|
||||||
|
if (self->pool_size < self->max_connections)
|
||||||
|
{
|
||||||
|
self->pool_size++;
|
||||||
|
g_mutex_unlock (&self->mutex);
|
||||||
|
|
||||||
|
conn = mysql_init (NULL);
|
||||||
|
|
||||||
|
if (!proxy_auth_reconnect (self, conn))
|
||||||
|
{
|
||||||
|
g_mutex_lock (&self->mutex);
|
||||||
|
self->pool_size--;
|
||||||
|
g_mutex_unlock (&self->mutex);
|
||||||
|
|
||||||
|
mysql_close (conn);
|
||||||
|
conn = NULL;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_mutex_unlock (&self->mutex);
|
||||||
|
conn = (MYSQL *) g_async_queue_pop (self->conn_pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mysql_errno (conn))
|
||||||
|
{
|
||||||
|
case CR_SERVER_LOST:
|
||||||
|
case CR_SERVER_GONE_ERROR:
|
||||||
|
if (!proxy_auth_reconnect (self, conn))
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replaces the user and the pass on the query template
|
||||||
|
|
||||||
|
GError * error = NULL;
|
||||||
|
RegexData regex_data;
|
||||||
|
regex_data.conn = conn;
|
||||||
|
regex_data.user = info->user_name;
|
||||||
|
regex_data.user_len = user_len;
|
||||||
|
regex_data.pass = pass;
|
||||||
|
regex_data.pass_len = pass_len;
|
||||||
|
|
||||||
|
query = g_regex_replace_eval (self->regex,
|
||||||
|
self->queryt, self->queryt_len, 0, 0, proxy_auth_regex_func, ®ex_data, &error);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
g_warning ("ProxyAuth: Can't evaluate regex: %s", error->message);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sends the query to the database
|
||||||
|
|
||||||
|
MYSQL_RES * result;
|
||||||
|
|
||||||
|
if (!mysql_query (conn, query)
|
||||||
|
&& (result = mysql_store_result (conn)))
|
||||||
|
{
|
||||||
|
MYSQL_ROW row = mysql_fetch_row (result);
|
||||||
|
|
||||||
|
if (row && row[0])
|
||||||
|
{
|
||||||
|
unsigned long row_len = mysql_fetch_lengths (result)[0];
|
||||||
|
|
||||||
|
if (row_len > 0 && row_len <= MYSQL_USERNAME_LENGTH)
|
||||||
|
{
|
||||||
|
strcpy (info->external_user, info->user_name);
|
||||||
|
strncpy (info->authenticated_as, row[0], row_len);
|
||||||
|
res = CR_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_free_result (result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
g_warning ("ProxyAuth: Error executing query: %s", mysql_error (conn));
|
||||||
|
end:
|
||||||
|
if (conn)
|
||||||
|
g_async_queue_push (self->conn_pool, conn);
|
||||||
|
|
||||||
|
g_free (query);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProxyAuth * pauth;
|
||||||
|
|
||||||
|
static int
|
||||||
|
proxy_auth_plugin_main (MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_AUTH_INFO * info)
|
||||||
|
{
|
||||||
|
return proxy_auth_authenticate (pauth, vio, info);;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
proxy_auth_plugin_init ()
|
||||||
|
{
|
||||||
|
pauth = proxy_auth_new ();
|
||||||
|
return proxy_auth_init (pauth) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
proxy_auth_plugin_deinit ()
|
||||||
|
{
|
||||||
|
proxy_auth_free (pauth);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct st_mysql_auth proxy_auth_handler =
|
||||||
|
{
|
||||||
|
MYSQL_AUTHENTICATION_INTERFACE_VERSION
|
||||||
|
,"mysql_clear_password" // Cleartext plugin required in the client
|
||||||
|
,proxy_auth_plugin_main
|
||||||
|
};
|
||||||
|
|
||||||
|
mysql_declare_plugin(proxy_auth)
|
||||||
|
{
|
||||||
|
MYSQL_AUTHENTICATION_PLUGIN
|
||||||
|
,&proxy_auth_handler
|
||||||
|
,"proxy_auth"
|
||||||
|
,"Alejandro T. Colombini"
|
||||||
|
,"Proxy user authentication server-side plugin"
|
||||||
|
,PLUGIN_LICENSE_GPL
|
||||||
|
,proxy_auth_plugin_init
|
||||||
|
,proxy_auth_plugin_deinit
|
||||||
|
,0x0100 // version 1.0
|
||||||
|
,NULL
|
||||||
|
,NULL
|
||||||
|
,NULL
|
||||||
|
,0
|
||||||
|
}
|
||||||
|
mysql_declare_plugin_end;
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
[db]
|
||||||
|
user = auth-user
|
||||||
|
pass = password
|
||||||
|
socket = /var/run/mysqld/mysqld.sock
|
||||||
|
schema = account
|
||||||
|
maxConnections = 4
|
|
@ -0,0 +1,516 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 - Alejandro T. Colombini
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <mysql.h>
|
||||||
|
#include <mysql/errmsg.h>
|
||||||
|
#include <mysql/plugin_auth.h>
|
||||||
|
#include <my_sys.h>
|
||||||
|
#include <glib-unix.h>
|
||||||
|
|
||||||
|
#define CONFIG_FILE _CONFIG_DIR"/proxy_auth.ini"
|
||||||
|
#define SQL_FILE _SQL_DIR"/proxy_auth.sql"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
gboolean initialized;
|
||||||
|
gchar * socket;
|
||||||
|
gchar * host;
|
||||||
|
gchar * user;
|
||||||
|
gchar * pass;
|
||||||
|
gchar * schema;
|
||||||
|
guint port;
|
||||||
|
guint last_error;
|
||||||
|
GArray * fields;
|
||||||
|
gchar * stmt;
|
||||||
|
gsize stmt_len;
|
||||||
|
|
||||||
|
gint pool_size;
|
||||||
|
gint max_connections;
|
||||||
|
GAsyncQueue * conn_pool;
|
||||||
|
GMutex mutex;
|
||||||
|
}
|
||||||
|
ProxyAuth;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
FIELD_USER,
|
||||||
|
FIELD_PASS
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
gboolean initialized;
|
||||||
|
MYSQL * conn;
|
||||||
|
MYSQL_STMT * stmt;
|
||||||
|
gboolean connected;
|
||||||
|
|
||||||
|
MYSQL_BIND * params;
|
||||||
|
char user[MYSQL_USERNAME_LENGTH];
|
||||||
|
gsize user_len;
|
||||||
|
char pass[256];
|
||||||
|
gsize pass_len;
|
||||||
|
my_bool pass_is_null;
|
||||||
|
|
||||||
|
MYSQL_BIND * result;
|
||||||
|
char proxy_user[MYSQL_USERNAME_LENGTH];
|
||||||
|
gsize proxy_user_len;
|
||||||
|
my_bool proxy_user_is_null;
|
||||||
|
}
|
||||||
|
ConnData;
|
||||||
|
|
||||||
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ConnData
|
||||||
|
|
||||||
|
static ConnData *
|
||||||
|
conn_data_new ()
|
||||||
|
{
|
||||||
|
ConnData * self = g_new (ConnData, 1);
|
||||||
|
self->initialized = FALSE;
|
||||||
|
self->connected = FALSE;
|
||||||
|
self->conn = mysql_init (NULL);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
conn_data_deinit (ConnData * self)
|
||||||
|
{
|
||||||
|
if (!self->initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (self->stmt)
|
||||||
|
mysql_stmt_close (self->stmt);
|
||||||
|
|
||||||
|
g_free (self->params);
|
||||||
|
g_free (self->result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
conn_data_free (ConnData * self)
|
||||||
|
{
|
||||||
|
conn_data_deinit (self);
|
||||||
|
mysql_close (self->conn);
|
||||||
|
g_free (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
conn_data_reconnect (ConnData * self, ProxyAuth * pauth)
|
||||||
|
{
|
||||||
|
if (self->connected)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
conn_data_deinit (self);
|
||||||
|
self->initialized = TRUE;
|
||||||
|
self->connected = FALSE;
|
||||||
|
self->stmt = NULL;
|
||||||
|
self->params = NULL;
|
||||||
|
self->result = NULL;
|
||||||
|
|
||||||
|
gboolean connected = mysql_real_connect (self->conn,
|
||||||
|
pauth->host, pauth->user, pauth->pass, pauth->schema, pauth->port, pauth->socket, 0) != NULL;
|
||||||
|
guint conn_error = mysql_errno (self->conn);
|
||||||
|
|
||||||
|
if (conn_error && pauth->last_error != conn_error)
|
||||||
|
g_warning ("ProxyAuth: Can't connect to database: %s",
|
||||||
|
mysql_error (self->conn));
|
||||||
|
|
||||||
|
pauth->last_error = conn_error;
|
||||||
|
|
||||||
|
if (!connected)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// Initializes the prepared statement
|
||||||
|
|
||||||
|
self->stmt = mysql_stmt_init (self->conn);
|
||||||
|
|
||||||
|
if (!self->stmt)
|
||||||
|
{
|
||||||
|
g_warning ("ProxyAuth: Error initializing statement: Out of memory");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mysql_stmt_prepare (self->stmt, pauth->stmt, pauth->stmt_len))
|
||||||
|
{
|
||||||
|
g_warning ("ProxyAuth: Error preparing statement: %s",
|
||||||
|
mysql_stmt_error (self->stmt));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the query params
|
||||||
|
|
||||||
|
gint i;
|
||||||
|
MYSQL_BIND * params = self->params = g_new0 (MYSQL_BIND, pauth->fields->len);
|
||||||
|
|
||||||
|
for (i = 0; i < pauth->fields->len; i++)
|
||||||
|
{
|
||||||
|
params[i].buffer_type = MYSQL_TYPE_STRING;
|
||||||
|
|
||||||
|
if (g_array_index (pauth->fields, gint, i) == FIELD_USER)
|
||||||
|
{
|
||||||
|
params[i].buffer = self->user;
|
||||||
|
params[i].length = &self->user_len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
params[i].buffer = self->pass;
|
||||||
|
params[i].length = &self->pass_len;
|
||||||
|
params[i].is_null = &self->pass_is_null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mysql_stmt_bind_param (self->stmt, params))
|
||||||
|
{
|
||||||
|
g_warning ("ProxyAuth: Error binding statement params: %s",
|
||||||
|
mysql_stmt_error (self->stmt));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binds the result
|
||||||
|
|
||||||
|
MYSQL_BIND * result = self->result = g_new0 (MYSQL_BIND, 1);
|
||||||
|
result->buffer_type = MYSQL_TYPE_STRING;
|
||||||
|
result->buffer = self->proxy_user;
|
||||||
|
result->buffer_length = MYSQL_USERNAME_LENGTH;
|
||||||
|
result->length = &self->proxy_user_len;
|
||||||
|
result->is_null = &self->proxy_user_is_null;
|
||||||
|
|
||||||
|
if (mysql_stmt_bind_result (self->stmt, result))
|
||||||
|
{
|
||||||
|
g_warning ("ProxyAuth: Error binding statement result: %s",
|
||||||
|
mysql_stmt_error (self->stmt));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->connected = TRUE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ProxyAuth
|
||||||
|
|
||||||
|
ProxyAuth *
|
||||||
|
proxy_auth_new ()
|
||||||
|
{
|
||||||
|
ProxyAuth * self = g_new (ProxyAuth, 1);
|
||||||
|
self->initialized = FALSE;
|
||||||
|
g_mutex_init (&self->mutex);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
proxy_auth_deinit (ProxyAuth * self)
|
||||||
|
{
|
||||||
|
if (!self->initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_free (self->socket);
|
||||||
|
g_free (self->host);
|
||||||
|
g_free (self->user);
|
||||||
|
g_free (self->pass);
|
||||||
|
g_free (self->schema);
|
||||||
|
g_free (self->stmt);
|
||||||
|
|
||||||
|
if (self->conn_pool)
|
||||||
|
g_async_queue_unref (self->conn_pool);
|
||||||
|
if (self->fields)
|
||||||
|
g_array_free (self->fields, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
proxy_auth_free (ProxyAuth * self)
|
||||||
|
{
|
||||||
|
proxy_auth_deinit (self);
|
||||||
|
g_mutex_clear (&self->mutex);
|
||||||
|
g_free (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
proxy_auth_regex_func (const GMatchInfo * info, GString * res, gpointer data)
|
||||||
|
{
|
||||||
|
GArray * fields = (GArray *) data;
|
||||||
|
gchar * match = g_match_info_fetch (info, 0);
|
||||||
|
gint field = -1;
|
||||||
|
|
||||||
|
if (!g_strcmp0 (match, "#user"))
|
||||||
|
field = FIELD_USER;
|
||||||
|
else if (!g_strcmp0 (match, "#pass"))
|
||||||
|
field = FIELD_PASS;
|
||||||
|
|
||||||
|
if (field != -1)
|
||||||
|
{
|
||||||
|
g_string_append_c (res, '?');
|
||||||
|
g_array_append_val (fields, field);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
g_string_append (res, match);
|
||||||
|
|
||||||
|
g_free (match);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
proxy_auth_init (ProxyAuth * self)
|
||||||
|
{
|
||||||
|
gboolean res = FALSE;
|
||||||
|
GError * error = NULL;
|
||||||
|
gchar * queryt = NULL;
|
||||||
|
GRegex * regex = NULL;
|
||||||
|
|
||||||
|
proxy_auth_deinit (self);
|
||||||
|
self->initialized = TRUE;
|
||||||
|
self->socket = NULL;
|
||||||
|
self->host = NULL;
|
||||||
|
self->user = NULL;
|
||||||
|
self->pass = NULL;
|
||||||
|
self->schema = NULL;
|
||||||
|
self->conn_pool = NULL;
|
||||||
|
self->fields = NULL;
|
||||||
|
self->stmt = NULL;
|
||||||
|
self->stmt_len = 0;
|
||||||
|
|
||||||
|
// Reading the configuration file
|
||||||
|
|
||||||
|
GKeyFile * key_file = g_key_file_new ();
|
||||||
|
|
||||||
|
if (!g_key_file_load_from_file (key_file, CONFIG_FILE, G_KEY_FILE_NONE, &error))
|
||||||
|
{
|
||||||
|
g_warning ("ProxyAuth: Can't open configuration file: %s", error->message);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->socket = NULL;
|
||||||
|
self->host = g_key_file_get_string (key_file, "db", "host", NULL);
|
||||||
|
|
||||||
|
if (!self->host)
|
||||||
|
self->socket = g_key_file_get_string (key_file, "db", "socket", NULL);
|
||||||
|
|
||||||
|
self->user = g_key_file_get_string (key_file, "db", "user", NULL);
|
||||||
|
self->pass = g_key_file_get_string (key_file, "db", "pass", NULL);
|
||||||
|
self->schema = g_key_file_get_string (key_file, "db", "schema", NULL);
|
||||||
|
self->port = (guint) g_key_file_get_integer (key_file, "db", "port", NULL);
|
||||||
|
self->max_connections = g_key_file_get_integer (key_file, "db", "maxConnections", NULL);
|
||||||
|
|
||||||
|
// Reading the query template
|
||||||
|
|
||||||
|
gsize queryt_len;
|
||||||
|
|
||||||
|
if (!g_file_get_contents (SQL_FILE, &queryt, &queryt_len, &error))
|
||||||
|
{
|
||||||
|
g_warning ("ProxyAuth: Can't read the query template: %s", error->message);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the regular expression
|
||||||
|
|
||||||
|
regex = g_regex_new ("#\\w+",
|
||||||
|
G_REGEX_OPTIMIZE | G_REGEX_MULTILINE, 0, &error);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
g_warning ("ProxyAuth: Can't create the regex: %s", error->message);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->fields = g_array_sized_new (FALSE, FALSE, sizeof (gint), 2);
|
||||||
|
self->stmt = g_regex_replace_eval (regex,
|
||||||
|
queryt, queryt_len, 0, 0, proxy_auth_regex_func, self->fields, &error);
|
||||||
|
self->stmt_len = strlen (self->stmt);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
g_warning ("ProxyAuth: Can't evaluate regex: %s", error->message);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the connection pool
|
||||||
|
|
||||||
|
self->conn_pool = g_async_queue_new_full ((GDestroyNotify) conn_data_free);
|
||||||
|
self->pool_size = 0;
|
||||||
|
self->last_error = 0;
|
||||||
|
|
||||||
|
if (self->max_connections < 1)
|
||||||
|
self->max_connections = 1;
|
||||||
|
|
||||||
|
res = TRUE;
|
||||||
|
end:
|
||||||
|
if (regex)
|
||||||
|
g_regex_unref (regex);
|
||||||
|
|
||||||
|
g_clear_error (&error);
|
||||||
|
g_free (queryt);
|
||||||
|
g_key_file_free (key_file);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
proxy_auth_authenticate (ProxyAuth * self, MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_AUTH_INFO * info)
|
||||||
|
{
|
||||||
|
int res = CR_ERROR;
|
||||||
|
gchar * query = NULL;
|
||||||
|
unsigned char * pkt;
|
||||||
|
|
||||||
|
// Check for the username
|
||||||
|
|
||||||
|
guint user_len = info->user_name_length;
|
||||||
|
|
||||||
|
if (info->user_name == NULL
|
||||||
|
&& (user_len = vio->read_packet (vio, &pkt)) < 0)
|
||||||
|
return CR_ERROR;
|
||||||
|
|
||||||
|
if (user_len > MYSQL_USERNAME_LENGTH)
|
||||||
|
return CR_ERROR;
|
||||||
|
|
||||||
|
// Read the password and check if it's valid
|
||||||
|
|
||||||
|
guint pass_len = vio->read_packet (vio, &pkt) - 1;
|
||||||
|
|
||||||
|
if (pass_len < 0)
|
||||||
|
return CR_ERROR;
|
||||||
|
|
||||||
|
if (!pass_len || *pkt == '\0')
|
||||||
|
{
|
||||||
|
info->password_used = PASSWORD_USED_NO;
|
||||||
|
return CR_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
char pass[pass_len + 1];
|
||||||
|
memcpy (pass, pkt, pass_len);
|
||||||
|
pass[pass_len] = '\0';
|
||||||
|
|
||||||
|
info->password_used = PASSWORD_USED_YES;
|
||||||
|
|
||||||
|
// Gets a connection from the pool
|
||||||
|
|
||||||
|
ConnData * conn_data = (ConnData *) g_async_queue_try_pop (self->conn_pool);
|
||||||
|
|
||||||
|
if (!conn_data)
|
||||||
|
{
|
||||||
|
g_mutex_lock (&self->mutex);
|
||||||
|
|
||||||
|
if (self->pool_size < self->max_connections)
|
||||||
|
{
|
||||||
|
self->pool_size++;
|
||||||
|
g_mutex_unlock (&self->mutex);
|
||||||
|
conn_data = conn_data_new ();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_mutex_unlock (&self->mutex);
|
||||||
|
conn_data = (ConnData *) g_async_queue_pop (self->conn_pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conn_data_reconnect (conn_data, self))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
// Sends the query to the database
|
||||||
|
|
||||||
|
strncpy (conn_data->user, info->user_name, user_len);
|
||||||
|
conn_data->user_len = user_len;
|
||||||
|
strncpy (conn_data->pass, pass, pass_len);
|
||||||
|
conn_data->pass_len = pass_len;
|
||||||
|
conn_data->pass_is_null = pass_len <= 0;
|
||||||
|
|
||||||
|
MYSQL_STMT * stmt = conn_data->stmt;
|
||||||
|
|
||||||
|
if (!mysql_stmt_execute (stmt)
|
||||||
|
&& !mysql_stmt_fetch (stmt))
|
||||||
|
{
|
||||||
|
strcpy (info->external_user, info->user_name);
|
||||||
|
strncpy (info->authenticated_as,
|
||||||
|
conn_data->proxy_user, conn_data->proxy_user_len);
|
||||||
|
res = CR_OK;
|
||||||
|
|
||||||
|
mysql_stmt_free_result (stmt);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (mysql_stmt_errno (stmt))
|
||||||
|
{
|
||||||
|
case CR_SERVER_LOST:
|
||||||
|
case CR_SERVER_GONE_ERROR:
|
||||||
|
conn_data->connected = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_warning ("ProxyAuth: Error executing query: %s",
|
||||||
|
mysql_stmt_error (stmt));
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
if (conn_data)
|
||||||
|
g_async_queue_push (self->conn_pool, conn_data);
|
||||||
|
|
||||||
|
g_free (query);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Plugin
|
||||||
|
|
||||||
|
ProxyAuth * pauth;
|
||||||
|
|
||||||
|
static int
|
||||||
|
proxy_auth_plugin_main (MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_AUTH_INFO * info)
|
||||||
|
{
|
||||||
|
my_init ();
|
||||||
|
|
||||||
|
if (mysql_thread_init ())
|
||||||
|
g_warning ("ProxyAuth: Can't initialize MySQL thread");
|
||||||
|
|
||||||
|
return proxy_auth_authenticate (pauth, vio, info);;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
proxy_auth_plugin_init ()
|
||||||
|
{
|
||||||
|
if (mysql_library_init (0, NULL, NULL))
|
||||||
|
g_warning ("ProxyAuth: Can't initialize MySQL library");
|
||||||
|
|
||||||
|
pauth = proxy_auth_new ();
|
||||||
|
return proxy_auth_init (pauth) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
proxy_auth_plugin_deinit ()
|
||||||
|
{
|
||||||
|
proxy_auth_free (pauth);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct st_mysql_auth proxy_auth_handler =
|
||||||
|
{
|
||||||
|
MYSQL_AUTHENTICATION_INTERFACE_VERSION
|
||||||
|
,"mysql_clear_password" // Cleartext plugin required in the client
|
||||||
|
,proxy_auth_plugin_main
|
||||||
|
};
|
||||||
|
|
||||||
|
mysql_declare_plugin(proxy_auth)
|
||||||
|
{
|
||||||
|
MYSQL_AUTHENTICATION_PLUGIN
|
||||||
|
,&proxy_auth_handler
|
||||||
|
,"proxy_auth"
|
||||||
|
,"Alejandro T. Colombini"
|
||||||
|
,"Proxy user authentication server-side plugin"
|
||||||
|
,PLUGIN_LICENSE_GPL
|
||||||
|
,proxy_auth_plugin_init
|
||||||
|
,proxy_auth_plugin_deinit
|
||||||
|
,0x0100 // version 1.0
|
||||||
|
,NULL
|
||||||
|
,NULL
|
||||||
|
,NULL
|
||||||
|
,0
|
||||||
|
}
|
||||||
|
mysql_declare_plugin_end;
|
||||||
|
|
|
@ -1,240 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2013 - Alejandro T. Colombini
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <mysql/plugin_auth.h>
|
|
||||||
#include <mysql.h>
|
|
||||||
|
|
||||||
#define MAX_BUFFER_SIZE 256
|
|
||||||
#define CONFIG_FILE _CONFIG_DIR"/proxy_auth.cnf"
|
|
||||||
#define SQL_FILE _SQL_DIR"/proxy_auth.sql"
|
|
||||||
|
|
||||||
static char * strcat_quoted (char * query, const char * str, int str_len)
|
|
||||||
{
|
|
||||||
int quoted_len = str_len + 2;
|
|
||||||
char *buff, q[quoted_len];
|
|
||||||
|
|
||||||
buff = stpcpy (q, "'");
|
|
||||||
buff = stpcpy (buff, str);
|
|
||||||
buff = stpcpy (buff, "'");
|
|
||||||
strcat (query, q);
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char * get_stmt (const char * user, int user_len, const char * pass, int pass_len)
|
|
||||||
{
|
|
||||||
long stmt_len, q_len;
|
|
||||||
size_t prev;
|
|
||||||
char * buffer, * query;
|
|
||||||
FILE * file;
|
|
||||||
|
|
||||||
if (!(file = fopen (SQL_FILE, "r")))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// Get the size of file
|
|
||||||
|
|
||||||
if (fseek (file, 0, SEEK_END) < 0
|
|
||||||
|| (stmt_len = ftell (file)) < 0
|
|
||||||
|| fseek (file, 0, SEEK_SET) < 0)
|
|
||||||
{
|
|
||||||
fclose (file);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char stmt[stmt_len + 1];
|
|
||||||
|
|
||||||
// Get the contents of file
|
|
||||||
|
|
||||||
if (fread (stmt, stmt_len, 1, file) < 1)
|
|
||||||
{
|
|
||||||
fclose (file);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose (file);
|
|
||||||
stmt[stmt_len] = '\0';
|
|
||||||
|
|
||||||
q_len = stmt_len + user_len + pass_len + 1;
|
|
||||||
query = malloc (q_len);
|
|
||||||
strcpy (query, "");
|
|
||||||
|
|
||||||
// Substitute #user and #pass in stmt by the contents of user and pass
|
|
||||||
|
|
||||||
buffer = stmt;
|
|
||||||
prev = (size_t) stmt;
|
|
||||||
|
|
||||||
while ((buffer = strstr (buffer, "#")))
|
|
||||||
{
|
|
||||||
int offset = 1, tok_len = 5;
|
|
||||||
char token[tok_len + 1];
|
|
||||||
|
|
||||||
strncat (query, (char *) prev, (size_t) (buffer - prev));
|
|
||||||
strncpy (token, buffer, tok_len);
|
|
||||||
token[tok_len] = '\0';
|
|
||||||
|
|
||||||
if (!strcmp (token, "#user"))
|
|
||||||
{
|
|
||||||
strcat_quoted (query, user, user_len);
|
|
||||||
offset = tok_len;
|
|
||||||
}
|
|
||||||
else if (!strcmp (token, "#pass"))
|
|
||||||
{
|
|
||||||
strcat_quoted (query, pass, pass_len);
|
|
||||||
offset = tok_len;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
strcat (query, "#");
|
|
||||||
|
|
||||||
buffer = buffer + offset;
|
|
||||||
prev = (size_t) buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
strncat (query, (char *) prev, prev);
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int proxy_auth_main (MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_AUTH_INFO * info)
|
|
||||||
{
|
|
||||||
int i = 0, res = CR_ERROR;
|
|
||||||
int pass_len, name_len = info->user_name_length;
|
|
||||||
unsigned char * pkt;
|
|
||||||
char buffer[MAX_BUFFER_SIZE], config[3][MAX_BUFFER_SIZE];
|
|
||||||
char * query;
|
|
||||||
FILE * file;
|
|
||||||
MYSQL conn;
|
|
||||||
|
|
||||||
// Check for the username
|
|
||||||
|
|
||||||
if (info->user_name == NULL)
|
|
||||||
if ((name_len = vio->read_packet(vio, &pkt)) < 0)
|
|
||||||
return CR_ERROR;
|
|
||||||
|
|
||||||
if (name_len > MYSQL_USERNAME_LENGTH)
|
|
||||||
return CR_ERROR;
|
|
||||||
|
|
||||||
// Read the password and check if it's valid
|
|
||||||
|
|
||||||
if ((pass_len = vio->read_packet (vio, &pkt)) < 0)
|
|
||||||
return CR_ERROR;
|
|
||||||
|
|
||||||
if (!pass_len || *pkt == '\0')
|
|
||||||
{
|
|
||||||
info->password_used = PASSWORD_USED_NO;
|
|
||||||
return CR_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
char pass[pass_len + 1];
|
|
||||||
memcpy (pass, pkt, pass_len);
|
|
||||||
pass[pass_len] = '\0';
|
|
||||||
|
|
||||||
info->password_used = PASSWORD_USED_YES;
|
|
||||||
|
|
||||||
// Get connection data from CONFIG_FILE
|
|
||||||
|
|
||||||
if (!(file = fopen (CONFIG_FILE, "r")))
|
|
||||||
return CR_ERROR;
|
|
||||||
|
|
||||||
while (i < 3 && fgets (buffer, MAX_BUFFER_SIZE, file))
|
|
||||||
if (buffer[0] != '#'
|
|
||||||
&& buffer[0] != '\n'
|
|
||||||
&& buffer[0] != ' ')
|
|
||||||
{
|
|
||||||
int len;
|
|
||||||
strcpy (config[i], buffer);
|
|
||||||
len = strlen (config[i]);
|
|
||||||
config[i][len-1] = '\0';
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i < 3)
|
|
||||||
return CR_ERROR;
|
|
||||||
|
|
||||||
fclose (file);
|
|
||||||
|
|
||||||
// Connect to the database
|
|
||||||
|
|
||||||
mysql_init (&conn);
|
|
||||||
|
|
||||||
if (!mysql_real_connect (&conn,
|
|
||||||
NULL, config[0], config[1], config[2], 0, "/var/run/mysqld/mysqld.sock", 0))
|
|
||||||
{
|
|
||||||
mysql_close (&conn);
|
|
||||||
return CR_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Form the query, send it and then set the results on info
|
|
||||||
|
|
||||||
if (!(query = get_stmt (info->user_name, name_len, pass, pass_len)))
|
|
||||||
{
|
|
||||||
mysql_close (&conn);
|
|
||||||
return CR_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mysql_query (&conn, query))
|
|
||||||
{
|
|
||||||
MYSQL_RES * result;
|
|
||||||
|
|
||||||
if ((result = mysql_store_result (&conn)))
|
|
||||||
{
|
|
||||||
MYSQL_ROW row = mysql_fetch_row (result);
|
|
||||||
|
|
||||||
if (row && row[0])
|
|
||||||
{
|
|
||||||
unsigned long row_len = mysql_fetch_lengths (result)[0];
|
|
||||||
|
|
||||||
if (row_len > 0 && row_len <= MYSQL_USERNAME_LENGTH)
|
|
||||||
{
|
|
||||||
strcpy (info->external_user, info->user_name);
|
|
||||||
strncpy (info->authenticated_as, row[0], row_len);
|
|
||||||
res = CR_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mysql_free_result (result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mysql_close (&conn);
|
|
||||||
free (query);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct st_mysql_auth proxy_auth_handler =
|
|
||||||
{
|
|
||||||
MYSQL_AUTHENTICATION_INTERFACE_VERSION
|
|
||||||
,"mysql_clear_password" // Cleartext plugin required in the client
|
|
||||||
,proxy_auth_main
|
|
||||||
};
|
|
||||||
|
|
||||||
mysql_declare_plugin(proxy_auth)
|
|
||||||
{
|
|
||||||
MYSQL_AUTHENTICATION_PLUGIN
|
|
||||||
,&proxy_auth_handler
|
|
||||||
,"proxy_auth"
|
|
||||||
,"Alejandro T. Colombini"
|
|
||||||
,"Proxy user authentication server-side plugin"
|
|
||||||
,PLUGIN_LICENSE_GPL
|
|
||||||
,NULL
|
|
||||||
,NULL
|
|
||||||
,0x0100 // version 1.0
|
|
||||||
,NULL
|
|
||||||
,NULL
|
|
||||||
,NULL
|
|
||||||
,0
|
|
||||||
}
|
|
||||||
mysql_declare_plugin_end;
|
|
|
@ -1,9 +0,0 @@
|
||||||
# Settings for the internal connection of the proxy_auth plugin
|
|
||||||
# Order matters. Blank lines and lines started with a space or a '#' are ignored
|
|
||||||
|
|
||||||
# User
|
|
||||||
root
|
|
||||||
# Password
|
|
||||||
123456
|
|
||||||
# Database
|
|
||||||
account
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
NAME=sql_printf
|
||||||
|
FILE=$(NAME).so
|
||||||
|
SRC=$(NAME).c
|
||||||
|
URL=/usr/lib/mysql/plugin/$(FILE)
|
||||||
|
|
||||||
|
SQL_DROP="DROP FUNCTION IF EXISTS $(NAME);"
|
||||||
|
|
||||||
|
.PHONY: install uninstall clean
|
||||||
|
|
||||||
|
$(FILE): $(SRC)
|
||||||
|
rm -f $@
|
||||||
|
gcc -shared -o $@ $< -fPIC
|
||||||
|
|
||||||
|
install:
|
||||||
|
cp $(FILE) $(URL)
|
||||||
|
echo $(SQL_DROP)
|
||||||
|
echo "CREATE FUNCTION $(NAME) RETURNS STRING SONAME '$(FILE)';"
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -f $(URL)
|
||||||
|
echo $(SQL_DROP)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(FILE)
|
||||||
|
|
Reference in New Issue