Chapter 7. Enabling Multiple Users

Table of Contents

1. Analysis
2. Design
2.1. The login process
2.2. Obtaining source code
2.3. Creating support files
2.4. Dependencies
2.5. Assigning ownership and permissions
3. Construction
3.1. Verify presence of getty and login
3.2. Modify inittab for multi-user mode
3.3. Create tty devices
3.4. Create support files in /etc
3.5. Copy required libraries
3.6. Set directory and file permissions
3.7. Create the root disk image
3.8. Copy the image to diskette
4. Implementation
4.1. System Startup
4.2. Add a new user to the system
4.3. Test the new user's ability to use the system
4.4. System shutdown

1. Analysis

Up to now the system has been operating in single-user mode. There is no login process and anyone who boots the system goes straight into a shell with root privileges. Obviously, this is not the normal operating mode for most GNU/Linux distributions. Most systems feature multi-user capability where many users can access the system simultaneously with different privilege levels. These multi-user systems also support virtual consoles so that the keyboard and video display can be multiplexed between several terminal sessions. So in this phase we would like to add the following enhancements to the system:

  • Enable multi-user capability.

  • Create multiple, virtual consoles.

2. Design

2.1. The login process

The From-Powerup-To-BASH-Prompt-HOWTO does a good job of outlining the steps in the login process. Basically it works like this.

  1. The init daemon starts a getty process on the terminal.

  2. The getty program displays the contents of /etc/issue and prompts for a user name.

  3. When the user name is entered, control is handed off to the login program.

  4. The login program asks for a password and verifies the credentials using /etc/passwd, /etc/group and possibly /etc/shadow.

  5. If everything is okay the user's shell is started.

2.2. Obtaining source code

The getty and login programs were already installed as part of the util-linux package so there is no need to download any new source code.

2.3. Creating support files

2.3.1. Device nodes

Details about virtual console device files can be found in the Linux kernel source code file called devices.txt in the Documentation directory. We will need to create tty1 through tty6 for each of the virtual consoles as well as tty0 and tty to represent the current virtual console.

2.3.2. /etc/issue

The /etc/issue file is pretty easy to construct. It can contain any text we want displayed on the screen prior to the login prompt. It could be something friendly like "Welcome to Pocket Linux", something menacing like "Authorized users only!" or something informational like "Connected to tty1 at 9600bps". The agetty(8) manpage explains how to display information like tty line and baud rate using escape codes.

2.3.3. /etc/passwd

The format of /etc/passwd can be obtained by reading the passwd(5) manpage. We can easily create a user account by adding a line like "root::0:0:superuser:/root:/bin/sh" to the file.

Maintaining passwords will be somewhat challenging because of the system being loaded into ramdisk. Any changes to /etc/passwd will be lost when the system is shutdown. So to make things easy, we will create all users with null passwords.

2.3.4. /etc/group

The structure of /etc/group is available from the group(5) manpage. A line of "root::0:root" would define a group called "root" with no password, a group id of zero and the user root assigned to it as the only member.

2.3.5. Conventions

User and group names and id's are generally not chosen at random. Most Linux systems have very similar looking /etc/passwd and /etc/group files. Definitions for commonly used user id and group id assignments may be found in one of several places:

2.4. Dependencies

Running ldd on the login program from util-linux will reveal that it is linked to the libraries libcrypt.so.1, libc.so.6 and ld-linux.so.2. In addition to these libraries there is another, unseen dependency on libnss_files.so.2 and the configuration file /etc/nsswitch.conf.

The name service switch library libnss_files.so.2 and nsswitch.conf are required for libc.so.6, and consequently the login program, to access the /etc/passwd file. Without libnss and its configuration file, all logins will mysteriously fail. More information about glibc's use of the name service switch libraries can be found at http://www.gnu.org/software/libc/manual/html_node/Name-Service-Switch.html.

2.5. Assigning ownership and permissions

