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);
}
/**
* 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;
while (fgets(buf, sizeof(buf), file) != NULL)
{
// 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
{
// append buffer to str
strcpy(str + size - 1, buf);
size += strlength;
/**
* 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;
}
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/**
* 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;
}
/**
* 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.
_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;
}

Maarten de Waard
committed
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/**
* 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;

Maarten de Waard
committed
// Loop through all the lines in the input file
do
{

Maarten de Waard
committed
if (line_number != id)
{
// 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);
}

Maarten de Waard
committed
}
else
{
// Insert an ssh key instead of a line
ch = *ssh_key++;

Maarten de Waard
committed
{
// New line in ssh key is not allowed, because that will screw
// up the indices
putc(ch, authorized_keys_out);
ch = *ssh_key++;

Maarten de Waard
committed
}
// ignore replaced line
do
{

Maarten de Waard
committed
ch = getc(authorized_keys_in);
} while (ch != EOF && ch != '\n');
}
{
// Insert newline
putc('\n', authorized_keys_out);
// Increment line number
line_number++;
}

Maarten de Waard
committed
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;
}

Maarten de Waard
committed
/**
* 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

Maarten de Waard
committed
*/
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);

Maarten de Waard
committed
else
return asprintf(ssh_key_with_command, "%s", ssh_key);

Maarten de Waard
committed
}
* 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);