1 /* Copyright (c) 2006 Trusted Computer Solutions, Inc. */
2 #include <errno.h>
3 #include <poll.h>
4 #include <signal.h>
5 #include <stdint.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <syslog.h>
10 #include <unistd.h>
11 #include <selinux/selinux.h>
12 #include <sys/capability.h>
13 #include <sys/resource.h>
14 #include <sys/socket.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <sys/uio.h>
18 #include <sys/un.h>
19
20 #include "mcscolor.h"
21 #include "mcstrans.h"
22
23 #ifdef UNUSED
24 #elif defined(__GNUC__)
25 # define UNUSED(x) UNUSED_ ## x __attribute__((unused))
26 #elif defined(__LCLINT__)
27 # define UNUSED(x) /*@unused@*/ x
28 #else
29 # define UNUSED(x) x
30 #endif
31
32 #define SETRANS_UNIX_SOCKET "/var/run/setrans/.setrans-unix"
33
34 #define SETRANS_INIT 1
35 #define RAW_TO_TRANS_CONTEXT 2
36 #define TRANS_TO_RAW_CONTEXT 3
37 #define RAW_CONTEXT_TO_COLOR 4
38 #define MAX_DATA_BUF 4096
39 #define MAX_DESCRIPTORS 8192
40
41 #ifdef DEBUG
42 //#define log_debug(fmt, ...) syslog(LOG_DEBUG, fmt, __VA_ARGS__)
43 #define log_debug(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
44 #else
45 #define log_debug(fmt, ...) do {} while (0)
46 #endif
47
48 #define SETRANSD_PATHNAME "/sbin/mcstransd"
49
50 /* name of program (for error messages) */
51 #define SETRANSD_PROGNAME "mcstransd"
52
53 static int sockfd = -1; /* socket we are listening on */
54
55 static volatile int restart_daemon = 0;
56 static void cleanup_exit(int ret) __attribute__ ((noreturn));
57 static void
cleanup_exit(int ret)58 cleanup_exit(int ret)
59 {
60 finish_context_colors();
61 finish_context_translations();
62 if (sockfd >=0)
63 (void)unlink(SETRANS_UNIX_SOCKET);
64
65 log_debug("%s\n", "cleanup_exit");
66
67 exit(ret);
68 }
69
70 static void clean_exit(void);
clean_exit(void)71 static __attribute__((noreturn)) void clean_exit(void)
72 {
73 log_debug("%s\n", "clean_exit");
74 cleanup_exit(0);
75 }
76
77 static int
send_response(int fd,uint32_t function,char * data,int32_t ret_val)78 send_response(int fd, uint32_t function, char *data, int32_t ret_val)
79 {
80 struct iovec resp_hdr[3];
81 uint32_t data_size;
82 struct iovec resp_data;
83 ssize_t count;
84
85 if (!data)
86 data = (char *)"";
87
88 data_size = strlen(data) + 1;
89
90 resp_hdr[0].iov_base = &function;
91 resp_hdr[0].iov_len = sizeof(function);
92 resp_hdr[1].iov_base = &data_size;
93 resp_hdr[1].iov_len = sizeof(data_size);
94 resp_hdr[2].iov_base = &ret_val;
95 resp_hdr[2].iov_len = sizeof(ret_val);
96
97 while (((count = writev(fd, resp_hdr, 3)) < 0) && (errno == EINTR));
98 if (count != (sizeof(function) + sizeof(data_size) + sizeof(ret_val))) {
99 syslog(LOG_ERR, "Failed to write response header");
100 return -1;
101 }
102
103 resp_data.iov_base = data;
104 resp_data.iov_len = data_size;
105
106 while (((count = writev(fd, &resp_data, 1)) < 0) && (errno == EINTR));
107 if (count < 0 || (size_t)count != data_size) {
108 syslog(LOG_ERR, "Failed to write response data");
109 return -1;
110 }
111
112 return ret_val;
113 }
114
115 static int
get_peer_pid(int fd,pid_t * pid)116 get_peer_pid(int fd, pid_t *pid)
117 {
118 int ret;
119 socklen_t size = sizeof(struct ucred);
120 struct ucred peercred;
121
122 /* get the context of the requesting process */
123 ret = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &peercred, &size);
124 if (ret < 0) {
125 syslog(LOG_ERR, "Failed to get PID of client process");
126 return -1;
127 }
128 *pid = peercred.pid;
129 return ret;
130 }
131
132
133 static int
process_request(int fd,uint32_t function,char * data1,char * UNUSED (data2))134 process_request(int fd, uint32_t function, char *data1, char *UNUSED(data2))
135 {
136 int32_t result;
137 char *out = NULL;
138 int ret;
139
140 switch (function) {
141 case SETRANS_INIT:
142 result = 0;
143 ret = send_response(fd, function, NULL, result);
144 break;
145 case RAW_TO_TRANS_CONTEXT:
146 result = trans_context(data1, &out);
147 ret = send_response(fd, function, out, result);
148 break;
149 case TRANS_TO_RAW_CONTEXT:
150 result = untrans_context(data1, &out);
151 ret = send_response(fd, function, out, result);
152 break;
153 case RAW_CONTEXT_TO_COLOR:
154 result = raw_color(data1, &out);
155 ret = send_response(fd, function, out, result);
156 break;
157 default:
158 result = -1;
159 ret = -1;
160 break;
161 }
162
163 if (result) {
164 pid_t pid = 0;
165 get_peer_pid(fd, &pid);
166 syslog(LOG_ERR, "Invalid request func=%d from=%u",
167 function, pid);
168 }
169
170 free(out);
171
172 return ret;
173 }
174
175 static int
service_request(int fd)176 service_request(int fd)
177 {
178 struct iovec req_hdr[3];
179 uint32_t function;
180 uint32_t data1_size;
181 uint32_t data2_size;
182 struct iovec req_data[2];
183 char *data1;
184 char *data2;
185 int ret;
186 ssize_t count;
187
188 req_hdr[0].iov_base = &function;
189 req_hdr[0].iov_len = sizeof(function);
190 req_hdr[1].iov_base = &data1_size;
191 req_hdr[1].iov_len = sizeof(data1_size);
192 req_hdr[2].iov_base = &data2_size;
193 req_hdr[2].iov_len = sizeof(data2_size);
194
195 while (((count = readv(fd, req_hdr, 3)) < 0) && (errno == EINTR));
196 if (count <= 0) {
197 return 1;
198 }
199 if (count != (sizeof(function) + sizeof(data1_size) +
200 sizeof(data2_size) )) {
201 log_debug("Failed to read request header %d != %u\n",(int)count,
202 (unsigned)(sizeof(function) + sizeof(data1_size) +
203 sizeof(data2_size) ));
204 return -1;
205 }
206
207 if (!data1_size || !data2_size || data1_size > MAX_DATA_BUF ||
208 data2_size > MAX_DATA_BUF ) {
209 log_debug("Header invalid data1_size=%u data2_size=%u\n",
210 data1_size, data2_size);
211 return -1;
212 }
213
214 data1 = malloc(data1_size);
215 if (!data1) {
216 log_debug("Could not allocate %d bytes\n", data1_size);
217 return -1;
218 }
219 data2 = malloc(data2_size);
220 if (!data2) {
221 free(data1);
222 log_debug("Could not allocate %d bytes\n", data2_size);
223 return -1;
224 }
225
226 req_data[0].iov_base = data1;
227 req_data[0].iov_len = data1_size;
228 req_data[1].iov_base = data2;
229 req_data[1].iov_len = data2_size;
230
231 while (((count = readv(fd, req_data, 2)) < 0) && (errno == EINTR));
232 if (count <= 0 || (size_t)count != (data1_size + data2_size) ||
233 data1[data1_size - 1] != '\0' || data2[data2_size - 1] != '\0') {
234 free(data1);
235 free(data2);
236 log_debug("Failed to read request data (%d)\n", (int)count);
237 return -1;
238 }
239
240 ret = process_request(fd, function, data1, data2);
241
242 free(data1);
243 free(data2);
244
245 return ret;
246 }
247
248 static int
add_pollfd(struct pollfd ** ufds,int * nfds,int connfd)249 add_pollfd(struct pollfd **ufds, int *nfds, int connfd)
250 {
251 int ii = 0;
252
253 /* First see if we can find an already invalidated ufd */
254 for (ii = 0; ii < *nfds; ii++) {
255 if ((*ufds)[ii].fd == -1)
256 break;
257 }
258
259 if (ii == *nfds) {
260 struct pollfd *tmp = (struct pollfd *)realloc(*ufds,
261 (*nfds+1)*sizeof(struct pollfd));
262 if (!tmp) {
263 syslog(LOG_ERR, "realloc failed for %d fds", *nfds+1);
264 return -1;
265 }
266
267 *ufds = tmp;
268 (*nfds)++;
269 }
270
271 (*ufds)[ii].fd = connfd;
272 (*ufds)[ii].events = POLLIN|POLLPRI;
273 (*ufds)[ii].revents = 0;
274
275 return 0;
276 }
277
278 static void
adj_pollfds(struct pollfd ** ufds,int * nfds)279 adj_pollfds(struct pollfd **ufds, int *nfds)
280 {
281 int ii, jj;
282
283 jj = 0;
284 for (ii = 0; ii < *nfds; ii++) {
285 if ((*ufds)[ii].fd != -1) {
286 if (jj < ii)
287 (*ufds)[jj] = (*ufds)[ii];
288 jj++;
289 }
290 }
291 *nfds = jj;
292 }
293
294 static int
process_events(struct pollfd ** ufds,int * nfds)295 process_events(struct pollfd **ufds, int *nfds)
296 {
297 int ii = 0;
298 int ret = 0;
299
300 for (ii = 0; ii < *nfds; ii++) {
301 short revents = (*ufds)[ii].revents;
302 int connfd = (*ufds)[ii].fd;
303
304 if (revents & (POLLIN | POLLPRI)) {
305 if (connfd == sockfd) {
306
307 /* Probably received a connection */
308 if ((connfd = accept(sockfd, NULL, NULL)) < 0) {
309 syslog(LOG_ERR, "accept() failed: %m");
310 return -1;
311 }
312
313 if (add_pollfd(ufds, nfds, connfd)) {
314 syslog(LOG_ERR,
315 "Failed to add fd (%d) to poll list\n",
316 connfd);
317 return -1;
318 }
319 } else {
320 ret = service_request(connfd);
321 if (ret) {
322 if (ret < 0) {
323 syslog(LOG_ERR,
324 "Servicing of request "
325 "failed for fd (%d)\n",
326 connfd);
327 }
328 /* Setup pollfd for deletion later. */
329 (*ufds)[ii].fd = -1;
330 close(connfd);
331 /* So we don't get bothered later */
332 revents = revents & ~(POLLHUP);
333 }
334 }
335 revents = revents & ~(POLLIN | POLLPRI);
336 }
337 if (revents & POLLHUP) {
338 log_debug("The connection with fd (%d) hung up\n",
339 connfd);
340
341 /* Set the pollfd up for deletion later. */
342 (*ufds)[ii].fd = -1;
343 close(connfd);
344
345 revents = revents & ~(POLLHUP);
346 }
347 if (revents) {
348 syslog(LOG_ERR, "Unknown/error events (%x) encountered"
349 " for fd (%d)\n", revents, connfd);
350
351 /* Set the pollfd up for deletion later. */
352 (*ufds)[ii].fd = -1;
353 close(connfd);
354 }
355
356 (*ufds)[ii].revents = 0;
357 }
358
359 /* Delete any invalidated ufds */
360 adj_pollfds(ufds, nfds);
361
362 return 0;
363 }
364
365 static void
366 process_connections(void) __attribute__ ((noreturn));
367
368 static void
process_connections(void)369 process_connections(void)
370 {
371 int ret = 0;
372 int nfds = 1;
373
374 struct pollfd *ufds = (struct pollfd *)malloc(sizeof(struct pollfd));
375 if (!ufds) {
376 syslog(LOG_ERR, "Failed to allocate a pollfd");
377 cleanup_exit(1);
378 }
379 ufds[0].fd = sockfd;
380 ufds[0].events = POLLIN|POLLPRI;
381 ufds[0].revents = 0;
382
383 while (1) {
384 if (restart_daemon) {
385 syslog(LOG_NOTICE, "Reload Translations");
386 finish_context_colors();
387 finish_context_translations();
388 if (init_translations()) {
389 syslog(LOG_ERR, "Failed to initialize label translations");
390 cleanup_exit(1);
391 }
392 if (init_colors()) {
393 syslog(LOG_ERR, "Failed to initialize color translations");
394 syslog(LOG_ERR, "No color information will be available");
395 }
396 restart_daemon = 0;
397 }
398
399 ret = poll(ufds, nfds, -1);
400 if (ret < 0) {
401 if (errno == EINTR) {
402 continue;
403 }
404 syslog(LOG_ERR, "poll() failed: %m");
405 cleanup_exit(1);
406 }
407
408 ret = process_events(&ufds, &nfds);
409 if (ret) {
410 syslog(LOG_ERR, "Error processing events");
411 cleanup_exit(1);
412 }
413 }
414 }
415
416 static void
417 sigterm_handler(int sig) __attribute__ ((noreturn));
418
419 static void
sigterm_handler(int UNUSED (sig))420 sigterm_handler(int UNUSED(sig))
421 {
422 cleanup_exit(0);
423 }
424
425 static void
sighup_handler(int UNUSED (sig))426 sighup_handler(int UNUSED(sig))
427 {
428 restart_daemon = 1;
429 }
430
431 static void
initialize(void)432 initialize(void)
433 {
434 struct sigaction act;
435 struct sockaddr_un addr;
436 struct rlimit rl ;
437
438 if (init_translations()) {
439 syslog(LOG_ERR, "Failed to initialize label translations");
440 cleanup_exit(1);
441 }
442 if (init_colors()) {
443 syslog(LOG_ERR, "Failed to initialize color translations");
444 syslog(LOG_ERR, "No color information will be available");
445 }
446
447 /* the socket will be unlinked when the daemon terminates */
448 act.sa_handler = sigterm_handler;
449 sigemptyset(&act.sa_mask);
450 sigaddset(&act.sa_mask, SIGINT);
451 sigaddset(&act.sa_mask, SIGQUIT);
452 sigaddset(&act.sa_mask, SIGTERM);
453 sigaddset(&act.sa_mask, SIGHUP);
454 act.sa_flags = 0;
455 sigaction(SIGINT, &act, NULL);
456 sigaction(SIGQUIT, &act, NULL);
457 sigaction(SIGTERM, &act, NULL);
458
459 /* restart the daemon on SIGHUP */
460 act.sa_handler = sighup_handler;
461 sigemptyset(&act.sa_mask);
462 sigaddset(&act.sa_mask, SIGINT);
463 sigaddset(&act.sa_mask, SIGQUIT);
464 sigaddset(&act.sa_mask, SIGTERM);
465 act.sa_flags = 0;
466 sigaction(SIGHUP, &act, NULL);
467
468 /* ignore SIGPIPE (in case a client terminates after sending request) */
469 act.sa_handler = SIG_IGN;
470 sigemptyset(&act.sa_mask);
471 act.sa_flags = 0;
472 sigaction(SIGPIPE, &act, NULL);
473
474 atexit(clean_exit);
475
476 sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
477 if (sockfd < 0) {
478 syslog(LOG_ERR, "socket() failed: %m");
479 cleanup_exit(1);
480 }
481
482 memset(&addr, 0, sizeof(addr));
483 addr.sun_family = AF_UNIX;
484 strncpy(addr.sun_path, SETRANS_UNIX_SOCKET, sizeof(addr.sun_path) - 1);
485
486 (void)unlink(SETRANS_UNIX_SOCKET);
487
488 if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
489 syslog(LOG_ERR, "bind() failed: %m");
490 cleanup_exit(1);
491 }
492
493 if (listen(sockfd, SOMAXCONN) < 0) {
494 syslog(LOG_ERR, "listen() failed: %m");
495 cleanup_exit(1);
496 }
497
498 if (chmod(SETRANS_UNIX_SOCKET, S_IRWXU | S_IRWXG | S_IRWXO)) {
499 syslog(LOG_ERR, "chmod() failed: %m");
500 cleanup_exit(1);
501 }
502
503 /* Raise the rlimit for file descriptors... */
504 rl.rlim_max = MAX_DESCRIPTORS;
505 rl.rlim_cur = MAX_DESCRIPTORS;
506 setrlimit(RLIMIT_NOFILE, &rl);
507
508 }
509
dropprivs(void)510 static void dropprivs(void)
511 {
512 cap_t new_caps;
513
514 new_caps = cap_init();
515 if (cap_set_proc(new_caps)) {
516 syslog(LOG_ERR, "Error dropping capabilities, aborting: %s\n",
517 strerror(errno));
518 cleanup_exit(-1);
519 }
520 cap_free(new_caps);
521 }
522
usage(char * program)523 static void usage(char *program)
524 {
525 printf("%s [-f] [-h] \n", program);
526 }
527
528 int
main(int argc,char * argv[])529 main(int argc, char *argv[])
530 {
531 int opt;
532 int do_fork = 1;
533 while ((opt = getopt(argc, argv, "hf")) > 0) {
534 switch (opt) {
535 case 'f':
536 do_fork = 0;
537 break;
538 case 'h':
539 usage(argv[0]);
540 exit(0);
541 break;
542 case '?':
543 usage(argv[0]);
544 exit(-1);
545 }
546 }
547
548 #ifndef DEBUG
549 /* Make sure we are root */
550 if (getuid() != 0) {
551 syslog(LOG_ERR, "You must be root to run this program.\n");
552 return 4;
553 }
554 #endif
555
556 openlog(SETRANSD_PROGNAME, 0, LOG_DAEMON);
557 syslog(LOG_NOTICE, "%s starting", argv[0]);
558
559 initialize();
560
561 #ifndef DEBUG
562 dropprivs();
563
564 /* run in the background as a daemon */
565 if (do_fork && daemon(0, 0)) {
566 syslog(LOG_ERR, "daemon() failed: %m");
567 cleanup_exit(1);
568 }
569 #endif
570
571 syslog(LOG_NOTICE, "%s initialized", argv[0]);
572 process_connections();
573
574 /* we should never get here */
575 return 1;
576 }
577
578