diff --git a/.gitattributes b/.gitattributes index ec952516..62cdd2a7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -202,6 +202,7 @@ bureau/admin/images/plus.png -text bureau/admin/images/quota.png -text bureau/admin/images/stat.png -text bureau/admin/index.php -text +bureau/admin/ip_main.php -text bureau/admin/js/alternc.js -text bureau/admin/js/jquery.min.js -text bureau/admin/js/jquery_ui/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png -text @@ -251,6 +252,7 @@ bureau/admin/menu_aide.php -text bureau/admin/menu_brouteur.php -text bureau/admin/menu_dom.php -text bureau/admin/menu_ftp.php -text +bureau/admin/menu_ip.php -text bureau/admin/menu_lang.php -text bureau/admin/menu_mail.php -text bureau/admin/menu_mem.php -text @@ -428,6 +430,7 @@ install/upgrades/0.9.6.sql -text install/upgrades/0.9.7.sql -text install/upgrades/0.9.9.sql -text install/upgrades/1.0.sql -text +install/upgrades/1.1.sql -text install/upgrades/README -text man/alternc-admintools.8 -text man/alternc-admintools.fr.8 -text diff --git a/bureau/admin/ip_main.php b/bureau/admin/ip_main.php new file mode 100644 index 00000000..bf99ca20 --- /dev/null +++ b/bureau/admin/ip_main.php @@ -0,0 +1,128 @@ + array ("get", "integer", ""), + "is_subnet" => array ("post", "string", ""), + "id" => array ("post", "integer", 0), + "ip" => array ("post", "string", ""), + "subnet" => array ("post", "integer" ,0), + "infos" => array ("post", "string" ,""), +); +getFields($fields); + +if (!empty($delete_id)) { + if (! $authip->ip_delete($delete_id)) { + $error="Error during recording"; + } +} + +if (!empty($is_subnet) && !empty($ip)) { + if (! $authip->ip_save($id, $ip, $subnet, $infos)) { + $error="Error during recording"; + } +} + +?> + +

+
+
+ + +

+ + + + + + + + + + + +
+ + + list_ip() as $i) { + if (checkip($i['ip'])) { + if ($i['subnet']==32) { + $txt="Address IPv4"; + $ip="${i['ip']}"; + } else { + $txt="Subnet IPv4"; + $ip="${i['ip']}/${i['subnet']}"; + } + } elseif (checkipv6($i['ip'])) { + if ($i['subnet']==128) { + $txt="Address IPv6"; + $ip="${i['ip']}"; + } else { + $txt="Subnet IPv6"; + $ip="${i['ip']}/${i['subnet']}"; + } + } + echo ""; + ?> + + + + + +
$txt$ip${i['infos']}
+
+
+ - );" > + +
+ +


+ +
+ + +

+

+
+ / +

+

+
+ +

