From 06dee154d03519eaebd1cb9e52dd03a7cf2fb498 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Wed, 1 Jun 2016 16:24:47 +0200 Subject: [PATCH] * Admin/Mail: fix admin editing (personal) mail account of other user destroys password, because it was encrypted with users session password and therefore not available --- api/src/Mail/Account.php | 14 +++++++++++++- api/src/Mail/Credentials.php | 29 ++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/api/src/Mail/Account.php b/api/src/Mail/Account.php index 9d44aad2e8..45362ef894 100644 --- a/api/src/Mail/Account.php +++ b/api/src/Mail/Account.php @@ -1216,6 +1216,13 @@ class Account implements \ArrayAccess // add imap credentials $cred_type = $data['acc_imap_username'] == $data['acc_smtp_username'] && $data['acc_imap_password'] == $data['acc_smtp_password'] ? 3 : 1; + // if both passwords are unavailable, they seem identical, do NOT store them together, as they are not! + if ($cred_type == 3 && $data['acc_imap_password'] == Credentials::UNAVAILABLE && + $data['acc_imap_password'] == Credentials::UNAVAILABLE && + $data['acc_imap_cred_id'] != $data['acc_smtp_cred_id']) + { + $cred_type = 1; + } Credentials::write($data['acc_id'], $data['acc_imap_username'], $data['acc_imap_password'], $cred_type, $valid_for, $data['acc_imap_cred_id']); // add smtp credentials if necessary and different from imap @@ -1225,6 +1232,11 @@ class Account implements \ArrayAccess 2, $valid_for, $data['acc_smtp_cred_id'] != $data['acc_imap_cred_id'] ? $data['acc_smtp_cred_id'] : null); } + // delete evtl. existing SMTP credentials, after storing IMAP&SMTP together now + elseif ($data['acc_smtp_cred_id']) + { + Credentials::delete($data['acc_id'], $valid_for, Credentials::SMTP, true); + } // store or delete admin credentials if ($data['acc_imap_admin_username'] && $data['acc_imap_admin_password']) { @@ -1263,7 +1275,7 @@ class Account implements \ArrayAccess * * @param array|Account $account * @param int $account_id =null - * @return boolean + * @return int account_id for whom credentials are valid or 0 for all */ protected static function credentials_valid_for($account, $account_id=null) { diff --git a/api/src/Mail/Credentials.php b/api/src/Mail/Credentials.php index 03d281e193..86970cdf8d 100644 --- a/api/src/Mail/Credentials.php +++ b/api/src/Mail/Credentials.php @@ -68,6 +68,11 @@ class Credentials */ const SYSTEM = 2; + /** + * Returned for passwords, when an admin reads an accounts with a password encrypted with users session password + */ + const UNAVAILABLE = '**unavailable**'; + /** * Translate type to prefix * @@ -263,6 +268,12 @@ class Credentials 'cred_type' => $type, 'cred_pw_enc' => $pw_enc, ); + // check if password is unavailable (admin edits an account with password encrypted with users session PW) and NOT store it + if ($password == self::UNAVAILABLE) + { + error_log(__METHOD__."(".array2string(func_get_args()).") can NOT store unavailable password, storing without password!"); + unset($data['cred_password'], $data['cred_pw_enc']); + } //error_log(__METHOD__."($acc_id, '$username', '$password', $type, $account_id, $cred_id, $mcrypt) storing ".array2string($data).' '.function_backtrace()); if ($cred_id > 0) { @@ -290,9 +301,10 @@ class Credentials * @param int $acc_id * @param int|array $account_id =null * @param int $type =self::ALL self::IMAP, self::SMTP or self::ADMIN + * @param boolean $exact_type =false true: delete only cred_type=$type, false: delete cred_type&$type * @return int number of rows deleted */ - public static function delete($acc_id, $account_id=null, $type=self::ALL) + public static function delete($acc_id, $account_id=null, $type=self::ALL, $exact_type=false) { if (!($acc_id > 0) && !isset($account_id)) { @@ -301,8 +313,14 @@ class Credentials $where = array(); if ($acc_id > 0) $where['acc_id'] = $acc_id; if (isset($account_id)) $where['account_id'] = $account_id; - if ($type != self::ALL) $where[] = '(cred_type & '.(int)$type.') > 0'; // postgreSQL require > 0, or gives error as it expects boolean - + if ($exact_type) + { + $where['cred_type'] = $type; + } + elseif ($type != self::ALL) + { + $where[] = '(cred_type & '.(int)$type.') > 0'; // postgreSQL require > 0, or gives error as it expects boolean + } self::$db->delete(self::TABLE, $where, __LINE__, __FILE__, self::APP); // invalidate cache: we allways unset everything about an account to simplify cache handling @@ -359,6 +377,11 @@ class Credentials return base64_decode($row['cred_password']); case self::USER: + if ($row['account_id'] != $GLOBALS['egw_info']['user']['account_id']) + { + return self::UNAVAILABLE; + } + // fall through case self::SYSTEM: if (($row['cred_pw_enc'] != self::USER || !$mcrypt) && !($mcrypt = self::init_crypt($row['cred_pw_enc'] == self::USER)))