00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <config.h>
00021
00022 #include <fstream>
00023 #include <map>
00024 #include <string>
00025 #include <iostream>
00026
00027 #include <boost/program_options.hpp>
00028 #include <boost/filesystem.hpp>
00029
00030 #include <drizzled/configmake.h>
00031 #include <drizzled/plugin/authentication.h>
00032 #include <drizzled/identifier.h>
00033 #include <drizzled/util/convert.h>
00034 #include <drizzled/algorithm/sha1.h>
00035 #include <drizzled/module/option_map.h>
00036
00037 namespace po= boost::program_options;
00038 namespace fs= boost::filesystem;
00039
00040 using namespace std;
00041 using namespace drizzled;
00042
00043 namespace auth_file
00044 {
00045
00046 static const fs::path DEFAULT_USERS_FILE= SYSCONFDIR "/drizzle.users";
00047
00048 class AuthFile: public plugin::Authentication
00049 {
00050 const fs::path users_file;
00051
00052 public:
00053
00054 AuthFile(string name_arg, fs::path users_file_arg);
00055
00059 string& getError(void);
00060
00067 bool loadFile(void);
00068
00069 private:
00070
00074 bool authenticate(const identifier::User &sctx, const string &password);
00075
00087 bool verifyMySQLHash(const string &password,
00088 const string &scramble_bytes,
00089 const string &scrambled_password);
00090
00091 string error;
00092
00096 std::map<string, string> users;
00097 };
00098
00099 AuthFile::AuthFile(string name_arg, fs::path users_file_arg):
00100 plugin::Authentication(name_arg),
00101 users_file(users_file_arg),
00102 error(),
00103 users()
00104 {
00105 }
00106
00107 string& AuthFile::getError(void)
00108 {
00109 return error;
00110 }
00111
00112 bool AuthFile::loadFile(void)
00113 {
00114 ifstream file(users_file.string().c_str());
00115
00116 if (!file.is_open())
00117 {
00118 error = "Could not open users file: ";
00119 error += users_file.string();
00120 return false;
00121 }
00122
00123 while (!file.eof())
00124 {
00125 string line;
00126 getline(file, line);
00127
00128
00129 if (line == "" || line[line.find_first_not_of(" \t")] == '#')
00130 continue;
00131
00132 string username;
00133 string password;
00134 size_t password_offset = line.find(":");
00135 if (password_offset == string::npos)
00136 username = line;
00137 else
00138 {
00139 username = string(line, 0, password_offset);
00140 password = string(line, password_offset + 1);
00141 }
00142
00143 std::pair<std::map<std::string, std::string>::iterator, bool> result=
00144 users.insert(std::pair<std::string, std::string>(username, password));
00145
00146 if (result.second == false)
00147 {
00148 error = "Duplicate entry found in users file: ";
00149 error += username;
00150 file.close();
00151 return false;
00152 }
00153 }
00154
00155 file.close();
00156 return true;
00157 }
00158
00159 bool AuthFile::verifyMySQLHash(const string &password,
00160 const string &scramble_bytes,
00161 const string &scrambled_password)
00162 {
00163 if (scramble_bytes.size() != SHA1_DIGEST_LENGTH ||
00164 scrambled_password.size() != SHA1_DIGEST_LENGTH)
00165 {
00166 return false;
00167 }
00168
00169 SHA1_CTX ctx;
00170 uint8_t local_scrambled_password[SHA1_DIGEST_LENGTH];
00171 uint8_t temp_hash[SHA1_DIGEST_LENGTH];
00172 uint8_t scrambled_password_check[SHA1_DIGEST_LENGTH];
00173
00174
00175 SHA1Init(&ctx);
00176 SHA1Update(&ctx, reinterpret_cast<const uint8_t *>(password.c_str()),
00177 password.size());
00178 SHA1Final(temp_hash, &ctx);
00179
00180 SHA1Init(&ctx);
00181 SHA1Update(&ctx, temp_hash, SHA1_DIGEST_LENGTH);
00182 SHA1Final(local_scrambled_password, &ctx);
00183
00184
00185 SHA1Init(&ctx);
00186 SHA1Update(&ctx, reinterpret_cast<const uint8_t*>(scramble_bytes.c_str()),
00187 SHA1_DIGEST_LENGTH);
00188 SHA1Update(&ctx, local_scrambled_password, SHA1_DIGEST_LENGTH);
00189 SHA1Final(temp_hash, &ctx);
00190
00191
00192
00193 for (int x= 0; x < SHA1_DIGEST_LENGTH; x++)
00194 temp_hash[x]= temp_hash[x] ^ scrambled_password[x];
00195
00196
00197 SHA1Init(&ctx);
00198 SHA1Update(&ctx, temp_hash, SHA1_DIGEST_LENGTH);
00199 SHA1Final(scrambled_password_check, &ctx);
00200
00201
00202 return memcmp(local_scrambled_password, scrambled_password_check, SHA1_DIGEST_LENGTH) == 0;
00203 }
00204
00205 bool AuthFile::authenticate(const identifier::User &sctx, const string &password)
00206 {
00207 std::map<std::string, std::string>::const_iterator user= users.find(sctx.username());
00208 if (user == users.end())
00209 return false;
00210
00211 if (sctx.getPasswordType() == identifier::User::MYSQL_HASH)
00212 return verifyMySQLHash(user->second, sctx.getPasswordContext(), password);
00213
00214 if (password == user->second)
00215 return true;
00216
00217 return false;
00218 }
00219
00220 static int init(module::Context &context)
00221 {
00222 const module::option_map &vm= context.getOptions();
00223
00224 AuthFile *auth_file = new AuthFile("auth_file", fs::path(vm["users"].as<string>()));
00225 if (not auth_file->loadFile())
00226 {
00227 errmsg_printf(error::ERROR, _("Could not load auth file: %s\n"),
00228 auth_file->getError().c_str());
00229 delete auth_file;
00230 return 1;
00231 }
00232
00233 context.add(auth_file);
00234 context.registerVariable(new sys_var_const_string_val("users", vm["users"].as<string>()));
00235
00236 return 0;
00237 }
00238
00239
00240 static void init_options(drizzled::module::option_context &context)
00241 {
00242 context("users",
00243 po::value<string>()->default_value(DEFAULT_USERS_FILE.string()),
00244 N_("File to load for usernames and passwords"));
00245 }
00246
00247 }
00248
00249 DRIZZLE_PLUGIN(auth_file::init, NULL, auth_file::init_options);