kdbus: the driver, original and non-working
[platform/kernel/linux-rpi.git] / tools / testing / selftests / kdbus / test-endpoint.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <fcntl.h>
4 #include <stdlib.h>
5 #include <stddef.h>
6 #include <unistd.h>
7 #include <stdint.h>
8 #include <errno.h>
9 #include <assert.h>
10 #include <libgen.h>
11 #include <sys/capability.h>
12 #include <sys/wait.h>
13 #include <stdbool.h>
14
15 #include "kdbus-api.h"
16 #include "kdbus-util.h"
17 #include "kdbus-enum.h"
18 #include "kdbus-test.h"
19
20 #define KDBUS_SYSNAME_MAX_LEN                   63
21
22 static int install_name_add_match(struct kdbus_conn *conn, const char *name)
23 {
24         struct {
25                 struct kdbus_cmd_match cmd;
26                 struct {
27                         uint64_t size;
28                         uint64_t type;
29                         struct kdbus_notify_name_change chg;
30                 } item;
31                 char name[64];
32         } buf;
33         int ret;
34
35         /* install the match rule */
36         memset(&buf, 0, sizeof(buf));
37         buf.item.type = KDBUS_ITEM_NAME_ADD;
38         buf.item.chg.old_id.id = KDBUS_MATCH_ID_ANY;
39         buf.item.chg.new_id.id = KDBUS_MATCH_ID_ANY;
40         strncpy(buf.name, name, sizeof(buf.name) - 1);
41         buf.item.size = sizeof(buf.item) + strlen(buf.name) + 1;
42         buf.cmd.size = sizeof(buf.cmd) + buf.item.size;
43
44         ret = kdbus_cmd_match_add(conn->fd, &buf.cmd);
45         if (ret < 0)
46                 return ret;
47
48         return 0;
49 }
50
51 static int create_endpoint(const char *buspath, uid_t uid, const char *name,
52                            uint64_t flags)
53 {
54         struct {
55                 struct kdbus_cmd cmd;
56
57                 /* name item */
58                 struct {
59                         uint64_t size;
60                         uint64_t type;
61                         /* max should be KDBUS_SYSNAME_MAX_LEN */
62                         char str[128];
63                 } name;
64         } ep_make;
65         int fd, ret;
66
67         fd = open(buspath, O_RDWR);
68         if (fd < 0)
69                 return fd;
70
71         memset(&ep_make, 0, sizeof(ep_make));
72
73         snprintf(ep_make.name.str,
74                  /* Use the KDBUS_SYSNAME_MAX_LEN or sizeof(str) */
75                  KDBUS_SYSNAME_MAX_LEN > strlen(name) ?
76                  KDBUS_SYSNAME_MAX_LEN : sizeof(ep_make.name.str),
77                  "%u-%s", uid, name);
78
79         ep_make.name.type = KDBUS_ITEM_MAKE_NAME;
80         ep_make.name.size = KDBUS_ITEM_HEADER_SIZE +
81                             strlen(ep_make.name.str) + 1;
82
83         ep_make.cmd.flags = flags;
84         ep_make.cmd.size = sizeof(ep_make.cmd) + ep_make.name.size;
85
86         ret = kdbus_cmd_endpoint_make(fd, &ep_make.cmd);
87         if (ret < 0) {
88                 kdbus_printf("error creating endpoint: %d (%m)\n", ret);
89                 return ret;
90         }
91
92         return fd;
93 }
94
95 static int unpriv_test_custom_ep(const char *buspath)
96 {
97         int ret, ep_fd1, ep_fd2;
98         char *ep1, *ep2, *tmp1, *tmp2;
99
100         tmp1 = strdup(buspath);
101         tmp2 = strdup(buspath);
102         ASSERT_RETURN(tmp1 && tmp2);
103
104         ret = asprintf(&ep1, "%s/%u-%s", dirname(tmp1), getuid(), "apps1");
105         ASSERT_RETURN(ret >= 0);
106
107         ret = asprintf(&ep2, "%s/%u-%s", dirname(tmp2), getuid(), "apps2");
108         ASSERT_RETURN(ret >= 0);
109
110         free(tmp1);
111         free(tmp2);
112
113         /* endpoint only accessible to current uid */
114         ep_fd1 = create_endpoint(buspath, getuid(), "apps1", 0);
115         ASSERT_RETURN(ep_fd1 >= 0);
116
117         /* endpoint world accessible */
118         ep_fd2 = create_endpoint(buspath, getuid(), "apps2",
119                                   KDBUS_MAKE_ACCESS_WORLD);
120         ASSERT_RETURN(ep_fd2 >= 0);
121
122         ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_UID, ({
123                 int ep_fd;
124                 struct kdbus_conn *ep_conn;
125
126                 /*
127                  * Make sure that we are not able to create custom
128                  * endpoints
129                  */
130                 ep_fd = create_endpoint(buspath, getuid(),
131                                         "unpriv_costum_ep", 0);
132                 ASSERT_EXIT(ep_fd == -EPERM);
133
134                 /*
135                  * Endpoint "apps1" only accessible to same users,
136                  * that own the endpoint. Access denied by VFS
137                  */
138                 ep_conn = kdbus_hello(ep1, 0, NULL, 0);
139                 ASSERT_EXIT(!ep_conn && errno == EACCES);
140
141                 /* Endpoint "apps2" world accessible */
142                 ep_conn = kdbus_hello(ep2, 0, NULL, 0);
143                 ASSERT_EXIT(ep_conn);
144
145                 kdbus_conn_free(ep_conn);
146
147                 _exit(EXIT_SUCCESS);
148         }),
149         ({ 0; }));
150         ASSERT_RETURN(ret == 0);
151
152         close(ep_fd1);
153         close(ep_fd2);
154         free(ep1);
155         free(ep2);
156
157         return 0;
158 }
159
160 static int update_endpoint(int fd, const char *name)
161 {
162         int len = strlen(name) + 1;
163         struct {
164                 struct kdbus_cmd cmd;
165
166                 /* name item */
167                 struct {
168                         uint64_t size;
169                         uint64_t type;
170                         char str[KDBUS_ALIGN8(len)];
171                 } name;
172
173                 struct {
174                         uint64_t size;
175                         uint64_t type;
176                         struct kdbus_policy_access access;
177                 } access;
178         } ep_update;
179         int ret;
180
181         memset(&ep_update, 0, sizeof(ep_update));
182
183         ep_update.name.size = KDBUS_ITEM_HEADER_SIZE + len;
184         ep_update.name.type = KDBUS_ITEM_NAME;
185         strncpy(ep_update.name.str, name, sizeof(ep_update.name.str) - 1);
186
187         ep_update.access.size = sizeof(ep_update.access);
188         ep_update.access.type = KDBUS_ITEM_POLICY_ACCESS;
189         ep_update.access.access.type = KDBUS_POLICY_ACCESS_WORLD;
190         ep_update.access.access.access = KDBUS_POLICY_SEE;
191
192         ep_update.cmd.size = sizeof(ep_update);
193
194         ret = kdbus_cmd_endpoint_update(fd, &ep_update.cmd);
195         if (ret < 0) {
196                 kdbus_printf("error updating endpoint: %d (%m)\n", ret);
197                 return ret;
198         }
199
200         return 0;
201 }
202
203 int kdbus_test_custom_endpoint(struct kdbus_test_env *env)
204 {
205         char *ep, *tmp;
206         int ret, ep_fd;
207         struct kdbus_msg *msg;
208         struct kdbus_conn *ep_conn;
209         struct kdbus_conn *reader;
210         const char *name = "foo.bar.baz";
211         const char *epname = "foo";
212         char fake_ep[KDBUS_SYSNAME_MAX_LEN + 1] = {'\0'};
213
214         memset(fake_ep, 'X', sizeof(fake_ep) - 1);
215
216         /* Try to create a custom endpoint with a long name */
217         ret = create_endpoint(env->buspath, getuid(), fake_ep, 0);
218         ASSERT_RETURN(ret == -ENAMETOOLONG);
219
220         /* Try to create a custom endpoint with a different uid */
221         ret = create_endpoint(env->buspath, getuid() + 1, "foobar", 0);
222         ASSERT_RETURN(ret == -EINVAL);
223
224         /* create a custom endpoint, and open a connection on it */
225         ep_fd = create_endpoint(env->buspath, getuid(), "foo", 0);
226         ASSERT_RETURN(ep_fd >= 0);
227
228         tmp = strdup(env->buspath);
229         ASSERT_RETURN(tmp);
230
231         ret = asprintf(&ep, "%s/%u-%s", dirname(tmp), getuid(), epname);
232         free(tmp);
233         ASSERT_RETURN(ret >= 0);
234
235         /* Register a connection that listen to broadcasts */
236         reader = kdbus_hello(ep, 0, NULL, 0);
237         ASSERT_RETURN(reader);
238
239         /* Register to kernel signals */
240         ret = kdbus_add_match_id(reader, 0x1, KDBUS_ITEM_ID_ADD,
241                                  KDBUS_MATCH_ID_ANY);
242         ASSERT_RETURN(ret == 0);
243
244         ret = kdbus_add_match_id(reader, 0x2, KDBUS_ITEM_ID_REMOVE,
245                                  KDBUS_MATCH_ID_ANY);
246         ASSERT_RETURN(ret == 0);
247
248         ret = install_name_add_match(reader, name);
249         ASSERT_RETURN(ret == 0);
250
251         /* Monitor connections are not supported on custom endpoints */
252         ep_conn = kdbus_hello(ep, KDBUS_HELLO_MONITOR, NULL, 0);
253         ASSERT_RETURN(!ep_conn && errno == EOPNOTSUPP);
254
255         ep_conn = kdbus_hello(ep, 0, NULL, 0);
256         ASSERT_RETURN(ep_conn);
257
258         /*
259          * Add a name add match on the endpoint connection, acquire name from
260          * the unfiltered connection, and make sure the filtered connection
261          * did not get the notification on the name owner change. Also, the
262          * endpoint connection may not be able to call conn_info, neither on
263          * the name nor on the ID.
264          */
265         ret = install_name_add_match(ep_conn, name);
266         ASSERT_RETURN(ret == 0);
267
268         ret = kdbus_name_acquire(env->conn, name, NULL);
269         ASSERT_RETURN(ret == 0);
270
271         ret = kdbus_msg_recv(ep_conn, NULL, NULL);
272         ASSERT_RETURN(ret == -EAGAIN);
273
274         ret = kdbus_conn_info(ep_conn, 0, name, 0, NULL);
275         ASSERT_RETURN(ret == -ESRCH);
276
277         ret = kdbus_conn_info(ep_conn, 0, "random.crappy.name", 0, NULL);
278         ASSERT_RETURN(ret == -ESRCH);
279
280         ret = kdbus_conn_info(ep_conn, env->conn->id, NULL, 0, NULL);
281         ASSERT_RETURN(ret == -ENXIO);
282
283         ret = kdbus_conn_info(ep_conn, 0x0fffffffffffffffULL, NULL, 0, NULL);
284         ASSERT_RETURN(ret == -ENXIO);
285
286         /* Check that the reader did not receive anything */
287         ret = kdbus_msg_recv(reader, NULL, NULL);
288         ASSERT_RETURN(ret == -EAGAIN);
289
290         /*
291          * Release the name again, update the custom endpoint policy,
292          * and try again. This time, the connection on the custom endpoint
293          * should have gotten it.
294          */
295         ret = kdbus_name_release(env->conn, name);
296         ASSERT_RETURN(ret == 0);
297
298         ret = update_endpoint(ep_fd, name);
299         ASSERT_RETURN(ret == 0);
300
301         ret = kdbus_name_acquire(env->conn, name, NULL);
302         ASSERT_RETURN(ret == 0);
303
304         ret = kdbus_msg_recv(ep_conn, &msg, NULL);
305         ASSERT_RETURN(ret == 0);
306         ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_NAME_ADD);
307         ASSERT_RETURN(msg->items[0].name_change.old_id.id == 0);
308         ASSERT_RETURN(msg->items[0].name_change.new_id.id == env->conn->id);
309         ASSERT_RETURN(strcmp(msg->items[0].name_change.name, name) == 0);
310         kdbus_msg_free(msg);
311
312         ret = kdbus_msg_recv(reader, &msg, NULL);
313         ASSERT_RETURN(ret == 0);
314         ASSERT_RETURN(strcmp(msg->items[0].name_change.name, name) == 0);
315
316         kdbus_msg_free(msg);
317
318         ret = kdbus_conn_info(ep_conn, 0, name, 0, NULL);
319         ASSERT_RETURN(ret == 0);
320
321         ret = kdbus_conn_info(ep_conn, env->conn->id, NULL, 0, NULL);
322         ASSERT_RETURN(ret == 0);
323
324         /* If we have privileges test custom endpoints */
325         ret = test_is_capable(CAP_SETUID, CAP_SETGID, -1);
326         ASSERT_RETURN(ret >= 0);
327
328         /*
329          * All uids/gids are mapped and we have the necessary caps
330          */
331         if (ret && all_uids_gids_are_mapped()) {
332                 ret = unpriv_test_custom_ep(env->buspath);
333                 ASSERT_RETURN(ret == 0);
334         }
335
336         kdbus_conn_free(reader);
337         kdbus_conn_free(ep_conn);
338         close(ep_fd);
339
340         return TEST_OK;
341 }