#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)
|