X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_filedb/lib/DbValue.cpp?ds=inline diff --git a/libsecurity_filedb/lib/DbValue.cpp b/libsecurity_filedb/lib/DbValue.cpp new file mode 100644 index 00000000..fba7fc0b --- /dev/null +++ b/libsecurity_filedb/lib/DbValue.cpp @@ -0,0 +1,555 @@ +/* + * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. + * + * The contents of this file constitute Original Code as defined in and are + * subject to the Apple Public Source License Version 1.2 (the 'License'). + * You may not use this file except in compliance with the License. Please obtain + * a copy of the License at http://www.apple.com/publicsource and read it before + * using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS + * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT + * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the + * specific language governing rights and limitations under the License. + */ + + +// +// DbValue.cpp +// + +#include "DbValue.h" +#include + +// +// DbValue +// + +DbValue::~DbValue() +{ +} + +// +// UInt32Value +// + +UInt32Value::UInt32Value(const ReadSection &rs, uint32 &offset) +: BasicValue(rs.at(offset)) +{ + offset += size(); +} + +UInt32Value::UInt32Value(const CSSM_DATA &data) +{ + switch (data.Length) + { + case 1: + mValue = *reinterpret_cast(data.Data); + break; + case 2: + mValue = *reinterpret_cast(data.Data); + break; + case 4: + mValue = *reinterpret_cast(data.Data); + break; + default: + CssmError::throwMe(CSSMERR_DL_INVALID_VALUE); + } +} + +UInt32Value::~UInt32Value() +{ +} + +void +UInt32Value::pack(WriteSection &ws, uint32 &offset) const +{ + offset = ws.put(offset, mValue); +} + +// +// SInt32Value +// + +SInt32Value::SInt32Value(const ReadSection &rs, uint32 &offset) +: BasicValue(static_cast(rs.at(offset))) +{ + offset += size(); +} + +SInt32Value::SInt32Value(const CSSM_DATA &data) +{ + switch (data.Length) + { + case 1: + mValue = *reinterpret_cast(data.Data); + break; + case 2: + mValue = *reinterpret_cast(data.Data); + break; + case 4: + mValue = *reinterpret_cast(data.Data); + break; + default: + CssmError::throwMe(CSSMERR_DL_INVALID_VALUE); + } +} + +SInt32Value::~SInt32Value() +{ +} + +void +SInt32Value::pack(WriteSection &ws, uint32 &offset) const +{ + offset = ws.put(offset, static_cast(mValue)); +} + +// +// DoubleValue +// + +DoubleValue::DoubleValue(const ReadSection &rs, uint32 &offset) +{ + Range r(offset, size()); + mValue = *reinterpret_cast(rs.range(r)); + offset += size(); +} + +DoubleValue::DoubleValue(const CSSM_DATA &data) +{ + switch (data.Length) + { + case 4: + mValue = *reinterpret_cast(data.Data); + break; + case 8: + mValue = *reinterpret_cast(data.Data); + break; + default: + CssmError::throwMe(CSSMERR_DL_INVALID_VALUE); + } +} + +DoubleValue::~DoubleValue() +{ +} + +void +DoubleValue::pack(WriteSection &ws, uint32 &offset) const +{ + offset = ws.put(offset, size(), bytes()); +} + +// +// BlobValue +// + +BlobValue::BlobValue(const ReadSection &rs, uint32 &offset) +{ + Length = rs.at(offset); + Data = const_cast(rs.range(Range(offset + AtomSize, Length))); + offset = ReadSection::align(offset + Length + AtomSize); +} + +BlobValue::BlobValue(const CSSM_DATA &data) +: CssmData(CssmData::overlay(data)) +{ +} + +BlobValue::~BlobValue() +{ +} + +void +BlobValue::pack(WriteSection &ws, uint32 &offset) const +{ + offset = ws.put(offset, Length); + offset = ws.put(offset, Length, Data); +} + +BlobValue::Comparator::~Comparator() +{ +} + +int +BlobValue::Comparator::operator () (const uint8 *ptr1, const uint8 *ptr2, uint32 length) +{ + return memcmp(ptr1, ptr2, length); +} + +bool +BlobValue::evaluate(const BlobValue &other, CSSM_DB_OPERATOR op) const +{ + return evaluate(*this, other, op, Comparator()); +} + +bool +BlobValue::evaluate(const CssmData &inData1, const CssmData &inData2, CSSM_DB_OPERATOR op, + Comparator compare) +{ + uint32 length1 = inData1.Length, length2 = inData2.Length; + const uint8 *data1 = inData1.Data; + const uint8 *data2 = inData2.Data; + + switch (op) { + + case CSSM_DB_CONTAINS_INITIAL_SUBSTRING: + if (length1 > length2) + return false; + length2 = length1; + goto DB_EQUAL; + + case CSSM_DB_CONTAINS_FINAL_SUBSTRING: + if (length1 > length2) + return false; + data2 += (length2 - length1); + length2 = length1; + // dropthrough... + + case CSSM_DB_EQUAL: + DB_EQUAL: + if (length1 != length2) + return false; + if (length1 == 0) + return true; + return compare(data1, data2, length1) == 0; + + case CSSM_DB_NOT_EQUAL: + if (length1 != length2) + return true; + if (length1 == 0) + return false; + return compare(data1, data2, length1) != 0; + + case CSSM_DB_LESS_THAN: + case CSSM_DB_GREATER_THAN: + { + uint32 length = min(length1, length2); + int result = (length == 0) ? 0 : compare(data1, data2, length); + + if (result < 0 || (result == 0 && length1 < length2)) + return op == CSSM_DB_LESS_THAN; + else if (result > 0 || (result == 0 && length1 > length2)) + return op == CSSM_DB_GREATER_THAN; + break; + } + + case CSSM_DB_CONTAINS: + if (length1 > length2) + return false; + if (length1 == 0) + return true; + // Both buffers are at least 1 byte long. + for (const uint8 *data = data2; data + length1 <= data2 + length2; data++) + if (compare(data1, data, length1) == 0) + return true; + break; + + default: + CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY); + } + + return false; +} + +// +// TimeDateValue +// + +TimeDateValue::TimeDateValue(const ReadSection &rs, uint32 &offset) +{ + Length = kTimeDateSize; + Data = const_cast(rs.range(Range(offset, Length))); + offset = ReadSection::align(offset + Length); +} + +TimeDateValue::TimeDateValue(const CSSM_DATA &data) +: BlobValue(data) +{ + if (Length != kTimeDateSize || !isValidDate()) + CssmError::throwMe(CSSMERR_DL_INVALID_VALUE); +} + +TimeDateValue::~TimeDateValue() +{ +} + +void +TimeDateValue::pack(WriteSection &ws, uint32 &offset) const +{ + offset = ws.put(offset, Length, Data); +} + +bool +TimeDateValue::isValidDate() const +{ + if (Length != kTimeDateSize || Data[kTimeDateSize - 1] != 0 || + Data[kTimeDateSize - 2] != 'Z') + return false; + + for (uint32 i = 0; i < kTimeDateSize - 2; i++) + if (!isdigit(Data[i])) + return false; + + uint32 month = rangeValue(4, 2); + if (month < 1 || month > 12) + return false; + + uint32 day = rangeValue(6, 2); + if (day < 1 || day > 31) + return false; + + uint32 hour = rangeValue(8, 2); + if (hour < 0 || hour > 23) + return false; + + uint32 minute = rangeValue(10, 2); + if (minute < 0 || minute > 59) + return false; + + uint32 second = rangeValue(12, 2); + if (second < 0 || second > 59) + return false; + + return true; +} + +uint32 +TimeDateValue::rangeValue(uint32 start, uint32 length) const +{ + uint32 value = 0; + for (uint32 i = 0; i < length; i++) + value = value * 10 + Data[start + i] - '0'; + return value; +} + +// +// StringValue +// + +StringValue::StringValue(const ReadSection &rs, uint32 &offset) +: BlobValue(rs, offset) +{ +} + +StringValue::StringValue(const CSSM_DATA &data) +: BlobValue(data) +{ +} + +StringValue::~StringValue() +{ +} + +int +StringValue::Comparator::operator () (const uint8 *ptr1, const uint8 *ptr2, uint32 length) +{ + return strncmp(reinterpret_cast(ptr1), + reinterpret_cast(ptr2), length); +} + +bool +StringValue::evaluate(const StringValue &other, CSSM_DB_OPERATOR op) const +{ + return BlobValue::evaluate(*this, other, op, StringValue::Comparator()); +} + +// +// BigNumValue +// + +BigNumValue::BigNumValue(const ReadSection &rs, uint32 &offset) +: BlobValue(rs, offset) +{ +} + +BigNumValue::BigNumValue(const CSSM_DATA &data) +: BlobValue(data) +{ + // remove trailing zero bytes + while (Length > 1 && Data[Length - 1] == 0) + Length--; + + // if the number is zero (positive or negative), make the length zero + if (Length == 1 && (Data[0] & ~kSignBit) == 0) + Length = 0; +} + +BigNumValue::~BigNumValue() +{ +} + +// Walk the contents of two equal-sized bignums, moving backward +// from the high-order bytes, and return the comparison result +// ala memcmp. + +int +BigNumValue::compare(const uint8 *a, const uint8 *b, int length) +{ + for (int diff, i = length - 1; i >= 1; i--) + if ((diff = a[i] - b[i])) + return diff; + + // for the last (i.e. first) byte, mask out the sign bit + return (a[0] & ~kSignBit) - (b[0] & ~kSignBit); +} + +// Compare two bignums, assuming they are in canonical form (i.e., +// no bytes containing trailing zeros. + +bool +BigNumValue::evaluate(const BigNumValue &other, CSSM_DB_OPERATOR op) const +{ + uint32 length1 = Length, length2 = other.Length; + uint8 sign1 = length1 ? (Data[0] & kSignBit) : 0; + uint8 sign2 = length2 ? (other.Data[0] & kSignBit) : 0; + + switch (op) + { + case CSSM_DB_EQUAL: + case CSSM_DB_NOT_EQUAL: + return BlobValue::evaluate(other, op); + + case CSSM_DB_LESS_THAN: + if (sign1 ^ sign2) + // different signs: return true iff left value is the negative one + return sign1; + else if (length1 != length2) + // in canonical form, shorter numbers have smaller absolute value + return sign1 ? (length1 > length2) : (length1 < length2); + else { + // same length, same sign... + int c = compare(Data, other.Data, length1); + return sign1 ? (c > 0) : (c < 0); + } + break; + + case CSSM_DB_GREATER_THAN: + if (sign1 ^ sign2) + return sign2; + else if (length1 != length2) + return sign1 ? (length1 < length2) : (length1 > length2); + else { + int c = compare(Data, other.Data, length1); + return sign1 ? (c < 0) : (c > 0); + } + break; + + case CSSM_DB_CONTAINS: + case CSSM_DB_CONTAINS_INITIAL_SUBSTRING: + case CSSM_DB_CONTAINS_FINAL_SUBSTRING: + default: + CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY); + } +} + +// +// MultiUInt32Value +// + +MultiUInt32Value::MultiUInt32Value(const ReadSection &rs, uint32 &offset) +{ + // this is relatively expensive, since it copies the data from the + // read section to get the endianness correct + + mNumValues = rs.at(offset); + mValues = new uint32[mNumValues]; + + for (uint32 i = 0; i < mNumValues; i++) + mValues[i] = rs.at(offset + (i + 1) * AtomSize); + + offset = ReadSection::align(offset + (mNumValues + 1) * AtomSize); + mOwnsValues = true; +} + +MultiUInt32Value::MultiUInt32Value(const CSSM_DATA &data) +{ + if (data.Length & (sizeof(uint32) - 1)) + CssmError::throwMe(CSSMERR_DL_INVALID_VALUE); + + mNumValues = data.Length / sizeof(uint32); + mValues = reinterpret_cast(data.Data); + mOwnsValues = false; +} + +MultiUInt32Value::~MultiUInt32Value() +{ + if (mOwnsValues) + delete [] mValues; +} + +void +MultiUInt32Value::pack(WriteSection &ws, uint32 &offset) const +{ + offset = ws.put(offset, mNumValues); + for (uint32 i = 0; i < mNumValues; i++) + offset = ws.put(offset, mValues[i]); +} + +static inline int +uint32cmp(const uint32 *a, const uint32 *b, uint32 length) +{ + return memcmp(a, b, length * sizeof(uint32)); +} + +bool +MultiUInt32Value::evaluate(const MultiUInt32Value &other, CSSM_DB_OPERATOR op) const +{ + uint32 length1 = mNumValues, length2 = other.mNumValues; + const uint32 *values1 = mValues; + const uint32 *values2 = other.mValues; + + switch (op) + { + case CSSM_DB_EQUAL: + if (length1 == length2) + return uint32cmp(values1, values2, length1) == 0; + break; + + case CSSM_DB_NOT_EQUAL: + if (length1 != length2 || uint32cmp(values1, values2, length1)) + return true; + break; + + case CSSM_DB_CONTAINS_INITIAL_SUBSTRING: + if (length1 <= length2) + return uint32cmp(values1, values2, length1) == 0; + break; + + case CSSM_DB_CONTAINS_FINAL_SUBSTRING: + if (length1 <= length2) + return uint32cmp(values1, values2 + (length2 - length1), length1) == 0; + break; + + case CSSM_DB_CONTAINS: + if (length1 <= length2) { + + if (length1 == 0) + return true; + + for (const uint32 *values = values2; values + length1 < values2 + length2; values++) + if (uint32cmp(values1, values, length1) == 0) + return true; + } + break; + + case CSSM_DB_LESS_THAN: + // this is not required by the spec, but is required to sort indexes over + // multi uint32 keys... + if (length1 < length2) + return true; + else if (length1 == length2) + return uint32cmp(values1, values2, length1) < 0; + break; + + default: + CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY); + } + + return false; +} + +