]>
Commit | Line | Data |
---|---|---|
4d0bb651 | 1 | #include <dispatch/dispatch.h> |
00337e45 | 2 | #include <os/assumes.h> |
4d0bb651 A |
3 | #include <errno.h> |
4 | #include <fcntl.h> | |
e0055cbe A |
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> | |
4d0bb651 A |
13 | |
14 | #include "commoncrypto.h" | |
15 | ||
e0055cbe A |
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 | ||
4d0bb651 A |
24 | /* Generic version of libmd's *_File() functions. */ |
25 | char * | |
26 | Digest_File(CCDigestAlg algorithm, const char *filename, char *buf) | |
27 | { | |
4d0bb651 A |
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 | ||
00337e45 | 45 | (void)os_assumes_zero(CCDigestInit(algorithm, &ctx)); |
4d0bb651 A |
46 | |
47 | queue = dispatch_queue_create("com.apple.mtree.io", NULL); | |
00337e45 | 48 | os_assert(queue); |
4d0bb651 | 49 | sema = dispatch_semaphore_create(0); |
00337e45 | 50 | os_assert(sema); |
4d0bb651 A |
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 | }); | |
00337e45 | 59 | os_assert(io); |
4d0bb651 A |
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) { | |
00337e45 | 63 | (void)os_assumes_zero(CCDigestUpdate(&ctx, buffer, size)); |
4d0bb651 A |
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. */ | |
00337e45 | 85 | (void)os_assumes_zero(CCDigestFinal(&ctx, digest)); |
4d0bb651 | 86 | length = CCDigestOutputSize(&ctx); |
00337e45 | 87 | os_assert(length <= sizeof(digest)); |
4d0bb651 A |
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'; | |
e0055cbe A |
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 | } | |
4d0bb651 | 215 | |
e0055cbe A |
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 | ||
4d0bb651 A |
237 | return buf; |
238 | } | |
e0055cbe | 239 |