mirror of
				https://github.com/swaywm/sway.git
				synced 2025-11-03 09:01:43 -05:00 
			
		
		
		
	Read configs from /etc/sway/security.d/*
This commit is contained in:
		
							parent
							
								
									eabfb6c559
								
							
						
					
					
						commit
						126ce571da
					
				
					 9 changed files with 77 additions and 42 deletions
				
			
		| 
						 | 
					@ -340,6 +340,8 @@ void free_config(struct sway_config *config);
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
char *do_var_replacement(char *str);
 | 
					char *do_var_replacement(char *str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct cmd_results *check_security_config();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int input_identifier_cmp(const void *item, const void *data);
 | 
					int input_identifier_cmp(const void *item, const void *data);
 | 
				
			||||||
void merge_input_config(struct input_config *dst, struct input_config *src);
 | 
					void merge_input_config(struct input_config *dst, struct input_config *src);
 | 
				
			||||||
void apply_input_config(struct input_config *ic, struct libinput_device *dev);
 | 
					void apply_input_config(struct input_config *ic, struct libinput_device *dev);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,6 +29,11 @@ ipc __PREFIX__/bin/swaybar {
 | 
				
			||||||
    outputs enabled
 | 
					    outputs enabled
 | 
				
			||||||
    workspaces enabled
 | 
					    workspaces enabled
 | 
				
			||||||
    command enabled
 | 
					    command enabled
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    events {
 | 
				
			||||||
 | 
					        workspace enabled
 | 
				
			||||||
 | 
					        mode enabled
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ipc __PREFIX__/bin/swaygrab {
 | 
					ipc __PREFIX__/bin/swaygrab {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,9 @@ struct cmd_results *cmd_commands(int argc, char **argv) {
 | 
				
			||||||
	if ((error = checkarg(argc, "commands", EXPECTED_EQUAL_TO, 1))) {
 | 
						if ((error = checkarg(argc, "commands", EXPECTED_EQUAL_TO, 1))) {
 | 
				
			||||||
		return error;
 | 
							return error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if ((error = check_security_config())) {
 | 
				
			||||||
 | 
							return error;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (strcmp(argv[0], "{") != 0) {
 | 
						if (strcmp(argv[0], "{") != 0) {
 | 
				
			||||||
		return cmd_results_new(CMD_FAILURE, "commands", "Expected block declaration");
 | 
							return cmd_results_new(CMD_FAILURE, "commands", "Expected block declaration");
 | 
				
			||||||
| 
						 | 
					@ -19,10 +22,5 @@ struct cmd_results *cmd_commands(int argc, char **argv) {
 | 
				
			||||||
		return cmd_results_new(CMD_FAILURE, "commands", "Can only be used in config file.");
 | 
							return cmd_results_new(CMD_FAILURE, "commands", "Can only be used in config file.");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!current_config_path || strcmp(SYSCONFDIR "/sway/security", current_config_path) != 0) {
 | 
					 | 
				
			||||||
		return cmd_results_new(CMD_INVALID, "permit",
 | 
					 | 
				
			||||||
				"This command is only permitted to run from " SYSCONFDIR "/sway/security");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return cmd_results_new(CMD_BLOCK_COMMANDS, NULL, NULL);
 | 
						return cmd_results_new(CMD_BLOCK_COMMANDS, NULL, NULL);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,9 @@ struct cmd_results *cmd_ipc(int argc, char **argv) {
 | 
				
			||||||
	if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 2))) {
 | 
						if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 2))) {
 | 
				
			||||||
		return error;
 | 
							return error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if ((error = check_security_config())) {
 | 
				
			||||||
 | 
							return error;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const char *program = argv[0];
 | 
						const char *program = argv[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,11 +29,6 @@ struct cmd_results *cmd_ipc(int argc, char **argv) {
 | 
				
			||||||
		return cmd_results_new(CMD_FAILURE, "ipc", "Can only be used in config file.");
 | 
							return cmd_results_new(CMD_FAILURE, "ipc", "Can only be used in config file.");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!current_config_path || strcmp(SYSCONFDIR "/sway/security", current_config_path) != 0) {
 | 
					 | 
				
			||||||
		return cmd_results_new(CMD_INVALID, "permit",
 | 
					 | 
				
			||||||
				"This command is only permitted to run from " SYSCONFDIR "/sway/security");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	current_policy = alloc_ipc_policy(program);
 | 
						current_policy = alloc_ipc_policy(program);
 | 
				
			||||||
	list_add(config->ipc_policies, current_policy);
 | 
						list_add(config->ipc_policies, current_policy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -62,19 +62,13 @@ struct cmd_results *cmd_permit(int argc, char **argv) {
 | 
				
			||||||
	if ((error = checkarg(argc, "permit", EXPECTED_MORE_THAN, 1))) {
 | 
						if ((error = checkarg(argc, "permit", EXPECTED_MORE_THAN, 1))) {
 | 
				
			||||||
		return error;
 | 
							return error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if ((error = check_security_config())) {
 | 
				
			||||||
	if (!current_config_path || strcmp(SYSCONFDIR "/sway/security", current_config_path) != 0) {
 | 
							return error;
 | 
				
			||||||
		return cmd_results_new(CMD_INVALID, "permit",
 | 
					 | 
				
			||||||
				"This command is only permitted to run from " SYSCONFDIR "/sway/security");
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct feature_policy *policy = get_policy(argv[0]);
 | 
						struct feature_policy *policy = get_policy(argv[0]);
 | 
				
			||||||
	policy->features |= get_features(argc, argv, &error);
 | 
						policy->features |= get_features(argc, argv, &error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (error) {
 | 
					 | 
				
			||||||
		return error;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sway_log(L_DEBUG, "Permissions granted to %s for features %d",
 | 
						sway_log(L_DEBUG, "Permissions granted to %s for features %d",
 | 
				
			||||||
			policy->program, policy->features);
 | 
								policy->program, policy->features);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -86,19 +80,13 @@ struct cmd_results *cmd_reject(int argc, char **argv) {
 | 
				
			||||||
	if ((error = checkarg(argc, "reject", EXPECTED_MORE_THAN, 1))) {
 | 
						if ((error = checkarg(argc, "reject", EXPECTED_MORE_THAN, 1))) {
 | 
				
			||||||
		return error;
 | 
							return error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if ((error = check_security_config())) {
 | 
				
			||||||
	if (!current_config_path || strcmp(SYSCONFDIR "/sway/security", current_config_path) != 0) {
 | 
							return error;
 | 
				
			||||||
		return cmd_results_new(CMD_INVALID, "permit",
 | 
					 | 
				
			||||||
				"This command is only permitted to run from " SYSCONFDIR "/sway/security");
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct feature_policy *policy = get_policy(argv[0]);
 | 
						struct feature_policy *policy = get_policy(argv[0]);
 | 
				
			||||||
	policy->features &= ~get_features(argc, argv, &error);
 | 
						policy->features &= ~get_features(argc, argv, &error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (error) {
 | 
					 | 
				
			||||||
		return error;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sway_log(L_DEBUG, "Permissions granted to %s for features %d",
 | 
						sway_log(L_DEBUG, "Permissions granted to %s for features %d",
 | 
				
			||||||
			policy->program, policy->features);
 | 
								policy->program, policy->features);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@
 | 
				
			||||||
#include <libinput.h>
 | 
					#include <libinput.h>
 | 
				
			||||||
#include <limits.h>
 | 
					#include <limits.h>
 | 
				
			||||||
#include <float.h>
 | 
					#include <float.h>
 | 
				
			||||||
 | 
					#include <dirent.h>
 | 
				
			||||||
#include "wayland-desktop-shell-server-protocol.h"
 | 
					#include "wayland-desktop-shell-server-protocol.h"
 | 
				
			||||||
#include "sway/commands.h"
 | 
					#include "sway/commands.h"
 | 
				
			||||||
#include "sway/config.h"
 | 
					#include "sway/config.h"
 | 
				
			||||||
| 
						 | 
					@ -485,6 +486,10 @@ static bool load_config(const char *path, struct sway_config *config) {
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int qstrcmp(const void* a, const void* b) {
 | 
				
			||||||
 | 
					    return strcmp(*((char**) a), *((char**) b));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool load_main_config(const char *file, bool is_active) {
 | 
					bool load_main_config(const char *file, bool is_active) {
 | 
				
			||||||
	input_init();
 | 
						input_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -512,7 +517,43 @@ bool load_main_config(const char *file, bool is_active) {
 | 
				
			||||||
	list_add(config->config_chain, path);
 | 
						list_add(config->config_chain, path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	config->reading = true;
 | 
						config->reading = true;
 | 
				
			||||||
	bool success = load_config(SYSCONFDIR "/sway/security", config);
 | 
						
 | 
				
			||||||
 | 
						// Read security configs
 | 
				
			||||||
 | 
						bool success = true;
 | 
				
			||||||
 | 
						DIR *dir = opendir(SYSCONFDIR "/sway/security.d");
 | 
				
			||||||
 | 
						if (!dir) {
 | 
				
			||||||
 | 
							sway_log(L_ERROR, "%s does not exist, sway will have no security configuration"
 | 
				
			||||||
 | 
									" and will probably be broken", SYSCONFDIR "/sway/security.d");
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							list_t *secconfigs = create_list();
 | 
				
			||||||
 | 
							char *base = SYSCONFDIR "/sway/security.d/";
 | 
				
			||||||
 | 
							struct dirent *ent = readdir(dir);
 | 
				
			||||||
 | 
							while (ent != NULL) {
 | 
				
			||||||
 | 
								if (ent->d_type == DT_REG) {
 | 
				
			||||||
 | 
									char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1);
 | 
				
			||||||
 | 
									strcpy(_path, base);
 | 
				
			||||||
 | 
									strcat(_path, ent->d_name);
 | 
				
			||||||
 | 
									list_add(secconfigs, _path);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								ent = readdir(dir);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							closedir(dir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							list_qsort(secconfigs, qstrcmp);
 | 
				
			||||||
 | 
							for (int i = 0; i < secconfigs->length; ++i) {
 | 
				
			||||||
 | 
								char *_path = secconfigs->items[i];
 | 
				
			||||||
 | 
								struct stat s;
 | 
				
			||||||
 | 
								if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || (s.st_mode & 0777) != 0644) {
 | 
				
			||||||
 | 
									sway_log(L_ERROR, "Refusing to load %s - it must be owned by root and mode 644", _path);
 | 
				
			||||||
 | 
									success = false;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									success = success && load_config(_path, config);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							free_flat_list(secconfigs);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	success = success && load_config(path, config);
 | 
						success = success && load_config(path, config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (is_active) {
 | 
						if (is_active) {
 | 
				
			||||||
| 
						 | 
					@ -620,6 +661,15 @@ bool load_include_configs(const char *path, struct sway_config *config) {
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct cmd_results *check_security_config() {
 | 
				
			||||||
 | 
						if (!current_config_path || strncmp(SYSCONFDIR "/sway/security.d/", current_config_path,
 | 
				
			||||||
 | 
									strlen(SYSCONFDIR "/sway/security.d/")) != 0) {
 | 
				
			||||||
 | 
							return cmd_results_new(CMD_INVALID, "permit",
 | 
				
			||||||
 | 
									"This command is only permitted to run from " SYSCONFDIR "/sway/security.d/*");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool read_config(FILE *file, struct sway_config *config) {
 | 
					bool read_config(FILE *file, struct sway_config *config) {
 | 
				
			||||||
	bool success = true;
 | 
						bool success = true;
 | 
				
			||||||
	enum cmd_status block = CMD_BLOCK_END;
 | 
						enum cmd_status block = CMD_BLOCK_END;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -241,8 +241,7 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ipc_client_disconnect(struct ipc_client *client)
 | 
					void ipc_client_disconnect(struct ipc_client *client) {
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!sway_assert(client != NULL, "client != NULL")) {
 | 
						if (!sway_assert(client != NULL, "client != NULL")) {
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -326,8 +325,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
 | 
				
			||||||
		ipc_client_disconnect(client);
 | 
							ipc_client_disconnect(client);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (client->payload_length > 0)
 | 
						if (client->payload_length > 0) {
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		ssize_t received = recv(client->fd, buf, client->payload_length, 0);
 | 
							ssize_t received = recv(client->fd, buf, client->payload_length, 0);
 | 
				
			||||||
		if (received == -1)
 | 
							if (received == -1)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
| 
						 | 
					@ -397,7 +395,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case IPC_GET_WORKSPACES:
 | 
						case IPC_GET_WORKSPACES:
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (!(client->security_policy & IPC_FEATURE_GET_TREE)) {
 | 
							if (!(client->security_policy & IPC_FEATURE_GET_WORKSPACES)) {
 | 
				
			||||||
			goto exit_denied;
 | 
								goto exit_denied;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		json_object *workspaces = json_object_new_array();
 | 
							json_object *workspaces = json_object_new_array();
 | 
				
			||||||
| 
						 | 
					@ -410,7 +408,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case IPC_GET_INPUTS:
 | 
						case IPC_GET_INPUTS:
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (!(client->security_policy & IPC_FEATURE_GET_TREE)) {
 | 
							if (!(client->security_policy & IPC_FEATURE_GET_INPUTS)) {
 | 
				
			||||||
			goto exit_denied;
 | 
								goto exit_denied;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		json_object *inputs = json_object_new_array();
 | 
							json_object *inputs = json_object_new_array();
 | 
				
			||||||
| 
						 | 
					@ -436,7 +434,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case IPC_GET_OUTPUTS:
 | 
						case IPC_GET_OUTPUTS:
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (!(client->security_policy & IPC_FEATURE_GET_TREE)) {
 | 
							if (!(client->security_policy & IPC_FEATURE_GET_OUTPUTS)) {
 | 
				
			||||||
			goto exit_denied;
 | 
								goto exit_denied;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		json_object *outputs = json_object_new_array();
 | 
							json_object *outputs = json_object_new_array();
 | 
				
			||||||
| 
						 | 
					@ -561,6 +559,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exit_denied:
 | 
					exit_denied:
 | 
				
			||||||
	ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied));
 | 
						ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied));
 | 
				
			||||||
 | 
						sway_log(L_DEBUG, "Denied IPC client access to %i", client->current_command);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exit_cleanup:
 | 
					exit_cleanup:
 | 
				
			||||||
	client->payload_length = 0;
 | 
						client->payload_length = 0;
 | 
				
			||||||
| 
						 | 
					@ -588,6 +587,8 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sway_log(L_DEBUG, "Send IPC reply: %s", payload);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -175,13 +175,6 @@ static void security_sanity_check() {
 | 
				
			||||||
		cap_free(cap);
 | 
							cap_free(cap);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	if (!stat(SYSCONFDIR "/sway", &s)) {
 | 
					 | 
				
			||||||
		if (s.st_uid != 0 || s.st_gid != 0
 | 
					 | 
				
			||||||
				|| (s.st_mode & S_IWGRP) || (s.st_mode & S_IWOTH)) {
 | 
					 | 
				
			||||||
			sway_log(L_ERROR,
 | 
					 | 
				
			||||||
				"!! DANGER !! " SYSCONFDIR "/sway is not secure! It should be owned by root and set to 0755 at the minimum");
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int main(int argc, char **argv) {
 | 
					int main(int argc, char **argv) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,7 +21,7 @@ you must make a few changes external to sway first.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Configuration of security features is limited to files in the security directory
 | 
					Configuration of security features is limited to files in the security directory
 | 
				
			||||||
(this is likely /etc/sway/security.d/*, but depends on your installation prefix).
 | 
					(this is likely /etc/sway/security.d/*, but depends on your installation prefix).
 | 
				
			||||||
Files in this directory must be owned by root:root and chmod 600. The default
 | 
					Files in this directory must be owned by root:root and chmod 644. The default
 | 
				
			||||||
security configuration is installed to /etc/sway/security.d/00-defaults, and
 | 
					security configuration is installed to /etc/sway/security.d/00-defaults, and
 | 
				
			||||||
should not be modified - it will be updated with the latest recommended security
 | 
					should not be modified - it will be updated with the latest recommended security
 | 
				
			||||||
defaults between releases. To override the defaults, you should add more files to
 | 
					defaults between releases. To override the defaults, you should add more files to
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue