1
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
4
* Copyright 2011 Daniel Nichter
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; version 2 of the License.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
#include <drizzled/identifier.h>
22
#include <drizzled/util/convert.h>
23
#include <drizzled/algorithm/sha1.h>
24
#include <drizzled/execute.h>
25
#include <drizzled/sql/result_set.h>
26
#include <drizzled/plugin/listen.h>
27
#include <drizzled/plugin/client.h>
28
#include <drizzled/catalog/local.h>
29
#include "auth_schema.h"
31
namespace drizzle_plugin {
32
namespace auth_schema {
34
AuthSchema::AuthSchema(bool enabled) :
35
plugin::Authentication("auth_schema"),
36
sysvar_enabled(enabled)
40
_ident_re= pcre_compile(
41
"^`[^`]+`", /* the pattern */
42
0, /* default options */
43
&error, /* for error message */
44
&erroffset, /* for error offset */
45
NULL); /* use default character tables */
48
bool AuthSchema::setTable(const string &table)
52
errmsg_printf(error::ERROR, _("auth_schema table cannot be an empty string"));
56
if (table.find(".") == string::npos)
58
errmsg_printf(error::ERROR, _("auth_schema must be schema-qualified"));
62
sysvar_table= escapeQuoteAuthTable(table);
64
return false; // success
67
bool AuthSchema::verifyMySQLPassword(const string &real_password,
68
const string &scramble_bytes,
69
const string &client_password)
71
if (scramble_bytes.size() != SHA1_DIGEST_LENGTH
72
|| client_password.size() != SHA1_DIGEST_LENGTH)
75
uint8_t real_password_hash[SHA1_DIGEST_LENGTH];
76
drizzled_hex_to_string(
77
reinterpret_cast<char *>(real_password_hash),
78
real_password.c_str(),
79
SHA1_DIGEST_LENGTH * 2);
81
/* Hash the scramble that was sent to client with the local password. */
83
uint8_t temp_hash[SHA1_DIGEST_LENGTH];
85
SHA1Update(&ctx, reinterpret_cast<const uint8_t*>(scramble_bytes.c_str()), SHA1_DIGEST_LENGTH);
86
SHA1Update(&ctx, real_password_hash, SHA1_DIGEST_LENGTH);
87
SHA1Final(temp_hash, &ctx);
89
/* Next, XOR the result with what the client sent to get the original
90
single-hashed password. */
91
for (int x= 0; x < SHA1_DIGEST_LENGTH; x++)
92
temp_hash[x]= temp_hash[x] ^ client_password[x];
94
/* Hash this result once more to get the double-hashed password again. */
95
uint8_t client_password_hash[SHA1_DIGEST_LENGTH];
97
SHA1Update(&ctx, temp_hash, SHA1_DIGEST_LENGTH);
98
SHA1Final(client_password_hash, &ctx);
100
/* These should match for a successful auth. */
101
return memcmp(real_password_hash, client_password_hash, SHA1_DIGEST_LENGTH) == 0;
104
bool AuthSchema::authenticate(const identifier::User &sctx, const string &password)
106
// If plugin is disabled, deny everyone.
107
if (not sysvar_enabled)
110
// Plugin only works with MySQL hash passwords, so client needs
111
// to connect with --protocol mysql-plugin-auth.
112
if (sctx.getPasswordType() != identifier::User::MYSQL_HASH)
115
// Anonymous users are not allowed.
116
string user= escapeString(sctx.username());
120
// Create an internal session for ourself the first time we're called.
121
// I don't know why but doing this in the constructor crashes Drizzle
124
_session= Session::make_shared(plugin::Listen::getNullClient(), catalog::local());
125
identifier::user::mptr user_id= identifier::User::make_shared();
126
user_id->setUser("auth_schema");
127
_session->setUser(user_id);
130
// Create an execute a SQL statement to select the user from the auth table.
131
// Execute wraps the SQL to run within a transaction.
132
string sql= "SELECT password FROM " + sysvar_table + " WHERE user='" + user + "' LIMIT 1;";
133
Execute execute(*(_session.get()), true);
134
sql::ResultSet result_set(1);
135
execute.run(sql, result_set);
136
sql::Exception exception= result_set.getException();
137
drizzled::error_t err= exception.getErrorCode();
138
if ((err != EE_OK) && (err != ER_EMPTY_QUERY))
140
errmsg_printf(error::ERROR,
141
_("Error querying authentication schema: %s (error code %d. Query: %s"),
142
exception.getErrorMessage().c_str(), exception.getErrorCode(), sql.c_str());
146
// If there's a result and it's not null, verify the password from
147
// the client against the real password from the auth table.
148
if (result_set.next() and not result_set.isNull(0))
150
string real_password= result_set.getString(0);
151
// Return true if auth succeeds, else return false.
152
return verifyMySQLPassword(
154
sctx.getPasswordContext(),
158
// User doesn't exist in auth table; auth fails.
162
string AuthSchema::escapeQuoteAuthTable(const string &table)
164
int pos= table.find(".");
165
string quoted_schema= escapeQuoteIdentifier(table.substr(0, pos));
166
string quoted_table= escapeQuoteIdentifier(table.substr(pos + 1, table.length() - pos));
167
return quoted_schema + "." + quoted_table;
170
string AuthSchema::escapeQuoteIdentifier(const string &input)
176
* The input may already be a quoted ident with no extra backticks.
179
int match_result= pcre_exec(
180
_ident_re, NULL, input.c_str(), input.length(), 0, 0, NULL, 0);
181
if (match_result >= 0)
184
const char *pos= input.c_str();
185
const char *end= input.c_str()+input.length();
188
for (; pos != end ; pos++)
192
ident.push_back('\\');
193
ident.push_back('`');
196
ident.push_back('\\');
197
ident.push_back('\\');
200
ident.push_back(*pos);
205
ident.push_back('`');
210
string AuthSchema::escapeString(const string &input)
215
const char *pos= input.c_str();
216
const char *end= input.c_str()+input.length();
219
for (; pos != end ; pos++)
251
AuthSchema::~AuthSchema()
253
if (_ident_re != NULL)
254
pcre_free(_ident_re);
257
} /* end namespace drizzle_plugin::auth_schema */
258
} /* end namespace drizzle_plugin */