~drizzle-trunk/drizzle/development

1432.1.1 by Eric Day
Added auth_file plugin.
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
2173.2.1 by Monty Taylor
Fixes incorrect usage of include
20
#include <config.h>
1432.1.1 by Eric Day
Added auth_file plugin.
21
22
#include <fstream>
23
#include <map>
24
#include <string>
1878.3.1 by Monty Taylor
Split set_var.* into sys_var.* and set_var.*
25
#include <iostream>
26
27
#include <boost/program_options.hpp>
1955.2.1 by vjsamuel1990 at gmail
Merge fix for bug-664049
28
#include <boost/filesystem.hpp>
1432.1.1 by Eric Day
Added auth_file plugin.
29
2173.2.1 by Monty Taylor
Fixes incorrect usage of include
30
#include <drizzled/configmake.h>
31
#include <drizzled/plugin/authentication.h>
32
#include <drizzled/identifier.h>
33
#include <drizzled/util/convert.h>
34
#include <drizzled/algorithm/sha1.h>
35
#include <drizzled/module/option_map.h>
1432.1.1 by Eric Day
Added auth_file plugin.
36
1711.5.1 by Vijay Samuel
Merge precautionary fix for auth_file to prevent plugin from breaking when my_getopt is removed.
37
namespace po= boost::program_options;
1955.2.1 by vjsamuel1990 at gmail
Merge fix for bug-664049
38
namespace fs= boost::filesystem;
39
1432.1.1 by Eric Day
Added auth_file plugin.
40
using namespace std;
41
using namespace drizzled;
42
2318.6.18 by Olaf van der Spek
Refactor
43
namespace auth_file {
1432.1.1 by Eric Day
Added auth_file plugin.
44
1955.2.1 by vjsamuel1990 at gmail
Merge fix for bug-664049
45
static const fs::path DEFAULT_USERS_FILE= SYSCONFDIR "/drizzle.users";
1432.1.1 by Eric Day
Added auth_file plugin.
46
2318.6.18 by Olaf van der Spek
Refactor
47
class AuthFile : public plugin::Authentication
1432.1.1 by Eric Day
Added auth_file plugin.
48
{
49
public:
2318.6.18 by Olaf van der Spek
Refactor
50
  AuthFile(fs::path users_file_arg);
1432.1.1 by Eric Day
Added auth_file plugin.
51
52
  /**
53
   * Retrieve the last error encountered in the class.
54
   */
2318.6.18 by Olaf van der Spek
Refactor
55
  const string& getError() const;
1432.1.1 by Eric Day
Added auth_file plugin.
56
57
  /**
58
   * Load the users file into a map cache.
59
   *
60
   * @return True on success, false on error. If false is returned an error
61
   *  is set and can be retrieved with getError().
62
   */
2318.6.18 by Olaf van der Spek
Refactor
63
  bool loadFile();
1432.1.1 by Eric Day
Added auth_file plugin.
64
65
private:
66
67
  /**
68
   * Base class method to check authentication for a user.
69
   */
2008.1.1 by Brian Aker
Adding user identifier that makes use of a shared ptr to handle concurrency
70
  bool authenticate(const identifier::User &sctx, const string &password);
1432.1.1 by Eric Day
Added auth_file plugin.
71
72
  /**
73
   * Verify the local and remote scrambled password match using the MySQL
74
   * hashing algorithm.
75
   *
76
   * @param[in] password Plain text password that is stored locally.
77
   * @param[in] scramble_bytes The random bytes that the server sent to the
78
   *  client for scrambling the password.
79
   * @param[in] scrambled_password The result of the client scrambling the
80
   *  password remotely.
81
   * @return True if the password matched, false if not.
82
   */
83
  bool verifyMySQLHash(const string &password,
84
                       const string &scramble_bytes,
85
                       const string &scrambled_password);
86
87
  string error;
2318.6.18 by Olaf van der Spek
Refactor
88
  const fs::path users_file;
1432.1.1 by Eric Day
Added auth_file plugin.
89
90
  /**
91
   * Cache or username:password entries from the file.
92
   */
2318.6.18 by Olaf van der Spek
Refactor
93
  typedef std::map<string, string> users_t;
94
  users_t users;
1432.1.1 by Eric Day
Added auth_file plugin.
95
};
96
2318.6.18 by Olaf van der Spek
Refactor
97
AuthFile::AuthFile(fs::path users_file_arg) :
98
  plugin::Authentication("auth_file"),
99
  users_file(users_file_arg)
1432.1.1 by Eric Day
Added auth_file plugin.
100
{
101
}
102
2318.6.18 by Olaf van der Spek
Refactor
103
const string& AuthFile::getError() const
1432.1.1 by Eric Day
Added auth_file plugin.
104
{
105
  return error;
106
}
107
2318.6.18 by Olaf van der Spek
Refactor
108
bool AuthFile::loadFile()
1432.1.1 by Eric Day
Added auth_file plugin.
109
{
1955.2.1 by vjsamuel1990 at gmail
Merge fix for bug-664049
110
  ifstream file(users_file.string().c_str());
1432.1.1 by Eric Day
Added auth_file plugin.
111
112
  if (!file.is_open())
113
  {
2318.6.18 by Olaf van der Spek
Refactor
114
    error = "Could not open users file: " + users_file.string();
1432.1.1 by Eric Day
Added auth_file plugin.
115
    return false;
116
  }
117
2318.6.18 by Olaf van der Spek
Refactor
118
  string line;
119
  while (getline(file, line))
1432.1.1 by Eric Day
Added auth_file plugin.
120
  {
121
    /* Ignore blank lines and lines starting with '#'. */
2318.6.18 by Olaf van der Spek
Refactor
122
    if (line.empty() || line[line.find_first_not_of(" \t")] == '#')
1432.1.1 by Eric Day
Added auth_file plugin.
123
      continue;
124
125
    string username;
126
    string password;
127
    size_t password_offset = line.find(":");
128
    if (password_offset == string::npos)
129
      username = line;
130
    else
131
    {
132
      username = string(line, 0, password_offset);
133
      password = string(line, password_offset + 1);
134
    }
135
2318.6.18 by Olaf van der Spek
Refactor
136
    if (not users.insert(pair<string, string>(username, password)).second)
1432.1.1 by Eric Day
Added auth_file plugin.
137
    {
2318.6.18 by Olaf van der Spek
Refactor
138
      error = "Duplicate entry found in users file: " + username;
1432.1.1 by Eric Day
Added auth_file plugin.
139
      return false;
140
    }
141
  }
142
  return true;
143
}
144
145
bool AuthFile::verifyMySQLHash(const string &password,
146
                               const string &scramble_bytes,
147
                               const string &scrambled_password)
148
{
2318.6.18 by Olaf van der Spek
Refactor
149
  if (scramble_bytes.size() != SHA1_DIGEST_LENGTH || scrambled_password.size() != SHA1_DIGEST_LENGTH)
1432.1.1 by Eric Day
Added auth_file plugin.
150
  {
151
    return false;
152
  }
153
154
  SHA1_CTX ctx;
155
  uint8_t local_scrambled_password[SHA1_DIGEST_LENGTH];
156
  uint8_t temp_hash[SHA1_DIGEST_LENGTH];
157
  uint8_t scrambled_password_check[SHA1_DIGEST_LENGTH];
158
159
  /* Generate the double SHA1 hash for the password stored locally first. */
160
  SHA1Init(&ctx);
2318.6.18 by Olaf van der Spek
Refactor
161
  SHA1Update(&ctx, reinterpret_cast<const uint8_t *>(password.c_str()), password.size());
1432.1.1 by Eric Day
Added auth_file plugin.
162
  SHA1Final(temp_hash, &ctx);
163
164
  SHA1Init(&ctx);
165
  SHA1Update(&ctx, temp_hash, SHA1_DIGEST_LENGTH);
166
  SHA1Final(local_scrambled_password, &ctx);
167
168
  /* Hash the scramble that was sent to client with the local password. */
169
  SHA1Init(&ctx);
2318.6.18 by Olaf van der Spek
Refactor
170
  SHA1Update(&ctx, reinterpret_cast<const uint8_t*>(scramble_bytes.c_str()), SHA1_DIGEST_LENGTH);
1432.1.1 by Eric Day
Added auth_file plugin.
171
  SHA1Update(&ctx, local_scrambled_password, SHA1_DIGEST_LENGTH);
172
  SHA1Final(temp_hash, &ctx);
173
174
  /* Next, XOR the result with what the client sent to get the original
175
     single-hashed password. */
176
  for (int x= 0; x < SHA1_DIGEST_LENGTH; x++)
177
    temp_hash[x]= temp_hash[x] ^ scrambled_password[x];
178
179
  /* Hash this result once more to get the double-hashed password again. */
180
  SHA1Init(&ctx);
181
  SHA1Update(&ctx, temp_hash, SHA1_DIGEST_LENGTH);
182
  SHA1Final(scrambled_password_check, &ctx);
183
184
  /* These should match for a successful auth. */
185
  return memcmp(local_scrambled_password, scrambled_password_check, SHA1_DIGEST_LENGTH) == 0;
186
}
187
2008.1.1 by Brian Aker
Adding user identifier that makes use of a shared ptr to handle concurrency
188
bool AuthFile::authenticate(const identifier::User &sctx, const string &password)
1432.1.1 by Eric Day
Added auth_file plugin.
189
{
2318.6.18 by Olaf van der Spek
Refactor
190
  string* user= find_ptr(users, sctx.username());
191
  if (not user)
1432.1.1 by Eric Day
Added auth_file plugin.
192
    return false;
2318.6.18 by Olaf van der Spek
Refactor
193
  return sctx.getPasswordType() == identifier::User::MYSQL_HASH
194
    ? verifyMySQLHash(*user, sctx.getPasswordContext(), password)
195
    : password == *user;
1432.1.1 by Eric Day
Added auth_file plugin.
196
}
197
1530.2.6 by Monty Taylor
Moved plugin::Context to module::Context.
198
static int init(module::Context &context)
1432.1.1 by Eric Day
Added auth_file plugin.
199
{
1711.5.1 by Vijay Samuel
Merge precautionary fix for auth_file to prevent plugin from breaking when my_getopt is removed.
200
  const module::option_map &vm= context.getOptions();
201
2318.6.18 by Olaf van der Spek
Refactor
202
  AuthFile *auth_file = new AuthFile(fs::path(vm["users"].as<string>()));
2126.3.3 by Brian Aker
Merge in error message rework. Many error messages are fixed in this patch.
203
  if (not auth_file->loadFile())
1432.1.1 by Eric Day
Added auth_file plugin.
204
  {
2318.6.18 by Olaf van der Spek
Refactor
205
    errmsg_printf(error::ERROR, _("Could not load auth file: %s\n"), auth_file->getError().c_str());
1432.1.1 by Eric Day
Added auth_file plugin.
206
    delete auth_file;
207
    return 1;
208
  }
209
210
  context.add(auth_file);
1863.1.5 by Monty Taylor
Fixed up the auth_file conversion.
211
  context.registerVariable(new sys_var_const_string_val("users", vm["users"].as<string>()));
2126.3.3 by Brian Aker
Merge in error message rework. Many error messages are fixed in this patch.
212
1432.1.1 by Eric Day
Added auth_file plugin.
213
  return 0;
214
}
215
216
1711.5.1 by Vijay Samuel
Merge precautionary fix for auth_file to prevent plugin from breaking when my_getopt is removed.
217
static void init_options(drizzled::module::option_context &context)
218
{
219
  context("users", 
1955.2.1 by vjsamuel1990 at gmail
Merge fix for bug-664049
220
          po::value<string>()->default_value(DEFAULT_USERS_FILE.string()),
1711.5.1 by Vijay Samuel
Merge precautionary fix for auth_file to prevent plugin from breaking when my_getopt is removed.
221
          N_("File to load for usernames and passwords"));
222
}
223
1432.1.1 by Eric Day
Added auth_file plugin.
224
} /* namespace auth_file */
225
1863.1.2 by Monty Taylor
auth_file
226
DRIZZLE_PLUGIN(auth_file::init, NULL, auth_file::init_options);