The VMM configuration side and the utilities side have been updated to now support
two file formats:
Version 0 : The previous version "legacy"
Version 1 : The new version "file integrity checking"
build_vm now has an option [-v] to select versions, version 1 is the default
v3_create now creates version 1 images when dynamic construction of an image file is done
Version 1 has these changes:
add integrity checking for files included in guest configs
This patch adds a hash field to the file headers built from guest
configurations. Both methods of guest creation (in-memory with XML and
file building with build_vm) have been modified to support this. The hash
is passed along with the file when it's added to Palacios, so integrity checks
can theoretically happen at any point, but currently
this is only being done at configuration time.
int i = 0;
int offset = 0;
unsigned long long file_offset = 0;
+ struct mem_file_hdr * hdrs = NULL;
/* Image size is:
8 byte header +
xml strlen +
8 bytes of zeros +
8 bytes (number of files) +
- num_files * 16 byte file header +
+ num_files * (16+sizeof(unsigned long)) byte file header +
8 bytes of zeroes +
file data
*/
}
guest_img_size = 8 + 4 + strlen(new_xml_str) + 8 + 8 +
- (num_files * 16) + 8 + file_data_size;
+ (num_files * (16*sizeof(unsigned long))) + 8 + file_data_size;
guest_img_data = malloc(guest_img_size);
memset(guest_img_data, 0, guest_img_size);
- memcpy(guest_img_data, "v3vee\0\0\0", 8);
+ //
+ // Dynamically built guests are version 1 by default now
+ //
+ memcpy(guest_img_data, "v3vee\0\0\1", 8);
offset += 8;
*(unsigned int *)(guest_img_data + offset) = strlen(new_xml_str);
*(unsigned long long *)(guest_img_data + offset) = num_files;
offset += 8;
+ hdrs = guest_img_data + offset;
// The file offset starts at the end of the file list
- file_offset = offset + (16 * num_files) + 8;
+ file_offset = offset + ((sizeof(unsigned long) + 16) * num_files) + 8;
for (i = 0; i < num_files; i++) {
- *(unsigned int *)(guest_img_data + offset) = i;
+ /*
+ unsigned int *)(guest_img_data + offset) = i;
offset += 4;
*(unsigned int *)(guest_img_data + offset) = files[i].size;
offset += 4;
*(unsigned long long *)(guest_img_data + offset) = file_offset;
offset += 8;
-
+ *(unsigned long *)(guest_img_data + offset) = 0;
+ offset += sizeof(unsigned long);
+ */
+ hdrs[i].file_idx = i;
+ hdrs[i].file_size = files[i].size;
+ hdrs[i].file_offset = file_offset;
+ hdrs[i].file_hash = 0;
+
+ offset += 16 + sizeof(unsigned long);
file_offset += files[i].size;
-
}
memset(guest_img_data + offset, 0, 8);
for (i = 0; i < num_files; i++) {
int fd = open(files[i].filename, O_RDONLY);
+ unsigned char * faddr = (unsigned char *)(guest_img_data + offset);
if (fd == -1) {
printf("Error: Could not open aux file (%s)\n", files[i].filename);
return -1;
}
- v3_read_file(fd, files[i].size, (unsigned char *)(guest_img_data + offset));
+ v3_read_file(fd, files[i].size, faddr);
+
+ /* store a hash of the file blob for integrity checking later */
+ hdrs[i].file_hash = v3_hash_buffer(faddr, files[i].size);
+ printf("File Hash: %llx\n", hdrs[i].file_hash);
close(fd);
return 0;
}
+
+
+/*
+ * generic ELF header buffer hash function.
+ * Mirrors internal Palacios implementation
+ */
+unsigned long v3_hash_buffer (unsigned char * msg, unsigned int len) {
+ unsigned long hash = 0;
+ unsigned long temp = 0;
+ unsigned int i;
+
+ for (i = 0; i < len; i++) {
+ hash = (hash << 4) + *(msg + i) + i;
+ if ((temp = (hash & 0xF0000000))) {
+ hash ^= (temp >> 24);
+ }
+ hash &= ~temp;
+ }
+ return hash;
+}
+
int launch_vm (const char * filename);
int stop_vm (const char * filename);
+unsigned long v3_hash_buffer (unsigned char * msg, unsigned int len);
/* XML-related structs */
struct cfg_value {
char id[256];
};
+struct mem_file_hdr {
+ unsigned int file_idx;
+ unsigned int file_size;
+ unsigned long long file_offset;
+ unsigned long file_hash;
+};
+
+
#endif
struct v3_cfg_file {
void * data;
uint64_t size;
+ ulong_t hash; // used only in version 1+
char tag[V3_MAX_TAG_LEN];
#include "vmm_config_class.h"
+
+/* The Palacios cookie encodes "v3vee" followed by a
+ 3 byte version code. There are currently two versions:
+
+ \0\0\0 => original (no checksum)
+ \0\0\1 => checksum
+*/
+#define COOKIE_LEN 8
+#define COOKIE_V0 "v3vee\0\0\0"
+#define COOKIE_V1 "v3vee\0\0\1"
+
+
+
+
// This is used to access the configuration file index table
-struct file_hdr {
+struct file_hdr_v0 {
uint32_t index;
uint32_t size;
uint64_t offset;
};
-struct file_idx_table {
+struct file_hdr_v1 {
+ uint32_t index;
+ uint32_t size;
+ uint64_t offset;
+ ulong_t hash;
+};
+
+
+struct file_idx_table_v0 {
uint64_t num_files;
- struct file_hdr hdrs[0];
+ struct file_hdr_v0 hdrs[0];
+};
+
+struct file_idx_table_v1 {
+ uint64_t num_files;
+ struct file_hdr_v1 hdrs[0];
};
struct v3_config * cfg = NULL;
int offset = 0;
uint_t xml_len = 0;
- struct file_idx_table * files = NULL;
+ struct file_idx_table_v0 * files_v0 = NULL;
+ struct file_idx_table_v1 * files_v1 = NULL;
v3_cfg_tree_t * file_tree = NULL;
+ int version=-1;
V3_Print("cfg data at %p\n", cfg_blob);
- if (memcmp(cfg_blob, "v3vee\0\0\0", 8) != 0) {
- PrintError("Invalid Configuration Header\n");
+ if (memcmp(cfg_blob, COOKIE_V0, COOKIE_LEN) == 0) {
+ version = 0;
+ } else if (memcmp(cfg_blob, COOKIE_V1, COOKIE_LEN) == 0) {
+ version = 1;
+ } else {
+ PrintError("Invalid Configuration Header Or Unknown Version\n");
return NULL;
- }
+ }
- offset += 8;
+ V3_Print("Handling Palacios Image Format, Version 0x%x\n",version);
+
+ offset += COOKIE_LEN;
cfg = (struct v3_config *)V3_Malloc(sizeof(struct v3_config));
offset += 8;
- files = (struct file_idx_table *)(cfg_blob + offset);
+ // This is hideous, but the file formats are still very close
+ if (version==0) {
+ files_v0 = (struct file_idx_table_v0 *)(cfg_blob + offset);
+ V3_Print("Number of files in cfg: %d\n", (uint32_t)(files_v0->num_files));
+ } else {
+ files_v1 = (struct file_idx_table_v1 *)(cfg_blob + offset);
+ V3_Print("Number of files in cfg: %d\n", (uint32_t)(files_v1->num_files));
+ }
- V3_Print("Number of files in cfg: %d\n", (uint32_t)(files->num_files));
file_tree = v3_cfg_subtree(v3_cfg_subtree(cfg->cfg, "files"), "file");
char * id = v3_cfg_val(file_tree, "id");
char * index = v3_cfg_val(file_tree, "index");
int idx = atoi(index);
- struct file_hdr * hdr = &(files->hdrs[idx]);
struct v3_cfg_file * file = NULL;
file = (struct v3_cfg_file *)V3_Malloc(sizeof(struct v3_cfg_file));
V3_Print("File index=%d id=%s\n", idx, id);
strncpy(file->tag, id, V3_MAX_TAG_LEN);
- file->size = hdr->size;
- file->data = cfg_blob + hdr->offset;
- V3_Print("Storing file data offset = %d, size=%d\n", (uint32_t)hdr->offset, hdr->size);
- V3_Print("file data at %p\n", file->data);
+ if (version==0) {
+ struct file_hdr_v0 * hdr = &(files_v0->hdrs[idx]);
+
+ file->size = hdr->size;
+ file->data = cfg_blob + hdr->offset;
+ file->hash = 0;
+
+ V3_Print("Storing file data offset = %d, size=%d\n", (uint32_t)hdr->offset, hdr->size);
+ V3_Print("file data at %p\n", file->data);
+
+ } else if (version==1) {
+ struct file_hdr_v1 * hdr = &(files_v1->hdrs[idx]);
+ unsigned long hash;
+
+ file->size = hdr->size;
+ file->data = cfg_blob + hdr->offset;
+ file->hash = hdr->hash;
+
+ V3_Print("Storing file data offset = %d, size=%d\n", (uint32_t)hdr->offset, hdr->size);
+ V3_Print("file data at %p\n", file->data);
+ V3_Print("Checking file data integrity...\n");
+ if ((hash = v3_hash_buffer(file->data, file->size)) != file->hash) {
+ PrintError("File data corrupted! (orig hash=0x%lx, new=0x%lx\n",
+ file->hash, hash);
+ return NULL;
+ }
+ V3_Print("File data OK\n");
+
+ }
+
+
list_add( &(file->file_node), &(cfg->file_list));
V3_Print("Keying file to name\n");
CC = gcc
-CFLAGS = -Wall -g
+INC = -I../../linux_usr
+CFLAGS = -Wall -g $(INC)
LDFLAGS =
-OBJS = main.c ezxml.c
+OBJS = main.c ezxml.c ../../linux_usr/libv3_ctrl.a
all: builder
builder: $(OBJS)
- $(CC) $(CFLAGS) $(OBJS) -o build_vm
+ $(CC) $(CFLAGS) $(OBJS) -lv3_ctrl -L../../linux_usr -o build_vm
clean:
- rm -f *.o build_vm
\ No newline at end of file
+ rm -f *.o build_vm
#include <getopt.h>
#include <unistd.h>
#include <string.h>
+#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <errno.h>
+#include <v3_ctrl.h>
#define MAX_FILES 256
+/*
struct file_info {
int size;
char filename[2048];
char id[256];
};
+*/
+
+/*
+
+ The Palacios cookie encodes "v3vee" followed by a
+ 3 byte version code. There are currently two versions:
+
+ \0\0\0 => original (no checksum)
+ \0\0\1 => checksum
+*/
+
+#define COOKIE "v3vee\0\0\0"
+#define COOKIE_LEN 8
+#define COOKIE_VERSION_OFFSET 5
+#define COOKIE_VERSION_LEN 3
+
+int version = 1; // default version is now 1
+
int num_files = 0;
struct file_info files[MAX_FILES];
int copy_file(int file_index, FILE * data_file);
void usage() {
- printf("Usage: build_vm <infile> [-o outfile]\n");
+ printf("Usage: build_vm <infile> [-v version] [-o outfile]\n");
}
int main(int argc, char ** argv) {
char * outfile = NULL;
char * infile = NULL;
-
memset((char *)files, 0, sizeof(files));
opterr = 0;
- while ((c = getopt(argc, argv, "ho:")) != -1) {
+ while ((c = getopt(argc, argv, "ho:v:")) != -1) {
switch (c) {
case 'o':
outfile = optarg;
break;
+ case 'v':
+ version = atoi(optarg);
+ break;
case 'h':
usage();
return 1;
return 1;
}
+ if (version != 0 && version != 1) {
+ printf("Only versions 0 and 1 are supported\n");
+ return 1;
+ }
+
infile = argv[optind];
}
+static int generate_file_hashes (char * filename,
+ unsigned long long hdr_offset,
+ int num_files) {
+ unsigned char * file_data;
+ unsigned char * out_data;
+ struct mem_file_hdr * hdrs = NULL;
+ int i, fd, out_fd;
+ struct stat st;
+
+ out_fd = open(filename, O_RDWR);
+ if (!out_fd) {
+ fprintf(stderr, "Couldn't open output file %s\n", filename);
+ return -1;
+ }
+ fstat(out_fd, &st);
+
+ /* mmap the out file, easy access to file headers */
+ if ((out_data = mmap(0,
+ st.st_size,
+ PROT_READ|PROT_WRITE,
+ MAP_SHARED,
+ out_fd,
+ 0)) == MAP_FAILED) {
+ fprintf(stderr, "Error mapping output file (%d)\n", errno);
+ return -1;
+ }
+
+ hdrs = (struct mem_file_hdr *)(out_data + hdr_offset);
+
+ /* mmap each file, then update it's hash */
+ for (i = 0; i < num_files; i++) {
+
+ fd = open(files[i].filename, O_RDONLY);
+ if (!fd) {
+ fprintf(stderr, "Error opening file %s\n",
+ files[i].filename);
+ return -1;
+ }
+
+ if ((file_data = mmap(0,
+ hdrs[i].file_size,
+ PROT_READ,
+ MAP_PRIVATE,
+ fd,
+ 0)) == MAP_FAILED) {
+ fprintf(stderr, "Could not mmap file for hashing\n");
+ return -1;
+ }
+
+ /* generate the hash and save it */
+ hdrs[i].file_hash = v3_hash_buffer(file_data, hdrs[i].file_size);
+ printf("Generating hash for file %s (hash=0x%lx)\n",
+ files[i].filename, hdrs[i].file_hash);
+
+ munmap(file_data, hdrs[i].file_size);
+ close(fd);
+ }
+
+ munmap(out_data, st.st_size);
+ return 0;
+}
+
+void gen_cookie(char *dest)
+{
+ memcpy(dest,COOKIE,COOKIE_LEN);
+ dest[COOKIE_VERSION_OFFSET] = (((unsigned)version) >> 16) & 0xff;
+ dest[COOKIE_VERSION_OFFSET+1] = (((unsigned)version) >> 8) & 0xff;
+ dest[COOKIE_VERSION_OFFSET+2] = (((unsigned)version) >> 0) & 0xff;
+}
+
+
int write_output(char * filename, ezxml_t cfg_output) {
FILE * data_file = fopen(filename, "w+");
char * new_cfg_str = ezxml_toxml(cfg_output);
int i = 0;
unsigned long long offset = 0;
unsigned long long file_cnt = num_files;
+ unsigned long long hdr_offset = 0;
+ char cookie[COOKIE_LEN];
- fwrite("v3vee\0\0\0", 8, 1, data_file);
- offset += 8;
+ gen_cookie(cookie);
+
+ fwrite(cookie, COOKIE_LEN, 1, data_file);
+ offset += COOKIE_LEN;
// printf("New config: \n%s\n", new_cfg_str);
fwrite(&file_cnt, 8, 1, data_file);
offset += 8;
- // each index entry is 16 bytes long plus end padding
- offset += (16 * num_files) + 8;
+ if (version==0) {
+ // for version 0, we simply have the file num, offset, size list
+ // each index entry is 16 bytes long plus end padding
+ offset += (16 * num_files) + 8;
+ } else if (version==1) {
+ // For a version 1, we have the file num, offset, size, and hash list
+ // We need to remember where this begins in the file, though...
+ hdr_offset = offset;
+
+ // each index entry is (16+sizeof(unsigned long)) bytes long plus end padding
+ offset += ((16 + sizeof(unsigned long)) * num_files) + 8;
+ }
+
for (i = 0; i < num_files; i++) {
fwrite(&i, 4, 1, data_file);
fwrite(&(files[i].size), 4, 1, data_file);
fwrite(&offset, 8, 1, data_file);
+ if (version==1) {
+ /* we can't generate the hash yet, zero for now */
+ fwrite(&zero, sizeof(unsigned long), 1, data_file);
+ }
+
offset += files[i].size;
}
}
+ fclose(data_file);
+ if (version==1) {
+ // We now will go back and place the hashes
+ if (generate_file_hashes(filename,
+ hdr_offset,
+ num_files) < 0) {
+ fprintf(stderr, "Error generating file hashes\n");
+ return -1;
+ }
+ }
- fclose(data_file);
return 0;
}