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.

558 lines
13 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1993 **/
  4. /**********************************************************************/
  5. /*
  6. security.c
  7. This module manages security for the W3 Service.
  8. FILE HISTORY:
  9. KeithMo 07-Mar-1993 Created.
  10. */
  11. #include "w3p.hxx"
  12. #include <lonsi.hxx>
  13. DWORD DeniedFlagTable[] = { 0,
  14. SF_DENIED_BY_CONFIG,
  15. SF_DENIED_RESOURCE,
  16. SF_DENIED_FILTER,
  17. SF_DENIED_APPLICATION
  18. };
  19. DWORD
  20. DeniedFlagsToSubStatus(
  21. DWORD fFlags
  22. )
  23. /*++
  24. Routine Description:
  25. Map a set of denied flags to a substatus for use in custom error lookup.
  26. Arguments:
  27. fFlags - The flags to be mapped.
  28. Return Value:
  29. The substatus if we can map it, or 0 otherwise.
  30. --*/
  31. {
  32. int i;
  33. fFlags &= ~SF_DENIED_LOGON;
  34. for (i = 0; i < sizeof(DeniedFlagTable)/sizeof(DWORD);i++)
  35. {
  36. if (DeniedFlagTable[i] == fFlags)
  37. {
  38. return i+1;
  39. }
  40. }
  41. return 0;
  42. }
  43. //
  44. // Public functions.
  45. //
  46. BOOL
  47. HTTP_REQ_BASE::SendAuthNeededResp(
  48. BOOL * pfFinished
  49. )
  50. /*++
  51. Routine Description:
  52. Sends an access denied HTTP server response with the accompanying
  53. authentication schemes the server supports
  54. Parameters:
  55. pfFinished - If set to TRUE, indicates no further processing is needed
  56. for this request
  57. Return Value:
  58. TRUE if successful, FALSE on error
  59. --*/
  60. {
  61. CHAR * pszTail;
  62. DWORD cbRespBufUsed;
  63. DWORD cbRespBufLeft;
  64. DWORD cbNeeded;
  65. LPCSTR pszAccessDeniedMsg;
  66. CHAR * pszMsgBody;
  67. BYTE cMsgBuffer[128] ={ '\0' };
  68. BUFFER bufMsg(cMsgBuffer, sizeof(cMsgBuffer));
  69. DWORD dwSubStatus;
  70. DWORD dwMsgSize;
  71. DWORD dwMsgSizeNeeded;
  72. BOOL bHaveCustom;
  73. STR strAuthHdrs;
  74. *pfFinished = FALSE;
  75. if ( QueryRespBuf()->QuerySize() < MIN_BUFFER_SIZE_FOR_HEADERS )
  76. {
  77. if ( !QueryRespBuf()->Resize( MIN_BUFFER_SIZE_FOR_HEADERS ) )
  78. {
  79. return FALSE;
  80. }
  81. }
  82. if ( !HTTP_REQ_BASE::BuildStatusLine( QueryRespBuf(),
  83. !IsProxyRequest() ? HT_DENIED :
  84. HT_PROXY_AUTH_REQ,
  85. NO_ERROR ))
  86. {
  87. return FALSE;
  88. }
  89. //
  90. // "Server: Microsoft/xxx
  91. //
  92. pszTail = (char*) QueryRespBuf()->QueryPtr() + QueryRespBufCB();
  93. APPEND_VER_STR( pszTail );
  94. //
  95. // "Date: <GMT Time>" - Time the response was sent.
  96. //
  97. // build Date: uses Date/Time cache
  98. pszTail += g_pDateTimeCache->GetFormattedCurrentDateTime( pszTail );
  99. //
  100. // See if we have a custom error message defined for this.
  101. //
  102. dwSubStatus = DeniedFlagsToSubStatus(_Filter.QueryDeniedFlags());
  103. if (CheckCustomError(&bufMsg, !IsProxyRequest() ?
  104. HT_DENIED : HT_PROXY_AUTH_REQ, dwSubStatus, pfFinished, &dwMsgSize, FALSE))
  105. {
  106. DBG_ASSERT(!*pfFinished);
  107. pszAccessDeniedMsg = (CHAR *)bufMsg.QueryPtr();
  108. dwMsgSizeNeeded = strlen(pszAccessDeniedMsg);
  109. bHaveCustom = TRUE;
  110. }
  111. else
  112. {
  113. pszAccessDeniedMsg = QueryW3Instance()->QueryAccessDeniedMsg();
  114. if ( memcmp( _strMethod.QueryStr(), "HEAD", 4 ))
  115. {
  116. dwMsgSize = strlen(pszAccessDeniedMsg);
  117. }
  118. else
  119. {
  120. dwMsgSize = 0;
  121. }
  122. dwMsgSizeNeeded = dwMsgSize;
  123. bHaveCustom = FALSE;
  124. }
  125. //
  126. // If this is not the first call, then return the current authentication
  127. // data blob otherwise return the forms of authentication the server
  128. // accepts
  129. //
  130. if ( IsAuthenticating() )
  131. {
  132. if ( !strAuthHdrs.Copy( IsProxyRequest() ? "Proxy-Authenticate" :
  133. "WWW-Authenticate" ) ||
  134. !strAuthHdrs.Append( ": " ) ||
  135. !strAuthHdrs.Append( _strAuthInfo ) ||
  136. !strAuthHdrs.Append( "\r\n" ) )
  137. {
  138. return FALSE;
  139. }
  140. }
  141. else
  142. {
  143. if ( !AppendAuthenticationHdrs( &strAuthHdrs,
  144. pfFinished ))
  145. {
  146. return FALSE;
  147. }
  148. if ( *pfFinished )
  149. {
  150. return TRUE;
  151. }
  152. }
  153. //
  154. // Make sure there's enough size for any ISAPI denial headers plus any
  155. // admin specified access denied message
  156. //
  157. cbRespBufUsed = QueryRespBufCB();
  158. cbRespBufLeft = QueryRespBuf()->QuerySize() - cbRespBufUsed;
  159. cbNeeded = dwMsgSizeNeeded +
  160. _strDenialHdrs.QueryCB() +
  161. 250 +
  162. strAuthHdrs.QueryCB();
  163. if ( cbNeeded > cbRespBufLeft )
  164. {
  165. if ( !QueryRespBuf()->Resize( cbNeeded + cbRespBufUsed ))
  166. {
  167. return FALSE;
  168. }
  169. }
  170. pszTail = QueryRespBufPtr() + cbRespBufUsed;
  171. memcpy( pszTail,
  172. strAuthHdrs.QueryStr(),
  173. strAuthHdrs.QueryCB() );
  174. pszTail += strAuthHdrs.QueryCB();
  175. if ( IsKeepConnSet() )
  176. {
  177. if (!IsOneOne())
  178. {
  179. if ( !IsProxyRequest() )
  180. {
  181. APPEND_STRING( pszTail, "Connection: keep-alive\r\n" );
  182. }
  183. else
  184. {
  185. APPEND_STRING( pszTail, "Proxy-Connection: keep-alive\r\n" );
  186. }
  187. }
  188. } else
  189. {
  190. if (IsOneOne())
  191. {
  192. if ( !IsProxyRequest() )
  193. {
  194. APPEND_STRING( pszTail, "Connection: close\r\n" );
  195. } else
  196. {
  197. APPEND_STRING( pszTail, "Proxy-Connection: close\r\n" );
  198. }
  199. }
  200. }
  201. //
  202. // Add any additional headers supplied by the filters plus the header
  203. // termination
  204. //
  205. APPEND_NUMERIC_HEADER( pszTail, "Content-Length: ", dwMsgSize, "\r\n" );
  206. if (!_strDenialHdrs.IsEmpty())
  207. {
  208. DWORD cb = _strDenialHdrs.QueryCCH();
  209. CHAR *pszDenialHdr = _strDenialHdrs.QueryStr();
  210. //
  211. // We must always have CR-LF at the end
  212. //
  213. DBG_ASSERT( cb >= 2 && pszDenialHdr[cb - 2] == '\r' && pszDenialHdr[cb - 1] == '\n' );
  214. APPEND_STR_HEADER( pszTail, "", _strDenialHdrs, "" );
  215. }
  216. if (!bHaveCustom)
  217. {
  218. APPEND_STRING( pszTail, "Content-Type: text/html\r\n\r\n" );
  219. }
  220. if ( memcmp( _strMethod.QueryStr(), "HEAD", 4 ))
  221. {
  222. APPEND_PSZ_HEADER( pszTail, "", pszAccessDeniedMsg, "" );
  223. }
  224. else
  225. {
  226. if (bHaveCustom)
  227. {
  228. DWORD dwBytesToCopy;
  229. // Copy in only the content-type header, which is everything
  230. // except for the message itself.
  231. dwBytesToCopy = dwMsgSizeNeeded - dwMsgSize;
  232. memcpy(pszTail, pszAccessDeniedMsg, dwBytesToCopy);
  233. pszTail += dwBytesToCopy;
  234. *pszTail++ = '\0';
  235. }
  236. }
  237. DBG_ASSERT( QueryRespBuf()->QuerySize() > QueryRespBufCB() );
  238. IF_DEBUG( PARSING )
  239. {
  240. DBGPRINTF(( DBG_CONTEXT,
  241. "[SendAuthNeededResp] Sending headers: %s",
  242. QueryRespBufPtr() ));
  243. }
  244. //
  245. // Add IO_FLAG_AND_RECV if we're doing a multi-leg authentication exchange and the
  246. // connection is to be kept open for the duration of the exchange
  247. //
  248. return SendHeader( QueryRespBufPtr(),
  249. (DWORD) -1,
  250. (IO_FLAG_ASYNC | ( ( IsAuthenticating() && IsKeepConnSet() ) ?
  251. IO_FLAG_AND_RECV :
  252. 0)),
  253. pfFinished );
  254. }
  255. BOOL
  256. HTTP_REQ_BASE::AppendAuthenticationHdrs(
  257. STR * pstrAuthenticationHdrs,
  258. BOOL * pfFinished
  259. )
  260. /*++
  261. Routine Description:
  262. This method adds the appropriate "WWW-Authenticate" strings to the passed
  263. server response string
  264. This routine assumes pRespBuf is large enough to hold the authentication
  265. headers
  266. Parameters:
  267. pStrAuthenticationHdrs - buffer
  268. pfFinished - Set to TRUE if no further processing is needed on this request
  269. Return Value:
  270. TRUE if successful, FALSE on error
  271. --*/
  272. {
  273. CHAR * pchField;
  274. DWORD dwAuth;
  275. BOOL fDoBasic;
  276. const LPSTR * apszNTProviders = NULL;
  277. PW3_SERVER_INSTANCE pInstance = QueryW3Instance();
  278. STACK_STR( strRealm, MAX_PATH); // make a local copy of the realm headers.
  279. //
  280. // If no realm was supplied in the registry, use the host name
  281. //
  282. if ( !_fBasicRealm )
  283. {
  284. fDoBasic = TRUE;
  285. }
  286. else
  287. {
  288. fDoBasic = FALSE;
  289. }
  290. //
  291. // Notify any Access Denied filters the user has been denied access
  292. //
  293. if ( _Filter.IsNotificationNeeded( SF_NOTIFY_ACCESS_DENIED,
  294. IsSecurePort() ))
  295. {
  296. if ( !_Filter.NotifyAccessDenied( _strURL.QueryStr(),
  297. _strPhysicalPath.QueryStr(),
  298. pfFinished ))
  299. {
  300. return FALSE;
  301. }
  302. if ( *pfFinished )
  303. {
  304. return TRUE;
  305. }
  306. }
  307. //
  308. // We may not have read the metadata for this URL yet if a filter
  309. // returned access denied during the initial read_raw notifications
  310. //
  311. if ( !QueryMetaData() )
  312. {
  313. DBGPRINTF(( DBG_CONTEXT,
  314. "[AppendAuthenticationHeaders] Warning - Metadata not read yet for NT auth list!\n"));
  315. return TRUE;
  316. }
  317. //
  318. // Send the correct header depending on if we're a proxy
  319. //
  320. if ( !IsProxyRequest() )
  321. {
  322. pchField = "WWW-Authenticate: ";
  323. }
  324. else
  325. {
  326. pchField = "Proxy-Authenticate: ";
  327. }
  328. dwAuth = QueryAuthentication();
  329. apszNTProviders = QueryMetaData()->QueryNTProviders();
  330. //
  331. // generate the realm information for this request
  332. //
  333. strRealm.Copy( QueryMetaData()->QueryRealm()
  334. ? QueryMetaData()->QueryRealm()
  335. : QueryHostAddr() );
  336. //
  337. // Append the appropriate authentication headers
  338. //
  339. if ( dwAuth & INET_INFO_AUTH_NT_AUTH )
  340. {
  341. DWORD i = 0;
  342. //
  343. // For each authentication package the server supports, add a
  344. // WWW-Authenticate header
  345. //
  346. while ( apszNTProviders[i] )
  347. {
  348. if ( !pstrAuthenticationHdrs->Append( pchField ) ||
  349. !pstrAuthenticationHdrs->Append( apszNTProviders[ i ] ) ||
  350. !pstrAuthenticationHdrs->Append( "\r\n" ) )
  351. {
  352. return FALSE;
  353. }
  354. i++;
  355. }
  356. }
  357. if ( fDoBasic && (dwAuth & INET_INFO_AUTH_CLEARTEXT) )
  358. {
  359. if ( !pstrAuthenticationHdrs->Append( pchField ) ||
  360. !pstrAuthenticationHdrs->Append( "Basic realm=\"" ) ||
  361. !pstrAuthenticationHdrs->Append( strRealm ) ||
  362. !pstrAuthenticationHdrs->Append( "\"\r\n" ) )
  363. {
  364. return FALSE;
  365. }
  366. }
  367. return TRUE;
  368. }
  369. BOOL
  370. HTTP_REQ_BASE::ExtractClearNameAndPswd(
  371. CHAR * pch,
  372. STR * pstrUserName,
  373. STR * pstrPassword,
  374. BOOL fUUEncoded
  375. )
  376. /*++
  377. Routine Description:
  378. This method breaks a string in the form "username:password" and
  379. places the components into pstrUserName and pstrPassword. If fUUEncoded
  380. is TRUE, then the string is UUDecoded first.
  381. Parameters:
  382. pch - Pointer to <username>:<password>
  383. Return Value:
  384. TRUE if successful, FALSE on error
  385. --*/
  386. {
  387. STACK_STR( strDecoded, MAX_PATH );
  388. CHAR * pchtmp;
  389. pch = SkipWhite( pch );
  390. if ( fUUEncoded )
  391. {
  392. if ( !uudecode( pch,
  393. &strDecoded ))
  394. {
  395. return FALSE;
  396. }
  397. pch = strDecoded.QueryStrA();
  398. }
  399. pchtmp = SkipTo( pch, TEXT(':') );
  400. if ( *pchtmp == TEXT(':') )
  401. {
  402. *pchtmp = TEXT('\0');
  403. if ( !_strUserName.Copy( pch ) ||
  404. !_strPassword.Copy( pchtmp + 1 ))
  405. {
  406. return FALSE;
  407. }
  408. }
  409. return TRUE;
  410. }
  411. HANDLE
  412. HTTP_REQ_BASE::QueryPrimaryToken(
  413. HANDLE * phDelete
  414. )
  415. /*++
  416. Routine Description:
  417. This method returns a non-impersonation user token handle that's
  418. usable with CreateProcessAsUser.
  419. Parameters:
  420. phDelete - If returned as non-null, the caller is responsible for calling
  421. CloseHandle on this value when done using the returned value.
  422. Return Value:
  423. The primary token handle if successful, FALSE otherwise
  424. --*/
  425. {
  426. *phDelete = NULL;
  427. if ( !UseVrAccessToken() )
  428. {
  429. return _tcpauth.QueryPrimaryToken();
  430. }
  431. return _pMetaData->QueryVrPrimaryAccessToken();
  432. }
  433.