PageRenderTime 29ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/ecrire/inc/acces.php

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