1<?php
2
3// Pandora FMS - http://pandorafms.com
4// ==================================================
5// Copyright (c) 2005-2009 Artica Soluciones Tecnologicas
6// Please see http://pandorafms.org for full contribution list
7
8// This program is free software; you can redistribute it and/or
9// modify it under the terms of the  GNU Lesser General Public License
10// as published by the Free Software Foundation; version 2
11
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16
17/**
18 * @package Include/auth
19 */
20
21if (!isset ($config)) {
22	die ('
23<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
24<html>
25	<head>
26		<title>Pandora FMS - The Flexible Monitoring System - Console error</title>
27		<meta http-equiv="expires" content="0">
28		<meta http-equiv="content-type" content="text/html; charset=utf8">
29		<meta name="resource-type" content="document">
30		<meta name="distribution" content="global">
31		<meta name="author" content="Sancho Lerena">
32		<meta name="copyright" content="This is GPL software. Created by Sancho Lerena and others">
33		<meta name="keywords" content="pandora, monitoring, system, GPL, software">
34		<meta name="robots" content="index, follow">
35		<link rel="icon" href="../../images/pandora.ico" type="image/ico">
36		<link rel="stylesheet" href="../styles/pandora.css" type="text/css">
37	</head>
38	<body>
39		<div id="main" style="float:left; margin-left: 100px">
40			<div align="center">
41				<div id="login_f">
42					<h1 id="log_f" class="error">You cannot access this file</h1>
43					<div>
44						<img src="../../images/pandora_logo.png" border="0" />
45					</div>
46					<div class="msg">
47						<span class="error">
48							<b>ERROR:</b> You can\'t access this file directly!
49						</span>
50					</div>
51				</div>
52			</div>
53		</div>
54	</body>
55</html>
56');
57}
58
59include_once($config['homedir'] . "/include/functions_profile.php");
60enterprise_include ('include/auth/mysql.php');
61
62$config["user_can_update_info"] = true;
63$config["user_can_update_password"] = true;
64$config["admin_can_add_user"] = true;
65$config["admin_can_delete_user"] = true;
66$config["admin_can_disable_user"] = false; //currently not implemented
67$config["admin_can_make_admin"] = true;
68
69/**
70 * process_user_login accepts $login and $pass and handles it according to current authentication scheme
71 *
72 * @param string $login
73 * @param string $pass
74 * @param boolean $api
75 *
76 * @return mixed False in case of error or invalid credentials, the username in case it's correct.
77 */
78function process_user_login ($login, $pass, $api = false) {
79	global $config, $mysql_cache;
80
81	// Always authenticate admins against the local database
82	if (strtolower ($config["auth"]) == 'mysql' || is_user_admin ($login)) {
83		return process_user_login_local ($login, $pass, $api);
84	}
85	else {
86		$login_remote = process_user_login_remote ($login, $pass, $api);
87		if ($login_remote == false && $config['fallback_local_auth'] == '1') {
88			return process_user_login_local ($login, $pass, $api);
89		}
90		else {
91			return $login_remote;
92		}
93	}
94
95	return false;
96}
97
98function process_user_login_local ($login, $pass, $api = false) {
99	global $config, $mysql_cache;
100
101	// Connect to Database
102	switch ($config["dbtype"]) {
103		case "mysql":
104			if (!$api) {
105				$sql = sprintf ("SELECT `id_user`, `password`
106					FROM `tusuario`
107					WHERE `id_user` = '%s' AND `not_login` = 0
108						AND `disabled` = 0", $login);
109			}
110			else {
111				$sql = sprintf ("SELECT `id_user`, `password`
112					FROM `tusuario`
113					WHERE `id_user` = '%s'
114						AND `disabled` = 0", $login);
115			}
116			break;
117		case "postgresql":
118			if (!$api) {
119				$sql = sprintf ('SELECT "id_user", "password"
120					FROM "tusuario"
121					WHERE "id_user" = \'%s\' AND "not_login" = 0
122						AND "disabled" = 0', $login);
123			}
124			else {
125				$sql = sprintf ('SELECT "id_user", "password"
126					FROM "tusuario"
127					WHERE "id_user" = \'%s\'
128						AND "disabled" = 0', $login);
129			}
130			break;
131		case "oracle":
132			if (!$api) {
133				$sql = sprintf ('SELECT id_user, password
134					FROM tusuario
135					WHERE id_user = \'%s\' AND not_login = 0
136						AND disabled = 0', $login);
137			}
138			else {
139				$sql = sprintf ('SELECT id_user, password
140					FROM tusuario
141					WHERE id_user = \'%s\'
142						AND disabled = 0', $login);
143			}
144			break;
145	}
146	$row = db_get_row_sql ($sql);
147
148	//Check that row exists, that password is not empty and that password is the same hash
149	if ($row !== false && $row["password"] !== md5 ("")
150		&& $row["password"] == md5 ($pass)) {
151		// Login OK
152		// Nick could be uppercase or lowercase (select in MySQL
153		// is not case sensitive)
154		// We get DB nick to put in PHP Session variable,
155		// to avoid problems with case-sensitive usernames.
156		// Thanks to David Muñiz for Bug discovery :)
157		return $row["id_user"];
158	}
159	else {
160		if (!user_can_login($login)) {
161			$mysql_cache["auth_error"] = "User only can use the API.";
162			$config["auth_error"] = "User only can use the API.";
163		}
164		else {
165			$mysql_cache["auth_error"] = "User not found in database or incorrect password";
166			$config["auth_error"] = "User not found in database or incorrect password";
167		}
168	}
169
170	return false;
171}
172
173function process_user_login_remote ($login, $pass, $api = false) {
174	global $config, $mysql_cache;
175
176
177
178	// Remote authentication
179	switch ($config["auth"]) {
180		// LDAP
181		case 'ldap':
182			if (ldap_process_user_login ($login, $pass) === false) {
183				$config["auth_error"] = "User not found in database or incorrect password";
184				return false;
185			}
186			break;
187
188		// Active Directory
189		case 'ad':
190			if (enterprise_hook ('ad_process_user_login', array ($login, $pass)) === false) {
191				$config["auth_error"] = "User not found in database or incorrect password";
192				return false;
193			}
194			break;
195
196		// Remote Pandora FMS
197		case 'pandora':
198			if (enterprise_hook ('remote_pandora_process_user_login', array ($login, $pass)) === false) {
199				$config["auth_error"] = "User not found in database or incorrect password";
200				return false;
201			}
202			break;
203
204		// Remote Babel Enterprise
205		case 'babel':
206			if (enterprise_hook ('remote_babel_process_user_login', array ($login, $pass)) === false) {
207				$config["auth_error"] = "User not found in database or incorrect password";
208				return false;
209			}
210			break;
211
212		// Remote Integria
213		case 'integria':
214			if (enterprise_hook ('remote_integria_process_user_login', array ($login, $pass)) === false) {
215				$config["auth_error"] = "User not found in database or incorrect password";
216				return false;
217			}
218			break;
219
220		// Unknown authentication method
221		default:
222			$config["auth_error"] = "User not found in database
223					or incorrect password";
224			return false;
225			break;
226	}
227
228	// Authentication ok, check if the user exists in the local database
229	if (is_user ($login)) {
230
231
232		if (!user_can_login($login)) {
233			return false;
234		}
235
236		if (($config["auth"] === 'ad') &&
237			(isset($config['ad_advanced_config']) && $config['ad_advanced_config'])) {
238
239
240
241			$return = enterprise_hook ('prepare_permissions_groups_of_user_ad',
242				array ($login, $pass, false, true));
243
244			if ($return === "error_permissions") {
245				$config["auth_error"] =
246					__("Problems with configuration permissions. Please contact with Administrator");
247				return false;
248			}
249			else {
250				if ($return === "permissions_changed") {
251					$config["auth_error"] = __("Your permmission have been change. Please, login again");
252					return false;
253				}
254			}
255		}
256		return $login;
257	}
258
259
260
261
262	// The user does not exist and can not be created
263	if ($config['autocreate_remote_users'] == 0 || is_user_blacklisted ($login)) {
264		$config["auth_error"] = __("Ooops User not found in
265				database or incorrect password");
266
267		return false;
268	}
269
270	// Create the user in the local database
271	if (isset($config['ad_advanced_config']) && $config['ad_advanced_config']) {
272
273
274		if ( defined('METACONSOLE') ) {
275			enterprise_include_once('include/functions_metaconsole.php');
276			enterprise_include_once ('meta/include/functions_groups_meta.php');
277
278			$return = groups_meta_synchronizing();
279
280			if ($return["group_create_err"] > 0  || $return["group_update_err"] > 0) {
281				$config["auth_error"] = __('Fail the group synchronizing');
282				return false;
283			}
284
285			$return = meta_tags_synchronizing();
286			if ($return['tag_create_err'] > 0 || $return['tag_update_err'] > 0) {
287				$config["auth_error"] = __('Fail the tag synchronizing');
288				return false;
289			}
290		}
291
292		// Create the user
293		if (enterprise_hook ('prepare_permissions_groups_of_user_ad',
294				array($login,
295					$pass,
296					array ('fullname' => $login,
297						'comments' => 'Imported from ' . $config['auth']),
298					false, defined('METACONSOLE'))) === false) {
299
300			$config["auth_error"] = __("User not found in database
301					or incorrect password");
302
303			return false;
304		}
305	}
306	else {
307		// Create the user in the local database
308		if (create_user ($login, $pass,
309				array ('fullname' => $login,
310				'comments' => 'Imported from ' . $config['auth'])
311			) === false) {
312			$config["auth_error"] = __("User not found in database or incorrect password");
313			return false;
314		}
315
316		//TODO: Check the creation in the nodes
317
318		profile_create_user_profile ($login, $config['default_remote_profile'],
319			$config['default_remote_group'], false, $config['default_assign_tags']);
320	}
321
322	return $login;
323}
324
325/**
326 * Checks if a user is administrator.
327 *
328 * @param string User id.
329 *
330 * @return bool True is the user is admin
331 */
332function is_user_admin ($id_user) {
333	$is_admin = (bool) db_get_value ('is_admin', 'tusuario', 'id_user', $id_user);
334
335	return $is_admin;
336}
337
338
339/**
340 * Get the user id field on a mixed structure.
341 *
342 * This function is needed to make auth system more compatible and independant.
343 *
344 * @param mixed User structure to get id. It might be a row returned from
345 * tusuario or tusuario_perfil. If it's not a row, the int value is returned.
346 *
347 * @return int User id of the mixed parameter.
348 */
349function get_user_id ($user) {
350	if (is_array ($user)) {
351		if (isset ($user['id_user']))
352			return $user['id_user'];
353		elseif (isset ($user['id_usuario']))
354			return $user['id_usuario'];
355		else
356			return false;
357	}
358	else {
359		return $user;
360	}
361}
362
363/**
364 * Check is a user exists in the system
365 *
366 * @param mixed User id.
367 *
368 * @return bool True if the user exists.
369 */
370function is_user ($user) {
371	$user = db_get_row('tusuario', 'id_user', get_user_id ($user));
372
373	if (! $user)
374		return false;
375
376	return true;
377}
378
379function user_can_login($user) {
380	$not_login = db_get_value('not_login', 'tusuario', 'id_user', $user);
381
382	if ($not_login != 0) {
383		return false;
384	}
385
386	return true;
387}
388
389/**
390 * Gets the users real name
391 *
392 * @param mixed User id.
393 *
394 * @return string The users full name
395 */
396function get_user_fullname ($user) {
397	return (string) db_get_value ('fullname', 'tusuario', 'id_user', get_user_id ($user));
398}
399
400/**
401 * Gets the users email
402 *
403 * @param mixed User id.
404 *
405 * @return string The users email address
406 */
407function get_user_email ($user) {
408	return (string) db_get_value ('email', 'tusuario', 'id_user', get_user_id ($user));
409}
410
411/**
412 * Gets a Users info
413 *
414 * @param mixed User id
415 *
416 * @return mixed An array of users
417 */
418function get_user_info ($user) {
419	return db_get_row ("tusuario", "id_user", get_user_id ($user));
420}
421
422/**
423 * Get a list of all users in an array [username] => array (userinfo)
424 * We can't simplify this because some auth schemes (like LDAP) automatically (or it's at least cheaper to) return all the information
425 * Functions like get_user_info allow selection of specifics (in functions_db)
426 *
427 * @param string Field to order by (id_user, fullname or registered)
428 *
429 * @return array An array of user information
430 */
431function get_users ($order = "fullname", $filter = false, $fields = false) {
432	if (is_array($order)) {
433		$filter['order'] = $order['field'] . ' ' . $order['order'];
434	}
435	else {
436		switch ($order) {
437			case "registered":
438			case "last_connect":
439			case "fullname":
440				break;
441			default:
442				$order = "fullname";
443				break;
444		}
445
446		$filter['order'] = $order." ASC";
447	}
448
449
450	$output = array();
451
452	$result = db_get_all_rows_filter ("tusuario", $filter, $fields);
453	if ($result !== false) {
454		foreach ($result as $row) {
455			$output[$row["id_user"]] = $row;
456		}
457	}
458
459	return $output;
460}
461
462/**
463 * Sets the last login for a user
464 *
465 * @param string User id
466 */
467function process_user_contact ($id_user) {
468	return db_process_sql_update ("tusuario",
469		array ("last_connect" => get_system_time ()),
470		array ("id_user" => $id_user));
471}
472
473/**
474 * Create a new user
475 *
476 * @return bool false
477 */
478function create_user ($id_user, $password, $user_info) {
479	$values = $user_info;
480	$values["id_user"] = $id_user;
481	$values["password"] = md5 ($password);
482	$values["last_connect"] = 0;
483	$values["registered"] = get_system_time ();
484
485	return (@db_process_sql_insert ("tusuario", $values)) !== false;
486}
487
488/**
489 * Save password history
490 *
491 * @return bool false
492 */
493function save_pass_history ($id_user, $password) {
494	$values["id_user"] = $id_user;
495	$values["password"] = md5 ($password);
496	$values["date_begin"] = date ("Y/m/d H:i:s", get_system_time());
497
498	return (@db_process_sql_insert ("tpassword_history", $values)) !== false;
499}
500
501/**
502 * Deletes the user
503 *
504 * @param string User id
505 */
506function delete_user ($id_user) {
507	$result = db_process_sql_delete('tusuario_perfil',
508		array('id_usuario' => $id_user));
509	if ($result === false) {
510		return false;
511	}
512
513	$result = db_process_sql_delete('tusuario',
514		array('id_user' => $id_user));
515	if ($result === false) {
516		return false;
517	}
518	return true;
519}
520
521/**
522 * Update the password in MD5 for user pass as id_user with
523 * password in plain text.
524 *
525 * @param string user User ID
526 * @param string password Password in plain text.
527 *
528 * @return mixed False in case of error or invalid values passed. Affected rows otherwise
529 */
530function update_user_password ($user, $password_new) {
531	global $config;
532	if (isset($config['auth']) && $config['auth'] == 'pandora') {
533		$sql = sprintf("UPDATE tusuario SET password = '" . md5($password_new) .
534		"', last_pass_change = '" . date("Y-m-d H:i:s", get_system_time()) .
535		"' WHERE id_user = '" . $user . "'");
536
537		$connection = mysql_connect_db($config['rpandora_server'],
538			$config['rpandora_dbname'], $config['rpandora_user'],
539			$config['rpandora_pass']);
540		$remote_pass_update = db_process_sql ($sql, 'affected_rows', $connection);
541
542		if (!$remote_pass_update) {
543			$config["auth_error"] = __('Could not changes password on remote pandora');
544			return false;
545		}
546	}
547	return db_process_sql_update ('tusuario',
548		array ('password' => md5 ($password_new), 'last_pass_change' => date ("Y/m/d H:i:s", get_system_time())),
549		array ('id_user' => $user));
550}
551
552/**
553 * Update the data of a user that user is choose with
554 * id_user.
555 *
556 * @param string user User ID
557 * @param array values Associative array with index as name of field and content.
558 *
559 * @return mixed False in case of error or invalid values passed. Affected rows otherwise
560 */
561function update_user ($id_user, $values) {
562	if (! is_array ($values))
563		return false;
564
565	return db_process_sql_update ("tusuario", $values, array ("id_user" => $id_user));
566}
567
568/**
569 * Authenticate against an LDAP server.
570 *
571 * @param string User login
572 * @param string User password (plain text)
573 *
574 * @return bool True if the login is correct, false in other case
575 */
576function ldap_process_user_login ($login, $password) {
577	global $config;
578
579	if (! function_exists ("ldap_connect")) {
580		$config["auth_error"] = __('Your installation of PHP does not support LDAP');
581
582		return false;
583	}
584
585	// Connect to the LDAP server
586	$ds = @ldap_connect ($config["ldap_server"], $config["ldap_port"]);
587
588	if (!$ds) {
589		$config["auth_error"] = 'Error connecting to LDAP server';
590
591		return false;
592	}
593
594	// Set the LDAP version
595	ldap_set_option ($ds, LDAP_OPT_PROTOCOL_VERSION, $config["ldap_version"]);
596
597	if ($config["ldap_start_tls"]) {
598		if (!@ldap_start_tls ($ds)) {
599			$config["auth_error"] = 'Could not start TLS for LDAP connection';
600			@ldap_close ($ds);
601
602			return false;
603		}
604	}
605
606	$ldap_login_attr  = isset($config["ldap_login_attr"]) ? io_safe_output($config["ldap_login_attr"]) . "=" : '';
607	$ldap_base_dn  = isset($config["ldap_base_dn"]) ? "," . io_safe_output($config["ldap_base_dn"]) : '';
608
609	if (strlen($password) == 0 ||
610		!@ldap_bind($ds,
611			$ldap_login_attr. io_safe_output($login) . $ldap_base_dn,
612				$password)) {
613
614		$config["auth_error"] = 'User not found in database or incorrect password';
615		@ldap_close ($ds);
616
617		return false;
618	}
619
620	@ldap_close ($ds);
621
622	return true;
623}
624
625/**
626 * Checks if a user is in the autocreate blacklist.
627 *
628 * @param string User
629 *
630 * @return bool True if the user is in the blacklist, false otherwise.
631 */
632function is_user_blacklisted ($user) {
633	global $config;
634
635	$blisted_users = explode (',', $config['autocreate_blacklist']);
636	foreach ($blisted_users as $blisted_user) {
637		if ($user == $blisted_user) {
638			return true;
639		}
640	}
641
642	return false;
643}
644
645//Reference the global use authorization error to last auth error.
646$config["auth_error"] = &$mysql_cache["auth_error"];
647?>