blob: 390783af2b0b3f5701fd5568901f16169415a0a0 [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?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
14use \PDO;
15
16class 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}