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.

353 lines
8.5 KiB

  1. From 7a3fa37583d4abf128f7f4c6eb1e7ffc90115eab Mon Sep 17 00:00:00 2001
  2. From: "djm@openbsd.org" <djm@openbsd.org>
  3. Date: Sun, 10 Feb 2019 11:15:52 +0000
  4. Subject: upstream: when checking that filenames sent by the server side
  5. match what the client requested, be prepared to handle shell-style brace
  6. alternations, e.g. "{foo,bar}".
  7. "looks good to me" millert@ + in snaps for the last week courtesy
  8. deraadt@
  9. OpenBSD-Commit-ID: 3b1ce7639b0b25b2248e3a30f561a548f6815f3e
  10. Origin: upstream, https://anongit.mindrot.org/openssh.git/commit/?id=3d896c157c722bc47adca51a58dca859225b5874
  11. Bug-Debian: https://bugs.debian.org/923486
  12. Last-Update: 2019-03-01
  13. Patch-Name: scp-handle-braces.patch
  14. ---
  15. scp.c | 280 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
  16. 1 file changed, 269 insertions(+), 11 deletions(-)
  17. diff --git a/scp.c b/scp.c
  18. index 035037bcc..3888baab0 100644
  19. --- a/scp.c
  20. +++ b/scp.c
  21. @@ -635,6 +635,253 @@ parse_scp_uri(const char *uri, char **userp, char **hostp, int *portp,
  22. return r;
  23. }
  24. +/* Appends a string to an array; returns 0 on success, -1 on alloc failure */
  25. +static int
  26. +append(char *cp, char ***ap, size_t *np)
  27. +{
  28. + char **tmp;
  29. +
  30. + if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL)
  31. + return -1;
  32. + tmp[(*np)] = cp;
  33. + (*np)++;
  34. + *ap = tmp;
  35. + return 0;
  36. +}
  37. +
  38. +/*
  39. + * Finds the start and end of the first brace pair in the pattern.
  40. + * returns 0 on success or -1 for invalid patterns.
  41. + */
  42. +static int
  43. +find_brace(const char *pattern, int *startp, int *endp)
  44. +{
  45. + int i;
  46. + int in_bracket, brace_level;
  47. +
  48. + *startp = *endp = -1;
  49. + in_bracket = brace_level = 0;
  50. + for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) {
  51. + switch (pattern[i]) {
  52. + case '\\':
  53. + /* skip next character */
  54. + if (pattern[i + 1] != '\0')
  55. + i++;
  56. + break;
  57. + case '[':
  58. + in_bracket = 1;
  59. + break;
  60. + case ']':
  61. + in_bracket = 0;
  62. + break;
  63. + case '{':
  64. + if (in_bracket)
  65. + break;
  66. + if (pattern[i + 1] == '}') {
  67. + /* Protect a single {}, for find(1), like csh */
  68. + i++; /* skip */
  69. + break;
  70. + }
  71. + if (*startp == -1)
  72. + *startp = i;
  73. + brace_level++;
  74. + break;
  75. + case '}':
  76. + if (in_bracket)
  77. + break;
  78. + if (*startp < 0) {
  79. + /* Unbalanced brace */
  80. + return -1;
  81. + }
  82. + if (--brace_level <= 0)
  83. + *endp = i;
  84. + break;
  85. + }
  86. + }
  87. + /* unbalanced brackets/braces */
  88. + if (*endp < 0 && (*startp >= 0 || in_bracket))
  89. + return -1;
  90. + return 0;
  91. +}
  92. +
  93. +/*
  94. + * Assembles and records a successfully-expanded pattern, returns -1 on
  95. + * alloc failure.
  96. + */
  97. +static int
  98. +emit_expansion(const char *pattern, int brace_start, int brace_end,
  99. + int sel_start, int sel_end, char ***patternsp, size_t *npatternsp)
  100. +{
  101. + char *cp;
  102. + int o = 0, tail_len = strlen(pattern + brace_end + 1);
  103. +
  104. + if ((cp = malloc(brace_start + (sel_end - sel_start) +
  105. + tail_len + 1)) == NULL)
  106. + return -1;
  107. +
  108. + /* Pattern before initial brace */
  109. + if (brace_start > 0) {
  110. + memcpy(cp, pattern, brace_start);
  111. + o = brace_start;
  112. + }
  113. + /* Current braced selection */
  114. + if (sel_end - sel_start > 0) {
  115. + memcpy(cp + o, pattern + sel_start,
  116. + sel_end - sel_start);
  117. + o += sel_end - sel_start;
  118. + }
  119. + /* Remainder of pattern after closing brace */
  120. + if (tail_len > 0) {
  121. + memcpy(cp + o, pattern + brace_end + 1, tail_len);
  122. + o += tail_len;
  123. + }
  124. + cp[o] = '\0';
  125. + if (append(cp, patternsp, npatternsp) != 0) {
  126. + free(cp);
  127. + return -1;
  128. + }
  129. + return 0;
  130. +}
  131. +
  132. +/*
  133. + * Expand the first encountered brace in pattern, appending the expanded
  134. + * patterns it yielded to the *patternsp array.
  135. + *
  136. + * Returns 0 on success or -1 on allocation failure.
  137. + *
  138. + * Signals whether expansion was performed via *expanded and whether
  139. + * pattern was invalid via *invalid.
  140. + */
  141. +static int
  142. +brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp,
  143. + int *expanded, int *invalid)
  144. +{
  145. + int i;
  146. + int in_bracket, brace_start, brace_end, brace_level;
  147. + int sel_start, sel_end;
  148. +
  149. + *invalid = *expanded = 0;
  150. +
  151. + if (find_brace(pattern, &brace_start, &brace_end) != 0) {
  152. + *invalid = 1;
  153. + return 0;
  154. + } else if (brace_start == -1)
  155. + return 0;
  156. +
  157. + in_bracket = brace_level = 0;
  158. + for (i = sel_start = brace_start + 1; i < brace_end; i++) {
  159. + switch (pattern[i]) {
  160. + case '{':
  161. + if (in_bracket)
  162. + break;
  163. + brace_level++;
  164. + break;
  165. + case '}':
  166. + if (in_bracket)
  167. + break;
  168. + brace_level--;
  169. + break;
  170. + case '[':
  171. + in_bracket = 1;
  172. + break;
  173. + case ']':
  174. + in_bracket = 0;
  175. + break;
  176. + case '\\':
  177. + if (i < brace_end - 1)
  178. + i++; /* skip */
  179. + break;
  180. + }
  181. + if (pattern[i] == ',' || i == brace_end - 1) {
  182. + if (in_bracket || brace_level > 0)
  183. + continue;
  184. + /* End of a selection, emit an expanded pattern */
  185. +
  186. + /* Adjust end index for last selection */
  187. + sel_end = (i == brace_end - 1) ? brace_end : i;
  188. + if (emit_expansion(pattern, brace_start, brace_end,
  189. + sel_start, sel_end, patternsp, npatternsp) != 0)
  190. + return -1;
  191. + /* move on to the next selection */
  192. + sel_start = i + 1;
  193. + continue;
  194. + }
  195. + }
  196. + if (in_bracket || brace_level > 0) {
  197. + *invalid = 1;
  198. + return 0;
  199. + }
  200. + /* success */
  201. + *expanded = 1;
  202. + return 0;
  203. +}
  204. +
  205. +/* Expand braces from pattern. Returns 0 on success, -1 on failure */
  206. +static int
  207. +brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
  208. +{
  209. + char *cp, *cp2, **active = NULL, **done = NULL;
  210. + size_t i, nactive = 0, ndone = 0;
  211. + int ret = -1, invalid = 0, expanded = 0;
  212. +
  213. + *patternsp = NULL;
  214. + *npatternsp = 0;
  215. +
  216. + /* Start the worklist with the original pattern */
  217. + if ((cp = strdup(pattern)) == NULL)
  218. + return -1;
  219. + if (append(cp, &active, &nactive) != 0) {
  220. + free(cp);
  221. + return -1;
  222. + }
  223. + while (nactive > 0) {
  224. + cp = active[nactive - 1];
  225. + nactive--;
  226. + if (brace_expand_one(cp, &active, &nactive,
  227. + &expanded, &invalid) == -1) {
  228. + free(cp);
  229. + goto fail;
  230. + }
  231. + if (invalid)
  232. + fatal("%s: invalid brace pattern \"%s\"", __func__, cp);
  233. + if (expanded) {
  234. + /*
  235. + * Current entry expanded to new entries on the
  236. + * active list; discard the progenitor pattern.
  237. + */
  238. + free(cp);
  239. + continue;
  240. + }
  241. + /*
  242. + * Pattern did not expand; append the finename component to
  243. + * the completed list
  244. + */
  245. + if ((cp2 = strrchr(cp, '/')) != NULL)
  246. + *cp2++ = '\0';
  247. + else
  248. + cp2 = cp;
  249. + if (append(xstrdup(cp2), &done, &ndone) != 0) {
  250. + free(cp);
  251. + goto fail;
  252. + }
  253. + free(cp);
  254. + }
  255. + /* success */
  256. + *patternsp = done;
  257. + *npatternsp = ndone;
  258. + done = NULL;
  259. + ndone = 0;
  260. + ret = 0;
  261. + fail:
  262. + for (i = 0; i < nactive; i++)
  263. + free(active[i]);
  264. + free(active);
  265. + for (i = 0; i < ndone; i++)
  266. + free(done[i]);
  267. + free(done);
  268. + return ret;
  269. +}
  270. +
  271. void
  272. toremote(int argc, char **argv)
  273. {
  274. @@ -998,7 +1245,8 @@ sink(int argc, char **argv, const char *src)
  275. unsigned long long ull;
  276. int setimes, targisdir, wrerrno = 0;
  277. char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048];
  278. - char *src_copy = NULL, *restrict_pattern = NULL;
  279. + char **patterns = NULL;
  280. + size_t n, npatterns = 0;
  281. struct timeval tv[2];
  282. #define atime tv[0]
  283. @@ -1028,16 +1276,13 @@ sink(int argc, char **argv, const char *src)
  284. * Prepare to try to restrict incoming filenames to match
  285. * the requested destination file glob.
  286. */
  287. - if ((src_copy = strdup(src)) == NULL)
  288. - fatal("strdup failed");
  289. - if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) {
  290. - *restrict_pattern++ = '\0';
  291. - }
  292. + if (brace_expand(src, &patterns, &npatterns) != 0)
  293. + fatal("%s: could not expand pattern", __func__);
  294. }
  295. for (first = 1;; first = 0) {
  296. cp = buf;
  297. if (atomicio(read, remin, cp, 1) != 1)
  298. - return;
  299. + goto done;
  300. if (*cp++ == '\n')
  301. SCREWUP("unexpected <newline>");
  302. do {
  303. @@ -1063,7 +1308,7 @@ sink(int argc, char **argv, const char *src)
  304. }
  305. if (buf[0] == 'E') {
  306. (void) atomicio(vwrite, remout, "", 1);
  307. - return;
  308. + goto done;
  309. }
  310. if (ch == '\n')
  311. *--cp = 0;
  312. @@ -1138,9 +1383,14 @@ sink(int argc, char **argv, const char *src)
  313. run_err("error: unexpected filename: %s", cp);
  314. exit(1);
  315. }
  316. - if (restrict_pattern != NULL &&
  317. - fnmatch(restrict_pattern, cp, 0) != 0)
  318. - SCREWUP("filename does not match request");
  319. + if (npatterns > 0) {
  320. + for (n = 0; n < npatterns; n++) {
  321. + if (fnmatch(patterns[n], cp, 0) == 0)
  322. + break;
  323. + }
  324. + if (n >= npatterns)
  325. + SCREWUP("filename does not match request");
  326. + }
  327. if (targisdir) {
  328. static char *namebuf;
  329. static size_t cursize;
  330. @@ -1299,7 +1549,15 @@ bad: run_err("%s: %s", np, strerror(errno));
  331. break;
  332. }
  333. }
  334. +done:
  335. + for (n = 0; n < npatterns; n++)
  336. + free(patterns[n]);
  337. + free(patterns);
  338. + return;
  339. screwup:
  340. + for (n = 0; n < npatterns; n++)
  341. + free(patterns[n]);
  342. + free(patterns);
  343. run_err("protocol error: %s", why);
  344. exit(1);
  345. }