1 /*
2 verificationresult.cpp - wraps a gpgme verify result
3 Copyright (C) 2004 Klarälvdalens Datakonsult AB
4 2016 Bundesamt für Sicherheit in der Informationstechnik
5 Software engineering by Intevation GmbH
6
7 This file is part of GPGME++.
8
9 GPGME++ is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public
11 License as published by the Free Software Foundation; either
12 version 2 of the License, or (at your option) any later version.
13
14 GPGME++ is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Library General Public License for more details.
18
19 You should have received a copy of the GNU Library General Public License
20 along with GPGME++; see the file COPYING.LIB. If not, write to the
21 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 Boston, MA 02110-1301, USA.
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <verificationresult.h>
30 #include <notation.h>
31 #include "result_p.h"
32 #include "util.h"
33 #include "key.h"
34 #include "context.h"
35
36 #include <gpgme.h>
37
38 #include <istream>
39 #include <algorithm>
40 #include <iterator>
41 #include <string>
42 #include <cstring>
43 #include <cstdlib>
44
45 #include <string.h>
46
47 class GpgME::VerificationResult::Private
48 {
49 public:
Private(const gpgme_verify_result_t r)50 explicit Private(const gpgme_verify_result_t r)
51 {
52 if (!r) {
53 return;
54 }
55 if (r->file_name) {
56 file_name = r->file_name;
57 }
58 // copy recursively, using compiler-generated copy ctor.
59 // We just need to handle the pointers in the structs:
60 for (gpgme_signature_t is = r->signatures ; is ; is = is->next) {
61 gpgme_signature_t scopy = new _gpgme_signature(*is);
62 if (is->fpr) {
63 scopy->fpr = strdup(is->fpr);
64 }
65 // PENDING(marc) why does this crash on Windows in strdup()?
66 # ifndef _WIN32
67 if (is->pka_address) {
68 scopy->pka_address = strdup(is->pka_address);
69 }
70 # else
71 scopy->pka_address = nullptr;
72 # endif
73 scopy->next = nullptr;
74 sigs.push_back(scopy);
75 // copy keys
76 if (scopy->key) {
77 keys.push_back(Key(scopy->key, true));
78 } else {
79 keys.push_back(Key());
80 }
81 // copy notations:
82 nota.push_back(std::vector<Nota>());
83 purls.push_back(nullptr);
84 for (gpgme_sig_notation_t in = is->notations ; in ; in = in->next) {
85 if (!in->name) {
86 if (in->value) {
87 purls.back() = strdup(in->value); // policy url
88 }
89 continue;
90 }
91 Nota n = { nullptr, nullptr, in->flags };
92 n.name = strdup(in->name);
93 if (in->value) {
94 n.value = strdup(in->value);
95 }
96 nota.back().push_back(n);
97 }
98 }
99 }
~Private()100 ~Private()
101 {
102 for (std::vector<gpgme_signature_t>::iterator it = sigs.begin() ; it != sigs.end() ; ++it) {
103 std::free((*it)->fpr);
104 std::free((*it)->pka_address);
105 delete *it; *it = nullptr;
106 }
107 for (std::vector< std::vector<Nota> >::iterator it = nota.begin() ; it != nota.end() ; ++it) {
108 for (std::vector<Nota>::iterator jt = it->begin() ; jt != it->end() ; ++jt) {
109 std::free(jt->name); jt->name = nullptr;
110 std::free(jt->value); jt->value = nullptr;
111 }
112 }
113 std::for_each(purls.begin(), purls.end(), &std::free);
114 }
115
116 struct Nota {
117 char *name;
118 char *value;
119 gpgme_sig_notation_flags_t flags;
120 };
121
122 std::vector<gpgme_signature_t> sigs;
123 std::vector< std::vector<Nota> > nota;
124 std::vector<GpgME::Key> keys;
125 std::vector<char *> purls;
126 std::string file_name;
127 Protocol proto;
128 };
129
VerificationResult(gpgme_ctx_t ctx,int error)130 GpgME::VerificationResult::VerificationResult(gpgme_ctx_t ctx, int error)
131 : GpgME::Result(error), d()
132 {
133 init(ctx);
134 }
135
VerificationResult(gpgme_ctx_t ctx,const Error & error)136 GpgME::VerificationResult::VerificationResult(gpgme_ctx_t ctx, const Error &error)
137 : GpgME::Result(error), d()
138 {
139 init(ctx);
140 }
141
init(gpgme_ctx_t ctx)142 void GpgME::VerificationResult::init(gpgme_ctx_t ctx)
143 {
144 if (!ctx) {
145 return;
146 }
147 gpgme_verify_result_t res = gpgme_op_verify_result(ctx);
148 if (!res) {
149 return;
150 }
151 d.reset(new Private(res));
152 gpgme_protocol_t proto = gpgme_get_protocol(ctx);
153 d->proto = proto == GPGME_PROTOCOL_OpenPGP ? OpenPGP :
154 proto == GPGME_PROTOCOL_CMS ? CMS :
155 UnknownProtocol;
156 }
157
make_standard_stuff(VerificationResult) const158 make_standard_stuff(VerificationResult)
159
160 const char *GpgME::VerificationResult::fileName() const
161 {
162 return d ? d->file_name.c_str() : nullptr ;
163 }
164
numSignatures() const165 unsigned int GpgME::VerificationResult::numSignatures() const
166 {
167 return d ? d->sigs.size() : 0 ;
168 }
169
signature(unsigned int idx) const170 GpgME::Signature GpgME::VerificationResult::signature(unsigned int idx) const
171 {
172 return Signature(d, idx);
173 }
174
signatures() const175 std::vector<GpgME::Signature> GpgME::VerificationResult::signatures() const
176 {
177 if (!d) {
178 return std::vector<Signature>();
179 }
180 std::vector<Signature> result;
181 result.reserve(d->sigs.size());
182 for (unsigned int i = 0 ; i < d->sigs.size() ; ++i) {
183 result.push_back(Signature(d, i));
184 }
185 return result;
186 }
187
Signature(const std::shared_ptr<VerificationResult::Private> & parent,unsigned int i)188 GpgME::Signature::Signature(const std::shared_ptr<VerificationResult::Private> &parent, unsigned int i)
189 : d(parent), idx(i)
190 {
191 }
192
Signature()193 GpgME::Signature::Signature() : d(), idx(0) {}
194
isNull() const195 bool GpgME::Signature::isNull() const
196 {
197 return !d || idx >= d->sigs.size() ;
198 }
199
summary() const200 GpgME::Signature::Summary GpgME::Signature::summary() const
201 {
202 if (isNull()) {
203 return None;
204 }
205 gpgme_sigsum_t sigsum = d->sigs[idx]->summary;
206 unsigned int result = 0;
207 if (sigsum & GPGME_SIGSUM_VALID) {
208 result |= Valid;
209 }
210 if (sigsum & GPGME_SIGSUM_GREEN) {
211 result |= Green;
212 }
213 if (sigsum & GPGME_SIGSUM_RED) {
214 result |= Red;
215 }
216 if (sigsum & GPGME_SIGSUM_KEY_REVOKED) {
217 result |= KeyRevoked;
218 }
219 if (sigsum & GPGME_SIGSUM_KEY_EXPIRED) {
220 result |= KeyExpired;
221 }
222 if (sigsum & GPGME_SIGSUM_SIG_EXPIRED) {
223 result |= SigExpired;
224 }
225 if (sigsum & GPGME_SIGSUM_KEY_MISSING) {
226 result |= KeyMissing;
227 }
228 if (sigsum & GPGME_SIGSUM_CRL_MISSING) {
229 result |= CrlMissing;
230 }
231 if (sigsum & GPGME_SIGSUM_CRL_TOO_OLD) {
232 result |= CrlTooOld;
233 }
234 if (sigsum & GPGME_SIGSUM_BAD_POLICY) {
235 result |= BadPolicy;
236 }
237 if (sigsum & GPGME_SIGSUM_SYS_ERROR) {
238 result |= SysError;
239 }
240 if (sigsum & GPGME_SIGSUM_TOFU_CONFLICT) {
241 result |= TofuConflict;
242 }
243 return static_cast<Summary>(result);
244 }
245
fingerprint() const246 const char *GpgME::Signature::fingerprint() const
247 {
248 return isNull() ? nullptr : d->sigs[idx]->fpr ;
249 }
250
status() const251 GpgME::Error GpgME::Signature::status() const
252 {
253 return Error(isNull() ? 0 : d->sigs[idx]->status);
254 }
255
creationTime() const256 time_t GpgME::Signature::creationTime() const
257 {
258 return static_cast<time_t>(isNull() ? 0 : d->sigs[idx]->timestamp);
259 }
260
expirationTime() const261 time_t GpgME::Signature::expirationTime() const
262 {
263 return static_cast<time_t>(isNull() ? 0 : d->sigs[idx]->exp_timestamp);
264 }
265
neverExpires() const266 bool GpgME::Signature::neverExpires() const
267 {
268 return expirationTime() == (time_t)0;
269 }
270
isWrongKeyUsage() const271 bool GpgME::Signature::isWrongKeyUsage() const
272 {
273 return !isNull() && d->sigs[idx]->wrong_key_usage;
274 }
275
isVerifiedUsingChainModel() const276 bool GpgME::Signature::isVerifiedUsingChainModel() const
277 {
278 return !isNull() && d->sigs[idx]->chain_model;
279 }
280
isDeVs() const281 bool GpgME::Signature::isDeVs() const
282 {
283 return !isNull() && d->sigs[idx]->is_de_vs;
284 }
285
pkaStatus() const286 GpgME::Signature::PKAStatus GpgME::Signature::pkaStatus() const
287 {
288 if (!isNull()) {
289 return static_cast<PKAStatus>(d->sigs[idx]->pka_trust);
290 }
291 return UnknownPKAStatus;
292 }
293
pkaAddress() const294 const char *GpgME::Signature::pkaAddress() const
295 {
296 if (!isNull()) {
297 return d->sigs[idx]->pka_address;
298 }
299 return nullptr;
300 }
301
validity() const302 GpgME::Signature::Validity GpgME::Signature::validity() const
303 {
304 if (isNull()) {
305 return Unknown;
306 }
307 switch (d->sigs[idx]->validity) {
308 default:
309 case GPGME_VALIDITY_UNKNOWN: return Unknown;
310 case GPGME_VALIDITY_UNDEFINED: return Undefined;
311 case GPGME_VALIDITY_NEVER: return Never;
312 case GPGME_VALIDITY_MARGINAL: return Marginal;
313 case GPGME_VALIDITY_FULL: return Full;
314 case GPGME_VALIDITY_ULTIMATE: return Ultimate;
315 }
316 }
317
validityAsString() const318 char GpgME::Signature::validityAsString() const
319 {
320 if (isNull()) {
321 return '?';
322 }
323 switch (d->sigs[idx]->validity) {
324 default:
325 case GPGME_VALIDITY_UNKNOWN: return '?';
326 case GPGME_VALIDITY_UNDEFINED: return 'q';
327 case GPGME_VALIDITY_NEVER: return 'n';
328 case GPGME_VALIDITY_MARGINAL: return 'm';
329 case GPGME_VALIDITY_FULL: return 'f';
330 case GPGME_VALIDITY_ULTIMATE: return 'u';
331 }
332 }
333
nonValidityReason() const334 GpgME::Error GpgME::Signature::nonValidityReason() const
335 {
336 return Error(isNull() ? 0 : d->sigs[idx]->validity_reason);
337 }
338
publicKeyAlgorithm() const339 unsigned int GpgME::Signature::publicKeyAlgorithm() const
340 {
341 if (!isNull()) {
342 return d->sigs[idx]->pubkey_algo;
343 }
344 return 0;
345 }
346
publicKeyAlgorithmAsString() const347 const char *GpgME::Signature::publicKeyAlgorithmAsString() const
348 {
349 if (!isNull()) {
350 return gpgme_pubkey_algo_name(d->sigs[idx]->pubkey_algo);
351 }
352 return nullptr;
353 }
354
hashAlgorithm() const355 unsigned int GpgME::Signature::hashAlgorithm() const
356 {
357 if (!isNull()) {
358 return d->sigs[idx]->hash_algo;
359 }
360 return 0;
361 }
362
hashAlgorithmAsString() const363 const char *GpgME::Signature::hashAlgorithmAsString() const
364 {
365 if (!isNull()) {
366 return gpgme_hash_algo_name(d->sigs[idx]->hash_algo);
367 }
368 return nullptr;
369 }
370
policyURL() const371 const char *GpgME::Signature::policyURL() const
372 {
373 return isNull() ? nullptr : d->purls[idx] ;
374 }
375
notation(unsigned int nidx) const376 GpgME::Notation GpgME::Signature::notation(unsigned int nidx) const
377 {
378 return GpgME::Notation(d, idx, nidx);
379 }
380
notations() const381 std::vector<GpgME::Notation> GpgME::Signature::notations() const
382 {
383 if (isNull()) {
384 return std::vector<GpgME::Notation>();
385 }
386 std::vector<GpgME::Notation> result;
387 result.reserve(d->nota[idx].size());
388 for (unsigned int i = 0 ; i < d->nota[idx].size() ; ++i) {
389 result.push_back(GpgME::Notation(d, idx, i));
390 }
391 return result;
392 }
393
key() const394 GpgME::Key GpgME::Signature::key() const
395 {
396 if (isNull()) {
397 return Key();
398 }
399 return d->keys[idx];
400 }
401
key(bool search,bool update) const402 GpgME::Key GpgME::Signature::key(bool search, bool update) const
403 {
404 if (isNull()) {
405 return Key();
406 }
407
408 GpgME::Key ret = key();
409 if (ret.isNull() && search && fingerprint ()) {
410 auto ctx = Context::createForProtocol (d->proto);
411 if (ctx) {
412 ctx->setKeyListMode(KeyListMode::Local |
413 KeyListMode::Signatures |
414 KeyListMode::SignatureNotations |
415 KeyListMode::Validate |
416 KeyListMode::WithTofu |
417 KeyListMode::WithKeygrip);
418 Error e;
419 ret = d->keys[idx] = ctx->key(fingerprint(), e, false);
420 delete ctx;
421 }
422 }
423 if (update) {
424 d->keys[idx].update();
425 ret = d->keys[idx];
426 }
427 return ret;
428 }
429
430 class GpgME::Notation::Private
431 {
432 public:
Private()433 Private() : d(), sidx(0), nidx(0), nota(nullptr) {}
Private(const std::shared_ptr<VerificationResult::Private> & priv,unsigned int sindex,unsigned int nindex)434 Private(const std::shared_ptr<VerificationResult::Private> &priv, unsigned int sindex, unsigned int nindex)
435 : d(priv), sidx(sindex), nidx(nindex), nota(nullptr)
436 {
437
438 }
Private(gpgme_sig_notation_t n)439 Private(gpgme_sig_notation_t n)
440 : d(), sidx(0), nidx(0), nota(n ? new _gpgme_sig_notation(*n) : nullptr)
441 {
442 if (nota && nota->name) {
443 nota->name = strdup(nota->name);
444 }
445 if (nota && nota->value) {
446 nota->value = strdup(nota->value);
447 }
448 }
Private(const Private & other)449 Private(const Private &other)
450 : d(other.d), sidx(other.sidx), nidx(other.nidx), nota(other.nota)
451 {
452 if (nota) {
453 nota->name = strdup(nota->name);
454 nota->value = strdup(nota->value);
455 }
456 }
~Private()457 ~Private()
458 {
459 if (nota) {
460 std::free(nota->name); nota->name = nullptr;
461 std::free(nota->value); nota->value = nullptr;
462 delete nota;
463 }
464 }
465
466 std::shared_ptr<VerificationResult::Private> d;
467 unsigned int sidx, nidx;
468 gpgme_sig_notation_t nota;
469 };
470
Notation(const std::shared_ptr<VerificationResult::Private> & parent,unsigned int sindex,unsigned int nindex)471 GpgME::Notation::Notation(const std::shared_ptr<VerificationResult::Private> &parent, unsigned int sindex, unsigned int nindex)
472 : d(new Private(parent, sindex, nindex))
473 {
474
475 }
476
Notation(gpgme_sig_notation_t nota)477 GpgME::Notation::Notation(gpgme_sig_notation_t nota)
478 : d(new Private(nota))
479 {
480
481 }
482
Notation()483 GpgME::Notation::Notation() : d() {}
484
isNull() const485 bool GpgME::Notation::isNull() const
486 {
487 if (!d) {
488 return true;
489 }
490 if (d->d) {
491 return d->sidx >= d->d->nota.size() || d->nidx >= d->d->nota[d->sidx].size() ;
492 }
493 return !d->nota;
494 }
495
name() const496 const char *GpgME::Notation::name() const
497 {
498 return
499 isNull() ? nullptr :
500 d->d ? d->d->nota[d->sidx][d->nidx].name :
501 d->nota ? d->nota->name : nullptr ;
502 }
503
value() const504 const char *GpgME::Notation::value() const
505 {
506 return
507 isNull() ? nullptr :
508 d->d ? d->d->nota[d->sidx][d->nidx].value :
509 d->nota ? d->nota->value : nullptr ;
510 }
511
flags() const512 GpgME::Notation::Flags GpgME::Notation::flags() const
513 {
514 return
515 convert_from_gpgme_sig_notation_flags_t(
516 isNull() ? 0:
517 d->d ? d->d->nota[d->sidx][d->nidx].flags :
518 d->nota ? d->nota->flags : 0);
519 }
520
isHumanReadable() const521 bool GpgME::Notation::isHumanReadable() const
522 {
523 return flags() & HumanReadable;
524 }
525
isCritical() const526 bool GpgME::Notation::isCritical() const
527 {
528 return flags() & Critical;
529 }
530
operator <<(std::ostream & os,const VerificationResult & result)531 std::ostream &GpgME::operator<<(std::ostream &os, const VerificationResult &result)
532 {
533 os << "GpgME::VerificationResult(";
534 if (!result.isNull()) {
535 os << "\n error: " << result.error()
536 << "\n fileName: " << protect(result.fileName())
537 << "\n signatures:\n";
538 const std::vector<Signature> sigs = result.signatures();
539 std::copy(sigs.begin(), sigs.end(),
540 std::ostream_iterator<Signature>(os, "\n"));
541 }
542 return os << ')';
543 }
544
operator <<(std::ostream & os,Signature::PKAStatus pkaStatus)545 std::ostream &GpgME::operator<<(std::ostream &os, Signature::PKAStatus pkaStatus)
546 {
547 #define OUTPUT( x ) if ( !(pkaStatus & (GpgME::Signature:: x)) ) {} else do { os << #x " "; } while(0)
548 os << "GpgME::Signature::PKAStatus(";
549 OUTPUT(UnknownPKAStatus);
550 OUTPUT(PKAVerificationFailed);
551 OUTPUT(PKAVerificationSucceeded);
552 #undef OUTPUT
553 return os << ')';
554 }
555
operator <<(std::ostream & os,Signature::Summary summary)556 std::ostream &GpgME::operator<<(std::ostream &os, Signature::Summary summary)
557 {
558 #define OUTPUT( x ) if ( !(summary & (GpgME::Signature:: x)) ) {} else do { os << #x " "; } while(0)
559 os << "GpgME::Signature::Summary(";
560 OUTPUT(Valid);
561 OUTPUT(Green);
562 OUTPUT(Red);
563 OUTPUT(KeyRevoked);
564 OUTPUT(KeyExpired);
565 OUTPUT(SigExpired);
566 OUTPUT(KeyMissing);
567 OUTPUT(CrlMissing);
568 OUTPUT(CrlTooOld);
569 OUTPUT(BadPolicy);
570 OUTPUT(SysError);
571 OUTPUT(TofuConflict);
572 #undef OUTPUT
573 return os << ')';
574 }
575
operator <<(std::ostream & os,const Signature & sig)576 std::ostream &GpgME::operator<<(std::ostream &os, const Signature &sig)
577 {
578 os << "GpgME::Signature(";
579 if (!sig.isNull()) {
580 os << "\n Summary: " << sig.summary()
581 << "\n Fingerprint: " << protect(sig.fingerprint())
582 << "\n Status: " << sig.status()
583 << "\n creationTime: " << sig.creationTime()
584 << "\n expirationTime: " << sig.expirationTime()
585 << "\n isWrongKeyUsage: " << sig.isWrongKeyUsage()
586 << "\n isVerifiedUsingChainModel: " << sig.isVerifiedUsingChainModel()
587 << "\n pkaStatus: " << sig.pkaStatus()
588 << "\n pkaAddress: " << protect(sig.pkaAddress())
589 << "\n validity: " << sig.validityAsString()
590 << "\n nonValidityReason: " << sig.nonValidityReason()
591 << "\n publicKeyAlgorithm: " << protect(sig.publicKeyAlgorithmAsString())
592 << "\n hashAlgorithm: " << protect(sig.hashAlgorithmAsString())
593 << "\n policyURL: " << protect(sig.policyURL())
594 << "\n isDeVs " << sig.isDeVs()
595 << "\n notations:\n";
596 const std::vector<Notation> nota = sig.notations();
597 std::copy(nota.begin(), nota.end(),
598 std::ostream_iterator<Notation>(os, "\n"));
599 }
600 return os << ')';
601 }
602
operator <<(std::ostream & os,Notation::Flags flags)603 std::ostream &GpgME::operator<<(std::ostream &os, Notation::Flags flags)
604 {
605 os << "GpgME::Notation::Flags(";
606 #define OUTPUT( x ) if ( !(flags & (GpgME::Notation:: x)) ) {} else do { os << #x " "; } while(0)
607 OUTPUT(HumanReadable);
608 OUTPUT(Critical);
609 #undef OUTPUT
610 return os << ')';
611 }
612
operator <<(std::ostream & os,const Notation & nota)613 std::ostream &GpgME::operator<<(std::ostream &os, const Notation ¬a)
614 {
615 os << "GpgME::Signature::Notation(";
616 if (!nota.isNull()) {
617 os << "\n name: " << protect(nota.name())
618 << "\n value: " << protect(nota.value())
619 << "\n flags: " << nota.flags()
620 << '\n';
621 }
622 return os << ")";
623 }
624