]> git.saurik.com Git - apple/security.git/blame - OSX/libsecurity_apple_cspdl/lib/SSDatabase.cpp
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / libsecurity_apple_cspdl / lib / SSDatabase.cpp
CommitLineData
b1ab9ed8 1/*
d8f41ccd 2 * Copyright (c) 2000-2001,2011-2014 Apple Inc. All Rights Reserved.
b1ab9ed8
A
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19//
20// SSDatabase.cpp - Security Server database object
21//
22#include "SSDatabase.h"
23
24#include <security_cdsa_utilities/KeySchema.h>
e3d460c9
A
25#include <security_utilities/CSPDLTransaction.h>
26#include <Security/SecBasePriv.h>
b1ab9ed8
A
27
28using namespace CssmClient;
29using namespace SecurityServer;
30
31const char *const SSDatabaseImpl::DBBlobRelationName = "DBBlob";
32
33
34SSDatabaseImpl::SSDatabaseImpl(ClientSession &inClientSession, const CssmClient::DL &dl,
35 const char *inDbName, const CSSM_NET_ADDRESS *inDbLocation)
36 : Db::Impl(dl, inDbName, inDbLocation), mClientSession(inClientSession), mSSDbHandle(noDb)
37{
e3d460c9 38 mTransaction = NULL;
b1ab9ed8
A
39}
40
41SSDatabaseImpl::~SSDatabaseImpl()
427c49bc 42try
b1ab9ed8
A
43{
44 if (mSSDbHandle != noDb)
45 mClientSession.releaseDb(mSSDbHandle);
46}
427c49bc
A
47catch (...)
48{
49}
b1ab9ed8
A
50
51SSUniqueRecord
fa7225c8 52SSDatabaseImpl::ssInsert(CSSM_DB_RECORDTYPE recordType,
b1ab9ed8 53 const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes,
fa7225c8 54 const CSSM_DATA *data)
b1ab9ed8
A
55{
56 SSUniqueRecord uniqueId(SSDatabase(this));
57 check(CSSM_DL_DataInsert(handle(), recordType,
58 attributes,
59 data, uniqueId));
60 // Activate uniqueId so CSSM_DL_FreeUniqueRecord() gets called when it goes out of scope.
61 uniqueId->activate();
62 return uniqueId;
63}
64
65void
66SSDatabaseImpl::authenticate(CSSM_DB_ACCESS_TYPE inAccessRequest,
67 const CSSM_ACCESS_CREDENTIALS *inAccessCredentials)
68{
69 mClientSession.authenticateDb(dbHandle(), inAccessRequest,
70 AccessCredentials::overlay(inAccessCredentials));
71}
72
73void
74SSDatabaseImpl::lock()
75{
76 mClientSession.lock(dbHandle());
77
78}
79
80void
81SSDatabaseImpl::unlock()
82{
83 mClientSession.unlock(dbHandle());
84}
85
86void
87SSDatabaseImpl::unlock(const CSSM_DATA &password)
88{
89 mClientSession.unlock(dbHandle(), CssmData::overlay(password));
90}
91
427c49bc
A
92void
93SSDatabaseImpl::stash()
94{
95 mClientSession.stashDb(dbHandle());
96}
97
98void
99SSDatabaseImpl::stashCheck()
100{
101 mClientSession.stashDbCheck(dbHandle());
102}
103
b1ab9ed8
A
104void
105SSDatabaseImpl::getSettings(uint32 &outIdleTimeout, bool &outLockOnSleep)
106{
107 DBParameters parameters;
108 mClientSession.getDbParameters(dbHandle(), parameters);
109 outIdleTimeout = parameters.idleTimeout;
110 outLockOnSleep = parameters.lockOnSleep;
111}
112
113void
114SSDatabaseImpl::setSettings(uint32 inIdleTimeout, bool inLockOnSleep)
115{
116 DBParameters parameters;
117 parameters.idleTimeout = inIdleTimeout;
118 parameters.lockOnSleep = inLockOnSleep;
119 mClientSession.setDbParameters(dbHandle(), parameters);
120
121 // Reencode the db blob.
122 CssmDataContainer dbb(allocator());
123 mClientSession.encodeDb(mSSDbHandle, dbb, allocator());
124 getDbBlobId()->modify(DBBlobRelationID, NULL, &dbb, CSSM_DB_MODIFY_ATTRIBUTE_NONE);
125}
126
127bool
128SSDatabaseImpl::isLocked()
129{
130 return mClientSession.isLocked(dbHandle());
131}
132
133void
134SSDatabaseImpl::changePassphrase(const CSSM_ACCESS_CREDENTIALS *cred)
135{
136 mClientSession.changePassphrase(dbHandle(), AccessCredentials::overlay(cred));
137
138 // Reencode the db blob.
139 CssmDataContainer dbb(allocator());
140 mClientSession.encodeDb(mSSDbHandle, dbb, allocator());
141 getDbBlobId()->modify(DBBlobRelationID, NULL, &dbb, CSSM_DB_MODIFY_ATTRIBUTE_NONE);
142}
143
144DbHandle
145SSDatabaseImpl::dbHandle()
146{
147 activate();
148 if (mForked()) {
149 // re-establish the dbHandle with the SecurityServer
150 CssmDataContainer dbb(allocator());
151 getDbBlobId(&dbb);
152 mSSDbHandle = mClientSession.decodeDb(mIdentifier,
153 AccessCredentials::overlay(accessCredentials()), dbb);
154 }
155 return mSSDbHandle;
156}
157
158void
159SSDatabaseImpl::commonCreate(const DLDbIdentifier &dlDbIdentifier, bool &autoCommit)
160{
161 mIdentifier = dlDbIdentifier;
162 // Set to false if autocommit should remain off after the create.
163 autoCommit = true;
164
165 // OpenParameters to use
166 CSSM_APPLEDL_OPEN_PARAMETERS newOpenParameters =
167 {
168 sizeof(CSSM_APPLEDL_OPEN_PARAMETERS),
169 CSSM_APPLEDL_OPEN_PARAMETERS_VERSION,
170 CSSM_FALSE, // do not auto-commit
171 0 // mask - do not use following fields
172 };
173
174 // Get the original openParameters and apply them to the ones we
175 // are passing in.
176 const CSSM_APPLEDL_OPEN_PARAMETERS *inOpenParameters =
177 reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS *>(openParameters());
178 if (inOpenParameters)
179 {
180 switch (inOpenParameters->version)
181 {
182 case 1:
183 if (inOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS))
184 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
185
186 newOpenParameters.mask = inOpenParameters->mask;
187 newOpenParameters.mode = inOpenParameters->mode;
188 /*DROPTHROUGH*/
189 case 0:
190 //if (inOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0))
191 // CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
192
193 // This will determine whether we leave autocommit off or not.
194 autoCommit = inOpenParameters->autoCommit == CSSM_FALSE ? false : true;
195 break;
196
197 default:
198 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
199 }
200 }
201
202 // Use the new openParameters
203 openParameters(&newOpenParameters);
204 try
205 {
206 DbImpl::create();
207 // Restore the original openparameters again.
208 openParameters(inOpenParameters);
209 }
210 catch (...)
211 {
212 // Make sure restore the original openparameters again even if
213 // create throws.
214 openParameters(inOpenParameters);
215 throw;
216 }
217
218 // @@@ The CSSM_DB_SCHEMA_ATTRIBUTE_INFO and CSSM_DB_SCHEMA_INDEX_INFO
219 // arguments should be optional.
220 createRelation(DBBlobRelationID, DBBlobRelationName,
221 0, (CSSM_DB_SCHEMA_ATTRIBUTE_INFO *)42,
222 0, (CSSM_DB_SCHEMA_INDEX_INFO *)42);
223
224 // @@@ Only iff not already in mDbInfo
225 createRelation(CSSM_DL_DB_RECORD_PUBLIC_KEY, "CSSM_DL_DB_RECORD_PUBLIC_KEY",
226 KeySchema::KeySchemaAttributeCount, KeySchema::KeySchemaAttributeList,
227 KeySchema::KeySchemaIndexCount, KeySchema::KeySchemaIndexList);
228
229 // @@@ Only iff not already in mDbInfo
230 createRelation(CSSM_DL_DB_RECORD_PRIVATE_KEY, "CSSM_DL_DB_RECORD_PRIVATE_KEY",
231 KeySchema::KeySchemaAttributeCount, KeySchema::KeySchemaAttributeList,
232 KeySchema::KeySchemaIndexCount, KeySchema::KeySchemaIndexList);
233
234 // @@@ Only iff not already in mDbInfo
235 createRelation(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, "CSSM_DL_DB_RECORD_SYMMETRIC_KEY",
236 KeySchema::KeySchemaAttributeCount, KeySchema::KeySchemaAttributeList,
237 KeySchema::KeySchemaIndexCount, KeySchema::KeySchemaIndexList);
238}
239
240void
fa7225c8 241SSDatabaseImpl::ssCreate(const DLDbIdentifier &dlDbIdentifier)
b1ab9ed8
A
242{
243 try
244 {
245 bool autoCommit;
246 commonCreate(dlDbIdentifier, autoCommit);
247
248 DBParameters dbParameters;
249 memset(&dbParameters, 0, sizeof(DBParameters));
250 dbParameters.idleTimeout = kDefaultIdleTimeout;
251 dbParameters.lockOnSleep = kDefaultLockOnSleep;
252
253 const AccessCredentials *cred = NULL;
254 const AclEntryInput *owner = NULL;
255 if (resourceControlContext())
256 {
257 cred = AccessCredentials::overlay(resourceControlContext()->AccessCred);
258 owner = &AclEntryInput::overlay(resourceControlContext()->InitialAclEntry);
259 }
260 mSSDbHandle = mClientSession.createDb(dlDbIdentifier, cred, owner, dbParameters);
261 CssmDataContainer dbb(allocator());
262 mClientSession.encodeDb(mSSDbHandle, dbb, allocator());
fa7225c8 263 secnotice("integrity", "opening %s", name());
b1ab9ed8
A
264 Db::Impl::insert(DBBlobRelationID, NULL, &dbb);
265 if (autoCommit)
266 {
267 passThrough(CSSM_APPLEFILEDL_COMMIT, NULL);
268 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
269 reinterpret_cast<const void *>(1));
270 }
271 }
272 catch(CssmError e)
273 {
274 if (e.error != CSSMERR_DL_DATASTORE_ALREADY_EXISTS)
275 {
276 DbImpl::deleteDb();
277 }
278 throw;
279 }
280 catch(...)
281 {
282 DbImpl::deleteDb();
283 throw;
284 }
285}
286
287void
fa7225c8 288SSDatabaseImpl::ssCreateWithBlob(const DLDbIdentifier &dlDbIdentifier, const CSSM_DATA &blob)
b1ab9ed8
A
289{
290 try
291 {
292 bool autoCommit;
293 commonCreate(dlDbIdentifier, autoCommit);
fa7225c8 294 secnotice("integrity", "opening %s", name());
b1ab9ed8
A
295 Db::Impl::insert(DBBlobRelationID, NULL, &blob);
296 if (autoCommit)
297 {
298 passThrough(CSSM_APPLEFILEDL_COMMIT, NULL);
299 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
300 reinterpret_cast<const void *>(1));
301 }
302 }
303 catch(...)
304 {
305 DbImpl::deleteDb();
306 throw;
307 }
308}
309
310void
fa7225c8 311SSDatabaseImpl::ssOpen(const DLDbIdentifier &dlDbIdentifier)
b1ab9ed8 312{
fa7225c8 313 load(dlDbIdentifier);
b1ab9ed8 314
fa7225c8
A
315 CssmDataContainer dbb(allocator());
316 getDbBlobId(&dbb);
e3d460c9
A
317
318 // Pull our version out of the database blob
b1ab9ed8
A
319 mSSDbHandle = mClientSession.decodeDb(dlDbIdentifier, AccessCredentials::overlay(accessCredentials()), dbb);
320}
321
322void
fa7225c8
A
323SSDatabaseImpl::load(const DLDbIdentifier &dlDbIdentifier) {
324 mIdentifier = dlDbIdentifier;
325 Db::Impl::open();
326
327 CssmDataContainer dbb(allocator());
328 getDbBlobId(&dbb);
329
330 secnotice("integrity", "loading %s", name());
331}
332
333void
334SSDatabaseImpl::ssRecode(const CssmData &dbHandleArray, const CssmData &agentData)
b1ab9ed8 335{
e3d460c9
A
336 // Start a transaction (Implies activate()).
337 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 0);
338
339 try
340 {
341 CssmDataContainer dbb(allocator());
342 // Make sure mSSDbHandle is valid.
343 CssmClient::DbUniqueRecord dbBlobId = getDbBlobId(&dbb);
344 if (mForked()) {
345 // re-establish the dbHandle with the SecurityServer
346 mSSDbHandle = mClientSession.decodeDb(mIdentifier,
347 AccessCredentials::overlay(accessCredentials()), dbb);
348 }
349 dbb.clear();
350
351 DbHandle successfulHdl = mClientSession.authenticateDbsForSync(dbHandleArray, agentData);
352
353 // Create a newDbHandle using the master secrets from the dbBlob we are
354 // recoding to.
355 SecurityServer::DbHandle clonedDbHandle =
356 mClientSession.recodeDbForSync(successfulHdl, mSSDbHandle);
357
358 // @@@ If the dbb changed since we fetched it we should abort or
359 // retry the operation here.
360
361 recodeHelper(clonedDbHandle, dbBlobId);
362
363 // Commit the transaction to the db
364 passThrough(CSSM_APPLEFILEDL_COMMIT, NULL);
365 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
366 reinterpret_cast<const void *>(1));
367 }
368 catch (...)
369 {
370 // Something went wrong rollback the transaction
371 passThrough(CSSM_APPLEFILEDL_ROLLBACK, NULL);
372 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
373 reinterpret_cast<const void *>(1));
374 throw;
375 }
376}
b1ab9ed8 377
e3d460c9
A
378uint32
379SSDatabaseImpl::recodeDbToVersion(uint32 newBlobVersion) {
380 // Start a transaction (Implies activate()).
381 DLTransaction transaction(handle());
382
383 try
384 {
385 if(isLocked()) {
fa7225c8 386 secnotice("integrity", "is currently locked");
e3d460c9 387 } else {
fa7225c8 388 secnotice("integrity", "is already unlocked");
e3d460c9
A
389 }
390
391 CssmDataContainer dbb(allocator());
392 // Make sure mSSDbHandle is valid.
393 CssmClient::DbUniqueRecord dbBlobId = getDbBlobId(&dbb);
394
395 // always establish the dbHandle with the SecurityServer
396 mSSDbHandle = mClientSession.decodeDb(mIdentifier, AccessCredentials::overlay(accessCredentials()), dbb);
397 dbb.clear();
398
399 // Create a newDbHandle using the master secrets from the dbBlob we are recoding to.
fa7225c8 400 secnotice("integrity", "recoding db with handle %d", mSSDbHandle);
e3d460c9 401 SecurityServer::DbHandle clonedDbHandle = mClientSession.recodeDbToVersion(newBlobVersion, mSSDbHandle);
fa7225c8 402 secnotice("integrity", "received db with handle %d", clonedDbHandle);
e3d460c9
A
403
404 // @@@ If the dbb changed since we fetched it we should abort or
405 // retry the operation here.
406
407 uint32 newBlobVersion = recodeHelper(clonedDbHandle, dbBlobId);
fa7225c8 408 secnotice("integrity", "committing transaction %d", clonedDbHandle);
e3d460c9
A
409
410 // Commit the transaction to the db
fa7225c8 411 transaction.commit();
e3d460c9
A
412 return newBlobVersion;
413 }
414 catch (...)
415 {
416 throw;
417 }
418}
b1ab9ed8 419
fa7225c8
A
420void
421SSDatabaseImpl::recodeFinished() {
422 mClientSession.recodeFinished(mSSDbHandle);
423}
424
e3d460c9
A
425void SSDatabaseImpl::takeFileLock() {
426 if(mTransaction) {
427 // you're already in the middle of a file lock.
428 return;
429 }
430 mTransaction = new DLTransaction(handle());
431 passThrough(CSSM_APPLEFILEDL_TAKE_FILE_LOCK, NULL, NULL);
432}
b1ab9ed8 433
e3d460c9
A
434void SSDatabaseImpl::releaseFileLock(bool success) {
435 if(mTransaction) {
436 try {
437 if(success) {
fa7225c8 438 mTransaction->commit();
e3d460c9 439 }
fa7225c8 440 // If we didn't commit, the destructor will roll back and re-enable autocommit
e3d460c9
A
441 delete mTransaction;
442 mTransaction = NULL;
443 } catch(...) {
444 mTransaction = NULL;
445 throw;
446 }
447 }
448}
b1ab9ed8 449
e3d460c9
A
450void SSDatabaseImpl::makeBackup() {
451 passThrough(CSSM_APPLEFILEDL_MAKE_BACKUP, NULL, NULL);
452}
b1ab9ed8 453
fa7225c8
A
454void SSDatabaseImpl::makeCopy(const char* path) {
455 passThrough(CSSM_APPLEFILEDL_MAKE_COPY, path, NULL);
456}
457
458void SSDatabaseImpl::deleteFile() {
459 passThrough(CSSM_APPLEFILEDL_DELETE_FILE, NULL, NULL);
460}
461
462SSDatabase SSDatabaseImpl::ssCloneTo(const DLDbIdentifier& dldbidentifier) {
463 makeCopy(dldbidentifier.dbName());
464 SSDatabase db(mClientSession, dl(), dldbidentifier.dbName(), dldbidentifier.dbLocation());
465
466 db->load(dldbidentifier);
467 db->mSSDbHandle = mClientSession.cloneDb(dldbidentifier, mSSDbHandle);
468
469 return db;
470}
471
472
b1ab9ed8 473
e3d460c9
A
474uint32 SSDatabaseImpl::recodeHelper(SecurityServer::DbHandle clonedDbHandle, CssmClient::DbUniqueRecord& dbBlobId) {
475 // Recode all keys
476 DbCursor cursor(SSDatabase(this));
477 cursor->recordType(CSSM_DL_DB_RECORD_ALL_KEYS);
478 CssmDataContainer keyBlob(allocator());
479 CssmClient::DbUniqueRecord keyBlobId;
480 DbAttributes attributes;
481 while (cursor->next(&attributes, &keyBlob, keyBlobId))
482 {
483 KeyHandle keyHandle = 0;
484 try {
485 // Decode the old key
486 CssmKey::Header header;
487 keyHandle = mClientSession.decodeKey(mSSDbHandle, keyBlob, header);
488 // Recode the key
489 CssmDataContainer newKeyBlob(mClientSession.returnAllocator);
490 mClientSession.recodeKey(mSSDbHandle, keyHandle, clonedDbHandle, newKeyBlob);
491 mClientSession.releaseKey(keyHandle);
492 keyHandle = 0;
493 // Write the recoded key blob to the database
494 keyBlobId->modify(attributes.recordType(), NULL, &newKeyBlob,
495 CSSM_DB_MODIFY_ATTRIBUTE_NONE);
496 } catch (CssmError cssme) {
497 const char* errStr = cssmErrorString(cssme.error);
fa7225c8
A
498 secnotice("integrity", "corrupt item while recoding: %d %s", (int) cssme.error, errStr);
499 secnotice("integrity", "deleting corrupt item");
e3d460c9
A
500
501
502 keyBlobId->deleteRecord();
503
504 if(keyHandle != 0) {
505 // tell securityd not to worry about this key again
506 try {
fa7225c8 507 secnotice("integrity", "releasing corrupt key");
e3d460c9
A
508 mClientSession.releaseKey(keyHandle);
509 } catch(CssmError cssme) {
510 // swallow the error
511 const char* errStr = cssmErrorString(cssme.error);
fa7225c8 512 secnotice("integrity", "couldn't release corrupt key: %d %s", (int) cssme.error, errStr);
e3d460c9
A
513 }
514 }
515 }
516 }
517
518 // Commit the new blob to securityd, reencode the db blob, release the
519 // cloned db handle and commit the new blob to the db.
520 CssmDataContainer dbb(allocator());
fa7225c8 521 secnotice("integrity", "committing %d", clonedDbHandle);
e3d460c9
A
522 mClientSession.commitDbForSync(mSSDbHandle, clonedDbHandle,
523 dbb, allocator());
524 dbBlobId->modify(DBBlobRelationID, NULL, &dbb,
525 CSSM_DB_MODIFY_ATTRIBUTE_NONE);
526 return getDbVersionFromBlob(dbb);
b1ab9ed8
A
527}
528
e3d460c9 529
b1ab9ed8
A
530void SSDatabaseImpl::getRecordIdentifier(CSSM_DB_UNIQUE_RECORD_PTR uniqueRecord, CSSM_DATA &recordID)
531{
532 // the unique ID is composed of three uint32s (plus one filler word). Pull
533 // them out and byte swap them
534 recordID.Length = sizeof (uint32) * kNumIDWords;
535 recordID.Data = (uint8*) allocator().malloc(recordID.Length);
536
537 // copy the data
538 uint32* dest = (uint32*) recordID.Data;
539 uint32* src = (uint32*) uniqueRecord->RecordIdentifier.Data;
540
541 dest[0] = htonl (src[0]);
542 dest[1] = htonl (src[1]);
543 dest[2] = htonl (src[2]);
544 dest[3] = 0;
545}
546
fa7225c8 547void SSDatabaseImpl::ssCopyBlob(CSSM_DATA& data)
b1ab9ed8
A
548{
549 // get the blob from the database
550 CssmDataContainer dbb(allocator());
551 getDbBlobId(&dbb);
552
553 // copy the data back
554 data.Data = dbb.Data;
555 data.Length = dbb.Length;
556
557 // zap the return structure so that we don't get zapped when dbb goes out of scope...
558 dbb.Data = NULL;
559 dbb.Length = 0;
560}
561
e3d460c9
A
562uint32
563SSDatabaseImpl::dbBlobVersion() {
564 CssmDataContainer dbb(allocator());
565 getDbBlobId(&dbb);
566 return getDbVersionFromBlob(dbb);
567}
568
569uint32
570SSDatabaseImpl::getDbVersionFromBlob(const CssmData& dbb) {
571 DbBlob* x = Allocator::standard().malloc<DbBlob>(dbb.length());
572 memcpy(x, dbb, dbb.length());
573 uint32 version = x->version();
574 Allocator::standard().free(x);
575 return version;
576}
577
b1ab9ed8
A
578DbUniqueRecordImpl *
579SSDatabaseImpl::newDbUniqueRecord()
580{
581 return new SSUniqueRecordImpl(SSDatabase(this));
582}
583
584CssmClient::DbUniqueRecord
585SSDatabaseImpl::getDbBlobId(CssmDataContainer *dbb)
586{
587 CssmClient::DbUniqueRecord dbBlobId;
588
589 DbCursor cursor(SSDatabase(this));
590 cursor->recordType(DBBlobRelationID);
591 if (!cursor->next(NULL, dbb, dbBlobId))
592 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
593
594 return dbBlobId;
595}
596
597
598
599SSUniqueRecordImpl::SSUniqueRecordImpl(const SSDatabase &db)
600: DbUniqueRecord::Impl(db)
601{
602}
603
604SSUniqueRecordImpl::~SSUniqueRecordImpl()
605{
606}
607
608SSDatabase
609SSUniqueRecordImpl::database() const
610{
611 return parent<SSDatabase>();
612}