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.

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