1 /*
2 * Portions of this file taken from wlroots; MIT licensed. Its purpose is to
3 * run a child process as root for opening evdev devices.
4 *
5 * NOTICE: Most of this code runs as root.
6 */
7 #ifdef __FreeBSD__
8 #define __BSD_VISIBLE 1
9 #endif
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <libinput.h>
13 #include <libudev.h>
14 #include <limits.h>
15 #include <stdbool.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <sys/stat.h>
19 #include <sys/socket.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23 #include "devmgr.h"
24
25 enum msg_type {
26 MSG_OPEN,
27 MSG_END,
28 };
29
30 struct msg {
31 enum msg_type msg_type;
32 char path[PATH_MAX];
33 };
34
recv_msg(int sock,int * fd_out,void * buf,size_t buf_len)35 static ssize_t recv_msg(int sock, int *fd_out, void *buf, size_t buf_len) {
36 char control[CMSG_SPACE(sizeof(*fd_out))] = {0};
37 struct iovec iovec = { .iov_base = buf, .iov_len = buf_len };
38 struct msghdr msghdr = {0};
39
40 if (buf) {
41 msghdr.msg_iov = &iovec;
42 msghdr.msg_iovlen = 1;
43 }
44
45 if (fd_out) {
46 msghdr.msg_control = &control;
47 msghdr.msg_controllen = sizeof(control);
48 }
49
50 ssize_t ret;
51 do {
52 ret = recvmsg(sock, &msghdr, MSG_CMSG_CLOEXEC);
53 } while (ret < 0 && errno == EINTR);
54
55 if (fd_out) {
56 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msghdr);
57 if (cmsg) {
58 memcpy(fd_out, CMSG_DATA(cmsg), sizeof(*fd_out));
59 } else {
60 *fd_out = -1;
61 }
62 }
63
64 return ret;
65 }
66
send_msg(int sock,int fd,void * buf,size_t buf_len)67 static void send_msg(int sock, int fd, void *buf, size_t buf_len) {
68 char control[CMSG_SPACE(sizeof(fd))] = {0};
69 struct iovec iovec = { .iov_base = buf, .iov_len = buf_len };
70 struct msghdr msghdr = {0};
71
72 if (buf) {
73 msghdr.msg_iov = &iovec;
74 msghdr.msg_iovlen = 1;
75 }
76
77 if (fd >= 0) {
78 msghdr.msg_control = &control;
79 msghdr.msg_controllen = sizeof(control);
80
81 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msghdr);
82 *cmsg = (struct cmsghdr) {
83 .cmsg_level = SOL_SOCKET,
84 .cmsg_type = SCM_RIGHTS,
85 .cmsg_len = CMSG_LEN(sizeof(fd)),
86 };
87 memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
88 }
89
90 ssize_t ret;
91 do {
92 ret = sendmsg(sock, &msghdr, 0);
93 } while (ret < 0 && errno == EINTR);
94 }
95
devmgr_run(int sockfd,const char * devpath)96 static void devmgr_run(int sockfd, const char *devpath) {
97 struct msg msg;
98 int fdin = -1;
99 bool running = true;
100
101 while (running && recv_msg(sockfd, &fdin, &msg, sizeof(msg)) > 0) {
102 switch (msg.msg_type) {
103 case MSG_OPEN:
104 errno = 0;
105 if (strstr(msg.path, devpath) != msg.path) {
106 /* Hackerman detected */
107 exit(1);
108 }
109 int fd = open(msg.path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
110 int ret = errno;
111 send_msg(sockfd, ret ? -1 : fd, &ret, sizeof(ret));
112 if (fd >= 0) {
113 close(fd);
114 }
115 break;
116 case MSG_END:
117 running = false;
118 send_msg(sockfd, -1, NULL, 0);
119 break;
120 }
121 }
122
123 exit(0);
124 }
125
devmgr_start(int * fd,pid_t * pid,const char * devpath)126 int devmgr_start(int *fd, pid_t *pid, const char *devpath) {
127 if (geteuid() != 0) {
128 fprintf(stderr, "wshowkeys needs to be setuid to read input events\n");
129 return 1;
130 }
131
132 int sock[2];
133 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock) < 0) {
134 fprintf(stderr, "devmgr: socketpair: %s", strerror(errno));
135 return -1;
136 }
137
138 pid_t child = fork();
139 if (child < 0) {
140 fprintf(stderr, "devmgr: fork: %s", strerror(errno));
141 close(sock[0]);
142 close(sock[1]);
143 return 1;
144 } else if (child == 0) {
145 close(sock[0]);
146 devmgr_run(sock[1], devpath); /* Does not return */
147 }
148 close(sock[1]);
149 *fd = sock[0];
150 *pid = child;
151
152 if (setgid(getgid()) != 0) {
153 fprintf(stderr, "devmgr: setgid: %s\n", strerror(errno));
154 return 1;
155 }
156 if (setuid(getuid()) != 0) {
157 fprintf(stderr, "devmgr: setuid: %s\n", strerror(errno));
158 return 1;
159 }
160 if (setuid(0) != -1) {
161 fprintf(stderr, "devmgr: failed to drop root\n");
162 return 1;
163 }
164
165 return 0;
166 }
167
devmgr_open(int sockfd,const char * path)168 int devmgr_open(int sockfd, const char *path) {
169 struct msg msg = { .msg_type = MSG_OPEN };
170 snprintf(msg.path, sizeof(msg.path), "%s", path);
171
172 send_msg(sockfd, -1, &msg, sizeof(msg));
173
174 int fd, err, ret;
175 int retry = 0;
176 do {
177 ret = recv_msg(sockfd, &fd, &err, sizeof(err));
178 } while (ret == 0 && retry++ < 3);
179
180 return err ? -err : fd;
181 }
182
devmgr_finish(int sock,pid_t pid)183 void devmgr_finish(int sock, pid_t pid) {
184 struct msg msg = { .msg_type = MSG_END };
185
186 send_msg(sock, -1, &msg, sizeof(msg));
187 recv_msg(sock, NULL, NULL, 0);
188
189 waitpid(pid, NULL, 0);
190
191 close(sock);
192 }
193