3 * Verify installed payload files from package metadata.
10 #include <sys/capability.h>
13 #include <acl/libacl.h>
16 #include <rpm/rpmcli.h>
17 #include <rpm/header.h>
18 #include <rpm/rpmlog.h>
19 #include <rpm/rpmfi.h>
20 #include <rpm/rpmts.h>
21 #include <rpm/rpmdb.h>
22 #include <rpm/rpmfileutil.h>
24 #include "lib/misc.h" /* uidToUname(), gnameToGid */
25 #include "lib/rpmchroot.h"
26 #include "lib/rpmte_internal.h" /* rpmteProcess() */
30 #define S_ISDEV(m) (S_ISBLK((m)) || S_ISCHR((m)))
32 /* If cap_compare() (Linux extension) not available, do it the hard way */
33 #if WITH_CAP && !defined(HAVE_CAP_COMPARE)
34 static int cap_compare(cap_t acap, cap_t bcap)
37 size_t asize = cap_size(acap);
38 size_t bsize = cap_size(bcap);
43 char *abuf = xcalloc(asize, sizeof(*abuf));
44 char *bbuf = xcalloc(bsize, sizeof(*bbuf));
45 cap_copy_ext(abuf, acap, asize);
46 cap_copy_ext(bbuf, bcap, bsize);
47 rc = memcmp(abuf, bbuf, asize);
55 int rpmVerifyFile(const rpmts ts, const rpmfi fi,
56 rpmVerifyAttrs * res, rpmVerifyAttrs omitMask)
58 rpm_mode_t fmode = rpmfiFMode(fi);
59 rpmfileAttrs fileAttrs = rpmfiFFlags(fi);
60 rpmVerifyAttrs flags = rpmfiVFlags(fi);
61 const char * fn = rpmfiFN(fi);
65 *res = RPMVERIFY_NONE;
68 * Check to see if the file was installed - if not pretend all is OK.
70 switch (rpmfiFState(fi)) {
71 case RPMFILE_STATE_NETSHARED:
72 case RPMFILE_STATE_REPLACED:
73 case RPMFILE_STATE_NOTINSTALLED:
74 case RPMFILE_STATE_WRONGCOLOR:
75 case RPMFILE_STATE_MISSING:
78 case RPMFILE_STATE_NORMAL:
82 if (fn == NULL || lstat(fn, &sb) != 0) {
83 *res |= RPMVERIFY_LSTATFAIL;
88 * Not all attributes of non-regular files can be verified.
90 if (S_ISDIR(sb.st_mode))
91 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
92 RPMVERIFY_LINKTO | RPMVERIFY_CAPS);
93 else if (S_ISLNK(sb.st_mode)) {
94 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
95 RPMVERIFY_MODE | RPMVERIFY_CAPS);
97 else if (S_ISFIFO(sb.st_mode))
98 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
99 RPMVERIFY_LINKTO | RPMVERIFY_CAPS);
100 else if (S_ISCHR(sb.st_mode))
101 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
102 RPMVERIFY_LINKTO | RPMVERIFY_CAPS);
103 else if (S_ISBLK(sb.st_mode))
104 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
105 RPMVERIFY_LINKTO | RPMVERIFY_CAPS);
107 flags &= ~(RPMVERIFY_LINKTO);
110 * Content checks of %ghost files are meaningless.
112 if (fileAttrs & RPMFILE_GHOST)
113 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
117 * Don't verify any features in omitMask.
119 flags &= ~(omitMask | RPMVERIFY_FAILURES);
122 if (flags & RPMVERIFY_FILEDIGEST) {
123 const unsigned char *digest;
127 /* XXX If --nomd5, then prelinked library sizes are not corrected. */
128 if ((digest = rpmfiFDigest(fi, &algo, &diglen))) {
129 unsigned char fdigest[diglen];
132 rc = rpmDoDigest(algo, fn, 0, fdigest, &fsize);
135 *res |= (RPMVERIFY_READFAIL|RPMVERIFY_FILEDIGEST);
136 } else if (memcmp(fdigest, digest, diglen)) {
137 *res |= RPMVERIFY_FILEDIGEST;
140 *res |= RPMVERIFY_FILEDIGEST;
144 if (flags & RPMVERIFY_LINKTO) {
148 if ((size = readlink(fn, linkto, sizeof(linkto)-1)) == -1)
149 *res |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO);
151 const char * flink = rpmfiFLink(fi);
153 if (flink == NULL || !rstreq(linkto, flink))
154 *res |= RPMVERIFY_LINKTO;
158 if (flags & RPMVERIFY_FILESIZE) {
159 if (sb.st_size != rpmfiFSize(fi))
160 *res |= RPMVERIFY_FILESIZE;
163 if (flags & RPMVERIFY_MODE) {
164 rpm_mode_t metamode = fmode;
168 * Platforms (like AIX) where sizeof(rpm_mode_t) != sizeof(mode_t)
169 * need the (rpm_mode_t) cast here.
171 filemode = (rpm_mode_t)sb.st_mode;
174 * Comparing the type of %ghost files is meaningless, but perms are OK.
176 if (fileAttrs & RPMFILE_GHOST) {
181 if (metamode != filemode)
182 *res |= RPMVERIFY_MODE;
186 * For now, any non-default acl's on a file is a difference as rpm
187 * cannot have set them.
189 acl_t facl = acl_get_file(fn, ACL_TYPE_ACCESS);
191 if (acl_equiv_mode(facl, NULL) == 1) {
192 *res |= RPMVERIFY_MODE;
199 if (flags & RPMVERIFY_RDEV) {
200 if (S_ISCHR(fmode) != S_ISCHR(sb.st_mode)
201 || S_ISBLK(fmode) != S_ISBLK(sb.st_mode))
203 *res |= RPMVERIFY_RDEV;
204 } else if (S_ISDEV(fmode) && S_ISDEV(sb.st_mode)) {
205 rpm_rdev_t st_rdev = (sb.st_rdev & 0xffff);
206 rpm_rdev_t frdev = (rpmfiFRdev(fi) & 0xffff);
207 if (st_rdev != frdev)
208 *res |= RPMVERIFY_RDEV;
213 if (flags & RPMVERIFY_CAPS) {
215 * Empty capability set ("=") is not exactly the same as no
216 * capabilities at all but suffices for now...
219 cap = cap_from_text(rpmfiFCaps(fi));
221 cap = cap_from_text("=");
223 fcap = cap_get_file(fn);
225 fcap = cap_from_text("=");
228 if (cap_compare(cap, fcap) != 0)
229 *res |= RPMVERIFY_CAPS;
236 if ((flags & RPMVERIFY_MTIME) && (sb.st_mtime != rpmfiFMtime(fi))) {
237 *res |= RPMVERIFY_MTIME;
240 if (flags & RPMVERIFY_USER) {
241 const char * name = uidToUname(sb.st_uid);
242 const char * fuser = rpmfiFUser(fi);
243 if (name == NULL || fuser == NULL || !rstreq(name, fuser))
244 *res |= RPMVERIFY_USER;
247 if (flags & RPMVERIFY_GROUP) {
248 const char * name = gidToGname(sb.st_gid);
249 const char * fgroup = rpmfiFGroup(fi);
250 if (name == NULL || fgroup == NULL || !rstreq(name, fgroup))
251 *res |= RPMVERIFY_GROUP;
258 * Return exit code from running verify script from header.
259 * @param ts transaction set
261 * @return 0 on success
263 static int rpmVerifyScript(rpmts ts, Header h)
267 if (headerIsEntry(h, RPMTAG_VERIFYSCRIPT)) {
268 /* fake up a erasure transaction element */
269 (void) rpmtsAddEraseElement(ts, h, -1);
271 rc = (rpmteProcess(rpmtsElement(ts, 0), PKG_VERIFY) != RPMRC_OK);
273 /* clean up our fake transaction bits */
281 #define _verify(_RPMVERIFY_F, _C, _pad) \
282 ((verifyResult & _RPMVERIFY_F) ? _C : _pad)
283 #define _verifylink(_RPMVERIFY_F, _C, _pad) \
284 ((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
285 (verifyResult & _RPMVERIFY_F) ? _C : _pad)
286 #define _verifyfile(_RPMVERIFY_F, _C, _pad) \
287 ((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
288 (verifyResult & _RPMVERIFY_F) ? _C : _pad)
289 char * rpmVerifyString(uint32_t verifyResult, const char *pad)
292 rasprintf(&fmt, "%s%s%s%s%s%s%s%s%s",
293 _verifyfile(RPMVERIFY_FILEDIGEST, "5", pad),
294 _verify(RPMVERIFY_FILESIZE, "S", pad),
295 _verifylink(RPMVERIFY_LINKTO, "L", pad),
296 _verify(RPMVERIFY_MTIME, "T", pad),
297 _verify(RPMVERIFY_RDEV, "D", pad),
298 _verify(RPMVERIFY_USER, "U", pad),
299 _verify(RPMVERIFY_GROUP, "G", pad),
300 _verify(RPMVERIFY_MODE, "M", pad),
301 _verify(RPMVERIFY_CAPS, "P", pad));
311 char * rpmFFlagsString(uint32_t fflags, const char *pad)
314 rasprintf(&fmt, "%s%s%s%s%s%s%s%s",
315 (fflags & RPMFILE_DOC) ? "d" : pad,
316 (fflags & RPMFILE_CONFIG) ? "c" : pad,
317 (fflags & RPMFILE_SPECFILE) ? "s" : pad,
318 (fflags & RPMFILE_MISSINGOK) ? "m" : pad,
319 (fflags & RPMFILE_NOREPLACE) ? "n" : pad,
320 (fflags & RPMFILE_GHOST) ? "g" : pad,
321 (fflags & RPMFILE_LICENSE) ? "l" : pad,
322 (fflags & RPMFILE_README) ? "r" : pad);
327 * Check file info from header against what's actually installed.
328 * @param ts transaction set
329 * @param h header to verify
330 * @param omitMask bits to disable verify checks
331 * @param ghosts should ghosts be verified?
332 * @return 0 no problems, 1 problems found
334 static int verifyHeader(rpmts ts, Header h, rpmVerifyAttrs omitMask, int ghosts)
336 rpmVerifyAttrs verifyResult = 0;
337 int ec = 0; /* assume no problems */
338 rpmfi fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_FLAGS_VERIFY);
341 while (rpmfiNext(fi) >= 0) {
342 rpmfileAttrs fileAttrs = rpmfiFFlags(fi);
343 char *buf = NULL, *attrFormat;
347 /* If not verifying %ghost, skip ghost files. */
348 if ((fileAttrs & RPMFILE_GHOST) && !ghosts)
351 rc = rpmVerifyFile(ts, fi, &verifyResult, omitMask);
353 /* Filter out timestamp differences of shared files */
354 if (rc == 0 && (verifyResult & RPMVERIFY_MTIME)) {
355 rpmdbMatchIterator mi;
356 mi = rpmtsInitIterator(ts, RPMDBI_BASENAMES, rpmfiFN(fi), 0);
357 if (rpmdbGetIteratorCount(mi) > 1)
358 verifyResult &= ~RPMVERIFY_MTIME;
359 rpmdbFreeIterator(mi);
362 attrFormat = rpmFFlagsString(fileAttrs, "");
363 ac = rstreq(attrFormat, "") ? ' ' : attrFormat[0];
365 if (!(fileAttrs & (RPMFILE_MISSINGOK|RPMFILE_GHOST)) || rpmIsVerbose()) {
366 rasprintf(&buf, _("missing %c %s"), ac, rpmfiFN(fi));
367 if ((verifyResult & RPMVERIFY_LSTATFAIL) != 0 &&
370 rasprintf(&app, " (%s)", strerror(errno));
376 } else if (verifyResult || rpmIsVerbose()) {
377 char *verifyFormat = rpmVerifyString(verifyResult, ".");
378 rasprintf(&buf, "%s %c %s", verifyFormat, ac, rpmfiFN(fi));
381 if (verifyResult) ec = 1;
386 rpmlog(RPMLOG_NOTICE, "%s\n", buf);
396 * Check installed package dependencies for problems.
397 * @param ts transaction set
399 * @return number of problems found (0 for no problems)
401 static int verifyDependencies(rpmts ts, Header h)
408 (void) rpmtsAddInstallElement(ts, h, NULL, 0, NULL);
410 (void) rpmtsCheck(ts);
411 te = rpmtsElement(ts, 0);
412 ps = rpmteProblems(te);
413 rc = rpmpsNumProblems(ps);
416 rpmlog(RPMLOG_NOTICE, _("Unsatisfied dependencies for %s:\n"),
418 rpmpsi psi = rpmpsInitIterator(ps);
421 while ((p = rpmpsiNext(psi)) != NULL) {
422 char * ps = rpmProblemString(p);
423 rpmlog(RPMLOG_NOTICE, "\t%s\n", ps);
426 psi = rpmpsFreeIterator(psi);
434 int showVerifyPackage(QVA_t qva, rpmts ts, Header h)
436 rpmVerifyAttrs omitMask = ((qva->qva_flags & VERIFY_ATTRS) ^ VERIFY_ATTRS);
437 int ghosts = (qva->qva_fflags & RPMFILE_GHOST);
441 if (qva->qva_flags & VERIFY_DEPS) {
442 if ((rc = verifyDependencies(ts, h)) != 0)
445 if (qva->qva_flags & VERIFY_FILES) {
446 if ((rc = verifyHeader(ts, h, omitMask, ghosts)) != 0)
449 if (qva->qva_flags & VERIFY_SCRIPT) {
450 if ((rc = rpmVerifyScript(ts, h)) != 0)
457 int rpmcliVerify(rpmts ts, QVA_t qva, char * const * argv)
459 rpmVSFlags vsflags, ovsflags;
461 FD_t scriptFd = fdDup(STDOUT_FILENO);
464 * Open the DB + indices explicitly before possible chroot,
465 * otherwises BDB is going to be unhappy...
467 rpmtsOpenDB(ts, O_RDONLY);
468 rpmdbOpenAll(rpmtsGetRdb(ts));
469 if (rpmChrootSet(rpmtsRootDir(ts)) || rpmChrootIn()) {
474 if (qva->qva_showPackage == NULL)
475 qva->qva_showPackage = showVerifyPackage;
477 vsflags = rpmExpandNumeric("%{?_vsflags_verify}");
478 if (rpmcliQueryFlags & VERIFY_DIGEST)
479 vsflags |= _RPMVSF_NODIGESTS;
480 if (rpmcliQueryFlags & VERIFY_SIGNATURE)
481 vsflags |= _RPMVSF_NOSIGNATURES;
482 if (rpmcliQueryFlags & VERIFY_HDRCHK)
483 vsflags |= RPMVSF_NOHDRCHK;
484 vsflags &= ~RPMVSF_NEEDPAYLOAD;
486 rpmtsSetScriptFd(ts, scriptFd);
487 ovsflags = rpmtsSetVSFlags(ts, vsflags);
488 ec = rpmcliArgIter(ts, qva, argv);
489 vsflags = rpmtsSetVSFlags(ts, ovsflags);
490 rpmtsSetScriptFd(ts, NULL);
492 if (qva->qva_showPackage == showVerifyPackage)
493 qva->qva_showPackage = NULL;
497 if (rpmChrootOut() || rpmChrootSet(NULL))