Created
September 19, 2015 14:35
-
-
Save ralt/d2ef1415687347eaaf45 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
The service, run as root, will listen on the /var/run/hermes.sock | |
socket. The only command it will understand is a four-bytes long | |
command, being a number mapping an action. | |
Really, only one action is needed: get me the content of the | |
connected hermes device. Having 4 bytes is cheap enough, and leaves | |
other possibilities later on, if needed. | |
The "get me the content" command needs the value "1". It can return | |
one of these 2 results: | |
- first byte 0, means "there is no hermes device" | |
- first byte 1, means "there is an hermes device". It is followed | |
by the 128-bytes token, then another 128-bytes token that is the | |
new one. | |
*/ | |
#include <stdio.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <sys/un.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdbool.h> | |
#include <stdint.h> | |
#include <unistd.h> | |
#include <sys/stat.h> | |
#include <grp.h> | |
#include <errno.h> | |
#include <glob.h> | |
#include <arpa/inet.h> | |
#include <linux/random.h> | |
#define FINGERPRINT_LENGTH 5 | |
#define COMMAND_GET_KEYS 1 | |
#define RET_NO_HERMES_DEVICE 0 | |
#define RET_HERMES_DEVICE_FOUND 1 | |
#define TOKEN_LENGTH 128 | |
static int globerr(const char*, int); | |
static bool is_block_device(const char*); | |
static bool has_hermes_fingerprint(const char*); | |
static bool is_hermes_device(const char*); | |
static size_t handle_command(uint32_t, uint8_t**, uint8_t**); | |
static size_t read_hermes_device(char*, uint8_t**); | |
static void generate_new_token(uint8_t **token, char *path); | |
int main(int argc, char *argv[]) | |
{ | |
const char *socket_path = "/var/run/hermes.sock"; | |
struct sockaddr_un addr; | |
uint32_t command; | |
int fd, rc, cl; | |
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { | |
perror("socket"); | |
exit(EXIT_FAILURE); | |
} | |
memset(&addr, 0, sizeof(addr)); | |
addr.sun_family = AF_UNIX; | |
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); | |
unlink(socket_path); | |
if (bind(fd, (const struct sockaddr*) &addr, sizeof(addr)) == -1) | |
{ | |
perror("bind"); | |
exit(EXIT_FAILURE); | |
} | |
if (chmod(socket_path, S_IRGRP | S_IWGRP) == -1) | |
{ | |
perror("chmod"); | |
exit(EXIT_FAILURE); | |
} | |
struct group *hermes_group = getgrnam("hermes"); | |
if (hermes_group == NULL) | |
{ | |
perror("hermes group not found"); | |
exit(EXIT_FAILURE); | |
} | |
if (chown(socket_path, 0, hermes_group->gr_gid) == -1) | |
{ | |
perror("chown"); | |
exit(EXIT_FAILURE); | |
} | |
if (listen(fd, 5) == -1) | |
{ | |
perror("listen"); | |
exit(EXIT_FAILURE); | |
} | |
while (true) | |
{ | |
if ((cl = accept(fd, NULL, NULL)) == -1) | |
{ | |
perror("accept"); | |
continue; | |
} | |
while ((rc = read(cl, &command, sizeof(command))) > 0) | |
{ | |
uint8_t *buffer; | |
uint8_t *new_token; | |
new_token = malloc(sizeof(uint8_t) * TOKEN_LENGTH); | |
if (new_token == NULL) | |
{ | |
perror("malloc new_token"); | |
exit(EXIT_FAILURE); | |
} | |
/* buffer_length is the buffer *without* the | |
new_token length. */ | |
size_t buffer_length = handle_command(command, | |
&buffer, | |
&new_token); | |
uint32_t data_length = htonl(buffer_length); | |
if (write(cl, &data_length, sizeof(data_length)) != | |
sizeof(data_length)) | |
{ | |
perror("write data_length"); | |
exit(EXIT_FAILURE); | |
} | |
if (write(cl, buffer, buffer_length) != buffer_length) | |
{ | |
perror("write buffer"); | |
exit(EXIT_FAILURE); | |
} | |
if (buffer[0] == RET_HERMES_DEVICE_FOUND) | |
{ | |
if (write(cl, new_token, TOKEN_LENGTH) != TOKEN_LENGTH) | |
{ | |
perror("write new_token"); | |
exit(EXIT_FAILURE); | |
} | |
} | |
free(new_token); | |
free(buffer); | |
} | |
if (rc == -1) | |
{ | |
perror("read"); | |
exit(EXIT_FAILURE); | |
} | |
if (rc == 0) | |
{ | |
continue; | |
} | |
} | |
if (close(fd) != 0) | |
{ | |
exit(EXIT_FAILURE); | |
} | |
return EXIT_SUCCESS; | |
} | |
static void generate_new_token(uint8_t **token, char *path) | |
{ | |
FILE *fd; | |
size_t bytes_read; | |
/* We don't really want to block -- a 128-bytes token is fine | |
as is. */ | |
if (getrandom(*token, TOKEN_LENGTH, GRND_NONBLOCK) != TOKEN_LENGTH) | |
{ | |
perror("getrandom"); | |
exit(EXIT_FAILURE); | |
} | |
fd = fopen(path, "wb"); | |
if (fd == NULL) | |
{ | |
fprintf(stderr, "%s: can't read %s\n", strerror(errno), path); | |
exit(EXIT_FAILURE); | |
} | |
if ((fseek(fd, FINGERPRINT_LENGTH, 0)) != 0) | |
{ | |
perror("fseek"); | |
exit(EXIT_FAILURE); | |
} | |
bytes_read = fwrite(*token, | |
sizeof(uint8_t), | |
TOKEN_LENGTH, | |
fd); | |
if (bytes_read != TOKEN_LENGTH) | |
{ | |
perror("write"); | |
exit(EXIT_FAILURE); | |
} | |
if (fclose(fd) != 0) | |
{ | |
perror("fclose"); | |
exit(EXIT_FAILURE); | |
} | |
} | |
static size_t handle_command(uint32_t command, uint8_t **buffer, uint8_t **new_token) | |
{ | |
glob_t files; | |
int retval; | |
size_t ret; | |
bool device_found = false; | |
char *hermes_device; | |
retval = glob("/dev/*", GLOB_ERR | GLOB_NOSORT, globerr, &files); | |
if (retval != 0) | |
{ | |
return false; | |
} | |
for (size_t i = 0; i < files.gl_pathc; i++) | |
{ | |
if (is_hermes_device(files.gl_pathv[i])) | |
{ | |
device_found = true; | |
hermes_device = malloc(strlen(files.gl_pathv[i]) * | |
sizeof(uint8_t)); | |
if (hermes_device == NULL) | |
{ | |
perror("malloc hermes_device"); | |
exit(EXIT_FAILURE); | |
} | |
memcpy(hermes_device, | |
files.gl_pathv[i], | |
strlen(files.gl_pathv[i]) + 1); | |
} | |
} | |
*buffer = malloc(sizeof(uint8_t)); | |
if (*buffer == NULL) | |
{ | |
perror("malloc buffer"); | |
exit(EXIT_FAILURE); | |
} | |
if (!device_found) | |
{ | |
*buffer[0] = RET_NO_HERMES_DEVICE; | |
ret = 1; | |
} | |
if (device_found) | |
{ | |
*buffer[0] = RET_HERMES_DEVICE_FOUND; | |
ret = read_hermes_device(hermes_device, buffer); | |
generate_new_token(new_token, hermes_device); | |
free(hermes_device); | |
} | |
globfree(&files); | |
return ret; | |
} | |
static size_t read_hermes_device(char *path, uint8_t **buffer) | |
{ | |
size_t offset = 1; | |
FILE *fd; | |
size_t bytes_read; | |
uint8_t token[TOKEN_LENGTH]; | |
uint32_t ret = 0; | |
fd = fopen(path, "rb"); | |
if (fd == NULL) | |
{ | |
fprintf(stderr, "%s: can't read %s\n", strerror(errno), path); | |
return false; | |
} | |
if ((fseek(fd, FINGERPRINT_LENGTH, 0)) != 0) | |
{ | |
fprintf(stderr, "%s: can't fseek %d bytes in %s\n", | |
strerror(errno), FINGERPRINT_LENGTH, path); | |
goto safe_close; | |
} | |
bytes_read = fread(token, | |
sizeof(uint8_t), | |
TOKEN_LENGTH, | |
fd); | |
if (bytes_read != TOKEN_LENGTH) | |
{ | |
fprintf(stderr, "%s: can't read the token\n", strerror(errno)); | |
goto safe_close; | |
} | |
/* Now copy all the data to the buffer */ | |
ret = TOKEN_LENGTH; | |
/* Let's not forget that there is 1 already-malloced byte for the command. */ | |
*buffer = realloc(*buffer, sizeof(uint8_t) * (offset + ret)); | |
if (*buffer == NULL) | |
{ | |
fprintf(stderr, "%s: can't malloc buffer\n", strerror(errno)); | |
exit(EXIT_FAILURE); | |
} | |
/* token */ | |
memcpy(*buffer + offset, token, TOKEN_LENGTH); | |
safe_close: | |
if (fclose(fd) != 0) | |
{ | |
fprintf(stderr, "%s: can't close %s\n", strerror(errno), path); | |
return false; | |
} | |
return ret + sizeof(uint8_t); | |
} | |
static int globerr(const char *path, int eerrno) | |
{ | |
return true; | |
} | |
static bool is_block_device(const char *path) | |
{ | |
int retval; | |
struct stat sb; | |
retval = stat(path, &sb); | |
if (retval == -1) | |
{ | |
return false; | |
} | |
return S_ISBLK(sb.st_mode); | |
} | |
static bool has_hermes_fingerprint(const char *path) | |
{ | |
bool ret; | |
FILE *fd; | |
size_t bytes_read; | |
uint8_t bytes[FINGERPRINT_LENGTH]; | |
const uint8_t expected_bytes[FINGERPRINT_LENGTH] = { 82, 111, 98, 105, 110 }; | |
fd = fopen(path, "rb"); | |
if (fd == NULL) | |
{ | |
fprintf(stderr, "%s: can't read %s\n", strerror(errno), path); | |
return false; | |
} | |
bytes_read = fread(&bytes, sizeof(uint8_t), FINGERPRINT_LENGTH, fd); | |
if (bytes_read < 1) | |
{ | |
fprintf(stderr, "%s: can't read enough bytes\n", strerror(errno)); | |
ret = false; | |
goto safe_exit; | |
} | |
for (size_t i = 0; i < bytes_read; i++) | |
{ | |
if (bytes[i] != expected_bytes[i]) | |
{ | |
ret = false; | |
goto safe_exit; | |
} | |
} | |
ret = true; | |
goto safe_exit; | |
safe_exit: | |
if (fclose(fd) != 0) | |
{ | |
fprintf(stderr, "%s: can't close %s\n", strerror(errno), path); | |
return false; | |
} | |
return ret; | |
} | |
static bool is_hermes_device(const char *path) | |
{ | |
if (!is_block_device(path)) | |
{ | |
return false; | |
} | |
if (!has_hermes_fingerprint(path)) | |
{ | |
return false; | |
} | |
return true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment