3 * Verify installed payload files from package metadata.
11 #include "legacy.h" /* XXX domd5(), uidToUname(), gnameToGid */
13 #include "misc.h" /* XXX for uidToUname() and gnameToGid() */
16 /*@access rpmProblem @*/
17 /*@access rpmTransactionSet @*/
18 /*@access PSM_t @*/ /* XXX for %verifyscript through psmStage() */
19 /*@access FD_t @*/ /* XXX compared with NULL */
21 #define S_ISDEV(m) (S_ISBLK((m)) || S_ISCHR((m)))
23 int rpmVerifyFile(const rpmTransactionSet ts, const TFI_t fi,
24 rpmVerifyAttrs * result, rpmVerifyAttrs omitMask)
26 unsigned short fmode = tfiGetFMode(fi);
27 rpmfileAttrs fileAttrs = tfiGetFFlags(fi);
28 rpmVerifyAttrs flags = tfiGetVFlags(fi);
29 const char * filespec = tfiGetFN(fi);
33 /* Prepend the path to root (if specified). */
34 if (ts->rootDir && *ts->rootDir != '\0'
35 && !(ts->rootDir[0] == '/' && ts->rootDir[1] == '\0'))
37 int nb = strlen(filespec) + strlen(ts->rootDir) + 1;
38 char * tb = alloca(nb);
43 t = stpcpy(t, ts->rootDir);
44 while (t > tb && t[-1] == '/') {
48 t = stpcpy(t, filespec);
52 *result = RPMVERIFY_NONE;
55 * Check to see if the file was installed - if not pretend all is OK.
57 switch (tfiGetFState(fi)) {
58 case RPMFILE_STATE_NETSHARED:
59 case RPMFILE_STATE_REPLACED:
60 case RPMFILE_STATE_NOTINSTALLED:
62 /*@notreached@*/ break;
63 case RPMFILE_STATE_NORMAL:
67 if (filespec == NULL || Lstat(filespec, &sb) != 0) {
68 *result |= RPMVERIFY_LSTATFAIL;
73 * Not all attributes of non-regular files can be verified.
75 if (S_ISDIR(sb.st_mode))
76 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
78 else if (S_ISLNK(sb.st_mode)) {
79 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
81 #if CHOWN_FOLLOWS_SYMLINK
82 flags &= ~(RPMVERIFY_USER | RPMVERIFY_GROUP);
85 else if (S_ISFIFO(sb.st_mode))
86 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
88 else if (S_ISCHR(sb.st_mode))
89 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
91 else if (S_ISBLK(sb.st_mode))
92 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
95 flags &= ~(RPMVERIFY_LINKTO);
98 * Content checks of %ghost files are meaningless.
100 if (fileAttrs & RPMFILE_GHOST)
101 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
105 * Don't verify any features in omitMask.
107 flags &= ~(omitMask | RPMVERIFY_LSTATFAIL|RPMVERIFY_READFAIL|RPMVERIFY_READLINKFAIL);
109 if (flags & RPMVERIFY_MD5) {
112 rc = domd5(filespec, md5sum, 0);
114 *result |= (RPMVERIFY_READFAIL|RPMVERIFY_MD5);
116 const byte * md5 = tfiGetMD5(fi);
117 if (md5 == NULL || memcmp(md5sum, md5, sizeof(md5sum)))
118 *result |= RPMVERIFY_MD5;
122 if (flags & RPMVERIFY_LINKTO) {
126 if ((size = Readlink(filespec, linkto, sizeof(linkto)-1)) == -1)
127 *result |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO);
129 const char * flink = tfiGetFLink(fi);
131 if (flink == NULL || strcmp(linkto, flink))
132 *result |= RPMVERIFY_LINKTO;
136 if (flags & RPMVERIFY_FILESIZE) {
137 if (sb.st_size != tfiGetFSize(fi))
138 *result |= RPMVERIFY_FILESIZE;
141 if (flags & RPMVERIFY_MODE) {
142 unsigned short metamode = fmode;
143 unsigned short filemode;
146 * Platforms (like AIX) where sizeof(unsigned short) != sizeof(mode_t)
147 * need the (unsigned short) cast here.
149 filemode = (unsigned short)sb.st_mode;
152 * Comparing the type of %ghost files is meaningless, but perms are OK.
154 if (fileAttrs & RPMFILE_GHOST) {
159 if (metamode != filemode)
160 *result |= RPMVERIFY_MODE;
163 if (flags & RPMVERIFY_RDEV) {
164 if (S_ISCHR(fmode) != S_ISCHR(sb.st_mode)
165 || S_ISBLK(fmode) != S_ISBLK(sb.st_mode))
167 *result |= RPMVERIFY_RDEV;
168 } else if (S_ISDEV(fmode) && S_ISDEV(sb.st_mode)) {
169 if (sb.st_rdev != tfiGetFRdev(fi))
170 *result |= RPMVERIFY_RDEV;
174 if (flags & RPMVERIFY_MTIME) {
175 if (sb.st_mtime != tfiGetFMtime(fi))
176 *result |= RPMVERIFY_MTIME;
179 if (flags & RPMVERIFY_USER) {
180 const char * name = uidToUname(sb.st_uid);
181 const char * fuser = tfiGetFUser(fi);
182 if (name == NULL || fuser == NULL || strcmp(name, fuser))
183 *result |= RPMVERIFY_USER;
186 if (flags & RPMVERIFY_GROUP) {
187 const char * name = uidToUname(sb.st_gid);
188 const char * fgroup = tfiGetFGroup(fi);
189 if (name == NULL || fgroup == NULL || strcmp(name, fgroup))
190 *result |= RPMVERIFY_GROUP;
197 * Return exit code from running verify script from header.
198 * @todo malloc/free/refcount handling is fishy here.
199 * @param qva parsed query/verify options
200 * @param ts transaction set
201 * @param fi file info set
202 * @param scriptFd file handle to use for stderr (or NULL)
203 * @return 0 on success
205 static int rpmVerifyScript(/*@unused@*/ QVA_t qva, rpmTransactionSet ts,
206 TFI_t fi, /*@null@*/ FD_t scriptFd)
207 /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
208 /*@modifies ts, fi, rpmGlobalMacroContext,
209 fileSystem, internalState @*/
215 psm = memset(alloca(sizeof(*psm)), 0, sizeof(*psm));
216 psm->ts = rpmtsLink(ts, "rpmVerifyScript");
218 if (scriptFd != NULL) {
219 savefd = ts->scriptFd;
220 ts->scriptFd = fdLink(scriptFd, "rpmVerifyScript");
223 psm->fi = rpmfiLink(fi, "rpmVerifyScript");
224 psm->stepName = "verify";
225 psm->scriptTag = RPMTAG_VERIFYSCRIPT;
226 psm->progTag = RPMTAG_VERIFYSCRIPTPROG;
227 rc = psmStage(psm, PSM_SCRIPT);
228 psm->fi = rpmfiUnlink(fi, "rpmVerifyScript");
230 if (scriptFd != NULL) {
231 ts->scriptFd = fdFree(ts->scriptFd, "rpmVerifyScript");
232 ts->scriptFd = savefd;
235 psm->ts = rpmtsUnlink(ts, "rpmVerifyScript");
240 int rpmVerifyDigest(Header h)
242 HGE_t hge = (HGE_t)headerGetEntry; /* XXX headerGetEntryMinMemory? */
243 HFD_t hfd = headerFreeData;
247 const char * hdigest;
249 int ec = 0; /* assume no problems */
251 /* Retrieve header digest. */
252 if (!hge(h, RPMTAG_SHA1HEADER, &hdt, (void **) &hdigest, NULL)
253 &&!hge(h, RPMTAG_SHA1RHN, &hdt, (void **) &hdigest, NULL))
257 /* Regenerate original header. */
258 if (!hge(h, RPMTAG_HEADERIMMUTABLE, &uht, &uh, &uhc))
261 if (hdigest == NULL || uh == NULL)
264 /* Compute header digest. */
265 { DIGEST_CTX ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE);
269 (void) rpmDigestUpdate(ctx, uh, uhc);
270 (void) rpmDigestFinal(ctx, (void **)&digest, &digestlen, 1);
272 /* XXX can't happen: report NULL malloc return as a digest failure. */
273 ec = (digest == NULL || strcmp(hdigest, digest)) ? 1 : 0;
274 digest = _free(digest);
278 hdigest = hfd(hdigest, hdt);
284 * Check file info from header against what's actually installed.
285 * @param qva parsed query/verify options
286 * @param ts transaction set
287 * @param fi file info set
288 * @return 0 no problems, 1 problems found
290 static int verifyHeader(QVA_t qva, const rpmTransactionSet ts, TFI_t fi)
291 /*@globals fileSystem, internalState @*/
292 /*@modifies fi, fileSystem, internalState @*/
296 rpmVerifyAttrs verifyResult = 0;
297 /*@-type@*/ /* FIX: union? */
298 rpmVerifyAttrs omitMask = ((qva->qva_flags & VERIFY_ATTRS) ^ VERIFY_ATTRS);
300 int ec = 0; /* assume no problems */
306 fi = rpmfiLink(fi, "verifyHeader");
308 if (fi != NULL) /* XXX lclint */
309 while ((i = tfiNext(fi)) >= 0) {
310 rpmfileAttrs fileAttrs;
313 fileAttrs = tfiGetFFlags(fi);
315 /* If not verifying %ghost, skip ghost files. */
316 if (!(qva->qva_fflags & RPMFILE_GHOST)
317 && (fileAttrs & RPMFILE_GHOST))
320 rc = rpmVerifyFile(ts, fi, &verifyResult, omitMask);
322 if (!(fileAttrs & RPMFILE_MISSINGOK) || rpmIsVerbose()) {
323 sprintf(te, _("missing %s"), tfiGetFN(fi));
327 } else if (verifyResult) {
328 const char * size, * md5, * link, * mtime, * mode;
329 const char * group, * user, * rdev;
330 /*@observer@*/ static const char *const aok = ".";
331 /*@observer@*/ static const char *const unknown = "?";
335 #define _verify(_RPMVERIFY_F, _C) \
336 ((verifyResult & _RPMVERIFY_F) ? _C : aok)
337 #define _verifylink(_RPMVERIFY_F, _C) \
338 ((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
339 (verifyResult & _RPMVERIFY_F) ? _C : aok)
340 #define _verifyfile(_RPMVERIFY_F, _C) \
341 ((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
342 (verifyResult & _RPMVERIFY_F) ? _C : aok)
344 md5 = _verifyfile(RPMVERIFY_MD5, "5");
345 size = _verify(RPMVERIFY_FILESIZE, "S");
346 link = _verifylink(RPMVERIFY_LINKTO, "L");
347 mtime = _verify(RPMVERIFY_MTIME, "T");
348 rdev = _verify(RPMVERIFY_RDEV, "D");
349 user = _verify(RPMVERIFY_USER, "U");
350 group = _verify(RPMVERIFY_GROUP, "G");
351 mode = _verify(RPMVERIFY_MODE, "M");
357 sprintf(te, "%s%s%s%s%s%s%s%s %c %s",
358 size, mode, md5, rdev, link, user, group, mtime,
359 ((fileAttrs & RPMFILE_CONFIG) ? 'c' :
360 (fileAttrs & RPMFILE_DOC) ? 'd' :
361 (fileAttrs & RPMFILE_GHOST) ? 'g' :
362 (fileAttrs & RPMFILE_LICENSE) ? 'l' :
363 (fileAttrs & RPMFILE_README) ? 'r' : ' '),
371 rpmMessage(RPMMESS_NORMAL, "%s", t);
376 fi = rpmfiUnlink(fi, "verifyHeader");
382 * Check installed package dependencies for problems.
383 * @param qva parsed query/verify options
384 * @param ts transaction set
386 * @return 0 no problems, 1 problems found
388 static int verifyDependencies(/*@unused@*/ QVA_t qva, rpmTransactionSet ts,
390 /*@globals fileSystem, internalState @*/
391 /*@modifies ts, h, fileSystem, internalState @*/
393 rpmProblem conflicts;
395 int rc = 0; /* assume no problems */
399 (void) rpmtransAddPackage(ts, h, NULL, 0, NULL);
401 (void) rpmdepCheck(ts, &conflicts, &numConflicts);
405 const char * pkgNEVR, * altNEVR;
410 for (i = 0; i < numConflicts; i++) {
412 altNEVR = (c->altNEVR ? c->altNEVR : "? ?altNEVR?");
413 nb += strlen(altNEVR+2) + sizeof(", ") - 1;
417 pkgNEVR = (conflicts->pkgNEVR ? conflicts->pkgNEVR : "?pkgNEVR?");
418 sprintf(te, _("Unsatisifed dependencies for %s: "), pkgNEVR);
420 for (i = 0; i < numConflicts; i++) {
422 altNEVR = (c->altNEVR ? c->altNEVR : "? ?altNEVR?");
423 if (i) te = stpcpy(te, ", ");
424 /* XXX FIXME: should probably supply the "[R|C] " type prefix */
425 te = stpcpy(te, altNEVR+2);
427 conflicts = rpmdepFreeConflicts(conflicts, numConflicts);
432 rpmMessage(RPMMESS_NORMAL, "%s", t);
445 int showVerifyPackage(QVA_t qva, rpmTransactionSet ts, Header h)
447 int scareMem = 1; /* XXX only rpmVerifyScript needs now */
453 if (qva->qva_flags & VERIFY_DIGEST) {
454 if ((rc = rpmVerifyDigest(h)) != 0) {
455 const char *n, *v, *r;
456 (void) headerNVR(h, &n, &v, &r);
457 rpmMessage(RPMMESS_NORMAL,
458 _("%s-%s-%s: immutable header region digest check failed\n"),
465 fi = fiNew(ts, NULL, h, RPMTAG_BASENAMES, scareMem);
468 if (qva->qva_flags & VERIFY_DEPS) {
469 if ((rc = verifyDependencies(qva, ts, h)) != 0)
472 if (qva->qva_flags & VERIFY_FILES) {
473 if ((rc = verifyHeader(qva, ts, fi)) != 0)
476 if ((qva->qva_flags & VERIFY_SCRIPT)
477 && headerIsEntry(h, RPMTAG_VERIFYSCRIPT))
479 FD_t fdo = fdDup(STDOUT_FILENO);
480 if ((rc = rpmVerifyScript(qva, ts, fi, fdo)) != 0)
492 int rpmcliVerify(rpmTransactionSet ts, QVA_t qva, const char ** argv)
497 if (qva->qva_showPackage == NULL)
498 qva->qva_showPackage = showVerifyPackage;
500 switch (qva->qva_source) {
503 if (!(qva->qva_flags & VERIFY_DEPS))
508 if (rpmtsOpenDB(ts, O_RDONLY))
509 return 1; /* XXX W2DO? */
513 /* XXX verifyFlags are inverted */
514 ts->nodigests = (qva->qva_flags & VERIFY_DIGEST);
515 ts->nosignatures = (qva->qva_flags & VERIFY_SIGNATURE);
517 if (qva->qva_source == RPMQV_ALL) {
518 /*@-nullpass@*/ /* FIX: argv can be NULL, cast to pass argv array */
519 ec = rpmQueryVerify(qva, ts, (const char *) argv);
523 while ((arg = *argv++) != NULL) {
524 ec += rpmQueryVerify(qva, ts, arg);
529 if (qva->qva_showPackage == showVerifyPackage)
530 qva->qva_showPackage = NULL;