#include <dispatch/dispatch.h>
#include <os/assumes.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/xattr.h>
#include <stdbool.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <sys/attr.h>
#include <unistd.h>
#include "commoncrypto.h"
const int kSHA256NullTerminatedBuffLen = 65;
static const char hex[] = "0123456789abcdef";
#define SHA256_Data(d, s, b) Digest_Data(kCCDigestSHA256, d, s, b)
char *Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf);
void Quicksort(char **array, int num);
char *
Digest_File(CCDigestAlg algorithm, const char *filename, char *buf)
{
int fd;
__block CCDigestCtx ctx;
dispatch_queue_t queue;
dispatch_semaphore_t sema;
dispatch_io_t io;
__block int s_error = 0;
uint8_t digest[32]; size_t i, length;
fd = open(filename, O_RDONLY);
if (fd < 0) {
return NULL;
}
(void)fcntl(fd, F_NOCACHE, 1);
(void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
queue = dispatch_queue_create("com.apple.mtree.io", NULL);
os_assert(queue);
sema = dispatch_semaphore_create(0);
os_assert(sema);
io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) {
if (error != 0) {
s_error = error;
}
(void)close(fd);
(void)dispatch_semaphore_signal(sema);
});
os_assert(io);
dispatch_io_read(io, 0, SIZE_MAX, queue, ^(__unused bool done, dispatch_data_t data, int error) {
if (data != NULL) {
(void)dispatch_data_apply(data, ^(__unused dispatch_data_t region, __unused size_t offset, const void *buffer, size_t size) {
(void)os_assumes_zero(CCDigestUpdate(&ctx, buffer, size));
return (bool)true;
});
}
if (error != 0) {
s_error = error;
}
});
dispatch_release(io);
(void)dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_release(queue);
dispatch_release(sema);
if (s_error != 0) {
errno = s_error;
return NULL;
}
(void)os_assumes_zero(CCDigestFinal(&ctx, digest));
length = CCDigestOutputSize(&ctx);
os_assert(length <= sizeof(digest));
for (i = 0; i < length; i++) {
buf[i+i] = hex[digest[i] >> 4];
buf[i+i+1] = hex[digest[i] & 0x0f];
}
buf[i+i] = '\0';
return buf;
}
char *SHA256_Path_XATTRs(char *path, char *buf)
{
char *xattrsSummary = NULL;
int options = XATTR_SHOWCOMPRESSION | XATTR_NOFOLLOW;
ssize_t nameBufSize = listxattr(path, NULL, 0, options);
if (nameBufSize > 0) {
char *nameBuf = malloc(nameBufSize);
listxattr(path, nameBuf, nameBufSize, options);
size_t xattrsLen = 1;
size_t xattrIndex = 0;
char **xattrs = malloc(xattrsLen * sizeof(char *));
char *nextName = nameBuf;
while (nextName < nameBuf + nameBufSize)
{
char *name = nextName;
if (xattrIndex == xattrsLen) {
xattrsLen *= 2;
xattrs = realloc(xattrs, xattrsLen * sizeof(char *));
}
xattrs[xattrIndex++] = name;
nextName += strlen(name) + 1;
}
qsort_b(xattrs, xattrIndex, sizeof(char *), ^(const void *l, const void *r) {
char *left = *(char **)l;
char *right = *(char **)r;
return strcmp(left, right);
});
bool didAddXATTR = false;
int xattrBufLen = kSHA256NullTerminatedBuffLen;
void *xattrBuf = malloc(xattrBufLen); char *digest;
ssize_t result = 0;
char *oldSummary = NULL;
for (int i = 0; i < xattrIndex; i++) {
char *name = xattrs[i];
ssize_t xlen = getxattr(path, name, NULL, 0, 0, options);
if (xlen > xattrBufLen) {
xattrBufLen = xlen;
xattrBuf = realloc(xattrBuf, xattrBufLen);
}
bzero(xattrBuf, xattrBufLen);
result = getxattr(path, name, xattrBuf, xattrBufLen, 0, options);
if (result < 0)
err(1, "SHA256_Path_XATTRs getxattr of \"%s\" at path \"%s\" failed with error", name, path );
digest = SHA256_Data(xattrBuf, xattrBufLen, buf);
if (!digest)
err(1, "%s", xattrsSummary);
if (!didAddXATTR)
{
didAddXATTR = true;
asprintf(&xattrsSummary, "%s:%s", name, digest);
} else {
oldSummary = xattrsSummary;
asprintf(&xattrsSummary, "%s, %s:%s", oldSummary, name, digest);
free(oldSummary);
}
}
free(xattrBuf);
free(nameBuf);
free(xattrs);
digest = SHA256_Data(xattrsSummary, strlen(xattrsSummary) * sizeof(char), buf);
if (!digest)
err(1, "%s", xattrsSummary);
free(xattrsSummary);
return digest;
}
return kNone;
}
char *SHA256_Path_ACL(char *path, char *buf)
{
int result = 0;
char *data = NULL;
char *digest = NULL;
struct attrlist list = {
.bitmapcount = ATTR_BIT_MAP_COUNT,
.commonattr = ATTR_CMN_RETURNED_ATTRS | ATTR_CMN_EXTENDED_SECURITY,
};
struct ACLBuf {
uint32_t len;
attribute_set_t returned_attrs;
attrreference_t acl;
char buf[8192]; } __attribute__((aligned(4), packed));
struct ACLBuf aclBuf;
result = getattrlist(path, &list, &aclBuf, sizeof(aclBuf), FSOPT_NOFOLLOW);
if (result)
err(1, "SHA256_Path_ACL: getattrlist");
if ( ( ! ( aclBuf.returned_attrs.commonattr & ATTR_CMN_EXTENDED_SECURITY ) )
|| ( aclBuf.acl.attr_length == 0 ) ) {
return kNone;
}
data = ((char*)&aclBuf.acl) + aclBuf.acl.attr_dataoffset;
digest = SHA256_Data(data, aclBuf.acl.attr_length, buf);
if (!digest)
err(1, "SHA256_Path_ACL: SHA256_Data");
return digest;
}
char *
Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf) {
uint8_t digest[32]; CCDigestCtx ctx;
size_t i, length;
(void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
(void)os_assumes_zero(CCDigestUpdate(&ctx, data, size));
(void)os_assumes_zero(CCDigestFinal(&ctx, digest));
length = CCDigestOutputSize(&ctx);
os_assert(length <= sizeof(digest));
for (i = 0; i < length; i++) {
buf[i+i] = hex[digest[i] >> 4];
buf[i+i+1] = hex[digest[i] & 0x0f];
}
buf[i+i] = '\0';
return buf;
}