Completed
Push — master ( e7b303...e0a449 )
by cam
04:41
created

acces.php ➔ ecrire_acces()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 27

Duplication

Lines 7
Ratio 25.93 %

Importance

Changes 0
Metric Value
cc 5
nc 3
nop 0
dl 7
loc 27
rs 9.1768
c 0
b 0
f 0
1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Système de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright © avec tendresse depuis 2001                                 *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribué sous licence GNU/GPL.     *
10
 *  Pour plus de détails 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 = 16, $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) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $aleas of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
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
0 ignored issues
show
Documentation introduced by
Should the return type not be string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
265
 */
266
function initialiser_sel() {
267
	if (!isset($GLOBALS['htsalt'])) {
268
		if (CRYPT_MD5) {
269
			$GLOBALS['htsalt'] = '$1$' . creer_pass_aleatoire();
270
		} else {
271
			$GLOBALS['htsalt'] = '';
272
		}
273
	}
274
	return $GLOBALS['htsalt'];
275
}
276
277
/**
278
 * Créer un fichier htpasswd
279
 *
280
 * Cette fonction ne sert qu'à la connexion en mode http_auth.non LDAP.
281
 * Voir le plugin «Accès Restreint»
282
 *
283
 * S'appuie sur la meta `creer_htpasswd` pour savoir s'il faut créer
284
 * le `.htpasswd`.
285
 *
286
 * @return null|void
287
 *     - null si pas de htpasswd à créer, ou si LDAP
288
 *     - void sinon.
289
 **/
