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