/* * dpkg - main program for package management * verify.c - verify package integrity * * Copyright © 2012-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 #include #include #include #include #include #include "main.h" enum verify_result { VERIFY_NONE, VERIFY_PASS, VERIFY_FAIL, }; struct verify_checks { int exists_errno; enum verify_result exists; enum verify_result mode; enum verify_result md5sum; }; typedef void verify_output_func(struct fsys_namenode *, struct verify_checks *); static int verify_result_rpm(enum verify_result result, int check) { switch (result) { case VERIFY_FAIL: return check; case VERIFY_PASS: return '.'; case VERIFY_NONE: default: return '?'; } } static void verify_output_rpm(struct fsys_namenode *namenode, struct verify_checks *checks) { char result[9]; char *error = NULL; int attr; memset(result, '?', sizeof(result)); if (checks->exists == VERIFY_FAIL) { memcpy(result, "missing ", sizeof(result)); if (checks->exists_errno != ENOENT) m_asprintf(&error, " (%s)", strerror(checks->exists_errno)); } else { result[1] = verify_result_rpm(checks->mode, 'M'); result[2] = verify_result_rpm(checks->md5sum, '5'); } if (namenode->flags & FNNF_OLD_CONFF) attr = 'c'; else attr = ' '; printf("%.9s %c %s%s\n", result, attr, namenode->name, error ? error : ""); free(error); } static verify_output_func *verify_output = verify_output_rpm; bool verify_set_output(const char *name) { if (strcmp(name, "rpm") == 0) verify_output = verify_output_rpm; else return false; return true; } static int verify_digest(const char *filename, struct fsys_namenode *fnn, struct verify_checks *checks) { struct dpkg_error err; static int fd; char hash[MD5HASHLEN + 1]; fd = open(filename, O_RDONLY); if (fd >= 0) { push_cleanup(cu_closefd, ehflag_bombout, 1, &fd); if (fd_md5(fd, hash, -1, &err) < 0) ohshit(_("cannot compute MD5 digest for file '%s': %s"), filename, err.str); pop_cleanup(ehflag_normaltidy); /* fd = open(cdr.buf) */ close(fd); if (strcmp(hash, fnn->newhash) == 0) { checks->md5sum = VERIFY_PASS; return 0; } else { checks->md5sum = VERIFY_FAIL; } } else { checks->md5sum = VERIFY_NONE; } return -1; } static int verify_file(const char *filename, struct fsys_namenode *fnn, struct pkginfo *pkg, struct verify_checks *checks) { struct stat st; int failures = 0; if (lstat(filename, &st) < 0) { checks->exists_errno = errno; checks->exists = VERIFY_FAIL; return 1; } checks->exists = VERIFY_PASS; if (fnn->newhash == NULL && fnn->oldhash != NULL) fnn->newhash = fnn->oldhash; if (fnn->newhash != NULL) { /* Mode check heuristic: If we know its digest, the pathname * must be a regular file. */ if (!S_ISREG(st.st_mode)) { checks->mode = VERIFY_FAIL; failures++; } if (verify_digest(filename, fnn, checks) < 0) failures++; } return failures; } static void verify_package(struct pkginfo *pkg) { struct fsys_namenode_list *file; struct varbuf filename = VARBUF_INIT; ensure_packagefiles_available(pkg); parse_filehash(pkg, &pkg->installed); pkg_conffiles_mark_old(pkg); for (file = pkg->files; file; file = file->next) { struct verify_checks checks; struct fsys_namenode *fnn; fnn = namenodetouse(file->namenode, pkg, &pkg->installed); varbuf_reset(&filename); varbuf_add_str(&filename, dpkg_fsys_get_dir()); varbuf_add_str(&filename, fnn->name); varbuf_end_str(&filename); memset(&checks, 0, sizeof(checks)); if (verify_file(filename.buf, fnn, pkg, &checks) > 0) verify_output(fnn, &checks); } varbuf_destroy(&filename); } int verify(const char *const *argv) { struct pkginfo *pkg; int rc = 0; modstatdb_open(msdbrw_readonly); ensure_diversions(); if (!*argv) { struct pkg_hash_iter *iter; iter = pkg_hash_iter_new(); while ((pkg = pkg_hash_iter_next_pkg(iter))) verify_package(pkg); pkg_hash_iter_free(iter); } else { const char *thisarg; while ((thisarg = *argv++)) { pkg = dpkg_options_parse_pkgname(cipaction, thisarg); if (pkg->status == PKG_STAT_NOTINSTALLED) { notice(_("package '%s' is not installed"), pkg_name(pkg, pnaw_nonambig)); rc = 1; continue; } verify_package(pkg); } } modstatdb_shutdown(); m_output(stdout, _("")); return rc; }