]> git.cameronkatri.com Git - apple_cmds.git/blob - file_cmds/mtree/commoncrypto.c
Merge branch 'apple'
[apple_cmds.git] / file_cmds / mtree / commoncrypto.c
1 #include <dispatch/dispatch.h>
2 #include <os/assumes.h>
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <string.h>
6 #include <sys/xattr.h>
7 #include <stdbool.h>
8 #include <err.h>
9 #include <errno.h>
10 #include <stdio.h>
11 #include <sys/attr.h>
12 #include <unistd.h>
13 #include <sys/xattr.h>
14 #include <sys/mount.h>
15 //#include <apfs/apfs_fsctl.h>
16
17 #include "commoncrypto.h"
18 #include "extern.h"
19 #include "metrics.h"
20
21 const int kSHA256NullTerminatedBuffLen = 65;
22 static const char hex[] = "0123456789abcdef";
23
24 /* Functions for SHA256_File_XATTRs */
25 #define SHA256_Data(d, s, b) Digest_Data(kCCDigestSHA256, d, s, b)
26 char *Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf);
27 void Quicksort(char **array, int num);
28
29 /* Generic version of libmd's *_File() functions. */
30 char *
31 Digest_File(CCDigestAlg algorithm, const char *filename, char *buf)
32 {
33 int fd;
34 __block CCDigestCtx ctx;
35 dispatch_queue_t queue;
36 dispatch_semaphore_t sema;
37 dispatch_io_t io;
38 __block int s_error = 0;
39 uint8_t digest[32]; // SHA256 is the biggest
40 size_t i, length;
41
42 /* dispatch_io_create_with_path requires an absolute path */
43 fd = open(filename, O_RDONLY);
44 if (fd < 0) {
45 return NULL;
46 }
47
48 (void)fcntl(fd, F_NOCACHE, 1);
49
50 (void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
51
52 queue = dispatch_queue_create("com.apple.mtree.io", NULL);
53 os_assert(queue);
54 sema = dispatch_semaphore_create(0);
55 os_assert(sema);
56
57 io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) {
58 if (error != 0) {
59 s_error = error;
60 RECORD_FAILURE(27440, s_error);
61 }
62 (void)close(fd);
63 (void)dispatch_semaphore_signal(sema);
64 });
65 os_assert(io);
66 dispatch_io_read(io, 0, SIZE_MAX, queue, ^(__unused bool done, dispatch_data_t data, int error) {
67 if (data != NULL) {
68 (void)dispatch_data_apply(data, ^(__unused dispatch_data_t region, __unused size_t offset, const void *buffer, size_t size) {
69 (void)os_assumes_zero(CCDigestUpdate(&ctx, buffer, size));
70 return (bool)true;
71 });
72 }
73
74 if (error != 0) {
75 s_error = error;
76 RECORD_FAILURE(27441, s_error);
77 }
78 });
79 dispatch_release(io); // it will close on its own
80
81 (void)dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
82
83 dispatch_release(queue);
84 dispatch_release(sema);
85
86 if (s_error != 0) {
87 errno = s_error;
88 return NULL;
89 }
90
91 /* Finalize and convert to hex. */
92 (void)os_assumes_zero(CCDigestFinal(&ctx, digest));
93 length = CCDigestOutputSize(&ctx);
94 os_assert(length <= sizeof(digest));
95 for (i = 0; i < length; i++) {
96 buf[i+i] = hex[digest[i] >> 4];
97 buf[i+i+1] = hex[digest[i] & 0x0f];
98 }
99 buf[i+i] = '\0';
100
101 return buf;
102 }
103
104 xattr_info *
105 SHA256_Path_XATTRs(char *path, char *buf) {
106 xattr_info *ai = NULL;
107
108 if (mflag) {
109 ai = get_xdstream_privateid(path, buf);
110 } else {
111 ai = calculate_SHA256_XATTRs(path, buf);
112 }
113
114 return ai;
115 }
116
117
118 xattr_info *
119 calculate_SHA256_XATTRs(char *path, char *buf)
120 {
121 errno_t error = 0;
122 char *xattrsSummary = NULL;
123 int options = XATTR_SHOWCOMPRESSION | XATTR_NOFOLLOW;
124 ssize_t nameBufSize = listxattr(path, NULL, 0, options);
125 uint64_t xd_obj_id = 0;
126 if (nameBufSize > 0) {
127 char *nameBuf = malloc(nameBufSize);
128
129 listxattr(path, nameBuf, nameBufSize, options);
130
131 size_t xattrsLen = 1;
132 size_t xattrIndex = 0;
133 char **xattrs = malloc(xattrsLen * sizeof(char *));
134 char *nextName = nameBuf;
135 while (nextName < nameBuf + nameBufSize)
136 {
137 char *name = nextName;
138 if (xattrIndex == xattrsLen) {
139 xattrsLen *= 2;
140 xattrs = realloc(xattrs, xattrsLen * sizeof(char *));
141 }
142 xattrs[xattrIndex++] = name;
143 nextName += strlen(name) + 1;
144 }
145
146 // sort the xattr array as they're not guaranteed to come in the same order
147 qsort_b(xattrs, xattrIndex, sizeof(char *), ^(const void *l, const void *r) {
148 char *left = *(char **)l;
149 char *right = *(char **)r;
150 return strcmp(left, right);
151 });
152
153 // gather the data for the xattrs
154 bool didAddXATTR = false;
155 int xattrBufLen = kSHA256NullTerminatedBuffLen;
156 void *xattrBuf = malloc(xattrBufLen); // resized if necessary
157 char *digest;
158 ssize_t result = 0;
159 char *oldSummary = NULL;
160 //XXX Make xattr_info an array of structs if necessary
161 xattr_info *ai = (xattr_info *) malloc(sizeof(xattr_info));
162 for (int i = 0; i < xattrIndex; i++) {
163 char *name = xattrs[i];
164 ssize_t xlen = getxattr(path, name, NULL, 0, 0, options);
165 if (xlen > xattrBufLen) {
166 xattrBufLen = xlen;
167 xattrBuf = realloc(xattrBuf, xattrBufLen);
168 }
169 bzero(xattrBuf, xattrBufLen);
170 result = getxattr(path, name, xattrBuf, xattrBufLen, 0, options);
171 if (result < 0) {
172 error = errno;
173 RECORD_FAILURE(27442, error);
174 errc(1, error, "SHA256_Path_XATTRs getxattr of \"%s\" at path \"%s\" failed with error", name, path);
175 }
176
177 digest = SHA256_Data(xattrBuf, xattrBufLen, buf);
178 if (!digest)
179 err(1, "%s", xattrsSummary);
180 if (!didAddXATTR)
181 {
182 didAddXATTR = true;
183 asprintf(&xattrsSummary, "%s:%s", name, digest);
184 } else {
185 oldSummary = xattrsSummary;
186 asprintf(&xattrsSummary, "%s, %s:%s", oldSummary, name, digest);
187 free(oldSummary);
188 }
189 #ifdef APFSIOC_XDSTREAM_OBJ_ID
190 // System volume has stream based xattrs only in form of resource forks
191 if (!strncmp(name, XATTR_RESOURCEFORK_NAME, XATTR_MAXNAMELEN)) {
192 struct xdstream_obj_id x_obj;
193 x_obj.xdi_name = name;
194 x_obj.xdi_xdtream_obj_id = 0;
195
196 result = fsctl(path, APFSIOC_XDSTREAM_OBJ_ID, &x_obj, 0);
197 if (!result) {
198 xd_obj_id = x_obj.xdi_xdtream_obj_id;
199 } else if (errno == ENOTTY) {
200 // Not an apfs filesystem, return zero.
201 xd_obj_id = 0;
202 } else {
203 error = errno;
204 RECORD_FAILURE(27444, error);
205 errc(1, error, "%s - SHA256_Path_XATTRs APFSIOC_XDSTREAM_OBJ_ID failed with %d", path, error);
206 }
207 }
208 #endif
209 ai->xdstream_priv_id = xd_obj_id;
210 }
211
212 free(xattrBuf);
213 free(nameBuf);
214 free(xattrs);
215
216 digest = SHA256_Data(xattrsSummary, strlen(xattrsSummary) * sizeof(char), buf);
217 if (!digest)
218 err(1, "%s", xattrsSummary);
219
220 ai->digest = digest;
221
222 free(xattrsSummary);
223 return ai;
224 }
225 return NULL;
226 }
227
228 xattr_info *
229 get_xdstream_privateid(char *path, char *buf) {
230 errno_t error = 0;
231 int options = XATTR_SHOWCOMPRESSION | XATTR_NOFOLLOW;
232 ssize_t nameBufSize = listxattr(path, NULL, 0, options);
233 uint64_t xd_obj_id = 0;
234
235 if (nameBufSize > 0) {
236 //XXX Make xattr_info an array of structs if necessary
237 xattr_info *ai = (xattr_info *) malloc(sizeof(xattr_info));
238 char *nameBuf = malloc(nameBufSize);
239 int result = 0;
240
241 listxattr(path, nameBuf, nameBufSize, options);
242
243 size_t xattrsLen = 1;
244 size_t xattrIndex = 0;
245 char **xattrs = malloc(xattrsLen * sizeof(char *));
246 char *nextName = nameBuf;
247 while (nextName < nameBuf + nameBufSize)
248 {
249 char *name = nextName;
250 if (xattrIndex == xattrsLen) {
251 xattrsLen *= 2;
252 xattrs = realloc(xattrs, xattrsLen * sizeof(char *));
253 }
254 xattrs[xattrIndex++] = name;
255 nextName += strlen(name) + 1;
256 }
257
258 #ifdef APFSIOC_XDSTREAM_OBJ_ID
259 for (int i = 0; i < xattrIndex; i++) {
260 char *name = xattrs[i];
261 // System volume has stream based xattrs only in form of resource forks
262 if (!strncmp(name, XATTR_RESOURCEFORK_NAME, XATTR_MAXNAMELEN)) {
263 struct xdstream_obj_id x_obj;
264 x_obj.xdi_name = name;
265 x_obj.xdi_xdtream_obj_id = 0;
266
267 result = fsctl(path, APFSIOC_XDSTREAM_OBJ_ID, &x_obj, 0);
268 if (!result && x_obj.xdi_xdtream_obj_id != 0) {
269 xd_obj_id = x_obj.xdi_xdtream_obj_id;
270 } else if (errno == ENOTTY) {
271 // Not an apfs filesystem, return zero.
272 xd_obj_id = 0;
273 } else {
274 error = errno;
275 RECORD_FAILURE(29983, error);
276 errc(1, error, "%s - SHA256_Path_XATTRs APFSIOC_XDSTREAM_OBJ_ID failed with %d", path, error);
277 }
278 }
279 }
280 #endif
281
282 ai->xdstream_priv_id = xd_obj_id;
283 // insert a dummy value as digest is not used in presence of mflag
284 ai->digest = "authapfs";
285
286 free(nameBuf);
287 free(xattrs);
288 return ai;
289 }
290
291 return NULL;
292 }
293
294 char *SHA256_Path_ACL(char *path, char *buf)
295 {
296 errno_t error = 0;
297 int result = 0;
298 char *data = NULL;
299 char *digest = NULL;
300
301 struct attrlist list = {
302 .bitmapcount = ATTR_BIT_MAP_COUNT,
303 .commonattr = ATTR_CMN_RETURNED_ATTRS | ATTR_CMN_EXTENDED_SECURITY,
304 };
305
306 struct ACLBuf {
307 uint32_t len;
308 attribute_set_t returned_attrs;
309 attrreference_t acl;
310 char buf[8192]; // current acls are up to 3116 bytes, but they may increase in the future
311 } __attribute__((aligned(4), packed));
312
313 struct ACLBuf aclBuf;
314
315 result = getattrlist(path, &list, &aclBuf, sizeof(aclBuf), FSOPT_NOFOLLOW);
316
317 if (result) {
318 error = errno;
319 RECORD_FAILURE(27445, error);
320 errc(1, error, "SHA256_Path_ACL: getattrlist");
321 }
322
323 // if the path does not have an acl, return none
324 if ( ( ! ( aclBuf.returned_attrs.commonattr & ATTR_CMN_EXTENDED_SECURITY ) )
325 || ( aclBuf.acl.attr_length == 0 ) ) {
326 return kNone;
327 }
328
329 data = ((char*)&aclBuf.acl) + aclBuf.acl.attr_dataoffset;
330
331 digest = SHA256_Data(data, aclBuf.acl.attr_length, buf);
332 if (!digest)
333 err(1, "SHA256_Path_ACL: SHA256_Data");
334
335 return digest;
336 }
337
338 /* Functions for Digest_Path_* */
339 char *
340 Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf) {
341
342 uint8_t digest[32]; // SHA256 is the biggest
343 CCDigestCtx ctx;
344 size_t i, length;
345
346 (void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
347 (void)os_assumes_zero(CCDigestUpdate(&ctx, data, size));
348
349 /* Finalize and convert to hex. */
350 (void)os_assumes_zero(CCDigestFinal(&ctx, digest));
351 length = CCDigestOutputSize(&ctx);
352 os_assert(length <= sizeof(digest));
353 for (i = 0; i < length; i++) {
354 buf[i+i] = hex[digest[i] >> 4];
355 buf[i+i+1] = hex[digest[i] & 0x0f];
356 }
357 buf[i+i] = '\0';
358
359 return buf;
360 }
361
362 uint64_t
363 get_sibling_id(const char *path)
364 {
365 struct attrlist attr_list = {0};
366 struct attrbuf attr_buf = {0};
367 errno_t error = 0;
368
369 attr_list.bitmapcount = ATTR_BIT_MAP_COUNT;
370 attr_list.forkattr = ATTR_CMNEXT_LINKID;
371
372 error = getattrlist(path, &attr_list, &attr_buf, sizeof(attr_buf), FSOPT_ATTR_CMN_EXTENDED | FSOPT_NOFOLLOW);
373 if (error) {
374 error = errno;
375 RECORD_FAILURE(27447, error);
376 errc(1, error, "get_sibling_id: getattrlist failed for %s\n", path);
377 }
378
379 return attr_buf.sibling_id;
380 }