3 * StatusNet, the distributed open-source microblogging tool
5 * Plugin to use crypt() for user password hashes
9 * LICENCE: This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 * @author Mikael Nordfeldth <mmn@hethane.se>
25 * @copyright 2012 StatusNet, Inc.
26 * @copyright 2012 Free Software Foundation, Inc http://www.fsf.org
27 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
28 * @link http://status.net/
31 if (!defined('STATUSNET')) {
36 * Plugin to use crypt() for user password hashes
40 * @author Mikael Nordfeldth <mmn@hethane.se>
41 * @copyright 2012 StatusNet, Inc.
42 * @copyright 2012 Free Software Foundation, Inc http://www.fsf.org
43 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
44 * @link http://status.net/
46 class AuthCryptPlugin extends AuthenticationPlugin
48 public $hash; // defaults to SHA512, i.e. '$6$', in onInitializePlugin()
49 public $hostile; // if true, password login means change password to crypt() hash
50 public $overwrite; // if true, password change means always store crypt() hash
56 function checkPassword($username, $password) {
57 $user = User::staticGet('nickname', common_canonical_nickname($username));
59 // crypt cuts the second parameter to its appropriate length based on hash scheme
60 if (!empty($user) && $user->password === crypt($password, $user->password)) {
63 // if we're hostile, check the password against old-style hashing
64 if (!empty($user) && $this->hostile && $user->password === md5($password . $user->id)) {
65 // update password hash entry to crypt() compatible
66 $this->changePassword($username, $password, $password);
73 // $oldpassword is already verified when calling this function... shouldn't this be private?!
74 function changePassword($username, $oldpassword, $newpassword) {
75 if (!$this->password_changeable) {
79 $user = User::staticGet('nickname', $username);
83 $original = clone($user);
85 // a new, unique salt per new record stored... but common_good_rand should be more diverse than hexdec
86 $user->password = crypt($newpassword, $this->hash . common_good_rand(CRYPT_SALT_LENGTH));
88 return (true === $user->validate() && $user->update($original));
95 function onInitializePlugin() {
96 $this->provider_name = 'crypt'; // we don't actually use the provider_name
98 if (!isset($this->hash)) {
99 $this->hash = '$6$'; // SHA512 supported on all systems since PHP 5.3.2
101 if (!isset($this->hostile)) {
102 $this->hostile = false; // overwrite old style password hashes?
104 if (!isset($this->overwrite)) {
105 $this->overwrite = true; // overwrite old style password hashes?
109 function onStartChangePassword($user, $oldpassword, $newpassword) {
110 if (!$this->checkPassword($user->nickname, $oldpassword)) {
111 // if we ARE in overwrite mode, test password with common_check_user
112 if (!$this->overwrite || !common_check_user($user->nickname, $oldpassword)) {
113 // either we're not in overwrite mode, or the password was incorrect
114 return !$this->authoritative;
116 // oldpassword was apparently ok
118 $changed = $this->changePassword($user->nickname, $oldpassword, $newpassword);
120 return (!$changed && empty($this->authoritative));
123 function onStartCheckPassword($nickname, $password, &$authenticatedUser) {
124 $authenticatedUser = $this->checkPassword($nickname, $password);
125 return (empty($authenticatedUser) && empty($this->authoritative));
128 function onCheckSchema() {
129 // we only use the User database, so default AuthenticationPlugin stuff can be ignored
133 function onUserDeleteRelated($user, &$tables) {
134 // not using User_username table, so no need to add it here.
138 function onPluginVersion(&$versions)
140 $versions[] = array('name' => 'AuthCrypt',
141 'version' => STATUSNET_VERSION,
142 'author' => 'Mikael Nordfeldth',
143 'homepage' => 'http://status.net/wiki/Plugin:AuthCrypt',
145 // TRANS: Plugin description.
146 _m('Authentication and password hashing with crypt()'));