Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Topt app passwords fixes (WIP) #805

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b43590b
slightly better docs for app passwords
DavidGoodwin Jan 1, 2024
3de3100
empty file
DavidGoodwin Jan 1, 2024
046f8f6
dead code?
DavidGoodwin Jan 1, 2024
e31cd65
add new lines to break it up a bit
DavidGoodwin Jan 1, 2024
3c80208
add more type hints
DavidGoodwin Jan 1, 2024
fd1920c
Login - add type hints; add @todo for future self
DavidGoodwin Jan 1, 2024
d2157a4
try and ensure a domain admin can only do things for their domains; a…
DavidGoodwin Jan 1, 2024
b53fa74
reformat?
DavidGoodwin Jan 1, 2024
600e45f
if totp is not enabled, nuke the session and redirect to login.php
DavidGoodwin Jan 1, 2024
c1a2736
we now pass a login thing into the TotpPf class
DavidGoodwin Jan 1, 2024
1c47b4f
we now pass a login thing into the TotpPf class
DavidGoodwin Jan 1, 2024
0324a87
formatting
DavidGoodwin Jan 1, 2024
bbb6e85
menu: Change TOTP -> Manage TOTP; try to use "Exemption" rather than …
DavidGoodwin Jan 1, 2024
fc8e7cc
pass Login instance into TotpPf
DavidGoodwin Jan 1, 2024
cdbc8d9
try to reorganise a little
DavidGoodwin Jan 1, 2024
192848b
pass Login into TotpPf etc
DavidGoodwin Jan 1, 2024
97328e3
html changes
DavidGoodwin Jan 1, 2024
6e1cba7
move app password code into Login
DavidGoodwin Jan 1, 2024
8faf1dc
test updates
DavidGoodwin Jan 1, 2024
c4a1188
something to help me not forget
DavidGoodwin Jan 1, 2024
7eef9a9
formatting
DavidGoodwin Jan 5, 2024
5c5c36c
add error_log() logging to mailbox save failure - see #780
DavidGoodwin Feb 17, 2024
977e436
remove autocomplete from totp login - see #806
DavidGoodwin Mar 30, 2024
07ea084
add link from TODO to ticket for #802
DavidGoodwin Apr 1, 2024
2faa37e
add link to ticket
DavidGoodwin May 17, 2024
31d4407
formatting
DavidGoodwin May 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion DOCUMENTS/Postfix-Dovecot-Postgresql-Example.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

The below covers some default(ish) configuration things for using Postfix, Dovecot with PostgreSQL.

See also: https://github.com/postfixadmin/postfixadmin/issues/184

# Postfix

