#include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SOCKET_PATH "selinux.filename.label" #define MAX_REQUEST_LEN 8192 #define MAX_BACKLOG 100 #define RECV_TIMEOUT 250 /* milliseconds */ #define offsetof(st, m) ((size_t)((char *)&((st *)0)->m - (char *)0)) /* * Requests are send to the server as single packet of the form: * snprintf("%x %s", mode, pathname); * Responses are sent to the client as string representing the raw context */ static void handle_request(int fd) { int rc; ssize_t len; char buffer[MAX_REQUEST_LEN + 1]; char *pathname; unsigned long model; mode_t mode; security_context_t con = NULL; struct pollfd pollfd; pollfd.fd = fd; pollfd.events = POLLIN; /* don't hang forever if the client sux */ rc = poll(&pollfd, 1, RECV_TIMEOUT); if (rc <= 0) goto out; /* get the request */ len = recv(fd, buffer, sizeof(buffer), 0); if (len <= 0) goto out; /* just to be safe! */ buffer[MAX_REQUEST_LEN] = '\0'; /* * Yes, I could use sscanf("%x %s") but it would needlessly duplicate * pathname. Whereas strtoul leaves me a pointer to it in buffer */ /* parse the mode portion to a mode_t */ errno = 0; /* To distinguish success/failure after call */ model = strtoul(buffer, &pathname, 16); if ((errno == ERANGE && model == ULONG_MAX) || (errno != 0 && model == 0)) goto out; mode = (mode_t)model; /* skip whitespace between mode and pathname */ while (pathname < &buffer[MAX_REQUEST_LEN - 1] && isspace(pathname[0])) pathname++; /* pathname now points at the pathname part of the buffer! */ if (pathname >= &buffer[MAX_REQUEST_LEN - 1] || !pathname[0]) goto out; /* do the lookup */ rc = matchpathcon(pathname, mode, &con); if (rc < 0) { if (errno == ENOENT) { con = strdup("<>"); if (!con) goto out; } else { goto out; } } /* send the response */ len = send(fd, con, strlen(con) + 1, 0); if (len <= 0) goto out; #ifdef VERBOSE printf("pathname=%s mode=%x context=%s\n", pathname, mode, con); #endif out: free(con); close(fd); } static void usage(char *progname) { fprintf(stderr, "usage: %s [-f,--foreground] [-s,--socket fd]\n", progname); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int rc, clientfd, c; int serverfd = -1; size_t len; struct sockaddr_un sockaddr_un; int option_index = 0; int foreground = 0; static struct option long_options[] = { {"foreground", no_argument, 0, 'f'}, {"socket", required_argument, 0, 's'}, {0, 0, 0, 0} }; while (1) { c = getopt_long(argc, argv, "fs:", long_options, &option_index); if (c == -1) break; switch (c) { case 'f': foreground = 1; break; case 's': serverfd = strtol(optarg, NULL, 0); if (serverfd < 0) { fprintf(stderr, "invalid socket fd\n"); usage(argv[0]); } break; case '?': default: usage(argv[0]); break; }; } c = sd_listen_fds(0); if (c > 1) { fprintf(stderr, "Too many file descriptors received.\n"); exit(EXIT_FAILURE); } else if (c == 1) serverfd = SD_LISTEN_FDS_START + 0; if (!foreground) daemon(0, 0); /* init the label regex db */ rc = matchpathcon_init(NULL); if (rc < 0) { perror("matchpathcon_init"); return errno; } /* if we didn't get the serverfd from systemd or command line, do it outselves */ if (serverfd == -1) { /* create the socket on which we will listen */ serverfd = socket(AF_UNIX, SOCK_SEQPACKET, 0); if (serverfd < 0) { perror("socket"); return errno; } /* set up out address */ sockaddr_un.sun_family = AF_UNIX; sockaddr_un.sun_path[0] = '\0'; memcpy(&sockaddr_un.sun_path[1], SOCKET_PATH, strlen(SOCKET_PATH)); /* calculating the length of the address is magic since it starts with nul. * there be dragons in here! */ len = offsetof(struct sockaddr_un, sun_path) + strlen(SOCKET_PATH) + 1; /* bind the socket to the address */ rc = bind(serverfd, (const struct sockaddr *)&sockaddr_un, len); if (rc < 0) { perror("bind"); return errno; } /* start listening for connections */ rc = listen(serverfd, MAX_BACKLOG); if (rc < 0) { perror("listen"); return errno; } } /* if (serverfd == -1) */ /* work yo! */ while (1) { clientfd = accept(serverfd, NULL, 0); if (clientfd < 0) { perror("accept"); return errno; } handle_request(clientfd); } close(serverfd); return rc; }