Monday, September 2, 2013

Linux Capabilities

It had been for a long time that processes' permission on UNIX/Linux systems are differentiated into two categories, privileged or non-privileged processes. The effective user ID of privileged processes is 0 while that of non-privileged processes is nonzero. User ID 0 belongs to the superuser or root. Such a granularity were viewed as too coarse by many. Starting from Kernel 2.2,  Linux introduces the concept of capabilities that divides the privileges that traditionally associated with superuser into many categories. Linux manual page Capabilities has a good discussion on this topic.

Apparently,  many Linux programmers do  not seem to have a good understanding on this new development. Michael Kerrisk has statistics on the usage of different Linux capabilities. Perhaps, it is easier to get what you need by just assuming the privilege of the superuser than figuring out what you do not really need, which requires perhaps higher cognitive load and activity.

This post demonstrates a few usage of capabilities from an application programmer point of view. 

Packet Socket

Packet socket requires that the opening process has effective UID 0 or the CAP_NET_RAW capability. The following example program sends a message over an Ethernet.

#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netinet/ether.h>
#include <netpacket/packet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int sockfd;
    struct sockaddr_ll dest_addr;

    if (argc < 3) {
        printf("Usage: %s destination_host message\n", argv[0]);
        exit(0);
    }

    sockfd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));

    if (sockfd == -1) {
        perror("Error calling socket(AF_PACKET, SOCK_DGRAM ...): ");
        exit(1);
    }


    /* When you send packets it is enough to specify sll_family, sll_addr,
     * sll_halen, sll_ifindex. The other fields should be 0. */
    memset(&dest_addr, '\0', sizeof(dest_addr));

    dest_addr.sll_family = AF_PACKET;
    dest_addr.sll_ifindex = 1;
    dest_addr.sll_halen = ETH_ALEN;

    if (ether_aton_r(argv[1],
            (struct ether_addr*)&(dest_addr.sll_addr)) == NULL) {
        fprintf(stderr,
            "Error: %s is not in the hex-digits-and-colons format.\n",
            argv[1]);
    }

   if (sendto(sockfd, argv[2], strlen(argv[2]), 0,
            (struct sockaddr*)&dest_addr, sizeof(dest_addr)) == -1) {
        perror("Error calling sendto(...): ");
        exit(1);
    }

    printf("Info: packet sent successful\n");

    close(sockfd);
    return 0;
}

The program takes two command line arguments. The first argument is the Ethernet address of destination host and the second argument is the message to send.

When you run it as a non-privileged user, for instance, as follows,

        $ ./sendpacket 00:0c:29:89:7a:4d "Hello, World"

you would receive an "Operation not permitted" error,

       Error calling socket(AF_PACKET, SOCK_DGRAM ...): : Operation not permitted

Two methods that we can use to make it work. First, run it under root, the traditional method,

        $ sudo ./sendpacket 00:0c:29:89:7a:4d "Hello, World"
        Info: packet sent successful

A new method, which is a better and preferred method, is to give the program minimal but necessary privilege -- since the packet socket requires the program with CAP_NET_RAW privilege, we ought to give the program the privilege, but only the privilege.

However, before that, let us check what privilege the program has,


        $ /sbin/getcap ./sendpacket

It outputs nothing, which means the program does not any privilege. Now we can give the program the privilege by

        $ sudo /sbin/setcap cap_net_raw=ep ./sendpacket

Now check the program's privilege again,

        $ /sbin/getcap ./sendpacket
        ./sendpacket = cap_net_raw+ep

Now the output indicates that the program has its effective privilege set as  CAP_NET_RAW. Run the program again as a non-privileged user,

        $ ./sendpacket 00:0c:29:89:7a:4d "Hello, World"
        Info: packet sent successful


Notable Issues

When I tried to set capability for the program on a Virtual Machine, I received an error:

        Failed to set capabilities on file `./sendpacket' (Operation not supported)

This is because that the file system that the file was on is actually a VMWare HGFS that moutns a Windows NTFS. The Windows NTFS does not support the security capability. When I copied the file to an ext4 file system, the problem went away.

libcap Library

For programming Linux capabilities, you need the libcap library.

On Ubuntu,

          sudo apt-get install libcap-dev

On CentOS/Fedora Linux,

          sudo yum install libcap-devel


Reference and Further Reading

  1. http://www.cis.syr.edu/~wedu/seed/Labs/Documentation/Linux/How_Linux_Capability_Works.pdf
  2. http://man7.org/linux/man-pages/man7/packet.7.html
  3. http://www.linuxjournal.com/article/5737
  4. http://ols.fedoraproject.org/OLS/Reprints-2008/hallyn-reprint.pdf
  5. http://www.cis.gvsu.edu/~kalafuta/cis458/f12/labs/lab3.html

No comments:

Post a Comment