file_cmds-321.100.10.0.1.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 // mflag is passed during manifest comparision while xflag is used to generate the specification
109 if (mflag || xflag) {
110 ai = get_xdstream_privateid(path, buf);
111 } else {
112 ai = calculate_SHA256_XATTRs(path, buf);
113 }
114
115 return ai;
116 }
117
118
119 xattr_info *
120 calculate_SHA256_XATTRs(char *path, char *buf)
121 {
122 errno_t error = 0;
123 char *xattrsSummary = NULL;
124 int options = XATTR_SHOWCOMPRESSION | XATTR_NOFOLLOW;
125 ssize_t nameBufSize = listxattr(path, NULL, 0, options);
126 uint64_t xd_obj_id = 0;
127 if (nameBufSize > 0) {
128 char *nameBuf = malloc(nameBufSize);
129
130 listxattr(path, nameBuf, nameBufSize, options);
131
132 size_t xattrsLen = 1;
133 size_t xattrIndex = 0;
134 char **xattrs = malloc(xattrsLen * sizeof(char *));
135 char *nextName = nameBuf;
136 while (nextName < nameBuf + nameBufSize)
137 {
138 char *name = nextName;
139 if (xattrIndex == xattrsLen) {
140 xattrsLen *= 2;
141 xattrs = realloc(xattrs, xattrsLen * sizeof(char *));
142 }
143 xattrs[xattrIndex++] = name;
144 nextName += strlen(name) + 1;
145 }
146
147 // sort the xattr array as they're not guaranteed to come in the same order
148 qsort_b(xattrs, xattrIndex, sizeof(char *), ^(const void *l, const void *r) {
149 char *left = *(char **)l;
150 char *right = *(char **)r;
151 return strcmp(left, right);
152 });
153
154 // gather the data for the xattrs
155 bool didAddXATTR = false;
156 int xattrBufLen = kSHA256NullTerminatedBuffLen;
157 void *xattrBuf = malloc(xattrBufLen); // resized if necessary
158 char *digest;
159 ssize_t result = 0;
160 char *oldSummary = NULL;
161 //XXX Make xattr_info an array of structs if necessary
162 xattr_info *ai = (xattr_info *) malloc(sizeof(xattr_info));
163 for (int i = 0; i < xattrIndex; i++) {
164 char *name = xattrs[i];
165 ssize_t xlen = getxattr(path, name, NULL, 0, 0, options);
166 if (xlen > xattrBufLen) {
167 xattrBufLen = xlen;
168 xattrBuf = realloc(xattrBuf, xattrBufLen);
169 }
170 bzero(xattrBuf, xattrBufLen);
171 result = getxattr(path, name, xattrBuf, xattrBufLen, 0, options);
172 if (result < 0) {
173 error = errno;
174 RECORD_FAILURE(27442, error);
175 errc(1, error, "SHA256_Path_XATTRs getxattr of \"%s\" at path \"%s\" failed with error", name, path);
176 }
177
178 digest = SHA256_Data(xattrBuf, xattrBufLen, buf);
179 if (!digest)
180 err(1, "%s", xattrsSummary);
181 if (!didAddXATTR)
182 {
183 didAddXATTR = true;
184 asprintf(&xattrsSummary, "%s:%s", name, digest);
185 } else {
186 oldSummary = xattrsSummary;
187 asprintf(&xattrsSummary, "%s, %s:%s", oldSummary, name, digest);
188 free(oldSummary);
189 }
190 #ifdef APFSIOC_XDSTREAM_OBJ_ID
191 // System volume has stream based xattrs only in form of resource forks
192 if (!strncmp(name, XATTR_RESOURCEFORK_NAME, XATTR_MAXNAMELEN)) {
193 struct xdstream_obj_id x_obj;
194 x_obj.xdi_name = name;
195 x_obj.xdi_xdtream_obj_id = 0;
196
197 result = fsctl(path, APFSIOC_XDSTREAM_OBJ_ID, &x_obj, 0);
198 if (!result) {
199 xd_obj_id = x_obj.xdi_xdtream_obj_id;
200 } else if (errno == ENOTTY) {
201 // Not an apfs filesystem, return zero.
202 xd_obj_id = 0;
203 } else {
204 error = errno;
205 RECORD_FAILURE(27444, error);
206 errc(1, error, "%s - SHA256_Path_XATTRs APFSIOC_XDSTREAM_OBJ_ID failed with %d", path, error);
207 }
208 }
209 #endif
210 ai->xdstream_priv_id = xd_obj_id;
211 }
212
213 free(xattrBuf);
214 free(nameBuf);
215 free(xattrs);
216
217 digest = SHA256_Data(xattrsSummary, strlen(xattrsSummary) * sizeof(char), buf);
218 if (!digest)
219 err(1, "%s", xattrsSummary);
220
221 ai->digest = digest;
222
223 free(xattrsSummary);
224 return ai;
225 }
226 return NULL;
227 }
228
229 xattr_info *
230 get_xdstream_privateid(char *path, char *buf) {
231 errno_t error = 0;
232 int options = XATTR_SHOWCOMPRESSION | XATTR_NOFOLLOW;
233 ssize_t nameBufSize = listxattr(path, NULL, 0, options);
234 uint64_t xd_obj_id = 0;
235
236 if (nameBufSize > 0) {
237 //XXX Make xattr_info an array of structs if necessary
238 xattr_info *ai = (xattr_info *) malloc(sizeof(xattr_info));
239 char *nameBuf = malloc(nameBufSize);
240 int result = 0;
241
242 listxattr(path, nameBuf, nameBufSize, options);
243
244 size_t xattrsLen = 1;
245 size_t xattrIndex = 0;
246 char **xattrs = malloc(xattrsLen * sizeof(char *));
247 char *nextName = nameBuf;
248 while (nextName < nameBuf + nameBufSize)
249 {
250 char *name = nextName;
251 if (xattrIndex == xattrsLen) {
252 xattrsLen *= 2;
253 xattrs = realloc(xattrs, xattrsLen * sizeof(char *));
254 }
255 xattrs[xattrIndex++] = name;
256 nextName += strlen(name) + 1;
257 }
258
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
281 ai->xdstream_priv_id = xd_obj_id;
282 // insert a dummy value as digest is not used in presence of mflag
283 ai->digest = "authapfs";
284
285 free(nameBuf);
286 free(xattrs);
287 return ai;
288 }
289
290 return NULL;
291 }
292
293 char *SHA256_Path_ACL(char *path, char *buf)
294 {
295 errno_t error = 0;
296 int result = 0;
297 char *data = NULL;
298 char *digest = NULL;
299
300 struct attrlist list = {
301 .bitmapcount = ATTR_BIT_MAP_COUNT,
302 .commonattr = ATTR_CMN_RETURNED_ATTRS | ATTR_CMN_EXTENDED_SECURITY,
303 };
304
305 struct ACLBuf {
306 uint32_t len;
307 attribute_set_t returned_attrs;
308 attrreference_t acl;
309 char buf[8192]; // current acls are up to 3116 bytes, but they may increase in the future
310 } __attribute__((aligned(4), packed));
311
312 struct ACLBuf aclBuf;
313
314 result = getattrlist(path, &list, &aclBuf, sizeof(aclBuf), FSOPT_NOFOLLOW);
315
316 if (result) {
317 error = errno;
318 RECORD_FAILURE(27445, error);
319 errc(1, error, "SHA256_Path_ACL: getattrlist");
320 }
321
322 // if the path does not have an acl, return none
323 if ( ( ! ( aclBuf.returned_attrs.commonattr & ATTR_CMN_EXTENDED_SECURITY ) )
324 || ( aclBuf.acl.attr_length == 0 ) ) {
325 return kNone;
326 }
327
328 data = ((char*)&aclBuf.acl) + aclBuf.acl.attr_dataoffset;
329
330 digest = SHA256_Data(data, aclBuf.acl.attr_length, buf);
331 if (!digest)
332 err(1, "SHA256_Path_ACL: SHA256_Data");
333
334 return digest;
335 }
336
337 /* Functions for Digest_Path_* */
338 char *
339 Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf) {
340
341 uint8_t digest[32]; // SHA256 is the biggest
342 CCDigestCtx ctx;
343 size_t i, length;
344
345 (void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
346 (void)os_assumes_zero(CCDigestUpdate(&ctx, data, size));
347
348 /* Finalize and convert to hex. */
349 (void)os_assumes_zero(CCDigestFinal(&ctx, digest));
350 length = CCDigestOutputSize(&ctx);
351 os_assert(length <= sizeof(digest));
352 for (i = 0; i < length; i++) {
353 buf[i+i] = hex[digest[i] >> 4];
354 buf[i+i+1] = hex[digest[i] & 0x0f];
355 }
356 buf[i+i] = '\0';
357
358 return buf;
359 }
360
361 uint64_t
362 get_sibling_id(const char *path)
363 {
364 struct attrlist attr_list = {0};
365 struct attrbuf attr_buf = {0};
366 errno_t error = 0;
367
368 attr_list.bitmapcount = ATTR_BIT_MAP_COUNT;
369 attr_list.forkattr = ATTR_CMNEXT_LINKID;
370
371 error = getattrlist(path, &attr_list, &attr_buf, sizeof(attr_buf), FSOPT_ATTR_CMN_EXTENDED | FSOPT_NOFOLLOW);
372 if (error) {
373 error = errno;
374 RECORD_FAILURE(27447, error);
375 errc(1, error, "get_sibling_id: getattrlist failed for %s\n", path);
376 }
377
378 return attr_buf.sibling_id;
379 }