#include <sys/syscall.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include "selinux_internal.h"
#include "policy.h"
#define UNSET (char *) -1
static __thread char *prev_current = UNSET;
static __thread char * prev_exec = UNSET;
static __thread char * prev_fscreate = UNSET;
static __thread char * prev_keycreate = UNSET;
static __thread char * prev_sockcreate = UNSET;
static pthread_once_t once = PTHREAD_ONCE_INIT;
static pthread_key_t destructor_key;
static int destructor_key_initialized = 0;
static __thread char destructor_initialized;
/* Bionic and glibc >= 2.30 declare gettid() system call wrapper in unistd.h and
* has a definition for it */
#ifdef __BIONIC__
#define OVERRIDE_GETTID 0
#elif !defined(__GLIBC_PREREQ)
#define OVERRIDE_GETTID 1
#elif !__GLIBC_PREREQ(2,30)
#define OVERRIDE_GETTID 1
#else
#define OVERRIDE_GETTID 0
#endif
#if OVERRIDE_GETTID
static pid_t gettid(void)
{
return syscall(__NR_gettid);
}
#endif
static void procattr_thread_destructor(void __attribute__((unused)) *unused)
{
if (prev_current != UNSET)
free(prev_current);
if (prev_exec != UNSET)
free(prev_exec);
if (prev_fscreate != UNSET)
free(prev_fscreate);
if (prev_keycreate != UNSET)
free(prev_keycreate);
if (prev_sockcreate != UNSET)
free(prev_sockcreate);
}
void __attribute__((destructor)) procattr_destructor(void);
void hidden __attribute__((destructor)) procattr_destructor(void)
{
if (destructor_key_initialized)
__selinux_key_delete(destructor_key);
}
static inline void init_thread_destructor(void)
{
if (destructor_initialized == 0) {
__selinux_setspecific(destructor_key, (void *)1);
destructor_initialized = 1;
}
}
static void init_procattr(void)
{
if (__selinux_key_create(&destructor_key, procattr_thread_destructor) == 0) {
destructor_key_initialized = 1;
}
}
static int openattr(pid_t pid, const char *attr, int flags)
{
int fd, rc;
char *path;
pid_t tid;
if (pid > 0) {
rc = asprintf(&path, "/proc/%d/attr/%s", pid, attr);
} else if (pid == 0) {
rc = asprintf(&path, "/proc/thread-self/attr/%s", attr);
if (rc < 0)
return -1;
fd = open(path, flags | O_CLOEXEC);
if (fd >= 0 || errno != ENOENT)
goto out;
free(path);
tid = gettid();
rc = asprintf(&path, "/proc/self/task/%d/attr/%s", tid, attr);
} else {
errno = EINVAL;
return -1;
}
if (rc < 0)
return -1;
fd = open(path, flags | O_CLOEXEC);
out:
free(path);
return fd;
}
static int getprocattrcon_raw(char ** context,
pid_t pid, const char *attr)
{
char *buf;
size_t size;
int fd;
ssize_t ret;
int errno_hold;
char * prev_context;
__selinux_once(once, init_procattr);
init_thread_destructor();
switch (attr[0]) {
case 'c':
prev_context = prev_current;
break;
case 'e':
prev_context = prev_exec;
break;
case 'f':
prev_context = prev_fscreate;
break;
case 'k':
prev_context = prev_keycreate;
break;
case 's':
prev_context = prev_sockcreate;
break;
case 'p':
prev_context = NULL;
break;
default:
errno = ENOENT;
return -1;
};
if (prev_context && prev_context != UNSET) {
*context = strdup(prev_context);
if (!(*context)) {
return -1;
}
return 0;
}
fd = openattr(pid, attr, O_RDONLY | O_CLOEXEC);
if (fd < 0)
return -1;
size = selinux_page_size;
buf = malloc(size);
if (!buf) {
ret = -1;
goto out;
}
memset(buf, 0, size);
do {
ret = read(fd, buf, size - 1);
} while (ret < 0 && errno == EINTR);
if (ret < 0)
goto out2;
if (ret == 0) {
*context = NULL;
goto out2;
}
*context = strdup(buf);
if (!(*context)) {
ret = -1;
goto out2;
}
ret = 0;
out2:
free(buf);
out:
errno_hold = errno;
close(fd);
errno = errno_hold;
return ret;
}
static int getprocattrcon(char ** context,
pid_t pid, const char *attr)
{
int ret;
char * rcontext;
ret = getprocattrcon_raw(&rcontext, pid, attr);
if (!ret) {
ret = selinux_raw_to_trans_context(rcontext, context);
freecon(rcontext);
}
return ret;
}
static int setprocattrcon_raw(const char * context,
pid_t pid, const char *attr)
{
int fd;
ssize_t ret;
int errno_hold;
char **prev_context, *context2 = NULL;
__selinux_once(once, init_procattr);
init_thread_destructor();
switch (attr[0]) {
case 'c':
prev_context = &prev_current;
break;
case 'e':
prev_context = &prev_exec;
break;
case 'f':
prev_context = &prev_fscreate;
break;
case 'k':
prev_context = &prev_keycreate;
break;
case 's':
prev_context = &prev_sockcreate;
break;
default:
errno = ENOENT;
return -1;
};
if (!context && !*prev_context)
return 0;
if (context && *prev_context && *prev_context != UNSET
&& !strcmp(context, *prev_context))
return 0;
fd = openattr(pid, attr, O_RDWR | O_CLOEXEC);
if (fd < 0)
return -1;
if (context) {
ret = -1;
context2 = strdup(context);
if (!context2)
goto out;
do {
ret = write(fd, context2, strlen(context2) + 1);
} while (ret < 0 && errno == EINTR);
} else {
do {
ret = write(fd, NULL, 0); /* clear */
} while (ret < 0 && errno == EINTR);
}
out:
errno_hold = errno;
close(fd);
errno = errno_hold;
if (ret < 0) {
free(context2);
return -1;
} else {
if (*prev_context != UNSET)
free(*prev_context);
*prev_context = context2;
return 0;
}
}
static int setprocattrcon(const char * context,
pid_t pid, const char *attr)
{
int ret;
char * rcontext;
if (selinux_trans_to_raw_context(context, &rcontext))
return -1;
ret = setprocattrcon_raw(rcontext, pid, attr);
freecon(rcontext);
return ret;
}
#define getselfattr_def(fn, attr) \
int get##fn##_raw(char **c) \
{ \
return getprocattrcon_raw(c, 0, #attr); \
} \
int get##fn(char **c) \
{ \
return getprocattrcon(c, 0, #attr); \
}
#define setselfattr_def(fn, attr) \
int set##fn##_raw(const char * c) \
{ \
return setprocattrcon_raw(c, 0, #attr); \
} \
int set##fn(const char * c) \
{ \
return setprocattrcon(c, 0, #attr); \
}
#define all_selfattr_def(fn, attr) \
getselfattr_def(fn, attr) \
setselfattr_def(fn, attr)
#define getpidattr_def(fn, attr) \
int get##fn##_raw(pid_t pid, char **c) \
{ \
if (pid <= 0) { \
errno = EINVAL; \
return -1; \
} else { \
return getprocattrcon_raw(c, pid, #attr); \
} \
} \
int get##fn(pid_t pid, char **c) \
{ \
if (pid <= 0) { \
errno = EINVAL; \
return -1; \
} else { \
return getprocattrcon(c, pid, #attr); \
} \
}
all_selfattr_def(con, current)
getpidattr_def(pidcon, current)
getselfattr_def(prevcon, prev)
all_selfattr_def(execcon, exec)
all_selfattr_def(fscreatecon, fscreate)
all_selfattr_def(sockcreatecon, sockcreate)
all_selfattr_def(keycreatecon, keycreate)
hidden_def(getcon_raw)
hidden_def(getcon)
hidden_def(getexeccon_raw)
hidden_def(getfilecon_raw)
hidden_def(getfilecon)
hidden_def(getfscreatecon_raw)
hidden_def(getkeycreatecon_raw)
hidden_def(getpeercon_raw)
hidden_def(getpidcon_raw)
hidden_def(getprevcon_raw)
hidden_def(getprevcon)
hidden_def(getsockcreatecon_raw)
hidden_def(setcon_raw)
hidden_def(setexeccon_raw)
hidden_def(setexeccon)
hidden_def(setfilecon_raw)
hidden_def(setfscreatecon_raw)
hidden_def(setkeycreatecon_raw)
hidden_def(setsockcreatecon_raw)