Previously, with the single user system, there was no need to worry about permissions when installing directories, files and device nodes. The shell was effectively operating as root, so everything was accessible. Things become more complex with the addition of multiple user capability. Now we need to make sure that every user has access to what they need and at the same time gets blocked from what they do not need.

A good guideline for assigning ownership and permissions would be to give the minimum level of access required. Take the /bin directory as an example. The Filesystem Hierarchy (FHS) document says, "/bin contains commands that may be used by both the system administrator and by users". From that statement we can infer that /bin should have read and execute permission for everyone. On the other hand, the /boot directory contains files for the boot loader. Chances are good that regular users will not need to access anything in the /boot directory. So the minimum level of access would be read permission for the root user and other administrators who are members of the root group. Normal users would have no permissions assigned on the /boot directory.

Most of the time we can assign similar permissions to all the commands in a directory, but there are some programs that prove to be exceptions to the rule. The su command is a good example. Other commands in the /bin directory have a minimum requirement of read and execute, but the su command needs to be setuid root in order to run correctly. Since it is a setuid binary, it might not be a good idea to allow just anyone to run it. Ownership of 0:0 (root user, root group) and permissions of rwsr-x--- (octal 4750) would be a good fit for su.

The same logic can be applied to other directories and files in the root filesystem using the following steps:

  1. Assign ownership to the root user and root group.

  2. Set the most restrictive permissions possible.

  3. Adjust ownership and permissions on an "as needed" basis.

3. Construction

3.1. Verify presence of getty and login

bash# ls ~/staging/sbin/getty
bash# ls ~/staging/bin/login

3.2. Modify inittab for multi-user mode

Modify ~/staging/etc/inittab by changing the default runlevel and adding getty entries as shown below.

# /etc/inittab - init daemon configuration file
#
# Default runlevel
id:2:initdefault:
#
# System initialization
si:S:sysinit:/etc/init.d/rc S
#
# Runlevel scripts
r0:0:wait:/etc/init.d/rc 0
r1:1:respawn:/bin/sh
r2:2:wait:/etc/init.d/rc 2
r3:3:wait:/etc/init.d/rc 3
r4:4:wait:/etc/init.d/rc 4
r5:5:wait:/etc/init.d/rc 5
r6:6:wait:/etc/init.d/rc 6
#
# Spawn virtual terminals
1:235:respawn:/sbin/getty 38400 tty1 linux
2:235:respawn:/sbin/getty 38400 tty2 linux
3:235:respawn:/sbin/getty 38400 tty3 linux
4:235:respawn:/sbin/getty 38400 tty4 linux
5:235:respawn:/sbin/getty 38400 tty5 linux
6:2345:respawn:/sbin/getty 38400 tty6 linux
#
# end of /etc/inittab

3.3. Create tty devices

bash# cd ~/staging/dev
bash# mknod ~/staging/dev/tty0 c 4 0
bash# mknod ~/staging/dev/tty1 c 4 1
bash# mknod ~/staging/dev/tty2 c 4 2
bash# mknod ~/staging/dev/tty3 c 4 3
bash# mknod ~/staging/dev/tty4 c 4 4
bash# mknod ~/staging/dev/tty5 c 4 5
bash# mknod ~/staging/dev/tty6 c 4 6
bash# mknod ~/staging/dev/tty c 5 0

3.4. Create support files in /etc

3.4.1. /etc/issue

Create the file ~/staging/etc/issue using the example below or design a customized message.

Connected to \l at \b bps.

Be sure that "\l" is a lowercase letter L and not the number one.

3.4.2. /etc/passwd

Use a text editor to create a minimal passwd file conforming to the Linux Standards Base (LSB) document. Save the file as ~/staging/etc/passwd

root::0:0:Super User:/root:/bin/sh
bin:x:1:1:Legacy UID:/bin:/bin/false
daemon:x:2:2:Legacy UID:/sbin:/bin/false

3.4.3. /etc/group

