inc/security.php
010bf846
 <?php
c208bd90
 /*
 This file belongs to the Webinterface of schokokeks.org Hosting
 
cf54502a
 Written 2008-2018 by schokokeks.org Hosting, namely
c208bd90
   Bernd Wurst <bernd@schokokeks.org>
   Hanno Böck <hanno@schokokeks.org>
 
 To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.
 
2626dd47
 You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see
c208bd90
 http://creativecommons.org/publicdomain/zero/1.0/
 
 Nevertheless, in case you use a significant part of this code, we ask (but not require, see the license) that you keep the authors' names in place and return your changes to the public. We would be especially happy if you tell us what you're going to do with this code.
 */
010bf846
 
33986238
 require_once('inc/error.php');
28cd1b25
 require_once('vendor/autoload.php');
33986238
 
010bf846
 
28cd1b25
 function strong_password($password, $user = array())
a78147ca
 {
d471bbe4
     $pwcheck = config('pwcheck');
     $result = null;
     if ($pwcheck) {
         DEBUG($pwcheck);
b1aba728
         $req = curl_init($pwcheck);
d471bbe4
         curl_setopt($req, CURLOPT_RETURNTRANSFER, 1);
         curl_setopt($req, CURLOPT_SSL_VERIFYPEER, 1);
         curl_setopt($req, CURLOPT_SSL_VERIFYSTATUS, 1);
         curl_setopt($req, CURLOPT_CONNECTTIMEOUT, 5);
         curl_setopt($req, CURLOPT_TIMEOUT, 5);
         curl_setopt($req, CURLOPT_FOLLOWLOCATION, 0);
b1aba728
         curl_setopt($req, CURLOPT_POST, 1);
         curl_setopt($req, CURLOPT_SAFE_UPLOAD, 1);
c97a9efe
         curl_setopt($req, CURLOPT_POSTFIELDS, "password=".urlencode($password));
d471bbe4
         $result = chop(curl_exec($req));
         DEBUG($result);
     }
     if ($result === 'good') {
         return true;
     } elseif ($result === 'bad') {
23c50b68
         return "Unsere Überprüfung hat ergeben, dass dieses Passwort in bisher veröffentlichten Passwortlisten enthalten ist. Es wird daher nicht akzeptiert.";
d471bbe4
     }
d5f29a79
     // Kein Online-Check eingerichtet oder der request war nicht erfolgreich
     DEBUG('using Zxcvbn for password check!');
     $passwordchecker = new ZxcvbnPhp\Zxcvbn();
3ff36bc9
     if ($user) {
         $strength = $passwordchecker->passwordStrength($password, $user);
     } else {
         $strength = $passwordchecker->passwordStrength($password);
     }
d5f29a79
     DEBUG('password strength: '.$strength['score']);
     if ($strength['score'] < 2) {
         return "Das Passwort ist zu einfach!";
2626dd47
     }
     return true;
a78147ca
 }
 
 
2626dd47
 function filter_input_general($input)
010bf846
 {
2626dd47
     if ($input === null) {
         return null;
     }
902c39b4
     $input = (string) $input;
c42127d3
     $filtered = preg_replace('/[\x00-\x09\x0b-\x0c\x0e-\x1f]/', '', $input);
     if ($filtered !== $input) {
         system_failure("Ihre Daten enthielten ungültige Zeichen!");
         logger(LOG_WARNING, 'inc/security', 'filter_input_general', 'Ungültige Daten!');
     }
     return $filtered;
33986238
 }
 
c42127d3
 function filter_input_oneline($input)
33986238
 {
c42127d3
     if ($input === null) {
         return null;
     }
902c39b4
     $input = (string) $input;
c42127d3
     $filtered = preg_replace('/[\x00-\x1f]/', '', $input);
     if ($filtered !== $input) {
2626dd47
         system_failure("Ihre Daten enthielten ungültige Zeichen!");
902c39b4
         logger(LOG_WARNING, 'inc/security', 'filter_input_oneline', 'Ungültige Daten!');
2626dd47
     }
c42127d3
     return $filtered;
010bf846
 }
 
 
7f815276
 function filter_output_html($data)
 {
     return htmlspecialchars($data, ENT_QUOTES);
 }
 
 
c42127d3
 function verify_input_ascii($data)
 {
902c39b4
     $data = (string) $data;
c42127d3
     $filtered = filter_var($data, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH);
     if ($filtered != $data) {
         logger(LOG_WARNING, 'inc/security', 'verify_input_ascii', 'Ungültige Daten: '.$data);
         system_failure("Ihre Eingabe enthielt ungültige Zeichen");
     }
     return $filtered;
 }
 
 
 function verify_input_identifier($data)
 {
902c39b4
     $data = (string) $data;
c42127d3
     if ($data === "") {
         system_failure("Leerer Bezeichner");
     }
     $filtered = preg_replace("/[^[:alnum:]\_\.\-]/", "", $data);
     if ($filtered !== $data) {
         logger(LOG_WARNING, 'inc/security', 'verify_input_identifier', 'Ungültige Daten: '.$data);
         system_failure("Ihre Daten enthielten ungültige Zeichen!");
     }
     return $filtered;
 }
 
