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>
25 #include "lib/rpmchroot.h"
26 #include "lib/rpmte_internal.h" /* rpmteProcess() */
27 #include "lib/rpmug.h"
31 #define S_ISDEV(m) (S_ISBLK((m)) || S_ISCHR((m)))
33 /* If cap_compare() (Linux extension) not available, do it the hard way */
34 #if WITH_CAP && !defined(HAVE_CAP_COMPARE)
35 static int cap_compare(cap_t acap, cap_t bcap)
38 size_t asize = cap_size(acap);
39 size_t bsize = cap_size(bcap);
44 char *abuf = xcalloc(asize, sizeof(*abuf));
45 char *bbuf = xcalloc(bsize, sizeof(*bbuf));
46 cap_copy_ext(abuf, acap, asize);
47 cap_copy_ext(bbuf, bcap, bsize);
48 rc = memcmp(abuf, bbuf, asize);
56 rpmVerifyAttrs rpmfilesVerify(rpmfiles fi, int ix, rpmVerifyAttrs omitMask)
58 rpm_mode_t fmode = rpmfilesFMode(fi, ix);
59 rpmfileAttrs fileAttrs = rpmfilesFFlags(fi, ix);
60 rpmVerifyAttrs flags = rpmfilesVFlags(fi, ix);
61 const char * fn = rpmfilesFN(fi, ix);
63 rpmVerifyAttrs vfy = RPMVERIFY_NONE;
66 * Check to see if the file was installed - if not pretend all is OK.
68 switch (rpmfilesFState(fi, ix)) {
69 case RPMFILE_STATE_NETSHARED:
70 case RPMFILE_STATE_NOTINSTALLED:
73 case RPMFILE_STATE_REPLACED:
74 /* For replaced files we can only verify if it exists at all */
75 flags = RPMVERIFY_LSTATFAIL;
77 case RPMFILE_STATE_WRONGCOLOR:
79 * Files with wrong color are supposed to share some attributes
80 * with the actually installed file - verify what we can.
82 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
83 RPMVERIFY_MTIME | RPMVERIFY_RDEV);
85 case RPMFILE_STATE_NORMAL:
86 /* File from a non-installed package, try to verify nevertheless */
87 case RPMFILE_STATE_MISSING:
91 if (fn == NULL || lstat(fn, &sb) != 0) {
92 vfy |= RPMVERIFY_LSTATFAIL;
96 /* If we expected a directory but got a symlink to one, follow the link */
97 if (S_ISDIR(fmode) && S_ISLNK(sb.st_mode)) {
99 /* ...if it actually points to a directory */
100 if (stat(fn, &dsb) == 0 && S_ISDIR(dsb.st_mode)) {
102 /* ...and is by a legit user, to match fsmVerify() behavior */
103 if (sb.st_uid == 0 ||
104 (rpmugUid(rpmfilesFUser(fi, ix), &fuid) == 0 &&
105 sb.st_uid == fuid)) {
106 sb = dsb; /* struct assignment */
111 /* Links have no mode, other types have no linkto */
112 if (S_ISLNK(sb.st_mode))
113 flags &= ~(RPMVERIFY_MODE);
115 flags &= ~(RPMVERIFY_LINKTO);
117 /* Not all attributes of non-regular files can be verified */
118 if (!S_ISREG(sb.st_mode))
119 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
120 RPMVERIFY_MTIME | RPMVERIFY_CAPS);
122 /* Content checks of %ghost files are meaningless. */
123 if (fileAttrs & RPMFILE_GHOST)
124 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
125 RPMVERIFY_MTIME | RPMVERIFY_LINKTO);
127 /* Don't verify any features in omitMask. */
128 flags &= ~(omitMask | RPMVERIFY_FAILURES);
131 if (flags & RPMVERIFY_FILEDIGEST) {
132 const unsigned char *digest;
136 /* XXX If --nomd5, then prelinked library sizes are not corrected. */
137 if ((digest = rpmfilesFDigest(fi, ix, &algo, &diglen))) {
138 unsigned char fdigest[diglen];
141 if (rpmDoDigest(algo, fn, 0, fdigest, &fsize)) {
142 vfy |= (RPMVERIFY_READFAIL|RPMVERIFY_FILEDIGEST);
145 if (memcmp(fdigest, digest, diglen))
146 vfy |= RPMVERIFY_FILEDIGEST;
149 vfy |= RPMVERIFY_FILEDIGEST;
153 if (flags & RPMVERIFY_LINKTO) {
157 if ((size = readlink(fn, linkto, sizeof(linkto)-1)) == -1)
158 vfy |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO);
160 const char * flink = rpmfilesFLink(fi, ix);
162 if (flink == NULL || !rstreq(linkto, flink))
163 vfy |= RPMVERIFY_LINKTO;
167 if (flags & RPMVERIFY_FILESIZE) {
168 if (sb.st_size != rpmfilesFSize(fi, ix))
169 vfy |= RPMVERIFY_FILESIZE;
172 if (flags & RPMVERIFY_MODE) {
173 rpm_mode_t metamode = fmode;
177 * Platforms (like AIX) where sizeof(rpm_mode_t) != sizeof(mode_t)
178 * need the (rpm_mode_t) cast here.
180 filemode = (rpm_mode_t)sb.st_mode;
183 * Comparing the type of %ghost files is meaningless, but perms are OK.
185 if (fileAttrs & RPMFILE_GHOST) {
190 if (metamode != filemode)
191 vfy |= RPMVERIFY_MODE;
195 * For now, any non-default acl's on a file is a difference as rpm
196 * cannot have set them.
198 acl_t facl = acl_get_file(fn, ACL_TYPE_ACCESS);
200 if (acl_equiv_mode(facl, NULL) == 1) {
201 vfy |= RPMVERIFY_MODE;
208 if (flags & RPMVERIFY_RDEV) {
209 if (S_ISCHR(fmode) != S_ISCHR(sb.st_mode)
210 || S_ISBLK(fmode) != S_ISBLK(sb.st_mode))
212 vfy |= RPMVERIFY_RDEV;
213 } else if (S_ISDEV(fmode) && S_ISDEV(sb.st_mode)) {
214 rpm_rdev_t st_rdev = (sb.st_rdev & 0xffff);
215 rpm_rdev_t frdev = (rpmfilesFRdev(fi, ix) & 0xffff);
216 if (st_rdev != frdev)
217 vfy |= RPMVERIFY_RDEV;
222 if (flags & RPMVERIFY_CAPS) {
224 * Empty capability set ("=") is not exactly the same as no
225 * capabilities at all but suffices for now...
228 cap = cap_from_text(rpmfilesFCaps(fi, ix));
230 cap = cap_from_text("=");
232 fcap = cap_get_file(fn);
234 fcap = cap_from_text("=");
237 if (cap_compare(cap, fcap) != 0)
238 vfy |= RPMVERIFY_CAPS;
245 if ((flags & RPMVERIFY_MTIME) && (sb.st_mtime != rpmfilesFMtime(fi, ix))) {
246 vfy |= RPMVERIFY_MTIME;
249 if (flags & RPMVERIFY_USER) {
250 const char * name = rpmugUname(sb.st_uid);
251 const char * fuser = rpmfilesFUser(fi, ix);
257 namematch = rstreq(name, fuser);
258 if (fuser && rpmugUid(fuser, &uid) == 0)
259 idmatch = (uid == sb.st_uid);
261 if (namematch != idmatch) {
262 rpmlog(RPMLOG_WARNING,
263 _("Duplicate username or UID for user %s\n"), fuser);
266 if (!(namematch || idmatch))
267 vfy |= RPMVERIFY_USER;
270 if (flags & RPMVERIFY_GROUP) {
271 const char * name = rpmugGname(sb.st_gid);
272 const char * fgroup = rpmfilesFGroup(fi, ix);
278 namematch = rstreq(name, fgroup);
279 if (fgroup && rpmugGid(fgroup, &gid) == 0)
280 idmatch = (gid == sb.st_gid);
282 if (namematch != idmatch) {
283 rpmlog(RPMLOG_WARNING,
284 _("Duplicate groupname or GID for group %s\n"), fgroup);
287 if (!(namematch || idmatch))
288 vfy |= RPMVERIFY_GROUP;
295 int rpmVerifyFile(const rpmts ts, const rpmfi fi,
296 rpmVerifyAttrs * res, rpmVerifyAttrs omitMask)
298 rpmVerifyAttrs vfy = rpmfiVerify(fi, omitMask);
302 return (vfy & RPMVERIFY_LSTATFAIL) ? 1 : 0;
306 * Return exit code from running verify script from header.
307 * @param ts transaction set
309 * @return 0 on success
311 static int rpmVerifyScript(rpmts ts, Header h)
315 if (headerIsEntry(h, RPMTAG_VERIFYSCRIPT)) {
316 /* fake up a erasure transaction element */
317 rpmte p = rpmteNew(ts, h, TR_REMOVED, NULL, NULL);
320 rpmteSetHeader(p, h);
322 rc = (rpmpsmRun(ts, p, PKG_VERIFY) != RPMRC_OK);
324 /* clean up our fake transaction bits */
335 #define _verify(_RPMVERIFY_F, _C, _pad) \
336 ((verifyResult & _RPMVERIFY_F) ? _C : _pad)
337 #define _verifylink(_RPMVERIFY_F, _C, _pad) \
338 ((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
339 (verifyResult & _RPMVERIFY_F) ? _C : _pad)
340 #define _verifyfile(_RPMVERIFY_F, _C, _pad) \
341 ((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
342 (verifyResult & _RPMVERIFY_F) ? _C : _pad)
343 char * rpmVerifyString(uint32_t verifyResult, const char *pad)
346 rasprintf(&fmt, "%s%s%s%s%s%s%s%s%s",
347 _verify(RPMVERIFY_FILESIZE, "S", pad),
348 _verify(RPMVERIFY_MODE, "M", pad),
349 _verifyfile(RPMVERIFY_FILEDIGEST, "5", pad),
350 _verify(RPMVERIFY_RDEV, "D", pad),
351 _verifylink(RPMVERIFY_LINKTO, "L", pad),
352 _verify(RPMVERIFY_USER, "U", pad),
353 _verify(RPMVERIFY_GROUP, "G", pad),
354 _verify(RPMVERIFY_MTIME, "T", pad),
355 _verify(RPMVERIFY_CAPS, "P", pad));
365 char * rpmFFlagsString(uint32_t fflags, const char *pad)
368 rasprintf(&fmt, "%s%s%s%s%s%s%s%s%s",
369 (fflags & RPMFILE_DOC) ? "d" : pad,
370 (fflags & RPMFILE_CONFIG) ? "c" : pad,
371 (fflags & RPMFILE_SPECFILE) ? "s" : pad,
372 (fflags & RPMFILE_MISSINGOK) ? "m" : pad,
373 (fflags & RPMFILE_NOREPLACE) ? "n" : pad,
374 (fflags & RPMFILE_GHOST) ? "g" : pad,
375 (fflags & RPMFILE_LICENSE) ? "l" : pad,
376 (fflags & RPMFILE_README) ? "r" : pad,
377 (fflags & RPMFILE_ARTIFACT) ? "a" : pad);
381 static const char * stateStr(rpmfileState fstate)
384 case RPMFILE_STATE_NORMAL:
386 case RPMFILE_STATE_NOTINSTALLED:
387 return rpmIsVerbose() ? _("not installed") : NULL;
388 case RPMFILE_STATE_NETSHARED:
389 return rpmIsVerbose() ? _("net shared") : NULL;
390 case RPMFILE_STATE_WRONGCOLOR:
391 return rpmIsVerbose() ? _("wrong color") : NULL;
392 case RPMFILE_STATE_REPLACED:
393 return _("replaced");
394 case RPMFILE_STATE_MISSING:
395 return _("no state");
397 return _("unknown state");
401 * Check file info from header against what's actually installed.
402 * @param ts transaction set
403 * @param h header to verify
404 * @param omitMask bits to disable verify checks
405 * @param skipAttr skip files with these attrs (eg %ghost)
406 * @return 0 no problems, 1 problems found
408 static int verifyHeader(rpmts ts, Header h, rpmVerifyAttrs omitMask,
409 rpmfileAttrs skipAttrs)
411 rpmVerifyAttrs verifyResult = 0;
412 rpmVerifyAttrs verifyAll = 0; /* assume no problems */
413 rpmfi fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_FLAGS_VERIFY);
419 while (rpmfiNext(fi) >= 0) {
420 rpmfileAttrs fileAttrs = rpmfiFFlags(fi);
421 char *buf = NULL, *attrFormat;
422 const char *fstate = NULL;
425 /* Skip on attributes (eg from --noghost) */
426 if (skipAttrs & fileAttrs)
429 verifyResult = rpmfiVerify(fi, omitMask);
431 /* Filter out timestamp differences of shared files */
432 if (verifyResult & RPMVERIFY_MTIME) {
433 rpmdbMatchIterator mi;
434 mi = rpmtsInitIterator(ts, RPMDBI_BASENAMES, rpmfiFN(fi), 0);
435 if (rpmdbGetIteratorCount(mi) > 1)
436 verifyResult &= ~RPMVERIFY_MTIME;
437 rpmdbFreeIterator(mi);
440 /* State is only meaningful for installed packages */
441 if (headerGetInstance(h))
442 fstate = stateStr(rpmfiFState(fi));
444 attrFormat = rpmFFlagsString(fileAttrs, "");
445 ac = rstreq(attrFormat, "") ? ' ' : attrFormat[0];
446 if (verifyResult & RPMVERIFY_LSTATFAIL) {
447 if (!(fileAttrs & (RPMFILE_MISSINGOK|RPMFILE_GHOST)) || rpmIsVerbose()) {
448 rasprintf(&buf, _("missing %c %s"), ac, rpmfiFN(fi));
449 if ((verifyResult & RPMVERIFY_LSTATFAIL) != 0 &&
452 rasprintf(&app, " (%s)", strerror(errno));
457 } else if (verifyResult || fstate || rpmIsVerbose()) {
458 char *verifyFormat = rpmVerifyString(verifyResult, ".");
459 rasprintf(&buf, "%s %c %s", verifyFormat, ac, rpmfiFN(fi));
466 buf = rstrscat(&buf, " (", fstate, ")", NULL);
467 rpmlog(RPMLOG_NOTICE, "%s\n", buf);
471 /* Filter out missing %ghost/%missingok errors from final result */
472 if (fileAttrs & (RPMFILE_MISSINGOK|RPMFILE_GHOST))
473 verifyResult &= ~RPMVERIFY_LSTATFAIL;
475 verifyAll |= verifyResult;
479 return (verifyAll != 0) ? 1 : 0;
483 * Check installed package dependencies for problems.
484 * @param ts transaction set
486 * @return number of problems found (0 for no problems)
488 static int verifyDependencies(rpmts ts, Header h)
495 (void) rpmtsAddInstallElement(ts, h, NULL, 0, NULL);
497 (void) rpmtsCheck(ts);
498 te = rpmtsElement(ts, 0);
499 ps = rpmteProblems(te);
500 rc = rpmpsNumProblems(ps);
503 rpmlog(RPMLOG_NOTICE, _("Unsatisfied dependencies for %s:\n"),
505 rpmpsi psi = rpmpsInitIterator(ps);
508 while ((p = rpmpsiNext(psi)) != NULL) {
509 char * ps = rpmProblemString(p);
510 rpmlog(RPMLOG_NOTICE, "\t%s\n", ps);
513 rpmpsFreeIterator(psi);
521 int showVerifyPackage(QVA_t qva, rpmts ts, Header h)
523 rpmVerifyAttrs omitMask = ((qva->qva_flags & VERIFY_ATTRS) ^ VERIFY_ATTRS);
527 if (qva->qva_flags & VERIFY_DEPS) {
528 if ((rc = verifyDependencies(ts, h)) != 0)
531 if (qva->qva_flags & VERIFY_FILES) {
532 if ((rc = verifyHeader(ts, h, omitMask, qva->qva_fflags)) != 0)
535 if (qva->qva_flags & VERIFY_SCRIPT) {
536 if ((rc = rpmVerifyScript(ts, h)) != 0)
543 int rpmcliVerify(rpmts ts, QVA_t qva, char * const * argv)
545 rpmVSFlags vsflags, ovsflags;
547 FD_t scriptFd = fdDup(STDOUT_FILENO);
550 * Open the DB + indices explicitly before possible chroot,
551 * otherwises BDB is going to be unhappy...
553 rpmtsOpenDB(ts, O_RDONLY);
554 rpmdbOpenAll(rpmtsGetRdb(ts));
555 if (rpmChrootSet(rpmtsRootDir(ts)) || rpmChrootIn()) {
560 if (qva->qva_showPackage == NULL)
561 qva->qva_showPackage = showVerifyPackage;
563 vsflags = rpmExpandNumeric("%{?_vsflags_verify}");
564 if (rpmcliQueryFlags & VERIFY_DIGEST)
565 vsflags |= _RPMVSF_NODIGESTS;
566 if (rpmcliQueryFlags & VERIFY_SIGNATURE)
567 vsflags |= _RPMVSF_NOSIGNATURES;
568 if (rpmcliQueryFlags & VERIFY_HDRCHK)
569 vsflags |= RPMVSF_NOHDRCHK;
570 vsflags &= ~RPMVSF_NEEDPAYLOAD;
572 rpmtsSetScriptFd(ts, scriptFd);
573 ovsflags = rpmtsSetVSFlags(ts, vsflags);
574 ec = rpmcliArgIter(ts, qva, argv);
575 rpmtsSetVSFlags(ts, ovsflags);
576 rpmtsSetScriptFd(ts, NULL);
578 if (qva->qva_showPackage == showVerifyPackage)
579 qva->qva_showPackage = NULL;
583 if (rpmChrootOut() || rpmChrootSet(NULL))