Add macro %isu_package to generate ISU Package
[platform/upstream/rpm.git] / lib / rpmvs.c
1 #include "system.h"
2
3 #include <rpm/rpmkeyring.h>
4 #include "lib/rpmvs.h"
5 #include "rpmio/digest.h"
6
7 #include "debug.h"
8
9 struct rpmvs_s {
10     struct rpmsinfo_s *sigs;
11     rpmRC *rcs;
12     char **results;
13     int nsigs;
14     int nalloced;
15 };
16
17 struct vfytag_s {
18     rpmTagVal tag;
19     rpmTagType tagtype;
20     rpm_count_t tagcount;
21     rpm_count_t tagsize;
22 };
23
24 static const struct vfytag_s rpmvfytags[] = {
25     {   RPMSIGTAG_SIZE,                 RPM_BIN_TYPE,           0,      0, },
26     {   RPMSIGTAG_PGP,                  RPM_BIN_TYPE,           0,      0, },
27     {   RPMSIGTAG_MD5,                  RPM_BIN_TYPE,           0,      16, },
28     {   RPMSIGTAG_GPG,                  RPM_BIN_TYPE,           0,      0, },
29     {   RPMSIGTAG_PGP5,                 RPM_BIN_TYPE,           0,      0, },
30     {   RPMSIGTAG_PAYLOADSIZE,          RPM_INT32_TYPE,         1,      4, },
31     {   RPMSIGTAG_RESERVEDSPACE,        RPM_BIN_TYPE,           0,      0, },
32     {   RPMTAG_DSAHEADER,               RPM_BIN_TYPE,           0,      0, },
33     {   RPMTAG_RSAHEADER,               RPM_BIN_TYPE,           0,      0, },
34     {   RPMTAG_SHA1HEADER,              RPM_STRING_TYPE,        1,      41, },
35     {   RPMSIGTAG_LONGSIZE,             RPM_INT64_TYPE,         1,      8, },
36     {   RPMSIGTAG_LONGARCHIVESIZE,      RPM_INT64_TYPE,         1,      8, },
37     {   RPMTAG_SHA256HEADER,            RPM_STRING_TYPE,        1,      65, },
38     {   RPMTAG_PAYLOADDIGEST,           RPM_STRING_ARRAY_TYPE,  0,      0, },
39     { 0 } /* sentinel */
40 };
41
42 struct vfyinfo_s {
43     rpmTagVal tag;
44     struct rpmsinfo_s vi;
45 };
46
47 static const struct vfyinfo_s rpmvfyitems[] = {
48     {   RPMSIGTAG_SIZE,
49         { RPMSIG_OTHER_TYPE,            0,
50         (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, },
51     {   RPMSIGTAG_PGP,
52         { RPMSIG_SIGNATURE_TYPE,                RPMVSF_NORSA,
53         (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, },
54     {   RPMSIGTAG_MD5,
55         { RPMSIG_DIGEST_TYPE,           RPMVSF_NOMD5,
56         (RPMSIG_HEADER|RPMSIG_PAYLOAD), PGPHASHALGO_MD5, }, },
57     {   RPMSIGTAG_GPG,
58         { RPMSIG_SIGNATURE_TYPE,                RPMVSF_NODSA,
59         (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, },
60     {   RPMSIGTAG_PGP5,
61         { RPMSIG_SIGNATURE_TYPE,                RPMVSF_NORSA,
62         (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, },
63     {   RPMSIGTAG_PAYLOADSIZE,
64         { RPMSIG_OTHER_TYPE,            0,
65         (RPMSIG_PAYLOAD),               0, }, },
66     {   RPMSIGTAG_RESERVEDSPACE,
67         { RPMSIG_OTHER_TYPE,            0,
68         0,                              0, }, },
69     {   RPMTAG_DSAHEADER,
70         { RPMSIG_SIGNATURE_TYPE,                RPMVSF_NODSAHEADER,
71         (RPMSIG_HEADER),                0, }, },
72     {   RPMTAG_RSAHEADER,
73         { RPMSIG_SIGNATURE_TYPE,                RPMVSF_NORSAHEADER,
74         (RPMSIG_HEADER),                0, }, },
75     {   RPMTAG_SHA1HEADER,
76         { RPMSIG_DIGEST_TYPE,           RPMVSF_NOSHA1HEADER,
77         (RPMSIG_HEADER),                PGPHASHALGO_SHA1, }, },
78     {   RPMSIGTAG_LONGSIZE,
79         { RPMSIG_OTHER_TYPE,            0,
80         (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, },
81     {   RPMSIGTAG_LONGARCHIVESIZE,
82         { RPMSIG_OTHER_TYPE,            0,
83         (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, }, },
84     {   RPMTAG_SHA256HEADER,
85         { RPMSIG_DIGEST_TYPE,           RPMVSF_NOSHA256HEADER,
86         (RPMSIG_HEADER),                PGPHASHALGO_SHA256, }, },
87     {   RPMTAG_PAYLOADDIGEST,
88         { RPMSIG_DIGEST_TYPE,           RPMVSF_NOPAYLOAD,
89         (RPMSIG_PAYLOAD),               PGPHASHALGO_SHA256, }, },
90     { 0 } /* sentinel */
91 };
92
93 static const char *rangeName(int range);
94 static const char * rpmSigString(rpmRC res);
95 static rpmRC rpmVerifySignature(rpmKeyring keyring, struct rpmsinfo_s *sinfo,
96                                DIGEST_CTX ctx, char ** result);
97
98 static int sinfoLookup(rpmTagVal tag)
99 {
100     const struct vfyinfo_s *start = &rpmvfyitems[0];
101     int ix = -1;
102     for (const struct vfyinfo_s *si = start; si->tag; si++) {
103         if (tag == si->tag) {
104             ix = si - start;
105             break;
106         }
107     }
108     return ix;
109 }
110
111 static int validHex(const char *str, size_t slen)
112 {
113     int valid = 0; /* Assume invalid */
114     const char *b;
115
116     /* Our hex data is always even sized and at least sha-1 long */
117     if (slen % 2 || slen < 40)
118         goto exit;
119
120     for (b = str ; *b != '\0'; b++) {
121         if (strchr("0123456789abcdefABCDEF", *b) == NULL)
122             goto exit;
123     }
124     valid = 1;
125
126 exit:
127     return valid;
128 }
129
130 static rpmRC rpmsinfoInit(rpmtd td, const char *origin,
131                       struct rpmsinfo_s *sinfo, char **msg)
132 {
133     rpmRC rc = RPMRC_FAIL;
134     const void *data = NULL;
135     const struct vfytag_s *tinfo;
136     const struct vfyinfo_s *vinfo;
137     rpm_count_t dlen = 0;
138     int ix;
139
140     if ((ix = sinfoLookup(td->tag)) == -1) {
141         /* anything unknown just falls through for now */
142         rc = RPMRC_OK;
143         goto exit;
144     }
145     vinfo = &rpmvfyitems[ix];
146     tinfo = &rpmvfytags[ix];
147     assert(tinfo->tag == vinfo->tag);
148
149     *sinfo = rpmvfyitems[ix].vi; /* struct assignment */
150
151     if (tinfo->tagtype && tinfo->tagtype != td->type) {
152         rasprintf(msg, _("%s tag %u: invalid type %u"),
153                         origin, td->tag, td->type);
154         goto exit;
155     }
156
157     if (tinfo->tagcount && tinfo->tagcount != td->count) {
158         rasprintf(msg, _("%s: tag %u: invalid count %u"),
159                         origin, td->tag, td->count);
160         goto exit;
161     }
162
163     switch (td->type) {
164     case RPM_STRING_TYPE:
165     case RPM_STRING_ARRAY_TYPE:
166         data = rpmtdGetString(td);
167         if (data)
168             dlen = strlen(data);
169         break;
170     case RPM_BIN_TYPE:
171         data = td->data;
172         dlen = td->count;
173         break;
174     }
175
176     /* MD5 has data length of 16, everything else is (much) larger */
177     if (sinfo->hashalgo && (data == NULL || dlen < 16)) {
178         rasprintf(msg, _("%s tag %u: invalid data %p (%u)"),
179                         origin, td->tag, data, dlen);
180         goto exit;
181     }
182
183     if (td->type == RPM_STRING_TYPE && td->size == 0)
184         td->size = dlen + 1;
185
186     if (tinfo->tagsize && (td->flags & RPMTD_IMMUTABLE) &&
187                 tinfo->tagsize != td->size) {
188         rasprintf(msg, _("%s tag %u: invalid size %u"),
189                         origin, td->tag, td->size);
190         goto exit;
191     }
192
193     if (sinfo->type == RPMSIG_SIGNATURE_TYPE) {
194         if (pgpPrtParams(data, dlen, PGPTAG_SIGNATURE, &sinfo->sig)) {
195             rasprintf(msg, _("%s tag %u: invalid OpenPGP signature"),
196                     origin, td->tag);
197             goto exit;
198         }
199         sinfo->hashalgo = pgpDigParamsAlgo(sinfo->sig, PGPVAL_HASHALGO);
200         sinfo->keyid = pgpGrab(sinfo->sig->signid+4, 4);
201     } else if (sinfo->type == RPMSIG_DIGEST_TYPE) {
202         if (td->type == RPM_BIN_TYPE) {
203             sinfo->dig = pgpHexStr(data, dlen);
204         } else {
205             if (!validHex(data, dlen)) {
206                 rasprintf(msg, _("%s: tag %u: invalid hex"), origin, td->tag);
207                 goto exit;
208             }
209             sinfo->dig = xstrdup(data);
210         }
211     }
212
213     if (sinfo->hashalgo)
214         sinfo->id = (td->tag << 16) | rpmtdGetIndex(td);
215
216     rc = RPMRC_OK;
217
218 exit:
219     return rc;
220 }
221
222 static void rpmsinfoFini(struct rpmsinfo_s *sinfo)
223 {
224     if (sinfo) {
225         if (sinfo->type == RPMSIG_SIGNATURE_TYPE)
226             pgpDigParamsFree(sinfo->sig);
227         else if (sinfo->type == RPMSIG_DIGEST_TYPE)
228             free(sinfo->dig);
229         free(sinfo->descr);
230         memset(sinfo, 0, sizeof(*sinfo));
231     }
232 }
233
234 static int rpmsinfoDisabled(const struct rpmsinfo_s *sinfo, rpmVSFlags vsflags)
235 {
236     if (!(sinfo->type & RPMSIG_VERIFIABLE_TYPE))
237         return 1;
238     if (vsflags & sinfo->disabler)
239         return 1;
240     if ((vsflags & RPMVSF_NEEDPAYLOAD) && (sinfo->range & RPMSIG_PAYLOAD))
241         return 1;
242     return 0;
243 }
244
245 static void rpmvsReserve(struct rpmvs_s *vs, int n)
246 {
247     if (vs->nsigs + n >= vs->nalloced) {
248         vs->nalloced = (vs->nsigs * 2) + n;
249         vs->rcs = xrealloc(vs->rcs, vs->nalloced * sizeof(*vs->rcs));
250         vs->results = xrealloc(vs->results, vs->nalloced * sizeof(*vs->results));
251         vs->sigs = xrealloc(vs->sigs, vs->nalloced * sizeof(*vs->sigs));
252     }
253 }
254
255 const char *rpmsinfoDescr(struct rpmsinfo_s *sinfo)
256 {
257     if (sinfo->descr == NULL) {
258         char *t;
259         switch (sinfo->type) {
260         case RPMSIG_DIGEST_TYPE:
261             rasprintf(&sinfo->descr, _("%s%s %s"),
262                     rangeName(sinfo->range),
263                     pgpValString(PGPVAL_HASHALGO, sinfo->hashalgo),
264                     _("digest"));
265             break;
266         case RPMSIG_SIGNATURE_TYPE:
267             t = sinfo->sig ? pgpIdentItem(sinfo->sig) : NULL;
268             rasprintf(&sinfo->descr, _("%s%s"),
269                     rangeName(sinfo->range), t ? t : _("signature"));
270             free(t);
271             break;
272         }
273     }
274     return sinfo->descr;
275 }
276
277 char *rpmsinfoMsg(struct rpmsinfo_s *sinfo, rpmRC rc, const char *emsg)
278 {
279     char *msg = NULL;
280     if (emsg) {
281         rasprintf(&msg, "%s: %s (%s)",
282                 rpmsinfoDescr(sinfo), rpmSigString(rc), emsg);
283     } else {
284         rasprintf(&msg, "%s: %s", rpmsinfoDescr(sinfo), rpmSigString(rc));
285     }
286     return msg;
287 }
288
289 void rpmvsAppend(struct rpmvs_s *sis, hdrblob blob, rpmTagVal tag)
290 {
291     struct rpmtd_s td;
292     rpmRC rc = hdrblobGet(blob, tag, &td);
293
294     if (rc == RPMRC_OK) {
295         const char *o = (blob->il > blob->ril) ? _("header") : _("package");
296         int ix;
297
298         rpmvsReserve(sis, rpmtdCount(&td));
299
300         while ((ix = rpmtdNext(&td)) >= 0) {
301             sis->results[sis->nsigs] = NULL;
302             sis->rcs[sis->nsigs] = rpmsinfoInit(&td, o,
303                                                 &sis->sigs[sis->nsigs],
304                                                 &sis->results[sis->nsigs]);
305             sis->nsigs++;
306         }
307         rpmtdFreeData(&td);
308     }
309 }
310
311 struct rpmvs_s *rpmvsCreate(hdrblob blob, rpmVSFlags vsflags)
312 {
313     struct rpmvs_s *sis = xcalloc(1, sizeof(*sis));
314
315     rpmvsReserve(sis, 2); /* XXX bump this up later */
316
317     for (const struct vfyinfo_s *si = &rpmvfyitems[0]; si->tag; si++) {
318         if (rpmsinfoDisabled(&si->vi, vsflags))
319             continue;
320         rpmvsAppend(sis, blob, si->tag);
321     }
322     return sis;
323 }
324
325 struct rpmvs_s *rpmvsFree(struct rpmvs_s *sis)
326 {
327     if (sis) {
328         free(sis->rcs);
329         for (int i = 0; i < sis->nsigs; i++) {
330             rpmsinfoFini(&sis->sigs[i]);
331             free(sis->results[i]);
332         }
333         free(sis->sigs);
334         free(sis->results);
335         free(sis);
336     }
337     return NULL;
338 }
339
340 void rpmvsInitDigests(struct rpmvs_s *sis, int range, rpmDigestBundle bundle)
341 {
342     for (int i = 0; i < sis->nsigs; i++) {
343         struct rpmsinfo_s *sinfo = &sis->sigs[i];
344         if (sinfo->range & range) {
345             if (sis->rcs[i] == RPMRC_OK)
346                 rpmDigestBundleAddID(bundle, sinfo->hashalgo, sinfo->id, 0);
347         }
348     }
349 }
350
351 int rpmvsVerifyItems(rpmPlugins plugins, struct rpmvs_s *sis, int range, rpmDigestBundle bundle,
352                        rpmKeyring keyring, rpmsinfoCb cb, void *cbdata)
353 {
354     int failed = 0;
355
356     for (int i = 0; i < sis->nsigs; i++) {
357         struct rpmsinfo_s *sinfo = &sis->sigs[i];
358
359         if (sinfo->range == range) {
360             if (sis->rcs[i] == RPMRC_OK) {
361                 DIGEST_CTX ctx = rpmDigestBundleDupCtx(bundle, sinfo->id);
362                 sis->results[i] = _free(sis->results[i]);
363                 sis->rcs[i] = rpmVerifySignature(keyring, sinfo, ctx, &sis->results[i]);
364                 /* Run verify hook for all plugins */
365                 sis->rcs[i] = rpmpluginsCallVerify(plugins, keyring, sinfo->id, sinfo->sig, ctx, sis->rcs[i]);
366                 rpmDigestFinal(ctx, NULL, NULL, 0);
367                 rpmDigestBundleFinal(bundle, sinfo->id, NULL, NULL, 0);
368             }
369
370             if (cb)
371                 sis->rcs[i] = cb(sinfo, sis->rcs[i], sis->results[i], cbdata);
372
373             if (sis->rcs[i] != RPMRC_OK)
374                 failed++;
375         }
376     }
377
378     return failed;
379 }
380
381 static const char * rpmSigString(rpmRC res)
382 {
383     const char * str;
384     switch (res) {
385     case RPMRC_OK:              str = "OK";             break;
386     case RPMRC_FAIL:            str = "BAD";            break;
387     case RPMRC_NOKEY:           str = "NOKEY";          break;
388     case RPMRC_NOTTRUSTED:      str = "NOTTRUSTED";     break;
389     default:
390     case RPMRC_NOTFOUND:        str = "UNKNOWN";        break;
391     }
392     return str;
393 }
394
395 static const char *rangeName(int range)
396 {
397     switch (range) {
398     case RPMSIG_HEADER:                         return _("Header ");
399     case RPMSIG_PAYLOAD:                        return _("Payload ");
400     }
401     /* trad. output for (RPMSIG_HEADER|RPMSIG_PAYLOAD) range is "" */
402     return "";
403 }
404
405 static rpmRC verifyDigest(struct rpmsinfo_s *sinfo, DIGEST_CTX digctx,
406                           char **msg)
407 {
408     rpmRC res = RPMRC_FAIL; /* assume failure */
409     char * dig = NULL;
410     size_t diglen = 0;
411     DIGEST_CTX ctx = rpmDigestDup(digctx);
412
413     if (rpmDigestFinal(ctx, (void **)&dig, &diglen, 1) || diglen == 0)
414         goto exit;
415
416     if (strcasecmp(sinfo->dig, dig) == 0) {
417         res = RPMRC_OK;
418     } else {
419         rasprintf(msg, "Expected %s != %s", sinfo->dig, dig);
420     }
421
422 exit:
423     free(dig);
424     return res;
425 }
426
427 /**
428  * Verify DSA/RSA signature.
429  * @param keyring       pubkey keyring
430  * @param sinfo         OpenPGP signature parameters
431  * @param hashctx       digest context
432  * @retval msg          verbose success/failure text
433  * @return              RPMRC_OK on success
434  */
435 static rpmRC
436 verifySignature(rpmKeyring keyring, struct rpmsinfo_s *sinfo,
437                 DIGEST_CTX hashctx, char **msg)
438 {
439     rpmRC res = rpmKeyringVerifySig(keyring, sinfo->sig, hashctx);
440
441     return res;
442 }
443
444 static rpmRC
445 rpmVerifySignature(rpmKeyring keyring, struct rpmsinfo_s *sinfo,
446                    DIGEST_CTX ctx, char ** result)
447 {
448     rpmRC res = RPMRC_FAIL;
449
450     if (sinfo->type == RPMSIG_DIGEST_TYPE)
451         res = verifyDigest(sinfo, ctx, result);
452     else if (sinfo->type == RPMSIG_SIGNATURE_TYPE)
453         res = verifySignature(keyring, sinfo, ctx, result);
454
455     return res;
456 }