/** * Check the username / password against the PAM system */ function SQUID_PAM_check($username, $password) { global $c; /** * @todo Think of the children! This is a horribly insecure use of unvalidated user input! Probably it should be done with a popen or something, and it seems remarkably dodgy to expect that naively quoted strings will work in any way reliably. * Meanwhile, I've quickly hacked something basic in place to improve the situation. No quotes/backslashes in passwords for YOU! */ $username = str_replace("'", "", str_replace('"', "", str_replace('\\', "", $username))); $password = str_replace("'", "", str_replace('"', "", str_replace('\\', "", $password))); $cmd = "echo '" . $username . "' '" . $password . "' | " . $c->authenticate_hook['config']['script'] . " -n common-auth"; $auth_result = exec($cmd); if ($auth_result == "OK") { if ($usr = getUserByName($username)) { return $usr; } else { dbg_error_log("PAM", "user %s doesn't exist in local DB, we need to create it", $username); $fullname = exec('getent passwd "' . $username . '"'); $fullname = preg_replace('{^[^:]+:[^:]+:\\d+:\\d+:([^:,]+)(,?[^:]*):.*$}', '$1', $fullname); $usr = (object) array('user_no' => 0, 'username' => $username, 'active' => 't', 'email' => $username . "@" . $c->authenticate_hook['config']['email_base'], 'updated' => date(), 'fullname' => $fullname); UpdateUserFromExternal($usr); return $usr; } } else { dbg_error_log("PAM", "User %s is not a valid username (or password was wrong)", $username); return false; } }
/** * Authenticate against a different PostgreSQL database which contains a usr table in * the AWL format. * * Use this as in the following example config snippet: * * require_once('auth-functions.php'); * $c->authenticate_hook = array( * 'call' => 'AuthExternalAwl', * 'config' => array( * // A PgSQL database connection string for the database containing user records * 'connection[]' => 'dbname=wrms host=otherhost port=5433 user=general', * // Which columns should be fetched from the database * 'columns' => "user_no, active, email_ok, joined, last_update AS updated, last_used, username, password, fullname, email", * // a WHERE clause to limit the records returned. * 'where' => "active AND org_code=7" * ) * ); * */ function AuthExternalAWL($username, $password) { global $c; $persistent = isset($c->authenticate_hook['config']['use_persistent']) && $c->authenticate_hook['config']['use_persistent']; if (isset($c->authenticate_hook['config']['columns'])) { $cols = $c->authenticate_hook['config']['columns']; } else { $cols = '*'; } if (isset($c->authenticate_hook['config']['where'])) { $andwhere = ' AND ' . $c->authenticate_hook['config']['where']; } else { $andwhere = ''; } $qry = new AwlQuery('SELECT ' . $cols . ' FROM usr WHERE lower(username) = :username ' . $andwhere, array(':username' => strtolower($username))); $authconn = $qry->SetConnection($c->authenticate_hook['config']['connection'], $persistent ? array(PDO::ATTR_PERSISTENT => true) : null); if (!$authconn) { echo <<<EOERRMSG <html><head><title>Database Connection Failure</title></head><body> <h1>Database Error</h1> <h3>Could not connect to PostgreSQL database</h3> </body> </html> EOERRMSG; exit(1); } if ($qry->Exec('Login', __LINE__, __FILE__) && $qry->rows() == 1) { $usr = $qry->Fetch(); if (session_validate_password($password, $usr->password)) { UpdateUserFromExternal($usr); /** * We disallow login by inactive users _after_ we have updated the local copy */ if (isset($usr->active) && $usr->active == 'f') { return false; } $qry = new AwlQuery('SELECT * FROM dav_principal WHERE username = :username', array(':username' => $usr->username)); if ($qry->Exec() && $qry->rows() == 1) { $principal = $qry->Fetch(); return $principal; } return $usr; // Somewhat optimistically } } return false; }
/** * sync LDAP Groups against the DB */ function sync_LDAP_groups() { global $c; $ldapDriver = getStaticLdap(); if ($ldapDriver->valid) { $mapping = $c->authenticate_hook['config']['group_mapping_field']; //$attributes = array('cn','modifyTimestamp','memberUid'); $attributes = array_values($mapping); $ldap_groups_tmp = $ldapDriver->getAllGroups($attributes); if (sizeof($ldap_groups_tmp) == 0) { return; } foreach ($ldap_groups_tmp as $key => $ldap_group) { $ldap_groups_info[$ldap_group[$mapping['username']]] = $ldap_group; if (is_array($ldap_groups_info[$ldap_group[$mapping['username']]][$mapping['members']])) { unset($ldap_groups_info[$ldap_group[$mapping['username']]][$mapping['members']]['count']); } else { $ldap_groups_info[$ldap_group[$mapping['username']]][$mapping['members']] = array($ldap_groups_info[$ldap_group[$mapping['username']]][$mapping['members']]); } unset($ldap_groups_tmp[$key]); } $db_groups = array(); $db_group_members = array(); $qry = new AwlQuery("SELECT g.username AS group_name, member.username AS member_name FROM dav_principal g LEFT JOIN group_member ON (g.principal_id=group_member.group_id) LEFT JOIN dav_principal member ON (member.principal_id=group_member.member_id) WHERE g.type_id = 3"); $qry->Exec('sync_LDAP', __LINE__, __FILE__); while ($db_group = $qry->Fetch()) { $db_groups[$db_group->group_name] = $db_group->group_name; $db_group_members[$db_group->group_name][] = $db_group->member_name; } $ldap_groups = array_keys($ldap_groups_info); // users only in ldap $groups_to_create = array_diff($ldap_groups, $db_groups); // users only in db $groups_to_deactivate = array_diff($db_groups, $ldap_groups); // users present in ldap and in the db $groups_to_update = array_intersect($db_groups, $ldap_groups); if (sizeof($groups_to_create)) { $c->messages[] = sprintf(i18n('- creating groups : %s'), join(', ', $groups_to_create)); $validUserFields = get_fields('usr'); foreach ($groups_to_create as $k => $group) { $user = (object) array('user_no' => 0, 'username' => ''); if (isset($c->authenticate_hook['config']['default_value']) && is_array($c->authenticate_hook['config']['default_value'])) { foreach ($c->authenticate_hook['config']['default_value'] as $field => $value) { if (isset($validUserFields[$field])) { $usr->{$field} = $value; dbg_error_log("LDAP", "Setting usr->%s to %s from configured defaults", $field, $value); } } } $user->user_no = 0; $ldap_values = $ldap_groups_info[$group]; foreach ($mapping as $field => $value) { dbg_error_log("LDAP", "Considering copying %s", $field); if (isset($validUserFields[$field])) { $user->{$field} = $ldap_values[$value]; dbg_error_log("LDAP", "Setting usr->%s to %s from LDAP field %s", $field, $ldap_values[$value], $value); } } if ($user->fullname == "") { $user->fullname = $group; } if ($user->displayname == "") { $user->displayname = $group; } $user->username = $group; $user->updated = "now"; /** @todo Use the 'updated' timestamp from LDAP for groups too */ UpdateUserFromExternal($user); $qry = new AwlQuery("UPDATE dav_principal set type_id = 3 WHERE username=:group ", array(':group' => $group)); $qry->Exec('sync_LDAP', __LINE__, __FILE__); $c->messages[] = sprintf(i18n('- adding users %s to group : %s'), join(',', $ldap_groups_info[$group][$mapping['members']]), $group); foreach ($ldap_groups_info[$group][$mapping['members']] as $member) { $qry = new AwlQuery("INSERT INTO group_member SELECT g.principal_id AS group_id,u.principal_id AS member_id FROM dav_principal g, dav_principal u WHERE g.username=:group AND u.username=:member;", array(':group' => $group, ':member' => $member)); $qry->Exec('sync_LDAP_groups', __LINE__, __FILE__); } } } if (sizeof($groups_to_update)) { $c->messages[] = sprintf(i18n('- updating groups : %s'), join(', ', $groups_to_update)); foreach ($groups_to_update as $group) { $db_members = array_values($db_group_members[$group]); $ldap_members = array_values($ldap_groups_info[$group][$mapping['members']]); $add_users = array_diff($ldap_members, $db_members); if (sizeof($add_users)) { $c->messages[] = sprintf(i18n('- adding %s to group : %s'), join(', ', $add_users), $group); foreach ($add_users as $member) { $qry = new AwlQuery("INSERT INTO group_member SELECT g.principal_id AS group_id,u.principal_id AS member_id FROM dav_principal g, dav_principal u WHERE g.username=:group AND u.username=:member", array(':group' => $group, ':member' => $member)); $qry->Exec('sync_LDAP_groups', __LINE__, __FILE__); } } $remove_users = array_diff($db_members, $ldap_members); if (sizeof($remove_users)) { $c->messages[] = sprintf(i18n('- removing %s from group : %s'), join(', ', $remove_users), $group); foreach ($remove_users as $member) { $qry = new AwlQuery("DELETE FROM group_member USING dav_principal g,dav_principal m WHERE group_id=g.principal_id AND member_id=m.principal_id AND g.username=:group AND m.username=:member", array(':group' => $group, ':member' => $member)); $qry->Exec('sync_LDAP_groups', __LINE__, __FILE__); } } } } if (sizeof($groups_to_deactivate)) { $c->messages[] = sprintf(i18n('- deactivate groups : %s'), join(', ', $groups_to_deactivate)); foreach ($groups_to_deactivate as $group) { $qry = new AwlQuery("UPDATE dav_principal set active='f'::bool WHERE username=:group AND type_id = 3", array(':group' => $group)); $qry->Exec('sync_LDAP', __LINE__, __FILE__); } } } }
/** * Check the username / password against the PAM system */ function PWAUTH_PAM_check($username, $password) { global $c; $program = $c->authenticate_hook['config']['path']; $email_base = $c->authenticate_hook['config']['email_base']; $pipe = popen(escapeshellarg($program), 'w'); $authinfo = sprintf("%s\n%s\n", $username, $password); $written = fwrite($pipe, $authinfo); dbg_error_log('pwauth', 'Bytes written: %d of %d', $written, strlen($authinfo)); $return_status = pclose($pipe); switch ($return_status) { case 0: // STATUS_OK: Authentication succeeded. dbg_error_log('pwauth', 'User %s successfully authenticated', $username); if ($user = getUserByName($username)) { return $user; } else { dbg_error_log('pwauth', 'User %s does not exist in local db, creating', $username); $fullname = exec(sprintf('getent passwd %s', escapeshellarg($username))); $fullname = preg_replace('{^[^:]+:[^:]+:\\d+:\\d+:([^:,]+)(,[^:]*):.*$}', '$1', $fullname); $user = (object) array('user_no' => 0, 'username' => $username, 'active' => 't', 'email' => sprintf('%s@%s', $username, $email_base), 'updated' => date('%r'), 'fullname' => $fullname); UpdateUserFromExternal($user); return $user; } break; /* * Note that for system configurations using PAM instead of * reading the password database directly, if PAM is unable to * read the password database, pwauth will return status 1. */ /* * Note that for system configurations using PAM instead of * reading the password database directly, if PAM is unable to * read the password database, pwauth will return status 1. */ case 1: case 2: // (1) STATUS_UNKNOWN: Invalid username or password. // (2) STATUS_INVALID: Invalid password. dbg_error_log('pwauth', 'Invalid username or password (username: %s)', $username); break; case 3: // STATUS_BLOCKED: UID for username is < pwauth's MIN_UNIX_UID dbg_error_log('pwauth', 'UID for username %s is < pwauth MIN_UNIX_UID', $username); break; case 4: // STATUS_EXPIRED: The user account has expired. dbg_error_log('pwauth', 'The account for %s has expired', $username); break; case 5: // STATUS_PW_EXPIRED: The user account's password has expired. dbg_error_log('pwauth', 'The account password for user %s has expired', $username); break; case 6: // STATUS_NOLOGIN: Logins to the system are administratively disabled. dbg_error_log('pwauth', 'Logins administratively disabled (%s)', $username); break; case 7: // STATUS_MANYFAILS: Too many login failures for user account. dbg_error_log('pwauth', 'Login rejected for %s, too many failures', $username); break; case 50: // STATUS_INT_USER: Configuration error, Web server cannot use pwauth dbg_error_log('pwauth', 'config error: see pwauth man page (%s)', 'STATUS_INT_USER'); break; case 51: // STATUS_INT_ARGS: pwauth received no username/passwd to check dbg_error_log('pwauth', 'error: pwauth received no username/password'); break; case 52: // STATUS_INT_ERR: unknown error dbg_error_log('pwauth', 'error: see pwauth man page (%s)', 'STATUS_INT_ERR'); break; case 53: // STATUS_INT_NOROOT: pwauth could not read the password database dbg_error_log('pwauth', 'config error: cannot read password database (%s)', 'STATUS_INT_NOROOT'); default: // Unknown error code. dbg_error_log('pwauth', 'An unknown error (%d) has occurred', $return_status); } return FALSE; }