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 static const fs::path DEFAULT_USERS_FILE= SYSCONFDIR "/drizzle.users";
00046
00047 class AuthFile : public plugin::Authentication
00048 {
00049 public:
00050 AuthFile(fs::path users_file_arg);
00051
00055 const string& getError() const;
00056
00063 bool loadFile();
00064
00065 private:
00066
00070 bool authenticate(const identifier::User &sctx, const string &password);
00071
00083 bool verifyMySQLHash(const string &password,
00084 const string &scramble_bytes,
00085 const string &scrambled_password);
00086
00087 string error;
00088 const fs::path users_file;
00089
00093 typedef std::map<string, string> users_t;
00094 users_t users;
00095 };
00096
00097 AuthFile::AuthFile(fs::path users_file_arg) :
00098 plugin::Authentication("auth_file"),
00099 users_file(users_file_arg)
00100 {
00101 }
00102
00103 const string& AuthFile::getError() const
00104 {
00105 return error;
00106 }
00107
00108 bool AuthFile::loadFile()
00109 {
00110 ifstream file(users_file.string().c_str());
00111
00112 if (!file.is_open())
00113 {
00114 error = "Could not open users file: " + users_file.string();
00115 return false;
00116 }
00117
00118 string line;
00119 while (getline(file, line))
00120 {
00121
00122 if (line.empty() || line[line.find_first_not_of(" \t")] == '#')
00123 continue;
00124
00125 string username;
00126 string password;
00127 size_t password_offset = line.find(":");
00128 if (password_offset == string::npos)
00129 username = line;
00130 else
00131 {
00132 username = string(line, 0, password_offset);
00133 password = string(line, password_offset + 1);
00134 }
00135
00136 if (not users.insert(pair<string, string>(username, password)).second)
00137 {
00138 error = "Duplicate entry found in users file: " + username;
00139 return false;
00140 }
00141 }
00142 return true;
00143 }
00144
00145 bool AuthFile::verifyMySQLHash(const string &password,
00146 const string &scramble_bytes,
00147 const string &scrambled_password)
00148 {
00149 if (scramble_bytes.size() != SHA1_DIGEST_LENGTH || scrambled_password.size() != SHA1_DIGEST_LENGTH)
00150 {
00151 return false;
00152 }
00153
00154 SHA1_CTX ctx;
00155 uint8_t local_scrambled_password[SHA1_DIGEST_LENGTH];
00156 uint8_t temp_hash[SHA1_DIGEST_LENGTH];
00157 uint8_t scrambled_password_check[SHA1_DIGEST_LENGTH];
00158
00159
00160 SHA1Init(&ctx);
00161 SHA1Update(&ctx, reinterpret_cast<const uint8_t *>(password.c_str()), password.size());
00162 SHA1Final(temp_hash, &ctx);
00163
00164 SHA1Init(&ctx);
00165 SHA1Update(&ctx, temp_hash, SHA1_DIGEST_LENGTH);
00166 SHA1Final(local_scrambled_password, &ctx);
00167
00168
00169 SHA1Init(&ctx);
00170 SHA1Update(&ctx, reinterpret_cast<const uint8_t*>(scramble_bytes.c_str()), SHA1_DIGEST_LENGTH);
00171 SHA1Update(&ctx, local_scrambled_password, SHA1_DIGEST_LENGTH);
00172 SHA1Final(temp_hash, &ctx);
00173
00174
00175
00176 for (int x= 0; x < SHA1_DIGEST_LENGTH; x++)
00177 temp_hash[x]= temp_hash[x] ^ scrambled_password[x];
00178
00179
00180 SHA1Init(&ctx);
00181 SHA1Update(&ctx, temp_hash, SHA1_DIGEST_LENGTH);
00182 SHA1Final(scrambled_password_check, &ctx);
00183
00184
00185 return memcmp(local_scrambled_password, scrambled_password_check, SHA1_DIGEST_LENGTH) == 0;
00186 }
00187
00188 bool AuthFile::authenticate(const identifier::User &sctx, const string &password)
00189 {
00190 string* user= find_ptr(users, sctx.username());
00191 if (not user)
00192 return false;
00193 return sctx.getPasswordType() == identifier::User::MYSQL_HASH
00194 ? verifyMySQLHash(*user, sctx.getPasswordContext(), password)
00195 : password == *user;
00196 }
00197
00198 static int init(module::Context &context)
00199 {
00200 const module::option_map &vm= context.getOptions();
00201
00202 AuthFile *auth_file = new AuthFile(fs::path(vm["users"].as<string>()));
00203 if (not auth_file->loadFile())
00204 {
00205 errmsg_printf(error::ERROR, _("Could not load auth file: %s\n"), auth_file->getError().c_str());
00206 delete auth_file;
00207 return 1;
00208 }
00209
00210 context.add(auth_file);
00211 context.registerVariable(new sys_var_const_string_val("users", vm["users"].as<string>()));
00212
00213 return 0;
00214 }
00215
00216
00217 static void init_options(drizzled::module::option_context &context)
00218 {
00219 context("users",
00220 po::value<string>()->default_value(DEFAULT_USERS_FILE.string()),
00221 N_("File to load for usernames and passwords"));
00222 }
00223
00224 }
00225
00226 DRIZZLE_PLUGIN(auth_file::init, NULL, auth_file::init_options);