1<?php 2/* Copyright (C) 2004-2009 Laurent Destailleur <eldy@users.sourceforge.net> 3 * Copyright (C) 2005-2007 Regis Houssin <regis.houssin@inodbox.com> 4 * Copyright (C) 2013-2015 Juanjo Menent <jmenent@2byte.es> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <https://www.gnu.org/licenses/>. 18 */ 19 20/** 21 * \file htdocs/admin/security.php 22 * \ingroup setup 23 * \brief Page de configuration du module securite 24 */ 25 26require '../main.inc.php'; 27require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; 28require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php'; 29 30$action = GETPOST('action', 'aZ09'); 31 32// Load translation files required by the page 33$langs->loadLangs(array("users", "admin", "other")); 34 35if (!$user->admin) accessforbidden(); 36 37// Allow/Disallow change to clear passwords once passwords are crypted 38$allow_disable_encryption = true; 39 40/* 41 * Actions 42 */ 43if ($action == 'setgeneraterule') 44{ 45 if (!dolibarr_set_const($db, 'USER_PASSWORD_GENERATED', $_GET["value"], 'chaine', 0, '', $conf->entity)) 46 { 47 dol_print_error($db); 48 } else { 49 header("Location: ".$_SERVER["PHP_SELF"]); 50 exit; 51 } 52} 53 54if ($action == 'activate_encrypt') 55{ 56 $error = 0; 57 58 $db->begin(); 59 60 dolibarr_set_const($db, "DATABASE_PWD_ENCRYPTED", "1", 'chaine', 0, '', $conf->entity); 61 62 $sql = "SELECT u.rowid, u.pass, u.pass_crypted"; 63 $sql .= " FROM ".MAIN_DB_PREFIX."user as u"; 64 $sql .= " WHERE u.pass IS NOT NULL AND LENGTH(u.pass) < 32"; // Not a MD5 value 65 66 $resql = $db->query($sql); 67 if ($resql) 68 { 69 $numrows = $db->num_rows($resql); 70 $i = 0; 71 while ($i < $numrows) 72 { 73 $obj = $db->fetch_object($resql); 74 if (dol_hash($obj->pass)) 75 { 76 $sql = "UPDATE ".MAIN_DB_PREFIX."user"; 77 $sql .= " SET pass_crypted = '".dol_hash($obj->pass)."', pass = NULL"; 78 $sql .= " WHERE rowid=".$obj->rowid; 79 //print $sql; 80 81 $resql2 = $db->query($sql); 82 if (!$resql2) 83 { 84 dol_print_error($db); 85 $error++; 86 break; 87 } 88 89 $i++; 90 } 91 } 92 } else dol_print_error($db); 93 94 //print $error." ".$sql; 95 //exit; 96 if (!$error) 97 { 98 $db->commit(); 99 header("Location: security.php"); 100 exit; 101 } else { 102 $db->rollback(); 103 dol_print_error($db, ''); 104 } 105} elseif ($action == 'disable_encrypt') 106{ 107 //On n'autorise pas l'annulation de l'encryption car les mots de passe ne peuvent pas etre decodes 108 //Do not allow "disable encryption" as passwords cannot be decrypted 109 if ($allow_disable_encryption) 110 { 111 dolibarr_del_const($db, "DATABASE_PWD_ENCRYPTED", $conf->entity); 112 } 113 header("Location: security.php"); 114 exit; 115} 116 117if ($action == 'activate_encryptdbpassconf') 118{ 119 $result = encodedecode_dbpassconf(1); 120 if ($result > 0) 121 { 122 sleep(3); // Don't know why but we need to wait file is completely saved before making the reload. Even with flush and clearstatcache, we need to wait. 123 124 // database value not required 125 //dolibarr_set_const($db, "MAIN_DATABASE_PWD_CONFIG_ENCRYPTED", "1"); 126 header("Location: security.php"); 127 exit; 128 } else { 129 setEventMessages($langs->trans('InstrucToEncodePass', dol_encode($dolibarr_main_db_pass)), null, 'warnings'); 130 } 131} elseif ($action == 'disable_encryptdbpassconf') 132{ 133 $result = encodedecode_dbpassconf(0); 134 if ($result > 0) 135 { 136 sleep(3); // Don't know why but we need to wait file is completely saved before making the reload. Even with flush and clearstatcache, we need to wait. 137 138 // database value not required 139 //dolibarr_del_const($db, "MAIN_DATABASE_PWD_CONFIG_ENCRYPTED",$conf->entity); 140 header("Location: security.php"); 141 exit; 142 } else { 143 setEventMessages($langs->trans('InstrucToClearPass', $dolibarr_main_db_pass), null, 'warnings'); 144 } 145} 146 147if ($action == 'activate_MAIN_SECURITY_DISABLEFORGETPASSLINK') 148{ 149 dolibarr_set_const($db, "MAIN_SECURITY_DISABLEFORGETPASSLINK", '1', 'chaine', 0, '', $conf->entity); 150 header("Location: security.php"); 151 exit; 152} elseif ($action == 'disable_MAIN_SECURITY_DISABLEFORGETPASSLINK') 153{ 154 dolibarr_del_const($db, "MAIN_SECURITY_DISABLEFORGETPASSLINK", $conf->entity); 155 header("Location: security.php"); 156 exit; 157} 158 159if ($action == 'updatepattern') 160{ 161 $pattern = GETPOST("pattern", "alpha"); 162 $explodePattern = explode(';', $pattern); 163 164 $patternInError = false; 165 if ($explodePattern[0] < 1 || $explodePattern[4] < 0) { 166 $patternInError = true; 167 } 168 169 if ($explodePattern[0] < $explodePattern[1] + $explodePattern[2] + $explodePattern[3]) { 170 $patternInError = true; 171 } 172 173 if (!$patternInError) { 174 dolibarr_set_const($db, "USER_PASSWORD_PATTERN", $pattern, 'chaine', 0, '', $conf->entity); 175 setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); 176 header("Location: security.php"); 177 exit; 178 } 179} 180 181 182 183/* 184 * View 185 */ 186 187$form = new Form($db); 188 189$wikihelp = 'EN:Setup_Security|FR:Paramétrage_Sécurité|ES:Configuración_Seguridad'; 190llxHeader('', $langs->trans("Passwords"), $wikihelp); 191 192print load_fiche_titre($langs->trans("SecuritySetup"), '', 'title_setup'); 193 194print '<span class="opacitymedium">'.$langs->trans("GeneratedPasswordDesc")."</span><br>\n"; 195print "<br>\n"; 196 197 198$head = security_prepare_head(); 199 200print dol_get_fiche_head($head, 'passwords', '', -1); 201 202 203// Choix du gestionnaire du generateur de mot de passe 204print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST">'; 205print '<input type="hidden" name="token" value="'.newToken().'">'; 206print '<input type="hidden" name="action" value="update">'; 207print '<input type="hidden" name="constname" value="USER_PASSWORD_GENERATED">'; 208print '<input type="hidden" name="consttype" value="yesno">'; 209 210// Charge tableau des modules generation 211$dir = "../core/modules/security/generate"; 212clearstatcache(); 213$handle = opendir($dir); 214$i = 1; 215if (is_resource($handle)) 216{ 217 while (($file = readdir($handle)) !== false) 218 { 219 if (preg_match('/(modGeneratePass[a-z]+)\.class\.php$/i', $file, $reg)) 220 { 221 // Charging the numbering class 222 $classname = $reg[1]; 223 require_once $dir.'/'.$file; 224 225 $obj = new $classname($db, $conf, $langs, $user); 226 $arrayhandler[$obj->id] = $obj; 227 $i++; 228 } 229 } 230 closedir($handle); 231} 232asort($arrayhandler); 233 234print '<table class="noborder centpercent">'; 235print '<tr class="liste_titre">'; 236print '<td colspan="2">'.$langs->trans("RuleForGeneratedPasswords").'</td>'; 237print '<td>'.$langs->trans("Example").'</td>'; 238print '<td class="center">'.$langs->trans("Activated").'</td>'; 239print '</tr>'; 240 241foreach ($arrayhandler as $key => $module) 242{ 243 // Show modules according to features level 244 if ($module->version == 'development' && $conf->global->MAIN_FEATURES_LEVEL < 2) continue; 245 if ($module->version == 'experimental' && $conf->global->MAIN_FEATURES_LEVEL < 1) continue; 246 247 if ($module->isEnabled()) 248 { 249 print '<tr class="oddeven"><td width="100">'; 250 print ucfirst($key); 251 print "</td><td>\n"; 252 print $module->getDescription().'<br>'; 253 print $langs->trans("MinLength").': '.$module->length; 254 print '</td>'; 255 256 // Show example of numbering module 257 print '<td class="nowrap">'; 258 $tmp = $module->getExample(); 259 if (preg_match('/^Error/', $tmp)) { 260 $langs->load("errors"); 261 print '<div class="error">'.$langs->trans($tmp).'</div>'; 262 } elseif ($tmp == 'NotConfigured') print $langs->trans($tmp); 263 else print $tmp; 264 print '</td>'."\n"; 265 266 print '<td width="100" align="center">'; 267 if ($conf->global->USER_PASSWORD_GENERATED == $key) 268 { 269 print img_picto('', 'tick'); 270 } else { 271 print '<a href="'.$_SERVER['PHP_SELF'].'?action=setgeneraterule&token='.newToken().'&value='.$key.'">'.$langs->trans("Activate").'</a>'; 272 } 273 print "</td></tr>\n"; 274 } 275} 276print '</table>'; 277print '</form>'; 278 279//if($conf->global->MAIN_SECURITY_DISABLEFORGETPASSLINK == 1) 280// Patter for Password Perso 281if ($conf->global->USER_PASSWORD_GENERATED == "Perso") { 282 $tabConf = explode(";", $conf->global->USER_PASSWORD_PATTERN); 283 print '<br>'; 284 print '<table class="noborder centpercent">'; 285 print '<tr class="liste_titre">'; 286 print '<td colspan="3"> '.$langs->trans("PasswordPatternDesc").'</td>'; 287 print '</tr>'; 288 289 290 print '<tr class="oddeven">'; 291 print '<td>'.$langs->trans("MinLength")."</td>"; 292 print '<td colspan="2"><input type="number" value="'.$tabConf[0].'" id="minlenght" min="1"></td>'; 293 print '</tr>'; 294 295 296 print '<tr class="oddeven">'; 297 print '<td>'.$langs->trans("NbMajMin")."</td>"; 298 print '<td colspan="2"><input type="number" value="'.$tabConf[1].'" id="NbMajMin" min="0"></td>'; 299 print '</tr>'; 300 301 302 print '<tr class="oddeven">'; 303 print '<td>'.$langs->trans("NbNumMin")."</td>"; 304 print '<td colspan="2"><input type="number" value="'.$tabConf[2].'" id="NbNumMin" min="0"></td>'; 305 print '</tr>'; 306 307 308 print '<tr class="oddeven">'; 309 print '<td>'.$langs->trans("NbSpeMin")."</td>"; 310 print '<td colspan="2"><input type="number" value="'.$tabConf[3].'" id="NbSpeMin" min="0"></td>'; 311 print '</tr>'; 312 313 314 print '<tr class="oddeven">'; 315 print '<td>'.$langs->trans("NbIteConsecutive")."</td>"; 316 print '<td colspan="2"><input type="number" value="'.$tabConf[4].'" id="NbIteConsecutive" min="0"></td>'; 317 print '</tr>'; 318 319 320 print '<tr class="oddeven">'; 321 print '<td>'.$langs->trans("NoAmbiCaracAutoGeneration")."</td>"; 322 print '<td colspan="2"><input type="checkbox" id="NoAmbiCaracAutoGeneration" '.($tabConf[5] ? "checked" : "").' min="0"> <span id="textcheckbox">'.($tabConf[5] ? $langs->trans("Activated") : $langs->trans("Disabled")).'</span></td>'; 323 print '</tr>'; 324 325 print '</table>'; 326 327 print '<br>'; 328 print '<div class="center">'; 329 print '<a class="button button-save" id="linkChangePattern">'.$langs->trans("Save").'</a>'; 330 print '</div>'; 331 print '<br><br>'; 332 333 print '<script type="text/javascript">'; 334 print ' function getStringArg(){'; 335 print ' var pattern = "";'; 336 print ' pattern += $("#minlenght").val() + ";";'; 337 print ' pattern += $("#NbMajMin").val() + ";";'; 338 print ' pattern += $("#NbNumMin").val() + ";";'; 339 print ' pattern += $("#NbSpeMin").val() + ";";'; 340 print ' pattern += $("#NbIteConsecutive").val() + ";";'; 341 print ' pattern += $("#NoAmbiCaracAutoGeneration")[0].checked ? "1" : "0";'; 342 print ' return pattern;'; 343 print ' }'; 344 345 print ' function valuePossible(){'; 346 print ' var fields = ["#minlenght", "#NbMajMin", "#NbNumMin", "#NbSpeMin", "#NbIteConsecutive"];'; 347 print ' for(var i = 0 ; i < fields.length ; i++){'; 348 print ' if($(fields[i]).val() < $(fields[i]).attr("min")){'; 349 print ' return false;'; 350 print ' }'; 351 print ' }'; 352 print ' '; 353 print ' var length = parseInt($("#minlenght").val());'; 354 print ' var length_mini = parseInt($("#NbMajMin").val()) + parseInt($("#NbNumMin").val()) + parseInt($("#NbSpeMin").val());'; 355 print ' return length >= length_mini;'; 356 print ' }'; 357 358 print ' function generatelink(){'; 359 print ' return "security.php?action=updatepattern&pattern="+getStringArg();'; 360 print ' }'; 361 362 print ' function valuePatternChange(){'; 363 print ' console.log("valuePatternChange");'; 364 print ' var lang_save = "'.$langs->trans("Save").'";'; 365 print ' var lang_error = "'.$langs->trans("Error").'";'; 366 print ' var lang_Disabled = "'.$langs->trans("Disabled").'";'; 367 print ' var lang_Activated = "'.$langs->trans("Activated").'";'; 368 print ' $("#textcheckbox").html($("#NoAmbiCaracAutoGeneration")[0].checked ? unescape(lang_Activated) : unescape(lang_Disabled));'; 369 print ' if(valuePossible()){'; 370 print ' $("#linkChangePattern").attr("href",generatelink()).text(lang_save);'; 371 print ' }'; 372 print ' else{'; 373 print ' $("#linkChangePattern").attr("href", null).text(lang_error);'; 374 print ' }'; 375 print ' }'; 376 377 print ' $("#minlenght").change(function(){valuePatternChange();});'; 378 print ' $("#NbMajMin").change(function(){valuePatternChange();});'; 379 print ' $("#NbNumMin").change(function(){valuePatternChange();});'; 380 print ' $("#NbSpeMin").change(function(){valuePatternChange();});'; 381 print ' $("#NbIteConsecutive").change(function(){valuePatternChange();});'; 382 print ' $("#NoAmbiCaracAutoGeneration").change(function(){valuePatternChange();});'; 383 384 print '</script>'; 385} 386 387 388// Cryptage mot de passe 389print '<br>'; 390print "<form method=\"post\" action=\"".$_SERVER["PHP_SELF"]."\">"; 391print '<input type="hidden" name="token" value="'.newToken().'">'; 392print "<input type=\"hidden\" name=\"action\" value=\"encrypt\">"; 393 394print '<table class="noborder centpercent">'; 395print '<tr class="liste_titre">'; 396print '<td colspan="3">'.$langs->trans("Parameters").'</td>'; 397print '<td class="center">'.$langs->trans("Activated").'</td>'; 398print '<td class="center">'.$langs->trans("Action").'</td>'; 399print '</tr>'; 400 401// Disable clear password in database 402print '<tr class="oddeven">'; 403print '<td colspan="3">'.$langs->trans("DoNotStoreClearPassword").'</td>'; 404print '<td align="center" width="60">'; 405if (!empty($conf->global->DATABASE_PWD_ENCRYPTED)) 406{ 407 print img_picto($langs->trans("Active"), 'tick'); 408} 409print '</td>'; 410if (!$conf->global->DATABASE_PWD_ENCRYPTED) 411{ 412 print '<td align="center" width="100">'; 413 print '<a href="security.php?action=activate_encrypt">'.$langs->trans("Activate").'</a>'; 414 print "</td>"; 415} 416 417// Database conf file encryption 418if (!empty($conf->global->DATABASE_PWD_ENCRYPTED)) 419{ 420 print '<td align="center" width="100">'; 421 if ($allow_disable_encryption) 422 { 423 //On n'autorise pas l'annulation de l'encryption car les mots de passe ne peuvent pas etre decodes 424 //Do not allow "disable encryption" as passwords cannot be decrypted 425 print '<a href="security.php?action=disable_encrypt">'.$langs->trans("Disable").'</a>'; 426 } else { 427 print '-'; 428 } 429 print "</td>"; 430} 431print "</td>"; 432print '</tr>'; 433 434// Cryptage du mot de base de la base dans conf.php 435 436print '<tr class="oddeven">'; 437print '<td colspan="3">'.$langs->trans("MainDbPasswordFileConfEncrypted").'</td>'; 438print '<td align="center" width="60">'; 439if (preg_match('/crypted:/i', $dolibarr_main_db_pass) || !empty($dolibarr_main_db_encrypted_pass)) 440{ 441 print img_picto($langs->trans("Active"), 'tick'); 442} 443 444print '</td>'; 445 446print '<td align="center" width="100">'; 447if (empty($dolibarr_main_db_pass) && empty($dolibarr_main_db_encrypted_pass)) 448{ 449 $langs->load("errors"); 450 print img_warning($langs->trans("WarningPassIsEmpty")); 451} else { 452 if (empty($dolibarr_main_db_encrypted_pass)) 453 { 454 print '<a href="security.php?action=activate_encryptdbpassconf">'.$langs->trans("Activate").'</a>'; 455 } 456 if (!empty($dolibarr_main_db_encrypted_pass)) 457 { 458 print '<a href="security.php?action=disable_encryptdbpassconf">'.$langs->trans("Disable").'</a>'; 459 } 460} 461print "</td>"; 462 463print "</td>"; 464print '</tr>'; 465 466 467// Disable link "Forget password" on logon 468 469print '<tr class="oddeven">'; 470print '<td colspan="3">'.$langs->trans("DisableForgetPasswordLinkOnLogonPage").'</td>'; 471print '<td align="center" width="60">'; 472if (!empty($conf->global->MAIN_SECURITY_DISABLEFORGETPASSLINK)) 473{ 474 print img_picto($langs->trans("Active"), 'tick'); 475} 476print '</td>'; 477if (empty($conf->global->MAIN_SECURITY_DISABLEFORGETPASSLINK)) 478{ 479 print '<td align="center" width="100">'; 480 print '<a href="security.php?action=activate_MAIN_SECURITY_DISABLEFORGETPASSLINK">'.$langs->trans("Activate").'</a>'; 481 print "</td>"; 482} 483if (!empty($conf->global->MAIN_SECURITY_DISABLEFORGETPASSLINK)) 484{ 485 print '<td align="center" width="100">'; 486 print '<a href="security.php?action=disable_MAIN_SECURITY_DISABLEFORGETPASSLINK">'.$langs->trans("Disable").'</a>'; 487 print "</td>"; 488} 489print "</td>"; 490print '</tr>'; 491 492 493print '</table>'; 494print '</form>'; 495print '<br>'; 496 497if (GETPOST('info', 'int') > 0) 498{ 499 if (function_exists('password_hash')) 500 { 501 print $langs->trans("Note: The function password_hash exists on your PHP")."<br>\n"; 502 } else { 503 print $langs->trans("Note: The function password_hash does not exists on your PHP")."<br>\n"; 504 } 505 print 'MAIN_SECURITY_HASH_ALGO = '.$conf->global->MAIN_SECURITY_HASH_ALGO."<br>\n"; 506 print 'MAIN_SECURITY_SALT = '.$conf->global->MAIN_SECURITY_SALT."<br>\n"; 507} 508 509print '</div>'; 510 511// End of page 512llxFooter(); 513$db->close(); 514