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.

396 lines
14 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. util.c
  5. Abstract:
  6. This module contains functions to parse and construct server
  7. file paths for client requests, request option negotiation,
  8. and client access security.
  9. Author:
  10. Jeffrey C. Venable, Sr. (jeffv) 01-Jun-2001
  11. Revision History:
  12. --*/
  13. #include "precomp.h"
  14. #define IS_SEPARATOR(c) (((c) == '\\') || ((c) == '/'))
  15. BOOL
  16. TftpdUtilIsValidString(char *string, unsigned int maxLength) {
  17. UINT x;
  18. // Make sure 'string' is null-terminated.
  19. for (x = 0; x < maxLength; x++)
  20. if (!string[x])
  21. return (TRUE);
  22. return (FALSE);
  23. } // TftpdUtilIsValidString()
  24. BOOL
  25. TftpdUtilCanonicalizeFileName(char *filename) {
  26. char *source, *destination, *lastComponent;
  27. // The canonicalization is done in place. Initialize the source and
  28. // destination pointers to point to the same place.
  29. source = destination = filename;
  30. // The lastComponent variable is used as a placeholder when
  31. // backtracking over trailing blanks and dots. It points to the
  32. // first character after the last directory separator or the
  33. // beginning of the pathname.
  34. lastComponent = filename;
  35. // Get rid of leading directory separators.
  36. while ((*source != 0) && IS_SEPARATOR(*source))
  37. source++;
  38. // Walk through the pathname until we reach the zero terminator. At
  39. // the start of this loop, source points to the first charaecter
  40. // after a directory separator or the first character of the
  41. // pathname.
  42. while (*source) {
  43. if (*source == '.') {
  44. // If we see a dot, look at the next character.
  45. if (IS_SEPARATOR(*(source + 1))) {
  46. // If the next character is a directory separator,
  47. // advance the source pointer to the directory
  48. // separator.
  49. source++;
  50. } else if ((*(source + 1) == '.') && IS_SEPARATOR(*(source + 2))) {
  51. // If the following characters are ".\", we have a "..\".
  52. // Advance the source pointer to the "\".
  53. source += 2;
  54. // Move the destination pointer to the character before the
  55. // last directory separator in order to prepare for backing
  56. // up. This may move the pointer before the beginning of
  57. // the name pointer.
  58. destination -= 2;
  59. // If destination points before the beginning of the name
  60. // pointer, fail because the user is attempting to go
  61. // to a higher directory than the TFTPD root. This is
  62. // the equivalent of a leading "..\", but may result from
  63. // a case like "dir\..\..\file".
  64. if (destination <= filename)
  65. return (FALSE);
  66. // Back up the destination pointer to after the last
  67. // directory separator or to the beginning of the pathname.
  68. // Backup to the beginning of the pathname will occur
  69. // in a case like "dir\..\file".
  70. while ((destination >= filename) && !IS_SEPARATOR(*destination))
  71. destination--;
  72. // destination points to \ or character before name; we
  73. // want it to point to character after last \.
  74. destination++;
  75. } else {
  76. // The characters after the dot are not "\" or ".\", so
  77. // so just copy source to destination until we reach a
  78. // directory separator character. This will occur in
  79. // a case like ".file" (filename starts with a dot).
  80. do {
  81. *destination++ = *source++;
  82. } while (*source && !IS_SEPARATOR(*source));
  83. } // if (IS_SEPARATOR(*(source + 1)))
  84. } else {
  85. // source does not point to a dot, so copy source to
  86. // destination until we get to a directory separator.
  87. while (*source && !IS_SEPARATOR(*source))
  88. *destination++ = *source++;
  89. } // if (*source == '.')
  90. // Truncate trailing blanks. destination should point to the last
  91. // character before the directory separator, so back up over blanks.
  92. while ((destination > lastComponent) && (*(destination - 1) == ' '))
  93. destination--;
  94. // At this point, source points to a directory separator or to
  95. // a zero terminator. If it is a directory separator, put one
  96. // in the destination.
  97. if (IS_SEPARATOR(*source)) {
  98. // If we haven't put the directory separator in the path name,
  99. // put it in.
  100. if ((destination != filename) && !IS_SEPARATOR(*(destination - 1)))
  101. *destination++ = '\\';
  102. // It is legal to have multiple directory separators, so get
  103. // rid of them here. Example: "dir\\\\\\\\file".
  104. do {
  105. source++;
  106. } while (*source && IS_SEPARATOR(*source));
  107. // Make lastComponent point to the character after the directory
  108. // separator.
  109. lastComponent = destination;
  110. } // if (IS_SEPARATOR(*source))
  111. } // while (*source)
  112. // We're just about done. If there was a trailing .. (example:
  113. // "file\.."), trailing . ("file\."), or multiple trailing
  114. // separators ("file\\\\"), then back up one since separators are
  115. // illegal at the end of a pathname.
  116. if ((destination != filename) && IS_SEPARATOR(*(destination - 1)))
  117. destination--;
  118. // Terminate the destination string.
  119. *destination = '\0';
  120. return (TRUE);
  121. } // TftpdUtilCanonicalizeFileName()
  122. BOOL
  123. TftpdUtilPrependStringToFileName(char *filename, DWORD maxLength, char *prefix) {
  124. DWORD prefixLength = strlen(prefix);
  125. DWORD filenameLength = strlen(filename);
  126. BOOL prefixHasSeparater = (prefix[prefixLength - 1] == '\\');
  127. BOOL filenameHasSeparater = (filename[0] == '\\');
  128. DWORD separatorLength = 0;
  129. if (!prefixHasSeparater && !filenameHasSeparater)
  130. separatorLength = 1;
  131. if (prefixHasSeparater && filenameHasSeparater)
  132. prefixLength--;
  133. if ((prefixLength + separatorLength + filenameLength) > (maxLength - 1))
  134. return (FALSE);
  135. // Move the existing string down to make room for the prefix.
  136. MoveMemory(filename + prefixLength + separatorLength, filename, filenameLength + 1);
  137. // Move the prefix into place.
  138. CopyMemory(filename, prefix, prefixLength);
  139. // If necessary, insert a backslash between the prefix and the file name.
  140. if (separatorLength)
  141. filename[prefixLength] = '\\';
  142. // Terminate the string.
  143. filename[prefixLength + separatorLength + filenameLength] = '\0';
  144. return (TRUE);
  145. } // TftpdUtilPrependStringToFileName()
  146. BOOL
  147. TftpdUtilGetFileModeAndOptions(PTFTPD_CONTEXT context, PTFTPD_BUFFER buffer) {
  148. DWORD remaining = (TFTPD_DEF_DATA - FIELD_OFFSET(TFTPD_BUFFER, message.data));
  149. char *filename, *mode, *option;
  150. int length;
  151. // Obtain and validate the requested filename.
  152. filename = buffer->message.rrq.data; // or wrq, same thing
  153. if (!TftpdUtilIsValidString(filename, remaining)) {
  154. TftpdIoSendErrorPacket(buffer, TFTPD_ERROR_ILLEGAL_OPERATION,
  155. "Malformed file name");
  156. return (FALSE);
  157. }
  158. length = (strlen(filename) + 1);
  159. remaining -= length;
  160. if (!TftpdUtilCanonicalizeFileName(filename)) {
  161. TftpdIoSendErrorPacket(buffer, TFTPD_ERROR_ILLEGAL_OPERATION,
  162. "Malformed file name");
  163. return (FALSE);
  164. }
  165. // Obtain and validate the mode.
  166. mode = (char *)(buffer->message.rrq.data + length);
  167. if (!TftpdUtilIsValidString(mode, remaining))
  168. return (FALSE);
  169. length = (strlen(mode) + 1);
  170. if (!_stricmp(mode, "netascii"))
  171. context->mode = TFTPD_MODE_TEXT;
  172. else if (!_stricmp(mode, "octet"))
  173. context->mode = TFTPD_MODE_BINARY;
  174. else {
  175. TftpdIoSendErrorPacket(buffer, TFTPD_ERROR_ILLEGAL_OPERATION,
  176. "Illegal TFTP operation");
  177. return (FALSE);
  178. }
  179. remaining -= length;
  180. // Obtain and validate any requested options.
  181. option = (char *)(mode + length);
  182. while (remaining && *option) {
  183. char *value;
  184. if (!TftpdUtilIsValidString(option, remaining))
  185. break;
  186. length = (strlen(option) + 1);
  187. remaining -= length;
  188. value = (char *)(option + length);
  189. if (!remaining || !TftpdUtilIsValidString(value, remaining))
  190. break;
  191. length = (strlen(value) + 1);
  192. remaining -= length;
  193. if (!_stricmp(option, "blksize")) {
  194. if (!(context->options & TFTPD_OPTION_BLKSIZE)) {
  195. int blksize = atoi(value);
  196. // Workaround for problem in .98 version of ROM, which
  197. // doesn't like our OACK response. If the requested blksize is
  198. // 1456, pretend that the option wasn't specified. In the case
  199. // of the ROM's TFTP layer, this is the only option specified,
  200. // so ignoring it will mean that we don't send an OACK, and the
  201. // ROM will deign to talk to us. Note that our TFTP code uses
  202. // a blksize of 1432, so this workaround won't affect us.
  203. if (blksize != 1456) {
  204. blksize = __max(TFTPD_MIN_DATA, blksize);
  205. blksize = __min(TFTPD_MAX_DATA, blksize);
  206. context->blksize = blksize;
  207. context->options |= TFTPD_OPTION_BLKSIZE;
  208. }
  209. }
  210. } else if (!_stricmp(option, "timeout")) {
  211. if (!(context->options & TFTPD_OPTION_TIMEOUT)) {
  212. int seconds = atoi(value);
  213. if ((seconds >= 1) && (seconds <= 255)) {
  214. context->timeout = (seconds * 1000);
  215. context->options |= TFTPD_OPTION_TIMEOUT;
  216. }
  217. }
  218. } else if (!_stricmp(option, "tsize")) {
  219. if (context->mode != TFTPD_MODE_TEXT) {
  220. context->options |= TFTPD_OPTION_TSIZE;
  221. context->filesize.QuadPart = _atoi64(value);
  222. }
  223. }
  224. // Advance over the option and its value to next option or NUL terminator.
  225. option += (strlen(option) + 1 + length);
  226. } // while (*option)
  227. if (!(context->options & TFTPD_OPTION_BLKSIZE))
  228. context->blksize = TFTPD_DEF_DATA;
  229. // Now that we've obtained all the information we need from the buffer, we're
  230. // free to overwrite it (reuse it) to prepend the filename with its prefix.
  231. if (!TftpdUtilPrependStringToFileName(filename,
  232. TFTPD_DEF_BUFFER - FIELD_OFFSET(TFTPD_BUFFER, message.rrq.data),
  233. globals.parameters.rootDirectory)) {
  234. TftpdIoSendErrorPacket(buffer, TFTPD_ERROR_ILLEGAL_OPERATION,
  235. "Malformed file name");
  236. return (FALSE);
  237. }
  238. length = (strlen(filename) + 1);
  239. context->filename = (char *)HeapAlloc(globals.hServiceHeap, HEAP_ZERO_MEMORY, length);
  240. if (context->filename == NULL) {
  241. TftpdIoSendErrorPacket(buffer, TFTPD_ERROR_UNDEFINED, "Out of memory");
  242. return (FALSE);
  243. }
  244. strcpy(context->filename, filename);
  245. return (TRUE);
  246. } // TftpdUtilGetFileModeAndOptions()
  247. PTFTPD_BUFFER
  248. TftpdUtilSendOackPacket(PTFTPD_BUFFER buffer) {
  249. PTFTPD_CONTEXT context = buffer->internal.context;
  250. char *oack;
  251. int length;
  252. // Build the OACK message.
  253. ZeroMemory(&buffer->message, buffer->internal.datasize);
  254. buffer->message.opcode = htons(TFTPD_OACK);
  255. oack = (char *)&buffer->message.oack.data;
  256. buffer->internal.io.bytes = (FIELD_OFFSET(TFTPD_BUFFER, message.oack.data) -
  257. FIELD_OFFSET(TFTPD_BUFFER, message.opcode));
  258. if (context->options & TFTPD_OPTION_BLKSIZE) {
  259. strcpy(oack, "blksize");
  260. oack += 8;
  261. _itoa(context->blksize, oack, 10);
  262. length = (strlen(oack) + 1);
  263. oack += length;
  264. buffer->internal.io.bytes += (8 + length);
  265. }
  266. if (context->options & TFTPD_OPTION_TIMEOUT) {
  267. strcpy(oack, "timeout");
  268. oack += 8;
  269. _itoa((context->timeout / 1000), oack, 10);
  270. length = (strlen(oack) + 1);
  271. oack += length;
  272. buffer->internal.io.bytes += (8 + length);
  273. }
  274. if (context->options & TFTPD_OPTION_TSIZE) {
  275. strcpy(oack, "tsize");
  276. oack += 6;
  277. _itoa((int)context->filesize.QuadPart, oack, 10);
  278. length = (strlen(oack) + 1);
  279. oack += length;
  280. buffer->internal.io.bytes += (6 + length);
  281. }
  282. TFTPD_DEBUG((TFTPD_TRACE_IO,
  283. "TftpdUtilSendOackPacket(buffer = %p, context = %p): Issuing OACK, %d bytes. "
  284. "[blksize = %d, timeout = %d, tsize = %d]\n",
  285. buffer, context, buffer->internal.io.bytes,
  286. context->blksize, context->timeout, context->filesize));
  287. if (!TftpdProcessComplete(buffer))
  288. return (buffer);
  289. return (TftpdIoSendPacket(buffer));
  290. } // TftpdUtilSendOackPacket()
  291. BOOL
  292. TftpdUtilMatch(const char *const p, const char *const q) {
  293. switch (*p) {
  294. case '\0' :
  295. return (!(*q));
  296. case '*' :
  297. return (TftpdUtilMatch(p + 1, q) || (*q && TftpdUtilMatch(p, q + 1)));
  298. case '?' :
  299. return (*q && TftpdUtilMatch(p + 1, q + 1));
  300. default :
  301. return ((*p == *q) && TftpdUtilMatch(p + 1, q + 1));
  302. } // switch (*p)
  303. } // TftpdUtilMatch()