mirror of
https://github.com/SELinuxProject/selinux
synced 2025-04-26 04:38:01 +00:00
439 lines
8.7 KiB
C
439 lines
8.7 KiB
C
/* Author: Trusted Computer Solutions, Inc.
|
|
*
|
|
* Modified:
|
|
* Yuichi Nakamura <ynakam@hitachisoft.jp>
|
|
- Stubs are used when DISABLE_SETRANS is defined,
|
|
it is to reduce size for such as embedded devices.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <netdb.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <sys/uio.h>
|
|
#include "selinux_internal.h"
|
|
#include "setrans_internal.h"
|
|
|
|
#ifndef DISABLE_SETRANS
|
|
static unsigned char has_setrans;
|
|
|
|
// Simple cache
|
|
static __thread char * prev_t2r_trans = NULL;
|
|
static __thread char * prev_t2r_raw = NULL;
|
|
static __thread char * prev_r2t_trans = NULL;
|
|
static __thread char * prev_r2t_raw = NULL;
|
|
static __thread char *prev_r2c_trans = NULL;
|
|
static __thread char * prev_r2c_raw = NULL;
|
|
|
|
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;
|
|
|
|
/*
|
|
* setransd_open
|
|
*
|
|
* This function opens a socket to the setransd.
|
|
* Returns: on success, a file descriptor ( >= 0 ) to the socket
|
|
* on error, a negative value
|
|
*/
|
|
static int setransd_open(void)
|
|
{
|
|
struct sockaddr_un addr;
|
|
int fd;
|
|
#ifdef SOCK_CLOEXEC
|
|
fd = socket(PF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
|
|
if (fd < 0 && errno == EINVAL)
|
|
#endif
|
|
{
|
|
fd = socket(PF_UNIX, SOCK_STREAM, 0);
|
|
if (fd >= 0)
|
|
if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
}
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, SETRANS_UNIX_SOCKET, sizeof(addr.sun_path));
|
|
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
/* Returns: 0 on success, <0 on failure */
|
|
static int
|
|
send_request(int fd, uint32_t function, const char *data1, const char *data2)
|
|
{
|
|
struct msghdr msgh;
|
|
struct iovec iov[5];
|
|
uint32_t data1_size;
|
|
uint32_t data2_size;
|
|
ssize_t count, expected;
|
|
unsigned int i;
|
|
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
if (!data1)
|
|
data1 = "";
|
|
if (!data2)
|
|
data2 = "";
|
|
|
|
data1_size = strlen(data1) + 1;
|
|
data2_size = strlen(data2) + 1;
|
|
|
|
iov[0].iov_base = &function;
|
|
iov[0].iov_len = sizeof(function);
|
|
iov[1].iov_base = &data1_size;
|
|
iov[1].iov_len = sizeof(data1_size);
|
|
iov[2].iov_base = &data2_size;
|
|
iov[2].iov_len = sizeof(data2_size);
|
|
iov[3].iov_base = (char *)data1;
|
|
iov[3].iov_len = data1_size;
|
|
iov[4].iov_base = (char *)data2;
|
|
iov[4].iov_len = data2_size;
|
|
memset(&msgh, 0, sizeof(msgh));
|
|
msgh.msg_iov = iov;
|
|
msgh.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
|
|
|
|
expected = 0;
|
|
for (i = 0; i < sizeof(iov) / sizeof(iov[0]); i++)
|
|
expected += iov[i].iov_len;
|
|
|
|
while (((count = sendmsg(fd, &msgh, MSG_NOSIGNAL)) < 0)
|
|
&& (errno == EINTR)) ;
|
|
if (count < 0 || count != expected)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Returns: 0 on success, <0 on failure */
|
|
static int
|
|
receive_response(int fd, uint32_t function, char **outdata, int32_t * ret_val)
|
|
{
|
|
struct iovec resp_hdr[3];
|
|
uint32_t func;
|
|
uint32_t data_size;
|
|
char *data;
|
|
struct iovec resp_data;
|
|
ssize_t count;
|
|
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
resp_hdr[0].iov_base = &func;
|
|
resp_hdr[0].iov_len = sizeof(func);
|
|
resp_hdr[1].iov_base = &data_size;
|
|
resp_hdr[1].iov_len = sizeof(data_size);
|
|
resp_hdr[2].iov_base = ret_val;
|
|
resp_hdr[2].iov_len = sizeof(*ret_val);
|
|
|
|
while (((count = readv(fd, resp_hdr, 3)) < 0) && (errno == EINTR)) ;
|
|
if (count != (sizeof(func) + sizeof(data_size) + sizeof(*ret_val))) {
|
|
return -1;
|
|
}
|
|
|
|
if (func != function || !data_size || data_size > MAX_DATA_BUF) {
|
|
return -1;
|
|
}
|
|
|
|
data = malloc(data_size);
|
|
if (!data)
|
|
return -1;
|
|
/* coveriety doesn't realize that data will be initialized in readv */
|
|
memset(data, 0, data_size);
|
|
|
|
resp_data.iov_base = data;
|
|
resp_data.iov_len = data_size;
|
|
|
|
while (((count = readv(fd, &resp_data, 1))) < 0 && (errno == EINTR)) ;
|
|
if (count < 0 || (uint32_t) count != data_size ||
|
|
data[data_size - 1] != '\0') {
|
|
free(data);
|
|
return -1;
|
|
}
|
|
*outdata = data;
|
|
return 0;
|
|
}
|
|
|
|
static int raw_to_trans_context(const char *raw, char **transp)
|
|
{
|
|
int ret;
|
|
int32_t ret_val;
|
|
int fd;
|
|
|
|
*transp = NULL;
|
|
|
|
fd = setransd_open();
|
|
if (fd < 0)
|
|
return fd;
|
|
|
|
ret = send_request(fd, RAW_TO_TRANS_CONTEXT, raw, NULL);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = receive_response(fd, RAW_TO_TRANS_CONTEXT, transp, &ret_val);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = ret_val;
|
|
out:
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
|
|
static int trans_to_raw_context(const char *trans, char **rawp)
|
|
{
|
|
int ret;
|
|
int32_t ret_val;
|
|
int fd;
|
|
|
|
*rawp = NULL;
|
|
|
|
fd = setransd_open();
|
|
if (fd < 0)
|
|
return fd;
|
|
ret = send_request(fd, TRANS_TO_RAW_CONTEXT, trans, NULL);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = receive_response(fd, TRANS_TO_RAW_CONTEXT, rawp, &ret_val);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = ret_val;
|
|
out:
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
|
|
static int raw_context_to_color(const char *raw, char **colors)
|
|
{
|
|
int ret;
|
|
int32_t ret_val;
|
|
int fd;
|
|
|
|
fd = setransd_open();
|
|
if (fd < 0)
|
|
return fd;
|
|
|
|
ret = send_request(fd, RAW_CONTEXT_TO_COLOR, raw, NULL);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = receive_response(fd, RAW_CONTEXT_TO_COLOR, colors, &ret_val);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = ret_val;
|
|
out:
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
|
|
static void setrans_thread_destructor(void __attribute__((unused)) *unused)
|
|
{
|
|
free(prev_t2r_trans);
|
|
free(prev_t2r_raw);
|
|
free(prev_r2t_trans);
|
|
free(prev_r2t_raw);
|
|
free(prev_r2c_trans);
|
|
free(prev_r2c_raw);
|
|
}
|
|
|
|
void __attribute__((destructor)) setrans_lib_destructor(void);
|
|
|
|
void __attribute__((destructor)) setrans_lib_destructor(void)
|
|
{
|
|
if (!has_setrans)
|
|
return;
|
|
if (destructor_key_initialized)
|
|
__selinux_key_delete(destructor_key);
|
|
}
|
|
|
|
static inline void init_thread_destructor(void)
|
|
{
|
|
if (!has_setrans)
|
|
return;
|
|
if (destructor_initialized == 0) {
|
|
__selinux_setspecific(destructor_key, (void *)1);
|
|
destructor_initialized = 1;
|
|
}
|
|
}
|
|
|
|
static void init_context_translations(void)
|
|
{
|
|
has_setrans = (access(SETRANS_UNIX_SOCKET, F_OK) == 0);
|
|
if (!has_setrans)
|
|
return;
|
|
if (__selinux_key_create(&destructor_key, setrans_thread_destructor) == 0)
|
|
destructor_key_initialized = 1;
|
|
}
|
|
|
|
int selinux_trans_to_raw_context(const char * trans,
|
|
char ** rawp)
|
|
{
|
|
if (!trans) {
|
|
*rawp = NULL;
|
|
return 0;
|
|
}
|
|
|
|
__selinux_once(once, init_context_translations);
|
|
init_thread_destructor();
|
|
|
|
if (!has_setrans) {
|
|
*rawp = strdup(trans);
|
|
goto out;
|
|
}
|
|
|
|
if (prev_t2r_trans && strcmp(prev_t2r_trans, trans) == 0) {
|
|
*rawp = strdup(prev_t2r_raw);
|
|
} else {
|
|
free(prev_t2r_trans);
|
|
prev_t2r_trans = NULL;
|
|
free(prev_t2r_raw);
|
|
prev_t2r_raw = NULL;
|
|
if (trans_to_raw_context(trans, rawp))
|
|
*rawp = strdup(trans);
|
|
if (*rawp) {
|
|
prev_t2r_trans = strdup(trans);
|
|
if (!prev_t2r_trans)
|
|
goto out;
|
|
prev_t2r_raw = strdup(*rawp);
|
|
if (!prev_t2r_raw) {
|
|
free(prev_t2r_trans);
|
|
prev_t2r_trans = NULL;
|
|
}
|
|
}
|
|
}
|
|
out:
|
|
return *rawp ? 0 : -1;
|
|
}
|
|
|
|
|
|
int selinux_raw_to_trans_context(const char * raw,
|
|
char ** transp)
|
|
{
|
|
if (!raw) {
|
|
*transp = NULL;
|
|
return 0;
|
|
}
|
|
|
|
__selinux_once(once, init_context_translations);
|
|
init_thread_destructor();
|
|
|
|
if (!has_setrans) {
|
|
*transp = strdup(raw);
|
|
goto out;
|
|
}
|
|
|
|
if (prev_r2t_raw && strcmp(prev_r2t_raw, raw) == 0) {
|
|
*transp = strdup(prev_r2t_trans);
|
|
} else {
|
|
free(prev_r2t_raw);
|
|
prev_r2t_raw = NULL;
|
|
free(prev_r2t_trans);
|
|
prev_r2t_trans = NULL;
|
|
if (raw_to_trans_context(raw, transp))
|
|
*transp = strdup(raw);
|
|
if (*transp) {
|
|
prev_r2t_raw = strdup(raw);
|
|
if (!prev_r2t_raw)
|
|
goto out;
|
|
prev_r2t_trans = strdup(*transp);
|
|
if (!prev_r2t_trans) {
|
|
free(prev_r2t_raw);
|
|
prev_r2t_raw = NULL;
|
|
}
|
|
}
|
|
}
|
|
out:
|
|
return *transp ? 0 : -1;
|
|
}
|
|
|
|
|
|
int selinux_raw_context_to_color(const char * raw, char **transp)
|
|
{
|
|
if (!raw) {
|
|
*transp = NULL;
|
|
return -1;
|
|
}
|
|
|
|
__selinux_once(once, init_context_translations);
|
|
init_thread_destructor();
|
|
|
|
if (!has_setrans) {
|
|
*transp = strdup(raw);
|
|
goto out;
|
|
}
|
|
|
|
if (prev_r2c_raw && strcmp(prev_r2c_raw, raw) == 0) {
|
|
*transp = strdup(prev_r2c_trans);
|
|
} else {
|
|
free(prev_r2c_raw);
|
|
prev_r2c_raw = NULL;
|
|
free(prev_r2c_trans);
|
|
prev_r2c_trans = NULL;
|
|
if (raw_context_to_color(raw, transp))
|
|
return -1;
|
|
if (*transp) {
|
|
prev_r2c_raw = strdup(raw);
|
|
if (!prev_r2c_raw)
|
|
goto out;
|
|
prev_r2c_trans = strdup(*transp);
|
|
if (!prev_r2c_trans) {
|
|
free(prev_r2c_raw);
|
|
prev_r2c_raw = NULL;
|
|
}
|
|
}
|
|
}
|
|
out:
|
|
return *transp ? 0 : -1;
|
|
}
|
|
|
|
#else /*DISABLE_SETRANS*/
|
|
|
|
int selinux_trans_to_raw_context(const char * trans,
|
|
char ** rawp)
|
|
{
|
|
if (!trans) {
|
|
*rawp = NULL;
|
|
return 0;
|
|
}
|
|
|
|
*rawp = strdup(trans);
|
|
|
|
return *rawp ? 0 : -1;
|
|
}
|
|
|
|
|
|
int selinux_raw_to_trans_context(const char * raw,
|
|
char ** transp)
|
|
{
|
|
if (!raw) {
|
|
*transp = NULL;
|
|
return 0;
|
|
}
|
|
*transp = strdup(raw);
|
|
|
|
return *transp ? 0 : -1;
|
|
}
|
|
|
|
#endif /*DISABLE_SETRANS*/
|