]> git.saurik.com Git - apple/security.git/blobdiff - OSX/sec/securityd/SecDbItem.c
Security-58286.1.32.tar.gz
[apple/security.git] / OSX / sec / securityd / SecDbItem.c
index c42735e7fc7494447cfb957e2a3dc70660fd107a..2bf7414dead519692e46fd03bdecb17cf9bf44c4 100644 (file)
 #include <Security/SecAccessControlPriv.h>
 #include <securityd/SecItemSchema.h>
 
+#include <keychain/ckks/CKKS.h>
+
 // MARK: type converters
 
 CFStringRef copyString(CFTypeRef obj) {
     CFTypeID tid = CFGetTypeID(obj);
-    if (tid == CFStringGetTypeID())
+    if (tid == CFStringGetTypeID()) {
         return CFStringCreateCopy(0, obj);
-    else if (tid == CFDataGetTypeID())
+    }else if (tid == CFDataGetTypeID()) {
         return CFStringCreateFromExternalRepresentation(0, obj, kCFStringEncodingUTF8);
-    else
+    } else if (tid == CFUUIDGetTypeID()) {
+        return CFUUIDCreateString(NULL, obj);
+    } else {
         return NULL;
+    }
 }
 
 CFDataRef copyData(CFTypeRef obj) {
@@ -71,6 +76,25 @@ CFDataRef copyData(CFTypeRef obj) {
     }
 }
 
