diff options
Diffstat (limited to 'src/conf_mode/system-login.py')
-rwxr-xr-x | src/conf_mode/system-login.py | 60 |
1 files changed, 47 insertions, 13 deletions
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py index e26b81e3d..fbb013cf3 100755 --- a/src/conf_mode/system-login.py +++ b/src/conf_mode/system-login.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2022 VyOS maintainers and contributors +# Copyright (C) 2020-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -16,22 +16,21 @@ import os -from crypt import crypt -from crypt import METHOD_SHA512 +from passlib.hosts import linux_context from psutil import users from pwd import getpwall from pwd import getpwnam -from spwd import getspnam from sys import exit from time import sleep from vyos.config import Config from vyos.configdict import dict_merge from vyos.configverify import verify_vrf +from vyos.defaults import directories from vyos.template import render from vyos.template import is_ipv4 from vyos.util import cmd -from vyos.util import call +from vyos.util import call, rc_cmd from vyos.util import run from vyos.util import DEVNULL from vyos.util import dict_search @@ -41,8 +40,14 @@ from vyos import airbag airbag.enable() autologout_file = "/etc/profile.d/autologout.sh" +limits_file = "/etc/security/limits.d/10-vyos.conf" radius_config_file = "/etc/pam_radius_auth.conf" +# LOGIN_TIMEOUT from /etc/loign.defs minus 10 sec +MAX_RADIUS_TIMEOUT: int = 50 +# MAX_RADIUS_TIMEOUT divided by 2 sec (minimum recomended timeout) +MAX_RADIUS_COUNT: int = 25 + def get_local_users(): """Return list of dynamically allocated users (see Debian Policy Manual)""" local_users = [] @@ -54,6 +59,13 @@ def get_local_users(): return local_users +def get_shadow_password(username): + with open('/etc/shadow') as f: + for user in f.readlines(): + items = user.split(":") + if username == items[0]: + return items[1] + return None def get_config(config=None): if config: @@ -118,18 +130,27 @@ def verify(login): if 'radius' in login: if 'server' not in login['radius']: raise ConfigError('No RADIUS server defined!') - + sum_timeout: int = 0 + radius_servers_count: int = 0 fail = True for server, server_config in dict_search('radius.server', login).items(): if 'key' not in server_config: raise ConfigError(f'RADIUS server "{server}" requires key!') - - if 'disabled' not in server_config: + if 'disable' not in server_config: + sum_timeout += int(server_config['timeout']) + radius_servers_count += 1 fail = False - continue + if fail: raise ConfigError('All RADIUS servers are disabled') + if radius_servers_count > MAX_RADIUS_COUNT: + raise ConfigError('Number of RADIUS servers more than 25 ') + + if sum_timeout > MAX_RADIUS_TIMEOUT: + raise ConfigError('Sum of RADIUS servers timeouts ' + 'has to be less or eq 50 sec') + verify_vrf(login['radius']) if 'source_address' in login['radius']: @@ -144,6 +165,9 @@ def verify(login): if ipv6_count > 1: raise ConfigError('Only one IPv6 source-address can be set!') + if 'max_login_session' in login and 'timeout' not in login: + raise ConfigError('"login timeout" must be configured!') + return None @@ -153,13 +177,13 @@ def generate(login): for user, user_config in login['user'].items(): tmp = dict_search('authentication.plaintext_password', user_config) if tmp: - encrypted_password = crypt(tmp, METHOD_SHA512) + encrypted_password = linux_context.hash(tmp) login['user'][user]['authentication']['encrypted_password'] = encrypted_password del login['user'][user]['authentication']['plaintext_password'] # remove old plaintext password and set new encrypted password env = os.environ.copy() - env['vyos_libexec_dir'] = '/usr/libexec/vyos' + env['vyos_libexec_dir'] = directories['base'] # Set default commands for re-adding user with encrypted password del_user_plain = f"system login user '{user}' authentication plaintext-password" @@ -183,10 +207,12 @@ def generate(login): add_user_encrypt = " ".join(add_user_encrypt) call(f"/opt/vyatta/sbin/my_delete {del_user_plain}", env=env) - call(f"/opt/vyatta/sbin/my_set {add_user_encrypt}", env=env) + ret, out = rc_cmd(f"/opt/vyatta/sbin/my_set {add_user_encrypt}", env=env) + if ret: + raise ConfigError(out) else: try: - if getspnam(user).sp_pwdp == dict_search('authentication.encrypted_password', user_config): + if get_shadow_password(user) == dict_search('authentication.encrypted_password', user_config): # If the current encrypted bassword matches the encrypted password # from the config - do not update it. This will remove the encrypted # value from the system logs. @@ -204,6 +230,14 @@ def generate(login): if os.path.isfile(radius_config_file): os.unlink(radius_config_file) + # /etc/security/limits.d/10-vyos.conf + if 'max_login_session' in login: + render(limits_file, 'login/limits.j2', login, + permission=0o644, user='root', group='root') + else: + if os.path.isfile(limits_file): + os.unlink(limits_file) + if 'timeout' in login: render(autologout_file, 'login/autologout.j2', login, permission=0o755, user='root', group='root') |