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.

1429 lines
40 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1998 - 1999
  3. Module Name:
  4. Abstract:
  5. Functions to manipulate SSL-tyle principal names and certificates
  6. Author:
  7. Jeff Roberts
  8. Revisions:
  9. Jeff Roberts (jroberts) 1-20-1998
  10. created the file
  11. --*/
  12. #include <precomp.hxx>
  13. #include <rpcssl.h>
  14. #include <cryptimp.hxx>
  15. #include <CharConv.hxx>
  16. #define INITIAL_NAME_LENGTH 100
  17. // The prefix lengths do not include the NULL char. Actual strings are one
  18. // character longer. This is generally OK, since these strings are prefixes and
  19. // the NULL char gets overwritten.
  20. #define MSSTD_PREFIX_LENGTH 6
  21. const RPC_CHAR MSSTD_PREFIX[] = RPC_T("msstd:");
  22. #define FULLPATH_PREFIX_LENGTH 8
  23. const RPC_CHAR FULLPATH_PREFIX[] = RPC_T("fullsic:");
  24. //------------------------------------------------------------------------
  25. DWORD
  26. AddComponentName( RPC_CHAR * * pBuffer,
  27. unsigned long * pBufferLength,
  28. unsigned long * pCursor,
  29. CERT_NAME_BLOB * Name
  30. );
  31. DWORD
  32. RpcCertMatchPrincipalName(
  33. PCCERT_CONTEXT Context,
  34. RPC_CHAR PrincipalName[]
  35. );
  36. DWORD
  37. MatchFullPathPrincipalName(
  38. PCCERT_CONTEXT Context,
  39. RPC_CHAR EncodedPrincipalName[]
  40. );
  41. DWORD
  42. CompareRdnElement(
  43. PCCERT_CONTEXT Context,
  44. DWORD dwGetNameStringType,
  45. void *pvGetNameStringTypePara,
  46. RPC_CHAR PrincipalName[],
  47. BOOL CaseSensitive
  48. );
  49. DWORD
  50. MarkPrincipalNameComponents(
  51. RPC_CHAR * PrincipalName,
  52. unsigned * pCount
  53. );
  54. PCCERT_CONTEXT
  55. FindMatchingCertificate( HCERTSTORE Store,
  56. RPC_CHAR * SubjectName,
  57. RPC_CHAR * IssuerName
  58. );
  59. DWORD
  60. MatchMsPrincipalName(
  61. PCCERT_CONTEXT Context,
  62. RPC_CHAR EncodedPrincipalName[]
  63. );
  64. DWORD
  65. DecodeEscapedString(
  66. RPC_CHAR * Source,
  67. RPC_CHAR * Destination
  68. );
  69. DWORD
  70. FindIssuerContext( PCCERT_CONTEXT * pContext,
  71. HCERTSTORE * pStore,
  72. BOOL * pfFreeStore
  73. );
  74. RPC_CHAR *
  75. EndOfRfc1779Name(
  76. RPC_CHAR * Name
  77. );
  78. unsigned
  79. Int4StrLen(
  80. unsigned long * String
  81. );
  82. //------------------------------------------------------------------------
  83. RPC_CHAR * __cdecl RpcpStringReverse (
  84. RPC_CHAR * string
  85. )
  86. {
  87. RPC_CHAR *start = string;
  88. RPC_CHAR *left = string;
  89. RPC_CHAR ch;
  90. while (*string++) /* find end of string */
  91. ;
  92. string -= sizeof(RPC_CHAR);
  93. while (left < string)
  94. {
  95. ch = *left;
  96. *left++ = *string;
  97. *string-- = ch;
  98. }
  99. return(start);
  100. }
  101. DWORD
  102. RpcCertMatchPrincipalName(
  103. PCCERT_CONTEXT Context,
  104. RPC_CHAR PrincipalName[]
  105. )
  106. /*++
  107. Routine Description:
  108. This routine
  109. Arguments:
  110. <Context> is a CryptoAPI 2.0 context
  111. <PrincipalName> is a principal name prefixed with either "msstd:" or "fullsic:"
  112. Return Value:
  113. 0 if successful, otherwise an error
  114. --*/
  115. {
  116. DWORD Status = 0;
  117. InitializeIfNecessary();
  118. if (!LoadCrypt32Imports())
  119. {
  120. return GetLastError();
  121. }
  122. if (0 == RpcpStringNCompare(PrincipalName, MSSTD_PREFIX, MSSTD_PREFIX_LENGTH))
  123. {
  124. Status = MatchMsPrincipalName(Context, PrincipalName + MSSTD_PREFIX_LENGTH);
  125. }
  126. else if (0 == RpcpStringNCompare(PrincipalName, FULLPATH_PREFIX, FULLPATH_PREFIX_LENGTH))
  127. {
  128. Status = MatchFullPathPrincipalName(Context, PrincipalName + FULLPATH_PREFIX_LENGTH);
  129. }
  130. else
  131. {
  132. ASSERT( 0 && "RPC: principal name is in incorrect format" );
  133. Status = ERROR_INVALID_PARAMETER;
  134. }
  135. return Status;
  136. }
  137. DWORD
  138. MatchMsPrincipalName(
  139. PCCERT_CONTEXT Context,
  140. RPC_CHAR EncodedPrincipalName[]
  141. )
  142. {
  143. PCERT_NAME_INFO Subject = 0;
  144. DWORD Status = 0;
  145. RPC_CHAR * PrincipalName = 0;
  146. //
  147. // It's time to decode the principal name.
  148. // The encoding is just a matter of extra backslashes when the
  149. // principal name contains our metacharacters. Decoding will
  150. // always make the string smaller.
  151. //
  152. PrincipalName = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) * RpcpStringLength(EncodedPrincipalName));
  153. Status = DecodeEscapedString(EncodedPrincipalName, PrincipalName);
  154. if (Status)
  155. {
  156. RpcpErrorAddRecord(EEInfoGCSecurityProvider,
  157. Status,
  158. EEInfoDLMatchMsPrincipalName10);
  159. return Status;
  160. }
  161. if (RpcpCharacter((const RPC_SCHAR *) PrincipalName, '@'))
  162. {
  163. // Compare the principal name to the certificate subject's
  164. // email-address RDN attribute.
  165. return CompareRdnElement(Context, CERT_NAME_EMAIL_TYPE, NULL, PrincipalName, TRUE);
  166. }
  167. else
  168. {
  169. // Compare the principal name to the certificate subject's
  170. // common-name RDN attribute.
  171. return CompareRdnElement(Context, CERT_NAME_ATTR_TYPE, szOID_COMMON_NAME, PrincipalName, FALSE);
  172. }
  173. ASSERT( 0 );
  174. }
  175. DWORD
  176. CompareRdnElement(
  177. PCCERT_CONTEXT Context,
  178. DWORD dwGetNameStringType,
  179. void *pvGetNameStringTypePara,
  180. RPC_CHAR PrincipalName[],
  181. BOOL CaseSensitive
  182. )
  183. {
  184. //
  185. // Decode the certificate's Subject field so we can see its attributes.
  186. //
  187. DWORD Status = 0;
  188. RPC_CHAR * pPrincipalName;
  189. DWORD NameSize = CertGetNameStringW(Context,
  190. dwGetNameStringType,
  191. 0, // dwFlags
  192. pvGetNameStringTypePara, // pvTypePara
  193. NULL,
  194. 0);
  195. if (NameSize > 1)
  196. {
  197. pPrincipalName = (RPC_CHAR *) _alloca( sizeof(wchar_t) * NameSize );
  198. NameSize = CertGetNameStringW(Context,
  199. dwGetNameStringType,
  200. 0,
  201. pvGetNameStringTypePara,
  202. pPrincipalName,
  203. NameSize);
  204. ASSERT(NameSize > 1);
  205. }
  206. else
  207. {
  208. RpcpErrorAddRecord(EEInfoGCSecurityProvider,
  209. ERROR_ACCESS_DENIED,
  210. EEInfoDLCompareRdnElement10,
  211. GetLastError());
  212. return ERROR_ACCESS_DENIED;
  213. }
  214. //
  215. // Now compare the cetificate info to the principal name.
  216. //
  217. if (CaseSensitive)
  218. {
  219. if (0 != RpcpStringSCompare(PrincipalName, pPrincipalName))
  220. {
  221. return ERROR_ACCESS_DENIED;
  222. }
  223. }
  224. else
  225. {
  226. if (0 != RpcpStringCompareInt(PrincipalName, pPrincipalName))
  227. {
  228. return ERROR_ACCESS_DENIED;
  229. }
  230. }
  231. return 0;
  232. }
  233. DWORD
  234. DecodeEscapedString(
  235. RPC_CHAR * Source,
  236. RPC_CHAR * Destination
  237. )
  238. {
  239. for (;;)
  240. {
  241. if (*Source == '\\')
  242. {
  243. if(Source[1] != '<' && Source[1] != '*')
  244. {
  245. Source++;
  246. }
  247. if (!*Source)
  248. {
  249. return ERROR_INVALID_PARAMETER;
  250. }
  251. *Destination = *Source;
  252. }
  253. else
  254. {
  255. *Destination = *Source;
  256. if (!*Source)
  257. {
  258. return 0;
  259. }
  260. }
  261. ++Source;
  262. ++Destination;
  263. }
  264. }
  265. DWORD
  266. MatchFullPathPrincipalName(
  267. PCCERT_CONTEXT Context,
  268. RPC_CHAR EncodedPrincipalName[]
  269. )
  270. /*++
  271. Routine Description:
  272. Arguments:
  273. <Context> is a CryptoAPI 2.0 context
  274. <PrincipalName> is a principal name with no prefix
  275. It can be in several forms:
  276. - complete and explicit, e.g.
  277. "\<CA>\<Name>"
  278. "\<CA>\<SA1>\<SA2>\<Name>"
  279. - wildcard in the subject-name position:
  280. "\<CA>\*"
  281. - no CA specified
  282. "name" - This is equivalent to "\*\<CA>"
  283. Return Value:
  284. 0 if all components were successfully validated
  285. ERROR_NO_TOP_LEVEL_AUTHORITY if the top cert isn't in the CA store
  286. Some other error if something failed.
  287. --*/
  288. {
  289. DWORD Status = 0;
  290. DWORD ReturnedStatus = 0;
  291. HCERTSTORE Store = Context->hCertStore;
  292. unsigned PrincipalNameLength;
  293. RPC_CHAR * CopyOfPrincipalName = 0;
  294. if (!*EncodedPrincipalName)
  295. {
  296. return ERROR_INVALID_PARAMETER;
  297. }
  298. PrincipalNameLength = RpcpStringLength(EncodedPrincipalName);
  299. //
  300. // If the name doesn't begin with a backslash, then treat it as "\*\subjectname".
  301. //
  302. if (EncodedPrincipalName[0] != '\\')
  303. {
  304. CopyOfPrincipalName = (RPC_CHAR *) _alloca( (5 + PrincipalNameLength + 2) *
  305. sizeof(RPC_CHAR));
  306. RpcpStringCopy(CopyOfPrincipalName, RPC_CONST_STRING("\\*\\<"));
  307. RpcpStringCat(CopyOfPrincipalName, EncodedPrincipalName);
  308. RpcpStringCat(CopyOfPrincipalName, RPC_CONST_STRING(">"));
  309. return MatchFullPathPrincipalName( Context, CopyOfPrincipalName );
  310. }
  311. //
  312. // Form is "\<CA>\<Name>". More precisely, the name looks like a
  313. // file system path, where each element of the path
  314. // is either
  315. // "*"
  316. // or
  317. // '<' plus an RFC1779 name plus '>'
  318. //
  319. unsigned idx;
  320. unsigned ComponentCount;
  321. RPC_CHAR ** Components = 0;
  322. RPC_CHAR * Cursor;
  323. CopyOfPrincipalName = (RPC_CHAR *) _alloca( (PrincipalNameLength + 2) * sizeof(RPC_CHAR));
  324. RpcpStringCopy(CopyOfPrincipalName, EncodedPrincipalName);
  325. //
  326. // Sizing pass. Count the number of path elements and separate them by '\0'.
  327. //
  328. Status = MarkPrincipalNameComponents( CopyOfPrincipalName, &ComponentCount );
  329. if (Status)
  330. {
  331. RpcpErrorAddRecord(EEInfoGCSecurityProvider,
  332. Status,
  333. EEInfoDLMatchFullPathPrincipalName10);
  334. return Status;
  335. }
  336. //
  337. // Init the component array. The 0'th element of the array points to
  338. // the last component of the principal name (i.e., the client's subject name).
  339. //
  340. Components = (RPC_CHAR **) _alloca( sizeof(RPC_CHAR *) * ComponentCount );
  341. Cursor = CopyOfPrincipalName;
  342. idx = ComponentCount-1;
  343. do
  344. {
  345. while (!*Cursor)
  346. {
  347. ++Cursor;
  348. }
  349. ASSERT( Cursor < CopyOfPrincipalName+PrincipalNameLength );
  350. Components[idx] = Cursor;
  351. Cursor += RpcpStringLength( Cursor );
  352. }
  353. while ( idx-- > 0 );
  354. //
  355. // Verify the principal name of all certs except the top authority one.
  356. //
  357. PCCERT_CONTEXT NewContext = 0;
  358. for (idx=0; idx < ComponentCount-1; ++idx)
  359. {
  360. if (NewContext)
  361. {
  362. CertFreeCertificateContext( NewContext );
  363. }
  364. NewContext = FindMatchingCertificate( Store,
  365. Components[idx],
  366. Components[idx+1]
  367. );
  368. if (!NewContext)
  369. {
  370. Status = GetLastError();
  371. if (Status == ERROR_NOT_ENOUGH_MEMORY)
  372. {
  373. ReturnedStatus = ERROR_NOT_ENOUGH_MEMORY;
  374. }
  375. else
  376. {
  377. ReturnedStatus = ERROR_ACCESS_DENIED;
  378. }
  379. RpcpErrorAddRecord(EEInfoGCSecurityProvider,
  380. ReturnedStatus,
  381. EEInfoDLMatchFullPathPrincipalName20,
  382. Status);
  383. return ReturnedStatus;
  384. }
  385. }
  386. //
  387. // Look for the top-level authority certificate in the CA and ROOT stores,
  388. // matching the last remaining principal name component.
  389. //
  390. HCERTSTORE CaStore;
  391. HCERTSTORE RootStore;
  392. CaStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
  393. 0,
  394. 0,
  395. CERT_SYSTEM_STORE_CURRENT_USER,
  396. RPC_CONST_STRING("CA")
  397. );
  398. if (!CaStore)
  399. {
  400. Status = GetLastError();
  401. RpcpErrorAddRecord(EEInfoGCSecurityProvider,
  402. Status,
  403. EEInfoDLMatchFullPathPrincipalName30);
  404. CertFreeCertificateContext( NewContext );
  405. return Status;
  406. }
  407. RootStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
  408. 0,
  409. 0,
  410. CERT_SYSTEM_STORE_CURRENT_USER,
  411. RPC_CONST_STRING("ROOT")
  412. );
  413. if (!RootStore)
  414. {
  415. Status = GetLastError();
  416. RpcpErrorAddRecord(EEInfoGCSecurityProvider,
  417. Status,
  418. EEInfoDLMatchFullPathPrincipalName40);
  419. CertCloseStore( CaStore, 0 );
  420. CertFreeCertificateContext( NewContext );
  421. return Status;
  422. }
  423. //
  424. // Use Context instead of Components[] for the issuer name
  425. // so that the self-signed cert that we look for
  426. // is always the one that matches the Context we have.
  427. // Otherwise we'd be wrong about strings with a wildcard as top-level authority.
  428. //
  429. PCCERT_CONTEXT Parent = 0;
  430. while (1)
  431. {
  432. Parent = CertFindCertificateInStore(
  433. RootStore,
  434. X509_ASN_ENCODING,
  435. 0, // no flags
  436. CERT_FIND_SUBJECT_NAME, // exact match
  437. &NewContext->pCertInfo->Issuer,
  438. Parent
  439. );
  440. if (!Parent)
  441. {
  442. Parent = CertFindCertificateInStore(
  443. CaStore,
  444. X509_ASN_ENCODING,
  445. 0, // no flags
  446. CERT_FIND_SUBJECT_NAME, // exact match
  447. &NewContext->pCertInfo->Issuer,
  448. Parent
  449. );
  450. }
  451. if (!Parent)
  452. {
  453. goto finish;
  454. }
  455. if (CertCompareCertificateName( X509_ASN_ENCODING,
  456. & Parent->pCertInfo->Issuer,
  457. &Context->pCertInfo->Issuer
  458. ))
  459. {
  460. goto finish;
  461. }
  462. }
  463. finish:
  464. CertCloseStore( CaStore, 0 );
  465. CertCloseStore( RootStore, 0 );
  466. CertFreeCertificateContext( NewContext );
  467. if (!Parent)
  468. {
  469. Status = GetLastError();
  470. if (Status == ERROR_NOT_ENOUGH_MEMORY)
  471. {
  472. ReturnedStatus = ERROR_NOT_ENOUGH_MEMORY;
  473. }
  474. else
  475. {
  476. ReturnedStatus = SEC_E_NO_AUTHENTICATING_AUTHORITY;
  477. }
  478. RpcpErrorAddRecord(EEInfoGCSecurityProvider,
  479. ReturnedStatus,
  480. EEInfoDLMatchFullPathPrincipalName50,
  481. Status);
  482. return ReturnedStatus;
  483. }
  484. CertFreeCertificateContext( Parent );
  485. return 0;
  486. }
  487. PCCERT_CONTEXT
  488. FindMatchingCertificate( HCERTSTORE Store,
  489. RPC_CHAR * SubjectName,
  490. RPC_CHAR * IssuerName
  491. )
  492. {
  493. PCCERT_CONTEXT Context = 0;
  494. CERT_NAME_BLOB * IssuerBlob = 0;
  495. CERT_NAME_BLOB * SubjectBlob = 0;
  496. ASSERT( IssuerName[0] != '*' || SubjectName[0] != '*' );
  497. //
  498. // Make an issuer blob, if necessary.
  499. //
  500. if (IssuerName[0] != '*')
  501. {
  502. unsigned long BlobSize = INITIAL_NAME_LENGTH;
  503. IssuerBlob = (CERT_NAME_BLOB *) _alloca( sizeof(CERT_NAME_BLOB)+INITIAL_NAME_LENGTH );
  504. IssuerBlob->cbData = INITIAL_NAME_LENGTH;
  505. IssuerBlob->pbData = (unsigned char *) (IssuerBlob+1);
  506. if (! CertStrToNameT( X509_ASN_ENCODING,
  507. IssuerName,
  508. 0, // no format restrictions
  509. NULL, // reserved, MBZ
  510. IssuerBlob->pbData,
  511. &IssuerBlob->cbData,
  512. NULL // would point to first invalid character
  513. ))
  514. {
  515. BlobSize = IssuerBlob->cbData;
  516. IssuerBlob = (CERT_NAME_BLOB *) _alloca( sizeof(CERT_NAME_BLOB)+BlobSize );
  517. IssuerBlob->cbData = BlobSize;
  518. IssuerBlob->pbData = (unsigned char *) (IssuerBlob+1);
  519. if (! CertStrToNameT( X509_ASN_ENCODING,
  520. IssuerName,
  521. 0, // no format restrictions
  522. NULL, // reserved, MBZ
  523. IssuerBlob->pbData,
  524. &IssuerBlob->cbData,
  525. NULL // would point to first invalid character
  526. ))
  527. {
  528. return 0;
  529. }
  530. }
  531. }
  532. //
  533. // Make a subject blob, if necessary.
  534. //
  535. if (SubjectName[0] != '*')
  536. {
  537. unsigned long BlobSize = INITIAL_NAME_LENGTH;
  538. SubjectBlob = (CERT_NAME_BLOB *) _alloca( sizeof(CERT_NAME_BLOB)+INITIAL_NAME_LENGTH );
  539. SubjectBlob->cbData = INITIAL_NAME_LENGTH;
  540. SubjectBlob->pbData = (unsigned char *) (SubjectBlob+1);
  541. if (! CertStrToNameT( X509_ASN_ENCODING,
  542. SubjectName,
  543. 0, // no format restrictions
  544. NULL, // reserved, MBZ
  545. SubjectBlob->pbData,
  546. &SubjectBlob->cbData,
  547. NULL // would point to first invalid character
  548. ))
  549. {
  550. BlobSize = SubjectBlob->cbData;
  551. SubjectBlob = (CERT_NAME_BLOB *) _alloca( sizeof(CERT_NAME_BLOB)+BlobSize );
  552. SubjectBlob->cbData = BlobSize;
  553. SubjectBlob->pbData = (unsigned char *) (SubjectBlob+1);
  554. if (! CertStrToNameT( X509_ASN_ENCODING,
  555. SubjectName,
  556. 0, // no format restrictions
  557. NULL, // reserved, MBZ
  558. SubjectBlob->pbData,
  559. &SubjectBlob->cbData,
  560. NULL // would point to first invalid character
  561. ))
  562. {
  563. return 0;
  564. }
  565. }
  566. }
  567. //
  568. // Search for a certificate.
  569. //
  570. if (SubjectName[0] == '*')
  571. {
  572. //
  573. // Search by issuer only.
  574. //
  575. Context = CertFindCertificateInStore(
  576. Store,
  577. X509_ASN_ENCODING,
  578. 0, // no flags
  579. CERT_FIND_ISSUER_NAME, // exact match
  580. IssuerBlob,
  581. NULL // previous context in search
  582. );
  583. return Context;
  584. }
  585. else if (IssuerName[0] == '*')
  586. {
  587. //
  588. // Search by subject only.
  589. //
  590. Context = CertFindCertificateInStore(
  591. Store,
  592. X509_ASN_ENCODING,
  593. 0, // no flags
  594. CERT_FIND_SUBJECT_NAME, // exact match
  595. SubjectBlob,
  596. NULL // previous context in search
  597. );
  598. return Context;
  599. }
  600. else
  601. {
  602. //
  603. // Search by both. The primary search is by subject,
  604. // on the theory that most subject names are distinct and
  605. // most issuer names are not.
  606. //
  607. while (1)
  608. {
  609. Context = CertFindCertificateInStore(
  610. Store,
  611. X509_ASN_ENCODING,
  612. 0, // no flags
  613. CERT_FIND_SUBJECT_NAME, // exact match
  614. SubjectBlob,
  615. NULL // previous context in search
  616. );
  617. if (!Context)
  618. {
  619. return Context;
  620. }
  621. if (CertCompareCertificateName( X509_ASN_ENCODING,
  622. &Context->pCertInfo->Issuer,
  623. IssuerBlob
  624. ))
  625. {
  626. return Context;
  627. }
  628. }
  629. }
  630. ASSERT( 0 && "should never get here" );
  631. }
  632. DWORD
  633. MarkPrincipalNameComponents(
  634. RPC_CHAR * PrincipalName,
  635. unsigned * pCount
  636. )
  637. {
  638. RPC_CHAR * Cursor;
  639. unsigned ComponentCount;
  640. ComponentCount = 0;
  641. Cursor = PrincipalName;
  642. do
  643. {
  644. ++ComponentCount;
  645. if ( *Cursor != '\\' )
  646. {
  647. return ERROR_INVALID_PARAMETER;
  648. }
  649. *Cursor = '\0';
  650. ++Cursor;
  651. if (*Cursor == '*')
  652. {
  653. ++Cursor;
  654. }
  655. else if (*Cursor == '<')
  656. {
  657. *Cursor = '\0';
  658. ++Cursor;
  659. Cursor = EndOfRfc1779Name( Cursor );
  660. if (*Cursor != '>')
  661. {
  662. return ERROR_INVALID_PARAMETER;
  663. }
  664. *Cursor = '\0';
  665. ++Cursor;
  666. }
  667. else
  668. {
  669. return ERROR_INVALID_PARAMETER;
  670. }
  671. }
  672. while ( *Cursor );
  673. *pCount = ComponentCount;
  674. return 0;
  675. }
  676. RPC_CHAR *
  677. EndOfRfc1779Name(
  678. RPC_CHAR * Name
  679. )
  680. {
  681. unsigned Quotes = 0;
  682. for ( ; *Name; ++Name)
  683. {
  684. if (*Name == '>')
  685. {
  686. if (0 == (Quotes % 2))
  687. {
  688. return Name;
  689. }
  690. }
  691. else if (*Name == '\\')
  692. {
  693. ++Name;
  694. }
  695. else if (*Name == '"')
  696. {
  697. ++Quotes;
  698. }
  699. }
  700. return Name;
  701. }
  702. RPC_STATUS
  703. RpcCertGeneratePrincipalName(
  704. PCCERT_CONTEXT Context,
  705. DWORD Flags,
  706. RPC_CHAR ** pBuffer
  707. )
  708. {
  709. THREAD *Thread;
  710. DWORD Status = 0;
  711. InitializeIfNecessary();
  712. Thread = ThreadSelf();
  713. if (Thread)
  714. {
  715. RpcpPurgeEEInfoFromThreadIfNecessary(Thread);
  716. }
  717. if (!LoadCrypt32Imports())
  718. {
  719. return GetLastError();
  720. }
  721. if (Flags & RPC_C_FULL_CERT_CHAIN)
  722. {
  723. RPC_CHAR * Buffer;
  724. unsigned long Cursor = 0;
  725. unsigned long BufferLength = INITIAL_NAME_LENGTH;
  726. HCERTSTORE Store = Context->hCertStore;
  727. BOOL fFreeStore = FALSE;
  728. Buffer = new RPC_CHAR[BufferLength];
  729. if (!Buffer)
  730. {
  731. return ERROR_NOT_ENOUGH_MEMORY;
  732. }
  733. PCCERT_CONTEXT Node = Context;
  734. do
  735. {
  736. Status = AddComponentName( &Buffer,
  737. &BufferLength,
  738. &Cursor,
  739. &Node->pCertInfo->Subject
  740. );
  741. if (Status)
  742. {
  743. RpcpErrorAddRecord(EEInfoGCSecurityProvider,
  744. Status,
  745. EEInfoDLRpcCertGeneratePrincipalName10);
  746. return Status;
  747. }
  748. //
  749. // Load the next certificate.
  750. //
  751. PCCERT_CONTEXT OldNode = Node;
  752. if (CertCompareCertificateName( X509_ASN_ENCODING,
  753. &Node->pCertInfo->Subject,
  754. &Node->pCertInfo->Issuer
  755. ))
  756. {
  757. break;
  758. }
  759. Node = CertFindCertificateInStore( Store,
  760. X509_ASN_ENCODING,
  761. 0, // no flags
  762. CERT_FIND_SUBJECT_NAME, // exact match
  763. &OldNode->pCertInfo->Issuer,
  764. NULL // previous context in search
  765. );
  766. if (!Node)
  767. {
  768. //
  769. // Take the top-level CA name from the current node's issuer field.
  770. //
  771. Status = AddComponentName( &Buffer,
  772. &BufferLength,
  773. &Cursor,
  774. &OldNode->pCertInfo->Issuer
  775. );
  776. if (Status)
  777. {
  778. RpcpErrorAddRecord(EEInfoGCSecurityProvider,
  779. Status,
  780. EEInfoDLRpcCertGeneratePrincipalName20);
  781. CertFreeCertificateContext( OldNode );
  782. return Status;
  783. }
  784. }
  785. if (OldNode != Context)
  786. {
  787. CertFreeCertificateContext( OldNode );
  788. }
  789. }
  790. while ( Node );
  791. if (Cursor+FULLPATH_PREFIX_LENGTH+1 > BufferLength)
  792. {
  793. DWORD LongerBufferLength = BufferLength+FULLPATH_PREFIX_LENGTH+1;
  794. RPC_CHAR * LongerBuffer = new RPC_CHAR[LongerBufferLength];
  795. if (!LongerBuffer)
  796. {
  797. delete Buffer;
  798. return ERROR_NOT_ENOUGH_MEMORY;
  799. }
  800. RpcpStringNCopy( LongerBuffer, Buffer, BufferLength );
  801. delete Buffer;
  802. Buffer = LongerBuffer;
  803. BufferLength = LongerBufferLength;
  804. }
  805. RpcpStringCopy( Buffer+Cursor,FULLPATH_PREFIX );
  806. RpcpStringReverse( Buffer+Cursor );
  807. RpcpStringReverse( Buffer );
  808. *pBuffer = Buffer;
  809. return 0;
  810. }
  811. else
  812. {
  813. RPC_CHAR * outputName;
  814. DWORD NameSize = CertGetNameStringW(Context,
  815. CERT_NAME_EMAIL_TYPE,
  816. 0, // dwFlags
  817. NULL, // pvTypePara
  818. NULL,
  819. 0);
  820. if (NameSize > 1)
  821. {
  822. outputName = new RPC_CHAR[ NameSize + MSSTD_PREFIX_LENGTH ];
  823. if (!outputName)
  824. {
  825. return ERROR_NOT_ENOUGH_MEMORY;
  826. }
  827. RpcpStringCopy(outputName, MSSTD_PREFIX);
  828. NameSize = CertGetNameStringW(Context,
  829. CERT_NAME_EMAIL_TYPE,
  830. 0,
  831. NULL,
  832. outputName + MSSTD_PREFIX_LENGTH,
  833. NameSize);
  834. ASSERT(NameSize > 1);
  835. }
  836. else
  837. {
  838. NameSize = CertGetNameStringW(Context,
  839. CERT_NAME_ATTR_TYPE,
  840. 0,
  841. szOID_COMMON_NAME,
  842. NULL,
  843. 0);
  844. if (NameSize > 1)
  845. {
  846. outputName = new RPC_CHAR[ NameSize + MSSTD_PREFIX_LENGTH ];
  847. if (!outputName)
  848. {
  849. return ERROR_NOT_ENOUGH_MEMORY;
  850. }
  851. RpcpStringCopy(outputName, MSSTD_PREFIX);
  852. NameSize = CertGetNameString(Context,
  853. CERT_NAME_ATTR_TYPE,
  854. 0,
  855. szOID_COMMON_NAME,
  856. outputName + MSSTD_PREFIX_LENGTH,
  857. NameSize);
  858. ASSERT(NameSize > 1);
  859. }
  860. else
  861. {
  862. RpcpErrorAddRecord(EEInfoGCSecurityProvider,
  863. ERROR_INVALID_PARAMETER,
  864. EEInfoDLRpcCertGeneratePrincipalName30,
  865. GetLastError());
  866. return ERROR_INVALID_PARAMETER;
  867. }
  868. }
  869. *pBuffer = outputName;
  870. return 0;
  871. }
  872. ASSERT( 0 && "never reach here" );
  873. return ERROR_INVALID_PARAMETER;
  874. }
  875. DWORD
  876. AddComponentName( RPC_CHAR * * pBuffer,
  877. unsigned long * pBufferLength,
  878. unsigned long * pCursor,
  879. CERT_NAME_BLOB * Name
  880. )
  881. {
  882. (*pBuffer)[(*pCursor)++] = '>';
  883. DWORD Length = *pBufferLength - *pCursor;
  884. Length = CertNameToStrT( X509_ASN_ENCODING,
  885. Name,
  886. CERT_X500_NAME_STR,
  887. *pBuffer + *pCursor,
  888. Length
  889. );
  890. if (Length >= *pBufferLength - *pCursor)
  891. {
  892. DWORD LongerBufferLength = 2 * *pBufferLength + Length;
  893. RPC_CHAR * LongerBuffer = new RPC_CHAR[LongerBufferLength];
  894. if (!LongerBuffer)
  895. {
  896. delete *pBuffer;
  897. return ERROR_NOT_ENOUGH_MEMORY;
  898. }
  899. RpcpStringNCopy( LongerBuffer, *pBuffer, *pBufferLength );
  900. delete *pBuffer;
  901. *pBuffer = LongerBuffer;
  902. *pBufferLength = LongerBufferLength;
  903. Length = *pBufferLength - *pCursor;
  904. Length = CertNameToStrT( X509_ASN_ENCODING,
  905. Name,
  906. CERT_X500_NAME_STR,
  907. *pBuffer + *pCursor,
  908. Length
  909. );
  910. }
  911. RpcpStringReverse(*pBuffer + *pCursor);
  912. *pCursor += Length-1; // write over the '\0'
  913. (*pBuffer)[(*pCursor)++] = '<';
  914. (*pBuffer)[(*pCursor)++] = '\\';
  915. return 0;
  916. }
  917. unsigned
  918. Int4StrLen(
  919. unsigned long * String
  920. )
  921. {
  922. unsigned long * Cursor = String;
  923. while (*Cursor)
  924. {
  925. ++Cursor;
  926. }
  927. return (unsigned) (Cursor - String);
  928. }
  929. HMODULE Crypt32Handle = 0;
  930. struct CRYPT32_FUNCTION_TABLE CFT;
  931. BOOL
  932. LoadCrypt32Imports()
  933. {
  934. if (0 == Crypt32Handle)
  935. {
  936. RequestGlobalMutex();
  937. if (Crypt32Handle)
  938. {
  939. goto Cleanup;
  940. }
  941. Crypt32Handle = LoadLibrary(RPC_CONST_SSTRING("crypt32.dll"));
  942. if (!Crypt32Handle)
  943. {
  944. goto Cleanup;
  945. }
  946. FARPROC * ppProc = (FARPROC *) &CFT;
  947. *ppProc++ = GetProcAddress(Crypt32Handle, "CertOpenStore");
  948. *ppProc++ = GetProcAddress(Crypt32Handle, "CertCloseStore");
  949. *ppProc++ = GetProcAddress(Crypt32Handle, "CertFindCertificateInStore");
  950. *ppProc++ = GetProcAddress(Crypt32Handle, "CertFreeCertificateContext");
  951. *ppProc++ = GetProcAddress(Crypt32Handle, "CertCompareCertificateName");
  952. #ifdef UNICODE
  953. *ppProc++ = GetProcAddress(Crypt32Handle, "CertStrToNameW");
  954. *ppProc++ = GetProcAddress(Crypt32Handle, "CertNameToStrW");
  955. #else
  956. *ppProc++ = GetProcAddress(Crypt32Handle, "CertStrToNameA");
  957. *ppProc++ = GetProcAddress(Crypt32Handle, "CertNameToStrA");
  958. #endif
  959. *ppProc++ = GetProcAddress(Crypt32Handle, "CertFindRDNAttr");
  960. *ppProc++ = GetProcAddress(Crypt32Handle, "CryptDecodeObject");
  961. #if MANUAL_CERT_CHECK
  962. *ppProc++ = GetProcAddress(Crypt32Handle, "CertVerifyCertificateChainPolicy");
  963. *ppProc++ = GetProcAddress(Crypt32Handle, "CertGetCertificateChain");
  964. *ppProc++ = GetProcAddress(Crypt32Handle, "CertFreeCertificateChain");
  965. #endif
  966. *ppProc++ = GetProcAddress(Crypt32Handle, "CertGetNameStringW");
  967. ppProc = (FARPROC *) &CFT;
  968. for (int i = 0; i < sizeof(CRYPT32_FUNCTION_TABLE)/sizeof(FARPROC); i++)
  969. {
  970. if (*ppProc++ == 0)
  971. {
  972. FreeLibrary(Crypt32Handle);
  973. Crypt32Handle = 0;
  974. goto Cleanup;
  975. }
  976. }
  977. ClearGlobalMutex();
  978. }
  979. return TRUE;
  980. Cleanup:
  981. ClearGlobalMutex();
  982. return FALSE;
  983. }
  984. #if MANUAL_CERT_CHECK
  985. DWORD
  986. RpcCertVerifyContext(
  987. IN PCERT_CONTEXT Context,
  988. IN DWORD CapabilityFlags
  989. )
  990. {
  991. DWORD s = 0;
  992. HCERTSTORE CaStore = 0;
  993. PCCERT_CHAIN_CONTEXT Chain = 0;
  994. //
  995. // Windows 2000 checked only the CA store, so we should check it also.
  996. //
  997. CaStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
  998. 0,
  999. 0,
  1000. CERT_SYSTEM_STORE_CURRENT_USER,
  1001. RPC_CONST_STRING("CA")
  1002. );
  1003. if (!CaStore)
  1004. {
  1005. s = GetLastError();
  1006. RpcpErrorAddRecord(EEInfoGCSecurityProvider,
  1007. s,
  1008. EEInfoDLRpcCertVerifyContext10);
  1009. goto Cleanup;
  1010. }
  1011. //
  1012. // Build a certificate chain from the single certificate we have.
  1013. //
  1014. CERT_CHAIN_PARA ChainParameters;
  1015. ChainParameters.cbSize = sizeof(CERT_CHAIN_PARA);
  1016. ChainParameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
  1017. ChainParameters.RequestedUsage.Usage.cUsageIdentifier = 0;
  1018. if (!CertGetCertificateChain( NULL, // use default chain engine
  1019. Context,
  1020. NULL, // match against current time
  1021. CaStore, // aditional store to search
  1022. &ChainParameters,
  1023. 0, // no special flags
  1024. 0, // reserved, MBZ
  1025. &Chain
  1026. ))
  1027. {
  1028. s = GetLastError();
  1029. RpcpErrorAddRecord(EEInfoGCSecurityProvider,
  1030. s,
  1031. EEInfoDLRpcCertVerifyContext20);
  1032. goto Cleanup;
  1033. }
  1034. //
  1035. // Verify the chain.
  1036. //
  1037. CERT_CHAIN_POLICY_PARA PolicyParameters;
  1038. CERT_CHAIN_POLICY_STATUS PolicyStatus;
  1039. PolicyParameters.cbSize = sizeof(CERT_CHAIN_POLICY_PARA);
  1040. PolicyParameters.dwFlags = 0;
  1041. PolicyParameters.pvExtraPolicyPara = 0;
  1042. if (CapabilityFlags & RPC_C_QOS_CAPABILITIES_ANY_AUTHORITY)
  1043. {
  1044. PolicyParameters.dwFlags |= CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG;
  1045. }
  1046. PolicyStatus.cbSize = sizeof(CERT_CHAIN_POLICY_STATUS);
  1047. if (!CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_BASE,
  1048. Chain,
  1049. &PolicyParameters,
  1050. &PolicyStatus))
  1051. {
  1052. s = GetLastError();
  1053. RpcpErrorAddRecord(EEInfoGCSecurityProvider,
  1054. s,
  1055. EEInfoDLRpcCertVerifyContext30);
  1056. goto Cleanup;
  1057. }
  1058. if (PolicyStatus.dwError)
  1059. {
  1060. s = ERROR_ACCESS_DENIED;
  1061. RpcpErrorAddRecord(EEInfoGCSecurityProvider,
  1062. s,
  1063. EEInfoDLRpcCertVerifyContext40,
  1064. GetLastError());
  1065. goto Cleanup;
  1066. }
  1067. //
  1068. // Certificate verification succeeded.
  1069. //
  1070. s = 0;
  1071. Cleanup:
  1072. if (Chain)
  1073. {
  1074. CertFreeCertificateChain( Chain );
  1075. }
  1076. if (CaStore)
  1077. {
  1078. CertCloseStore( CaStore, 0 );
  1079. }
  1080. return s;
  1081. }
  1082. #endif
  1083. RPC_STATUS
  1084. ValidateSchannelPrincipalName(
  1085. IN RPC_CHAR * EncodedName
  1086. )
  1087. //
  1088. // Does a quick syntactic check of an SSL principal name.
  1089. // The name should not be modified in any way.
  1090. //
  1091. // It should begin either with "msstd:" or "fullsic:".
  1092. // If msstd, then any non-enpty name will do.
  1093. // If fullsic, it should be a series of RFC1179 names, each surrounded
  1094. // by angle brackets, and separated by backslashes. We check this by cloning
  1095. // the string and marking the components.
  1096. //
  1097. {
  1098. #define MAX_SSL_SPN_LENGTH 8000
  1099. if (0 == RpcpStringNCompare(EncodedName, MSSTD_PREFIX, MSSTD_PREFIX_LENGTH))
  1100. {
  1101. if (EncodedName[MSSTD_PREFIX_LENGTH] == 0)
  1102. {
  1103. return ERROR_INVALID_PARAMETER;
  1104. }
  1105. }
  1106. else if (0 == RpcpStringNCompare(EncodedName, FULLPATH_PREFIX, FULLPATH_PREFIX_LENGTH))
  1107. {
  1108. RPC_STATUS Status;
  1109. size_t Length;
  1110. unsigned ComponentCount;
  1111. RPC_CHAR * PrincipalName;
  1112. RPC_CHAR * EncodedPrincipalName;
  1113. Length = RpcpStringLength( EncodedName );
  1114. if (Length > MAX_SSL_SPN_LENGTH)
  1115. {
  1116. return ERROR_INVALID_PARAMETER;
  1117. }
  1118. PrincipalName = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) * Length);
  1119. Status = DecodeEscapedString(EncodedName, PrincipalName);
  1120. if (Status)
  1121. {
  1122. return Status;
  1123. }
  1124. Status = MarkPrincipalNameComponents( PrincipalName + FULLPATH_PREFIX_LENGTH, &ComponentCount );
  1125. if (Status)
  1126. {
  1127. return Status;
  1128. }
  1129. if (ComponentCount < 1)
  1130. {
  1131. return ERROR_INVALID_PARAMETER;
  1132. }
  1133. }
  1134. else
  1135. {
  1136. return ERROR_INVALID_PARAMETER;
  1137. }
  1138. return RPC_S_OK;
  1139. }
  1140. RPC_STATUS
  1141. I_RpcTransCertMatchPrincipalName(
  1142. PCCERT_CONTEXT Context,
  1143. RPC_CHAR PrincipalName[]
  1144. )
  1145. /*++
  1146. Routine Description:
  1147. Verifies that the passed in context match the passed in
  1148. principal name and frees the certificate.
  1149. Arguments:
  1150. Context - certificate context.
  1151. PrincipalName - a principal name prefixed with either "msstd:" or "fullsic:"
  1152. Return Value:
  1153. RPC_S_OK or RPC_S_ACCESS_DENIED
  1154. --*/
  1155. {
  1156. RPC_STATUS RpcStatus;
  1157. RpcStatus = RpcCertMatchPrincipalName(Context, PrincipalName);
  1158. if (RpcStatus == ERROR_OUTOFMEMORY)
  1159. RpcStatus = RPC_S_OUT_OF_MEMORY;
  1160. else if (RpcStatus != RPC_S_OK)
  1161. RpcStatus = RPC_S_ACCESS_DENIED;
  1162. CertFreeCertificateContext(Context);
  1163. return RpcStatus;
  1164. }