Comparar commits
	
		
			15 Commits
		
	
	
		
			fa06287b13
			...
			215032f32c
		
	
	| Autor | SHA1 | Fecha | |
|---|---|---|---|
| 215032f32c | |||
| 411e00715d | |||
| 8a9b1730de | |||
| b2b501d97e | |||
| 26f391f736 | |||
| 68fd1a0a87 | |||
| b0d0beab22 | |||
| c44ce85628 | |||
| 25d8ed9bca | |||
| e389140436 | |||
| f6af1bb78f | |||
| 9192ec3aa4 | |||
| 51844ea3ab | |||
| 66c6d28dcd | |||
| 5cd45c09b7 | 
| @@ -4,6 +4,8 @@ | |||||||
| ## Status | ## Status | ||||||
| No release yet, expiremental, API is unstable, builds will break on updates of this library.  | No release yet, expiremental, API is unstable, builds will break on updates of this library.  | ||||||
|  |  | ||||||
|  | Currently, it's mainly evolving according to the needs of my other projects.  | ||||||
|  |  | ||||||
| ## Features | ## Features | ||||||
|  |  | ||||||
|   - Systemcall filtering (using seccomp-bpf) |   - Systemcall filtering (using seccomp-bpf) | ||||||
| @@ -36,7 +38,7 @@ the library may check against that. Execute | |||||||
| `echo 1 > /proc/sys/kernel/unprivileged_userns_clone` to disable that patch for now. | `echo 1 > /proc/sys/kernel/unprivileged_userns_clone` to disable that patch for now. | ||||||
|  |  | ||||||
| ### Examples | ### Examples | ||||||
|  |   - looqs: https://gitea.quitesimple.org/crtxcr/looqs | ||||||
|   - qswiki: https://gitea.quitesimple.org/crtxcr/qswiki |   - qswiki: https://gitea.quitesimple.org/crtxcr/qswiki | ||||||
|   - cgit sandboxed: https://gitea.quitesimple.org/crtxcr/cgitsb |   - cgit sandboxed: https://gitea.quitesimple.org/crtxcr/cgitsb | ||||||
|   - qpdfviewsb sandboxed (quick and dirty): https://gitea.quitesimple.org/crtxcr/qpdfviewsb |   - qpdfviewsb sandboxed (quick and dirty): https://gitea.quitesimple.org/crtxcr/qpdfviewsb | ||||||
|   | |||||||
							
								
								
									
										344
									
								
								qssb.h
									
									
									
									
									
								
							
							
						
						
									
										344
									
								
								qssb.h
									
									
									
									
									
								
							| @@ -40,6 +40,7 @@ | |||||||
