summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2019-03-01 09:57:53 +0000
committerColin Watson <cjwatson@debian.org>2019-03-01 09:58:48 +0000
commitbc8b39092a460d8e7e9d8d42d32f2c964d18fec3 (patch)
tree258baf7478e0e5737351c704c6313d5973cdd81f
parentb85f89c2c0ee71d4eee9c5ce3753eb89bc5fc715 (diff)
parent7a3fa37583d4abf128f7f4c6eb1e7ffc90115eab (diff)
Handle shell-style brace expansions in scp checks
-rw-r--r--debian/.git-dpm4
-rw-r--r--debian/changelog8
-rw-r--r--debian/patches/scp-handle-braces.patch353
-rw-r--r--debian/patches/series1
-rw-r--r--scp.c280
5 files changed, 633 insertions, 13 deletions
diff --git a/debian/.git-dpm b/debian/.git-dpm
index 42208f2de..6e6c8addb 100644
--- a/debian/.git-dpm
+++ b/debian/.git-dpm
@@ -1,6 +1,6 @@
1# see git-dpm(1) from git-dpm package 1# see git-dpm(1) from git-dpm package
2d94226d4fcefbc398c5583e12b5d07ca33884ba4 27a3fa37583d4abf128f7f4c6eb1e7ffc90115eab
3d94226d4fcefbc398c5583e12b5d07ca33884ba4 37a3fa37583d4abf128f7f4c6eb1e7ffc90115eab
43d246f10429fc9a37b98eabef94fe8dc7c61002b 43d246f10429fc9a37b98eabef94fe8dc7c61002b
53d246f10429fc9a37b98eabef94fe8dc7c61002b 53d246f10429fc9a37b98eabef94fe8dc7c61002b
6openssh_7.9p1.orig.tar.gz 6openssh_7.9p1.orig.tar.gz
diff --git a/debian/changelog b/debian/changelog
index 418d98d2f..614b4e9cf 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
1openssh (1:7.9p1-9) UNRELEASED; urgency=medium
2
3 * Apply upstream patch to make scp handle shell-style brace expansions
4 when checking that filenames sent by the server match what the client
5 requested (closes: #923486).
6
7 -- Colin Watson <cjwatson@debian.org> Fri, 01 Mar 2019 09:57:59 +0000
8
1openssh (1:7.9p1-8) unstable; urgency=medium 9openssh (1:7.9p1-8) unstable; urgency=medium
2 10
3 [ Colin Watson ] 11 [ Colin Watson ]
diff --git a/debian/patches/scp-handle-braces.patch b/debian/patches/scp-handle-braces.patch
new file mode 100644
index 000000000..0cbdcfdc0
--- /dev/null
+++ b/debian/patches/scp-handle-braces.patch
@@ -0,0 +1,353 @@
1From 7a3fa37583d4abf128f7f4c6eb1e7ffc90115eab Mon Sep 17 00:00:00 2001
2From: "djm@openbsd.org" <djm@openbsd.org>
3Date: Sun, 10 Feb 2019 11:15:52 +0000
4Subject: upstream: when checking that filenames sent by the server side
5
6match what the client requested, be prepared to handle shell-style brace
7alternations, e.g. "{foo,bar}".
8
9"looks good to me" millert@ + in snaps for the last week courtesy
10deraadt@
11
12OpenBSD-Commit-ID: 3b1ce7639b0b25b2248e3a30f561a548f6815f3e
13
14Origin: upstream, https://anongit.mindrot.org/openssh.git/commit/?id=3d896c157c722bc47adca51a58dca859225b5874
15Bug-Debian: https://bugs.debian.org/923486
16Last-Update: 2019-03-01
17
18Patch-Name: scp-handle-braces.patch
19---
20 scp.c | 280 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
21 1 file changed, 269 insertions(+), 11 deletions(-)
22
23diff --git a/scp.c b/scp.c
24index 035037bcc..3888baab0 100644
25--- a/scp.c
26+++ b/scp.c
27@@ -635,6 +635,253 @@ parse_scp_uri(const char *uri, char **userp, char **hostp, int *portp,
28 return r;
29 }
30
31+/* Appends a string to an array; returns 0 on success, -1 on alloc failure */
32+static int
33+append(char *cp, char ***ap, size_t *np)
34+{
35+ char **tmp;
36+
37+ if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL)
38+ return -1;
39+ tmp[(*np)] = cp;
40+ (*np)++;
41+ *ap = tmp;
42+ return 0;
43+}
44+
45+/*
46+ * Finds the start and end of the first brace pair in the pattern.
47+ * returns 0 on success or -1 for invalid patterns.
48+ */
49+static int
50+find_brace(const char *pattern, int *startp, int *endp)
51+{
52+ int i;
53+ int in_bracket, brace_level;
54+
55+ *startp = *endp = -1;
56+ in_bracket = brace_level = 0;
57+ for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) {
58+ switch (pattern[i]) {
59+ case '\\':
60+ /* skip next character */
61+ if (pattern[i + 1] != '\0')
62+ i++;
63+ break;
64+ case '[':
65+ in_bracket = 1;
66+ break;
67+ case ']':
68+ in_bracket = 0;
69+ break;
70+ case '{':
71+ if (in_bracket)
72+ break;
73+ if (pattern[i + 1] == '}') {
74+ /* Protect a single {}, for find(1), like csh */
75+ i++; /* skip */
76+ break;
77+ }
78+ if (*startp == -1)
79+ *startp = i;
80+ brace_level++;
81+ break;
82+ case '}':
83+ if (in_bracket)
84+ break;
85+ if (*startp < 0) {
86+ /* Unbalanced brace */
87+ return -1;
88+ }
89+ if (--brace_level <= 0)
90+ *endp = i;
91+ break;
92+ }
93+ }
94+ /* unbalanced brackets/braces */
95+ if (*endp < 0 && (*startp >= 0 || in_bracket))
96+ return -1;
97+ return 0;
98+}
99+
100+/*
101+ * Assembles and records a successfully-expanded pattern, returns -1 on
102+ * alloc failure.
103+ */
104+static int
105+emit_expansion(const char *pattern, int brace_start, int brace_end,
106+ int sel_start, int sel_end, char ***patternsp, size_t *npatternsp)
107+{
108+ char *cp;
109+ int o = 0, tail_len = strlen(pattern + brace_end + 1);
110+
111+ if ((cp = malloc(brace_start + (sel_end - sel_start) +
112+ tail_len + 1)) == NULL)
113+ return -1;
114+
115+ /* Pattern before initial brace */
116+ if (brace_start > 0) {
117+ memcpy(cp, pattern, brace_start);
118+ o = brace_start;
119+ }
120+ /* Current braced selection */
121+ if (sel_end - sel_start > 0) {
122+ memcpy(cp + o, pattern + sel_start,
123+ sel_end - sel_start);
124+ o += sel_end - sel_start;
125+ }
126+ /* Remainder of pattern after closing brace */
127+ if (tail_len > 0) {
128+ memcpy(cp + o, pattern + brace_end + 1, tail_len);
129+ o += tail_len;
130+ }
131+ cp[o] = '\0';
132+ if (append(cp, patternsp, npatternsp) != 0) {
133+ free(cp);
134+ return -1;
135+ }
136+ return 0;
137+}
138+
139+/*
140+ * Expand the first encountered brace in pattern, appending the expanded
141+ * patterns it yielded to the *patternsp array.
142+ *
143+ * Returns 0 on success or -1 on allocation failure.
144+ *
145+ * Signals whether expansion was performed via *expanded and whether
146+ * pattern was invalid via *invalid.
147+ */
148+static int
149+brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp,
150+ int *expanded, int *invalid)
151+{
152+ int i;
153+ int in_bracket, brace_start, brace_end, brace_level;
154+ int sel_start, sel_end;
155+
156+ *invalid = *expanded = 0;
157+
158+ if (find_brace(pattern, &brace_start, &brace_end) != 0) {
159+ *invalid = 1;
160+ return 0;
161+ } else if (brace_start == -1)
162+ return 0;
163+
164+ in_bracket = brace_level = 0;
165+ for (i = sel_start = brace_start + 1; i < brace_end; i++) {
166+ switch (pattern[i]) {
167+ case '{':
168+ if (in_bracket)
169+ break;
170+ brace_level++;
171+ break;
172+ case '}':
173+ if (in_bracket)
174+ break;
175+ brace_level--;
176+ break;
177+ case '[':
178+ in_bracket = 1;
179+ break;
180+ case ']':
181+ in_bracket = 0;
182+ break;
183+ case '\\':
184+ if (i < brace_end - 1)
185+ i++; /* skip */
186+ break;
187+ }
188+ if (pattern[i] == ',' || i == brace_end - 1) {
189+ if (in_bracket || brace_level > 0)
190+ continue;
191+ /* End of a selection, emit an expanded pattern */
192+
193+ /* Adjust end index for last selection */
194+ sel_end = (i == brace_end - 1) ? brace_end : i;
195+ if (emit_expansion(pattern, brace_start, brace_end,
196+ sel_start, sel_end, patternsp, npatternsp) != 0)
197+ return -1;
198+ /* move on to the next selection */
199+ sel_start = i + 1;
200+ continue;
201+ }
202+ }
203+ if (in_bracket || brace_level > 0) {
204+ *invalid = 1;
205+ return 0;
206+ }
207+ /* success */
208+ *expanded = 1;
209+ return 0;
210+}
211+
212+/* Expand braces from pattern. Returns 0 on success, -1 on failure */
213+static int
214+brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
215+{
216+ char *cp, *cp2, **active = NULL, **done = NULL;
217+ size_t i, nactive = 0, ndone = 0;
218+ int ret = -1, invalid = 0, expanded = 0;
219+
220+ *patternsp = NULL;
221+ *npatternsp = 0;
222+
223+ /* Start the worklist with the original pattern */
224+ if ((cp = strdup(pattern)) == NULL)
225+ return -1;
226+ if (append(cp, &active, &nactive) != 0) {
227+ free(cp);
228+ return -1;
229+ }
230+ while (nactive > 0) {
231+ cp = active[nactive - 1];
232+ nactive--;
233+ if (brace_expand_one(cp, &active, &nactive,
234+ &expanded, &invalid) == -1) {
235+ free(cp);
236+ goto fail;
237+ }
238+ if (invalid)
239+ fatal("%s: invalid brace pattern \"%s\"", __func__, cp);
240+ if (expanded) {
241+ /*
242+ * Current entry expanded to new entries on the
243+ * active list; discard the progenitor pattern.
244+ */
245+ free(cp);
246+ continue;
247+ }
248+ /*
249+ * Pattern did not expand; append the finename component to
250+ * the completed list
251+ */
252+ if ((cp2 = strrchr(cp, '/')) != NULL)
253+ *cp2++ = '\0';
254+ else
255+ cp2 = cp;
256+ if (append(xstrdup(cp2), &done, &ndone) != 0) {
257+ free(cp);
258+ goto fail;
259+ }
260+ free(cp);
261+ }
262+ /* success */
263+ *patternsp = done;
264+ *npatternsp = ndone;
265+ done = NULL;
266+ ndone = 0;
267+ ret = 0;
268+ fail:
269+ for (i = 0; i < nactive; i++)
270+ free(active[i]);
271+ free(active);
272+ for (i = 0; i < ndone; i++)
273+ free(done[i]);
274+ free(done);
275+ return ret;
276+}
277+
278 void
279 toremote(int argc, char **argv)
280 {
281@@ -998,7 +1245,8 @@ sink(int argc, char **argv, const char *src)
282 unsigned long long ull;
283 int setimes, targisdir, wrerrno = 0;
284 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048];
285- char *src_copy = NULL, *restrict_pattern = NULL;
286+ char **patterns = NULL;
287+ size_t n, npatterns = 0;
288 struct timeval tv[2];
289
290 #define atime tv[0]
291@@ -1028,16 +1276,13 @@ sink(int argc, char **argv, const char *src)
292 * Prepare to try to restrict incoming filenames to match
293 * the requested destination file glob.
294 */
295- if ((src_copy = strdup(src)) == NULL)
296- fatal("strdup failed");
297- if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) {
298- *restrict_pattern++ = '\0';
299- }
300+ if (brace_expand(src, &patterns, &npatterns) != 0)
301+ fatal("%s: could not expand pattern", __func__);
302 }
303 for (first = 1;; first = 0) {
304 cp = buf;
305 if (atomicio(read, remin, cp, 1) != 1)
306- return;
307+ goto done;
308 if (*cp++ == '\n')
309 SCREWUP("unexpected <newline>");
310 do {
311@@ -1063,7 +1308,7 @@ sink(int argc, char **argv, const char *src)
312 }
313 if (buf[0] == 'E') {
314 (void) atomicio(vwrite, remout, "", 1);
315- return;
316+ goto done;
317 }
318 if (ch == '\n')
319 *--cp = 0;
320@@ -1138,9 +1383,14 @@ sink(int argc, char **argv, const char *src)
321 run_err("error: unexpected filename: %s", cp);
322 exit(1);
323 }
324- if (restrict_pattern != NULL &&
325- fnmatch(restrict_pattern, cp, 0) != 0)
326- SCREWUP("filename does not match request");
327+ if (npatterns > 0) {
328+ for (n = 0; n < npatterns; n++) {
329+ if (fnmatch(patterns[n], cp, 0) == 0)
330+ break;
331+ }
332+ if (n >= npatterns)
333+ SCREWUP("filename does not match request");
334+ }
335 if (targisdir) {
336 static char *namebuf;
337 static size_t cursize;
338@@ -1299,7 +1549,15 @@ bad: run_err("%s: %s", np, strerror(errno));
339 break;
340 }
341 }
342+done:
343+ for (n = 0; n < npatterns; n++)
344+ free(patterns[n]);
345+ free(patterns);
346+ return;
347 screwup:
348+ for (n = 0; n < npatterns; n++)
349+ free(patterns[n]);
350+ free(patterns);
351 run_err("protocol error: %s", why);
352 exit(1);
353 }
diff --git a/debian/patches/series b/debian/patches/series
index a95dccaa6..ff6011442 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -30,3 +30,4 @@ have-progressmeter-force-update-at-beginning-and-end-transfer.patch
30check-filenames-in-scp-client.patch 30check-filenames-in-scp-client.patch
31fix-key-type-check.patch 31fix-key-type-check.patch
32request-rsa-sha2-cert-signatures.patch 32request-rsa-sha2-cert-signatures.patch
33scp-handle-braces.patch
diff --git a/scp.c b/scp.c
index 035037bcc..3888baab0 100644
--- a/scp.c
+++ b/scp.c
@@ -635,6 +635,253 @@ parse_scp_uri(const char *uri, char **userp, char **hostp, int *portp,
635 return r; 635 return r;
636} 636}
637 637
638/* Appends a string to an array; returns 0 on success, -1 on alloc failure */
639static int
640append(char *cp, char ***ap, size_t *np)
641{
642 char **tmp;
643
644 if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL)
645 return -1;
646 tmp[(*np)] = cp;
647 (*np)++;
648 *ap = tmp;
649 return 0;
650}
651
652/*
653 * Finds the start and end of the first brace pair in the pattern.
654 * returns 0 on success or -1 for invalid patterns.
655 */
656static int
657find_brace(const char *pattern, int *startp, int *endp)
658{
659 int i;
660 int in_bracket, brace_level;
661
662 *startp = *endp = -1;
663 in_bracket = brace_level = 0;
664 for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) {
665 switch (pattern[i]) {
666 case '\\':
667 /* skip next character */
668 if (pattern[i + 1] != '\0')
669 i++;
670 break;
671 case '[':
672 in_bracket = 1;
673 break;
674 case ']':
675 in_bracket = 0;
676 break;
677 case '{':
678 if (in_bracket)
679 break;
680 if (pattern[i + 1] == '}') {
681 /* Protect a single {}, for find(1), like csh */
682 i++; /* skip */
683 break;
684 }
685 if (*startp == -1)
686 *startp = i;
687 brace_level++;
688 break;
689 case '}':
690 if (in_bracket)
691 break;
692 if (*startp < 0) {
693 /* Unbalanced brace */
694 return -1;
695 }
696 if (--brace_level <= 0)
697 *endp = i;
698 break;
699 }
700 }
701 /* unbalanced brackets/braces */
702 if (*endp < 0 && (*startp >= 0 || in_bracket))
703 return -1;
704 return 0;
705}
706
707/*
708 * Assembles and records a successfully-expanded pattern, returns -1 on
709 * alloc failure.
710 */
711static int
712emit_expansion(const char *pattern, int brace_start, int brace_end,
713 int sel_start, int sel_end, char ***patternsp, size_t *npatternsp)
714{
715 char *cp;
716 int o = 0, tail_len = strlen(pattern + brace_end + 1);
717
718 if ((cp = malloc(brace_start + (sel_end - sel_start) +
719 tail_len + 1)) == NULL)
720 return -1;
721
722 /* Pattern before initial brace */
723 if (brace_start > 0) {
724 memcpy(cp, pattern, brace_start);
725 o = brace_start;
726 }
727 /* Current braced selection */
728 if (sel_end - sel_start > 0) {
729 memcpy(cp + o, pattern + sel_start,
730 sel_end - sel_start);
731 o += sel_end - sel_start;
732 }
733 /* Remainder of pattern after closing brace */
734 if (tail_len > 0) {
735 memcpy(cp + o, pattern + brace_end + 1, tail_len);
736 o += tail_len;
737 }
738 cp[o] = '\0';
739 if (append(cp, patternsp, npatternsp) != 0) {
740 free(cp);
741 return -1;
742 }
743 return 0;
744}
745
746/*
747 * Expand the first encountered brace in pattern, appending the expanded
748 * patterns it yielded to the *patternsp array.
749 *
750 * Returns 0 on success or -1 on allocation failure.
751 *
752 * Signals whether expansion was performed via *expanded and whether
753 * pattern was invalid via *invalid.
754 */
755static int
756brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp,
757 int *expanded, int *invalid)
758{
759 int i;
760 int in_bracket, brace_start, brace_end, brace_level;
761 int sel_start, sel_end;
762
763 *invalid = *expanded = 0;
764
765 if (find_brace(pattern, &brace_start, &brace_end) != 0) {
766 *invalid = 1;
767 return 0;
768 } else if (brace_start == -1)
769 return 0;
770
771 in_bracket = brace_level = 0;
772 for (i = sel_start = brace_start + 1; i < brace_end; i++) {
773 switch (pattern[i]) {
774 case '{':
775 if (in_bracket)
776 break;
777 brace_level++;
778 break;
779 case '}':
780 if (in_bracket)
781 break;
782 brace_level--;
783 break;
784 case '[':
785 in_bracket = 1;
786 break;
787 case ']':
788 in_bracket = 0;
789 break;
790 case '\\':
791 if (i < brace_end - 1)
792 i++; /* skip */
793 break;
794 }
795 if (pattern[i] == ',' || i == brace_end - 1) {
796 if (in_bracket || brace_level > 0)
797 continue;
798 /* End of a selection, emit an expanded pattern */
799
800 /* Adjust end index for last selection */
801 sel_end = (i == brace_end - 1) ? brace_end : i;
802 if (emit_expansion(pattern, brace_start, brace_end,
803 sel_start, sel_end, patternsp, npatternsp) != 0)
804 return -1;
805 /* move on to the next selection */
806 sel_start = i + 1;
807 continue;
808 }
809 }
810 if (in_bracket || brace_level > 0) {
811 *invalid = 1;
812 return 0;
813 }
814 /* success */
815 *expanded = 1;
816 return 0;
817}
818
819/* Expand braces from pattern. Returns 0 on success, -1 on failure */
820static int
821brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
822{
823 char *cp, *cp2, **active = NULL, **done = NULL;
824 size_t i, nactive = 0, ndone = 0;
825 int ret = -1, invalid = 0, expanded = 0;
826
827 *patternsp = NULL;
828 *npatternsp = 0;
829
830 /* Start the worklist with the original pattern */
831 if ((cp = strdup(pattern)) == NULL)
832 return -1;
833 if (append(cp, &active, &nactive) != 0) {
834 free(cp);
835 return -1;
836 }
837 while (nactive > 0) {
838 cp = active[nactive - 1];
839 nactive--;
840 if (brace_expand_one(cp, &active, &nactive,
841 &expanded, &invalid) == -1) {
842 free(cp);
843 goto fail;
844 }
845 if (invalid)
846 fatal("%s: invalid brace pattern \"%s\"", __func__, cp);
847 if (expanded) {
848 /*
849 * Current entry expanded to new entries on the
850 * active list; discard the progenitor pattern.
851 */
852 free(cp);
853 continue;
854 }
855 /*
856 * Pattern did not expand; append the finename component to
857 * the completed list
858 */
859 if ((cp2 = strrchr(cp, '/')) != NULL)
860 *cp2++ = '\0';
861 else
862 cp2 = cp;
863 if (append(xstrdup(cp2), &done, &ndone) != 0) {
864 free(cp);
865 goto fail;
866 }
867 free(cp);
868 }
869 /* success */
870 *patternsp = done;
871 *npatternsp = ndone;
872 done = NULL;
873 ndone = 0;
874 ret = 0;
875 fail:
876 for (i = 0; i < nactive; i++)
877 free(active[i]);
878 free(active);
879 for (i = 0; i < ndone; i++)
880 free(done[i]);
881 free(done);
882 return ret;
883}
884
638void 885void
639toremote(int argc, char **argv) 886toremote(int argc, char **argv)
640{ 887{
@@ -998,7 +1245,8 @@ sink(int argc, char **argv, const char *src)
998 unsigned long long ull; 1245 unsigned long long ull;
999 int setimes, targisdir, wrerrno = 0; 1246 int setimes, targisdir, wrerrno = 0;
1000 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; 1247 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048];
1001 char *src_copy = NULL, *restrict_pattern = NULL; 1248 char **patterns = NULL;
1249 size_t n, npatterns = 0;
1002 struct timeval tv[2]; 1250 struct timeval tv[2];
1003 1251
1004#define atime tv[0] 1252#define atime tv[0]
@@ -1028,16 +1276,13 @@ sink(int argc, char **argv, const char *src)
1028 * Prepare to try to restrict incoming filenames to match 1276 * Prepare to try to restrict incoming filenames to match
1029 * the requested destination file glob. 1277 * the requested destination file glob.
1030 */ 1278 */
1031 if ((src_copy = strdup(src)) == NULL) 1279 if (brace_expand(src, &patterns, &npatterns) != 0)
1032 fatal("strdup failed"); 1280 fatal("%s: could not expand pattern", __func__);
1033 if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) {
1034 *restrict_pattern++ = '\0';
1035 }
1036 } 1281 }
1037 for (first = 1;; first = 0) { 1282 for (first = 1;; first = 0) {
1038 cp = buf; 1283 cp = buf;
1039 if (atomicio(read, remin, cp, 1) != 1) 1284 if (atomicio(read, remin, cp, 1) != 1)
1040 return; 1285 goto done;
1041 if (*cp++ == '\n') 1286 if (*cp++ == '\n')
1042 SCREWUP("unexpected <newline>"); 1287 SCREWUP("unexpected <newline>");
1043 do { 1288 do {
@@ -1063,7 +1308,7 @@ sink(int argc, char **argv, const char *src)
1063 } 1308 }
1064 if (buf[0] == 'E') { 1309 if (buf[0] == 'E') {
1065 (void) atomicio(vwrite, remout, "", 1); 1310 (void) atomicio(vwrite, remout, "", 1);
1066 return; 1311 goto done;
1067 } 1312 }
1068 if (ch == '\n') 1313 if (ch == '\n')
1069 *--cp = 0; 1314 *--cp = 0;
@@ -1138,9 +1383,14 @@ sink(int argc, char **argv, const char *src)
1138 run_err("error: unexpected filename: %s", cp); 1383 run_err("error: unexpected filename: %s", cp);
1139 exit(1); 1384 exit(1);
1140 } 1385 }
1141 if (restrict_pattern != NULL && 1386 if (npatterns > 0) {
1142 fnmatch(restrict_pattern, cp, 0) != 0) 1387 for (n = 0; n < npatterns; n++) {
1143 SCREWUP("filename does not match request"); 1388 if (fnmatch(patterns[n], cp, 0) == 0)
1389 break;
1390 }
1391 if (n >= npatterns)
1392 SCREWUP("filename does not match request");
1393 }
1144 if (targisdir) { 1394 if (targisdir) {
1145 static char *namebuf; 1395 static char *namebuf;
1146 static size_t cursize; 1396 static size_t cursize;
@@ -1299,7 +1549,15 @@ bad: run_err("%s: %s", np, strerror(errno));
1299 break; 1549 break;
1300 } 1550 }
1301 } 1551 }
1552done:
1553 for (n = 0; n < npatterns; n++)
1554 free(patterns[n]);
1555 free(patterns);
1556 return;
1302screwup: 1557screwup:
1558 for (n = 0; n < npatterns; n++)
1559 free(patterns[n]);
1560 free(patterns);
1303 run_err("protocol error: %s", why); 1561 run_err("protocol error: %s", why);
1304 exit(1); 1562 exit(1);
1305} 1563}