Read configs from /etc/sway/security.d/*

This commit is contained in:
Drew DeVault 2017-02-20 07:42:08 -05:00
parent eabfb6c559
commit 126ce571da
9 changed files with 77 additions and 42 deletions

View file

@ -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);

View file

@ -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 {

View file

@ -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);
} }

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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;
} }

View file

@ -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) {

View file

@ -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