aboutsummaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2017-04-16 10:17:43 -0400
committerLibravatar GitHub <noreply@github.com>2017-04-16 10:17:43 -0400
commit7494a48378bff3b11304ba4077bda5a84ed10087 (patch)
tree21abe14fe200099fffe5de9b7770cf2ca921e371 /sway
parentMerge pull request #1175 from JerziKaminsky/fix_sway_assert_variadic (diff)
parentHandle symlinks as IPC security targets (diff)
downloadsway-7494a48378bff3b11304ba4077bda5a84ed10087.tar.gz
sway-7494a48378bff3b11304ba4077bda5a84ed10087.tar.zst
sway-7494a48378bff3b11304ba4077bda5a84ed10087.zip
Merge pull request #1173 from JerziKaminsky/security_resolve_symlink
FOR_REVIEW: IPC security - Allow policy targets to be symlinks
Diffstat (limited to 'sway')
-rw-r--r--sway/commands.c2
-rw-r--r--sway/commands/ipc.c10
-rw-r--r--sway/commands/permit.c58
-rw-r--r--sway/extensions.c8
-rw-r--r--sway/handlers.c10
-rw-r--r--sway/ipc-server.c2
-rw-r--r--sway/security.c72
7 files changed, 125 insertions, 37 deletions
diff --git a/sway/commands.c b/sway/commands.c
index 17c7d717..4d7af301 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -437,7 +437,7 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
437 free_argv(argc, argv); 437 free_argv(argc, argv);
438 goto cleanup; 438 goto cleanup;
439 } 439 }
440 if (!(get_command_policy(argv[0]) & context)) { 440 if (!(get_command_policy_mask(argv[0]) & context)) {
441 if (results) { 441 if (results) {
442 free_cmd_results(results); 442 free_cmd_results(results);
443 } 443 }
diff --git a/sway/commands/ipc.c b/sway/commands/ipc.c
index 8a7b849f..f0b3035a 100644
--- a/sway/commands/ipc.c
+++ b/sway/commands/ipc.c
@@ -1,3 +1,4 @@
1#define _XOPEN_SOURCE 500
1#include <stdio.h> 2#include <stdio.h>
2#include <string.h> 3#include <string.h>
3#include "sway/security.h" 4#include "sway/security.h"
@@ -18,8 +19,14 @@ struct cmd_results *cmd_ipc(int argc, char **argv) {
18 return error; 19 return error;
19 } 20 }
20 21
21 const char *program = argv[0]; 22 char *program = NULL;
22 23
24 if (!strcmp(argv[0], "*")) {
25 program = strdup(argv[0]);
26 } else if (!(program = resolve_path(argv[0]))) {
27 return cmd_results_new(
28 CMD_INVALID, "ipc", "Unable to resolve IPC Policy target.");
29 }
23 if (config->reading && strcmp("{", argv[1]) != 0) { 30 if (config->reading && strcmp("{", argv[1]) != 0) {
24 return cmd_results_new(CMD_INVALID, "ipc", 31 return cmd_results_new(CMD_INVALID, "ipc",
25 "Expected '{' at start of IPC config definition."); 32 "Expected '{' at start of IPC config definition.");
@@ -32,6 +39,7 @@ struct cmd_results *cmd_ipc(int argc, char **argv) {
32 current_policy = alloc_ipc_policy(program); 39 current_policy = alloc_ipc_policy(program);
33 list_add(config->ipc_policies, current_policy); 40 list_add(config->ipc_policies, current_policy);
34 41
42 free(program);
35 return cmd_results_new(CMD_BLOCK_IPC, NULL, NULL); 43 return cmd_results_new(CMD_BLOCK_IPC, NULL, NULL);
36} 44}
37 45
diff --git a/sway/commands/permit.c b/sway/commands/permit.c
index e2bec2e2..66fa4e2a 100644
--- a/sway/commands/permit.c
+++ b/sway/commands/permit.c
@@ -1,7 +1,9 @@
1#define _XOPEN_SOURCE 500
1#include <string.h> 2#include <string.h>
2#include "sway/commands.h" 3#include "sway/commands.h"
3#include "sway/config.h" 4#include "sway/config.h"
4#include "sway/security.h" 5#include "sway/security.h"
6#include "util.h"
5#include "log.h" 7#include "log.h"
6 8
7static enum secure_feature get_features(int argc, char **argv, 9static enum secure_feature get_features(int argc, char **argv,
@@ -38,25 +40,6 @@ static enum secure_feature get_features(int argc, char **argv,
38 return features; 40 return features;
39} 41}
40 42
41static struct feature_policy *get_policy(const char *name) {
42 struct feature_policy *policy = NULL;
43 for (int i = 0; i < config->feature_policies->length; ++i) {
44 struct feature_policy *p = config->feature_policies->items[i];
45 if (strcmp(p->program, name) == 0) {
46 policy = p;
47 break;
48 }
49 }
50 if (!policy) {
51 policy = alloc_feature_policy(name);
52 if (!policy) {
53 sway_abort("Unable to allocate security policy");
54 }
55 list_add(config->feature_policies, policy);
56 }
57 return policy;
58}
59
60struct cmd_results *cmd_permit(int argc, char **argv) { 43struct cmd_results *cmd_permit(int argc, char **argv) {
61 struct cmd_results *error = NULL; 44 struct cmd_results *error = NULL;
62 if ((error = checkarg(argc, "permit", EXPECTED_MORE_THAN, 1))) { 45 if ((error = checkarg(argc, "permit", EXPECTED_MORE_THAN, 1))) {
@@ -66,12 +49,29 @@ struct cmd_results *cmd_permit(int argc, char **argv) {
66 return error; 49 return error;
67 } 50 }
68 51
69 struct feature_policy *policy = get_policy(argv[0]); 52 bool assign_perms = true;
70 policy->features |= get_features(argc, argv, &error); 53 char *program = NULL;
71 54
55 if (!strcmp(argv[0], "*")) {
56 program = strdup(argv[0]);
57 } else {
58 program = resolve_path(argv[0]);
59 }
60 if (!program) {
61 sway_assert(program, "Unable to resolve IPC permit target '%s'."
62 " will issue empty policy", argv[0]);
63 assign_perms = false;
64 program = strdup(argv[0]);
65 }
66
67 struct feature_policy *policy = get_feature_policy(program);
68 if (assign_perms) {
69 policy->features |= get_features(argc, argv, &error);
70 }
72 sway_log(L_DEBUG, "Permissions granted to %s for features %d", 71 sway_log(L_DEBUG, "Permissions granted to %s for features %d",
73 policy->program, policy->features); 72 policy->program, policy->features);
74 73
74 free(program);
75 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 75 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
76} 76}
77 77
@@ -84,11 +84,25 @@ struct cmd_results *cmd_reject(int argc, char **argv) {
84 return error; 84 return error;
85 } 85 }
86 86
87 struct feature_policy *policy = get_policy(argv[0]); 87 char *program = NULL;
88 if (!strcmp(argv[0], "*")) {
89 program = strdup(argv[0]);
90 } else {
91 program = resolve_path(argv[0]);
92 }
93 if (!program) {
94 // Punt
95 sway_log(L_INFO, "Unable to resolve IPC reject target '%s'."
96 " Will use provided path", argv[0]);
97 program = strdup(argv[0]);
98 }
99
100 struct feature_policy *policy = get_feature_policy(program);
88 policy->features &= ~get_features(argc, argv, &error); 101 policy->features &= ~get_features(argc, argv, &error);
89 102
90 sway_log(L_DEBUG, "Permissions granted to %s for features %d", 103 sway_log(L_DEBUG, "Permissions granted to %s for features %d",
91 policy->program, policy->features); 104 policy->program, policy->features);
92 105
106 free(program);
93 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 107 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
94} 108}
diff --git a/sway/extensions.c b/sway/extensions.c
index 15d2f971..96957dbf 100644
--- a/sway/extensions.c
+++ b/sway/extensions.c
@@ -86,7 +86,7 @@ static void set_background(struct wl_client *client, struct wl_resource *resourc
86 struct wl_resource *_output, struct wl_resource *surface) { 86 struct wl_resource *_output, struct wl_resource *surface) {
87 pid_t pid; 87 pid_t pid;
88 wl_client_get_credentials(client, &pid, NULL, NULL); 88 wl_client_get_credentials(client, &pid, NULL, NULL);
89 if (!(get_feature_policy(pid) & FEATURE_BACKGROUND)) { 89 if (!(get_feature_policy_mask(pid) & FEATURE_BACKGROUND)) {
90 sway_log(L_INFO, "Denying background feature to %d", pid); 90 sway_log(L_INFO, "Denying background feature to %d", pid);
91 return; 91 return;
92 } 92 }
@@ -114,7 +114,7 @@ static void set_panel(struct wl_client *client, struct wl_resource *resource,
114 struct wl_resource *_output, struct wl_resource *surface) { 114 struct wl_resource *_output, struct wl_resource *surface) {
115 pid_t pid; 115 pid_t pid;
116 wl_client_get_credentials(client, &pid, NULL, NULL); 116 wl_client_get_credentials(client, &pid, NULL, NULL);
117 if (!(get_feature_policy(pid) & FEATURE_PANEL)) { 117 if (!(get_feature_policy_mask(pid) & FEATURE_PANEL)) {
118 sway_log(L_INFO, "Denying panel feature to %d", pid); 118 sway_log(L_INFO, "Denying panel feature to %d", pid);
119 return; 119 return;
120 } 120 }
@@ -152,7 +152,7 @@ static void desktop_ready(struct wl_client *client, struct wl_resource *resource
152static void set_panel_position(struct wl_client *client, struct wl_resource *resource, uint32_t position) { 152static void set_panel_position(struct wl_client *client, struct wl_resource *resource, uint32_t position) {
153 pid_t pid; 153 pid_t pid;
154 wl_client_get_credentials(client, &pid, NULL, NULL); 154 wl_client_get_credentials(client, &pid, NULL, NULL);
155 if (!(get_feature_policy(pid) & FEATURE_PANEL)) { 155 if (!(get_feature_policy_mask(pid) & FEATURE_PANEL)) {
156 sway_log(L_INFO, "Denying panel feature to %d", pid); 156 sway_log(L_INFO, "Denying panel feature to %d", pid);
157 return; 157 return;
158 } 158 }
@@ -191,7 +191,7 @@ static void set_lock_surface(struct wl_client *client, struct wl_resource *resou
191 struct wl_resource *_output, struct wl_resource *surface) { 191 struct wl_resource *_output, struct wl_resource *surface) {
192 pid_t pid; 192 pid_t pid;
193 wl_client_get_credentials(client, &pid, NULL, NULL); 193 wl_client_get_credentials(client, &pid, NULL, NULL);
194 if (!(get_feature_policy(pid) & FEATURE_LOCK)) { 194 if (!(get_feature_policy_mask(pid) & FEATURE_LOCK)) {
195 sway_log(L_INFO, "Denying lock feature to %d", pid); 195 sway_log(L_INFO, "Denying lock feature to %d", pid);
196 return; 196 return;
197 } 197 }
diff --git a/sway/handlers.c b/sway/handlers.c
index b61c0a19..a8de135f 100644
--- a/sway/handlers.c
+++ b/sway/handlers.c
@@ -595,7 +595,7 @@ static void handle_view_state_request(wlc_handle view, enum wlc_view_state_bit s
595 pid_t pid = wlc_view_get_pid(view); 595 pid_t pid = wlc_view_get_pid(view);
596 switch (state) { 596 switch (state) {
597 case WLC_BIT_FULLSCREEN: 597 case WLC_BIT_FULLSCREEN:
598 if (!(get_feature_policy(pid) & FEATURE_FULLSCREEN)) { 598 if (!(get_feature_policy_mask(pid) & FEATURE_FULLSCREEN)) {
599 sway_log(L_INFO, "Denying fullscreen to %d (%s)", pid, c->name); 599 sway_log(L_INFO, "Denying fullscreen to %d (%s)", pid, c->name);
600 break; 600 break;
601 } 601 }
@@ -811,7 +811,7 @@ static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifier
811 swayc_t *focused = get_focused_container(&root_container); 811 swayc_t *focused = get_focused_container(&root_container);
812 if (focused->type == C_VIEW) { 812 if (focused->type == C_VIEW) {
813 pid_t pid = wlc_view_get_pid(focused->handle); 813 pid_t pid = wlc_view_get_pid(focused->handle);
814 if (!(get_feature_policy(pid) & FEATURE_KEYBOARD)) { 814 if (!(get_feature_policy_mask(pid) & FEATURE_KEYBOARD)) {
815 return EVENT_HANDLED; 815 return EVENT_HANDLED;
816 } 816 }
817 } 817 }
@@ -875,7 +875,7 @@ static bool handle_pointer_motion(wlc_handle handle, uint32_t time, const struct
875 swayc_t *focused = get_focused_container(&root_container); 875 swayc_t *focused = get_focused_container(&root_container);
876 if (focused->type == C_VIEW) { 876 if (focused->type == C_VIEW) {
877 pid_t pid = wlc_view_get_pid(focused->handle); 877 pid_t pid = wlc_view_get_pid(focused->handle);
878 if (!(get_feature_policy(pid) & FEATURE_MOUSE)) { 878 if (!(get_feature_policy_mask(pid) & FEATURE_MOUSE)) {
879 return EVENT_HANDLED; 879 return EVENT_HANDLED;
880 } 880 }
881 } 881 }
@@ -953,7 +953,7 @@ static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct w
953 if (swayc_is_fullscreen(focused)) { 953 if (swayc_is_fullscreen(focused)) {
954 if (focused->type == C_VIEW) { 954 if (focused->type == C_VIEW) {
955 pid_t pid = wlc_view_get_pid(focused->handle); 955 pid_t pid = wlc_view_get_pid(focused->handle);
956 if (!(get_feature_policy(pid) & FEATURE_MOUSE)) { 956 if (!(get_feature_policy_mask(pid) & FEATURE_MOUSE)) {
957 return EVENT_HANDLED; 957 return EVENT_HANDLED;
958 } 958 }
959 } 959 }
@@ -1001,7 +1001,7 @@ static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct w
1001 1001
1002 if (focused->type == C_VIEW) { 1002 if (focused->type == C_VIEW) {
1003 pid_t pid = wlc_view_get_pid(focused->handle); 1003 pid_t pid = wlc_view_get_pid(focused->handle);
1004 if (!(get_feature_policy(pid) & FEATURE_MOUSE)) { 1004 if (!(get_feature_policy_mask(pid) & FEATURE_MOUSE)) {
1005 return EVENT_HANDLED; 1005 return EVENT_HANDLED;
1006 } 1006 }
1007 } 1007 }
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 67a3cdc8..dca881fa 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -181,7 +181,7 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) {
181 client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); 181 client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client);
182 182
183 pid_t pid = get_client_pid(client->fd); 183 pid_t pid = get_client_pid(client->fd);
184 client->security_policy = get_ipc_policy(pid); 184 client->security_policy = get_ipc_policy_mask(pid);
185 185
186 list_add(ipc_client_list, client); 186 list_add(ipc_client_list, client);
187 187
diff --git a/sway/security.c b/sway/security.c
index f8a96ba7..8eab6126 100644
--- a/sway/security.c
+++ b/sway/security.c
@@ -1,4 +1,6 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <sys/types.h>
3#include <sys/stat.h>
2#include <string.h> 4#include <string.h>
3#include <unistd.h> 5#include <unistd.h>
4#include <stdio.h> 6#include <stdio.h>
@@ -6,8 +8,46 @@
6#include "sway/security.h" 8#include "sway/security.h"
7#include "log.h" 9#include "log.h"
8 10
11static bool validate_ipc_target(const char *program) {
12 struct stat sb;
13
14 sway_log(L_DEBUG, "Validating IPC target '%s'", program);
15
16 if (!strcmp(program, "*")) {
17 return true;
18 }
19 if (lstat(program, &sb) == -1) {
20 return false;
21 }
22 if (!S_ISREG(sb.st_mode)) {
23 sway_log(L_ERROR,
24 "IPC target '%s' MUST be/point at an existing regular file",
25 program);
26 return false;
27 }
28 if (sb.st_uid != 0) {
29#ifdef NDEBUG
30 sway_log(L_ERROR, "IPC target '%s' MUST be owned by root", program);
31 return false;
32#else
33 sway_log(L_INFO, "IPC target '%s' MUST be owned by root (waived for debug build)", program);
34 return true;
35#endif
36 }
37 if (sb.st_mode & S_IWOTH) {
38 sway_log(L_ERROR, "IPC target '%s' MUST NOT be world writable", program);
39 return false;
40 }
41
42 return true;
43}
44
9struct feature_policy *alloc_feature_policy(const char *program) { 45struct feature_policy *alloc_feature_policy(const char *program) {
10 uint32_t default_policy = 0; 46 uint32_t default_policy = 0;
47
48 if (!validate_ipc_target(program)) {
49 return NULL;
50 }
11 for (int i = 0; i < config->feature_policies->length; ++i) { 51 for (int i = 0; i < config->feature_policies->length; ++i) {
12 struct feature_policy *policy = config->feature_policies->items[i]; 52 struct feature_policy *policy = config->feature_policies->items[i];
13 if (strcmp(policy->program, "*") == 0) { 53 if (strcmp(policy->program, "*") == 0) {
@@ -26,11 +66,16 @@ struct feature_policy *alloc_feature_policy(const char *program) {
26 return NULL; 66 return NULL;
27 } 67 }
28 policy->features = default_policy; 68 policy->features = default_policy;
69
29 return policy; 70 return policy;
30} 71}
31 72
32struct ipc_policy *alloc_ipc_policy(const char *program) { 73struct ipc_policy *alloc_ipc_policy(const char *program) {
33 uint32_t default_policy = 0; 74 uint32_t default_policy = 0;
75
76 if (!validate_ipc_target(program)) {
77 return NULL;
78 }
34 for (int i = 0; i < config->ipc_policies->length; ++i) { 79 for (int i = 0; i < config->ipc_policies->length; ++i) {
35 struct ipc_policy *policy = config->ipc_policies->items[i]; 80 struct ipc_policy *policy = config->ipc_policies->items[i];
36 if (strcmp(policy->program, "*") == 0) { 81 if (strcmp(policy->program, "*") == 0) {
@@ -49,6 +94,7 @@ struct ipc_policy *alloc_ipc_policy(const char *program) {
49 return NULL; 94 return NULL;
50 } 95 }
51 policy->features = default_policy; 96 policy->features = default_policy;
97
52 return policy; 98 return policy;
53} 99}
54 100
@@ -94,7 +140,27 @@ static const char *get_pid_exe(pid_t pid) {
94 return link; 140 return link;
95} 141}
96 142
97uint32_t get_feature_policy(pid_t pid) { 143struct feature_policy *get_feature_policy(const char *name) {
144 struct feature_policy *policy = NULL;
145
146 for (int i = 0; i < config->feature_policies->length; ++i) {
147 struct feature_policy *p = config->feature_policies->items[i];
148 if (strcmp(p->program, name) == 0) {
149 policy = p;
150 break;
151 }
152 }
153 if (!policy) {
154 policy = alloc_feature_policy(name);
155 if (!policy) {
156 sway_abort("Unable to allocate security policy");
157 }
158 list_add(config->feature_policies, policy);
159 }
160 return policy;
161}
162
163uint32_t get_feature_policy_mask(pid_t pid) {
98 uint32_t default_policy = 0; 164 uint32_t default_policy = 0;
99 const char *link = get_pid_exe(pid); 165 const char *link = get_pid_exe(pid);
100 166
@@ -111,7 +177,7 @@ uint32_t get_feature_policy(pid_t pid) {
111 return default_policy; 177 return default_policy;
112} 178}
113 179
114uint32_t get_ipc_policy(pid_t pid) { 180uint32_t get_ipc_policy_mask(pid_t pid) {
115 uint32_t default_policy = 0; 181 uint32_t default_policy = 0;
116 const char *link = get_pid_exe(pid); 182 const char *link = get_pid_exe(pid);
117 183
@@ -128,7 +194,7 @@ uint32_t get_ipc_policy(pid_t pid) {
128 return default_policy; 194 return default_policy;
129} 195}
130 196
131uint32_t get_command_policy(const char *cmd) { 197uint32_t get_command_policy_mask(const char *cmd) {
132 uint32_t default_policy = 0; 198 uint32_t default_policy = 0;
133 199
134 for (int i = 0; i < config->command_policies->length; ++i) { 200 for (int i = 0; i < config->command_policies->length; ++i) {