Skip to content

Instantly share code, notes, and snippets.

@ralt
Created September 19, 2015 14:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ralt/d2ef1415687347eaaf45 to your computer and use it in GitHub Desktop.
Save ralt/d2ef1415687347eaaf45 to your computer and use it in GitHub Desktop.
/*
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