Merge pull request #527 from YunoHost/migrate-pwd

Synchronize root and admin password
This commit is contained in:
Alexandre Aubin 2018-11-04 16:03:11 +01:00 committed by GitHub
commit 4999cd8ba0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 102 additions and 5 deletions

View file

@ -1446,7 +1446,7 @@ tools:
### tools_adminpw()
adminpw:
action_help: Change admin password
action_help: Change password of admin and root users
api: PUT /adminpw
configuration:
authenticate: all

View file

@ -273,6 +273,7 @@
"migration_description_0003_migrate_to_stretch": "Upgrade the system to Debian Stretch and YunoHost 3.0",
"migration_description_0004_php5_to_php7_pools": "Reconfigure the PHP pools to use PHP 7 instead of 5",
"migration_description_0005_postgresql_9p4_to_9p6": "Migrate databases from postgresql 9.4 to 9.6",
"migration_description_0006_sync_admin_and_root_passwords": "Synchronize admin and root passwords",
"migration_0003_backward_impossible": "The stretch migration cannot be reverted.",
"migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.",
"migration_0003_patching_sources_list": "Patching the sources.lists ...",
@ -289,6 +290,8 @@
"migration_0005_postgresql_94_not_installed": "Postgresql was not installed on your system. Nothing to do!",
"migration_0005_postgresql_96_not_installed": "Postgresql 9.4 has been found to be installed, but not postgresql 9.6 !? Something weird might have happened on your system :( ...",
"migration_0005_not_enough_space": "Not enough space is available in {path} to run the migration right now :(.",
"migration_0006_disclaimer": "Yunohost now expects admin and root passwords to be synchronized. By running this migration, your root password is going to be replaced by the admin password.",
"migration_0006_done": "Your root password have been replaced by your admin password.",
"migrations_backward": "Migrating backward.",
"migrations_bad_value_for_target": "Invalid number for target argument, available migrations numbers are 0 or {}",
"migrations_cant_reach_migration_file": "Can't access migrations files at path %s",
@ -371,6 +374,7 @@
"restore_running_app_script": "Running restore script of app '{app:s}'...",
"restore_running_hooks": "Running restoration hooks...",
"restore_system_part_failed": "Unable to restore the '{part:s}' system part",
"root_password_desynchronized": "The admin password has been changed, but YunoHost was unable to propagate this on the root password !",
"server_shutdown": "The server will shutdown",
"server_shutdown_confirm": "The server will shutdown immediatly, are you sure? [{answers:s}]",
"server_reboot": "The server will reboot",

View file

@ -0,0 +1,79 @@
import spwd
import crypt
import random
import string
import subprocess
from moulinette import m18n
from moulinette.core import MoulinetteError
from moulinette.utils.log import getActionLogger
from moulinette.utils.process import run_commands, check_output
from moulinette.utils.filesystem import append_to_file
from moulinette.authenticators.ldap import Authenticator
from yunohost.tools import Migration
logger = getActionLogger('yunohost.migration')
SMALL_PWD_LIST = ["yunohost", "olinuxino", "olinux", "raspberry", "admin", "root", "test", "rpi"]
class MyMigration(Migration):
"Synchronize admin and root passwords"
def migrate(self):
new_hash = self._get_admin_hash()
self._replace_root_hash(new_hash)
logger.info(m18n.n("migration_0006_done"))
def backward(self):
pass
@property
def mode(self):
# If the root password is still a "default" value,
# then this is an emergency and migration shall
# be applied automatically
#
# Otherwise, as playing with root password is touchy,
# we set this as a manual migration.
return "auto" if self._is_root_pwd_listed(SMALL_PWD_LIST) else "manual"
@property
def disclaimer(self):
if self._is_root_pwd_listed(SMALL_PWD_LIST):
return None
return m18n.n("migration_0006_disclaimer")
def _get_admin_hash(self):
"""
Fetch the admin hash from the LDAP db using slapcat
"""
admin_hash = check_output("slapcat \
| grep 'dn: cn=admin,dc=yunohost,dc=org' -A20 \
| grep userPassword -A2 \
| tr -d '\n ' \
| tr ':' ' ' \
| awk '{print $2}' \
| base64 -d \
| sed 's/{CRYPT}//g'")
return admin_hash
def _replace_root_hash(self, new_hash):
hash_root = spwd.getspnam("root").sp_pwd
with open('/etc/shadow', 'r') as before_file:
before = before_file.read()
with open('/etc/shadow', 'w') as after_file:
after_file.write(before.replace("root:" + hash_root,
"root:" + new_hash))
def _is_root_pwd_listed(self, pwd_list):
hash_root = spwd.getspnam("root").sp_pwd
for password in pwd_list:
if hash_root == crypt.crypt(password, hash_root):
return True
return False

View file

@ -129,18 +129,32 @@ def tools_adminpw(auth, new_password):
"""
from yunohost.user import _hash_user_password
from yunohost.utils.password import assert_password_is_strong_enough
import spwd
assert_password_is_strong_enough("admin", new_password)
new_hash = _hash_user_password(new_password)
try:
auth.update("cn=admin", {
"userPassword": _hash_user_password(new_password),
})
auth.update("cn=admin", { "userPassword": new_hash, })
except:
logger.exception('unable to change admin password')
raise MoulinetteError(errno.EPERM,
m18n.n('admin_password_change_failed'))
else:
# Write as root password
try:
hash_root = spwd.getspnam("root").sp_pwd
with open('/etc/shadow', 'r') as before_file:
before = before_file.read()
with open('/etc/shadow', 'w') as after_file:
after_file.write(before.replace("root:" + hash_root,
"root:" + new_hash.replace('{CRYPT}', '')))
except IOError as e:
logger.warning(m18n.n('root_password_desynchronized'))
return
logger.success(m18n.n('admin_password_changed'))