Commit a5ef7662 authored by Benjamin Sonntag's avatar Benjamin Sonntag
Browse files

Adding Password Policy management to AlternC \!

parent 161a04ce
......@@ -30,6 +30,7 @@ bureau/admin/adm_list.php -text
bureau/admin/adm_login.php -text
bureau/admin/adm_mxaccount.php -text
bureau/admin/adm_panel.php -text
bureau/admin/adm_passpolicy.php -text
bureau/admin/adm_quotadoedit.php -text
bureau/admin/adm_quotaedit.php -text
bureau/admin/adm_slaveaccount.php -text
......
......@@ -55,6 +55,7 @@ include_once("head.php");
<li class="lst1"><a href="adm_variables.php"><?php __("Configure AlternC variables"); ?></a></li>
<li class="lst2"><a href="quota_show_all.php"><?php __("Show all quotas"); ?></a></li>
<li class="lst1"><a href="stats_members.php"><?php __("Account creation statistics"); ?></a></li>
<li class="lst2"><a href="adm_passpolicy.php"><?php __("Password Policies"); ?></a></li>
<?php
// here we include any "adminmenu_*" file content
......
<?php
/*
adm_passpolicy.php
----------------------------------------------------------------------
AlternC - Web Hosting System
Copyright (C) 2002-2010 by the AlternC Development Team.
http://alternc.org/
----------------------------------------------------------------------
LICENSE
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License (GPL)
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
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.
To read the license please visit http://www.gnu.org/copyleft/gpl.html
----------------------------------------------------------------------
Original Author of file: Benjamin Sonntag
Purpose of file: Manage the password policy for AlternC
----------------------------------------------------------------------
*/
require_once("../class/config.php");
if (!$admin->enabled) {
__("This page is restricted to authorized staff");
exit();
}
$fields = array (
"edit" => array ("request", "string", ""),
"minsize" => array ("request", "integer", "0"),
"maxsize" => array ("request", "integer", "64"),
"classcount" => array ("request", "integer", "0"),
"allowlogin" => array ("request", "integer", "0"),
);
getFields($fields);
include_once("head.php");
?>
<h3><?php __("Manage Password Policy"); ?></h3>
<?php
if ($error) {
echo "<p class=\"error\">$error</p>";
}
$c=$admin->listPasswordPolicies();
//echo "<pre>"; print_r($c); echo "</pre>";
if ($doedit) {
if (!$c[$doedit]) {
echo "<p class=\"error\">"._("Policy not found")."</p>";
} else {
// Change it ;)
if ($admin->editPolicy($doedit,$minsize,$maxsize,$classcount,$allowlogin)) {
echo "<p class=\"info\">"._("Policy changed")."</p>";
unset($edit);
$c=$admin->listPasswordPolicies();
} else {
echo "<p class=\"error\">"._("Cannot edit the policy, an error occurred")."</p>";
}
}
}
if ($edit) {
if (!$c[$edit]) {
echo "<p class=\"error\">"._("Policy not found")."</p>";
} else {
?>
<p><?php __("Please choose which policy you want to apply to this password kind:"); ?></p>
<p><b><?php echo $c[$edit]["description"]; ?></b></p>
<form method="post" name="adm_passpolicy.php">
<input type="hidden" name="doedit" value="<?php echo $edit; ?>"/>
<table class="formv">
<tr class="lt1">
<th><?php __("Minimum Password Size:"); ?></th>
<td><select name="minsize" id="minsize"><?php for($i=0;$i<=64;$i++) {
echo "<option";
if ($c[$edit]["minsize"]==$i) echo " selected=\"selected\"";
echo ">$i</option>";
}
?></td></tr>
<tr class="lst2"><th><?php __("Maximum Password Size:"); ?></th>
<td><select name="maxsize" id="maxsize"><?php for($i=0;$i<=64;$i++) {
echo "<option";
if ($c[$edit]["maxsize"]==$i) echo " selected=\"selected\"";
echo ">$i</option>";
}
?></td></tr>
<tr class="lst1"> <th><?php __("In how many classes of characters must be the password (at least):"); ?></th>
<td><select name="classcount" id="classcount"><?php for($i=0;$i<=4;$i++) {
echo "<option";
if ($c[$edit]["classcount"]==$i) echo " selected=\"selected\"";
echo ">$i</option>";
}
?></td></tr>
<tr class="lst2"> <th><?php __("Do we allow the password to be like the login?"); ?></th>
<td><select name="allowlogin" id="allowlogin"><?php
echo "<option value=\"0\">"._("No")."</option>";
echo "<option value=\"1\""; if ($c[$edit]["allowlogin"]) echo " selected=\"selected\"";
echo ">"._("Yes")."</option>";
?></td></tr>
</table>
<p><input type="submit" name="go" value="<?php __("Apply this password policy"); ?>" /> &nbsp;
<input type="button" name="cancel" value="<?php __("Cancel and go back to the policy list"); ?>" onclick="history.go(-1);" /></p>
</form>
<p><?php __("The classes of characters are : <br />1. Low-case letters (a-z)<br />2. Upper-case letters (A-Z)<br />3. Figures (0-9)<br />4. Ascii symbols (!\"#$%&'()*+,-./:;<=>?@[\\]^_`)<br />5. Non-Ascii symbols (~...)"); ?></p>
</p>
<?php
require_once("foot.php");
exit();
}
}
if (is_array($c)) {
?>
<p>
<?php __("Here is the list of the password policies for each place a password may be needed in AlternC's services. For each of those password kind, you can choose which policy will be applied to passwords. A policy is a minimum and maximum password size, and how many classes of characters must appear in the password. You can also forbid (or not) to use the login or part of it as a password."); ?>
</p>
<table border="0" cellpadding="4" cellspacing="0">
<tr><th rowspan="2"></th><th rowspan="2"><?php __("Password Kind"); ?></th><th colspan="4"><?php __("Password Policy"); ?></th></tr>
<tr>
<th><?php __("Min Size"); ?></th>
<th><?php __("Max Size"); ?></th>
<th><?php __("Complexity"); ?></th>
<th><?php __("Allow Password=Login?"); ?></th>
</tr>
<?php
$col=1;
foreach($c as $v) {
$col=3-$col;
?>
<tr class="lst<?php echo $col; ?>">
<td class="center"><a href="adm_passpolicy.php?edit=<?php echo urlencode($v["name"]); ?>"><img src="images/edit.png" alt="<?php __("Change password policy"); ?>" /></a></td>
<td><?php echo $v["description"]; ?></td>
<td class="center"><?php echo $v["minsize"]; ?></td>
<td class="center"><?php echo $v["maxsize"]; ?></td>
<td class="center"><?php echo $v["classcount"]; ?></td>
<td class="center"><?php if ($v["allowlogin"]) __("Yes"); else __("No"); ?></td>
</tr>
<?php
}
?>
</table>
<?php } ?>
<?php include_once("foot.php"); ?>
......@@ -1104,8 +1104,64 @@ EOF;
*
*/
function checkPolicy($policy,$login,$password) {
global $db;
global $db,$err;
$pol=$this->listPasswordPolicies();
if (!$pol[$policy]) {
$err->raise("admin",14);
return false;
}
$pol=$pol[$policy];
// Ok, now let's check it :
$plen=strlen($password);
if ($plen<$pol["minsize"]) {
$err->raise("admin",15);
return false;
}
if ($plen>$pol["maxsize"]) {
$err->raise("admin",16);
return false;
}
if (!$pol["allowlogin"]) {
// We do misc check on password versus login :
$l2=str_replace("_","@",$l2);
$l2=str_replace(".","@",$l2);
$logins=explode("@",$login);
$logins[]=$login;
foreach($logins as $l) {
if (strpos($l,$password)!==false) {
$err->raise("admin",17);
return false;
}
}
}
if ($pol["classcount"]>0) {
$cls=array(0,0,0,0,0);
for($i=0;$i<strlen($password);$i++) {
$p=substr($password,$i,1);
if (strpos("abcdefghijklmnopqrstuvwxyz",$p)!==false) {
$cls[0]=1;
} elseif (strpos("ABCDEFGHIJKLMNOPQRSTUVWXYZ",$p)!==false) {
$cls[1]=1;
} elseif (strpos("0123456789",$p)!==false) {
$cls[2]=1;
} elseif (strpos('!"#$%&\'()*+,-./:;<=>?@[\\]^_`',$p)!==false) {
$cls[3]=1;
} else {
$cls[4]=1;
}
} // foreach
$clc=array_sum($cls);
if ($clc<$pol["classcount"]) {
$err->raise("admin",18,$pol["classcount"],$clc);
return false;
}
}
return true; // congratulations !
}
......
......@@ -162,7 +162,7 @@ class m_ftp {
* @return boolean TRUE si le compte a t modifi, FALSE si une erreur est survenue.
*/
function put_ftp_details($id,$prefixe,$login,$pass,$dir) {
global $mem,$db,$err,$bro,$cuid;
global $mem,$db,$err,$bro,$cuid,$admin;
$err->log("ftp","put_ftp_details",$id);
$db->query("SELECT count(*) AS cnt FROM ftpusers WHERE id='$id' and uid='$cuid';");
$db->next_record();
......@@ -197,6 +197,14 @@ class m_ftp {
return false;
}
if (trim($pass)) {
// Check this password against the password policy using common API :
if (is_callable(array($admin,"checkPolicy"))) {
if (!$admin->checkPolicy("ftp",$prefixe.$login,$pass)) {
return false; // The error has been raised by checkPolicy()
}
}
$db->query("UPDATE ftpusers SET name='".$prefixe.$login."', password='', encrypted_password=ENCRYPT('$pass'), homedir='/var/alternc/html/$l/$lo/$dir', uid='$cuid' WHERE id='$id';");
} else {
$db->query("UPDATE ftpusers SET name='".$prefixe.$login."', homedir='/var/alternc/html/$l/$lo/$dir', uid='$cuid' WHERE id='$id';");
......@@ -204,6 +212,7 @@ class m_ftp {
return true;
}
/* ----------------------------------------------------------------- */
/** Efface le compte ftp spcifi.
* @param integer $id Numro du compte FTP supprimer.
......@@ -233,7 +242,7 @@ class m_ftp {
*
*/
function add_ftp($prefixe,$login,$pass,$dir) {
global $mem,$db,$err,$quota,$bro,$cuid;
global $mem,$db,$err,$quota,$bro,$cuid,$admin;
$err->log("ftp","add_ftp",$prefixe."_".$login);
$dir=$bro->convertabsolute($dir);
if (substr($dir,0,1)=="/") {
......@@ -263,6 +272,14 @@ class m_ftp {
$err->raise("ftp",6);
return false;
}
// Check this password against the password policy using common API :
if (is_callable(array($admin,"checkPolicy"))) {
if (!$admin->checkPolicy("ftp",$prefixe.$login,$pass)) {
return false; // The error has been raised by checkPolicy()
}
}
if ($quota->cancreate("ftp")) {
$db->query("INSERT INTO ftpusers (name,password, encrypted_password,homedir,uid) VALUES ('".$prefixe.$login."', '', ENCRYPT('$pass'), '/var/alternc/html/$l/$lo/$dir', '$cuid')");
return true;
......
......@@ -48,6 +48,14 @@ class m_hta {
}
/**
* Password kind used in this class (hook for admin class)
*/
function alternc_password_policy() {
return array("hta"=>"Protected folders passwords");
}
/*---------------------------------------------------------------------------*/
/**
* Create a protected folder (.htaccess et .htpasswd)
......@@ -194,7 +202,7 @@ class m_hta {
* @return boolean TRUE if the user has been added, or FALSE if an error occurred
*/
function add_user($user,$password,$dir) {
global $err, $bro;
global $err, $bro, $admin;
$err->log("hta","add_user",$user."/".$dir);
$absolute=$bro->convertabsolute($dir,0);
if (!file_exists($absolute)) {
......@@ -202,6 +210,13 @@ class m_hta {
return false;
}
if (checkloginmail($user)){
// Check this password against the password policy using common API :
if (is_callable(array($admin,"checkPolicy"))) {
if (!$admin->checkPolicy("hta",$user,$password)) {
return false; // The error has been raised by checkPolicy()
}
}
$file = @fopen("$absolute/.htpasswd","a+");
if (!$file) {
$err->raise("hta",12);
......@@ -279,13 +294,21 @@ class m_hta {
* @return boolean TRUE if the password has been changed, or FALSE if an error occurred
*/
function change_pass($user,$newpass,$dir) {
global $bro,$err;
global $bro,$err,$admin;
$err->log("hta","change_pass",$user."/".$dir);
$absolute=$bro->convertabsolute($dir,0);
if (!file_exists($absolute)) {
$err->raise("hta",8,$dir);
return false;
}
// Check this password against the password policy using common API :
if (is_callable(array($admin,"checkPolicy"))) {
if (!$admin->checkPolicy("hta",$user,$password)) {
return false; // The error has been raised by checkPolicy()
}
}
touch("$absolute/.htpasswd.new");
$file = fopen("$absolute/.htpasswd","r");
$newf = fopen("$absolute/.htpasswd.new","a");
......
......@@ -288,7 +288,7 @@ class m_mail {
* @return boolean TRUE si l'email a bien t modifi, FALSE si une erreur s'est produite.
*/
function put_mail_details($mail,$pop,$pass,$alias) {
global $err,$cuid,$db;
global $err,$cuid,$db,$admin;
$err->log("mail","put_mail_details",$mail);
$mail=strtolower($mail);
$t=explode("@",$mail);
......@@ -336,6 +336,12 @@ class m_mail {
$err->raise("mail",4);
return false;
}
// Check this password against the password policy using common API :
if (is_callable(array($admin,"checkPolicy"))) {
if (!$admin->checkPolicy("pop",$email."@".$dom,$pass)) {
return false; // The error has been raised by checkPolicy()
}
}
}
$db->query("UPDATE mail_domain SET alias='".implode("\n",$account)."', pop='$pop' WHERE mail='$mail';");
......@@ -368,7 +374,7 @@ class m_mail {
* @return boolean TRUE si le compte a bien t cr, FALSE si une erreur s'est produite.
*/
function add_mail($dom,$mail,$pop,$pass,$alias) {
global $quota,$err,$cuid,$db;
global $quota,$err,$cuid,$db,$admin;
$err->log("mail","add_mail",$dom."/".$mail);
$account=array();
$mail=strtolower($mail);
......@@ -384,6 +390,16 @@ class m_mail {
$err->raise("mail",4);
return false;
}
if ($pop=="1") {
// Check this password against the password policy using common API :
if (is_callable(array($admin,"checkPolicy"))) {
if (!$admin->checkPolicy("pop",$mail."@".$dom,$pass)) {
return false; // The error has been raised by checkPolicy()
}
}
}
if ($pop=="1"){
$account[]=$mail."_".$dom;
}
......
......@@ -62,7 +62,7 @@ class m_mysql {
* Password kind used in this class (hook for admin class)
*/
function alternc_password_policy() {
return array("mysql_users"=>"MySQL users");
return array("mysql"=>"MySQL users");
}
......@@ -251,7 +251,7 @@ class m_mysql {
* @return boolean TRUE if the password has been successfully changed, FALSE else.
*/
function put_mysql_details($password) {
global $db,$err,$mem,$cuid;
global $db,$err,$mem,$cuid,$admin;
$err->log("mysql","put_mysql_details");
$db->query("SELECT * FROM db WHERE uid='$cuid';");
if (!$db->num_rows()) {
......@@ -265,6 +265,14 @@ class m_mysql {
$err->raise("mysql",8);
return false;
}
// Check this password against the password policy using common API :
if (is_callable(array($admin,"checkPolicy"))) {
if (!$admin->checkPolicy("mysql",$login,$password)) {
return false; // The error has been raised by checkPolicy()
}
}
// Update all the "pass" fields for this user :
$db->query("UPDATE db SET pass='$password' WHERE uid='$cuid';");
$db->query("SET PASSWORD FOR '$login'@'$this->client' = PASSWORD('$password')");
......@@ -277,7 +285,7 @@ class m_mysql {
* It also create the first database.
*/
function new_mysql($password) {
global $db,$err,$mem,$cuid;
global $db,$err,$mem,$cuid,$admin;
$err->log("mysql","new_mysql");
if (strlen($password)>16) {
$err->raise("mysql",8);
......@@ -290,6 +298,14 @@ class m_mysql {
}
$login=$mem->user["login"];
$dbname=$mem->user["login"];
// Check this password against the password policy using common API :
if (is_callable(array($admin,"checkPolicy"))) {
if (!$admin->checkPolicy("mysql",$login,$password)) {
return false; // The error has been raised by checkPolicy()
}
}
// OK, creation now...
$db->query("INSERT INTO db (uid,login,pass,db) VALUES ('$cuid','".$login."','$password','".$dbname."');");
// give everything but GRANT on $user.*
......@@ -456,8 +472,17 @@ class m_mysql {
return $c;
}
/* ------------------------------------------------------------ */
/**
* Create a new user in MySQL rights tables
* @param $usern the username (we will add _[alternc-account] to it)
* @param $password The password for this username
* @param $passconf The password confirmation
* @return TRUE if the user has been created in MySQL or FALSE if an error occurred
**/
function add_user($usern,$password,$passconf) {
global $db,$err,$quota,$mem,$cuid;
global $db,$err,$quota,$mem,$cuid,$admin;
$err->log("mysql","add_user",$usern);
$user=addslashes($mem->user["login"]."_$usern");
......@@ -486,6 +511,12 @@ class m_mysql {
return false;
}
// Check this password against the password policy using common API :
if (is_callable(array($admin,"checkPolicy"))) {
if (!$admin->checkPolicy("mysql",$user,$password)) {
return false; // The error has been raised by checkPolicy()
}
}
// We create the user account (the "file" right is the only one we need globally to be able to use load data into outfile)
$db->query("GRANT file ON *.* TO '$user'@'$this->client' IDENTIFIED BY '$pass';");
......@@ -494,6 +525,14 @@ class m_mysql {
return true;
}
/* ------------------------------------------------------------ */
/**
* Delete a new user in MySQL rights tables
* @param $user the username (we will add _[alternc-account] to it) to delete
* @return TRUE if the user has been deleted in MySQL or FALSE if an error occurred
**/
function del_user($user) {
global $db,$err,$mem,$cuid,$L_MYSQL_DATABASE;
$err->log("mysql","del_user",$user);
......@@ -518,6 +557,13 @@ class m_mysql {
return true;
}
/* ------------------------------------------------------------ */
/**
* Return the list of the database rights of user $user
* @param $user the username
* @return array An array of database name and rights
**/
function get_user_dblist($user) {
global $db,$err,$mem,$cuid,$L_MYSQL_DATABASE;
$err->log("mysql","get_user_dblist");
......@@ -536,6 +582,17 @@ class m_mysql {
return $r;
}
/* ------------------------------------------------------------ */
/**
* Set the access rights of user $user to database $dbn to be rights $rights
* @param $user the username to give rights to
* @param $dbn The database to give rights to
* @param $rights The rights as an array of MySQL keywords (insert, select ...)
* @return boolean TRUE if the rights has been applied or FALSE if an error occurred
*
**/
function set_user_rights($user,$dbn,$rights) {
global $mem, $db;
......@@ -580,8 +637,7 @@ class m_mysql {
}
}
// On remet zro tous les droits de l'utilisateur
// We reset all user rights on this DB :
$db->query("SELECT * FROM mysql.db WHERE User = '$usern' AND Db = '$dbname';");
if($db->num_rows())
$db->query("REVOKE ALL PRIVILEGES ON $dbname.* FROM '$usern'@'$this->client';");
......@@ -593,6 +649,8 @@ class m_mysql {
return TRUE;
}
} /* Class m_mysql */
?>
......@@ -73,6 +73,27 @@ msgstr "Ce TLD existe d
msgid "err_admin_13"
msgstr "Le login est trop long (16 caractères maximum)"
#. There is no password policy available, this password has been denied (it's a coding error though...)
msgid "err_admin_14"
msgstr "La politique de mot de passe demandée n'a pas été trouvée, Ce password est refusé (c'est une erreur de programmation ...)"
#. The password is too short according to your policy, please check it
msgid "err_admin_15"
msgstr "Le mot de passe est trop court selon votre politique de mot de passe, merci de vérifier"
#. The password is too long according to your policy, please check it
msgid "err_admin_16"
msgstr "Le mot de passe est trop long selon votre politique de mot de passe, merci de vérifier"
#. The password cannot be part of the login according to your policy, please check it
msgid "err_admin_17"
msgstr "Le mot de passe ne peut pas être le même que le nom d'utilisateur (ou quelque chose de similaire) selon votre politique de mot de passe, merci de vérifier"
#. The password must contains characters from %s different classes according to your policy (it only contains %s classes), please check it
msgid "err_admin_18"
msgstr "Le mot de passe doit contenir des caractères de %s classes différentes selon votre politique de mot de passe (il n'en contient que %s), merci de vérifier"
#. Domain names
msgid "quota_dom"
msgstr "Noms de domaines"
......@@ -378,6 +399,10 @@ msgstr "L'utilisateur '%s' existe d
msgid "err_hta_11"
msgstr "Veuillez saisir un nom d'utilisateur valide"
#. The file .htpasswd does not exist
msgid "err_hta_12"
msgstr "Le fichier .htpasswd n'existe pas"
#. Email Accounts
msgid "quota_mail"
msgstr "Comptes emails"
......@@ -730,6 +755,16 @@ msgstr ""
msgid "%3$d-%2$d-%1$d %4$d:%5$d"
msgstr "%1$d/%2$d/%3$d %4$d:%5$d"
msgid "AlternC's account password"
msgstr "Mots de passe des comptes AlternC"
msgid "POP/IMAP account passwords"
msgstr "Mots de passe des comptes POP/IMAP"
msgid "Protected folders passwords"
msgstr "Mots de passe des dossiers protégés"
#~ msgid "Editing subdomain %s"
#~ msgstr "Edition du sous-domaine %s"