Commit | Line | Data |
---|---|---|
e99dd67a EF |
1 | Author: Raphael Geissert <geissert@debian.org> |
2 | Bug-Debian: https://bugs.debian.org/731860 | |
3 | Description: Avoid directory traversal when extracting archives | |
4 | by skipping over leading slashes and any prefix containing ".." components. | |
5 | Forwarded: yes | |
6 | ||
7 | --- a/lib/decode.c | |
8 | +++ b/lib/decode.c | |
9 | @@ -22,6 +22,36 @@ | |
10 | #endif | |
11 | ||
12 | ||
13 | +char * | |
14 | +safer_name_suffix (char const *file_name) | |
15 | +{ | |
16 | + char const *p, *t; | |
17 | + p = t = file_name; | |
18 | + while (*p == '/') t = ++p; | |
19 | + while (*p) | |
20 | + { | |
21 | + while (p[0] == '.' && p[0] == p[1] && p[2] == '/') | |
22 | + { | |
23 | + p += 3; | |
24 | + t = p; | |
25 | + } | |
26 | + /* advance pointer past the next slash */ | |
27 | + while (*p && (p++)[0] != '/'); | |
28 | + } | |
29 | + | |
30 | + if (!*t) | |
31 | + { | |
32 | + t = "."; | |
33 | + } | |
34 | + | |
35 | + if (t != file_name) | |
36 | + { | |
37 | + /* TODO: warn somehow that the path was modified */ | |
38 | + } | |
39 | + return (char*)t; | |
40 | +} | |
41 | + | |
42 | + | |
43 | /* determine full path name */ | |
44 | char * | |
45 | th_get_pathname(TAR *t) | |
46 | @@ -29,17 +59,17 @@ th_get_pathname(TAR *t) | |
47 | static char filename[MAXPATHLEN]; | |
48 | ||
49 | if (t->th_buf.gnu_longname) | |
50 | - return t->th_buf.gnu_longname; | |
51 | + return safer_name_suffix(t->th_buf.gnu_longname); | |
52 | ||
53 | if (t->th_buf.prefix[0] != '\0') | |
54 | { | |
55 | snprintf(filename, sizeof(filename), "%.155s/%.100s", | |
56 | t->th_buf.prefix, t->th_buf.name); | |
57 | - return filename; | |
58 | + return safer_name_suffix(filename); | |
59 | } | |
60 | ||
61 | snprintf(filename, sizeof(filename), "%.100s", t->th_buf.name); | |
62 | - return filename; | |
63 | + return safer_name_suffix(filename); | |
64 | } | |
65 | ||
66 | ||
67 | --- a/lib/extract.c | |
68 | +++ b/lib/extract.c | |
69 | @@ -298,14 +298,14 @@ tar_extract_hardlink(TAR * t, char *real | |
70 | if (mkdirhier(dirname(filename)) == -1) | |
71 | return -1; | |
72 | libtar_hashptr_reset(&hp); | |
73 | - if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t), | |
74 | + if (libtar_hash_getkey(t->h, &hp, safer_name_suffix(th_get_linkname(t)), | |
75 | (libtar_matchfunc_t)libtar_str_match) != 0) | |
76 | { | |
77 | lnp = (char *)libtar_hashptr_data(&hp); | |
78 | linktgt = &lnp[strlen(lnp) + 1]; | |
79 | } | |
80 | else | |
81 | - linktgt = th_get_linkname(t); | |
82 | + linktgt = safer_name_suffix(th_get_linkname(t)); | |
83 | ||
84 | #ifdef DEBUG | |
85 | printf(" ==> extracting: %s (link to %s)\n", filename, linktgt); | |
86 | @@ -343,9 +343,9 @@ tar_extract_symlink(TAR *t, char *realna | |
87 | ||
88 | #ifdef DEBUG | |
89 | printf(" ==> extracting: %s (symlink to %s)\n", | |
90 | - filename, th_get_linkname(t)); | |
91 | + filename, safer_name_suffix(th_get_linkname(t))); | |
92 | #endif | |
93 | - if (symlink(th_get_linkname(t), filename) == -1) | |
94 | + if (symlink(safer_name_suffix(th_get_linkname(t)), filename) == -1) | |
95 | { | |
96 | #ifdef DEBUG | |
97 | perror("symlink()"); | |
98 | --- a/lib/internal.h | |
99 | +++ b/lib/internal.h | |
100 | @@ -15,3 +15,4 @@ | |
101 | ||
102 | #include <libtar.h> | |
103 | ||
104 | +char* safer_name_suffix(char const*); | |
105 | --- a/lib/output.c | |
106 | +++ b/lib/output.c | |
107 | @@ -123,9 +123,9 @@ th_print_long_ls(TAR *t) | |
108 | else | |
109 | printf(" link to "); | |
110 | if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL) | |
111 | - printf("%s", t->th_buf.gnu_longlink); | |
112 | + printf("%s", safer_name_suffix(t->th_buf.gnu_longlink)); | |
113 | else | |
114 | - printf("%.100s", t->th_buf.linkname); | |
115 | + printf("%.100s", safer_name_suffix(t->th_buf.linkname)); | |
116 | } | |
117 | ||
118 | putchar('\n'); |