xref: /trueos/sbin/launchd/ipc.c (revision 52e242948cdc417e9b623cb59475af3b97132fa6)
1 /*
2  * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3  *
4  * @APPLE_APACHE_LICENSE_HEADER_START@
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * @APPLE_APACHE_LICENSE_HEADER_END@
19  */
20 
21 #include "config.h"
22 #include "ipc.h"
23 
24 #include <sys/socket.h>
25 #include <sys/types.h>
26 #include <sys/queue.h>
27 #include <sys/event.h>
28 #include <sys/stat.h>
29 #include <sys/ucred.h>
30 #include <sys/fcntl.h>
31 #include <sys/un.h>
32 #include <sys/resource.h>
33 #include <sys/wait.h>
34 #include <sys/sysctl.h>
35 #include <sys/sockio.h>
36 #include <sys/time.h>
37 #include <sys/ioctl.h>
38 #include <unistd.h>
39 #include <signal.h>
40 #include <errno.h>
41 #include <libgen.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <stdarg.h>
45 #include <stdbool.h>
46 #include <paths.h>
47 #include <string.h>
48 #include <os/assumes.h>
49 
50 #include "launch.h"
51 #include "launch_priv.h"
52 #include "launchd.h"
53 #include "runtime.h"
54 #include "core.h"
55 
56 extern char **environ;
57 
58 static LIST_HEAD(, conncb) connections;
59 
60 static launch_data_t adjust_rlimits(launch_data_t in);
61 
62 static void ipc_readmsg2(launch_data_t data, const char *cmd, void *context);
63 static void ipc_readmsg(launch_data_t msg, void *context);
64 
65 static void ipc_listen_callback(void *obj __attribute__((unused)), struct kevent *kev);
66 
67 static kq_callback kqipc_listen_callback = ipc_listen_callback;
68 
69 static pid_t ipc_self = 0;
70 
71 char *sockpath = NULL;
72 static char *sockdir = NULL;
73 
74 static bool ipc_inited = false;
75 
76 static void
ipc_clean_up(void)77 ipc_clean_up(void)
78 {
79 	if (ipc_self != getpid()) {
80 		return;
81 	}
82 
83 	if (-1 == unlink(sockpath)) {
84 		launchd_syslog(LOG_WARNING, "unlink(\"%s\"): %s", sockpath, strerror(errno));
85 	} else if (-1 == rmdir(sockdir)) {
86 		launchd_syslog(LOG_WARNING, "rmdir(\"%s\"): %s", sockdir, strerror(errno));
87 	}
88 }
89 
90 void
ipc_server_init(void)91 ipc_server_init(void)
92 {
93 	struct sockaddr_un sun;
94 	mode_t oldmask;
95 	int r, fd = -1;
96 	char ourdir[1024];
97 
98 	if (ipc_inited) {
99 		return;
100 	}
101 
102 	memset(&sun, 0, sizeof(sun));
103 	sun.sun_family = AF_UNIX;
104 
105 	if (pid1_magic) {
106 		strcpy(ourdir, LAUNCHD_SOCK_PREFIX);
107 		strncpy(sun.sun_path, LAUNCHD_SOCK_PREFIX "/sock", sizeof(sun.sun_path));
108 
109 		unlink(ourdir);
110 		if (mkdir(ourdir, S_IRWXU) == -1) {
111 			if (errno == EROFS) {
112 				goto out_bad;
113 			} else if (errno == EEXIST) {
114 				struct stat sb;
115 				stat(ourdir, &sb);
116 				if (!S_ISDIR(sb.st_mode)) {
117 					errno = EEXIST;
118 					launchd_syslog(LOG_ERR, "mkdir(\"%s\"): %s", LAUNCHD_SOCK_PREFIX, strerror(errno));
119 					goto out_bad;
120 				}
121 			} else {
122 				launchd_syslog(LOG_ERR, "mkdir(\"%s\"): %s", ourdir, strerror(errno));
123 				goto out_bad;
124 			}
125 		}
126 	} else {
127 		snprintf(ourdir, sizeof(ourdir), _PATH_TMP "launchd-%u.XXXXXX", getpid());
128 		if (mkdtemp(ourdir) == NULL) {
129 			launchd_syslog(LOG_ERR, "Could not create critical directory \"%s\": %s", ourdir, strerror(errno));
130 			goto out_bad;
131 		}
132 		snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/sock", ourdir);
133 	}
134 
135 	if (unlink(sun.sun_path) == -1 && errno != ENOENT) {
136 		if (errno != EROFS) {
137 			launchd_syslog(LOG_ERR, "unlink(\"thesocket\"): %s", strerror(errno));
138 		}
139 		goto out_bad;
140 	}
141 
142 	if (posix_assumes_zero(fd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1) {
143 		goto out_bad;
144 	}
145 
146 	oldmask = umask(S_IRWXG|S_IRWXO);
147 	r = bind(fd, (struct sockaddr *)&sun, sizeof(sun));
148 	umask(oldmask);
149 
150 	if (r == -1) {
151 		if (errno != EROFS) {
152 			launchd_syslog(LOG_ERR, "bind(\"thesocket\"): %s", strerror(errno));
153 		}
154 		goto out_bad;
155 	}
156 
157 	if (listen(fd, SOMAXCONN) == -1) {
158 		launchd_syslog(LOG_ERR, "listen(\"thesocket\"): %s", strerror(errno));
159 		goto out_bad;
160 	}
161 
162 	if (kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &kqipc_listen_callback) == -1) {
163 		launchd_syslog(LOG_ERR, "kevent_mod(\"thesocket\", EVFILT_READ): %s", strerror(errno));
164 		goto out_bad;
165 	}
166 
167 	ipc_inited = true;
168 
169 	sockdir = strdup(ourdir);
170 	sockpath = strdup(sun.sun_path);
171 	ipc_self = getpid();
172 	atexit(ipc_clean_up);
173 
174 out_bad:
175 	if (!ipc_inited && fd != -1) {
176 		(void)runtime_close(fd);
177 	}
178 }
179 
180 void
ipc_open(int fd,job_t j)181 ipc_open(int fd, job_t j)
182 {
183 	struct conncb *c = calloc(1, sizeof(struct conncb));
184 
185 	fcntl(fd, F_SETFL, O_NONBLOCK);
186 
187 	c->kqconn_callback = ipc_callback;
188 	if (j) {
189 		c->conn = launchd_fdopen(-1, fd);
190 	} else {
191 		c->conn = launchd_fdopen(fd, -1);
192 	}
193 
194 	c->j = j;
195 	LIST_INSERT_HEAD(&connections, c, sle);
196 	kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &c->kqconn_callback);
197 }
198 
199 void
ipc_listen_callback(void * obj,struct kevent * kev)200 ipc_listen_callback(void *obj __attribute__((unused)), struct kevent *kev)
201 {
202 	struct sockaddr_un sun;
203 	socklen_t sl = sizeof(sun);
204 	int cfd;
205 
206 	if ((cfd = _fd(accept(kev->ident, (struct sockaddr *)&sun, &sl))) == -1) {
207 		return;
208 	}
209 
210 	if (geteuid() == 0) {
211 		uid_t euid, guid;
212 		if (getpeereid(cfd, &euid, &guid) == -1) {
213 			launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** launchd[%d] failed to getpeereid on incoming caller (%d)", getpid(), errno);
214 			(void)runtime_close(cfd);
215 			return;
216 		}
217 
218 		if (euid != geteuid()) {
219 			launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** launchd[%d] failed to euid check on incoming caller (%d != %d)", getpid(), euid, geteuid());
220 			(void)runtime_close(cfd);
221 			return;
222 		}
223 	}
224 
225 	ipc_open(cfd, NULL);
226 }
227 
228 void
ipc_callback(void * obj,struct kevent * kev)229 ipc_callback(void *obj, struct kevent *kev)
230 {
231 	struct conncb *c = obj;
232 	int r;
233 
234 	if (kev->filter == EVFILT_READ) {
235 		if (launchd_msg_recv(c->conn, ipc_readmsg, c) == -1 && errno != EAGAIN) {
236 			if (errno != ECONNRESET) {
237 				launchd_syslog(LOG_DEBUG, "%s(): recv: %s", __func__, strerror(errno));
238 			}
239 			ipc_close(c);
240 		}
241 	} else if (kev->filter == EVFILT_WRITE) {
242 		r = launchd_msg_send(c->conn, NULL);
243 		if (r == -1) {
244 			if (errno != EAGAIN) {
245 				launchd_syslog(LOG_DEBUG, "%s(): send: %s", __func__, strerror(errno));
246 				ipc_close(c);
247 			}
248 		} else if (r == 0) {
249 			kevent_mod(launchd_getfd(c->conn), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
250 		}
251 	} else {
252 		launchd_syslog(LOG_DEBUG, "%s(): unknown filter type!", __func__);
253 		ipc_close(c);
254 	}
255 }
256 
257 static void
set_user_env(launch_data_t obj,const char * key,void * context)258 set_user_env(launch_data_t obj, const char *key, void *context __attribute__((unused)))
259 {
260 	const char *v = launch_data_get_string(obj);
261 	if (v) {
262 		setenv(key, v, 1);
263 	} else {
264 		launchd_syslog(LOG_WARNING, "Attempt to set NULL environment variable: %s (type = %d)", key, launch_data_get_type(obj));
265 	}
266 }
267 
268 void
ipc_close_all_with_job(job_t j)269 ipc_close_all_with_job(job_t j)
270 {
271 	struct conncb *ci, *cin;
272 
273 	LIST_FOREACH_SAFE(ci, &connections, sle, cin) {
274 		if (ci->j == j) {
275 			ipc_close(ci);
276 		}
277 	}
278 }
279 
280 void
ipc_close_fds(launch_data_t o)281 ipc_close_fds(launch_data_t o)
282 {
283 	size_t i;
284 
285 	switch (launch_data_get_type(o)) {
286 	case LAUNCH_DATA_DICTIONARY:
287 		launch_data_dict_iterate(o, (void (*)(launch_data_t, const char *, void *))ipc_close_fds, NULL);
288 		break;
289 	case LAUNCH_DATA_ARRAY:
290 		for (i = 0; i < launch_data_array_get_count(o); i++)
291 			ipc_close_fds(launch_data_array_get_index(o, i));
292 		break;
293 	case LAUNCH_DATA_FD:
294 		if (launch_data_get_fd(o) != -1) {
295 			(void)runtime_close(launch_data_get_fd(o));
296 		}
297 		break;
298 	default:
299 		break;
300 	}
301 }
302 
303 void
ipc_revoke_fds(launch_data_t o)304 ipc_revoke_fds(launch_data_t o)
305 {
306 	size_t i;
307 
308 	switch (launch_data_get_type(o)) {
309 	case LAUNCH_DATA_DICTIONARY:
310 		launch_data_dict_iterate(o, (void (*)(launch_data_t, const char *, void *))ipc_revoke_fds, NULL);
311 		break;
312 	case LAUNCH_DATA_ARRAY:
313 		for (i = 0; i < launch_data_array_get_count(o); i++)
314 			ipc_revoke_fds(launch_data_array_get_index(o, i));
315 		break;
316 	case LAUNCH_DATA_FD:
317 		launch_data_set_fd(o, -1);
318 		break;
319 	default:
320 		break;
321 	}
322 }
323 
324 struct readmsg_context {
325 	struct conncb *c;
326 	launch_data_t resp;
327 };
328 
329 void
ipc_readmsg(launch_data_t msg,void * context)330 ipc_readmsg(launch_data_t msg, void *context)
331 {
332 	struct readmsg_context rmc = { context, NULL };
333 
334 	if (LAUNCH_DATA_DICTIONARY == launch_data_get_type(msg)) {
335 		launch_data_dict_iterate(msg, ipc_readmsg2, &rmc);
336 	} else if (LAUNCH_DATA_STRING == launch_data_get_type(msg)) {
337 		ipc_readmsg2(NULL, launch_data_get_string(msg), &rmc);
338 	} else {
339 		rmc.resp = launch_data_new_errno(EINVAL);
340 	}
341 
342 	if (NULL == rmc.resp) {
343 		rmc.resp = launch_data_new_errno(ENOSYS);
344 	}
345 
346 	ipc_close_fds(msg);
347 
348 	if (launchd_msg_send(rmc.c->conn, rmc.resp) == -1) {
349 		if (errno == EAGAIN) {
350 			kevent_mod(launchd_getfd(rmc.c->conn), EVFILT_WRITE, EV_ADD, 0, 0, &rmc.c->kqconn_callback);
351 		} else {
352 			launchd_syslog(LOG_DEBUG, "launchd_msg_send() == -1: %s", strerror(errno));
353 			ipc_close(rmc.c);
354 		}
355 	}
356 	launch_data_free(rmc.resp);
357 }
358 
359 void
ipc_readmsg2(launch_data_t data,const char * cmd,void * context)360 ipc_readmsg2(launch_data_t data, const char *cmd, void *context)
361 {
362 	struct readmsg_context *rmc = context;
363 	launch_data_t resp = NULL;
364 	job_t j;
365 
366 	if (rmc->resp) {
367 		return;
368 	}
369 
370 	/* Do not allow commands other than check-in to come over the trusted socket
371 	 * on the Desktop. On Embedded, allow all commands over the trusted socket
372 	 * if the job has the God Mode key set.
373 	 */
374 #if TARGET_OS_EMBEDDED
375 	bool allow_privileged_ops = (!rmc->c->j || job_is_god(rmc->c->j));
376 #else
377 	bool allow_privileged_ops = !rmc->c->j;
378 #endif
379 
380 	if (rmc->c->j && strcmp(cmd, LAUNCH_KEY_CHECKIN) == 0) {
381 		resp = job_export(rmc->c->j);
382 		job_checkin(rmc->c->j);
383 	} else if (allow_privileged_ops) {
384 #if TARGET_OS_EMBEDDED
385 		launchd_embedded_handofgod = rmc->c->j && job_is_god(rmc->c->j);
386 #endif
387 		if (data == NULL) {
388 			if (!strcmp(cmd, LAUNCH_KEY_SHUTDOWN)) {
389 				launchd_shutdown();
390 				resp = launch_data_new_errno(0);
391 			} else if (!strcmp(cmd, LAUNCH_KEY_GETJOBS)) {
392 				resp = job_export_all();
393 				ipc_revoke_fds(resp);
394 			} else if (!strcmp(cmd, LAUNCH_KEY_GETRESOURCELIMITS)) {
395 				resp = adjust_rlimits(NULL);
396 			} else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGESELF)) {
397 				struct rusage rusage;
398 				getrusage(RUSAGE_SELF, &rusage);
399 				resp = launch_data_new_opaque(&rusage, sizeof(rusage));
400 			} else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGECHILDREN)) {
401 				struct rusage rusage;
402 				getrusage(RUSAGE_CHILDREN, &rusage);
403 				resp = launch_data_new_opaque(&rusage, sizeof(rusage));
404 			}
405 		} else {
406 			if (!strcmp(cmd, LAUNCH_KEY_STARTJOB)) {
407 				if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
408 					errno = job_dispatch(j, true) ? 0 : errno;
409 				}
410 				resp = launch_data_new_errno(errno);
411 			} else if (!strcmp(cmd, LAUNCH_KEY_STOPJOB)) {
412 				if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
413 					errno = 0;
414 					job_stop(j);
415 				}
416 				resp = launch_data_new_errno(errno);
417 			} else if (!strcmp(cmd, LAUNCH_KEY_REMOVEJOB)) {
418 				if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
419 					errno = 0;
420 					job_remove(j);
421 				}
422 				resp = launch_data_new_errno(errno);
423 			} else if (!strcmp(cmd, LAUNCH_KEY_SUBMITJOB)) {
424 				if (launch_data_get_type(data) == LAUNCH_DATA_ARRAY) {
425 					resp = job_import_bulk(data);
426 				} else {
427 					if (job_import(data)) {
428 						errno = 0;
429 					}
430 					resp = launch_data_new_errno(errno);
431 				}
432 			} else if (!strcmp(cmd, LAUNCH_KEY_UNSETUSERENVIRONMENT)) {
433 				unsetenv(launch_data_get_string(data));
434 				resp = launch_data_new_errno(0);
435 			} else if (!strcmp(cmd, LAUNCH_KEY_SETUSERENVIRONMENT)) {
436 				launch_data_dict_iterate(data, set_user_env, NULL);
437 				resp = launch_data_new_errno(0);
438 			} else if (!strcmp(cmd, LAUNCH_KEY_SETRESOURCELIMITS)) {
439 				resp = adjust_rlimits(data);
440 			} else if (!strcmp(cmd, LAUNCH_KEY_GETJOB)) {
441 				if ((j = job_find(NULL, launch_data_get_string(data))) == NULL) {
442 					resp = launch_data_new_errno(errno);
443 				} else {
444 					resp = job_export(j);
445 					ipc_revoke_fds(resp);
446 				}
447 			}
448 		}
449 #if TARGET_OS_EMBEDDED
450 		launchd_embedded_handofgod = false;
451 #endif
452 	} else {
453 		resp = launch_data_new_errno(EACCES);
454 	}
455 
456 	rmc->resp = resp;
457 }
458 
459 static int
close_abi_fixup(int fd)460 close_abi_fixup(int fd)
461 {
462 	return runtime_close(fd);
463 }
464 
465 void
ipc_close(struct conncb * c)466 ipc_close(struct conncb *c)
467 {
468 	LIST_REMOVE(c, sle);
469 	launchd_close(c->conn, close_abi_fixup);
470 	free(c);
471 }
472 
473 launch_data_t
adjust_rlimits(launch_data_t in)474 adjust_rlimits(launch_data_t in)
475 {
476 	/* If I never have to deal with this rlimit nonsense again, I'll be a very
477 	 * happy man.
478 	 */
479 	struct rlimit l[RLIM_NLIMITS];
480 	struct rlimit *ltmp;
481 	size_t i,ltmpsz;
482 
483 	for (i = 0; i < RLIM_NLIMITS; i++) {
484 		(void)posix_assumes_zero(getrlimit(i, l + i));
485 	}
486 
487 	if (in) {
488 		ltmp = launch_data_get_opaque(in);
489 		ltmpsz = launch_data_get_opaque_size(in);
490 
491 		if (ltmpsz > sizeof(l)) {
492 			launchd_syslog(LOG_WARNING, "Too much rlimit data sent!");
493 			ltmpsz = sizeof(l);
494 		}
495 
496 		for (i = 0; i < (ltmpsz / sizeof(struct rlimit)); i++) {
497 			if (ltmp[i].rlim_cur == l[i].rlim_cur && ltmp[i].rlim_max == l[i].rlim_max) {
498 				continue;
499 			}
500 
501 			if (/* XXX readcfg_pid && */ pid1_magic && (i == RLIMIT_NOFILE || i == RLIMIT_NPROC)) {
502 				int gmib[] = { CTL_KERN, KERN_MAXPROC };
503 				int pmib[] = { CTL_KERN, KERN_MAXPROCPERUID };
504 				const char *gstr = "kern.maxproc";
505 				const char *pstr = "kern.maxprocperuid";
506 				int gval = ltmp[i].rlim_max;
507 				int pval = ltmp[i].rlim_cur;
508 				switch (i) {
509 				case RLIMIT_NOFILE:
510 					gmib[1] = KERN_MAXFILES;
511 					pmib[1] = KERN_MAXFILESPERPROC;
512 					gstr = "kern.maxfiles";
513 					pstr = "kern.maxfilesperproc";
514 					break;
515 				default:
516 					break;
517 				}
518 
519 				if (gval > 0) {
520 					(void)posix_assumes_zero(sysctl(gmib, 2, NULL, NULL, &gval, sizeof(gval)));
521 				} else {
522 					launchd_syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", gstr);
523 				}
524 				if (pval > 0) {
525 					(void)posix_assumes_zero(sysctl(pmib, 2, NULL, NULL, &pval, sizeof(pval)));
526 				} else {
527 					launchd_syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", pstr);
528 				}
529 			}
530 			(void)posix_assumes_zero(setrlimit(i, ltmp + i));
531 			/* the kernel may have clamped the values we gave it */
532 			(void)posix_assumes_zero(getrlimit(i, l + i));
533 		}
534 	}
535 
536 	return launch_data_new_opaque(l, sizeof(struct rlimit) * RLIM_NLIMITS);
537 }
538