290
function ecrire_acces() {
291
	$htaccess = _DIR_RESTREINT . _ACCESS_FILE_NAME;
292
	$htpasswd = _DIR_TMP . _AUTH_USER_FILE;
293
294
	// Cette variable de configuration peut etre posee par un plugin
295
	// par exemple acces_restreint ;
296
	// si .htaccess existe, outrepasser spip_meta
297 View Code Duplication
	if ((!isset($GLOBALS['meta']['creer_htpasswd'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
298
			or ($GLOBALS['meta']['creer_htpasswd'] != 'oui'))
299
		and !@file_exists($htaccess)
300
	) {
301
		spip_unlink($htpasswd);
302
		spip_unlink($htpasswd . '-admin');
303
		return;
304
	}
305
306
	# remarque : ici on laisse passer les "nouveau" de maniere a leur permettre
307
	# de devenir redacteur le cas echeant (auth http)... a nettoyer
308
	// attention, il faut au prealable se connecter a la base (necessaire car utilise par install)
309
	// TODO: factoriser avec auth/spip qui fait deja ce job et generaliser le test spip_connect_ldap()
310
311
	if (spip_connect_ldap()) {
0 ignored issues
show
Deprecated Code introduced by
The function spip_connect_ldap() has been deprecated with message: Utiliser l'authentification LDAP de auth/ldap

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
312
		return;
313
	}
314
315
	generer_htpasswd_files($htpasswd, "$htpasswd-admin");
316
}
317
318
/**
319
 * Generer le fichier de htpasswd contenant les htpass
320
 * @param $htpasswd
321
 * @param $htpasswd_admin
322
 */
323
function generer_htpasswd_files($htpasswd, $htpasswd_admin) {
324
	if ($generer_htpasswd = charger_fonction('generer_htpasswd_files', 'inc', true)) {
325
		$generer_htpasswd($htpasswd, $htpasswd_admin);
326
	}
327
328
	$pwd_all = ''; // login:htpass pour tous
329
	$pwd_admin = ''; // login:htpass pour les admins
330
331
	$res = sql_select('login, htpass, statut', 'spip_auteurs', "htpass!='' AND login!='' AND ".sql_in('statut', ['1comite', '0minirezo', 'nouveau']));
332
	while ($row = sql_fetch($res)) {
333
		if (strlen($row['login']) and strlen($row['htpass'])) {
334
			$ligne = $row['login'] . ':' . $row['htpass'] . "\n";
335
			$pwd_all .= $ligne;
336
			if ($row['statut'] == '0minirezo') {
337
				$pwd_admin .= $ligne;
338
			}
339
		}
340
	}
341
342
	if ($pwd_all) {
343
		ecrire_fichier($htpasswd, $pwd_all);
344
		ecrire_fichier($htpasswd_admin, $pwd_admin);
345
		spip_log("Ecriture de $htpasswd et $htpasswd_admin", 'htpass');
346
	}
347
}
348
349
/**
350
 * Créer un password htaccess
351
 *
352
 * @link http://docs.php.net/manual/fr/function.crypt.php Documentation de `crypt()`
353
 *
354
 * @param string $pass
355
 *   Le mot de passe
356
 * @return void|string
357
 *  La chaîne hachée si fonction crypt présente, rien sinon.
358
 */
359
function generer_htpass($pass) {
360
	if ($generer_htpass = charger_fonction('generer_htpass', 'inc', true)) {
361
		return $generer_htpass($pass);
362
	}
363
	elseif (function_exists('crypt')) {
364
		return crypt($pass, initialiser_sel());
365
	}
366
	return '';
367
}
368
369
/**
370
 * Installe ou vérifie un fichier .htaccess, y compris sa prise en compte par Apache
371
 *
372
 * @uses recuperer_lapage()
373
 * @param string $rep
374
 *     Nom du répertoire où SPIP doit vérifier l'existence d'un fichier .htaccess
375
 * @param bool $force
376
 * @return boolean
377
 */
378
function verifier_htaccess($rep, $force = false) {
379
	$htaccess = rtrim($rep, '/') . '/' . _ACCESS_FILE_NAME;
380
	if (((@file_exists($htaccess)) or defined('_TEST_DIRS')) and !$force) {
381
		return true;
382
	}
383
384
	// directive deny compatible Apache 2.0+
385
	$deny =
386
		'# Deny all requests from Apache 2.4+.
387
<IfModule mod_authz_core.c>
388
  Require all denied
389
</IfModule>
390
# Deny all requests from Apache 2.0-2.2.
391
<IfModule !mod_authz_core.c>
392
  Deny from all
393
</IfModule>
394
';
395
	// support des vieilles versions Apache 1.x mais uniquement si elles l'annoncent (pas en mode PROD)
396
	if (function_exists('apache_get_version')
397
		and $v = apache_get_version()
398
		and strncmp($v, 'Apache/1.', 9) == 0) {
399
		$deny = "deny from all\n";
400
	}
401
402
	if ($ht = @fopen($htaccess, 'w')) {
403
		fputs($ht, $deny);
404
		fclose($ht);
405
		@chmod($htaccess, _SPIP_CHMOD & 0666);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
406
		$t = rtrim($rep, '/') . '/.ok';
407
		if ($ht = @fopen($t, 'w')) {
408
			@fclose($ht);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
409
			include_spip('inc/distant');
410
			$t = substr($t, strlen(_DIR_RACINE));
411
			$t = url_de_base() . $t;
412
			$ht = recuperer_lapage($t, false, 'HEAD', 0);
0 ignored issues
show
Deprecated Code introduced by
The function recuperer_lapage() has been deprecated.

This function has been deprecated.

Loading history...
413
			// htaccess inoperant si on a recupere des entetes HTTP
414
			// (ignorer la reussite si connexion par fopen)
415
			$ht = !(isset($ht[0]) and $ht[0]);
416
		}
417
	}
418
	spip_log("Creation de $htaccess " . ($ht ? ' reussie' : ' manquee'));
419
420
	return $ht;
421
}
422
423
/**
424
 * Créer un fichier .htaccess pour chaque répertoire d'extension
425
 * dans `_DIR_IMG` si la configuration le demande
426
 *
427
 * @note
428
 *     La variable de configuration `creer_htaccess` peut être posée
429
 *     par un plugin tel acces_restreint.
430
 *
431
 * @uses _DIR_IMG
432
 * @uses verifier_htaccess()
433
 *
434
 * @return string
435
 *         Valeur de la configuration `creer_htaccess`
436
 */
437
function gerer_htaccess() {
438
	// Cette variable de configuration peut etre posee par un plugin
439
	// par exemple acces_restreint
440
	$f = (isset($GLOBALS['meta']['creer_htaccess']) and ($GLOBALS['meta']['creer_htaccess'] === 'oui'));
441
	$dirs = sql_allfetsel('extension', 'spip_types_documents');
442
	$dirs[] = array('extension' => 'distant');
443
	foreach ($dirs as $e) {
444
		if (is_dir($dir = _DIR_IMG . $e['extension'])) {
445
			if ($f) {
446
				verifier_htaccess($dir);
447
			} else {
448
				spip_unlink($dir . '/' . _ACCESS_FILE_NAME);
449
			}
450
		}
451
	}
452
453
	return isset($GLOBALS['meta']['creer_htaccess']) ? $GLOBALS['meta']['creer_htaccess'] : '';
454
}
455
456