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.

597 lines
15 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name :
  4. basicprovider.cxx
  5. Abstract:
  6. Basic authentication provider
  7. Author:
  8. Bilal Alam (balam) 10-Jan-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. ULW3.DLL
  13. --*/
  14. #include "precomp.hxx"
  15. #include "basicprovider.hxx"
  16. #include "uuencode.hxx"
  17. HRESULT
  18. BASIC_AUTH_PROVIDER::DoesApply(
  19. W3_MAIN_CONTEXT * pMainContext,
  20. BOOL * pfApplies
  21. )
  22. /*++
  23. Routine Description:
  24. Does basic authentication apply to this request?
  25. Arguments:
  26. pMainContext - Main context representing request
  27. pfApplies - Set to true if SSPI is applicable
  28. Return Value:
  29. HRESULT
  30. --*/
  31. {
  32. LPCSTR pszAuthHeader;
  33. if ( pMainContext == NULL ||
  34. pfApplies == NULL )
  35. {
  36. DBG_ASSERT( FALSE );
  37. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  38. }
  39. *pfApplies = FALSE;
  40. //
  41. // Get auth type
  42. //
  43. pszAuthHeader = pMainContext->QueryRequest()->GetHeader( HttpHeaderAuthorization );
  44. //
  45. // No package, no auth
  46. //
  47. if ( pszAuthHeader == NULL )
  48. {
  49. return NO_ERROR;
  50. }
  51. //
  52. // Is it basic?
  53. //
  54. if ( _strnicmp( pszAuthHeader, "Basic", sizeof("Basic") - 1 ) == 0 )
  55. {
  56. *pfApplies = TRUE;
  57. }
  58. return NO_ERROR;
  59. }
  60. HRESULT
  61. BASIC_AUTH_PROVIDER::DoAuthenticate(
  62. W3_MAIN_CONTEXT * pMainContext,
  63. BOOL * pfFilterFinished
  64. )
  65. /*++
  66. Routine Description:
  67. Do the authentication thing!
  68. Arguments:
  69. pMainContext - main context
  70. pfFilterFinished - Set to TRUE if filter wants out
  71. Return Value:
  72. HRESULT
  73. --*/
  74. {
  75. CHAR * pszAuthHeader = NULL;
  76. HRESULT hr;
  77. BOOL fRet;
  78. STACK_BUFFER ( buffDecoded, 256 );
  79. CHAR * pszDecoded;
  80. CHAR * pszColon;
  81. // add 1 to strUserDomain for separator "\"
  82. STACK_STRA( strUserDomain, UNLEN + IIS_DNLEN + 1 + 1 );
  83. STACK_STRA( strUserName, UNLEN + 1 );
  84. STACK_STRA( strPassword, PWLEN + 1 );
  85. STACK_STRA( strDomainName, IIS_DNLEN + 1 );
  86. // add 1 to strUserDomainW for separator "\"
  87. STACK_STRU( strUserDomainW, UNLEN + IIS_DNLEN + 1 + 1 );
  88. STACK_STRU( strUserNameW, UNLEN + 1 );
  89. STACK_STRU( strPasswordW, PWLEN + 1 );
  90. STACK_STRU( strRemotePasswordW, PWLEN + 1 );
  91. STACK_STRU( strDomainNameW, IIS_DNLEN + 1 );
  92. // add 1 to strRemoteUserNameW for separator "\"
  93. STACK_STRU( strRemoteUserNameW, UNLEN + IIS_DNLEN + 1 );
  94. W3_METADATA * pMetaData = NULL;
  95. TOKEN_CACHE_ENTRY * pCachedToken = NULL;
  96. BASIC_USER_CONTEXT * pUserContext = NULL;
  97. DWORD dwLogonError = ERROR_SUCCESS;
  98. BOOL fPossibleUPNLogon = FALSE;
  99. if ( pMainContext == NULL )
  100. {
  101. DBG_ASSERT( FALSE );
  102. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  103. }
  104. //
  105. // Get the part after the auth type
  106. //
  107. pszAuthHeader = (PSTR) pMainContext->QueryRequest()->GetHeader( HttpHeaderAuthorization );
  108. DBG_ASSERT( pszAuthHeader != NULL );
  109. //
  110. // We better have an Authorization: Basic header if we got to here!
  111. //
  112. DBG_ASSERT( _strnicmp( pszAuthHeader, "Basic", 5 ) == 0 );
  113. //
  114. // Advance to good stuff
  115. //
  116. if ( pszAuthHeader[ 5 ] == '\0' )
  117. {
  118. pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
  119. Http401BadLogon );
  120. hr = NO_ERROR;
  121. goto exit;
  122. }
  123. pszAuthHeader = pszAuthHeader + 6;
  124. //
  125. // UUDecode the buffer
  126. //
  127. if ( !uudecode( pszAuthHeader,
  128. &buffDecoded ) )
  129. {
  130. hr = HRESULT_FROM_WIN32( GetLastError() );
  131. goto exit;
  132. }
  133. pszDecoded = (CHAR*) buffDecoded.QueryPtr();
  134. //
  135. // Now split out user:password
  136. //
  137. pszColon = strchr( pszDecoded, ':' );
  138. if ( pszColon == NULL )
  139. {
  140. //
  141. // Bad credentials
  142. //
  143. pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
  144. Http401BadLogon );
  145. hr = NO_ERROR;
  146. goto exit;
  147. }
  148. //
  149. // Get user/password
  150. //
  151. hr = strUserDomain.Copy( pszDecoded,
  152. DIFF( pszColon - pszDecoded ) );
  153. if ( FAILED( hr ) )
  154. {
  155. goto exit;
  156. }
  157. hr = strPassword.Copy( pszColon + 1 );
  158. if ( FAILED( hr ) )
  159. {
  160. goto exit;
  161. }
  162. //
  163. // Copy the user name into the request
  164. //
  165. hr = pMainContext->QueryRequest()->SetRequestUserName( strUserDomain );
  166. if ( FAILED( hr ) )
  167. {
  168. goto exit;
  169. }
  170. //
  171. // Copy the password into the request (filters suck)
  172. //
  173. hr = pMainContext->QueryRequest()->SetRequestPassword( strPassword );
  174. if ( FAILED( hr ) )
  175. {
  176. goto exit;
  177. }
  178. //
  179. // Remember the unmapped user name and password
  180. //
  181. hr = strRemoteUserNameW.CopyA( strUserDomain.QueryStr() );
  182. if ( FAILED( hr ) )
  183. {
  184. goto exit;
  185. }
  186. hr = strRemotePasswordW.CopyA( strPassword.QueryStr() );
  187. if ( FAILED( hr ) )
  188. {
  189. goto exit;
  190. }
  191. //
  192. // Notify authentication filters
  193. //
  194. if ( pMainContext->IsNotificationNeeded( SF_NOTIFY_AUTHENTICATION ) )
  195. {
  196. HTTP_FILTER_AUTHENT filterAuthent;
  197. hr = strUserDomain.Resize( SF_MAX_USERNAME );
  198. if ( FAILED( hr ) )
  199. {
  200. goto exit;
  201. }
  202. hr = strPassword.Resize( SF_MAX_PASSWORD );
  203. if ( FAILED( hr ) )
  204. {
  205. goto exit;
  206. }
  207. filterAuthent.pszUser = strUserDomain.QueryStr();
  208. filterAuthent.cbUserBuff = SF_MAX_USERNAME;
  209. filterAuthent.pszPassword = strPassword.QueryStr();
  210. filterAuthent.cbPasswordBuff = SF_MAX_PASSWORD;
  211. fRet = pMainContext->NotifyFilters( SF_NOTIFY_AUTHENTICATION,
  212. &filterAuthent,
  213. pfFilterFinished );
  214. if ( !fRet )
  215. {
  216. hr = HRESULT_FROM_WIN32( GetLastError() );
  217. goto exit;
  218. }
  219. if ( *pfFilterFinished )
  220. {
  221. pMainContext->SetDone();
  222. goto exit;
  223. }
  224. strUserDomain.SetLen( strlen( strUserDomain.QueryStr() ) );
  225. strPassword.SetLen( strlen( strPassword.QueryStr() ) );
  226. }
  227. pMetaData = pMainContext->QueryUrlContext()->QueryMetaData();
  228. DBG_ASSERT( pMetaData != NULL );
  229. if( pMetaData->QueryAuthTypeSupported( MD_AUTH_ANONYMOUS ) &&
  230. strUserDomain.IsEmpty() &&
  231. strPassword.IsEmpty() )
  232. {
  233. //
  234. // If user domain and password strings are empty, then we will
  235. // fall back to anonymous authentication
  236. //
  237. AUTH_PROVIDER * pAnonymousProvider =
  238. W3_STATE_AUTHENTICATION::QueryAnonymousProvider();
  239. DBG_ASSERT( pAnonymousProvider != NULL );
  240. hr = pAnonymousProvider->DoAuthenticate( pMainContext,
  241. pfFilterFinished );
  242. goto exit;
  243. }
  244. //
  245. // Convert to unicode
  246. //
  247. hr = strUserDomainW.CopyA( strUserDomain.QueryStr() );
  248. if ( FAILED( hr ) )
  249. {
  250. goto exit;
  251. }
  252. hr = strPasswordW.CopyA( strPassword.QueryStr() );
  253. if ( FAILED( hr ) )
  254. {
  255. goto exit;
  256. }
  257. //
  258. // Get username/domain out of domain\username
  259. //
  260. hr = W3_STATE_AUTHENTICATION::SplitUserDomain( strUserDomainW,
  261. &strUserNameW,
  262. &strDomainNameW,
  263. pMetaData->QueryDomainName(),
  264. &fPossibleUPNLogon );
  265. if ( FAILED( hr ) )
  266. {
  267. goto exit;
  268. }
  269. //
  270. // Try to get the token
  271. //
  272. DBG_ASSERT( g_pW3Server->QueryTokenCache() != NULL );
  273. hr = g_pW3Server->QueryTokenCache()->GetCachedToken(
  274. strUserNameW.QueryStr(),
  275. strDomainNameW.QueryStr(),
  276. strPasswordW.QueryStr(),
  277. pMetaData->QueryLogonMethod(),
  278. FALSE,
  279. fPossibleUPNLogon,
  280. pMainContext->QueryRequest()->
  281. QueryRemoteSockAddress(),
  282. &pCachedToken,
  283. &dwLogonError );
  284. if ( FAILED( hr ) )
  285. {
  286. goto exit;
  287. }
  288. //
  289. // If pCachedToken is NULL, then logon failed
  290. //
  291. if ( pCachedToken == NULL )
  292. {
  293. DBG_ASSERT( dwLogonError != ERROR_SUCCESS );
  294. if( dwLogonError == ERROR_PASSWORD_MUST_CHANGE ||
  295. dwLogonError == ERROR_PASSWORD_EXPIRED )
  296. {
  297. hr = HRESULT_FROM_WIN32( dwLogonError );
  298. goto exit;
  299. }
  300. pMainContext->QueryResponse()->SetStatus(
  301. HttpStatusUnauthorized,
  302. Http401BadLogon );
  303. pMainContext->SetErrorStatus( HRESULT_FROM_WIN32( dwLogonError ) );
  304. hr = NO_ERROR;
  305. goto exit;
  306. }
  307. //
  308. // We have a token! Setup a W3_USER_CONTEXT and we're done
  309. //
  310. pUserContext = new BASIC_USER_CONTEXT( this );
  311. if ( FAILED( hr ) )
  312. {
  313. goto exit;
  314. }
  315. hr = pUserContext->Create( pCachedToken,
  316. strUserNameW,
  317. strDomainNameW,
  318. strRemotePasswordW,
  319. strRemoteUserNameW,
  320. strUserDomainW,
  321. pMetaData->QueryLogonMethod() );
  322. if ( FAILED( hr ) )
  323. {
  324. pUserContext->DereferenceUserContext();
  325. pUserContext = NULL;
  326. goto exit;
  327. }
  328. pMainContext->SetUserContext( pUserContext );
  329. exit:
  330. //
  331. // Zero out all copies of password in this routine
  332. //
  333. ZeroMemory( buffDecoded.QueryPtr(), buffDecoded.QuerySize() );
  334. if( strPassword.QueryCB() )
  335. {
  336. SecureZeroMemory( ( VOID * )strPassword.QueryStr(),
  337. strPassword.QueryCB() );
  338. }
  339. if( strPasswordW.QueryCB() )
  340. {
  341. SecureZeroMemory( ( VOID * )strPasswordW.QueryStr(),
  342. strPassword.QueryCB() );
  343. }
  344. if( strRemotePasswordW.QueryCB() )
  345. {
  346. SecureZeroMemory( ( VOID * )strRemotePasswordW.QueryStr(),
  347. strPassword.QueryCB() );
  348. }
  349. return hr;
  350. }
  351. HRESULT
  352. BASIC_AUTH_PROVIDER::OnAccessDenied(
  353. W3_MAIN_CONTEXT * pMainContext
  354. )
  355. /*++
  356. Routine Description:
  357. Add basic authentication header
  358. Arguments:
  359. pMainContext - Main context
  360. Return Value:
  361. HRESULT
  362. --*/
  363. {
  364. STACK_STRU( strAuthHeader, 256 );
  365. STACK_STRA( straAuthHeader, 256 );
  366. STACK_STRU( strHostAddr, 256 );
  367. HRESULT hr;
  368. W3_METADATA * pMetaData;
  369. if ( pMainContext == NULL )
  370. {
  371. DBG_ASSERT( FALSE );
  372. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  373. }
  374. hr = strAuthHeader.Copy( L"Basic realm=\"" );
  375. if ( FAILED( hr ) )
  376. {
  377. return hr;
  378. }
  379. pMetaData = pMainContext->QueryUrlContext()->QueryMetaData();
  380. DBG_ASSERT( pMetaData != NULL );
  381. //
  382. // If a realm is configured, use it. Otherwise use host address of
  383. // request
  384. //
  385. if ( pMetaData->QueryRealm() != NULL )
  386. {
  387. hr = strAuthHeader.Append( pMetaData->QueryRealm() );
  388. }
  389. else
  390. {
  391. hr = pMainContext->QueryRequest()->GetHostAddr( &strHostAddr );
  392. if ( FAILED( hr ) )
  393. {
  394. return hr;
  395. }
  396. hr = strAuthHeader.Append( strHostAddr );
  397. }
  398. if ( FAILED( hr ) )
  399. {
  400. return hr;
  401. }
  402. hr = strAuthHeader.Append( L"\"" );
  403. if ( FAILED( hr ) )
  404. {
  405. return hr;
  406. }
  407. if (FAILED(hr = straAuthHeader.CopyW(strAuthHeader.QueryStr())))
  408. {
  409. return hr;
  410. }
  411. return pMainContext->QueryResponse()->SetHeader( "WWW-Authenticate",
  412. 16,
  413. straAuthHeader.QueryStr(),
  414. (USHORT)straAuthHeader.QueryCCH() );
  415. }
  416. HRESULT
  417. BASIC_USER_CONTEXT::Create(
  418. TOKEN_CACHE_ENTRY * pCachedToken,
  419. STRU & strUserName,
  420. STRU & strDomainName,
  421. STRU & strPassword,
  422. STRU & strRemoteUserName,
  423. STRU & strMappedDomainUser,
  424. DWORD dwLogonMethod
  425. )
  426. /*++
  427. Routine Description:
  428. Initialize a basic user context
  429. Arguments:
  430. pCachedToken - The token
  431. strUserName - User name (without embedded domain)
  432. strDomainName - Domain name
  433. strPassword - Password
  434. strRemoteUserName - Unmapped user name
  435. strMappedDomainUser - Mapped domain user (may have embedded domain)
  436. dwLogonMethod - Logon method
  437. Return Value:
  438. HRESULT
  439. --*/
  440. {
  441. HRESULT hr;
  442. UNREFERENCED_PARAMETER( strUserName );
  443. UNREFERENCED_PARAMETER( strDomainName );
  444. UNREFERENCED_PARAMETER( dwLogonMethod );
  445. if ( pCachedToken == NULL )
  446. {
  447. DBG_ASSERT( FALSE );
  448. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  449. }
  450. hr = _strUserName.Copy( strMappedDomainUser );
  451. if ( FAILED( hr ) )
  452. {
  453. return hr;
  454. }
  455. hr = _strPassword.Append( strPassword );
  456. if ( FAILED( hr ) )
  457. {
  458. return hr;
  459. }
  460. hr = _strRemoteUserName.Append( strRemoteUserName );
  461. if ( FAILED( hr ) )
  462. {
  463. return hr;
  464. }
  465. _pCachedToken = pCachedToken;
  466. SetCachedToken( TRUE );
  467. return NO_ERROR;
  468. }