Assumptions :
Expand Down Expand Up @@ -237,7 +239,12 @@ password_query = SELECT username AS user,password FROM mailbox WHERE username =
user_query = SELECT '/var/mail/vmail/' || maildir AS home, 8 as uid, 8 as gid FROM mailbox WHERE username = '%u' AND active = '1'
```

Password query with app password and allowed remote IP support:
### With application password ('app password')

An application password is intended to provide a way of sharing access to a specific account, while maintaining a unique password. In other words: providing multiple passwords for one account.

PostfixAdmin app passwords cannot be used to sign in to PostfixAdmin itself, but can be used by e.g. dovecot with the following password query :

```
password query = SELECT user, password FROM (\
SELECT username AS user, password, '0' AS is_app_password FROM\
Expand Down
Empty file.
13 changes: 10 additions & 3 deletions DOCUMENTS/SECURITY.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,20 @@ To enhance the security of admin and mailbox user accounts, Postfixadmin
supports a set of different features:

1. Multi-factor authentication with TOTP for admin and mailbox users.

2. Synchronize the TOTP secret with a Mail front end, for example
Roundcubemail. This enables TRUSTED mail user clients (MUAs) to
implement MFA internally.

3. Enable MUAs with allowed IP addresses to log in with username and
password. Use this feature with care. It basically deactivates MFA
for specified IPs. This feature is intended for mail user clients
that implement MFA themselves, for example Roundcubemail. However,
this can also be used to deactivate MFA when a VPN is used or other
use cases.

4. Allow SMTP, IMAP and POP login with app passwords when a TOTP secret
is set. The app passwords cannot be used to log in at Postfixadmin
is set. The app passwords cannot be used to log in to Postfixadmin
itself. That means only the normal user password plus the TOTP factor
allow adding, changing or removing app passwords.

Expand All @@ -70,17 +73,21 @@ read carefully through the documentation before activating these
features.

To activate those features, run through the following procedure:

1. Change your MDA (and if required MTA) password query. You can
take a look at the example query listed in the
Postfix-Dovecot-Postgresql-Example.md file. The example should work
for Dovecot out of the box.
for Dovecot out of the box.

2. Set up synchronization of TOTP secrets with a mail user client
application. This is important. Otherwise MFA will not be used to
protect access to mails.

Use the mailbox_post_TOTP_change_secret_script setting in the
config.inc.php. The mailbox username and domain will be passed
as parameters, the shared secret via stdin. For Roundcubemail you can
have a look at the scripts/examples/sync-roundcubemail-totp.php example.
have a look at the scripts/examples/sync-roundcubemail-totp.php example.

3. Activate TOTP and app passwords in the config.inc.php by setting
$CONF['totp'] = 'YES';
$CONF['app_passwords'] = 'YES';
9 changes: 9 additions & 0 deletions TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

App passwords :

* app passwords only make sense for corresponding mailbox entries; i.e an admin shouldn't be able to create one for themself.
* admins should be able to add them for user(s) (need to check domain permission)
* Fix query in the dovecot integration example - it does not work - see https://github.com/postfixadmin/postfixadmin/issues/802

Probably need a Process class to put the various proc_open stuff in?

13 changes: 5 additions & 8 deletions functions.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -696,21 +696,18 @@ function check_owner($username, $domain)
* @param string $username
* @return array of domain names.
*/
function list_domains_for_admin($username)
function list_domains_for_admin(string $username): array
{
$table_domain = table_by_key('domain');
$table_domain_admins = table_by_key('domain_admins');

$condition = array();

$E_username = escape_string($username);
$condition = [];
$pvalues = [];

$query = "SELECT $table_domain.domain FROM $table_domain ";
$condition[] = "$table_domain.domain != 'ALL'";

$pvalues = array();

$result = db_query_one("SELECT username FROM $table_domain_admins WHERE username= :username AND domain='ALL'", array('username' => $username));
$result = db_query_one("SELECT username FROM $table_domain_admins WHERE username= :username AND domain='ALL'", ['username' => $username]);
if (empty($result)) { # not a superadmin
$pvalues['username'] = $username;
$pvalues['active'] = db_get_boolean(true);
Expand Down Expand Up @@ -2011,7 +2008,7 @@ function db_query(string $sql, array $values = array(), bool $ignore_errors = fa
* @param string $additionalwhere (default '').
* @return int|mixed rows deleted.
*/
function db_delete($table, $where, $delete, $additionalwhere = '')
function db_delete(string $table, string $where, string $delete,string $additionalwhere = '')
{
$table = table_by_key($table);

Expand Down
22 changes: 12 additions & 10 deletions languages/en.lang
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ $PALANG['pMenu_dkim_signing'] = 'Signing Table';
$PALANG['pMenu_security'] = 'Security';
$PALANG['pMenu_password'] = 'Password';
$PALANG['pMenu_totp'] = 'TOTP';
$PALANG['pMenu_totp_exceptions'] = 'TOTP exceptions';
$PALANG['pMenu_totp_exceptions'] = 'TOTP exemptions';
$PALANG['pMenu_app_passwords'] = 'Application passwords';
$PALANG['pMenu_viewlog'] = 'View Log';
$PALANG['pMenu_logout'] = 'Logout';
Expand Down Expand Up @@ -182,30 +182,32 @@ $PALANG['pEdit_mailbox_domain_error'] = 'This domain is not yours: ';
$PALANG['pEdit_mailbox_result_error'] = 'Unable to modify the mailbox!';

$PALANG['pUsersMain_totp_exceptions'] = 'Allow trusted IP-addresses to access your mailbox without TOTP';
$PALANG['pUsersMenu_totp'] = 'Change TOTP';
$PALANG['pUsersMenu_totp'] = 'Manage TOTP';
$PALANG['pTOTP_welcome'] = 'Setup your Time-based One-time Password';
$PALANG['pUsersMain_totp'] = 'Setup your Time-based One-time Password';
$PALANG['pTOTP_secret'] = 'TOTP secret';
$PALANG['pTOTP_code'] = 'TOTP code (empty to disable)';
$PALANG['pTOTP_secret_result_error'] = 'Could not change TOTP secret';
$PALANG['change_TOTP'] = 'Change TOTP settings';
$PALANG['pTOTP_code_text'] = 'Scan the above image and enter the current code shown, in order to enable TOTP auth for your account.';
$PALANG['pTOTP_code_mismatch'] = 'Incorrect one-time-password entered';
$PALANG['pTOTP_qr'] = 'TOTP QR-code';
$PALANG['pTOTP_confirm'] = 'Please confirm your login by providing your current TOTP code';
$PALANG['pTotp_failed'] = 'Your entered code is not valid. Please use a current code.';
$PALANG['pTotp_stored'] = 'You successfully confirmed your new TOTP secret.';
$PALANG['pTotp_exceptions_welcome'] = 'Add TOPT-exempt address';
$PALANG['pTotp_exceptions_user'] = 'Username (or domain for admins)';
$PALANG['pTotp_exceptions_add'] = 'Add exception';
$PALANG['pTotp_exceptions_address'] = 'IP-Address';
$PALANG['pTotp_exceptions_add'] = 'Add exemption';
$PALANG['pTotp_exceptions_address'] = 'IP Address';
$PALANG['pTotp_exceptions_description'] = 'Description';
$PALANG['pTotp_exceptions_list'] = 'Existing exceptions';
$PALANG['pTotp_exceptions_list'] = 'Existing exemptions';
$PALANG['pTotp_exceptions_revoke'] = 'Revoke';
$PALANG['pTotp_exceptions_revoked'] = 'Exception revoked';
$PALANG['pTotp_exception_result_success'] = 'Exception added';
$PALANG['pTotp_exception_result_error'] = 'Could not add exception';
$PALANG['pEdit_totp_exception_result_error'] = 'Unable to modify TOTP exceptions';
$PALANG['pException_ip_empty_error'] = 'IP cannot be empty';
$PALANG['pTotp_exceptions_revoked'] = 'Exemption revoked';
$PALANG['pTotp_exception_result_success'] = 'Exemption added';
$PALANG['pTotp_exception_result_error'] = 'Could not add exemption';
$PALANG['pEdit_totp_exception_result_error'] = 'Unable to modify TOTP exemptions';
$PALANG['pException_ip_empty_error'] = 'IP address cannot be empty';
$PALANG['pException_ip_error'] = 'IP address does not validate';
$PALANG['pException_desc_empty_error'] = 'Description cannot be empty';
$PALANG['pException_user_entire_domain_error'] = 'Only admins can add exceptions for an entire domain.';
$PALANG['pException_user_global_error'] = 'Only superadmins can add global exceptions';
Expand Down
9 changes: 4 additions & 5 deletions model/Login.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,14 @@ public function __construct(string $tableName)

/**
* Attempt to log a user in.
* @param string $tablename
* @param string $username
* @param string $password
* @return boolean true on successful login (i.e. password matches etc)
*/
public function login($username, $password): bool
public function login(string $username, string $password): bool
{
$active = db_get_boolean(true);
$query = "SELECT password FROM {$this->key_table} WHERE username = :username AND active = :active";

$values = array('username' => $username, 'active' => $active);
$values = ['username' => $username, 'active' => $active];

$result = db_query_all($query, $values);

Expand Down Expand Up @@ -229,6 +226,8 @@ public function addAppPassword(string $username, string $password, string $app_d
$app_pass = pacrypt($app_pass, '', $username);


// @todo - should an admin be able to add an application password for a user? this doesn't allow them to.

$result = db_insert('mailbox_app_password', ['username' => $username, 'description' => $app_desc, 'password_hash' => $app_pass], []);

if ($result != 1) {
Expand Down
1 change: 1 addition & 0 deletions model/PFAHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,7 @@ public function save(): bool
$result = db_update($this->db_table, $this->id_field, $this->id, $db_values, array('modified'), true);
}
} catch (PDOException $e) {
error_log(__FILE__ . " - failed to save mailbox; message : " . $e->getMessage()); // see #780
$this->errormsg[] = Config::lang_f($this->msg['store_error'], $this->label);
return false;
}
Expand Down