Page MenuHomePhabricator

No OneTemporary

diff --git a/appinfo/info.xml b/appinfo/info.xml
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -1,20 +1,20 @@
<?xml version="1.0"?>
<info>
<id>user_sql</id>
<name>SQL user backend</name>
<summary>Authenticate Users by SQL</summary>
<description>Authenticate Users by SQL</description>
- <version>2.3.1</version>
+ <version>2.3.2</version>
<licence>agpl</licence>
<author>Andreas Boehler &lt;dev (at) aboehler
(dot) at &gt;</author>
<namespace>user_sql</namespace>
<types>
<authentication/>
</types>
<category>auth</category>
<dependencies>
<owncloud min-version="8.1" max-version="9.1"/>
<nextcloud min-version="9" max-version="10"/>
</dependencies>
</info>
diff --git a/dovecot_hmacmd5.php b/dovecot_hmacmd5.php
new file mode 100644
--- /dev/null
+++ b/dovecot_hmacmd5.php
@@ -0,0 +1,198 @@
+<?php
+
+/**
+ *
+ * dovecot_hmacmd5.php V1.01
+ *
+ * Generates HMAC-MD5 'contexts' for Dovecot's password files.
+ * Adapted for user_sql by Andreas Boehler
+ *
+ * (C) 2008 Hajo Noerenberg
+ *
+ * http://www.noerenberg.de/
+ * https://github.com/hn/dovecot-misc
+ *
+ * Most of the code has been shamelessly stolen from various sources:
+ *
+ * (C) Paul Johnston 1999 - 2000 / http://pajhome.org.uk/crypt/md5/
+ * (C) William K. Cole 2008 / http://www.scconsult.com/bill/crampass.pl
+ * (C) Borfast 2002 / http://www.zend.com/code/codex.php?ozid=962&single=1
+ * (C) Thomas Weber / http://pajhome.org.uk/crypt/md5/contrib/md5.java.txt
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3.0 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/gpl-3.0.txt>.
+ *
+ */
+
+/* Convert a 32-bit number to a hex string with ls-byte first
+ */
+
+function rhex($n) {
+ $hex_chr = "0123456789abcdef";
+ for($j = 0; $j <= 3; $j++)
+ $r .= $hex_chr[($n >> ($j * 8 + 4)) & 0x0F] . $hex_chr[($n >> ($j * 8)) & 0x0F];
+ return $r;
+}
+
+/* zeroFill() is needed because PHP doesn't have a zero-fill
+ * right shift operator like JavaScript's >>>
+ */
+
+function zeroFill($a, $b) {
+ $z = hexdec(80000000);
+ if ($z & $a) {
+ $a >>= 1;
+ $a &= (~$z);
+ $a |= 0x40000000;
+ $a >>= ($b-1);
+ } else {
+ $a >>= $b;
+ }
+ return $a;
+}
+
+/* Bitwise rotate a 32-bit number to the left
+ */
+
+function bit_rol($num, $cnt) {
+ return ($num << $cnt) | (zeroFill($num, (32 - $cnt)));
+}
+
+/* Add integers, wrapping at 2^32
+ */
+
+function safe_add($x, $y) {
+ return (($x&0x7FFFFFFF) + ($y&0x7FFFFFFF)) ^ ($x&0x80000000) ^ ($y&0x80000000);
+}
+
+/* These functions implement the four basic operations the algorithm uses.
+ */
+
+function md5_cmn($q, $a, $b, $x, $s, $t) {
+ return safe_add(bit_rol(safe_add(safe_add($a, $q), safe_add($x, $t)), $s), $b);
+}
+function md5_ff($a, $b, $c, $d, $x, $s, $t) {
+ return md5_cmn(($b & $c) | ((~$b) & $d), $a, $b, $x, $s, $t);
+}
+function md5_gg($a, $b, $c, $d, $x, $s, $t) {
+ return md5_cmn(($b & $d) | ($c & (~$d)), $a, $b, $x, $s, $t);
+}
+function md5_hh($a, $b, $c, $d, $x, $s, $t) {
+ return md5_cmn($b ^ $c ^ $d, $a, $b, $x, $s, $t);
+}
+function md5_ii($a, $b, $c, $d, $x, $s, $t) {
+ return md5_cmn($c ^ ($b | (~$d)), $a, $b, $x, $s, $t);
+}
+
+/* Calculate the first round of the MD5 algorithm
+ */
+
+function md5_oneround($s, $io) {
+
+ $s = str_pad($s, 64, chr(0x00));
+
+ $x = array_fill(0, 16, 0);
+
+ for($i = 0; $i < 64; $i++)
+ $x[$i >> 2] |= (($io ? 0x36 : 0x5c) ^ ord($s[$i])) << (($i % 4) * 8);
+
+ $a = $olda = 1732584193;
+ $b = $oldb = -271733879;
+ $c = $oldc = -1732584194;
+ $d = $oldd = 271733878;
+
+ $a = md5_ff($a, $b, $c, $d, $x[ 0], 7 , -680876936);
+ $d = md5_ff($d, $a, $b, $c, $x[ 1], 12, -389564586);
+ $c = md5_ff($c, $d, $a, $b, $x[ 2], 17, 606105819);
+ $b = md5_ff($b, $c, $d, $a, $x[ 3], 22, -1044525330);
+ $a = md5_ff($a, $b, $c, $d, $x[ 4], 7 , -176418897);
+ $d = md5_ff($d, $a, $b, $c, $x[ 5], 12, 1200080426);
+ $c = md5_ff($c, $d, $a, $b, $x[ 6], 17, -1473231341);
+ $b = md5_ff($b, $c, $d, $a, $x[ 7], 22, -45705983);
+ $a = md5_ff($a, $b, $c, $d, $x[ 8], 7 , 1770035416);
+ $d = md5_ff($d, $a, $b, $c, $x[ 9], 12, -1958414417);
+ $c = md5_ff($c, $d, $a, $b, $x[10], 17, -42063);
+ $b = md5_ff($b, $c, $d, $a, $x[11], 22, -1990404162);
+ $a = md5_ff($a, $b, $c, $d, $x[12], 7 , 1804603682);
+ $d = md5_ff($d, $a, $b, $c, $x[13], 12, -40341101);
+ $c = md5_ff($c, $d, $a, $b, $x[14], 17, -1502002290);
+ $b = md5_ff($b, $c, $d, $a, $x[15], 22, 1236535329);
+
+ $a = md5_gg($a, $b, $c, $d, $x[ 1], 5 , -165796510);
+ $d = md5_gg($d, $a, $b, $c, $x[ 6], 9 , -1069501632);
+ $c = md5_gg($c, $d, $a, $b, $x[11], 14, 643717713);
+ $b = md5_gg($b, $c, $d, $a, $x[ 0], 20, -373897302);
+ $a = md5_gg($a, $b, $c, $d, $x[ 5], 5 , -701558691);
+ $d = md5_gg($d, $a, $b, $c, $x[10], 9 , 38016083);
+ $c = md5_gg($c, $d, $a, $b, $x[15], 14, -660478335);
+ $b = md5_gg($b, $c, $d, $a, $x[ 4], 20, -405537848);
+ $a = md5_gg($a, $b, $c, $d, $x[ 9], 5 , 568446438);
+ $d = md5_gg($d, $a, $b, $c, $x[14], 9 , -1019803690);
+ $c = md5_gg($c, $d, $a, $b, $x[ 3], 14, -187363961);
+ $b = md5_gg($b, $c, $d, $a, $x[ 8], 20, 1163531501);
+ $a = md5_gg($a, $b, $c, $d, $x[13], 5 , -1444681467);
+ $d = md5_gg($d, $a, $b, $c, $x[ 2], 9 , -51403784);
+ $c = md5_gg($c, $d, $a, $b, $x[ 7], 14, 1735328473);
+ $b = md5_gg($b, $c, $d, $a, $x[12], 20, -1926607734);
+
+ $a = md5_hh($a, $b, $c, $d, $x[ 5], 4 , -378558);
+ $d = md5_hh($d, $a, $b, $c, $x[ 8], 11, -2022574463);
+ $c = md5_hh($c, $d, $a, $b, $x[11], 16, 1839030562);
+ $b = md5_hh($b, $c, $d, $a, $x[14], 23, -35309556);
+ $a = md5_hh($a, $b, $c, $d, $x[ 1], 4 , -1530992060);
+ $d = md5_hh($d, $a, $b, $c, $x[ 4], 11, 1272893353);
+ $c = md5_hh($c, $d, $a, $b, $x[ 7], 16, -155497632);
+ $b = md5_hh($b, $c, $d, $a, $x[10], 23, -1094730640);
+ $a = md5_hh($a, $b, $c, $d, $x[13], 4 , 681279174);
+ $d = md5_hh($d, $a, $b, $c, $x[ 0], 11, -358537222);
+ $c = md5_hh($c, $d, $a, $b, $x[ 3], 16, -722521979);
+ $b = md5_hh($b, $c, $d, $a, $x[ 6], 23, 76029189);
+ $a = md5_hh($a, $b, $c, $d, $x[ 9], 4 , -640364487);
+ $d = md5_hh($d, $a, $b, $c, $x[12], 11, -421815835);
+ $c = md5_hh($c, $d, $a, $b, $x[15], 16, 530742520);
+ $b = md5_hh($b, $c, $d, $a, $x[ 2], 23, -995338651);
+
+ $a = md5_ii($a, $b, $c, $d, $x[ 0], 6 , -198630844);
+ $d = md5_ii($d, $a, $b, $c, $x[ 7], 10, 1126891415);
+ $c = md5_ii($c, $d, $a, $b, $x[14], 15, -1416354905);
+ $b = md5_ii($b, $c, $d, $a, $x[ 5], 21, -57434055);
+ $a = md5_ii($a, $b, $c, $d, $x[12], 6 , 1700485571);
+ $d = md5_ii($d, $a, $b, $c, $x[ 3], 10, -1894986606);
+ $c = md5_ii($c, $d, $a, $b, $x[10], 15, -1051523);
+ $b = md5_ii($b, $c, $d, $a, $x[ 1], 21, -2054922799);
+ $a = md5_ii($a, $b, $c, $d, $x[ 8], 6 , 1873313359);
+ $d = md5_ii($d, $a, $b, $c, $x[15], 10, -30611744);
+ $c = md5_ii($c, $d, $a, $b, $x[ 6], 15, -1560198380);
+ $b = md5_ii($b, $c, $d, $a, $x[13], 21, 1309151649);
+ $a = md5_ii($a, $b, $c, $d, $x[ 4], 6 , -145523070);
+ $d = md5_ii($d, $a, $b, $c, $x[11], 10, -1120210379);
+ $c = md5_ii($c, $d, $a, $b, $x[ 2], 15, 718787259);
+ $b = md5_ii($b, $c, $d, $a, $x[ 9], 21, -343485551);
+
+ $a = safe_add($a, $olda);
+ $b = safe_add($b, $oldb);
+ $c = safe_add($c, $oldc);
+ $d = safe_add($d, $oldd);
+
+ return rhex($a) . rhex($b) . rhex($c) . rhex($d);
+}
+
+function dovecot_hmacmd5 ($s) {
+ if (strlen($s) > 64) $s=pack("H*", md5($s));
+ return "{HMAC-MD5}" . md5_oneround($s, 0) . md5_oneround($s, 1);
+}
+
+function dovecot_crammd5 ($s) {
+ if (strlen($s) > 64) $s=pack("H*", md5($s));
+ return "{CRAM-MD5}" . md5_oneround($s, 0) . md5_oneround($s, 1);
+}
diff --git a/templates/settings.php b/templates/settings.php
--- a/templates/settings.php
+++ b/templates/settings.php
@@ -1,168 +1,168 @@
<?php $ocVersion = $_['ocVersion'];
$cfgClass = $ocVersion >= 7 ? 'section' : 'personalblock';
?>
<div class="<?php p($cfgClass); ?>">
<h2><?php p($l->t('SQL User Backend')); ?></h2>
<form id="sqlForm" action="#" method="post" class="<?php p($cfgClass); ?>">
<div id="sqlDiv" class="<?php p($cfgClass); ?>">
<label for="sql_domain_chooser"><?php p($l -> t('Settings for Domain')) ?></label>
<select id="sql_domain_chooser" name="sql_domain_chooser">
<?php foreach ($_['allowed_domains'] as $domain): ?>
<option value="<?php p($domain); ?>"><?php p($domain); ?></option>
<?php endforeach ?>
</select>
<ul>
<li><a id="sqlBasicSettings" href="#sql-1"><?php p($l -> t('Connection Settings')); ?></a></li>
<li><a id="sqlColSettings" href="#sql-2"><?php p($l -> t('Column Settings')); ?></a></li>
<li><a id="sqlEmailSettings" href="#sql-3"><?php p($l -> t('E-Mail Settings')); ?></a></li>
<li><a id="sqlDomainSettings" href="#sql-4"><?php p($l -> t('Domain Settings')); ?></a></li>
<li><a id="sqlGethomeSettings" href="#sql-5"><?php p($l -> t('getHome Settings')); ?></a></li>
</ul>
<fieldset id="sql-1">
<p><label for="sql_driver"><?php p($l -> t('SQL Driver')); ?></label>
<?php $db_driver = array('mysql' => 'MySQL', 'pgsql' => 'PostgreSQL'); ?>
<select id="sql_driver" name="sql_driver">
<?php
foreach ($db_driver as $driver => $name):
//echo $_['sql_driver'];
if($_['sql_driver'] === $driver): ?>
<option selected="selected" value="<?php p($driver); ?>"><?php p($name); ?></option>
<?php else: ?>
<option value="<?php p($driver); ?>"><?php p($name); ?></option>
<?php endif;
endforeach;
?>
</select>
</p>
<p><label for="sql_hostname"><?php p($l -> t('Host')); ?></label><input type="text" id="sql_hostname" name="sql_hostname" value="<?php p($_['sql_hostname']); ?>"></p>
<p><label for="sql_username"><?php p($l -> t('Username')); ?></label><input type="text" id="sql_username" name="sql_username" value="<?php p($_['sql_username']); ?>" /></p>
<p><label for="sql_database"><?php p($l -> t('Database')); ?></label><input type="text" id="sql_database" name="sql_database" value="<?php p($_['sql_database']); ?>" /></p>
<p><label for="sql_password"><?php p($l -> t('Password')); ?></label><input type="password" id="sql_password" name="sql_password" value="<?php p($_['sql_password']); ?>" /></p>
<p><input type="submit" id="sqlVerify" value="<?php p($l -> t('Verify Settings')); ?>"></p>
</fieldset>
<fieldset id="sql-2">
<p><label for="sql_table"><?php p($l -> t('Table')); ?></label><input type="text" id="sql_table" name="sql_table" value="<?php p($_['sql_table']); ?>" /></p>
<p><label for="col_username"><?php p($l -> t('Username Column')); ?></label><input type="text" id="col_username" name="col_username" value="<?php p($_['col_username']); ?>" /></p>
<p><label for="col_password"><?php p($l -> t('Password Column')); ?></label><input type="text" id="col_password" name="col_password" value="<?php p($_['col_password']); ?>" /></p>
<p><label for="set_allow_pwchange"><?php p($l -> t('Allow password changing (read README!)')); ?></label><input type="checkbox" id="set_allow_pwchange" name="set_allow_pwchange" value="1"<?php
if($_['set_allow_pwchange'])
p(' checked');
?>><br>
<em><?php p($l -> t('Allow changing passwords. Imposes a security risk as password salts are not recreated')); ?></em></p>
<p><label for="col_displayname"><?php p($l -> t('Real Name Column')); ?></label><input type="text" id="col_displayname" name="col_displayname" value="<?php p($_['col_displayname']); ?>" /></p>
<p><label for="set_crypt_type"><?php p($l -> t('Encryption Type')); ?></label>
- <?php $crypt_types = array('md5' => 'MD5', 'md5crypt' => 'MD5 Crypt', 'cleartext' => 'Cleartext', 'mysql_encrypt' => 'mySQL ENCRYPT()', 'system' => 'System (crypt)', 'mysql_password' => 'mySQL PASSWORD()', 'joomla' => 'Joomla MD5 Encryption', 'joomla2' => 'Joomla > 2.5.18 phpass', 'ssha256' => 'Salted SSHA256', 'redmine' => 'Redmine'); ?>
+ <?php $crypt_types = array('md5' => 'MD5', 'md5crypt' => 'MD5 Crypt', 'cleartext' => 'Cleartext', 'mysql_encrypt' => 'mySQL ENCRYPT()', 'system' => 'System (crypt)', 'mysql_password' => 'mySQL PASSWORD()', 'joomla' => 'Joomla MD5 Encryption', 'joomla2' => 'Joomla > 2.5.18 phpass', 'ssha256' => 'Salted SSHA256', 'redmine' => 'Redmine', 'crammd5' => 'CRAM-MD5', 'hmacmd5' => 'HMAC-MD5'); ?>
<select id="set_crypt_type" name="set_crypt_type">
<?php
foreach ($crypt_types as $driver => $name):
//echo $_['set_crypt_type'];
if($_['set_crypt_type'] === $driver): ?>
<option selected="selected" value="<?php p($driver); ?>"><?php p($name); ?></option>
<?php else: ?>
<option value="<?php p($driver); ?>"><?php p($name); ?></option>
<?php endif;
endforeach;
?>
</select>
</p>
<p><label for="col_active"><?php p($l -> t('User Active Column')); ?></label><input type="text" id="col_active" name="col_active" value="<?php p($_['col_active']); ?>" /></p>
<p><label for="set_active_invert"><?php p($l -> t('Invert Active Value')); ?></label><input type="checkbox" id="set_active_invert" name="set_active_invert" value="1"<?php
if($_['set_active_invert'])
p(' checked');
?> /><br>
<em><?php p($l -> t("Invert the logic of the active column (for blocked users in the SQL DB)")); ?></em></p>
</fieldset>
<fieldset id="sql-3">
<p><label for="col_email"><?php p($l -> t('E-Mail Column')); ?></label><input type="text" id="col_email" name="col_email" value="<?php p($_['col_email']); ?>" /></p>
<p><label for="set_mail_sync_mode"><?php p($l -> t('E-Mail address sync mode')); ?></label>
<?php $mail_modes = array('none' => 'No Synchronisation', 'initial' => 'Synchronise only once', 'forceoc' => 'ownCloud always wins', 'forcesql' => 'SQL always wins'); ?>
<select id="set_mail_sync_mode" name="set_mail_sync_mode">
<?php
foreach ($mail_modes as $mode => $name):
//echo $_['set_mail_sync_mode'];
if($_['set_mail_sync_mode'] === $mode): ?>
<option selected="selected" value="<?php p($mode); ?>"><?php p($name); ?></option>
<?php else: ?>
<option value="<?php p($mode); ?>"><?php p($name); ?></option>
<?php endif;
endforeach;
?>
</select>
</p>
</fieldset>
<fieldset id="sql-4">
<p><label for="set_default_domain"><?php p($l -> t('Append Default Domain')); ?></label><input type="text" id="set_default_domain", name="set_default_domain" value="<?php p($_['set_default_domain']); ?>" /><br>
<em><?php p($l -> t('Append this string, e.g. a domain name, to each user name. The @-sign is automatically inserted.')); ?></em>
</p>
<p><label for="set_strip_domain"><?php p($l -> t('Strip Domain Part from Username')); ?></label><input type="checkbox" id="set_strip_domain" name="set_strip_domain" value="1"<?php
if($_['set_strip_domain'])
p(' checked');
?> /><br>
<em><?php p($l -> t("Strip Domain Part including @-sign from Username when logging in and retrieving username lists")); ?></em></p>
</fieldset>
<fieldset id="sql-5">
<p><label for="set_enable_gethome"><?php p($l -> t('Enable support for getHome()')); ?></label><input type="checkbox" id="set_enable_gethome", name="set_enable_gethome" value="1" <?php
if($_['set_enable_gethome'])
p(' checked');
?>/></p>
<p><label for="set_gethome_mode"><?php p($l -> t('Method for getHome')); ?></label>
<?php $gethome_modes = array('query' => 'SQL Column', 'static' => 'Static (with Variables)'); ?>
<select id="set_gethome_mode" name="set_gethome_mode">
<?php
foreach ($gethome_modes as $mode => $name):
//echo $_['set_mail_sync_mode'];
if($_['set_gethome_mode'] === $mode): ?>
<option selected="selected" value="<?php p($mode); ?>"><?php p($name); ?></option>
<?php else: ?>
<option value="<?php p($mode); ?>"><?php p($name); ?></option>
<?php endif;
endforeach;
?>
</select>
</p>
<p><label for="col_gethome"><?php p($l -> t('Home Column')); ?></label><input type="text" id="col_gethome" name="col_gethome" value="<?php p($_['col_gethome']); ?>"></p>
<p><label for="set_gethome"><?php p($l -> t('Home Dir')); ?></label><input type="text" id="set_gethome" name="set_gethome" value="<?php p($_['set_gethome']); ?>"><br>
<em><?php p($l -> t('You can use the placeholders %%u to specify the user ID (before appending the default domain), %%ud to specify the user ID (after appending the default domain) and %%d to specify the default domain')); ?></em></p>
</fieldset>
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']); ?>" id="requesttoken" />
<input type="hidden" name="appname" value="user_sql" />
<input id="sqlSubmit" type="submit" value="<?php p($l -> t('Save')); ?>" />
<div id="sql_update_message" class="statusmessage"><?php p($l -> t('Saving...')); ?></div>
<div id="sql_loading_message" class="statusmessage"><?php p($l -> t('Loading...')); ?></div>
<div id="sql_verify_message" class="statusmessage"><?php p($l -> t('Verifying...')); ?></div>
<div id="sql_error_message" class="errormessage"></div>
<div id="sql_success_message" class="successmessage"></div>
</div>
</form>
</div>
diff --git a/user_sql.php b/user_sql.php
--- a/user_sql.php
+++ b/user_sql.php
@@ -1,782 +1,794 @@
<?php
/**
* ownCloud - user_sql
*
* @author Andreas Böhler and contributors
* @copyright 2012-2015 Andreas Böhler <dev (at) aboehler (dot) at>
*
* credits go to Ed W for several SQL injection fixes and caching support
* credits go to Frédéric France for providing Joomla support
* credits go to Mark Jansenn for providing Joomla 2.5.18+ / 3.2.1+ support
* credits go to Dominik Grothaus for providing SSHA256 support and fixing a few bugs
* credits go to Sören Eberhardt-Biermann for providing multi-host support
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\user_sql;
use \OCA\user_sql\lib\Helper;
class OC_USER_SQL extends \OC_User_Backend implements \OCP\IUserBackend, \OCP\UserInterface
{
protected $cache;
protected $settings;
protected $helper;
protected $session_cache_name;
protected $ocConfig;
/**
* The default constructor. It loads the settings for the given domain
* and tries to connect to the database.
*/
public function __construct()
{
$memcache = \OC::$server->getMemCacheFactory();
if ( $memcache -> isAvailable())
{
$this -> cache = $memcache -> create();
}
$this -> helper = new \OCA\user_sql\lib\Helper();
$domain = \OC::$server->getRequest()->getServerHost();
$this -> settings = $this -> helper -> loadSettingsForDomain($domain);
$this -> ocConfig = \OC::$server->getConfig();
$this -> helper -> connectToDb($this -> settings);
$this -> session_cache_name = 'USER_SQL_CACHE';
return false;
}
/**
* Sync the user's E-Mail address with the address stored by ownCloud.
* We have three (four) sync modes:
* - none: Does nothing
* - initial: Do the sync only once from SQL -> ownCloud
* - forcesql: The SQL database always wins and sync to ownCloud
* - forceoc: ownCloud always wins and syncs to SQL
*
* @param string $uid The user's ID to sync
* @return bool Success or Fail
*/
private function doEmailSync($uid)
{
\OCP\Util::writeLog('OC_USER_SQL', "Entering doEmailSync for UID: $uid", \OCP\Util::DEBUG);
if($this -> settings['col_email'] === '')
return false;
if($this -> settings['set_mail_sync_mode'] === 'none')
return false;
$ocUid = $uid;
$uid = $this -> doUserDomainMapping($uid);
$row = $this -> helper -> runQuery('getMail', array('uid' => $uid));
if($row === false)
{
return false;
}
$newMail = $row[$this -> settings['col_email']];
$currMail = $this->ocConfig->getUserValue($ocUid, 'settings', 'email', '');
switch($this -> settings['set_mail_sync_mode'])
{
case 'initial':
if($currMail === '')
$this->ocConfig->setUserValue($ocUid, 'settings', 'email', $newMail);
break;
case 'forcesql':
if($currMail !== $newMail)
$this->ocConfig->setUserValue($ocUid, 'settings', 'email', $newMail);
break;
case 'forceoc':
if(($currMail !== '') && ($currMail !== $newMail))
{
$row = $this -> helper -> runQuery('setMail', array('uid' => $uid, 'currMail' => $currMail), true);
if($row === false)
{
\OCP\Util::writeLog('OC_USER_SQL', "Could not update E-Mail address in SQL database!", \OCP\Util::ERROR);
}
}
break;
}
return true;
}
/**
* This maps the username to the specified domain name.
* It can only append a default domain name.
*
* @param string $uid The UID to work with
* @return string The mapped UID
*/
private function doUserDomainMapping($uid)
{
$uid = trim($uid);
if($this -> settings['set_default_domain'] !== '')
{
\OCP\Util::writeLog('OC_USER_SQL', "Append default domain: ".$this -> settings['set_default_domain'], \OCP\Util::DEBUG);
if(strpos($uid, '@') === false)
{
$uid .= "@" . $this -> settings['set_default_domain'];
}
}
$uid = strtolower($uid);
\OCP\Util::writeLog('OC_USER_SQL', 'Returning mapped UID: ' . $uid, \OCP\Util::DEBUG);
return $uid;
}
/**
* Return the actions implemented by this backend
* @param $actions
* @return bool
*/
public function implementsAction($actions)
{
return (bool)((\OC_User_Backend::CHECK_PASSWORD
| \OC_User_Backend::GET_DISPLAYNAME
| \OC_User_Backend::COUNT_USERS
| $this -> settings['set_allow_pwchange'] === 'true' ? \OC_User_Backend::SET_PASSWORD : 0
| $this -> settings['set_enable_gethome'] === 'true' ? \OC_User_Backend::GET_HOME : 0
) & $actions);
}
/**
* Checks if this backend has user listing support
* @return bool
*/
public function hasUserListings()
{
return true;
}
/**
* Return the user's home directory, if enabled
* @param string $uid The user's ID to retrieve
* @return mixed The user's home directory or false
*/
public function getHome($uid)
{
\OCP\Util::writeLog('OC_USER_SQL', "Entering getHome for UID: $uid", \OCP\Util::DEBUG);
if($this -> settings['set_enable_gethome'] !== 'true')
return false;
$uidMapped = $this -> doUserDomainMapping($uid);
$home = false;
switch($this->settings['set_gethome_mode'])
{
case 'query':
\OCP\Util::writeLog('OC_USER_SQL', "getHome with Query selected, running Query...", \OCP\Util::DEBUG);
$row = $this -> helper -> runQuery('getHome', array('uid' => $uidMapped));
if($row === false)
{
\OCP\Util::writeLog('OC_USER_SQL', "Got no row, return false", \OCP\Util::DEBUG);
return false;
}
$home = $row[$this -> settings['col_gethome']];
break;
case 'static':
\OCP\Util::writeLog('OC_USER_SQL', "getHome with static selected", \OCP\Util::DEBUG);
$home = $this -> settings['set_gethome'];
$home = str_replace('%ud', $uidMapped, $home);
$home = str_replace('%u', $uid, $home);
$home = str_replace('%d', $this -> settings['set_default_domain'], $home);
break;
}
\OCP\Util::writeLog('OC_USER_SQL', "Returning getHome for UID: $uid with Home $home", \OCP\Util::DEBUG);
return $home;
}
/**
* Create a new user account using this backend
* @return bool always false, as we can't create users
*/
public function createUser()
{
// Can't create user
\OCP\Util::writeLog('OC_USER_SQL', 'Not possible to create local users from web frontend using SQL user backend', \OCP\Util::ERROR);
return false;
}
/**
* Delete a user account using this backend
* @param string $uid The user's ID to delete
* @return bool always false, as we can't delete users
*/
public function deleteUser($uid)
{
// Can't delete user
\OCP\Util::writeLog('OC_USER_SQL', 'Not possible to delete local users from web frontend using SQL user backend', \OCP\Util::ERROR);
return false;
}
/**
* Set (change) a user password
* This can be enabled/disabled in the settings (set_allow_pwchange)
*
* @param string $uid The user ID
* @param string $password The user's new password
* @return bool The return status
*/
public function setPassword($uid, $password)
{
// Update the user's password - this might affect other services, that
// use the same database, as well
\OCP\Util::writeLog('OC_USER_SQL', "Entering setPassword for UID: $uid", \OCP\Util::DEBUG);
if($this -> settings['set_allow_pwchange'] !== 'true')
return false;
$uid = $this -> doUserDomainMapping($uid);
$row = $this -> helper -> runQuery('getPass', array('uid' => $uid));
if($row === false)
{
return false;
}
$old_password = $row[$this -> settings['col_password']];
if($this -> settings['set_crypt_type'] === 'joomla2')
{
if(!class_exists('\PasswordHash'))
require_once('PasswordHash.php');
$hasher = new \PasswordHash(10, true);
$enc_password = $hasher -> HashPassword($password);
}
// Redmine stores the salt separatedly, this doesn't play nice with the way
// we check passwords
elseif($this -> settings['set_crypt_type'] === 'redmine')
{
$salt = $this -> helper -> runQuery('getRedmineSalt', array('uid' => $uid));
if(!$salt)
return false;
$enc_password = sha1($salt['salt'].sha1($password));
} else
{
$enc_password = $this -> pacrypt($password, $old_password);
}
$res = $this -> helper -> runQuery('setPass', array('uid' => $uid, 'enc_password' => $enc_password), true);
if($res === false)
{
\OCP\Util::writeLog('OC_USER_SQL', "Could not update password!", \OCP\Util::ERROR);
return false;
}
\OCP\Util::writeLog('OC_USER_SQL', "Updated password successfully, return true", \OCP\Util::DEBUG);
return true;
}
/**
* Check if the password is correct
* @param string $uid The username
* @param string $password The password
* @return bool true/false
*
* Check if the password is correct without logging in the user
*/
public function checkPassword($uid, $password)
{
\OCP\Util::writeLog('OC_USER_SQL', "Entering checkPassword() for UID: $uid", \OCP\Util::DEBUG);
$uid = $this -> doUserDomainMapping($uid);
$row = $this -> helper -> runQuery('getPass', array('uid' => $uid));
if($row === false)
{
\OCP\Util::writeLog('OC_USER_SQL', "Got no row, return false", \OCP\Util::DEBUG);
return false;
}
$db_pass = $row[$this -> settings['col_password']];
\OCP\Util::writeLog('OC_USER_SQL', "Encrypting and checking password", \OCP\Util::DEBUG);
// Joomla 2.5.18 switched to phPass, which doesn't play nice with the way
// we check passwords
if($this -> settings['set_crypt_type'] === 'joomla2')
{
if(!class_exists('\PasswordHash'))
require_once('PasswordHash.php');
$hasher = new \PasswordHash(10, true);
$ret = $hasher -> CheckPassword($password, $db_pass);
}
// Redmine stores the salt separatedly, this doesn't play nice with the way
// we check passwords
elseif($this -> settings['set_crypt_type'] === 'redmine')
{
$salt = $this -> helper -> runQuery('getRedmineSalt', array('uid' => $uid));
if(!$salt)
return false;
$ret = sha1($salt['salt'].sha1($password)) === $db_pass;
} else
{
$ret = $this -> pacrypt($password, $db_pass) === $db_pass;
}
if($ret)
{
\OCP\Util::writeLog('OC_USER_SQL', "Passwords matching, return true", \OCP\Util::DEBUG);
if($this -> settings['set_strip_domain'] === 'true')
{
$uid = explode("@", $uid);
$uid = $uid[0];
}
return $uid;
} else
{
\OCP\Util::writeLog('OC_USER_SQL', "Passwords do not match, return false", \OCP\Util::DEBUG);
return false;
}
}
/**
* Count the number of users
* @return int The user count
*/
public function countUsers()
{
\OCP\Util::writeLog('OC_USER_SQL', "Entering countUsers()", \OCP\Util::DEBUG);
$search = "%".$this -> doUserDomainMapping("");
$userCount = $this -> helper -> runQuery('countUsers', array('search' => $search));
if($userCount === false)
{
$userCount = 0;
}
else {
$userCount = reset($userCount);
}
\OCP\Util::writeLog('OC_USER_SQL', "Return usercount: ".$userCount, \OCP\Util::DEBUG);
return $userCount;
}
/**
* Get a list of all users
* @param string $search The search term (can be empty)
* @param int $limit The search limit (can be null)
* @param int $offset The search offset (can be null)
* @return array with all uids
*/
public function getUsers($search = '', $limit = null, $offset = null)
{
\OCP\Util::writeLog('OC_USER_SQL', "Entering getUsers() with Search: $search, Limit: $limit, Offset: $offset", \OCP\Util::DEBUG);
$users = array();
if($search !== '')
{
$search = "%".$this -> doUserDomainMapping($search."%")."%";
}
else
{
$search = "%".$this -> doUserDomainMapping("")."%";
}
$rows = $this -> helper -> runQuery('getUsers', array('search' => $search), false, true, array('limit' => $limit, 'offset' => $offset));
if($rows === false)
return array();
foreach($rows as $row)
{
$uid = $row[$this -> settings['col_username']];
if($this -> settings['set_strip_domain'] === 'true')
{
$uid = explode("@", $uid);
$uid = $uid[0];
}
$users[] = strtolower($uid);
}
\OCP\Util::writeLog('OC_USER_SQL', "Return list of results", \OCP\Util::DEBUG);
return $users;
}
/**
* Check if a user exists
* @param string $uid the username
* @return boolean
*/
public function userExists($uid)
{
$cacheKey = 'sql_user_exists_' . $uid;
$cacheVal = $this -> getCache ($cacheKey);
\OCP\Util::writeLog('OC_USER_SQL', "userExists() for UID: $uid cacheVal: $cacheVal", \OCP\Util::DEBUG);
if(!is_null($cacheVal))
return (bool)$cacheVal;
\OCP\Util::writeLog('OC_USER_SQL', "Entering userExists() for UID: $uid", \OCP\Util::DEBUG);
// Only if the domain is removed for internal user handling,
// we should add the domain back when checking existance
if($this -> settings['set_strip_domain'] === 'true')
{
$uid = $this -> doUserDomainMapping($uid);
}
$exists = (bool)$this -> helper -> runQuery('userExists', array('uid' => $uid));;
$this -> setCache ($cacheKey, $exists, 60);
if(!$exists)
{
\OCP\Util::writeLog('OC_USER_SQL', "Empty row, user does not exists, return false", \OCP\Util::DEBUG);
return false;
} else
{
\OCP\Util::writeLog('OC_USER_SQL', "User exists, return true", \OCP\Util::DEBUG);
return true;
}
}
/**
* Get the display name of the user
* @param string $uid The user ID
* @return mixed The user's display name or FALSE
*/
public function getDisplayName($uid)
{
\OCP\Util::writeLog('OC_USER_SQL', "Entering getDisplayName() for UID: $uid", \OCP\Util::DEBUG);
$this -> doEmailSync($uid);
$uid = $this -> doUserDomainMapping($uid);
if(!$this -> userExists($uid))
{
return false;
}
$row = $this -> helper -> runQuery('getDisplayName', array('uid' => $uid));
if(!$row)
{
\OCP\Util::writeLog('OC_USER_SQL', "Empty row, user has no display name or does not exist, return false", \OCP\Util::DEBUG);
return false;
} else
{
\OCP\Util::writeLog('OC_USER_SQL', "User exists, return true", \OCP\Util::DEBUG);
$displayName = $row[$this -> settings['col_displayname']];
return $displayName; ;
}
return false;
}
public function getDisplayNames($search = '', $limit = null, $offset = null)
{
$uids = $this -> getUsers($search, $limit, $offset);
$displayNames = array();
foreach($uids as $uid)
{
$displayNames[$uid] = $this -> getDisplayName($uid);
}
return $displayNames;
}
/**
* Returns the backend name
* @return string
*/
public function getBackendName()
{
return 'SQL';
}
/**
* The following functions were directly taken from PostfixAdmin and just
* slightly modified
* to suit our needs.
* Encrypt a password, using the apparopriate hashing mechanism as defined in
* config.inc.php ($this->crypt_type).
* When wanting to compare one pw to another, it's necessary to provide the
* salt used - hence
* the second parameter ($pw_db), which is the existing hash from the DB.
*
* @param string $pw cleartext password
* @param string $pw_db encrypted password from database
* @return string encrypted password.
*/
private function pacrypt($pw, $pw_db = "")
{
\OCP\Util::writeLog('OC_USER_SQL', "Entering private pacrypt()", \OCP\Util::DEBUG);
$pw = stripslashes($pw);
$password = "";
$salt = "";
if($this -> settings['set_crypt_type'] === 'md5crypt')
{
$split_salt = preg_split('/\$/', $pw_db);
if(isset($split_salt[2]))
{
$salt = $split_salt[2];
}
$password = $this -> md5crypt($pw, $salt);
} elseif($this -> settings['set_crypt_type'] === 'md5')
{
$password = md5($pw);
} elseif($this -> settings['set_crypt_type'] === 'system')
{
// We never generate salts, as user creation is not allowed here
$password = crypt($pw, $pw_db);
} elseif($this -> settings['set_crypt_type'] === 'cleartext')
{
$password = $pw;
}
// See
// https://sourceforge.net/tracker/?func=detail&atid=937966&aid=1793352&group_id=191583
// this is apparently useful for pam_mysql etc.
elseif($this -> settings['set_crypt_type'] === 'mysql_encrypt')
{
if($pw_db !== "")
{
$salt = substr($pw_db, 0, 2);
$row = $this -> helper -> runQuery('mysqlEncryptSalt', array('pw' => $pw, 'salt' => $salt));
} else
{
$row = $this -> helper -> runQuery('mysqlEncrypt', array('pw' => $pw));
}
if($row === false)
{
return false;
}
$password = $row[0];
} elseif($this -> settings['set_crypt_type'] === 'mysql_password')
{
$row = $this -> helper -> runQuery('mysqlPassword', array('pw' => $pw));
if($row === false)
{
return false;
}
$password = $row[0];
}
// The following is by Frédéric France
elseif($this -> settings['set_crypt_type'] === 'joomla')
{
$split_salt = preg_split('/:/', $pw_db);
if(isset($split_salt[1]))
{
$salt = $split_salt[1];
}
$password = ($salt) ? md5($pw . $salt) : md5($pw);
$password .= ':' . $salt;
}
+ elseif($this-> settings['set_crypt_type'] === 'hmacmd5')
+ {
+ require_once('dovecot_hmacmd5.php');
+ $password = dovecot_hmacmd5($pw);
+ }
+
+ elseif($this-> settings['set_crypt_type'] === 'crammd5')
+ {
+ require_once('dovecot_hmacmd5.php');
+ $password = dovecot_crammd5($pw);
+ }
+
elseif($this-> settings['set_crypt_type'] === 'ssha256')
{
$salted_password = base64_decode(preg_replace('/{SSHA256}/i','',$pw_db));
$salt = substr($salted_password,-(strlen($salted_password)-32));
$password = $this->ssha256($pw,$salt);
} else
{
\OCP\Util::writeLog('OC_USER_SQL', "unknown/invalid crypt_type settings: ".$this->settings['set_crypt_type'], \OCP\Util::ERROR);
die('unknown/invalid Encryption type setting: ' . $this -> settings['set_crypt_type']);
}
\OCP\Util::writeLog('OC_USER_SQL', "pacrypt() done, return", \OCP\Util::DEBUG);
return $password;
}
/**
* md5crypt
* Creates MD5 encrypted password
* @param string $pw The password to encrypt
* @param string $salt The salt to use
* @param string $magic ?
* @return string The encrypted password
*/
private function md5crypt($pw, $salt = "", $magic = "")
{
$MAGIC = "$1$";
if($magic === "")
$magic = $MAGIC;
if($salt === "")
$salt = $this -> create_salt();
$slist = explode("$", $salt);
if($slist[0] === "1")
$salt = $slist[1];
$salt = substr($salt, 0, 8);
$ctx = $pw . $magic . $salt;
$final = $this -> pahex2bin(md5($pw . $salt . $pw));
for($i = strlen($pw); $i > 0; $i -= 16)
{
if($i > 16)
{
$ctx .= substr($final, 0, 16);
} else
{
$ctx .= substr($final, 0, $i);
}
}
$i = strlen($pw);
while($i > 0)
{
if($i & 1)
$ctx .= chr(0);
else
$ctx .= $pw[0];
$i = $i>>1;
}
$final = $this -> pahex2bin(md5($ctx));
for($i = 0; $i < 1000; $i++)
{
$ctx1 = "";
if($i & 1)
{
$ctx1 .= $pw;
} else
{
$ctx1 .= substr($final, 0, 16);
}
if($i % 3)
$ctx1 .= $salt;
if($i % 7)
$ctx1 .= $pw;
if($i & 1)
{
$ctx1 .= substr($final, 0, 16);
} else
{
$ctx1 .= $pw;
}
$final = $this -> pahex2bin(md5($ctx1));
}
$passwd = "";
$passwd .= $this -> to64(((ord($final[0])<<16) | (ord($final[6])<<8) | (ord($final[12]))), 4);
$passwd .= $this -> to64(((ord($final[1])<<16) | (ord($final[7])<<8) | (ord($final[13]))), 4);
$passwd .= $this -> to64(((ord($final[2])<<16) | (ord($final[8])<<8) | (ord($final[14]))), 4);
$passwd .= $this -> to64(((ord($final[3])<<16) | (ord($final[9])<<8) | (ord($final[15]))), 4);
$passwd .= $this -> to64(((ord($final[4])<<16) | (ord($final[10])<<8) | (ord($final[5]))), 4);
$passwd .= $this -> to64(ord($final[11]), 2);
return "$magic$salt\$$passwd";
}
/**
* Create a new salte
* @return string The salt
*/
private function create_salt()
{
srand((double) microtime() * 1000000);
$salt = substr(md5(rand(0, 9999999)), 0, 8);
return $salt;
}
/**
* Encrypt using SSHA256 algorithm
* @param string $pw The password
* @param string $salt The salt to use
* @return string The hashed password, prefixed by {SSHA256}
*/
private function ssha256($pw, $salt)
{
return '{SSHA256}'.base64_encode(hash('sha256',$pw.$salt,true).$salt);
}
/**
* PostfixAdmin's hex2bin function
* @param string $str The string to convert
* @return string The converted string
*/
private function pahex2bin($str)
{
if(function_exists('hex2bin'))
{
return hex2bin($str);
} else
{
$len = strlen($str);
$nstr = "";
for($i = 0; $i < $len; $i += 2)
{
$num = sscanf(substr($str, $i, 2), "%x");
$nstr .= chr($num[0]);
}
return $nstr;
}
}
/**
* Convert to 64?
*/
private function to64($v, $n)
{
$ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
$ret = "";
while(($n - 1) >= 0)
{
$n--;
$ret .= $ITOA64[$v & 0x3f];
$v = $v>>6;
}
return $ret;
}
/**
* Store a value in memcache or the session, if no memcache is available
* @param string $key The key
* @param mixed $value The value to store
* @param int $ttl (optional) defaults to 3600 seconds.
*/
private function setCache($key, $value, $ttl=3600)
{
if ($this -> cache === NULL)
{
$_SESSION[$this -> session_cache_name][$key] = array(
'value' => $value,
'time' => time(),
'ttl' => $ttl,
);
} else
{
$this -> cache -> set($key,$value,$ttl);
}
}
/**
* Fetch a value from memcache or session, if memcache is not available.
* Returns NULL if there's no value stored or the value expired.
* @param string $key
* @return mixed|NULL
*/
private function getCache($key)
{
$retVal = NULL;
if ($this -> cache === NULL)
{
if (isset($_SESSION[$this -> session_cache_name],$_SESSION[$this -> session_cache_name][$key]))
{
$value = $_SESSION[$this -> session_cache_name][$key];
if (time() < $value['time'] + $value['ttl'])
{
$retVal = $value['value'];
}
}
} else
{
$retVal = $this -> cache -> get ($key);
}
return $retVal;
}
}
?>

File Metadata

Mime Type
text/x-diff
Expires
Thu, Dec 5, 1:45 AM (1 h, 41 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
531929
Default Alt Text
(46 KB)

Event Timeline