Drizzled Public API Documentation

auth_file.cc
1 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3  *
4  * Copyright (C) 2010 Eric Day
5  *
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.
9  *
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.
14  *
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
18  */
19 
20 #include <config.h>
21 
22 #include <fstream>
23 #include <map>
24 #include <string>
25 #include <iostream>
26 
27 #include <boost/program_options.hpp>
28 #include <boost/filesystem.hpp>
29 
30 #include <drizzled/item.h>
31 #include <drizzled/configmake.h>
32 #include <drizzled/plugin/authentication.h>
33 #include <drizzled/identifier.h>
34 #include <drizzled/util/convert.h>
37 
38 namespace po= boost::program_options;
39 namespace fs= boost::filesystem;
40 
41 using namespace std;
42 using namespace drizzled;
43 
44 namespace auth_file {
45 
46 static const fs::path DEFAULT_USERS_FILE= SYSCONFDIR "/drizzle.users";
47 typedef std::map<string, string> users_t;
48 bool updateUsersFile(Session *, set_var *);
49 bool parseUsersFile(std::string, users_t&);
50 
51 class AuthFile : public plugin::Authentication
52 {
53 public:
54  AuthFile(std::string users_file_arg);
55 
59  const string& getError() const;
60 
61  std::string& getUsersFile();
62  bool setUsersFile(std::string& usersFile);
63 
64 private:
65 
69  bool authenticate(const identifier::User &sctx, const string &password);
70 
82  bool verifyMySQLHash(const string &password,
83  const string &scramble_bytes,
84  const string &scrambled_password);
85 
86  string error;
87  fs::path users_file;
88  std::string sysvar_users_file;
89 
93  users_t users;
98  void setUsers(users_t);
103  void clearUsers();
104 };
105 AuthFile *auth_file= NULL;
106 
107 AuthFile::AuthFile(std::string users_file_arg) :
108  plugin::Authentication("auth_file"),
109  users_file(users_file_arg), sysvar_users_file(users_file_arg)
110 {
111 }
112 
113 const string& AuthFile::getError() const
114 {
115  return error;
116 }
117 
118 std::string& AuthFile::getUsersFile()
119 {
120  return sysvar_users_file;
121 }
122 
123 bool AuthFile::setUsersFile(std::string& usersFile)
124 {
125  if (usersFile.empty())
126  {
127  errmsg_printf(error::ERROR, _("users file cannot be an empty string"));
128  return false; // error
129  }
130  users_t users_dummy;
131  if(parseUsersFile(usersFile, users_dummy))
132  {
133  this->clearUsers();
134  this->setUsers(users_dummy);
135  sysvar_users_file= usersFile;
136  fs::path newUsersFile(getUsersFile());
137  users_file= newUsersFile;
138  return true; //success
139  }
140  return false; // error
141 }
142 
143 bool AuthFile::verifyMySQLHash(const string &password,
144  const string &scramble_bytes,
145  const string &scrambled_password)
146 {
147  if (scramble_bytes.size() != SHA1_DIGEST_LENGTH || scrambled_password.size() != SHA1_DIGEST_LENGTH)
148  {
149  return false;
150  }
151 
152  SHA1_CTX ctx;
153  uint8_t local_scrambled_password[SHA1_DIGEST_LENGTH];
154  uint8_t temp_hash[SHA1_DIGEST_LENGTH];
155  uint8_t scrambled_password_check[SHA1_DIGEST_LENGTH];
156 
157  /* Generate the double SHA1 hash for the password stored locally first. */
158  SHA1Init(&ctx);
159  SHA1Update(&ctx, reinterpret_cast<const uint8_t *>(password.c_str()), password.size());
160  SHA1Final(temp_hash, &ctx);
161 
162  SHA1Init(&ctx);
163  SHA1Update(&ctx, temp_hash, SHA1_DIGEST_LENGTH);
164  SHA1Final(local_scrambled_password, &ctx);
165 
166  /* Hash the scramble that was sent to client with the local password. */
167  SHA1Init(&ctx);
168  SHA1Update(&ctx, reinterpret_cast<const uint8_t*>(scramble_bytes.c_str()), SHA1_DIGEST_LENGTH);
169  SHA1Update(&ctx, local_scrambled_password, SHA1_DIGEST_LENGTH);
170  SHA1Final(temp_hash, &ctx);
171 
172  /* Next, XOR the result with what the client sent to get the original
173  single-hashed password. */
174  for (int x= 0; x < SHA1_DIGEST_LENGTH; x++)
175  temp_hash[x]= temp_hash[x] ^ scrambled_password[x];
176 
177  /* Hash this result once more to get the double-hashed password again. */
178  SHA1Init(&ctx);
179  SHA1Update(&ctx, temp_hash, SHA1_DIGEST_LENGTH);
180  SHA1Final(scrambled_password_check, &ctx);
181 
182  /* These should match for a successful auth. */
183  return memcmp(local_scrambled_password, scrambled_password_check, SHA1_DIGEST_LENGTH) == 0;
184 }
185 
186 bool AuthFile::authenticate(const identifier::User &sctx, const string &password)
187 {
188  string* user= find_ptr(users, sctx.username());
189  if (not user)
190  return false;
191  return sctx.getPasswordType() == identifier::User::MYSQL_HASH
192  ? verifyMySQLHash(*user, sctx.getPasswordContext(), password)
193  : password == *user;
194 }
195 
196 void AuthFile::setUsers(users_t users_dummy)
197 {
198  users.insert(users_dummy.begin(), users_dummy.end());
199 }
200 
202 {
203  users.clear();
204 }
205 
211 bool updateUsersFile(Session *, set_var* var)
212 {
213  if (not var->value->str_value.empty())
214  {
215  std::string newUsersFile(var->value->str_value.data());
216  if (auth_file->setUsersFile(newUsersFile))
217  return false; //success
218  else
219  return true; // error
220  }
221  errmsg_printf(error::ERROR, _("auth_file file cannot be NULL"));
222  return true; // error
223 }
224 
231 bool parseUsersFile(std::string new_users_file, users_t& users_dummy)
232 {
233  ifstream file(new_users_file.c_str());
234 
235  if (!file.is_open())
236  {
237  string error_msg= "Could not open users file: " + new_users_file;
238  errmsg_printf(error::ERROR, "%s", _(error_msg.c_str()));
239  return false;
240  }
241 
242  string line;
243  try
244  {
245  while (getline(file, line))
246  {
247  /* Ignore blank lines and lines starting with '#'. */
248  if (line.empty() || line[line.find_first_not_of(" \t")] == '#')
249  continue;
250 
251  string username;
252  string password;
253  size_t password_offset = line.find(":");
254  if (password_offset == string::npos)
255  username = line;
256  else
257  {
258  username = string(line, 0, password_offset);
259  password = string(line, password_offset + 1);
260  }
261 
262  if (not users_dummy.insert(pair<string, string>(username, password)).second)
263  {
264  string error_msg= "Duplicate entry found in users file: " + username;
265  errmsg_printf(error::ERROR, "%s", _(error_msg.c_str()));
266  return false;
267  }
268  }
269  return true;
270  }
271  catch (const std::exception &e)
272  {
273  /* On any non-EOF break, unparseable line */
274  string error_msg= "Unable to parse users file " + new_users_file + ":" + e.what();
275  errmsg_printf(error::ERROR, "%s", _(error_msg.c_str()));
276  return false;
277  }
278 }
279 
280 static int init(module::Context &context)
281 {
282  const module::option_map &vm= context.getOptions();
283 
284  auth_file= new AuthFile(vm["users"].as<string>());
285  if (!auth_file->setUsersFile(auth_file->getUsersFile()))
286  {
287  errmsg_printf(error::ERROR, _("Could not load auth file: %s\n"), auth_file->getError().c_str());
288  delete auth_file;
289  return 1;
290  }
291 
292  context.add(auth_file);
293  context.registerVariable(new sys_var_std_string("users", auth_file->getUsersFile(), NULL, &updateUsersFile));
294 
295  return 0;
296 }
297 
298 
299 static void init_options(drizzled::module::option_context &context)
300 {
301  context("users",
302  po::value<string>()->default_value(DEFAULT_USERS_FILE.string()),
303  N_("File to load for usernames and passwords"));
304 }
305 
306 } /* namespace auth_file */
307 
308 DRIZZLE_DECLARE_PLUGIN
309 {
310  DRIZZLE_VERSION_ID,
311  "auth_file",
312  "0.1",
313  "Eric Day",
314  N_("Authentication against a plain text file"),
315  PLUGIN_LICENSE_GPL,
316  auth_file::init,
317  NULL,
318  auth_file::init_options
319 }
320 DRIZZLE_DECLARE_PLUGIN_END;
bool verifyMySQLHash(const string &password, const string &scramble_bytes, const string &scrambled_password)
Definition: auth_file.cc:143
A set of Session members describing the current authenticated user.
Definition: user.h:34
SHA1 Declarations.
An Proxy Wrapper around boost::program_options::variables_map.
bool authenticate(const identifier::User &sctx, const string &password)
Definition: auth_file.cc:186
void setUsers(users_t)
Definition: auth_file.cc:196
String str_value
Definition: item.h:107
const string & getError() const
Definition: auth_file.cc:113