/*
 * Copyright (c) 1997,2020 Andrew G Morgan <morgan@kernel.org>
 *
 * This file contains internal definitions for the various functions in
 * this small capability library.
 */

#ifndef LIBCAP_H
#define LIBCAP_H

#include <errno.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/capability.h>

#ifndef __u8
#define __u8    uint8_t
#endif /* __8 */

#ifndef __u32
#define __u32   uint32_t
#endif /* __u32 */

/* include the names for the caps and a definition of __CAP_BITS */
#include "cap_names.h"

#ifndef _LINUX_CAPABILITY_U32S_1
# define _LINUX_CAPABILITY_U32S_1          1
#endif /* ndef _LINUX_CAPABILITY_U32S_1 */

/*
 * Do we match the local kernel?
 */

#if !defined(_LINUX_CAPABILITY_VERSION)

# error Kernel <linux/capability.h> does not support library
# error file "libcap.h" --> fix and recompile libcap

#elif !defined(_LINUX_CAPABILITY_VERSION_2)

# warning Kernel <linux/capability.h> does not support 64-bit capabilities
# warning and libcap is being built with no support for 64-bit capabilities

# ifndef _LINUX_CAPABILITY_VERSION_1
#  define _LINUX_CAPABILITY_VERSION_1 0x19980330
# endif

# _LIBCAP_CAPABILITY_VERSION  _LINUX_CAPABILITY_VERSION_1
# _LIBCAP_CAPABILITY_U32S     _LINUX_CAPABILITY_U32S_1

#elif defined(_LINUX_CAPABILITY_VERSION_3)

# if (_LINUX_CAPABILITY_VERSION_3 != 0x20080522)
#  error Kernel <linux/capability.h> v3 does not match library
#  error file "libcap.h" --> fix and recompile libcap
# else
#  define _LIBCAP_CAPABILITY_VERSION  _LINUX_CAPABILITY_VERSION_3
#  define _LIBCAP_CAPABILITY_U32S     _LINUX_CAPABILITY_U32S_3
# endif

#elif (_LINUX_CAPABILITY_VERSION_2 != 0x20071026)

# error Kernel <linux/capability.h> does not match library
# error file "libcap.h" --> fix and recompile libcap

#else

# define _LIBCAP_CAPABILITY_VERSION  _LINUX_CAPABILITY_VERSION_2
# define _LIBCAP_CAPABILITY_U32S     _LINUX_CAPABILITY_U32S_2

#endif

#undef _LINUX_CAPABILITY_VERSION
#undef _LINUX_CAPABILITY_U32S

/*
 * This is a pointer to a struct containing three consecutive
 * capability sets in the order of the cap_flag_t type: the are
 * effective,inheritable and permitted.  This is the type that the
 * user-space routines think of as 'internal' capabilities - this is
 * the type that is passed to the kernel with the system calls related
 * to processes.
 */

#if defined(VFS_CAP_REVISION_MASK) && !defined(VFS_CAP_U32)
# define VFS_CAP_U32_1                   1
# define XATTR_CAPS_SZ_1                 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_1))
# define VFS_CAP_U32                     VFS_CAP_U32_1
struct _cap_vfs_cap_data {
    __le32 magic_etc;
    struct {
	__le32 permitted;
	__le32 inheritable;
    } data[VFS_CAP_U32_1];
};
# define vfs_cap_data                    _cap_vfs_cap_data
#endif

#ifndef CAP_TO_INDEX
# define CAP_TO_INDEX(x)     ((x) >> 5)  /* 1 << 5 == bits in __u32 */
#endif /* ndef CAP_TO_INDEX */

#ifndef CAP_TO_MASK
# define CAP_TO_MASK(x)      (1 << ((x) & 31))
#endif /* ndef CAP_TO_MASK */

#define NUMBER_OF_CAP_SETS      3   /* effective, inheritable, permitted */
#define __CAP_BLKS   (_LIBCAP_CAPABILITY_U32S)
#define CAP_SET_SIZE (__CAP_BLKS * sizeof(__u32))

#define CAP_T_MAGIC 0xCA90D0
struct _cap_struct {
    __u8 mutex;
    struct __user_cap_header_struct head;
    union {
	struct __user_cap_data_struct set;
	__u32 flat[NUMBER_OF_CAP_SETS];
    } u[_LIBCAP_CAPABILITY_U32S];
    uid_t rootid;
};

/*
 * Elementary exclusive locking primatives for situations where
 * linking with pthreads needs it, but such linking is not common.
 *
 *  _cap_mu_blocked(x) attempts to lock x but if already locked, returns true
 *  _cap_mu_lock(x)    attempts to lock and waits until the lock is granted
 *  _cap_mu_unlock(x)  unconditionally unlocks the lock
 *  _cap_mu_unlock_return(x, y) unlock lock x and return value y
 */
#define _cap_mu_blocked(x)          \
    __atomic_test_and_set((void *)(x), __ATOMIC_SEQ_CST)
#define _cap_mu_lock(x)             \
    while (_cap_mu_blocked(x)) sched_yield()
#define _cap_mu_unlock(x)           \
    __atomic_clear((void *) (x), __ATOMIC_SEQ_CST)
#define _cap_mu_unlock_return(x, y) \
    do { _cap_mu_unlock(x); return (y); } while (0)

/* the maximum bits supportable */
#define __CAP_MAXBITS (__CAP_BLKS * 32)

/* string magic for cap_free */
#define CAP_S_MAGIC 0xCA95D0

/* iab set magic for cap_free */
#define CAP_IAB_MAGIC 0xCA91AB

/* launcher magic for cap_free */
#define CAP_LAUNCH_MAGIC 0xCA91AC

