Skip to content
Snippets Groups Projects
auxiliary.c 9.19 KiB
Newer Older
/**
 * Use a fifo to signal that we want to stop the api server.
 */
void stop_server()
{
    printf("Stopping cryptops-api server...\n");
    int fifo;
    char fifo_path[] = FIFO_PATH;
    fifo = open(fifo_path, O_WRONLY);
    char * msg = "stop";
    write(fifo, msg, strlen(msg) + 1);
}

Maarten de Waard's avatar
Maarten de Waard committed
/**
 * Read the contents of an already opened file into a string
 * @param   file    The file as opened by fopen with "rb"
 * @return          Contents of the file
 */
char * read_from_file(FILE * file)
{
    char buf[100];
    char * str = NULL;
    char * temp = NULL;
    // start with size of 1 to make room for null terminator
    unsigned int size = 1;
Maarten de Waard's avatar
Maarten de Waard committed
    unsigned int strlength;
    if (file)
    {
        while (fgets(buf, sizeof(buf), file) != NULL)
        {
Maarten de Waard's avatar
Maarten de Waard committed
            strlength = strlen(buf);
            // allocate room for the buf that gets appended
            temp = realloc(str, size + strlength);
            if (temp == NULL)
            {
                printf("Could not allocate memory for file reading\n");
                return NULL;
            }
            else
            {
Maarten de Waard's avatar
Maarten de Waard committed
                str = temp;
            }
            // append buffer to str
            strcpy(str + size - 1, buf);
Maarten de Waard's avatar
Maarten de Waard committed
        }
        pclose(file);
    }
    return str;
}

/**
 * Read a file completely into a string.
 * @param  filename Path to the file to read.
 * @return          Contents of the file.
 */
