Overview
The goal here is for you to gain some hands-on practice. This is a companion guide to our lecture on this topic, and a required myCourses quiz. You’ll have written questions on the exam that resemble these questions.
We’re going to give you a script that emulates a basic permissions system. Importantly, each time we run our script, we’re starting from a completely clean container. This is intentional. Playing around with permissions is infamous for putting the system into a different state than you expect, and can get very confusing. So, for this practice, we’ll always start from a generic Linux operating system, add our users, groups, files, etc. so you can know exactly what state the system is in.
Secondarily, we’re giving you some practice with Docker.
Docker
Messing with permissions is not something you want to do to a regular machine. So for this project, we’re going to be using Docker. Docker is a containerization system that allows you to fake an entire operating system for a single program. We’re going to use a generic Docker container in Linux, and we’ll run a script to get the information we need. We’re going to use docker run
to run a shell script, and that will output to our console.
To use Docker, you have a few options:
-
Install locally on your own machine. These days, Docker is becoming a standard software development tool - so you may already have Docker Desktop already installed.
-
Use our hydra servers. These are special servers that are isolated from other shared resources so that we can run docker containers from the command line. Here’s how you use it:
- There are six servers, named
hydra00.cs.rit.edu
throughhydra05.cs.rit.edu
. (Yes, these are currently CS servers.) For basic load balancing, use the following servers based on the month you were born:
Birth Month Server Jan-Feb hydra00.cs.rit.edu
Mar-Apr hydra01.cs.rit.edu
May-Jun hydra02.cs.rit.edu
Jul-Aug hydra03.cs.rit.edu
Sep-Oct hydra04.cs.rit.edu
Nov-Dec hydra05.cs.rit.edu
- SSH into your hydra server using your RIT username and password.
- Important Hydra itself runs Linux, however, you don’t have permissions to do what we’ll do in this project. That’s why we’re running our scripts inside a Docker container.
- When you SSH in, you’ll be able to do your
docker run
commands as you wish. - You’ll be editing a script on Hydra. You can use
vim
ornano
to get this done. Or, you can set up your local VS Code to connect to hydra using its Remote Explorer (your instructor may demo this).
- There are six servers, named
Note: we will not be able to run Docker from the SE lab machines.
Script Copypasta
Copy and paste the following script into a file called perms.sh
.
Note about line endings. If you copy this from a Windows machine directly into an editor like Notepad, you might get errors about non-ASCII characters and other cryptic errors. Coding editors like (e.g. VS Code) will auto-detect the line endings and fix this for you. Alternatively, you can run dos2unix perms.sh
on Hydra convert the line endings.
echo "\n--- CREATE USERS ---"
adduser paul --shell /bin/bash --disabled-password --gecos ""
adduser leto --shell /bin/bash --disabled-password --gecos ""
adduser vlad --shell /bin/bash --disabled-password --gecos ""
echo "\n--- CREATE GROUPS ---"
addgroup arrakis
adduser paul arrakis
adduser leto arrakis
# NOTE that vlad is NOT in arrakis
echo "\n--- UMASK ---"
umask
echo "\n--- SET UP DIRECTORY ---"
mkdir /dune
chmod 777 /dune # BAD SECURITY. This is for educational purposes.
ls -la /dune
echo "\n--- SET UP FILES ---"
echo "the spice must flow" >> /dune/melange.txt
chown paul /dune/melange.txt
chgrp arrakis /dune/melange.txt
chmod g+w /dune/melange.txt
chmod o-rwx /dune/melange.txt
mkdir /dune/vault
chmod 771 /dune/vault
chown paul /dune/vault
chgrp arrakis /dune/vault
echo "secrets!" >> /dune/vault/secret1234.txt
chmod o=rw /dune/vault/secret1234.txt
mkdir /dune/workspace
chgrp arrakis /dune/workspace
chmod g+s /dune/workspace # setgid
touch arrakis /dune/workspace/shared.txt
cp /usr/bin/whoami /dune
mv /dune/whoami /dune/whoamireally
chown paul /dune/whoamireally
chmod u+s /dune/whoamireally # setuid
echo "\n--- ACT AS PAUL ---"
echo -n "Who am I? "
su paul -c 'whoami'
su paul -c 'umask'
su paul -c 'cat /dune/melange.txt'
su paul -c 'touch /dune/paul_only.txt'
su paul -c 'chgrp arrakis /dune/paul_only.txt'
su paul -c 'chmod g-rwx /dune/paul_only.txt'
su paul -c 'echo -n "Running whoamireally: " ; /dune/whoamireally'
su paul -c 'umask 007 ; touch /dune/arrakis_only.txt'
su paul -c 'ls -l /dune/arrakis*'
echo "\n--- ACT AS LETO ---"
echo -n "Who am I? "
su leto -c 'whoami'
su leto -c 'umask'
su leto -c 'touch /dune/leto.txt'
su leto -c 'cat /dune/melange.txt'
su leto -c 'ls /dune/vault'
su leto -c 'cat /dune/vault/secret1234.txt'
su leto -c 'echo -n "Running whoamireally as leto: " ; /dune/whoamireally'
su leto -c 'cat /dune/arrakis_only.txt'
echo "\n--- ACT AS VLAD ---"
echo -n "Who am I? "
su vlad -c 'whoami'
su vlad -c 'umask'
su vlad -c 'touch /dune/vlad.txt'
su vlad -c 'cat /dune/melange.txt'
su vlad -c 'ls /dune/vault'
su vlad -c 'cat /dune/vault/secret1234.txt'
su vlad -c 'echo -n "Running whoamireally as vlad: " ; /dune/whoamireally'
su vlad -c 'cat /dune/arrakis_only.txt'
echo "\n--- PERMISSIONS ---"
ls -l /dune
ls -la /dune/vault
ls -la /dune/workspace
echo "" # newline at the end of the output
# bash
Running the Script
Your Docker command to run this will look like this. Note that it is all one line.
Compatibility note:
- This will work in Mac and Linux (Hydra).
- This will also work in most Windows Powershell (not CMD), prompts. If it doesn’t, try replacing
$(pwd)
with your current directory path (see below).
docker run -it --rm --mount type=bind,source="$(pwd)",target=/root ubuntu:22.04 sh /root/perms.sh
Let’s break down each piece of this, using the documentation
docker run
is our command-it
essentially means “interactive”, or “show everything to the console stdout and use the shell when reading from stdin”.--rm
means “delete everything when this run is done”, which makes our run clean and doesn’t fill up our hard drive with past runs.--mount type=bind,source="$(pwd)",target=/root
means “my current folder should be mapped to the/root
folder in the container. This mount command allows us to run our script without having to create a whole new container. Thepwd
means “print working directory”, so you could also replace this with the full filepath of your current directory (this part is what might vary from OS to OS)ubuntu:22.04
is the image we want to run. If your machine doesn’t have it installed, Docker will call out to DockerHub and download the image. You may see downloading feedback the first time your run this. The Linux version we chose is pretty arbitrary - you can use lots of different Linux images for this project.sh /root/perms.sh
- means “run the shell script”, which, since we mounted our local folder with--mount
it’ll find our file to run.
Commands
Here’s a reference to all of the commands we are using:
ls
lists file permissions. The-a
will show files that start with.
, including the pseudo-directories of.
(“this” directory) and..
(parent directory)mkdir
creates a new directoryumask
shows the current default permissions mask if no number is given. Invoking with a number (e.g.umask 077
) sets the current user’s umask.chmod
changes the permissions on a file or directory. See our lecture on how it works.chgrp
changes the group of a file or directorychown
changes who owns a file or directorycp
copies the filemv
moves the file, in this case effectively renaming the filesu
allows you to act as another user, i.e. spoof them. (Note: this is why we need to be in Docker - you need root privileges to usesu
). Ordinarily we might usesudo
, butsudo
is no longer installed by default in Ubuntu, but the-c
option tosu
will suffice for our purposes.adduser
does two things:- Creates a new user. Our invocation is pretty non-standard and intended to make scripting easier
- Adds a user to a group
addgroup
creates a new groupecho
is a standard shell scripting command that prints stuff to the console. Also:- The
-n
option omits the newline - We are using the “pipe append” (
>>
) to create files and put some contents in them
- The
cat
means “concatenate to stdout”, effectively just “print the contents of the file to the console”;
allows us to put two commands into one, simplifying our statementbash
(commented out by default). If you want to try these commands interactively in this environment, you can uncomment this. You can also placebash
between any other lines in this scrtipt and execution will halt there. Typeexit
to finish running the script when you’re done.
Our Setup
We have the following users set up:
- Everything in our scenario takes place in the
/dune
folder. paul
andleto
are members of the grouparrakis
vlad
is not a member of the grouparrakis
Note that, by default, all users have a group that is also named after them. So paul
is in a group called paul
, and so on. This is the default group that is set when you make a new file or folder.
From there we are doing the following:
- The file
/dune/melange.txt
is a file that is group editable and in thearrakis
group - The directory
/dune/vault
is in thearrakis
group. It is set up so that anyone can open a file if they know the name, but people outside thearrakis
group cannot usels
to find the name. We have a file calledsecret1234.txt
in it. - The OS-standard binary
whoami
is copied and renamed towhoamireally
and thesetuid
permission is set. Ordinarilywhoami
prints what the current user’s name is, but in this case we’re tampering with a copy of the binary and changing the permissions to take advantage ofsetuid
. Thus, when, say,vlad
runswhoamireally
, it sayspaul
.
Default Output
From the copypasta above, the output should look something like this, e.g. your timestamps will look different. The Permission denied
errors, however, are expected.
--- CREATE USERS ---
Adding user `paul' ...
Adding new group `paul' (1000) ...
Adding new user `paul' (1000) with group `paul' ...
Creating home directory `/home/paul' ...
Copying files from `/etc/skel' ...
Adding user `leto' ...
Adding new group `leto' (1001) ...
Adding new user `leto' (1001) with group `leto' ...
Creating home directory `/home/leto' ...
Copying files from `/etc/skel' ...
Adding user `vlad' ...
Adding new group `vlad' (1002) ...
Adding new user `vlad' (1002) with group `vlad' ...
Creating home directory `/home/vlad' ...
Copying files from `/etc/skel' ...
--- CREATE GROUPS ---
Adding group `arrakis' (GID 1003) ...
Done.
Adding user `paul' to group `arrakis' ...
Adding user paul to group arrakis
Done.
Adding user `leto' to group `arrakis' ...
Adding user leto to group arrakis
Done.
--- UMASK ---
0022
--- SET UP DIRECTORY ---
total 8
drwxrwxrwx 2 root root 4096 Aug 14 19:44 .
drwxr-xr-x 1 root root 4096 Aug 14 19:44 ..
--- SET UP FILES ---
--- ACT AS PAUL ---
Who am I? paul
0002
the spice must flow
Running whoamireally: paul
-rw-rw---- 1 paul paul 0 Aug 14 19:44 /dune/arrakis_only.txt
--- ACT AS LETO ---
Who am I? leto
0002
the spice must flow
secret1234.txt
secrets!
Running whoamireally as leto: paul
cat: /dune/arrakis_only.txt: Permission denied
--- ACT AS VLAD ---
Who am I? vlad
0002
cat: /dune/melange.txt: Permission denied
ls: cannot open directory '/dune/vault': Permission denied
secrets!
Running whoamireally as vlad: paul
cat: /dune/arrakis_only.txt: Permission denied
--- PERMISSIONS ---
total 44
-rw-rw---- 1 paul paul 0 Aug 14 19:44 arrakis_only.txt
-rw-rw-r-- 1 leto leto 0 Aug 14 19:44 leto.txt
-rw-rw---- 1 paul arrakis 20 Aug 14 19:44 melange.txt
-rw----r-- 1 paul arrakis 0 Aug 14 19:44 paul_only.txt
drwxrwx--x 2 root arrakis 4096 Aug 14 19:44 vault
-rw-rw-r-- 1 vlad vlad 0 Aug 14 19:44 vlad.txt
-rwsr-xr-x 1 paul root 31232 Aug 14 19:44 whoamireally
drwxr-sr-x 2 root arrakis 4096 Aug 14 19:44 workspace
total 12
drwxrwx--x 2 root arrakis 4096 Aug 14 19:44 .
drwxrwxrwx 4 root root 4096 Aug 14 19:44 ..
-rw-r--rw- 1 root root 9 Aug 14 19:44 secret1234.txt
total 8
drwxr-sr-x 2 root arrakis 4096 Aug 14 19:44 .
drwxrwxrwx 4 root root 4096 Aug 14 19:44 ..
-rw-r--r-- 1 root arrakis 0 Aug 14 19:44 shared.txt
This is now YOUR script
At this point, we’re giving you a toolbox and we’re asking you to explore and answer questions. We’ve demonstrated much of what we lectured on, and now it’s your turn to mess around in the sandbox.
You will undoubtedly have to edit this script and experiment on your own to answer these questions. You won’t be able to answer all of the questions by just studying the output - experiment!
Questions
The following questions are on myCourses, but are repeated here for convenience. All of these assume the system is in the state left by the above script.
Making vault
personal
Which of the following chmod
commands should be used to make the vault
readable, searchable, and writeable only to paul
and not anyone else (arrakis
or not)?.
chmod 000
chmod 777
chmod 711
chmod 700
chmod 600
Why can Vlad see the secret?
Why can vlad
read the contents of the file in /dune/vault/secret1234.txt
?
Choose one:
- Because the
/dune
directory is777
- Because Docker runs as root
- Because
/dune
has executable permissions on non-arrakis
users - Because
/dune
has read permissions for all users
Hide the secret
Suppose paul
wants to change the permissions on only the file secret1234.txt
so that anyone outside of arrakis
could not open it - even if they knew the name of the file (but paul
and leto
could still access it since they are in arrakis
). What chmod command should be used?
chmod ug=rw /dune/vault/secret1234.txt
chmod o-r /dune/vault/secret1234.txt
chmod a-r /dune/vault/secret1234.txt
For these permissions, what is the octal notation?
-rw--w----
Why the group?
Check the group ownership of /dune/vault/secret1234.txt
and /dune/workspace/shared.txt
. Why is shared.txt
in the arrakis
group and secret1234.txt
not?
Choose one.
workspace
uses the sticky bitworkspace
uses setgid and is owned by thearrakis
group.shared.txt
uses setuid- We used
touch
and notecho
to create the file workspace
is owned byarrakis
andvault
is not.- The
umask
is set to have permissive group permissions
Why no execute?
What would happen if we tried to execute melange.txt
with su paul -c './melange.txt'
, and why?
- The code runs, because umask is
0022
, but it’s not valid shell syntax so it fails - The code runs and nothing happens because umask is
0002
forpaul
- The code does not run because files are never created with execute permissions from umask, and we never gave execute permissions
- The code does not run because the directory also has execute permissions
- The code does run because
chmod 777 /dune
was run at the beginning.
Copying whoamireally?
Suppose vlad
makes a copy of whoamireally
into /dune
and gives it read and execute permissions for non-group members. Then, paul
attempts to execute it. What happens?
- The output is
paul
- The output is
leto
- The output is
vlad
- The execution fails
Permission for standard tools
Changing passwords in Linux requires you to run passwd
, which is located in /bin/passwd
. You can only change your own password, however. Passwords are stored in the root-only file /etc/shadow
, which means you would need to temporarily act as root to change your own password. But… you don’t need root access to change your own password. How does Linux accomplish this?
- The
/bin/passwd
has setuid and is owned byroot
- The
/bin/passwd
has setgid and is in theroot
group - The
/etc/shadow
and/bin/password
are both owned by root - The
/bin/passwd
is owned byroot
, and when you execute a program, the owner becomes the acting user - Nobody knows, only Linus Torvalds knows this secret.