#define magic_of(x)           ((x) ? *(-2 + (const __u32 *) x) : 0)
#define good_cap_t(x)         (CAP_T_MAGIC   == magic_of(x))
#define good_cap_iab_t(x)     (CAP_IAB_MAGIC == magic_of(x))
#define good_cap_launch_t(x)  (CAP_LAUNCH_MAGIC == magic_of(x))

/*
 * kernel API cap set abstraction
 */

#define raise_cap(x, set)    u[(x) >> 5].flat[set]       |=  (1u << ((x)&31))
#define lower_cap(x, set)    u[(x) >> 5].flat[set]       &= ~(1u << ((x)&31))
#define isset_cap(y, x, set) ((y)->u[(x) >> 5].flat[set] &   (1u << ((x)&31)))

/*
 * These match CAP_DIFFERS() expectations
 */
#define LIBCAP_EFF   (1 << CAP_EFFECTIVE)
#define LIBCAP_INH   (1 << CAP_INHERITABLE)
#define LIBCAP_PER   (1 << CAP_PERMITTED)

/*
 * library debugging
 */
#ifdef DEBUG

#include <stdio.h>
# define _cap_debug(f, x...)  do { \
    fprintf(stderr, "%s(%s:%d): ", __FUNCTION__, __FILE__, __LINE__); \
    fprintf(stderr, f, ## x); \
    fprintf(stderr, "\n"); \
} while (0)

# define _cap_debugcap(s, c, set) do { \
    unsigned _cap_index; \
    fprintf(stderr, "%s(%s:%d): %s", __FUNCTION__, __FILE__, __LINE__, s); \
    for (_cap_index=_LIBCAP_CAPABILITY_U32S; _cap_index-- > 0; ) { \
       fprintf(stderr, "%08x", (c).u[_cap_index].flat[set]); \
    } \
    fprintf(stderr, "\n"); \
} while (0)

#else /* !DEBUG */

# define _cap_debug(f, x...)
# define _cap_debugcap(s, c, set)

#endif /* DEBUG */

extern char *_libcap_strdup(const char *text);
extern void _libcap_initialize(void);

#define EXECABLE_INITIALIZE _libcap_initialize()

/*
 * These are semi-public prototypes, they will only be defined in
 * <sys/capability.h> if _POSIX_SOURCE is not #define'd, so we
 * place them here too.
 */

extern int capget(cap_user_header_t header, cap_user_data_t data);
extern int capgetp(pid_t pid, cap_t cap_d);
extern int capsetp(pid_t pid, cap_t cap_d);

/* prctl based API for altering character of current process */
#define PR_GET_KEEPCAPS    7
#define PR_SET_KEEPCAPS    8
#define PR_CAPBSET_READ   23
#define PR_CAPBSET_DROP   24
#define PR_GET_SECUREBITS 27
#define PR_SET_SECUREBITS 28

/*
 * The library compares sizeof() with integer return values. To avoid
 * signed/unsigned comparisons, leading to unfortunate
 * misinterpretations of -1, we provide a convenient cast-to-signed-integer
 * version of sizeof().
 */
#define ssizeof(x) ((ssize_t) sizeof(x))

/*
 * Put this here as a macro so we can unit test it.
 */
#define _binary_search(val, fn, low, high, fallback) do {	\
	cap_value_t min = low, max = high;			\
	while (min <= max) {					\
	    cap_value_t mid = (min+max) / 2;			\
	    if (fn(mid) < 0) {					\
		max = mid - 1;					\
	    } else {						\
		min = mid + 1;					\
	    }							\
	}							\
	val = min ? (min <= high ? min : fallback) : fallback;	\
    } while(0)

/*
 * cap_iab_s holds a collection of inheritable capability bits. The i
 * bits are inheritable (these are the same as those in cap_t), the a
 * bits are ambient bits (which cannot be a superset of i&p), and nb
 * are the bits that will be dropped from the bounding set when
 * applied.
 */
struct cap_iab_s {
    __u8 mutex;
    __u32 i[_LIBCAP_CAPABILITY_U32S];
    __u32 a[_LIBCAP_CAPABILITY_U32S];
    __u32 nb[_LIBCAP_CAPABILITY_U32S];
};

#define LIBCAP_IAB_I_FLAG (1U << CAP_IAB_INH)
#define LIBCAP_IAB_A_FLAG (1U << CAP_IAB_AMB)
#define LIBCAP_IAB_IA_FLAG (LIBCAP_IAB_I_FLAG | LIBCAP_IAB_A_FLAG)
#define LIBCAP_IAB_NB_FLAG (1U << CAP_IAB_BOUND)

/*
 * The following support launching another process without destroying
 * the state of the current process. This is especially useful for
 * multithreaded applications.
 */
struct cap_launch_s {
    __u8 mutex;
    /*
     * Once forked but before active privilege is changed, this
     * function (if non-NULL) is called.
     */
    int (*custom_setup_fn)(void *detail);

    /*
     * user and groups to be used by the forked child.
     */
    int change_uids;
    uid_t uid;

    int change_gids;
    gid_t gid;
    int ngroups;
    const gid_t *groups;

    /*
     * mode holds the preferred capability mode. Any non-uncertain
     * setting here will require an empty ambient set.
     */
    int change_mode;
    cap_mode_t mode;

    /*
     * i,a,[n]b caps. These bitmaps hold all of the capability sets that
     * cap_launch will affect. nb holds values to be lowered in the bounding
     * set.
     */
    struct cap_iab_s *iab;

    /* chroot holds a preferred chroot for the launched child. */
    char *chroot;

    /*
     * execve style arguments
     */
    const char *arg0;
    const char *const *argv;
    const char *const *envp;
};

#endif /* LIBCAP_H */