1 /** \ingroup rpmcli
2 * \file lib/verify.c
3 * Verify installed payload files from package metadata.
4 */
5
6 #include "system.h"
7
8 #include <errno.h>
9 #include <fcntl.h>
10 #if WITH_CAP
11 #include <sys/capability.h>
12 #endif
13 #if WITH_ACL
14 #include <acl/libacl.h>
15 #endif
16
17 #include <rpm/rpmcli.h>
18 #include <rpm/header.h>
19 #include <rpm/rpmlog.h>
20 #include <rpm/rpmfi.h>
21 #include <rpm/rpmts.h>
22 #include <rpm/rpmdb.h>
23 #include <rpm/rpmfileutil.h>
24
25 #include "lib/misc.h"
26 #include "lib/rpmchroot.h"
27 #include "lib/rpmte_internal.h" /* rpmteProcess() */
28 #include "lib/rpmug.h"
29
30 #include "debug.h"
31
32 #define S_ISDEV(m) (S_ISBLK((m)) || S_ISCHR((m)))
33
34 /* If cap_compare() (Linux extension) not available, do it the hard way */
35 #if WITH_CAP && !defined(HAVE_CAP_COMPARE)
cap_compare(cap_t acap,cap_t bcap)36 static int cap_compare(cap_t acap, cap_t bcap)
37 {
38 int rc = 0;
39 size_t asize = cap_size(acap);
40 size_t bsize = cap_size(bcap);
41
42 if (asize != bsize) {
43 rc = 1;
44 } else {
45 char *abuf = xcalloc(asize, sizeof(*abuf));
46 char *bbuf = xcalloc(bsize, sizeof(*bbuf));
47 cap_copy_ext(abuf, acap, asize);
48 cap_copy_ext(bbuf, bcap, bsize);
49 rc = memcmp(abuf, bbuf, asize);
50 free(abuf);
51 free(bbuf);
52 }
53 return rc;
54 }
55 #endif
56
rpmfilesVerify(rpmfiles fi,int ix,rpmVerifyAttrs omitMask)57 rpmVerifyAttrs rpmfilesVerify(rpmfiles fi, int ix, rpmVerifyAttrs omitMask)
58 {
59 rpmfileAttrs fileAttrs = rpmfilesFFlags(fi, ix);
60 rpmVerifyAttrs flags = rpmfilesVFlags(fi, ix);
61 char * fn = rpmfilesFN(fi, ix);
62 struct stat sb, fsb;
63 rpmVerifyAttrs vfy = RPMVERIFY_NONE;
64
65 /*
66 * Check to see if the file was installed - if not pretend all is OK.
67 */
68 switch (rpmfilesFState(fi, ix)) {
69 case RPMFILE_STATE_NETSHARED:
70 case RPMFILE_STATE_NOTINSTALLED:
71 goto exit;
72 break;
73 case RPMFILE_STATE_REPLACED:
74 /* For replaced files we can only verify if it exists at all */
75 flags = RPMVERIFY_LSTATFAIL;
76 break;
77 case RPMFILE_STATE_WRONGCOLOR:
78 /*
79 * Files with wrong color are supposed to share some attributes
80 * with the actually installed file - verify what we can.
81 */
82 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
83 RPMVERIFY_MTIME | RPMVERIFY_RDEV);
84 break;
85 case RPMFILE_STATE_NORMAL:
86 /* File from a non-installed package, try to verify nevertheless */
87 case RPMFILE_STATE_MISSING:
88 break;
89 }
90
91 if (fn == NULL || lstat(fn, &sb) != 0 || rpmfilesStat(fi, ix, 0, &fsb)) {
92 vfy |= RPMVERIFY_LSTATFAIL;
93 goto exit;
94 }
95
96 /* If we expected a directory but got a symlink to one, follow the link */
97 if (S_ISDIR(fsb.st_mode) && S_ISLNK(sb.st_mode)) {
98 struct stat dsb;
99 /* ...if it actually points to a directory */
100 if (stat(fn, &dsb) == 0 && S_ISDIR(dsb.st_mode)) {
101 /* ...and is by a legit user, to match fsmVerify() behavior */
102 if (sb.st_uid == 0 || sb.st_uid == dsb.st_uid)
103 sb = dsb; /* struct assignment */
104 }
105 }
106
107 /* Links have no mode, other types have no linkto */
108 if (S_ISLNK(sb.st_mode))
109 flags &= ~(RPMVERIFY_MODE);
110 else
111 flags &= ~(RPMVERIFY_LINKTO);
112
113 /* Not all attributes of non-regular files can be verified */
114 if (!S_ISREG(sb.st_mode))
115 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
116 RPMVERIFY_MTIME | RPMVERIFY_CAPS);
117
118 /* Content checks of %ghost files are meaningless. */
119 if (fileAttrs & RPMFILE_GHOST)
120 flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
121 RPMVERIFY_MTIME | RPMVERIFY_LINKTO);
122
123 /* Don't verify any features in omitMask. */
124 flags &= ~(omitMask | RPMVERIFY_FAILURES);
125
126
127 if (flags & RPMVERIFY_FILEDIGEST) {
128 const unsigned char *digest;
129 int algo;
130 size_t diglen;
131
132 if ((digest = rpmfilesFDigest(fi, ix, &algo, &diglen))) {
133 unsigned char fdigest[diglen];
134
135 if (rpmDoDigest(algo, fn, 0, fdigest)) {
136 vfy |= (RPMVERIFY_READFAIL|RPMVERIFY_FILEDIGEST);
137 } else {
138 if (memcmp(fdigest, digest, diglen))
139 vfy |= RPMVERIFY_FILEDIGEST;
140 }
141 } else {
142 vfy |= RPMVERIFY_FILEDIGEST;
143 }
144 }
145
146 if (flags & RPMVERIFY_LINKTO) {
147 char linkto[1024+1];
148 int size = 0;
149
150 if ((size = readlink(fn, linkto, sizeof(linkto)-1)) == -1)
151 vfy |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO);
152 else {
153 const char * flink = rpmfilesFLink(fi, ix);
154 linkto[size] = '\0';
155 if (flink == NULL || !rstreq(linkto, flink))
156 vfy |= RPMVERIFY_LINKTO;
157 }
158 }
159
160 if ((flags & RPMVERIFY_FILESIZE) && (sb.st_size != fsb.st_size))
161 vfy |= RPMVERIFY_FILESIZE;
162
163 if (flags & RPMVERIFY_MODE) {
164 mode_t metamode = fsb.st_mode;
165 mode_t filemode = sb.st_mode;
166
167 /*
168 * Comparing the type of %ghost files is meaningless, but perms are OK.
169 */
170 if (fileAttrs & RPMFILE_GHOST) {
171 metamode &= ~S_IFMT;
172 filemode &= ~S_IFMT;
173 }
174
175 if (metamode != filemode)
176 vfy |= RPMVERIFY_MODE;
177
178 #if WITH_ACL
179 /*
180 * For now, any non-default acl's on a file is a difference as rpm
181 * cannot have set them.
182 */
183 acl_t facl = acl_get_file(fn, ACL_TYPE_ACCESS);
184 if (facl) {
185 if (acl_equiv_mode(facl, NULL) == 1) {
186 vfy |= RPMVERIFY_MODE;
187 }
188 acl_free(facl);
189 }
190 #endif
191 }
192
193 if (flags & RPMVERIFY_RDEV) {
194 if (S_ISCHR(fsb.st_mode) != S_ISCHR(sb.st_mode)
195 || S_ISBLK(fsb.st_mode) != S_ISBLK(sb.st_mode))
196 {
197 vfy |= RPMVERIFY_RDEV;
198 } else if (S_ISDEV(fsb.st_mode) && S_ISDEV(sb.st_mode)) {
199 rpm_rdev_t st_rdev = (sb.st_rdev & 0xffff);
200 rpm_rdev_t frdev = (fsb.st_rdev & 0xffff);
201 if (st_rdev != frdev)
202 vfy |= RPMVERIFY_RDEV;
203 }
204 }
205
206 #if WITH_CAP
207 if (flags & RPMVERIFY_CAPS) {
208 cap_t cap = NULL;
209 cap_t fcap = cap_get_file(fn);
210 const char *captext = rpmfilesFCaps(fi, ix);
211
212 /* captext "" means no capability */
213 if (captext && captext[0])
214 cap = cap_from_text(captext);
215
216 if ((fcap || cap) && (cap_compare(cap, fcap) != 0))
217 vfy |= RPMVERIFY_CAPS;
218
219 cap_free(fcap);
220 cap_free(cap);
221 }
222 #endif
223
224 if ((flags & RPMVERIFY_MTIME) && (sb.st_mtime != fsb.st_mtime))
225 vfy |= RPMVERIFY_MTIME;
226
227 if ((flags & RPMVERIFY_USER) && (sb.st_uid != fsb.st_uid))
228 vfy |= RPMVERIFY_USER;
229
230 if ((flags & RPMVERIFY_GROUP) && (sb.st_gid != fsb.st_gid))
231 vfy |= RPMVERIFY_GROUP;
232
233 exit:
234 free(fn);
235 return vfy;
236 }
237
238 /**
239 * Return exit code from running verify script from header.
240 * @param ts transaction set
241 * @param h header
242 * @return 0 on success
243 */
rpmVerifyScript(rpmts ts,Header h)244 static int rpmVerifyScript(rpmts ts, Header h)
245 {
246 int rc = 0;
247
248 if (headerIsEntry(h, RPMTAG_VERIFYSCRIPT)) {
249 /* fake up a transaction element */
250 rpmte p = rpmteNew(ts, h, TR_RPMDB, NULL, NULL, 0);
251
252 if (p != NULL) {
253 rpmteSetHeader(p, h);
254
255 rc = (rpmpsmRun(ts, p, PKG_VERIFY) != RPMRC_OK);
256
257 /* clean up our fake transaction bits */
258 rpmteFree(p);
259 } else {
260 rc = RPMRC_FAIL;
261 }
262 }
263
264 return rc;
265 }
266
267 #define unknown "?"
268 #define _verify(_RPMVERIFY_F, _C, _pad) \
269 ((verifyResult & _RPMVERIFY_F) ? _C : _pad)
270 #define _verifylink(_RPMVERIFY_F, _C, _pad) \
271 ((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
272 (verifyResult & _RPMVERIFY_F) ? _C : _pad)
273 #define _verifyfile(_RPMVERIFY_F, _C, _pad) \
274 ((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
275 (verifyResult & _RPMVERIFY_F) ? _C : _pad)
rpmVerifyString(uint32_t verifyResult,const char * pad)276 char * rpmVerifyString(uint32_t verifyResult, const char *pad)
277 {
278 char *fmt = NULL;
279 rasprintf(&fmt, "%s%s%s%s%s%s%s%s%s",
280 _verify(RPMVERIFY_FILESIZE, "S", pad),
281 _verify(RPMVERIFY_MODE, "M", pad),
282 _verifyfile(RPMVERIFY_FILEDIGEST, "5", pad),
283 _verify(RPMVERIFY_RDEV, "D", pad),
284 _verifylink(RPMVERIFY_LINKTO, "L", pad),
285 _verify(RPMVERIFY_USER, "U", pad),
286 _verify(RPMVERIFY_GROUP, "G", pad),
287 _verify(RPMVERIFY_MTIME, "T", pad),
288 _verify(RPMVERIFY_CAPS, "P", pad));
289
290 return fmt;
291 }
292 #undef _verifyfile
293 #undef _verifylink
294 #undef _verify
295 #undef aok
296 #undef unknown
297
rpmFFlagsString(uint32_t fflags,const char * pad)298 char * rpmFFlagsString(uint32_t fflags, const char *pad)
299 {
300 char *fmt = NULL;
301 rasprintf(&fmt, "%s%s%s%s%s%s%s%s%s",
302 (fflags & RPMFILE_DOC) ? "d" : pad,
303 (fflags & RPMFILE_CONFIG) ? "c" : pad,
304 (fflags & RPMFILE_SPECFILE) ? "s" : pad,
305 (fflags & RPMFILE_MISSINGOK) ? "m" : pad,
306 (fflags & RPMFILE_NOREPLACE) ? "n" : pad,
307 (fflags & RPMFILE_GHOST) ? "g" : pad,
308 (fflags & RPMFILE_LICENSE) ? "l" : pad,
309 (fflags & RPMFILE_README) ? "r" : pad,
310 (fflags & RPMFILE_ARTIFACT) ? "a" : pad);
311 return fmt;
312 }
313
stateStr(rpmfileState fstate)314 static const char * stateStr(rpmfileState fstate)
315 {
316 switch (fstate) {
317 case RPMFILE_STATE_NORMAL:
318 return NULL;
319 case RPMFILE_STATE_NOTINSTALLED:
320 return rpmIsVerbose() ? _("not installed") : NULL;
321 case RPMFILE_STATE_NETSHARED:
322 return rpmIsVerbose() ? _("net shared") : NULL;
323 case RPMFILE_STATE_WRONGCOLOR:
324 return rpmIsVerbose() ? _("wrong color") : NULL;
325 case RPMFILE_STATE_REPLACED:
326 return _("replaced");
327 case RPMFILE_STATE_MISSING:
328 return _("no state");
329 }
330 return _("unknown state");
331 }
332
333 /**
334 * Check file info from header against what's actually installed.
335 * @param ts transaction set
336 * @param h header to verify
337 * @param omitMask bits to disable verify checks
338 * @param incAttr skip files without these attrs (eg %ghost)
339 * @param skipAttr skip files with these attrs (eg %ghost)
340 * @return 0 no problems, 1 problems found
341 */
verifyHeader(rpmts ts,Header h,rpmVerifyAttrs omitMask,rpmfileAttrs incAttrs,rpmfileAttrs skipAttrs)342 static int verifyHeader(rpmts ts, Header h, rpmVerifyAttrs omitMask,
343 rpmfileAttrs incAttrs, rpmfileAttrs skipAttrs)
344 {
345 rpmVerifyAttrs verifyResult = 0;
346 rpmVerifyAttrs verifyAll = 0; /* assume no problems */
347 rpmfi fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_FLAGS_VERIFY);
348
349 if (fi == NULL)
350 return 1;
351
352 rpmfiInit(fi, 0);
353 while (rpmfiNext(fi) >= 0) {
354 rpmfileAttrs fileAttrs = rpmfiFFlags(fi);
355 char *buf = NULL, *attrFormat;
356 const char *fstate = NULL;
357 char ac;
358
359 /* If filtering by inclusion, skip non-matching (eg --configfiles) */
360 if (incAttrs && !(incAttrs & fileAttrs))
361 continue;
362
363 /* Skip on attributes (eg from --noghost) */
364 if (skipAttrs & fileAttrs)
365 continue;
366
367 verifyResult = rpmfiVerify(fi, omitMask);
368
369 /* Filter out timestamp differences of shared files */
370 if (verifyResult & RPMVERIFY_MTIME) {
371 rpmdbMatchIterator mi;
372 mi = rpmtsInitIterator(ts, RPMDBI_BASENAMES, rpmfiFN(fi), 0);
373 if (rpmdbGetIteratorCount(mi) > 1)
374 verifyResult &= ~RPMVERIFY_MTIME;
375 rpmdbFreeIterator(mi);
376 }
377
378 /* State is only meaningful for installed packages */
379 if (headerGetInstance(h))
380 fstate = stateStr(rpmfiFState(fi));
381
382 attrFormat = rpmFFlagsString(fileAttrs, "");
383 ac = rstreq(attrFormat, "") ? ' ' : attrFormat[0];
384 if (verifyResult & RPMVERIFY_LSTATFAIL) {
385 if (!(fileAttrs & (RPMFILE_MISSINGOK|RPMFILE_GHOST)) || rpmIsVerbose()) {
386 rasprintf(&buf, _("missing %c %s"), ac, rpmfiFN(fi));
387 if ((verifyResult & RPMVERIFY_LSTATFAIL) != 0 &&
388 errno != ENOENT) {
389 char *app;
390 rasprintf(&app, " (%s)", strerror(errno));
391 rstrcat(&buf, app);
392 free(app);
393 }
394 }
395 } else if (verifyResult || fstate || rpmIsVerbose()) {
396 char *verifyFormat = rpmVerifyString(verifyResult, ".");
397 rasprintf(&buf, "%s %c %s", verifyFormat, ac, rpmfiFN(fi));
398 free(verifyFormat);
399 }
400 free(attrFormat);
401
402 if (buf) {
403 if (fstate)
404 buf = rstrscat(&buf, " (", fstate, ")", NULL);
405 rpmlog(RPMLOG_NOTICE, "%s\n", buf);
406 buf = _free(buf);
407 }
408
409 /* Filter out missing %ghost/%missingok errors from final result */
410 if (fileAttrs & (RPMFILE_MISSINGOK|RPMFILE_GHOST))
411 verifyResult &= ~RPMVERIFY_LSTATFAIL;
412
413 verifyAll |= verifyResult;
414 }
415 rpmfiFree(fi);
416
417 return (verifyAll != 0) ? 1 : 0;
418 }
419
420 /**
421 * Check installed package dependencies for problems.
422 * @param ts transaction set
423 * @param h header
424 * @return number of problems found (0 for no problems)
425 */
verifyDependencies(rpmts ts,Header h)426 static int verifyDependencies(rpmts ts, Header h)
427 {
428 rpmps ps;
429 rpmte te;
430 int rc;
431
432 rpmtsEmpty(ts);
433 (void) rpmtsAddInstallElement(ts, h, NULL, 0, NULL);
434
435 (void) rpmtsCheck(ts);
436 te = rpmtsElement(ts, 0);
437 ps = rpmteProblems(te);
438 rc = rpmpsNumProblems(ps);
439
440 if (rc > 0) {
441 rpmlog(RPMLOG_NOTICE, _("Unsatisfied dependencies for %s:\n"),
442 rpmteNEVRA(te));
443 rpmpsi psi = rpmpsInitIterator(ps);
444 rpmProblem p;
445
446 while ((p = rpmpsiNext(psi)) != NULL) {
447 char * ps = rpmProblemString(p);
448 rpmlog(RPMLOG_NOTICE, "\t%s\n", ps);
449 free(ps);
450 }
451 rpmpsFreeIterator(psi);
452 }
453 rpmpsFree(ps);
454 rpmtsEmpty(ts);
455
456 return rc;
457 }
458
showVerifyPackage(QVA_t qva,rpmts ts,Header h)459 int showVerifyPackage(QVA_t qva, rpmts ts, Header h)
460 {
461 int ec = 0;
462 int rc;
463
464 if (qva->qva_flags & VERIFY_DEPS) {
465 if ((rc = verifyDependencies(ts, h)) != 0)
466 ec = rc;
467 }
468 if (qva->qva_flags & VERIFY_FILES) {
469 if ((rc = verifyHeader(ts, h, qva->qva_ofvattr,
470 qva->qva_incattr, qva->qva_excattr)) != 0)
471 ec = rc;
472 }
473 if (qva->qva_flags & VERIFY_SCRIPT) {
474 if ((rc = rpmVerifyScript(ts, h)) != 0)
475 ec = rc;
476 }
477
478 return ec;
479 }
480
rpmcliVerify(rpmts ts,QVA_t qva,char * const * argv)481 int rpmcliVerify(rpmts ts, QVA_t qva, char * const * argv)
482 {
483 rpmVSFlags vsflags, ovsflags;
484 int ec = 0;
485 FD_t scriptFd = fdDup(STDOUT_FILENO);
486
487 /*
488 * Open the DB + indices explicitly before possible chroot,
489 * otherwises BDB is going to be unhappy...
490 */
491 rpmtsOpenDB(ts, O_RDONLY);
492 rpmdbOpenAll(rpmtsGetRdb(ts));
493 if (rpmChrootSet(rpmtsRootDir(ts)) || rpmChrootIn()) {
494 ec = 1;
495 goto exit;
496 }
497
498 if (qva->qva_showPackage == NULL)
499 qva->qva_showPackage = showVerifyPackage;
500
501 vsflags = rpmExpandNumeric("%{?_vsflags_verify}");
502 vsflags |= rpmcliVSFlags;
503 vsflags &= ~RPMVSF_NEEDPAYLOAD;
504
505 rpmtsSetScriptFd(ts, scriptFd);
506 ovsflags = rpmtsSetVSFlags(ts, vsflags);
507 ec = rpmcliArgIter(ts, qva, argv);
508 rpmtsSetVSFlags(ts, ovsflags);
509 rpmtsSetScriptFd(ts, NULL);
510
511 if (qva->qva_showPackage == showVerifyPackage)
512 qva->qva_showPackage = NULL;
513
514 rpmtsEmpty(ts);
515
516 if (rpmChrootOut() || rpmChrootSet(NULL))
517 ec = 1;
518
519 exit:
520 Fclose(scriptFd);
521
522 return ec;
523 }
524