From 67ebf6ad35ce1e2d12c227ad4b07f1c4c48a3bbf Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Tue, 17 Jul 2018 10:32:48 +1200 Subject: [PATCH] do not let the user choose a reserved username using the same logic as d-i --- reserved-usernames | 107 +++++++++++++++++++++++++++++++++ snapcraft.yaml | 2 + subiquity/ui/views/identity.py | 33 ++++++++-- 3 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 reserved-usernames diff --git a/reserved-usernames b/reserved-usernames new file mode 100644 index 00000000..2b6ba2af --- /dev/null +++ b/reserved-usernames @@ -0,0 +1,107 @@ +# Static users from base-passwd/passwd.master (3.5.11). +root +daemon +bin +sys +sync +games +man +lp +mail +news +uucp +proxy +www-data +backup +list +irc +gnats +nobody + +# Other static groups from base-passwd/group.master (3.5.11). +adm +tty +disk +kmem +dialout +fax +voice +cdrom +floppy +tape +sudo +audio +dip +operator +src +shadow +utmp +video +sasl +plugdev +staff +users +nogroup + +# Reserved usernames listed in base-passwd/README (3.5.11). +netplan +ftn +mysql +tac-plus +alias +qmail +qmaild +qmails +qmailr +qmailq +qmaill +qmailp +asterisk +vpopmail +vchkpw + +# Ubuntu creates the admin group and adds the first user to it in order to +# grant them sudo privileges. +admin + +# Other miscellaneous system users/groups created by common packages. While +# it's useful to add things here that people might run into, it's not +# absolutely critical; the worst that will happen is that the installation +# will fail at some later point. +Debian-exim +bind +crontab +cupsys +dcc +dhcp +dictd +dovecot +fetchmail +firebird +ftp +fuse +gdm +haldaemon +hplilp +identd +jwhois +klog +lpadmin +maas +messagebus +mythtv +netdev +powerdev +radvd +saned +sbuild +scanner +slocate +ssh +sshd +ssl-cert +sslwrap +statd +syslog +telnetd +tftpd diff --git a/snapcraft.yaml b/snapcraft.yaml index 8bb49680..242e3d37 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -57,9 +57,11 @@ parts: echo "get passwd/user-default-groups" | \ debconf-communicate user-setup | \ cut -d ' ' -f 2- > users-and-groups + cp /usr/lib/user-setup/reserved-usernames . stage-packages: [libc6] stage: - users-and-groups + - reserved-usernames kbdnames: plugin: dump build-packages: diff --git a/subiquity/ui/views/identity.py b/subiquity/ui/views/identity.py index ccb7da6c..6f616279 100644 --- a/subiquity/ui/views/identity.py +++ b/subiquity/ui/views/identity.py @@ -14,6 +14,7 @@ # along with this program. If not, see . import logging +import os import re from urwid import ( @@ -116,6 +117,10 @@ _ssh_import_data = { class IdentityForm(Form): + def __init__(self, reserved_usernames): + self.reserved_usernames = reserved_usernames + super().__init__() + realname = RealnameField(_("Your name:")) hostname = UsernameField( _("Your server's name:"), @@ -151,15 +156,21 @@ class IdentityForm(Form): return _("Hostname must match NAME_REGEX, i.e. [a-z_][a-z0-9_-]*") def validate_username(self): - if len(self.username.value) < 1: + username = self.username.value + if len(username) < 1: return _("Username missing") - if len(self.username.value) > USERNAME_MAXLEN: + if len(username) > USERNAME_MAXLEN: return _("Username too long, must be < ") + str(USERNAME_MAXLEN) - if not re.match(r'[a-z_][a-z0-9_-]*', self.username.value): + if not re.match(r'[a-z_][a-z0-9_-]*', username): return _("Username must match NAME_REGEX, i.e. [a-z_][a-z0-9_-]*") + if username in self.reserved_usernames: + return _( + 'The username "{username}" is reserved for use by the system.' + ).format(username=username) + def validate_password(self): if len(self.password.value) < 1: return _("Password must be set") @@ -293,7 +304,21 @@ class IdentityView(BaseView): self.opts = opts self.items = [] - self.form = IdentityForm() + reserved_usernames_path = ( + os.path.join(os.environ.get("SNAP", "."), "reserved-usernames")) + reserved_usernames = set() + if os.path.exists(reserved_usernames_path): + with open(reserved_usernames_path) as fp: + for line in fp: + line = line.strip() + if line.startswith('#') or not line: + continue + reserved_usernames.add(line) + else: + reserved_usernames.add('root') + + self.form = IdentityForm(reserved_usernames) + connect_signal(self.form, 'submit', self.done) connect_signal(self.form.confirm_password.widget, 'change', self._check_password)