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 "rpmio/ugid.h" /* uidToUname(), gnameToGid */
21 #define S_ISDEV(m) (S_ISBLK((m)) || S_ISCHR((m)))
23 extern int _rpmds_unspecified_epoch_noise;
25 int rpmVerifyFile(const rpmts ts, const rpmfi fi,
26 rpmVerifyAttrs * res, rpmVerifyAttrs omitMask)
28 rpm_mode_t fmode = rpmfiFMode(fi);
29 rpmfileAttrs fileAttrs = rpmfiFFlags(fi);
30 rpmVerifyAttrs flags = rpmfiVFlags(fi);
31 const char * fn = rpmfiFN(fi);
35 *res = RPMVERIFY_NONE;
38 * Check to see if the file was installed - if not pretend all is OK.
40 switch (rpmfiFState(fi)) {
41 case RPMFILE_STATE_NETSHARED:
42 case RPMFILE_STATE_REPLACED:
43 case RPMFILE_STATE_NOTINSTALLED:
44 case RPMFILE_STATE_WRONGCOLOR:
47 case RPMFILE_STATE_NORMAL:
51 if (fn == NULL || lstat(fn, &sb) != 0) {
52 *res |= RPMVERIFY_LSTATFAIL;
57 * Not all attributes of non-regular files can be verified.
59 if (S_ISDIR(sb.st_mode))
60 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
62 else if (S_ISLNK(sb.st_mode)) {
63 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
65 #if CHOWN_FOLLOWS_SYMLINK
66 flags &= ~(RPMVERIFY_USER | RPMVERIFY_GROUP);
69 else if (S_ISFIFO(sb.st_mode))
70 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
72 else if (S_ISCHR(sb.st_mode))
73 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
75 else if (S_ISBLK(sb.st_mode))
76 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
79 flags &= ~(RPMVERIFY_LINKTO);
82 * Content checks of %ghost files are meaningless.
84 if (fileAttrs & RPMFILE_GHOST)
85 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
89 * Don't verify any features in omitMask.
91 flags &= ~(omitMask | RPMVERIFY_FAILURES);
94 if (flags & RPMVERIFY_MD5) {
95 const unsigned char *digest;
99 /* XXX If --nomd5, then prelinked library sizes are not corrected. */
100 if ((digest = rpmfiFDigest(fi, &algo, &diglen))) {
101 unsigned char fdigest[diglen];
104 rc = rpmDoDigest(algo, fn, 0, fdigest, &fsize);
107 *res |= (RPMVERIFY_READFAIL|RPMVERIFY_MD5);
108 } else if (memcmp(fdigest, digest, diglen)) {
109 *res |= RPMVERIFY_MD5;
112 *res |= RPMVERIFY_MD5;
116 if (flags & RPMVERIFY_LINKTO) {
120 if ((size = readlink(fn, linkto, sizeof(linkto)-1)) == -1)
121 *res |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO);
123 const char * flink = rpmfiFLink(fi);
125 if (flink == NULL || strcmp(linkto, flink))
126 *res |= RPMVERIFY_LINKTO;
130 if (flags & RPMVERIFY_FILESIZE) {
131 if (sb.st_size != rpmfiFSize(fi))
132 *res |= RPMVERIFY_FILESIZE;
135 if (flags & RPMVERIFY_MODE) {
136 rpm_mode_t metamode = fmode;
140 * Platforms (like AIX) where sizeof(rpm_mode_t) != sizeof(mode_t)
141 * need the (rpm_mode_t) cast here.
143 filemode = (rpm_mode_t)sb.st_mode;
146 * Comparing the type of %ghost files is meaningless, but perms are OK.
148 if (fileAttrs & RPMFILE_GHOST) {
153 if (metamode != filemode)
154 *res |= RPMVERIFY_MODE;
157 if (flags & RPMVERIFY_RDEV) {
158 if (S_ISCHR(fmode) != S_ISCHR(sb.st_mode)
159 || S_ISBLK(fmode) != S_ISBLK(sb.st_mode))
161 *res |= RPMVERIFY_RDEV;
162 } else if (S_ISDEV(fmode) && S_ISDEV(sb.st_mode)) {
163 rpm_rdev_t st_rdev = (sb.st_rdev & 0xffff);
164 rpm_rdev_t frdev = (rpmfiFRdev(fi) & 0xffff);
165 if (st_rdev != frdev)
166 *res |= RPMVERIFY_RDEV;
170 if (flags & RPMVERIFY_MTIME) {
171 if (sb.st_mtime != rpmfiFMtime(fi))
172 *res |= RPMVERIFY_MTIME;
175 if (flags & RPMVERIFY_USER) {
176 const char * name = uidToUname(sb.st_uid);
177 const char * fuser = rpmfiFUser(fi);
178 if (name == NULL || fuser == NULL || strcmp(name, fuser))
179 *res |= RPMVERIFY_USER;
182 if (flags & RPMVERIFY_GROUP) {
183 const char * name = gidToGname(sb.st_gid);
184 const char * fgroup = rpmfiFGroup(fi);
185 if (name == NULL || fgroup == NULL || strcmp(name, fgroup))
186 *res |= RPMVERIFY_GROUP;
193 * Return exit code from running verify script from header.
194 * @todo malloc/free/refcount handling is fishy here.
195 * @param qva parsed query/verify options
196 * @param ts transaction set
197 * @param fi file info set
198 * @param scriptFd file handle to use for stderr (or NULL)
199 * @return 0 on success
201 static int rpmVerifyScript(QVA_t qva, rpmts ts,
202 rpmfi fi, FD_t scriptFd)
204 rpmpsm psm = rpmpsmNew(ts, NULL, fi);
207 if (psm == NULL) /* XXX can't happen */
210 if (scriptFd != NULL)
211 rpmtsSetScriptFd(rpmpsmGetTs(psm), scriptFd);
213 rc = rpmpsmScriptStage(psm, RPMTAG_VERIFYSCRIPT, RPMTAG_VERIFYSCRIPTPROG);
214 rc = rpmpsmStage(psm, PSM_SCRIPT);
216 if (scriptFd != NULL)
217 rpmtsSetScriptFd(rpmpsmGetTs(psm), NULL);
219 psm = rpmpsmFree(psm);
225 * Check file info from header against what's actually installed.
226 * @param qva parsed query/verify options
227 * @param ts transaction set
228 * @param fi file info set
229 * @return 0 no problems, 1 problems found
231 static int verifyHeader(QVA_t qva, const rpmts ts, rpmfi fi)
233 rpmVerifyAttrs verifyResult = 0;
235 rpmVerifyAttrs omitMask = ((qva->qva_flags & VERIFY_ATTRS) ^ VERIFY_ATTRS);
236 int ec = 0; /* assume no problems */
240 fi = rpmfiLink(fi, RPMDBG_M("verifyHeader"));
241 fi = rpmfiInit(fi, 0);
242 if (fi != NULL) /* XXX lclint */
243 while ((i = rpmfiNext(fi)) >= 0) {
244 rpmfileAttrs fileAttrs;
247 fileAttrs = rpmfiFFlags(fi);
249 /* If not verifying %ghost, skip ghost files. */
250 if (!(qva->qva_fflags & RPMFILE_GHOST)
251 && (fileAttrs & RPMFILE_GHOST))
254 rc = rpmVerifyFile(ts, fi, &verifyResult, omitMask);
256 if (!(fileAttrs & (RPMFILE_MISSINGOK|RPMFILE_GHOST)) || rpmIsVerbose()) {
257 rasprintf(&buf, _("missing %c %s"),
258 ((fileAttrs & RPMFILE_CONFIG) ? 'c' :
259 (fileAttrs & RPMFILE_DOC) ? 'd' :
260 (fileAttrs & RPMFILE_GHOST) ? 'g' :
261 (fileAttrs & RPMFILE_LICENSE) ? 'l' :
262 (fileAttrs & RPMFILE_PUBKEY) ? 'P' :
263 (fileAttrs & RPMFILE_README) ? 'r' : ' '),
265 if ((verifyResult & RPMVERIFY_LSTATFAIL) != 0 &&
268 rasprintf(&app, " (%s)", strerror(errno));
274 } else if (verifyResult || rpmIsVerbose()) {
275 const char * size, * MD5, * link, * mtime, * mode;
276 const char * group, * user, * rdev;
277 static const char *const aok = ".";
278 static const char *const unknown = "?";
282 #define _verify(_RPMVERIFY_F, _C) \
283 ((verifyResult & _RPMVERIFY_F) ? _C : aok)
284 #define _verifylink(_RPMVERIFY_F, _C) \
285 ((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
286 (verifyResult & _RPMVERIFY_F) ? _C : aok)
287 #define _verifyfile(_RPMVERIFY_F, _C) \
288 ((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
289 (verifyResult & _RPMVERIFY_F) ? _C : aok)
291 MD5 = _verifyfile(RPMVERIFY_MD5, "5");
292 size = _verify(RPMVERIFY_FILESIZE, "S");
293 link = _verifylink(RPMVERIFY_LINKTO, "L");
294 mtime = _verify(RPMVERIFY_MTIME, "T");
295 rdev = _verify(RPMVERIFY_RDEV, "D");
296 user = _verify(RPMVERIFY_USER, "U");
297 group = _verify(RPMVERIFY_GROUP, "G");
298 mode = _verify(RPMVERIFY_MODE, "M");
304 rasprintf(&buf, "%s%s%s%s%s%s%s%s %c %s",
305 size, mode, MD5, rdev, link, user, group, mtime,
306 ((fileAttrs & RPMFILE_CONFIG) ? 'c' :
307 (fileAttrs & RPMFILE_DOC) ? 'd' :
308 (fileAttrs & RPMFILE_GHOST) ? 'g' :
309 (fileAttrs & RPMFILE_LICENSE) ? 'l' :
310 (fileAttrs & RPMFILE_PUBKEY) ? 'P' :
311 (fileAttrs & RPMFILE_README) ? 'r' : ' '),
316 rpmlog(RPMLOG_NOTICE, "%s\n", buf);
320 fi = rpmfiUnlink(fi, RPMDBG_M("verifyHeader"));
326 * Check installed package dependencies for problems.
327 * @param qva parsed query/verify options
328 * @param ts transaction set
330 * @return number of problems found (0 for no problems)
332 static int verifyDependencies(QVA_t qva, rpmts ts,
337 int rc = 0; /* assume no problems */
341 (void) rpmtsAddInstallElement(ts, h, NULL, 0, NULL);
344 ps = rpmtsProblems(ts);
346 psi = rpmpsInitIterator(ps);
347 if (rpmpsNumProblems(ps) > 0) {
348 char *nevra = headerGetNEVRA(h, NULL);
349 rpmlog(RPMLOG_NOTICE, _("Unsatisfied dependencies for %s:\n"), nevra);
351 while (rpmpsNextIterator(psi) >= 0) {
352 rpmProblem p = rpmpsGetProblem(psi);
353 char * ps = rpmProblemString(p);
354 rpmlog(RPMLOG_NOTICE, "\t%s\n", ps);
359 psi = rpmpsFreeIterator(psi);
367 int showVerifyPackage(QVA_t qva, rpmts ts, Header h)
369 int scareMem = 1; /* XXX only rpmVerifyScript needs now */
374 fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem);
377 if (qva->qva_flags & VERIFY_DEPS) {
378 int save_noise = _rpmds_unspecified_epoch_noise;
380 _rpmds_unspecified_epoch_noise = 1;
381 if ((rc = verifyDependencies(qva, ts, h)) != 0)
383 _rpmds_unspecified_epoch_noise = save_noise;
385 if (qva->qva_flags & VERIFY_FILES) {
386 if ((rc = verifyHeader(qva, ts, fi)) != 0)
389 if ((qva->qva_flags & VERIFY_SCRIPT)
390 && headerIsEntry(h, RPMTAG_VERIFYSCRIPT))
392 FD_t fdo = fdDup(STDOUT_FILENO);
393 if ((rc = rpmVerifyScript(qva, ts, fi, fdo)) != 0)
405 int rpmcliVerify(rpmts ts, QVA_t qva, char * const * argv)
407 rpmVSFlags vsflags, ovsflags;
409 const char * rootDir = rpmtsRootDir(ts);
412 * Open the DB + indices explicitly before possible chroot,
413 * otherwises BDB is going to be unhappy...
415 rpmtsOpenDB(ts, O_RDONLY);
416 rpmdbOpenAll(rpmtsGetRdb(ts));
417 if (rootDir && strcmp(rootDir, "/") != 0) {
418 if (chroot(rootDir) == -1) {
419 rpmlog(RPMLOG_ERR, _("Unable to change root directory: %m\n"));
423 rpmtsSetChrootDone(ts, 1);
427 if (qva->qva_showPackage == NULL)
428 qva->qva_showPackage = showVerifyPackage;
430 /* XXX verify flags are inverted from query. */
431 vsflags = rpmExpandNumeric("%{?_vsflags_verify}");
432 if (!(qva->qva_flags & VERIFY_DIGEST))
433 vsflags |= _RPMVSF_NODIGESTS;
434 if (!(qva->qva_flags & VERIFY_SIGNATURE))
435 vsflags |= _RPMVSF_NOSIGNATURES;
436 if (!(qva->qva_flags & VERIFY_HDRCHK))
437 vsflags |= RPMVSF_NOHDRCHK;
438 vsflags &= ~RPMVSF_NEEDPAYLOAD;
440 ovsflags = rpmtsSetVSFlags(ts, vsflags);
441 ec = rpmcliArgIter(ts, qva, argv);
442 vsflags = rpmtsSetVSFlags(ts, ovsflags);
444 if (qva->qva_showPackage == showVerifyPackage)
445 qva->qva_showPackage = NULL;
449 if (rpmtsChrootDone(ts)) {
450 /* only done if previous chroot succeeded, assume success */
452 rpmtsSetChrootDone(ts, 0);