/* * Copyright (c) 2010 Apple Inc. All rights reserved. * * This document is the property of Apple Inc. * It is considered confidential and proprietary. * * This document may not be reproduced or transmitted in any form, * in whole or in part, without the express written permission of * Apple Inc. */ #include #include #include #include #include #include #include #include #include "AppleEffaceableStorageKeys.h" #include "AppleEffaceableStorageFormat.h" #include "effaceable.h" // ============================================================================= // XXX correct hard-coding of SHA1 buffer size #define SHA1_HASH_SIZE 20 // ============================================================================= bool connectClient(const char * name, io_service_t * service, io_connect_t * connection) { bool ok = false; mach_port_t master; CFDictionaryRef match; io_service_t found_service; io_connect_t found_connection; if (KERN_SUCCESS != IOMasterPort(MACH_PORT_NULL, &master)) { warnx("IOMasterPort failed"); } else if (IO_OBJECT_NULL == (match = IOServiceMatching(name))) { warnx("IOServiceMatching failed for %s", name); } else if (IO_OBJECT_NULL == (found_service = IOServiceGetMatchingService(master, match))) { warnx("IOServiceGetMatchingService failed"); } else if (KERN_SUCCESS != IOServiceOpen(found_service, mach_task_self(), 0, &found_connection)) { warnx("IOServiceOpen failed for class '%s'", name); } else { *service = found_service; *connection = found_connection; ok = true; } if (!ok) { // XXX clean up } return ok; } bool wipeStorage(io_connect_t connection) { bool ok = false; if (KERN_SUCCESS != IOConnectCallScalarMethod(connection, kAppleEffaceableStorageMethodWipeStorage, NULL, 0, NULL, NULL)) { errx(1, "wipe failed"); } else { ok = true; } return ok; } bool getCapacity(io_connect_t connection, IOByteCount * capacity) { bool ok = false; const uint32_t expected_count = 1; uint32_t output_count = expected_count; uint64_t output[output_count]; if (KERN_SUCCESS != IOConnectCallScalarMethod(connection, kAppleEffaceableStorageMethodGetCapacity, NULL, 0, output, &output_count)) { warnx("IOConnectCallScalarMethod failed"); } else if (expected_count != output_count) { warnx("unexpected scalar output count"); } else { *capacity = (IOByteCount) output[0]; ok = true; } return ok; } bool getBytes(io_connect_t connection, void * buf, IOByteCount offset, IOByteCount count) { bool ok = false; uint64_t input; size_t output_count = count; input = (uint64_t)offset; if (KERN_SUCCESS != IOConnectCallMethod(connection, kAppleEffaceableStorageMethodGetBytes, &input, 1, NULL, 0, NULL, NULL, buf, &output_count)) { warnx("IOConnectCallMethod failed"); } else if (count != output_count) { warnx("unexpected output struct count"); } else { ok = true; } return ok; } bool setBytes(io_connect_t connection, const void * buf, IOByteCount offset, IOByteCount count) { bool ok = false; uint64_t input; input = (uint64_t)offset; if (KERN_SUCCESS != IOConnectCallMethod(connection, kAppleEffaceableStorageMethodSetBytes, &input, 1, buf, count, NULL, NULL, NULL, NULL)) { warnx("IOConnectCallMethod failed"); } else { ok = true; } return ok; } bool isFormatted(io_connect_t connection, bool * is_formatted) { bool ok = false; const uint32_t expected_count = 1; uint32_t output_count = expected_count; uint64_t output[output_count]; if (KERN_SUCCESS != IOConnectCallScalarMethod(connection, kAppleEffaceableStorageMethodIsFormatted, NULL, 0, output, &output_count)) { warnx("IOConnectCallScalarMethod failed"); } else if (expected_count != output_count) { warnx("unexpected scalar output count"); } else { *is_formatted = (bool) output[0]; ok = true; } return ok; } bool formatStorage(io_connect_t connection) { bool ok = false; if (KERN_SUCCESS != IOConnectCallScalarMethod(connection, kAppleEffaceableStorageMethodFormatStorage, NULL, 0, NULL, NULL)) { warnx("IOConnectCallScalarMethod failed"); } else { ok = true; } return ok; } // ============================================================================= int main(int argc, char *argv[]) { io_service_t service; io_connect_t connection; if (0 != getuid()) { errx(1, "must be run as root (uid = %d)", getuid()); } if (!connectClient(kAppleEffaceableStorageClassName, &service, &connection)) { errx(1, "unable to connect to service"); } // crude command parser...really only intended as test vehicle if (1 >= argc) { printUsage(argc, argv); } else { if (0 == strcmp("format", argv[1])) { if (formatStorage(connection)) { printf("format succeeded\n"); } else { printf("format failed\n"); } } else if (0 == strcmp("wipe", argv[1])) { wipeStorage(connection); } else if (0 == strcmp("test", argv[1])) { quickTest(connection); } else if (0 == strcmp("lockers", argv[1])) { lockerList(connection); } else if (0 == strcmp("nonce", argv[1])) { generateNonce(connection); } else if (2 >= argc) { printUsage(argc, argv); } else if (0 == strcmp("efface", argv[1])) { lockerEfface(connection, argv[2]); } else if (3 >= argc) { printUsage(argc, argv); } else if (0 == strcmp("set", argv[1])) { lockerSet(connection, argv[2], argv[3]); } else if (0 == strcmp("get", argv[1])) { lockerGet(connection, argv[2], argv[3]); } else { printUsage(argc, argv); errx(1, "unrecognized subcommand"); } } // XXX clean up service and connection return 0; } void printUsage(int argc, char *argv[]) { printf("USAGE: %s \n\n", ((0 < argc) ? argv[0] : "?")); printf(" Where is one of following\n\n"); printf(" format - format storage\n"); printf(" wipe - wipe storage\n"); printf(" test - perform quick test\n"); printf(" lockers - list lockers and contents\n"); printf(" nonce - generate a nonce and report its SHA1 hash\n"); printf(" set - write into locker \n"); printf(" get - write from locker \n"); printf("\n"); } // ============================================================================= void hexdump(const void * buf, IOByteCount count) { int i, j; for (i = 0; i < count; i += 16) { printf("0x%08x:", ((unsigned) buf) + i); for (j = 0; (j < 16) && ((i + j) < count); j++) { printf(" %2.2x", ((uint8_t*)buf)[i+j]); } printf("\n"); } } void quickTest(io_connect_t connection) { const IOByteCount count = 64; IOByteCount offset; IOByteCount capacity; void * sbuf = NULL; void * gbuf = NULL; unsigned i; if (!getCapacity(connection, &capacity)) { errx(1, "unable to get capacity"); } printf("capacity reported as %u; expected 960\n", (unsigned) capacity); offset = capacity - count - 1; sbuf = malloc(count); gbuf = malloc(count); if ((NULL == sbuf) || (NULL == gbuf)) { errx(1, "failed to allocate buffers"); } if (!getBytes(connection, gbuf, offset, count)) { errx(1, "unable to get bytes"); } printf("read %u bytes from %u offset\n", (unsigned) count, (unsigned) offset); hexdump(gbuf, count); srandom(((unsigned *)gbuf)[0]); for (i = 0; i < (count / sizeof(long)); i++) { ((long *)sbuf)[i] = random(); } if (!setBytes(connection, sbuf, offset, count)) { errx(1, "unable to set bytes"); } printf("wrote %u bytes to %u offset\n", (unsigned) count, (unsigned) offset); hexdump(sbuf, count); if (!getBytes(connection, gbuf, offset, count)) { errx(1, "unable to get bytes"); } printf("read %u bytes from %u offset\n", (unsigned) count, (unsigned) offset); hexdump(gbuf, count); if (0 != bcmp(sbuf, gbuf, count)) { errx(1, "bytes read back differ from those written"); } printf("test passed\n"); if (NULL != sbuf) { free(sbuf); } if (NULL != gbuf) { free(gbuf); } } // ============================================================================= #define UNTAG(x) (int)(((x) >> 24) & 0xff),(int)(((x) >> 16) & 0xff),(int)(((x) >> 8) & 0xff),(int)((x) & 0xff) void lockerList(io_connect_t connection) { uint8_t *buf, *cursor; IOByteCount size; AppleEffaceableStorageLockerHeader *header; if (!getCapacity(connection, &size)) errx(1, "unable to get capacity"); if (size != kAppleEffaceableStorageLockerSize) warnx("size %lu not %lu as expected", size, kAppleEffaceableStorageLockerSize); buf = malloc(size); if (!getBytes(connection, buf, 0, size)) errx(1, "unable to read"); cursor = buf; for (;;) { header = (AppleEffaceableStorageLockerHeader *)cursor; if (header->magic != kAppleEffaceableStorageLockerMagic) { warnx("unexpected magic 0x%04x at %u, expected 0x%04x", header->magic, cursor - buf, kAppleEffaceableStorageLockerMagic); break; } if (header->type_id == kAppleEffaceableStorageLockerSentinel) { printf("\n"); break; } printf("0x%08x/0x%04x %c%c%c%c %s\n", (unsigned)header->type_id, header->data_size, UNTAG(header->type_id), (header->type_id & kAppleEffaceableStorageLockerProtected) ? "(protected)" : ""); cursor += sizeof(*header); if (header->data_size > 0) { if ((cursor + header->data_size) > (buf + size)) { warnx("data overflows buffer"); break; } hexdump(cursor, header->data_size); cursor += header->data_size; } if ((cursor + sizeof(*header)) > (buf + size)) { warnx("header overflows buffer"); break; } } } void lockerGet(io_connect_t connection, char *tag, char *file) { UInt32 tagVal; UInt8 tagStr[4]; UInt8 buf[1024]; UInt64 input, output; uint32_t output_count; UInt32 output_size; int fd; // get the tag memset(tagStr, 0, 4); strncpy((char *)tagStr, tag, 4); tagVal = ((UInt32)tagStr[0] << 24) + ((UInt32)tagStr[1] << 16) + ((UInt32)tagStr[2] << 8) + (UInt32)tagStr[3]; // get the locker input = tagVal; output_count = 1; output_size = sizeof(buf); if (KERN_SUCCESS != IOConnectCallMethod(connection, // connection kAppleEffaceableStorageMethodGetLocker, // selector &input, // input 1, // input count NULL, // input struct 0, // input struct size &output, // output &output_count, // output count &buf, // output struct &output_size)) { // output struct size errx(1, "getLocker call failed"); } // write the file if ((fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0666)) < 0) err(1, "failed to create %s", file); warnx("using descriptor %d", fd); if (write(fd, buf, output_size) != output_size) err(1, "failed writing %lu bytes to %s (fd %d)", output_size, file, fd); close(fd); exit(0); } void lockerSet(io_connect_t connection, char *tag, char *file) { UInt32 tagVal; UInt8 tagStr[4]; UInt8 buf[1024]; UInt64 input; int fd, size; // get the tag memset(tagStr, 0, 4); strncpy((char *)tagStr, tag, 4); tagVal = ((UInt32)tagStr[0] << 24) + ((UInt32)tagStr[1] << 16) + ((UInt32)tagStr[2] << 8) + (UInt32)tagStr[3]; // read the file if ((fd = open(file, O_RDONLY)) < 0) err(1, "failed to open %s", file); if ((size = read(fd, buf, sizeof(buf))) < 0) err(1, "failed to read from %s", file); close(fd); // make the call input = tagVal; if (KERN_SUCCESS != IOConnectCallMethod(connection, kAppleEffaceableStorageMethodSetLocker, &input, 1, &buf, size, NULL, 0, NULL, NULL)) { errx(1, "setLocker call failed"); } exit(0); } void lockerEfface(io_connect_t connection, char *tag) { UInt32 tagVal; UInt8 tagStr[4]; UInt64 input; // get the tag memset(tagStr, 0, 4); strncpy((char *)tagStr, tag, 4); tagVal = ((UInt32)tagStr[0] << 24) + ((UInt32)tagStr[1] << 16) + ((UInt32)tagStr[2] << 8) + (UInt32)tagStr[3]; // make the call input = tagVal; if (KERN_SUCCESS != IOConnectCallMethod(connection, kAppleEffaceableStorageMethodEffaceLocker, &input, 1, NULL, 0, NULL, 0, NULL, NULL)) { errx(1, "effaceLocker call failed"); } exit(0); } void generateNonce(io_connect_t connection) { UInt8 buf[SHA1_HASH_SIZE]; UInt32 output_size = sizeof(buf); UInt32 idx; memset(buf, 0, output_size); if (KERN_SUCCESS != IOConnectCallMethod(connection, // connection kAppleEffaceableStorageMethodGenerateNonce, // selector NULL, // input 0, // input count NULL, // input struct 0, // input struct size NULL, // output NULL, // output count &buf, // output struct &output_size)) { // output struct size errx(1, "generateNonce call failed"); } // report the nonce SHA1 hash for (idx = 0; idx < output_size; idx++) { printf("%s%02X", (idx ? " " : ""), buf[idx]); } printf("\n"); exit(0); }