/** * Processes multi-edit actions. * * Accessing requires 'admin.edit' privileges. */ function admin_multi_edit() { global $txp_user; require_privs('admin.edit'); $selected = ps('selected'); $method = ps('edit_method'); $changed = array(); $msg = ''; if (!$selected or !is_array($selected)) { return author_list(); } $clause = ''; if ($method === 'resetpassword') { $clause = " AND last_access IS NOT NULL"; } elseif ($method === 'resendactivation') { $clause = " AND last_access IS NULL"; } $names = safe_column("name", 'txp_users', "name IN (" . join(',', quote_list($selected)) . ") AND name != '" . doSlash($txp_user) . "'" . $clause); if (!$names) { return author_list(); } switch ($method) { case 'delete': $assign_assets = ps('assign_assets'); if (!$assign_assets) { $msg = array('must_reassign_assets', E_ERROR); } elseif (in_array($assign_assets, $names)) { $msg = array('cannot_assign_assets_to_deletee', E_ERROR); } elseif (remove_user($names, $assign_assets)) { $changed = $names; callback_event('authors_deleted', '', 0, $changed); $msg = 'author_deleted'; } break; case 'changeprivilege': if (change_user_group($names, ps('privs'))) { $changed = $names; $msg = 'author_updated'; } break; case 'resetpassword': foreach ($names as $name) { send_reset_confirmation_request($name); $changed[] = $name; } $msg = 'password_reset_confirmation_request_sent'; break; case 'resendactivation': foreach ($names as $name) { send_account_activation($name); $changed[] = $name; } $msg = 'resend_activation_request_sent'; break; } if ($changed) { return author_list(gTxt($msg, array('{name}' => txpspecialchars(join(', ', $changed))))); } author_list($msg); }
function doTxpValidate() { global $logout, $txp_user; $p_userid = ps('p_userid'); $p_password = ps('p_password'); $p_reset = ps('p_reset'); $stay = ps('stay'); $logout = gps('logout'); $message = ''; $pub_path = preg_replace('|//$|', '/', rhu . '/'); if (cs('txp_login') and strpos(cs('txp_login'), ',')) { $txp_login = explode(',', cs('txp_login')); $c_hash = end($txp_login); $c_userid = join(',', array_slice($txp_login, 0, -1)); } else { $c_hash = ''; $c_userid = ''; } if ($logout) { setcookie('txp_login', '', time() - 3600); setcookie('txp_login_public', '', time() - 3600, $pub_path); } if ($c_userid and strlen($c_hash) == 32) { $nonce = safe_field('nonce', 'txp_users', "name='" . doSlash($c_userid) . "' AND last_access > DATE_SUB(NOW(), INTERVAL 30 DAY)"); if ($nonce and $nonce === md5($c_userid . pack('H*', $c_hash))) { // cookie is good if ($logout) { // destroy nonce safe_update('txp_users', "nonce = '" . doSlash(md5(uniqid(mt_rand(), TRUE))) . "'", "name = '" . doSlash($c_userid) . "'"); } else { // create $txp_user $txp_user = $c_userid; } return $message; } else { setcookie('txp_login', $c_userid, time() + 3600 * 24 * 365); setcookie('txp_login_public', '', time() - 3600, $pub_path); $message = array(gTxt('bad_cookie'), E_ERROR); } } elseif ($p_userid and $p_password) { $name = txp_validate($p_userid, $p_password); if ($name !== FALSE) { $c_hash = md5(uniqid(mt_rand(), TRUE)); $nonce = md5($name . pack('H*', $c_hash)); safe_update('txp_users', "nonce = '" . doSlash($nonce) . "'", "name = '" . doSlash($name) . "'"); setcookie('txp_login', $name . ',' . $c_hash, $stay ? time() + 3600 * 24 * 365 : 0, null, null, null, LOGIN_COOKIE_HTTP_ONLY); setcookie('txp_login_public', substr(md5($nonce), -10) . $name, $stay ? time() + 3600 * 24 * 30 : 0, $pub_path); // login is good, create $txp_user $txp_user = $name; return ''; } else { sleep(3); $message = array(gTxt('could_not_log_in'), E_ERROR); } } elseif ($p_reset) { sleep(3); include_once txpath . '/lib/txplib_admin.php'; $message = $p_userid ? send_reset_confirmation_request($p_userid) : ''; } elseif (gps('reset')) { $message = ''; } elseif (gps('confirm')) { sleep(3); $confirm = pack('H*', gps('confirm')); $name = substr($confirm, 5); $nonce = safe_field('nonce', 'txp_users', "name = '" . doSlash($name) . "'"); if ($nonce and $confirm === pack('H*', substr(md5($nonce), 0, 10)) . $name) { include_once txpath . '/lib/txplib_admin.php'; $message = reset_author_pass($name); } } $txp_user = ''; return $message; }
/** * Validates the sent login form and creates a session. * * During the reset request procedure, it is conceivable to verify the * token as soon as it's presented in the URL, but that would: * a) require refactoring code similarities in both p_confirm and p_alter branches * b) require some way (e.g. an Exception) to signal back to doLoginForm() that * the token is bogus so the 'change your password' form is not displayed. * c) leak information about the validity of a token, thus allowing rapid brute-force * attempts. * * The inconvenience of a real user following an expired token and being told so * after they've set a password is a small price to pay for the improved security * and reduction of attack surface that validating after submission affords. * * @todo Could the checks be done via a (reusable) Validator()? * * @return string A localised feedback message * @see doLoginForm() */ function doTxpValidate() { global $logout, $txp_user; $p_userid = ps('p_userid'); $p_password = ps('p_password'); $p_reset = ps('p_reset'); $p_alter = ps('p_alter'); $p_set = ps('p_set'); $stay = ps('stay'); $p_confirm = gps('confirm'); $logout = gps('logout'); $message = ''; $pub_path = preg_replace('|//$|', '/', rhu . '/'); if (cs('txp_login') && strpos(cs('txp_login'), ',')) { $txp_login = explode(',', cs('txp_login')); $c_hash = end($txp_login); $c_userid = join(',', array_slice($txp_login, 0, -1)); } else { $c_hash = ''; $c_userid = ''; } if ($logout) { setcookie('txp_login', '', time() - 3600); setcookie('txp_login_public', '', time() - 3600, $pub_path); } if ($c_userid && strlen($c_hash) === 32) { // Cookie exists. // @todo Improve security by using a better nonce/salt mechanism. md5 and uniqid are bad. $r = safe_row("name, nonce", 'txp_users', "name = '" . doSlash($c_userid) . "' AND last_access > DATE_SUB(NOW(), INTERVAL 30 DAY)"); if ($r && $r['nonce'] && $r['nonce'] === md5($c_userid . pack('H*', $c_hash))) { // Cookie is good. if ($logout) { // Destroy nonce. safe_update('txp_users', "nonce = '" . doSlash(md5(uniqid(mt_rand(), true))) . "'", "name = '" . doSlash($c_userid) . "'"); } else { // Create $txp_user. $txp_user = $r['name']; } return $message; } else { txp_status_header('401 Your session has expired'); setcookie('txp_login', $c_userid, time() + 3600 * 24 * 365); setcookie('txp_login_public', '', time() - 3600, $pub_path); $message = array(gTxt('bad_cookie'), E_ERROR); } } elseif ($p_userid && $p_password) { // Incoming login vars. $name = txp_validate($p_userid, $p_password); if ($name !== false) { $c_hash = md5(uniqid(mt_rand(), true)); $nonce = md5($name . pack('H*', $c_hash)); safe_update('txp_users', "nonce = '" . doSlash($nonce) . "'", "name = '" . doSlash($name) . "'"); setcookie('txp_login', $name . ',' . $c_hash, $stay ? time() + 3600 * 24 * 365 : 0, null, null, null, LOGIN_COOKIE_HTTP_ONLY); setcookie('txp_login_public', substr(md5($nonce), -10) . $name, $stay ? time() + 3600 * 24 * 30 : 0, $pub_path); // Login is good, create $txp_user. $txp_user = $name; return ''; } else { sleep(3); txp_status_header('401 Could not log in with that username/password'); $message = array(gTxt('could_not_log_in'), E_ERROR); } } elseif ($p_reset) { // Reset request. sleep(3); include_once txpath . '/lib/txplib_admin.php'; $message = $p_userid ? send_reset_confirmation_request($p_userid) : ''; } elseif ($p_alter || $p_set) { // Password change/set confirmation. sleep(3); global $sitename; $pass = ps('p_password'); $type = $p_alter ? 'password_reset' : 'account_activation'; if (trim($pass) === '') { $message = array(gTxt('password_required'), E_ERROR); } else { $hash = gps('hash'); $selector = substr($hash, SALT_LENGTH); $tokenInfo = safe_row("reference_id, token, expires", 'txp_token', "selector = '" . doSlash($selector) . "' AND type='{$type}'"); if ($tokenInfo) { if (strtotime($tokenInfo['expires']) <= time()) { $message = array(gTxt('token_expired'), E_ERROR); } else { $uid = assert_int($tokenInfo['reference_id']); $row = safe_row("name, email, nonce, pass AS old_pass", 'txp_users', "user_id = {$uid}"); if ($row && $row['nonce'] && $hash === bin2hex(pack('H*', substr(hash(HASHING_ALGORITHM, $row['nonce'] . $selector . $row['old_pass']), 0, SALT_LENGTH))) . $selector) { if (change_user_password($row['name'], $pass)) { $body = gTxt('salutation', array('{name}' => $row['name'])) . n . n . ($p_alter ? gTxt('password_change_confirmation') : gTxt('password_set_confirmation') . n . n . gTxt('log_in_at') . ': ' . hu . 'textpattern/index.php'); $message = $p_alter ? gTxt('password_changed') : gTxt('password_set'); txpMail($row['email'], "[{$sitename}] " . $message, $body); // Invalidate all tokens in the wild for this user. safe_delete("txp_token", "reference_id = {$uid} AND type IN ('password_reset', 'account_activation')"); } } else { $message = array(gTxt('invalid_token'), E_ERROR); } } } else { $message = array(gTxt('invalid_token'), E_ERROR); } } } $txp_user = ''; return $message; }