+ " /> +
+
+
+
+ + + diff --git a/bureau/admin/menu_ip.php b/bureau/admin/menu_ip.php new file mode 100644 index 00000000..7f91404d --- /dev/null +++ b/bureau/admin/menu_ip.php @@ -0,0 +1,37 @@ + + + diff --git a/bureau/class/m_admin.php b/bureau/class/m_admin.php index 7c4f6f9c..4b1370b3 100644 --- a/bureau/class/m_admin.php +++ b/bureau/class/m_admin.php @@ -108,7 +108,7 @@ class m_admin { $db->next_record(); reset($db->Record); while (list($key,$val)=each($db->Record)) { - $c[$key]=$val; + $c[$key]=$val; } } return $c; @@ -141,7 +141,7 @@ class m_admin { $db->next_record(); reset($db->Record); while (list($key,$val)=each($db->Record)) { - $c[$key]=$val; + $c[$key]=$val; } } @@ -150,7 +150,7 @@ class m_admin { $db->next_record(); reset($db->Record); while (list($key,$val)=each($db->Record)) { - $c[$key]=$val; + $c[$key]=$val; } } @@ -350,10 +350,10 @@ class m_admin { if (!$db->f("cnt")) { $db->query("SELECT max(m.uid)+1 as nextid FROM membres m"); if (!$db->next_record()) { - $uid=2000; + $uid=2000; } else { - $uid=$db->Record["nextid"]; - if ($uid<=2000) $uid=2000; + $uid=$db->Record["nextid"]; + if ($uid<=2000) $uid=2000; } // on le créé ensuite dans system.membres et system.local $db->query("INSERT INTO membres (uid,login,pass,mail,creator,canpass,type,created, notes) VALUES ('$uid','$login','$pass','$mail','$cuid','$canpass', '$type', NOW(), '$notes');"); @@ -363,9 +363,9 @@ class m_admin { // Declenchons les autres classes. $mem->su($uid); foreach($classes as $c) { - if (method_exists($GLOBALS[$c],"alternc_add_member")) { - $GLOBALS[$c]->alternc_add_member(); - } + if (method_exists($GLOBALS[$c],"alternc_add_member")) { + $GLOBALS[$c]->alternc_add_member(); + } } $mem->unsu(); return $uid; @@ -477,9 +477,9 @@ EOF; } if (($db->query("UPDATE local SET nom='$nom', prenom='$prenom' WHERE uid='$uid';")) - &&($db->query("UPDATE membres SET mail='$mail', canpass='$canpass', enabled='$enabled', `type`='$type', notes='$notes' $ssq WHERE uid='$uid';"))){ + &&($db->query("UPDATE membres SET mail='$mail', canpass='$canpass', enabled='$enabled', `type`='$type', notes='$notes' $ssq WHERE uid='$uid';"))){ if($_POST['reset_quotas'] == "on") - $quota->addquotas(); + $quota->addquotas(); $this->renew_update($uid, $duration); return true; } @@ -584,21 +584,21 @@ EOF; // Send the event to the other classes : foreach($classes as $c) { - if (method_exists($GLOBALS[$c],"alternc_del_member")) { - $GLOBALS[$c]->alternc_del_member(); - } + if (method_exists($GLOBALS[$c],"alternc_del_member")) { + $GLOBALS[$c]->alternc_del_member(); + } } if (($db->query("DELETE FROM membres WHERE uid='$uid';")) && - ($db->query("DELETE FROM local WHERE uid='$uid';"))) { - exec("/usr/lib/alternc/mem_del ".$tt["login"]); - $mem->unsu(); - // If this user was (one day) an administrator one, he may have a list of his own accounts. Let's associate those accounts to nobody as a creator. - $db->query("UPDATE membres SET creator=2000 WHERE creator='$uid';"); - return true; + ($db->query("DELETE FROM local WHERE uid='$uid';"))) { + exec("/usr/lib/alternc/mem_del ".$tt["login"]); + $mem->unsu(); + // If this user was (one day) an administrator one, he may have a list of his own accounts. Let's associate those accounts to nobody as a creator. + $db->query("UPDATE membres SET creator=2000 WHERE creator='$uid';"); + return true; } else { - $err->raise("admin",2); - $mem->unsu(); - return false; + $err->raise("admin",2); + $mem->unsu(); + return false; } } @@ -882,47 +882,47 @@ EOF; $dontexist=false; // Check du domaine if ($c["gesdns"]==1) { - // Check du NS qui pointe chez nous - $out=array(); - exec("dig +short NS ".escapeshellarg($c["domaine"]),$out); - if (count($out)==0) { - $dontexist=true; - } else { - if (!in_array($L_NS1.".",$out) || !in_array($L_NS2.".",$out)) { - $errno=1; $errstr.="NS for this domain are not $L_NS1 and $L_NS2 BUT ".implode(",",$out)."\n"; - } - } + // Check du NS qui pointe chez nous + $out=array(); + exec("dig +short NS ".escapeshellarg($c["domaine"]),$out); + if (count($out)==0) { + $dontexist=true; + } else { + if (!in_array($L_NS1.".",$out) || !in_array($L_NS2.".",$out)) { + $errno=1; $errstr.="NS for this domain are not $L_NS1 and $L_NS2 BUT ".implode(",",$out)."\n"; + } + } } if ($c["gesmx"]==1 && !$dontexist) { - $out=array(); - exec("dig +short MX ".escapeshellarg($c["domaine"]),$out); - $out2=array(); - foreach($out as $o) { - list($t,$out2[])=explode(" ",$o); - } - if (!in_array($L_MX.".",$out2)) { - $errno=1; $errstr.="MX is not $L_MX BUT ".implode(",",$out2)."\n"; - } + $out=array(); + exec("dig +short MX ".escapeshellarg($c["domaine"]),$out); + $out2=array(); + foreach($out as $o) { + list($t,$out2[])=explode(" ",$o); + } + if (!in_array($L_MX.".",$out2)) { + $errno=1; $errstr.="MX is not $L_MX BUT ".implode(",",$out2)."\n"; + } } if (!$dontexist) { - // On liste les sous-domaine et on verifie qu'ils pointent bien chez nous... - $db->query("SELECT * FROM sub_domaines WHERE domaine='".addslashes($c["domaine"])."' ORDER BY sub;"); - while ($db->next_record()) { - $d=$db->Record; - if ($d["type"]==0) { - // Check l'IP : - $out=array(); - exec("dig +short A ".escapeshellarg($d["sub"].(($d["sub"]!="")?".":"").$c["domaine"]),$out); - if (!in_array($L_PUBLIC_IP,$out)) { - $errstr.="subdomain '".$d["sub"]."' don't point to $L_PUBLIC_IP but to ".implode(",",$out)."\n"; - $errno=1; - } - } - } + // On liste les sous-domaine et on verifie qu'ils pointent bien chez nous... + $db->query("SELECT * FROM sub_domaines WHERE domaine='".addslashes($c["domaine"])."' ORDER BY sub;"); + while ($db->next_record()) { + $d=$db->Record; + if ($d["type"]==0) { + // Check l'IP : + $out=array(); + exec("dig +short A ".escapeshellarg($d["sub"].(($d["sub"]!="")?".":"").$c["domaine"]),$out); + if (!in_array($L_PUBLIC_IP,$out)) { + $errstr.="subdomain '".$d["sub"]."' don't point to $L_PUBLIC_IP but to ".implode(",",$out)."\n"; + $errno=1; + } + } + } } if ($dontexist) { - $errno=2; - $errstr="Domain don't exist anymore !"; + $errno=2; + $errstr="Domain don't exist anymore !"; } if ($errno==0) $errstr="OK"; $checked[$c["domaine"]]=array("errno"=>$errno, "errstr"=>$errstr); diff --git a/etc/alternc/menulist.txt b/etc/alternc/menulist.txt index 1efa9cc1..3fac3bde 100644 --- a/etc/alternc/menulist.txt +++ b/etc/alternc/menulist.txt @@ -7,6 +7,7 @@ menu_ftp.php menu_sta2.php menu_sql.php menu_quota.php +menu_ip.php menu_aide.php menu_lang.php menu_mem.php diff --git a/etc/alternc/templates/proftpd/proftpd.conf b/etc/alternc/templates/proftpd/proftpd.conf index c3b9eafd..e8a96933 100644 --- a/etc/alternc/templates/proftpd/proftpd.conf +++ b/etc/alternc/templates/proftpd/proftpd.conf @@ -75,6 +75,39 @@ SQLAuthTypes Crypt # Only mysql authentication enabled SQLAuthenticate users AuthPAM off + +# What this SQL query do : +# - check if there is IP limitation for this account. If there isn't, allow everyone (by returning a TRUE) +# - if there is some limitation : +# - convert ip to integer (if convert impossible, it's an ipv6. Mysql6 will have ipv6 function, for mysql5 alternc create some function) +# - calculate the last IP of the subnet. If the subnet is 32, return the original IP +# - check that the user's ip is in an allowed range +# - add the IP range who are defined as "always from everyone" (uid=0. Not uid=2000, because we could want to have some limitation for the root account) +SQLUserWhereClause " \ +true in ( \ +select if(count(*)>0,false,(select value from variable where name='auth_ip_ftp_default_yes')) \ +from authorised_ip_affected aia, ftpusers f \ +where cast(aia.parameters as signed integer)=f.id and f.name='%U'\ +UNION \ +select \ + ifnull(inet_aton('%h'),inet_aton6('%h')) \ + between ifnull(inet_aton(ip),inet_aton6(ip)) \ + and ifnull( inet_aton(ip) + if(subnet=32,0,conv( lpad('',(32-subnet),'1'), 2 , 10)) , inet_aton6(ip) + conv( lpad('',(128-subnet),'1'), 2 , 10) ) \ +from authorised_ip ai, authorised_ip_affected aia, ftpusers f \ +where f.name='%U' and cast(aia.parameters as signed integer)=f.id and ai.id=aia.authorised_ip_id and aia.protocol='ftp' \ +UNION \ +select \ + ifnull(inet_aton('%h'),inet_aton6('%h')) \ + between ifnull(inet_aton(ip),inet_aton6(ip)) \ + and ifnull( inet_aton(ip) + if(subnet=32,0,conv( lpad('',(32-subnet),'1'), 2 , 10)) , inet_aton6(ip) + conv( lpad('',(128-subnet),'1'), 2 , 10) ) \ +from authorised_ip ai \ +where ai.uid=0 \ +) \ +" + +# Uncomment this line if you want to debug Proftpd's SQL +#SQLLogFile /var/log/proftpd/sql.log + # Default : www-data.www-data SQLDefaultGID 33 SQLDefaultUID 33 diff --git a/install/upgrades/1.1.sql b/install/upgrades/1.1.sql new file mode 100644 index 00000000..ea7b153b --- /dev/null +++ b/install/upgrades/1.1.sql @@ -0,0 +1,81 @@ +-- Add function who are not in mysql 5 to be able ton convert ipv6 to decimal (and reverse it) +DELIMITER // +CREATE FUNCTION INET_ATON6(n CHAR(39)) +RETURNS DECIMAL(39) UNSIGNED +DETERMINISTIC +BEGIN + RETURN CAST(CONV(SUBSTRING(n FROM 1 FOR 4), 16, 10) AS DECIMAL(39)) + * 5192296858534827628530496329220096 -- 65536 ^ 7 + + CAST(CONV(SUBSTRING(n FROM 6 FOR 4), 16, 10) AS DECIMAL(39)) + * 79228162514264337593543950336 -- 65536 ^ 6 + + CAST(CONV(SUBSTRING(n FROM 11 FOR 4), 16, 10) AS DECIMAL(39)) + * 1208925819614629174706176 -- 65536 ^ 5 + + CAST(CONV(SUBSTRING(n FROM 16 FOR 4), 16, 10) AS DECIMAL(39)) + * 18446744073709551616 -- 65536 ^ 4 + + CAST(CONV(SUBSTRING(n FROM 21 FOR 4), 16, 10) AS DECIMAL(39)) + * 281474976710656 -- 65536 ^ 3 + + CAST(CONV(SUBSTRING(n FROM 26 FOR 4), 16, 10) AS DECIMAL(39)) + * 4294967296 -- 65536 ^ 2 + + CAST(CONV(SUBSTRING(n FROM 31 FOR 4), 16, 10) AS DECIMAL(39)) + * 65536 -- 65536 ^ 1 + + CAST(CONV(SUBSTRING(n FROM 36 FOR 4), 16, 10) AS DECIMAL(39)) + ; +END; +// +DELIMITER ; +DELIMITER // +CREATE FUNCTION INET_NTOA6(n DECIMAL(39) UNSIGNED) +RETURNS CHAR(39) +DETERMINISTIC +BEGIN + DECLARE a CHAR(39) DEFAULT ''; + DECLARE i INT DEFAULT 7; + DECLARE q DECIMAL(39) UNSIGNED DEFAULT 0; + DECLARE r INT DEFAULT 0; + WHILE i DO + -- DIV doesn't work with nubers > bigint + SET q := FLOOR(n / 65536); + SET r := n MOD 65536; + SET n := q; + SET a := CONCAT_WS(':', LPAD(CONV(r, 10, 16), 4, '0'), a); + + SET i := i - 1; + END WHILE; + + SET a := TRIM(TRAILING ':' FROM CONCAT_WS(':', + LPAD(CONV(n, 10, 16), 4, '0'), + a)); + + RETURN a; + +END; +// +DELIMITER ; + +-- New table for the authorised IP +CREATE TABLE IF NOT EXISTS `authorised_ip` ( + `id` int(10) unsigned NOT NULL auto_increment, + `uid` int(11) unsigned NOT NULL default '0', + `ip` varchar(40) not null, + `subnet` integer(3) not null default 32, + `infos` varchar(255) not null default '', + PRIMARY KEY (`id`), + KEY `uid` (`uid`) +) ENGINE=MyISAM COMMENT='Table with list of authorised ip and subnet'; + +-- Who have authorised IP ? +CREATE TABLE IF NOT EXISTS `authorised_ip_affected` ( + `id` int(10) unsigned NOT NULL auto_increment, + `authorised_ip_id` int(10) unsigned not null, + `protocol` varchar(15) not null, + `parameters` varchar(30) default '', + PRIMARY KEY (`id`) +) ENGINE=MyISAM COMMENT='Table with list of protocol <-> authorised ip and subnet'; + +INSERT IGNORE INTO `variable` (`name` ,`value` ,`comment`) +VALUES ( +'auth_ip_ftp_default_yes', '1', +'This variable set if you want to allow all IP address to access FTP by default. If the user start to define some IP or subnet in the allow list, only those he defined will be allowed. This variable can take two value : 0 or 1.' +); + +