blob: 08b8452ad0bcdbbe5982d1780b6f1dd71ef68396 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2018 Google LLC
*/
#include <stdio.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <errno.h>
#include <string.h>
#include <poll.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/pkcs7.h>
#include <openssl/sha.h>
#include <openssl/md5.h>
#include "utils.h"
int mount_fs(char *mount_dir, char *backing_dir, int read_timeout_ms)
{
static const char fs_name[] = INCFS_NAME;
char mount_options[512];
int result;
snprintf(mount_options, ARRAY_SIZE(mount_options),
"read_timeout_ms=%u",
read_timeout_ms);
result = mount(backing_dir, mount_dir, fs_name, 0, mount_options);
if (result != 0)
perror("Error mounting fs.");
return result;
}
int mount_fs_opt(char *mount_dir, char *backing_dir, char *opt)
{
static const char fs_name[] = INCFS_NAME;
int result;
result = mount(backing_dir, mount_dir, fs_name, 0, opt);
if (result != 0)
perror("Error mounting fs.");
return result;
}
int unlink_node(int fd, int parent_ino, char *filename)
{
return 0;
}
static EVP_PKEY *deserialize_private_key(const char *pem_key)
{
BIO *bio = NULL;
EVP_PKEY *pkey = NULL;
int len = strlen(pem_key);
bio = BIO_new_mem_buf(pem_key, len);
if (!bio)
return NULL;
pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
BIO_free(bio);
return pkey;
}
static X509 *deserialize_cert(const char *pem_cert)
{
BIO *bio = NULL;
X509 *cert = NULL;
int len = strlen(pem_cert);
bio = BIO_new_mem_buf(pem_cert, len);
if (!bio)
return NULL;
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
BIO_free(bio);
return cert;
}
bool sign_pkcs7(const void *data_to_sign, size_t data_size,
char *pkey_pem, char *cert_pem,
void **sig_ret, size_t *sig_size_ret)
{
/*
* PKCS#7 signing flags:
*
* - PKCS7_BINARY signing binary data, so skip MIME translation
*
* - PKCS7_NOATTR omit extra authenticated attributes, such as
* SMIMECapabilities
*
* - PKCS7_PARTIAL PKCS7_sign() creates a handle only, then
* PKCS7_sign_add_signer() can add a signer later.
* This is necessary to change the message digest
* algorithm from the default of SHA-1. Requires
* OpenSSL 1.0.0 or later.
*/
int pkcs7_flags = PKCS7_BINARY | PKCS7_NOATTR | PKCS7_PARTIAL;
void *sig;
size_t sig_size;
BIO *bio = NULL;
PKCS7 *p7 = NULL;
EVP_PKEY *pkey = NULL;
X509 *cert = NULL;
bool ok = false;
const EVP_MD *md = EVP_sha256();
pkey = deserialize_private_key(pkey_pem);
if (!pkey) {
printf("deserialize_private_key failed\n");
goto out;
}
cert = deserialize_cert(cert_pem);
if (!cert) {
printf("deserialize_cert failed\n");
goto out;
}
bio = BIO_new_mem_buf(data_to_sign, data_size);
if (!bio)
goto out;
p7 = PKCS7_sign(NULL, NULL, NULL, bio, pkcs7_flags);
if (!p7) {
printf("failed to initialize PKCS#7 signature object\n");
goto out;
}
if (!PKCS7_sign_add_signer(p7, cert, pkey, md, pkcs7_flags)) {
printf("failed to add signer to PKCS#7 signature object\n");
goto out;
}
if (PKCS7_final(p7, bio, pkcs7_flags) != 1) {
printf("failed to finalize PKCS#7 signature\n");
goto out;
}
BIO_free(bio);
bio = BIO_new(BIO_s_mem());
if (!bio) {
printf("out of memory\n");
goto out;
}
if (i2d_PKCS7_bio(bio, p7) != 1) {
printf("failed to DER-encode PKCS#7 signature object\n");
goto out;
}
sig_size = BIO_get_mem_data(bio, &sig);
*sig_ret = malloc(sig_size);
memcpy(*sig_ret, sig, sig_size);
*sig_size_ret = sig_size;
ok = true;
out:
PKCS7_free(p7);
BIO_free(bio);
return ok;
}
int crypto_emit_file(int fd, char *dir, char *filename, incfs_uuid_t *id_out,
size_t size, const char *root_hash, char *sig, size_t sig_size,
char *add_data)
{
int mode = __S_IFREG | 0555;
struct incfs_file_signature_info sig_info = {
.hash_tree_alg = root_hash
? INCFS_HASH_TREE_SHA256
: 0,
.root_hash = ptr_to_u64(root_hash),
.additional_data = ptr_to_u64(add_data),
.additional_data_size = strlen(add_data),
.signature = ptr_to_u64(sig),
.signature_size = sig_size,
};
struct incfs_new_file_args args = {
.size = size,
.mode = mode,
.file_name = ptr_to_u64(filename),
.directory_path = ptr_to_u64(dir),
.signature_info = ptr_to_u64(&sig_info),
.file_attr = 0,
.file_attr_len = 0
};
md5(filename, strlen(filename), (char *)args.file_id.bytes);
if (ioctl(fd, INCFS_IOC_CREATE_FILE, &args) != 0)
return -errno;
*id_out = args.file_id;
return 0;
}
int emit_file(int fd, char *dir, char *filename, incfs_uuid_t *id_out,
size_t size, char *attr)
{
int mode = __S_IFREG | 0555;
struct incfs_file_signature_info sig_info = {
.hash_tree_alg = 0,
.root_hash = ptr_to_u64(NULL)
};
struct incfs_new_file_args args = {
.size = size,
.mode = mode,
.file_name = ptr_to_u64(filename),
.directory_path = ptr_to_u64(dir),
.signature_info = ptr_to_u64(&sig_info),
.file_attr = ptr_to_u64(attr),
.file_attr_len = attr ? strlen(attr) : 0
};
md5(filename, strlen(filename), (char *)args.file_id.bytes);
if (ioctl(fd, INCFS_IOC_CREATE_FILE, &args) != 0)
return -errno;
*id_out = args.file_id;
return 0;
}
int get_file_bmap(int cmd_fd, int ino, unsigned char *buf, int buf_size)
{
return 0;
}
int get_file_signature(int fd, unsigned char *buf, int buf_size)
{
struct incfs_get_file_sig_args args = {
.file_signature = ptr_to_u64(buf),
.file_signature_buf_size = buf_size
};
if (ioctl(fd, INCFS_IOC_READ_FILE_SIGNATURE, &args) == 0)
return args.file_signature_len_out;
return -errno;
}
loff_t get_file_size(char *name)
{
struct stat st;
if (stat(name, &st) == 0)
return st.st_size;
return -ENOENT;
}
int open_commands_file(char *mount_dir)
{
char cmd_file[255];
int cmd_fd;
snprintf(cmd_file, ARRAY_SIZE(cmd_file),
"%s/%s", mount_dir, INCFS_PENDING_READS_FILENAME);
cmd_fd = open(cmd_file, O_RDONLY);
if (cmd_fd < 0)
perror("Can't open commands file");
return cmd_fd;
}
int open_log_file(char *mount_dir)
{
char cmd_file[255];
int cmd_fd;
snprintf(cmd_file, ARRAY_SIZE(cmd_file), "%s/.log", mount_dir);
cmd_fd = open(cmd_file, O_RDWR);
if (cmd_fd < 0)
perror("Can't open log file");
return cmd_fd;
}
int wait_for_pending_reads(int fd, int timeout_ms,
struct incfs_pending_read_info *prs, int prs_count)
{
ssize_t read_res = 0;
if (timeout_ms > 0) {
int poll_res = 0;
struct pollfd pollfd = {
.fd = fd,
.events = POLLIN
};
poll_res = poll(&pollfd, 1, timeout_ms);
if (poll_res < 0)
return -errno;
if (poll_res == 0)
return 0;
if (!(pollfd.revents | POLLIN))
return 0;
}
read_res = read(fd, prs, prs_count * sizeof(*prs));
if (read_res < 0)
return -errno;
return read_res / sizeof(*prs);
}
char *concat_file_name(const char *dir, char *file)
{
char full_name[FILENAME_MAX] = "";
if (snprintf(full_name, ARRAY_SIZE(full_name), "%s/%s", dir, file) < 0)
return NULL;
return strdup(full_name);
}
int delete_dir_tree(const char *dir_path)
{
DIR *dir = NULL;
struct dirent *dp;
int result = 0;
dir = opendir(dir_path);
if (!dir) {
result = -errno;
goto out;
}
while ((dp = readdir(dir))) {
char *full_path;
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
full_path = concat_file_name(dir_path, dp->d_name);
if (dp->d_type == DT_DIR)
result = delete_dir_tree(full_path);
else
result = unlink(full_path);
free(full_path);
if (result)
goto out;
}
out:
if (dir)
closedir(dir);
if (!result)
rmdir(dir_path);
return result;
}
void sha256(char *data, size_t dsize, char *hash)
{
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, data, dsize);
SHA256_Final((unsigned char *)hash, &ctx);
}
void md5(char *data, size_t dsize, char *hash)
{
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, data, dsize);
MD5_Final((unsigned char *)hash, &ctx);
}