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 : mode_t mode = stab->st_mode;
41 : :
42 [ # # # # ]: 0 : if (S_ISREG(mode) ? (mode & 07000) :
43 [ # # # # ]: 0 : !(S_ISLNK(mode) || S_ISDIR(mode) ||
44 [ # # # # ]: 0 : S_ISFIFO(mode) || S_ISSOCK(mode))) {
45 [ # # ]: 0 : if (chmod(pathname, 0600))
46 : 0 : return -1;
47 : : }
48 : :
49 [ # # ]: 0 : if (unlink(pathname))
50 : 0 : return -1;
51 : :
52 : 0 : return 0;
53 : : }
54 : :
55 : : /**
56 : : * Securely unlink a pathname.
57 : : *
58 : : * If the pathname to remove is:
59 : : *
60 : : * 1. a sticky or set-id file, or
61 : : * 2. an unknown object (i.e., not a file, link, directory, fifo or socket)
62 : : *
63 : : * we change its mode so that a malicious user cannot use it, even if it's
64 : : * linked to another file.
65 : : */
66 : : int
67 : 0 : secure_unlink(const char *pathname)
68 : : {
69 : : struct stat stab;
70 : :
71 [ # # ]: 0 : if (lstat(pathname, &stab))
72 : 0 : return -1;
73 : :
74 : 0 : return secure_unlink_statted(pathname, &stab);
75 : : }
76 : :
77 : : /**
78 : : * Securely remove a pathname.
79 : : *
80 : : * This is a secure version of remove(3) using secure_unlink() instead of
81 : : * unlink(2).
82 : : *
83 : : * @retval 0 On success.
84 : : * @retval -1 On failure, just like unlink(2) & rmdir(2).
85 : : */
86 : : int
87 : 0 : secure_remove(const char *pathname)
88 : : {
89 : : int rc, e;
90 : :
91 [ # # ]: 0 : if (!rmdir(pathname)) {
92 : 0 : debug(dbg_eachfiledetail, "secure_remove '%s' rmdir OK",
93 : : pathname);
94 : 0 : return 0;
95 : : }
96 : :
97 [ # # ]: 0 : if (errno != ENOTDIR) {
98 : 0 : e = errno;
99 : 0 : debug(dbg_eachfiledetail, "secure_remove '%s' rmdir %s",
100 : : pathname, strerror(e));
101 : 0 : errno = e;
102 : 0 : return -1;
103 : : }
104 : :
105 : 0 : rc = secure_unlink(pathname);
106 : 0 : e = errno;
107 [ # # ]: 0 : debug(dbg_eachfiledetail, "secure_remove '%s' unlink %s",
108 : 0 : pathname, rc ? strerror(e) : "OK");
109 : 0 : errno = e;
110 : :
111 : 0 : return rc;
112 : : }
113 : :
114 : : /**
115 : : * Remove a pathname and anything below it.
116 : : *
117 : : * This function removes pathname and all its contents recursively.
118 : : */
119 : : void
120 : 21 : path_remove_tree(const char *pathname)
121 : : {
122 : : pid_t pid;
123 : : const char *u;
124 : :
125 : 21 : u = path_skip_slash_dotslash(pathname);
126 [ - + ]: 21 : if (u[0] == '\0')
127 : 0 : internerr("pathname '%s' reduces to nothing", pathname);
128 : :
129 : 21 : debug(dbg_eachfile, "%s '%s'", __func__, pathname);
130 [ + + ]: 21 : if (!rmdir(pathname))
131 : 7 : return; /* Deleted it OK, it was a directory. */
132 [ + - - + ]: 14 : if (errno == ENOENT || errno == ELOOP)
133 : 0 : return;
134 [ - + ]: 14 : if (errno == ENOTDIR) {
135 : : /* Either it's a file, or one of the path components is. If
136 : : * one of the path components is this will fail again ... */
137 [ # # ]: 0 : if (secure_unlink(pathname) == 0)
138 : 0 : return; /* OK, it was. */
139 [ # # ]: 0 : if (errno == ENOTDIR)
140 : 0 : return;
141 : : }
142 : : /* Trying to remove a directory or a file on a read-only filesystem,
143 : : * even if non-existent, always returns EROFS. */
144 [ - + ]: 14 : if (errno == EROFS) {
145 [ # # # # ]: 0 : if (access(pathname, F_OK) < 0 && errno == ENOENT)
146 : 0 : return;
147 : 0 : errno = EROFS;
148 : : }
149 [ - + - - ]: 14 : if (errno != ENOTEMPTY && errno != EEXIST) /* Huh? */
150 : 0 : ohshite(_("unable to securely remove '%.255s'"), pathname);
151 : :
152 : 14 : pid = subproc_fork();
153 [ + + ]: 28 : if (pid == 0) {
154 : 14 : execlp(RM, "rm", "-rf", "--", pathname, NULL);
155 : 14 : ohshite(_("unable to execute %s (%s)"),
156 : : _("rm command for cleanup"), RM);
157 : : }
158 : 14 : debug(dbg_eachfile, "%s running rm -rf '%s'", __func__, pathname);
159 : 14 : subproc_reap(pid, _("rm command for cleanup"), 0);
160 : : }
|