1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
/* This is a daemon contacted by all processes which can load extensions. It
* currently does the work of reading the plists in
* /Library/Substitute/DynamicLibraries in order to avoid loading objc/CF
* libraries into the target binary (unless actually required by loaded
* libraries). By itself this would not merit the overhead of a separate
* daemon, but in the future this will also coordinate hot loading and
* unloading, for which purpose I think a daemon would be a cleaner solution
* than task_for_pid'ing everything in sight. */
#define IB_LOG_NAME "substituted"
#include "darwin/mach-decls.h"
#include "ib-log.h"
#include "substituted-messages.h"
#include "substituted-plist-loader.h"
#include <mach/mach.h>
#include <stddef.h>
//#include <sys/types.h>
/* libbsm.h */
struct au_tid;
void audit_token_to_au32(audit_token_t, uid_t *, uid_t *, gid_t *, uid_t *,
gid_t *, pid_t *, pid_t *, struct au_tid *);
struct msgbuf {
mach_msg_header_t hdr;
union {
struct substituted_msg_body_hello hello;
} u;
mach_msg_audit_trailer_t trail_space;
};
static void handle_hello(struct msgbuf *buf, pid_t pid) {
if (buf->hdr.msgh_size < offsetof(struct msgbuf, u.hello.argv0)) {
ib_log("message too short");
return;
}
((char *) buf)[buf->hdr.msgh_size] = '\0'; /* overwrite trailer or whatever */
const char *name = buf->u.hello.argv0;
if (IB_VERBOSE)
ib_log("got hello from pid %d [%s]", pid, name);
int error = 0;
const void *bundle_list = NULL;
size_t bundle_list_size = 0;
if (buf->u.hello.proto_version != SUBSTITUTED_PROTO_VERSION) {
error = 1;
} else {
get_bundle_list(name, &bundle_list, &bundle_list_size);
}
struct {
mach_msg_header_t hdr;
struct substituted_msg_body_hello_resp b;
} resp;
resp.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0) |
MACH_MSGH_BITS_COMPLEX;
resp.hdr.msgh_size = sizeof(resp);
resp.hdr.msgh_remote_port = buf->hdr.msgh_remote_port;
resp.hdr.msgh_local_port = MACH_PORT_NULL;
resp.hdr.msgh_reserved = 0;
resp.hdr.msgh_id = SUBSTITUTED_MSG_HELLO_RESP;
resp.b.body.msgh_descriptor_count = 1;
mach_msg_ool_descriptor_t *ool = &resp.b.bundle_list_ool;
ool->pad1 = 0;
ool->type = MACH_MSG_OOL_DESCRIPTOR;
ool->deallocate = 0;
ool->copy = MACH_MSG_PHYSICAL_COPY;
ool->address = (void *) bundle_list;
ool->size = (mach_msg_size_t) bundle_list_size;
resp.b.error = error;
kern_return_t kr = mach_msg(&resp.hdr, MACH_SEND_MSG, sizeof(resp), 0,
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (kr)
ib_log("mach_msg(hello resp) -> %x", kr);
else /* don't re-destroy the moved right */
buf->hdr.msgh_remote_port = MACH_PORT_NULL;
}
int main() {
mach_port_t service;
kern_return_t kr = bootstrap_check_in(bootstrap_port,
"com.ex.substituted",
&service);
while (1) {
struct msgbuf buf;
mach_msg_option_t option =
MACH_RCV_MSG |
MACH_RCV_TRAILER_TYPE(MACH_RCV_TRAILER_AUDIT) |
MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
kr = mach_msg(&buf.hdr, option, 0, sizeof(buf), service,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
switch (kr) {
case MACH_MSG_SUCCESS:
break;
case MACH_RCV_BODY_ERROR:
mach_msg_destroy(&buf.hdr);
/* fallthrough */
case MACH_RCV_TOO_LARGE:
case MACH_RCV_HEADER_ERROR:
case MACH_RCV_SCATTER_SMALL:
ib_log("mach_msg(rcv) -> %x", kr);
continue;
default:
ib_log("mach_msg(rcv) -> %x (fatal)", kr);
return 1;
}
mach_msg_audit_trailer_t *real_trail = (void *) &buf.hdr +
round_msg(buf.hdr.msgh_size);
pid_t pid;
audit_token_to_au32(real_trail->msgh_audit, NULL, NULL, NULL, NULL,
NULL, &pid, NULL, NULL);
switch (buf.hdr.msgh_id) {
case SUBSTITUTED_MSG_HELLO:
handle_hello(&buf, pid);
break;
default:
ib_log("unknown message %d", buf.hdr.msgh_id);
break;
}
mach_msg_destroy(&buf.hdr);
}
}
|