[SPIP] ~v3.0.20-->v3.0.25
[lhc/web/clavette_www.git] / www / ecrire / auth / spip.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2016 *
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 if (!defined('_ECRIRE_INC_VERSION')) return;
14
15 /**
16 * Authentifie et si ok retourne le tableau de la ligne SQL de l'utilisateur
17 * Si risque de secu repere a l'installation retourne False
18 *
19 * @param string $login
20 * @param string $pass
21 * @param string $serveur
22 * @param bool $phpauth
23 * @return array|bool
24 */
25 function auth_spip_dist ($login, $pass, $serveur='', $phpauth=false) {
26
27 // retrouver le login
28 $login = auth_spip_retrouver_login($login);
29 // login inconnu, n'allons pas plus loin
30 if (!$login) return array();
31
32 $md5pass = "";
33 $shapass = $shanext = "";
34
35 if (preg_match(",^\{([0-9a-f]{64});([0-9a-f]{64})\}$,i",$pass,$regs)){
36 $shapass = $regs[1];
37 $shanext = $regs[2];
38 }
39 // compat avec une base mixte md5/sha256 : le js a envoye les 2 hash
40 elseif (preg_match(",^\{([0-9a-f]{64});([0-9a-f]{64});([0-9a-f]{32});([0-9a-f]{32})\}$,i",$pass,$regs)){
41 $shapass = $regs[1];
42 $shanext = $regs[2];
43 $md5pass = $regs[3];
44 //$md5next = $regs[4];
45 }
46
47 // si envoi non crypte, crypter maintenant
48 elseif ($pass) {
49 $row = sql_fetsel("alea_actuel, alea_futur", "spip_auteurs", "login=" . sql_quote($login,$serveur,'text'),'','','','',$serveur);
50
51 if ($row) {
52 include_spip('auth/sha256.inc');
53 $shapass = _nano_sha256($row['alea_actuel'] . $pass);
54 $shanext = _nano_sha256($row['alea_futur'] . $pass);
55 $md5pass = md5($row['alea_actuel'] . $pass);
56 }
57 }
58
59 // login inexistant ou mot de passe vide
60 if (!$shapass AND !$md5pass) return array();
61
62 $row = sql_fetsel("*", "spip_auteurs", "login=" . sql_quote($login,$serveur,'text') . " AND pass=" . sql_quote($shapass,$serveur,'text') . " AND statut<>'5poubelle'",'','','','',$serveur);
63
64 // compat avec les anciennes bases en md5
65 if (!$row AND $md5pass)
66 $row = sql_fetsel("*", "spip_auteurs", "login=" . sql_quote($login,$serveur,'text') . " AND pass=" . sql_quote($md5pass,$serveur,'text') . " AND statut<>'5poubelle'",'','','','',$serveur);
67
68 // login/mot de passe incorrect
69 if (!$row) return array();
70
71 // fait tourner le codage du pass dans la base
72 // sauf si phpauth : cela reviendrait a changer l'alea a chaque hit, et aucune action verifiable par securiser_action()
73 if ($shanext AND !$phpauth) {
74
75 include_spip('inc/acces'); // pour creer_uniqid
76 @sql_update('spip_auteurs', array('alea_actuel' => 'alea_futur', 'pass' => sql_quote($shanext,$serveur,'text'), 'alea_futur' => sql_quote(creer_uniqid(),$serveur,'text')), "id_auteur=" . $row['id_auteur'].' AND pass IN ('.sql_quote($shapass,$serveur,'text').', '.sql_quote($md5pass,$serveur,'text').')','',$serveur);
77 // En profiter pour verifier la securite de tmp/
78 // Si elle ne fonctionne pas a l'installation, prevenir
79 if (!verifier_htaccess(_DIR_TMP) AND defined('_ECRIRE_INSTALL'))
80 return false;
81 }
82 return $row;
83 }
84
85 /**
86 * Completer le formulaire de login avec le js ou les saisie specifiques a ce mode d'auth
87 *
88 * @param array $flux
89 * @return array
90 */
91 function auth_spip_formulaire_login($flux){
92 // faut il encore envoyer md5 ?
93 // on regarde si il reste des pass md5 en base pour des auteurs en statut pas poubelle
94 // les hash md5 ont une longueur 32, les sha 64
95 // en evitant une requete sql a chaque affichage du formulaire login sans session
96 // (perf issue pour les sites qui mettent le formulaire de login sur la home)
97 $compat_md5 = false;
98 if (!isset($GLOBALS['meta']['sha_256_only']) OR _request('var_mode')){
99 $compat_md5 = sql_countsel("spip_auteurs", "length(pass)=32 AND statut<>'poubelle'");
100 if ($compat_md5 AND isset($GLOBALS['meta']['sha_256_only'])) effacer_meta('sha_256_only');
101 if (!$compat_md5) ecrire_meta('sha_256_only','oui');
102 }
103
104 // javascript qui gere la securite du login en evitant de faire circuler le pass en clair
105 $flux['data'].=
106 ($compat_md5?'<script type="text/javascript" src="'._DIR_JAVASCRIPT.'md5.js"></script>':'')
107 .'<script type="text/javascript" src="'._DIR_JAVASCRIPT.'login-sha-min.js"></script>'
108 .'<script type="text/javascript">/*<![CDATA[*/'
109 ."var alea_actuel='".$flux['args']['contexte']['_alea_actuel']."';"
110 ."var alea_futur='".$flux['args']['contexte']['_alea_futur']."';"
111 ."var login='".$flux['args']['contexte']['var_login']."';"
112 ."var page_auteur = '".generer_url_public('informer_auteur')."';"
113 ."var informe_auteur_en_cours = false;"
114 ."var attente_informe = 0;"
115 ."var compat_md5 = ".($compat_md5?"true;":"false;")
116 ."jQuery(function(){
117 jQuery('#password')
118 .after(\"<em id='pass_securise'><img src='".chemin_image('cadenas-16.png')."' width='16' height='16' alt='" . attribut_html(_T('login_securise')) . "' title='" . attribut_html(_T('login_securise')) . "' \/><\/em>\");
119 affiche_login_secure();
120 jQuery('#var_login').change(actualise_auteur);
121 jQuery('form#formulaire_login').submit(login_submit);
122 });"
123 ."/*]]>*/</script>";
124
125 return $flux;
126 }
127
128
129 /**
130 * Informer du droit de modifier ou non son login
131 * @param string $serveur
132 * @return bool
133 * toujours true pour un auteur cree dans SPIP
134 */
135 function auth_spip_autoriser_modifier_login($serveur=''){
136 if (strlen($serveur))
137 return false; // les fonctions d'ecriture sur base distante sont encore incompletes
138 return true;
139 }
140
141 /**
142 * Verification de la validite d'un login pour le mode d'auth concerne
143 *
144 * @param string $new_login
145 * @param int $id_auteur
146 * si auteur existant deja
147 * @param string $serveur
148 * @return string
149 * message d'erreur si login non valide, chaine vide sinon
150 */
151 function auth_spip_verifier_login($new_login, $id_auteur=0, $serveur=''){
152 // login et mot de passe
153 if (strlen($new_login)){
154 if (strlen($new_login) < _LOGIN_TROP_COURT)
155 return _T('info_login_trop_court_car_pluriel',array('nb'=>_LOGIN_TROP_COURT));
156 else {
157 $n = sql_countsel('spip_auteurs', "login=" . sql_quote($new_login) . " AND id_auteur!=".intval($id_auteur)." AND statut!='5poubelle'",'','',$serveur);
158 if ($n)
159 return _T('info_login_existant');
160 }
161 }
162 return '';
163 }
164
165 /**
166 * Modifier le login d'un auteur SPIP
167 *
168 * @param string $new_login
169 * @param int $id_auteur
170 * @param string $serveur
171 * @return bool
172 */
173 function auth_spip_modifier_login($new_login, $id_auteur, $serveur=''){
174 if (is_null($new_login) OR auth_spip_verifier_login($new_login,$id_auteur,$serveur)!='')
175 return false;
176 if (!$id_auteur = intval($id_auteur)
177 OR !$auteur = sql_fetsel('login','spip_auteurs','id_auteur='.intval($id_auteur),'','','','',$serveur))
178 return false;
179 if ($new_login == $auteur['login'])
180 return true; // on a rien fait mais c'est bon !
181
182 include_spip('action/editer_auteur');
183
184 // vider le login des auteurs a la poubelle qui avaient ce meme login
185 if (strlen($new_login)){
186 $anciens = sql_allfetsel('id_auteur','spip_auteurs','login='.sql_quote($new_login,$serveur,'text')." AND statut='5poubelle'",'','','','',$serveur);
187 while ($row = array_pop($anciens)){
188 auteur_modifier($row['id_auteur'], array('login'=>''), true); // manque la gestion de $serveur
189 }
190 }
191
192 auteur_modifier($id_auteur, array('login'=>$new_login), true); // manque la gestion de $serveur
193
194 return true;
195 }
196
197 /**
198 * Retrouver le login de quelqu'un qui cherche a se loger
199 * Reconnaitre aussi ceux qui donnent leur nom ou email au lieu du login
200 *
201 * @param string $login
202 * @param string $serveur
203 * @return string
204 */
205 function auth_spip_retrouver_login($login, $serveur=''){
206 if (!strlen($login)) return null; // pas la peine de requeter
207 $l = sql_quote($login,$serveur,'text');
208 if ($r = sql_getfetsel('login', 'spip_auteurs',
209 "statut<>'5poubelle'" .
210 " AND (length(pass)>0)" .
211 " AND (login=$l)",'','','','',$serveur))
212 return $r;
213 // Si pas d'auteur avec ce login
214 // regarder s'il a saisi son nom ou son mail.
215 // Ne pas fusionner avec la requete precedente
216 // car un nom peut etre homonyme d'un autre login
217 else return sql_getfetsel('login', 'spip_auteurs',
218 "statut<>'5poubelle'" .
219 " AND (length(pass)>0)" .
220 " AND (login<>'' AND (nom=$l OR email=$l))",'','','','',$serveur);
221 }
222
223
224 /**
225 * informer sur un login
226 * Ce dernier transmet le tableau ci-dessous a la fonction JS informer_auteur
227 * Il est invoque par la fonction JS actualise_auteur via la globale JS
228 * page_auteur=#URL_PAGE{informer_auteur} dans le squelette login
229 * N'y aurait-il pas plus simple ?
230 *
231 * @param array $infos
232 * @param array $row
233 * @param string $serveur
234 * @return array
235 */
236 function auth_spip_informer_login($infos, $row, $serveur=''){
237
238 // pour la methode SPIP on a besoin des alea en plus pour encoder le pass avec
239 $infos['alea_actuel'] = $row['alea_actuel'];
240 $infos['alea_futur'] = $row['alea_futur'];
241
242 return $infos;
243 }
244
245 /**
246 * Informer du droit de modifier ou non le pass
247 * @param string $serveur
248 * @return bool
249 * toujours true pour un auteur cree dans SPIP
250 */
251 function auth_spip_autoriser_modifier_pass($serveur=''){
252 if (strlen($serveur))
253 return false; // les fonctions d'ecriture sur base distante sont encore incompletes
254 return true;
255 }
256
257
258 /**
259 * Verification de la validite d'un mot de passe pour le mode d'auth concerne
260 * c'est ici que se font eventuellement les verifications de longueur mini/maxi
261 * ou de force
262 *
263 * @param string $login
264 * Le login de l'auteur : permet de verifier que pass et login sont differents
265 * meme a la creation lorsque l'auteur n'existe pas encore
266 * @param string $new_pass
267 * Nouveau mot de passe
268 * @param int $id_auteur
269 * si auteur existant deja
270 * @param string $serveur
271 * @return string
272 * message d'erreur si login non valide, chaine vide sinon
273 */
274 function auth_spip_verifier_pass($login, $new_pass, $id_auteur=0, $serveur=''){
275 // login et mot de passe
276 if (strlen($new_pass) < _PASS_LONGUEUR_MINI)
277 return _T('info_passe_trop_court_car_pluriel',array('nb'=>_PASS_LONGUEUR_MINI));
278
279 return '';
280 }
281
282 /**
283 * Modifier le mot de passe de l'auteur sur le serveur concerne
284 * en s'occupant du hash et companie
285 * @param string $login
286 * @param string $new_pass
287 * @param int $id_auteur
288 * @param string $serveur
289 * @return bool
290 */
291 function auth_spip_modifier_pass($login, $new_pass, $id_auteur, $serveur=''){
292 if (is_null($new_pass) OR auth_spip_verifier_pass($login, $new_pass,$id_auteur,$serveur)!='')
293 return false;
294
295 if (!$id_auteur = intval($id_auteur)
296 OR !sql_fetsel('login','spip_auteurs','id_auteur='.intval($id_auteur),'','','','',$serveur))
297 return false;
298
299 $c = array();
300 include_spip('inc/acces');
301 include_spip('auth/sha256.inc');
302 $htpass = generer_htpass($new_pass);
303 $alea_actuel = creer_uniqid();
304 $alea_futur = creer_uniqid();
305 $pass = _nano_sha256($alea_actuel.$new_pass);
306 $c['pass'] = $pass;
307 $c['htpass'] = $htpass;
308 $c['alea_actuel'] = $alea_actuel;
309 $c['alea_futur'] = $alea_futur;
310 $c['low_sec'] = '';
311
312 include_spip('action/editer_auteur');
313 auteur_modifier($id_auteur, $c, true); // manque la gestion de $serveur
314
315 return true; // on a bien modifie le pass
316 }
317
318 /**
319 * Synchroniser les fichiers htpasswd
320 *
321 * @param int $id_auteur
322 * @param array $champs
323 * @param array $options
324 * all=>true permet de demander la regeneration complete des acces apres operation en base (import, upgrade)
325 * @param string $serveur
326 * @return void
327 */
328 function auth_spip_synchroniser_distant($id_auteur, $champs, $options = array(), $serveur=''){
329 // ne rien faire pour une base distante : on ne sait pas regenerer les htaccess
330 if (strlen($serveur))
331 return;
332 // si un login, pass ou statut a ete modifie
333 // regenerer les fichier htpass
334 if (isset($champs['login'])
335 OR isset($champs['pass'])
336 OR isset($champs['statut'])
337 OR (isset($options['all']) AND $options['all'])
338 ) {
339
340 $htaccess = _DIR_RESTREINT . _ACCESS_FILE_NAME;
341 $htpasswd = _DIR_TMP . _AUTH_USER_FILE;
342
343 // Cette variable de configuration peut etre posee par un plugin
344 // par exemple acces_restreint ;
345 // si .htaccess existe, outrepasser spip_meta
346 if (($GLOBALS['meta']['creer_htpasswd'] != 'oui')
347 AND !@file_exists($htaccess)) {
348 spip_unlink($htpasswd);
349 spip_unlink($htpasswd."-admin");
350 return;
351 }
352
353 # remarque : ici on laisse passer les "nouveau" de maniere a leur permettre
354 # de devenir redacteur le cas echeant (auth http)... a nettoyer
355 // attention, il faut au prealable se connecter a la base (necessaire car utilise par install)
356
357 $p1 = ''; // login:htpass pour tous
358 $p2 = ''; // login:htpass pour les admins
359 $s = sql_select("login, htpass, statut", "spip_auteurs", sql_in("statut", array('1comite','0minirezo','nouveau')));
360 while ($t = sql_fetch($s)) {
361 if (strlen($t['login']) AND strlen($t['htpass'])) {
362 $p1 .= $t['login'].':'.$t['htpass']."\n";
363 if ($t['statut'] == '0minirezo')
364 $p2 .= $t['login'].':'.$t['htpass']."\n";
365 }
366 }
367 sql_free($s);
368 if ($p1) {
369 ecrire_fichier($htpasswd, $p1);
370 ecrire_fichier($htpasswd.'-admin', $p2);
371 spip_log("Ecriture de $htpasswd et $htpasswd-admin");
372 }
373 }
374 }
375
376 ?>