[SPIP] ~v3.2.5-->v3.2.7
[lhc/web/www.git] / www / ecrire / inc / acces.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2019 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
12
13 /**
14 * Gestion des nombres aléatoires et de certains accès au site
15 *
16 * @package SPIP\Core\Authentification
17 **/
18
19 if (!defined('_ECRIRE_INC_VERSION')) {
20 return;
21 }
22
23 /**
24 * Créer un mot de passe
25 *
26 * @param int $longueur
27 * Longueur du password créé
28 * @param string $sel
29 * Clé pour un salage supplémentaire
30 * @return string
31 * Mot de passe
32 **/
33 function creer_pass_aleatoire($longueur = 8, $sel = '') {
34 $seed = (int)round(((float)microtime() + 1) * time());
35
36 mt_srand($seed);
37 srand($seed);
38 $s = '';
39 $pass = '';
40 for ($i = 0; $i < $longueur; $i++) {
41 if (!$s) {
42 $s = mt_rand();
43 if (!$s) {
44 $s = rand();
45 }
46 $s = substr(md5(uniqid($s) . $sel), 0, 16);
47 }
48 $r = unpack('Cr', pack('H2', $s . $s));
49 $x = $r['r'] & 63;
50 if ($x < 10) {
51 $x = chr($x + 48);
52 } else {
53 if ($x < 36) {
54 $x = chr($x + 55);
55 } else {
56 if ($x < 62) {
57 $x = chr($x + 61);
58 } else {
59 if ($x == 63) {
60 $x = '/';
61 } else {
62 $x = '.';
63 }
64 }
65 }
66 }
67 $pass .= $x;
68 $s = substr($s, 2);
69 }
70 $pass = preg_replace('@[./]@', 'a', $pass);
71 $pass = preg_replace('@[I1l]@', 'L', $pass);
72 $pass = preg_replace('@[0O]@', 'o', $pass);
73
74 return $pass;
75 }
76
77 /**
78 * Créer un identifiant aléatoire
79 *
80 * @return string Identifiant
81 */
82 function creer_uniqid() {
83 static $seeded;
84
85 if (!$seeded) {
86 $seed = (int)round(((float)microtime() + 1) * time());
87 mt_srand($seed);
88 srand($seed);
89 $seeded = true;
90 }
91
92 $s = mt_rand();
93 if (!$s) {
94 $s = rand();
95 }
96
97 return uniqid($s, 1);
98 }
99
100 /**
101 * Charge les aléas ehpémères s'il ne sont pas encore dans la globale
102 *
103 * Si les métas 'alea_ephemere' et 'alea_ephemere_ancien' se sont pas encore chargées
104 * en méta (car elles ne sont pas stockées, pour sécurité, dans le fichier cache des métas),
105 * alors on les récupère en base. Et on les ajoute à nos métas globales.
106 *
107 * @see touch_meta()
108 * @return string Retourne l'alea éphemère actuel au passage
109 */
110 function charger_aleas() {
111 if (!isset($GLOBALS['meta']['alea_ephemere'])) {
112 include_spip('base/abstract_sql');
113 $aleas = sql_allfetsel(
114 array('nom', 'valeur'),
115 'spip_meta',
116 sql_in("nom", array('alea_ephemere', 'alea_ephemere_ancien')),
117 '', '', '', '', '',
118 'continue'
119 );
120 if ($aleas) {
121 foreach ($aleas as $a) {
122 $GLOBALS['meta'][$a['nom']] = $a['valeur'];
123 }
124 return $GLOBALS['meta']['alea_ephemere'];
125 } else {
126 spip_log("aleas indisponibles", "session");
127 return "";
128 }
129 }
130 return $GLOBALS['meta']['alea_ephemere'];
131 }
132
133 /**
134 * Renouveller l'alea (utilisé pour sécuriser les scripts du répertoire `action/`)
135 **/
136 function renouvelle_alea() {
137 charger_aleas();
138 ecrire_meta('alea_ephemere_ancien', @$GLOBALS['meta']['alea_ephemere'], 'non');
139 $GLOBALS['meta']['alea_ephemere'] = md5(creer_uniqid());
140 ecrire_meta('alea_ephemere', $GLOBALS['meta']['alea_ephemere'], 'non');
141 ecrire_meta('alea_ephemere_date', time(), 'non');
142 spip_log("renouvellement de l'alea_ephemere");
143 }
144
145
146 /**
147 * Retourne une clé de sécurité faible (low_sec) pour l'auteur indiqué
148 *
149 * low-security est un ensemble de fonctions pour gérer de l'identification
150 * faible via les URLs (suivi RSS, iCal...)
151 *
152 * Retourne la clé de sécurité low_sec de l'auteur (la génère si elle n'exite pas)
153 * ou la clé de sécurité low_sec du site (si auteur invalide)(la génère si elle
154 * n'existe pas).
155 *
156 * @param int $id_auteur
157 * Identifiant de l'auteur
158 * @return string
159 * Clé de sécurité.
160 **/
161 function low_sec($id_auteur) {
162 // Pas d'id_auteur : low_sec
163 if (!$id_auteur = intval($id_auteur)) {
164 if (!$low_sec = $GLOBALS['meta']['low_sec']) {
165 ecrire_meta('low_sec', $low_sec = creer_pass_aleatoire());
166 }
167 } else {
168 $low_sec = sql_getfetsel('low_sec', 'spip_auteurs', 'id_auteur = '.intval($id_auteur));
169 if (!$low_sec) {
170 $low_sec = creer_pass_aleatoire();
171 sql_updateq('spip_auteurs', array('low_sec' => $low_sec), 'id_auteur = '.intval($id_auteur));
172 }
173 }
174
175 return $low_sec;
176 }
177
178 /**
179 * Inclure les arguments significatifs pour le hachage
180 *
181 * Cas particulier du statut pour compatibilité ancien rss/suivi_revisions
182 *
183 * @param string $op
184 * @param array $args
185 * @param string $lang
186 * @param string $mime
187 * Par défaut 'rss'.
188 * @return string
189 */
190 function param_low_sec($op, $args = array(), $lang = '', $mime = 'rss') {
191 $a = $b = '';
192 foreach ($args as $val => $var) {
193 if ($var) {
194 if ($val <> 'statut') {
195 $a .= ':' . $val . '-' . $var;
196 }
197 $b .= $val . '=' . $var . '&';
198 }
199 }
200 $a = substr($a, 1);
201 $id = intval(@$GLOBALS['connect_id_auteur']);
202
203 return $b
204 . 'op='
205 . $op
206 . '&id='
207 . $id
208 . '&cle='
209 . afficher_low_sec($id, "$mime $op $a")
210 . (!$a ? '' : "&args=$a")
211 . (!$lang ? '' : "&lang=$lang");
212 }
213
214 /**
215 * Retourne une clé basée sur le low_sec de l'auteur et l'action demandé
216 *
217 * @uses low_sec()
218 *
219 * @param int $id_auteur
220 * Identifiant de l'auteur
221 * @param string $action
222 * Action désirée
223 * @return string
224 * Clé
225 **/
226 function afficher_low_sec($id_auteur, $action = '') {
227 return substr(md5($action . low_sec($id_auteur)), 0, 8);
228 }
229
230 /**
231 * Vérifie une clé basée sur le low_sec de l'auteur et l'action demandé
232 *
233 * @uses afficher_low_sec()
234 *
235 * @param int $id_auteur
236 * Identifiant de l'auteur
237 * @param string $cle
238 * Clé à comparer
239 * @param string $action
240 * Action désirée
241 * @return bool
242 * true si les clés corresponde, false sinon
243 **/
244 function verifier_low_sec($id_auteur, $cle, $action = '') {
245 return ($cle == afficher_low_sec($id_auteur, $action));
246 }
247
248 /**
249 * Efface la clé de sécurité faible (low_sec) d'un auteur
250 *
251 * @param int $id_auteur
252 * Identifiant de l'auteur
253 **/
254 function effacer_low_sec($id_auteur) {
255 if (!$id_auteur = intval($id_auteur)) {
256 return;
257 } // jamais trop prudent ;)
258 sql_updateq('spip_auteurs', array('low_sec' => ''), 'id_auteur = '.intval($id_auteur));
259 }
260
261 /**
262 * Initialiser la globale htsalt si cela n'a pas déjà été fait.
263 *
264 * @return void|bool
265 */
266 function initialiser_sel() {
267 if (CRYPT_MD5) {
268 $GLOBALS['htsalt'] = '$1$' . creer_pass_aleatoire();
269 } else {
270 return '';
271 }
272 }
273
274 /**
275 * Créer un fichier htpasswd
276 *
277 * Cette fonction ne sert qu'à la connexion en mode http_auth.non LDAP.
278 * Voir le plugin «Accès Restreint»
279 *
280 * S'appuie sur la meta `creer_htpasswd` pour savoir s'il faut créer
281 * le `.htpasswd`.
282 *
283 * @return null|void
284 * - null si pas de htpasswd à créer, ou si LDAP
285 * - void sinon.
286 **/
287 function ecrire_acces() {
288 $htaccess = _DIR_RESTREINT . _ACCESS_FILE_NAME;
289 $htpasswd = _DIR_TMP . _AUTH_USER_FILE;
290
291 // Cette variable de configuration peut etre posee par un plugin
292 // par exemple acces_restreint ;
293 // si .htaccess existe, outrepasser spip_meta
294 if ((!isset($GLOBALS['meta']['creer_htpasswd'])
295 or ($GLOBALS['meta']['creer_htpasswd'] != 'oui'))
296 and !@file_exists($htaccess)
297 ) {
298 spip_unlink($htpasswd);
299 spip_unlink($htpasswd . '-admin');
300 return;
301 }
302
303 # remarque : ici on laisse passer les "nouveau" de maniere a leur permettre
304 # de devenir redacteur le cas echeant (auth http)... a nettoyer
305 // attention, il faut au prealable se connecter a la base (necessaire car utilise par install)
306 // TODO: factoriser avec auth/spip qui fait deja ce job et generaliser le test spip_connect_ldap()
307
308 if (spip_connect_ldap()) {
309 return;
310 }
311 $p1 = ''; // login:htpass pour tous
312 $p2 = ''; // login:htpass pour les admins
313 $s = sql_select('login, htpass, statut', 'spip_auteurs', sql_in('statut', array('1comite', '0minirezo', 'nouveau')));
314 while ($t = sql_fetch($s)) {
315 if (strlen($t['login']) and strlen($t['htpass'])) {
316 $p1 .= $t['login'] . ':' . $t['htpass'] . "\n";
317 if ($t['statut'] == '0minirezo') {
318 $p2 .= $t['login'] . ':' . $t['htpass'] . "\n";
319 }
320 }
321 }
322 if ($p1) {
323 ecrire_fichier($htpasswd, $p1);
324 ecrire_fichier($htpasswd . '-admin', $p2);
325 spip_log("Ecriture de $htpasswd et $htpasswd-admin");
326 }
327 }
328
329 /**
330 * Créer un password htaccess
331 *
332 * @link http://docs.php.net/manual/fr/function.crypt.php Documentation de `crypt()`
333 *
334 * @global string $htsalt
335 * Une chaîne de sel sur laquelle sera fondée le hachage.
336 * @param string $pass
337 * Le mot de passe
338 * @return void|string
339 * La chaîne hachée si fonction crypt présente, rien sinon.
340 */
341 function generer_htpass($pass) {
342 if (function_exists('crypt')) {
343 return crypt($pass, $GLOBALS['htsalt']);
344 }
345 }
346
347 /**
348 * Installe ou vérifie un fichier .htaccess, y compris sa prise en compte par Apache
349 *
350 * @uses recuperer_lapage()
351 * @param string $rep
352 * Nom du répertoire où SPIP doit vérifier l'existence d'un fichier .htaccess
353 * @param bool $force
354 * @return boolean
355 */
356 function verifier_htaccess($rep, $force = false) {
357 $htaccess = rtrim($rep, '/') . '/' . _ACCESS_FILE_NAME;
358 if (((@file_exists($htaccess)) or defined('_TEST_DIRS')) and !$force) {
359 return true;
360 }
361
362 // directive deny compatible Apache 2.0+
363 $deny =
364 '# Deny all requests from Apache 2.4+.
365 <IfModule mod_authz_core.c>
366 Require all denied
367 </IfModule>
368 # Deny all requests from Apache 2.0-2.2.
369 <IfModule !mod_authz_core.c>
370 Deny from all
371 </IfModule>
372 ';
373 // support des vieilles versions Apache 1.x mais uniquement si elles l'annoncent (pas en mode PROD)
374 if (function_exists('apache_get_version')
375 and $v = apache_get_version()
376 and strncmp($v, 'Apache/1.', 9) == 0) {
377 $deny = "deny from all\n";
378 }
379
380 if ($ht = @fopen($htaccess, 'w')) {
381 fputs($ht, $deny);
382 fclose($ht);
383 @chmod($htaccess, _SPIP_CHMOD & 0666);
384 $t = rtrim($rep, '/') . '/.ok';
385 if ($ht = @fopen($t, 'w')) {
386 @fclose($ht);
387 include_spip('inc/distant');
388 $t = substr($t, strlen(_DIR_RACINE));
389 $t = url_de_base() . $t;
390 $ht = recuperer_lapage($t, false, 'HEAD', 0);
391 // htaccess inoperant si on a recupere des entetes HTTP
392 // (ignorer la reussite si connexion par fopen)
393 $ht = !(isset($ht[0]) and $ht[0]);
394 }
395 }
396 spip_log("Creation de $htaccess " . ($ht ? ' reussie' : ' manquee'));
397
398 return $ht;
399 }
400
401 /**
402 * Créer un fichier .htaccess pour chaque répertoire d'extension
403 * dans `_DIR_IMG` si la configuration le demande
404 *
405 * @note
406 * La variable de configuration `creer_htaccess` peut être posée
407 * par un plugin tel acces_restreint.
408 *
409 * @uses _DIR_IMG
410 * @uses verifier_htaccess()
411 *
412 * @return string
413 * Valeur de la configuration `creer_htaccess`
414 */
415 function gerer_htaccess() {
416 // Cette variable de configuration peut etre posee par un plugin
417 // par exemple acces_restreint
418 $f = (isset($GLOBALS['meta']['creer_htaccess']) and ($GLOBALS['meta']['creer_htaccess'] === 'oui'));
419 $dirs = sql_allfetsel('extension', 'spip_types_documents');
420 $dirs[] = array('extension' => 'distant');
421 foreach ($dirs as $e) {
422 if (is_dir($dir = _DIR_IMG . $e['extension'])) {
423 if ($f) {
424 verifier_htaccess($dir);
425 } else {
426 spip_unlink($dir . '/' . _ACCESS_FILE_NAME);
427 }
428 }
429 }
430
431 return isset($GLOBALS['meta']['creer_htaccess']) ? $GLOBALS['meta']['creer_htaccess'] : '';
432 }
433
434 initialiser_sel();