Compare commits
	
		
			3 Commits
		
	
	
		
			57238b535c
			...
			fa06287b13
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| fa06287b13 | |||
| 68694723fe | |||
| 4a4d551e75 | 
							
								
								
									
										242
									
								
								qssb.h
									
									
									
									
									
								
							
							
						
						
									
										242
									
								
								qssb.h
									
									
									
									
									
								
							@@ -29,6 +29,8 @@
 | 
				
			|||||||
#include <sys/mount.h>
 | 
					#include <sys/mount.h>
 | 
				
			||||||
#include <sys/prctl.h>
 | 
					#include <sys/prctl.h>
 | 
				
			||||||
#include <sys/random.h>
 | 
					#include <sys/random.h>
 | 
				
			||||||
 | 
					#include <sys/time.h>
 | 
				
			||||||
 | 
					#include <sys/resource.h>
 | 
				
			||||||
#include <stdarg.h>
 | 
					#include <stdarg.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
@@ -155,7 +157,28 @@ static int default_blacklisted_syscals[] = {
 | 
				
			|||||||
	QSSB_SYS(init_module),
 | 
						QSSB_SYS(init_module),
 | 
				
			||||||
	QSSB_SYS(finit_module),
 | 
						QSSB_SYS(finit_module),
 | 
				
			||||||
	QSSB_SYS(delete_module),
 | 
						QSSB_SYS(delete_module),
 | 
				
			||||||
	-1
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* TODO: Check for completion
 | 
				
			||||||
 | 
					 * Known blacklisting problem (catch up game, etc.)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * However, we use it to enhance "no_fs" policy, which does not solely rely
 | 
				
			||||||
 | 
					 * on seccomp anyway */
 | 
				
			||||||
 | 
					static int fs_access_syscalls[] = {
 | 
				
			||||||
 | 
						QSSB_SYS(chdir),
 | 
				
			||||||
 | 
						QSSB_SYS(truncate),
 | 
				
			||||||
 | 
						QSSB_SYS(stat),
 | 
				
			||||||
 | 
						QSSB_SYS(flock),
 | 
				
			||||||
 | 
						QSSB_SYS(chmod),
 | 
				
			||||||
 | 
						QSSB_SYS(chown),
 | 
				
			||||||
 | 
						QSSB_SYS(setxattr),
 | 
				
			||||||
 | 
						QSSB_SYS(utime),
 | 
				
			||||||
 | 
						QSSB_SYS(ioctl),
 | 
				
			||||||
 | 
						QSSB_SYS(fcntl),
 | 
				
			||||||
 | 
						QSSB_SYS(access),
 | 
				
			||||||
 | 
						QSSB_SYS(open),
 | 
				
			||||||
 | 
						QSSB_SYS(openat),
 | 
				
			||||||
 | 
						QSSB_SYS(unlink),
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct qssb_path_policy
 | 
					struct qssb_path_policy
 | 
				
			||||||
@@ -165,6 +188,16 @@ struct qssb_path_policy
 | 
				
			|||||||
	struct qssb_path_policy *next;
 | 
						struct qssb_path_policy *next;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct qssb_allocated_entry
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						void *data; /* the actual data */
 | 
				
			||||||
 | 
						size_t size; /* number of bytes allocated for size */
 | 
				
			||||||
 | 
						size_t used; /* number of bytes in use */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Number of bytes to grow the buffer in qssb_allocated_entry  with */
 | 
				
			||||||
 | 
					#define QSSB_ENTRY_ALLOC_SIZE 32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Policy tells qssb what to do */
 | 
					/* Policy tells qssb what to do */
 | 
				
			||||||
struct qssb_policy
 | 
					struct qssb_policy
 | 
				
			||||||
@@ -173,36 +206,106 @@ struct qssb_policy
 | 
				
			|||||||
	int preserve_cwd;
 | 
						int preserve_cwd;
 | 
				
			||||||
	int not_dumpable;
 | 
						int not_dumpable;
 | 
				
			||||||
	int no_new_privs;
 | 
						int no_new_privs;
 | 
				
			||||||
 | 
						int no_fs;
 | 
				
			||||||
 | 
						int no_new_fds;
 | 
				
			||||||
	int namespace_options;
 | 
						int namespace_options;
 | 
				
			||||||
	/* Bind mounts all paths in path_policies into the chroot and applies
 | 
						/* Bind mounts all paths in path_policies into the chroot and applies
 | 
				
			||||||
	 non-landlock policies */
 | 
						 non-landlock policies */
 | 
				
			||||||
	int mount_path_policies_to_chroot;
 | 
						int mount_path_policies_to_chroot;
 | 
				
			||||||
	int *blacklisted_syscalls;
 | 
					 | 
				
			||||||
	int *whitelisted_syscalls;
 | 
					 | 
				
			||||||
	char chroot_target_path[PATH_MAX];
 | 
						char chroot_target_path[PATH_MAX];
 | 
				
			||||||
	const char *chdir_path;
 | 
						const char *chdir_path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Do not manually add policies here, use qssb_append_path_polic*() */
 | 
						/* Do not manually add policies here, use qssb_append_path_polic*() */
 | 
				
			||||||
	struct qssb_path_policy *path_policies;
 | 
						struct qssb_path_policy *path_policies;
 | 
				
			||||||
	struct qssb_path_policy **path_policies_tail;
 | 
						struct qssb_path_policy **path_policies_tail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Do not manually add entries here, use qssb_append_denied_syscall() etc. */
 | 
				
			||||||
 | 
						struct qssb_allocated_entry denied_syscalls;
 | 
				
			||||||
 | 
						struct qssb_allocated_entry allowed_syscalls;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int qssb_entry_append(struct qssb_allocated_entry *entry, void *data, size_t bytes)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t remaining = entry->size - entry->used;
 | 
				
			||||||
 | 
						if(remaining < bytes)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							size_t expandval = QSSB_ENTRY_ALLOC_SIZE > bytes ? QSSB_ENTRY_ALLOC_SIZE : bytes;
 | 
				
			||||||
 | 
							size_t sizenew = entry->size + expandval;
 | 
				
			||||||
 | 
							int *datanew = (int *) realloc(entry->data, sizenew);
 | 
				
			||||||
 | 
							if(datanew == NULL)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								QSSB_LOG_ERROR("failed to resize array: %s\n", strerror(errno));
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							entry->size = sizenew;
 | 
				
			||||||
 | 
							entry->data = datanew;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						uint8_t *target = (uint8_t *) entry->data;
 | 
				
			||||||
 | 
						memcpy(target + entry->used, data, bytes);
 | 
				
			||||||
 | 
						entry->used = entry->used + bytes;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int qssb_append_syscall(struct qssb_allocated_entry *entry, int *syscalls, size_t n)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return qssb_entry_append(entry, syscalls, n * sizeof(int));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int qssb_append_denied_syscall(struct qssb_policy *qssb_policy, int syscall)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return qssb_append_syscall(&qssb_policy->denied_syscalls, &syscall, 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int qssb_append_allowed_syscall(struct qssb_policy *qssb_policy, int syscall)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return qssb_append_syscall(&qssb_policy->allowed_syscalls, &syscall, 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int qssb_append_allowed_syscalls(struct qssb_policy *qssb_policy, int *syscalls, size_t n)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return qssb_append_syscall(&qssb_policy->allowed_syscalls, syscalls, n);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int qssb_append_denied_syscalls(struct qssb_policy *qssb_policy, int *syscalls, size_t n)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return qssb_append_syscall(&qssb_policy->denied_syscalls, syscalls, n);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Creates the default policy
 | 
					/* Creates the default policy
 | 
				
			||||||
 * Must be freed using qssb_free_policy
 | 
					 * Must be freed using qssb_free_policy
 | 
				
			||||||
 * @returns: default policy */
 | 
					 * @returns: default policy */
 | 
				
			||||||
struct qssb_policy *qssb_init_policy()
 | 
					struct qssb_policy *qssb_init_policy()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct qssb_policy *result = (struct qssb_policy *) calloc(1, sizeof(struct qssb_policy));
 | 
						struct qssb_policy *result = (struct qssb_policy *) calloc(1, sizeof(struct qssb_policy));
 | 
				
			||||||
	result->blacklisted_syscalls = default_blacklisted_syscals;
 | 
					 | 
				
			||||||
	result->drop_caps = 1;
 | 
						result->drop_caps = 1;
 | 
				
			||||||
	result->not_dumpable = 1;
 | 
						result->not_dumpable = 1;
 | 
				
			||||||
	result->no_new_privs = 1;
 | 
						result->no_new_privs = 1;
 | 
				
			||||||
 | 
						result->no_fs = 0;
 | 
				
			||||||
 | 
						result->no_new_fds = 0;
 | 
				
			||||||
	result->namespace_options = QSSB_UNSHARE_MOUNT | QSSB_UNSHARE_USER;
 | 
						result->namespace_options = QSSB_UNSHARE_MOUNT | QSSB_UNSHARE_USER;
 | 
				
			||||||
	result->chdir_path = NULL;
 | 
						result->chdir_path = NULL;
 | 
				
			||||||
	result->mount_path_policies_to_chroot = 0;
 | 
						result->mount_path_policies_to_chroot = 0;
 | 
				
			||||||
	result->chroot_target_path[0] = '\0';
 | 
						result->chroot_target_path[0] = '\0';
 | 
				
			||||||
	result->path_policies = NULL;
 | 
						result->path_policies = NULL;
 | 
				
			||||||
	result->path_policies_tail = &(result->path_policies);
 | 
						result->path_policies_tail = &(result->path_policies);
 | 
				
			||||||
 | 
						result->allowed_syscalls.data = NULL;
 | 
				
			||||||
 | 
						result->allowed_syscalls.size = 0;
 | 
				
			||||||
 | 
						result->allowed_syscalls.used = 0;
 | 
				
			||||||
 | 
						result->denied_syscalls.data = NULL;
 | 
				
			||||||
 | 
						result->denied_syscalls.size = 0;
 | 
				
			||||||
 | 
						result->denied_syscalls.used = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t blacklisted_syscalls_count = sizeof(default_blacklisted_syscals)/sizeof(default_blacklisted_syscals[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int appendresult = qssb_append_denied_syscalls(result, default_blacklisted_syscals, blacklisted_syscalls_count);
 | 
				
			||||||
 | 
						if(appendresult != 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -526,13 +629,13 @@ static int drop_caps()
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * Enables the per_syscall seccomp action for system calls
 | 
					 * Enables the per_syscall seccomp action for system calls
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * syscalls: array of system calls numbers. -1 must be the last entry.
 | 
					 * syscalls: array of system calls numbers.
 | 
				
			||||||
 * per_syscall: action to apply for each system call
 | 
					 * per_syscall: action to apply for each system call
 | 
				
			||||||
 * default_action: the default action at the end
 | 
					 * default_action: the default action at the end
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @returns: 0 on success, -1 on error
 | 
					 * @returns: 0 on success, -1 on error
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int seccomp_enable(int *syscalls, unsigned int per_syscall, unsigned int default_action)
 | 
					static int seccomp_enable(int *syscalls, size_t n, unsigned int per_syscall, unsigned int default_action)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct sock_filter filter[1024] =
 | 
						struct sock_filter filter[1024] =
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -540,15 +643,13 @@ static int seccomp_enable(int *syscalls, unsigned int per_syscall, unsigned int
 | 
				
			|||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unsigned short int current_filter_index = 1;
 | 
						unsigned short int current_filter_index = 1;
 | 
				
			||||||
	while(*syscalls >= 0)
 | 
						for(size_t i = 0; i < n; i++)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		unsigned int sysc = (unsigned int) *syscalls;
 | 
							unsigned int sysc = (unsigned int) syscalls[i];
 | 
				
			||||||
		struct sock_filter syscall = BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, sysc, 0, 1);
 | 
							struct sock_filter syscall = BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, sysc, 0, 1);
 | 
				
			||||||
		struct sock_filter action = BPF_STMT(BPF_RET+BPF_K, per_syscall);
 | 
							struct sock_filter action = BPF_STMT(BPF_RET+BPF_K, per_syscall);
 | 
				
			||||||
		filter[current_filter_index++] = syscall;
 | 
							filter[current_filter_index++] = syscall;
 | 
				
			||||||
		filter[current_filter_index++] = action;
 | 
							filter[current_filter_index++] = action;
 | 
				
			||||||
 | 
					 | 
				
			||||||
		++syscalls;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct sock_filter da = BPF_STMT(BPF_RET+BPF_K, default_action);
 | 
						struct sock_filter da = BPF_STMT(BPF_RET+BPF_K, default_action);
 | 
				
			||||||
@@ -572,21 +673,21 @@ static int seccomp_enable(int *syscalls, unsigned int per_syscall, unsigned int
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * Blacklists the specified systemcalls.
 | 
					 * Blacklists the specified systemcalls.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * syscalls: array of system calls numbers. -1 must be the last entry.
 | 
					 * syscalls: array of system calls numbers.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int seccomp_enable_blacklist(int *syscalls)
 | 
					static int seccomp_enable_blacklist(int *syscalls, size_t n)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return seccomp_enable(syscalls, SECCOMP_RET_KILL, SECCOMP_RET_ALLOW);
 | 
						return seccomp_enable(syscalls, n, SECCOMP_RET_KILL, SECCOMP_RET_ALLOW);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Whitelists the specified systemcalls.
 | 
					 * Whitelists the specified systemcalls.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * syscalls: array of system calls numbers. -1 must be the last entry.
 | 
					 * syscalls: array of system calls numbers.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int seccomp_enable_whitelist(int *syscalls)
 | 
					static int seccomp_enable_whitelist(int *syscalls, size_t n)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return seccomp_enable(syscalls, SECCOMP_RET_ALLOW, SECCOMP_RET_KILL);
 | 
						return seccomp_enable(syscalls, n, SECCOMP_RET_ALLOW, SECCOMP_RET_KILL);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if HAVE_LANDLOCK == 1
 | 
					#if HAVE_LANDLOCK == 1
 | 
				
			||||||
@@ -708,9 +809,9 @@ static int landlock_prepare_ruleset(struct qssb_path_policy *policies)
 | 
				
			|||||||
/* Checks for illogical or dangerous combinations */
 | 
					/* Checks for illogical or dangerous combinations */
 | 
				
			||||||
static int check_policy_sanity(struct qssb_policy *policy)
 | 
					static int check_policy_sanity(struct qssb_policy *policy)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if(policy->blacklisted_syscalls != NULL && policy->whitelisted_syscalls != NULL)
 | 
						if(policy->denied_syscalls.used > 0 && policy->allowed_syscalls.used > 0)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		QSSB_LOG_ERROR("Error: Cannot mix blacklisted and whitelisted systemcalls\n");
 | 
							QSSB_LOG_ERROR("Error: Cannot mix allowed and denied systemcalls in policy\n");
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -730,23 +831,86 @@ static int check_policy_sanity(struct qssb_policy *policy)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if(policy->no_new_privs != 1)
 | 
						if(policy->no_new_privs != 1)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if(policy->blacklisted_syscalls != NULL || policy->whitelisted_syscalls != NULL)
 | 
							if(policy->allowed_syscalls.used > 0 || policy->denied_syscalls.used > 0)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			QSSB_LOG_ERROR("no_new_privs = 1 is required for seccomp filtering!\n");
 | 
								QSSB_LOG_ERROR("no_new_privs = 1 is required for seccomp filtering!\n");
 | 
				
			||||||
			return -1;
 | 
								return -1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if(policy->path_policies != NULL && policy->mount_path_policies_to_chroot != 1)
 | 
						if(policy->path_policies != NULL)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		#if HAVE_LANDLOCK != 1
 | 
					
 | 
				
			||||||
			QSSB_LOG_ERROR("Path policies cannot be enforced! System needs landlock support or set mount_path_policies_to_chroot = 1\n");
 | 
							if(policy->mount_path_policies_to_chroot != 1)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								#if HAVE_LANDLOCK != 1
 | 
				
			||||||
 | 
									QSSB_LOG_ERROR("Path policies cannot be enforced! System needs landlock support or set mount_path_policies_to_chroot = 1\n");
 | 
				
			||||||
 | 
									return -1;
 | 
				
			||||||
 | 
								#endif
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if(policy->no_fs == 1)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								QSSB_LOG_ERROR("If path_policies are specified, no_fs cannot be set to 1");
 | 
				
			||||||
			return -1;
 | 
								return -1;
 | 
				
			||||||
		#endif
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void close_file_fds()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long max_files = sysconf(_SC_OPEN_MAX);
 | 
				
			||||||
 | 
						for(long i = 3; i <= max_files; i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							close((int)i);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Takes away file system access from the process
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * We use this when "no_fs" is given in the policy.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is useful for restricted subprocesses that do some computational work
 | 
				
			||||||
 | 
					 * and do not require filesystem access
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @returns: 0 on success, < 0 on error
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int enable_no_fs(struct qssb_policy *policy)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
							close_file_fds();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if(chdir("/proc/self/fdinfo") != 0)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								QSSB_LOG_ERROR("Failed to change to safe directory: %s\n", strerror(errno));
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if(chroot(".") != 0)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								QSSB_LOG_ERROR("Failed to chroot into safe directory: %s\n", strerror(errno));
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if(chdir("/") != 0)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								QSSB_LOG_ERROR("Failed to chdir into safe directory inside chroot: %s\n", strerror(errno));
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if(policy->allowed_syscalls.used == 0)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								size_t fs_access_syscalls_count = sizeof(fs_access_syscalls)/sizeof(fs_access_syscalls[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								int ret = qssb_append_denied_syscalls(policy, fs_access_syscalls, fs_access_syscalls_count);
 | 
				
			||||||
 | 
								if(ret != 0)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									QSSB_LOG_ERROR("Failed to add system calls to blacklist\n");
 | 
				
			||||||
 | 
									return -1;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Enables the specified qssb_policy.
 | 
					/* Enables the specified qssb_policy.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This function is not atomic (and can't be). This means some
 | 
					 * This function is not atomic (and can't be). This means some
 | 
				
			||||||
@@ -827,7 +991,6 @@ int qssb_enable_policy(struct qssb_policy *policy)
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(policy->chdir_path == NULL)
 | 
						if(policy->chdir_path == NULL)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		policy->chdir_path = "/";
 | 
							policy->chdir_path = "/";
 | 
				
			||||||
@@ -839,6 +1002,25 @@ int qssb_enable_policy(struct qssb_policy *policy)
 | 
				
			|||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if(policy->no_fs)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if(enable_no_fs(policy) != 0)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								QSSB_LOG_ERROR("Failed to take away filesystem access of process\n");
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if(policy->no_new_fds)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const struct rlimit nofile = {0, 0};
 | 
				
			||||||
 | 
							if (setrlimit(RLIMIT_NOFILE, &nofile) == -1)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								QSSB_LOG_ERROR("setrlimit: Failed to set rlimit: %s\n", strerror(errno));
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if(policy->drop_caps)
 | 
						if(policy->drop_caps)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if(drop_caps() < 0)
 | 
							if(drop_caps() < 0)
 | 
				
			||||||
@@ -876,18 +1058,22 @@ int qssb_enable_policy(struct qssb_policy *policy)
 | 
				
			|||||||
	close(landlock_ruleset_fd);
 | 
						close(landlock_ruleset_fd);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if(policy->whitelisted_syscalls != NULL)
 | 
						if(policy->allowed_syscalls.used > 0)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if(seccomp_enable_whitelist(policy->whitelisted_syscalls) <0)
 | 
							int *syscalls = (int *)policy->allowed_syscalls.data;
 | 
				
			||||||
 | 
							size_t n = policy->allowed_syscalls.used / sizeof(int);
 | 
				
			||||||
 | 
							if(seccomp_enable_whitelist(syscalls, n) < 0)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			QSSB_LOG_ERROR("seccomp_enable_whitelist failed\n");
 | 
								QSSB_LOG_ERROR("seccomp_enable_whitelist failed\n");
 | 
				
			||||||
			return -1;
 | 
								return -1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if(policy->blacklisted_syscalls != NULL)
 | 
						if(policy->denied_syscalls.used > 0)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if(seccomp_enable_blacklist(policy->blacklisted_syscalls) <0)
 | 
							int *syscalls = (int *)policy->denied_syscalls.data;
 | 
				
			||||||
 | 
							size_t n = policy->denied_syscalls.used / sizeof(int);
 | 
				
			||||||
 | 
							if(seccomp_enable_blacklist(syscalls, n) < 0)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			QSSB_LOG_ERROR("seccomp_enable_blacklist failed\n");
 | 
								QSSB_LOG_ERROR("seccomp_enable_blacklist failed\n");
 | 
				
			||||||
			return -1;
 | 
								return -1;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										86
									
								
								test.c
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								test.c
									
									
									
									
									
								
							@@ -1,5 +1,10 @@
 | 
				
			|||||||
#include "qssb.h"
 | 
					#include "qssb.h"
 | 
				
			||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					#include <dirent.h>
 | 
				
			||||||
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					#include <sys/socket.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int test_default_main(int argc, char *argv[])
 | 
					int test_default_main(int argc, char *argv[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct qssb_policy *policy = qssb_init_policy();
 | 
						struct qssb_policy *policy = qssb_init_policy();
 | 
				
			||||||
@@ -10,9 +15,11 @@ int test_default_main(int argc, char *argv[])
 | 
				
			|||||||
int test_both_syscalls(int argc, char *argv[])
 | 
					int test_both_syscalls(int argc, char *argv[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct qssb_policy *policy = qssb_init_policy();
 | 
						struct qssb_policy *policy = qssb_init_policy();
 | 
				
			||||||
	int bla[] = { 1,2,3};
 | 
						int syscalls[] = {1,2,3};
 | 
				
			||||||
	policy->blacklisted_syscalls = &bla;
 | 
					
 | 
				
			||||||
	policy->whitelisted_syscalls = &bla;
 | 
						qssb_append_denied_syscalls(policy, syscalls, 3);
 | 
				
			||||||
 | 
						qssb_append_allowed_syscalls(policy, syscalls, 3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int ret = qssb_enable_policy(policy);
 | 
						int ret = qssb_enable_policy(policy);
 | 
				
			||||||
	if(ret != 0)
 | 
						if(ret != 0)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -24,8 +31,9 @@ int test_both_syscalls(int argc, char *argv[])
 | 
				
			|||||||
int test_seccomp_blacklisted(int argc, char *argv[])
 | 
					int test_seccomp_blacklisted(int argc, char *argv[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct qssb_policy *policy = qssb_init_policy();
 | 
						struct qssb_policy *policy = qssb_init_policy();
 | 
				
			||||||
	int blacklisted[] = { QSSB_SYS(getuid) };
 | 
					
 | 
				
			||||||
	policy->blacklisted_syscalls = blacklisted;
 | 
						qssb_append_denied_syscall(policy, QSSB_SYS(getuid));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int ret = qssb_enable_policy(policy);
 | 
						int ret = qssb_enable_policy(policy);
 | 
				
			||||||
	uid_t pid = geteuid();
 | 
						uid_t pid = geteuid();
 | 
				
			||||||
	pid = getuid();
 | 
						pid = getuid();
 | 
				
			||||||
@@ -35,8 +43,9 @@ int test_seccomp_blacklisted(int argc, char *argv[])
 | 
				
			|||||||
int test_seccomp_blacklisted_call_permitted(int argc, char *argv[])
 | 
					int test_seccomp_blacklisted_call_permitted(int argc, char *argv[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct qssb_policy *policy = qssb_init_policy();
 | 
						struct qssb_policy *policy = qssb_init_policy();
 | 
				
			||||||
	int blacklisted[] = { QSSB_SYS(getuid) };
 | 
					
 | 
				
			||||||
	policy->blacklisted_syscalls = blacklisted;
 | 
						qssb_append_denied_syscall(policy, QSSB_SYS(getuid));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int ret = qssb_enable_policy(policy);
 | 
						int ret = qssb_enable_policy(policy);
 | 
				
			||||||
	//geteuid is not blacklisted, so must succeed
 | 
						//geteuid is not blacklisted, so must succeed
 | 
				
			||||||
	uid_t pid = geteuid();
 | 
						uid_t pid = geteuid();
 | 
				
			||||||
@@ -69,6 +78,65 @@ int test_landlock_deny_write(int argc, char *argv[])
 | 
				
			|||||||
	return 1;
 | 
						return 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int test_nofs(int argc, char *argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct qssb_policy *policy = qssb_init_policy();
 | 
				
			||||||
 | 
						policy->no_fs = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int ret = qssb_enable_policy(policy);
 | 
				
			||||||
 | 
						if(ret != 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fprintf(stderr, "Failed to activate nofs sandbox\n");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int s = socket(AF_INET,SOCK_STREAM,0);
 | 
				
			||||||
 | 
						if(s == -1)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fprintf(stderr, "Failed to open socket but this was not requested by policy\n");
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Expect seccomp to take care of this */
 | 
				
			||||||
 | 
						if(open("/test", O_CREAT | O_WRONLY) >= 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fprintf(stderr, "Failed: Do not expect write access\n");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int test_no_new_fds(int argc, char *argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct qssb_policy *policy = qssb_init_policy();
 | 
				
			||||||
 | 
						policy->no_new_fds = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int ret = qssb_enable_policy(policy);
 | 
				
			||||||
 | 
						if(ret != 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fprintf(stderr, "Failed to activate no_new_fd sandbox\n");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if(open("/tmp/test", O_CREAT | O_WRONLY) >= 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fprintf(stderr, "Failed: Could open new file descriptor\n");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int s = socket(AF_INET,SOCK_STREAM,0);
 | 
				
			||||||
 | 
						if(s >= 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fprintf(stderr, "Failed: socket got opened but policy denied\n");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct dispatcher
 | 
					struct dispatcher
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char *name;
 | 
						char *name;
 | 
				
			||||||
@@ -81,7 +149,9 @@ struct dispatcher dispatchers[] = {
 | 
				
			|||||||
	{ "seccomp-blacklisted", &test_seccomp_blacklisted, false },
 | 
						{ "seccomp-blacklisted", &test_seccomp_blacklisted, false },
 | 
				
			||||||
	{ "seccomp-blacklisted-permitted", &test_seccomp_blacklisted_call_permitted, true },
 | 
						{ "seccomp-blacklisted-permitted", &test_seccomp_blacklisted_call_permitted, true },
 | 
				
			||||||
	{ "landlock", &test_landlock, true },
 | 
						{ "landlock", &test_landlock, true },
 | 
				
			||||||
	{ "landlock-deny-write", &test_landlock_deny_write, true }
 | 
						{ "landlock-deny-write", &test_landlock_deny_write, true },
 | 
				
			||||||
 | 
						{ "no_fs", &test_nofs, false},
 | 
				
			||||||
 | 
						{ "no_new_fds", &test_no_new_fds, true}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int main(int argc, char *argv[])
 | 
					int main(int argc, char *argv[])
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user