From 96f5fae930b8213c199069c7aab079c6fb9cd334 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 27 Jan 2020 20:57:45 +0100 Subject: login: T1948: initial rewrite in XML/Python --- debian/control | 1 + interface-definitions/system-login.xml.in | 176 ++++++++++++++++++++++++++++++ src/conf_mode/system-login.py | 145 ++++++++++++++++++++++++ 3 files changed, 322 insertions(+) create mode 100644 interface-definitions/system-login.xml.in create mode 100644 src/conf_mode/system-login.py diff --git a/debian/control b/debian/control index 9df421977..6e59ea2fb 100644 --- a/debian/control +++ b/debian/control @@ -63,6 +63,7 @@ Depends: python3, openvpn, openvpn-auth-ldap, openvpn-auth-radius, + libpam-radius-auth, mtr-tiny, telnet, traceroute, diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in new file mode 100644 index 000000000..33197d191 --- /dev/null +++ b/interface-definitions/system-login.xml.in @@ -0,0 +1,176 @@ + + + + + + + User Login + 400 + + + + + RADIUS based user authentication + + + + + RADIUS client source address + + ipv4 + TFTP IPv4 listen address + + + + + + + + + RADIUS server configuration + + + + + RADIUS shared secret key + + + + + RADIUS authentication port + + 1-65535 + Numeric IP port (default: 1812) + + + + + + + + + Timeout for RADIUS session + + 1-30 + Session timeout in seconds (default: 2) + + + + + Timeout must be between 1 and 30 seconds + + + + + + + + + User account information + + [a-zA-Z0-9\-_\.]{1,100} + + Username contains illegal characters or\nexceeds 100 character limitation. + + + + + Password authentication + + + + + Encrypted password + + (\*|\!) + [a-zA-Z0-9\.\/]{13}$ + \$1\$[a-zA-Z0-9\./]*\$[a-zA-Z0-9\./]{22}$ + \$5\$[a-zA-Z0-9\./]*\$[a-zA-Z0-9\./]{43}$ + \$6\$[a-zA-Z0-9\./]*\$[a-zA-Z0-9\./]{86}$ + + Invalid encrypted password for $VAR(../../@). + + + + + Plaintext password used for encryption + + + + + Remote access public keys + + >identifier< + Key identifier used by ssh-keygen (usually of form user@host) + + + + + + Public key value (base64-encoded) + + + + + + + + Optional public key options + + + + + + + ssh-dss ssh-rsa ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521 ssh-ed25519 + + + ssh-dss + + + + ssh-rsa + + + + ecdsa-sha2-nistp256 + + + + ecdsa-sha2-nistp384 + + + + ssh-ed25519 + + + + (ssh-dss|ssh-rsa|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521|ssh-ed25519s) + + + + + + + + + + Full name of the user (use quotes for names with spaces) + + [^:]*$ + + Cannot use ':' in full name + + + + + Home directory + + + + + + + + + diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py new file mode 100644 index 000000000..2c1e4dc3e --- /dev/null +++ b/src/conf_mode/system-login.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 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 +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import sys +import os + +from vyos.config import Config +from vyos import ConfigError + +default_config_data = { + 'deleted': False, + 'radius_server': [], + 'radius_source': '', + 'user': [] +} + +def get_config(): + login = default_config_data + conf = Config() + base_level = ['system', 'login'] + + if not conf.exists(base_level): + login['deleted'] = True + return login + + if conf.exists(base_level + ['radius', 'source-address']): + login['radius_source'] = conf.return_value(['radius', 'source-address']) + + # Read in all RADIUS servers and store to list + for server in conf.list_nodes(base_level + ['radius', 'server']): + radius = { + 'address': server, + 'key': '', + 'port': '1812', + 'timeout': '2' + } + conf.set_level(base_level + ['radius', 'server', server]) + + # RADIUS shared secret + if conf.exists(['key']): + radius['key'] = conf.return_value(['key']) + + # RADIUS authentication port + if conf.exists(['port']): + radius['port'] = conf.return_value(['port']) + + # RADIUS session timeout + if conf.exists(['timeout']): + radius['timeout'] = conf.return_value(['timeout']) + + # Append individual RADIUS server configuration to global server list + login['radius_server'].append(radius) + + # Read in all local users and store to list + for username in conf.list_nodes(base_level + ['user']): + user = { + 'name': username, + 'password_plaintext': '', + 'password_encrypted': '', + 'public_keys': [], + 'full_name': '', + 'home_dir': '/home/' + username, + } + conf.set_level(base_level + ['user', username]) + + # Plaintext password + if conf.exists(['authentication', 'plaintext-password']): + user['password_plaintext'] = conf.return_value(['authentication', 'plaintext-password']) + + # Encrypted password + if conf.exists(['authentication', 'encrypted-password']): + user['password_encrypted'] = conf.return_value(['authentication', 'encrypted-password']) + + # Read in public keys + for id in conf.list_nodes(['authentication', 'public-keys']): + key = { + 'name': id, + 'key': '', + 'options': '', + 'type': '' + } + conf.set_level(base_level + ['user', username, 'authentication', 'public-keys', id]) + + # Public Key portion + if conf.exists(['key']): + user['key'] = conf.return_value(['key']) + + # Options for individual public key + if conf.exists(['options']): + user['options'] = conf.return_value(['options']) + + # Type of public key + if conf.exists(['type']): + user['type'] = conf.return_value(['type']) + + # Append individual public key to list of user keys + user['public_keys'].append(key) + + # set proper config level + conf.set_level(base_level + ['user', username]) + + # User real name + if conf.exists(['full-name']): + user['full_name'] = conf.return_value(['full-name']) + + # User home-directory + if conf.exists(['home-directory']): + user['home_dir'] = conf.return_value(['home-directory']) + + return login + +def verify(login): + pass + +def generate(login): + import pprint + pprint.pprint(login) + + pass + +def apply(login): + pass + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + sys.exit(1) -- cgit v1.2.3