summaryrefslogtreecommitdiffstats
path: root/core/modules/run-virt/pw_daemon.c
diff options
context:
space:
mode:
authorJonathan Bauer2016-12-23 13:12:09 +0100
committerJonathan Bauer2016-12-23 13:12:09 +0100
commit6806ae4a850fc7785a8c05304237cf53b5b8f951 (patch)
treeb1dd8413d6c7b9a250251da7f0d49bb52b4ddc57 /core/modules/run-virt/pw_daemon.c
parentwrong kernel version variable used (diff)
downloadmltk-6806ae4a850fc7785a8c05304237cf53b5b8f951.tar.gz
mltk-6806ae4a850fc7785a8c05304237cf53b5b8f951.tar.xz
mltk-6806ae4a850fc7785a8c05304237cf53b5b8f951.zip
merge with latest dev version (tm-scripts commit f5a59daf8d70a9027118292cd40b18c221897408)
Diffstat (limited to 'core/modules/run-virt/pw_daemon.c')
-rw-r--r--core/modules/run-virt/pw_daemon.c315
1 files changed, 315 insertions, 0 deletions
diff --git a/core/modules/run-virt/pw_daemon.c b/core/modules/run-virt/pw_daemon.c
new file mode 100644
index 00000000..768a5b00
--- /dev/null
+++ b/core/modules/run-virt/pw_daemon.c
@@ -0,0 +1,315 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <linux/random.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/prctl.h>
+#include <sys/un.h>
+
+static const ssize_t KEYLEN = 16;
+
+static pid_t udpPid = -1;
+static char *username = NULL;
+static uint8_t *passwordEnc = NULL;
+static size_t passwordLen = 0;
+static uint8_t *key1 = NULL, *key2 = NULL;
+static char *key1s = NULL, *key2s = NULL;
+
+static int mode_daemon();
+static int mode_query(const char *socketPath);
+static void sig_handler(int sig);
+static int setup_vars(const char *envuser, const char *envpass);
+static uint8_t* keygen();
+static char* bin2hex(uint8_t* bin, size_t len);
+static uint8_t* xorString(const char* inputText, const uint8_t* key);
+static int init_udp();
+
+int main(int argc, char **argv)
+{
+ if (argc > 1 && strcmp(argv[1], "--daemon") == 0) {
+ return mode_daemon();
+ } else if (argc > 2 && strcmp(argv[1], "--query") == 0) {
+ return mode_query(argv[2]);
+ }
+ fprintf(stderr, "Invalid call. Use --daemon or --query\n");
+ return 1;
+}
+
+static int mode_query(const char *socketPath)
+{
+ int fd;
+ struct sockaddr_un remote;
+ struct timeval tv;
+ char buffer[200];
+ ssize_t ret;
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1) {
+ perror("Cannot create unix socket for connecting");
+ return 1;
+ }
+ memset(&remote, 0, sizeof(remote));
+ remote.sun_family = AF_UNIX;
+ strncpy(remote.sun_path, socketPath, sizeof(remote.sun_path)-1);
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+ if (connect(fd, (struct sockaddr*)&remote, sizeof(remote)) == -1) {
+ perror("Cannot connect to pw daemon");
+ return 1;
+ }
+ if (write(fd, "GET", 3) == -1) {
+ perror("Writing to pw daemon failed");
+ return 1;
+ }
+ ret = read(fd, buffer, sizeof(buffer)-1);
+ if (ret == -1) {
+ perror("Reading from pw daemon failed");
+ return 1;
+ }
+ if (ret < 1 || (size_t)ret > sizeof(buffer)-1) {
+ fprintf(stderr, "Reply from pw daemon has invalid length\n");
+ return 1;
+ }
+ if (buffer[ret-1] != '\n') {
+ fprintf(stderr, "Corrupted reply received from pw daemon\n");
+ return 1;
+ }
+ buffer[ret] = '\0';
+ printf("%s", buffer);
+ return 0;
+}
+
+static int mode_daemon()
+{
+ int listenFd, udpPort = -1;
+ struct sockaddr_un addr;
+ struct sigaction sig;
+ const char *envuser = getenv("USERNAME");
+ const char *envpass = getenv("PASSWORD");
+ const char *pwsocket = getenv("PWSOCKET");
+ memset(&addr, 0, sizeof(addr));
+ memset(&sig, 0, sizeof(sig));
+ if (envuser == NULL) {
+ fprintf(stderr, "USERNAME not set\n");
+ return 1;
+ }
+ if (envpass == NULL) {
+ fprintf(stderr, "PASSWORD not set\n");
+ return 1;
+ }
+ if (pwsocket == NULL) {
+ fprintf(stderr, "PWSOCKET not set\n");
+ return 1;
+ }
+ if (setup_vars(envuser, envpass) == -1) {
+ fprintf(stderr, "Error setting up variables\n");
+ return 1;
+ }
+ listenFd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (listenFd == -1) {
+ perror("Could not create unix socket");
+ return 1;
+ }
+ // Change permissions before bind, so it will be created with
+ // the right ones right away
+ if (fchmod(listenFd, S_IRUSR | S_IWUSR) == -1) {
+ perror("Cannot set permissions on socket fd prior to binding");
+ return 1;
+ }
+ remove(pwsocket);
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, pwsocket, sizeof(addr.sun_path)-1);
+ if (bind(listenFd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
+ perror("Could not bind unix socket");
+ return 1;
+ }
+ if (listen(listenFd, 10) == -1) {
+ perror("Cannot listen on unix socket");
+ return 1;
+ }
+ // Mainloop
+ sig.sa_handler = &sig_handler;
+ sigaction(SIGCHLD, &sig, NULL);
+ for (;;) {
+ struct sockaddr_un remote;
+ socklen_t len = sizeof(remote);
+ int fd = accept(listenFd, (struct sockaddr*)&remote, &len);
+ if (fd != -1) {
+ if (udpPort == -1) {
+ udpPort = init_udp();
+ }
+ // Success, handle client
+ pid_t child = fork();
+ if (child == 0) {
+ // This is the child
+ ssize_t ret;
+ char buffer[200];
+ ret = read(fd, buffer, sizeof(buffer));
+ if (ret >= 3 && strncmp(buffer, "GET", 3) == 0) {
+ snprintf(buffer, sizeof(buffer), "%d\t%s\t%s\t%s\n", udpPort, key1s, key2s, username);
+ ret = write(fd, buffer, strlen(buffer));
+ }
+ close(fd);
+ return 0;
+ } else {
+ // Parent, close child fd
+ close(fd);
+ }
+ } else {
+ // Error?
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR || errno == ECONNABORTED)
+ continue;
+ perror("Fatal accept error, bailing out");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void sig_handler(int sig)
+{
+ pid_t p;
+ int status = sig; // Mute unused warning
+ while ((p = waitpid(-1, &status, WNOHANG)) > 0) {
+ if (p == udpPid) {
+ fprintf(stderr, "UDP listener died!\n");
+ exit(1);
+ }
+ }
+}
+
+static int setup_vars(const char *envuser, const char *envpass)
+{
+ srand((unsigned int)getpid() ^ (unsigned int)time(NULL));
+ key1 = keygen();
+ key2 = keygen();
+ key1s = bin2hex(key1, (size_t)KEYLEN);
+ key2s = bin2hex(key2, (size_t)KEYLEN);
+ username = strdup(envuser);
+ passwordEnc = xorString(envpass, key2);
+ passwordLen = strlen(envpass) + 2; // +2 for 2byte length prefix
+ if (key1s == NULL || key2s == NULL || username == NULL || passwordEnc == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+static uint8_t* keygen()
+{
+ ssize_t done = 0, ret;
+ uint8_t *key = malloc(KEYLEN);
+ int entropy;
+ int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
+ if (fd != -1) {
+ if (ioctl(fd, RNDGETENTCNT, &entropy) == 0 && entropy > 0) { //Make sure we opened a random device
+ while (done < KEYLEN) {
+ ret = read(fd, key + done, (size_t)(KEYLEN - done));
+ if (ret == -1) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ if (ret == 0)
+ break;
+ done += ret;
+ }
+ }
+ close(fd);
+ fprintf(stderr, "Got %d bytes from urandom\n", (int)done);
+ }
+ while (done < KEYLEN) {
+ key[done++] = (char)(rand() & 0xff);
+ }
+ return key;
+}
+
+static uint8_t* xorString(const char* inputText, const uint8_t* key)
+{
+ uint8_t *text = (uint8_t*)inputText;
+ size_t len = strlen(inputText);
+ size_t i;
+ uint8_t *retval = malloc(len + 2);
+ uint8_t *ptr = retval + 2;
+ retval[0] = (uint8_t)(len & 0xff00) >> 8;
+ retval[1] = (uint8_t)(len & 0xff);
+ for (i = 0; i < len; ++i) {
+ ptr[i] = text[i] ^ key[i % KEYLEN];
+ }
+ return retval;
+}
+
+static char* bin2hex(uint8_t* bin, size_t len)
+{
+ static const char hexconvtab[] = "0123456789abcdef";
+ char *retval = malloc(len * 2 + 1);
+ size_t i;
+ for (i = 0; i < len; ++i) {
+ retval[i*2] = hexconvtab[bin[i] >> 4];
+ retval[i*2+1] = hexconvtab[bin[i] & 0xf];
+ }
+ retval[i*2] = '\0';
+ return retval;
+}
+
+static int init_udp()
+{
+ uint16_t port = 0;
+ int fd;
+ int tries = 0;
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd == -1) {
+ perror("Cannot create udp socket");
+ return -1;
+ }
+ for (;;) {
+ port = (uint16_t)(40000 + rand() % 20000);
+ struct sockaddr_in local;
+ local.sin_family = AF_INET;
+ local.sin_port = htons((uint16_t)port);
+ local.sin_addr.s_addr = INADDR_ANY;
+ if (bind(fd, (struct sockaddr*)&local, sizeof(local)) == -1) {
+ if (++tries > 100) {
+ perror("Cannot bind udp socket");
+ close(fd);
+ return -1;
+ }
+ continue;
+ }
+ break;
+ }
+ udpPid = fork();
+ if (udpPid == -1) {
+ perror("Forking udp listener failed");
+ close(fd);
+ return -1;
+ }
+ if (udpPid != 0) {
+ close(fd);
+ return port;
+ }
+ // Child
+ prctl(PR_SET_PDEATHSIG, SIGTERM);
+ for (;;) {
+ struct sockaddr_in remote;
+ socklen_t remoteLen = sizeof(remote);
+ uint8_t buffer[KEYLEN];
+ ssize_t ret = recvfrom(fd, buffer, KEYLEN, 0, (struct sockaddr*)&remote, &remoteLen);
+ if (ret == KEYLEN && memcmp(key1, buffer, KEYLEN) == 0) {
+ if (sendto(fd, passwordEnc, passwordLen, 0, (struct sockaddr*)&remote, sizeof(remote)) == -1) {
+ perror("Could not send password to remote peer");
+ }
+ }
+ }
+}
+