Use a text editor to create an LSB conforming group file and save it as ~/staging/etc/group

root::0:root
bin:x:1:root,bin,daemon
daemon:x:2:root,bin,daemon

3.4.4. /etc/nsswitch.conf

Create the following file and save it as ~/staging/etc/nsswitch.conf

passwd: files
group:  files

3.5. Copy required libraries

bash# cp /lib/libnss_files.so.2 ~/staging/lib
bash# strip --strip-unneeded ~/staging/lib/*

3.6. Set directory and file permissions

Set minimal privileges on all files and directories under ~/staging. Everything is owned by the root user and the root group. Permissions are read-write for the owner and read-only for the group. Exceptions to the blanket permissions are handled case by case.

bash# cd ~/staging
bash# chown -R 0:0 ~/staging/*
bash# chmod -R 640 ~/staging/*

Set execute permission on all directories. (Note the capital "X")

bash# chmod -R +X ~/staging/*

Files in /bin are read and execute for all, but su is an exception.

bash# chmod 755 ~/staging/bin/*
bash# chmod 4750 ~/staging/bin/su

Files in /dev have various permissions. Disk devices should be accessible to administrators only. Other files like /dev/null should have full privileges granted to everyone.

bash# chmod 660 ~/staging/dev/fd0 dev/ram0
bash# chmod 666 ~/staging/dev/null
bash# chmod 622 ~/staging/dev/console
bash# chmod 600 ~/staging/dev/initctl
bash# chmod 622 ~/staging/dev/tty
bash# chmod 622 ~/staging/dev/tty?

The passwd and group files must be world readable.

bash# chmod 644 ~/staging/etc/passwd
bash# chmod 644 ~/staging/etc/group

The scripts in /etc/init.d are read and execute for administrators.

bash# chmod 750 ~/staging/etc/init.d/*

Libraries need read and execute permissions for everyone.

bash# chmod 755 ~/staging/lib/*

Only root should have access to the /root directory.

bash# chmod 700 ~/staging/root

Make files in /sbin read and execute for administrators.

bash# chmod 750 ~/staging/sbin/*

Temp should be read-write for all with the sticky bit set.

bash# chmod 1777 ~/staging/tmp

3.7. Create the root disk image

bash# cd /
bash# dd if=/dev/zero of=/dev/ram7 bs=1k count=4096
bash# mke2fs -m0 /dev/ram7 4096
bash# mount /dev/ram7 /mnt
bash# cp -dpR ~/staging/* /mnt
bash# umount /dev/ram7
bash# dd if=/dev/ram7 of=~/phase6-image bs=1k count=4096
bash# gzip -9 ~/phase6-image

3.8. Copy the image to diskette

Insert the diskette labeled "root disk" into drive fd0.

bash# dd if=~/phase6-image.gz of=/dev/fd0 bs=1k

4. Implementation

4.1. System Startup

If everything goes well, the virtual console display should look similar to the following example:

Connected to tty1 at 38400 bps.
gnu-linux login:

4.2. Add a new user to the system

Log in as root.

Create a new, unprivileged user and new group by appending a line to the /etc/passwd and /etc/group files, respectively. Be sure to use a double greater-than (>>) to avoid accidentally overwriting the files.

bash# echo "floyd::501:500:User:/home/floyd:/bin/sh" >>/etc/passwd
bash# echo "users::500:" >>/etc/group
bash# mkdir /home/floyd
bash# chown floyd.users /home/floyd
bash# chmod 700 /home/floyd

4.3. Test the new user's ability to use the system

Switch to virtual terminal tty2 by pressing ALT+F2.

Log in as floyd.

Try the following commands and verify that they work.

bash$ pwd
bash$ ls -l /
bash$ cat /etc/passwd

Try the following commands and verify that they do not work.

bash$ ls /root
bash$ /sbin/shutdown -h now
bash$ su -

4.4. System shutdown

Switch back to tty1 where root is logged in.

bash# shutdown -h now