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