382 lines
11 KiB
C
382 lines
11 KiB
C
#include "system.h"
|
|
|
|
#include <rpmlib.h>
|
|
|
|
#include "md5.h"
|
|
#include "misc.h"
|
|
#include "install.h"
|
|
|
|
#include "build/rpmbuild.h"
|
|
#include <rpmurl.h>
|
|
|
|
static int _ie = 0x44332211;
|
|
static union _endian { int i; char b[4]; } *_endian = (union _endian *)&_ie;
|
|
#define IS_BIG_ENDIAN() (_endian->b[0] == '\x44')
|
|
#define IS_LITTLE_ENDIAN() (_endian->b[0] == '\x11')
|
|
|
|
#define S_ISDEV(m) (S_ISBLK((m)) || S_ISCHR((m)))
|
|
|
|
#define POPT_NOFILES 1000
|
|
|
|
/* ========== Verify specific popt args */
|
|
static void verifyArgCallback(poptContext con, enum poptCallbackReason reason,
|
|
const struct poptOption * opt, const char * arg,
|
|
QVA_t *qva)
|
|
{
|
|
switch (opt->val) {
|
|
case POPT_NOFILES: qva->qva_flags |= VERIFY_FILES; break;
|
|
}
|
|
}
|
|
|
|
static int noFiles = 0;
|
|
struct poptOption rpmVerifyPoptTable[] = {
|
|
{ NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA,
|
|
verifyArgCallback, 0, NULL, NULL },
|
|
{ "nofiles", '\0', 0, &noFiles, POPT_NOFILES,
|
|
N_("don't verify files in package"), NULL},
|
|
{ 0, 0, 0, 0, 0, NULL, NULL }
|
|
};
|
|
|
|
/* ======================================================================== */
|
|
/* XXX static */
|
|
int rpmVerifyFile(const char * prefix, Header h, int filenum, int * result,
|
|
int omitMask)
|
|
{
|
|
char ** fileList, ** md5List, ** linktoList;
|
|
int_32 * verifyFlags, flags;
|
|
int_32 * sizeList, * mtimeList;
|
|
unsigned short * modeList, * rdevList;
|
|
char * fileStatesList;
|
|
char * filespec;
|
|
char * name;
|
|
gid_t gid;
|
|
int type, count, rc;
|
|
struct stat sb;
|
|
unsigned char md5sum[40];
|
|
int_32 * uidList, * gidList;
|
|
char linkto[1024];
|
|
int size;
|
|
char ** unameList, ** gnameList;
|
|
int_32 useBrokenMd5;
|
|
|
|
if (IS_BIG_ENDIAN()) { /* XXX was ifdef WORDS_BIGENDIAN */
|
|
int_32 * brokenPtr;
|
|
if (!headerGetEntry(h, RPMTAG_BROKENMD5, NULL, (void **) &brokenPtr, NULL)) {
|
|
char * rpmVersion;
|
|
|
|
if (headerGetEntry(h, RPMTAG_RPMVERSION, NULL, (void **) &rpmVersion,
|
|
NULL)) {
|
|
useBrokenMd5 = ((rpmvercmp(rpmVersion, "2.3.3") >= 0) &&
|
|
(rpmvercmp(rpmVersion, "2.3.8") <= 0));
|
|
} else {
|
|
useBrokenMd5 = 1;
|
|
}
|
|
headerAddEntry(h, RPMTAG_BROKENMD5, RPM_INT32_TYPE, &useBrokenMd5, 1);
|
|
} else {
|
|
useBrokenMd5 = *brokenPtr;
|
|
}
|
|
} else {
|
|
useBrokenMd5 = 0;
|
|
}
|
|
|
|
headerGetEntry(h, RPMTAG_FILEMODES, &type, (void **) &modeList, &count);
|
|
|
|
if (headerGetEntry(h, RPMTAG_FILEVERIFYFLAGS, &type, (void **) &verifyFlags,
|
|
&count)) {
|
|
flags = verifyFlags[filenum];
|
|
} else {
|
|
flags = RPMVERIFY_ALL;
|
|
}
|
|
|
|
headerGetEntry(h, RPMTAG_FILENAMES, &type, (void **) &fileList, &count);
|
|
filespec = alloca(strlen(fileList[filenum]) + strlen(prefix) + 5);
|
|
strcpy(filespec, prefix);
|
|
strcat(filespec, "/");
|
|
strcat(filespec, fileList[filenum]);
|
|
|
|
free(fileList);
|
|
|
|
*result = 0;
|
|
|
|
/* Check to see if the file was installed - if not pretend all is OK */
|
|
if (headerGetEntry(h, RPMTAG_FILESTATES, &type,
|
|
(void **) &fileStatesList, &count) && fileStatesList) {
|
|
if (fileStatesList[filenum] == RPMFILE_STATE_NOTINSTALLED)
|
|
return 0;
|
|
}
|
|
|
|
if (lstat(filespec, &sb)) {
|
|
*result |= RPMVERIFY_LSTATFAIL;
|
|
return 1;
|
|
}
|
|
|
|
if (S_ISDIR(sb.st_mode))
|
|
flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
|
|
RPMVERIFY_LINKTO);
|
|
else if (S_ISLNK(sb.st_mode)) {
|
|
flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
|
|
RPMVERIFY_MODE);
|
|
# 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);
|
|
else if (S_ISCHR(sb.st_mode))
|
|
flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
|
|
RPMVERIFY_LINKTO);
|
|
else if (S_ISBLK(sb.st_mode))
|
|
flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
|
|
RPMVERIFY_LINKTO);
|
|
else
|
|
flags &= ~(RPMVERIFY_LINKTO);
|
|
|
|
/* Don't verify any features in omitMask */
|
|
flags &= ~(omitMask | RPMVERIFY_LSTATFAIL|RPMVERIFY_READFAIL|RPMVERIFY_READLINKFAIL);
|
|
|
|
if (flags & RPMVERIFY_MD5) {
|
|
headerGetEntry(h, RPMTAG_FILEMD5S, &type, (void **) &md5List, &count);
|
|
if (useBrokenMd5) {
|
|
rc = mdfileBroken(filespec, md5sum);
|
|
} else {
|
|
rc = mdfile(filespec, md5sum);
|
|
}
|
|
|
|
if (rc)
|
|
*result |= (RPMVERIFY_READFAIL|RPMVERIFY_MD5);
|
|
else if (strcmp(md5sum, md5List[filenum]))
|
|
*result |= RPMVERIFY_MD5;
|
|
free(md5List);
|
|
}
|
|
if (flags & RPMVERIFY_LINKTO) {
|
|
headerGetEntry(h, RPMTAG_FILELINKTOS, &type, (void **) &linktoList, &count);
|
|
size = readlink(filespec, linkto, sizeof(linkto));
|
|
if (size == -1)
|
|
*result |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO);
|
|
else {
|
|
linkto[size] = '\0';
|
|
if (strcmp(linkto, linktoList[filenum]))
|
|
*result |= RPMVERIFY_LINKTO;
|
|
}
|
|
free(linktoList);
|
|
}
|
|
|
|
if (flags & RPMVERIFY_FILESIZE) {
|
|
headerGetEntry(h, RPMTAG_FILESIZES, &type, (void **) &sizeList, &count);
|
|
if (sizeList[filenum] != sb.st_size)
|
|
*result |= RPMVERIFY_FILESIZE;
|
|
}
|
|
|
|
if (flags & RPMVERIFY_MODE) {
|
|
if (modeList[filenum] != sb.st_mode)
|
|
*result |= RPMVERIFY_MODE;
|
|
}
|
|
|
|
if (flags & RPMVERIFY_RDEV) {
|
|
if (S_ISCHR(modeList[filenum]) != S_ISCHR(sb.st_mode) ||
|
|
S_ISBLK(modeList[filenum]) != S_ISBLK(sb.st_mode)) {
|
|
*result |= RPMVERIFY_RDEV;
|
|
} else if (S_ISDEV(modeList[filenum]) && S_ISDEV(sb.st_mode)) {
|
|
headerGetEntry(h, RPMTAG_FILERDEVS, NULL, (void **) &rdevList,
|
|
NULL);
|
|
if (rdevList[filenum] != sb.st_rdev)
|
|
*result |= RPMVERIFY_RDEV;
|
|
}
|
|
}
|
|
|
|
if (flags & RPMVERIFY_MTIME) {
|
|
headerGetEntry(h, RPMTAG_FILEMTIMES, NULL, (void **) &mtimeList, NULL);
|
|
if (mtimeList[filenum] != sb.st_mtime)
|
|
*result |= RPMVERIFY_MTIME;
|
|
}
|
|
|
|
if (flags & RPMVERIFY_USER) {
|
|
if (headerGetEntry(h, RPMTAG_FILEUSERNAME, NULL, (void **) &unameList,
|
|
NULL)) {
|
|
name = uidToUname(sb.st_uid);
|
|
if (!name || strcmp(unameList[filenum], name))
|
|
*result |= RPMVERIFY_USER;
|
|
free(unameList);
|
|
} else if (headerGetEntry(h, RPMTAG_FILEUIDS, NULL, (void **) &uidList,
|
|
&count)) {
|
|
if (uidList[filenum] != sb.st_uid)
|
|
*result |= RPMVERIFY_GROUP;
|
|
} else {
|
|
rpmError(RPMERR_INTERNAL, _("package lacks both user name and id "
|
|
"lists (this should never happen)"));
|
|
*result |= RPMVERIFY_GROUP;
|
|
}
|
|
}
|
|
|
|
if (flags & RPMVERIFY_GROUP) {
|
|
if (headerGetEntry(h, RPMTAG_FILEGROUPNAME, NULL, (void **) &gnameList,
|
|
NULL)) {
|
|
rc = gnameToGid(gnameList[filenum],&gid);
|
|
if (rc || (gid != sb.st_gid))
|
|
*result |= RPMVERIFY_GROUP;
|
|
free(gnameList);
|
|
} else if (headerGetEntry(h, RPMTAG_FILEGIDS, NULL, (void **) &gidList,
|
|
&count)) {
|
|
if (gidList[filenum] != sb.st_gid)
|
|
*result |= RPMVERIFY_GROUP;
|
|
} else {
|
|
rpmError(RPMERR_INTERNAL, _("package lacks both group name and id "
|
|
"lists (this should never happen)"));
|
|
*result |= RPMVERIFY_GROUP;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* XXX static */
|
|
int rpmVerifyScript(const char * root, Header h, FD_t err)
|
|
{
|
|
return runInstScript(root, h, RPMTAG_VERIFYSCRIPT, RPMTAG_VERIFYSCRIPTPROG,
|
|
0, 0, err);
|
|
}
|
|
|
|
/* ======================================================================== */
|
|
static int verifyHeader(QVA_t *qva, Header h)
|
|
{
|
|
const char ** fileList;
|
|
int count, type;
|
|
int verifyResult;
|
|
int i, ec, rc;
|
|
int_32 * fileFlagsList;
|
|
int omitMask = 0;
|
|
|
|
ec = 0;
|
|
if (!(qva->qva_flags & VERIFY_MD5)) omitMask = RPMVERIFY_MD5;
|
|
|
|
if (headerGetEntry(h, RPMTAG_FILEFLAGS, NULL, (void **) &fileFlagsList, NULL) &&
|
|
headerGetEntry(h, RPMTAG_FILENAMES, &type, (void **) &fileList, &count)) {
|
|
for (i = 0; i < count; i++) {
|
|
if ((rc = rpmVerifyFile(qva->qva_prefix, h, i, &verifyResult, omitMask)) != 0) {
|
|
fprintf(stdout, _("missing %s\n"), fileList[i]);
|
|
} else {
|
|
const char * size, * md5, * link, * mtime, * mode;
|
|
const char * group, * user, * rdev;
|
|
static const char * aok = ".";
|
|
static const char * unknown = "?";
|
|
|
|
if (!verifyResult) continue;
|
|
|
|
rc = 1;
|
|
|
|
#define _verify(_RPMVERIFY_F, _C) \
|
|
((verifyResult & _RPMVERIFY_F) ? _C : aok)
|
|
#define _verifylink(_RPMVERIFY_F, _C) \
|
|
((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
|
|
(verifyResult & _RPMVERIFY_F) ? _C : aok)
|
|
#define _verifyfile(_RPMVERIFY_F, _C) \
|
|
((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
|
|
(verifyResult & _RPMVERIFY_F) ? _C : aok)
|
|
|
|
md5 = _verifyfile(RPMVERIFY_MD5, "5");
|
|
size = _verify(RPMVERIFY_FILESIZE, "S");
|
|
link = _verifylink(RPMVERIFY_LINKTO, "L");
|
|
mtime = _verify(RPMVERIFY_MTIME, "T");
|
|
rdev = _verify(RPMVERIFY_RDEV, "D");
|
|
user = _verify(RPMVERIFY_USER, "U");
|
|
group = _verify(RPMVERIFY_GROUP, "G");
|
|
mode = _verify(RPMVERIFY_MODE, "M");
|
|
|
|
#undef _verify
|
|
#undef _verifylink
|
|
#undef _verifyfile
|
|
|
|
fprintf(stdout, "%s%s%s%s%s%s%s%s %c %s\n",
|
|
size, mode, md5, rdev, link, user, group, mtime,
|
|
fileFlagsList[i] & RPMFILE_CONFIG ? 'c' : ' ',
|
|
fileList[i]);
|
|
}
|
|
if (rc)
|
|
ec = rc;
|
|
}
|
|
|
|
free(fileList);
|
|
}
|
|
return ec;
|
|
}
|
|
|
|
static int verifyDependencies(rpmdb db, Header h) {
|
|
rpmTransactionSet rpmdep;
|
|
struct rpmDependencyConflict * conflicts;
|
|
int numConflicts;
|
|
const char * name, * version, * release;
|
|
int type, count, i;
|
|
|
|
rpmdep = rpmtransCreateSet(db, NULL);
|
|
rpmtransAddPackage(rpmdep, h, NULL, NULL, 0, NULL);
|
|
|
|
rpmdepCheck(rpmdep, &conflicts, &numConflicts);
|
|
rpmtransFree(rpmdep);
|
|
|
|
if (numConflicts) {
|
|
headerGetEntry(h, RPMTAG_NAME, &type, (void **) &name, &count);
|
|
headerGetEntry(h, RPMTAG_VERSION, &type, (void **) &version, &count);
|
|
headerGetEntry(h, RPMTAG_RELEASE, &type, (void **) &release, &count);
|
|
fprintf(stdout, _("Unsatisfied dependencies for %s-%s-%s: "),
|
|
name, version, release);
|
|
for (i = 0; i < numConflicts; i++) {
|
|
if (i) fprintf(stdout, ", ");
|
|
fprintf(stdout, "%s", conflicts[i].needsName);
|
|
if (conflicts[i].needsFlags) {
|
|
printDepFlags(stdout, conflicts[i].needsVersion,
|
|
conflicts[i].needsFlags);
|
|
}
|
|
}
|
|
fprintf(stdout, "\n");
|
|
rpmdepFreeConflicts(conflicts, numConflicts);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int showVerifyPackage(QVA_t *qva, rpmdb db, Header h)
|
|
{
|
|
int ec, rc;
|
|
FD_t fdo;
|
|
ec = 0;
|
|
if ((qva->qva_flags & VERIFY_DEPS) &&
|
|
(rc = verifyDependencies(db, h)) != 0)
|
|
ec = rc;
|
|
if ((qva->qva_flags & VERIFY_FILES) &&
|
|
(rc = verifyHeader(qva, h)) != 0)
|
|
ec = rc;;
|
|
fdo = fdDup(STDOUT_FILENO);
|
|
if ((qva->qva_flags & VERIFY_SCRIPT) &&
|
|
(rc = rpmVerifyScript(qva->qva_prefix, h, fdo)) != 0)
|
|
ec = rc;
|
|
fdClose(fdo);
|
|
return ec;
|
|
}
|
|
|
|
int rpmVerify(QVA_t *qva, enum rpmQVSources source, const char *arg)
|
|
{
|
|
rpmdb db = NULL;
|
|
int rc;
|
|
|
|
switch (source) {
|
|
case RPMQV_RPM:
|
|
if (!(qva->qva_flags & VERIFY_DEPS))
|
|
break;
|
|
/* fall thru */
|
|
default:
|
|
if (rpmdbOpen(qva->qva_prefix, &db, O_RDONLY, 0644)) {
|
|
fprintf(stderr, _("rpmVerify: rpmdbOpen() failed\n"));
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
rc = rpmQueryVerify(qva, source, arg, db, showVerifyPackage);
|
|
|
|
if (db)
|
|
rpmdbClose(db);
|
|
|
|
return rc;
|
|
}
|