On a second thought, rename rpmfiDigest() to rpmfiFDigest() for consistency
[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 "rpmio/ugid.h"         /* uidToUname(), gnameToGid */
18
19 #include "debug.h"
20
21 #define S_ISDEV(m) (S_ISBLK((m)) || S_ISCHR((m)))
22
23 extern int _rpmds_unspecified_epoch_noise;
24
25 int rpmVerifyFile(const rpmts ts, const rpmfi fi,
26                 rpmVerifyAttrs * res, rpmVerifyAttrs omitMask)
27 {
28     rpm_mode_t fmode = rpmfiFMode(fi);
29     rpmfileAttrs fileAttrs = rpmfiFFlags(fi);
30     rpmVerifyAttrs flags = rpmfiVFlags(fi);
31     const char * fn = rpmfiFN(fi);
32     struct stat sb;
33     int rc;
34
35     *res = RPMVERIFY_NONE;
36
37     /*
38      * Check to see if the file was installed - if not pretend all is OK.
39      */
40     switch (rpmfiFState(fi)) {
41     case RPMFILE_STATE_NETSHARED:
42     case RPMFILE_STATE_REPLACED:
43     case RPMFILE_STATE_NOTINSTALLED:
44     case RPMFILE_STATE_WRONGCOLOR:
45         return 0;
46         break;
47     case RPMFILE_STATE_NORMAL:
48         break;
49     }
50
51     if (fn == NULL || lstat(fn, &sb) != 0) {
52         *res |= RPMVERIFY_LSTATFAIL;
53         return 1;
54     }
55
56     /*
57      * Not all attributes of non-regular files can be verified.
58      */
59     if (S_ISDIR(sb.st_mode))
60         flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | 
61                         RPMVERIFY_LINKTO);
62     else if (S_ISLNK(sb.st_mode)) {
63         flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
64                 RPMVERIFY_MODE);
65 #if CHOWN_FOLLOWS_SYMLINK
66             flags &= ~(RPMVERIFY_USER | RPMVERIFY_GROUP);
67 #endif
68     }
69     else if (S_ISFIFO(sb.st_mode))
70         flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | 
71                         RPMVERIFY_LINKTO);
72     else if (S_ISCHR(sb.st_mode))
73         flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | 
74                         RPMVERIFY_LINKTO);
75     else if (S_ISBLK(sb.st_mode))
76         flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | 
77                         RPMVERIFY_LINKTO);
78     else 
79         flags &= ~(RPMVERIFY_LINKTO);
80
81     /*
82      * Content checks of %ghost files are meaningless.
83      */
84     if (fileAttrs & RPMFILE_GHOST)
85         flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | 
86                         RPMVERIFY_LINKTO);
87
88     /*
89      * Don't verify any features in omitMask.
90      */
91     flags &= ~(omitMask | RPMVERIFY_FAILURES);
92
93
94     if (flags & RPMVERIFY_MD5) {
95         const unsigned char *digest; 
96         pgpHashAlgo algo;
97         size_t diglen;
98
99         /* XXX If --nomd5, then prelinked library sizes are not corrected. */
100         if ((digest = rpmfiFDigest(fi, &algo, &diglen))) {
101             unsigned char fdigest[diglen];
102             rpm_off_t fsize;
103
104             rc = rpmDoDigest(algo, fn, 0, fdigest, &fsize);
105             sb.st_size = fsize;
106             if (rc) {
107                 *res |= (RPMVERIFY_READFAIL|RPMVERIFY_MD5);
108             } else if (memcmp(fdigest, digest, diglen)) {
109                 *res |= RPMVERIFY_MD5;
110             }
111         } else {
112             *res |= RPMVERIFY_MD5;
113         } 
114     } 
115
116     if (flags & RPMVERIFY_LINKTO) {
117         char linkto[1024+1];
118         int size = 0;
119
120         if ((size = readlink(fn, linkto, sizeof(linkto)-1)) == -1)
121             *res |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO);
122         else {
123             const char * flink = rpmfiFLink(fi);
124             linkto[size] = '\0';
125             if (flink == NULL || strcmp(linkto, flink))
126                 *res |= RPMVERIFY_LINKTO;
127         }
128     } 
129
130     if (flags & RPMVERIFY_FILESIZE) {
131         if (sb.st_size != rpmfiFSize(fi))
132             *res |= RPMVERIFY_FILESIZE;
133     } 
134
135     if (flags & RPMVERIFY_MODE) {
136         rpm_mode_t metamode = fmode;
137         rpm_mode_t filemode;
138
139         /*
140          * Platforms (like AIX) where sizeof(rpm_mode_t) != sizeof(mode_t)
141          * need the (rpm_mode_t) cast here. 
142          */
143         filemode = (rpm_mode_t)sb.st_mode;
144
145         /*
146          * Comparing the type of %ghost files is meaningless, but perms are OK.
147          */
148         if (fileAttrs & RPMFILE_GHOST) {
149             metamode &= ~0xf000;
150             filemode &= ~0xf000;
151         }
152
153         if (metamode != filemode)
154             *res |= RPMVERIFY_MODE;
155     }
156
157     if (flags & RPMVERIFY_RDEV) {
158         if (S_ISCHR(fmode) != S_ISCHR(sb.st_mode)
159          || S_ISBLK(fmode) != S_ISBLK(sb.st_mode))
160         {
161             *res |= RPMVERIFY_RDEV;
162         } else if (S_ISDEV(fmode) && S_ISDEV(sb.st_mode)) {
163             rpm_rdev_t st_rdev = (sb.st_rdev & 0xffff);
164             rpm_rdev_t frdev = (rpmfiFRdev(fi) & 0xffff);
165             if (st_rdev != frdev)
166                 *res |= RPMVERIFY_RDEV;
167         } 
168     }
169
170     if (flags & RPMVERIFY_MTIME) {
171         if (sb.st_mtime != rpmfiFMtime(fi))
172             *res |= RPMVERIFY_MTIME;
173     }
174
175     if (flags & RPMVERIFY_USER) {
176         const char * name = uidToUname(sb.st_uid);
177         const char * fuser = rpmfiFUser(fi);
178         if (name == NULL || fuser == NULL || strcmp(name, fuser))
179             *res |= RPMVERIFY_USER;
180     }
181
182     if (flags & RPMVERIFY_GROUP) {
183         const char * name = gidToGname(sb.st_gid);
184         const char * fgroup = rpmfiFGroup(fi);
185         if (name == NULL || fgroup == NULL || strcmp(name, fgroup))
186             *res |= RPMVERIFY_GROUP;
187     }
188
189     return 0;
190 }
191
192 /**
193  * Return exit code from running verify script from header.
194  * @todo malloc/free/refcount handling is fishy here.
195  * @param qva           parsed query/verify options
196  * @param ts            transaction set
197  * @param fi            file info set
198  * @param scriptFd      file handle to use for stderr (or NULL)
199  * @return              0 on success
200  */
201 static int rpmVerifyScript(QVA_t qva, rpmts ts,
202                 rpmfi fi, FD_t scriptFd)
203 {
204     rpmpsm psm = rpmpsmNew(ts, NULL, fi);
205     int rc = 0;
206
207     if (psm == NULL)    /* XXX can't happen */
208         return rc;
209
210     if (scriptFd != NULL)
211         rpmtsSetScriptFd(rpmpsmGetTs(psm), scriptFd);
212
213     rc = rpmpsmScriptStage(psm, RPMTAG_VERIFYSCRIPT, RPMTAG_VERIFYSCRIPTPROG);
214     rc = rpmpsmStage(psm, PSM_SCRIPT);
215
216     if (scriptFd != NULL)
217         rpmtsSetScriptFd(rpmpsmGetTs(psm), NULL);
218
219     psm = rpmpsmFree(psm);
220
221     return rc;
222 }
223
224 /**
225  * Check file info from header against what's actually installed.
226  * @param qva           parsed query/verify options
227  * @param ts            transaction set
228  * @param fi            file info set
229  * @return              0 no problems, 1 problems found
230  */
231 static int verifyHeader(QVA_t qva, const rpmts ts, rpmfi fi)
232 {
233     rpmVerifyAttrs verifyResult = 0;
234     /* FIX: union? */
235     rpmVerifyAttrs omitMask = ((qva->qva_flags & VERIFY_ATTRS) ^ VERIFY_ATTRS);
236     int ec = 0;         /* assume no problems */
237     char *buf = NULL;
238     int i;
239
240     fi = rpmfiLink(fi, RPMDBG_M("verifyHeader"));
241     fi = rpmfiInit(fi, 0);
242     if (fi != NULL)     /* XXX lclint */
243     while ((i = rpmfiNext(fi)) >= 0) {
244         rpmfileAttrs fileAttrs;
245         int rc;
246
247         fileAttrs = rpmfiFFlags(fi);
248
249         /* If not verifying %ghost, skip ghost files. */
250         if (!(qva->qva_fflags & RPMFILE_GHOST)
251         && (fileAttrs & RPMFILE_GHOST))
252             continue;
253
254         rc = rpmVerifyFile(ts, fi, &verifyResult, omitMask);
255         if (rc) {
256             if (!(fileAttrs & (RPMFILE_MISSINGOK|RPMFILE_GHOST)) || rpmIsVerbose()) {
257                 rasprintf(&buf, _("missing   %c %s"),
258                         ((fileAttrs & RPMFILE_CONFIG)   ? 'c' :
259                          (fileAttrs & RPMFILE_DOC)      ? 'd' :
260                          (fileAttrs & RPMFILE_GHOST)    ? 'g' :
261                          (fileAttrs & RPMFILE_LICENSE)  ? 'l' :
262                          (fileAttrs & RPMFILE_PUBKEY)   ? 'P' :
263                          (fileAttrs & RPMFILE_README)   ? 'r' : ' '), 
264                         rpmfiFN(fi));
265                 if ((verifyResult & RPMVERIFY_LSTATFAIL) != 0 &&
266                     errno != ENOENT) {
267                     char *app;
268                     rasprintf(&app, " (%s)", strerror(errno));
269                     rstrcat(&buf, app);
270                     free(app);
271                 }
272                 ec = rc;
273             }
274         } else if (verifyResult || rpmIsVerbose()) {
275             const char * size, * MD5, * link, * mtime, * mode;
276             const char * group, * user, * rdev;
277             static const char *const aok = ".";
278             static const char *const unknown = "?";
279
280             ec = 1;
281
282 #define _verify(_RPMVERIFY_F, _C)       \
283         ((verifyResult & _RPMVERIFY_F) ? _C : aok)
284 #define _verifylink(_RPMVERIFY_F, _C)   \
285         ((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
286          (verifyResult & _RPMVERIFY_F) ? _C : aok)
287 #define _verifyfile(_RPMVERIFY_F, _C)   \
288         ((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
289          (verifyResult & _RPMVERIFY_F) ? _C : aok)
290         
291             MD5 = _verifyfile(RPMVERIFY_MD5, "5");
292             size = _verify(RPMVERIFY_FILESIZE, "S");
293             link = _verifylink(RPMVERIFY_LINKTO, "L");
294             mtime = _verify(RPMVERIFY_MTIME, "T");
295             rdev = _verify(RPMVERIFY_RDEV, "D");
296             user = _verify(RPMVERIFY_USER, "U");
297             group = _verify(RPMVERIFY_GROUP, "G");
298             mode = _verify(RPMVERIFY_MODE, "M");
299
300 #undef _verifyfile
301 #undef _verifylink
302 #undef _verify
303
304             rasprintf(&buf, "%s%s%s%s%s%s%s%s  %c %s",
305                         size, mode, MD5, rdev, link, user, group, mtime,
306                         ((fileAttrs & RPMFILE_CONFIG)   ? 'c' :
307                          (fileAttrs & RPMFILE_DOC)      ? 'd' :
308                          (fileAttrs & RPMFILE_GHOST)    ? 'g' :
309                          (fileAttrs & RPMFILE_LICENSE)  ? 'l' :
310                          (fileAttrs & RPMFILE_PUBKEY)   ? 'P' :
311                          (fileAttrs & RPMFILE_README)   ? 'r' : ' '), 
312                         rpmfiFN(fi));
313         }
314
315         if (buf) {
316             rpmlog(RPMLOG_NOTICE, "%s\n", buf);
317             buf = _free(buf);
318         }
319     }
320     fi = rpmfiUnlink(fi, RPMDBG_M("verifyHeader"));
321         
322     return ec;
323 }
324
325 /**
326  * Check installed package dependencies for problems.
327  * @param qva           parsed query/verify options
328  * @param ts            transaction set
329  * @param h             header
330  * @return              number of problems found (0 for no problems)
331  */
332 static int verifyDependencies(QVA_t qva, rpmts ts,
333                 Header h)
334 {
335     rpmps ps;
336     rpmpsi psi;
337     int rc = 0;         /* assume no problems */
338     int xx;
339
340     rpmtsEmpty(ts);
341     (void) rpmtsAddInstallElement(ts, h, NULL, 0, NULL);
342
343     xx = rpmtsCheck(ts);
344     ps = rpmtsProblems(ts);
345
346     psi = rpmpsInitIterator(ps);
347     if (rpmpsNumProblems(ps) > 0) {
348         char *nevra = headerGetNEVRA(h, NULL);
349         rpmlog(RPMLOG_NOTICE, _("Unsatisfied dependencies for %s:\n"), nevra);
350         free(nevra);
351         while (rpmpsNextIterator(psi) >= 0) {
352             rpmProblem p = rpmpsGetProblem(psi);
353             char * ps = rpmProblemString(p);
354             rpmlog(RPMLOG_NOTICE, "\t%s\n", ps);
355             free(ps);
356             rc++;       
357         }
358     }
359     psi = rpmpsFreeIterator(psi);
360     ps = rpmpsFree(ps);
361
362     rpmtsEmpty(ts);
363
364     return rc;
365 }
366
367 int showVerifyPackage(QVA_t qva, rpmts ts, Header h)
368 {
369     int scareMem = 1;   /* XXX only rpmVerifyScript needs now */
370     rpmfi fi;
371     int ec = 0;
372     int rc;
373
374     fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem);
375     if (fi != NULL) {
376
377         if (qva->qva_flags & VERIFY_DEPS) {
378             int save_noise = _rpmds_unspecified_epoch_noise;
379             if (rpmIsVerbose())
380                 _rpmds_unspecified_epoch_noise = 1;
381             if ((rc = verifyDependencies(qva, ts, h)) != 0)
382                 ec = rc;
383             _rpmds_unspecified_epoch_noise = save_noise;
384         }
385         if (qva->qva_flags & VERIFY_FILES) {
386             if ((rc = verifyHeader(qva, ts, fi)) != 0)
387                 ec = rc;
388         }
389         if ((qva->qva_flags & VERIFY_SCRIPT)
390          && headerIsEntry(h, RPMTAG_VERIFYSCRIPT))
391         {
392             FD_t fdo = fdDup(STDOUT_FILENO);
393             if ((rc = rpmVerifyScript(qva, ts, fi, fdo)) != 0)
394                 ec = rc;
395             if (fdo != NULL)
396                 rc = Fclose(fdo);
397         }
398
399         fi = rpmfiFree(fi);
400     }
401
402     return ec;
403 }
404
405 int rpmcliVerify(rpmts ts, QVA_t qva, char * const * argv)
406 {
407     rpmVSFlags vsflags, ovsflags;
408     int ec = 0, xx;
409     const char * rootDir = rpmtsRootDir(ts);
410
411     /* 
412      * Open the DB + indices explicitly before possible chroot,
413      * otherwises BDB is going to be unhappy...
414      */
415     rpmtsOpenDB(ts, O_RDONLY);
416     rpmdbOpenAll(rpmtsGetRdb(ts));
417     if (rootDir && strcmp(rootDir, "/") != 0) {
418         if (chroot(rootDir) == -1) {
419             rpmlog(RPMLOG_ERR, _("Unable to change root directory: %m\n"));
420             ec = 1;
421             goto exit;
422         } else {
423             rpmtsSetChrootDone(ts, 1);
424         }
425     }
426
427     if (qva->qva_showPackage == NULL)
428         qva->qva_showPackage = showVerifyPackage;
429
430     /* XXX verify flags are inverted from query. */
431     vsflags = rpmExpandNumeric("%{?_vsflags_verify}");
432     if (!(qva->qva_flags & VERIFY_DIGEST))
433         vsflags |= _RPMVSF_NODIGESTS;
434     if (!(qva->qva_flags & VERIFY_SIGNATURE))
435         vsflags |= _RPMVSF_NOSIGNATURES;
436     if (!(qva->qva_flags & VERIFY_HDRCHK))
437         vsflags |= RPMVSF_NOHDRCHK;
438     vsflags &= ~RPMVSF_NEEDPAYLOAD;
439
440     ovsflags = rpmtsSetVSFlags(ts, vsflags);
441     ec = rpmcliArgIter(ts, qva, argv);
442     vsflags = rpmtsSetVSFlags(ts, ovsflags);
443
444     if (qva->qva_showPackage == showVerifyPackage)
445         qva->qva_showPackage = NULL;
446
447     rpmtsEmpty(ts);
448
449     if (rpmtsChrootDone(ts)) {
450         /* only done if previous chroot succeeded, assume success */
451         xx = chroot(".");
452         rpmtsSetChrootDone(ts, 0);
453     }
454
455 exit:
456
457     return ec;
458 }