char * read_file(const char * filename)
{
    if (filename != NULL)
    {
        FILE * file = fopen(filename, "rb");
        return read_from_file(file);
/**
 * Respond to the request with a simple json structure '{$field: $value}'.
 * @param  response    response struct to use
 * @param  http_status HTTP status code to return
 * @param  field       name of the json field to return
 * @param  value       json value to return
int send_simple_response(struct _u_response * response, int http_status,
    const char * field, const char * value)
{
    json_t * json_body = NULL;
    json_body = json_object();
    json_object_set_new(json_body, field, json_string(value));
    ulfius_set_json_body_response(response, http_status, json_body);
    json_decref(json_body);
    return U_CALLBACK_CONTINUE;
}

/**
 * Determine the size of a block device.
 * @param  device_path Path to the block device.
 * @return             Size of the device in bytes.
 */
unsigned long device_size(char * device_path)
{
    int fd;
    unsigned long numblocks = 0;

    fd = open(device_path, O_RDONLY);
    ioctl(fd, BLKGETSIZE, &numblocks);
    close(fd);
    printf("Number of blocks: %lu, this makes %.3f GB\n",
        numblocks, (double)numblocks * 512.0 / (1024 * 1024 * 1024));
    return (numblocks * 512);
}

/**
 * Determine available memory.
 * @return Available memory in bytes.
 */
unsigned long available_memory()
{
  struct sysinfo myinfo;
  unsigned long free_bytes;
  sysinfo(&myinfo);
  free_bytes = myinfo.mem_unit * myinfo.freeram;
  printf("Free memory: %lu B, %lu MB\n",
      free_bytes, free_bytes / 1024 / 1024);
  return free_bytes;
}

/**
 * Determine the usage of the given filesystem.
 * @param  path Path of the mounted filesystem.
 * @return      Number of bytes used, or -1 in case of failure.
 */
unsigned long filesystem_usage(const char * path)
{
    struct statvfs s;
    int r = statvfs(path, &s);
    if (r != 0)
    {
        return -1;
    }
    return (s.f_blocks - s.f_bfree) * s.f_bsize;
}

/**
 * Check if the files on the given filesystem would fit into memory.
 * @param  path          Path of the mounted filesystem.
 * @param  safety_margin Fraction (between zero and one) of free memory to
                         deem available for containing the files.
 * @return               Whether the filesystem seems to fit into memory.
 */
bool filesystem_fits_in_memory(const char * path, double safety_margin)
{
    // Determine the filesystem usage of the device to be encrypted.
    unsigned long size = filesystem_usage(path);
    if (size < 0)
    {
        // Something went wrong in determining the filesystem usage.
        // Return false as a precaution.
        printf("Determining file system usage failed (size: %lu)\n", size);
    printf("file system usage: %lu bytes\n", size);

    // Determine the available memory.
    unsigned long memory = available_memory();

    double projected_usage = (double)size / (double)memory;
    printf("projected memory usage: %.3f\n", projected_usage);
    return (projected_usage <= safety_margin);
}

/**
 * Mount a filesystem; create the mount target if it doesn't exist.
 * @param  device_path Path to the device to mount.
 * @param  mount_path  Path to the mount target.
 * @return             Status code.
 */
int temporary_mount(char * device_path, char * mount_path,
    char * filesystem_type)
{
    struct stat st = {0};
    if (stat(mount_path, &st) == -1)
    {
        mkdir(mount_path, 0700);
    }
    // Mount the device.
    unsigned long mount_flags = MS_NOATIME | MS_NODEV | MS_NOEXEC;
    int r = mount(device_path, mount_path, filesystem_type,
        mount_flags, "");
    return r;
}
Arie Peterson's avatar
Arie Peterson committed
/**
 * Reboot the system. We cannot simply use the `reboot` command because
 * we're running as init (pid 0).
 */
void reboot_initrd()
{
    pid_t pid;
    pid = vfork();
    if (pid == 0)
    {
        // Child.
Arie Peterson's avatar
Arie Peterson committed
        reboot(RB_AUTOBOOT);
        _exit(EXIT_SUCCESS);
    }
    // Parent (init) waits.
    while (1);
}

/**
 * Parse an integer written in decimal.
 * @param[in]  input  String to parse.
 * @param[out] result Resulting integer, or NULL on failure.
 * @return            Return code: 0 on success, nonzero on failure.
 */
int parse_int(const char * input, int * result)
{
    if (input[0] == '\0' || isspace((unsigned char) input[0]))
    {
        return -1;
    }
    char * end;
    long l = strtol(input, &end, 10);
    if (*end != '\0')
    {
        return -1;
    }
    *result = l;
    return 0;
}

/**
 * Write an ssh_key to a specific rule in the authorized_keys file.
 *
 * @param[in]   id      line number/ssh_key id
 * @param[in]   ssh_key ssh key contents. If this string is empty, this is seen
 *                      as deleting the key. Newline characters are ignored
 */
int replace_ssh_key(int id, const char * ssh_key)
{
    // Open file.
    FILE * authorized_keys_in = fopen(AUTHORIZED_KEYS_PATH, "r");
    // Check if that succeeded.
    if (authorized_keys_in == NULL)
    {
        printf("Could not open authorized_keys file for reading\n");
        return -1;
    }

    // Make tmp outfile for authorized keys
    char * authorized_keys_out_name;
    asprintf(&authorized_keys_out_name, "%s%s", AUTHORIZED_KEYS_PATH, ".tmp");

    // Open file.
    FILE * authorized_keys_out = fopen(authorized_keys_out_name, "w");
    // Check if that succeeded.
    if (authorized_keys_out == NULL)
    {
        printf("Could not open authorized_keys tmp file for writing\n");
        return -2;
    }

    char ch;
    int line_number = 1;
    // Loop through all the lines in the input file
    do
    {
            // Copy 1 line
            ch = getc(authorized_keys_in);
            while (ch != EOF && ch != '\n')
            {
                // Copy all lines that don't have id as line number
                putc(ch, authorized_keys_out);
                ch = getc(authorized_keys_in);
            }
            // Insert an ssh key instead of a line
            ch = *ssh_key++;
            while (ch != '\0')
                // New line in ssh key is not allowed, because that will screw
                // up the indices
                    putc(ch, authorized_keys_out);
                ch = *ssh_key++;
                ch = getc(authorized_keys_in);
            } while (ch != EOF && ch != '\n');
        }

        {
            // Insert newline
            putc('\n', authorized_keys_out);
            // Increment line number
            line_number++;
        }
    } while (ch != EOF);

    fclose(authorized_keys_in);
    fclose(authorized_keys_out);

    // Remove old authorized_keys file and replace it with the new one
    remove(AUTHORIZED_KEYS_PATH);
    rename(authorized_keys_out_name, AUTHORIZED_KEYS_PATH);
    return 0;
}
/**
 * Add the SSH_COMMAND string in front of ssh_key unless it's already there
 * because people have seen it being used in ssh_keys_list.
 * @param[out]  ssh_key_with_command    the ssh_key with the command prepended
 *                                      if needed
 * @param[in]   ssh_key                 a valid ssh key string
 */
int add_ssh_command(char ** ssh_key_with_command, const char * ssh_key)
{
    if (strncmp(SSH_COMMAND, ssh_key, strlen(SSH_COMMAND)) != 0)
        return asprintf(ssh_key_with_command, "%s %s", SSH_COMMAND, ssh_key);
        return asprintf(ssh_key_with_command, "%s", ssh_key);
 * Check if the given path already exists (as a (special) file or directory).
 * @param  path  Path to test.
 * @return       `true` if path exists, `false` otherwise.
 */
bool path_exists(const char * path)
{
    struct stat st = {0};
    return (stat(path, &st) == 0);