Originally Published: Thursday, 2 December 1999 Author: Joseph Gooch
Published to: develop_articles/Development Articles Page: 1/1 - [Printable]

WizMods Visit Network Tools

This week we've got another great article from Wizard. He has outlined how and why he changed some popular network connectivity testing tools to understand Linux Capabilities.

   Page 1 of 1  

Continuing in the WizMods series, this article will outline my changes to ewping, ewtraceroute, and fping. Ask yourself, "Why in the world would I want normal users running a suid-root executable for normal network probing?" It's absurd. So, I addressed that problem in this mod. Traceroute and ping are very similar in that they both use a raw socket for data (A raw socket is used to send and receive custom packets. It is needed to send and receive the ICMP packets that ping and traceroute require.). It follows that the capability that ping and traceroute require is called CAP_NET_RAW (capability to use raw sockets). Yes, just one measly capability out of the 26 that the Linux kernel suports.

So now you're wondering "What is this ewping that you speak of?" Ewping and ewtraceroute are versions of ping and traceroute written by Eric Wassenaar (e07@nikhef.nl). I like his tools better -- the output looks better and it has more nifty options than stock pings. From looking at the code, ewtools also look more secure. It drops suid immediately after the socket() call, so there isn't much room for an exploit. It's a good example to use as a template for adding capabilities to other programs, so I'll walk you through it. As for fping, this tool ping multiple targets simultaneously, and thus is much more efficient for use with network monitoring programs.

On to the coding! Here are the pieces that I've added to the source.

First, we have the obligatory include:


#ifdef CAPABILITIES
#include <sys/capability.h>
#endif

That include file comes with libcap. If you haven't downloaded my libcap yet, you're wrong. The url's in my initial article along with an explanation.

Moving on with the explanations, here's the snippet of code that I put as early in main() as possible:

/*
 * Let's drop caps before anything else
 */
#ifdef CAPABILITIES
        {
                int uidtoset=getuid();
                int gidtoset=getgid();
                int result=0;
                cap_t cap_d=cap_init();
                cap_value_t cap_values[]={
                        CAP_NET_RAW
                };

                if (!cap_d) {
                        fprintf(stderr, "Could not alloc cap struct\n");
                        exit(-1);
                }

                cap_clear(cap_d);
                cap_set_flag(cap_d, CAP_PERMITTED, 1, cap_values, CAP_SET);
                cap_set_flag(cap_d, CAP_EFFECTIVE, 1, cap_values, CAP_SET);
                if (uidtoset) {
                        /* Kloobie ran us, switch to them */
                        result=cap_set_me_up(uidtoset,gidtoset,cap_d);
                } else {
                        /* Root ran us, why attempt to setuid? */
                        result=cap_set_proc(cap_d);
                }
                if (result) {
                        fprintf(stderr, "Could not set capabilities: %s", strerror(errno));
                        exit(1);
                }
                cap_free(&cap_d);
        }
#endif

First, I call and save the results of getuid() and getgid(). Since these utilities run as setuid root, this call returns the actual user that ran the program. We'll change to their user later. Then, we call cap_init(). This call is part of libcap; cap_init() allocates a structure that we'll use to define which capabilities we need. Then I initialize the array of values. These utilities only use one capability, the most I've ever given a program was 3. Then we make sure cap_init() gave us a structure, if not, exit before we segfault. Clear all capabilities from the struct with cap_clear(), then set the effective and permitted sets of cap_d to include CAP_NET_RAW. The inherited set stays empty so any process we spawn doesn't have any caps. Now, if getuid() returned non-zero, we run cap_set_me_up(), which sets our caps and calls setuid() and setgid() safely all at once. If getuid() returned 0, we're root, so we'll just drop our caps to an acceptible point. It's not likely that root will try to exploit itself. We check that the call succeeded, and then free our capability struct.

Now we let ping initialize. Ping calls socket() and gets a raw socket for its use. It binds the socket to a specific source IP if that was requested, then it calls setuid(getuid()). In a non-capabilities model, this would drop its privileges, but since we are already that user, we'll drop our excessive privileges ourself, with this snippet:

#ifdef CAPABILITIES
        {
                cap_t cap_d=cap_init();

                if (!cap_d) {
                        perror("cap struct alloc");
                        exit (-1);
                }
                cap_clear(cap_d);
                if (cap_set_proc(cap_d)) {
                        perror("Could not drop caps");
                        exit (-1);
                }
                cap_free(&cap_d);
        }
#endif

This code gets our cap_d struct and checks it for sanity, then clears all capabilties in it. Then we call cap_set_proc() to assign the capabilities to ourself, and clean up. Ping no longer has any excessive privileges. We've accomplished our goal!

As for fping and ewtraceroute, they're pretty much the same procedure as here. Each utility will allocate a socket and drop privileges similarly to ewping, so adding the capabilities code is the same. Have fun, happy coding, see you next time.

Ewping Source RPM

Ewtraceroute Source RPM

Fping Source RPM

Address all comments, suggestions, or questions to mrwizard@linux.com. I'll try to answer all I can.

Joseph Gooch is a freelance Systems Administrator with over 6 years of experience with Windows 9x, NT, Netware, and Linux machines. He also has experience as a Network Engineer and a Programmer. He is currently attending Penn State University working on his Computer Science degree.





   Page 1 of 1