Skip to main content

sequoia_git/
policy.rs

1use std::{
2    collections::{BTreeMap, BTreeSet, btree_map::Entry, HashSet},
3    env,
4    fmt,
5    fs,
6    io::{self, Read, Write},
7    path::{Path, PathBuf},
8    time::SystemTime,
9};
10
11use anyhow::Context;
12
13use git2::{
14    Repository,
15    Oid,
16};
17use serde::{Deserialize, Serialize};
18
19use sequoia_openpgp::{
20    self as openpgp,
21    Cert,
22    Fingerprint,
23    KeyHandle,
24    Packet,
25    cert::{
26        amalgamation::ValidAmalgamation,
27        prelude::{SubordinateKeyAmalgamation, UserIDAmalgamation},
28        raw::{RawCert, RawCertParser},
29    },
30    packet::{
31        Signature,
32        UserID,
33        key::PublicParts,
34    },
35    parse::Parse,
36    parse::{stream::*},
37    policy::StandardPolicy,
38    serialize::Serialize as _,
39    types::SignatureType,
40};
41
42use crate::{
43    Error,
44    Result,
45    utils::prune_cert,
46    utils::serialize_packet,
47    utils::serialize_signature,
48};
49
50/// Whether to trace execution by default (on stderr).
51const TRACE: bool = false;
52
53/// A policy for OpenPGP signatures in git.
54///
55/// A `Policy` governs state changes in git repositories.  A state
56/// change is a change from one git commit with a policy embedded into
57/// it to the next commit, which may change the policy, the source, or
58/// both.
59#[derive(Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
60pub struct Policy {
61    /// Policy version.
62    ///
63    /// We provide backwards-compatibility but not
64    /// forward-compatibility, so that we can evolve the policy
65    /// language.
66    #[serde(default)]
67    version: usize,
68
69    /// Set of commits that is assumed to be good.
70    ///
71    /// The commits will pass verification even if it would fail for
72    /// whatever reason.
73    ///
74    /// To change this set, you need the `audit` right.
75    #[serde(default)]
76    commit_goodlist: BTreeSet<String>,
77
78    /// Set of authorizations.
79    ///
80    /// The key is a free-form, human-readable identifier for the
81    /// authorization.
82    #[serde(default)]
83    authorization: BTreeMap<String, Authorization>,
84}
85
86impl Policy {
87    /// Returns the path to the policy file in the current git
88    /// repository.
89    fn working_dir_policy_file()  -> Result<PathBuf> {
90        let git = git2::Repository::discover(env::current_dir()?)?;
91        if let Some(wd) = git.workdir() {
92            Ok(wd.join("openpgp-policy.toml"))
93        } else {
94            Err(Error::InvalidOperation("doesn't work on bare repos".into()))
95        }
96    }
97
98    pub fn parse_bytes<D: AsRef<[u8]>>(bytes: D) -> Result<Self> {
99        let bytes = bytes.as_ref();
100        let s = std::str::from_utf8(bytes)
101            .map_err(|e| Error::StorageError(e.to_string()))?;
102        let policy = toml::from_str(s)
103            .map_err(|e| Error::StorageError(e.to_string()))?;
104        Ok(policy)
105    }
106
107    /// Reads the policy from the given path.
108    pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Policy> {
109        let path = path.as_ref();
110        let mut f = fs::File::open(path)?;
111
112        let mut s = String::new();
113        f.read_to_string(&mut s)?;
114        let p: Policy =
115            toml::from_str(&s).map_err(|e| Error::StorageError(e.to_string()))?;
116
117        Ok(p)
118    }
119
120    /// Reads the policy from the given path.
121    pub fn read_file_or_default<P: AsRef<Path>>(path: P) -> Result<Policy> {
122        let path = path.as_ref();
123        let mut f = match fs::File::open(path) {
124            Ok(f) => f,
125            Err(e) => if e.kind() == io::ErrorKind::NotFound {
126                return Ok(Policy::default());
127            } else {
128                return Err(e.into());
129            },
130        };
131
132        let mut s = String::new();
133        f.read_to_string(&mut s)?;
134        let p: Policy =
135            toml::from_str(&s).map_err(|e| Error::StorageError(e.to_string()))?;
136
137        Ok(p)
138    }
139
140    /// Reads the policy from the current git working directory.
141    ///
142    /// Default to the empty policy if the work directory doesn't
143    /// contain a policy.
144    pub fn read_from_working_dir() -> Result<Policy> {
145        Self::read_file_or_default(&Self::working_dir_policy_file()?)
146    }
147
148    /// Reads the policy from the given git commit.
149    pub fn read_bytes_from_commit(git: &Repository, commit: &Oid)
150        -> Result<Vec<u8>>
151    {
152        tracer!(TRACE, "Policy::read_bytes_from_commit");
153        t!("(_, {})", commit);
154
155        let commit = git.find_commit(commit.clone())?;
156        let tree = commit.tree()?;
157        let result = if let Some(entry) = tree.get_name("openpgp-policy.toml") {
158            Ok(entry.to_object(&git)?.peel_to_blob()?.content().to_vec())
159        } else {
160            Err(Error::MissingPolicy(commit.id()))
161        };
162        result
163    }
164
165    /// Reads the policy from the given git commit.
166    pub fn read_from_commit(git: &Repository, commit: &Oid) -> Result<Self> {
167        Self::parse_bytes(Self::read_bytes_from_commit(git, commit)?)
168    }
169
170    /// Writes the policy into a file with the given path.
171    pub fn write<P: AsRef<Path>>(&self, path: P) -> Result<()> {
172        let path = path.as_ref();
173        let mut new =
174            tempfile::NamedTempFile::new_in(path.parent().unwrap())?;
175
176        new.write_all(toml::to_string_pretty(&self)
177                      .map_err(|e| Error::StorageError(e.to_string()))?
178                      .as_bytes())?;
179
180        new.persist(path).map_err(|e| Error::StorageError(e.to_string()))?;
181
182        Ok(())
183    }
184
185    /// Writes the policy to the current git working directory.
186    pub fn write_to_working_dir(&self) -> Result<()> {
187        self.write(&Self::working_dir_policy_file()?)
188    }
189
190    /// The policy version.
191    pub fn version(&self) -> usize {
192        self.version
193    }
194
195    /// Set of commits that is assumed to be good.
196    ///
197    /// The commits will pass verification even if it would fail for
198    /// whatever reason.
199    ///
200    /// To change this set, you need the `audit` right.
201    pub fn commit_goodlist(&self) -> &BTreeSet<String> {
202        &self.commit_goodlist
203    }
204
205    /// Set of commits that is assumed to be good.
206    ///
207    /// The commits will pass verification even if it would fail for
208    /// whatever reason.
209    ///
210    /// To change this set, you need the `audit` right.
211    pub fn commit_goodlist_mut(&mut self) -> &mut BTreeSet<String> {
212        &mut self.commit_goodlist
213    }
214
215    /// Set of authorizations.
216    ///
217    /// The key is a free-form, human-readable identifier for the
218    /// authorization.
219    pub fn authorization(&self) -> &BTreeMap<String, Authorization> {
220        &self.authorization
221    }
222
223    /// Set of authorizations.
224    ///
225    /// The key is a free-form, human-readable identifier for the
226    /// authorization.
227    pub fn authorization_mut(&mut self) -> &mut BTreeMap<String, Authorization> {
228        &mut self.authorization
229    }
230
231    /// Computes the difference between this policy and `other`.
232    pub fn diff<'f, 't>(&'f self, other: &'t Policy) -> Result<Diff<'f, 't>> {
233        let mut changes = Vec::new();
234
235        // First, the version.
236        if self.version != other.version {
237            changes.push(Change::VersionChange {
238                from: self.version,
239                to: other.version,
240            });
241        }
242
243        // Then, the commit goodlist.
244        for c in self.commit_goodlist.difference(&other.commit_goodlist) {
245            changes.push(Change::UngoodlistCommit(c.parse()?));
246        }
247        for c in other.commit_goodlist.difference(&self.commit_goodlist) {
248            changes.push(Change::GoodlistCommit(c.parse()?));
249        }
250
251        // This null authorization comes in handy when introducing
252        // new users and removing users.
253        let null_auth = Authorization::default();
254
255        // Now for the authorizations.  First, see if some vanished.
256        for (k, from) in self.authorization.iter()
257            .filter(|(k, _)| ! other.authorization.contains_key(k.as_str()))
258        {
259            // First, remove all the rights and certs.
260            from.diff(&null_auth, k.into(), &mut changes);
261
262            // Finally, remove the user.
263            changes.push(Change::RetireUser(k.into()));
264        }
265
266        // Then, compare the common ones.
267        for (k, from, to) in self.authorization.iter()
268            .filter_map(|(k, from)| other.authorization.get(k)
269                        .map(|to| (k, from, to)))
270        {
271            from.diff(to, k.into(), &mut changes);
272        }
273
274        // See if new users were introduced.
275        for (k, to) in other.authorization.iter()
276            .filter(|(k, _)| ! self.authorization.contains_key(k.as_str()))
277        {
278            // First introduce the new user.
279            changes.push(Change::AddUser(k.into()));
280
281            // Then, all the new rights and certs.
282            null_auth.diff(to, k.into(), &mut changes);
283        }
284
285        Ok(Diff {
286            version: DIFF_JSON_VERSION,
287            from: self,
288            changes,
289            to: other,
290        })
291    }
292
293    /// Verifies that the given commit adheres to this policy.
294    ///
295    /// During verification, the key(s) used are stored in
296    /// `signer_keys`, and the primary user id of the issuing cert at
297    /// the time of the signing is stored in `primary_uids`.  This
298    /// information can be used to prune certs in a policy.
299    ///
300    /// If the commit is goodlisted, this function returns Ok with an
301    /// empty vector of verification results.
302    pub fn verify(&self, git: &Repository, commit_id: &Oid,
303                  commit_policy: &Policy,
304                  signer_keys: &mut BTreeSet<Fingerprint>,
305                  primary_uids: &mut BTreeSet<UserID>)
306                  -> Result<Vec<Result<(String, Signature, Cert, Fingerprint)>>>
307    {
308        tracer!(TRACE, "Policy::verify");
309        t!("verify(_, {})", commit_id);
310
311        if self.commit_goodlist.contains(&commit_id.to_string()) {
312            Ok(vec![])
313        } else {
314            let Ok((sig, data)) = git.extract_signature(commit_id, None)
315            else {
316                return Ok(vec![Err(Error::MissingSignature(commit_id.clone()))]);
317            };
318            t!("{} bytes of signature", sig.len());
319
320            //let commit = git.find_commit(commit_id.clone())?;
321            //let commit_time = commit.time();
322            //let commit_time = std::time::UNIX_EPOCH
323            //    + std::time::Duration::new(commit_time.seconds() as u64, 0);
324
325            // Note the commit time and the signature time will often
326            // diverge.  This is because the signature is created
327            // after the commit is made (the signature is over the
328            // commit, including the creation time).  If we use the
329            // commit's time as the reference time, then the signature
330            // will appear to have been made in the future.  Note: it
331            // is not enough to allow a few seconds of divergence,
332            // because commits can be resigned without changing the
333            // commit's time.
334            self.verify_(&sig[..], &data[..],
335                         commit_policy,
336                         None,
337                         signer_keys,
338                         primary_uids,
339                         Error::MissingSignature(commit_id.clone()),
340                         Right::SignCommit)
341        }
342    }
343
344    /// Verifies that the given tag adheres to the policy.
345    ///
346    /// During verification, the key(s) used are stored in
347    /// `signer_keys`, and the primary user id of the issuing cert at
348    /// the time of the signing is stored in `primary_uids`.  This
349    /// information can be used to prune certs in a policy.
350    pub fn verify_tag(&self, git: &Repository, tag_id: Oid,
351                      commit_policy: &Policy,
352                      signer_keys: &mut BTreeSet<Fingerprint>,
353                      primary_uids: &mut BTreeSet<UserID>)
354        -> Result<Vec<Result<(String, Signature, Cert, Fingerprint)>>>
355    {
356        tracer!(TRACE, "Policy::verify_tag");
357
358        // libgit2 appears to not currently support extracting
359        // signatures from tags.
360        //
361        // https://github.com/rust-lang/git2-rs/issues/1039
362        //
363        // But we can read the tag's content directly from the object
364        // database, and split it into data and signature parts
365        // outself.
366        let odb = git.odb().context("Getting repository's odb")?;
367        let reader = odb.read(tag_id)
368            .with_context(|| {
369                format!("Reading tag object ({})", tag_id)
370            })?;
371        let content = reader.data();
372
373        // Use memmem::TwoWaySearcher. https://docs.rs/memmem/0.1.1/memmem/
374        let begin_signature = b"-----BEGIN PGP SIGNATURE-----\n";
375        let mut start = None;
376        for (i, window) in content.windows(begin_signature.len()).enumerate() {
377            if window == begin_signature {
378                start = Some(i);
379                break;
380            }
381        }
382        let Some(start) = start else {
383            t!("Tag does not contain a signature");
384            return Ok(vec![Err(Error::MissingSignature(tag_id.clone()))]);
385        };
386        let data = &content[..start];
387        t!("Data: {}", String::from_utf8_lossy(data));
388        let sig = &content[start..];
389        t!("Signature: {}", String::from_utf8_lossy(sig));
390
391        //let commit = git.find_commit(commit_id.clone())?;
392        //let commit_time = commit.time();
393        //let commit_time = std::time::UNIX_EPOCH
394        //    + std::time::Duration::new(commit_time.seconds() as u64, 0);
395
396        // Note the commit time and the signature time will often
397        // diverge.  This is because the signature is created
398        // after the commit is made (the signature is over the
399        // commit, including the creation time).  If we use the
400        // commit's time as the reference time, then the signature
401        // will appear to have been made in the future.  Note: it
402        // is not enough to allow a few seconds of divergence,
403        // because commits can be resigned without changing the
404        // commit's time.
405        self.verify_(sig, data,
406                     commit_policy,
407                     None,
408                     signer_keys,
409                     primary_uids,
410                     Error::MissingSignature(tag_id.clone()),
411                     Right::SignTag)
412    }
413
414    pub fn verify_archive<T, S>(&self,
415                                signature: S,
416                                archive: T)
417                                -> Result<Vec<Result<(String, Signature, Cert,
418                                                      Fingerprint)>>>
419    where
420        T: AsRef<[u8]>,
421        S: AsRef<[u8]>,
422    {
423        let mut signer_keys = Default::default();
424        let mut primary_uids = Default::default();
425        self.verify_(signature.as_ref(),
426                     archive.as_ref(),
427                     self,
428                     None,
429                     &mut signer_keys,
430                     &mut primary_uids,
431                     Error::MissingDataSignature("Tarball".into()),
432                     Right::SignArchive)
433    }
434
435    fn verify_(&self,
436               signature: &[u8],
437               data: &[u8],
438               commit_policy: &Policy,
439               commit_time: Option<SystemTime>,
440               signer_keys: &mut BTreeSet<Fingerprint>,
441               primary_uids: &mut BTreeSet<UserID>,
442               missing_signature_error: Error,
443               require_right: Right)
444               -> Result<Vec<Result<(String, Signature, Cert, Fingerprint)>>>
445    {
446        tracer!(TRACE, "Policy::verify_");
447        t!("verify_({} bytes, {} bytes, _, {:?}, _, _, {}, {})",
448           signature.len(), data.len(), commit_time,
449           missing_signature_error, require_right);
450
451        let p = &StandardPolicy::new();
452        let h = Helper {
453            parent_policy: self,
454            child_policy: commit_policy,
455            signer_keys,
456            primary_uids,
457            results: Default::default(),
458        };
459
460        let mut v = DetachedVerifierBuilder::from_bytes(signature)?
461            .with_policy(p, commit_time, h)?;
462        v.verify_bytes(data)?;
463        let h = v.into_helper();
464        let signature_results = h.results;
465
466        if signature_results.is_empty() {
467            t!("no signatures found!");
468            return Ok(vec![Err(missing_signature_error)]);
469        }
470
471        if signature_results.iter().all(|r| r.is_err()) {
472            let e = signature_results.into_iter().find(|r| r.is_err())
473                .expect("not empty and not all were ok");
474            return Err(e.unwrap_err());
475        }
476
477        // If we are here, there is at least one valid OpenPGP
478        // signature.  Compute the diff between the policies, and
479        // check whether the authorization invariant is intact.
480        let diff = self.diff(commit_policy)?;
481
482        let mut results: Vec<Result<(String, Signature, Cert, Fingerprint)>>
483            = Vec::new();
484        for r in signature_results {
485            match r {
486                Ok((sig, cert, signer_fpr)) => {
487                    // Find all authorizations that contain a
488                    // certificate that did issue a valid signature.
489                    let cert_fp = cert.fingerprint();
490                    for (name, a) in self.authorization.iter()
491                        .filter(|(_, a)| a.certs().into_iter()
492                                .flat_map(|r| r.into_iter())
493                                .flat_map(|r| r.into_iter())
494                                .any(|c| c.fingerprint() == cert_fp))
495                    {
496                        t!("{}: valid signature", name);
497                        let r = a.rights();
498                        t!("{}: {:?}", name, r);
499
500                        if let Err(e) = r.assert(require_right)
501                            .and_then(|_| diff.assert(&r))
502                        {
503                            results.push(Err(e));
504                        } else {
505                            results.push(
506                                Ok((name.into(), sig.clone(), cert.clone(),
507                                    signer_fpr.clone())));
508                        }
509                    }
510                },
511                Err(e) => results.push(Err(e)),
512            }
513        }
514
515        Ok(results)
516    }
517}
518
519// This fetches keys and computes the validity of the verification.
520struct Helper<'p> {
521    parent_policy: &'p Policy,
522    child_policy: &'p Policy,
523    //signer_userids: &'p mut BTreeSet<openpgp::packet::UserID>,
524    signer_keys: &'p mut BTreeSet<openpgp::Fingerprint>,
525    primary_uids: &'p mut BTreeSet<UserID>,
526    results: Vec<Result<(Signature, Cert, Fingerprint)>>,
527}
528
529impl Helper<'_> {
530    fn handle_result(&mut self, r: VerificationResult) {
531        tracer!(TRACE, "VerificationHelper::handle_result");
532        match r {
533            Ok(sig) => {
534                self.signer_keys.insert(sig.ka.key().fingerprint());
535
536                if let Ok(userid) = sig.ka.valid_cert().primary_userid() {
537                    let u = userid.userid();
538                    if ! self.primary_uids.contains(u) {
539                        self.primary_uids.insert(u.clone());
540                    }
541                }
542
543                self.results.push(
544                    Ok((sig.sig.clone(), sig.ka.cert().clone(),
545                        sig.ka.key().fingerprint().clone())));
546            },
547            Err(e) => {
548                t!("Signature verification failed: {}", e);
549                use VerificationError::*;
550                self.results.push(Err(match e {
551                    MalformedSignature { error, .. } =>
552                        Error::BadSignature(error.to_string()),
553                    MissingKey { sig } => {
554                        let mut issuers = sig.get_issuers();
555                        if issuers.is_empty() {
556                            Error::BadSignature(
557                                "No issuer information".into())
558                        } else {
559                            Error::MissingKey(issuers.remove(0))
560                        }
561                    },
562                    UnboundKey { cert, error, .. } =>
563                        Error::BadKey(cert.key_handle(),
564                                      error.to_string()),
565                    BadKey { ka, error, .. } =>
566                        Error::BadKey(ka.cert().key_handle(),
567                                      error.to_string()),
568                    BadSignature { error, .. } =>
569                        Error::BadSignature(error.to_string()),
570                    UnknownSignature { sig, .. } =>
571                        Error::BadSignature(sig.error().to_string()),
572                    u =>
573                        Error::BadSignature(u.to_string()),
574                }));
575            },
576        }
577    }
578}
579
580impl VerificationHelper for Helper<'_> {
581    fn get_certs(&mut self, ids: &[KeyHandle]) -> openpgp::Result<Vec<Cert>> {
582        tracer!(TRACE, "VerificationHelper::get_certs");
583        t!("get_certs({:?})", ids);
584
585        let mut matches: BTreeMap<Fingerprint, Cert> = BTreeMap::new();
586
587        let mut parent: BTreeMap<Fingerprint, Vec<RawCert>> = BTreeMap::new();
588
589        for (name, auth) in self.parent_policy.authorization.iter() {
590            for cert in auth.certs()? {
591                let cert = cert?;
592
593                // parent contains all of the certificates.
594                let entry = parent.entry(cert.fingerprint());
595                match entry {
596                    Entry::Occupied(mut oe) => {
597                        oe.get_mut().push(cert.clone());
598                    }
599                    Entry::Vacant(ve) => {
600                        ve.insert(vec![ cert.clone() ]);
601                    }
602                }
603
604                if cert.keys().any(
605                    |k| ids.iter().any(|i| i.aliases(&k.key_handle())))
606                {
607                    t!("Signature could be from {}", name);
608
609                    let cert = Cert::try_from(cert)?;
610
611                    let entry = matches.entry(cert.fingerprint());
612                    match entry {
613                        Entry::Occupied(mut oe) => {
614                            let c: &mut Cert = oe.get_mut();
615                            *c = c.clone().merge_public(cert)?;
616                        }
617                        Entry::Vacant(ve) => {
618                            ve.insert(cert);
619                        }
620                    }
621                }
622            }
623        }
624
625        // We now merge in certificate updates from the child.  That
626        // is, we *don't* add new certificates.
627
628        // Optimize the typical case: the policies are the same and
629        // thus the child has no updates.
630        if self.parent_policy != self.child_policy {
631            // Merge child into parent stripping any revocation
632            // certificates from the child.
633            let merge = |parent: Cert, child: Cert| -> Result<Cert> {
634                let child = child.into_packets().filter(|p| {
635                    if let Packet::Signature(sig) = p {
636                        ! matches!(sig.typ(),
637                                   SignatureType::KeyRevocation
638                                   | SignatureType::SubkeyRevocation
639                                   | SignatureType::CertificationRevocation)
640                    } else {
641                        true
642                    }
643                });
644
645                Ok(parent.insert_packets(child)?.0)
646            };
647
648            for (name, auth) in self.child_policy.authorization.iter() {
649                let child_certs = if let Ok(certs) = auth.certs() {
650                    certs
651                } else {
652                    // Silently ignore any parse errors.
653                    continue;
654                };
655
656                for child_cert in child_certs {
657                    let child_cert = if let Ok(cert) = child_cert {
658                        cert
659                    } else {
660                        // Silently ignore any parse errors.
661                        continue;
662                    };
663
664                    let fpr = child_cert.fingerprint();
665                    if let Some(cert) = matches.get_mut(&fpr) {
666                        t!("Updating {}", fpr);
667
668                        let child_cert = if let Ok(cert)
669                            = Cert::try_from(child_cert)
670                        {
671                            cert
672                        } else {
673                            // Silently ignore any parse errors.
674                            continue;
675                        };
676
677                        if let Ok(merged) = merge(cert.clone(), child_cert) {
678                            *cert = merged;
679                        }
680                    } else {
681                        if child_cert.keys().any(
682                            |k| ids.iter().any(|i| i.aliases(&k.key_handle())))
683                        {
684                            t!("Signature could be from {}", name);
685
686                            // cert is not in matches, but it is
687                            // present in the parent policy.  This
688                            // happens if the certificate gains a
689                            // subkey in the child policy, and the
690                            // commit is signed by that subkey.
691                            if let Some(certs)
692                                = parent.get(&child_cert.fingerprint())
693                            {
694                                if let Ok(child_cert) = Cert::try_from(child_cert) {
695                                    let mut parent: Option<Cert> = None;
696                                    for c in certs.into_iter() {
697                                        let c = if let Ok(c) = Cert::try_from(c) {
698                                            c
699                                        } else {
700                                            continue;
701                                        };
702                                        if let Some(parent) = parent.as_mut() {
703                                            if let Ok(merged) =
704                                                parent.clone().merge_public(c)
705                                            {
706                                                *parent = merged;
707                                            }
708                                        } else {
709                                            parent = Some(c);
710                                        }
711                                    }
712
713                                    let parent = if let Some(parent) = parent {
714                                        parent
715                                    } else {
716                                        continue;
717                                    };
718
719                                    // Merge in the child.
720                                    if let Ok(merged) = merge(parent, child_cert) {
721                                        matches.insert(
722                                            merged.fingerprint(), merged);
723                                    }
724                                }
725                            }
726                        }
727                    }
728                }
729            }
730        }
731
732        Ok(matches.into_values().collect())
733    }
734    fn check(&mut self, structure: MessageStructure) -> openpgp::Result<()> {
735        tracer!(TRACE, "VerificationHelper::get_certs");
736        if false {
737            t!("check({:?})", structure);
738        }
739
740        for (i, layer) in structure.into_iter().enumerate() {
741            match layer {
742                MessageLayer::SignatureGroup { results } if i == 0 => {
743                    for r in results {
744                        self.handle_result(r);
745                    }
746                },
747                _ => return Err(Error::BadSignature(
748                    "Unexpected signature structure".into()).into()),
749            }
750        }
751        Ok(())
752    }
753}
754
755#[derive(Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
756pub struct Authorization {
757    #[serde(default, skip_serializing_if = "bool_is_false")]
758    pub sign_commit: bool,
759    #[serde(default, skip_serializing_if = "bool_is_false")]
760    pub sign_tag: bool,
761    #[serde(default, skip_serializing_if = "bool_is_false")]
762    pub sign_archive: bool,
763    #[serde(default, skip_serializing_if = "bool_is_false")]
764    pub add_user: bool,
765    #[serde(default, skip_serializing_if = "bool_is_false")]
766    pub retire_user: bool,
767    #[serde(default, skip_serializing_if = "bool_is_false")]
768    pub audit: bool,
769    pub keyring: String,
770}
771
772fn bool_is_false(b: &bool) -> bool {
773    *b == false
774}
775
776impl Authorization {
777    pub fn rights(&self) -> Rights {
778        use Right::*;
779
780        let mut r = BTreeSet::default();
781
782        if self.sign_commit {
783            r.insert(SignCommit);
784        }
785        if self.sign_tag {
786            r.insert(SignTag);
787        }
788        if self.sign_archive {
789            r.insert(SignArchive);
790        }
791        if self.add_user {
792            r.insert(AddUser);
793        }
794        if self.retire_user {
795            r.insert(RetireUser);
796        }
797        if self.audit {
798            r.insert(Audit);
799        }
800
801        Rights(r)
802    }
803
804    pub fn certs(&self) -> Result<impl Iterator<Item = openpgp::Result<RawCert<'_>>>> {
805        Ok(RawCertParser::from_bytes(self.keyring.as_bytes())?)
806    }
807
808    pub fn set_certs(&mut self, certs: Vec<openpgp::Cert>) -> Result<()> {
809        self.set_certs_filter(certs, |_| true, |_| true)
810    }
811
812    pub fn set_certs_filter<S, U>(&mut self, certs: Vec<openpgp::Cert>,
813                                  mut subkeys: S,
814                                  mut userids: U)
815                                  -> Result<()>
816    where
817        S: FnMut(&SubordinateKeyAmalgamation<PublicParts>) -> bool,
818        U: FnMut(&UserIDAmalgamation) -> bool,
819    {
820        let mut keyring = Vec::new();
821
822        for c in certs {
823            let c = prune_cert(c, &mut subkeys, &mut userids)?;
824            c.armored().export(&mut keyring)?;
825        }
826
827        self.keyring = String::from_utf8(keyring)
828            .map_err(|e| Error::StorageError(e.to_string()))?;
829        Ok(())
830    }
831
832    /// Computes the difference between this authorization and `other`
833    /// recording the changes in `changes`.
834    fn diff(&self, other: &Authorization, name: String,
835            changes: &mut Vec<Change>)
836    {
837        let (from, to) = (self, other);
838
839        // First, see if rights were removed.
840        if from.sign_commit && ! to.sign_commit {
841            changes.push(Change::RemoveRight(name.clone(), Right::SignCommit));
842        }
843        if from.sign_tag && ! to.sign_tag {
844            changes.push(Change::RemoveRight(name.clone(), Right::SignTag));
845        }
846        if from.sign_archive && ! to.sign_archive {
847            changes.push(Change::RemoveRight(name.clone(), Right::SignArchive));
848        }
849        if from.add_user && ! to.add_user {
850            changes.push(Change::RemoveRight(name.clone(), Right::AddUser));
851        }
852        if from.retire_user && ! to.retire_user {
853            changes.push(Change::RemoveRight(name.clone(), Right::RetireUser));
854        }
855        if from.audit && ! to.audit {
856            changes.push(Change::RemoveRight(name.clone(), Right::Audit));
857        }
858
859        // Then, see if rights were added.
860        if ! from.sign_commit && to.sign_commit {
861            changes.push(Change::AddRight(name.clone(), Right::SignCommit));
862        }
863        if ! from.sign_tag && to.sign_tag {
864            changes.push(Change::AddRight(name.clone(), Right::SignTag));
865        }
866        if ! from.sign_archive && to.sign_archive {
867            changes.push(Change::AddRight(name.clone(), Right::SignArchive));
868        }
869        if ! from.add_user && to.add_user {
870            changes.push(Change::AddRight(name.clone(), Right::AddUser));
871        }
872        if ! from.retire_user && to.retire_user {
873            changes.push(Change::AddRight(name.clone(), Right::RetireUser));
874        }
875        if ! from.audit && to.audit {
876            changes.push(Change::AddRight(name.clone(), Right::Audit));
877        }
878
879        // Check for changes to the certificates.
880        if self.keyring != other.keyring {
881            // Parse the keyring.
882            //
883            // XXX: We silently ignore certificates that we are unable
884            // to parse.  We should perhaps reject any changes that we
885            // don't understand.
886            fn parse_keyring<'a>(name: &str, keyring: &'a str)
887                -> BTreeMap<Fingerprint, Vec<RawCert<'a>>>
888            {
889                match RawCertParser::from_bytes(keyring) {
890                    Err(err) => {
891                        eprintln!("Parsing {}'s keyring: {}", name, err);
892                        Default::default()
893                    }
894                    Ok(certs) => {
895                        let certs = certs.into_iter()
896                            .filter_map(|cert| {
897                                match cert {
898                                    Err(err) => {
899                                        eprintln!("Parsing certificate from {}'s keyring: {}",
900                                                  name, err);
901                                        None
902                                    }
903                                    Ok(cert) => Some(cert),
904                                }
905                            });
906
907                        let mut map: BTreeMap<Fingerprint, Vec<RawCert>> = BTreeMap::new();
908                        for cert in certs {
909                            let entry = map.entry(cert.fingerprint());
910                            match entry {
911                                Entry::Occupied(mut oe) => {
912                                    let oe: &mut Vec<RawCert> = oe.get_mut();
913                                    oe.push(cert);
914                                }
915                                Entry::Vacant(ve) => {
916                                    ve.insert(vec![ cert ]);
917                                }
918                            }
919                        }
920
921                        map
922                    }
923                }
924            }
925
926            let old = parse_keyring(&name, &self.keyring);
927            let new = parse_keyring(&name, &other.keyring);
928
929            // Identify certificates have been added or removed.
930            let old_certs: BTreeSet<&Fingerprint> = old.keys().collect();
931            let new_certs: BTreeSet<&Fingerprint> = new.keys().collect();
932
933            for &removed in old_certs.difference(&new_certs) {
934                changes.push(Change::RemoveCert(name.clone(), removed.clone()));
935            }
936            for &added in new_certs.difference(&old_certs) {
937                changes.push(Change::AddCert(name.clone(), added.clone()));
938            }
939
940            // For certificates that the two keyrings have in common,
941            // identify the packets that have been added or removed.
942            for &fpr in old_certs.intersection(&new_certs) {
943                let old = old.get(fpr).expect("have it");
944                let new = new.get(fpr).expect("have it");
945
946                if old == new {
947                    continue;
948                }
949
950                // Convert each signature into a tuple consisting of a
951                // signature and its associated component.
952                let into_packets = |certs: Vec<RawCert>| -> HashSet<(Packet, Signature)> {
953                    let mut pairs: HashSet<(Packet, Signature)> = HashSet::new();
954
955                    for cert in certs.into_iter() {
956                        let mut packets = cert.packets();
957                        let primary_key
958                            = packets.next().expect("have a primary key");
959                        let primary_key = match Packet::try_from(primary_key) {
960                            Ok(p) => p,
961                            Err(err) => {
962                                eprintln!(
963                                    "Warning: {} has a corrupted primary key \
964                                     packet (skipped): {}",
965                                    cert.fingerprint(), err);
966                                continue;
967                            }
968                        };
969
970                        packets.fold(
971                            primary_key,
972                            |component, packet| {
973                                let packet = match Packet::try_from(packet) {
974                                    Ok(p) => p,
975                                    Err(err) => {
976                                        eprintln!(
977                                            "Warning: {} has a corrupted packet \
978                                             (skipped): {}",
979                                            cert.fingerprint(), err);
980                                        return component;
981                                    }
982                                };
983
984                                match packet {
985                                    Packet::Signature(sig) => {
986                                        pairs.insert(
987                                            (component.clone(), sig.clone()));
988                                        component
989                                    }
990                                    Packet::Marker(_) => {
991                                        // Silently skip marker packets.
992                                        component
993                                    }
994                                    _ => {
995                                        // New component.
996                                        packet.clone()
997                                    }
998                                }
999                            });
1000                    }
1001
1002                    pairs
1003                };
1004
1005                let old_packets = into_packets(old.clone());
1006                let new_packets = into_packets(new.clone());
1007
1008                for (component, sig) in old_packets.difference(&new_packets) {
1009                    changes.push(Change::RemovePacket(name.clone(),
1010                                                      fpr.clone(),
1011                                                      component.clone(),
1012                                                      sig.clone()));
1013                }
1014                for (component, sig) in new_packets.difference(&old_packets) {
1015                    changes.push(Change::AddPacket(name.clone(),
1016                                                   fpr.clone(),
1017                                                   component.clone(),
1018                                                   sig.clone()));
1019                }
1020            }
1021        }
1022    }
1023}
1024
1025// The version of the commit output.  This follows semantic
1026// versioning.
1027static DIFF_JSON_VERSION: &'static str = "1.0.0";
1028
1029/// The difference between two [`Policy`]s.
1030#[derive(Serialize)]
1031pub struct Diff<'f, 't> {
1032    version: &'static str,
1033
1034    pub from: &'f Policy,
1035    pub changes: Vec<Change>,
1036    pub to: &'t Policy,
1037}
1038
1039impl Diff<'_, '_> {
1040    fn assert(&self, r: &Rights) -> Result<()> {
1041        for c in &self.changes {
1042            c.assert(r)?;
1043        }
1044        Ok(())
1045    }
1046}
1047
1048use crate::utils::{serialize_fp, serialize_oid};
1049
1050#[derive(Clone, Serialize)]
1051pub enum Change {
1052    VersionChange {
1053        from: usize,
1054        to: usize,
1055    },
1056    GoodlistCommit(
1057        #[serde(serialize_with = "serialize_oid")] Oid),
1058    UngoodlistCommit(
1059        #[serde(serialize_with = "serialize_oid")] Oid),
1060
1061    AddUser(String),
1062    RetireUser(String),
1063
1064    AddRight(String, Right),
1065    RemoveRight(String, Right),
1066
1067    AddCert(String,
1068            #[serde(serialize_with = "serialize_fp")] Fingerprint),
1069    RemoveCert(String,
1070               #[serde(serialize_with = "serialize_fp")] Fingerprint),
1071
1072    AddPacket(String,
1073              // The certificate.
1074              #[serde(serialize_with = "serialize_fp")] Fingerprint,
1075              // The component that the packet is associated with.
1076              #[serde(serialize_with = "serialize_packet")] Packet,
1077              // The signature that was added.
1078              #[serde(serialize_with = "serialize_signature")] Signature),
1079    RemovePacket(String,
1080                 // The certificate.
1081                 #[serde(serialize_with = "serialize_fp")] Fingerprint,
1082                 // The component that the packet is associated with.
1083                 #[serde(serialize_with = "serialize_packet")] Packet,
1084                 // The signature that was removed.
1085                 #[serde(serialize_with = "serialize_signature")] Signature),
1086}
1087
1088impl Change {
1089    fn assert(&self, r: &Rights) -> Result<()> {
1090        use Change::*;
1091        match self {
1092            VersionChange { .. } => r.assert(Right::Audit),
1093            GoodlistCommit(_) => r.assert(Right::Audit),
1094            UngoodlistCommit(_) => r.assert(Right::Audit),
1095
1096            // Rights management.
1097            AddUser(_) => r.assert(Right::AddUser),
1098            RetireUser(_) => r.assert(Right::RetireUser),
1099            AddRight(_, right) =>
1100                r.assert(Right::AddUser).and_then(|_| r.assert(*right)),
1101            RemoveRight(_, right) =>
1102                r.assert(Right::RetireUser).and_then(|_| r.assert(*right)),
1103
1104            // Cert management.
1105            AddCert(_, _) => r.assert(Right::AddUser),
1106            RemoveCert(_, _) => r.assert(Right::RetireUser),
1107
1108            // Lenient cert updates.
1109            AddPacket(_, _, _, _) => Ok(()),
1110
1111            // Strict cert trimmings.
1112            RemovePacket(_, fpr, _component, sig) => {
1113                if sig.get_issuers().into_iter()
1114                    .any(|kh| kh.aliases(KeyHandle::from(fpr)))
1115                {
1116                    // First party signatures can only be removed by
1117                    // authorized users.
1118                    r.assert(Right::RetireUser)
1119                } else {
1120                    Ok(())
1121                }
1122            }
1123        }
1124    }
1125}
1126
1127#[derive(Debug)]
1128pub struct Rights(BTreeSet<Right>);
1129
1130impl Rights {
1131    fn assert(&self, r: Right) -> Result<()> {
1132        if ! self.0.contains(&r) {
1133            Err(Error::Unauthorized(format!("Right {} is missing", r)))
1134        } else {
1135            Ok(())
1136        }
1137    }
1138}
1139
1140#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord)]
1141pub enum Right {
1142    SignCommit,
1143    SignTag,
1144    SignArchive,
1145    AddUser,
1146    RetireUser,
1147    Audit,
1148}
1149
1150impl fmt::Display for Right {
1151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1152        use Right::*;
1153        match self {
1154            SignCommit => f.write_str("sign-commit"),
1155            SignTag => f.write_str("sign-tag"),
1156            SignArchive => f.write_str("sign-archive"),
1157            AddUser => f.write_str("add-user"),
1158            RetireUser => f.write_str("retire-user"),
1159            Audit => f.write_str("audit"),
1160        }
1161    }
1162}