Leaked source code of windows server 2003
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.

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