+CFTypeRef copyUUID(CFTypeRef obj) {
+    CFTypeID tid = CFGetTypeID(obj);
+    if (tid == CFDataGetTypeID()) {
+        CFIndex length = CFDataGetLength(obj);
+        if (length != 0 && length != 16)
+            return NULL;
+        return CFDataCreateCopy(NULL, obj);
+    } else if (tid == CFNullGetTypeID()) {
+        return CFDataCreate(NULL, NULL, 0);
+    } else if (tid == CFUUIDGetTypeID()) {
+        CFUUIDBytes uuidbytes = CFUUIDGetUUIDBytes(obj);
+        CFDataRef uuiddata = CFDataCreate(NULL, (void*) &uuidbytes, sizeof(uuidbytes));
+        return uuiddata;
+    } else {
+        return NULL;
+    }
+}
+
+
 CFTypeRef copyBlob(CFTypeRef obj) {
     CFTypeID tid = CFGetTypeID(obj);
     if (tid == CFDataGetTypeID()) {
@@ -160,8 +184,18 @@ static CFNumberRef SecDbColumnCopyNumber(CFAllocatorRef allocator, sqlite3_stmt
     }
 }
 
-static CFStringRef SecDbColumnCopyString(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) {
+static CFTypeRef SecDbColumnCopyString(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error,
+                                       CFOptionFlags flags) {
     const unsigned char *text = sqlite3_column_text(stmt, col);
+    if (!text || 0 == strlen((const char *)text)) {
+        if (flags & kSecDbDefaultEmptyFlag) {
+            return CFSTR("");
+        } else if (flags & kSecDbDefault0Flag) {
+            return CFSTR("0");
+        } else {
+            return kCFNull;
+        }
+    }
     return CFStringCreateWithBytes(allocator, text, strlen((const char *)text), kCFStringEncodingUTF8, false);
 }
 
@@ -193,7 +227,8 @@ static bool SecDbIsTombstoneDbInsertAttr(const SecDbAttr *attr) {
 #endif
 
 static bool SecDbIsTombstoneDbUpdateAttr(const SecDbAttr *attr) {
-    return SecDbIsTombstoneDbSelectAttr(attr) || attr->kind == kSecDbAccessAttr || attr->kind == kSecDbCreationDateAttr || attr->kind == kSecDbRowIdAttr;
+    // We add AuthenticatedData to include UUIDs, which can't be primary keys
+    return SecDbIsTombstoneDbSelectAttr(attr) || attr->kind == kSecDbAccessAttr || attr->kind == kSecDbCreationDateAttr || attr->kind == kSecDbRowIdAttr || (attr->flags & kSecDbInAuthenticatedDataFlag);
 }
 
 CFTypeRef SecDbAttrCopyDefaultValue(const SecDbAttr *attr, CFErrorRef *error) {
@@ -208,6 +243,9 @@ CFTypeRef SecDbAttrCopyDefaultValue(const SecDbAttr *attr, CFErrorRef *error) {
         case kSecDbDataAttr:
             value = CFDataCreate(kCFAllocatorDefault, NULL, 0);
             break;
+        case kSecDbUUIDAttr:
+            value = CFDataCreate(kCFAllocatorDefault, NULL, 0);
+            break;
         case kSecDbNumberAttr:
         case kSecDbSyncAttr:
         case kSecDbTombAttr:
@@ -329,7 +367,7 @@ CFDataRef SecDbItemCopyEncryptedDataToBackup(SecDbItemRef item, uint64_t handle,
     if (attributes || auth_attributes) {
         SecAccessControlRef access_control = SecDbItemCopyAccessControl(item, error);
         if (access_control) {
-            if (ks_encrypt_data(keybag, access_control, item->credHandle, attributes, auth_attributes, &edata, error)) {
+            if (ks_encrypt_data(keybag, access_control, item->credHandle, attributes, auth_attributes, &edata, false, error)) {
                 item->_edataState = kSecDbItemEncrypting;
             } else {
                 seccritical("ks_encrypt_data (db): failed: %@", error ? *error : (CFErrorRef)CFSTR(""));
@@ -399,6 +437,9 @@ static CFTypeRef SecDbItemCopyValue(SecDbItemRef item, const SecDbAttr *attr, CF
                 value = kCFNull;
             }
             break;
+        case kSecDbUUIDAttr:
+            value = CFDataCreate(CFGetAllocator(item), NULL, 0);
+            break;
         case kSecDbNumberAttr:
         case kSecDbSyncAttr:
         case kSecDbTombAttr:
@@ -465,13 +506,27 @@ CFTypeRef SecDbItemGetValue(SecDbItemRef item, const SecDbAttr *desc, CFErrorRef
     return value;
 }
 
+CFTypeRef SecDbItemGetValueKind(SecDbItemRef item, SecDbAttrKind descKind, CFErrorRef *error) {
+    CFTypeRef result = NULL;
+
+    const SecDbClass * itemClass = SecDbItemGetClass(item);
+    const SecDbAttr * desc = SecDbClassAttrWithKind(itemClass, descKind, error);
+
+    if (desc) {
+        result = SecDbItemGetValue(item, desc, error);
+    }
+
+    return result;
+}
+
+
 // Similar as SecDbItemGetValue, but if attr represents attribute stored into DB field as hash, returns
 // hashed value for the attribute.
 static CFTypeRef SecDbItemCopyValueForDb(SecDbItemRef item, const SecDbAttr *desc, CFErrorRef *error) {
     CFTypeRef value = NULL;
     CFStringRef hash_name = NULL;
     hash_name = SecDbAttrGetHashName(desc);
-    if ((desc->flags & (kSecDbSHA1ValueInFlag | kSecDbInFlag)) != 0) {
+    if ((desc->flags & kSecDbSHA1ValueInFlag) && (desc->flags & kSecDbInFlag)) {
         value = CFRetainSafe(CFDictionaryGetValue(item->attributes, hash_name));
     }
 
@@ -554,6 +609,20 @@ static CFStringRef SecDbItemCopyFormatDescription(CFTypeRef cf, CFDictionaryRef
                         }
                     }
                     break;
+                case kSecDbUUIDAttr:
+                    if ((value = SecDbItemGetValue(item, attr, NULL))) {
+                        if (CFEqual(attr->name, kSecAttrMultiUser)) {
+                            if (isData(value)) {
+                                CFStringAppend(attrs, CFSTR(","));
+                                if (CFDataGetLength(value)) {
+                                    CFStringAppendHexData(attrs, value);
+                                } else {
+                                    CFStringAppend(attrs, attr->name);
+                                }
+                            }
+                        }
+                    }
+                    break;
                 case kSecDbCreationDateAttr:
                     // We don't care about this and every object has one.
                     break;
@@ -672,6 +741,7 @@ static SecDbItemRef SecDbItemCreate(CFAllocatorRef allocator, const SecDbClass *
     item->keybag = keybag;
     item->_edataState = kSecDbItemDirty;
     item->cryptoOp = kAKSKeyOpDecrypt;
+
     return item;
 }
 
@@ -753,6 +823,10 @@ bool SecDbItemSetValue(SecDbItemRef item, const SecDbAttr *desc, CFTypeRef value
             break;
         case kSecDbUTombAttr:
             attr = CFRetainSafe(asBoolean(value, NULL));
+            break;
+        case kSecDbUUIDAttr:
+            attr = copyUUID(value);
+            break;
     }
 
     if (attr) {
@@ -841,7 +915,8 @@ SecDbColumnCopyValueWithAttr(CFAllocatorRef allocator, sqlite3_stmt *stmt, const
                     value = SecDbColumnCopyDouble(allocator, stmt, col, error);
                     break;
                 case SQLITE_TEXT:
-                    value = SecDbColumnCopyString(allocator, stmt, col, error);
+                    value = SecDbColumnCopyString(allocator, stmt, col, error,
+                                                  attr->flags);
                     break;
                 case SQLITE_BLOB:
                     value = SecDbColumnCopyData(allocator, stmt, col, error);
@@ -853,9 +928,11 @@ SecDbColumnCopyValueWithAttr(CFAllocatorRef allocator, sqlite3_stmt *stmt, const
             break;
         case kSecDbAccessAttr:
         case kSecDbStringAttr:
-            value = SecDbColumnCopyString(allocator, stmt, col, error);
+            value = SecDbColumnCopyString(allocator, stmt, col, error,
+                                          attr->flags);
             break;
         case kSecDbDataAttr:
+        case kSecDbUUIDAttr:
         case kSecDbSHA1Attr:
         case kSecDbPrimaryKeyAttr:
         case kSecDbEncryptedDataAttr:
@@ -882,18 +959,19 @@ SecDbItemRef SecDbItemCreateWithStatement(CFAllocatorRef allocator, const SecDbC
     SecDbForEachAttr(class, attr) {
         if (return_attr(attr)) {
             CFTypeRef value = SecDbColumnCopyValueWithAttr(allocator, stmt, attr, col++, error);
-            if (value) {
-                CFDictionarySetValue(item->attributes, SecDbAttrGetHashName(attr), value);
-                CFRelease(value);
-            }
+            require_action_quiet(value, errOut, CFReleaseNull(item));
+
+            CFDictionarySetValue(item->attributes, SecDbAttrGetHashName(attr), value);
+            CFRelease(value);
         }
 
-        const SecDbAttr *data_attr = SecDbClassAttrWithKind(class, kSecDbEncryptedDataAttr, error);
+        const SecDbAttr *data_attr = SecDbClassAttrWithKind(class, kSecDbEncryptedDataAttr, NULL);
         if (data_attr != NULL && CFDictionaryGetValue(item->attributes, data_attr->name) != NULL) {
             item->_edataState = kSecDbItemEncrypted;
         }
     }
 
+errOut:
     return item;
 }
 
@@ -914,12 +992,12 @@ SecDbItemRef SecDbItemCreateWithEncryptedData(CFAllocatorRef allocator, const Se
 bool SecDbItemInV2(SecDbItemRef item) {
     const SecDbClass *iclass = SecDbItemGetClass(item);
     return  (SecDbItemGetCachedValueWithName(item, kSecAttrSyncViewHint) == NULL &&
-             (iclass == &genp_class || iclass == &inet_class || iclass == &keys_class || iclass == &cert_class));
+             (iclass == genp_class() || iclass == inet_class() || iclass == keys_class() || iclass == cert_class()));
 }
 
 // Return true iff an item for which SecDbItemIsSyncable() and SecDbItemInV2() already return true should be part of the v0 view.
 bool SecDbItemInV2AlsoInV0(SecDbItemRef item) {
-    return  (SecDbItemGetCachedValueWithName(item, kSecAttrTokenID) == NULL && SecDbItemGetClass(item) != &cert_class);
+    return  (SecDbItemGetCachedValueWithName(item, kSecAttrTokenID) == NULL && SecDbItemGetClass(item) != cert_class());
 }
 
 SecDbItemRef SecDbItemCopyWithUpdates(SecDbItemRef item, CFDictionaryRef updates, CFErrorRef *error) {
@@ -1000,6 +1078,18 @@ static SecDbItemRef SecDbItemCopyTombstone(SecDbItemRef item, CFBooleanRef makeT
     return new_item;
 }
 
+bool SecDbItemIsEngineInternalState(SecDbItemRef itemObject) {
+    // Only used for controlling logging
+    // Use agrp=com.apple.security.sos, since it is not encrypted
+    if (!itemObject) {
+        return false;
+    }
+    const SecDbAttr *agrp = SecDbAttrWithKey(SecDbItemGetClass(itemObject), kSecAttrAccessGroup, NULL);
+    CFTypeRef cfval = SecDbItemGetValue(itemObject, agrp, NULL);
+    return cfval && CFStringCompareSafe(cfval, kSOSInternalAccessGroup, NULL) == kCFCompareEqualTo;
+}
+
+
 // MARK: -
 // MARK: SQL Construction helpers -- These should become private in the future
 
@@ -1057,7 +1147,7 @@ SecDbAppendWhereOrAndIn(CFMutableStringRef sql, CFStringRef col, bool *needWhere
         return SecDbAppendWhereOrAndEquals(sql, col, needWhere);
     SecDbAppendWhereOrAnd(sql, needWhere);
     CFStringAppend(sql, col);
-    CFStringAppend(sql, CFSTR(" in ("));
+    CFStringAppend(sql, CFSTR(" IN ("));
     SecDbAppendCountArgsAndCloseParen(sql, count);
 }
 
@@ -1067,7 +1157,7 @@ SecDbAppendWhereOrAndNotIn(CFMutableStringRef sql, CFStringRef col, bool *needWh
         return SecDbAppendWhereOrAndNotEquals(sql, col, needWhere);
     SecDbAppendWhereOrAnd(sql, needWhere);
     CFStringAppend(sql, col);
-    CFStringAppend(sql, CFSTR(" not in ("));
+    CFStringAppend(sql, CFSTR(" NOT IN ("));
     SecDbAppendCountArgsAndCloseParen(sql, count);
 }
 
@@ -1138,7 +1228,7 @@ bool SecDbItemSetRowId(SecDbItemRef item, sqlite3_int64 rowid, CFErrorRef *error
     return ok;
 }
 
-static bool SecDbItemClearRowId(SecDbItemRef item, CFErrorRef *error) {
+bool SecDbItemClearRowId(SecDbItemRef item, CFErrorRef *error) {
     bool ok = true;
     const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbRowIdAttr, error);
     if (attr) {
@@ -1196,7 +1286,7 @@ static SecDbQueryRef SecDbQueryCreateWithItemPrimaryKey(SecDbItemRef item, CFErr
     if (!dict)
         return NULL;
 
-    SecDbQueryRef query = query_create(item->class, NULL, error);
+    SecDbQueryRef query = query_create(item->class, NULL, NULL, error);
     if (query) {
         CFReleaseSafe(query->q_item);
         query->q_item = dict;
@@ -1303,11 +1393,21 @@ static bool SecDbItemDoInsert(SecDbItemRef item, SecDbConnectionRef dbconn, CFEr
     if (ok) {
         secnotice("item", "inserted %@", item);
         SecDbItemRecordUpdate(dbconn, NULL, item);
+    } else {
+        if (SecDbItemIsEngineInternalState(item)) {
+            secdebug ("item", "insert failed for item %@ with %@", item, error ? *error : NULL);
+        } else {
+            secnotice("item", "insert failed for item %@ with %@", item, error ? *error : NULL);
+        }
     }
 
     return ok;
 }
 
+bool SecErrorIsSqliteDuplicateItemError(CFErrorRef error) {
+    return error && CFErrorGetCode(error) == SQLITE_CONSTRAINT && CFEqual(kSecDbErrorDomain, CFErrorGetDomain(error));
+}
+
 bool SecDbItemInsertOrReplace(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error, void(^duplicate)(SecDbItemRef item, SecDbItemRef *replace)) {
     __block CFErrorRef localError = NULL;
     __block bool ok = SecDbItemDoInsert(item, dbconn, &localError);
@@ -1421,7 +1521,13 @@ bool SecDbItemDoUpdate(SecDbItemRef old_item, SecDbItemRef new_item, SecDbConnec
         CFRelease(sql);
     }
     if (ok) {
-        secnotice("item", "replaced %@ with %@ in %@", old_item, new_item, dbconn);
+        if (SecDbItemIsEngineInternalState(old_item)) {
+            secdebug ("item", "replaced %@ in %@", old_item, dbconn);
+            secdebug ("item", "    with %@ in %@", new_item, dbconn);
+        } else {
+            secnotice("item", "replaced %@ in %@", old_item, dbconn);
+            secnotice("item", "    with %@ in %@", new_item, dbconn);
+        }
         SecDbItemRecordUpdate(dbconn, old_item, new_item);
     }
     return ok;
@@ -1503,7 +1609,7 @@ static bool SecDbItemDeleteTombstone(SecDbItemRef item, SecDbConnectionRef dbcon
 #endif
 
 // Replace old_item with new_item.  If primary keys are the same this does an update otherwise it does a delete + add
-bool SecDbItemUpdate(SecDbItemRef old_item, SecDbItemRef new_item, SecDbConnectionRef dbconn, CFBooleanRef makeTombstone, CFErrorRef *error) {
+bool SecDbItemUpdate(SecDbItemRef old_item, SecDbItemRef new_item, SecDbConnectionRef dbconn, CFBooleanRef makeTombstone, bool uuid_from_primary_key, CFErrorRef *error) {
     __block bool ok = true;
     __block CFErrorRef localError = NULL;
 
@@ -1515,6 +1621,11 @@ bool SecDbItemUpdate(SecDbItemRef old_item, SecDbItemRef new_item, SecDbConnecti
     bool pk_equal = ok && CFEqual(old_pk, new_pk);
     if (pk_equal) {
         ok = SecDbItemMakeYounger(new_item, old_item, error);
+    } else if(!CFEqualSafe(makeTombstone, kCFBooleanFalse) && SecCKKSIsEnabled()) {
+        // The primary keys aren't equal, and we're going to make a tombstone.
+        // Help CKKS out: the tombstone should have the existing item's UUID, and the newly updated item should have a new UUID.
+
+        s3dl_item_make_new_uuid(new_item, uuid_from_primary_key, error);
     }
     ok = ok && SecDbItemDoUpdate(old_item, new_item, dbconn, &localError, ^bool(const SecDbAttr *attr) {
         return attr->kind == kSecDbRowIdAttr;