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.

540 lines
16 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 2000
  6. //
  7. // File: parser.cxx
  8. //
  9. // Contents: Digest Access Parser for directives
  10. // Main entry points into this dll:
  11. // ParseForNames
  12. // Very primitive parser. Most strings are quoted except for NC
  13. //
  14. // History: KDamour 16Mar00 Based on IIS authfilt.cxx
  15. //
  16. //------------------------------------------------------------------------
  17. #include <global.h>
  18. // Local function prototypes
  19. // Check for backslash character in a counted string of chars
  20. BOOL CheckBSlashChar(
  21. IN PSTR pcStr,
  22. IN USHORT len);
  23. // Helper function to DigestParser2
  24. NTSTATUS DigestProcessEntry(
  25. IN PSTR pcBeginName,
  26. IN PSTR pcEndName,
  27. IN PSTR pcBeginValue,
  28. IN PSTR pcEndValue,
  29. IN PSTR *pNameTable,
  30. IN UINT cNameTable,
  31. IN BOOL fBSlashEncoded,
  32. OUT PDIGEST_PARAMETER pDigest);
  33. // Used by parser to find the keywords
  34. // Keep insync with enum MD5_AUTH_NAME
  35. PSTR MD5_AUTH_NAMES[] = {
  36. "username",
  37. "realm",
  38. "nonce",
  39. "cnonce",
  40. "nc",
  41. "algorithm",
  42. "qop",
  43. "method",
  44. "uri",
  45. "response",
  46. "hentity",
  47. "authzid",
  48. "domain",
  49. "stale",
  50. "opaque",
  51. "maxbuf",
  52. "charset",
  53. "cipher",
  54. "digest-uri",
  55. "rspauth",
  56. "nextnonce"
  57. "" // Not really needed
  58. };
  59. enum STATE_TYPE
  60. {
  61. READY,
  62. DIRECTIVE,
  63. COMMAFIND,
  64. EQUALFIND,
  65. ASSIGNMENT,
  66. QUOTEDVALUE,
  67. VALUE,
  68. ENDING,
  69. PROCESS_ENTRY,
  70. FAILURE
  71. };
  72. //+--------------------------------------------------------------------
  73. //
  74. // Function: DigestParser2
  75. //
  76. // Synopsis: Parse list of name=value pairs for known names
  77. //
  78. // Effects:
  79. //
  80. // Arguments: pszStr - line to parse ( '\0' delimited - terminated)
  81. // pNameTable - table of known names
  82. // cNameTable - number of known names
  83. // pDigest - set all of the directives in pDigest->strParams[x}
  84. //
  85. // Returns: STATUS_SUCCESS if success, otherwise error
  86. //
  87. // Notes:
  88. // Buffers are not wide Unicode!
  89. //
  90. //
  91. //---------------------------------------------------------------------
  92. NTSTATUS DigestParser2(
  93. PSecBuffer pInputBuf,
  94. PSTR *pNameTable,
  95. UINT cNameTable,
  96. OUT PDIGEST_PARAMETER pDigest
  97. )
  98. {
  99. NTSTATUS Status = STATUS_SUCCESS;
  100. PSTR pcBeginName = NULL;
  101. PSTR pcEndName = NULL;
  102. PSTR pcBeginValue = NULL;
  103. PSTR pcEndValue = NULL;
  104. PSTR pcEndBuffer = NULL; // End of buffer to prevent NC increment from going past end
  105. PSTR pcCurrent = NULL;
  106. STATE_TYPE parserstate = DIRECTIVE;
  107. BOOL fEscapedChar = FALSE;
  108. // Verify that buffer exists and is of type single byte characters (not Unicode)
  109. if (!pInputBuf || (pInputBuf->cbBuffer && !pInputBuf->pvBuffer) ||
  110. (PBUFFERTYPE(pInputBuf) != SECBUFFER_TOKEN))
  111. {
  112. Status = SEC_E_INVALID_TOKEN;
  113. DebugLog((DEB_ERROR, "DigestParser2: Incorrect digest buffer format status 0x%x\n", Status));
  114. goto CleanUp;
  115. }
  116. if (!pInputBuf->cbBuffer)
  117. {
  118. return STATUS_SUCCESS; // Nothing to process happens with makesignature
  119. }
  120. pcEndBuffer = (char *)pInputBuf->pvBuffer + pInputBuf->cbBuffer;
  121. for (pcCurrent = (char *)pInputBuf->pvBuffer; pcCurrent < pcEndBuffer; pcCurrent++)
  122. {
  123. if (parserstate == FAILURE)
  124. {
  125. break;
  126. }
  127. if (*pcCurrent == CHAR_NULL)
  128. { // If we hit a premature End of String then Exit immediately from scan
  129. break;
  130. }
  131. if (parserstate == COMMAFIND)
  132. {
  133. if (isspace((int) (unsigned char)*pcCurrent))
  134. {
  135. continue; // get next char within for loop
  136. }
  137. if (*pcCurrent == CHAR_COMMA)
  138. {
  139. pcBeginName = NULL;
  140. pcEndName = NULL;
  141. pcBeginValue = pcEndValue = NULL;
  142. parserstate = DIRECTIVE;
  143. continue;
  144. }
  145. else
  146. {
  147. DebugLog((DEB_ERROR, "DigestParser2: CommaFind bad char 0x%x\n", *pcCurrent));
  148. parserstate = FAILURE; // only leading spaces or a comma is expected
  149. continue;
  150. }
  151. }
  152. if (parserstate == DIRECTIVE)
  153. {
  154. if (*pcCurrent == CHAR_EQUAL)
  155. {
  156. parserstate = ASSIGNMENT;
  157. continue;
  158. }
  159. if (isspace((int) (unsigned char)*pcCurrent))
  160. {
  161. if (!pcBeginName)
  162. {
  163. continue; // leading space chars so get next char
  164. }
  165. else
  166. {
  167. parserstate = EQUALFIND; // spaces are not allowed - directive is a single token
  168. continue;
  169. }
  170. }
  171. if (!pcBeginName)
  172. {
  173. pcBeginName = pcCurrent; // mnark begining of token
  174. }
  175. pcEndName = pcCurrent;
  176. continue;
  177. }
  178. if (parserstate == EQUALFIND)
  179. {
  180. if (isspace((int) (unsigned char)*pcCurrent))
  181. {
  182. continue; // get next char within for loop
  183. }
  184. if (*pcCurrent == CHAR_EQUAL)
  185. {
  186. parserstate = ASSIGNMENT;
  187. continue;
  188. }
  189. else
  190. {
  191. parserstate = FAILURE; // only leading spaces or a equal is expected
  192. continue;
  193. }
  194. }
  195. if (parserstate == ASSIGNMENT)
  196. {
  197. if (*pcCurrent == CHAR_DQUOTE)
  198. {
  199. parserstate = QUOTEDVALUE;
  200. continue;
  201. }
  202. if (isspace((int) (unsigned char)*pcCurrent))
  203. {
  204. continue; // get next char within for loop
  205. }
  206. pcBeginValue = pcCurrent;
  207. pcEndValue = pcCurrent;
  208. parserstate = VALUE;
  209. continue;
  210. }
  211. if (parserstate == QUOTEDVALUE)
  212. {
  213. if ((*pcCurrent == CHAR_BACKSLASH) && (fEscapedChar == FALSE))
  214. {
  215. // used to escape the following character
  216. fEscapedChar = TRUE;
  217. if (!pcBeginValue)
  218. {
  219. pcBeginValue = pcCurrent;
  220. pcEndValue = pcCurrent;
  221. continue;
  222. }
  223. continue;
  224. }
  225. if ((*pcCurrent == CHAR_DQUOTE) && (fEscapedChar == FALSE))
  226. {
  227. Status = DigestProcessEntry(pcBeginName, pcEndName, pcBeginValue, pcEndValue,
  228. pNameTable, cNameTable, TRUE, pDigest);
  229. if (!NT_SUCCESS(Status))
  230. {
  231. DebugLog((DEB_ERROR, "DigestParser2: Failed to process directive 0x%x\n", Status));
  232. goto CleanUp;
  233. }
  234. parserstate = COMMAFIND; // start again statemachine
  235. continue;
  236. }
  237. fEscapedChar = FALSE; // reset to not escaped state
  238. if (!pcBeginValue)
  239. {
  240. pcBeginValue = pcCurrent;
  241. pcEndValue = pcCurrent;
  242. continue;
  243. }
  244. pcEndValue = pcCurrent;
  245. continue;
  246. }
  247. if (parserstate == VALUE)
  248. {
  249. if (*pcCurrent == CHAR_COMMA)
  250. {
  251. Status = DigestProcessEntry(pcBeginName, pcEndName, pcBeginValue, pcEndValue,
  252. pNameTable, cNameTable, FALSE, pDigest);
  253. if (!NT_SUCCESS(Status))
  254. {
  255. DebugLog((DEB_ERROR, "DigestParser2: Failed to process directive 0x%x\n", Status));
  256. goto CleanUp;
  257. }
  258. pcBeginName = NULL;
  259. pcEndName = NULL;
  260. pcBeginValue = pcEndValue = NULL;
  261. parserstate = DIRECTIVE; // find separator if any
  262. continue;
  263. }
  264. // token ends on first white space
  265. if (isspace((int) (unsigned char)*pcCurrent))
  266. {
  267. Status = DigestProcessEntry(pcBeginName, pcEndName, pcBeginValue, pcEndValue,
  268. pNameTable, cNameTable, FALSE, pDigest);
  269. if (!NT_SUCCESS(Status))
  270. {
  271. DebugLog((DEB_ERROR, "DigestParser2: Failed to process directive 0x%x\n", Status));
  272. goto CleanUp;
  273. }
  274. parserstate = COMMAFIND; // find separator if any
  275. continue;
  276. }
  277. else
  278. {
  279. pcEndValue = pcCurrent;
  280. }
  281. }
  282. }
  283. if ((parserstate == FAILURE) || (parserstate == QUOTEDVALUE) ||
  284. (parserstate == ASSIGNMENT) || (parserstate == DIRECTIVE) ||
  285. (parserstate == EQUALFIND))
  286. {
  287. Status = SEC_E_ILLEGAL_MESSAGE;
  288. goto CleanUp;
  289. }
  290. // There might be a NULL terminated directive value to process
  291. if ((parserstate == VALUE))
  292. {
  293. Status = DigestProcessEntry(pcBeginName, pcEndName, pcBeginValue, pcEndValue,
  294. pNameTable, cNameTable, FALSE, pDigest);
  295. }
  296. CleanUp:
  297. DebugLog((DEB_TRACE, "DigestParser: leaving status 0x%x\n", Status));
  298. return(Status);
  299. }
  300. NTSTATUS DigestProcessEntry(
  301. IN PSTR pcBeginName,
  302. IN PSTR pcEndName,
  303. IN PSTR pcBeginValue,
  304. IN PSTR pcEndValue,
  305. IN PSTR *pNameTable,
  306. IN UINT cNameTable,
  307. IN BOOL fBSlashEncoded,
  308. IN OUT PDIGEST_PARAMETER pDigest
  309. )
  310. {
  311. NTSTATUS Status = STATUS_SUCCESS;
  312. USHORT cbName = 0;
  313. USHORT cbValue = 0;
  314. UINT iN = 0;
  315. BOOL fBSPresent = FALSE;
  316. PCHAR pcTemp = NULL;
  317. PSTR pcDst = NULL;
  318. PSTR pcLoc = NULL;
  319. USHORT iCnt = 0;
  320. // DebugLog((DEB_TRACE_FUNC, "DigestProcessEntry: Entering\n"));
  321. if (!pcBeginName || !pcEndName)
  322. {
  323. DebugLog((DEB_ERROR, "DigestProcessEntry: Badly formed directive\n"));
  324. return (STATUS_UNSUCCESSFUL);
  325. }
  326. cbName = (USHORT)(pcEndName - pcBeginName) + 1;
  327. if (pcBeginValue && pcEndValue)
  328. {
  329. cbValue = (USHORT)(pcEndValue - pcBeginValue) + 1;
  330. }
  331. else
  332. cbValue = 0;
  333. for ( iN = 0 ; iN < cNameTable ; ++iN )
  334. {
  335. if ( !_strnicmp( pNameTable[iN], pcBeginName, cbName ) )
  336. {
  337. // DebugLog((DEB_TRACE, "DigestParser: directive found\n"));
  338. break;
  339. }
  340. }
  341. if ( iN < cNameTable ) // We found a match!!!!!
  342. {
  343. if (iN == MD5_AUTH_DIGESTURI)
  344. {
  345. iN = MD5_AUTH_URI; // Map SASL's "digest-uri" to "uri"
  346. }
  347. pDigest->usDirectiveCnt[iN] = pDigest->usDirectiveCnt[iN] + 1; // indicate that directive was found
  348. if (cbValue)
  349. {
  350. // For space optimization, if not Backslash encoded then use orginal memory buffer
  351. // To simply code, can removed all refernces and just use a copy of the original
  352. // while removing the backslash characters
  353. if ((fBSlashEncoded == TRUE) &&
  354. ( !(pDigest->usFlags & FLAG_NOBS_DECODE)))
  355. {
  356. // quick search to see if there is a BackSlash character there
  357. fBSPresent = CheckBSlashChar(pcBeginValue, cbValue);
  358. if (fBSPresent == TRUE)
  359. {
  360. pcDst = (PCHAR)DigestAllocateMemory(cbValue + 1);
  361. if (!pcDst)
  362. {
  363. Status = SEC_E_INSUFFICIENT_MEMORY;
  364. DebugLog((DEB_ERROR, "DigestProcessEntry: allocate error 0x%x\n", Status));
  365. goto CleanUp;
  366. }
  367. // Now copy over the string removing and back slash encoding
  368. pcLoc = pcBeginValue;
  369. pcTemp = pcDst;
  370. while (pcLoc <= pcEndValue)
  371. {
  372. if (*pcLoc == CHAR_BACKSLASH)
  373. {
  374. pcLoc++; // eat the backslash
  375. // Indicate possible broken BS encoding by client
  376. // check only the username and look for any BS chars without BS BS pattern (proper encoding)
  377. if ((iN == MD5_AUTH_USERNAME) && (*pcLoc != CHAR_BACKSLASH))
  378. {
  379. DebugLog((DEB_WARN, "DigestProcessEntry: possible broken BS encoding by client\n"));
  380. pDigest->usFlags = pDigest->usFlags | FLAG_BS_ENCODE_CLIENT_BROKEN;
  381. }
  382. }
  383. *pcTemp++ = *pcLoc++;
  384. iCnt++;
  385. }
  386. // give the memory to member structure
  387. // clear out any previous memory alloc (if called on a retry)
  388. StringFree(&(pDigest->strDirective[iN]));
  389. pDigest->strDirective[iN].Buffer = pcDst;
  390. pDigest->strDirective[iN].Length = iCnt;
  391. pDigest->strDirective[iN].MaximumLength = cbValue+1;
  392. pcDst = NULL;
  393. pDigest->refstrParam[iN].Buffer = pDigest->strDirective[iN].Buffer;
  394. pDigest->refstrParam[iN].Length = pDigest->strDirective[iN].Length;
  395. pDigest->refstrParam[iN].MaximumLength = pDigest->strDirective[iN].MaximumLength;
  396. }
  397. else
  398. {
  399. pDigest->refstrParam[iN].Buffer = pcBeginValue;
  400. pDigest->refstrParam[iN].Length = cbValue;
  401. pDigest->refstrParam[iN].MaximumLength = cbValue;
  402. }
  403. }
  404. else
  405. {
  406. pDigest->refstrParam[iN].Buffer = pcBeginValue;
  407. pDigest->refstrParam[iN].Length = cbValue;
  408. pDigest->refstrParam[iN].MaximumLength = cbValue;
  409. }
  410. }
  411. }
  412. CleanUp:
  413. // DebugLog((DEB_TRACE_FUNC, "DigestProcessEntry: Leaving 0x%x\n", Status));
  414. return(Status);
  415. }
  416. //+--------------------------------------------------------------------
  417. //
  418. // Function: CheckBSlashChar
  419. //
  420. // Synopsis: Search a string for a Back Slash character
  421. //
  422. // Effects:
  423. //
  424. // Arguments:
  425. // pcStr - pointer to string of characters
  426. // len - number of characters to search
  427. //
  428. // Returns: TRUE if found, FALSE otherwise
  429. //
  430. // Notes:
  431. //
  432. //
  433. //---------------------------------------------------------------------
  434. BOOL CheckBSlashChar(
  435. IN PSTR pcStr,
  436. IN USHORT len)
  437. {
  438. BOOL fFound = FALSE;
  439. USHORT i = 0;
  440. for (i = 0; i < len; i++)
  441. {
  442. if (*pcStr++ == CHAR_BACKSLASH)
  443. {
  444. fFound = TRUE;
  445. break;
  446. }
  447. }
  448. return (fFound);
  449. }
  450. /*
  451. //+--------------------------------------------------------------------
  452. //
  453. // Function: DigestTokenVerify
  454. //
  455. // Synopsis: Verify that a token conforms to RFC 2616 sect 2.2
  456. //
  457. // Effects:
  458. //
  459. // Arguments:
  460. // pcBeginToken - character pointer to beginning of token
  461. // pcEndToken - character pointer to ending of token
  462. //
  463. // Returns: TRUE if conforms to token defination, FALSE otherwise
  464. //
  465. // Notes:
  466. //
  467. //
  468. //---------------------------------------------------------------------
  469. BOOLEAN DigestTokenVerify(
  470. IN PSTR pcBeginToken,
  471. IN PSTR pcEndToken
  472. )
  473. {
  474. BOOLEAN fToken = FALSE;
  475. USHORT cbToken = 0;
  476. USHORT cbValue = 0;
  477. USHORT iCnt = 0;
  478. if (!pcBeginName || !pcEndName)
  479. {
  480. DebugLog((DEB_ERROR, "DigestProcessEntry: Badly formed directive\n"));
  481. return FALSE;
  482. }
  483. cbToken = (USHORT)(pcEndName - pcBeginName) + 1;
  484. for (iCnt = 0; iCnt < cbToken; iCnt++)
  485. {
  486. }
  487. CleanUp:
  488. return(Status);
  489. }
  490. */