Current File : /home/igihango/public_html/ecrire/inc/acces.php |
<?php
/***************************************************************************\
* SPIP, Système de publication pour l'internet *
* *
* Copyright © avec tendresse depuis 2001 *
* Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James *
* *
* Ce programme est un logiciel libre distribué sous licence GNU/GPL. *
\***************************************************************************/
/**
* Gestion des nombres aléatoires et de certains accès au site
*
* @package SPIP\Core\Authentification
**/
if (!defined('_ECRIRE_INC_VERSION')) {
return;
}
/**
* Créer un mot de passe
*
* @param int $longueur
* Longueur du password créé
* @param string $sel
* Clé pour un salage supplémentaire
* @return string
* Mot de passe
**/
function creer_pass_aleatoire($longueur = 16, $sel = '') {
$seed = (int)round(((float)microtime() + 1) * time());
mt_srand($seed);
$s = '';
$pass = '';
for ($i = 0; $i < $longueur; $i++) {
if (!$s) {
$s = random_int(0, mt_getrandmax());
if (!$s) {
$s = random_int(0, mt_getrandmax());
}
$s = substr(md5(uniqid($s) . $sel), 0, 16);
}
$r = unpack('Cr', pack('H2', $s . $s));
$x = $r['r'] & 63;
if ($x < 10) {
$x = chr($x + 48);
} else {
if ($x < 36) {
$x = chr($x + 55);
} else {
if ($x < 62) {
$x = chr($x + 61);
} else {
if ($x == 63) {
$x = '/';
} else {
$x = '.';
}
}
}
}
$pass .= $x;
$s = substr($s, 2);
}
$pass = preg_replace('@[./]@', 'a', $pass);
$pass = preg_replace('@[I1l]@', 'L', $pass);
$pass = preg_replace('@[0O]@', 'o', $pass);
return $pass;
}
/**
* Créer un identifiant aléatoire
*
* @return string Identifiant
*/
function creer_uniqid() {
static $seeded;
if (!$seeded) {
$seed = (int)round(((float)microtime() + 1) * time());
mt_srand($seed);
$seeded = true;
}
$s = random_int(0, mt_getrandmax());
if (!$s) {
$s = random_int(0, mt_getrandmax());
}
return uniqid($s, 1);
}
/**
* Charge les aléas ehpémères s'il ne sont pas encore dans la globale
*
* Si les métas 'alea_ephemere' et 'alea_ephemere_ancien' se sont pas encore chargées
* en méta (car elles ne sont pas stockées, pour sécurité, dans le fichier cache des métas),
* alors on les récupère en base. Et on les ajoute à nos métas globales.
*
* @see touch_meta()
* @return string Retourne l'alea éphemère actuel au passage
*/
function charger_aleas() {
if (!isset($GLOBALS['meta']['alea_ephemere'])) {
include_spip('base/abstract_sql');
$aleas = sql_allfetsel(
['nom', 'valeur'],
'spip_meta',
sql_in('nom', ['alea_ephemere', 'alea_ephemere_ancien']),
'',
'',
'',
'',
'',
'continue'
);
if ($aleas) {
foreach ($aleas as $a) {
$GLOBALS['meta'][$a['nom']] = $a['valeur'];
}
return $GLOBALS['meta']['alea_ephemere'];
} else {
spip_log('aleas indisponibles', 'session');
return '';
}
}
return $GLOBALS['meta']['alea_ephemere'];
}
/**
* Renouveller l'alea (utilisé pour sécuriser les scripts du répertoire `action/`)
**/
function renouvelle_alea() {
charger_aleas();
ecrire_meta('alea_ephemere_ancien', @$GLOBALS['meta']['alea_ephemere'], 'non');
$GLOBALS['meta']['alea_ephemere'] = md5(creer_uniqid());
ecrire_meta('alea_ephemere', $GLOBALS['meta']['alea_ephemere'], 'non');
ecrire_meta('alea_ephemere_date', time(), 'non');
spip_log("renouvellement de l'alea_ephemere");
}
/**
* Retourne une clé de sécurité faible (low_sec) pour l'auteur indiqué
*
* low-security est un ensemble de fonctions pour gérer de l'identification
* faible via les URLs (suivi RSS, iCal...)
*
* Retourne la clé de sécurité low_sec de l'auteur (la génère si elle n'exite pas)
* ou la clé de sécurité low_sec du site (si auteur invalide)(la génère si elle
* n'existe pas).
*
* @param int $id_auteur
* Identifiant de l'auteur
* @return string
* Clé de sécurité.
**/
function low_sec($id_auteur) {
// Pas d'id_auteur : low_sec
if (!$id_auteur = intval($id_auteur)) {
include_spip('inc/config');
if (!$low_sec = lire_config('low_sec')) {
ecrire_meta('low_sec', $low_sec = creer_pass_aleatoire());
}
} else {
$low_sec = sql_getfetsel('low_sec', 'spip_auteurs', 'id_auteur = ' . intval($id_auteur));
if (!$low_sec) {
$low_sec = creer_pass_aleatoire();
sql_updateq('spip_auteurs', ['low_sec' => $low_sec], 'id_auteur = ' . intval($id_auteur));
}
}
return $low_sec;
}
/**
* Vérifie un accès à faible sécurité
*
* Vérifie qu'un visiteur peut accéder à la page demandée,
* qui est protégée par une clé, calculée à partir du low_sec de l'auteur,
* et des paramètres le composant l'appel (op, args)
*
* @param int $id_auteur
* L'auteur qui demande la page
* @param string $cle
* La clé à tester
* @param string $dir
* Un type d'accès (nom du répertoire dans lequel sont rangés les squelettes demandés, tel que 'rss')
* @param string $op
* Nom de l'opération éventuelle
* @param string $args
* Nom de l'argument calculé
* @return bool
* True si on a le droit d'accès, false sinon.
**@example
* `[(#ID_AUTEUR|securiser_acces{#ENV{cle}, rss, #ENV{op}, #ENV{args}}|sinon_interdire_acces)]`
*
* @see generer_url_api_low_sec() pour generer une url api low sec
* @see afficher_low_sec() pour calculer une clé valide
* @uses verifier_low_sec()
*
* @filtre
*/
function securiser_acces_low_sec(
$id_auteur,
#[\SensitiveParameter]
$cle,
$dir,
$op = '',
$args = ''
) {
if ($op) {
$dir .= " $op $args";
}
return verifier_low_sec($id_auteur, $cle, $dir);
}
/**
* Generer une url xxx.api/$id_auteur/$cle/$format/$fond?$args
* @param string $script
* @param string $format
* @param string $fond
* @param string $path
* @param string $args
* @param bool $no_entities
* @param bool|null $public
* @return string
*/
function generer_url_api_low_sec(string $script, string $format, string $fond, string $path, string $args, bool $no_entities = false, ?bool $public = null) {
$id_auteur = $GLOBALS['visiteur_session']['id_auteur'] ?? 0;
$cle = afficher_low_sec($id_auteur, "$script/$format $fond $args");
$path = "$id_auteur/$cle/$format/$fond" . ($path ? "/$path" : '');
return generer_url_api($script, $path, $args, $no_entities, $public);
}
/**
* inclure les arguments significatifs pour le hachage
*
* Cas particulier du statut pour compatibilité ancien rss/suivi_revisions
*
* @param string $op
* @param array $args
* @param string $lang
* @param string $mime
* Par défaut 'rss'.
* @return string
* @deprecated 4.1
*/
function param_low_sec($op, $args = [], $lang = '', $mime = 'rss') {
trigger_deprecation('spip', '4.1', 'Using "%s" is deprecated, use "%s" instead', __FUNCTION__, 'generer_url_api_low_sec');
$a = $b = '';
foreach ($args as $val => $var) {
if ($var) {
if ($val <> 'statut') {
$a .= ':' . $val . '-' . $var;
}
$b .= $val . '=' . $var . '&';
}
}
$a = substr($a, 1);
$id = intval(@$GLOBALS['connect_id_auteur']);
return $b
. 'op='
. $op
. '&id='
. $id
. '&cle='
. afficher_low_sec($id, "$mime $op $a")
. (!$a ? '' : "&args=$a")
. (!$lang ? '' : "&lang=$lang");
}
/**
* Retourne une clé basée sur le low_sec de l'auteur et l'action demandé
*
* @uses low_sec()
*
* @param int $id_auteur
* Identifiant de l'auteur
* @param string $action
* Action désirée
* @return string
* Clé
**/
function afficher_low_sec($id_auteur, $action = '') {
return substr(md5($action . low_sec($id_auteur)), 0, 8);
}
/**
* Vérifie une clé basée sur le low_sec de l'auteur et l'action demandé
*
* @uses afficher_low_sec()
*
* @param int $id_auteur
* Identifiant de l'auteur
* @param string $cle
* Clé à comparer
* @param string $action
* Action désirée
* @return bool
* true si les clés corresponde, false sinon
**/
function verifier_low_sec(
$id_auteur,
#[\SensitiveParameter]
$cle,
$action = ''
) {
return ($cle == afficher_low_sec($id_auteur, $action));
}
/**
* Efface la clé de sécurité faible (low_sec) d'un auteur
*
* @param int $id_auteur
* Identifiant de l'auteur
**/
function effacer_low_sec($id_auteur) {
if (!$id_auteur = intval($id_auteur)) {
return;
} // jamais trop prudent ;)
sql_updateq('spip_auteurs', ['low_sec' => ''], 'id_auteur = ' . intval($id_auteur));
}
/**
* Créer un fichier htpasswd
*
* Cette fonction ne sert qu'à la connexion en mode http_auth.non LDAP.
* Voir le plugin «Accès Restreint»
*
* S'appuie sur la meta `creer_htpasswd` pour savoir s'il faut créer
* le `.htpasswd`.
*
* @return null|void
* - null si pas de htpasswd à créer, ou si LDAP
* - void sinon.
**/
function ecrire_acces() {
$htaccess = _DIR_RESTREINT . _ACCESS_FILE_NAME;
$htpasswd = _DIR_TMP . _AUTH_USER_FILE;
// Cette variable de configuration peut etre posee par un plugin
// par exemple acces_restreint ;
// si .htaccess existe, outrepasser spip_meta
if (
(!isset($GLOBALS['meta']['creer_htpasswd'])
or ($GLOBALS['meta']['creer_htpasswd'] != 'oui'))
and !@file_exists($htaccess)
) {
spip_unlink($htpasswd);
spip_unlink($htpasswd . '-admin');
return;
}
# remarque : ici on laisse passer les "nouveau" de maniere a leur permettre
# de devenir redacteur le cas echeant (auth http)... a nettoyer
// attention, il faut au prealable se connecter a la base (necessaire car utilise par install)
// TODO: factoriser avec auth/spip qui fait deja ce job et generaliser le test auth_ldap_connect()
if (include_spip('auth/ldap') and auth_ldap_connect()) {
return;
}
generer_htpasswd_files($htpasswd, "$htpasswd-admin");
}
/**
* Generer le fichier de htpasswd contenant les htpass
* @param $htpasswd
* @param $htpasswd_admin
*/
function generer_htpasswd_files($htpasswd, $htpasswd_admin) {
if ($generer_htpasswd = charger_fonction('generer_htpasswd_files', 'inc', true)) {
$generer_htpasswd($htpasswd, $htpasswd_admin);
}
$pwd_all = ''; // login:htpass pour tous
$pwd_admin = ''; // login:htpass pour les admins
$res = sql_select('login, htpass, statut', 'spip_auteurs', "htpass!='' AND login!='' AND " . sql_in('statut', ['1comite', '0minirezo', 'nouveau']));
while ($row = sql_fetch($res)) {
if (strlen($row['login']) and strlen($row['htpass'])) {
$ligne = $row['login'] . ':' . $row['htpass'] . "\n";
$pwd_all .= $ligne;
if ($row['statut'] == '0minirezo') {
$pwd_admin .= $ligne;
}
}
}
if ($pwd_all) {
ecrire_fichier($htpasswd, $pwd_all);
ecrire_fichier($htpasswd_admin, $pwd_admin);
spip_log("Ecriture de $htpasswd et $htpasswd_admin", 'htpass');
}
}
/**
* Créer un password htaccess
*
* @link http://docs.php.net/manual/fr/function.crypt.php Documentation de `crypt()`
*
* @param string $pass
* Le mot de passe
* @return void|string
* La chaîne hachée si fonction crypt présente, rien sinon.
*/
function generer_htpass(
#[\SensitiveParameter]
$pass
) {
if ($generer_htpass = charger_fonction('generer_htpass', 'inc', true)) {
return $generer_htpass($pass);
}
return '';
}
/**
* Installe ou vérifie un fichier .htaccess, y compris sa prise en compte par Apache
*
* @uses recuperer_url()
* @param string $rep
* Nom du répertoire où SPIP doit vérifier l'existence d'un fichier .htaccess
* @param bool $force
* @return boolean
*/
function verifier_htaccess($rep, $force = false) {
$htaccess = rtrim($rep, '/') . '/' . _ACCESS_FILE_NAME;
if (((@file_exists($htaccess)) or defined('_TEST_DIRS')) and !$force) {
return true;
}
// directive deny compatible Apache 2.0+
$deny =
'# Deny all requests from Apache 2.4+.
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
# Deny all requests from Apache 2.0-2.2.
<IfModule !mod_authz_core.c>
Deny from all
</IfModule>
';
// support des vieilles versions Apache 1.x mais uniquement si elles l'annoncent (pas en mode PROD)
if (
function_exists('apache_get_version')
and $v = apache_get_version()
and strncmp($v, 'Apache/1.', 9) == 0
) {
$deny = "deny from all\n";
}
if ($ht = @fopen($htaccess, 'w')) {
fputs($ht, $deny);
fclose($ht);
@chmod($htaccess, _SPIP_CHMOD & 0666);
$t = rtrim($rep, '/') . '/.ok';
if ($ht = @fopen($t, 'w')) {
@fclose($ht);
include_spip('inc/distant');
$t = substr($t, strlen(_DIR_RACINE));
$t = url_de_base() . $t;
$ht = recuperer_url($t, ['methode' => 'HEAD', 'taille_max' => 0, 'follow_location' => false]);
$ht = ($ht['status'] ?? null) === 403;
}
}
spip_log("Creation de $htaccess " . ($ht ? ' reussie' : ' manquee'));
return $ht;
}
/**
* Créer un fichier .htaccess pour chaque répertoire d'extension
* dans `_DIR_IMG` si la configuration le demande
*
* @note
* La variable de configuration `creer_htaccess` peut être posée
* par un plugin tel acces_restreint.
*
* @uses _DIR_IMG
* @uses verifier_htaccess()
*
* @return string
* Valeur de la configuration `creer_htaccess`
*/
function gerer_htaccess() {
// Cette variable de configuration peut etre posee par un plugin
// par exemple acces_restreint
$f = (isset($GLOBALS['meta']['creer_htaccess']) and ($GLOBALS['meta']['creer_htaccess'] === 'oui'));
$dirs = sql_allfetsel('extension', 'spip_types_documents');
$dirs[] = ['extension' => 'distant'];
foreach ($dirs as $e) {
if (is_dir($dir = _DIR_IMG . $e['extension'])) {
if ($f) {
verifier_htaccess($dir);
} else {
spip_unlink($dir . '/' . _ACCESS_FILE_NAME);
}
}
}
return $GLOBALS['meta']['creer_htaccess'] ?? '';
}