3 * Verify installed payload files from package metadata.
8 #include <rpm/rpmcli.h>
9 #include <rpm/header.h>
10 #include <rpm/rpmlog.h>
11 #include <rpm/rpmfi.h>
12 #include <rpm/rpmts.h>
13 #include <rpm/rpmdb.h>
14 #include <rpm/rpmfileutil.h>
17 #include "lib/misc.h" /* uidToUname(), gnameToGid */
18 #include "lib/rpmte_internal.h" /* rpmteOpen(), rpmteClose() */
22 #define S_ISDEV(m) (S_ISBLK((m)) || S_ISCHR((m)))
24 extern int _rpmds_unspecified_epoch_noise;
25 extern int _cacheDependsRC;
27 /* If cap_compare() (Linux extension) not available, do it the hard way */
28 #if WITH_CAP && !defined(HAVE_CAP_COMPARE)
29 static int cap_compare(cap_t acap, cap_t bcap)
32 size_t asize = cap_size(acap);
33 size_t bsize = cap_size(bcap);
38 char *abuf = xcalloc(asize, sizeof(*abuf));
39 char *bbuf = xcalloc(bsize, sizeof(*bbuf));
40 cap_copy_ext(abuf, acap, asize);
41 cap_copy_ext(bbuf, bcap, bsize);
42 rc = memcmp(abuf, bbuf, asize);
50 int rpmVerifyFile(const rpmts ts, const rpmfi fi,
51 rpmVerifyAttrs * res, rpmVerifyAttrs omitMask)
53 rpm_mode_t fmode = rpmfiFMode(fi);
54 rpmfileAttrs fileAttrs = rpmfiFFlags(fi);
55 rpmVerifyAttrs flags = rpmfiVFlags(fi);
56 const char * fn = rpmfiFN(fi);
60 *res = RPMVERIFY_NONE;
63 * Check to see if the file was installed - if not pretend all is OK.
65 switch (rpmfiFState(fi)) {
66 case RPMFILE_STATE_NETSHARED:
67 case RPMFILE_STATE_REPLACED:
68 case RPMFILE_STATE_NOTINSTALLED:
69 case RPMFILE_STATE_WRONGCOLOR:
72 case RPMFILE_STATE_NORMAL:
76 if (fn == NULL || lstat(fn, &sb) != 0) {
77 *res |= RPMVERIFY_LSTATFAIL;
82 * Not all attributes of non-regular files can be verified.
84 if (S_ISDIR(sb.st_mode))
85 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
86 RPMVERIFY_LINKTO | RPMVERIFY_CAPS);
87 else if (S_ISLNK(sb.st_mode)) {
88 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
89 RPMVERIFY_MODE | RPMVERIFY_CAPS);
90 #if CHOWN_FOLLOWS_SYMLINK
91 flags &= ~(RPMVERIFY_USER | RPMVERIFY_GROUP);
94 else if (S_ISFIFO(sb.st_mode))
95 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
96 RPMVERIFY_LINKTO | RPMVERIFY_CAPS);
97 else if (S_ISCHR(sb.st_mode))
98 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
99 RPMVERIFY_LINKTO | RPMVERIFY_CAPS);
100 else if (S_ISBLK(sb.st_mode))
101 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
102 RPMVERIFY_LINKTO | RPMVERIFY_CAPS);
104 flags &= ~(RPMVERIFY_LINKTO);
107 * Content checks of %ghost files are meaningless.
109 if (fileAttrs & RPMFILE_GHOST)
110 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
114 * Don't verify any features in omitMask.
116 flags &= ~(omitMask | RPMVERIFY_FAILURES);
119 if (flags & RPMVERIFY_FILEDIGEST) {
120 const unsigned char *digest;
124 /* XXX If --nomd5, then prelinked library sizes are not corrected. */
125 if ((digest = rpmfiFDigest(fi, &algo, &diglen))) {
126 unsigned char fdigest[diglen];
129 rc = rpmDoDigest(algo, fn, 0, fdigest, &fsize);
132 *res |= (RPMVERIFY_READFAIL|RPMVERIFY_FILEDIGEST);
133 } else if (memcmp(fdigest, digest, diglen)) {
134 *res |= RPMVERIFY_FILEDIGEST;
137 *res |= RPMVERIFY_FILEDIGEST;
141 if (flags & RPMVERIFY_LINKTO) {
145 if ((size = readlink(fn, linkto, sizeof(linkto)-1)) == -1)
146 *res |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO);
148 const char * flink = rpmfiFLink(fi);
150 if (flink == NULL || !rstreq(linkto, flink))
151 *res |= RPMVERIFY_LINKTO;
155 if (flags & RPMVERIFY_FILESIZE) {
156 if (sb.st_size != rpmfiFSize(fi))
157 *res |= RPMVERIFY_FILESIZE;
160 if (flags & RPMVERIFY_MODE) {
161 rpm_mode_t metamode = fmode;
165 * Platforms (like AIX) where sizeof(rpm_mode_t) != sizeof(mode_t)
166 * need the (rpm_mode_t) cast here.
168 filemode = (rpm_mode_t)sb.st_mode;
171 * Comparing the type of %ghost files is meaningless, but perms are OK.
173 if (fileAttrs & RPMFILE_GHOST) {
178 if (metamode != filemode)
179 *res |= RPMVERIFY_MODE;
183 * For now, any non-default acl's on a file is a difference as rpm
184 * cannot have set them.
186 acl_t facl = acl_get_file(fn, ACL_TYPE_ACCESS);
188 if (acl_equiv_mode(facl, NULL) == 1) {
189 *res |= RPMVERIFY_MODE;
196 if (flags & RPMVERIFY_RDEV) {
197 if (S_ISCHR(fmode) != S_ISCHR(sb.st_mode)
198 || S_ISBLK(fmode) != S_ISBLK(sb.st_mode))
200 *res |= RPMVERIFY_RDEV;
201 } else if (S_ISDEV(fmode) && S_ISDEV(sb.st_mode)) {
202 rpm_rdev_t st_rdev = (sb.st_rdev & 0xffff);
203 rpm_rdev_t frdev = (rpmfiFRdev(fi) & 0xffff);
204 if (st_rdev != frdev)
205 *res |= RPMVERIFY_RDEV;
210 if (flags & RPMVERIFY_CAPS) {
212 * Empty capability set ("=") is not exactly the same as no
213 * capabilities at all but suffices for now...
216 cap = cap_from_text(rpmfiFCaps(fi));
218 cap = cap_from_text("=");
220 fcap = cap_get_file(fn);
222 fcap = cap_from_text("=");
225 if (cap_compare(cap, fcap) != 0)
226 *res |= RPMVERIFY_CAPS;
233 if ((flags & RPMVERIFY_MTIME) && (sb.st_mtime != rpmfiFMtime(fi))) {
234 /* Filter out timestamp differences of shared files */
235 rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, fn, 0);
236 if (rpmdbGetIteratorCount(mi) < 2)
237 *res |= RPMVERIFY_MTIME;
238 rpmdbFreeIterator(mi);
241 if (flags & RPMVERIFY_USER) {
242 const char * name = uidToUname(sb.st_uid);
243 const char * fuser = rpmfiFUser(fi);
244 if (name == NULL || fuser == NULL || !rstreq(name, fuser))
245 *res |= RPMVERIFY_USER;
248 if (flags & RPMVERIFY_GROUP) {
249 const char * name = gidToGname(sb.st_gid);
250 const char * fgroup = rpmfiFGroup(fi);
251 if (name == NULL || fgroup == NULL || !rstreq(name, fgroup))
252 *res |= RPMVERIFY_GROUP;
259 * Return exit code from running verify script from header.
260 * @todo malloc/free/refcount handling is fishy here.
261 * @param qva parsed query/verify options
262 * @param ts transaction set
264 * @param scriptFd file handle to use for stderr (or NULL)
265 * @return 0 on success
267 static int rpmVerifyScript(QVA_t qva, rpmts ts, Header h, FD_t scriptFd)
273 /* fake up a erasure transaction element */
274 rc = rpmtsAddEraseElement(ts, h, -1);
275 te = rpmtsElement(ts, 0);
276 rpmteOpen(te, ts, 0);
278 if (scriptFd != NULL)
279 rpmtsSetScriptFd(ts, scriptFd);
281 /* create psm to run the script */
282 psm = rpmpsmNew(ts, te);
283 rpmpsmScriptStage(psm, RPMTAG_VERIFYSCRIPT, RPMTAG_VERIFYSCRIPTPROG);
284 rc = rpmpsmStage(psm, PSM_SCRIPT);
285 psm = rpmpsmFree(psm);
287 if (scriptFd != NULL)
288 rpmtsSetScriptFd(ts, NULL);
290 /* clean up our fake transaction bits */
291 rpmteClose(te, ts, 0);
298 * Check file info from header against what's actually installed.
299 * @param qva parsed query/verify options
300 * @param ts transaction set
301 * @param h header to verify
302 * @return 0 no problems, 1 problems found
304 static int verifyHeader(QVA_t qva, const rpmts ts, Header h)
306 rpmVerifyAttrs verifyResult = 0;
308 rpmVerifyAttrs omitMask = ((qva->qva_flags & VERIFY_ATTRS) ^ VERIFY_ATTRS);
309 int ec = 0; /* assume no problems */
312 rpmfi fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_FLAGS_VERIFY);
314 while (rpmfiNext(fi) >= 0) {
315 rpmfileAttrs fileAttrs;
318 fileAttrs = rpmfiFFlags(fi);
320 /* If not verifying %ghost, skip ghost files. */
321 if (!(qva->qva_fflags & RPMFILE_GHOST)
322 && (fileAttrs & RPMFILE_GHOST))
325 rc = rpmVerifyFile(ts, fi, &verifyResult, omitMask);
327 if (!(fileAttrs & (RPMFILE_MISSINGOK|RPMFILE_GHOST)) || rpmIsVerbose()) {
328 rasprintf(&buf, _("missing %c %s"),
329 ((fileAttrs & RPMFILE_CONFIG) ? 'c' :
330 (fileAttrs & RPMFILE_DOC) ? 'd' :
331 (fileAttrs & RPMFILE_GHOST) ? 'g' :
332 (fileAttrs & RPMFILE_LICENSE) ? 'l' :
333 (fileAttrs & RPMFILE_PUBKEY) ? 'P' :
334 (fileAttrs & RPMFILE_README) ? 'r' : ' '),
336 if ((verifyResult & RPMVERIFY_LSTATFAIL) != 0 &&
339 rasprintf(&app, " (%s)", strerror(errno));
345 } else if (verifyResult || rpmIsVerbose()) {
346 const char * size, * filedigest, * link, * mtime, * mode;
347 const char * group, * user, * rdev, *caps;
348 static const char *const aok = ".";
349 static const char *const unknown = "?";
351 ec = (verifyResult != 0);
353 #define _verify(_RPMVERIFY_F, _C) \
354 ((verifyResult & _RPMVERIFY_F) ? _C : aok)
355 #define _verifylink(_RPMVERIFY_F, _C) \
356 ((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
357 (verifyResult & _RPMVERIFY_F) ? _C : aok)
358 #define _verifyfile(_RPMVERIFY_F, _C) \
359 ((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
360 (verifyResult & _RPMVERIFY_F) ? _C : aok)
362 filedigest = _verifyfile(RPMVERIFY_FILEDIGEST, "5");
363 size = _verify(RPMVERIFY_FILESIZE, "S");
364 link = _verifylink(RPMVERIFY_LINKTO, "L");
365 mtime = _verify(RPMVERIFY_MTIME, "T");
366 rdev = _verify(RPMVERIFY_RDEV, "D");
367 user = _verify(RPMVERIFY_USER, "U");
368 group = _verify(RPMVERIFY_GROUP, "G");
369 mode = _verify(RPMVERIFY_MODE, "M");
370 caps = _verify(RPMVERIFY_CAPS, "P");
376 rasprintf(&buf, "%s%s%s%s%s%s%s%s%s %c %s",
377 size, mode, filedigest, rdev, link, user, group, mtime, caps,
378 ((fileAttrs & RPMFILE_CONFIG) ? 'c' :
379 (fileAttrs & RPMFILE_DOC) ? 'd' :
380 (fileAttrs & RPMFILE_GHOST) ? 'g' :
381 (fileAttrs & RPMFILE_LICENSE) ? 'l' :
382 (fileAttrs & RPMFILE_PUBKEY) ? 'P' :
383 (fileAttrs & RPMFILE_README) ? 'r' : ' '),
388 rpmlog(RPMLOG_NOTICE, "%s\n", buf);
398 * Check installed package dependencies for problems.
399 * @param qva parsed query/verify options
400 * @param ts transaction set
402 * @return number of problems found (0 for no problems)
404 static int verifyDependencies(QVA_t qva, rpmts ts,
409 int rc = 0; /* assume no problems */
413 (void) rpmtsAddInstallElement(ts, h, NULL, 0, NULL);
416 ps = rpmtsProblems(ts);
418 psi = rpmpsInitIterator(ps);
419 if (rpmpsNumProblems(ps) > 0) {
420 char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
421 rpmlog(RPMLOG_NOTICE, _("Unsatisfied dependencies for %s:\n"), nevra);
423 while (rpmpsNextIterator(psi) >= 0) {
424 rpmProblem p = rpmpsGetProblem(psi);
425 char * ps = rpmProblemString(p);
426 rpmlog(RPMLOG_NOTICE, "\t%s\n", ps);
431 psi = rpmpsFreeIterator(psi);
439 int showVerifyPackage(QVA_t qva, rpmts ts, Header h)
444 if (qva->qva_flags & VERIFY_DEPS) {
445 if ((rc = verifyDependencies(qva, ts, h)) != 0)
448 if (qva->qva_flags & VERIFY_FILES) {
449 if ((rc = verifyHeader(qva, ts, h)) != 0)
452 if ((qva->qva_flags & VERIFY_SCRIPT)
453 && headerIsEntry(h, RPMTAG_VERIFYSCRIPT))
455 FD_t fdo = fdDup(STDOUT_FILENO);
456 if ((rc = rpmVerifyScript(qva, ts, h, fdo)) != 0)
465 int rpmcliVerify(rpmts ts, QVA_t qva, char * const * argv)
467 rpmVSFlags vsflags, ovsflags;
469 const char * rootDir = rpmtsRootDir(ts);
472 * Open the DB + indices explicitly before possible chroot,
473 * otherwises BDB is going to be unhappy...
475 rpmtsOpenDB(ts, O_RDONLY);
476 rpmdbOpenAll(rpmtsGetRdb(ts));
477 if (rootDir && !rstreq(rootDir, "/")) {
478 if (chroot(rootDir) == -1) {
479 rpmlog(RPMLOG_ERR, _("Unable to change root directory: %m\n"));
483 rpmtsSetChrootDone(ts, 1);
487 if (qva->qva_showPackage == NULL)
488 qva->qva_showPackage = showVerifyPackage;
490 /* XXX verify flags are inverted from query. */
491 vsflags = rpmExpandNumeric("%{?_vsflags_verify}");
492 if (!(qva->qva_flags & VERIFY_DIGEST))
493 vsflags |= _RPMVSF_NODIGESTS;
494 if (!(qva->qva_flags & VERIFY_SIGNATURE))
495 vsflags |= _RPMVSF_NOSIGNATURES;
496 if (!(qva->qva_flags & VERIFY_HDRCHK))
497 vsflags |= RPMVSF_NOHDRCHK;
498 vsflags &= ~RPMVSF_NEEDPAYLOAD;
500 ovsflags = rpmtsSetVSFlags(ts, vsflags);
501 ec = rpmcliArgIter(ts, qva, argv);
502 vsflags = rpmtsSetVSFlags(ts, ovsflags);
504 if (qva->qva_showPackage == showVerifyPackage)
505 qva->qva_showPackage = NULL;
509 if (rpmtsChrootDone(ts)) {
510 /* only done if previous chroot succeeded, assume success */
512 rpmtsSetChrootDone(ts, 0);