1 #include <darwintest.h>
2 #include <stdint.h>
3 #include <bsm/libbsm.h>
4 #include <System/sys/codesign.h>
5 #include <sys/errno.h>
6 #include <stdio.h>
7 #include <unistd.h>
8
9 #define MAXBUFLEN 1024 * 1024
10
11 T_GLOBAL_META(
12 T_META_NAMESPACE("xnu.codesigning"),
13 T_META_RADAR_COMPONENT_NAME("xnu"),
14 T_META_RADAR_COMPONENT_VERSION("codesigning"),
15 T_META_OWNER("rkendallkuppe"));
16
17 int
get_blob(pid_t pid,int op)18 get_blob(pid_t pid, int op)
19 {
20 uint8_t header[8];
21 unsigned int cnt;
22 int rcent;
23
24 for (cnt = 0; cnt < sizeof(header); cnt++) {
25 rcent = csops(pid, op, header, cnt);
26 if (rcent != -1 && errno != ERANGE) {
27 T_ASSERT_FAIL("errno != ERANGE for short header");
28 }
29 }
30
31 rcent = csops(pid, op, header, sizeof(header));
32 if (rcent == -1 && errno == ERANGE) {
33 uint32_t len, bufferlen, bufferlen2;
34
35 memcpy(&len, &header[4], 4);
36 bufferlen = ntohl(len);
37
38 T_LOG("Checking bufferlen on blob from kernel for csop %d", op);
39 T_ASSERT_LE_INT(bufferlen, MAXBUFLEN, "Buffer length %zu on blob from kernel can't exceed %zu",
40 (size_t)bufferlen, MAXBUFLEN);
41 T_ASSERT_NE_INT(bufferlen, 0, "Buffer length %zu on blob from kernel can't be zero",
42 (size_t)bufferlen);
43 T_ASSERT_GE_INT(bufferlen, 8, "Buffer length %zu on blob from kernel can't be less than a byte",
44 (size_t)bufferlen);
45
46 uint8_t buffer[bufferlen + 1];
47
48 rcent = csops(pid, op, buffer, bufferlen - 1);
49 T_ASSERT_POSIX_ERROR(errno, ERANGE, "Performing CS OPS csops with a full buffer - 1");
50
51 rcent = csops(pid, op, buffer, bufferlen);
52 T_ASSERT_EQ_INT(rcent, 0, "Performing CS OPS with full buffer.");
53
54 memcpy(&len, &buffer[4], 4);
55 bufferlen2 = ntohl(len);
56
57 if (op == CS_OPS_BLOB) {
58 T_ASSERT_LE_INT(bufferlen2, bufferlen, "Checking %zu is %zu larger on second try",
59 (size_t)bufferlen2, (size_t)bufferlen);
60
61 /*
62 * CS_OPS_BLOB may want a bigger buffer than the size
63 * of the actual blob. If the blob came in through a
64 * load command for example, then CS_OPS_BLOB will
65 * want to copy out the whole buffer that the load
66 * command points to, which is usually an estimated
67 * size. The actual blob, and therefore the size in
68 * the blob's header, may be smaller.
69 */
70 T_LOG("Blob is smaller (%zu) than expected (%zu). This is fine.",
71 (size_t)bufferlen2, (size_t)bufferlen);
72 } else {
73 T_ASSERT_EQ_INT(bufferlen2, bufferlen, "Checking bufferlen sizes are different");
74 }
75
76 rcent = csops(pid, op, buffer, bufferlen + 1);
77 T_ASSERT_EQ_INT(rcent, 0, "Performing CS OPS with a full buffer + 1");
78
79 return 0;
80 } else if (rcent == 0) {
81 return 0;
82 } else {
83 return 1;
84 }
85 }
86
87 /*
88 * This source is compiled with different names and build flags.
89 * Makefile has the detail of the TESTNAME.
90 */
91
92 T_DECL(TESTNAME, "CS OP, code sign operations test")
93 {
94 uint32_t status;
95 int rcent;
96 pid_t pid;
97
98 pid = getpid();
99
100
101 rcent = get_blob(pid, CS_OPS_ENTITLEMENTS_BLOB);
102 T_ASSERT_EQ_INT(rcent, 0, "Getting CS OPS entitlements blob");
103
104 rcent = get_blob(0, CS_OPS_ENTITLEMENTS_BLOB);
105 T_ASSERT_EQ_INT(rcent, 0, "Getting CS OPS entitlements blob");
106
107 rcent = get_blob(pid, CS_OPS_BLOB);
108
109 T_ASSERT_EQ_INT(rcent, 0, "Getting CS OPS blob");
110
111 rcent = get_blob(0, CS_OPS_BLOB);
112 T_ASSERT_EQ_INT(rcent, 0, "Getting CS OPS blob");
113
114 rcent = get_blob(pid, CS_OPS_IDENTITY);
115 T_ASSERT_EQ_INT(rcent, 0, "Getting CS OPs identity");
116
117 rcent = get_blob(0, CS_OPS_IDENTITY);
118 T_ASSERT_EQ_INT(rcent, 0, "Getting CS OPs identity");
119
120 rcent = csops(pid, CS_OPS_SET_STATUS, &status, sizeof(status) - 1);
121 T_ASSERT_NE_INT(rcent, 0, "Checking CS OPs set status by setting buffer to (status - 1)");
122
123 status = CS_RESTRICT;
124 rcent = csops(pid, CS_OPS_SET_STATUS, &status, sizeof(status));
125 T_ASSERT_EQ_INT(rcent, 0, "Checking CS OPs set status by setting proc RESTRICTED");
126
127 rcent = csops(pid, CS_OPS_STATUS, &status, sizeof(status));
128 T_ASSERT_EQ_INT(rcent, 0, "Getting CS OPs status of process");
129
130 /*
131 * Only run the folling tests if not HARD since otherwise
132 * we'll just die when marking ourself invalid.
133 */
134
135 if ((status & CS_KILL) == 0) {
136 rcent = csops(pid, CS_OPS_MARKINVALID, NULL, 0);
137 T_ASSERT_POSIX_ZERO(rcent, 0, "Setting CS OPs mark proc invalid");
138
139 status = CS_ENFORCEMENT;
140 rcent = csops(pid, CS_OPS_SET_STATUS, &status, sizeof(status));
141 T_ASSERT_POSIX_ERROR(rcent, -1, "Managed to set flags on an INVALID proc");
142
143 rcent = get_blob(pid, CS_OPS_ENTITLEMENTS_BLOB);
144 T_ASSERT_POSIX_ERROR(rcent, 1, "Got entitlements while invalid");
145
146 rcent = get_blob(pid, CS_OPS_IDENTITY);
147 T_ASSERT_POSIX_ERROR(rcent, 1, "Getting CS OPS identity");
148
149 rcent = get_blob(0, CS_OPS_IDENTITY);
150 T_ASSERT_POSIX_ERROR(rcent, 1, "Getting CS OPS identity");
151
152 rcent = get_blob(0, CS_OPS_BLOB);
153 T_ASSERT_POSIX_ERROR(rcent, 1, "Geting CS OPS blob");
154 }
155 }
156