Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/openlaunchd
Path: blob/master/launchd/ipc.c
374 views
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/wait.h>
33
#include <sys/sysctl.h>
34
#include <sys/sockio.h>
35
#include <sys/time.h>
36
#include <sys/resource.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
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
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
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
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
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
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
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
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
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
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
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
460
close_abi_fixup(int fd)
461
{
462
return runtime_close(fd);
463
}
464
465
void
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
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
539