file_cmds-321.40.3.tar.gz
[apple/file_cmds.git] / 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 for (int i = 0; i < xattrIndex; i++) {
259 char *name = xattrs[i];
260 // System volume has stream based xattrs only in form of resource forks
261 if (!strncmp(name, XATTR_RESOURCEFORK_NAME, XATTR_MAXNAMELEN)) {
262 struct xdstream_obj_id x_obj;
263 x_obj.xdi_name = name;
264 x_obj.xdi_xdtream_obj_id = 0;
265
266 result = fsctl(path, APFSIOC_XDSTREAM_OBJ_ID, &x_obj, 0);
267 if (!result && x_obj.xdi_xdtream_obj_id != 0) {
268 xd_obj_id = x_obj.xdi_xdtream_obj_id;
269 } else if (errno == ENOTTY) {
270 // Not an apfs filesystem, return zero.
271 xd_obj_id = 0;
272 } else {
273 error = errno;
274 RECORD_FAILURE(29983, error);
275 errc(1, error, "%s - SHA256_Path_XATTRs APFSIOC_XDSTREAM_OBJ_ID failed with %d", path, error);
276 }
277 }
278 }
279
280 ai->xdstream_priv_id = xd_obj_id;
281 // insert a dummy value as digest is not used in presence of mflag
282 ai->digest = "authapfs";
283
284 free(nameBuf);
285 free(xattrs);
286 return ai;
287 }
288
289 return NULL;
290 }
291
292 char *SHA256_Path_ACL(char *path, char *buf)
293 {
294 errno_t error = 0;
295 int result = 0;
296 char *data = NULL;
297 char *digest = NULL;
298
299 struct attrlist list = {
300 .bitmapcount = ATTR_BIT_MAP_COUNT,
301 .commonattr = ATTR_CMN_RETURNED_ATTRS | ATTR_CMN_EXTENDED_SECURITY,
302 };
303
304 struct ACLBuf {
305 uint32_t len;
306 attribute_set_t returned_attrs;
307 attrreference_t acl;
308 char buf[8192]; // current acls are up to 3116 bytes, but they may increase in the future
309 } __attribute__((aligned(4), packed));
310
311 struct ACLBuf aclBuf;
312
313 result = getattrlist(path, &list, &aclBuf, sizeof(aclBuf), FSOPT_NOFOLLOW);
314
315 if (result) {
316 error = errno;
317 RECORD_FAILURE(27445, error);
318 errc(1, error, "SHA256_Path_ACL: getattrlist");
319 }
320
321 // if the path does not have an acl, return none
322 if ( ( ! ( aclBuf.returned_attrs.commonattr & ATTR_CMN_EXTENDED_SECURITY ) )
323 || ( aclBuf.acl.attr_length == 0 ) ) {
324 return kNone;
325 }
326
327 data = ((char*)&aclBuf.acl) + aclBuf.acl.attr_dataoffset;
328
329 digest = SHA256_Data(data, aclBuf.acl.attr_length, buf);
330 if (!digest)
331 err(1, "SHA256_Path_ACL: SHA256_Data");
332
333 return digest;
334 }
335
336 /* Functions for Digest_Path_* */
337 char *
338 Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf) {
339
340 uint8_t digest[32]; // SHA256 is the biggest
341 CCDigestCtx ctx;
342 size_t i, length;
343
344 (void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
345 (void)os_assumes_zero(CCDigestUpdate(&ctx, data, size));
346
347 /* Finalize and convert to hex. */
348 (void)os_assumes_zero(CCDigestFinal(&ctx, digest));
349 length = CCDigestOutputSize(&ctx);
350 os_assert(length <= sizeof(digest));
351 for (i = 0; i < length; i++) {
352 buf[i+i] = hex[digest[i] >> 4];
353 buf[i+i+1] = hex[digest[i] & 0x0f];
354 }
355 buf[i+i] = '\0';
356
357 return buf;
358 }
359
360 uint64_t
361 get_sibling_id(const char *path)
362 {
363 struct attrlist attr_list = {0};
364 struct attrbuf attr_buf = {0};
365 errno_t error = 0;
366
367 attr_list.bitmapcount = ATTR_BIT_MAP_COUNT;
368 attr_list.forkattr = ATTR_CMNEXT_LINKID;
369
370 error = getattrlist(path, &attr_list, &attr_buf, sizeof(attr_buf), FSOPT_ATTR_CMN_EXTENDED | FSOPT_NOFOLLOW);
371 if (error) {
372 error = errno;
373 RECORD_FAILURE(27447, error);
374 errc(1, error, "get_sibling_id: getattrlist failed for %s\n", path);
375 }
376
377 return attr_buf.sibling_id;
378 }