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