/** * Authenticates the user if authentication headers are present * * Our handling of the speedlimit here requires some explanation ... * Atompub clients will usually try to do everything without logging in first. * Since that would mean that we can't provide feeds for drafts, items with * special permissions, etc. we ask them to log in (PLG_RET_AUTH_FAILED). * That, however, means that every request from an Atompub client will count * as one failed login attempt. So doing a couple of requests in quick * succession will surely get the client blocked. Therefore * - a request without any login credentials counts as one failed login attempt * - a request with wrong login credentials counts as two failed login attempts * - if, after a successful login, we have only one failed attempt on record, * we reset the speedlimit * This still ensures that * - repeated failed logins (without or with invalid credentials) will cause the * client to be blocked eventually * - this can not be used for dictionary attacks * */ function WS_authenticate() { global $_CONF, $_TABLES, $_USER, $_GROUPS, $_RIGHTS, $WS_VERBOSE; $uid = ''; $username = ''; $password = ''; $status = -1; if (isset($_SERVER['PHP_AUTH_USER'])) { $username = COM_applyBasicFilter($_SERVER['PHP_AUTH_USER']); $password = $_SERVER['PHP_AUTH_PW']; if ($WS_VERBOSE) { COM_errorLog("WS: Attempting to log in user '{$username}'"); } /** this does not work! ******************************************************* } elseif (!empty($_SERVER['HTTP_X_WSSE']) && (strpos($_SERVER['HTTP_X_WSSE'], 'UsernameToken') !== false)) { // this is loosely based on a code snippet taken from Elgg (elgg.org) $wsse = str_replace('UsernameToken', '', $_SERVER['HTTP_X_WSSE']); $wsse = explode(',', $wsse); $username = ''; $pwdigest = ''; $created = ''; $nonce = ''; foreach ($wsse as $element) { $element = explode('=', $element); $key = array_shift($element); if (count($element) == 1) { $val = $element[0]; } else { $val = implode('=', $element); } $key = trim($key); $val = trim($val, "\x22\x27"); if ($key == 'Username') { $username = COM_applyBasicFilter($val); } elseif ($key == 'PasswordDigest') { $pwdigest = $val; } elseif ($key == 'Created') { $created = $val; } elseif ($key == 'Nonce') { $nonce = $val; } } if (!empty($username) && !empty($pwdigest) && !empty($created) && !empty($nonce)) { $uname = DB_escapeString($username); $pwd = DB_getItem($_TABLES['users'], 'passwd', "username = '******'"); // ... and here we would need the _unencrypted_ password if (!empty($pwd)) { $mydigest = pack('H*', sha1($nonce . $created . $pwd)); $mydigest = base64_encode($mydigest); if ($pwdigest == $mydigest) { $password = $pwd; } } } if ($WS_VERBOSE) { COM_errorLog("WS: Attempting to log in user '$username' (via WSSE)"); } ******************************************************************************/ } elseif (!empty($_SERVER['REMOTE_USER'])) { /* PHP installed as CGI may not have access to authorization headers of * Apache. In that case, use .htaccess to store the auth header as * explained at * http://wiki.geeklog.net/wiki/index.php/Webservices_API#Authentication */ list($auth_type, $auth_data) = explode(' ', $_SERVER['REMOTE_USER']); list($username, $password) = explode(':', base64_decode($auth_data)); $username = COM_applyBasicFilter($username); if ($WS_VERBOSE) { COM_errorLog("WS: Attempting to log in user '{$username}' (via \$_SERVER['REMOTE_USER'])"); } } else { if ($WS_VERBOSE) { COM_errorLog("WS: No login given"); } // fallthrough (see below) } COM_clearSpeedlimit($_CONF['login_speedlimit'], 'wsauth'); if (COM_checkSpeedlimit('wsauth', $_CONF['login_attempts']) > 0) { WS_error(PLG_RET_PERMISSION_DENIED, 'Speed Limit exceeded'); } if (!empty($username) && !empty($password)) { if ($_CONF['user_login_method']['3rdparty']) { // remote users will have to use username@servicename $u = explode('@', $username); if (count($u) > 1) { $sv = $u[count($u) - 1]; if (!empty($sv)) { $modules = SEC_collectRemoteAuthenticationModules(); foreach ($modules as $smod) { if (strcasecmp($sv, $smod) == 0) { array_pop($u); // drop the service name $uname = implode('@', $u); $status = SEC_remoteAuthentication($uname, $password, $smod, $uid); break; } } } } } if ($status == -1 && $_CONF['user_login_method']['standard']) { $status = SEC_authenticate($username, $password, $uid); } } if ($status == USER_ACCOUNT_ACTIVE) { $_USER = SESS_getUserDataFromId($uid); PLG_loginUser($_USER['uid']); // Global array of groups current user belongs to $_GROUPS = SEC_getUserGroups($_USER['uid']); // Global array of current user permissions [read,edit] $_RIGHTS = explode(',', SEC_getUserPermissions()); if ($_CONF['restrict_webservices']) { if (!SEC_hasRights('webservices.atompub')) { COM_updateSpeedlimit('wsauth'); if ($WS_VERBOSE) { COM_errorLog("WS: User '{$_USER['username']}' ({$_USER['uid']}) does not have permission to use the webservices"); } // reset user, groups, and rights, just in case ... $_USER = array(); $_GROUPS = array(); $_RIGHTS = array(); WS_error(PLG_RET_AUTH_FAILED); } } if ($WS_VERBOSE) { COM_errorLog("WS: User '{$_USER['username']}' ({$_USER['uid']}) successfully logged in"); } // if there were less than 2 failed login attempts, reset speedlimit if (COM_checkSpeedlimit('wsauth', 2) == 0) { if ($WS_VERBOSE) { COM_errorLog("WS: Successful login - resetting speedlimit"); } COM_resetSpeedlimit('wsauth'); } } else { COM_updateSpeedlimit('wsauth'); if (!empty($username) && !empty($password)) { COM_updateSpeedlimit('wsauth'); if ($WS_VERBOSE) { COM_errorLog("WS: Wrong login credentials - counting as 2 failed attempts"); } } elseif ($WS_VERBOSE) { COM_errorLog("WS: Empty login credentials - counting as 1 failed attempt"); } WS_error(PLG_RET_AUTH_FAILED); } }
} else { $status = -2; // User just visited login page no error. -1 = error } if ($status == USER_ACCOUNT_ACTIVE) { // logged in AOK. if ($mode === 'tokenexpired') { resend_request(); // won't come back } DB_change($_TABLES['users'], 'pwrequestid', "NULL", 'uid', $uid); $userdata = SESS_getUserDataFromId($uid); $_USER = $userdata; $sessid = SESS_newSession($_USER['uid'], $_SERVER['REMOTE_ADDR'], $_CONF['session_cookie_timeout'], $_CONF['cookie_ip']); SESS_setSessionCookie($sessid, $_CONF['session_cookie_timeout'], $_CONF['cookie_session'], $_CONF['cookie_path'], $_CONF['cookiedomain'], $_CONF['cookiesecure']); PLG_loginUser($_USER['uid']); // Now that we handled session cookies, handle longterm cookie if (!isset($_COOKIE[$_CONF['cookie_name']]) || !isset($_COOKIE['cookie_password'])) { // Either their cookie expired or they are new $cooktime = COM_getUserCookieTimeout(); if ($VERBOSE) { COM_errorLog("Trying to set permanent cookie with time of {$cooktime}", 1); } if ($cooktime > 0) { // They want their cookie to persist for some amount of time so set it now if ($VERBOSE) { COM_errorLog('Trying to set permanent cookie', 1); } SEC_setCookie($_CONF['cookie_name'], $_USER['uid'], time() + $cooktime); SEC_setCookie($_CONF['cookie_password'], $_USER['passwd'], time() + $cooktime); }
/** * Authenticates the user if authentication headers are present * * Our handling of the speedlimit here requires some explanation ... * Atompub clients will usually try to do everything without logging in first. * Since that would mean that we can't provide feeds for drafts, items with * special permissions, etc. we ask them to log in (PLG_RET_AUTH_FAILED). * That, however, means that every request from an Atompub client will count * as one failed login attempt. So doing a couple of requests in quick * succession will surely get the client blocked. Therefore * - a request without any login credentials counts as one failed login attempt * - a request with wrong login credentials counts as two failed login attempts * - if, after a successful login, we have only one failed attempt on record, * we reset the speedlimit * This still ensures that * - repeated failed logins (without or with invalid credentials) will cause the * client to be blocked eventually * - this can not be used for dictionary attacks * */ function WS_authenticate() { global $_CONF, $_TABLES, $_USER, $_GROUPS, $_RIGHTS, $WS_VERBOSE; $uid = ''; $username = ''; $password = ''; $status = -1; if (isset($_SERVER['PHP_AUTH_USER'])) { $username = $_SERVER['PHP_AUTH_USER']; $password = $_SERVER['PHP_AUTH_PW']; $username = COM_applyFilter($username); $password = COM_applyFilter($password); if ($WS_VERBOSE) { COM_errorLog("WS: Attempting to log in user '{$username}'"); } } elseif (!empty($_SERVER['REMOTE_USER'])) { /* PHP installed as CGI may not have access to authorization headers of * Apache. In that case, use .htaccess to store the auth header */ list($auth_type, $auth_data) = explode(' ', $_SERVER['REMOTE_USER']); list($username, $password) = explode(':', base64_decode($auth_data)); $username = COM_applyFilter($username); $password = COM_applyFilter($password); if ($WS_VERBOSE) { COM_errorLog("WS: Attempting to log in user '{$username}' (via \$_SERVER['REMOTE_USER'])"); } } else { if ($WS_VERBOSE) { COM_errorLog("WS: No login given"); } // fallthrough (see below) } COM_clearSpeedlimit($_CONF['login_speedlimit'], 'wsauth'); if (COM_checkSpeedlimit('wsauth', $_CONF['login_attempts']) > 0) { WS_error(PLG_RET_PERMISSION_DENIED, 'Speed Limit exceeded'); } if (!empty($username) && !empty($password)) { if ($_CONF['user_login_method']['3rdparty']) { // remote users will have to use username@servicename $u = explode('@', $username); if (count($u) > 1) { $sv = $u[count($u) - 1]; if (!empty($sv)) { $modules = SEC_collectRemoteAuthenticationModules(); foreach ($modules as $smod) { if (strcasecmp($sv, $smod) == 0) { array_pop($u); // drop the service name $uname = implode('@', $u); $status = SEC_remoteAuthentication($uname, $password, $smod, $uid); break; } } } } } if ($status == -1 && $_CONF['user_login_method']['standard']) { $status = SEC_authenticate($username, $password, $uid); } } if ($status == USER_ACCOUNT_ACTIVE) { $_USER = SESS_getUserDataFromId($uid); PLG_loginUser($_USER['uid']); // Global array of groups current user belongs to $_GROUPS = SEC_getUserGroups($_USER['uid']); // Global array of current user permissions [read,edit] $_RIGHTS = explode(',', SEC_getUserPermissions()); if ($_CONF['restrict_webservices']) { if (!SEC_hasRights('webservices.atompub')) { COM_updateSpeedlimit('wsauth'); if ($WS_VERBOSE) { COM_errorLog("WS: User '{$_USER['username']}' ({$_USER['uid']}) does not have permission to use the webservices"); } // reset user, groups, and rights, just in case ... $_USER = array(); $_GROUPS = array(); $_RIGHTS = array(); WS_error(PLG_RET_AUTH_FAILED); } } if ($WS_VERBOSE) { COM_errorLog("WS: User '{$_USER['username']}' ({$_USER['uid']}) successfully logged in"); } // if there were less than 2 failed login attempts, reset speedlimit if (COM_checkSpeedlimit('wsauth', 2) == 0) { if ($WS_VERBOSE) { COM_errorLog("WS: Successful login - resetting speedlimit"); } COM_resetSpeedlimit('wsauth'); } } else { COM_updateSpeedlimit('wsauth'); if (!empty($username) && !empty($password)) { COM_updateSpeedlimit('wsauth'); if ($WS_VERBOSE) { COM_errorLog("WS: Wrong login credentials - counting as 2 failed attempts"); } } elseif ($WS_VERBOSE) { COM_errorLog("WS: Empty login credentials - counting as 1 failed attempt"); } WS_error(PLG_RET_AUTH_FAILED); } }
/** * Complete the login process - setup new session * * Complete the login process - create new session for user * * @param int $uid User ID of logged in user * @return none * */ function SESS_completeLogin($uid) { global $_TABLES, $_CONF, $_SYSTEM, $_USER; $request_ip = !empty($_SERVER['REMOTE_ADDR']) ? htmlspecialchars($_SERVER['REMOTE_ADDR']) : ''; // build the $_USER array $userdata = SESS_getUserDataFromId($uid); $_USER = $userdata; // save old session data $savedSessionData = json_encode($_SESSION); // create the session $sessid = SESS_newSession($_USER['uid'], $request_ip, $_CONF['session_cookie_timeout']); if (isset($_COOKIE[$_CONF['cookie_session']])) { $cookie_domain = $_CONF['cookiedomain']; $cookie_path = $_CONF['cookie_path']; setcookie($_COOKIE[$_CONF['cookie_session']], '', time() - 42000, $cookie_path, $cookie_domain, $_CONF['cookiesecure'], true); } session_id($sessid); session_start(); $_SESSION = json_decode($savedSessionData, true); // initialize session counter SESS_setVar('session.counter', 1); if (!isset($_USER['tzid']) || empty($_USER['tzid'])) { $_USER['tzid'] = $_CONF['timezone']; } // Let plugins act on login event PLG_loginUser($_USER['uid']); // check and see if they have remember me set $cooktime = (int) $_USER['cookietimeout']; if ($cooktime > 0) { $cookieTimeout = time() + $cooktime; $token_ttl = $cooktime; // set userid cookie SEC_setCookie($_CONF['cookie_name'], $_USER['uid'], $cookieTimeout, $_CONF['cookie_path'], $_CONF['cookiedomain'], $_CONF['cookiesecure'], true); $ltToken = SEC_createTokenGeneral('ltc', $token_ttl); // set long term cookie SEC_setCookie($_CONF['cookie_password'], $ltToken, $cookieTimeout, $_CONF['cookie_path'], $_CONF['cookiedomain'], $_CONF['cookiesecure'], true); } DB_query("UPDATE {$_TABLES['users']} set remote_ip='" . DB_escapeString($request_ip) . "' WHERE uid=" . (int) $_USER['uid'], 1); if ($_CONF['allow_user_themes']) { // set theme cookie (or update it ) SEC_setcookie($_CONF['cookie_theme'], $_USER['theme'], time() + 31536000, $_CONF['cookie_path'], $_CONF['cookiedomain'], $_CONF['cookiesecure'], true); } }