doxygen cleanup.
[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 "psm.h"
9 #include <rpmcli.h>
10
11 #include "legacy.h"     /* XXX domd5(), uidToUname(), gnameToGid */
12 #include "ugid.h"
13 #include "misc.h"       /* XXX for uidToUname() and gnameToGid() */
14 #include "debug.h"
15
16 /*@access rpmProblem @*/
17 /*@access rpmTransactionSet @*/
18 /*@access PSM_t @*/     /* XXX for %verifyscript through psmStage() */
19 /*@access FD_t @*/      /* XXX compared with NULL */
20
21 #define S_ISDEV(m) (S_ISBLK((m)) || S_ISCHR((m)))
22
23 int rpmVerifyFile(const rpmTransactionSet ts, const TFI_t fi,
24                 rpmVerifyAttrs * result, rpmVerifyAttrs omitMask)
25 {
26     unsigned short fmode = tfiGetFMode(fi);
27     rpmfileAttrs fileAttrs = tfiGetFFlags(fi);
28     rpmVerifyAttrs flags = tfiGetVFlags(fi);
29     const char * filespec = tfiGetFN(fi);
30     int rc;
31     struct stat sb;
32
33     /* Prepend the path to root (if specified). */
34     if (ts->rootDir && *ts->rootDir != '\0'
35      && !(ts->rootDir[0] == '/' && ts->rootDir[1] == '\0'))
36     {
37         int nb = strlen(filespec) + strlen(ts->rootDir) + 1;
38         char * tb = alloca(nb);
39         char * t;
40
41         t = tb;
42         *t = '\0';
43         t = stpcpy(t, ts->rootDir);
44         while (t > tb && t[-1] == '/') {
45             --t;
46             *t = '\0';
47         }
48         t = stpcpy(t, filespec);
49         filespec = t;
50     }
51
52     *result = RPMVERIFY_NONE;
53
54     /*
55      * Check to see if the file was installed - if not pretend all is OK.
56      */
57     switch (tfiGetFState(fi)) {
58     case RPMFILE_STATE_NETSHARED:
59     case RPMFILE_STATE_REPLACED:
60     case RPMFILE_STATE_NOTINSTALLED:
61         return 0;
62         /*@notreached@*/ break;
63     case RPMFILE_STATE_NORMAL:
64         break;
65     }
66
67     if (filespec == NULL || Lstat(filespec, &sb) != 0) {
68         *result |= RPMVERIFY_LSTATFAIL;
69         return 1;
70     }
71
72     /*
73      * Not all attributes of non-regular files can be verified.
74      */
75     if (S_ISDIR(sb.st_mode))
76         flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | 
77                         RPMVERIFY_LINKTO);
78     else if (S_ISLNK(sb.st_mode)) {
79         flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
80                 RPMVERIFY_MODE);
81 #if CHOWN_FOLLOWS_SYMLINK
82             flags &= ~(RPMVERIFY_USER | RPMVERIFY_GROUP);
83 #endif
84     }
85     else if (S_ISFIFO(sb.st_mode))
86         flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | 
87                         RPMVERIFY_LINKTO);
88     else if (S_ISCHR(sb.st_mode))
89         flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | 
90                         RPMVERIFY_LINKTO);
91     else if (S_ISBLK(sb.st_mode))
92         flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | 
93                         RPMVERIFY_LINKTO);
94     else 
95         flags &= ~(RPMVERIFY_LINKTO);
96
97     /*
98      * Content checks of %ghost files are meaningless.
99      */
100     if (fileAttrs & RPMFILE_GHOST)
101         flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | 
102                         RPMVERIFY_LINKTO);
103
104     /*
105      * Don't verify any features in omitMask.
106      */
107     flags &= ~(omitMask | RPMVERIFY_LSTATFAIL|RPMVERIFY_READFAIL|RPMVERIFY_READLINKFAIL);
108
109     if (flags & RPMVERIFY_MD5) {
110         byte md5sum[16];
111
112         rc = domd5(filespec, md5sum, 0);
113         if (rc)
114             *result |= (RPMVERIFY_READFAIL|RPMVERIFY_MD5);
115         else {
116             const byte * md5 = tfiGetMD5(fi);
117             if (md5 == NULL || memcmp(md5sum, md5, sizeof(md5sum)))
118                 *result |= RPMVERIFY_MD5;
119         }
120     } 
121
122     if (flags & RPMVERIFY_LINKTO) {
123         char linkto[1024];
124         int size = 0;
125
126         if ((size = Readlink(filespec, linkto, sizeof(linkto)-1)) == -1)
127             *result |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO);
128         else {
129             const char * flink = tfiGetFLink(fi);
130             linkto[size] = '\0';
131             if (flink == NULL || strcmp(linkto, flink))
132                 *result |= RPMVERIFY_LINKTO;
133         }
134     } 
135
136     if (flags & RPMVERIFY_FILESIZE) {
137         if (sb.st_size != tfiGetFSize(fi))
138             *result |= RPMVERIFY_FILESIZE;
139     } 
140
141     if (flags & RPMVERIFY_MODE) {
142         unsigned short metamode = fmode;
143         unsigned short filemode;
144
145         /*
146          * Platforms (like AIX) where sizeof(unsigned short) != sizeof(mode_t)
147          * need the (unsigned short) cast here. 
148          */
149         filemode = (unsigned short)sb.st_mode;
150
151         /*
152          * Comparing the type of %ghost files is meaningless, but perms are OK.
153          */
154         if (fileAttrs & RPMFILE_GHOST) {
155             metamode &= ~0xf000;
156             filemode &= ~0xf000;
157         }
158
159         if (metamode != filemode)
160             *result |= RPMVERIFY_MODE;
161     }
162
163     if (flags & RPMVERIFY_RDEV) {
164         if (S_ISCHR(fmode) != S_ISCHR(sb.st_mode)
165          || S_ISBLK(fmode) != S_ISBLK(sb.st_mode))
166         {
167             *result |= RPMVERIFY_RDEV;
168         } else if (S_ISDEV(fmode) && S_ISDEV(sb.st_mode)) {
169             if (sb.st_rdev != tfiGetFRdev(fi))
170                 *result |= RPMVERIFY_RDEV;
171         } 
172     }
173
174     if (flags & RPMVERIFY_MTIME) {
175         if (sb.st_mtime != tfiGetFMtime(fi))
176             *result |= RPMVERIFY_MTIME;
177     }
178
179     if (flags & RPMVERIFY_USER) {
180         const char * name = uidToUname(sb.st_uid);
181         const char * fuser = tfiGetFUser(fi);
182         if (name == NULL || fuser == NULL || strcmp(name, fuser))
183             *result |= RPMVERIFY_USER;
184     }
185
186     if (flags & RPMVERIFY_GROUP) {
187         const char * name = uidToUname(sb.st_gid);
188         const char * fgroup = tfiGetFGroup(fi);
189         if (name == NULL || fgroup == NULL || strcmp(name, fgroup))
190             *result |= RPMVERIFY_GROUP;
191     }
192
193     return 0;
194 }
195
196 /**
197  * Return exit code from running verify script from header.
198  * @todo malloc/free/refcount handling is fishy here.
199  * @param qva           parsed query/verify options
200  * @param ts            transaction set
201  * @param fi            file info set
202  * @param scriptFd      file handle to use for stderr (or NULL)
203  * @return              0 on success
204  */
205 static int rpmVerifyScript(/*@unused@*/ QVA_t qva, rpmTransactionSet ts,
206                 TFI_t fi, /*@null@*/ FD_t scriptFd)
207         /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
208         /*@modifies ts, fi, rpmGlobalMacroContext,
209                 fileSystem, internalState @*/
210 {
211     int rc = 0;
212     FD_t savefd = NULL;
213     PSM_t psm;
214
215     psm = memset(alloca(sizeof(*psm)), 0, sizeof(*psm));
216     psm->ts = rpmtsLink(ts, "rpmVerifyScript");
217
218     if (scriptFd != NULL) {
219         savefd = ts->scriptFd;
220         ts->scriptFd = fdLink(scriptFd, "rpmVerifyScript");
221     }
222
223     psm->fi = rpmfiLink(fi, "rpmVerifyScript");
224     psm->stepName = "verify";
225     psm->scriptTag = RPMTAG_VERIFYSCRIPT;
226     psm->progTag = RPMTAG_VERIFYSCRIPTPROG;
227     rc = psmStage(psm, PSM_SCRIPT);
228     psm->fi = rpmfiUnlink(fi, "rpmVerifyScript");
229
230     if (scriptFd != NULL) {
231         ts->scriptFd = fdFree(ts->scriptFd, "rpmVerifyScript");
232         ts->scriptFd = savefd;
233     }
234
235     psm->ts = rpmtsUnlink(ts, "rpmVerifyScript");
236
237     return rc;
238 }
239
240 int rpmVerifyDigest(Header h)
241 {
242     HGE_t hge = (HGE_t)headerGetEntry;  /* XXX headerGetEntryMinMemory? */
243     HFD_t hfd = headerFreeData;
244     void * uh = NULL;
245     rpmTagType uht;
246     int_32 uhc;
247     const char * hdigest;
248     rpmTagType hdt;
249     int ec = 0;         /* assume no problems */
250
251     /* Retrieve header digest. */
252     if (!hge(h, RPMTAG_SHA1HEADER, &hdt, (void **) &hdigest, NULL)
253      &&!hge(h, RPMTAG_SHA1RHN, &hdt, (void **) &hdigest, NULL))
254     {
255             return 0;
256     }
257     /* Regenerate original header. */
258     if (!hge(h, RPMTAG_HEADERIMMUTABLE, &uht, &uh, &uhc))
259         return 0;
260
261     if (hdigest == NULL || uh == NULL)
262         return 0;
263
264     /* Compute header digest. */
265     {   DIGEST_CTX ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE);
266         const char * digest;
267         size_t digestlen;
268
269         (void) rpmDigestUpdate(ctx, uh, uhc);
270         (void) rpmDigestFinal(ctx, (void **)&digest, &digestlen, 1);
271
272         /* XXX can't happen: report NULL malloc return as a digest failure. */
273         ec = (digest == NULL || strcmp(hdigest, digest)) ? 1 : 0;
274         digest = _free(digest);
275     }
276
277     uh = hfd(uh, uht);
278     hdigest = hfd(hdigest, hdt);
279
280     return ec;
281 }
282
283 /**
284  * Check file info from header against what's actually installed.
285  * @param qva           parsed query/verify options
286  * @param ts            transaction set
287  * @param fi            file info set
288  * @return              0 no problems, 1 problems found
289  */
290 static int verifyHeader(QVA_t qva, const rpmTransactionSet ts, TFI_t fi)
291         /*@globals fileSystem, internalState @*/
292         /*@modifies fi, fileSystem, internalState  @*/
293 {
294     char buf[BUFSIZ];
295     char * t, * te;
296     rpmVerifyAttrs verifyResult = 0;
297     /*@-type@*/ /* FIX: union? */
298     rpmVerifyAttrs omitMask = ((qva->qva_flags & VERIFY_ATTRS) ^ VERIFY_ATTRS);
299     /*@=type@*/
300     int ec = 0;         /* assume no problems */
301     int i;
302
303     te = t = buf;
304     *te = '\0';
305
306     fi = rpmfiLink(fi, "verifyHeader");
307     fi = tfiInit(fi, 0);
308     if (fi != NULL)     /* XXX lclint */
309     while ((i = tfiNext(fi)) >= 0) {
310         rpmfileAttrs fileAttrs;
311         int rc;
312
313         fileAttrs = tfiGetFFlags(fi);
314
315         /* If not verifying %ghost, skip ghost files. */
316         if (!(qva->qva_fflags & RPMFILE_GHOST)
317         && (fileAttrs & RPMFILE_GHOST))
318             continue;
319
320         rc = rpmVerifyFile(ts, fi, &verifyResult, omitMask);
321         if (rc) {
322             if (!(fileAttrs & RPMFILE_MISSINGOK) || rpmIsVerbose()) {
323                 sprintf(te, _("missing    %s"), tfiGetFN(fi));
324                 te += strlen(te);
325                 ec = rc;
326             }
327         } else if (verifyResult) {
328             const char * size, * md5, * link, * mtime, * mode;
329             const char * group, * user, * rdev;
330             /*@observer@*/ static const char *const aok = ".";
331             /*@observer@*/ static const char *const unknown = "?";
332
333             ec = 1;
334
335 #define _verify(_RPMVERIFY_F, _C)       \
336         ((verifyResult & _RPMVERIFY_F) ? _C : aok)
337 #define _verifylink(_RPMVERIFY_F, _C)   \
338         ((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
339          (verifyResult & _RPMVERIFY_F) ? _C : aok)
340 #define _verifyfile(_RPMVERIFY_F, _C)   \
341         ((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
342          (verifyResult & _RPMVERIFY_F) ? _C : aok)
343         
344             md5 = _verifyfile(RPMVERIFY_MD5, "5");
345             size = _verify(RPMVERIFY_FILESIZE, "S");
346             link = _verifylink(RPMVERIFY_LINKTO, "L");
347             mtime = _verify(RPMVERIFY_MTIME, "T");
348             rdev = _verify(RPMVERIFY_RDEV, "D");
349             user = _verify(RPMVERIFY_USER, "U");
350             group = _verify(RPMVERIFY_GROUP, "G");
351             mode = _verify(RPMVERIFY_MODE, "M");
352
353 #undef _verify
354 #undef _verifylink
355 #undef _verifyfile
356
357             sprintf(te, "%s%s%s%s%s%s%s%s %c %s",
358                         size, mode, md5, rdev, link, user, group, mtime, 
359                         ((fileAttrs & RPMFILE_CONFIG)   ? 'c' :
360                          (fileAttrs & RPMFILE_DOC)      ? 'd' :
361                          (fileAttrs & RPMFILE_GHOST)    ? 'g' :
362                          (fileAttrs & RPMFILE_LICENSE)  ? 'l' :
363                          (fileAttrs & RPMFILE_README)   ? 'r' : ' '), 
364                         tfiGetFN(fi));
365             te += strlen(te);
366         }
367
368         if (te > t) {
369             *te++ = '\n';
370             *te = '\0';
371             rpmMessage(RPMMESS_NORMAL, "%s", t);
372             te = t = buf;
373             *t = '\0';
374         }
375     }
376     fi = rpmfiUnlink(fi, "verifyHeader");
377         
378     return ec;
379 }
380
381 /**
382  * Check installed package dependencies for problems.
383  * @param qva           parsed query/verify options
384  * @param ts            transaction set
385  * @param h             header
386  * @return              0 no problems, 1 problems found
387  */
388 static int verifyDependencies(/*@unused@*/ QVA_t qva, rpmTransactionSet ts,
389                 Header h)
390         /*@globals fileSystem, internalState @*/
391         /*@modifies ts, h, fileSystem, internalState @*/
392 {
393     rpmProblem conflicts;
394     int numConflicts;
395     int rc = 0;         /* assume no problems */
396     int i;
397
398     rpmtransClean(ts);
399     (void) rpmtransAddPackage(ts, h, NULL, 0, NULL);
400
401     (void) rpmdepCheck(ts, &conflicts, &numConflicts);
402
403     /*@-branchstate@*/
404     if (numConflicts) {
405         const char * pkgNEVR, * altNEVR;
406         rpmProblem c;
407         char * t, * te;
408         int nb = 512;
409
410         for (i = 0; i < numConflicts; i++) {
411             c = conflicts + i;
412             altNEVR = (c->altNEVR ? c->altNEVR : "? ?altNEVR?");
413             nb += strlen(altNEVR+2) + sizeof(", ") - 1;
414         }
415         te = t = alloca(nb);
416         *te = '\0';
417         pkgNEVR = (conflicts->pkgNEVR ? conflicts->pkgNEVR : "?pkgNEVR?");
418         sprintf(te, _("Unsatisifed dependencies for %s: "), pkgNEVR);
419         te += strlen(te);
420         for (i = 0; i < numConflicts; i++) {
421             c = conflicts + i;
422             altNEVR = (c->altNEVR ? c->altNEVR : "? ?altNEVR?");
423             if (i) te = stpcpy(te, ", ");
424             /* XXX FIXME: should probably supply the "[R|C] " type prefix */
425             te = stpcpy(te, altNEVR+2);
426         }
427         conflicts = rpmdepFreeConflicts(conflicts, numConflicts);
428
429         if (te > t) {
430             *te++ = '\n';
431             *te = '\0';
432             rpmMessage(RPMMESS_NORMAL, "%s", t);
433             te = t;
434             *t = '\0';
435         }
436         rc = 1;
437     }
438     /*@=branchstate@*/
439
440     rpmtransClean(ts);
441
442     return rc;
443 }
444
445 int showVerifyPackage(QVA_t qva, rpmTransactionSet ts, Header h)
446 {
447     int scareMem = 1;   /* XXX only rpmVerifyScript needs now */
448     TFI_t fi;
449     int ec = 0;
450     int rc;
451
452 #ifdef  DYING
453     if (qva->qva_flags & VERIFY_DIGEST) {
454         if ((rc = rpmVerifyDigest(h)) != 0) {
455             const char *n, *v, *r;
456             (void) headerNVR(h, &n, &v, &r);
457             rpmMessage(RPMMESS_NORMAL,
458                    _("%s-%s-%s: immutable header region digest check failed\n"),
459                         n, v, r);
460             ec = rc;
461         }
462     }
463 #endif
464
465     fi = fiNew(ts, NULL, h, RPMTAG_BASENAMES, scareMem);
466     if (fi != NULL) {
467
468         if (qva->qva_flags & VERIFY_DEPS) {
469             if ((rc = verifyDependencies(qva, ts, h)) != 0)
470                 ec = rc;
471         }
472         if (qva->qva_flags & VERIFY_FILES) {
473             if ((rc = verifyHeader(qva, ts, fi)) != 0)
474                 ec = rc;
475         }
476         if ((qva->qva_flags & VERIFY_SCRIPT)
477          && headerIsEntry(h, RPMTAG_VERIFYSCRIPT))
478         {
479             FD_t fdo = fdDup(STDOUT_FILENO);
480             if ((rc = rpmVerifyScript(qva, ts, fi, fdo)) != 0)
481                 ec = rc;
482             if (fdo)
483                 rc = Fclose(fdo);
484         }
485
486         fi = fiFree(fi, 1);
487     }
488
489     return ec;
490 }
491
492 int rpmcliVerify(rpmTransactionSet ts, QVA_t qva, const char ** argv)
493 {
494     const char * arg;
495     int ec = 0;
496
497     if (qva->qva_showPackage == NULL)
498         qva->qva_showPackage = showVerifyPackage;
499
500     switch (qva->qva_source) {
501 #ifdef  DYING
502     case RPMQV_RPM:
503         if (!(qva->qva_flags & VERIFY_DEPS))
504             break;
505         /*@fallthrough@*/
506 #endif
507     default:
508         if (rpmtsOpenDB(ts, O_RDONLY))
509             return 1;   /* XXX W2DO? */
510         break;
511     }
512
513     /* XXX verifyFlags are inverted */
514     ts->nodigests = (qva->qva_flags & VERIFY_DIGEST);
515     ts->nosignatures = (qva->qva_flags & VERIFY_SIGNATURE);
516
517     if (qva->qva_source == RPMQV_ALL) {
518         /*@-nullpass@*/ /* FIX: argv can be NULL, cast to pass argv array */
519         ec = rpmQueryVerify(qva, ts, (const char *) argv);
520         /*@=nullpass@*/
521     } else {
522         if (argv != NULL)
523         while ((arg = *argv++) != NULL) {
524             ec += rpmQueryVerify(qva, ts, arg);
525             rpmtransClean(ts);
526         }
527     }
528
529     if (qva->qva_showPackage == showVerifyPackage)
530         qva->qva_showPackage = NULL;
531
532     return ec;
533 }