| Matthias Andreas Benkard | 7b2a3a1 | 2021-08-16 10:57:25 +0200 | [diff] [blame^] | 1 | <?php namespace LeeSherwood\Ejabberd\CommandExecutors; | 
|  | 2 |  | 
|  | 3 | /** | 
|  | 4 | * Dummy command executor for ejabberd | 
|  | 5 | * | 
|  | 6 | * This class implements the command executor interface and just returns some sane boolean values. | 
|  | 7 | * You may want to use this class to test your ejabberd external authentication module is set up correctly | 
|  | 8 | * before you start creating your custom code. | 
|  | 9 | * | 
|  | 10 | * @package LeeSherwood\Ejabberd | 
|  | 11 | * @author Lee Sherwood | 
|  | 12 | */ | 
|  | 13 |  | 
|  | 14 | use \PDO; | 
|  | 15 |  | 
|  | 16 | class mailcowCommandExecutor implements CommandExecutorInterface { | 
|  | 17 |  | 
|  | 18 | /** | 
|  | 19 | * Authenticate a user (login) | 
|  | 20 | * | 
|  | 21 | * @param string $username | 
|  | 22 | * @param string $servername | 
|  | 23 | * @param string $password | 
|  | 24 | * | 
|  | 25 | * @return bool | 
|  | 26 | */ | 
|  | 27 |  | 
|  | 28 | public static function verify_salted_hash($hash, $password, $algo, $salt_length) { | 
|  | 29 | // Decode hash | 
|  | 30 | $dhash = base64_decode($hash); | 
|  | 31 | // Get first n bytes of binary which equals a SSHA hash | 
|  | 32 | $ohash = substr($dhash, 0, $salt_length); | 
|  | 33 | // Remove SSHA hash from decoded hash to get original salt string | 
|  | 34 | $osalt = str_replace($ohash, '', $dhash); | 
|  | 35 | // Check single salted SSHA hash against extracted hash | 
|  | 36 | if (hash_equals(hash($algo, $password . $osalt, true), $ohash)) { | 
|  | 37 | return true; | 
|  | 38 | } | 
|  | 39 | return false; | 
|  | 40 | } | 
|  | 41 |  | 
|  | 42 | public static function verify_hash($hash, $password) { | 
|  | 43 | if (preg_match('/^{(.+)}(.+)/i', $hash, $hash_array)) { | 
|  | 44 | $scheme = strtoupper($hash_array[1]); | 
|  | 45 | $hash = $hash_array[2]; | 
|  | 46 | switch ($scheme) { | 
|  | 47 | case "ARGON2I": | 
|  | 48 | case "ARGON2ID": | 
|  | 49 | case "BLF-CRYPT": | 
|  | 50 | case "CRYPT": | 
|  | 51 | case "DES-CRYPT": | 
|  | 52 | case "MD5-CRYPT": | 
|  | 53 | case "MD5": | 
|  | 54 | case "SHA256-CRYPT": | 
|  | 55 | case "SHA512-CRYPT": | 
|  | 56 | return password_verify($password, $hash); | 
|  | 57 |  | 
|  | 58 | case "CLEAR": | 
|  | 59 | case "CLEARTEXT": | 
|  | 60 | case "PLAIN": | 
|  | 61 | return $password == $hash; | 
|  | 62 |  | 
|  | 63 | case "LDAP-MD5": | 
|  | 64 | $hash = base64_decode($hash); | 
|  | 65 | return hash_equals(hash('md5', $password, true), $hash); | 
|  | 66 |  | 
|  | 67 | case "PBKDF2": | 
|  | 68 | $components = explode('$', $hash); | 
|  | 69 | $salt = $components[2]; | 
|  | 70 | $rounds = $components[3]; | 
|  | 71 | $hash = $components[4]; | 
|  | 72 | return hash_equals(hash_pbkdf2('sha1', $password, $salt, $rounds), $hash); | 
|  | 73 |  | 
|  | 74 | case "PLAIN-MD4": | 
|  | 75 | return hash_equals(hash('md4', $password), $hash); | 
|  | 76 |  | 
|  | 77 | case "PLAIN-MD5": | 
|  | 78 | return md5($password) == $hash; | 
|  | 79 |  | 
|  | 80 | case "PLAIN-TRUNC": | 
|  | 81 | $components = explode('-', $hash); | 
|  | 82 | if (count($components) > 1) { | 
|  | 83 | $trunc_len = $components[0]; | 
|  | 84 | $trunc_password = $components[1]; | 
|  | 85 |  | 
|  | 86 | return substr($password, 0, $trunc_len) == $trunc_password; | 
|  | 87 | } else { | 
|  | 88 | return $password == $hash; | 
|  | 89 | } | 
|  | 90 |  | 
|  | 91 | case "SHA": | 
|  | 92 | case "SHA1": | 
|  | 93 | case "SHA256": | 
|  | 94 | case "SHA512": | 
|  | 95 | // SHA is an alias for SHA1 | 
|  | 96 | $scheme = $scheme == "SHA" ? "sha1" : strtolower($scheme); | 
|  | 97 | $hash = base64_decode($hash); | 
|  | 98 | return hash_equals(hash($scheme, $password, true), $hash); | 
|  | 99 |  | 
|  | 100 | case "SMD5": | 
|  | 101 | return self::verify_salted_hash($hash, $password, 'md5', 16); | 
|  | 102 |  | 
|  | 103 | case "SSHA": | 
|  | 104 | return self::verify_salted_hash($hash, $password, 'sha1', 20); | 
|  | 105 |  | 
|  | 106 | case "SSHA256": | 
|  | 107 | return self::verify_salted_hash($hash, $password, 'sha256', 32); | 
|  | 108 |  | 
|  | 109 | case "SSHA512": | 
|  | 110 | return self::verify_salted_hash($hash, $password, 'sha512', 64); | 
|  | 111 |  | 
|  | 112 | default: | 
|  | 113 | return false; | 
|  | 114 | } | 
|  | 115 | } | 
|  | 116 | return false; | 
|  | 117 | } | 
|  | 118 |  | 
|  | 119 | public function authenticate($username, $servername, $password) | 
|  | 120 | { | 
|  | 121 | $database_type = 'mysql'; | 
|  | 122 | $database_sock = '/var/run/mysqld/mysqld.sock'; | 
|  | 123 | $database_user = '__DBUSER__'; | 
|  | 124 | $database_pass = '__DBPASS__'; | 
|  | 125 | $database_name = '__DBNAME__'; | 
|  | 126 |  | 
|  | 127 | $dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name; | 
|  | 128 | $opt = [ | 
|  | 129 | PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, | 
|  | 130 | PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, | 
|  | 131 | PDO::ATTR_EMULATE_PREPARES   => false | 
|  | 132 | ]; | 
|  | 133 | try { | 
|  | 134 | $pdo = new PDO($dsn, $database_user, $database_pass, $opt); | 
|  | 135 | } | 
|  | 136 | catch (PDOException $e) { | 
|  | 137 | return false; | 
|  | 138 | } | 
|  | 139 | if (!filter_var($username, FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) { | 
|  | 140 | return false; | 
|  | 141 | } | 
|  | 142 | $username = strtolower(trim($username)); | 
|  | 143 | $stmt = $pdo->prepare("SELECT `password` FROM `mailbox` | 
|  | 144 | INNER JOIN domain on mailbox.domain = domain.domain | 
|  | 145 | WHERE `kind` NOT REGEXP 'location|thing|group' | 
|  | 146 | AND `mailbox`.`active`= '1' | 
|  | 147 | AND `domain`.`active`= '1' | 
|  | 148 | AND `domain`.`xmpp` = '1' | 
|  | 149 | AND JSON_UNQUOTE(JSON_VALUE(`mailbox`.`attributes`, '$.xmpp_access')) = '1' | 
|  | 150 | AND CONCAT(`domain`.`xmpp_prefix`, '.', `domain`.`domain`) = :servername | 
|  | 151 | AND `username` = CONCAT(:local_part, '@', `domain`.`domain`)"); | 
|  | 152 | $stmt->execute(array(':local_part' => $username, ':servername' => $servername)); | 
|  | 153 | $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); | 
|  | 154 | foreach ($rows as $row) { | 
|  | 155 | if (self::verify_hash($row['password'], $password) !== false) { | 
|  | 156 | return true; | 
|  | 157 | } | 
|  | 158 | } | 
|  | 159 | return false; | 
|  | 160 | } | 
|  | 161 |  | 
|  | 162 | /** | 
|  | 163 | * Check if a user exists | 
|  | 164 | * | 
|  | 165 | * @param string $username | 
|  | 166 | * @param string $servername | 
|  | 167 | * | 
|  | 168 | * @return bool | 
|  | 169 | */ | 
|  | 170 | public function userExists($username, $servername) | 
|  | 171 | { | 
|  | 172 | return true; | 
|  | 173 | } | 
|  | 174 |  | 
|  | 175 | /** | 
|  | 176 | * Set a password for a user | 
|  | 177 | * | 
|  | 178 | * @param string $username | 
|  | 179 | * @param string $servername | 
|  | 180 | * @param string $password | 
|  | 181 | * | 
|  | 182 | * @return bool | 
|  | 183 | */ | 
|  | 184 | public function setPassword($username, $servername, $password) | 
|  | 185 | { | 
|  | 186 | return false; | 
|  | 187 | } | 
|  | 188 |  | 
|  | 189 | /** | 
|  | 190 | * Register a user | 
|  | 191 | * | 
|  | 192 | * @param string $username | 
|  | 193 | * @param string $servername | 
|  | 194 | * @param string $password | 
|  | 195 | * | 
|  | 196 | * @return bool | 
|  | 197 | */ | 
|  | 198 | public function register($username, $servername, $password) | 
|  | 199 | { | 
|  | 200 | return false; | 
|  | 201 | } | 
|  | 202 |  | 
|  | 203 | /** | 
|  | 204 | * Delete a user | 
|  | 205 | * | 
|  | 206 | * @param string $username | 
|  | 207 | * @param string $servername | 
|  | 208 | * | 
|  | 209 | * @return bool | 
|  | 210 | */ | 
|  | 211 | public function removeUser($username, $servername) | 
|  | 212 | { | 
|  | 213 | return false; | 
|  | 214 | } | 
|  | 215 |  | 
|  | 216 | /** | 
|  | 217 | * Delete a user with password validation | 
|  | 218 | * | 
|  | 219 | * @param string $username | 
|  | 220 | * @param string $servername | 
|  | 221 | * @param string $password | 
|  | 222 | * | 
|  | 223 | * @return bool | 
|  | 224 | */ | 
|  | 225 | public function removeUserWithPassword($username, $servername, $password) | 
|  | 226 | { | 
|  | 227 | return false; | 
|  | 228 | } | 
|  | 229 | } |