1/* Author: Trusted Computer Solutions, Inc. 2 * 3 * Modified: 4 * Yuichi Nakamura <ynakam@hitachisoft.jp> 5 - Stubs are used when DISABLE_SETRANS is defined, 6 it is to reduce size for such as embedded devices. 7*/ 8 9#include <sys/types.h> 10#include <sys/socket.h> 11#include <sys/un.h> 12 13#include <errno.h> 14#include <stdlib.h> 15#include <netdb.h> 16#include <fcntl.h> 17#include <stdio.h> 18#include <string.h> 19#include <ctype.h> 20#include <unistd.h> 21#include <sys/uio.h> 22#include "selinux_internal.h" 23#include "setrans_internal.h" 24 25#ifndef DISABLE_SETRANS 26static unsigned char has_setrans; 27 28// Simple cache 29static __thread char * prev_t2r_trans = NULL; 30static __thread char * prev_t2r_raw = NULL; 31static __thread char * prev_r2t_trans = NULL; 32static __thread char * prev_r2t_raw = NULL; 33static __thread char *prev_r2c_trans = NULL; 34static __thread char * prev_r2c_raw = NULL; 35 36static pthread_once_t once = PTHREAD_ONCE_INIT; 37static pthread_key_t destructor_key; 38static int destructor_key_initialized = 0; 39static __thread char destructor_initialized; 40 41/* 42 * setransd_open 43 * 44 * This function opens a socket to the setransd. 45 * Returns: on success, a file descriptor ( >= 0 ) to the socket 46 * on error, a negative value 47 */ 48static int setransd_open(void) 49{ 50 struct sockaddr_un addr; 51 int fd; 52#ifdef SOCK_CLOEXEC 53 fd = socket(PF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); 54 if (fd < 0 && errno == EINVAL) 55#endif 56 { 57 fd = socket(PF_UNIX, SOCK_STREAM, 0); 58 if (fd >= 0) 59 if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { 60 close(fd); 61 return -1; 62 } 63 } 64 if (fd < 0) 65 return -1; 66 67 memset(&addr, 0, sizeof(addr)); 68 addr.sun_family = AF_UNIX; 69 70 if (strlcpy(addr.sun_path, SETRANS_UNIX_SOCKET, sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) { 71 close(fd); 72 errno = EOVERFLOW; 73 return -1; 74 } 75 76 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 77 close(fd); 78 return -1; 79 } 80 81 return fd; 82} 83 84/* Returns: 0 on success, <0 on failure */ 85static int 86send_request(int fd, uint32_t function, const char *data1, const char *data2) 87{ 88 struct msghdr msgh; 89 struct iovec iov[5]; 90 uint32_t data1_size; 91 uint32_t data2_size; 92 ssize_t count, expected; 93 unsigned int i; 94 95 if (fd < 0) 96 return -1; 97 98 if (!data1) 99 data1 = ""; 100 if (!data2) 101 data2 = ""; 102 103 data1_size = strlen(data1) + 1; 104 data2_size = strlen(data2) + 1; 105 106 iov[0].iov_base = &function; 107 iov[0].iov_len = sizeof(function); 108 iov[1].iov_base = &data1_size; 109 iov[1].iov_len = sizeof(data1_size); 110 iov[2].iov_base = &data2_size; 111 iov[2].iov_len = sizeof(data2_size); 112 iov[3].iov_base = (char *)data1; 113 iov[3].iov_len = data1_size; 114 iov[4].iov_base = (char *)data2; 115 iov[4].iov_len = data2_size; 116 memset(&msgh, 0, sizeof(msgh)); 117 msgh.msg_iov = iov; 118 msgh.msg_iovlen = sizeof(iov) / sizeof(iov[0]); 119 120 expected = 0; 121 for (i = 0; i < sizeof(iov) / sizeof(iov[0]); i++) 122 expected += iov[i].iov_len; 123 124 while (((count = sendmsg(fd, &msgh, MSG_NOSIGNAL)) < 0) 125 && (errno == EINTR)) ; 126 if (count < 0 || count != expected) 127 return -1; 128 129 return 0; 130} 131 132/* Returns: 0 on success, <0 on failure */ 133static int 134receive_response(int fd, uint32_t function, char **outdata, int32_t * ret_val) 135{ 136 struct iovec resp_hdr[3]; 137 uint32_t func; 138 uint32_t data_size; 139 char *data; 140 struct iovec resp_data; 141 ssize_t count; 142 143 if (fd < 0) 144 return -1; 145 146 resp_hdr[0].iov_base = &func; 147 resp_hdr[0].iov_len = sizeof(func); 148 resp_hdr[1].iov_base = &data_size; 149 resp_hdr[1].iov_len = sizeof(data_size); 150 resp_hdr[2].iov_base = ret_val; 151 resp_hdr[2].iov_len = sizeof(*ret_val); 152 153 while (((count = readv(fd, resp_hdr, 3)) < 0) && (errno == EINTR)) ; 154 if (count != (sizeof(func) + sizeof(data_size) + sizeof(*ret_val))) { 155 return -1; 156 } 157 158 if (func != function || !data_size || data_size > MAX_DATA_BUF) { 159 return -1; 160 } 161 162 data = malloc(data_size); 163 if (!data) 164 return -1; 165 /* coveriety doesn't realize that data will be initialized in readv */ 166 memset(data, 0, data_size); 167 168 resp_data.iov_base = data; 169 resp_data.iov_len = data_size; 170 171 while (((count = readv(fd, &resp_data, 1))) < 0 && (errno == EINTR)) ; 172 if (count < 0 || (uint32_t) count != data_size || 173 data[data_size - 1] != '\0') { 174 free(data); 175 return -1; 176 } 177 *outdata = data; 178 return 0; 179} 180 181static int raw_to_trans_context(const char *raw, char **transp) 182{ 183 int ret; 184 int32_t ret_val; 185 int fd; 186 187 *transp = NULL; 188 189 fd = setransd_open(); 190 if (fd < 0) 191 return fd; 192 193 ret = send_request(fd, RAW_TO_TRANS_CONTEXT, raw, NULL); 194 if (ret) 195 goto out; 196 197 ret = receive_response(fd, RAW_TO_TRANS_CONTEXT, transp, &ret_val); 198 if (ret) 199 goto out; 200 201 ret = ret_val; 202 out: 203 close(fd); 204 return ret; 205} 206 207static int trans_to_raw_context(const char *trans, char **rawp) 208{ 209 int ret; 210 int32_t ret_val; 211 int fd; 212 213 *rawp = NULL; 214 215 fd = setransd_open(); 216 if (fd < 0) 217 return fd; 218 ret = send_request(fd, TRANS_TO_RAW_CONTEXT, trans, NULL); 219 if (ret) 220 goto out; 221 222 ret = receive_response(fd, TRANS_TO_RAW_CONTEXT, rawp, &ret_val); 223 if (ret) 224 goto out; 225 226 ret = ret_val; 227 out: 228 close(fd); 229 return ret; 230} 231 232static int raw_context_to_color(const char *raw, char **colors) 233{ 234 int ret; 235 int32_t ret_val; 236 int fd; 237 238 fd = setransd_open(); 239 if (fd < 0) 240 return fd; 241 242 ret = send_request(fd, RAW_CONTEXT_TO_COLOR, raw, NULL); 243 if (ret) 244 goto out; 245 246 ret = receive_response(fd, RAW_CONTEXT_TO_COLOR, colors, &ret_val); 247 if (ret) 248 goto out; 249 250 ret = ret_val; 251out: 252 close(fd); 253 return ret; 254} 255 256static void setrans_thread_destructor(void __attribute__((unused)) *unused) 257{ 258 free(prev_t2r_trans); 259 free(prev_t2r_raw); 260 free(prev_r2t_trans); 261 free(prev_r2t_raw); 262 free(prev_r2c_trans); 263 free(prev_r2c_raw); 264} 265 266void __attribute__((destructor)) setrans_lib_destructor(void); 267 268void __attribute__((destructor)) setrans_lib_destructor(void) 269{ 270 if (!has_setrans) 271 return; 272 if (destructor_key_initialized) 273 __selinux_key_delete(destructor_key); 274} 275 276static inline void init_thread_destructor(void) 277{ 278 if (!has_setrans) 279 return; 280 if (destructor_initialized == 0) { 281 __selinux_setspecific(destructor_key, /* some valid address to please GCC */ &selinux_page_size); 282 destructor_initialized = 1; 283 } 284} 285 286static void init_context_translations(void) 287{ 288 has_setrans = (access(SETRANS_UNIX_SOCKET, F_OK) == 0); 289 if (!has_setrans) 290 return; 291 if (__selinux_key_create(&destructor_key, setrans_thread_destructor) == 0) 292 destructor_key_initialized = 1; 293} 294 295int selinux_trans_to_raw_context(const char * trans, 296 char ** rawp) 297{ 298 if (!trans) { 299 *rawp = NULL; 300 return 0; 301 } 302 303 __selinux_once(once, init_context_translations); 304 init_thread_destructor(); 305 306 if (!has_setrans) { 307 *rawp = strdup(trans); 308 goto out; 309 } 310 311 if (prev_t2r_trans && strcmp(prev_t2r_trans, trans) == 0) { 312 *rawp = strdup(prev_t2r_raw); 313 } else { 314 free(prev_t2r_trans); 315 prev_t2r_trans = NULL; 316 free(prev_t2r_raw); 317 prev_t2r_raw = NULL; 318 if (trans_to_raw_context(trans, rawp)) 319 *rawp = strdup(trans); 320 if (*rawp) { 321 prev_t2r_trans = strdup(trans); 322 if (!prev_t2r_trans) 323 goto out; 324 prev_t2r_raw = strdup(*rawp); 325 if (!prev_t2r_raw) { 326 free(prev_t2r_trans); 327 prev_t2r_trans = NULL; 328 } 329 } 330 } 331 out: 332 return *rawp ? 0 : -1; 333} 334 335 336int selinux_raw_to_trans_context(const char * raw, 337 char ** transp) 338{ 339 if (!raw) { 340 *transp = NULL; 341 return 0; 342 } 343 344 __selinux_once(once, init_context_translations); 345 init_thread_destructor(); 346 347 if (!has_setrans) { 348 *transp = strdup(raw); 349 goto out; 350 } 351 352 if (prev_r2t_raw && strcmp(prev_r2t_raw, raw) == 0) { 353 *transp = strdup(prev_r2t_trans); 354 } else { 355 free(prev_r2t_raw); 356 prev_r2t_raw = NULL; 357 free(prev_r2t_trans); 358 prev_r2t_trans = NULL; 359 if (raw_to_trans_context(raw, transp)) 360 *transp = strdup(raw); 361 if (*transp) { 362 prev_r2t_raw = strdup(raw); 363 if (!prev_r2t_raw) 364 goto out; 365 prev_r2t_trans = strdup(*transp); 366 if (!prev_r2t_trans) { 367 free(prev_r2t_raw); 368 prev_r2t_raw = NULL; 369 } 370 } 371 } 372 out: 373 return *transp ? 0 : -1; 374} 375 376 377int selinux_raw_context_to_color(const char * raw, char **transp) 378{ 379 if (!raw) { 380 *transp = NULL; 381 return -1; 382 } 383 384 __selinux_once(once, init_context_translations); 385 init_thread_destructor(); 386 387 if (!has_setrans) { 388 *transp = strdup(raw); 389 goto out; 390 } 391 392 if (prev_r2c_raw && strcmp(prev_r2c_raw, raw) == 0) { 393 *transp = strdup(prev_r2c_trans); 394 } else { 395 free(prev_r2c_raw); 396 prev_r2c_raw = NULL; 397 free(prev_r2c_trans); 398 prev_r2c_trans = NULL; 399 if (raw_context_to_color(raw, transp)) 400 return -1; 401 if (*transp) { 402 prev_r2c_raw = strdup(raw); 403 if (!prev_r2c_raw) 404 goto out; 405 prev_r2c_trans = strdup(*transp); 406 if (!prev_r2c_trans) { 407 free(prev_r2c_raw); 408 prev_r2c_raw = NULL; 409 } 410 } 411 } 412 out: 413 return *transp ? 0 : -1; 414} 415 416#else /*DISABLE_SETRANS*/ 417 418int selinux_trans_to_raw_context(const char * trans, 419 char ** rawp) 420{ 421 if (!trans) { 422 *rawp = NULL; 423 return 0; 424 } 425 426 *rawp = strdup(trans); 427 428 return *rawp ? 0 : -1; 429} 430 431 432int selinux_raw_to_trans_context(const char * raw, 433 char ** transp) 434{ 435 if (!raw) { 436 *transp = NULL; 437 return 0; 438 } 439 *transp = strdup(raw); 440 441 return *transp ? 0 : -1; 442} 443 444#endif /*DISABLE_SETRANS*/ 445