syspriv.h 1.85 KB
Newer Older
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1 2
#ifndef _BIRD_SYSPRIV_H_
#define _BIRD_SYSPRIV_H_
3

Ondřej Zajíček's avatar
Ondřej Zajíček committed
4 5 6 7 8
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <unistd.h>
9 10 11 12 13 14 15 16
#include <sys/prctl.h>
#include <linux/capability.h>

#ifndef _LINUX_CAPABILITY_VERSION_3
#define _LINUX_CAPABILITY_VERSION_3  0x20080522
#define _LINUX_CAPABILITY_U32S_3     2
#endif

root's avatar
root committed
17 18 19 20 21
/* CAP_TO_MASK is missing in CentOS header files */
#ifndef CAP_TO_MASK
#define CAP_TO_MASK(x)      (1 << ((x) & 31))
#endif

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
/* capset() prototype is missing ... */
int capset(cap_user_header_t hdrp, const cap_user_data_t datap);

static inline int
set_capabilities(u32 caps)
{
  struct __user_cap_header_struct cap_hdr;
  struct __user_cap_data_struct cap_dat[_LINUX_CAPABILITY_U32S_3];
  int err;

  cap_hdr.version = _LINUX_CAPABILITY_VERSION_3;
  cap_hdr.pid = 0;

  memset(cap_dat, 0, sizeof(cap_dat));
  cap_dat[0].effective = cap_dat[0].permitted = caps;

  err = capset(&cap_hdr, cap_dat);
  if (!err)
    return 0;

  /* Kernel may support do not support our version of capability interface.
       The last call returned supported version so we just retry it. */
  if (errno == EINVAL)
  {
    err = capset(&cap_hdr, cap_dat);
    if (!err)
      return 0;
  }

  return -1;
}

static void
drop_uid(uid_t uid)
{
  u32 caps =
    CAP_TO_MASK(CAP_NET_BIND_SERVICE) |
    CAP_TO_MASK(CAP_NET_BROADCAST) |
    CAP_TO_MASK(CAP_NET_ADMIN) |
    CAP_TO_MASK(CAP_NET_RAW);

63 64
  /* change effective user ID to be able to switch to that
     user ID completely after dropping CAP_SETUID */
65 66 67
  if (seteuid(uid) < 0)
    die("seteuid: %m");

68
  /* restrict the capabilities */
69 70 71
  if (set_capabilities(caps) < 0)
    die("capset: %m");

72
  /* keep the capabilities after dropping root ID */
73 74 75
  if (prctl(PR_SET_KEEPCAPS, 1) < 0)
    die("prctl: %m");

76
  /* completely switch to the unprivileged user ID */
77 78 79
  if (setresuid(uid, uid, uid) < 0)
    die("setresuid: %m");
}
Ondřej Zajíček's avatar
Ondřej Zajíček committed
80 81

#endif /* _BIRD_SYSPRIV_H_ */