Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

789 lines
22 KiB

  1. // V1ContRec.cpp -- definition of CV1ContainerRecord
  2. // (c) Copyright Schlumberger Technology Corp., unpublished work, created
  3. // 2000. This computer program includes Confidential, Proprietary
  4. // Information and is a Trade Secret of Schlumberger Technology Corp. All
  5. // use, disclosure, and/or reproduction is prohibited unless authorized
  6. // in writing. All Rights Reserved.
  7. //////////////////////////////////////////////////////////////////////
  8. #include "NoWarning.h"
  9. #include <scuArrayP.h>
  10. #include <slbCrc32.h>
  11. #include <iopPubBlob.h>
  12. #include <SmartCard.h>
  13. #include "TransactionWrap.h"
  14. #include "V1Paths.h"
  15. #include "V1Card.h"
  16. #include "V1ContRec.h"
  17. using namespace std;
  18. using namespace scu;
  19. using namespace cci;
  20. using namespace iop;
  21. /////////////////////////// LOCAL/HELPER /////////////////////////////////
  22. namespace
  23. {
  24. enum // KeyId in card
  25. {
  26. kidExchange = 0x00,
  27. kidSignature = 0x01,
  28. kidNone = 0xFF
  29. };
  30. TCHAR const
  31. szCachedCertSignature[] = TEXT("CERTSI");
  32. TCHAR const
  33. szCachedCertExchange[] = TEXT("CERTEX");
  34. TCHAR const
  35. szCachedPublicKeySignature[] = TEXT("PUBKSI");
  36. TCHAR const
  37. szCachedPublicKeyExchange[] = TEXT("PUBKEX");
  38. BYTE
  39. AsKeyId(KeySpec ks)
  40. {
  41. BYTE kid;
  42. switch (ks)
  43. {
  44. case ksExchange:
  45. kid = kidExchange;
  46. break;
  47. case ksSignature:
  48. kid = kidSignature;
  49. break;
  50. default:
  51. throw cci::Exception(cci::ccBadKeySpec);
  52. }
  53. return kid;
  54. }
  55. } // namespace
  56. /////////////////////////// PUBLIC /////////////////////////////////
  57. // Types
  58. // C'tors/D'tors
  59. CV1ContainerRecord::CV1ContainerRecord(CV1Card const &rv1card,
  60. string const &rsCntrType,
  61. CreateMode mode)
  62. : m_rcard(rv1card),
  63. m_sCntrType(rsCntrType),
  64. m_szKeyPath(0)
  65. {
  66. m_szKeyPath = IsDefault()
  67. ? CV1Paths::DefaultKey()
  68. : CV1Paths::DefaultContainer();
  69. switch (mode)
  70. {
  71. case cmNever:
  72. if (!Exists())
  73. throw Exception(ccInvalidParameter);
  74. break;
  75. case cmConditionally:
  76. if (!Exists())
  77. Create();
  78. break;
  79. case cmAlways:
  80. if (!Exists())
  81. Create();
  82. else
  83. throw Exception(ccOutOfSymbolTableEntries);
  84. break;
  85. case cmNoCheck:
  86. break;
  87. default:
  88. throw Exception(ccInvalidParameter);
  89. break;
  90. }
  91. }
  92. CV1ContainerRecord::~CV1ContainerRecord()
  93. {}
  94. // Operators
  95. // Operations
  96. string
  97. CV1ContainerRecord::ComputeSignature(KeySpec ks,
  98. string const &rsCipher) const
  99. {
  100. CTransactionWrap wrap(m_rcard);
  101. m_rcard.SmartCard().Select(m_szKeyPath);
  102. AutoArrayPtr<BYTE> aabBuffer(new BYTE[rsCipher.length()]);
  103. m_rcard.SmartCard().InternalAuth(ktRSA1024,
  104. AsKeyId(ks),
  105. static_cast<BYTE>(rsCipher.length()),
  106. reinterpret_cast<BYTE const *>(rsCipher.data()),
  107. aabBuffer.Get());
  108. return string(reinterpret_cast<char *>(aabBuffer.Get()),
  109. rsCipher.length());
  110. }
  111. void
  112. CV1ContainerRecord::Delete() const
  113. {
  114. CTransactionWrap wrap(m_rcard);
  115. // if (IsEmpty())
  116. // throw scu::OsException(NTE_BAD_KEYSET_PARAM);
  117. // Open the container file and find the offset of the container
  118. DWORD dwFileSize = OpenContainer();
  119. DWORD dwOffset = 0x00;
  120. DWORD dwLen = FindOffset(dwOffset);
  121. // Actually check the existence of key container
  122. if (sizeof ContainerBuffer > dwLen)
  123. throw scu::OsException(NTE_BAD_KEYSET);
  124. // Intialize search variables
  125. DWORD dwNext = dwOffset + dwLen;
  126. // Get following ContainerBuffer
  127. ContainerBuffer container;
  128. GetContainer(dwNext, container);
  129. dwLen = container.Size;
  130. // Move all following blocks up to deleted block position
  131. while (sizeof container <= dwLen)
  132. {
  133. basic_string<BYTE> bsBuffer(reinterpret_cast<BYTE *>(&container),
  134. sizeof container);
  135. WORD cPublicKeysLength = dwLen - sizeof container;
  136. AutoArrayPtr<BYTE> aabPublicKeys(new BYTE[cPublicKeysLength]);
  137. if (cPublicKeysLength > 0)
  138. {
  139. m_rcard.SmartCard().ReadBinary(dwNext + sizeof container,
  140. cPublicKeysLength,
  141. aabPublicKeys.Get());
  142. }
  143. bsBuffer.append(aabPublicKeys.Get(), cPublicKeysLength);
  144. m_rcard.SmartCard().WriteBinary(dwOffset,
  145. static_cast<WORD>(bsBuffer.length()),
  146. bsBuffer.data());
  147. dwOffset += dwLen;
  148. dwNext += dwLen;
  149. GetContainer(dwNext, container);
  150. dwLen = container.Size;
  151. }; // end while loop
  152. // NO MORE CONTAINERS TO MOVE UP
  153. // if there is still room put 2 null bytes of termination
  154. const BYTE NullSize[]= {0x00, 0x00};
  155. if ((dwOffset + 2) <= dwFileSize)
  156. m_rcard.SmartCard().WriteBinary(dwOffset, 2, NullSize);
  157. }
  158. void
  159. CV1ContainerRecord::Name(string const &rsNewName)
  160. {
  161. m_rcard.CardId(rsNewName);
  162. }
  163. void
  164. CV1ContainerRecord::Read(KeySpec ks,
  165. CPublicKeyBlob &rKeyBlob) const
  166. {
  167. CTransactionWrap wrap(m_rcard);
  168. if ((ksSignature != ks) && (ksExchange != ks))
  169. throw Exception(ccBadKeySpec);
  170. string sBuffer;
  171. DWORD dwExponent;
  172. if (GetContainerContent(ks, sBuffer, dwExponent))
  173. {
  174. CopyMemory(rKeyBlob.bModulus, sBuffer.data(),
  175. sBuffer.length());
  176. rKeyBlob.bModulusLength = static_cast<BYTE>(sBuffer.length());
  177. *reinterpret_cast<DWORD *>(rKeyBlob.bExponent) =
  178. dwExponent;
  179. }
  180. else
  181. rKeyBlob.bModulusLength = 0;
  182. }
  183. void
  184. CV1ContainerRecord::Read(KeySpec ks,
  185. string &rsBlob) const
  186. {
  187. CTransactionWrap wrap(m_rcard);
  188. DWORD dwOriginalCrc = 0;
  189. // Get blob from the container
  190. if (!GetContainerContent(ks,
  191. rsBlob,
  192. dwOriginalCrc))
  193. throw Exception(ccNoCertificate);
  194. // If a non-zero CRC exists, then verify integrity of the
  195. // compressed certificate by comparing the CRC read
  196. // (original) against a test one generated using the
  197. // compressed certificate read. If the CRCs aren't equal,
  198. // then the certificate is corrupted and it shouldn't be
  199. // decompressed because the decompress routine may go into
  200. // an infinite loop or otherwise fail badly without
  201. // notification. If the original CRC is zero, then a CRC
  202. // wasn't performed so for backward compatibility with
  203. // earlier versions the decompression is taken with the
  204. // inherent risk.
  205. if (0 != dwOriginalCrc)
  206. {
  207. DWORD dwTestCrc = Crc32(rsBlob.data(), rsBlob.length());
  208. if (dwTestCrc != dwOriginalCrc)
  209. throw Exception(ccSymbolDataCorrupted);
  210. }
  211. }
  212. void
  213. CV1ContainerRecord::Write(KeySpec ks,
  214. CPrivateKeyBlob const &rKeyBlob)
  215. {
  216. CTransactionWrap wrap(m_rcard);
  217. m_rcard.SmartCard().Select(CV1Paths::PrivateKeys());
  218. // Make sure that previous key blocks exists in Secret Key file
  219. // or at least the header of the block exists
  220. // in order for the Card OS to be able to retrieve the key
  221. // that is added in this procedure:
  222. // Write the header of previous keys
  223. WORD const wPrivateKeyBlockSize = 323;
  224. // inversion necessary for PRIVATE KEY BLOC SIZE
  225. WORD wBSize = (wPrivateKeyBlockSize >> 8) & 0x00FF;
  226. wBSize += (wPrivateKeyBlockSize << 8) & 0x00FF00;
  227. BYTE bId;
  228. DWORD dwOffset;
  229. BYTE bKeyId = AsKeyId(ks);
  230. for (dwOffset = 0x00, bId = 0;
  231. bId < bKeyId;
  232. bId++, dwOffset += wPrivateKeyBlockSize)
  233. {
  234. BYTE Header[3];
  235. CopyMemory(Header, &wBSize, sizeof WORD);
  236. Header[2] = bId + 1;
  237. m_rcard.SmartCard().WriteBinary(dwOffset, 3, Header);
  238. }
  239. m_rcard.SmartCard().WritePrivateKey(rKeyBlob, bKeyId);
  240. }
  241. void
  242. CV1ContainerRecord::Write(KeySpec ks,
  243. CPublicKeyBlob const &rKeyBlob)
  244. {
  245. CTransactionWrap wrap(m_rcard);
  246. DWORD dwExponent = *(reinterpret_cast<DWORD const *>(rKeyBlob.bExponent));
  247. Write(ks, reinterpret_cast<BYTE const *>(rKeyBlob.bModulus),
  248. rKeyBlob.bModulusLength, dwExponent);
  249. }
  250. void
  251. CV1ContainerRecord::Write(KeySpec ks,
  252. string const &rsBlob) const
  253. {
  254. CTransactionWrap wrap(m_rcard);
  255. // Calculate the CRC to verify when reading reading the
  256. // blob back.
  257. DWORD dwCrc = 0;
  258. if (rsBlob.length())
  259. dwCrc = Crc32(rsBlob.data(), rsBlob.length());
  260. Write(ks, reinterpret_cast<BYTE const *>(rsBlob.data()),
  261. static_cast<WORD>(rsBlob.length()), dwCrc);
  262. }
  263. // Access
  264. string
  265. CV1ContainerRecord::CertName()
  266. {
  267. static string const sCertContainerName("CERT");
  268. return sCertContainerName;
  269. }
  270. string
  271. CV1ContainerRecord::DefaultName()
  272. {
  273. static string const sDefaultName("USER");
  274. return sDefaultName;
  275. }
  276. string
  277. CV1ContainerRecord::Name() const
  278. {
  279. return m_rcard.CardId();
  280. }
  281. // Predicates
  282. bool
  283. CV1ContainerRecord::Exists() const
  284. {
  285. CTransactionWrap wrap(m_rcard);
  286. DWORD dwLen = 0;
  287. try
  288. {
  289. if (m_rcard.CardId() == m_sCntrType)
  290. return true;
  291. DWORD dwOffset = 0x00;
  292. dwLen = FindOffset(dwOffset);
  293. }
  294. catch (iop::CSmartCard::Exception &)
  295. {
  296. }
  297. return (sizeof ContainerBuffer <= dwLen);
  298. }
  299. bool
  300. CV1ContainerRecord::KeyExists(KeySpec ks) const
  301. {
  302. CTransactionWrap wrap(m_rcard);
  303. bool fExists = false;
  304. //
  305. // Does a key of this type exist in this container?
  306. // Note: assumes that m_KeyPath is set to correct container path?
  307. //
  308. // Open the container file
  309. DWORD dwFileSize = OpenContainer();
  310. DWORD dwOffset = 0x00;
  311. DWORD const dwLen = FindOffset(dwOffset);
  312. //
  313. // Actually check the existence of key container
  314. // by seeing if we have a record of the right size
  315. //
  316. ContainerBuffer container;
  317. if (sizeof container <= dwLen)
  318. {
  319. GetContainer(dwOffset, container);
  320. //
  321. // Check which key exists by checking lengths
  322. //
  323. switch (ks)
  324. {
  325. case ksExchange:
  326. if (0x00 < container.XK_wLen)
  327. fExists = true;
  328. break;
  329. case ksSignature:
  330. if (0x00 < container.SK_wLen)
  331. fExists = true;
  332. break;
  333. }
  334. }
  335. return fExists;
  336. }
  337. // Static Variables
  338. /////////////////////////// PROTECTED /////////////////////////////////
  339. // C'tors/D'tors
  340. // Operators
  341. // Operations
  342. // Access
  343. // Predicates
  344. // Static Variables
  345. /////////////////////////// PRIVATE /////////////////////////////////
  346. // C'tors/D'tors
  347. // Operators
  348. // Operations
  349. void
  350. CV1ContainerRecord::Create() const
  351. {
  352. // Open the file and find the offset to the container
  353. DWORD dwFileSize = OpenContainer();
  354. DWORD dwOffset = 0x00;
  355. DWORD dwLen = FindOffset(dwOffset);
  356. // Actually check the existence of key container
  357. if (sizeof ContainerBuffer <= dwLen)
  358. throw scu::OsException(NTE_EXISTS);
  359. // Set the new the container management data
  360. dwLen = SetContainer(dwOffset);
  361. // if there is still room put 2 null bytes of termination
  362. if ((dwOffset + dwLen + 2) <= dwFileSize)
  363. {
  364. const BYTE NullSize[] = { 0x00, 0x00 };
  365. m_rcard.SmartCard().WriteBinary(dwOffset + dwLen,
  366. sizeof NullSize, NullSize);
  367. }
  368. }
  369. DWORD
  370. CV1ContainerRecord::FindOffset(DWORD &rdwOffset) const
  371. {
  372. DWORD dwFileSize = OpenContainer();
  373. if ((rdwOffset + sizeof ContainerBuffer) > dwFileSize)
  374. return 0x00;
  375. bool fFound = false;
  376. DWORD dwLen = sizeof ContainerBuffer; // arbitrary value to start
  377. size_t const cBufferSize =
  378. sizeof WORD + (sizeof BYTE *
  379. ContainerBuffer::cMaxContainerNameLength) + 1;
  380. // +1 allows null terminator
  381. AutoArrayPtr<BYTE> aabBuffer(new BYTE[cBufferSize]);
  382. while (!fFound &&
  383. (0x00 < dwLen) &&
  384. ((rdwOffset + sizeof ContainerBuffer) <= dwFileSize))
  385. {
  386. m_rcard.SmartCard().ReadBinary(rdwOffset,
  387. cBufferSize - 1,
  388. aabBuffer.Get());
  389. WORD const *pwLen = reinterpret_cast<WORD *>(aabBuffer.Get());
  390. dwLen = *pwLen;
  391. aabBuffer[cBufferSize - 1] = 0x00; // ensure null terminate string
  392. string sName(reinterpret_cast<char *>(&aabBuffer[sizeof WORD]));
  393. if ((m_sCntrType == sName) && (0x00 < dwLen))
  394. fFound = true;
  395. else
  396. rdwOffset += dwLen;
  397. }
  398. if (fFound)
  399. return (dwLen & 0x00FFFF);
  400. else
  401. return 0x00;
  402. }
  403. void
  404. CV1ContainerRecord::GetContainer(DWORD dwOffset,
  405. ContainerBuffer &rcontainer) const
  406. {
  407. bool fClearContainer = true;
  408. try
  409. {
  410. DWORD dwFileSize = OpenContainer();
  411. if ((dwOffset + sizeof rcontainer) <= dwFileSize)
  412. {
  413. m_rcard.SmartCard().ReadBinary(dwOffset, sizeof rcontainer,
  414. reinterpret_cast<BYTE *>(&rcontainer));
  415. fClearContainer = false;
  416. }
  417. }
  418. catch (...)
  419. {
  420. }
  421. if (fClearContainer)
  422. {
  423. rcontainer.Size = 0x00;
  424. rcontainer.Name[0] = '\0';
  425. }
  426. }
  427. bool
  428. CV1ContainerRecord::GetContainerContent(KeySpec ks,
  429. string &rsBuffer,
  430. DWORD &rdwExponent) const
  431. {
  432. bool fExists = false;
  433. OpenContainer();
  434. DWORD dwOffset = 0x00;
  435. if (0x00 != FindOffset(dwOffset))
  436. {
  437. fExists = true;
  438. ContainerBuffer container;
  439. GetContainer(dwOffset, container);
  440. DWORD dwKeyLength = 0;
  441. AutoArrayPtr<BYTE> aabKey;
  442. if (ksExchange == ks)
  443. {
  444. if (0x00 < container.XK_wLen)
  445. {
  446. rdwExponent = container.XK_dwExp;
  447. dwKeyLength = container.XK_wLen;
  448. aabKey = AutoArrayPtr<BYTE>(new BYTE[container.XK_wLen]);
  449. m_rcard.SmartCard().ReadBinary(dwOffset + sizeof container,
  450. container.XK_wLen,
  451. aabKey.Get());
  452. }
  453. }
  454. else
  455. {
  456. if (0x00 < container.SK_wLen)
  457. {
  458. rdwExponent = container.SK_dwExp;
  459. dwKeyLength = container.SK_wLen;
  460. aabKey = AutoArrayPtr<BYTE>(new BYTE[container.SK_wLen]);
  461. m_rcard.SmartCard().ReadBinary(dwOffset +
  462. sizeof container +
  463. container.XK_wLen,
  464. container.SK_wLen,
  465. aabKey.Get());
  466. }
  467. }
  468. if (aabKey.Get())
  469. rsBuffer.assign(reinterpret_cast<char *>(aabKey.Get()),
  470. dwKeyLength);
  471. }
  472. return fExists;
  473. }
  474. DWORD
  475. CV1ContainerRecord::OpenContainer() const
  476. {
  477. DWORD dwFileSize;
  478. string sPath(m_szKeyPath);
  479. sPath.append("/");
  480. sPath.append(CV1Paths::RelativeContainers());
  481. dwFileSize = m_rcard.OpenFile(sPath.c_str());
  482. return dwFileSize;
  483. }
  484. DWORD
  485. CV1ContainerRecord::SetContainer(DWORD dwOffset) const
  486. {
  487. DWORD dwFileSize;
  488. dwFileSize = OpenContainer();
  489. if ((dwOffset + sizeof ContainerBuffer) > dwFileSize)
  490. throw Exception(ccOutOfSymbolTableSpace);
  491. // Create the container buffer
  492. ContainerBuffer container;
  493. ZeroMemory(&container, sizeof container);
  494. container.Size = sizeof container;
  495. CopyMemory(container.Name, m_sCntrType.data(), m_sCntrType.length());
  496. m_rcard.SmartCard().WriteBinary(dwOffset, sizeof container,
  497. reinterpret_cast<BYTE *>(&container));
  498. return container.Size;
  499. }
  500. void
  501. CV1ContainerRecord::Write(KeySpec ks,
  502. BYTE const *pbModulus,
  503. WORD wModulusLength,
  504. DWORD dwExponent) const
  505. {
  506. // Open container, get the data
  507. DWORD dwFileSize = OpenContainer();
  508. DWORD dwOffset = 0x00;
  509. DWORD dwLen = FindOffset(dwOffset);
  510. ContainerBuffer container;
  511. GetContainer(dwOffset, container);
  512. // Check which key exists
  513. AutoArrayPtr<BYTE> aabXKey(new BYTE[container.XK_wLen]);
  514. if (0x00 < container.XK_wLen)
  515. m_rcard.SmartCard().ReadBinary(dwOffset + sizeof container,
  516. container.XK_wLen,
  517. aabXKey.Get());
  518. AutoArrayPtr<BYTE> aabSKey(new BYTE[container.SK_wLen]);
  519. if (0x00 < container.SK_wLen)
  520. m_rcard.SmartCard().ReadBinary(dwOffset + sizeof container +
  521. container.XK_wLen,
  522. container.SK_wLen,
  523. aabSKey.Get());
  524. // Give an arbitrary value if key spec not specified
  525. if ((ksSignature != ks) && (ksExchange != ks))
  526. {
  527. if (0x00 == container.XK_wLen)
  528. ks = ksExchange;
  529. else
  530. {
  531. if (0x00 == container.SK_wLen)
  532. ks = ksSignature;
  533. else
  534. throw Exception(ccBadKeySpec);
  535. }
  536. }
  537. // Is it the last container of Container file?
  538. ContainerBuffer NextContainer;
  539. GetContainer(dwOffset + dwLen, NextContainer);
  540. bool fDeleted = false;
  541. if (sizeof NextContainer <= NextContainer.Size)
  542. {
  543. // Delete the existing container
  544. Delete();
  545. fDeleted = true;
  546. // No need to recreate it now
  547. }
  548. // Now the container is at the end of the Container file
  549. // Find the "NEW" offset of the container which may not exist anymore
  550. dwOffset = 0x00;
  551. FindOffset(dwOffset); // keep the INITIAL dwLen of the container
  552. // Check that there is enough room to put the new key
  553. bool fEnoughMemory = false;
  554. switch (ks)
  555. {
  556. case ksExchange:
  557. if ((dwOffset + dwLen - container.XK_wLen +
  558. wModulusLength) <= dwFileSize)
  559. {
  560. aabXKey = AutoArrayPtr<BYTE>(new BYTE[wModulusLength]);
  561. CopyMemory(aabXKey.Get(), pbModulus, wModulusLength);
  562. container.XK_dwExp = dwExponent;
  563. container.XK_wLen = wModulusLength;
  564. fEnoughMemory = true;
  565. }
  566. break;
  567. case ksSignature:
  568. if ((dwOffset + dwLen - container.SK_wLen +
  569. wModulusLength) <= dwFileSize)
  570. {
  571. aabSKey = AutoArrayPtr<BYTE>(new BYTE[wModulusLength]);
  572. CopyMemory(aabSKey.Get(), pbModulus, wModulusLength);
  573. container.SK_dwExp = dwExponent;
  574. container.SK_wLen = wModulusLength;
  575. fEnoughMemory = true;
  576. }
  577. break;
  578. }
  579. // Recreate the container buffer accounting for "card tearing"
  580. // where the card could be pulled during the write operation.
  581. // This is done using a type of transact and commit phases.
  582. // The container size is initially set to zero, then the container
  583. // contents are written (transaction), followed by resetting the
  584. // container size to the actual length of the container to
  585. // "commit" the changes to the card.
  586. container.Size = 0;
  587. DWORD const dwTrueSize = sizeof container + container.XK_wLen +
  588. container.SK_wLen;
  589. size_t cBufferSize = dwTrueSize;
  590. BYTE const abNull[] = {0x00,0x00};
  591. bool fAppendNull = (dwTrueSize + sizeof abNull) <= dwFileSize;
  592. if (fAppendNull)
  593. cBufferSize += sizeof abNull;
  594. AutoArrayPtr<BYTE> aabBuffer(new BYTE[cBufferSize]);
  595. BYTE *pbBuffer = aabBuffer.Get();
  596. CopyMemory(pbBuffer, &container, sizeof container);
  597. pbBuffer += sizeof container;
  598. CopyMemory(pbBuffer, aabXKey.Get(), container.XK_wLen);
  599. pbBuffer += container.XK_wLen;
  600. CopyMemory(pbBuffer, aabSKey.Get(), container.SK_wLen);
  601. pbBuffer += container.SK_wLen;
  602. if (fAppendNull)
  603. {
  604. CopyMemory(pbBuffer, abNull, sizeof abNull);
  605. pbBuffer += sizeof abNull;
  606. }
  607. // Rewrite the container even if there is not enough to write the
  608. // NEW public key, then there should be enough room to write the
  609. // existing key.
  610. m_rcard.SmartCard().WriteBinary(dwOffset,
  611. pbBuffer - aabBuffer.Get(),
  612. aabBuffer.Get());
  613. // Now commit these changes with the actual size.
  614. container.Size = dwTrueSize;
  615. m_rcard.SmartCard().WriteBinary(dwOffset, sizeof container.Size,
  616. reinterpret_cast<BYTE *>(&container));
  617. if (!fEnoughMemory)
  618. throw Exception(ccOutOfSymbolTableSpace);
  619. }
  620. // Access
  621. // Predicates
  622. bool
  623. CV1ContainerRecord::IsDefault() const
  624. {
  625. return (DefaultName() == m_sCntrType);
  626. }
  627. // Static Variables