#include "authutilities.h"
#include <Security/AuthorizationTags.h>
+#include <dispatch/private.h>
+#include <CommonCrypto/CommonCrypto.h>
+
+AUTHD_DEFINE_LOG
typedef struct _auth_item_s * auth_item_t;
+void auth_items_crypt_worker(auth_items_t items, CFDataRef encryption_key, bool(*function)(auth_item_t, CFDataRef));
#pragma mark -
#pragma mark auth_item_t
item->data.value = realloc(item->data.value, item->bufLen);
if (item->data.value == NULL) {
// this is added to prevent running off into random memory if a string buffer doesn't have a null char
- LOGE("realloc failed");
+ os_log_error(AUTHD_LOG, "items: realloc failed");
abort();
}
((uint8_t*)item->data.value)[item->bufLen-1] = '\0';
xpc_object_t xpc_data = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(xpc_data, AUTH_XPC_ITEM_NAME, item->data.name);
if (item->data.value) {
- xpc_dictionary_set_data(xpc_data, AUTH_XPC_ITEM_VALUE, item->data.value, item->data.valueLength);
+ // <rdar://problem/13033889> authd is holding on to multiple copies of my password in the clear
+ bool sensitive = strcmp(item->data.name, "password") == 0;
+ if (sensitive) {
+ vm_address_t vmBytes = 0;
+ size_t xpcOutOfBandBlockSize = (item->data.valueLength > 32768 ? item->data.valueLength : 32768); // min 16K on 64-bit systems and 12K on 32-bit systems
+ vm_allocate(mach_task_self(), &vmBytes, xpcOutOfBandBlockSize, VM_FLAGS_ANYWHERE);
+ memcpy((void *)vmBytes, item->data.value, item->data.valueLength);
+ dispatch_data_t dispData = dispatch_data_create((void *)vmBytes, xpcOutOfBandBlockSize, DISPATCH_TARGET_QUEUE_DEFAULT, DISPATCH_DATA_DESTRUCTOR_VM_DEALLOCATE); // out-of-band mapping
+ xpc_object_t xpcData = xpc_data_create_with_dispatch_data(dispData);
+ dispatch_release(dispData);
+ xpc_dictionary_set_value(xpc_data, AUTH_XPC_ITEM_VALUE, xpcData);
+ xpc_release(xpcData);
+ xpc_dictionary_set_uint64(xpc_data, AUTH_XPC_ITEM_SENSITIVE_VALUE_LENGTH, item->data.valueLength);
+ } else {
+ xpc_dictionary_set_data(xpc_data, AUTH_XPC_ITEM_VALUE, item->data.value, item->data.valueLength);
+ }
}
xpc_dictionary_set_uint64(xpc_data, AUTH_XPC_ITEM_FLAGS, item->data.flags);
xpc_dictionary_set_uint64(xpc_data, AUTH_XPC_ITEM_TYPE, item->type);
{
auth_item_t item = (auth_item_t)value;
- CFReleaseSafe(item->cfKey);
+ CFReleaseNull(item->cfKey);
if (item->data.name) {
free((void*)item->data.name);
+ /* cannot set item->data.name to NULL because item->data.name is non-nullable public API (rdar://problem/32235322)
+ * cannot leave item->data.name pointing to original data (rdar://problem/31006596)
+ * => suppress the warning */
+ #ifndef __clang_analyzer__
+ item->data.name = NULL;
+ #endif
}
if (item->data.value) {
memset(item->data.value, 0, item->data.valueLength);
- free(item->data.value);
+ free_safe(item->data.value);
}
}
item->data.name = _copy_string(xpc_dictionary_get_string(data, AUTH_XPC_ITEM_NAME));
item->data.flags = (uint32_t)xpc_dictionary_get_uint64(data, AUTH_XPC_ITEM_FLAGS);
item->type = (uint32_t)xpc_dictionary_get_uint64(data, AUTH_XPC_ITEM_TYPE);
-
+
size_t len;
const void * value = xpc_dictionary_get_data(data, AUTH_XPC_ITEM_VALUE, &len);
if (value) {
- item->bufLen = len;
- item->data.valueLength = len;
- item->data.value = calloc(1u, len);
- memcpy(item->data.value, value, len);
+ // <rdar://problem/13033889> authd is holding on to multiple copies of my password in the clear
+ bool sensitive = xpc_dictionary_get_value(data, AUTH_XPC_ITEM_SENSITIVE_VALUE_LENGTH);
+ if (sensitive) {
+ size_t sensitiveLength = (size_t)xpc_dictionary_get_uint64(data, AUTH_XPC_ITEM_SENSITIVE_VALUE_LENGTH);
+ item->bufLen = sensitiveLength;
+ item->data.valueLength = sensitiveLength;
+ item->data.value = calloc(1u, sensitiveLength);
+ memcpy(item->data.value, value, sensitiveLength);
+ memset_s((void *)value, len, 0, sensitiveLength); // clear the sensitive data, memset_s is never optimized away
+ } else {
+ item->bufLen = len;
+ item->data.valueLength = len;
+ item->data.value = calloc(1u, len);
+ memcpy(item->data.value, value, len);
+ }
}
done:
return item;
}
+static bool auth_item_crypt_worker(auth_item_t item, CFDataRef key, int operation)
+{
+ bool result = false;
+
+ if (!key)
+ return result;
+
+ if (item->data.value && item->data.valueLength) {
+ size_t required_length = 0;
+ CCCryptorStatus status = CCCrypt(operation, kCCAlgorithmAES, kCCOptionPKCS7Padding,
+ CFDataGetBytePtr(key), CFDataGetLength(key), NULL,
+ item->data.value, item->data.valueLength, NULL, 0, &required_length);
+ require(status == kCCBufferTooSmall, done);
+
+ void *buffer = calloc(1u, required_length);
+ status = CCCrypt(operation, kCCAlgorithmAES, kCCOptionPKCS7Padding,
+ CFDataGetBytePtr(key), CFDataGetLength(key), NULL,
+ item->data.value, item->data.valueLength, buffer, required_length, &required_length);
+ if (status == kCCSuccess) {
+ memset(item->data.value, 0, item->data.valueLength);
+ free(item->data.value);
+ item->data.value = buffer;
+ item->data.valueLength = required_length;
+ result = true;
+ } else {
+ free(buffer);
+ }
+ }
+
+done:
+ return result;
+}
+
+static bool auth_item_decrypt(auth_item_t item, CFDataRef key)
+{
+ return auth_item_crypt_worker(item, key, kCCDecrypt);
+}
+
+static bool auth_item_encrypt(auth_item_t item, CFDataRef key)
+{
+ return auth_item_crypt_worker(item, key, kCCEncrypt);
+}
+
#pragma mark -
#pragma mark auth_items_t
CFDictionaryRemoveAllValues(items->dictionary);
}
+void
+auth_items_crypt_worker(auth_items_t items, CFDataRef encryption_key, bool(*function)(auth_item_t, CFDataRef))
+{
+ auth_items_iterate(items, ^bool(const char *key) {
+ CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
+ auth_item_t item = (auth_item_t)CFDictionaryGetValue(items->dictionary, lookup);
+ function(item, encryption_key);
+ CFReleaseSafe(lookup);
+ return true;
+ });
+}
+
+void
+auth_items_encrypt(auth_items_t items, CFDataRef encryption_key)
+{
+ auth_items_crypt_worker(items, encryption_key, auth_item_encrypt);
+}
+
+void
+auth_items_decrypt(auth_items_t items, CFDataRef encryption_key)
+{
+ auth_items_crypt_worker(items, encryption_key, auth_item_decrypt);
+}
+
void
auth_items_copy(auth_items_t items, auth_items_t src)
{
});
}
+void
+auth_items_content_copy(auth_items_t items, auth_items_t src)
+{
+ auth_items_iterate(src, ^bool(const char *key) {
+ CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
+ auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup);
+ auth_item_t new_item = auth_item_create(item->type, item->data.name, item->data.value, item->data.valueLength, item->data.flags);
+ if (new_item)
+ CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(new_item), new_item);
+ CFReleaseSafe(lookup);
+ CFReleaseSafe(new_item);
+ return true;
+ });
+}
+
void
auth_items_copy_xpc(auth_items_t items, const xpc_object_t src)
{
});
}
+// unlike previous method, this one creates true new copy including their content
+void
+auth_items_content_copy_with_flags(auth_items_t items, auth_items_t src, uint32_t flags)
+{
+ auth_items_iterate(src, ^bool(const char *key) {
+ if (auth_items_check_flags(src, key, flags)) {
+ CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
+ auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup);
+ auth_item_t new_item = auth_item_create(item->type, item->data.name, item->data.value, item->data.valueLength, item->data.flags);
+ if (new_item)
+ CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(new_item), new_item);
+ CFReleaseSafe(lookup);
+ CFReleaseSafe(new_item);
+ }
+ return true;
+ });
+}
+
bool
auth_items_iterate(auth_items_t items, auth_items_iterator_t iter)
{
if (item) {
#if DEBUG
if (!(item->type == AI_TYPE_STRING || item->type == AI_TYPE_UNKNOWN)) {
- LOGV("auth_items: key = %s, invalid type=%i expected=%i",
+ os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i",
item->data.name, item->type, AI_TYPE_STRING);
}
#endif
if (item) {
#if DEBUG
if (!(item->type == AI_TYPE_DATA || item->type == AI_TYPE_UNKNOWN)) {
- LOGV("auth_items: key = %s, invalid type=%i expected=%i",
+ os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i",
item->data.name, item->type, AI_TYPE_DATA);
}
#endif
if (item && (item->data.flags & flags) == flags) {
#if DEBUG
if (!(item->type == AI_TYPE_DATA || item->type == AI_TYPE_UNKNOWN)) {
- LOGV("auth_items: key = %s, invalid type=%i expected=%i",
+ os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i",
item->data.name, item->type, AI_TYPE_DATA);
}
#endif
if (item) {
#if DEBUG
if (!(item->type == AI_TYPE_BOOL || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(bool))) {
- LOGV("auth_items: key = %s, invalid type=%i expected=%i or size=%li expected=%li",
+ os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
item->data.name, item->type, AI_TYPE_BOOL, item->data.valueLength, sizeof(bool));
}
#endif
if (item) {
#if DEBUG
if (!(item->type ==AI_TYPE_INT || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(int32_t))) {
- LOGV("auth_items: key = %s, invalid type=%i expected=%i or size=%li expected=%li",
+ os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
item->data.name, item->type, AI_TYPE_INT, item->data.valueLength, sizeof(int32_t));
}
#endif
if (item) {
#if DEBUG
if (!(item->type ==AI_TYPE_UINT || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(uint32_t))) {
- LOGV("auth_items: key = %s, invalid type=%i expected=%i or size=%li expected=%li",
+ os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
item->data.name, item->type, AI_TYPE_UINT, item->data.valueLength, sizeof(uint32_t));
}
#endif
if (item) {
#if DEBUG
if (!(item->type ==AI_TYPE_INT64 || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(int64_t))) {
- LOGV("auth_items: key = %s, invalid type=%i expected=%i or size=%li expected=%li",
+ os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
item->data.name, item->type, AI_TYPE_INT64, item->data.valueLength, sizeof(int64_t));
}
#endif
if (item) {
#if DEBUG
if (!(item->type ==AI_TYPE_UINT64 || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(uint64_t))) {
- LOGV("auth_items: key = %s, invalid type=%i expected=%i or size=%li expected=%li",
+ os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
item->data.name, item->type, AI_TYPE_UINT64, item->data.valueLength, sizeof(uint64_t));
}
#endif
if (item) {
#if DEBUG
if (!(item->type ==AI_TYPE_DOUBLE || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(double))) {
- LOGV("auth_items: key = %s, invalid type=%i expected=%i or size=%li expected=%li",
+ os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
item->data.name, item->type, AI_TYPE_DOUBLE, item->data.valueLength, sizeof(double));
}
#endif