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->stepName = "verify";
00329 psm->scriptTag = RPMTAG_VERIFYSCRIPT;
00330 psm->progTag = RPMTAG_VERIFYSCRIPTPROG;
00331 rc = psmStage(psm, PSM_SCRIPT);
00332 freeFi(fi);
00333 fi = _free(fi);
00334 ts = rpmtransFree(ts);
00335 return rc;
00336 }
00337
00338 int rpmVerifyDigest(Header h)
00339 {
00340 HGE_t hge = (HGE_t)headerGetEntry;
00341 HFD_t hfd = headerFreeData;
00342 void * uh = NULL;
00343 rpmTagType uht;
00344 int_32 uhc;
00345 const char * hdigest;
00346 rpmTagType hdt;
00347 int flags = RPMDIGEST_SHA1;
00348 int ec = 0;
00349
00350
00351 if (!hge(h, RPMTAG_SHA1HEADER, &hdt, (void **) &hdigest, NULL)
00352 && !hge(h, RPMTAG_SHA1RHN, &hdt, (void **) &hdigest, NULL))
00353 {
00354 if (hge(h, RPMTAG_BADSHA1HEADER, &hdt, (void **) &hdigest, NULL))
00355 flags |= (RPMDIGEST_REVERSE|RPMDIGEST_BCSWAP);
00356 else
00357 return 0;
00358 }
00359
00360 if (!hge(h, RPMTAG_HEADERIMMUTABLE, &uht, &uh, &uhc))
00361 return 0;
00362
00363 if (hdigest == NULL || uh == NULL)
00364 return 0;
00365
00366
00367 { DIGEST_CTX ctx = rpmDigestInit(flags);
00368 const char * digest;
00369 size_t digestlen;
00370
00371 rpmDigestUpdate(ctx, uh, uhc);
00372 rpmDigestFinal(ctx, (void **)&digest, &digestlen, 1);
00373
00374
00375 ec = (digest == NULL || strcmp(hdigest, digest)) ? 1 : 0;
00376 digest = _free(digest);
00377 }
00378
00379 uh = hfd(uh, uht);
00380 hdigest = hfd(hdigest, hdt);
00381
00382 return ec;
00383 }
00384
00390 static int verifyHeader(QVA_t qva, Header h)
00391
00392 {
00393 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
00394 char buf[BUFSIZ];
00395 char * t, * te;
00396 const char * prefix = (qva->qva_prefix ? qva->qva_prefix : "");
00397 const char ** fileNames = NULL;
00398 int count;
00399 int_32 * fileFlags = NULL;
00400 rpmVerifyAttrs verifyResult = 0;
00401 rpmVerifyAttrs omitMask = ((qva->qva_flags & VERIFY_ATTRS) ^ VERIFY_ATTRS);
00402 int ec = 0;
00403 int i;
00404
00405 te = t = buf;
00406 *te = '\0';
00407
00408 if (!hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &fileFlags, NULL))
00409 goto exit;
00410
00411 if (!headerIsEntry(h, RPMTAG_BASENAMES))
00412 goto exit;
00413
00414 rpmBuildFileList(h, &fileNames, &count);
00415
00416 for (i = 0; i < count; i++) {
00417 rpmfileAttrs fileAttrs;
00418 int rc;
00419
00420 fileAttrs = fileFlags[i];
00421
00422
00423 if (!(qva->qva_fflags & RPMFILE_GHOST)
00424 && (fileAttrs & RPMFILE_GHOST))
00425 continue;
00426
00427 rc = rpmVerifyFile(prefix, h, i, &verifyResult, omitMask);
00428 if (rc) {
00429 if (!(fileAttrs & RPMFILE_MISSINGOK) || rpmIsVerbose()) {
00430 sprintf(te, _("missing %s"), fileNames[i]);
00431 te += strlen(te);
00432 ec = rc;
00433 }
00434 } else if (verifyResult) {
00435 const char * size, * md5, * link, * mtime, * mode;
00436 const char * group, * user, * rdev;
00437 static const char *const aok = ".";
00438 static const char *const unknown = "?";
00439
00440 ec = 1;
00441
00442 #define _verify(_RPMVERIFY_F, _C) \
00443 ((verifyResult & _RPMVERIFY_F) ? _C : aok)
00444 #define _verifylink(_RPMVERIFY_F, _C) \
00445 ((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
00446 (verifyResult & _RPMVERIFY_F) ? _C : aok)
00447 #define _verifyfile(_RPMVERIFY_F, _C) \
00448 ((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
00449 (verifyResult & _RPMVERIFY_F) ? _C : aok)
00450
00451 md5 = _verifyfile(RPMVERIFY_MD5, "5");
00452 size = _verify(RPMVERIFY_FILESIZE, "S");
00453 link = _verifylink(RPMVERIFY_LINKTO, "L");
00454 mtime = _verify(RPMVERIFY_MTIME, "T");
00455 rdev = _verify(RPMVERIFY_RDEV, "D");
00456 user = _verify(RPMVERIFY_USER, "U");
00457 group = _verify(RPMVERIFY_GROUP, "G");
00458 mode = _verify(RPMVERIFY_MODE, "M");
00459
00460 #undef _verify
00461 #undef _verifylink
00462 #undef _verifyfile
00463
00464 sprintf(te, "%s%s%s%s%s%s%s%s %c %s",
00465 size, mode, md5, rdev, link, user, group, mtime,
00466 ((fileAttrs & RPMFILE_CONFIG) ? 'c' :
00467 (fileAttrs & RPMFILE_DOC) ? 'd' :
00468 (fileAttrs & RPMFILE_GHOST) ? 'g' :
00469 (fileAttrs & RPMFILE_LICENSE) ? 'l' :
00470 (fileAttrs & RPMFILE_README) ? 'r' : ' '),
00471 fileNames[i]);
00472 te += strlen(te);
00473 }
00474
00475 if (te > t) {
00476 *te++ = '\n';
00477 *te = '\0';
00478 rpmMessage(RPMMESS_NORMAL, "%s", t);
00479 te = t = buf;
00480 *t = '\0';
00481 }
00482 }
00483
00484 exit:
00485 fileNames = _free(fileNames);
00486 return ec;
00487 }
00488
00495 static int verifyDependencies(rpmdb rpmdb, Header h)
00496
00497 {
00498 rpmTransactionSet ts;
00499 rpmDependencyConflict conflicts;
00500 int numConflicts;
00501 int rc = 0;
00502 int i;
00503
00504 ts = rpmtransCreateSet(rpmdb, NULL);
00505 (void) rpmtransAddPackage(ts, h, NULL, NULL, 0, NULL);
00506
00507 (void) rpmdepCheck(ts, &conflicts, &numConflicts);
00508 ts = rpmtransFree(ts);
00509
00510 if (numConflicts) {
00511 const char *n, *v, *r;
00512 char * t, * te;
00513 int nb = 512;
00514 (void) headerNVR(h, &n, &v, &r);
00515
00516 for (i = 0; i < numConflicts; i++) {
00517 nb += strlen(conflicts[i].needsName) + sizeof(", ") - 1;
00518 if (conflicts[i].needsFlags)
00519 nb += strlen(conflicts[i].needsVersion) + 5;
00520 }
00521 te = t = alloca(nb);
00522 *te = '\0';
00523 sprintf(te, _("Unsatisfied dependencies for %s-%s-%s: "), n, v, r);
00524 te += strlen(te);
00525 for (i = 0; i < numConflicts; i++) {
00526 if (i) te = stpcpy(te, ", ");
00527 te = stpcpy(te, conflicts[i].needsName);
00528 if (conflicts[i].needsFlags) {
00529 int flags = conflicts[i].needsFlags;
00530 *te++ = ' ';
00531 if (flags & RPMSENSE_LESS) *te++ = '<';
00532 if (flags & RPMSENSE_GREATER) *te++ = '>';
00533 if (flags & RPMSENSE_EQUAL) *te++ = '=';
00534 *te++ = ' ';
00535 te = stpcpy(te, conflicts[i].needsVersion);
00536 }
00537 }
00538 conflicts = rpmdepFreeConflicts(conflicts, numConflicts);
00539 if (te > t) {
00540 *te++ = '\n';
00541 *te = '\0';
00542 rpmMessage(RPMMESS_NORMAL, "%s", t);
00543 te = t;
00544 *t = '\0';
00545 }
00546 rc = 1;
00547 }
00548 return rc;
00549 }
00550
00551 int showVerifyPackage(QVA_t qva, rpmdb rpmdb, Header h)
00552 {
00553 const char * prefix = (qva->qva_prefix ? qva->qva_prefix : "");
00554 int ec = 0;
00555 int rc;
00556
00557 if (qva->qva_flags & VERIFY_DIGEST) {
00558 if ((rc = rpmVerifyDigest(h)) != 0) {
00559 const char *n, *v, *r;
00560 (void) headerNVR(h, &n, &v, &r);
00561 rpmMessage(RPMMESS_NORMAL,
00562 _("%s-%s-%s: immutable header region digest check failed\n"),
00563 n, v, r);
00564 ec = rc;
00565 }
00566 }
00567 if (qva->qva_flags & VERIFY_DEPS) {
00568 if ((rc = verifyDependencies(rpmdb, h)) != 0)
00569 ec = rc;
00570 }
00571 if (qva->qva_flags & VERIFY_FILES) {
00572 if ((rc = verifyHeader(qva, h)) != 0)
00573 ec = rc;
00574 }
00575 if (qva->qva_flags & VERIFY_SCRIPT) {
00576 FD_t fdo = fdDup(STDOUT_FILENO);
00577 if ((rc = rpmVerifyScript(prefix, h, fdo)) != 0)
00578 ec = rc;
00579 if (fdo)
00580 rc = Fclose(fdo);
00581 }
00582 return ec;
00583 }
00584
00585 int rpmVerify(QVA_t qva, rpmQVSources source, const char * arg)
00586 {
00587 rpmdb rpmdb = NULL;
00588 int rc;
00589
00590 switch (source) {
00591 case RPMQV_RPM:
00592 if (!(qva->qva_flags & VERIFY_DEPS))
00593 break;
00594
00595 default:
00596 if ((rc = rpmdbOpen(qva->qva_prefix, &rpmdb, O_RDONLY, 0644)) != 0)
00597 return 1;
00598 break;
00599 }
00600
00601 rc = rpmQueryVerify(qva, source, arg, rpmdb, showVerifyPackage);
00602
00603 if (rpmdb != NULL)
00604 (void) rpmdbClose(rpmdb);
00605
00606 return rc;
00607 }