[Home] [Credit Search] [Category Browser] [Staff Roll Call] | The LINUX.COM Article Archive |
Originally Published: Wednesday, 20 October 1999 | Author: Jim Hewlett |
Published to: enchance_articles_security/Advanced Security Articles | Page: 1/1 - [Std View] |
Linux Capabilites
|
Inheritable, effective, and permitted.
Permitted contains everything that the current process is allowed to do. Effective contains everything that the current process can currently do. Inheritable tells us which processes we will allow to gain caps from us. Each of those sets may contain multiple capabilities. If you're permitted to do everything, but your effective set is empty, you can't really do anything special.
However, you as a process can set your effective set to include anything in your permitted set. It's like an on/off set. As example lets use named. I set cap_net_bind_service+ep and no i set. Now named itself can bind to low ports (cap_net_bind_service), and that is set to the effective and permitted (+ep). Now, anything exec()'d by named has no privs at all. This is because we did not set the inheritable (remember, that's i).
In a capabilities only model, init is started with =eip, which means it has a full set of everything in all 3 sets. From there you need a capabilities aware init to pass caps to what needs them, or file system information to do it. Since neither exist right now, the kernel has some compatibility mode stuff in it (remember that Linux Capabilities are still a work in progress).
When exec() is called, if your effective id is 0 (aka suid, or actually root) your effective and permitted set is set to what your inheritable set was when you called exec(). If your effective id is not 0, your effective and permitted sets are cleared.
Example:
~[zeppelin(imagine-dragon)1]$: ps PID TTY TIME CMD 23540 tty5 00:00:00 bash 23528 tty5 00:00:00 psThis is a normal user:(shell is pid 23540)
~[zeppelin(imagine-dragon)3]$: /sbin/getpcaps 23540 Capabilities for `23540': =i Capabilities for `(null)': =iThe following is normal for root. I had a full inheritable set, so when euid==0 is true, the kernel set my effective and permitted sets to match my inheritable:
~[zeppelin(imagine-dragon)4]$: su - [~(root@imagine-dragon)1]#: /sbin/getpcaps 23540 Capabilities for `23540': =i Capabilities for `(null)': =eipExit the root shell, rerun command:
~[zeppelin(imagine-dragon)5]$: /sbin/getpcaps 23540 Capabilities for `23540': =i Capabilities for `(null)': =iNow I don't have anything in my permitted set, so I can't change my own caps. Swap over to root console:
[~(root@imagine-dragon)120]#: setpcaps cap_setuid,cap_setgid=i 23540 [caps set to:= cap_setgid,cap_setuid+i] [caps set on 23540]Back to the normal user and rerun getpcaps:
~[zeppelin(imagine-dragon)6]$: /sbin/getpcaps 23540 Capabilities for `23540': = cap_setgid,cap_setuid+i Capabilities for `(null)': = cap_setgid,cap_setuid+iYou can restrict caps like that. So I can't actually setuid or setgid as zeppelin now, because it's not in my effective set. I'm saying only inherit those 2 capabilities. now, I su:
~[zeppelin(imagine-dragon)7]$: su - su: fchown to STDIN: Operation not permitted /dev/tty5: Operation not permittedKernel only could set what I could inherit. Right now I have a root shell, but I can only set uid/gid.[~(root@imagine-dragon)1]#: /sbin/getpcaps 23540 Capabilities for `23540': = cap_setgid,cap_setuid+i Capabilities for `(null)': = cap_setgid,cap_setuid+eip
With every good thing, there is something that isn't so. Compatibility mode has some quirks.
[~(root@imagine-dragon)121]#: setpcaps =eip 23540 [caps set to:=eip] [caps set on 23540]I set the normal user's shell to have all capabilities and that shell can do anything now. Not so! Consider the following:
~[zeppelin(imagine-dragon)8]$: /sbin/getpcaps 23540 Capabilities for `23540': =eip Capabilities for `(null)': =iThe second zeppelin runs an external program, compatibility mode drops the caps. So the question is, how can I cap named? If I setuid first then I lose the caps to set my caps. If I set my caps and then setuid, I have to give myself the setuid cap, and then setuid and lose my caps. That's where our good friend Mister Wizard comes in (no, not the Nickelodeon wizard. Although, he's pretty neat too.). He has modified libcap's execcap program to do a nifty trick. It spawns a root process, which therefore has all caps. The main process setuid's and therefore loses all caps. The spawned process sets the main process's caps back to god mode and dies. The main process now has god caps, under a normal user. Then the main process drops its own caps to what they should be. All his mods set the inheritable set to null, so that if someone breaches named (In my examples it is chrooted. See Peter Clark's article on doing this), they don't have:
Now some code to show how the modification is done to our example above (named). The + (plus) signs are what was added to the existing code. Don't forget to add #include <sys/capability.h> to the top of the file which contains the main() function (ns_main.c for named) with the other #includes and use -lcap when linking
Named binds to a privileged port, so it needs cap_net_bind_service. In named's main procedure it chroots to whatever dir you gave it. Then it does setuid() and setgid() and continues to run. Let's modify where it does setuid and setgid.
These variables are defined for later use:
+ int uidtoset=getuid(),gidtoset=getgid(); + cap_t cap_d; + cap_value_t cap_values[]={ + CAP_NET_BIND_SERVICE + };
Now call libcap's init proc:
+ cap_d = cap_init(); + if (!cap_d) { + ns_panic(ns_log_security, 1, "Could not allocate capabilities struct: %s", + strerror(errno)); + }
Now we figure out what uid and gid we need to set to (different for each program. group_ and user_ stuff is named specific.):
+ if (group_name != NULL) { + gidtoset=group_id; + ns_info(ns_log_security, "group = %s", group_name); + } + if (user_name != NULL) { + if (getuid() == 0 && initgroups(user_name, group_id) < 0) + ns_panic(ns_log_security, 1, "initgroups(%s, %d): %s", + user_name, (int)group_id, strerror(errno)); + endgrent(); + endpwent(); + uidtoset=user_id; + ns_info(ns_log_security, "user = %s", user_name); + }
Now that a proper uidtoset and gidtoset are setup and all the supplemental groups are set, we get to the fun stuff. Starting with setting the structure to null for inherited, effective and permitted:
+ cap_clear(cap_d);
Set CAP_NET_BIND_SERVICE for permitted and effective (the 1 value that was defined in cap_values array):
+ cap_set_flag(cap_d, CAP_PERMITTED, 1, cap_values, CAP_SET); + cap_set_flag(cap_d, CAP_EFFECTIVE, 1, cap_values, CAP_SET);
Now for the nifty function (cap_set_me_up) Mister Wizard made that gets around losing caps when you change UID/GID which was explained earlier:
+ ns_info(ns_log_security, "setting capabilities"); + if (cap_set_me_up(uidtoset,gidtoset,cap_d)) { + ns_panic(ns_log_security, 1, "cap_set_me_up failed: %s", + strerror(errno)); + }
Now named capped, time to clean up:
+ ns_info(ns_log_security, "capability struct set"); + cap_free(&cap_d); + }
Well there you have it. Named should compile and run capped. Test it out with getpcaps (comes with libcap).
(from ps):
named 30479 0.0 1.4 2988 1876 ? S Oct02 0:12 named -u named -g named -t /home2/services/named -s
You will see something like:
boboshrimps(root):~# getpcaps 30479 Capabilities for `30479': = cap_net_bind_service+ep Capabilities for `(null)': =eip
You'll need Mister Wizard's modified version to get around the cap quirk you read about earlier. Get it here. If there are any problems reaching the site, let me know.
One last modification needs to be done before all of this will actually work. This involves some kernel changes. Yes, I know. I can hear you all screaming about your uptimes. Change in linux/include/linux/capability.h:
#define CAP_INIT_EFF_SET to_cap_t(~0 & ~CAP_TO_MASK(CAP_SETPCAP)) #define CAP_INIT_INH_SET to_cap_t(~0 & ~CAP_TO_MASK(CAP_SETPCAP))
to:
#define CAP_INIT_EFF_SET { ~0 } #define CAP_INIT_INH_SET { ~0 }
Thus giving init a full set of caps. Compile and reboot. Currently for Linux 2.2 we have to do all these mods to daemons/whatever in order to use caps. However, Linux may eventually have file system support, so there will be no need for hacking up code.
Lastly, but most importantly, I'd like to thank Mister Wizard for letting me borrow his gray matter.
Comments, suggestions, or additions are welcome. Just point them to jh@linux.com
Jim Hewlett is a freelance UNIX Systems Administrator with over 4 years of experience maintaining machines and networks throughout the south-east.