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.

467 lines
12 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. cliauth.cxx
  5. Abstract:
  6. Contains Schannel/SSPI specific code for handling Client Authenication
  7. multiplexed between several asynchronous requests using fibers
  8. Contents:
  9. CliAuthRefreshCredential
  10. CliAuthSelectCredential
  11. Author:
  12. Arthur L Bierer (arthurbi) 13-Jun-1996
  13. Environment:
  14. Win32 user-mode DLL
  15. Revision History:
  16. 13-Jun-1996 arthurbi
  17. Created, based on orginal code from a-petesk.
  18. --*/
  19. #include <wininetp.h>
  20. extern "C" {
  21. #include <nt.h>
  22. #include <ntrtl.h>
  23. #include <nturtl.h>
  24. #include <ntsecapi.h>
  25. }
  26. CERT_CONTEXT_ARRAY::CERT_CONTEXT_ARRAY()
  27. {
  28. _error = ERROR_SUCCESS;
  29. _iSelected = -1;
  30. _ppCertContexts = (PCCERT_CONTEXT *)
  31. ALLOCATE_MEMORY(LMEM_FIXED,
  32. sizeof(PCERT_CONTEXT)* CERT_CONTEXT_ARRAY_ALLOC_UNIT);
  33. if ( _ppCertContexts == NULL ) {
  34. _error = GetLastError();
  35. }
  36. _cAlloced = CERT_CONTEXT_ARRAY_ALLOC_UNIT;
  37. _cCertContexts = 0;
  38. ClearCreds(_hCreds);
  39. InitializeCriticalSection(&_cs);
  40. }
  41. void CERT_CONTEXT_ARRAY::Reset(void)
  42. {
  43. if ( _ppCertContexts )
  44. {
  45. for ( DWORD i = 0; i < _cCertContexts; i++ )
  46. {
  47. INET_ASSERT(_ppCertContexts[i]);
  48. CertFreeCertificateContext(_ppCertContexts[i]);
  49. }
  50. }
  51. _cCertContexts = 0;
  52. // It is important that this Free is guarded by a try except.
  53. // These objects get freed up at dll unload time and there is a circular
  54. // dependency between winient and schannel which can cause schannel to
  55. // get unloaded. If that is the case we could fault here.
  56. if (!IsCredClear(_hCreds))
  57. {
  58. __try
  59. {
  60. g_FreeCredentialsHandle(&_hCreds);
  61. }
  62. __except ( EXCEPTION_EXECUTE_HANDLER )
  63. {
  64. // do nothing.
  65. }
  66. }
  67. }
  68. CERT_CONTEXT_ARRAY::~CERT_CONTEXT_ARRAY()
  69. {
  70. Reset();
  71. FREE_MEMORY(_ppCertContexts);
  72. DeleteCriticalSection(&_cs);
  73. }
  74. DWORD
  75. CliAuthSelectCredential(
  76. IN PCtxtHandle phContext,
  77. IN LPTSTR pszPackageName,
  78. IN CERT_CONTEXT_ARRAY* pCertContextArray,
  79. OUT PCredHandle phCredential)
  80. /*++
  81. Routine Description:
  82. Uses a selected Certificate Chain to produce a Credential handle.
  83. The credential handle will be used by SCHANNEL to produce a valid Client
  84. Auth session with a server.
  85. Arguments:
  86. phContext - SSPI Context Handle
  87. pszPackageName - Name of the SSPI package we're using.
  88. pSelectedCert - Cert that User wishes us to use for Client Auth with this server.
  89. (BUGBUG who should free this? )
  90. phCredential - Outgoing SSPI Credential handle that we may generate
  91. IMPORTANT: Do not free the credential handle returned by this function.
  92. These have to be cached for the lifetime of the process so the user
  93. doesn't get prompted forthe password over and over. Unfortunately there is
  94. no ref-counting mechanism on CredHandle's so callers of this function need to
  95. make sure they don't free the handle.
  96. Return Value:
  97. DWORD
  98. Success - ERROR_SUCCESS
  99. Caller should return ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED,
  100. to its caller. The appropriate Cert chain was generated,
  101. and the User needs to select it using UI.
  102. Failure -
  103. ERROR_NOT_ENOUGH_MEMORY -
  104. Out of Memory
  105. ERROR_INTERNET_SECURITY_CHANNEL_ERROR -
  106. Call Down to SSPI or WinTrust failed.
  107. ERROR_INTERNET_CLIENT_AUTH_NOT_SETUP -
  108. Client Auth is not setup on this machine.
  109. --*/
  110. {
  111. SCHANNEL_CRED CredData = {SCHANNEL_CRED_VERSION,
  112. 0,
  113. NULL,
  114. 0,
  115. 0,
  116. NULL,
  117. 0,
  118. NULL,
  119. DEFAULT_SECURE_PROTOCOLS,
  120. 0,
  121. 0,
  122. 0,
  123. SCH_CRED_MANUAL_CRED_VALIDATION |
  124. SCH_CRED_NO_DEFAULT_CREDS
  125. };
  126. SECURITY_STATUS scRet;
  127. DWORD i;
  128. PCERT_BLOB pBlob;
  129. DWORD index;
  130. DWORD error;
  131. PCCERT_CONTEXT pCert;
  132. CredHandle hCreds;
  133. DEBUG_ENTER((DBG_SOCKETS,
  134. Dword,
  135. "CliAuthSelectCredential",
  136. "%#x, %s, %x, %x",
  137. phContext,
  138. pszPackageName,
  139. pCertContextArray,
  140. phCredential
  141. ));
  142. INET_ASSERT(phContext);
  143. INET_ASSERT(pCertContextArray);
  144. INET_ASSERT(pszPackageName);
  145. pCertContextArray->LockCredHandle( );
  146. if ( pCertContextArray->GetArraySize() == 0 )
  147. {
  148. error = ERROR_SUCCESS;
  149. goto quit;
  150. }
  151. // First check and see if the Cert context already has a CredHandle associated with it.
  152. hCreds = pCertContextArray->GetCredHandle( );
  153. if (!IsCredClear(hCreds))
  154. {
  155. *phCredential = hCreds;
  156. error = ERROR_SUCCESS;
  157. goto quit;
  158. }
  159. pCert = pCertContextArray->GetSelectedCertContext();
  160. //
  161. // Setup strucutres for AcquireCredentialsHandle call.
  162. //
  163. if ( pCert )
  164. {
  165. CredData.cCreds = 1;
  166. CredData.paCred = &pCert;
  167. }
  168. InternetReadRegistryDword("SecureProtocols",
  169. (LPDWORD)&CredData.grbitEnabledProtocols
  170. );
  171. scRet = g_AcquireCredentialsHandle(
  172. NULL,
  173. pszPackageName,
  174. SECPKG_CRED_OUTBOUND,
  175. NULL,
  176. &CredData,
  177. NULL,
  178. NULL,
  179. phCredential,
  180. NULL);
  181. error = MapInternetError((DWORD)scRet);
  182. if (error == ERROR_SUCCESS)
  183. {
  184. pCertContextArray->SetCredHandle(*phCredential);
  185. }
  186. quit:
  187. pCertContextArray->UnlockCredHandle();
  188. DEBUG_LEAVE(error);
  189. return error;
  190. }
  191. DWORD
  192. CliAuthAcquireCertContexts(
  193. IN PCtxtHandle phContext,
  194. IN LPTSTR pszPackageName,
  195. OUT CERT_CONTEXT_ARRAY** ppCertContextArray
  196. )
  197. /*++
  198. Routine Description:
  199. Acquires a List of valid Certificate Chains for use in Client Authentication.
  200. Gathers an issuer list from the current context, and uses CAPI stored Certificates
  201. to build a list which will be selected from by the user at a later point.
  202. Arguments:
  203. phContext - SSPI Context Handle
  204. pszPackageName - Name of the SSPI package we're using.
  205. phCredential - Outgoing SSPI Credential handle that we may generate
  206. ppCertContextArray - Outgoing List of Certifcate Contexts that can be selected
  207. among to generate a Context.
  208. Return Value:
  209. DWORD
  210. Success - ERROR_SUCCESS
  211. Caller should return ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED,
  212. to its caller. The appropriate Cert chain was generated,
  213. and the User needs to select it using UI.
  214. Failure -
  215. ERROR_NOT_ENOUGH_MEMORY -
  216. Out of Memory
  217. ERROR_INTERNET_SECURITY_CHANNEL_ERROR -
  218. Call Down to SSPI or WinTrust failed.
  219. ERROR_INTERNET_CLIENT_AUTH_NOT_SETUP -
  220. Client Auth is not setup on this machine.
  221. --*/
  222. {
  223. DEBUG_ENTER((DBG_SOCKETS,
  224. Dword,
  225. "CliAuthAcquireCertContexts",
  226. "%#x, %s, %x",
  227. phContext,
  228. pszPackageName,
  229. ppCertContextArray
  230. ));
  231. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  232. BOOL async;
  233. SECURITY_STATUS scRet;
  234. DWORD cCerts;
  235. CERT_CHAIN_FIND_BY_ISSUER_PARA FindByIssuerPara;
  236. SecPkgContext_IssuerListInfoEx IssuerListInfo;
  237. PCCERT_CHAIN_CONTEXT pChainContext;
  238. PCCERT_CONTEXT pCertContext;
  239. DWORD error;
  240. if (lpThreadInfo != NULL) {
  241. async = _InternetGetAsync(lpThreadInfo);
  242. _InternetSetAsync(lpThreadInfo, FALSE);
  243. }
  244. INET_ASSERT(ppCertContextArray);
  245. INET_ASSERT(*ppCertContextArray == NULL );
  246. *ppCertContextArray = NULL;
  247. IssuerListInfo.cIssuers = 0;
  248. IssuerListInfo.aIssuers = NULL;
  249. if ( phContext == NULL )
  250. {
  251. error = ERROR_INVALID_PARAMETER;
  252. goto quit;
  253. }
  254. //
  255. // Attempt to find out whether we have any issuers
  256. // from this connection that the Server might have
  257. // told us about.
  258. //
  259. scRet = g_QueryContextAttributes(phContext,
  260. SECPKG_ATTR_ISSUER_LIST_EX,
  261. &IssuerListInfo);
  262. if(FAILED(scRet))
  263. {
  264. error = MapInternetError((DWORD) scRet);
  265. goto quit;
  266. }
  267. cCerts = 0;
  268. //
  269. // Create our CertChain Array for keeping CertChains around
  270. //
  271. *ppCertContextArray = new CERT_CONTEXT_ARRAY();
  272. if ( *ppCertContextArray == NULL )
  273. {
  274. error = ERROR_NOT_ENOUGH_MEMORY;
  275. goto quit;
  276. }
  277. error = (*ppCertContextArray)->GetError();
  278. if ( error != ERROR_SUCCESS)
  279. {
  280. goto quit;
  281. }
  282. if (g_CertFindChainInStore == NULL || g_CertFreeCertificateChain == NULL)
  283. {
  284. // We don't support client-auth unless we have the new crypto dlls
  285. error = ERROR_CALL_NOT_IMPLEMENTED;
  286. goto quit;
  287. }
  288. ZeroMemory(&FindByIssuerPara, sizeof(FindByIssuerPara));
  289. FindByIssuerPara.cbSize = sizeof(FindByIssuerPara);
  290. FindByIssuerPara.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
  291. FindByIssuerPara.dwKeySpec = 0;
  292. FindByIssuerPara.cIssuer = IssuerListInfo.cIssuers;
  293. FindByIssuerPara.rgIssuer = IssuerListInfo.aIssuers;
  294. pChainContext = NULL;
  295. while (TRUE)
  296. {
  297. // Find a certificate chain.
  298. if(g_bOpenMyCertStore && g_hMyCertStore == NULL)
  299. ReopenMyCertStore();
  300. pChainContext = g_CertFindChainInStore(g_hMyCertStore,
  301. X509_ASN_ENCODING,
  302. 0,
  303. CERT_CHAIN_FIND_BY_ISSUER,
  304. &FindByIssuerPara,
  305. pChainContext);
  306. if (pChainContext == NULL)
  307. break;
  308. // Get pointer to leaf certificate context.
  309. pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext;
  310. // This could only happen if there is a bug in the crypto code. But we will deal with
  311. // that and continue looking in any case.
  312. if (pCertContext == NULL)
  313. {
  314. INET_ASSERT(FALSE);
  315. continue;
  316. }
  317. BOOL AcceptCert=FALSE;
  318. // retrieve key usage field
  319. BYTE KeyUsage;
  320. BOOL BKeyUsage = CertGetIntendedKeyUsage(pCertContext->dwCertEncodingType, pCertContext->pCertInfo, &KeyUsage, sizeof(KeyUsage));
  321. // if there is no key usage field (BKeyUsage is FALSE) or key usage is set to CERT_DIGITAL_SIGNATURE_KEY_USAGE, then accept certificate
  322. // as client certificate for SSL auth
  323. if ( !BKeyUsage || (KeyUsage & CERT_DIGITAL_SIGNATURE_KEY_USAGE))
  324. AcceptCert = TRUE;
  325. if (AcceptCert)
  326. error = (*ppCertContextArray)->AddCertContext(pCertContext);
  327. if (error != ERROR_SUCCESS)
  328. {
  329. g_CertFreeCertificateChain(pChainContext);
  330. goto quit;
  331. }
  332. }
  333. quit:
  334. if ( error != ERROR_SUCCESS &&
  335. *ppCertContextArray != NULL )
  336. {
  337. delete *ppCertContextArray;
  338. *ppCertContextArray = NULL;
  339. }
  340. if (IssuerListInfo.aIssuers != NULL)
  341. {
  342. g_FreeContextBuffer(IssuerListInfo.aIssuers);
  343. }
  344. if (lpThreadInfo != NULL) {
  345. _InternetSetAsync(lpThreadInfo, async);
  346. }
  347. DEBUG_LEAVE(error);
  348. return error;
  349. }