7f815276
 
2626dd47
 function filter_input_username($input)
010bf846
 {
c42127d3
     $username = preg_replace("/[^[:alnum:]\_\.\+\-]/", "", $input);
2626dd47
     if ($username === "") {
         system_failure("Leerer Benutzername!");
     }
     return $username;
391c907d
 }
 
2626dd47
 function verify_input_username($input)
391c907d
 {
2626dd47
     if (filter_input_username($input) != $input) {
         logger(LOG_WARNING, 'inc/security', 'verify_input_username', 'Ungültige Daten: '.$input);
         system_failure("Ihre Daten enthielten ungültige Zeichen!");
     }
010bf846
 }
 
33986238
 
 
2626dd47
 function filter_input_hostname($input, $wildcard=false)
07263d42
 {
2626dd47
     DEBUG('filter_input_hostname("'.$input.'", $wildcard='.$wildcard.')');
ec5ed2ed
     $input = strtolower($input);
c42127d3
     $input = trim($input, "\t\n\r\x00 .");
2626dd47
     if (preg_replace("/[^.]_/", "", $input) != $input) {
         system_failure("Der Unterstrich ist nur als erstes Zeichen eines Hostnames erlaubt.");
     }
ec5ed2ed
     if (preg_replace("/[^[:alnum:]_*\.\-]/u", "", $input) != $input) {
2626dd47
         system_failure("Ihre Daten enthielten ungültige Zeichen!");
     }
     if (preg_match("/^.+\*/", $input)) {
         system_failure("Ihre Daten enthielten ungültige Zeichen (Wildcard-Stern muss ganz vorne stehen)!");
     }
     if (! $wildcard && preg_replace("/^\*/", "", $input) != $input) {
         system_failure("Ihre Daten enthielten ungültige Zeichen (Keine Wildcards erlaubt)!");
     }
     if (strstr($input, '..')) {
         system_failure("Ungültiger Hostname");
     }
     return $input;
07263d42
 }
 
2626dd47
 function verify_input_hostname($input, $wildcard=false)
3e33855c
 {
2626dd47
     if (filter_input_hostname($input, $wildcard) != $input) {
         logger(LOG_WARNING, 'inc/security', 'verify_input_hostname', 'Ungültige Daten: '.$input);
         system_failure("Ihre Daten enthielten ungültige Zeichen!");
     }
3e33855c
 }
 
 
d0416d23
 function verify_input_hostname_utf8($input)
 {
     $puny = idn_to_ascii($input, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46);
     if ($puny === false) {
         system_failure("Ungültiger Hostname! idn ".$input);
     }
     $filter = filter_var($puny, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME);
c42127d3
     if ($filter !== $puny) {
d0416d23
         system_failure("Ungültiger Hostname! filter ".$input);
     }
 }
 
 
2626dd47
 function verify_input_ipv4($input)
3e33855c
 {
2626dd47
     if (! preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $input)) {
         system_failure('Keine IP-Adresse');
     }
3e33855c
 }
 
 
2626dd47
 function verify_input_ipv6($input)
3e33855c
 {
2626dd47
     // ripped from Perl module Net-IPv6Addr v0.2
     if (! preg_match("/^(([0-9a-f]{1,4}:){7}[0-9a-f]{1,4}|[0-9a-f]{0,4}::|:(?::[a-f0-9]{1,4}){1,6}|(?:[a-f0-9]{1,4}:){1,6}:|(?:[a-f0-9]{1,4}:)(?::[a-f0-9]{1,4}){1,6}|(?:[a-f0-9]{1,4}:){2}(?::[a-f0-9]{1,4}){1,5}|(?:[a-f0-9]{1,4}:){3}(?::[a-f0-9]{1,4}){1,4}|(?:[a-f0-9]{1,4}:){4}(?::[a-f0-9]{1,4}){1,3}|(?:[a-f0-9]{1,4}:){5}(?::[a-f0-9]{1,4}){1,2}|(?:[a-f0-9]{1,4}:){6}(?::[a-f0-9]{1,4}))$/i", $input)) {
         system_failure("Ungültige IPv6-Adresse");
     }
3e33855c
 }
33986238
 
2626dd47
 function verify_input_recorddata($input)
33a6604d
 {
2626dd47
     if (strstr($input, "\\") || strstr($input, '"')) {
         system_failure("Ungültige Zeichen");
     }
33a6604d
 }
33986238
 
