From: Sean Finney <sean...@debian.org> Date: Sun, 11 Oct 2009 12:43:50 +0200
A small number of functions from help.c are required for outside-of-dpkg functioning of the conffiledb routines. As we want to be able to have functional unit tests as well as to use these routines in other cmdline programs (such as a hypothetical dpkg-conffile utility), we need to move these functions out of help.c which is otherwise hopelessly intertwined with the main dpkg code. Therefore, the following functions have been moved to a new util.c file: * void ensure_pathname_nonexisting(const char *pathname) * int secure_unlink(const char *pathname) * int secure_unlink_statted(const char *pathname, const struct stat *stab) Doxygen documentation has been added to the functions in the process. The Makefile.am automake template has also been updated to include this file in dpkg_SOURCES and no further changes are required to dpkg source code. [jn: rebased on current dpkg] Signed-off-by: Jonathan Nieder <jrnie...@gmail.com> --- Hi, debug is now part of libdpkg so I left that one out. help.c describes itself as "help.c - various helper routines", which doesn't seem so far from util.c. Do I understand correctly that the main purpose is to make ensure_pathname_nonexisting et al usable as a separate translation unit that doesn't depend on too much else? In the long term it might be most intuitive to do something like what 1.16.0~160 (Move and generalize debug API from dpkg to libdpkg, 2011-02-04) does. src/Makefile.am | 3 +- src/help.c | 61 -------------------------- src/util.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 62 deletions(-) create mode 100644 src/util.c diff --git a/src/Makefile.am b/src/Makefile.am index e29abf3..51e2b9f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -44,7 +44,8 @@ dpkg_SOURCES = \ remove.c \ select.c \ trigproc.c \ - update.c + update.c \ + util.c dpkg_LDADD = \ $(LDADD) \ diff --git a/src/help.c b/src/help.c index 1be86e6..4ad8823 100644 --- a/src/help.c +++ b/src/help.c @@ -525,67 +525,6 @@ void oldconffsetflags(const struct conffile *searchconff) { } } -/* - * 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); -} - -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; -} - -void ensure_pathname_nonexisting(const char *pathname) { - pid_t pid; - const char *u; - - u = path_skip_slash_dotslash(pathname); - assert(*u); - - debug(dbg_eachfile,"ensure_pathname_nonexisting `%s'",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,"ensure_pathname_nonexisting running rm -rf"); - subproc_wait_check(pid, "rm cleanup", 0); -} void log_action(const char *action, struct pkginfo *pkg) { log_message("%s %s %s %s", action, pkg->name, diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..3dd4fed --- /dev/null +++ b/src/util.c @@ -0,0 +1,126 @@ +/* + * dpkg - main program for package management + * util.c - miscellaneous utility functions + * + * Copyright © 1995 Ian Jackson <i...@chiark.greenend.org.uk> + * + * 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, + * 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <config.h> +#include <compat.h> + +#include <assert.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "main.h" + +#include "dpkg/dpkg.h" +#include "dpkg/i18n.h" +#include "dpkg/path.h" +#include "dpkg/subproc.h" + +/** + * Safely unlink a given pathname, with checks on file type and mode. + * + * 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. + * + * @param pathname the path to unlink + * @return 0 on success, -1 on error. + */ +int +secure_unlink(const char *pathname) +{ + struct stat stab; + + if (lstat(pathname, &stab)) + return -1; + + return secure_unlink_statted(pathname, &stab); +} + +/** + * Safely unlink a given pathname, checking file type and mode against + * externally provided file type and mode information. + * + * See notes in secure_unlink. + * + * @param pathname the path to unlink. + * @param stab a struct stat containing information about the path. + * @return 0 on success, -1 on error. + */ +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; +} + +/** + * Delete a pathname and anything that may be beneath it. + * + * This function is a generic catch-all delete utility, which + * will remove anything that may exist at the given pathname. If + * The pathname is a nonempty directory, it will be recursively + * removed with a fork/exec of rm -rf. + * + * @param pathname the path to be removed. + */ +void +ensure_pathname_nonexisting(const char *pathname) +{ + pid_t pid; + const char *u; + + u = path_skip_slash_dotslash(pathname); + assert(*u); + + debug(dbg_eachfile, "ensure_pathname_nonexisting `%s'", 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, "ensure_pathname_nonexisting running rm -rf"); + subproc_wait_check(pid, "rm cleanup", 0); +} -- 1.7.5.1 -- To UNSUBSCRIBE, email to debian-dpkg-requ...@lists.debian.org with a subject of "unsubscribe". Trouble? Contact listmas...@lists.debian.org Archive: http://lists.debian.org/20110506074930.GA1040@elie