/* * libdpkg - Debian packaging suite library routines * path-remove.c - path removal functionss * * Copyright © 1994-1995 Ian Jackson * Copyright © 2007-2015 Guillem Jover * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include int secure_unlink_statted(const char *pathname, const struct stat *stab) { if (S_ISREG(stab->st_mode) ? (stab->st_mode & 07000) : !(S_ISLNK(stab->st_mode) || S_ISDIR(stab->st_mode) || S_ISFIFO(stab->st_mode) || S_ISSOCK(stab->st_mode))) { if (chmod(pathname, 0600)) return -1; } if (unlink(pathname)) return -1; return 0; } /* * If the pathname to remove is: * * 1. a sticky or set-id file, or * 2. an unknown object (i.e., not a file, link, directory, fifo or socket) * * we change its mode so that a malicious user cannot use it, even if it's * linked to another file. */ int secure_unlink(const char *pathname) { struct stat stab; if (lstat(pathname, &stab)) return -1; return secure_unlink_statted(pathname, &stab); } /** * Securely remove a pathname. * * This is a secure version of remove(3) using secure_unlink() instead of * unlink(2). * * @retval 0 On success. * @retval -1 On failure, just like unlink(2) & rmdir(2). */ int secure_remove(const char *pathname) { int rc, e; if (!rmdir(pathname)) { debug(dbg_eachfiledetail, "secure_remove '%s' rmdir OK", pathname); return 0; } if (errno != ENOTDIR) { e = errno; debug(dbg_eachfiledetail, "secure_remove '%s' rmdir %s", pathname, strerror(e)); errno = e; return -1; } rc = secure_unlink(pathname); e = errno; debug(dbg_eachfiledetail, "secure_remove '%s' unlink %s", pathname, rc ? strerror(e) : "OK"); errno = e; return rc; } void path_remove_tree(const char *pathname) { pid_t pid; const char *u; u = path_skip_slash_dotslash(pathname); assert(*u); debug(dbg_eachfile, "%s '%s'", __func__, pathname); if (!rmdir(pathname)) return; /* Deleted it OK, it was a directory. */ if (errno == ENOENT || errno == ELOOP) return; if (errno == ENOTDIR) { /* Either it's a file, or one of the path components is. If * one of the path components is this will fail again ... */ if (secure_unlink(pathname) == 0) return; /* OK, it was. */ if (errno == ENOTDIR) return; } if (errno != ENOTEMPTY && errno != EEXIST) /* Huh? */ ohshite(_("unable to securely remove '%.255s'"), pathname); pid = subproc_fork(); if (pid == 0) { execlp(RM, "rm", "-rf", "--", pathname, NULL); ohshite(_("unable to execute %s (%s)"), _("rm command for cleanup"), RM); } debug(dbg_eachfile, "%s running rm -rf '%s'", __func__, pathname); subproc_reap(pid, _("rm command for cleanup"), 0); }