tar,unzip: postpone creation of symlinks with "suspicious" targets
[oweals/busybox.git] / archival / libarchive / data_extract_all.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4  */
5 #include "libbb.h"
6 #include "bb_archive.h"
7
8 void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
9 {
10         file_header_t *file_header = archive_handle->file_header;
11         int dst_fd;
12         int res;
13         char *hard_link;
14 #if ENABLE_FEATURE_TAR_LONG_OPTIONS
15         char *dst_name;
16 #else
17 # define dst_name (file_header->name)
18 #endif
19
20 #if ENABLE_FEATURE_TAR_SELINUX
21         char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE];
22         if (!sctx)
23                 sctx = archive_handle->tar__sctx[PAX_GLOBAL];
24         if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */
25                 setfscreatecon(sctx);
26                 free(archive_handle->tar__sctx[PAX_NEXT_FILE]);
27                 archive_handle->tar__sctx[PAX_NEXT_FILE] = NULL;
28         }
29 #endif
30
31         /* Hard links are encoded as regular files of size 0
32          * with a nonempty link field */
33         hard_link = NULL;
34         if (S_ISREG(file_header->mode) && file_header->size == 0)
35                 hard_link = file_header->link_target;
36
37 #if ENABLE_FEATURE_TAR_LONG_OPTIONS
38         dst_name = file_header->name;
39         if (archive_handle->tar__strip_components) {
40                 unsigned n = archive_handle->tar__strip_components;
41                 do {
42                         dst_name = strchr(dst_name, '/');
43                         if (!dst_name || dst_name[1] == '\0') {
44                                 data_skip(archive_handle);
45                                 goto ret;
46                         }
47                         dst_name++;
48                         /*
49                          * Link target is shortened only for hardlinks:
50                          * softlinks restored unchanged.
51                          */
52                         if (hard_link) {
53 // GNU tar 1.26 does not check that we reached end of link name:
54 // if "dir/hardlink" is hardlinked to "file",
55 // tar xvf a.tar --strip-components=1 says:
56 //  tar: hardlink: Cannot hard link to '': No such file or directory
57 // and continues processing. We silently skip such entries.
58                                 hard_link = strchr(hard_link, '/');
59                                 if (!hard_link || hard_link[1] == '\0') {
60                                         data_skip(archive_handle);
61                                         goto ret;
62                                 }
63                                 hard_link++;
64                         }
65                 } while (--n != 0);
66         }
67 #endif
68
69         if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) {
70                 char *slash = strrchr(dst_name, '/');
71                 if (slash) {
72                         *slash = '\0';
73                         bb_make_directory(dst_name, -1, FILEUTILS_RECUR);
74                         *slash = '/';
75                 }
76         }
77
78         if (archive_handle->ah_flags & ARCHIVE_UNLINK_OLD) {
79                 /* Remove the entry if it exists */
80                 if (!S_ISDIR(file_header->mode)) {
81                         if (hard_link) {
82                                 /* Ugly special case:
83                                  * tar cf t.tar hardlink1 hardlink2 hardlink1
84                                  * results in this tarball structure:
85                                  * hardlink1
86                                  * hardlink2 -> hardlink1
87                                  * hardlink1 -> hardlink1 <== !!!
88                                  */
89                                 if (strcmp(hard_link, dst_name) == 0)
90                                         goto ret;
91                         }
92                         /* Proceed with deleting */
93                         if (unlink(dst_name) == -1
94                          && errno != ENOENT
95                         ) {
96                                 bb_perror_msg_and_die("can't remove old file %s",
97                                                 dst_name);
98                         }
99                 }
100         }
101         else if (archive_handle->ah_flags & ARCHIVE_EXTRACT_NEWER) {
102                 /* Remove the existing entry if its older than the extracted entry */
103                 struct stat existing_sb;
104                 if (lstat(dst_name, &existing_sb) == -1) {
105                         if (errno != ENOENT) {
106                                 bb_perror_msg_and_die("can't stat old file");
107                         }
108                 }
109                 else if (existing_sb.st_mtime >= file_header->mtime) {
110                         if (!S_ISDIR(file_header->mode)) {
111                                 bb_error_msg("%s not created: newer or "
112                                         "same age file exists", dst_name);
113                         }
114                         data_skip(archive_handle);
115                         goto ret;
116                 }
117                 else if ((unlink(dst_name) == -1) && (errno != EISDIR)) {
118                         bb_perror_msg_and_die("can't remove old file %s",
119                                         dst_name);
120                 }
121         }
122
123         /* Handle hard links separately */
124         if (hard_link) {
125                 res = link(hard_link, dst_name);
126                 if (res != 0) {
127                         /* shared message */
128                         bb_perror_msg("can't create %slink '%s' to '%s'",
129                                 "hard", dst_name, hard_link
130                         );
131                 }
132                 /* Hardlinks have no separate mode/ownership, skip chown/chmod */
133                 goto ret;
134         }
135
136         /* Create the filesystem entry */
137         switch (file_header->mode & S_IFMT) {
138         case S_IFREG: {
139                 /* Regular file */
140                 char *dst_nameN;
141                 int flags = O_WRONLY | O_CREAT | O_EXCL;
142                 if (archive_handle->ah_flags & ARCHIVE_O_TRUNC)
143                         flags = O_WRONLY | O_CREAT | O_TRUNC;
144                 dst_nameN = dst_name;
145 #ifdef ARCHIVE_REPLACE_VIA_RENAME
146                 if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME)
147                         /* rpm-style temp file name */
148                         dst_nameN = xasprintf("%s;%x", dst_name, (int)getpid());
149 #endif
150                 dst_fd = xopen3(dst_nameN,
151                         flags,
152                         file_header->mode
153                         );
154                 bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size);
155                 close(dst_fd);
156 #ifdef ARCHIVE_REPLACE_VIA_RENAME
157                 if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) {
158                         xrename(dst_nameN, dst_name);
159                         free(dst_nameN);
160                 }
161 #endif
162                 break;
163         }
164         case S_IFDIR:
165                 res = mkdir(dst_name, file_header->mode);
166                 if ((res != 0)
167                  && (errno != EISDIR) /* btw, Linux doesn't return this */
168                  && (errno != EEXIST)
169                 ) {
170                         bb_perror_msg("can't make dir %s", dst_name);
171                 }
172                 break;
173         case S_IFLNK:
174                 /* Symlink */
175 //TODO: what if file_header->link_target == NULL (say, corrupted tarball?)
176
177                 /* To avoid a directory traversal attack via symlinks,
178                  * do not restore symlinks with ".." components
179                  * or symlinks starting with "/", unless a magic
180                  * envvar is set.
181                  *
182                  * For example, consider a .tar created via:
183                  *  $ tar cvf bug.tar anything.txt
184                  *  $ ln -s /tmp symlink
185                  *  $ tar --append -f bug.tar symlink
186                  *  $ rm symlink
187                  *  $ mkdir symlink
188                  *  $ tar --append -f bug.tar symlink/evil.py
189                  *
190                  * This will result in an archive that contains:
191                  *  $ tar --list -f bug.tar
192                  *  anything.txt
193                  *  symlink [-> /tmp]
194                  *  symlink/evil.py
195                  *
196                  * Untarring bug.tar would otherwise place evil.py in '/tmp'.
197                  */
198                 create_or_remember_symlink(&archive_handle->symlink_placeholders,
199                                 file_header->link_target,
200                                 dst_name);
201                 break;
202         case S_IFSOCK:
203         case S_IFBLK:
204         case S_IFCHR:
205         case S_IFIFO:
206                 res = mknod(dst_name, file_header->mode, file_header->device);
207                 if (res != 0) {
208                         bb_perror_msg("can't create node %s", dst_name);
209                 }
210                 break;
211         default:
212                 bb_error_msg_and_die("unrecognized file type");
213         }
214
215         if (!S_ISLNK(file_header->mode)) {
216                 if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_OWNER)) {
217                         uid_t uid = file_header->uid;
218                         gid_t gid = file_header->gid;
219 #if ENABLE_FEATURE_TAR_UNAME_GNAME
220                         if (!(archive_handle->ah_flags & ARCHIVE_NUMERIC_OWNER)) {
221                                 if (file_header->tar__uname) {
222 //TODO: cache last name/id pair?
223                                         struct passwd *pwd = getpwnam(file_header->tar__uname);
224                                         if (pwd) uid = pwd->pw_uid;
225                                 }
226                                 if (file_header->tar__gname) {
227                                         struct group *grp = getgrnam(file_header->tar__gname);
228                                         if (grp) gid = grp->gr_gid;
229                                 }
230                         }
231 #endif
232                         /* GNU tar 1.15.1 uses chown, not lchown */
233                         chown(dst_name, uid, gid);
234                 }
235                 /* uclibc has no lchmod, glibc is even stranger -
236                  * it has lchmod which seems to do nothing!
237                  * so we use chmod... */
238                 if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_PERM)) {
239                         chmod(dst_name, file_header->mode);
240                 }
241                 if (archive_handle->ah_flags & ARCHIVE_RESTORE_DATE) {
242                         struct timeval t[2];
243
244                         t[1].tv_sec = t[0].tv_sec = file_header->mtime;
245                         t[1].tv_usec = t[0].tv_usec = 0;
246                         utimes(dst_name, t);
247                 }
248         }
249
250  ret: ;
251 #if ENABLE_FEATURE_TAR_SELINUX
252         if (sctx) {
253                 /* reset the context after creating an entry */
254                 setfscreatecon(NULL);
255         }
256 #endif
257 }