| #include <linux/filter.h> | #include <linux/filter.h> | ||||||
| #include <linux/seccomp.h> | #include <linux/seccomp.h> | ||||||
| #include <linux/version.h> | #include <linux/version.h> | ||||||
|  | #include <linux/audit.h> | ||||||
| #include <sys/capability.h> | #include <sys/capability.h> | ||||||
| #include <stddef.h> | #include <stddef.h> | ||||||
| #include <inttypes.h> | #include <inttypes.h> | ||||||
| @@ -58,17 +59,18 @@ | |||||||
| 	#endif | 	#endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| //TODO: stolen from kernel samples/seccomp, GPLv2...? |  | ||||||
| #define ALLOW \ | #if defined(__i386__) | ||||||
| 	BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) | #define SECCOMP_AUDIT_ARCH AUDIT_ARCH_I386 | ||||||
| #define DENY \ | #elif defined(__x86_64__) | ||||||
| 	BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL) | #define SECCOMP_AUDIT_ARCH AUDIT_ARCH_X86_64 | ||||||
|  | #else | ||||||
|  | #warning Seccomp support has not been tested for qssb.h for this platform yet | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define SYSCALL(nr, jt) \ | #define SYSCALL(nr, jt) \ | ||||||
| 	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (nr), 0, 1), jt | 	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (nr), 0, 1), jt | ||||||
|  |  | ||||||
| #define LOAD_SYSCALL_NR \ |  | ||||||
| 	BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ |  | ||||||
| 		 offsetof(struct seccomp_data, nr)) |  | ||||||
|  |  | ||||||
| #define QSSB_UNSHARE_NETWORK 1<<1 | #define QSSB_UNSHARE_NETWORK 1<<1 | ||||||
| #define QSSB_UNSHARE_USER 1<<2 | #define QSSB_UNSHARE_USER 1<<2 | ||||||
| @@ -140,7 +142,7 @@ static inline int landlock_restrict_self(const int ruleset_fd, | |||||||
|  */ |  */ | ||||||
|  /* TODO: more execv* in some architectures */ |  /* TODO: more execv* in some architectures */ | ||||||
|  /* TODO: add more */ |  /* TODO: add more */ | ||||||
| static int default_blacklisted_syscals[] = { | static long default_blacklisted_syscalls[] = { | ||||||
| 	QSSB_SYS(setuid), | 	QSSB_SYS(setuid), | ||||||
| 	QSSB_SYS(setgid), | 	QSSB_SYS(setgid), | ||||||
| 	QSSB_SYS(chroot), | 	QSSB_SYS(chroot), | ||||||
| @@ -164,7 +166,7 @@ static int default_blacklisted_syscals[] = { | |||||||
|  * |  * | ||||||
|  * However, we use it to enhance "no_fs" policy, which does not solely rely |  * However, we use it to enhance "no_fs" policy, which does not solely rely | ||||||
|  * on seccomp anyway */ |  * on seccomp anyway */ | ||||||
| static int fs_access_syscalls[] = { | static long fs_access_syscalls[] = { | ||||||
| 	QSSB_SYS(chdir), | 	QSSB_SYS(chdir), | ||||||
| 	QSSB_SYS(truncate), | 	QSSB_SYS(truncate), | ||||||
| 	QSSB_SYS(stat), | 	QSSB_SYS(stat), | ||||||
| @@ -188,13 +190,29 @@ struct qssb_path_policy | |||||||
| 	struct qssb_path_policy *next; | 	struct qssb_path_policy *next; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| struct qssb_allocated_entry | struct qssb_allocated_entry | ||||||
| { | { | ||||||
| 	void *data; /* the actual data */ | 	void *data; /* the actual data */ | ||||||
| 	size_t size; /* number of bytes allocated for size */ | 	size_t size; /* number of bytes allocated for data */ | ||||||
| 	size_t used; /* number of bytes in use */ | 	size_t used; /* number of bytes in use */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | /* Special value */ | ||||||
|  | #define QSSB_SYSCALL_MATCH_ALL -1 | ||||||
|  |  | ||||||
|  | #define QSSB_SYSCALL_ALLOW 1 | ||||||
|  | #define QSSB_SYSCALL_DENY_KILL_PROCESS 2 | ||||||
|  | #define QSSB_SYSCALL_DENY_RET_ERROR 3 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | struct qssb_syscall_policy | ||||||
|  | { | ||||||
|  | 	struct qssb_allocated_entry syscall; | ||||||
|  | 	unsigned int policy; | ||||||
|  | 	struct qssb_syscall_policy *next; | ||||||
|  | }; | ||||||
|  |  | ||||||
| /* Number of bytes to grow the buffer in qssb_allocated_entry  with */ | /* Number of bytes to grow the buffer in qssb_allocated_entry  with */ | ||||||
| #define QSSB_ENTRY_ALLOC_SIZE 32 | #define QSSB_ENTRY_ALLOC_SIZE 32 | ||||||
|  |  | ||||||
| @@ -209,6 +227,7 @@ struct qssb_policy | |||||||
| 	int no_fs; | 	int no_fs; | ||||||
| 	int no_new_fds; | 	int no_new_fds; | ||||||
| 	int namespace_options; | 	int namespace_options; | ||||||
|  | 	int disable_syscall_filter; | ||||||
| 	/* 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; | ||||||
| @@ -219,9 +238,9 @@ struct qssb_policy | |||||||
| 	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. */ | 	/* Do not manually add policies here, use qssb_append_syscall_policy() */ | ||||||
| 	struct qssb_allocated_entry denied_syscalls; | 	struct qssb_syscall_policy *syscall_policies; | ||||||
| 	struct qssb_allocated_entry allowed_syscalls; | 	struct qssb_syscall_policy **syscall_policies_tail; | ||||||
|  |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -232,6 +251,11 @@ static int qssb_entry_append(struct qssb_allocated_entry *entry, void *data, siz | |||||||
| 	{ | 	{ | ||||||
| 		size_t expandval = QSSB_ENTRY_ALLOC_SIZE > bytes ? QSSB_ENTRY_ALLOC_SIZE : bytes; | 		size_t expandval = QSSB_ENTRY_ALLOC_SIZE > bytes ? QSSB_ENTRY_ALLOC_SIZE : bytes; | ||||||
| 		size_t sizenew = entry->size + expandval; | 		size_t sizenew = entry->size + expandval; | ||||||
|  | 		if(sizenew < entry->size) | ||||||
|  | 		{ | ||||||
|  | 			QSSB_LOG_ERROR("overflow in qssb_entry_append\n"); | ||||||
|  | 			return -EINVAL; | ||||||
|  | 		} | ||||||
| 		int *datanew = (int *) realloc(entry->data, sizenew); | 		int *datanew = (int *) realloc(entry->data, sizenew); | ||||||
| 		if(datanew == NULL) | 		if(datanew == NULL) | ||||||
| 		{ | 		{ | ||||||
| @@ -247,32 +271,68 @@ static int qssb_entry_append(struct qssb_allocated_entry *entry, void *data, siz | |||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int qssb_append_syscall(struct qssb_allocated_entry *entry, int *syscalls, size_t n) | static int qssb_append_syscall(struct qssb_allocated_entry *entry, long *syscalls, size_t n) | ||||||
| { | { | ||||||
| 	return qssb_entry_append(entry, syscalls, n * sizeof(int)); | 	return qssb_entry_append(entry, syscalls, n * sizeof(long)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int is_valid_syscall_policy(unsigned int policy) | ||||||
| int qssb_append_denied_syscall(struct qssb_policy *qssb_policy, int syscall) |  | ||||||
| { | { | ||||||
| 	return qssb_append_syscall(&qssb_policy->denied_syscalls, &syscall, 1); | 	return policy == QSSB_SYSCALL_ALLOW || policy == QSSB_SYSCALL_DENY_RET_ERROR || policy == QSSB_SYSCALL_DENY_KILL_PROCESS; | ||||||
| } | } | ||||||
|  |  | ||||||
| int qssb_append_allowed_syscall(struct qssb_policy *qssb_policy, int syscall) | static void get_syscall_array(struct qssb_syscall_policy *policy, long **syscall, size_t *n) | ||||||
| { | { | ||||||
| 	return qssb_append_syscall(&qssb_policy->allowed_syscalls, &syscall, 1); | 	*syscall = (long *) policy->syscall.data; | ||||||
|  | 	*n = policy->syscall.used / sizeof(long); | ||||||
| } | } | ||||||
|  |  | ||||||
| int qssb_append_allowed_syscalls(struct qssb_policy *qssb_policy, int *syscalls, size_t n) | int qssb_append_syscalls_policy(struct qssb_policy *qssb_policy, unsigned int syscall_policy, long *syscalls, size_t n) | ||||||
| { | { | ||||||
|  | 	/* Check whether we already have this policy. If so, merge new entries to the existing ones */ | ||||||
|  | 	struct qssb_syscall_policy *current_policy = qssb_policy->syscall_policies; | ||||||
|  | 	while(current_policy) | ||||||
|  | 	{ | ||||||
|  | 		if(current_policy->policy == syscall_policy) | ||||||
|  | 		{ | ||||||
|  | 			return qssb_append_syscall(¤t_policy->syscall, syscalls, n); | ||||||
|  | 		} | ||||||
|  | 		current_policy = current_policy->next; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return qssb_append_syscall(&qssb_policy->allowed_syscalls, syscalls, n); | 	/* We don't so we create a new policy */ | ||||||
|  | 	struct qssb_syscall_policy *newpolicy = (struct qssb_syscall_policy *) calloc(1, sizeof(struct qssb_syscall_policy)); | ||||||
|  | 	if(newpolicy == NULL) | ||||||
|  | 	{ | ||||||
|  | 		QSSB_LOG_ERROR("Failed to allocate memory for syscall policy\n"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	int ret = qssb_append_syscall(&newpolicy->syscall, syscalls, n); | ||||||
|  | 	if(ret != 0) | ||||||
|  | 	{ | ||||||
|  | 		QSSB_LOG_ERROR("Failed to append syscall\n"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	newpolicy->next = NULL; | ||||||
|  | 	newpolicy->policy = syscall_policy; | ||||||
|  |  | ||||||
|  | 	*(qssb_policy->syscall_policies_tail) = newpolicy; | ||||||
|  | 	qssb_policy->syscall_policies_tail = &(newpolicy->next); | ||||||
|  |  | ||||||
|  | 	qssb_policy->disable_syscall_filter = 0; | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int qssb_append_denied_syscalls(struct qssb_policy *qssb_policy, int *syscalls, size_t n) | int qssb_append_syscall_policy(struct qssb_policy *qssb_policy, unsigned int syscall_policy, long syscall) | ||||||
| { | { | ||||||
|  | 	return qssb_append_syscalls_policy(qssb_policy, syscall_policy, &syscall, 1); | ||||||
|  | } | ||||||
|  |  | ||||||
| 	return qssb_append_syscall(&qssb_policy->denied_syscalls, syscalls, n); | int qssb_append_syscall_default_policy(struct qssb_policy *qssb_policy, unsigned int default_policy) | ||||||
|  | { | ||||||
|  | 	return qssb_append_syscall_policy(qssb_policy, default_policy, QSSB_SYSCALL_MATCH_ALL); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Creates the default policy | /* Creates the default policy | ||||||
| @@ -287,25 +347,16 @@ struct qssb_policy *qssb_init_policy() | |||||||
| 	result->no_fs = 0; | 	result->no_fs = 0; | ||||||
| 	result->no_new_fds = 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->disable_syscall_filter = 0; | ||||||
| 	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]); | 	result->syscall_policies = NULL; | ||||||
|  | 	result->syscall_policies_tail = &(result->syscall_policies); | ||||||
|  |  | ||||||
| 	int appendresult = qssb_append_denied_syscalls(result, default_blacklisted_syscals, blacklisted_syscalls_count); |  | ||||||
| 	if(appendresult != 0) |  | ||||||
| 	{ |  | ||||||
| 		return NULL; |  | ||||||
| 	} |  | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -350,6 +401,8 @@ int qssb_append_path_policy(struct qssb_policy *qssb_policy, unsigned int path_p | |||||||
| 	return qssb_append_path_policies(qssb_policy, path_policy, path, NULL); | 	return qssb_append_path_policies(qssb_policy, path_policy, path, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Fills buffer with random characters a-z. |  * Fills buffer with random characters a-z. | ||||||
|  * The string will be null terminated. |  * The string will be null terminated. | ||||||
| @@ -532,6 +585,14 @@ void qssb_free_policy(struct qssb_policy *ctxt) | |||||||
| 			current = current->next; | 			current = current->next; | ||||||
| 			free(tmp); | 			free(tmp); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		struct qssb_syscall_policy *sc_policy = ctxt->syscall_policies; | ||||||
|  | 		while(sc_policy != NULL) | ||||||
|  | 		{ | ||||||
|  | 			struct qssb_syscall_policy *tmp = sc_policy; | ||||||
|  | 			sc_policy = sc_policy->next; | ||||||
|  | 			free(tmp); | ||||||
|  | 		} | ||||||
| 		free(ctxt); | 		free(ctxt); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -626,36 +687,72 @@ static int drop_caps() | |||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void append_syscalls_to_bpf(long *syscalls, size_t n, unsigned int action, struct sock_filter *filter, unsigned short int *start_index) | ||||||
|  | { | ||||||
|  | 	if(action == QSSB_SYSCALL_ALLOW) | ||||||
|  | 	{ | ||||||
|  | 		action = SECCOMP_RET_ALLOW; | ||||||
|  | 	} | ||||||
|  | 	if(action == QSSB_SYSCALL_DENY_KILL_PROCESS) | ||||||
|  | 	{ | ||||||
|  | 		action = SECCOMP_RET_KILL_PROCESS; | ||||||
|  | 	} | ||||||
|  | 	if(action == QSSB_SYSCALL_DENY_RET_ERROR) | ||||||
|  | 	{ | ||||||
|  | 		action = SECCOMP_RET_ERRNO|EACCES; | ||||||
|  | 	} | ||||||
|  | 	for(size_t i = 0; i < n; i++) | ||||||
|  | 	{ | ||||||
|  | 		long syscall = syscalls[i]; | ||||||
|  | 		if(syscall != QSSB_SYSCALL_MATCH_ALL) | ||||||
|  | 		{ | ||||||
|  | 			struct sock_filter syscall_check = BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall, 0, 1); | ||||||
|  | 			filter[(*start_index)++] = syscall_check; | ||||||
|  | 		} | ||||||
|  | 		struct sock_filter syscall_action = BPF_STMT(BPF_RET+BPF_K, action); | ||||||
|  | 		/* TODO: we can do better than adding this below every jump */ | ||||||
|  | 		filter[(*start_index)++] = syscall_action; | ||||||
|  | 	} | ||||||
|  | } | ||||||
| /* | /* | ||||||
|  * Enables the per_syscall seccomp action for system calls |  * Enables the seccomp policy | ||||||
|  * |  * | ||||||
|  * syscalls: array of system calls numbers. |  * policy: qssb policy object | ||||||
|  * per_syscall: action to apply for each system call |  | ||||||
|  * 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, size_t n, unsigned int per_syscall, unsigned int default_action) |  | ||||||
|  | static int qssb_enable_syscall_policy(struct qssb_policy *policy) | ||||||
| { | { | ||||||
| 	struct sock_filter filter[1024] = | 	struct sock_filter filter[1024] = | ||||||
| 	{ | 	{ | ||||||
| 		LOAD_SYSCALL_NR, | 		BPF_STMT(BPF_LD+BPF_W+BPF_ABS,offsetof(struct seccomp_data, arch)), | ||||||
|  | 		BPF_JUMP (BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_AUDIT_ARCH, 1, 0), | ||||||
|  | 		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL_PROCESS), | ||||||
|  | 		BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)), | ||||||
|  | 		BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, __X32_SYSCALL_BIT, 0, 1), | ||||||
|  | 		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL_PROCESS), | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	unsigned short int current_filter_index = 1; | 	unsigned short int current_filter_index = 6; | ||||||
| 	for(size_t i = 0; i < n; i++) |  | ||||||
|  | 	struct qssb_syscall_policy *current_policy = policy->syscall_policies; | ||||||
|  | 	while(current_policy) | ||||||
| 	{ | 	{ | ||||||
| 		unsigned int sysc = (unsigned int) syscalls[i]; | 		if(!is_valid_syscall_policy(current_policy->policy)) | ||||||
| 		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); | 			QSSB_LOG_ERROR("invalid syscall policy specified"); | ||||||
| 		filter[current_filter_index++] = syscall; | 			return -1; | ||||||
| 		filter[current_filter_index++] = action; | 		} | ||||||
|  | 		long *syscalls = NULL; | ||||||
|  | 		size_t n = 0; | ||||||
|  | 		get_syscall_array(current_policy, &syscalls, &n); | ||||||
|  | 		append_syscalls_to_bpf(syscalls, n, current_policy->policy, filter, ¤t_filter_index); | ||||||
|  | 		current_policy = current_policy->next; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	struct sock_filter da = BPF_STMT(BPF_RET+BPF_K, default_action); |  | ||||||
| 	filter[current_filter_index] = da; |  | ||||||
|  |  | ||||||
| 	++current_filter_index; |  | ||||||
| 	struct sock_fprog prog = { | 	struct sock_fprog prog = { | ||||||
| 		.len = current_filter_index , | 		.len = current_filter_index , | ||||||
| 		.filter = filter, | 		.filter = filter, | ||||||
| @@ -670,26 +767,6 @@ static int seccomp_enable(int *syscalls, size_t n, unsigned int per_syscall, uns | |||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * Blacklists the specified systemcalls. |  | ||||||
|  * |  | ||||||
|  * syscalls: array of system calls numbers. |  | ||||||
|  */ |  | ||||||
| static int seccomp_enable_blacklist(int *syscalls, size_t n) |  | ||||||
| { |  | ||||||
| 	return seccomp_enable(syscalls, n, SECCOMP_RET_KILL, SECCOMP_RET_ALLOW); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * Whitelists the specified systemcalls. |  | ||||||
|  * |  | ||||||
|  * syscalls: array of system calls numbers. |  | ||||||
|  */ |  | ||||||
| static int seccomp_enable_whitelist(int *syscalls, size_t n) |  | ||||||
| { |  | ||||||
| 	return seccomp_enable(syscalls, n, SECCOMP_RET_ALLOW, SECCOMP_RET_KILL); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #if HAVE_LANDLOCK == 1 | #if HAVE_LANDLOCK == 1 | ||||||
| static unsigned int qssb_flags_to_landlock(unsigned int flags) | static unsigned int qssb_flags_to_landlock(unsigned int flags) | ||||||
| { | { | ||||||
| @@ -809,12 +886,17 @@ 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->denied_syscalls.used > 0 && policy->allowed_syscalls.used > 0) | 	if(policy->no_new_privs != 1) | ||||||
| 	{ | 	{ | ||||||
| 		QSSB_LOG_ERROR("Error: Cannot mix allowed and denied systemcalls in policy\n"); | 		if(policy->syscall_policies != NULL) | ||||||
| 		return -EINVAL; | 		{ | ||||||
|  | 			QSSB_LOG_ERROR("no_new_privs = 1 is required for seccomp filtering!\n"); | ||||||
|  | 			return -1; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	/* TODO: check if we have ALLOWED, but no default deny */ | ||||||
|  |  | ||||||
| 	if(policy->mount_path_policies_to_chroot == 1) | 	if(policy->mount_path_policies_to_chroot == 1) | ||||||
| 	{ | 	{ | ||||||
| 		if(policy->path_policies == NULL) | 		if(policy->path_policies == NULL) | ||||||
| @@ -829,14 +911,6 @@ static int check_policy_sanity(struct qssb_policy *policy) | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if(policy->no_new_privs != 1) |  | ||||||
| 	{ |  | ||||||
| 		if(policy->allowed_syscalls.used > 0 || policy->denied_syscalls.used > 0) |  | ||||||
| 		{ |  | ||||||
| 			QSSB_LOG_ERROR("no_new_privs = 1 is required for seccomp filtering!\n"); |  | ||||||
| 			return -1; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if(policy->path_policies != NULL) | 	if(policy->path_policies != NULL) | ||||||
| 	{ | 	{ | ||||||
| @@ -854,6 +928,45 @@ static int check_policy_sanity(struct qssb_policy *policy) | |||||||
| 			return -1; | 			return -1; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	struct qssb_syscall_policy *syscall_policy = policy->syscall_policies; | ||||||
|  | 	if(syscall_policy != NULL) | ||||||
|  | 	{ | ||||||
|  | 		/* A few sanitiy checks... but we cannot check overall whether it's reasonable */ | ||||||
|  | 		int i = 0; | ||||||
|  | 		int last_match_all = -1; | ||||||
|  | 		int match_all_policy = 0; | ||||||
|  | 		int last_policy; | ||||||
|  | 		while(syscall_policy) | ||||||
|  | 		{ | ||||||
|  | 			long *syscall; | ||||||
|  | 			size_t n = 0; | ||||||
|  | 			get_syscall_array(syscall_policy, &syscall, &n); | ||||||
|  | 			if(syscall[n-1] == QSSB_SYSCALL_MATCH_ALL) | ||||||
|  | 			{ | ||||||
|  | 				last_match_all = i; | ||||||
|  | 				match_all_policy = syscall_policy->policy; | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				last_policy = syscall_policy->policy; | ||||||
|  | 			} | ||||||
|  | 			syscall_policy = syscall_policy->next; | ||||||
|  | 			++i; | ||||||
|  | 		} | ||||||
|  | 		if(last_match_all == -1 || i - last_match_all != 1) | ||||||
|  | 		{ | ||||||
|  | 			QSSB_LOG_ERROR("The last entry in the syscall policy list must match all syscalls (default rule)\n"); | ||||||
|  | 			return -1; | ||||||
|  | 		} | ||||||
|  | 		/* Most likely a mistake and not intended */ | ||||||
|  | 		if(last_policy == match_all_policy) | ||||||
|  | 		{ | ||||||
|  | 			QSSB_LOG_ERROR("Last policy for a syscall matches default policy\n"); | ||||||
|  | 			return -1; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -897,20 +1010,39 @@ static int enable_no_fs(struct qssb_policy *policy) | |||||||
| 			return -1; | 			return -1; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if(policy->allowed_syscalls.used == 0) | 		//TODO: we don't have to do this if there whitelisted policies, in that case we will be behind the default deny anyway | ||||||
|  | 		size_t fs_access_syscalls_count = sizeof(fs_access_syscalls)/sizeof(fs_access_syscalls[0]); | ||||||
|  | 		int ret = qssb_append_syscalls_policy(policy, QSSB_SYSCALL_DENY_RET_ERROR, fs_access_syscalls, fs_access_syscalls_count); | ||||||
|  | 		if(ret != 0) | ||||||
| 		{ | 		{ | ||||||
| 			size_t fs_access_syscalls_count = sizeof(fs_access_syscalls)/sizeof(fs_access_syscalls[0]); | 			QSSB_LOG_ERROR("Failed to add system calls to policy\n"); | ||||||
|  | 			return -1; | ||||||
| 			int ret = qssb_append_denied_syscalls(policy, fs_access_syscalls, fs_access_syscalls_count); | 		} | ||||||
| 			if(ret != 0) | 		if(qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW) != 0) | ||||||
| 			{ | 		{ | ||||||
| 				QSSB_LOG_ERROR("Failed to add system calls to blacklist\n"); | 			QSSB_LOG_ERROR("Failed to add default policy when adding denied filesystem-related system calls\n"); | ||||||
| 				return -1; | 			return -1; | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 		return 0; | 		return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int qssb_append_predefined_standard_syscall_policy(struct qssb_policy *policy) | ||||||
|  | { | ||||||
|  | 	size_t blacklisted_syscalls_count = sizeof(default_blacklisted_syscalls)/sizeof(default_blacklisted_syscalls[0]); | ||||||
|  |  | ||||||
|  | 	int appendresult = qssb_append_syscalls_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, default_blacklisted_syscalls, blacklisted_syscalls_count); | ||||||
|  | 	if(appendresult != 0) | ||||||
|  | 	{ | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 	appendresult = qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW); | ||||||
|  | 	if(appendresult != 0) | ||||||
|  | 	{ | ||||||
|  | 		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 | ||||||
| @@ -1058,26 +1190,18 @@ int qssb_enable_policy(struct qssb_policy *policy) | |||||||
| 	close(landlock_ruleset_fd); | 	close(landlock_ruleset_fd); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| 	if(policy->allowed_syscalls.used > 0) | 	if(policy->syscall_policies == NULL && policy->disable_syscall_filter == 0) | ||||||
| 	{ | 	{ | ||||||
| 		int *syscalls = (int *)policy->allowed_syscalls.data; | 			if(qssb_append_predefined_standard_syscall_policy(policy) != 0) | ||||||
| 		size_t n = policy->allowed_syscalls.used / sizeof(int); | 			{ | ||||||
| 		if(seccomp_enable_whitelist(syscalls, n) < 0) | 				QSSB_LOG_ERROR("Failed to add standard predefined syscall policy\n"); | ||||||
| 		{ | 				return -1; | ||||||
| 			QSSB_LOG_ERROR("seccomp_enable_whitelist failed\n"); | 			} | ||||||
| 			return -1; |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if(policy->denied_syscalls.used > 0) | 	if(policy->syscall_policies != NULL) | ||||||
| 	{ | 	{ | ||||||
| 		int *syscalls = (int *)policy->denied_syscalls.data; | 		return qssb_enable_syscall_policy(policy); | ||||||
| 		size_t n = policy->denied_syscalls.used / sizeof(int); |  | ||||||
| 		if(seccomp_enable_blacklist(syscalls, n) < 0) |  | ||||||
| 		{ |  | ||||||
| 			QSSB_LOG_ERROR("seccomp_enable_blacklist failed\n"); |  | ||||||
| 			return -1; |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return 0; | 	return 0; | ||||||
|   | |||||||
							
								
								
									
										217
									
								
								test.c
									
									
									
									
									
								
							
							
						
						
									
										217
									
								
								test.c
									
									
									
									
									
								
							| @@ -4,47 +4,113 @@ | |||||||
| #include <dirent.h> | #include <dirent.h> | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
| #include <sys/socket.h> | #include <sys/socket.h> | ||||||
|  | #include <sys/wait.h> | ||||||
|  |  | ||||||
| int test_default_main(int argc, char *argv[]) | int xqssb_enable_policy(struct qssb_policy *policy) | ||||||
|  | { | ||||||
|  | 	int ret = qssb_enable_policy(policy); | ||||||
|  | 	if(ret != 0) | ||||||
|  | 	{ | ||||||
|  | 		fprintf(stderr, "qssb_enable_policy() failed: %i\n", ret); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int test_default_main() | ||||||
| { | { | ||||||
| 	struct qssb_policy *policy = qssb_init_policy(); | 	struct qssb_policy *policy = qssb_init_policy(); | ||||||
| 	int ret = qssb_enable_policy(policy); | 	int ret = qssb_enable_policy(policy); | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| int test_both_syscalls(int argc, char *argv[]) | static int test_expected_kill(int (*f)()) | ||||||
| { | { | ||||||
| 	struct qssb_policy *policy = qssb_init_policy(); | 	pid_t pid = fork(); | ||||||
| 	int syscalls[] = {1,2,3}; | 	if(pid == 0) | ||||||
|  |  | ||||||
| 	qssb_append_denied_syscalls(policy, syscalls, 3); |  | ||||||
| 	qssb_append_allowed_syscalls(policy, syscalls, 3); |  | ||||||
|  |  | ||||||
| 	int ret = qssb_enable_policy(policy); |  | ||||||
| 	if(ret != 0) |  | ||||||
| 	{ | 	{ | ||||||
| 		return 0; | 		return f(); | ||||||
| 	} | 	} | ||||||
| 	return 1; | 	int status = 0; | ||||||
| } | 	waitpid(pid, &status, 0); | ||||||
|  |  | ||||||
| int test_seccomp_blacklisted(int argc, char *argv[]) | 	if(WIFSIGNALED(status)) | ||||||
| { | 	{ | ||||||
| 	struct qssb_policy *policy = qssb_init_policy(); | 		int c = WTERMSIG(status); | ||||||
|  | 		if(c == SIGSYS) | ||||||
| 	qssb_append_denied_syscall(policy, QSSB_SYS(getuid)); | 		{ | ||||||
|  | 			printf("Got expected signal\n"); | ||||||
| 	int ret = qssb_enable_policy(policy); | 			return 0; | ||||||
| 	uid_t pid = geteuid(); | 		} | ||||||
| 	pid = getuid(); | 		printf("Unexpected status code: %i\n", c); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		int c = WEXITSTATUS(status); | ||||||
|  | 		printf("Process was not killed, test fails. Status code of exit: %i\n", c); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int test_seccomp_blacklisted_call_permitted(int argc, char *argv[]) |  | ||||||
|  | static int test_successful_exit(int (*f)()) | ||||||
|  | { | ||||||
|  | 	pid_t pid = fork(); | ||||||
|  | 	if(pid == 0) | ||||||
|  | 	{ | ||||||
|  | 		return f(); | ||||||
|  | 	} | ||||||
|  | 	int status = 0; | ||||||
|  | 	waitpid(pid, &status, 0); | ||||||
|  |  | ||||||
|  | 	if(WIFSIGNALED(status)) | ||||||
|  | 	{ | ||||||
|  | 		int c = WTERMSIG(status); | ||||||
|  | 		printf("Received signal, which was not expected. Signal was: %i\n", c); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		int c = WEXITSTATUS(status); | ||||||
|  | 		if(c != 0) | ||||||
|  | 		{ | ||||||
|  | 			printf("Process failed to exit properly. Status code is: %i\n", c); | ||||||
|  | 		} | ||||||
|  | 		return c; | ||||||
|  | 	} | ||||||
|  | 	printf("Process exited sucessfully as expected"); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int do_test_seccomp_blacklisted() | ||||||
|  | { | ||||||
|  | 	struct qssb_policy *policy = qssb_init_policy(); | ||||||
|  | 	qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(getuid)); | ||||||
|  | 	qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW); | ||||||
|  |  | ||||||
|  | 	xqssb_enable_policy(policy); | ||||||
|  |  | ||||||
|  | 	uid_t pid = geteuid(); | ||||||
|  | 	pid = getuid(); | ||||||
|  | 	return 0; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
|  | int test_seccomp_blacklisted() | ||||||
|  | { | ||||||
|  | 	return test_expected_kill(&do_test_seccomp_blacklisted); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int do_test_seccomp_blacklisted_call_permitted() | ||||||
| { | { | ||||||
| 	struct qssb_policy *policy = qssb_init_policy(); | 	struct qssb_policy *policy = qssb_init_policy(); | ||||||
|  |  | ||||||
| 	qssb_append_denied_syscall(policy, QSSB_SYS(getuid)); | 	qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(getuid)); | ||||||
|  | 	qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW); | ||||||
|  |  | ||||||
| 	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 | ||||||
| @@ -52,7 +118,71 @@ int test_seccomp_blacklisted_call_permitted(int argc, char *argv[]) | |||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int test_landlock(int argc, char *argv[]) |  | ||||||
|  | int test_seccomp_blacklisted_call_permitted() | ||||||
|  | { | ||||||
|  | 	return test_successful_exit(&do_test_seccomp_blacklisted_call_permitted); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int do_test_seccomp_x32_kill() | ||||||
|  | { | ||||||
|  | 	struct qssb_policy *policy = qssb_init_policy(); | ||||||
|  |  | ||||||
|  | 	qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(getuid)); | ||||||
|  | 	qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW); | ||||||
|  |  | ||||||
|  | 	xqssb_enable_policy(policy); | ||||||
|  |  | ||||||
|  | 	/* Attempt to bypass by falling back to x32 should be blocked */ | ||||||
|  | 	syscall(QSSB_SYS(getuid)+__X32_SYSCALL_BIT); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int test_seccomp_x32_kill() | ||||||
|  | { | ||||||
|  | 	return test_expected_kill(&do_test_seccomp_x32_kill); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Tests whether seccomp rules end with a policy matching all syscalls */ | ||||||
|  | int test_seccomp_require_last_matchall() | ||||||
|  | { | ||||||
|  | 	struct qssb_policy *policy = qssb_init_policy(); | ||||||
|  |  | ||||||
|  | 	qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(getuid)); | ||||||
|  |  | ||||||
|  | 	int status = qssb_enable_policy(policy); | ||||||
|  | 	if(status == 0) | ||||||
|  | 	{ | ||||||
|  | 		printf("Failed. Should not have been enabled!"); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int do_test_seccomp_errno() | ||||||
|  | { | ||||||
|  | 	struct qssb_policy *policy = qssb_init_policy(); | ||||||
|  |  | ||||||
|  | 	qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_RET_ERROR, QSSB_SYS(close)); | ||||||
|  | 	qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW); | ||||||
|  |  | ||||||
|  | 	xqssb_enable_policy(policy); | ||||||
|  | 	uid_t id = getuid(); | ||||||
|  |  | ||||||
|  | 	int fd = close(0); | ||||||
|  | 	printf("close() return code: %i, errno: %s\n", fd, strerror(errno)); | ||||||
|  | 	return fd == -1 ? 0 : 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int test_seccomp_errno() | ||||||
|  | { | ||||||
|  | 	return test_successful_exit(&do_test_seccomp_errno); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int test_landlock() | ||||||
| { | { | ||||||
| 	struct qssb_policy *policy = qssb_init_policy(); | 	struct qssb_policy *policy = qssb_init_policy(); | ||||||
| 	qssb_append_path_policy(policy, QSSB_FS_ALLOW_READ, "/proc/self/fd"); | 	qssb_append_path_policy(policy, QSSB_FS_ALLOW_READ, "/proc/self/fd"); | ||||||
| @@ -65,7 +195,7 @@ int test_landlock(int argc, char *argv[]) | |||||||
| 	return 1; | 	return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| int test_landlock_deny_write(int argc, char *argv[]) | int test_landlock_deny_write() | ||||||
| { | { | ||||||
| 	struct qssb_policy *policy = qssb_init_policy(); | 	struct qssb_policy *policy = qssb_init_policy(); | ||||||
| 	qssb_append_path_policy(policy, QSSB_FS_ALLOW_READ, "/tmp/"); | 	qssb_append_path_policy(policy, QSSB_FS_ALLOW_READ, "/tmp/"); | ||||||
| @@ -78,7 +208,7 @@ int test_landlock_deny_write(int argc, char *argv[]) | |||||||
| 	return 1; | 	return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| int test_nofs(int argc, char *argv[]) | int test_nofs() | ||||||
| { | { | ||||||
| 	struct qssb_policy *policy = qssb_init_policy(); | 	struct qssb_policy *policy = qssb_init_policy(); | ||||||
| 	policy->no_fs = 1; | 	policy->no_fs = 1; | ||||||
| @@ -94,21 +224,21 @@ int test_nofs(int argc, char *argv[]) | |||||||
| 	if(s == -1) | 	if(s == -1) | ||||||
| 	{ | 	{ | ||||||
| 		fprintf(stderr, "Failed to open socket but this was not requested by policy\n"); | 		fprintf(stderr, "Failed to open socket but this was not requested by policy\n"); | ||||||
| 		return 0; | 		return 1; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/* Expect seccomp to take care of this */ | 	/* Expect seccomp to take care of this */ | ||||||
| 	if(open("/test", O_CREAT | O_WRONLY) >= 0) | 	if(open("/test", O_CREAT | O_WRONLY) >= 0) | ||||||
| 	{ | 	{ | ||||||
| 		fprintf(stderr, "Failed: Do not expect write access\n"); | 		fprintf(stderr, "Failed: We do not expect write access\n"); | ||||||
| 		return -1; | 		return 1; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| int test_no_new_fds(int argc, char *argv[]) | int test_no_new_fds() | ||||||
| { | { | ||||||
| 	struct qssb_policy *policy = qssb_init_policy(); | 	struct qssb_policy *policy = qssb_init_policy(); | ||||||
| 	policy->no_new_fds = 1; | 	policy->no_new_fds = 1; | ||||||
| @@ -140,23 +270,24 @@ int test_no_new_fds(int argc, char *argv[]) | |||||||
| struct dispatcher | struct dispatcher | ||||||
| { | { | ||||||
| 	char *name; | 	char *name; | ||||||
| 	int (*f)(int, char **); | 	int (*f)(); | ||||||
| 	bool must_exit_zero; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct dispatcher dispatchers[] = { | struct dispatcher dispatchers[] = { | ||||||
| 	{ "default", &test_default_main, true }, | 	{ "default", &test_default_main }, | ||||||
| 	{ "seccomp-blacklisted", &test_seccomp_blacklisted, false }, | 	{ "seccomp-blacklisted", &test_seccomp_blacklisted}, | ||||||
| 	{ "seccomp-blacklisted-permitted", &test_seccomp_blacklisted_call_permitted, true }, | 	{ "seccomp-blacklisted-permitted", &test_seccomp_blacklisted_call_permitted}, | ||||||
| 	{ "landlock", &test_landlock, true }, | 	{ "seccomp-x32-kill", &test_seccomp_x32_kill}, | ||||||
| 	{ "landlock-deny-write", &test_landlock_deny_write, true }, | 	{ "seccomp-require-last-matchall", &test_seccomp_require_last_matchall}, | ||||||
| 	{ "no_fs", &test_nofs, false}, | 	{ "seccomp-errno", &test_seccomp_errno}, | ||||||
| 	{ "no_new_fds", &test_no_new_fds, true} | 	{ "landlock", &test_landlock}, | ||||||
|  | 	{ "landlock-deny-write", &test_landlock_deny_write }, | ||||||
|  | 	{ "no_fs", &test_nofs}, | ||||||
|  | 	{ "no_new_fds", &test_no_new_fds} | ||||||
| }; | }; | ||||||
|  |  | ||||||
| int main(int argc, char *argv[]) | int main(int argc, char *argv[]) | ||||||
| { | { | ||||||
|  |  | ||||||
| 	if(argc < 2) | 	if(argc < 2) | ||||||
| 	{ | 	{ | ||||||
| 		fprintf(stderr, "Usage: %s [testname]\n", argv[0]); | 		fprintf(stderr, "Usage: %s [testname]\n", argv[0]); | ||||||
| @@ -167,7 +298,7 @@ int main(int argc, char *argv[]) | |||||||
| 	{ | 	{ | ||||||
| 		for(unsigned int i = 0; i < sizeof(dispatchers)/sizeof(dispatchers[0]); i++) | 		for(unsigned int i = 0; i < sizeof(dispatchers)/sizeof(dispatchers[0]); i++) | ||||||
| 		{ | 		{ | ||||||
| 			printf("%s:%i\n", dispatchers[i].name, dispatchers[i].must_exit_zero ? 1 : 0); | 			printf("%s\n", dispatchers[i].name); | ||||||
| 		} | 		} | ||||||
| 		return EXIT_SUCCESS; | 		return EXIT_SUCCESS; | ||||||
| 	} | 	} | ||||||
| @@ -177,7 +308,7 @@ int main(int argc, char *argv[]) | |||||||
| 		struct dispatcher *current = &dispatchers[i]; | 		struct dispatcher *current = &dispatchers[i]; | ||||||
| 		if(strcmp(current->name, test) == 0) | 		if(strcmp(current->name, test) == 0) | ||||||
| 		{ | 		{ | ||||||
| 			return current->f(argc, argv); | 			return current->f(); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	fprintf(stderr, "Unknown test\n"); | 	fprintf(stderr, "Unknown test\n"); | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								test.sh
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								test.sh
									
									
									
									
									
								
							| @@ -32,34 +32,23 @@ function runtest_success() | |||||||
| function runtest() | function runtest() | ||||||
| { | { | ||||||
| 	testname="$1" | 	testname="$1" | ||||||
| 	must_exit_zero="$2" | 	test_log_file="$2" | ||||||
| 	test_log_file="$3" |  | ||||||
|  |  | ||||||
| 	echo "Running: $testname. Date: $(date)" > "${test_log_file}" | 	echo "Running: $testname. Date: $(date)" > "${test_log_file}" | ||||||
|  |  | ||||||
| 	echo -n "Running $1... " | 	echo -n "Running $1... " | ||||||
| 	#exit 1 to suppress shell message like "./test.sh: line 18: pid Bad system call" | 	#exit $? to suppress shell message like "./test.sh: line 18: pid Bad system call" | ||||||
| 	(./test $1 || exit 1) &>> "${test_log_file}" | 	(./test $1 || exit $?) &>> "${test_log_file}" | ||||||
| 	ret=$? | 	ret=$? | ||||||
| 	SUCCESS=0 | 	SUCCESS="no" | ||||||
| 	if [ $must_exit_zero -eq 1 ] ; then | 	if [ $ret -eq 0 ] ; then | ||||||
| 		if [ $ret -eq 0 ] ; then | 		runtest_success | ||||||
| 			runtest_success | 		SUCCESS="yes" | ||||||
| 			SUCCESS=1 |  | ||||||
| 		else |  | ||||||
| 			runtest_fail |  | ||||||
| 		fi |  | ||||||
| 	else | 	else | ||||||
| 		if [ $ret -eq 0 ] ; then | 		runtest_fail | ||||||
| 			runtest_fail |  | ||||||
| 		else |  | ||||||
| 			runtest_success |  | ||||||
| 			SUCCESS=1 |  | ||||||
| 		fi |  | ||||||
| 	fi | 	fi | ||||||
|  |  | ||||||
| 	echo "Finished: ${testname}. Date: $(date). Success: $SUCCESS" >> "${test_log_file}" | 	echo "Finished: ${testname}. Date: $(date). Success: $SUCCESS" >> "${test_log_file}" | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| GIT_ID=$( git log --pretty="format:%h" -n1 ) | GIT_ID=$( git log --pretty="format:%h" -n1 ) | ||||||
| @@ -73,9 +62,8 @@ LOG_OUTPUT_DIR_PATH="${LOG_OUTPUT_DIR}/qssb_test_${GIT_ID}_${TIMESTAMP}" | |||||||
| [ -d "$LOG_OUTPUT_DIR_PATH" ] || mkdir -p "$LOG_OUTPUT_DIR_PATH" | [ -d "$LOG_OUTPUT_DIR_PATH" ] || mkdir -p "$LOG_OUTPUT_DIR_PATH" | ||||||
|  |  | ||||||
| for test in $( ./test --dumptests ) ; do | for test in $( ./test --dumptests ) ; do | ||||||
| 	testname=$( echo $test | cut -d":" -f1 ) | 	testname=$( echo $test ) | ||||||
| 	must_exit_zero=$( echo "$test" | cut -d":" -f2 ) | 	runtest "$testname" "${LOG_OUTPUT_DIR_PATH}/log.${testname}" | ||||||
| 	runtest "$testname" $must_exit_zero "${LOG_OUTPUT_DIR_PATH}/log.${testname}" |  | ||||||
| done | done | ||||||
| echo | echo | ||||||
| echo "Tests finished. Logs in $(realpath ${LOG_OUTPUT_DIR_PATH})" | echo "Tests finished. Logs in $(realpath ${LOG_OUTPUT_DIR_PATH})" | ||||||
|   | |||||||
		Referencia en una nueva incidencia
	
	Block a user