Sanitize python object -> tag number exception handling
[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 <rpm/rpmcli.h>
9 #include <rpm/header.h>
10 #include <rpm/rpmlog.h>
11 #include <rpm/rpmfi.h>
12 #include <rpm/rpmts.h>
13 #include <rpm/rpmdb.h>
14 #include <rpm/rpmfileutil.h>
15
16 #include "lib/psm.h"
17 #include "lib/misc.h"   /* uidToUname(), gnameToGid */
18 #include "lib/rpmte_internal.h" /* rpmteOpen(), rpmteClose() */
19
20 #include "debug.h"
21
22 #define S_ISDEV(m) (S_ISBLK((m)) || S_ISCHR((m)))
23
24 extern int _rpmds_unspecified_epoch_noise;
25 extern int _cacheDependsRC;
26
27 /* If cap_compare() (Linux extension) not available, do it the hard way */
28 #if WITH_CAP && !defined(HAVE_CAP_COMPARE)
29 static int cap_compare(cap_t acap, cap_t bcap)
30 {
31     int rc = 0;
32     size_t asize = cap_size(acap);
33     size_t bsize = cap_size(bcap);
34
35     if (asize != bsize) {
36         rc = 1;
37     } else {
38         char *abuf = xcalloc(asize, sizeof(*abuf));
39         char *bbuf = xcalloc(bsize, sizeof(*bbuf));
40         cap_copy_ext(abuf, acap, asize);
41         cap_copy_ext(bbuf, bcap, bsize);
42         rc = memcmp(abuf, bbuf, asize);
43         free(abuf);
44         free(bbuf);
45     }
46     return rc;
47 }
48 #endif
49         
50 int rpmVerifyFile(const rpmts ts, const rpmfi fi,
51                 rpmVerifyAttrs * res, rpmVerifyAttrs omitMask)
52 {
53     rpm_mode_t fmode = rpmfiFMode(fi);
54     rpmfileAttrs fileAttrs = rpmfiFFlags(fi);
55     rpmVerifyAttrs flags = rpmfiVFlags(fi);
56     const char * fn = rpmfiFN(fi);
57     struct stat sb;
58     int rc;
59
60     *res = RPMVERIFY_NONE;
61
62     /*
63      * Check to see if the file was installed - if not pretend all is OK.
64      */
65     switch (rpmfiFState(fi)) {
66     case RPMFILE_STATE_NETSHARED:
67     case RPMFILE_STATE_REPLACED:
68     case RPMFILE_STATE_NOTINSTALLED:
69     case RPMFILE_STATE_WRONGCOLOR:
70         return 0;
71         break;
72     case RPMFILE_STATE_NORMAL:
73         break;
74     }
75
76     if (fn == NULL || lstat(fn, &sb) != 0) {
77         *res |= RPMVERIFY_LSTATFAIL;
78         return 1;
79     }
80
81     /*
82      * Not all attributes of non-regular files can be verified.
83      */
84     if (S_ISDIR(sb.st_mode))
85         flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
86                         RPMVERIFY_LINKTO | RPMVERIFY_CAPS);
87     else if (S_ISLNK(sb.st_mode)) {
88         flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
89                         RPMVERIFY_MODE | RPMVERIFY_CAPS);
90 #if CHOWN_FOLLOWS_SYMLINK
91             flags &= ~(RPMVERIFY_USER | RPMVERIFY_GROUP);
92 #endif
93     }
94     else if (S_ISFIFO(sb.st_mode))
95         flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
96                         RPMVERIFY_LINKTO | RPMVERIFY_CAPS);
97     else if (S_ISCHR(sb.st_mode))
98         flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
99                         RPMVERIFY_LINKTO | RPMVERIFY_CAPS);
100     else if (S_ISBLK(sb.st_mode))
101         flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
102                         RPMVERIFY_LINKTO | RPMVERIFY_CAPS);
103     else 
104         flags &= ~(RPMVERIFY_LINKTO);
105
106     /*
107      * Content checks of %ghost files are meaningless.
108      */
109     if (fileAttrs & RPMFILE_GHOST)
110         flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
111                         RPMVERIFY_LINKTO);
112
113     /*
114      * Don't verify any features in omitMask.
115      */
116     flags &= ~(omitMask | RPMVERIFY_FAILURES);
117
118
119     if (flags & RPMVERIFY_FILEDIGEST) {
120         const unsigned char *digest; 
121         pgpHashAlgo 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         /* Filter out timestamp differences of shared files */
235         rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, fn, 0);
236         if (rpmdbGetIteratorCount(mi) < 2) 
237             *res |= RPMVERIFY_MTIME;
238         rpmdbFreeIterator(mi);
239     }
240
241     if (flags & RPMVERIFY_USER) {
242         const char * name = uidToUname(sb.st_uid);
243         const char * fuser = rpmfiFUser(fi);
244         if (name == NULL || fuser == NULL || !rstreq(name, fuser))
245             *res |= RPMVERIFY_USER;
246     }
247
248     if (flags & RPMVERIFY_GROUP) {
249         const char * name = gidToGname(sb.st_gid);
250         const char * fgroup = rpmfiFGroup(fi);
251         if (name == NULL || fgroup == NULL || !rstreq(name, fgroup))
252             *res |= RPMVERIFY_GROUP;
253     }
254
255     return 0;
256 }
257
258 /**
259  * Return exit code from running verify script from header.
260  * @todo malloc/free/refcount handling is fishy here.
261  * @param qva           parsed query/verify options
262  * @param ts            transaction set
263  * @param h             header
264  * @param scriptFd      file handle to use for stderr (or NULL)
265  * @return              0 on success
266  */
267 static int rpmVerifyScript(QVA_t qva, rpmts ts, Header h, FD_t scriptFd)
268 {
269     rpmpsm psm = NULL;
270     rpmte te = NULL;
271     int rc = 0;
272
273     /* fake up a erasure transaction element */
274     rc = rpmtsAddEraseElement(ts, h, -1);
275     te = rpmtsElement(ts, 0);
276     rpmteOpen(te, ts, 0);
277     
278     if (scriptFd != NULL)
279         rpmtsSetScriptFd(ts, scriptFd);
280
281     /* create psm to run the script */
282     psm = rpmpsmNew(ts, te);
283     rpmpsmScriptStage(psm, RPMTAG_VERIFYSCRIPT, RPMTAG_VERIFYSCRIPTPROG);
284     rc = rpmpsmStage(psm, PSM_SCRIPT);
285     psm = rpmpsmFree(psm);
286
287     if (scriptFd != NULL)
288         rpmtsSetScriptFd(ts, NULL);
289
290     /* clean up our fake transaction bits */
291     rpmteClose(te, ts, 0);
292     rpmtsEmpty(ts);
293
294     return rc;
295 }
296
297 /**
298  * Check file info from header against what's actually installed.
299  * @param qva           parsed query/verify options
300  * @param ts            transaction set
301  * @param h             header to verify
302  * @return              0 no problems, 1 problems found
303  */
304 static int verifyHeader(QVA_t qva, const rpmts ts, Header h)
305 {
306     rpmVerifyAttrs verifyResult = 0;
307     /* FIX: union? */
308     rpmVerifyAttrs omitMask = ((qva->qva_flags & VERIFY_ATTRS) ^ VERIFY_ATTRS);
309     int ec = 0;         /* assume no problems */
310     char *buf = NULL;
311
312     rpmfi fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_FLAGS_VERIFY);
313     rpmfiInit(fi, 0);
314     while (rpmfiNext(fi) >= 0) {
315         rpmfileAttrs fileAttrs;
316         int rc;
317
318         fileAttrs = rpmfiFFlags(fi);
319
320         /* If not verifying %ghost, skip ghost files. */
321         if (!(qva->qva_fflags & RPMFILE_GHOST)
322         && (fileAttrs & RPMFILE_GHOST))
323             continue;
324
325         rc = rpmVerifyFile(ts, fi, &verifyResult, omitMask);
326         if (rc) {
327             if (!(fileAttrs & (RPMFILE_MISSINGOK|RPMFILE_GHOST)) || rpmIsVerbose()) {
328                 rasprintf(&buf, _("missing   %c %s"),
329                         ((fileAttrs & RPMFILE_CONFIG)   ? 'c' :
330                          (fileAttrs & RPMFILE_DOC)      ? 'd' :
331                          (fileAttrs & RPMFILE_GHOST)    ? 'g' :
332                          (fileAttrs & RPMFILE_LICENSE)  ? 'l' :
333                          (fileAttrs & RPMFILE_PUBKEY)   ? 'P' :
334                          (fileAttrs & RPMFILE_README)   ? 'r' : ' '), 
335                         rpmfiFN(fi));
336                 if ((verifyResult & RPMVERIFY_LSTATFAIL) != 0 &&
337                     errno != ENOENT) {
338                     char *app;
339                     rasprintf(&app, " (%s)", strerror(errno));
340                     rstrcat(&buf, app);
341                     free(app);
342                 }
343                 ec = rc;
344             }
345         } else if (verifyResult || rpmIsVerbose()) {
346             const char * size, * filedigest, * link, * mtime, * mode;
347             const char * group, * user, * rdev, *caps;
348             static const char *const aok = ".";
349             static const char *const unknown = "?";
350
351             ec = (verifyResult != 0);
352
353 #define _verify(_RPMVERIFY_F, _C)       \
354         ((verifyResult & _RPMVERIFY_F) ? _C : aok)
355 #define _verifylink(_RPMVERIFY_F, _C)   \
356         ((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
357          (verifyResult & _RPMVERIFY_F) ? _C : aok)
358 #define _verifyfile(_RPMVERIFY_F, _C)   \
359         ((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
360          (verifyResult & _RPMVERIFY_F) ? _C : aok)
361         
362             filedigest = _verifyfile(RPMVERIFY_FILEDIGEST, "5");
363             size = _verify(RPMVERIFY_FILESIZE, "S");
364             link = _verifylink(RPMVERIFY_LINKTO, "L");
365             mtime = _verify(RPMVERIFY_MTIME, "T");
366             rdev = _verify(RPMVERIFY_RDEV, "D");
367             user = _verify(RPMVERIFY_USER, "U");
368             group = _verify(RPMVERIFY_GROUP, "G");
369             mode = _verify(RPMVERIFY_MODE, "M");
370             caps = _verify(RPMVERIFY_CAPS, "P");
371
372 #undef _verifyfile
373 #undef _verifylink
374 #undef _verify
375
376             rasprintf(&buf, "%s%s%s%s%s%s%s%s%s  %c %s",
377                         size, mode, filedigest, rdev, link, user, group, mtime, caps,
378                         ((fileAttrs & RPMFILE_CONFIG)   ? 'c' :
379                          (fileAttrs & RPMFILE_DOC)      ? 'd' :
380                          (fileAttrs & RPMFILE_GHOST)    ? 'g' :
381                          (fileAttrs & RPMFILE_LICENSE)  ? 'l' :
382                          (fileAttrs & RPMFILE_PUBKEY)   ? 'P' :
383                          (fileAttrs & RPMFILE_README)   ? 'r' : ' '), 
384                         rpmfiFN(fi));
385         }
386
387         if (buf) {
388             rpmlog(RPMLOG_NOTICE, "%s\n", buf);
389             buf = _free(buf);
390         }
391     }
392     rpmfiFree(fi);
393         
394     return ec;
395 }
396
397 /**
398  * Check installed package dependencies for problems.
399  * @param qva           parsed query/verify options
400  * @param ts            transaction set
401  * @param h             header
402  * @return              number of problems found (0 for no problems)
403  */
404 static int verifyDependencies(QVA_t qva, rpmts ts,
405                 Header h)
406 {
407     rpmps ps;
408     rpmpsi psi;
409     int rc = 0;         /* assume no problems */
410     int xx;
411
412     rpmtsEmpty(ts);
413     (void) rpmtsAddInstallElement(ts, h, NULL, 0, NULL);
414
415     xx = rpmtsCheck(ts);
416     ps = rpmtsProblems(ts);
417
418     psi = rpmpsInitIterator(ps);
419     if (rpmpsNumProblems(ps) > 0) {
420         char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
421         rpmlog(RPMLOG_NOTICE, _("Unsatisfied dependencies for %s:\n"), nevra);
422         free(nevra);
423         while (rpmpsNextIterator(psi) >= 0) {
424             rpmProblem p = rpmpsGetProblem(psi);
425             char * ps = rpmProblemString(p);
426             rpmlog(RPMLOG_NOTICE, "\t%s\n", ps);
427             free(ps);
428             rc++;       
429         }
430     }
431     psi = rpmpsFreeIterator(psi);
432     ps = rpmpsFree(ps);
433
434     rpmtsEmpty(ts);
435
436     return rc;
437 }
438
439 int showVerifyPackage(QVA_t qva, rpmts ts, Header h)
440 {
441     int ec = 0;
442     int rc;
443
444     if (qva->qva_flags & VERIFY_DEPS) {
445         if ((rc = verifyDependencies(qva, ts, h)) != 0)
446             ec = rc;
447     }
448     if (qva->qva_flags & VERIFY_FILES) {
449         if ((rc = verifyHeader(qva, ts, h)) != 0)
450             ec = rc;
451     }
452     if ((qva->qva_flags & VERIFY_SCRIPT)
453      && headerIsEntry(h, RPMTAG_VERIFYSCRIPT))
454     {
455         FD_t fdo = fdDup(STDOUT_FILENO);
456         if ((rc = rpmVerifyScript(qva, ts, h, fdo)) != 0)
457             ec = rc;
458         if (fdo != NULL)
459             rc = Fclose(fdo);
460     }
461
462     return ec;
463 }
464
465 int rpmcliVerify(rpmts ts, QVA_t qva, char * const * argv)
466 {
467     rpmVSFlags vsflags, ovsflags;
468     int ec = 0, xx;
469     const char * rootDir = rpmtsRootDir(ts);
470
471     /* 
472      * Open the DB + indices explicitly before possible chroot,
473      * otherwises BDB is going to be unhappy...
474      */
475     rpmtsOpenDB(ts, O_RDONLY);
476     rpmdbOpenAll(rpmtsGetRdb(ts));
477     if (rootDir && !rstreq(rootDir, "/")) {
478         if (chroot(rootDir) == -1) {
479             rpmlog(RPMLOG_ERR, _("Unable to change root directory: %m\n"));
480             ec = 1;
481             goto exit;
482         } else {
483             rpmtsSetChrootDone(ts, 1);
484         }
485     }
486
487     if (qva->qva_showPackage == NULL)
488         qva->qva_showPackage = showVerifyPackage;
489
490     /* XXX verify flags are inverted from query. */
491     vsflags = rpmExpandNumeric("%{?_vsflags_verify}");
492     if (!(qva->qva_flags & VERIFY_DIGEST))
493         vsflags |= _RPMVSF_NODIGESTS;
494     if (!(qva->qva_flags & VERIFY_SIGNATURE))
495         vsflags |= _RPMVSF_NOSIGNATURES;
496     if (!(qva->qva_flags & VERIFY_HDRCHK))
497         vsflags |= RPMVSF_NOHDRCHK;
498     vsflags &= ~RPMVSF_NEEDPAYLOAD;
499
500     ovsflags = rpmtsSetVSFlags(ts, vsflags);
501     ec = rpmcliArgIter(ts, qva, argv);
502     vsflags = rpmtsSetVSFlags(ts, ovsflags);
503
504     if (qva->qva_showPackage == showVerifyPackage)
505         qva->qva_showPackage = NULL;
506
507     rpmtsEmpty(ts);
508
509     if (rpmtsChrootDone(ts)) {
510         /* only done if previous chroot succeeded, assume success */
511         xx = chroot(".");
512         rpmtsSetChrootDone(ts, 0);
513     }
514
515 exit:
516
517     return ec;
518 }