v0.2.0-rc - implement basic integrity checking framework
This commit is contained in:
406
src/libglacier.c
406
src/libglacier.c
@@ -27,14 +27,24 @@
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/* Buffer and size constants */
|
||||
#define BUFFER_SIZE 1024
|
||||
#define MAX_CHILDREN 64
|
||||
#define MAX_SIZE 256
|
||||
#define PATH_MAX_SIZE 512
|
||||
#define MAX_RECURSION_DEPTH 100
|
||||
#define DEFAULT_PERMISSIONS 0750
|
||||
|
||||
/* Define LG_VERBOSE if not defined elsewhere */
|
||||
#ifndef LG_VERBOSE
|
||||
#define LG_VERBOSE 0
|
||||
#endif
|
||||
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned char uchar;
|
||||
@@ -48,19 +58,20 @@ typedef unsigned char uchar;
|
||||
*/
|
||||
|
||||
config_t cfg; /* Context for libconfig */
|
||||
config_setting_t *setting; /* Pointer for setting */
|
||||
const char str; /* Unsure what this does, will possibly remove later */
|
||||
config_setting_t *setting = NULL; /* Pointer for setting */
|
||||
|
||||
int GLACIER_ALLOW_SERVICES; /* Declaration of GLACIER_ALLOW_SERVICES as given in glacier.cfg */
|
||||
char GLACIER_ALLOWED_LICENSES; /* Declaration of GLACIER_ALLOWED_LICENSES as given in glacier.cfg */
|
||||
int GLACIER_DO_INT_CHECK; /* Declaration of GLACIER_DO_INT_CHECK as given in glacier.cfg */
|
||||
int GLACIER_VERBOSE; /* Declaration of GLACIER_VERBOSE as given in glacier.cfg */
|
||||
/* Configuration variables with default values */
|
||||
int GLACIER_ALLOW_SERVICES = 0; /* Declaration of GLACIER_ALLOW_SERVICES as given in glacier.cfg */
|
||||
char *GLACIER_ALLOWED_LICENSES = NULL; /* Declaration of GLACIER_ALLOWED_LICENSES as given in glacier.cfg */
|
||||
int GLACIER_DO_INT_CHECK = 1; /* Declaration of GLACIER_DO_INT_CHECK as given in glacier.cfg */
|
||||
int GLACIER_VERBOSE = 0; /* Declaration of GLACIER_VERBOSE as given in glacier.cfg */
|
||||
|
||||
const char *GLACIER_REPO; /* Declaration of GLACIER_REPO as defined in profile.cfg */
|
||||
const char *GLACIER_ARCH; /* Declaration of GLACIER_ARCH as defined in profile.cfg */
|
||||
const char *GLACIER_TARGET; /* Declaration of GLACIER_TARGET as defined in profile.cfg */
|
||||
const char *GLACIER_LOCALDB; /* Declaration of GLACIER_LOCALDB as defined in profile.cfg */
|
||||
const char *GLACIER_SYSTEM_PROFILE; /* Declaration of GLACIER_SYSTEM_PROFILE as defined in profile.cfg */
|
||||
/* Profile variables */
|
||||
const char *GLACIER_REPO = NULL; /* Declaration of GLACIER_REPO as defined in profile.cfg */
|
||||
const char *GLACIER_ARCH = NULL; /* Declaration of GLACIER_ARCH as defined in profile.cfg */
|
||||
const char *GLACIER_TARGET = NULL; /* Declaration of GLACIER_TARGET as defined in profile.cfg */
|
||||
const char *GLACIER_LOCALDB = NULL; /* Declaration of GLACIER_LOCALDB as defined in profile.cfg */
|
||||
const char *GLACIER_SYSTEM_PROFILE = NULL; /* Declaration of GLACIER_SYSTEM_PROFILE as defined in profile.cfg */
|
||||
|
||||
/* Required runtime files */
|
||||
const char *runtime_files[] = {
|
||||
@@ -333,12 +344,65 @@ load_setting_from_config(char SETTING[])
|
||||
struct node
|
||||
*create_node(char *data)
|
||||
{
|
||||
if (data == NULL) {
|
||||
if (LG_VERBOSE == 1) { errlog("NULL data passed to create_node()"); }
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct node *newNode = (struct node*)malloc(sizeof(struct node));
|
||||
newNode->data = strdup(data);
|
||||
if (newNode == NULL) {
|
||||
if (LG_VERBOSE == 1) { errlog("Memory allocation failed in create_node()"); }
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newNode->data = strdup(data);
|
||||
if (newNode->data == NULL) {
|
||||
if (LG_VERBOSE == 1) { errlog("String duplication failed in create_node()"); }
|
||||
free(newNode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newNode->numChildren = 0;
|
||||
newNode->left = NULL;
|
||||
newNode->right = NULL;
|
||||
|
||||
for (int i = 0; i < MAX_CHILDREN; i++) {
|
||||
newNode->children[i] = NULL;
|
||||
}
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
/*
|
||||
* free_node
|
||||
*
|
||||
* DESCRIPTION: Recursively free a node and all its children.
|
||||
* PARAMETERS: struct node *root
|
||||
* DEFINED IN: data.h
|
||||
*
|
||||
*/
|
||||
|
||||
void
|
||||
free_node(struct node *root)
|
||||
{
|
||||
if (root == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Free all children recursively */
|
||||
for (int i = 0; i < root->numChildren; i++) {
|
||||
free_node(root->children[i]);
|
||||
}
|
||||
|
||||
/* Free data string */
|
||||
if (root->data != NULL) {
|
||||
free(root->data);
|
||||
}
|
||||
|
||||
/* Free node itself */
|
||||
free(root);
|
||||
}
|
||||
|
||||
/*
|
||||
* add_child
|
||||
*
|
||||
@@ -348,14 +412,31 @@ struct node
|
||||
*
|
||||
*/
|
||||
|
||||
void
|
||||
int
|
||||
add_child(struct node *parent, struct node *child)
|
||||
{
|
||||
if (parent == NULL || child == NULL) {
|
||||
if (LG_VERBOSE == 1) {
|
||||
errlog("NULL pointer passed to add_child()");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (parent->numChildren < 0 || parent->numChildren >= MAX_CHILDREN) {
|
||||
if (LG_VERBOSE == 1) {
|
||||
errlog("Invalid numChildren value in parent node");
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (parent->numChildren < MAX_CHILDREN) {
|
||||
parent->children[parent->numChildren++] = child;
|
||||
return 0;
|
||||
} else {
|
||||
if (LG_VERBOSE == 1) { errlog("Maximum number of children exceeded"); }
|
||||
exit(1);
|
||||
if (LG_VERBOSE == 1) {
|
||||
errlog("Maximum number of children exceeded");
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,22 +449,53 @@ add_child(struct node *parent, struct node *child)
|
||||
*
|
||||
*/
|
||||
|
||||
void
|
||||
int
|
||||
print_tree(struct node *root, int level)
|
||||
{
|
||||
if (root == NULL) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (level < 0) {
|
||||
if (LG_VERBOSE == 1) {
|
||||
errlog("Invalid level value in print_tree()");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (level > MAX_RECURSION_DEPTH) {
|
||||
/* Safety check to prevent stack overflow from recursive calls */
|
||||
if (LG_VERBOSE == 1) {
|
||||
errlog("Maximum recursion depth exceeded in print_tree()");
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
for (int i = 0; i < level; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
printf("%s\n", root->data);
|
||||
if (root->data == NULL) {
|
||||
printf("(NULL)\n");
|
||||
} else {
|
||||
printf("%s\n", root->data);
|
||||
}
|
||||
|
||||
if (root->numChildren < 0 || root->numChildren > MAX_CHILDREN) {
|
||||
if (LG_VERBOSE == 1) {
|
||||
errlog("Invalid numChildren value in node");
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
for (int i = 0; i < root->numChildren; i++) {
|
||||
print_tree(root->children[i], level + 1);
|
||||
if (print_tree(root->children[i], level + 1) != 0) {
|
||||
/* Propagate errors up the call stack */
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -398,18 +510,31 @@ print_tree(struct node *root, int level)
|
||||
int
|
||||
mkworkspace(void)
|
||||
{
|
||||
DIR* workspace = opendir("/tmp/glacier-workspace");
|
||||
const char *workspace_path = "/tmp/glacier-workspace";
|
||||
|
||||
DIR* workspace = opendir(workspace_path);
|
||||
if (workspace) {
|
||||
/* Workspace exists */
|
||||
closedir(workspace);
|
||||
/* infolog("Not creating new workspace, valid workspace already exists."); */
|
||||
return 0;
|
||||
} else if (ENOENT == errno) {
|
||||
/* infolog("Creating new Glacier workspace..."); */
|
||||
mkdir("/tmp/glacier-workspace", 0777);
|
||||
if (mkdir(workspace_path, DEFAULT_PERMISSIONS) != 0) {
|
||||
if (LG_VERBOSE == 1) {
|
||||
errlog("in mkworkspace()");
|
||||
errlog("mkdir() failed to create workspace directory");
|
||||
fprintf(stderr, "Error: %s\n", strerror(errno));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
/* Some other error occurred */
|
||||
if (LG_VERBOSE == 1) {
|
||||
errlog("in mkworkspace()");
|
||||
errlog("mkdir() failed to run");
|
||||
errlog("opendir() failed to check workspace");
|
||||
fprintf(stderr, "Error: %s\n", strerror(errno));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -433,46 +558,87 @@ prepare_pkg(char PACKAGE[])
|
||||
errlog("This can be done by running 'glacier-update-pkgdb' as root.");
|
||||
return 1;
|
||||
} else {
|
||||
char PKG_NEW[512];
|
||||
strcat(PKG_NEW, "/tmp/glacier-workspace/");
|
||||
strcat(PKG_NEW, PACKAGE);
|
||||
strcat(PKG_NEW, ".tar");
|
||||
char PKG_NEW[PATH_MAX_SIZE];
|
||||
PKG_NEW[0] = '\0'; /* Initialize the string buffer */
|
||||
snprintf(PKG_NEW, sizeof(PKG_NEW), "/tmp/glacier-workspace/%s.tar", PACKAGE);
|
||||
|
||||
FILE *pkg_old, *pkg_new;
|
||||
char filename[100], contents;
|
||||
char buffer[BUFFER_SIZE];
|
||||
size_t bytes_read;
|
||||
|
||||
pkg_old = fopen(PACKAGE, "r");
|
||||
pkg_new = fopen(PKG_NEW, "a+");
|
||||
if (pkg_old == NULL) {
|
||||
errlog("Failed to open source package file");
|
||||
return 1;
|
||||
}
|
||||
|
||||
pkg_new = fopen(PKG_NEW, "w");
|
||||
if (pkg_new == NULL) {
|
||||
errlog("Failed to create destination package file");
|
||||
fclose(pkg_old);
|
||||
return 1;
|
||||
}
|
||||
|
||||
contents = fgetc(pkg_old);
|
||||
|
||||
while (contents != EOF) {
|
||||
fputc(contents, pkg_new);
|
||||
contents = fgetc(pkg_old);
|
||||
/* Use buffered I/O for better efficiency */
|
||||
while ((bytes_read = fread(buffer, 1, sizeof(buffer), pkg_old)) > 0) {
|
||||
if (fwrite(buffer, 1, bytes_read, pkg_new) != bytes_read) {
|
||||
errlog("Failed to write to destination file");
|
||||
fclose(pkg_old);
|
||||
fclose(pkg_new);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(pkg_old);
|
||||
fclose(pkg_new);
|
||||
char *tar_args[] = {
|
||||
"/bin/tar", /* This should be changed to /glacier/bin/tar later on */
|
||||
"-xvf",
|
||||
PKG_NEW,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Fork before exec to prevent process replacement */
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
errlog("Failed to fork process");
|
||||
return 1;
|
||||
} else if (pid == 0) {
|
||||
/* Child process */
|
||||
char *tar_args[] = {
|
||||
"/bin/tar", /* This should be changed to /glacier/bin/tar later on */
|
||||
"-xvf",
|
||||
PKG_NEW,
|
||||
NULL,
|
||||
};
|
||||
|
||||
execvp(
|
||||
"/bin/tar", /* Above comment applies here */
|
||||
tar_args
|
||||
);
|
||||
if (errno != 0) {
|
||||
printf(COL_RED "[x] " COL_RESET "Error while unpacking archive for package %s.\n", PACKAGE);
|
||||
return errno;
|
||||
execvp("/bin/tar", tar_args);
|
||||
/* If we get here, execvp failed */
|
||||
fprintf(stderr, COL_RED "[x] " COL_RESET "Failed to execute tar: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
/* Parent process */
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
|
||||
printf(COL_RED "[x] " COL_RESET "Error while unpacking archive for package %s.\n", PACKAGE);
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
if (remove(PKG_NEW) != 0) {
|
||||
warnlog("Failed to remove temporary package file");
|
||||
}
|
||||
|
||||
char pkg_dir[PATH_MAX_SIZE];
|
||||
pkg_dir[0] = '\0'; /* Initialize the string buffer */
|
||||
snprintf(pkg_dir, sizeof(pkg_dir), "/tmp/glacier-workspace/%s", PACKAGE);
|
||||
|
||||
if (chdir(pkg_dir) != 0) {
|
||||
errlog("Failed to change directory");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
remove(PKG_NEW);
|
||||
char pkg_dir[512];
|
||||
strcat(pkg_dir, "/tmp/glacier-workspace/");
|
||||
strcat(pkg_dir, PACKAGE);
|
||||
chdir(pkg_dir);
|
||||
}
|
||||
|
||||
/* Should never reach here, but added for completeness */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -487,20 +653,44 @@ prepare_pkg(char PACKAGE[])
|
||||
int
|
||||
run_make_task(char TASK[])
|
||||
{
|
||||
char *build_args[] = {
|
||||
"/bin/make", /* This should be changed to /glacier/bin/make later on */
|
||||
TASK,
|
||||
NULL
|
||||
};
|
||||
|
||||
execvp(
|
||||
"/bin/make", /* Above comment applies here */
|
||||
build_args
|
||||
);
|
||||
if (errno != 0) {
|
||||
errlog("An error occurred while running the make task.");
|
||||
return errno;
|
||||
if (TASK == NULL) {
|
||||
errlog("No task specified for make");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Fork before exec to prevent process replacement */
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
errlog("Failed to fork process");
|
||||
return 1;
|
||||
} else if (pid == 0) {
|
||||
/* Child process */
|
||||
char *build_args[] = {
|
||||
"/bin/make", /* This should be changed to /glacier/bin/make later on */
|
||||
TASK,
|
||||
NULL
|
||||
};
|
||||
|
||||
execvp("/bin/make", build_args);
|
||||
/* If we get here, execvp failed */
|
||||
errlog("Failed to execute make");
|
||||
fprintf(stderr, "Error: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
/* Parent process */
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
return WEXITSTATUS(status);
|
||||
} else {
|
||||
errlog("Make process terminated abnormally");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Should never reach here, but added for completeness */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -515,6 +705,15 @@ run_make_task(char TASK[])
|
||||
int
|
||||
print_hash(uchar *hash, uint length)
|
||||
{
|
||||
if (hash == NULL) {
|
||||
errlog("NULL hash pointer passed to print_hash()");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
warnlog("Zero-length hash passed to print_hash()");
|
||||
}
|
||||
|
||||
for (uint index = 0; index < length; index++) {
|
||||
printf("%02x", hash[index]);
|
||||
}
|
||||
@@ -533,43 +732,49 @@ print_hash(uchar *hash, uint length)
|
||||
*
|
||||
*/
|
||||
|
||||
/*int
|
||||
stash_hash(uchar *stored_hash, uchar *hash, uint length)
|
||||
int
|
||||
stash_hash(char *stored_hash, unsigned int stored_hash_size, const uchar *hash, uint length)
|
||||
{
|
||||
if (sizeof(stored_hash) != 32 && LG_VERBOSE == 1) {
|
||||
errlog("in stash_hash()");
|
||||
errlog("size of stored hash string must equal 32 bytes");
|
||||
return 1;
|
||||
}
|
||||
else if (sizeof(stored_hash) != 32) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint max_size_of_input = 3;
|
||||
|
||||
for (uint index = 0; index < length; index++) {
|
||||
if (hash[index] > max_size_of_input) {
|
||||
if (stored_hash == NULL || hash == NULL) {
|
||||
if (LG_VERBOSE == 1) {
|
||||
errlog("in stash_hash()");
|
||||
errlog("size of input exceeds maximum allowed size (hash[index] > 3)");
|
||||
return 1;
|
||||
errlog("NULL pointer provided");
|
||||
}
|
||||
snprintf(stored_hash[index], max_size_of_input, "%02x", hash[index]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (stored_hash_size < (length * 2 + 1)) {
|
||||
if (LG_VERBOSE == 1) {
|
||||
errlog("in stash_hash()");
|
||||
errlog("Output buffer too small for hash");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Clear the buffer first */
|
||||
stored_hash[0] = '\0';
|
||||
|
||||
/* Format each byte as a 2-character hex value */
|
||||
for (uint index = 0; index < length; index++) {
|
||||
char hex[3];
|
||||
snprintf(hex, sizeof(hex), "%02x", hash[index]);
|
||||
strncat(stored_hash, hex, stored_hash_size - strlen(stored_hash) - 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
} */
|
||||
}
|
||||
|
||||
/*
|
||||
* hash_file
|
||||
*
|
||||
* DESCRIPTION: Performs a hashing operation on a file and stores the result
|
||||
* PARAMETERS: unsigned char *h
|
||||
* PARAMETERS: const char *filename
|
||||
* DEFINED IN: security.h
|
||||
*
|
||||
*/
|
||||
|
||||
unsigned char
|
||||
hash_file(const char *filename)
|
||||
int
|
||||
hash_file(const char *filename, unsigned char *out_hash, unsigned int *out_length)
|
||||
{
|
||||
FILE *data = fopen(filename, "rb");
|
||||
if (! data && LG_VERBOSE == 1) {
|
||||
@@ -585,9 +790,11 @@ hash_file(const char *filename)
|
||||
if (! context && LG_VERBOSE == 1) {
|
||||
errlog("in hash_file()");
|
||||
errlog("error creating envelope context");
|
||||
fclose(data);
|
||||
return 2;
|
||||
}
|
||||
else if (! context) {
|
||||
fclose(data);
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -601,6 +808,22 @@ hash_file(const char *filename)
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Read and hash file content
|
||||
unsigned char buffer[BUFFER_SIZE];
|
||||
size_t bytes_read;
|
||||
|
||||
while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, data)) > 0) {
|
||||
if (EVP_DigestUpdate(context, buffer, bytes_read) != 1) {
|
||||
if (LG_VERBOSE == 1) {
|
||||
errlog("in hash_file()");
|
||||
errlog("error updating digest");
|
||||
}
|
||||
EVP_MD_CTX_free(context);
|
||||
fclose(data);
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (ferror(data)) {
|
||||
if (LG_VERBOSE == 1) {
|
||||
errlog("in hash_file()");
|
||||
@@ -608,24 +831,21 @@ hash_file(const char *filename)
|
||||
}
|
||||
EVP_MD_CTX_free(context);
|
||||
fclose(data);
|
||||
return 4;
|
||||
return 5;
|
||||
}
|
||||
|
||||
fclose(data);
|
||||
|
||||
uchar hash[EVP_MAX_MD_SIZE];
|
||||
uint hash_length;
|
||||
|
||||
if (EVP_DigestFinal_ex(context, hash, &hash_length) != 1) {
|
||||
if (EVP_DigestFinal_ex(context, out_hash, out_length) != 1) {
|
||||
if (LG_VERBOSE == 1) {
|
||||
errlog("in hash_file()");
|
||||
errlog("error finalizing digest");
|
||||
}
|
||||
EVP_MD_CTX_free(context);
|
||||
return 5;
|
||||
return 6;
|
||||
}
|
||||
|
||||
EVP_MD_CTX_free(context);
|
||||
|
||||
return *hash;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user