Safer set-uid programs in Ada with the suid-helper library 
Tags: setuidprogram, safety, ada
Published: 2020-02-22 19:03:35

---
title: Safer set-uid programs in Ada with the suid-helper library 
published: true
description: 
tags: set-uid program, safety, Ada
---

# Set-uid programs: the good, the bad and the risky

In *nix systems access control is traditionally often done by checking if the user requesting a given operation has read/write/execution privilege with respect to a given file/directory. 

In some cases this policy is not fine-grained enough.  An example is the common `passwd` that allows the user to change its password. Changing the password requires modifying the file `/etc/passwd` that stores the passwords (to be honest, this was true on older systems, nowadays things are a bit different, but we stick to this for the sake of the example).  This causes a problem with the permission accesses of `/etc/passwd`: the file must be clearly not writable by the ordinary user, but... how could the user change its password?

The solution is allow the user to modify `/etc/passwd` in a controlled way, via an executable that will change only the user's password.  In other words, the user must gain temporally limited root privileges (_administrator privileges_ for you Window people 😄). This is done with the idea of __set-user-ID executable__. If an executable is marked as _setuid_, the user running the program gets the privileges of the _owner_ of the executable (usually root).  In this way the program can carry out privileged actions on behalf of the user.


## The three identities 

We need a bit of more detail.  In current *nix systems a running executable has 3 identities
* The **real** user ID.  This is the user that is _running_ the code.
* The **effective** user ID.  This is the identity that is used to check the accesses.  In a normal program this is equal to the real user ID, but in a _setuid_ program it is initialized to the identity of the _owner_ of the executable.
* The **saved** user ID.  This is initialized with the effective user ID; therefore, in a normal program it is equal to the real UID, while in a _setuid_ program is equal to the ID of the _owner_.

How do these IDs interact?  It is really simple: 

> The program can change its *effective* ID to its *real* or *saved* ID.

This means that a normal program starts with the effective ID equal to the identity of the user and it cannot be changed; while a _setuid_ program can set it to the *real* identity (we will say that it *drops* the privileges) or to the identity of the owner (we will say that it *restores* the privileges).  It is possible for the program to _definitively drop_ the privileges by setting the saved ID to the real ID.

## Good practices in `setuid` codes

Of course a _setuid_ program is a potential security risk because of this _privilege escalation_ provided by the _setuid_ mechanism. Sure, if the program was correctly written, without weakness nor bugs, it would be safe to have _setuid_ code, but... You know... We are all human beings and a defensive approach to limit the potential damage it is not a bad idea.  We will consider two simple good practices

1. __Drop the privileges__ as soon as possible, restoring them only when necessary.
2. Consider the __environment variables__ as _tainted_ since their value is under the control of the user.  For example, the user could set the `PATH` variable to force the code to execute programs under the control of the user. It would be better not using environment variables at all, but if you really need _replace_ the variable values with trusted values (e.g. write in `PATH` a list of trusted directory) or, at least, _sanitize_ the value given by the user.

# What is `setuid-helper` and how can it help me?

[`setuid-helper`](https://gitlab.com/mockturtle/suid-helper) is a small Ada library that provides a package `Setuid.Helper` designed to help following the above guidelines for _setuid_ programs.

During the package initialization (_elaboration_ in Ada jargon) the package does three things

1. It saves the environment variables received from the user
2. It deletes every environment variable 
3. It drops the privileges

This means that when the first instruction of the main is executed, the program runs with the privileges dropped and the environment empty.  The only way to restore the privileges is by using the procedure `With_Privileges_Do`.  This procedure exists in different _flavors_ that differ in how the action to be done is specified.

In the simplest case 
```Ada
procedure With_Privileges_Do (Callback         : access procedure;
                              Drop_Permanently : Boolean := True);

```
the procedure `With_Privileges_Do` expects a `Callback` parameter represented by an access (a _pointer_ for you `C` people 😄) to a parameter-less procedure. The semantic is really simple: the privileges are restored, `Callback` called and the privileged are dropped again.  Unless the second parameter is `False`, the privileges are permanently dropped and it will not be possible to restore them again. Note that permanent dropping is the default.

This is a typical usage example where the callback is defined _locally_ inside a `declare` block

```Ada
   declare
      procedure Unmount_Callback is
      begin
         Unmount (Mountpoint_Name);
      end Unmount_Callback;
   begin
      With_Privileges_Do (Callback         => Unmount_Callback'Access,
                          Drop_Permanently => True);
   end;
```

> There are two other versions of `With_Privileges_Do`: one expects a _handler_ object (that can store a status), the other expects a function and it can return a value. See [the `.ads` file](https://gitlab.com/mockturtle/suid-helper/-/blob/master/src/suid-helper.ads) and the [README file](https://gitlab.com/mockturtle/suid-helper/-/blob/master/README.md) for more details.


## Managing the environment

As said before, it is maybe better not using environment variables at all.  This is not always possible and `setuid.helper` provides a way to manage it as safely as possible, keeping two different environments: one normal and one privileged.

 More precisely, the package mantains three "enviroments"

1. The _tainted_ environment.  This is a copy of the original environment. It cannot be changed, but it can be read with the functions `Tainted_Variable`. This allows to import in a controlled way the values provided by the user in the environment.
2. The _user_ environment.  This is used when the privileges are dropped. It is initially empty and it can be written with `Set_User_Environment`.
3. The _safe_ environment used when privileges are on. It is initially empty and it can be written with `Set_Safe_Environment`.  

Procedure `Set_Safe_Environment`  expects the value to be of type `Safe_Value`, not `String`.  A string can be converted to a `Safe_Value` by calling the function `Bless`. 

> Just to be clear: it is expected that the value given to `Bless` is checked or sanitized, but nothing prevents the programmer to call `Bless` with any unsafe value. The usage of a different type is to avoid involuntary short-circuits where a value of the original environment (to be read with `Tainted_Variable`, _nomen omen_...) is given to  `Set_Safe_Environment`

# Portability, installation, ...

Just check the beginning of the [README file](https://gitlab.com/mockturtle/suid-helper/-/blob/master/README.md). I work with Linux and in Linux it works; I guess it should work with any modern *nix (if it works for you under another *nix please let me know).

What about Windows?  Honestly, I do not know much Windows, but I am afraid that the idea of the _setuid bit_ is very *nix-ish.  Is maybe possible to bring the same idea to Windows?  Well, let me know, never say never...

 
# Conclusion

`setuid-helper` is a pretty young library (currently is 0.1.0 since I am not sure about the stability of its API).  Any feedback is welcome.