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