. * */ /** * * This class handles all of the session related stuff in Ampache * it takes over for the vauth libs, and takes some stuff out of other * classes where it didn't belong. */ class Auth { /** * Constructor * * This should never be called */ private function __construct() { // Rien a faire } /** * logout * * This is called when you want to log out and nuke your session. * This is the function used for the Ajax logouts, if no id is passed * it tries to find one from the session, * @param string $key * @param boolean $relogin */ public static function logout($key='', $relogin = true) { // If no key is passed try to find the session id $key = $key ? $key : session_id(); // Nuke the cookie before all else Session::destroy($key); if ((!$relogin) && AmpConfig::get('logout_redirect')) { $target = AmpConfig::get('logout_redirect'); } else { $target = AmpConfig::get('web_path') . '/login.php'; } // Do a quick check to see if this is an AJAXed logout request // if so use the iframe to redirect if (defined('AJAX_INCLUDE')) { ob_end_clean(); ob_start(); xoutput_headers(); $results = array(); $results['rfc3514'] = ''; echo xoutput_from_array($results); } else { /* Redirect them to the login page */ header('Location: ' . $target); } exit; } /** * login * * This takes a username and password and then returns the results * based on what happens when we try to do the auth. * @param string $username * @param string $password * @param boolean $allow_ui * @return array */ public static function login($username, $password, $allow_ui = false) { $results = array(); foreach (AmpConfig::get('auth_methods') as $method) { $function_name = $method . '_auth'; if (!method_exists('Auth', $function_name)) { continue; } $results = self::$function_name($username, $password); if ($results['success'] || ($allow_ui && !empty($results['ui_required']))) { break; } } return $results; } /** * login_step2 * * This process authenticate step2 for an auth module * @param string $auth_mod * @return array */ public static function login_step2($auth_mod) { $results = null; if (in_array($auth_mod, AmpConfig::get('auth_methods'))) { $function_name = $auth_mod . '_auth_2'; if (method_exists('Auth', $function_name)) { $results = self::$function_name(); } } return $results; } /** * mysql_auth * * This is the core function of our built-in authentication. * @param string $username * @param string $password * @return array */ private static function mysql_auth($username, $password) { if (strlen($password) && strlen($username)) { $sql = 'SELECT `password` FROM `user` WHERE `username` = ?'; $db_results = Dba::read($sql, array($username)); if ($row = Dba::fetch_assoc($db_results)) { // Use SHA2 now... cooking with fire. // For backwards compatibility we hash a couple of different // variations of the password. Increases collision chances, but // doesn't break things. // FIXME: Break things in the future. $hashed_password = array(); $hashed_password[] = hash('sha256', $password); $hashed_password[] = hash('sha256', Dba::escape(stripslashes(htmlspecialchars(strip_tags($password))))); // Automagically update the password if it's old and busted. if ($row['password'] == $hashed_password[1] && $hashed_password[0] != $hashed_password[1]) { $user = User::get_from_username($username); $user->update_password($password); } if (in_array($row['password'], $hashed_password)) { return array( 'success' => true, 'type' => 'mysql', 'username' => $username ); } } } return array( 'success' => false, 'error' => 'MySQL login attempt failed' ); } /** * pam_auth * * Check to make sure the pam_auth function is implemented (module is * installed), then check the credentials. * @param string $username * @param string $password * @return array */ private static function pam_auth($username, $password) { $results = array(); if (!function_exists('pam_auth')) { $results['success'] = false; $results['error'] = 'The PAM PHP module is not installed'; return $results; } $password = scrub_in($password); if (pam_auth($username, $password)) { $results['success'] = true; $results['type'] = 'pam'; $results['username'] = $username; } else { $results['success'] = false; $results['error'] = 'PAM login attempt failed'; } return $results; } /** * external_auth * * Calls an external program compatible with mod_authnz_external * such as pwauth. * @param string $username * @param string $password * @return array */ private static function external_auth($username, $password) { $authenticator = AmpConfig::get('external_authenticator'); if (!$authenticator) { return array( 'success' => false, 'error' => 'No external authenticator configured' ); } //FIXME: should we do input sanitization? $proc = proc_open($authenticator, array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w') ), $pipes); if (is_resource($proc)) { fwrite($pipes[0], $username . "\n" . $password . "\n"); fclose($pipes[0]); fclose($pipes[1]); if ($stderr = fread($pipes[2], 8192)) { debug_event('external_auth', "fread error: " . $stderr, 5); } fclose($pipes[2]); } else { return array( 'success' => false, 'error' => 'Failed to run external authenticator' ); } if (proc_close($proc) == 0) { return array( 'success' => true, 'type' => 'external', 'username' => $username ); } return array( 'success' => false, 'error' => 'The external authenticator did not accept the login' ); } /** * ldap_auth * @param string $username * @param string $password * @return array */ private static function ldap_auth($username, $password) { return LDAP::auth($username, $password); } /** * http_auth * This auth method relies on HTTP auth from the webserver * * @param string $username * @param string $password * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ private static function http_auth($username, $password) { $results = array(); if (($_SERVER['REMOTE_USER'] == $username) || ($_SERVER['HTTP_REMOTE_USER'] == $username)) { $results['success'] = true; $results['type'] = 'http'; $results['username'] = $username; $results['name'] = $username; $results['email'] = ''; } else { $results['success'] = false; $results['error'] = 'HTTP auth login attempt failed'; } return $results; } // http_auth /** * openid_auth * Authenticate user with OpenID * * @param string $username * @param string $password * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ private static function openid_auth($username, $password) { $results = array(); // Username contains the openid url. We don't care about password here. $website = $username; if (strpos($website, 'http://') === 0 || strpos($website, 'https://') === 0) { $consumer = Openid::get_consumer(); if ($consumer) { $auth_request = $consumer->begin($website); if ($auth_request) { $sreg_request = Auth_OpenID_SRegRequest::build( // Required array('nickname'), // Optional array('fullname', 'email') ); if ($sreg_request) { $auth_request->addExtension($sreg_request); } $pape_request = new Auth_OpenID_PAPE_Request(Openid::get_policies()); if ($pape_request) { $auth_request->addExtension($pape_request); } // Redirect the user to the OpenID server for authentication. // Store the token for this authentication so we can verify the response. // For OpenID 1, send a redirect. For OpenID 2, use a Javascript // form to send a POST request to the server. if ($auth_request->shouldSendRedirect()) { $redirect_url = $auth_request->redirectURL(AmpConfig::get('web_path'), Openid::get_return_url()); if (Auth_OpenID::isFailure($redirect_url)) { $results['success'] = false; $results['error'] = 'Could not redirect to server: ' . $redirect_url->message; } else { // Send redirect. debug_event('auth', 'OpenID 1: redirecting to ' . $redirect_url, '5'); header("Location: " . $redirect_url); } } else { // Generate form markup and render it. $form_id = 'openid_message'; $form_html = $auth_request->htmlMarkup(AmpConfig::get('web_path'), Openid::get_return_url(), false, array('id' => $form_id)); if (Auth_OpenID::isFailure($form_html)) { $results['success'] = false; $results['error'] = 'Could not render authentication form.'; } else { debug_event('auth', 'OpenID 2: javascript redirection code to OpenID form.', '5'); // First step is a success, UI interaction required. $results['success'] = false; $results['ui_required'] = $form_html; } } } else { debug_event('auth', $website . ' is not a valid OpenID.', '3'); $results['success'] = false; $results['error'] = 'Not a valid OpenID.'; } } else { debug_event('auth', 'Cannot initialize OpenID resources.', '3'); $results['success'] = false; $results['error'] = 'Cannot initialize OpenID resources.'; } } else { debug_event('auth', 'Skipped OpenID authentication: missing scheme in ' . $website . '.', '3'); $results['success'] = false; $results['error'] = 'Missing scheme in OpenID.'; } return $results; } /** * openid_auth_2 * Authenticate user with OpenID, step 2 * @return array */ private static function openid_auth_2() { $results = array(); $results['type'] = 'openid'; $consumer = Openid::get_consumer(); if ($consumer) { $response = $consumer->complete(Openid::get_return_url()); if ($response->status == Auth_OpenID_CANCEL) { $results['success'] = false; $results['error'] = 'OpenID verification cancelled.'; } else { if ($response->status == Auth_OpenID_FAILURE) { $results['success'] = false; $results['error'] = 'OpenID authentication failed: ' . $response->message; } else { if ($response->status == Auth_OpenID_SUCCESS) { // Extract the identity URL and Simple Registration data (if it was returned). $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response); $sreg = $sreg_resp->contents(); $results['website'] = $response->getDisplayIdentifier(); if (@$sreg['email']) { $results['email'] = $sreg['email']; } if (@$sreg['nickname']) { $results['username'] = $sreg['nickname']; } if (@$sreg['fullname']) { $results['name'] = $sreg['fullname']; } $users = User::get_from_website($results['website']); if (count($users) > 0) { if (count($users) == 1) { $user = new User($users[0]); $results['success'] = true; $results['username'] = $user->username; } else { // Several users for the same website/openid? Allowed but stupid, try to get a match on username. // Should we make website field unique? foreach ($users as $id) { $user = new User($id); if ($user->username == $results['username']) { $results['success'] = true; $results['username'] = $user->username; } } } } else { // Don't return success if an user already exists for this username but don't have this openid identity as website $user = User::get_from_username($results['username']); if ($user->id) { $results['success'] = false; $results['error'] = 'No user associated to this OpenID and username already taken.'; } else { $results['success'] = true; $results['error'] = 'No user associated to this OpenID.'; } } } } } } return $results; } }