00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <config.h>
00021 #include <drizzled/identifier.h>
00022 #include <drizzled/util/convert.h>
00023 #include <drizzled/algorithm/sha1.h>
00024 #include <drizzled/execute.h>
00025 #include <drizzled/sql/result_set.h>
00026 #include <drizzled/plugin/listen.h>
00027 #include <drizzled/plugin/client.h>
00028 #include <drizzled/catalog/local.h>
00029 #include "auth_schema.h"
00030
00031 namespace drizzle_plugin {
00032 namespace auth_schema {
00033
00034 AuthSchema::AuthSchema(bool enabled) :
00035 plugin::Authentication("auth_schema"),
00036 sysvar_enabled(enabled)
00037 {
00038 const char *error;
00039 int erroffset;
00040 _ident_re= pcre_compile(
00041 "^`[^`]+`",
00042 0,
00043 &error,
00044 &erroffset,
00045 NULL);
00046 }
00047
00048 bool AuthSchema::setTable(const string &table)
00049 {
00050 if (table.empty())
00051 {
00052 errmsg_printf(error::ERROR, _("auth_schema table cannot be an empty string"));
00053 return true;
00054 }
00055
00056 if (table.find(".") == string::npos)
00057 {
00058 errmsg_printf(error::ERROR, _("auth_schema must be schema-qualified"));
00059 return true;
00060 }
00061
00062 sysvar_table= escapeQuoteAuthTable(table);
00063
00064 return false;
00065 }
00066
00067 bool AuthSchema::verifyMySQLPassword(const string &real_password,
00068 const string &scramble_bytes,
00069 const string &client_password)
00070 {
00071 if (scramble_bytes.size() != SHA1_DIGEST_LENGTH
00072 || client_password.size() != SHA1_DIGEST_LENGTH)
00073 return false;
00074
00075 uint8_t real_password_hash[SHA1_DIGEST_LENGTH];
00076 drizzled_hex_to_string(
00077 reinterpret_cast<char *>(real_password_hash),
00078 real_password.c_str(),
00079 SHA1_DIGEST_LENGTH * 2);
00080
00081
00082 SHA1_CTX ctx;
00083 uint8_t temp_hash[SHA1_DIGEST_LENGTH];
00084 SHA1Init(&ctx);
00085 SHA1Update(&ctx, reinterpret_cast<const uint8_t*>(scramble_bytes.c_str()), SHA1_DIGEST_LENGTH);
00086 SHA1Update(&ctx, real_password_hash, SHA1_DIGEST_LENGTH);
00087 SHA1Final(temp_hash, &ctx);
00088
00089
00090
00091 for (int x= 0; x < SHA1_DIGEST_LENGTH; x++)
00092 temp_hash[x]= temp_hash[x] ^ client_password[x];
00093
00094
00095 uint8_t client_password_hash[SHA1_DIGEST_LENGTH];
00096 SHA1Init(&ctx);
00097 SHA1Update(&ctx, temp_hash, SHA1_DIGEST_LENGTH);
00098 SHA1Final(client_password_hash, &ctx);
00099
00100
00101 return memcmp(real_password_hash, client_password_hash, SHA1_DIGEST_LENGTH) == 0;
00102 }
00103
00104 bool AuthSchema::authenticate(const identifier::User &sctx, const string &password)
00105 {
00106
00107 if (not sysvar_enabled)
00108 return false;
00109
00110
00111
00112 if (sctx.getPasswordType() != identifier::User::MYSQL_HASH)
00113 return false;
00114
00115
00116 string user= escapeString(sctx.username());
00117 if (user.empty())
00118 return false;
00119
00120
00121
00122 if (not _session)
00123 {
00124 _session= Session::make_shared(plugin::Listen::getNullClient(), catalog::local());
00125 identifier::user::mptr user_id= identifier::User::make_shared();
00126 user_id->setUser("auth_schema");
00127 _session->setUser(user_id);
00128 }
00129
00130
00131
00132 string sql= "SELECT password FROM " + sysvar_table + " WHERE user='" + user + "' LIMIT 1;";
00133 Execute execute(*(_session.get()), true);
00134 sql::ResultSet result_set(1);
00135 execute.run(sql, result_set);
00136 sql::Exception exception= result_set.getException();
00137 drizzled::error_t err= exception.getErrorCode();
00138 if ((err != EE_OK) && (err != ER_EMPTY_QUERY))
00139 {
00140 errmsg_printf(error::ERROR,
00141 _("Error querying authentication schema: %s (error code %d. Query: %s"),
00142 exception.getErrorMessage().c_str(), exception.getErrorCode(), sql.c_str());
00143 return false;
00144 }
00145
00146
00147
00148 if (result_set.next() and not result_set.isNull(0))
00149 {
00150 string real_password= result_set.getString(0);
00151
00152 return verifyMySQLPassword(
00153 real_password,
00154 sctx.getPasswordContext(),
00155 password);
00156 }
00157
00158
00159 return false;
00160 }
00161
00162 string AuthSchema::escapeQuoteAuthTable(const string &table)
00163 {
00164 int pos= table.find(".");
00165 string quoted_schema= escapeQuoteIdentifier(table.substr(0, pos));
00166 string quoted_table= escapeQuoteIdentifier(table.substr(pos + 1, table.length() - pos));
00167 return quoted_schema + "." + quoted_table;
00168 }
00169
00170 string AuthSchema::escapeQuoteIdentifier(const string &input)
00171 {
00172 if (input.empty())
00173 return "``";
00174
00179 int match_result= pcre_exec(
00180 _ident_re, NULL, input.c_str(), input.length(), 0, 0, NULL, 0);
00181 if (match_result >= 0)
00182 return input;
00183
00184 const char *pos= input.c_str();
00185 const char *end= input.c_str()+input.length();
00186 string ident= "`";
00187
00188 for (; pos != end ; pos++)
00189 {
00190 switch (*pos) {
00191 case '`':
00192 ident.push_back('\\');
00193 ident.push_back('`');
00194 break;
00195 case '\\':
00196 ident.push_back('\\');
00197 ident.push_back('\\');
00198 break;
00199 default:
00200 ident.push_back(*pos);
00201 break;
00202 }
00203 }
00204
00205 ident.push_back('`');
00206
00207 return ident;
00208 }
00209
00210 string AuthSchema::escapeString(const string &input)
00211 {
00212 if (input.empty())
00213 return input;
00214
00215 const char *pos= input.c_str();
00216 const char *end= input.c_str()+input.length();
00217 string res;
00218
00219 for (; pos != end ; pos++)
00220 {
00221 switch (*pos) {
00222 case 0:
00223 res.push_back('\\');
00224 res.push_back('0');
00225 break;
00226 case '\n':
00227 res.push_back('\\');
00228 res.push_back('n');
00229 break;
00230 case '\r':
00231 res.push_back('\\');
00232 res.push_back('r');
00233 break;
00234 case '\\':
00235 res.push_back('\\');
00236 res.push_back('\\');
00237 break;
00238 case '\'':
00239 res.push_back('\\');
00240 res.push_back('\'');
00241 break;
00242 default:
00243 res.push_back(*pos);
00244 break;
00245 }
00246 }
00247
00248 return res;
00249 }
00250
00251 AuthSchema::~AuthSchema()
00252 {
00253 if (_ident_re != NULL)
00254 pcre_free(_ident_re);
00255 }
00256
00257 }
00258 }