#include #include #include #include #include #include #include #include #include #include #include #include #include int get_policy_data(const char *, void **); void generate_error(int, const char *, const char *); void process_reload_signal(int); int create_socket(unsigned short); void parse_requests(int, void *, int, const char *, const char *); int create_fdset(fd_set *, int *, unsigned, int); int * array_add(int *, unsigned, int); int * array_remove(int *, unsigned, int); void send_policy_info(int, void *, unsigned); volatile sig_atomic_t reload_next; int main (int argc, char ** argv) { if (argc != 2) generate_error(1, *argv, argv[1]); void * policydata; int policylength = get_policy_data(argv[1], &policydata); if (policylength < 0) generate_error(-policylength, *argv, argv[1]); reload_next = 0; int socketnumber = create_socket(843); if (socketnumber < 0) generate_error(2, *argv, argv[1]); if (signal(SIGHUP, &process_reload_signal) == SIG_ERR) fprintf(stderr, "%s: could not install SIGHUP handler\n", *argv); parse_requests(socketnumber, policydata, policylength, *argv, argv[1]); close(socketnumber); return 0; } int get_policy_data (const char * filename, void ** data) { int fd = open(filename, 0); if (fd < 0) switch (errno) { case EACCES: return -3; case ENOENT: case ENOTDIR: return -4; default: return -5; } struct stat statdata; if(fstat(fd, &statdata)) { close(fd); return -5; } int remainder = statdata.st_size; int size = remainder; *data = malloc(size + 1); char * readp = *data; int readsize; while (remainder > 0) { readsize = read(fd, readp, remainder); if (readsize > 0) { remainder -= readsize; readp += readsize; continue; } if (!readsize) { size = readp - ((char *) (*data)); break; } if ((errno == EINTR) || (errno = EAGAIN)) continue; close(fd); if (errno == EISDIR) return -6; return -5; } close(fd); ((char *) (*data))[size] = 0; return size + 1; } void generate_error (int error, const char * programname, const char * filename) { switch (error) { case 1: fprintf(stderr, "usage: %s policyfile\n", programname); break; case 2: fprintf(stderr, "%s: could not create listening socket on port 843\n", programname); break; case 3: fprintf(stderr, "%s: permission denied when accessing policy file %s\n", programname, filename); break; case 4: fprintf(stderr, "%s: policy file %s does not exist\n", programname, filename); break; case 5: fprintf(stderr, "%s: unknown error accessing policy file %s\n", programname, filename); break; case 6: fprintf(stderr, "%s: policy file %s is actually a directory\n", programname, filename); break; } exit(error); } void process_reload_signal (int signalnumber) { reload_next = 1; } int create_socket (unsigned short port) { int number = socket(AF_INET, SOCK_STREAM || SOCK_NONBLOCK, 0); if (number < 0) return -1; struct sockaddr_in sa; sa.sin_family = AF_INET; sa.sin_port = htons(port); sa.sin_addr.s_addr = INADDR_ANY; if (bind(number, (struct sockaddr *) &sa, sizeof sa)) { close(number); return -1; } if (listen(number, 128)) { close(number); return -1; } return number; } void parse_requests (int socketnumber, void * policydata, int policylength, const char * programname, const char * filename) { struct timeval tv = {.tv_sec = 5, .tv_usec = 0}; struct timeval tvs; int cont = 1; int result; fd_set fdset; int * clients = NULL; unsigned clientcount = 0; int * copy; unsigned current, limit; while (cont) { result = create_fdset(&fdset, clients, clientcount, socketnumber); tvs = tv; result = select(result, &fdset, NULL, NULL, &tvs); if (reload_next) { void * newdata; int status = get_policy_data(filename, &newdata); if (status < 0) fprintf(stderr, "%s: could not reload policy data from %s\n", programname, filename); else { free(policydata); policydata = newdata; policylength = status; } reload_next = 0; } if (result <= 0) continue; copy = malloc(clientcount * sizeof(int)); memcpy(copy, clients, clientcount * sizeof(int)); limit = clientcount; if (FD_ISSET(socketnumber, &fdset)) { int newsocket = accept(socketnumber, NULL, NULL); if (newsocket > 0) clients = array_add(clients, clientcount ++, newsocket); } for (current = 0; current < limit; current ++) if (FD_ISSET(copy[current], &fdset)) { char buffer[1024]; int reception = recv(copy[current], buffer, 1024, MSG_DONTWAIT); if (!reception) { close(copy[current]); clients = array_remove(clients, clientcount --, copy[current]); } if (reception <= 0) continue; unsigned short pos; for (pos = 0; pos < reception; pos ++) if (!buffer[pos]) send_policy_info(copy[current], policydata, policylength); } free(copy); } } int create_fdset (fd_set * fdset, int * fds, unsigned count, int listener) { int * max = fds + count; int * current; int maxfd = listener + 1; FD_ZERO(fdset); FD_SET(listener, fdset); for (current = fds; current < max; current ++) { if (*current >= maxfd) maxfd = *current + 1; FD_SET(*current, fdset); } return maxfd; } int * array_add (int * array, unsigned length, int new) { array = realloc(array, sizeof(int) * (length + 1)); array[length] = new; return array; } int * array_remove (int * array, unsigned length, int item) { if (!length) { free(array); return NULL; } unsigned position; for (position = 0; position < length; position ++) if (array[position] == item) break; if (position >= length) return array; array[position] = array[-- length]; return realloc(array, sizeof(int) * length); } void send_policy_info (int destsocket, void * data, unsigned length) { char * pending = data; unsigned size = length; int status, retries = 0; while (size) { status = send(destsocket, pending, size, MSG_NOSIGNAL); if (status >= 0) { retries = 0; size -= status; pending += status; continue; } if (errno == EPIPE) return; retries ++; if (retries >= 3) return; } }