file_cmds-264.1.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
14 #include "commoncrypto.h"
15
16 const int kSHA256NullTerminatedBuffLen = 65;
17 static const char hex[] = "0123456789abcdef";
18
19 /* Functions for SHA256_File_XATTRs */
20 #define SHA256_Data(d, s, b) Digest_Data(kCCDigestSHA256, d, s, b)
21 char *Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf);
22 void Quicksort(char **array, int num);
23
24 /* Generic version of libmd's *_File() functions. */
25 char *
26 Digest_File(CCDigestAlg algorithm, const char *filename, char *buf)
27 {
28 int fd;
29 __block CCDigestCtx ctx;
30 dispatch_queue_t queue;
31 dispatch_semaphore_t sema;
32 dispatch_io_t io;
33 __block int s_error = 0;
34 uint8_t digest[32]; // SHA256 is the biggest
35 size_t i, length;
36
37 /* dispatch_io_create_with_path requires an absolute path */
38 fd = open(filename, O_RDONLY);
39 if (fd < 0) {
40 return NULL;
41 }
42
43 (void)fcntl(fd, F_NOCACHE, 1);
44
45 (void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
46
47 queue = dispatch_queue_create("com.apple.mtree.io", NULL);
48 os_assert(queue);
49 sema = dispatch_semaphore_create(0);
50 os_assert(sema);
51
52 io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) {
53 if (error != 0) {
54 s_error = error;
55 }
56 (void)close(fd);
57 (void)dispatch_semaphore_signal(sema);
58 });
59 os_assert(io);
60 dispatch_io_read(io, 0, SIZE_MAX, queue, ^(__unused bool done, dispatch_data_t data, int error) {
61 if (data != NULL) {
62 (void)dispatch_data_apply(data, ^(__unused dispatch_data_t region, __unused size_t offset, const void *buffer, size_t size) {
63 (void)os_assumes_zero(CCDigestUpdate(&ctx, buffer, size));
64 return (bool)true;
65 });
66 }
67
68 if (error != 0) {
69 s_error = error;
70 }
71 });
72 dispatch_release(io); // it will close on its own
73
74 (void)dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
75
76 dispatch_release(queue);
77 dispatch_release(sema);
78
79 if (s_error != 0) {
80 errno = s_error;
81 return NULL;
82 }
83
84 /* Finalize and convert to hex. */
85 (void)os_assumes_zero(CCDigestFinal(&ctx, digest));
86 length = CCDigestOutputSize(&ctx);
87 os_assert(length <= sizeof(digest));
88 for (i = 0; i < length; i++) {
89 buf[i+i] = hex[digest[i] >> 4];
90 buf[i+i+1] = hex[digest[i] & 0x0f];
91 }
92 buf[i+i] = '\0';
93
94 return buf;
95 }
96
97 char *SHA256_Path_XATTRs(char *path, char *buf)
98 {
99 char *xattrsSummary = NULL;
100 int options = XATTR_SHOWCOMPRESSION | XATTR_NOFOLLOW;
101 ssize_t nameBufSize = listxattr(path, NULL, 0, options);
102 if (nameBufSize > 0) {
103 char *nameBuf = malloc(nameBufSize);
104
105 listxattr(path, nameBuf, nameBufSize, options);
106
107 size_t xattrsLen = 1;
108 size_t xattrIndex = 0;
109 char **xattrs = malloc(xattrsLen * sizeof(char *));
110 char *nextName = nameBuf;
111 while (nextName < nameBuf + nameBufSize)
112 {
113 char *name = nextName;
114 if (xattrIndex == xattrsLen) {
115 xattrsLen *= 2;
116 xattrs = realloc(xattrs, xattrsLen * sizeof(char *));
117 }
118 xattrs[xattrIndex++] = name;
119 nextName += strlen(name) + 1;
120 }
121
122 // sort the xattr array as they're not guaranteed to come in the same order
123 qsort_b(xattrs, xattrIndex, sizeof(char *), ^(const void *l, const void *r) {
124 char *left = *(char **)l;
125 char *right = *(char **)r;
126 return strcmp(left, right);
127 });
128
129 // gather the data for the xattrs
130 bool didAddXATTR = false;
131 int xattrBufLen = kSHA256NullTerminatedBuffLen;
132 void *xattrBuf = malloc(xattrBufLen); // resized if necessary
133 char *digest;
134 ssize_t result = 0;
135 char *oldSummary = NULL;
136 for (int i = 0; i < xattrIndex; i++) {
137 char *name = xattrs[i];
138 ssize_t xlen = getxattr(path, name, NULL, 0, 0, options);
139 if (xlen > xattrBufLen) {
140 xattrBufLen = xlen;
141 xattrBuf = realloc(xattrBuf, xattrBufLen);
142 }
143 bzero(xattrBuf, xattrBufLen);
144 result = getxattr(path, name, xattrBuf, xattrBufLen, 0, options);
145 if (result < 0)
146 err(1, "SHA256_Path_XATTRs: getxattr");
147
148 digest = SHA256_Data(xattrBuf, xattrBufLen, buf);
149 if (!digest)
150 err(1, "%s", xattrsSummary);
151 if (!didAddXATTR)
152 {
153 didAddXATTR = true;
154 asprintf(&xattrsSummary, "%s:%s", name, digest);
155 } else {
156 oldSummary = xattrsSummary;
157 asprintf(&xattrsSummary, "%s, %s:%s", oldSummary, name, digest);
158 free(oldSummary);
159 }
160 }
161
162 free(xattrBuf);
163 free(nameBuf);
164 free(xattrs);
165
166 digest = SHA256_Data(xattrsSummary, strlen(xattrsSummary) * sizeof(char), buf);
167 if (!digest)
168 err(1, "%s", xattrsSummary);
169
170 free(xattrsSummary);
171 return digest;
172 }
173 return kNone;
174 }
175
176 char *SHA256_Path_ACL(char *path, char *buf)
177 {
178 int result = 0;
179 char *data = NULL;
180 char *digest = NULL;
181
182 struct attrlist list = {
183 .bitmapcount = ATTR_BIT_MAP_COUNT,
184 .commonattr = ATTR_CMN_RETURNED_ATTRS | ATTR_CMN_EXTENDED_SECURITY,
185 };
186
187 struct ACLBuf {
188 uint32_t len;
189 attribute_set_t returned_attrs;
190 attrreference_t acl;
191 char buf[8192]; // current acls are up to 3116 bytes, but they may increase in the future
192 } __attribute__((aligned(4), packed));
193
194 struct ACLBuf aclBuf;
195
196 result = getattrlist(path, &list, &aclBuf, sizeof(aclBuf), FSOPT_NOFOLLOW);
197
198 if (result)
199 err(1, "SHA256_Path_ACL: getattrlist");
200
201 // if the path does not have an acl, return none
202 if ( ( ! ( aclBuf.returned_attrs.commonattr & ATTR_CMN_EXTENDED_SECURITY ) )
203 || ( aclBuf.acl.attr_length == 0 ) ) {
204 return kNone;
205 }
206
207 data = ((char*)&aclBuf.acl) + aclBuf.acl.attr_dataoffset;
208
209 digest = SHA256_Data(data, aclBuf.acl.attr_length, buf);
210 if (!digest)
211 err(1, "SHA256_Path_ACL: SHA256_Data");
212
213 return digest;
214 }
215
216 /* Functions for Digest_Path_* */
217 char *
218 Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf) {
219
220 uint8_t digest[32]; // SHA256 is the biggest
221 CCDigestCtx ctx;
222 size_t i, length;
223
224 (void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
225 (void)os_assumes_zero(CCDigestUpdate(&ctx, data, size));
226
227 /* Finalize and convert to hex. */
228 (void)os_assumes_zero(CCDigestFinal(&ctx, digest));
229 length = CCDigestOutputSize(&ctx);
230 os_assert(length <= sizeof(digest));
231 for (i = 0; i < length; i++) {
232 buf[i+i] = hex[digest[i] >> 4];
233 buf[i+i+1] = hex[digest[i] & 0x0f];
234 }
235 buf[i+i] = '\0';
236
237 return buf;
238 }
239