00001
00006 #include "system.h"
00007
00008 #include <rpmlib.h>
00009 #include <rpmurl.h>
00010
00011 #include "psm.h"
00012 #include "md5.h"
00013 #include "misc.h"
00014 #include "debug.h"
00015
00016
00017
00018
00019 static int _ie = 0x44332211;
00020 static union _vendian { int i; char b[4]; } *_endian = (union _vendian *)&_ie;
00021 #define IS_BIG_ENDIAN() (_endian->b[0] == '\x44')
00022 #define IS_LITTLE_ENDIAN() (_endian->b[0] == '\x11')
00023
00024 #define S_ISDEV(m) (S_ISBLK((m)) || S_ISCHR((m)))
00025
00026 #define POPT_NODEPS 1000
00027 #define POPT_NOFILES 1001
00028 #define POPT_NOMD5 1002
00029 #define POPT_NOSCRIPTS 1003
00030
00031
00032 static void verifyArgCallback(poptContext con,
00033 enum poptCallbackReason reason,
00034 const struct poptOption * opt, const char * arg,
00035 const void * data)
00036 {
00037 QVA_t *qva = &rpmQVArgs;
00038 switch (opt->val) {
00039 case POPT_NODEPS: qva->qva_flags |= VERIFY_DEPS; break;
00040 case POPT_NOFILES: qva->qva_flags |= VERIFY_FILES; break;
00041 case POPT_NOMD5: qva->qva_flags |= VERIFY_MD5; break;
00042 case POPT_NOSCRIPTS: qva->qva_flags |= VERIFY_SCRIPT; break;
00043 }
00044 }
00045
00046 static int noDeps = 0;
00047 static int noFiles = 0;
00048 static int noMd5 = 0;
00049 static int noScripts = 0;
00050
00052 struct poptOption rpmVerifyPoptTable[] = {
00053 { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA,
00054 verifyArgCallback, 0, NULL, NULL },
00055 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmQVSourcePoptTable, 0,
00056 NULL, NULL },
00057 { "nodeps", '\0', 0, &noDeps, POPT_NODEPS,
00058 N_("do not verify package dependencies"),
00059 NULL },
00060 { "nofiles", '\0', 0, &noFiles, POPT_NOFILES,
00061 N_("don't verify files in package"),
00062 NULL},
00063 { "nomd5", '\0', 0, &noMd5, POPT_NOMD5,
00064 N_("do not verify file md5 checksums"),
00065 NULL },
00066 { "noscripts", '\0', 0, &noScripts, POPT_NOSCRIPTS,
00067 N_("do not execute %verifyscript (if any)"),
00068 NULL },
00069 POPT_TABLEEND
00070 };
00071
00077 static void * _free( const void * this) {
00078 if (this) free((void *)this);
00079 return NULL;
00080 }
00081
00082
00083 int rpmVerifyFile(const char * prefix, Header h, int filenum,
00084 int * result, int omitMask)
00085 {
00086 char ** baseNames, ** md5List, ** linktoList, ** dirNames;
00087 int_32 * verifyFlags;
00088 rpmVerifyAttrs flags;
00089 int_32 * sizeList, * mtimeList, * dirIndexes;
00090 unsigned short * modeList, * rdevList;
00091 char * fileStatesList;
00092 char * filespec;
00093 char * name;
00094 gid_t gid;
00095 int type, count, rc;
00096 struct stat sb;
00097 unsigned char md5sum[40];
00098 int_32 * uidList, * gidList;
00099 char linkto[1024];
00100 int size;
00101 char ** unameList, ** gnameList;
00102 int_32 useBrokenMd5;
00103
00104 if (IS_BIG_ENDIAN()) {
00105 int_32 * brokenPtr;
00106 if (!headerGetEntry(h, RPMTAG_BROKENMD5, NULL, (void **) &brokenPtr, NULL)) {
00107 char * rpmVersion;
00108
00109 if (headerGetEntry(h, RPMTAG_RPMVERSION, NULL, (void **) &rpmVersion,
00110 NULL)) {
00111 useBrokenMd5 = ((rpmvercmp(rpmVersion, "2.3.3") >= 0) &&
00112 (rpmvercmp(rpmVersion, "2.3.8") <= 0));
00113 } else {
00114 useBrokenMd5 = 1;
00115 }
00116 headerAddEntry(h, RPMTAG_BROKENMD5, RPM_INT32_TYPE, &useBrokenMd5, 1);
00117 } else {
00118 useBrokenMd5 = *brokenPtr;
00119 }
00120 } else {
00121 useBrokenMd5 = 0;
00122 }
00123
00124 headerGetEntry(h, RPMTAG_FILEMODES, &type, (void **) &modeList, &count);
00125
00126 if (headerGetEntry(h, RPMTAG_FILEVERIFYFLAGS, &type, (void **) &verifyFlags,
00127 &count)) {
00128 flags = verifyFlags[filenum];
00129 } else {
00130 flags = RPMVERIFY_ALL;
00131 }
00132
00133 headerGetEntry(h, RPMTAG_BASENAMES, &type, (void **) &baseNames,
00134 &count);
00135 headerGetEntry(h, RPMTAG_DIRINDEXES, &type, (void **) &dirIndexes,
00136 NULL);
00137 headerGetEntry(h, RPMTAG_DIRNAMES, &type, (void **) &dirNames, NULL);
00138
00139 filespec = alloca(strlen(dirNames[dirIndexes[filenum]]) +
00140 strlen(baseNames[filenum]) + strlen(prefix) + 5);
00141 sprintf(filespec, "%s/%s%s", prefix, dirNames[dirIndexes[filenum]],
00142 baseNames[filenum]);
00143 free(baseNames);
00144 free(dirNames);
00145
00146 *result = 0;
00147
00148
00149 if (headerGetEntry(h, RPMTAG_FILESTATES, &type,
00150 (void **) &fileStatesList, &count) && fileStatesList) {
00151 if (fileStatesList[filenum] == RPMFILE_STATE_NOTINSTALLED)
00152 return 0;
00153 }
00154
00155 if (lstat(filespec, &sb)) {
00156 *result |= RPMVERIFY_LSTATFAIL;
00157 return 1;
00158 }
00159
00160 if (S_ISDIR(sb.st_mode))
00161 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
00162 RPMVERIFY_LINKTO);
00163 else if (S_ISLNK(sb.st_mode)) {
00164 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
00165 RPMVERIFY_MODE);
00166 # if CHOWN_FOLLOWS_SYMLINK
00167 flags &= ~(RPMVERIFY_USER | RPMVERIFY_GROUP);
00168 # endif
00169 }
00170 else if (S_ISFIFO(sb.st_mode))
00171 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
00172 RPMVERIFY_LINKTO);
00173 else if (S_ISCHR(sb.st_mode))
00174 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
00175 RPMVERIFY_LINKTO);
00176 else if (S_ISBLK(sb.st_mode))
00177 flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
00178 RPMVERIFY_LINKTO);
00179 else
00180 flags &= ~(RPMVERIFY_LINKTO);
00181
00182
00183 flags &= ~(omitMask | RPMVERIFY_LSTATFAIL|RPMVERIFY_READFAIL|RPMVERIFY_READLINKFAIL);
00184
00185 if (flags & RPMVERIFY_MD5) {
00186 headerGetEntry(h, RPMTAG_FILEMD5S, &type, (void **) &md5List, &count);
00187 if (useBrokenMd5) {
00188 rc = mdfileBroken(filespec, md5sum);
00189 } else {
00190 rc = mdfile(filespec, md5sum);
00191 }
00192
00193 if (rc)
00194 *result |= (RPMVERIFY_READFAIL|RPMVERIFY_MD5);
00195 else if (strcmp(md5sum, md5List[filenum]))
00196 *result |= RPMVERIFY_MD5;
00197 free(md5List);
00198 }
00199 if (flags & RPMVERIFY_LINKTO) {
00200 headerGetEntry(h, RPMTAG_FILELINKTOS, &type, (void **) &linktoList, &count);
00201 size = readlink(filespec, linkto, sizeof(linkto)-1);
00202 if (size == -1)
00203 *result |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO);
00204 else {
00205 linkto[size] = '\0';
00206 if (strcmp(linkto, linktoList[filenum]))
00207 *result |= RPMVERIFY_LINKTO;
00208 }
00209 free(linktoList);
00210 }
00211
00212 if (flags & RPMVERIFY_FILESIZE) {
00213 headerGetEntry(h, RPMTAG_FILESIZES, &type, (void **) &sizeList, &count);
00214 if (sizeList[filenum] != sb.st_size)
00215 *result |= RPMVERIFY_FILESIZE;
00216 }
00217
00218 if (flags & RPMVERIFY_MODE) {
00219
00220
00221
00222
00223 if (modeList[filenum] != (unsigned short)sb.st_mode)
00224 *result |= RPMVERIFY_MODE;
00225 }
00226
00227 if (flags & RPMVERIFY_RDEV) {
00228 if (S_ISCHR(modeList[filenum]) != S_ISCHR(sb.st_mode) ||
00229 S_ISBLK(modeList[filenum]) != S_ISBLK(sb.st_mode)) {
00230 *result |= RPMVERIFY_RDEV;
00231 } else if (S_ISDEV(modeList[filenum]) && S_ISDEV(sb.st_mode)) {
00232 headerGetEntry(h, RPMTAG_FILERDEVS, NULL, (void **) &rdevList,
00233 NULL);
00234 if (rdevList[filenum] != sb.st_rdev)
00235 *result |= RPMVERIFY_RDEV;
00236 }
00237 }
00238
00239 if (flags & RPMVERIFY_MTIME) {
00240 headerGetEntry(h, RPMTAG_FILEMTIMES, NULL, (void **) &mtimeList, NULL);
00241 if (mtimeList[filenum] != sb.st_mtime)
00242 *result |= RPMVERIFY_MTIME;
00243 }
00244
00245 if (flags & RPMVERIFY_USER) {
00246 if (headerGetEntry(h, RPMTAG_FILEUSERNAME, NULL, (void **) &unameList,
00247 NULL)) {
00248 name = uidToUname(sb.st_uid);
00249 if (!name || strcmp(unameList[filenum], name))
00250 *result |= RPMVERIFY_USER;
00251 free(unameList);
00252 } else if (headerGetEntry(h, RPMTAG_FILEUIDS, NULL, (void **) &uidList,
00253 &count)) {
00254 if (uidList[filenum] != sb.st_uid)
00255 *result |= RPMVERIFY_GROUP;
00256 } else {
00257 rpmError(RPMERR_INTERNAL, _("package lacks both user name and id "
00258 "lists (this should never happen)\n"));
00259 *result |= RPMVERIFY_GROUP;
00260 }
00261 }
00262
00263 if (flags & RPMVERIFY_GROUP) {
00264 if (headerGetEntry(h, RPMTAG_FILEGROUPNAME, NULL, (void **) &gnameList,
00265 NULL)) {
00266 rc = gnameToGid(gnameList[filenum],&gid);
00267 if (rc || (gid != sb.st_gid))
00268 *result |= RPMVERIFY_GROUP;
00269 free(gnameList);
00270 } else if (headerGetEntry(h, RPMTAG_FILEGIDS, NULL, (void **) &gidList,
00271 &count)) {
00272 if (gidList[filenum] != sb.st_gid)
00273 *result |= RPMVERIFY_GROUP;
00274 } else {
00275 rpmError(RPMERR_INTERNAL, _("package lacks both group name and id "
00276 "lists (this should never happen)\n"));
00277 *result |= RPMVERIFY_GROUP;
00278 }
00279 }
00280
00281 return 0;
00282 }
00283
00292 int rpmVerifyScript(const char * rootDir, Header h, FD_t scriptFd)
00293 {
00294 rpmdb rpmdb = NULL;
00295 rpmTransactionSet ts = rpmtransCreateSet(rpmdb, rootDir);
00296 TFI_t fi = xcalloc(1, sizeof(*fi));
00297 struct psm_s psmbuf;
00298 PSM_t psm = &psmbuf;
00299 int rc;
00300
00301 if (scriptFd)
00302 ts->scriptFd = fdLink(scriptFd, "rpmVerifyScript");
00303 fi->magic = TFIMAGIC;
00304 loadFi(h, fi);
00305 memset(psm, 0, sizeof(*psm));
00306 psm->ts = ts;
00307 psm->fi = fi;
00308 psm->scriptTag = RPMTAG_VERIFYSCRIPT;
00309 psm->progTag = RPMTAG_VERIFYSCRIPTPROG;
00310 rc = psmStage(psm, PSM_SCRIPT);
00311 freeFi(fi);
00312 fi = _free(fi);
00313 rpmtransFree(ts);
00314 return rc;
00315 }
00316
00317
00318 static int verifyHeader(QVA_t *qva, Header h)
00319 {
00320 char buf[BUFSIZ];
00321 char * t, * te;
00322
00323 const char ** fileNames = NULL;
00324 int count;
00325 int_32 * fileFlagsList = NULL;
00326 rpmVerifyAttrs verifyResult = 0;
00327 rpmVerifyAttrs omitMask = !(qva->qva_flags & VERIFY_MD5)
00328 ? RPMVERIFY_MD5 : RPMVERIFY_NONE;
00329 int ec = 0;
00330 int i;
00331
00332 te = t = buf;
00333 *te = '\0';
00334
00335 if (!headerGetEntry(h, RPMTAG_FILEFLAGS, NULL, (void **) &fileFlagsList, NULL))
00336 goto exit;
00337
00338 if (!headerIsEntry(h, RPMTAG_BASENAMES))
00339 goto exit;
00340
00341 rpmBuildFileList(h, &fileNames, &count);
00342
00343 for (i = 0; i < count; i++) {
00344 int rc;
00345
00346 rc = rpmVerifyFile(qva->qva_prefix, h, i, &verifyResult, omitMask);
00347 if (rc) {
00348 sprintf(te, _("missing %s"), fileNames[i]);
00349 te += strlen(te);
00350 ec = rc;
00351 } else if (verifyResult) {
00352 const char * size, * md5, * link, * mtime, * mode;
00353 const char * group, * user, * rdev;
00354 static const char *const aok = ".";
00355 static const char *const unknown = "?";
00356
00357 ec = 1;
00358
00359 #define _verify(_RPMVERIFY_F, _C) \
00360 ((verifyResult & _RPMVERIFY_F) ? _C : aok)
00361 #define _verifylink(_RPMVERIFY_F, _C) \
00362 ((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
00363 (verifyResult & _RPMVERIFY_F) ? _C : aok)
00364 #define _verifyfile(_RPMVERIFY_F, _C) \
00365 ((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
00366 (verifyResult & _RPMVERIFY_F) ? _C : aok)
00367
00368 md5 = _verifyfile(RPMVERIFY_MD5, "5");
00369 size = _verify(RPMVERIFY_FILESIZE, "S");
00370 link = _verifylink(RPMVERIFY_LINKTO, "L");
00371 mtime = _verify(RPMVERIFY_MTIME, "T");
00372 rdev = _verify(RPMVERIFY_RDEV, "D");
00373 user = _verify(RPMVERIFY_USER, "U");
00374 group = _verify(RPMVERIFY_GROUP, "G");
00375 mode = _verify(RPMVERIFY_MODE, "M");
00376
00377 #undef _verify
00378 #undef _verifylink
00379 #undef _verifyfile
00380
00381 sprintf(te, "%s%s%s%s%s%s%s%s %c %s",
00382 size, mode, md5, rdev, link, user, group, mtime,
00383 fileFlagsList[i] & RPMFILE_CONFIG ? 'c' : ' ',
00384 fileNames[i]);
00385 te += strlen(te);
00386 }
00387
00388 if (te > t) {
00389 *te++ = '\n';
00390 *te = '\0';
00391 rpmMessage(RPMMESS_NORMAL, "%s", t);
00392 te = t = buf;
00393 *t = '\0';
00394 }
00395 }
00396
00397 exit:
00398 if (fileNames) free(fileNames);
00399 return ec;
00400 }
00401
00402 static int verifyDependencies(rpmdb rpmdb, Header h)
00403 {
00404 rpmTransactionSet rpmdep;
00405 struct rpmDependencyConflict * conflicts;
00406 int numConflicts;
00407 int rc = 0;
00408 int i;
00409
00410 rpmdep = rpmtransCreateSet(rpmdb, NULL);
00411 rpmtransAddPackage(rpmdep, h, NULL, NULL, 0, NULL);
00412
00413 rpmdepCheck(rpmdep, &conflicts, &numConflicts);
00414 rpmtransFree(rpmdep);
00415
00416 if (numConflicts) {
00417 const char * name, * version, * release;
00418 char * t, * te;
00419 int nb = 512;
00420 headerNVR(h, &name, &version, &release);
00421
00422 for (i = 0; i < numConflicts; i++) {
00423 nb += strlen(conflicts[i].needsName) + sizeof(", ") - 1;
00424 if (conflicts[i].needsFlags)
00425 nb += strlen(conflicts[i].needsVersion) + 5;
00426 }
00427 te = t = alloca(nb);
00428 *te = '\0';
00429 sprintf(te, _("Unsatisfied dependencies for %s-%s-%s: "),
00430 name, version, release);
00431 te += strlen(te);
00432 for (i = 0; i < numConflicts; i++) {
00433 if (i) te = stpcpy(te, ", ");
00434 te = stpcpy(te, conflicts[i].needsName);
00435 if (conflicts[i].needsFlags) {
00436 int flags = conflicts[i].needsFlags;
00437 *te++ = ' ';
00438 if (flags & RPMSENSE_LESS) *te++ = '<';
00439 if (flags & RPMSENSE_GREATER) *te++ = '>';
00440 if (flags & RPMSENSE_EQUAL) *te++ = '=';
00441 *te++ = ' ';
00442 te = stpcpy(te, conflicts[i].needsVersion);
00443 }
00444 }
00445 rpmdepFreeConflicts(conflicts, numConflicts);
00446 if (te > t) {
00447 *te++ = '\n';
00448 *te = '\0';
00449 rpmMessage(RPMMESS_NORMAL, "%s", t);
00450 te = t;
00451 *t = '\0';
00452 }
00453 rc = 1;
00454 }
00455 return rc;
00456 }
00457
00458 int showVerifyPackage(QVA_t *qva, rpmdb rpmdb, Header h)
00459 {
00460 FD_t fdo;
00461 int ec = 0;
00462 int rc;
00463
00464 if ((qva->qva_flags & VERIFY_DEPS) &&
00465 (rc = verifyDependencies(rpmdb, h)) != 0)
00466 ec = rc;
00467 if ((qva->qva_flags & VERIFY_FILES) &&
00468 (rc = verifyHeader(qva, h)) != 0)
00469 ec = rc;;
00470 fdo = fdDup(STDOUT_FILENO);
00471 if ((qva->qva_flags & VERIFY_SCRIPT) &&
00472 (rc = rpmVerifyScript(qva->qva_prefix, h, fdo)) != 0)
00473 ec = rc;
00474 Fclose(fdo);
00475 return ec;
00476 }
00477
00478 int rpmVerify(QVA_t *qva, rpmQVSources source, const char *arg)
00479 {
00480 rpmdb rpmdb = NULL;
00481 int rc;
00482
00483 switch (source) {
00484 case RPMQV_RPM:
00485 if (!(qva->qva_flags & VERIFY_DEPS))
00486 break;
00487
00488 default:
00489 if (rpmdbOpen(qva->qva_prefix, &rpmdb, O_RDONLY, 0644))
00490 return 1;
00491 break;
00492 }
00493
00494 rc = rpmQueryVerify(qva, source, arg, rpmdb, showVerifyPackage);
00495
00496 if (rpmdb)
00497 rpmdbClose(rpmdb);
00498
00499 return rc;
00500 }