upgrade rpm version to 4.14.1
[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 rpmVerifyAttrs rpmfilesVerify(rpmfiles fi, int ix, rpmVerifyAttrs omitMask)
57 {
58     rpm_mode_t fmode = rpmfilesFMode(fi, ix);
59     rpmfileAttrs fileAttrs = rpmfilesFFlags(fi, ix);
60     rpmVerifyAttrs flags = rpmfilesVFlags(fi, ix);
61     const char * fn = rpmfilesFN(fi, ix);
62     struct stat sb;
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) {
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(fmode) && 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             uid_t fuid;
102             /* ...and is by a legit user, to match fsmVerify() behavior */
103             if (sb.st_uid == 0 ||
104                         (rpmugUid(rpmfilesFUser(fi, ix), &fuid) == 0 &&
105                          sb.st_uid == fuid)) {
106                 sb = dsb; /* struct assignment */
107             }
108         }
109     }
110
111     /* Links have no mode, other types have no linkto */
112     if (S_ISLNK(sb.st_mode))
113         flags &= ~(RPMVERIFY_MODE);
114     else
115         flags &= ~(RPMVERIFY_LINKTO);
116
117     /* Not all attributes of non-regular files can be verified */
118     if (!S_ISREG(sb.st_mode))
119         flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
120                    RPMVERIFY_MTIME | RPMVERIFY_CAPS);
121
122     /* Content checks of %ghost files are meaningless. */
123     if (fileAttrs & RPMFILE_GHOST)
124         flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
125                    RPMVERIFY_MTIME | RPMVERIFY_LINKTO);
126
127     /* Don't verify any features in omitMask. */
128     flags &= ~(omitMask | RPMVERIFY_FAILURES);
129
130
131     if (flags & RPMVERIFY_FILEDIGEST) {
132         const unsigned char *digest; 
133         int algo;
134         size_t diglen;
135
136         /* XXX If --nomd5, then prelinked library sizes are not corrected. */
137         if ((digest = rpmfilesFDigest(fi, ix, &algo, &diglen))) {
138             unsigned char fdigest[diglen];
139             rpm_loff_t fsize;
140
141             if (rpmDoDigest(algo, fn, 0, fdigest, &fsize)) {
142                 vfy |= (RPMVERIFY_READFAIL|RPMVERIFY_FILEDIGEST);
143             } else {
144                 sb.st_size = fsize;
145                 if (memcmp(fdigest, digest, diglen))
146                     vfy |= RPMVERIFY_FILEDIGEST;
147             }
148         } else {
149             vfy |= RPMVERIFY_FILEDIGEST;
150         } 
151     } 
152
153     if (flags & RPMVERIFY_LINKTO) {
154         char linkto[1024+1];
155         int size = 0;
156
157         if ((size = readlink(fn, linkto, sizeof(linkto)-1)) == -1)
158             vfy |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO);
159         else {
160             const char * flink = rpmfilesFLink(fi, ix);
161             linkto[size] = '\0';
162             if (flink == NULL || !rstreq(linkto, flink))
163                 vfy |= RPMVERIFY_LINKTO;
164         }
165     } 
166
167     if (flags & RPMVERIFY_FILESIZE) {
168         if (sb.st_size != rpmfilesFSize(fi, ix))
169             vfy |= RPMVERIFY_FILESIZE;
170     } 
171
172     if (flags & RPMVERIFY_MODE) {
173         rpm_mode_t metamode = fmode;
174         rpm_mode_t filemode;
175
176         /*
177          * Platforms (like AIX) where sizeof(rpm_mode_t) != sizeof(mode_t)
178          * need the (rpm_mode_t) cast here. 
179          */
180         filemode = (rpm_mode_t)sb.st_mode;
181
182         /*
183          * Comparing the type of %ghost files is meaningless, but perms are OK.
184          */
185         if (fileAttrs & RPMFILE_GHOST) {
186             metamode &= ~0xf000;
187             filemode &= ~0xf000;
188         }
189
190         if (metamode != filemode)
191             vfy |= RPMVERIFY_MODE;
192
193 #if WITH_ACL
194         /*
195          * For now, any non-default acl's on a file is a difference as rpm
196          * cannot have set them.
197          */
198         acl_t facl = acl_get_file(fn, ACL_TYPE_ACCESS);
199         if (facl) {
200             if (acl_equiv_mode(facl, NULL) == 1) {
201                 vfy |= RPMVERIFY_MODE;
202             }
203             acl_free(facl);
204         }
205 #endif 
206     }
207
208     if (flags & RPMVERIFY_RDEV) {
209         if (S_ISCHR(fmode) != S_ISCHR(sb.st_mode)
210          || S_ISBLK(fmode) != S_ISBLK(sb.st_mode))
211         {
212             vfy |= RPMVERIFY_RDEV;
213         } else if (S_ISDEV(fmode) && S_ISDEV(sb.st_mode)) {
214             rpm_rdev_t st_rdev = (sb.st_rdev & 0xffff);
215             rpm_rdev_t frdev = (rpmfilesFRdev(fi, ix) & 0xffff);
216             if (st_rdev != frdev)
217                 vfy |= RPMVERIFY_RDEV;
218         } 
219     }
220
221 #if WITH_CAP
222     if (flags & RPMVERIFY_CAPS) {
223         /*
224          * Empty capability set ("=") is not exactly the same as no
225          * capabilities at all but suffices for now... 
226          */
227         cap_t cap, fcap;
228         cap = cap_from_text(rpmfilesFCaps(fi, ix));
229         if (!cap) {
230             cap = cap_from_text("=");
231         }
232         fcap = cap_get_file(fn);
233         if (!fcap) {
234             fcap = cap_from_text("=");
235         }
236         
237         if (cap_compare(cap, fcap) != 0)
238             vfy |= RPMVERIFY_CAPS;
239
240         cap_free(fcap);
241         cap_free(cap);
242     }
243 #endif
244
245     if ((flags & RPMVERIFY_MTIME) && (sb.st_mtime != rpmfilesFMtime(fi, ix))) {
246         vfy |= RPMVERIFY_MTIME;
247     }
248
249     if (flags & RPMVERIFY_USER) {
250         const char * name = rpmugUname(sb.st_uid);
251         const char * fuser = rpmfilesFUser(fi, ix);
252         uid_t uid;
253         int namematch = 0;
254         int idmatch = 0;
255
256         if (name && fuser)
257            namematch =  rstreq(name, fuser);
258         if (fuser && rpmugUid(fuser, &uid) == 0)
259             idmatch = (uid == sb.st_uid);
260
261         if (namematch != idmatch) {
262             rpmlog(RPMLOG_WARNING,
263                     _("Duplicate username or UID for user %s\n"), fuser);
264         }
265
266         if (!(namematch || idmatch))
267             vfy |= RPMVERIFY_USER;
268     }
269
270     if (flags & RPMVERIFY_GROUP) {
271         const char * name = rpmugGname(sb.st_gid);
272         const char * fgroup = rpmfilesFGroup(fi, ix);
273         gid_t gid;
274         int namematch = 0;
275         int idmatch = 0;
276
277         if (name && fgroup)
278             namematch = rstreq(name, fgroup);
279         if (fgroup && rpmugGid(fgroup, &gid) == 0)
280             idmatch = (gid == sb.st_gid);
281
282         if (namematch != idmatch) {
283             rpmlog(RPMLOG_WARNING,
284                     _("Duplicate groupname or GID for group %s\n"), fgroup);
285         }
286
287         if (!(namematch || idmatch))
288             vfy |= RPMVERIFY_GROUP;
289     }
290
291 exit:
292     return vfy;
293 }
294
295 int rpmVerifyFile(const rpmts ts, const rpmfi fi,
296                 rpmVerifyAttrs * res, rpmVerifyAttrs omitMask)
297 {
298     rpmVerifyAttrs vfy = rpmfiVerify(fi, omitMask);
299     if (res)
300         *res = vfy;
301
302     return (vfy & RPMVERIFY_LSTATFAIL) ? 1 : 0;
303 }
304
305 /**
306  * Return exit code from running verify script from header.
307  * @param ts            transaction set
308  * @param h             header
309  * @return              0 on success
310  */
311 static int rpmVerifyScript(rpmts ts, Header h)
312 {
313     int rc = 0;
314
315     if (headerIsEntry(h, RPMTAG_VERIFYSCRIPT)) {
316         /* fake up a erasure transaction element */
317         rpmte p = rpmteNew(ts, h, TR_REMOVED, NULL, NULL);
318
319         if (p != NULL) {
320             rpmteSetHeader(p, h);
321
322             rc = (rpmpsmRun(ts, p, PKG_VERIFY) != RPMRC_OK);
323
324             /* clean up our fake transaction bits */
325             rpmteFree(p);
326         } else {
327             rc = RPMRC_FAIL;
328         }
329     }
330
331     return rc;
332 }
333
334 #define unknown "?"
335 #define _verify(_RPMVERIFY_F, _C, _pad) \
336         ((verifyResult & _RPMVERIFY_F) ? _C : _pad)
337 #define _verifylink(_RPMVERIFY_F, _C, _pad)     \
338         ((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
339          (verifyResult & _RPMVERIFY_F) ? _C : _pad)
340 #define _verifyfile(_RPMVERIFY_F, _C, _pad)     \
341         ((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
342          (verifyResult & _RPMVERIFY_F) ? _C : _pad)
343 char * rpmVerifyString(uint32_t verifyResult, const char *pad)
344 {
345     char *fmt = NULL;
346     rasprintf(&fmt, "%s%s%s%s%s%s%s%s%s",
347                 _verify(RPMVERIFY_FILESIZE, "S", pad),
348                 _verify(RPMVERIFY_MODE, "M", pad),
349                 _verifyfile(RPMVERIFY_FILEDIGEST, "5", pad),
350                 _verify(RPMVERIFY_RDEV, "D", pad),
351                 _verifylink(RPMVERIFY_LINKTO, "L", pad),
352                 _verify(RPMVERIFY_USER, "U", pad),
353                 _verify(RPMVERIFY_GROUP, "G", pad),
354                 _verify(RPMVERIFY_MTIME, "T", pad),
355                 _verify(RPMVERIFY_CAPS, "P", pad));
356                 
357     return fmt;
358 }
359 #undef _verifyfile
360 #undef _verifylink
361 #undef _verify
362 #undef aok
363 #undef unknown
364
365 char * rpmFFlagsString(uint32_t fflags, const char *pad)
366 {
367     char *fmt = NULL;
368     rasprintf(&fmt, "%s%s%s%s%s%s%s%s%s",
369                 (fflags & RPMFILE_DOC) ? "d" : pad,
370                 (fflags & RPMFILE_CONFIG) ? "c" : pad,
371                 (fflags & RPMFILE_SPECFILE) ? "s" : pad,
372                 (fflags & RPMFILE_MISSINGOK) ? "m" : pad,
373                 (fflags & RPMFILE_NOREPLACE) ? "n" : pad,
374                 (fflags & RPMFILE_GHOST) ? "g" : pad,
375                 (fflags & RPMFILE_LICENSE) ? "l" : pad,
376                 (fflags & RPMFILE_README) ? "r" : pad,
377                 (fflags & RPMFILE_ARTIFACT) ? "a" : pad);
378     return fmt;
379 }
380
381 static const char * stateStr(rpmfileState fstate)
382 {
383     switch (fstate) {
384     case RPMFILE_STATE_NORMAL:
385         return NULL;
386     case RPMFILE_STATE_NOTINSTALLED:
387         return rpmIsVerbose() ? _("not installed") : NULL;
388     case RPMFILE_STATE_NETSHARED:
389         return rpmIsVerbose() ? _("net shared") : NULL;
390     case RPMFILE_STATE_WRONGCOLOR:
391         return rpmIsVerbose() ? _("wrong color") : NULL;
392     case RPMFILE_STATE_REPLACED:
393         return _("replaced");
394     case RPMFILE_STATE_MISSING:
395         return _("no state");
396     }
397     return _("unknown state");
398 }
399
400 /**
401  * Check file info from header against what's actually installed.
402  * @param ts            transaction set
403  * @param h             header to verify
404  * @param omitMask      bits to disable verify checks
405  * @param skipAttr      skip files with these attrs (eg %ghost)
406  * @return              0 no problems, 1 problems found
407  */
408 static int verifyHeader(rpmts ts, Header h, rpmVerifyAttrs omitMask,
409                         rpmfileAttrs skipAttrs)
410 {
411     rpmVerifyAttrs verifyResult = 0;
412     rpmVerifyAttrs verifyAll = 0; /* assume no problems */
413     rpmfi fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_FLAGS_VERIFY);
414
415     if (fi == NULL)
416         return 1;
417
418     rpmfiInit(fi, 0);
419     while (rpmfiNext(fi) >= 0) {
420         rpmfileAttrs fileAttrs = rpmfiFFlags(fi);
421         char *buf = NULL, *attrFormat;
422         const char *fstate = NULL;
423         char ac;
424
425         /* Skip on attributes (eg from --noghost) */
426         if (skipAttrs & fileAttrs)
427             continue;
428
429         verifyResult = rpmfiVerify(fi, omitMask);
430
431         /* Filter out timestamp differences of shared files */
432         if (verifyResult & RPMVERIFY_MTIME) {
433             rpmdbMatchIterator mi;
434             mi = rpmtsInitIterator(ts, RPMDBI_BASENAMES, rpmfiFN(fi), 0);
435             if (rpmdbGetIteratorCount(mi) > 1) 
436                 verifyResult &= ~RPMVERIFY_MTIME;
437             rpmdbFreeIterator(mi);
438         }
439
440         /* State is only meaningful for installed packages */
441         if (headerGetInstance(h))
442             fstate = stateStr(rpmfiFState(fi));
443
444         attrFormat = rpmFFlagsString(fileAttrs, "");
445         ac = rstreq(attrFormat, "") ? ' ' : attrFormat[0];
446         if (verifyResult & RPMVERIFY_LSTATFAIL) {
447             if (!(fileAttrs & (RPMFILE_MISSINGOK|RPMFILE_GHOST)) || rpmIsVerbose()) {
448                 rasprintf(&buf, _("missing   %c %s"), ac, rpmfiFN(fi));
449                 if ((verifyResult & RPMVERIFY_LSTATFAIL) != 0 &&
450                     errno != ENOENT) {
451                     char *app;
452                     rasprintf(&app, " (%s)", strerror(errno));
453                     rstrcat(&buf, app);
454                     free(app);
455                 }
456             }
457         } else if (verifyResult || fstate || rpmIsVerbose()) {
458             char *verifyFormat = rpmVerifyString(verifyResult, ".");
459             rasprintf(&buf, "%s  %c %s", verifyFormat, ac, rpmfiFN(fi));
460             free(verifyFormat);
461         }
462         free(attrFormat);
463
464         if (buf) {
465             if (fstate)
466                 buf = rstrscat(&buf, " (", fstate, ")", NULL);
467             rpmlog(RPMLOG_NOTICE, "%s\n", buf);
468             buf = _free(buf);
469         }
470
471         /* Filter out missing %ghost/%missingok errors from final result */
472         if (fileAttrs & (RPMFILE_MISSINGOK|RPMFILE_GHOST))
473             verifyResult &= ~RPMVERIFY_LSTATFAIL;
474
475         verifyAll |= verifyResult;
476     }
477     rpmfiFree(fi);
478         
479     return (verifyAll != 0) ? 1 : 0;
480 }
481
482 /**
483  * Check installed package dependencies for problems.
484  * @param ts            transaction set
485  * @param h             header
486  * @return              number of problems found (0 for no problems)
487  */
488 static int verifyDependencies(rpmts ts, Header h)
489 {
490     rpmps ps;
491     rpmte te;
492     int rc;
493
494     rpmtsEmpty(ts);
495     (void) rpmtsAddInstallElement(ts, h, NULL, 0, NULL);
496
497     (void) rpmtsCheck(ts);
498     te = rpmtsElement(ts, 0);
499     ps = rpmteProblems(te);
500     rc = rpmpsNumProblems(ps);
501
502     if (rc > 0) {
503         rpmlog(RPMLOG_NOTICE, _("Unsatisfied dependencies for %s:\n"),
504                rpmteNEVRA(te));
505         rpmpsi psi = rpmpsInitIterator(ps);
506         rpmProblem p;
507
508         while ((p = rpmpsiNext(psi)) != NULL) {
509             char * ps = rpmProblemString(p);
510             rpmlog(RPMLOG_NOTICE, "\t%s\n", ps);
511             free(ps);
512         }
513         rpmpsFreeIterator(psi);
514     }
515     rpmpsFree(ps);
516     rpmtsEmpty(ts);
517
518     return rc;
519 }
520
521 int showVerifyPackage(QVA_t qva, rpmts ts, Header h)
522 {
523     rpmVerifyAttrs omitMask = ((qva->qva_flags & VERIFY_ATTRS) ^ VERIFY_ATTRS);
524     int ec = 0;
525     int rc;
526
527     if (qva->qva_flags & VERIFY_DEPS) {
528         if ((rc = verifyDependencies(ts, h)) != 0)
529             ec = rc;
530     }
531     if (qva->qva_flags & VERIFY_FILES) {
532         if ((rc = verifyHeader(ts, h, omitMask, qva->qva_fflags)) != 0)
533             ec = rc;
534     }
535     if (qva->qva_flags & VERIFY_SCRIPT) {
536         if ((rc = rpmVerifyScript(ts, h)) != 0)
537             ec = rc;
538     }
539
540     return ec;
541 }
542
543 int rpmcliVerify(rpmts ts, QVA_t qva, char * const * argv)
544 {
545     rpmVSFlags vsflags, ovsflags;
546     int ec = 0;
547     FD_t scriptFd = fdDup(STDOUT_FILENO);
548
549     /* 
550      * Open the DB + indices explicitly before possible chroot,
551      * otherwises BDB is going to be unhappy...
552      */
553     rpmtsOpenDB(ts, O_RDONLY);
554     rpmdbOpenAll(rpmtsGetRdb(ts));
555     if (rpmChrootSet(rpmtsRootDir(ts)) || rpmChrootIn()) {
556         ec = 1;
557         goto exit;
558     }
559
560     if (qva->qva_showPackage == NULL)
561         qva->qva_showPackage = showVerifyPackage;
562
563     vsflags = rpmExpandNumeric("%{?_vsflags_verify}");
564     if (rpmcliQueryFlags & VERIFY_DIGEST)
565         vsflags |= _RPMVSF_NODIGESTS;
566     if (rpmcliQueryFlags & VERIFY_SIGNATURE)
567         vsflags |= _RPMVSF_NOSIGNATURES;
568     if (rpmcliQueryFlags & VERIFY_HDRCHK)
569         vsflags |= RPMVSF_NOHDRCHK;
570     vsflags &= ~RPMVSF_NEEDPAYLOAD;
571
572     rpmtsSetScriptFd(ts, scriptFd);
573     ovsflags = rpmtsSetVSFlags(ts, vsflags);
574     ec = rpmcliArgIter(ts, qva, argv);
575     rpmtsSetVSFlags(ts, ovsflags);
576     rpmtsSetScriptFd(ts, NULL);
577
578     if (qva->qva_showPackage == showVerifyPackage)
579         qva->qva_showPackage = NULL;
580
581     rpmtsEmpty(ts);
582
583     if (rpmChrootOut() || rpmChrootSet(NULL))
584         ec = 1;
585
586 exit:
587     Fclose(scriptFd);
588
589     return ec;
590 }