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