Branch data Line data Source code
1 : : /*
2 : : * libdpkg - Debian packaging suite library routines
3 : : * path-remove.c - path removal functions
4 : : *
5 : : * Copyright © 1994-1995 Ian Jackson <ijackson@chiark.greenend.org.uk>
6 : : * Copyright © 2007-2015 Guillem Jover <guillem@debian.org>
7 : : *
8 : : * This is free software; you can redistribute it and/or modify
9 : : * it under the terms of the GNU General Public License as published by
10 : : * the Free Software Foundation; either version 2 of the License, or
11 : : * (at your option) any later version.
12 : : *
13 : : * This is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : : * GNU General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU General Public License
19 : : * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 : : */
21 : :
22 : : #include <config.h>
23 : : #include <compat.h>
24 : :
25 : : #include <sys/stat.h>
26 : :
27 : : #include <errno.h>
28 : : #include <string.h>
29 : : #include <unistd.h>
30 : :
31 : : #include <dpkg/i18n.h>
32 : : #include <dpkg/dpkg.h>
33 : : #include <dpkg/path.h>
34 : : #include <dpkg/debug.h>
35 : : #include <dpkg/subproc.h>
36 : :
37 : : int
38 : 0 : secure_unlink_statted(const char *pathname, const struct stat *stab)
39 : : {
40 [ # # # # ]: 0 : if (S_ISREG(stab->st_mode) ? (stab->st_mode & 07000) :
41 [ # # # # ]: 0 : !(S_ISLNK(stab->st_mode) || S_ISDIR(stab->st_mode) ||
42 [ # # # # ]: 0 : S_ISFIFO(stab->st_mode) || S_ISSOCK(stab->st_mode))) {
43 [ # # ]: 0 : if (chmod(pathname, 0600))
44 : 0 : return -1;
45 : : }
46 : :
47 [ # # ]: 0 : if (unlink(pathname))
48 : 0 : return -1;
49 : :
50 : 0 : return 0;
51 : : }
52 : :
53 : : /**
54 : : * Securely unlink a pathname.
55 : : *
56 : : * If the pathname to remove is:
57 : : *
58 : : * 1. a sticky or set-id file, or
59 : : * 2. an unknown object (i.e., not a file, link, directory, fifo or socket)
60 : : *
61 : : * we change its mode so that a malicious user cannot use it, even if it's
62 : : * linked to another file.
63 : : */
64 : : int
65 : 0 : secure_unlink(const char *pathname)
66 : : {
67 : : struct stat stab;
68 : :
69 [ # # ]: 0 : if (lstat(pathname, &stab))
70 : 0 : return -1;
71 : :
72 : 0 : return secure_unlink_statted(pathname, &stab);
73 : : }
74 : :
75 : : /**
76 : : * Securely remove a pathname.
77 : : *
78 : : * This is a secure version of remove(3) using secure_unlink() instead of
79 : : * unlink(2).
80 : : *
81 : : * @retval 0 On success.
82 : : * @retval -1 On failure, just like unlink(2) & rmdir(2).
83 : : */
84 : : int
85 : 0 : secure_remove(const char *pathname)
86 : : {
87 : : int rc, e;
88 : :
89 [ # # ]: 0 : if (!rmdir(pathname)) {
90 : 0 : debug(dbg_eachfiledetail, "secure_remove '%s' rmdir OK",
91 : : pathname);
92 : 0 : return 0;
93 : : }
94 : :
95 [ # # ]: 0 : if (errno != ENOTDIR) {
96 : 0 : e = errno;
97 : 0 : debug(dbg_eachfiledetail, "secure_remove '%s' rmdir %s",
98 : : pathname, strerror(e));
99 : 0 : errno = e;
100 : 0 : return -1;
101 : : }
102 : :
103 : 0 : rc = secure_unlink(pathname);
104 : 0 : e = errno;
105 [ # # ]: 0 : debug(dbg_eachfiledetail, "secure_remove '%s' unlink %s",
106 : 0 : pathname, rc ? strerror(e) : "OK");
107 : 0 : errno = e;
108 : :
109 : 0 : return rc;
110 : : }
111 : :
112 : : /**
113 : : * Remove a pathname and anything below it.
114 : : *
115 : : * This function removes pathname and all its contents recursively.
116 : : */
117 : : void
118 : 21 : path_remove_tree(const char *pathname)
119 : : {
120 : : pid_t pid;
121 : : const char *u;
122 : :
123 : 21 : u = path_skip_slash_dotslash(pathname);
124 [ - + ]: 21 : if (u[0] == '\0')
125 : 0 : internerr("pathname '%s' reduces to nothing", pathname);
126 : :
127 : 21 : debug(dbg_eachfile, "%s '%s'", __func__, pathname);
128 [ + + ]: 21 : if (!rmdir(pathname))
129 : 7 : return; /* Deleted it OK, it was a directory. */
130 [ + - - + ]: 14 : if (errno == ENOENT || errno == ELOOP)
131 : 0 : return;
132 [ - + ]: 14 : if (errno == ENOTDIR) {
133 : : /* Either it's a file, or one of the path components is. If
134 : : * one of the path components is this will fail again ... */
135 [ # # ]: 0 : if (secure_unlink(pathname) == 0)
136 : 0 : return; /* OK, it was. */
137 [ # # ]: 0 : if (errno == ENOTDIR)
138 : 0 : return;
139 : : }
140 : : /* Trying to remove a directory or a file on a read-only filesystem,
141 : : * even if non-existent, always returns EROFS. */
142 [ - + ]: 14 : if (errno == EROFS) {
143 [ # # # # ]: 0 : if (access(pathname, F_OK) < 0 && errno == ENOENT)
144 : 0 : return;
145 : 0 : errno = EROFS;
146 : : }
147 [ - + - - ]: 14 : if (errno != ENOTEMPTY && errno != EEXIST) /* Huh? */
148 : 0 : ohshite(_("unable to securely remove '%.255s'"), pathname);
149 : :
150 : 14 : pid = subproc_fork();
151 [ + + ]: 28 : if (pid == 0) {
152 : 14 : execlp(RM, "rm", "-rf", "--", pathname, NULL);
153 : 14 : ohshite(_("unable to execute %s (%s)"),
154 : : _("rm command for cleanup"), RM);
155 : : }
156 : 14 : debug(dbg_eachfile, "%s running rm -rf '%s'", __func__, pathname);
157 : 14 : subproc_reap(pid, _("rm command for cleanup"), 0);
158 : : }
|