1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 #include <poll.h>
5 #include <stddef.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/un.h>
9 #include <unistd.h>
10 
11 #include "sd-event.h"
12 
13 #include "alloc-util.h"
14 #include "errno-util.h"
15 #include "fd-util.h"
16 #include "format-util.h"
17 #include "io-util.h"
18 #include "socket-util.h"
19 #include "strxcpyx.h"
20 #include "udev-ctrl.h"
21 #include "util.h"
22 
23 /* wire protocol magic must match */
24 #define UDEV_CTRL_MAGIC                                0xdead1dea
25 
26 typedef struct UdevCtrlMessageWire {
27         char version[16];
28         unsigned magic;
29         UdevCtrlMessageType type;
30         UdevCtrlMessageValue value;
31 } UdevCtrlMessageWire;
32 
33 struct UdevCtrl {
34         unsigned n_ref;
35         int sock;
36         int sock_connect;
37         union sockaddr_union saddr;
38         socklen_t addrlen;
39         bool bound;
40         bool connected;
41         bool maybe_disconnected;
42         sd_event *event;
43         sd_event_source *event_source;
44         sd_event_source *event_source_connect;
45         udev_ctrl_handler_t callback;
46         void *userdata;
47 };
48 
udev_ctrl_new_from_fd(UdevCtrl ** ret,int fd)49 int udev_ctrl_new_from_fd(UdevCtrl **ret, int fd) {
50         _cleanup_close_ int sock = -1;
51         UdevCtrl *uctrl;
52 
53         assert(ret);
54 
55         if (fd < 0) {
56                 sock = socket(AF_UNIX, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
57                 if (sock < 0)
58                         return log_error_errno(errno, "Failed to create socket: %m");
59         }
60 
61         uctrl = new(UdevCtrl, 1);
62         if (!uctrl)
63                 return -ENOMEM;
64 
65         *uctrl = (UdevCtrl) {
66                 .n_ref = 1,
67                 .sock = fd >= 0 ? fd : TAKE_FD(sock),
68                 .sock_connect = -1,
69                 .bound = fd >= 0,
70         };
71 
72         uctrl->saddr.un = (struct sockaddr_un) {
73                 .sun_family = AF_UNIX,
74                 .sun_path = "/run/udev/control",
75         };
76 
77         uctrl->addrlen = SOCKADDR_UN_LEN(uctrl->saddr.un);
78 
79         *ret = TAKE_PTR(uctrl);
80         return 0;
81 }
82 
udev_ctrl_enable_receiving(UdevCtrl * uctrl)83 int udev_ctrl_enable_receiving(UdevCtrl *uctrl) {
84         assert(uctrl);
85 
86         if (uctrl->bound)
87                 return 0;
88 
89         (void) sockaddr_un_unlink(&uctrl->saddr.un);
90         if (bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0)
91                 return log_error_errno(errno, "Failed to bind udev control socket: %m");
92 
93         if (listen(uctrl->sock, 0) < 0)
94                 return log_error_errno(errno, "Failed to listen udev control socket: %m");
95 
96         uctrl->bound = true;
97         return 0;
98 }
99 
udev_ctrl_disconnect(UdevCtrl * uctrl)100 static void udev_ctrl_disconnect(UdevCtrl *uctrl) {
101         if (!uctrl)
102                 return;
103 
104         uctrl->event_source_connect = sd_event_source_unref(uctrl->event_source_connect);
105         uctrl->sock_connect = safe_close(uctrl->sock_connect);
106 }
107 
udev_ctrl_free(UdevCtrl * uctrl)108 static UdevCtrl *udev_ctrl_free(UdevCtrl *uctrl) {
109         assert(uctrl);
110 
111         udev_ctrl_disconnect(uctrl);
112 
113         sd_event_source_unref(uctrl->event_source);
114         safe_close(uctrl->sock);
115 
116         sd_event_unref(uctrl->event);
117         return mfree(uctrl);
118 }
119 
120 DEFINE_TRIVIAL_REF_UNREF_FUNC(UdevCtrl, udev_ctrl, udev_ctrl_free);
121 
udev_ctrl_attach_event(UdevCtrl * uctrl,sd_event * event)122 int udev_ctrl_attach_event(UdevCtrl *uctrl, sd_event *event) {
123         int r;
124 
125         assert_return(uctrl, -EINVAL);
126         assert_return(!uctrl->event, -EBUSY);
127 
128         if (event)
129                 uctrl->event = sd_event_ref(event);
130         else {
131                 r = sd_event_default(&uctrl->event);
132                 if (r < 0)
133                         return r;
134         }
135 
136         return 0;
137 }
138 
udev_ctrl_get_event_source(UdevCtrl * uctrl)139 sd_event_source *udev_ctrl_get_event_source(UdevCtrl *uctrl) {
140         assert(uctrl);
141 
142         return uctrl->event_source;
143 }
144 
udev_ctrl_disconnect_and_listen_again(UdevCtrl * uctrl)145 static void udev_ctrl_disconnect_and_listen_again(UdevCtrl *uctrl) {
146         udev_ctrl_disconnect(uctrl);
147         udev_ctrl_unref(uctrl);
148         (void) sd_event_source_set_enabled(uctrl->event_source, SD_EVENT_ON);
149         /* We don't return NULL here because uctrl is not freed */
150 }
151 
152 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(UdevCtrl*, udev_ctrl_disconnect_and_listen_again, NULL);
153 
udev_ctrl_connection_event_handler(sd_event_source * s,int fd,uint32_t revents,void * userdata)154 static int udev_ctrl_connection_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
155         _cleanup_(udev_ctrl_disconnect_and_listen_againp) UdevCtrl *uctrl = NULL;
156         UdevCtrlMessageWire msg_wire;
157         struct iovec iov = IOVEC_MAKE(&msg_wire, sizeof(UdevCtrlMessageWire));
158         CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
159         struct msghdr smsg = {
160                 .msg_iov = &iov,
161                 .msg_iovlen = 1,
162                 .msg_control = &control,
163                 .msg_controllen = sizeof(control),
164         };
165         struct cmsghdr *cmsg;
166         struct ucred *cred;
167         ssize_t size;
168 
169         assert(userdata);
170 
171         /* When UDEV_CTRL_EXIT is received, manager unref udev_ctrl object.
172          * To avoid the object freed, let's increment the refcount. */
173         uctrl = udev_ctrl_ref(userdata);
174 
175         size = next_datagram_size_fd(fd);
176         if (size < 0)
177                 return log_error_errno(size, "Failed to get size of message: %m");
178         if (size == 0)
179                 return 0; /* Client disconnects? */
180 
181         size = recvmsg_safe(fd, &smsg, 0);
182         if (size == -EINTR)
183                 return 0;
184         if (size < 0)
185                 return log_error_errno(size, "Failed to receive ctrl message: %m");
186 
187         cmsg_close_all(&smsg);
188 
189         cmsg = CMSG_FIRSTHDR(&smsg);
190 
191         if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) {
192                 log_error("No sender credentials received, ignoring message");
193                 return 0;
194         }
195 
196         cred = (struct ucred *) CMSG_DATA(cmsg);
197 
198         if (cred->uid != 0) {
199                 log_error("Invalid sender uid "UID_FMT", ignoring message", cred->uid);
200                 return 0;
201         }
202 
203         if (msg_wire.magic != UDEV_CTRL_MAGIC) {
204                 log_error("Message magic 0x%08x doesn't match, ignoring message", msg_wire.magic);
205                 return 0;
206         }
207 
208         if (msg_wire.type == _UDEV_CTRL_END_MESSAGES)
209                 return 0;
210 
211         if (uctrl->callback)
212                 (void) uctrl->callback(uctrl, msg_wire.type, &msg_wire.value, uctrl->userdata);
213 
214         /* Do not disconnect and wait for next message. */
215         uctrl = udev_ctrl_unref(uctrl);
216         return 0;
217 }
218 
udev_ctrl_event_handler(sd_event_source * s,int fd,uint32_t revents,void * userdata)219 static int udev_ctrl_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
220         UdevCtrl *uctrl = userdata;
221         _cleanup_close_ int sock = -1;
222         struct ucred ucred;
223         int r;
224 
225         assert(uctrl);
226 
227         sock = accept4(fd, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK);
228         if (sock < 0) {
229                 if (ERRNO_IS_ACCEPT_AGAIN(errno))
230                         return 0;
231 
232                 return log_error_errno(errno, "Failed to accept ctrl connection: %m");
233         }
234 
235         /* check peer credential of connection */
236         r = getpeercred(sock, &ucred);
237         if (r < 0) {
238                 log_error_errno(r, "Failed to receive credentials of ctrl connection: %m");
239                 return 0;
240         }
241 
242         if (ucred.uid > 0) {
243                 log_error("Invalid sender uid "UID_FMT", closing connection", ucred.uid);
244                 return 0;
245         }
246 
247         /* enable receiving of the sender credentials in the messages */
248         r = setsockopt_int(sock, SOL_SOCKET, SO_PASSCRED, true);
249         if (r < 0)
250                 log_warning_errno(r, "Failed to set SO_PASSCRED, ignoring: %m");
251 
252         r = sd_event_add_io(uctrl->event, &uctrl->event_source_connect, sock, EPOLLIN, udev_ctrl_connection_event_handler, uctrl);
253         if (r < 0) {
254                 log_error_errno(r, "Failed to create event source for udev control connection: %m");
255                 return 0;
256         }
257 
258         (void) sd_event_source_set_description(uctrl->event_source_connect, "udev-ctrl-connection");
259 
260         /* Do not accept multiple connection. */
261         (void) sd_event_source_set_enabled(uctrl->event_source, SD_EVENT_OFF);
262 
263         uctrl->sock_connect = TAKE_FD(sock);
264         return 0;
265 }
266 
udev_ctrl_start(UdevCtrl * uctrl,udev_ctrl_handler_t callback,void * userdata)267 int udev_ctrl_start(UdevCtrl *uctrl, udev_ctrl_handler_t callback, void *userdata) {
268         int r;
269 
270         assert(uctrl);
271 
272         if (!uctrl->event) {
273                 r = udev_ctrl_attach_event(uctrl, NULL);
274                 if (r < 0)
275                         return r;
276         }
277 
278         r = udev_ctrl_enable_receiving(uctrl);
279         if (r < 0)
280                 return r;
281 
282         uctrl->callback = callback;
283         uctrl->userdata = userdata;
284 
285         r = sd_event_add_io(uctrl->event, &uctrl->event_source, uctrl->sock, EPOLLIN, udev_ctrl_event_handler, uctrl);
286         if (r < 0)
287                 return r;
288 
289         (void) sd_event_source_set_description(uctrl->event_source, "udev-ctrl");
290 
291         return 0;
292 }
293 
udev_ctrl_send(UdevCtrl * uctrl,UdevCtrlMessageType type,const void * data)294 int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, const void *data) {
295         UdevCtrlMessageWire ctrl_msg_wire = {
296                 .version = "udev-" STRINGIFY(PROJECT_VERSION),
297                 .magic = UDEV_CTRL_MAGIC,
298                 .type = type,
299         };
300 
301         if (uctrl->maybe_disconnected)
302                 return -ENOANO; /* to distinguish this from other errors. */
303 
304         if (type == UDEV_CTRL_SET_ENV) {
305                 assert(data);
306                 strscpy(ctrl_msg_wire.value.buf, sizeof(ctrl_msg_wire.value.buf), data);
307         } else if (IN_SET(type, UDEV_CTRL_SET_LOG_LEVEL, UDEV_CTRL_SET_CHILDREN_MAX))
308                 ctrl_msg_wire.value.intval = PTR_TO_INT(data);
309 
310         if (!uctrl->connected) {
311                 if (connect(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0)
312                         return -errno;
313                 uctrl->connected = true;
314         }
315 
316         if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0)
317                 return -errno;
318 
319         if (type == UDEV_CTRL_EXIT)
320                 uctrl->maybe_disconnected = true;
321 
322         return 0;
323 }
324 
udev_ctrl_wait(UdevCtrl * uctrl,usec_t timeout)325 int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout) {
326         _cleanup_(sd_event_source_disable_unrefp) sd_event_source *source_io = NULL, *source_timeout = NULL;
327         int r;
328 
329         assert(uctrl);
330 
331         if (uctrl->sock < 0)
332                 return 0;
333         if (!uctrl->connected)
334                 return 0;
335 
336         if (!uctrl->maybe_disconnected) {
337                 r = udev_ctrl_send(uctrl, _UDEV_CTRL_END_MESSAGES, NULL);
338                 if (r < 0)
339                         return r;
340         }
341 
342         if (timeout == 0)
343                 return 0;
344 
345         if (!uctrl->event) {
346                 r = udev_ctrl_attach_event(uctrl, NULL);
347                 if (r < 0)
348                         return r;
349         }
350 
351         r = sd_event_add_io(uctrl->event, &source_io, uctrl->sock, EPOLLIN, NULL, INT_TO_PTR(0));
352         if (r < 0)
353                 return r;
354 
355         (void) sd_event_source_set_description(source_io, "udev-ctrl-wait-io");
356 
357         if (timeout != USEC_INFINITY) {
358                 r = sd_event_add_time_relative(
359                                 uctrl->event, &source_timeout, CLOCK_BOOTTIME,
360                                 timeout,
361                                 0, NULL, INT_TO_PTR(-ETIMEDOUT));
362                 if (r < 0)
363                         return r;
364 
365                 (void) sd_event_source_set_description(source_timeout, "udev-ctrl-wait-timeout");
366         }
367 
368         return sd_event_loop(uctrl->event);
369 }
370