00001
00006 #include "system.h"
00007
00008 #include <rpmcli.h>
00009
00010 #include "psm.h"
00011 #include "md5.h"
00012 #include "misc.h"
00013 #include "debug.h"
00014
00015
00016
00017
00018
00019
00020 static int _ie = 0x44332211;
00021 static union _vendian {
00022 int i;
00023 char b[4];
00024 } *_endian = (union _vendian *)&_ie;
00025 #define IS_BIG_ENDIAN() (_endian->b[0] == '\x44')
00026 #define IS_LITTLE_ENDIAN() (_endian->b[0] == '\x11')
00027
00028 #define S_ISDEV(m) (S_ISBLK((m)) || S_ISCHR((m)))
00029
00030 int rpmVerifyFile(const char * root, Header h, int filenum,
00031 rpmVerifyAttrs * result, rpmVerifyAttrs omitMask)
00032 {
00033 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
00034 HFD_t hfd = headerFreeData;
00035 int_32 * fileFlags;
00036 rpmfileAttrs fileAttrs = RPMFILE_NONE;
00037 int_32 * verifyFlags;
00038 rpmVerifyAttrs flags = RPMVERIFY_ALL;
00039 unsigned short * modeList;
00040 const char * fileStatesList;
00041 const char * filespec = NULL;
00042 int count;
00043 int rc;
00044 struct stat sb;
00045 int_32 useBrokenMd5;
00046
00047 if (IS_BIG_ENDIAN()) {
00048 int_32 * brokenPtr;
00049 if (!hge(h, RPMTAG_BROKENMD5, NULL, (void **) &brokenPtr, NULL)) {
00050 HAE_t hae = (HAE_t)headerAddEntry;
00051 const char * rpmVersion;
00052
00053 if (hge(h, RPMTAG_RPMVERSION, NULL, (void **) &rpmVersion, NULL)) {
00054 useBrokenMd5 = ((rpmvercmp(rpmVersion, "2.3.3") >= 0) &&
00055 (rpmvercmp(rpmVersion, "2.3.8") <= 0));
00056 } else {
00057 useBrokenMd5 = 1;
00058 }
00059 (void) hae(h, RPMTAG_BROKENMD5, RPM_INT32_TYPE, &useBrokenMd5, 1);
00060 } else {
00061 useBrokenMd5 = *brokenPtr;
00062 }
00063 } else {
00064 useBrokenMd5 = 0;
00065 }
00066
00067 (void) hge(h, RPMTAG_FILEMODES, NULL, (void **) &modeList, &count);
00068 if (hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &fileFlags, NULL))
00069 fileAttrs = fileFlags[filenum];
00070
00071 if (hge(h, RPMTAG_FILEVERIFYFLAGS, NULL, (void **) &verifyFlags, NULL))
00072 flags = verifyFlags[filenum];
00073
00074 {
00075 const char ** baseNames;
00076 const char ** dirNames;
00077 int_32 * dirIndexes;
00078 rpmTagType bnt, dnt;
00079
00080 if (hge(h, RPMTAG_BASENAMES, &bnt, (void **) &baseNames, NULL)
00081 && hge(h, RPMTAG_DIRNAMES, &dnt, (void **) &dirNames, NULL)
00082 && hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL))
00083 {
00084 int nb = (strlen(dirNames[dirIndexes[filenum]]) +
00085 strlen(baseNames[filenum]) + strlen(root) + 5);
00086 char * t = alloca(nb);
00087 filespec = t;
00088 *t = '\0';
00089 if (root && !(root[0] == '/' && root[1] == '\0')) {
00090 t = stpcpy(t, root);
00091 while (t > filespec && t[-1] == '/') {
00092 --t;
00093 *t = '\0';
00094 }
00095 }
00096 t = stpcpy(t, dirNames[dirIndexes[filenum]]);
00097 t = stpcpy(t, baseNames[filenum]);
00098 }
00099 baseNames = hfd(baseNames, bnt);
00100 dirNames = hfd(dirNames, dnt);
00101 }
00102
00103 *result = RPMVERIFY_NONE;
00104
00105
00106
00107
00108 if (hge(h, RPMTAG_FILESTATES, NULL, (void **) &fileStatesList, NULL) &&
00109 fileStatesList != NULL)
00110 {
00111 rpmfileState fstate = fileStatesList[filenum];
00112 switch (fstate) {
00113 case RPMFILE_STATE_NETSHARED:
00114 case RPMFILE_STATE_REPLACED:
00115 case RPMFILE_STATE_NOTINSTALLED:
00116 return 0;
00117 break;
00118 case RPMFILE_STATE_NORMAL:
00119 break;
00120 }
00121 }
00122
00123 if (filespec == NULL) {
00124 *result |= RPMVERIFY_LSTATFAIL;
00125 return 1;
00126 }
00127
00128 if (Lstat(filespec, &sb) != 0) {
00129 *result |= RPMVERIFY_LSTATFAIL;
00130 return 1;
00131 }
00132
00133
00134
00135
00136 if (S_ISDIR(sb.st_mode))
00137 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
00138 RPMVERIFY_LINKTO);
00139 else if (S_ISLNK(sb.st_mode)) {
00140 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
00141 RPMVERIFY_MODE);
00142 #if CHOWN_FOLLOWS_SYMLINK
00143 flags &= ~(RPMVERIFY_USER | RPMVERIFY_GROUP);
00144 #endif
00145 }
00146 else if (S_ISFIFO(sb.st_mode))
00147 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
00148 RPMVERIFY_LINKTO);
00149 else if (S_ISCHR(sb.st_mode))
00150 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
00151 RPMVERIFY_LINKTO);
00152 else if (S_ISBLK(sb.st_mode))
00153 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
00154 RPMVERIFY_LINKTO);
00155 else
00156 flags &= ~(RPMVERIFY_LINKTO);
00157
00158
00159
00160
00161 if (fileAttrs & RPMFILE_GHOST)
00162 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
00163 RPMVERIFY_LINKTO);
00164
00165
00166
00167
00168 flags &= ~(omitMask | RPMVERIFY_LSTATFAIL|RPMVERIFY_READFAIL|RPMVERIFY_READLINKFAIL);
00169
00170 if (flags & RPMVERIFY_MD5) {
00171 unsigned char md5sum[40];
00172 const char ** md5List;
00173 rpmTagType mdt;
00174
00175 if (!hge(h, RPMTAG_FILEMD5S, &mdt, (void **) &md5List, NULL))
00176 *result |= RPMVERIFY_MD5;
00177 else {
00178 if (useBrokenMd5)
00179 rc = mdfileBroken(filespec, md5sum);
00180 else
00181 rc = mdfile(filespec, md5sum);
00182
00183 if (rc)
00184 *result |= (RPMVERIFY_READFAIL|RPMVERIFY_MD5);
00185 else if (strcmp(md5sum, md5List[filenum]))
00186 *result |= RPMVERIFY_MD5;
00187 }
00188 md5List = hfd(md5List, mdt);
00189 }
00190
00191 if (flags & RPMVERIFY_LINKTO) {
00192 char linkto[1024];
00193 int size = 0;
00194 const char ** linktoList;
00195 rpmTagType ltt;
00196
00197 if (!hge(h, RPMTAG_FILELINKTOS, <t, (void **) &linktoList, NULL)
00198 || (size = Readlink(filespec, linkto, sizeof(linkto)-1)) == -1)
00199 *result |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO);
00200 else {
00201 linkto[size] = '\0';
00202 if (strcmp(linkto, linktoList[filenum]))
00203 *result |= RPMVERIFY_LINKTO;
00204 }
00205 linktoList = hfd(linktoList, ltt);
00206 }
00207
00208 if (flags & RPMVERIFY_FILESIZE) {
00209 int_32 * sizeList;
00210
00211 if (!hge(h, RPMTAG_FILESIZES, NULL, (void **) &sizeList, NULL)
00212 || sizeList[filenum] != sb.st_size)
00213 *result |= RPMVERIFY_FILESIZE;
00214 }
00215
00216 if (flags & RPMVERIFY_MODE) {
00217 unsigned short metamode = modeList[filenum];
00218 unsigned short filemode;
00219
00220
00221
00222
00223
00224 filemode = (unsigned short)sb.st_mode;
00225
00226
00227
00228
00229 if (fileAttrs & RPMFILE_GHOST) {
00230 metamode &= ~0xf000;
00231 filemode &= ~0xf000;
00232 }
00233
00234 if (metamode != filemode)
00235 *result |= RPMVERIFY_MODE;
00236 }
00237
00238 if (flags & RPMVERIFY_RDEV) {
00239 if (S_ISCHR(modeList[filenum]) != S_ISCHR(sb.st_mode) ||
00240 S_ISBLK(modeList[filenum]) != S_ISBLK(sb.st_mode))
00241 {
00242 *result |= RPMVERIFY_RDEV;
00243 } else if (S_ISDEV(modeList[filenum]) && S_ISDEV(sb.st_mode)) {
00244 unsigned short * rdevList;
00245 if (!hge(h, RPMTAG_FILERDEVS, NULL, (void **) &rdevList, NULL)
00246 || rdevList[filenum] != sb.st_rdev)
00247 *result |= RPMVERIFY_RDEV;
00248 }
00249 }
00250
00251 if (flags & RPMVERIFY_MTIME) {
00252 int_32 * mtimeList;
00253
00254 if (!hge(h, RPMTAG_FILEMTIMES, NULL, (void **) &mtimeList, NULL)
00255 || mtimeList[filenum] != sb.st_mtime)
00256 *result |= RPMVERIFY_MTIME;
00257 }
00258
00259 if (flags & RPMVERIFY_USER) {
00260 const char * name;
00261 const char ** unameList;
00262 int_32 * uidList;
00263 rpmTagType unt;
00264
00265 if (hge(h, RPMTAG_FILEUSERNAME, &unt, (void **) &unameList, NULL)) {
00266 name = uidToUname(sb.st_uid);
00267 if (!name || strcmp(unameList[filenum], name))
00268 *result |= RPMVERIFY_USER;
00269 unameList = hfd(unameList, unt);
00270 } else if (hge(h, RPMTAG_FILEUIDS, NULL, (void **) &uidList, NULL)) {
00271 if (uidList[filenum] != sb.st_uid)
00272 *result |= RPMVERIFY_GROUP;
00273 } else {
00274 rpmError(RPMERR_INTERNAL, _("package lacks both user name and id "
00275 "lists (this should never happen)\n"));
00276 *result |= RPMVERIFY_GROUP;
00277 }
00278 }
00279
00280 if (flags & RPMVERIFY_GROUP) {
00281 const char ** gnameList;
00282 int_32 * gidList;
00283 rpmTagType gnt;
00284 gid_t gid;
00285
00286 if (hge(h, RPMTAG_FILEGROUPNAME, &gnt, (void **) &gnameList, NULL)) {
00287 rc = gnameToGid(gnameList[filenum], &gid);
00288 if (rc || (gid != sb.st_gid))
00289 *result |= RPMVERIFY_GROUP;
00290 gnameList = hfd(gnameList, gnt);
00291 } else if (hge(h, RPMTAG_FILEGIDS, NULL, (void **) &gidList, NULL)) {
00292 if (gidList[filenum] != sb.st_gid)
00293 *result |= RPMVERIFY_GROUP;
00294 } else {
00295 rpmError(RPMERR_INTERNAL, _("package lacks both group name and id "
00296 "lists (this should never happen)\n"));
00297 *result |= RPMVERIFY_GROUP;
00298 }
00299 }
00300
00301 return 0;
00302 }
00303
00312 int rpmVerifyScript(const char * rootDir, Header h, FD_t scriptFd)
00313 {
00314 rpmdb rpmdb = NULL;
00315 rpmTransactionSet ts = rpmtransCreateSet(rpmdb, rootDir);
00316 TFI_t fi = xcalloc(1, sizeof(*fi));
00317 struct psm_s psmbuf;
00318 PSM_t psm = &psmbuf;
00319 int rc;
00320
00321 if (scriptFd != NULL)
00322 ts->scriptFd = fdLink(scriptFd, "rpmVerifyScript");
00323 fi->magic = TFIMAGIC;
00324 loadFi(h, fi);
00325 memset(psm, 0, sizeof(*psm));
00326 psm->ts = ts;
00327 psm->fi = fi;
00328 psm->scriptTag = RPMTAG_VERIFYSCRIPT;
00329 psm->progTag = RPMTAG_VERIFYSCRIPTPROG;
00330 rc = psmStage(psm, PSM_SCRIPT);
00331 freeFi(fi);
00332 fi = _free(fi);
00333 ts = rpmtransFree(ts);
00334 return rc;
00335 }
00336
00337 int rpmVerifyDigest(Header h)
00338 {
00339 HGE_t hge = (HGE_t)headerGetEntry;
00340 HFD_t hfd = headerFreeData;
00341 void * uh = NULL;
00342 rpmTagType uht;
00343 int_32 uhc;
00344 const char * hdigest;
00345 rpmTagType hdt;
00346 int flags = RPMDIGEST_SHA1;
00347 int ec = 0;
00348
00349
00350 if (!hge(h, RPMTAG_SHA1HEADER, &hdt, (void **) &hdigest, NULL)
00351 && !hge(h, RPMTAG_SHA1RHN, &hdt, (void **) &hdigest, NULL))
00352 {
00353 if (hge(h, RPMTAG_BADSHA1HEADER, &hdt, (void **) &hdigest, NULL))
00354 flags |= (RPMDIGEST_REVERSE|RPMDIGEST_BCSWAP);
00355 else
00356 return 0;
00357 }
00358
00359 if (!hge(h, RPMTAG_HEADERIMMUTABLE, &uht, &uh, &uhc))
00360 return 0;
00361
00362 if (hdigest == NULL || uh == NULL)
00363 return 0;
00364
00365
00366 { DIGEST_CTX ctx = rpmDigestInit(flags);
00367 const char * digest;
00368 size_t digestlen;
00369
00370 rpmDigestUpdate(ctx, uh, uhc);
00371 rpmDigestFinal(ctx, (void **)&digest, &digestlen, 1);
00372
00373
00374 ec = (digest == NULL || strcmp(hdigest, digest)) ? 1 : 0;
00375 digest = _free(digest);
00376 }
00377
00378 uh = hfd(uh, uht);
00379 hdigest = hfd(hdigest, hdt);
00380
00381 return ec;
00382 }
00383
00389 static int verifyHeader(QVA_t qva, Header h)
00390
00391 {
00392 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
00393 char buf[BUFSIZ];
00394 char * t, * te;
00395 const char * prefix = (qva->qva_prefix ? qva->qva_prefix : "");
00396 const char ** fileNames = NULL;
00397 int count;
00398 int_32 * fileFlags = NULL;
00399 rpmVerifyAttrs verifyResult = 0;
00400 rpmVerifyAttrs omitMask = ((qva->qva_flags & VERIFY_ATTRS) ^ VERIFY_ATTRS);
00401 int ec = 0;
00402 int i;
00403
00404 te = t = buf;
00405 *te = '\0';
00406
00407 if (!hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &fileFlags, NULL))
00408 goto exit;
00409
00410 if (!headerIsEntry(h, RPMTAG_BASENAMES))
00411 goto exit;
00412
00413 rpmBuildFileList(h, &fileNames, &count);
00414
00415 for (i = 0; i < count; i++) {
00416 rpmfileAttrs fileAttrs;
00417 int rc;
00418
00419 fileAttrs = fileFlags[i];
00420
00421
00422 if (!(qva->qva_fflags & RPMFILE_GHOST)
00423 && (fileAttrs & RPMFILE_GHOST))
00424 continue;
00425
00426 rc = rpmVerifyFile(prefix, h, i, &verifyResult, omitMask);
00427 if (rc) {
00428 if (!(fileAttrs & RPMFILE_MISSINGOK) || rpmIsVerbose()) {
00429 sprintf(te, _("missing %s"), fileNames[i]);
00430 te += strlen(te);
00431 ec = rc;
00432 }
00433 } else if (verifyResult) {
00434 const char * size, * md5, * link, * mtime, * mode;
00435 const char * group, * user, * rdev;
00436 static const char *const aok = ".";
00437 static const char *const unknown = "?";
00438
00439 ec = 1;
00440
00441 #define _verify(_RPMVERIFY_F, _C) \
00442 ((verifyResult & _RPMVERIFY_F) ? _C : aok)
00443 #define _verifylink(_RPMVERIFY_F, _C) \
00444 ((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
00445 (verifyResult & _RPMVERIFY_F) ? _C : aok)
00446 #define _verifyfile(_RPMVERIFY_F, _C) \
00447 ((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
00448 (verifyResult & _RPMVERIFY_F) ? _C : aok)
00449
00450 md5 = _verifyfile(RPMVERIFY_MD5, "5");
00451 size = _verify(RPMVERIFY_FILESIZE, "S");
00452 link = _verifylink(RPMVERIFY_LINKTO, "L");
00453 mtime = _verify(RPMVERIFY_MTIME, "T");
00454 rdev = _verify(RPMVERIFY_RDEV, "D");
00455 user = _verify(RPMVERIFY_USER, "U");
00456 group = _verify(RPMVERIFY_GROUP, "G");
00457 mode = _verify(RPMVERIFY_MODE, "M");
00458
00459 #undef _verify
00460 #undef _verifylink
00461 #undef _verifyfile
00462
00463 sprintf(te, "%s%s%s%s%s%s%s%s %c %s",
00464 size, mode, md5, rdev, link, user, group, mtime,
00465 ((fileAttrs & RPMFILE_CONFIG) ? 'c' :
00466 (fileAttrs & RPMFILE_DOC) ? 'd' :
00467 (fileAttrs & RPMFILE_GHOST) ? 'g' :
00468 (fileAttrs & RPMFILE_LICENSE) ? 'l' :
00469 (fileAttrs & RPMFILE_README) ? 'r' : ' '),
00470 fileNames[i]);
00471 te += strlen(te);
00472 }
00473
00474 if (te > t) {
00475 *te++ = '\n';
00476 *te = '\0';
00477 rpmMessage(RPMMESS_NORMAL, "%s", t);
00478 te = t = buf;
00479 *t = '\0';
00480 }
00481 }
00482
00483 exit:
00484 fileNames = _free(fileNames);
00485 return ec;
00486 }
00487
00494 static int verifyDependencies(rpmdb rpmdb, Header h)
00495
00496 {
00497 rpmTransactionSet ts;
00498 rpmDependencyConflict conflicts;
00499 int numConflicts;
00500 int rc = 0;
00501 int i;
00502
00503 ts = rpmtransCreateSet(rpmdb, NULL);
00504 (void) rpmtransAddPackage(ts, h, NULL, NULL, 0, NULL);
00505
00506 (void) rpmdepCheck(ts, &conflicts, &numConflicts);
00507 ts = rpmtransFree(ts);
00508
00509 if (numConflicts) {
00510 const char *n, *v, *r;
00511 char * t, * te;
00512 int nb = 512;
00513 (void) headerNVR(h, &n, &v, &r);
00514
00515 for (i = 0; i < numConflicts; i++) {
00516 nb += strlen(conflicts[i].needsName) + sizeof(", ") - 1;
00517 if (conflicts[i].needsFlags)
00518 nb += strlen(conflicts[i].needsVersion) + 5;
00519 }
00520 te = t = alloca(nb);
00521 *te = '\0';
00522 sprintf(te, _("Unsatisfied dependencies for %s-%s-%s: "), n, v, r);
00523 te += strlen(te);
00524 for (i = 0; i < numConflicts; i++) {
00525 if (i) te = stpcpy(te, ", ");
00526 te = stpcpy(te, conflicts[i].needsName);
00527 if (conflicts[i].needsFlags) {
00528 int flags = conflicts[i].needsFlags;
00529 *te++ = ' ';
00530 if (flags & RPMSENSE_LESS) *te++ = '<';
00531 if (flags & RPMSENSE_GREATER) *te++ = '>';
00532 if (flags & RPMSENSE_EQUAL) *te++ = '=';
00533 *te++ = ' ';
00534 te = stpcpy(te, conflicts[i].needsVersion);
00535 }
00536 }
00537 conflicts = rpmdepFreeConflicts(conflicts, numConflicts);
00538 if (te > t) {
00539 *te++ = '\n';
00540 *te = '\0';
00541 rpmMessage(RPMMESS_NORMAL, "%s", t);
00542 te = t;
00543 *t = '\0';
00544 }
00545 rc = 1;
00546 }
00547 return rc;
00548 }
00549
00550 int showVerifyPackage(QVA_t qva, rpmdb rpmdb, Header h)
00551 {
00552 const char * prefix = (qva->qva_prefix ? qva->qva_prefix : "");
00553 int ec = 0;
00554 int rc;
00555
00556 if (qva->qva_flags & VERIFY_DIGEST) {
00557 if ((rc = rpmVerifyDigest(h)) != 0) {
00558 const char *n, *v, *r;
00559 (void) headerNVR(h, &n, &v, &r);
00560 rpmMessage(RPMMESS_NORMAL,
00561 _("%s-%s-%s: immutable header region digest check failed\n"),
00562 n, v, r);
00563 ec = rc;
00564 }
00565 }
00566 if (qva->qva_flags & VERIFY_DEPS) {
00567 if ((rc = verifyDependencies(rpmdb, h)) != 0)
00568 ec = rc;
00569 }
00570 if (qva->qva_flags & VERIFY_FILES) {
00571 if ((rc = verifyHeader(qva, h)) != 0)
00572 ec = rc;
00573 }
00574 if (qva->qva_flags & VERIFY_SCRIPT) {
00575 FD_t fdo = fdDup(STDOUT_FILENO);
00576 if ((rc = rpmVerifyScript(prefix, h, fdo)) != 0)
00577 ec = rc;
00578 if (fdo)
00579 rc = Fclose(fdo);
00580 }
00581 return ec;
00582 }
00583
00584 int rpmVerify(QVA_t qva, rpmQVSources source, const char * arg)
00585 {
00586 rpmdb rpmdb = NULL;
00587 int rc;
00588
00589 switch (source) {
00590 case RPMQV_RPM:
00591 if (!(qva->qva_flags & VERIFY_DEPS))
00592 break;
00593
00594 default:
00595 if ((rc = rpmdbOpen(qva->qva_prefix, &rpmdb, O_RDONLY, 0644)) != 0)
00596 return 1;
00597 break;
00598 }
00599
00600 rc = rpmQueryVerify(qva, source, arg, rpmdb, showVerifyPackage);
00601
00602 if (rpmdb != NULL)
00603 (void) rpmdbClose(rpmdb);
00604
00605 return rc;
00606 }