diff options
-rw-r--r-- | debian/changelog | 2 | ||||
-rw-r--r-- | src/archives.c | 23 | ||||
-rw-r--r-- | src/errors.c | 8 | ||||
-rw-r--r-- | src/main.h | 1 | ||||
-rw-r--r-- | src/script.c | 9 | ||||
-rw-r--r-- | t/cppcheck/cppcheck.supp | 2 |
6 files changed, 37 insertions, 8 deletions
diff --git a/debian/changelog b/debian/changelog index 500111abe..eb68b75dc 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,8 @@ dpkg (1.19.2) UNRELEASED; urgency=medium [ Guillem Jover ] + * dpkg: Fix --force-not-root for chown() and chmod() based syscalls, and + give a more meaningful error message on chroot(). Closes: #614126 * Perl modules: - Dpkg::OpenPGP: Ignore Version field in enarmored output. Fixes CPAN#127217. diff --git a/src/archives.c b/src/archives.c index e802baaee..a1013d73a 100644 --- a/src/archives.c +++ b/src/archives.c @@ -353,6 +353,7 @@ tarobject_extract(struct tarcontext *tc, struct tar_entry *te, char fnamebuf[256]; char fnamenewbuf[256]; char *newhash; + int rc; switch (te->type) { case TAR_FILETYPE_FILE: @@ -388,9 +389,11 @@ tarobject_extract(struct tarcontext *tc, struct tar_entry *te, namenode->statoverride->uid, namenode->statoverride->gid, namenode->statoverride->mode); - if (fchown(fd, st->uid, st->gid)) + rc = fchown(fd, st->uid, st->gid); + if (forcible_nonroot_error(rc)) ohshite(_("error setting ownership of '%.255s'"), te->name); - if (fchmod(fd, st->mode & ~S_IFMT)) + rc = fchmod(fd, st->mode & ~S_IFMT); + if (forcible_nonroot_error(rc)) ohshite(_("error setting permissions of '%.255s'"), te->name); /* Postpone the fsync, to try to avoid massive I/O degradation. */ @@ -497,16 +500,21 @@ tarobject_set_mtime(struct tar_entry *te, const char *path) static void tarobject_set_perms(struct tar_entry *te, const char *path, struct file_stat *st) { + int rc; + if (te->type == TAR_FILETYPE_FILE) return; /* Already handled using the file descriptor. */ if (te->type == TAR_FILETYPE_SYMLINK) { - if (lchown(path, st->uid, st->gid)) + rc = lchown(path, st->uid, st->gid); + if (forcible_nonroot_error(rc)) ohshite(_("error setting ownership of symlink '%.255s'"), path); } else { - if (chown(path, st->uid, st->gid)) + rc = chown(path, st->uid, st->gid); + if (forcible_nonroot_error(rc)) ohshite(_("error setting ownership of '%.255s'"), path); - if (chmod(path, st->mode & ~S_IFMT)) + rc = chmod(path, st->mode & ~S_IFMT); + if (forcible_nonroot_error(rc)) ohshite(_("error setting permissions of '%.255s'"), path); } } @@ -1020,6 +1028,8 @@ tarobject(void *ctx, struct tar_entry *ti) ohshite(_("unable to move aside '%.255s' to install new version"), ti->name); } else if (S_ISLNK(stab.st_mode)) { + int rc; + /* We can't make a symlink with two hardlinks, so we'll have to * copy it. (Pretend that making a copy of a symlink is the same * as linking to it.) */ @@ -1038,7 +1048,8 @@ tarobject(void *ctx, struct tar_entry *ti) varbuf_end_str(&symlinkfn); if (symlink(symlinkfn.buf,fnametmpvb.buf)) ohshite(_("unable to make backup symlink for '%.255s'"), ti->name); - if (lchown(fnametmpvb.buf,stab.st_uid,stab.st_gid)) + rc = lchown(fnametmpvb.buf, stab.st_uid, stab.st_gid); + if (forcible_nonroot_error(rc)) ohshite(_("unable to chown backup symlink for '%.255s'"), ti->name); tarobject_set_se_context(fnamevb.buf, fnametmpvb.buf, stab.st_mode); } else { diff --git a/src/errors.c b/src/errors.c index 3d2d71967..6fa940856 100644 --- a/src/errors.c +++ b/src/errors.c @@ -146,3 +146,11 @@ void forcibleerr(int forceflag, const char *fmt, ...) { } va_end(args); } + +int +forcible_nonroot_error(int rc) +{ + if (fc_nonroot && errno == EPERM) + return 0; + return rc; +} diff --git a/src/main.h b/src/main.h index 319d8afd0..d1b31b196 100644 --- a/src/main.h +++ b/src/main.h @@ -228,6 +228,7 @@ void cu_prermremove(int argc, void **argv); void print_error_perpackage(const char *emsg, const void *data); void print_error_perarchive(const char *emsg, const void *data); void forcibleerr(int forceflag, const char *format, ...) DPKG_ATTR_PRINTF(2); +int forcible_nonroot_error(int rc); int reportbroken_retexitstatus(int ret); bool skip_due_to_hold(struct pkginfo *pkg); diff --git a/src/script.c b/src/script.c index 66792135b..0865b953f 100644 --- a/src/script.c +++ b/src/script.c @@ -106,6 +106,8 @@ maintscript_pre_exec(struct command *cmd) changedir = "/"; if (instdirlen > 0 && !fc_script_chrootless) { + int rc; + if (strncmp(admindir, instdir, instdirlen) != 0) ohshit(_("admindir must be inside instdir for dpkg to work properly")); if (setenv("DPKG_ADMINDIR", admindir + instdirlen, 1) < 0) @@ -113,7 +115,12 @@ maintscript_pre_exec(struct command *cmd) if (setenv("DPKG_ROOT", "", 1) < 0) ohshite(_("unable to setenv for subprocesses")); - if (chroot(instdir)) + rc = chroot(instdir); + if (rc && fc_nonroot && errno == EPERM) + ohshit(_("not enough privileges to change root " + "directory with --force-not-root, consider " + "using --force-script-chrootless?")); + else if (rc) ohshite(_("failed to chroot to '%.250s'"), instdir); } /* Switch to a known good directory to give the maintainer script diff --git a/t/cppcheck/cppcheck.supp b/t/cppcheck/cppcheck.supp index e7888e513..7f11afac0 100644 --- a/t/cppcheck/cppcheck.supp +++ b/t/cppcheck/cppcheck.supp @@ -13,7 +13,7 @@ unusedStructMember:lib/compat/obstack.c nullPointerArithmetic:lib/compat/obstack.c // Ignore, we use this construct to ease portability. -knownConditionTrueFalse:src/script.c:162 +knownConditionTrueFalse:src/script.c:169 // Ignore, We use our own test assertions macros. literalWithCharPtrCompare:lib/dpkg/t/t-test.c |