aboutsummaryrefslogtreecommitdiffstats
path: root/file_cmds/mtree/commoncrypto.c
diff options
context:
space:
mode:
Diffstat (limited to 'file_cmds/mtree/commoncrypto.c')
-rw-r--r--file_cmds/mtree/commoncrypto.c378
1 files changed, 378 insertions, 0 deletions
diff --git a/file_cmds/mtree/commoncrypto.c b/file_cmds/mtree/commoncrypto.c
new file mode 100644
index 0000000..11e97ce
--- /dev/null
+++ b/file_cmds/mtree/commoncrypto.c
@@ -0,0 +1,378 @@
+#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 <sys/xattr.h>
+#include <sys/mount.h>
+#include <apfs/apfs_fsctl.h>
+
+#include "commoncrypto.h"
+#include "extern.h"
+#include "metrics.h"
+
+const int kSHA256NullTerminatedBuffLen = 65;
+static const char hex[] = "0123456789abcdef";
+
+/* Functions for SHA256_File_XATTRs */
+#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);
+
+/* Generic version of libmd's *_File() functions. */
+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]; // SHA256 is the biggest
+ size_t i, length;
+
+ /* dispatch_io_create_with_path requires an absolute path */
+ 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;
+ RECORD_FAILURE(27440, s_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;
+ RECORD_FAILURE(27441, s_error);
+ }
+ });
+ dispatch_release(io); // it will close on its own
+
+ (void)dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
+
+ dispatch_release(queue);
+ dispatch_release(sema);
+
+ if (s_error != 0) {
+ errno = s_error;
+ return NULL;
+ }
+
+ /* Finalize and convert to hex. */
+ (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;
+}
+
+xattr_info *
+SHA256_Path_XATTRs(char *path, char *buf) {
+ xattr_info *ai = NULL;
+
+ if (mflag) {
+ ai = get_xdstream_privateid(path, buf);
+ } else {
+ ai = calculate_SHA256_XATTRs(path, buf);
+ }
+
+ return ai;
+}
+
+
+xattr_info *
+calculate_SHA256_XATTRs(char *path, char *buf)
+{
+ errno_t error = 0;
+ char *xattrsSummary = NULL;
+ int options = XATTR_SHOWCOMPRESSION | XATTR_NOFOLLOW;
+ ssize_t nameBufSize = listxattr(path, NULL, 0, options);
+ uint64_t xd_obj_id = 0;
+ 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;
+ }
+
+ // sort the xattr array as they're not guaranteed to come in the same order
+ qsort_b(xattrs, xattrIndex, sizeof(char *), ^(const void *l, const void *r) {
+ char *left = *(char **)l;
+ char *right = *(char **)r;
+ return strcmp(left, right);
+ });
+
+ // gather the data for the xattrs
+ bool didAddXATTR = false;
+ int xattrBufLen = kSHA256NullTerminatedBuffLen;
+ void *xattrBuf = malloc(xattrBufLen); // resized if necessary
+ char *digest;
+ ssize_t result = 0;
+ char *oldSummary = NULL;
+ //XXX Make xattr_info an array of structs if necessary
+ xattr_info *ai = (xattr_info *) malloc(sizeof(xattr_info));
+ 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) {
+ error = errno;
+ RECORD_FAILURE(27442, error);
+ errc(1, error, "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);
+ }
+#ifdef APFSIOC_XDSTREAM_OBJ_ID
+ // System volume has stream based xattrs only in form of resource forks
+ if (!strncmp(name, XATTR_RESOURCEFORK_NAME, XATTR_MAXNAMELEN)) {
+ struct xdstream_obj_id x_obj;
+ x_obj.xdi_name = name;
+ x_obj.xdi_xdtream_obj_id = 0;
+
+ result = fsctl(path, APFSIOC_XDSTREAM_OBJ_ID, &x_obj, 0);
+ if (!result) {
+ xd_obj_id = x_obj.xdi_xdtream_obj_id;
+ } else if (errno == ENOTTY) {
+ // Not an apfs filesystem, return zero.
+ xd_obj_id = 0;
+ } else {
+ error = errno;
+ RECORD_FAILURE(27444, error);
+ errc(1, error, "%s - SHA256_Path_XATTRs APFSIOC_XDSTREAM_OBJ_ID failed with %d", path, error);
+ }
+ }
+#endif
+ ai->xdstream_priv_id = xd_obj_id;
+ }
+
+ free(xattrBuf);
+ free(nameBuf);
+ free(xattrs);
+
+ digest = SHA256_Data(xattrsSummary, strlen(xattrsSummary) * sizeof(char), buf);
+ if (!digest)
+ err(1, "%s", xattrsSummary);
+
+ ai->digest = digest;
+
+ free(xattrsSummary);
+ return ai;
+ }
+ return NULL;
+}
+
+xattr_info *
+get_xdstream_privateid(char *path, char *buf) {
+ errno_t error = 0;
+ int options = XATTR_SHOWCOMPRESSION | XATTR_NOFOLLOW;
+ ssize_t nameBufSize = listxattr(path, NULL, 0, options);
+ uint64_t xd_obj_id = 0;
+
+ if (nameBufSize > 0) {
+ //XXX Make xattr_info an array of structs if necessary
+ xattr_info *ai = (xattr_info *) malloc(sizeof(xattr_info));
+ char *nameBuf = malloc(nameBufSize);
+ int result = 0;
+
+ 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;
+ }
+
+ for (int i = 0; i < xattrIndex; i++) {
+ char *name = xattrs[i];
+ // System volume has stream based xattrs only in form of resource forks
+ if (!strncmp(name, XATTR_RESOURCEFORK_NAME, XATTR_MAXNAMELEN)) {
+ struct xdstream_obj_id x_obj;
+ x_obj.xdi_name = name;
+ x_obj.xdi_xdtream_obj_id = 0;
+
+ result = fsctl(path, APFSIOC_XDSTREAM_OBJ_ID, &x_obj, 0);
+ if (!result && x_obj.xdi_xdtream_obj_id != 0) {
+ xd_obj_id = x_obj.xdi_xdtream_obj_id;
+ } else if (errno == ENOTTY) {
+ // Not an apfs filesystem, return zero.
+ xd_obj_id = 0;
+ } else {
+ error = errno;
+ RECORD_FAILURE(29983, error);
+ errc(1, error, "%s - SHA256_Path_XATTRs APFSIOC_XDSTREAM_OBJ_ID failed with %d", path, error);
+ }
+ }
+ }
+
+ ai->xdstream_priv_id = xd_obj_id;
+ // insert a dummy value as digest is not used in presence of mflag
+ ai->digest = "authapfs";
+
+ free(nameBuf);
+ free(xattrs);
+ return ai;
+ }
+
+ return NULL;
+}
+
+char *SHA256_Path_ACL(char *path, char *buf)
+{
+ errno_t error = 0;
+ 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]; // current acls are up to 3116 bytes, but they may increase in the future
+ } __attribute__((aligned(4), packed));
+
+ struct ACLBuf aclBuf;
+
+ result = getattrlist(path, &list, &aclBuf, sizeof(aclBuf), FSOPT_NOFOLLOW);
+
+ if (result) {
+ error = errno;
+ RECORD_FAILURE(27445, error);
+ errc(1, error, "SHA256_Path_ACL: getattrlist");
+ }
+
+ // if the path does not have an acl, return none
+ 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;
+}
+
+/* Functions for Digest_Path_* */
+char *
+Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf) {
+
+ uint8_t digest[32]; // SHA256 is the biggest
+ CCDigestCtx ctx;
+ size_t i, length;
+
+ (void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
+ (void)os_assumes_zero(CCDigestUpdate(&ctx, data, size));
+
+ /* Finalize and convert to hex. */
+ (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;
+}
+
+uint64_t
+get_sibling_id(const char *path)
+{
+ struct attrlist attr_list = {0};
+ struct attrbuf attr_buf = {0};
+ errno_t error = 0;
+
+ attr_list.bitmapcount = ATTR_BIT_MAP_COUNT;
+ attr_list.forkattr = ATTR_CMNEXT_LINKID;
+
+ error = getattrlist(path, &attr_list, &attr_buf, sizeof(attr_buf), FSOPT_ATTR_CMN_EXTENDED | FSOPT_NOFOLLOW);
+ if (error) {
+ error = errno;
+ RECORD_FAILURE(27447, error);
+ errc(1, error, "get_sibling_id: getattrlist failed for %s\n", path);
+ }
+
+ return attr_buf.sibling_id;
+}