/** \ingroup rpmcli * Verify file attributes (including file digest). * @param vf file data to verify * #param spew should verify results be printed? * @return 0 on success (or not installed), 1 on error */ static int rpmvfVerify(rpmvf vf, int spew) /*@globals h_errno, fileSystem, internalState @*/ /*@modifies vf, fileSystem, internalState @*/ { rpmVerifyAttrs res = RPMVERIFY_NONE; struct stat sb; int ec = 0; /* Check to see if the file was installed - if not pretend all is OK. */ switch (vf->fstate) { default: case RPMFILE_STATE_NETSHARED: case RPMFILE_STATE_REPLACED: case RPMFILE_STATE_NOTINSTALLED: case RPMFILE_STATE_WRONGCOLOR: goto exit; /*@notreached@*/ break; case RPMFILE_STATE_NORMAL: break; } assert(vf->fn != NULL); if (vf->fn == NULL || Lstat(vf->fn, &sb) != 0) { res |= RPMVERIFY_LSTATFAIL; ec = 1; goto exit; } /* Not all attributes of non-regular files can be verified. */ if (S_ISDIR(sb.st_mode)) vf->vflags &= ~(RPMVERIFY_FDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_LINKTO | RPMVERIFY_HMAC); else if (S_ISLNK(sb.st_mode)) { vf->vflags &= ~(RPMVERIFY_FDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_MODE | RPMVERIFY_HMAC); #if CHOWN_FOLLOWS_SYMLINK vf->vflags &= ~(RPMVERIFY_USER | RPMVERIFY_GROUP); #endif } else if (S_ISFIFO(sb.st_mode)) vf->vflags &= ~(RPMVERIFY_FDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_LINKTO | RPMVERIFY_HMAC); else if (S_ISCHR(sb.st_mode)) vf->vflags &= ~(RPMVERIFY_FDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_LINKTO | RPMVERIFY_HMAC); else if (S_ISBLK(sb.st_mode)) vf->vflags &= ~(RPMVERIFY_FDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_LINKTO | RPMVERIFY_HMAC); else vf->vflags &= ~(RPMVERIFY_LINKTO); if (vf->vflags & (RPMVERIFY_FDIGEST | RPMVERIFY_HMAC)) { if (vf->digest == NULL || vf->dlen == 0) res |= RPMVERIFY_FDIGEST; else { /* XXX If --nofdigest, then prelinked library sizes fail to verify. */ unsigned char * fdigest = memset(alloca(vf->dlen), 0, vf->dlen); size_t fsize = 0; #define _mask (RPMVERIFY_FDIGEST|RPMVERIFY_HMAC) unsigned dflags = (vf->vflags & _mask) == RPMVERIFY_HMAC ? 0x2 : 0x0; #undef _mask int rc = dodigest(vf->dalgo, vf->fn, fdigest, dflags, &fsize); sb.st_size = fsize; if (rc) res |= (RPMVERIFY_READFAIL|RPMVERIFY_FDIGEST); else if (memcmp(fdigest, vf->digest, vf->dlen)) res |= RPMVERIFY_FDIGEST; } } if (vf->vflags & RPMVERIFY_LINKTO) { char linkto[1024+1]; int size = 0; if ((size = Readlink(vf->fn, linkto, sizeof(linkto)-1)) == -1) res |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO); else { linkto[size] = '\0'; if (vf->flink == NULL || strcmp(linkto, vf->flink)) res |= RPMVERIFY_LINKTO; } } if (vf->vflags & RPMVERIFY_FILESIZE) { if (sb.st_size != vf->sb.st_size) res |= RPMVERIFY_FILESIZE; } if (vf->vflags & RPMVERIFY_MODE) { /* XXX AIX has sizeof(mode_t) > sizeof(unsigned short) */ unsigned short metamode = (unsigned short)vf->sb.st_mode; unsigned short filemode = (unsigned short)sb.st_mode; /* Comparing type of %ghost files is meaningless, but perms are OK. */ if (vf->fflags & RPMFILE_GHOST) { metamode &= ~0xf000; filemode &= ~0xf000; } if (metamode != filemode) res |= RPMVERIFY_MODE; } if (vf->vflags & RPMVERIFY_RDEV) { if (S_ISCHR(vf->sb.st_mode) != S_ISCHR(sb.st_mode) || S_ISBLK(vf->sb.st_mode) != S_ISBLK(sb.st_mode)) res |= RPMVERIFY_RDEV; else if (S_ISDEV(vf->sb.st_mode) && S_ISDEV(sb.st_mode)) { rpmuint16_t st_rdev = (rpmuint16_t)(sb.st_rdev & 0xffff); rpmuint16_t frdev = (rpmuint16_t)(vf->sb.st_rdev & 0xffff); if (st_rdev != frdev) res |= RPMVERIFY_RDEV; } } if (vf->vflags & RPMVERIFY_MTIME) { if (sb.st_mtime != vf->sb.st_mtime) res |= RPMVERIFY_MTIME; } if (vf->vflags & RPMVERIFY_USER) { const char * fuser = uidToUname(sb.st_uid); if (fuser == NULL || vf->fuser == NULL || strcmp(fuser, vf->fuser)) res |= RPMVERIFY_USER; } if (vf->vflags & RPMVERIFY_GROUP) { const char * fgroup = gidToGname(sb.st_gid); if (fgroup == NULL || vf->fgroup == NULL || strcmp(fgroup, vf->fgroup)) res |= RPMVERIFY_GROUP; } exit: if (spew) { /* XXX no output w verify(...) probe. */ char buf[BUFSIZ]; char * t = buf; char * te = t; *te = '\0'; if (ec) { if (!(vf->fflags & (RPMFILE_MISSINGOK|RPMFILE_GHOST)) || rpmIsVerbose()) { sprintf(te, _("missing %c %s"), ((vf->fflags & RPMFILE_CONFIG) ? 'c' : (vf->fflags & RPMFILE_DOC) ? 'd' : (vf->fflags & RPMFILE_GHOST) ? 'g' : (vf->fflags & RPMFILE_LICENSE) ? 'l' : (vf->fflags & RPMFILE_PUBKEY) ? 'P' : (vf->fflags & RPMFILE_README) ? 'r' : ' '), vf->fn); if ((res & RPMVERIFY_LSTATFAIL) != 0 && errno != ENOENT) { te += strlen(te); sprintf(te, " (%s)", strerror(errno)); } } } else if (res || rpmIsVerbose()) { /*@observer@*/ static const char aok[] = "."; /*@observer@*/ static const char unknown[] = "?"; #define _verify(_RPMVERIFY_F, _C) \ ((res & _RPMVERIFY_F) ? _C : aok) #define _verifylink(_RPMVERIFY_F, _C) \ ((res & RPMVERIFY_READLINKFAIL) ? unknown : \ (res & _RPMVERIFY_F) ? _C : aok) #define _verifyfile(_RPMVERIFY_F, _C) \ ((res & RPMVERIFY_READFAIL) ? unknown : \ (res & _RPMVERIFY_F) ? _C : aok) const char * digest = _verifyfile(RPMVERIFY_FDIGEST, "5"); const char * size = _verify(RPMVERIFY_FILESIZE, "S"); const char * link = _verifylink(RPMVERIFY_LINKTO, "L"); const char * mtime = _verify(RPMVERIFY_MTIME, "T"); const char * rdev = _verify(RPMVERIFY_RDEV, "D"); const char * user = _verify(RPMVERIFY_USER, "U"); const char * group = _verify(RPMVERIFY_GROUP, "G"); const char * mode = _verify(RPMVERIFY_MODE, "M"); #undef _verifyfile #undef _verifylink #undef _verify sprintf(te, "%s%s%s%s%s%s%s%s %c %s", size, mode, digest, rdev, link, user, group, mtime, ((vf->fflags & RPMFILE_CONFIG) ? 'c' : (vf->fflags & RPMFILE_DOC) ? 'd' : (vf->fflags & RPMFILE_GHOST) ? 'g' : (vf->fflags & RPMFILE_LICENSE) ? 'l' : (vf->fflags & RPMFILE_PUBKEY) ? 'P' : (vf->fflags & RPMFILE_README) ? 'r' : ' '), vf->fn); } if (t && *t) rpmlog(RPMLOG_NOTICE, "%s\n", t); } return (res != 0); }
int rpmVerifyFile(const rpmts ts, const rpmfi fi, rpmVerifyAttrs * res, rpmVerifyAttrs omitMask) { rpm_mode_t fmode = rpmfiFMode(fi); rpmfileAttrs fileAttrs = rpmfiFFlags(fi); rpmVerifyAttrs flags = rpmfiVFlags(fi); const char * fn = rpmfiFN(fi); struct stat sb; int rc; *res = RPMVERIFY_NONE; /* * Check to see if the file was installed - if not pretend all is OK. */ switch (rpmfiFState(fi)) { case RPMFILE_STATE_NETSHARED: case RPMFILE_STATE_REPLACED: case RPMFILE_STATE_NOTINSTALLED: case RPMFILE_STATE_WRONGCOLOR: return 0; break; case RPMFILE_STATE_NORMAL: break; } if (fn == NULL || lstat(fn, &sb) != 0) { *res |= RPMVERIFY_LSTATFAIL; return 1; } /* * Not all attributes of non-regular files can be verified. */ if (S_ISDIR(sb.st_mode)) flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_LINKTO | RPMVERIFY_CAPS); else if (S_ISLNK(sb.st_mode)) { flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_MODE | RPMVERIFY_CAPS); #if CHOWN_FOLLOWS_SYMLINK flags &= ~(RPMVERIFY_USER | RPMVERIFY_GROUP); #endif } else if (S_ISFIFO(sb.st_mode)) flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_LINKTO | RPMVERIFY_CAPS); else if (S_ISCHR(sb.st_mode)) flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_LINKTO | RPMVERIFY_CAPS); else if (S_ISBLK(sb.st_mode)) flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_LINKTO | RPMVERIFY_CAPS); else flags &= ~(RPMVERIFY_LINKTO); /* * Content checks of %ghost files are meaningless. */ if (fileAttrs & RPMFILE_GHOST) flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_LINKTO); /* * Don't verify any features in omitMask. */ flags &= ~(omitMask | RPMVERIFY_FAILURES); if (flags & RPMVERIFY_MD5) { const unsigned char *digest; pgpHashAlgo algo; size_t diglen; /* XXX If --nomd5, then prelinked library sizes are not corrected. */ if ((digest = rpmfiFDigest(fi, &algo, &diglen))) { unsigned char fdigest[diglen]; rpm_loff_t fsize; rc = rpmDoDigest(algo, fn, 0, fdigest, &fsize); sb.st_size = fsize; if (rc) { *res |= (RPMVERIFY_READFAIL|RPMVERIFY_MD5); } else if (memcmp(fdigest, digest, diglen)) { *res |= RPMVERIFY_MD5; } } else { *res |= RPMVERIFY_MD5; } } if (flags & RPMVERIFY_LINKTO) { char linkto[1024+1]; int size = 0; if ((size = readlink(fn, linkto, sizeof(linkto)-1)) == -1) *res |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO); else { const char * flink = rpmfiFLink(fi); linkto[size] = '\0'; if (flink == NULL || strcmp(linkto, flink)) *res |= RPMVERIFY_LINKTO; } } if (flags & RPMVERIFY_FILESIZE) { if (sb.st_size != rpmfiFSize(fi)) *res |= RPMVERIFY_FILESIZE; } if (flags & RPMVERIFY_MODE) { rpm_mode_t metamode = fmode; rpm_mode_t filemode; /* * Platforms (like AIX) where sizeof(rpm_mode_t) != sizeof(mode_t) * need the (rpm_mode_t) cast here. */ filemode = (rpm_mode_t)sb.st_mode; /* * Comparing the type of %ghost files is meaningless, but perms are OK. */ if (fileAttrs & RPMFILE_GHOST) { metamode &= ~0xf000; filemode &= ~0xf000; } if (metamode != filemode) *res |= RPMVERIFY_MODE; #if WITH_ACL /* * For now, any non-default acl's on a file is a difference as rpm * cannot have set them. */ acl_t facl = acl_get_file(fn, ACL_TYPE_ACCESS); if (facl) { if (acl_equiv_mode(facl, NULL) == 1) { *res |= RPMVERIFY_MODE; } acl_free(facl); } #endif } if (flags & RPMVERIFY_RDEV) { if (S_ISCHR(fmode) != S_ISCHR(sb.st_mode) || S_ISBLK(fmode) != S_ISBLK(sb.st_mode)) { *res |= RPMVERIFY_RDEV; } else if (S_ISDEV(fmode) && S_ISDEV(sb.st_mode)) { rpm_rdev_t st_rdev = (sb.st_rdev & 0xffff); rpm_rdev_t frdev = (rpmfiFRdev(fi) & 0xffff); if (st_rdev != frdev) *res |= RPMVERIFY_RDEV; } } #if WITH_CAP if (flags & RPMVERIFY_CAPS) { /* * Empty capability set ("=") is not exactly the same as no * capabilities at all but suffices for now... */ cap_t cap, fcap; cap = cap_from_text(rpmfiFCaps(fi)); if (!cap) { cap = cap_from_text("="); } fcap = cap_get_file(fn); if (!fcap) { fcap = cap_from_text("="); } if (cap_compare(cap, fcap) != 0) *res |= RPMVERIFY_CAPS; cap_free(fcap); cap_free(cap); } #endif if ((flags & RPMVERIFY_MTIME) && (sb.st_mtime != rpmfiFMtime(fi))) { /* Filter out timestamp differences of shared files */ rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, fn, 0); if (rpmdbGetIteratorCount(mi) < 2) *res |= RPMVERIFY_MTIME; rpmdbFreeIterator(mi); } if (flags & RPMVERIFY_USER) { const char * name = uidToUname(sb.st_uid); const char * fuser = rpmfiFUser(fi); if (name == NULL || fuser == NULL || strcmp(name, fuser)) *res |= RPMVERIFY_USER; } if (flags & RPMVERIFY_GROUP) { const char * name = gidToGname(sb.st_gid); const char * fgroup = rpmfiFGroup(fi); if (name == NULL || fgroup == NULL || strcmp(name, fgroup)) *res |= RPMVERIFY_GROUP; } return 0; }