cmd/libsnap: add privilege elevation helpers #2852

Closed
wants to merge 1 commit into
from

Conversation

Projects
None yet
2 participants
Contributor

zyga commented Feb 14, 2017

This patch adds a small set of functions for working with privilege
elevation. Since snap-confine runs as root via the setuid-root mechanism
we always start with elevated permissions. For added security, througout
the execution of the process, permission are lowered and raised
(temporarily) as appropriate, to limit the extent of the possible damage
should the given code be compromised. Finally snap-confine permanently
lowers permissions before handing off to the next executable.

All this code was in snap-confine.c but now it is easier to reuse
elesewhere. The error messages have been cleaned up for clarity and
readability.

Signed-off-by: Zygmunt Krynicki zygmunt.krynicki@canonical.com

cmd/libsnap: add privilege elevation helpers
This patch adds a small set of functions for working with privilege
elevation. Since snap-confine runs as root via the setuid-root mechanism
we always start with elevated permissions. For added security, througout
the execution of the process, permission are lowered and raised
(temporarily) as appropriate, to limit the extent of the possible damage
should the given code be compromised. Finally snap-confine permanently
lowers permissions before handing off to the next executable.

All this code was in snap-confine.c but now it is easier to reuse
elesewhere. The error messages have been cleaned up for clarity and
readability.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>

The more I worked through this PR the less I liked how I imagined it would be used. For example, in the code we currently only drop and raise the uid but this PR would move that over to dropping/raising both. It's conceivable we'd want to drop just the gid sometimes. It is also tricky to keep track of the ruid, euid, and suid in a library in the ways that we are in the code when dropping and raising.

I propose instead that since all the current priv dropping code is simple, heavily audited and verified, we don't add all these helpers and instead add a single helper: sc_drop_privs() that drops setgroups(), gid and uid permanently (ie, lines 183-195 of snap-confine.c, but with dropping setgroups() before). Call that anywhere you want to permanently drop. This will allow you to use a library function to drop for the debugging output and string parsing you want to use this in.

+#include "utils.h"
+
+static uid_t real_uid;
+static gid_t real_gid;
@jdstrand

jdstrand Feb 14, 2017

Contributor

Let's not use static variables. Instead, put uid_t real_uid and gid_t real_gid in each function that needs them and call getuid()/getgid() or getresuid()/getresgid() as needed. The static variables allow for the possibility that the variables are not updated. Indeed, in this PR they aren't ever updated when raising or dropping.

+{
+ real_uid = getuid();
+ real_gid = getgid();
+}
@jdstrand

jdstrand Feb 14, 2017

Contributor

Feel free to drop this (see above).

+ real_gid = getgid();
+}
+
+void sc_privs_lower_permanently()
@jdstrand

jdstrand Feb 14, 2017

Contributor

The terminology is typically 'drop' and 'raise'. Please rename to 'sc_drop_privs_permanently()'.

+
+ if (getegid() == 0) {
+ // Note that we do not call setgroups() here because it is ok that the
+ // user keeps the groups he already belongs to.
@jdstrand

jdstrand Feb 14, 2017

Contributor

This isn't true for the die() scenario. If we are successful all the way through to exec(), then not calling setgroups() is ok, but we want to call setgroups() on error conditions.

I suggest:

  • create sc_drop_privs_permanently_no_setgroups() that is this function
  • create sc_drop_privs_permanently() that drops via setgroups() then calls sc_drop_privs_permanently_no_setgroups()
  • use sc_drop_privs_permanently() everwhere except in snap-confine.c line 182, where you replace the if clause with sc_drop_privs_permanently_no_setgroups()
+ }
+ if (real_gid != 0 && (getuid() == 0 || geteuid() == 0)) {
+ die("cannot permanently lower permissions (gid still elevated)");
+ }
@jdstrand

jdstrand Feb 14, 2017

Contributor

You changed the order. It is supposed to be, setgid(), setuid(), verify everything dropped correctly. With what you have here, you interleaved the verification and added 'lowered'. Please keep this simple. Take what is in snap-confine.c lines 182-195 as is. You don't need 'lowered' because failures should've die()d. Just debug() unconditionally at the end that you lowered.

+ }
+}
+
+void sc_privs_lower_temporarily()
@jdstrand

jdstrand Feb 14, 2017

Contributor

Please rename 'sc_drop_privs_temporarily()'.

+
+ if (lowered) {
+ debug("elevated permission have been temporarily lowered");
+ }
@jdstrand

jdstrand Feb 14, 2017

Contributor

All the same comments here as for above, except take lines 160-168 of snap-confine.c

+ }
+}
+
+void sc_privs_raise()
@jdstrand

jdstrand Feb 14, 2017

Contributor

I prefer 'sc_raise_privs' and 'sc_drop_privs_*', but if you prefer 'sc_privs_raise' and 'sc_privs_drop_*' I won't block.

+
+ if (real_gid != 0) {
+ if (setegid(0) != 0) {
+ die("cannot set effective group identifier to %d", 0);
@jdstrand

jdstrand Feb 14, 2017

Contributor

It's a little weird to use 'die("... %d", 0);' instead of just 'die("... 0");'

+
+ if (raised) {
+ debug("permissions have been elevated");
+ }
@jdstrand

jdstrand Feb 14, 2017

Contributor

All the same comments here as for above, except take lines 769-777 of seccomp-support.c and add setegid() and remove the 'raised' variable.

Contributor

zyga commented Feb 15, 2017

I read the inline comments and I just now read the main remark and I think you are spot on. We don't need the elaborate mechanism now, just the simple "drop everything now" call. I'll make this happen in a moment. I'm sorry for taking so long but I wasn't feeling well today and I didn't work for the fist half of the day much.

I'll close and re-open when I have this.

@zyga zyga closed this Feb 15, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment