Ethereal-dev: [Ethereal-dev] Running Ethereal as an unprivileged user

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Richard Urwin <richard@xxxxxxxxxxxxxxx>
Date: Wed, 10 Sep 2003 23:40:06 +0100
Here is a C program that demonstrates using Linux (POSIX.1e)
capabilities to run as a normal user but keeping certain privilages.

It runs, as can be seen below, under a standard MDK9.1 kernel. In fact
it should work under any kernel from 2.2.19 onward.

I propose that we add this functionality into Ethereal, when built on
Linux, and when libcap and the kernel headers are available.
(captest.c calls the kernel directly so you wont need libcap to try it,
but that is discouraged.)

Example output from captest.c:-
--------------------------------------------------------------------------------------------------
I am running under this Linux version:
Linux version 2.4.21-0.13mdk (flepied@xxxxxxxxxxxxxxxxxxx) (gcc version 3.2.2 (Mandrake Linux 9.1 3.2.2-3mdk)) #1 Fri Mar 14 15:08:06 EST 2003

This is how a setuid(root) process is started:
UID=501 EffUID=0
PR_SET_KEEPCAPS=0
cap CAP_NET_RAW = effective SET, permitted SET, inheritable CLEAR
cap CAP_NET_ADMIN = effective SET, permitted SET, inheritable CLEAR
cap CAP_SYS_ADMIN = effective SET, permitted SET, inheritable CLEAR

First we call prctl(PR_SET_KEEPCAPS, 1) to indicate that we are
capability-aware and want to keep capabilities over a change of uid
Now PR_SET_KEEPCAPS=1

Then we clear all capabilities except the ones we need:
Set just CAP_NET_RAW, CAP_NET_ADMIN.
cap CAP_NET_RAW = effective SET, permitted SET, inheritable CLEAR
cap CAP_NET_ADMIN = effective SET, permitted SET, inheritable CLEAR
cap CAP_SYS_ADMIN = effective CLEAR, permitted CLEAR, inheritable CLEAR

Then we swap down to the uid of the invoking user. The effective capabilities
are cleared, but we are still permitted to set the ones that we kept.
As user: UID=501 EffUID=501
cap CAP_NET_RAW = effective CLEAR, permitted SET, inheritable CLEAR
cap CAP_NET_ADMIN = effective CLEAR, permitted SET, inheritable CLEAR
cap CAP_SYS_ADMIN = effective CLEAR, permitted CLEAR, inheritable CLEAR

Finally we add back the capabilities that we need
added CAP_NET_ADMIN, CAP_NET_RAW to effective.
cap CAP_NET_RAW = effective SET, permitted SET, inheritable CLEAR
cap CAP_NET_ADMIN = effective SET, permitted SET, inheritable CLEAR
cap CAP_SYS_ADMIN = effective CLEAR, permitted CLEAR, inheritable CLEAR

Forking keeps the same capabilities for the child process:
cap CAP_NET_RAW = effective SET, permitted SET, inheritable CLEAR
cap CAP_NET_ADMIN = effective SET, permitted SET, inheritable CLEAR
cap CAP_SYS_ADMIN = effective CLEAR, permitted CLEAR, inheritable CLEAR
--------------------------------------------------------------------------------------------------------


-- 
Richard Urwin
/* This program demonstrates the use of POSIX.1e capabilities in stock Linux
 * distributions, with a view to adding the same functionality to Ethereal.
 * Note that this code requires the kernel headers, but not the libcap library.
 * for the real version we should use libcap rather than calling the kernel directly.
*/

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <sys/prctl.h>
#include <linux/unistd.h>
#include <linux/capability.h>

_syscall2(int, capget, cap_user_header_t, header, cap_user_data_t, dataptr);
_syscall2(int, capset, cap_user_header_t, header, cap_user_data_t, dataptr);

typedef struct __user_cap_header_struct capheader_t;
typedef struct __user_cap_data_struct capdata_t;

char * isbit (__u32 word, int bit, char * buffer)
{
  sprintf (buffer, word & (1<< bit) ?"SET": "CLEAR");
  return buffer;
}

char * describe_cap (capdata_t *data, int cap)
{
  static char buffer[80];
  static char buffer1[10], buffer2[10],buffer3[10];

  sprintf (buffer, "effective %s, permitted %s, inheritable %s",
          isbit(data->effective,cap, buffer1),
          isbit(data->permitted,cap, buffer2),
          isbit(data->inheritable,cap, buffer3));
  return buffer;
}

void remove_all(capdata_t *data) {
  data->effective = 0;
  data->permitted = 0;
  data->inheritable = 0;
}

void add_cap(capdata_t *data, int cap) {
  data->effective |= (1 << cap);
  data->permitted |= (1 << cap);
}

void add_cap_eff(capdata_t *data, int cap) {
  data->effective |= (1 << cap);
}

void cap_get(capheader_t *header, capdata_t *data) {
  if (capget(header, data) == 0) return;
  perror("capget");
  exit(-1);
}

void cap_set(capheader_t *header, capdata_t *data) {
  if (capset(header, data) == 0) return;
  perror("capset");
  exit(-1);
}

main() {
  capheader_t header;
  capdata_t data;

  if (geteuid() != 0) {
    printf("Run me as setuid root please\n");
    exit(1);
  }
  
  if (getuid() == 0) {
    printf ("Run by root, so we shouldn't do anything\n");
    exit (0);
  }
 
  header.version = _LINUX_CAPABILITY_VERSION;
  header.pid = 0;
  data.effective = data.permitted = data.inheritable = 0;
  
  printf ("\nI am running under this Linux version:\n");
  system ("cat /proc/version");
  printf ("\nThis is how a setuid(root) process is started:\n");
  printf("UID=%d EffUID=%d\n", getuid(), geteuid());
  printf("PR_SET_KEEPCAPS=%d\n", prctl(PR_GET_KEEPCAPS));
  cap_get(&header, &data);
  printf ("cap CAP_NET_RAW = %s\n", describe_cap (&data, CAP_NET_RAW));
  printf ("cap CAP_NET_ADMIN = %s\n", describe_cap (&data, CAP_NET_ADMIN));
  printf ("cap CAP_SYS_ADMIN = %s\n\n", describe_cap (&data, CAP_SYS_ADMIN));

  
  printf ("%s\n%s\n",
          "First we call prctl(PR_SET_KEEPCAPS, 1) to indicate that we are capability",
          "aware and want to keep capabilities over a change of uid");

  if (prctl(PR_SET_KEEPCAPS, 1) < 0)
  {
    perror ("prctl");
    exit(1);
  }

  printf("Now PR_SET_KEEPCAPS=%d\n\n", prctl(PR_GET_KEEPCAPS));
  

  
  printf ("Then we clear all capabilities except the ones we need:\n");

  remove_all(&data);
  add_cap(&data, CAP_NET_RAW);
  add_cap(&data, CAP_NET_ADMIN);
  cap_set(&header, &data);

  printf ("Set just CAP_NET_RAW, CAP_NET_ADMIN.\n");
  
  cap_get(&header, &data);
  printf ("cap CAP_NET_RAW = %s\n", describe_cap (&data, CAP_NET_RAW));
  printf ("cap CAP_NET_ADMIN = %s\n", describe_cap (&data, CAP_NET_ADMIN));
  printf ("cap CAP_SYS_ADMIN = %s\n\n", describe_cap (&data, CAP_SYS_ADMIN));


  
  printf ("%s\n%s\n",
        "Then we swap down to the uid of the invoking user. The effective capabilities",
        "are cleared, but we are still permitted to set the ones that we kept.");

  setuid(getuid());

  printf("As user: UID=%d EffUID=%d\n", getuid(), geteuid());
  cap_get(&header, &data);
  printf ("cap CAP_NET_RAW = %s\n", describe_cap (&data, CAP_NET_RAW));
  printf ("cap CAP_NET_ADMIN = %s\n", describe_cap (&data, CAP_NET_ADMIN));
  printf ("cap CAP_SYS_ADMIN = %s\n\n", describe_cap (&data, CAP_SYS_ADMIN));
  
 
 
  printf ("Finally we add back the capabilities that we need\n");

  add_cap_eff(&data, CAP_NET_RAW);
  add_cap_eff(&data, CAP_NET_ADMIN);
  cap_set(&header, &data);
 
 printf ("added CAP_NET_ADMIN, CAP_NET_RAW to effective.\n");

 cap_get(&header, &data);
 printf ("cap CAP_NET_RAW = %s\n", describe_cap (&data, CAP_NET_RAW));
 printf ("cap CAP_NET_ADMIN = %s\n", describe_cap (&data, CAP_NET_ADMIN));
 printf ("cap CAP_SYS_ADMIN = %s\n\n", describe_cap (&data, CAP_SYS_ADMIN));
 
 switch (fork())
 {
   case 0:
     printf ("Forking keeps the same capabilities for the child process:\n");
     cap_get(&header, &data);
     printf ("cap CAP_NET_RAW = %s\n", describe_cap (&data, CAP_NET_RAW));
     printf ("cap CAP_NET_ADMIN = %s\n", describe_cap (&data, CAP_NET_ADMIN));
     printf ("cap CAP_SYS_ADMIN = %s\n\n", describe_cap (&data, CAP_SYS_ADMIN));
     exit (0);
   
   case -1:
     perror ("fork failed");
     break;
     
   default:
     wait (NULL);
     break;
 }
}