Lec 23: O.S. Security: Setting/Getting User/Group Privileges
Table of Contents
1 O.S. Security Basics for Users and Groups
Through this class we have seen a number of security settings provided by the operating system. Formost, we discussed users and groups, file permissions, the terminal login, and finally the concept of system calls generally and how that system is designed to protect the user from itself.
Let's take a moment to review some of these concepts and the O.S. security settings thereof.
1.1 Users and Groups
All users are defined in the /etc/passwd
file with lines like such:
.-- user name .-- full name .--- home directory | | | v v v aviv:x:35001:10120:Adam Aviv {}:/home/scs/aviv:/bin/bash ^ ^ ^ uid ----' '--- gid (Default) '--- default shell
This is my passwd entry. It stores my user name, my user id, my
default group id, my full name, my home directory, and my default
shell. Every user has a unique username, user id, and default group;
however, a user can be assigned to multiple groups. Group information
is fined in the /etc/group
directory, and here is entry for my
default group:
.-- group name | v scs:*:10120:webadmin,www-data,lucas,slack ^ \___________________________/ gid ---' | '- Additional users in that group
From the command line, the tool id
will print this information, as
well as groups
aviv@saddleback: ~ $ id uid=35001(aviv) gid=10120(scs) groups=10120(scs),27(sudo),15000(mids) aviv@saddleback: ~ $ groups scs sudo mids
One thing you might notice is that I am in the sudo
group … on
this computer, at least. We'll come back to this later.
1.2 Permissions
Access to files are permission-ed based on the user and group
designation of the accessing agent. Typically, this is a user, but the
same designations are assigned to running processes. The permissions
of a file can be observed using ls -l
or stat
:
aviv@saddleback: demo $ ls -l total 4 -rwxr--r-- 1 aviv scs 0 Mar 27 09:41 a -rw--wx--- 1 aviv scs 0 Mar 27 09:41 b -------rwx 1 aviv scs 0 Mar 27 09:41 c drwxr-x--- 2 aviv scs 4096 Mar 27 09:41 d aviv@saddleback: demo $ stat b File: ‘b’ Size: 0 Blocks: 0 IO Block: 524288 regular empty file Device: 1ch/28d Inode: 34996239 Links: 1 Access: (0630/-rw--wx---) Uid: (35001/ aviv) Gid: (10120/ scs) Access: 2015-03-27 09:41:22.884094861 -0400 Modify: 2015-03-27 09:41:22.884094861 -0400 Change: 2015-03-27 09:41:41.292753637 -0400
And if we look at a particular mode portion, let's recall how the permissions are identified:
user other .- group | | | .-. .-. v -rwxr--r-- 1 aviv scs 0 Mar 27 09:41 a ^ '-' ^ | | '-- user/owner | group | '- Directory Bit
When an operation is performed on the file, such as reading, writing,
or executing, the user taking the action is compared to the
permission. If the right permissions are set, and the user matches
those permissions, the action can be taken. For example, a user that
is not aviv
may still read the file if they are in group
scs
. More, even a user who meets neither criteria, the owner or
group of the file, can still read the file because the other read
permission is set.
Permissions of files can be changed using three commands:
chmod
: change the permissions string of the file which can only be done by the owner of the file (or super user).chgrp
: change the group of the file which can only be done by the owner of the file (or super user)chown
: change the owner of the file which can only be done by the super user.
The super user for unix systems is referred as root
. It has full
privileges and can do whatever it wants. Creating multiple levels of
permissions by dividing users from one privilege to another is a key
security concept. A key question is how does this occur? To understand
this process, we have to start with when the user logs in.
1.3 Terminal Login and Password Checking
The log procedure is not a huge focus of this lesson; however, what
happens after login is incredibly relevant. Recall from the lectures
on the tty
that when a user "calls" a tty
the program getty
will
execute login
. Following the diagram:
runs as root ....................................... : .-------. : : | getty | : : '-------' : : | : : exec() <-------. : : | | : : (1) v | (failed) : : .--------. | ............: : | login | ------' : : '--------' : runs as the user : | (success) : .................................. : | ............: : : : | : ............: .-------. (3) : : fork() -:---:- exec() --> | shell | : :.............: ^ : '-------' : (2) | : | .----. : changes ______| : fork() --- exec() --> | ls | : user : '----' : :............................................:
At (1), the log procedure is going to authentic a user by asking for a
password. While it would seem logical for passwords to be stored in
/etc/passwd
, it isn't. The reason /etc/passwd
is named such is
that it used to store password; however, that is no longer the
case. Now passwords are stored are in the file /etc/shadow
which is
carefully protected, and passwords are not stored in plain text in
/etc/shadow
but rather stored using secure hashes.
Of more interest to this lesson is what happens if the user
successfully logs into the system. The log in procedure needs to run
in a privileged state so that passwords can be checked from
/etc/shadow
. Only the root user has access to read/write the file,
but we do not want it to be the case that when the user's shell starts
up he/she gets the same permissions as the root user. To prevent that,
there is a deescalation of privilege level by setting the effective
user of the shell program to the user the that just logged onto the
system – occurring at step (2). By the time the shell executes
commands, the user is running — occurring at step (3).
2 Users/Group Capabilities of Programs
Running programs inherit the permissions of the user who execute it, but we will see how that might change. To start, let us first look at the system programming constructs for observing and testing the users permission, and the errors associated with permission. Following, we can look at how to escalate or change the permission settings.
2.1 Observing the privilege settings of programs
There are two basic system calls for retrieving useer and group information for an execution program.
uid_t getuid(void)
: Returns the real user id of the calling process.gid_t getgid(void)
: Returns the real group id of the calling process.
Let's look at an example program.
#include <unistd.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char * argv[]){ uid_t uid; gid_t gid; uid = getuid(); gid = getgid(); printf("uid=%d gid=%d\n", uid, gid); }
When executing, my user id and group id is printed to the terminal:
m179998@saddleback: git $ ~aviv/lec-23-demo/get_uidgid uid=179998 gid=15000
These values are mine. If you were to run the same program, you would
get a different value. For example, I have a test student account
m17998
and if I run this program as that user:
m159998@saddleback: git $ ~aviv/lec-23-demo/get_uidgid uid=35013 gid=15000 m159998@saddleback: git $ ls -l ~aviv/lec-23-demo/get_uidgid -rwxr-x--x 1 aviv scs 8622 Mar 30 10:40 /home/scs/aviv/lec-23-demo/get_uidgid
Even though the program is owned by the user aviv
, the execution of
the program as the process takes on the permissions of the user that
runs the program, which is m179998
in this demo. However, this could
be changed.
2.2 Extra permission modes for set-user-id/set-group-id
There is also an obvious need to be able to set the user/group privileges of a running program. For example, consider the submission system we have been using all semester. You all run a program in my home directory:
~aviv/bin/ic221-submit
This program, run by you, has your user and group permissions, but it is able to take your submission and copy/save those submissions to my home directory, with my permissions at a location where you do not have access to write. How is that possible?
If you could somehow change the user/group setting of the running program to my user/group priviledge, then you could write to my home directory. This is essentially how the submission system works.
To have a program run with a different user's capabilities requires
additional permissions on the program beyond just that the user can
execute the program. These permissions are called the set-bit and
if we look at the man page for chmod
, the set-bit is composed of
three permission bits we previously ignored:
A numeric mode is from one to four octal digits (0-7), derived by adding up the bits with values 4, 2, and 1. Omitted digits are assumed to be leading zeros. *The first digit selects the set user ID (4) and set group ID (2) and restricted deletion or sticky (1) attributes.* The second digit selects permissions for the user who owns the file: read (4), write (2), and execute (1); the third selects permissions for other users in the file's group, with the same values; and the fourth for other users not in the file's group, with the same values.
That is, we previously assumed a permission string contained 3 octal digits, but really there are 4 octal digits. The missing octal digit is that for the set-bits. There are three possible set-bit settings and they are combined in the same way as other permissions:
- 4 or
s+u
: set-user-id : sets the program's effective user id to the owner of the program - 2 or
s+g
: set-group-id : sets the program's effective group id to the group of the program - 1 or
t
: the sticky bit : used to denote the memory loading of a program or directory
These bits are used in much the same way as the other permission
modes. For example, we can change the permission of our get_uidgid
program
from before like so:
chmod 6751 get_uidgid
And we can interpet the octals like so:
set group bits user | other | | | | V V V V 110 111 101 001 6 7 5 1
When we look at the ls -l
output of the program, the permission
string reflects these settings with an "s" in the execute part of the
string for user and group.
aviv@saddleback: lec-23-demo $ ls -l get_uidgid -rwsr-s--x 1 aviv scs 8778 Mar 30 16:45 get_uidgid
2.3 Real vs. Effective Capabilities
With the set-bits, when the program runs, the capabilities of the program are effectively that of the owner and group of the program. However, the real user id and real group id remain that of the user who ran the program. This brings up the concept of effective vs. real identifiers:
- real user id (or group id) : the identifier of the actual user who executed a program
- effective user id (or group id) : the idenifier for the capabilities or permissions settings of an executing program.
The system calls getuid()
and getgid()
return the real user and
group identifiers, but we can also retrieve the effective user and
group identifiers:
uid_t geteuid(void)
: return the effective user identifier for the calling processgid_t getegid(void)
: return the effective group identifer for the calling process
We now have enough to test set-bit programs using a the following program that prints both the real and effective user/group identities.
#include <unistd.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char * argv[]){ uid_t uid,euid; gid_t gid,egid; uid = getuid(); gid = getgid(); printf(" uid=%d gid=%d\n", uid, gid); euid = geteuid(); egid = getegid(); printf("euid=%d egid=%d\n", euid, egid); }
As the owner of the file, after compilation, the permissions can be set to add set-user-id:
aviv@saddleback: lec-23-demo $ make get_euidegid cc get_euidegid.c -o get_euidegid aviv@saddleback: lec-23-demo $ chmod u+s get_euidegid aviv@saddleback: lec-23-demo $ ls -l get_euidegid -rwsr-x--x 1 aviv scs 8730 Mar 31 08:31 get_euidegid
Now as the m179998 user, the program can be run, and we see that the
effective user id of the program is aviv
's id:
m179998@saddleback: ~ $ ~aviv/lec-23-demo/get_euidegid uid=179998 gid=15000 euid=35001 egid=15000 m179998@saddleback: ~ $ id uid=179998(m179998) gid=15000(mids) groups=15000(mids),15001(ic221) m179998@saddleback: ~ $ id aviv uid=35001(aviv) gid=10120(scs) groups=27(sudo),15000(mids),10120(scs),15001(ic221)
Continuing the example, we can see the other set-bit settings give different effective user/group settings:
aviv@saddleback: lec-23-demo $ chmod g+s get_euidegid aviv@saddleback: lec-23-demo $ ls -l get_euidegid -rwsr-s--x 1 aviv scs 8730 Mar 31 08:31 get_euidegid
m179998@saddleback: ~ $ ~aviv/lec-23-demo/get_euidegid uid=179998 gid=15000 euid=35001 egid=10120
aviv@saddleback: lec-23-demo $ ls -l get_euidegid -rwxr-s--x 1 aviv scs 8730 Mar 31 08:31 get_euidegid
m179998@saddleback: ~ $ ~aviv/lec-23-demo/get_euidegid uid=179998 gid=15000 euid=179998 egid=10120
And just to show how the effective user ID plays a role, let's consider what happens when we have set-group-id program that opens a file:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> int main(int argc, char * argv[]){ int i,fd; for(i=0;i<argc;i++){ //create an empty file if( (fd = open(argv[i],O_CREAT,0666) > 0) ){ close(fd); }else{ perror("open"); } } return 0; }
When we compile we can set this program to set-group-id:
aviv@saddleback: lec-23-demo $ make create_file cc create_file.c -o create_file aviv@saddleback: lec-23-demo $ chmod g+s create_file aviv@saddleback: lec-23-demo $ ls -l create_file -rwxr-s--x 1 aviv scs 8620 Mar 31 08:41 create_file
Now, let's create a file as the m179998
user:
m179998@saddleback: ~ $ ~aviv/lec-23-demo/create_file a b c m179998@saddleback: ~ $ ls -l a b c -rw-r----- 1 m179998 scs 0 Mar 31 08:42 a -rw-r----- 1 m179998 scs 0 Mar 31 08:42 b -rw-r----- 1 m179998 scs 0 Mar 31 08:42 c
Notice that the group of the file is scs
not mids
, which is the
default group.
However, see what happens if we make this program set-user-id instead:
aviv@saddleback: lec-23-demo $ chmod g-s create_file aviv@saddleback: lec-23-demo $ chmod u+s create_file aviv@saddleback: lec-23-demo $ ls -l create_file -rwsr-x--x 1 aviv scs 8620 Mar 31 08:41 create_file
m179998@saddleback: ~ $ rm -f a b c m179998@saddleback: ~ $ ~aviv/lec-23-demo/create_file a b c open: Permission denied open: Permission denied open: Permission denied m179998@saddleback: ~ $ ls -l a b c ls: cannot access a: No such file or directory ls: cannot access b: No such file or directory ls: cannot access c: No such file or directory
The operation is not permitted, and this is because the user aviv
does not have the permission to write to the directory. But, what if
we wanted to create a file in the director that is owned by aviv
in
a directory that the real user has permission to write to? What we
need is a way to programmatically change the capabilities.
2.4 Programmatically Downgrading/Upgrading Capabilities
The set-bits automatically start a program with the effective user or group id set; however, there are times when we might want to downgrade priviledge or change permission dynamically. There are two system calls to change user/group settings of an executing process:
setuid(uid_t uid)
: change the effective user id of a process touid
setgid(gid_t gid)
: change the effective group id of a proces togid
The requirements of setuid()
(for all users other than root) is that
the effective user id can be changed to the real user id of the
program or to an effective user id as described in the set-bits. The
root user, however, can downgrade to any user id and upgrade back to
the root user. For setgid()
the user can chance the group id to any
group the user belongs to or as allowed by the set-group-id bit.
Now we can look at a program that downgrades and upgrades a program dynamically:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(){ uid_t uid,euid; gid_t gid,egid; uid_t saved_euid; uid = getuid(); gid = getgid(); printf(" uid=%d gid=%d\n", uid, gid); euid = geteuid(); egid = getegid(); printf("euid=%d egid=%d\n", euid, egid); saved_euid=euid; setuid(uid); printf("---- setuid(%d) ----\n",uid); uid = getuid(); gid = getgid(); printf(" uid=%d gid=%d\n", uid, gid); euid = geteuid(); egid = getegid(); printf("euid=%d egid=%d\n", euid, egid); setuid(saved_euid); printf("---- setuid(%d) ----\n",saved_euid); uid = getuid(); gid = getgid(); printf(" uid=%d gid=%d\n", uid, gid); euid = geteuid(); egid = getegid(); printf("euid=%d egid=%d\n", euid, egid); }
If we look at the output with the program set-user-id ran by m179998
:
m179998@saddleback: ~ $ ~aviv/lec-23-demo/setuid uid=179998 gid=15000 euid=35001 egid=15000 ---- setuid(179998) ---- uid=179998 gid=15000 euid=179998 egid=15000 ---- setuid(35001) ---- uid=179998 gid=15000 euid=35001 egid=15000
3 sudo
and su
With this understanding of how user and group id are assigned, we can
turn our attention to two built in commands that perform these
actions. In particular, sudo
and su
which will execute a command
as a specified user or switch to a specified user. By default, these
commands execute as the root user, and you need to know the root
password or have sudo access to use them.
We can see this as the case if I were to run the get_euidegid
program using sudo
. First notice that it is no longer set-group or
set-user:
aviv@saddleback: lec-23-demo $ ls -l get_euidegid -rwxr-x--x 1 aviv scs 8730 Mar 31 08:31 get_euidegid aviv@saddleback: lec-23-demo $ sudo ./get_euidegid [sudo] password for aviv: uid=0 gid=0 euid=0 egid=0
After sudo authenticated me, the program's effective and real user identification becomes 0 which is the uid/gid for the root user.
3.1 sudoers
Who has permission to run sudo
commands? This is important because
on many modern unix systems, like ubuntu, there is no default root
password. Instead certain users are deemed to be sudoers
or
privileged users. These are set in a special configuraiton file called
the /etc/sudoers
.
aviv@saddleback: lec-23-demo $ cat /etc/sudoers cat: /etc/sudoers: Permission denied aviv@saddleback: lec-23-demo $ sudo cat /etc/sudo sudoers sudoers.d/ aviv@saddleback: lec-23-demo $ sudo cat /etc/sudoers # # This file MUST be edited with the 'visudo' command as root. # # Please consider adding local content in /etc/sudoers.d/ instead of # directly modifying this file. # # See the man page for details on how to write a sudoers file. # Defaults env_reset Defaults mail_badpass Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" # Host alias specification # User alias specification # Cmnd alias specification # User privilege specification root ALL=(ALL:ALL) ALL # Members of the admin group may gain root privileges %admin ALL=(ALL) ALL # Allow members of group sudo to execute any command %sudo ALL=(ALL:ALL) ALL # See sudoers(5) for more information on "#include" directives: #includedir /etc/sudoers.d
Notice that only root has access to read this file, and since I am a
sudoer on saddleback
I can get access to it. If you look carefully,
you can perform a basic parse of the settings. The root user has full
sudo permissions, and other sudoer's are determine based on group
membership. Users in the sudo or admin group may run commands as root,
and I am a member of the sudo group:
aviv@saddleback: lec-23-demo $ id uid=35001(aviv) gid=10120(scs) groups=10120(scs),27(sudo),15000(mids),15001(ic221)
However, on a lab machine, I do not have such group settings:
aviv@mich302csd01u: ~ $ id uid=35001(aviv) gid=10120(scs) groups=10120(scs),15000(mids)