2626dd47
 function filter_quotes($input)
0dae1273
 {
2626dd47
     return preg_replace('/["\'`]/', '', $input);
0dae1273
 }
 
33986238
 
 
2626dd47
 function filter_shell($input)
19a2c966
 {
2626dd47
     return preg_replace('/[^-[:alnum:]\_\.\+ßäöüÄÖÜ/%§=]/', '', $input);
33986238
 }
 
2626dd47
 function verify_shell($input)
33986238
 {
2626dd47
     if (filter_shell($input) != $input) {
         system_failure("Ihre Daten enthielten ungültige Zeichen!");
     }
19a2c966
 }
010bf846
 
33986238
 
690f4b9d
 function filter_ssh_key($key)
 {
2626dd47
     $keyparts = explode(" ", trim($key));
690f4b9d
 
2626dd47
     if ((count($keyparts) > 3) || (count($keyparts) < 2)) {
         system_failure("Ungültiger SSH-Key!");
     }
690f4b9d
 
2626dd47
     if (preg_match("/^[a-z0-9]+-[a-z0-9-]+$/", $keyparts[0]) === 0) {
         system_failure("Ungültiger SSH-Key!");
     }
690f4b9d
 
2626dd47
     if (base64_decode($keyparts[1], 1) == false) {
         system_failure("Ungültiger SSH-Key!");
     }
690f4b9d
 
01d16858
     if ((count($keyparts) === 3) && (preg_match("/^[a-zA-Z0-9@._-]+$/", $keyparts[2]) === 0)) {
2626dd47
         system_failure("Ungültige Zeichen im Kommentar des SSH-Keys!");
     }
690f4b9d
 
2626dd47
     if (count($keyparts) === 2) {
         return $keyparts[0]." ".$keyparts[1];
     } else {
         return $keyparts[0]." ".$keyparts[1]." ".$keyparts[2];
     }
690f4b9d
 }
 
33986238
 
2626dd47
 function check_path($input)
af8c8976
 {
2626dd47
     DEBUG("checking {$input} for valid path name");
7f815276
     if ($input != filter_output_html($input)) {
2626dd47
         logger(LOG_WARNING, 'inc/security', 'check_path', 'HTML-Krams im Pfad: '.$input);
         DEBUG("HTML-Krams im Pfad");
         return false;
     }
     $components = explode("/", $input);
     foreach ($components as $item) {
         if ($item == '..') {
             logger(LOG_WARNING, 'inc/security', 'check_path', '»..« im Pfad: '.$input);
             DEBUG("»..« im Pfad");
             return false;
         }
3c90031d
         if (strlen($item) > 255) {
             return false;
         }
     }
     if (strlen($input) > 2048) {
         return false;
af8c8976
     }
2626dd47
     return (preg_match('/^[ A-Za-z0-9.@\/_-]*$/', $input) == 1);
af8c8976
 }
 
 
d5f2f3f4
 function in_homedir($path)
 {
2626dd47
     DEBUG("Prüfe »{$path}«");
     if (! check_path($path)) {
         DEBUG('Kein Pfad');
         return false;
     }
     if (! isset($_SESSION['userinfo']['homedir'])) {
         DEBUG("Kann homedir nicht ermitteln");
         return false;
     }
ee11b5e6
     return strncmp($_SESSION['userinfo']['homedir'], $path, strlen($_SESSION['userinfo']['homedir'])) == 0;
d5f2f3f4
 }
 
2626dd47
 function check_date($input)
92f2aa62
 {
2626dd47
     return (bool) preg_match("/[0-9]{4}-(0?[1-9]|10|11|12)-([012]?[0-9]|30|31)/", $input);
92f2aa62
 }
 
d5f2f3f4
 
2626dd47
 function check_emailaddr($input)
0d1b7607
 {
2626dd47
     return (bool) filter_var($input, FILTER_VALIDATE_EMAIL) == $input;
dd4ea394
 }
 
2626dd47
 function check_domain($input)
dd4ea394
 {
2626dd47
     return (bool) preg_match("/^[a-z0-9\.\-]+\.[a-z\-]{2,63}$/i", $input);
0d1b7607
 }
7c5fe202
 
 function check_input_types($input, $types)
 {
     foreach ($types as $key => $type) {
         if (!array_key_exists($key, $input)) {
             system_failure("Interner Fehler bei Eingabevariablen");
         }
         if ($type === 'int') {
             if ($input[$key] !== (string)(int)$input[$key]) {
                 system_failure("Interner Fehler bei Eingabevariablen");
             }
             continue;
         } elseif ($type === 'string') {
             if (!is_string($input[$key])) {
                 system_failure("Interner Fehler bei Eingabevariablen");
             }
         } else {
             system_failure("Interner Fehler: Ungültier Typ");
         }
     }
 }