Why are not all permitted capabilities of a linux process effective all the time

capabilitiesSecurity

man 7 capabilities documents that the capabilities of a process on a linux box are recorded as three masks:

  • permitted
  • effective
  • inheritable

I have an idea to what extent the inheritable mask would come to play but I am unclear about why there seems to be a need/use case to separate capabilities that are permitted from those that are effective?

Is there a case where some permitted capabilities are not effective? Which could spice up an answer to this question?

Bonus round

Given the case some Capabilities are not effective and yet permitted, what keeps a process from setting them effective? It would seem to me at least that a rogue process would not hesitate to set all what is permitted as effective, and normally even attempt to escalate privileges further?

Best Answer

The difference between effective/permitted capabilities is similar to the difference between real/effective UIDs in setuid programs. The idea isn't to stop a rogue app from escalating privs (you wouldn't grant them privs in the first place, same as you wouldn't setuid them) but to allow a program to run with minimal privileges and only escalate where necessary. This helps minimize the impact of bugs

A very contrived example: I want to have a program that will let me send a SIGHUP to processes owned by the user or to allow a God user to send SIGHUP to init.

This program has the CAP_KILL capability set on the file.

The pseudo code might look something like:

drop_effective CAP_KILL
repeat forever:
  get_process_id_from user
  if process_id==1 and user_is_God:
    set_effective CAP_KILL
    kill(-1,1)
    drop_effective CAP_KILL
 else:
   kill(-1,process_id)

The obvious bug, here, is that I don't check that the user is allowed to send the signal in the first place. Because I've dropped the effective CAP_KILL permission I won't be allowing the user to kill processes other than their own.

Very contrived, for sure! But the idea is to run as far as possible with "least privileges" and only enable privileges when necessary.

Now this won't necessarily protect against buffer overflow attacks because the injected code could enable permitted privileges, so capability aware code should also drop permitted privileges once they are no longer needed; eg a webserver might drop CAP_NET_BIND_SERVICE after it has bound to port 80. You can't enable something not in your permitted set!

Related Question