PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/mantisbt-1.2.8/core/ldap_api.php

#
PHP | 527 lines | 436 code | 27 blank | 64 comment | 20 complexity | 76f1ebd87051d9ee2ccbba915e7cde92 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0
  1. <?php
  2. # MantisBT - a php based bugtracking system
  3. # MantisBT is free software: you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation, either version 2 of the License, or
  6. # (at your option) any later version.
  7. #
  8. # MantisBT is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with MantisBT. If not, see <http://www.gnu.org/licenses/>.
  15. /**
  16. * LDAP API
  17. * @package CoreAPI
  18. * @subpackage LDAPAPI
  19. * @copyright Copyright (C) 2000 - 2002 Kenzaburo Ito - kenito@300baud.org
  20. * @copyright Copyright (C) 2002 - 2011 MantisBT Team - mantisbt-dev@lists.sourceforge.net
  21. * @link http://www.mantisbt.org
  22. */
  23. /**
  24. * Logs the most recent LDAP error
  25. * @param resource $p_ds LDAP resource identifier returned by ldap_connect
  26. */
  27. function ldap_log_error( $p_ds ) {
  28. log_event( LOG_LDAP, "ERROR #" . ldap_errno( $p_ds ) . ": " . ldap_error( $p_ds ) );
  29. }
  30. /**
  31. * Connect and bind to the LDAP directory
  32. * @param string $p_binddn
  33. * @param string $p_password
  34. * @return resource or false
  35. */
  36. function ldap_connect_bind( $p_binddn = '', $p_password = '' ) {
  37. if( !extension_loaded( 'ldap' ) ) {
  38. log_event( LOG_LDAP, "Error: LDAP extension missing in php" );
  39. trigger_error( ERROR_LDAP_EXTENSION_NOT_LOADED, ERROR );
  40. }
  41. $t_ldap_server = config_get( 'ldap_server' );
  42. # Connect to LDAP server
  43. log_event( LOG_LDAP, "Attempting connection to LDAP URI '{$t_ldap_server}'." );
  44. $t_ds = @ldap_connect( $t_ldap_server );
  45. if ( $t_ds !== false && $t_ds > 0 ) {
  46. log_event( LOG_LDAP, "Connection accepted by LDAP server" );
  47. $t_protocol_version = config_get( 'ldap_protocol_version' );
  48. if( $t_protocol_version > 0 ) {
  49. log_event( LOG_LDAP, "Setting LDAP protocol version to " . $t_protocol_version );
  50. $t_result = @ldap_set_option( $t_ds, LDAP_OPT_PROTOCOL_VERSION, $t_protocol_version );
  51. if( !$t_result ) {
  52. ldap_log_error( $t_ds );
  53. }
  54. }
  55. # Set referrals flag.
  56. $t_follow_referrals = ON == config_get( 'ldap_follow_referrals' );
  57. $t_result = @ldap_set_option( $t_ds, LDAP_OPT_REFERRALS, $t_follow_referrals );
  58. if( !$t_result ) {
  59. ldap_log_error( $t_ds );
  60. }
  61. # If no Bind DN and Password is set, attempt to login as the configured
  62. # Bind DN.
  63. if( is_blank( $p_binddn ) && is_blank( $p_password ) ) {
  64. $p_binddn = config_get( 'ldap_bind_dn', '' );
  65. $p_password = config_get( 'ldap_bind_passwd', '' );
  66. }
  67. if( !is_blank( $p_binddn ) && !is_blank( $p_password ) ) {
  68. log_event( LOG_LDAP, "Attempting bind to ldap server with username and password" );
  69. $t_br = @ldap_bind( $t_ds, $p_binddn, $p_password );
  70. } else {
  71. # Either the Bind DN or the Password are empty, so attempt an anonymous bind.
  72. log_event( LOG_LDAP, "Attempting anonymous bind to ldap server" );
  73. $t_br = @ldap_bind( $t_ds );
  74. }
  75. if ( !$t_br ) {
  76. ldap_log_error( $t_ds );
  77. log_event( LOG_LDAP, "Bind to ldap server failed" );
  78. trigger_error( ERROR_LDAP_SERVER_CONNECT_FAILED, ERROR );
  79. } else {
  80. log_event( LOG_LDAP, "Bind to ldap server successful" );
  81. }
  82. } else {
  83. log_event( LOG_LDAP, "Connection to ldap server failed" );
  84. trigger_error( ERROR_LDAP_SERVER_CONNECT_FAILED, ERROR );
  85. }
  86. return $t_ds;
  87. }
  88. $g_cache_ldap_email = array();
  89. /**
  90. * returns an email address from LDAP, given a userid
  91. * @param int $p_user_id
  92. * @return string
  93. */
  94. function ldap_email( $p_user_id ) {
  95. global $g_cache_ldap_email;
  96. if( isset( $g_cache_ldap_email[ (int)$p_user_id ] ) ) {
  97. return $g_cache_ldap_email[ (int)$p_user_id ];
  98. }
  99. $t_username = user_get_field( $p_user_id, 'username' );
  100. $t_email = ldap_email_from_username( $t_username );
  101. $g_cache_ldap_email[ (int)$p_user_id ] = $t_email;
  102. return $t_email;
  103. }
  104. /**
  105. * Return an email address from LDAP, given a username
  106. * @param string $p_username
  107. * @return string
  108. */
  109. function ldap_email_from_username( $p_username ) {
  110. if ( ldap_simulation_is_enabled() ) {
  111. return ldap_simulation_email_from_username( $p_username );
  112. }
  113. $t_email = ldap_get_field_from_username( $p_username, 'mail' );
  114. if ( $t_email === null ) {
  115. return '';
  116. }
  117. return $t_email;
  118. }
  119. /**
  120. * Gets a user's real name (common name) given the id.
  121. *
  122. * @param int $p_user_id The user id.
  123. * @return string real name.
  124. */
  125. function ldap_realname( $p_user_id ) {
  126. $t_username = user_get_field( $p_user_id, 'username' );
  127. return ldap_realname_from_username( $t_username );
  128. }
  129. /**
  130. * Gets a user real name given their user name.
  131. *
  132. * @param string $p_username The user's name.
  133. * @return string The user's real name.
  134. */
  135. function ldap_realname_from_username( $p_username ) {
  136. if ( ldap_simulation_is_enabled() ) {
  137. return ldap_simulatiom_realname_from_username( $p_username );
  138. }
  139. $t_ldap_realname_field = config_get( 'ldap_realname_field' );
  140. $t_realname = ldap_get_field_from_username( $p_username, $t_ldap_realname_field );
  141. if ( $t_realname === null ) {
  142. return '';
  143. }
  144. return $t_realname;
  145. }
  146. /**
  147. * Escapes the LDAP string to disallow injection.
  148. *
  149. * @param string $p_string The string to escape.
  150. * @return string The escaped string.
  151. */
  152. function ldap_escape_string( $p_string ) {
  153. $t_find = array( '\\', '*', '(', ')', '/', "\x00" );
  154. $t_replace = array( '\5c', '\2a', '\28', '\29', '\2f', '\00' );
  155. $t_string = str_replace( $t_find, $t_replace, $p_string );
  156. return $t_string;
  157. }
  158. /**
  159. * Gets the value of a specific field from LDAP given the user name
  160. * and LDAP field name.
  161. *
  162. * @todo Implement caching by retrieving all needed information in one query.
  163. * @todo Implement logging to LDAP queries same way like DB queries.
  164. *
  165. * @param string $p_username The user name.
  166. * @param string $p_field The LDAP field name.
  167. * @return string The field value or null if not found.
  168. */
  169. function ldap_get_field_from_username( $p_username, $p_field ) {
  170. $t_ldap_organization = config_get( 'ldap_organization' );
  171. $t_ldap_root_dn = config_get( 'ldap_root_dn' );
  172. $t_ldap_uid_field = config_get( 'ldap_uid_field' );
  173. $c_username = ldap_escape_string( $p_username );
  174. log_event( LOG_LDAP, "Retrieving field '$p_field' for '$p_username'" );
  175. # Bind
  176. log_event( LOG_LDAP, "Binding to LDAP server" );
  177. $t_ds = @ldap_connect_bind();
  178. if ( $t_ds === false ) {
  179. ldap_log_error( $t_ds );
  180. return null;
  181. }
  182. # Search
  183. $t_search_filter = "(&$t_ldap_organization($t_ldap_uid_field=$c_username))";
  184. $t_search_attrs = array( $t_ldap_uid_field, $p_field, 'dn' );
  185. log_event( LOG_LDAP, "Searching for $t_search_filter" );
  186. $t_sr = @ldap_search( $t_ds, $t_ldap_root_dn, $t_search_filter, $t_search_attrs );
  187. if ( $t_sr === false ) {
  188. ldap_log_error( $t_ds );
  189. ldap_unbind( $t_ds );
  190. log_event( LOG_LDAP, "ldap search failed" );
  191. return null;
  192. }
  193. # Get results
  194. $t_info = ldap_get_entries( $t_ds, $t_sr );
  195. if ( $t_info === false ) {
  196. ldap_log_error( $t_ds );
  197. log_event( LOG_LDAP, "ldap_get_entries() returned false." );
  198. return null;
  199. }
  200. # Free results / unbind
  201. log_event( LOG_LDAP, "Unbinding from LDAP server" );
  202. ldap_free_result( $t_sr );
  203. ldap_unbind( $t_ds );
  204. # If no matches, return null.
  205. if ( count( $t_info ) == 0 ) {
  206. log_event( LOG_LDAP, "No matches found." );
  207. return null;
  208. }
  209. # Make sure the requested field exists
  210. if( is_array($t_info[0]) && array_key_exists( $p_field, $t_info[0] ) ) {
  211. $t_value = $t_info[0][$p_field][0];
  212. log_event( LOG_LDAP, "Found value '{$t_value}' for field '{$p_field}'." );
  213. } else {
  214. log_event( LOG_LDAP, "WARNING: field '$p_field' does not exist" );
  215. return null;
  216. }
  217. return $t_value;
  218. }
  219. /**
  220. * Return true if the $uid has an assigngroup=$p_group tag, false otherwise
  221. * @param int $p_user_id
  222. * @param string $p_group
  223. * @return bool
  224. */
  225. function ldap_has_group( $p_user_id, $p_group ) {
  226. $t_ldap_organization = config_get( 'ldap_organization' );
  227. $t_ldap_root_dn = config_get( 'ldap_root_dn' );
  228. $t_username = user_get_field( $p_user_id, 'username' );
  229. $t_ldap_uid_field = config_get( 'ldap_uid_field', 'uid' );
  230. $t_search_filter = "(&$t_ldap_organization($t_ldap_uid_field=$t_username)(assignedgroup=$p_group))";
  231. $t_search_attrs = array(
  232. $t_ldap_uid_field,
  233. 'dn',
  234. 'assignedgroup',
  235. );
  236. $t_ds = ldap_connect_bind();
  237. log_event( LOG_LDAP, "Searching for $t_search_filter" );
  238. $t_sr = @ldap_search( $t_ds, $t_ldap_root_dn, $t_search_filter, $t_search_attrs );
  239. if( $t_sr === false ) {
  240. ldap_log_error( $t_ds );
  241. log_event( LOG_LDAP, "ldap search failed" );
  242. $t_entries = 0;
  243. } else {
  244. $t_entries = ldap_count_entries( $t_ds, $t_sr );
  245. if( $t_entries === false ) {
  246. ldap_log_error( $t_ds );
  247. $t_entries = 0;
  248. }
  249. ldap_free_result( $t_sr );
  250. }
  251. ldap_unbind( $t_ds );
  252. if( $t_entries > 0 ) {
  253. return true;
  254. } else {
  255. return false;
  256. }
  257. }
  258. /**
  259. * Attempt to authenticate the user against the LDAP directory
  260. * return true on successful authentication, false otherwise
  261. * @param int $p_user_id
  262. * @param string $p_password
  263. * @return bool
  264. */
  265. function ldap_authenticate( $p_user_id, $p_password ) {
  266. # if password is empty and ldap allows anonymous login, then
  267. # the user will be able to login, hence, we need to check
  268. # for this special case.
  269. if ( is_blank( $p_password ) ) {
  270. return false;
  271. }
  272. $t_username = user_get_field( $p_user_id, 'username' );
  273. return ldap_authenticate_by_username( $t_username, $p_password );
  274. }
  275. /**
  276. * Authenticates an user via LDAP given the username and password.
  277. *
  278. * @param string $p_username The user name.
  279. * @param string $p_password The password.
  280. * @return true: authenticated, false: failed to authenticate.
  281. */
  282. function ldap_authenticate_by_username( $p_username, $p_password ) {
  283. if ( ldap_simulation_is_enabled() ) {
  284. log_event( LOG_LDAP, "Authenticating via LDAP simulation" );
  285. $t_authenticated = ldap_simulation_authenticate_by_username( $p_username, $p_password );
  286. } else {
  287. $c_username = ldap_escape_string( $p_username );
  288. $t_ldap_organization = config_get( 'ldap_organization' );
  289. $t_ldap_root_dn = config_get( 'ldap_root_dn' );
  290. $t_ldap_uid_field = config_get( 'ldap_uid_field', 'uid' );
  291. $t_search_filter = "(&$t_ldap_organization($t_ldap_uid_field=$c_username))";
  292. $t_search_attrs = array(
  293. $t_ldap_uid_field,
  294. 'dn',
  295. );
  296. # Bind
  297. log_event( LOG_LDAP, "Binding to LDAP server" );
  298. $t_ds = ldap_connect_bind();
  299. if ( $t_ds === false ) {
  300. ldap_log_error( $t_ds );
  301. trigger_error( ERROR_LDAP_AUTH_FAILED, ERROR );
  302. }
  303. # Search for the user id
  304. log_event( LOG_LDAP, "Searching for $t_search_filter" );
  305. $t_sr = ldap_search( $t_ds, $t_ldap_root_dn, $t_search_filter, $t_search_attrs );
  306. if ( $t_sr === false ) {
  307. ldap_log_error( $t_ds );
  308. ldap_unbind( $t_ds );
  309. log_event( LOG_LDAP, "ldap search failed" );
  310. trigger_error( ERROR_LDAP_AUTH_FAILED, ERROR );
  311. }
  312. $t_info = @ldap_get_entries( $t_ds, $t_sr );
  313. if ( $t_info === false ) {
  314. ldap_log_error( $t_ds );
  315. ldap_free_result( $t_sr );
  316. ldap_unbind( $t_ds );
  317. trigger_error( ERROR_LDAP_AUTH_FAILED, ERROR );
  318. }
  319. $t_authenticated = false;
  320. if ( $t_info['count'] > 0 ) {
  321. # Try to authenticate to each until we get a match
  322. for ( $i = 0; $i < $t_info['count']; $i++ ) {
  323. $t_dn = $t_info[$i]['dn'];
  324. log_event( LOG_LDAP, "Checking {$t_info[$i]['dn']}" );
  325. # Attempt to bind with the DN and password
  326. if ( @ldap_bind( $t_ds, $t_dn, $p_password ) ) {
  327. $t_authenticated = true;
  328. break;
  329. }
  330. }
  331. } else {
  332. log_event( LOG_LDAP, "No matching entries found" );
  333. }
  334. log_event( LOG_LDAP, "Unbinding from LDAP server" );
  335. ldap_free_result( $t_sr );
  336. ldap_unbind( $t_ds );
  337. }
  338. # If user authenticated successfully then update the local DB with information
  339. # from LDAP. This will allow us to use the local data after login without
  340. # having to go back to LDAP. This will also allow fallback to DB if LDAP is down.
  341. if ( $t_authenticated ) {
  342. $t_user_id = user_get_id_by_name( $p_username );
  343. if ( false !== $t_user_id ) {
  344. user_set_field( $t_user_id, 'password', md5( $p_password ) );
  345. if ( ON == config_get( 'use_ldap_realname' ) ) {
  346. $t_realname = ldap_realname( $t_user_id );
  347. user_set_field( $t_user_id, 'realname', $t_realname );
  348. }
  349. if ( ON == config_get( 'use_ldap_email' ) ) {
  350. $t_email = ldap_email_from_username( $p_username );
  351. user_set_field( $t_user_id, 'email', $t_email );
  352. }
  353. }
  354. log_event( LOG_LDAP, "User '$p_username' authenticated" );
  355. } else {
  356. log_event( LOG_LDAP, "Authentication failed" );
  357. }
  358. return $t_authenticated;
  359. }
  360. /**
  361. * Checks if the LDAP simulation mode is enabled.
  362. *
  363. * @return bool true if enabled, false otherwise.
  364. */
  365. function ldap_simulation_is_enabled() {
  366. $t_filename = config_get( 'ldap_simulation_file_path' );
  367. return !is_blank( $t_filename );
  368. }
  369. /**
  370. * Gets a user from LDAP simulation mode given the username.
  371. *
  372. * @param string $p_username The user name.
  373. * @return mixed an associate array with user information or null if not found.
  374. */
  375. function ldap_simulation_get_user( $p_username ) {
  376. $t_filename = config_get( 'ldap_simulation_file_path' );
  377. $t_lines = file( $t_filename );
  378. if ( $t_lines === false ) {
  379. log_event( LOG_LDAP, "ldap_simulation_get_user: could not read simulation data from $t_filename." );
  380. trigger_error( ERROR_LDAP_SERVER_CONNECT_FAILED, ERROR );
  381. }
  382. foreach ( $t_lines as $t_line ) {
  383. $t_line = trim( $t_line, " \t\r\n" );
  384. $t_row = explode( ',', $t_line );
  385. if ( $t_row[0] != $p_username ) {
  386. continue;
  387. }
  388. $t_user = array();
  389. $t_user['username'] = $t_row[0];
  390. $t_user['realname'] = $t_row[1];
  391. $t_user['email'] = $t_row[2];
  392. $t_user['password'] = $t_row[3];
  393. return $t_user;
  394. }
  395. log_event( LOG_LDAP, "ldap_simulation_get_user: user '$p_username' not found." );
  396. return null;
  397. }
  398. /**
  399. * Given a username, gets the email address or empty address if user is not found.
  400. *
  401. * @param string $p_username The user name.
  402. * @return The email address or blank if user is not found.
  403. */
  404. function ldap_simulation_email_from_username( $p_username ) {
  405. $t_user = ldap_simulation_get_user( $p_username );
  406. if ( $t_user === null ) {
  407. log_event( LOG_LDAP, "ldap_simulation_email_from_username: user '$p_username' not found." );
  408. return '';
  409. }
  410. log_event( LOG_LDAP, "ldap_simulation_email_from_username: user '$p_username' has email '{$t_user['email']}'." );
  411. return $t_user['email'];
  412. }
  413. /**
  414. * Given a username, this methods gets the realname or empty string if not found.
  415. *
  416. * @param string $p_username The username.
  417. * @return string The real name or an empty string if not found.
  418. */
  419. function ldap_simulatiom_realname_from_username( $p_username ) {
  420. $t_user = ldap_simulation_get_user( $p_username );
  421. if ( $t_user === null ) {
  422. log_event( LOG_LDAP, "ldap_simulatiom_realname_from_username: user '$p_username' not found." );
  423. return '';
  424. }
  425. log_event( LOG_LDAP, "ldap_simulatiom_realname_from_username: user '$p_username' has email '{$t_user['realname']}'." );
  426. return $t_user['realname'];
  427. }
  428. /**
  429. * Authenticates the specified user id / password based on the simulation data.
  430. *
  431. * @param string $p_username The username.
  432. * @param string $p_password The password.
  433. * @return bool true for authenticated, false otherwise.
  434. */
  435. function ldap_simulation_authenticate_by_username( $p_username, $p_password ) {
  436. $c_username = ldap_escape_string( $p_username );
  437. $t_user = ldap_simulation_get_user( $c_username );
  438. if ( $t_user === null ) {
  439. log_event( LOG_LDAP, "ldap_simulation_authenticate: user '$p_username' not found." );
  440. return false;
  441. }
  442. if ( $t_user['password'] != $p_password ) {
  443. log_event( LOG_LDAP, "ldap_simulation_authenticate: expected password '{$t_user['password']}' and got '$p_password'." );
  444. return false;
  445. }
  446. log_event( LOG_LDAP, "ldap_simulation_authenticate: authentication successful for user '$p_username'." );
  447. return true;
  448. }