[SPIP] ~v3.0.20-->v3.0.25
[lhc/web/clavette_www.git] / www / ecrire / auth / ldap.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 // Authentifie via LDAP et retourne la ligne SQL decrivant l'utilisateur si ok
16
17 // Attributs LDAP correspondants a ceux de SPIP, notamment pour le login
18 // ne pas ecraser une definition perso dans mes_options
19 if (!isset($GLOBALS['ldap_attributes']) OR !is_array($GLOBALS['ldap_attributes'])){
20 $GLOBALS['ldap_attributes'] = array(
21 'login' => array('sAMAccountName', 'uid', 'login', 'userid', 'cn','sn'),
22 'nom' => "cn",
23 'email' => "mail",
24 'bio' => "description");
25 }
26
27 /**
28 * Fonction principale d'authentification du module auth/ldap
29 *
30 * - On se bind avec le compte generique defini dans config/ldap.php,
31 * - On determine le DN de l'utilisateur candidat a l'authentification,
32 * - On se re-bind avec ce DN et le mot de passe propose.
33 *
34 * Si la connexion est autorisee, on renvoie pour enregistrement en session,
35 * en plus des champs SQL habituels, les informations de connexion de
36 * l'utilisateur (DN et password). Cela permettra de se binder en cours de
37 * session sous son identite specifique pour les operations necessitant des
38 * privileges particuliers.
39 * TODO: Gerer une constante de conf qui permette de choisir entre ce
40 * comportement et tout faire avec le compte generique.
41 *
42 * @param string $login
43 * @param string $pass
44 * @param string $serveur
45 * @param bool $phpauth
46 * @return string
47 */
48 // http://doc.spip.org/@inc_auth_ldap_dist
49 function auth_ldap_dist ($login, $pass, $serveur='', $phpauth=false) {
50
51 #spip_log("ldap $login " . ($pass ? "mdp fourni" : "mdp absent"));
52
53 // Utilisateur connu ?
54 // si http auth, inutile de reauthentifier: cela
55 // ne marchera pas avec auth http autre que basic.
56 $checkpass = isset($_SERVER["REMOTE_USER"])?false:true;
57 if (!($dn = auth_ldap_search($login, $pass, $checkpass, $serveur))) return array();
58 $credentials_ldap = array('ldap_dn' => $dn, 'ldap_password' => $pass);
59
60 // Si l'utilisateur figure deja dans la base, y recuperer les infos
61 $r = sql_fetsel("*", "spip_auteurs", "login=" . sql_quote($login) . " AND source='ldap'",'','','','',$serveur);
62
63 if ($r) return array_merge($r, $credentials_ldap);
64
65 // sinon importer les infos depuis LDAP,
66
67 if ($GLOBALS['meta']["ldap_statut_import"]
68 AND $desc = auth_ldap_retrouver($dn, array(), $serveur)) {
69 // rajouter le statut indique a l'install
70 $desc['statut'] = $GLOBALS['meta']["ldap_statut_import"];
71 $desc['login'] = $login;
72 $desc['source'] = 'ldap';
73 $desc['pass'] = '';
74
75 $r = sql_insertq('spip_auteurs', $desc,'',$serveur);
76 }
77
78 if ($r)
79 return array_merge(
80 $credentials_ldap,
81 sql_fetsel("*", "spip_auteurs", "id_auteur=".intval($r),'','','','',$serveur)
82 );
83
84 // sinon echec
85 spip_log("Creation de l'auteur '$login' impossible");
86 return array();
87 }
88
89 /**
90 * Connexion a l'annuaire LDAP
91 * Il faut passer par spip_connect() pour avoir les info
92 * donc potentiellement indiquer un serveur
93 * meme si dans les fait cet argument est toujours vide
94 *
95 * @param string $serveur
96 * @return string
97 */
98 function auth_ldap_connect($serveur='') {
99 include_spip('base/connect_sql');
100 static $connexions_ldap = array();
101 if (isset($connexions_ldap[$serveur])) return $connexions_ldap[$serveur];
102 $connexion = spip_connect($serveur);
103 if (!is_array($connexion['ldap'])) {
104 if ($connexion['authentification']['ldap']) {
105 $f = _DIR_CONNECT . $connexion['authentification']['ldap'];
106 unset($GLOBALS['ldap_link']);
107 if (is_readable($f)) { include_once($f); };
108 if (isset($GLOBALS['ldap_link']))
109 $connexion['ldap'] = array('link' => $GLOBALS['ldap_link'],
110 'base' => $GLOBALS['ldap_base']);
111 else spip_log("connection LDAP $serveur mal definie dans $f");
112 if (isset($GLOBALS['ldap_champs']))
113 $connexion['ldap']['attributes'] = $GLOBALS['ldap_champs'];
114 } else spip_log("connection LDAP $serveur inconnue");
115 }
116 return $connexions_ldap[$serveur]=$connexion['ldap'];
117 }
118
119 /**
120 * Retrouver un login, et verifier son pass si demande par $checkpass
121 *
122 * @param string $login
123 * @param string $pass
124 * @param bool $checkpass
125 * @param string $serveur
126 * @return string
127 * le login trouve ou chaine vide si non trouve
128 */
129 function auth_ldap_search($login, $pass, $checkpass=true, $serveur=''){
130 // Securite anti-injection et contre un serveur LDAP laxiste
131 $login_search = preg_replace("/[^-@._\s\d\w]/", "", $login);
132 if (!strlen($login_search) OR ($checkpass AND !strlen($pass)) )
133 return '';
134
135 // verifier la connexion
136 if (!$ldap = auth_ldap_connect($serveur))
137 return '';
138
139 $ldap_link = isset($ldap['link']) ? $ldap['link'] : null;
140 $ldap_base = isset($ldap['base']) ? $ldap['base'] : null;
141 $desc = isset($ldap['attributes']) && $ldap['attributes'] ? $ldap['attributes'] : $GLOBALS['ldap_attributes'] ;
142
143 $logins = is_array($desc['login']) ? $desc['login'] : array($desc['login']);
144
145 // Tenter une recherche pour essayer de retrouver le DN
146 foreach($logins as $att) {
147 $result = @ldap_search($ldap_link, $ldap_base, "$att=$login_search", array("dn"));
148 $info = @ldap_get_entries($ldap_link, $result);
149 // Ne pas accepter les resultats si plus d'une entree
150 // (on veut un attribut unique)
151
152 if (is_array($info) AND $info['count'] == 1) {
153 $dn = $info[0]['dn'];
154 if (!$checkpass) return $dn;
155 if (@ldap_bind($ldap_link, $dn, $pass)) return $dn;
156 }
157 }
158
159 if ($checkpass AND !isset($dn)) {
160 // Si echec, essayer de deviner le DN
161 foreach($logins as $att) {
162 $dn = "$att=$login_search, $ldap_base";
163 if (@ldap_bind($ldap_link, $dn, $pass))
164 return "$att=$login_search, $ldap_base";
165 }
166 }
167 return '';
168 }
169
170 /**
171 * Retrouver un dn
172 * @param string $dn
173 * @param array $desc
174 * @param string $serveur
175 * @return array
176 */
177 function auth_ldap_retrouver($dn, $desc=array(), $serveur='')
178 {
179 // Lire les infos sur l'utilisateur a partir de son DN depuis LDAP
180
181 if (!$ldap = spip_connect_ldap($serveur)) {
182 spip_log("ldap $serveur injoignable");
183 return array();
184 }
185
186 $ldap_link = $ldap['link'];
187 if (!$desc) {
188 $desc = $ldap['attributes'] ? $ldap['attributes'] : $GLOBALS['ldap_attributes'] ;
189 unset($desc['login']);
190 }
191 $result = @ldap_read($ldap_link, $dn, "objectClass=*", array_values($desc));
192
193 if (!$result) return array();
194
195 // Recuperer les donnees du premier (unique?) compte de l'auteur
196 $val = @ldap_get_entries($ldap_link, $result);
197 if (!is_array($val) OR !is_array($val[0])) return array();
198 $val = $val[0];
199
200 // Convertir depuis UTF-8 (jeu de caracteres par defaut)
201 include_spip('inc/charsets');
202
203 foreach ($desc as $k => $v)
204 $desc[$k] = importer_charset($val[strtolower($v)][0], 'utf-8');
205 return $desc;
206 }
207
208
209 /**
210 * Retrouver le login de quelqu'un qui cherche a se loger
211 *
212 * @param string $login
213 * @param string $serveur
214 * @return string
215 */
216 function auth_ldap_retrouver_login($login, $serveur='')
217 {
218 return auth_ldap_search($login, '', false, $serveur) ? $login : '';
219 }
220
221 /**
222 * Verification de la validite d'un mot de passe pour le mode d'auth concerne
223 * c'est ici que se font eventuellement les verifications de longueur mini/maxi
224 * ou de force.
225 *
226 * @param string $new_pass
227 * @param string $login
228 * le login de l'auteur : permet de verifier que pass et login sont differents
229 * meme a la creation lorsque l'auteur n'existe pas encore
230 * @param int $id_auteur
231 * si auteur existant deja
232 * @param string $serveur
233 * @return string
234 * message d'erreur si login non valide, chaine vide sinon
235 */
236 function auth_ldap_verifier_pass($login, $new_pass, $id_auteur=0, $serveur=''){
237 include_spip('auth/spip');
238 return auth_spip_verifier_pass($login, $new_pass, $id_auteur, $serveur);
239 }
240
241 /**
242 * Informer du droit de modifier ou non le pass
243 *
244 * On ne peut pas détecter a l'avance si l'autorisation sera donnee, il
245 * faudra informer l'utilisateur a posteriori si la modif n'a pas pu se
246 * faire.
247 * @param string $serveur
248 * @return bool
249 * pour un auteur LDAP, a priori toujours true, a conditiion que le serveur
250 * l'autorise: par exemple, pour OpenLDAP il faut avoir dans slapd.conf:
251 * access to attr=userPassword
252 * by self write
253 * ...
254 */
255 function auth_ldap_autoriser_modifier_pass($serveur=''){
256 return true;
257 }
258
259 /**
260 * Fonction de modification du mot de passe
261 *
262 * On se bind au LDAP cette fois sous l'identite de l'utilisateur, car le
263 * compte generique defini dans config/ldap.php n'a generalement pas (et
264 * ne devrait pas avoir) les droits suffisants pour faire la modification.
265 * @param $login
266 * @param $new_pass
267 * @param $id_auteur
268 * @param string $serveur
269 * @return bool
270 * informe du succes ou de l'echec du changement du mot de passe
271 */
272 function auth_ldap_modifier_pass($login, $new_pass, $id_auteur, $serveur=''){
273 if (is_null($new_pass) OR auth_ldap_verifier_pass($login, $new_pass,$id_auteur,$serveur)!='') {
274 return false;
275 }
276 if (!$ldap = auth_ldap_connect($serveur))
277 return '';
278 $link = $ldap['link'];
279 include_spip("inc/session");
280 $dn = session_get('ldap_dn');
281 if ('' == $dn) {
282 return false;
283 }
284 if (!ldap_bind($link, $dn, session_get('ldap_password'))) {
285 return false;
286 }
287 $encoded_pass = "{MD5}".base64_encode(pack("H*",md5($new_pass)));
288 $success = ldap_mod_replace($link, $dn, array('userPassword' => $encoded_pass));
289 return $success;
290 }
291
292
293
294 ?>