blob: bb88ea34c0898c0b2aff4b6d25b8ad6c1ff847d3 [file] [log] [blame]
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +01001<?php
2function domain_admin($_action, $_data = null) {
3 global $pdo;
4 global $lang;
5 $_data_log = $_data;
6 !isset($_data_log['password']) ?: $_data_log['password'] = '*';
7 !isset($_data_log['password2']) ?: $_data_log['password2'] = '*';
8 !isset($_data_log['user_old_pass']) ?: $_data_log['user_old_pass'] = '*';
9 !isset($_data_log['user_new_pass']) ?: $_data_log['user_new_pass'] = '*';
10 !isset($_data_log['user_new_pass2']) ?: $_data_log['user_new_pass2'] = '*';
11 switch ($_action) {
12 case 'add':
13 $username = strtolower(trim($_data['username']));
14 $password = $_data['password'];
15 $password2 = $_data['password2'];
16 $domains = (array)$_data['domains'];
17 $active = intval($_data['active']);
18 if ($_SESSION['mailcow_cc_role'] != "admin") {
19 $_SESSION['return'][] = array(
20 'type' => 'danger',
21 'log' => array(__FUNCTION__, $_action, $_data_log),
22 'msg' => 'access_denied'
23 );
24 return false;
25 }
26 if (empty($domains)) {
27 $_SESSION['return'][] = array(
28 'type' => 'danger',
29 'log' => array(__FUNCTION__, $_action, $_data_log),
30 'msg' => 'domain_invalid'
31 );
32 return false;
33 }
34 if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username) || $username == 'API') {
35 $_SESSION['return'][] = array(
36 'type' => 'danger',
37 'log' => array(__FUNCTION__, $_action, $_data_log),
38 'msg' => array('username_invalid', $username)
39 );
40 return false;
41 }
42
43 $stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
44 WHERE `username` = :username");
45 $stmt->execute(array(':username' => $username));
46 $num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
47
48 $stmt = $pdo->prepare("SELECT `username` FROM `admin`
49 WHERE `username` = :username");
50 $stmt->execute(array(':username' => $username));
51 $num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
52
53 $stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
54 WHERE `username` = :username");
55 $stmt->execute(array(':username' => $username));
56 $num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
57
58 foreach ($num_results as $num_results_each) {
59 if ($num_results_each != 0) {
60 $_SESSION['return'][] = array(
61 'type' => 'danger',
62 'log' => array(__FUNCTION__, $_action, $_data_log),
63 'msg' => array('object_exists', htmlspecialchars($username))
64 );
65 return false;
66 }
67 }
68 if (password_check($password, $password2) !== true) {
69 continue;
70 }
71 $password_hashed = hash_password($password);
72 $valid_domains = 0;
73 foreach ($domains as $domain) {
74 if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
75 $_SESSION['return'][] = array(
76 'type' => 'danger',
77 'log' => array(__FUNCTION__, $_action, $_data_log),
78 'msg' => array('domain_invalid', htmlspecialchars($domain))
79 );
80 continue;
81 }
82 $valid_domains++;
83 $stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
84 VALUES (:username, :domain, :created, :active)");
85 $stmt->execute(array(
86 ':username' => $username,
87 ':domain' => $domain,
88 ':created' => date('Y-m-d H:i:s'),
89 ':active' => $active
90 ));
91 }
92 if ($valid_domains != 0) {
93 $stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
94 VALUES (:username, :password_hashed, '0', :active)");
95 $stmt->execute(array(
96 ':username' => $username,
97 ':password_hashed' => $password_hashed,
98 ':active' => $active
99 ));
100 }
101 $stmt = $pdo->prepare("INSERT INTO `da_acl` (`username`) VALUES (:username)");
102 $stmt->execute(array(
103 ':username' => $username
104 ));
105 $_SESSION['return'][] = array(
106 'type' => 'success',
107 'log' => array(__FUNCTION__, $_action, $_data_log),
108 'msg' => array('domain_admin_added', htmlspecialchars($username))
109 );
110 break;
111 case 'edit':
112 if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
113 $_SESSION['return'][] = array(
114 'type' => 'danger',
115 'log' => array(__FUNCTION__, $_action, $_data_log),
116 'msg' => 'access_denied'
117 );
118 return false;
119 }
120 // Administrator
121 if ($_SESSION['mailcow_cc_role'] == "admin") {
122 if (!is_array($_data['username'])) {
123 $usernames = array();
124 $usernames[] = $_data['username'];
125 }
126 else {
127 $usernames = $_data['username'];
128 }
129 foreach ($usernames as $username) {
130 $is_now = domain_admin('details', $username);
131 $domains = (isset($_data['domains'])) ? (array)$_data['domains'] : null;
132 if (!empty($is_now)) {
133 $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
134 $domains = (!empty($domains)) ? $domains : $is_now['selected_domains'];
135 $username_new = (!empty($_data['username_new'])) ? $_data['username_new'] : $is_now['username'];
136 }
137 else {
138 $_SESSION['return'][] = array(
139 'type' => 'danger',
140 'log' => array(__FUNCTION__, $_action, $_data_log),
141 'msg' => 'access_denied'
142 );
143 continue;
144 }
145 $password = $_data['password'];
146 $password2 = $_data['password2'];
147 if (!empty($domains)) {
148 foreach ($domains as $domain) {
149 if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
150 $_SESSION['return'][] = array(
151 'type' => 'danger',
152 'log' => array(__FUNCTION__, $_action, $_data_log),
153 'msg' => array('domain_invalid', htmlspecialchars($domain))
154 );
155 continue 2;
156 }
157 }
158 }
159 if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) {
160 $_SESSION['return'][] = array(
161 'type' => 'danger',
162 'log' => array(__FUNCTION__, $_action, $_data_log),
163 'msg' => array('username_invalid', $username_new)
164 );
165 continue;
166 }
167 if ($username_new != $username) {
168 if (!empty(domain_admin('details', $username_new)['username'])) {
169 $_SESSION['return'][] = array(
170 'type' => 'danger',
171 'log' => array(__FUNCTION__, $_action, $_data_log),
172 'msg' => array('username_invalid', $username_new)
173 );
174 continue;
175 }
176 }
177 $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
178 $stmt->execute(array(
179 ':username' => $username,
180 ));
181 $stmt = $pdo->prepare("UPDATE `da_acl` SET `username` = :username_new WHERE `username` = :username");
182 $stmt->execute(array(
183 ':username_new' => $username_new,
184 ':username' => $username
185 ));
186 if (!empty($domains)) {
187 foreach ($domains as $domain) {
188 $stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
189 VALUES (:username_new, :domain, :created, :active)");
190 $stmt->execute(array(
191 ':username_new' => $username_new,
192 ':domain' => $domain,
193 ':created' => date('Y-m-d H:i:s'),
194 ':active' => $active
195 ));
196 }
197 }
198 if (!empty($password)) {
199 if (password_check($password, $password2) !== true) {
200 return false;
201 }
202 $password_hashed = hash_password($password);
203 $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username");
204 $stmt->execute(array(
205 ':password_hashed' => $password_hashed,
206 ':username_new' => $username_new,
207 ':username' => $username,
208 ':active' => $active
209 ));
210 if (isset($_data['disable_tfa'])) {
211 $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
212 $stmt->execute(array(':username' => $username));
213 }
214 else {
215 $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
216 $stmt->execute(array(':username_new' => $username_new, ':username' => $username));
217 }
218 }
219 else {
220 $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username");
221 $stmt->execute(array(
222 ':username_new' => $username_new,
223 ':username' => $username,
224 ':active' => $active
225 ));
226 if (isset($_data['disable_tfa'])) {
227 $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
228 $stmt->execute(array(':username' => $username));
229 }
230 else {
231 $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
232 $stmt->execute(array(':username_new' => $username_new, ':username' => $username));
233 }
234 }
235 $_SESSION['return'][] = array(
236 'type' => 'success',
237 'log' => array(__FUNCTION__, $_action, $_data_log),
238 'msg' => array('domain_admin_modified', htmlspecialchars($username))
239 );
240 }
241 return true;
242 }
243 // Domain administrator
244 // Can only edit itself
245 elseif ($_SESSION['mailcow_cc_role'] == "domainadmin") {
246 $username = $_SESSION['mailcow_cc_username'];
247 $password_old = $_data['user_old_pass'];
248 $password_new = $_data['user_new_pass'];
249 $password_new2 = $_data['user_new_pass2'];
250
251 $stmt = $pdo->prepare("SELECT `password` FROM `admin`
252 WHERE `username` = :user");
253 $stmt->execute(array(':user' => $username));
254 $row = $stmt->fetch(PDO::FETCH_ASSOC);
255 if (!verify_hash($row['password'], $password_old)) {
256 $_SESSION['return'][] = array(
257 'type' => 'danger',
258 'log' => array(__FUNCTION__, $_action, $_data_log),
259 'msg' => 'access_denied'
260 );
261 return false;
262 }
263 if (password_check($password_new, $password_new2) !== true) {
264 return false;
265 }
266 $password_hashed = hash_password($password_new);
267 $stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username");
268 $stmt->execute(array(
269 ':password_hashed' => $password_hashed,
270 ':username' => $username
271 ));
272 $_SESSION['return'][] = array(
273 'type' => 'success',
274 'log' => array(__FUNCTION__, $_action, $_data_log),
275 'msg' => array('domain_admin_modified', htmlspecialchars($username))
276 );
277 }
278 break;
279 case 'delete':
280 if ($_SESSION['mailcow_cc_role'] != "admin") {
281 $_SESSION['return'][] = array(
282 'type' => 'danger',
283 'log' => array(__FUNCTION__, $_action, $_data_log),
284 'msg' => 'access_denied'
285 );
286 return false;
287 }
288 $usernames = (array)$_data['username'];
289 foreach ($usernames as $username) {
290 if (empty(domain_admin('details', $username))) {
291 $_SESSION['return'][] = array(
292 'type' => 'danger',
293 'log' => array(__FUNCTION__, $_action, $_data_log),
294 'msg' => array('username_invalid', $username)
295 );
296 continue;
297 }
298 $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
299 $stmt->execute(array(
300 ':username' => $username,
301 ));
302 $stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
303 $stmt->execute(array(
304 ':username' => $username,
305 ));
306 $stmt = $pdo->prepare("DELETE FROM `da_acl` WHERE `username` = :username");
307 $stmt->execute(array(
308 ':username' => $username,
309 ));
310 $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
311 $stmt->execute(array(
312 ':username' => $username,
313 ));
314 $stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username");
315 $stmt->execute(array(
316 ':username' => $username,
317 ));
318 $_SESSION['return'][] = array(
319 'type' => 'success',
320 'log' => array(__FUNCTION__, $_action, $_data_log),
321 'msg' => array('domain_admin_removed', htmlspecialchars($username))
322 );
323 }
324 break;
325 case 'get':
326 $domainadmins = array();
327 if ($_SESSION['mailcow_cc_role'] != "admin") {
328 $_SESSION['return'][] = array(
329 'type' => 'danger',
330 'log' => array(__FUNCTION__, $_action, $_data_log),
331 'msg' => 'access_denied'
332 );
333 return false;
334 }
335 $stmt = $pdo->query("SELECT DISTINCT
336 `username`
337 FROM `domain_admins`
338 WHERE `username` IN (
339 SELECT `username` FROM `admin`
340 WHERE `superadmin`!='1'
341 )");
342 $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
343 while ($row = array_shift($rows)) {
344 $domainadmins[] = $row['username'];
345 }
346 return $domainadmins;
347 break;
348 case 'details':
349 $domainadmindata = array();
350 if ($_SESSION['mailcow_cc_role'] == "domainadmin" && $_data != $_SESSION['mailcow_cc_username']) {
351 return false;
352 }
353 elseif ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
354 return false;
355 }
356 if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $_data))) {
357 return false;
358 }
359 $stmt = $pdo->prepare("SELECT
360 `tfa`.`active` AS `tfa_active`,
361 `domain_admins`.`username`,
362 `domain_admins`.`created`,
363 `domain_admins`.`active` AS `active`
364 FROM `domain_admins`
365 LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`domain_admins`.`username`
366 WHERE `domain_admins`.`username`= :domain_admin");
367 $stmt->execute(array(
368 ':domain_admin' => $_data
369 ));
370 $row = $stmt->fetch(PDO::FETCH_ASSOC);
371 if (empty($row)) {
372 return false;
373 }
374 $domainadmindata['username'] = $row['username'];
375 $domainadmindata['tfa_active'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
376 $domainadmindata['tfa_active_int'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
377 $domainadmindata['active'] = $row['active'];
378 $domainadmindata['active_int'] = $row['active'];
379 $domainadmindata['created'] = $row['created'];
380 // GET SELECTED
381 $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
382 WHERE `domain` IN (
383 SELECT `domain` FROM `domain_admins`
384 WHERE `username`= :domain_admin)");
385 $stmt->execute(array(':domain_admin' => $_data));
386 $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
387 while($row = array_shift($rows)) {
388 $domainadmindata['selected_domains'][] = $row['domain'];
389 }
390 // GET UNSELECTED
391 $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
392 WHERE `domain` NOT IN (
393 SELECT `domain` FROM `domain_admins`
394 WHERE `username`= :domain_admin)");
395 $stmt->execute(array(':domain_admin' => $_data));
396 $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
397 while($row = array_shift($rows)) {
398 $domainadmindata['unselected_domains'][] = $row['domain'];
399 }
400 if (!isset($domainadmindata['unselected_domains'])) {
401 $domainadmindata['unselected_domains'] = "";
402 }
403
404 return $domainadmindata;
405 break;
406 }
407}
408function domain_admin_sso($_action, $_data) {
409 global $pdo;
410
411 switch ($_action) {
412 case 'check':
413 $token = $_data;
414
415 $stmt = $pdo->prepare("SELECT `t1`.`username` FROM `da_sso` AS `t1` JOIN `admin` AS `t2` ON `t1`.`username` = `t2`.`username` WHERE `t1`.`token` = :token AND `t1`.`created` > DATE_SUB(NOW(), INTERVAL '30' SECOND) AND `t2`.`active` = 1 AND `t2`.`superadmin` = 0;");
416 $stmt->execute(array(
417 ':token' => preg_replace('/[^a-zA-Z0-9-]/', '', $token)
418 ));
419 $return = $stmt->fetch(PDO::FETCH_ASSOC);
420 return empty($return['username']) ? false : $return['username'];
421 case 'issue':
422 if ($_SESSION['mailcow_cc_role'] != "admin") {
423 $_SESSION['return'][] = array(
424 'type' => 'danger',
425 'log' => array(__FUNCTION__, $_action, $_data),
426 'msg' => 'access_denied'
427 );
428 return false;
429 }
430
431 $username = $_data['username'];
432
433 $stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
434 WHERE `username` = :username");
435 $stmt->execute(array(':username' => $username));
436 $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
437
438 if ($num_results < 1) {
439 $_SESSION['return'][] = array(
440 'type' => 'danger',
441 'log' => array(__FUNCTION__, $_action, $_data),
442 'msg' => array('object_doesnt_exist', htmlspecialchars($username))
443 );
444 return false;
445 }
446
447 $token = implode('-', array(
448 strtoupper(bin2hex(random_bytes(3))),
449 strtoupper(bin2hex(random_bytes(3))),
450 strtoupper(bin2hex(random_bytes(3))),
451 strtoupper(bin2hex(random_bytes(3))),
452 strtoupper(bin2hex(random_bytes(3)))
453 ));
454
455 $stmt = $pdo->prepare("INSERT INTO `da_sso` (`username`, `token`)
456 VALUES (:username, :token)");
457 $stmt->execute(array(
458 ':username' => $username,
459 ':token' => $token
460 ));
461
462 // perform cleanup
463 $pdo->query("DELETE FROM `da_sso` WHERE created < DATE_SUB(NOW(), INTERVAL '30' SECOND);");
464
465 return ['token' => $token];
466 break;
467 }
468}