1 // Copyright 2015-2017 Benjamin Fry <benjaminfry@me.com>
2 //
3 // Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4 // http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5 // http://opensource.org/licenses/MIT>, at your option. This file may not be
6 // copied, modified, or distributed except according to those terms.
7 
8 //! allows a DNS domain name holder to specify one or more Certification
9 //! Authorities (CAs) authorized to issue certificates for that domain.
10 //!
11 //! [RFC 6844, DNS Certification Authority Authorization, January 2013](https://tools.ietf.org/html/rfc6844)
12 //!
13 //! ```text
14 //! The Certification Authority Authorization (CAA) DNS Resource Record
15 //! allows a DNS domain name holder to specify one or more Certification
16 //! Authorities (CAs) authorized to issue certificates for that domain.
17 //! CAA Resource Records allow a public Certification Authority to
18 //! implement additional controls to reduce the risk of unintended
19 //! certificate mis-issue.  This document defines the syntax of the CAA
20 //! record and rules for processing CAA records by certificate issuers.
21 //! ```
22 
23 use std::str;
24 
25 use crate::error::*;
26 use crate::rr::domain::Name;
27 use crate::serialize::binary::*;
28 use url::Url;
29 
30 /// The CAA RR Type
31 ///
32 /// [RFC 6844, DNS Certification Authority Authorization, January 2013](https://tools.ietf.org/html/rfc6844#section-3)
33 ///
34 /// ```text
35 /// 3.  The CAA RR Type
36 ///
37 /// A CAA RR consists of a flags byte and a tag-value pair referred to as
38 /// a property.  Multiple properties MAY be associated with the same
39 /// domain name by publishing multiple CAA RRs at that domain name.  The
40 /// following flag is defined:
41 ///
42 /// Issuer Critical:  If set to '1', indicates that the corresponding
43 ///    property tag MUST be understood if the semantics of the CAA record
44 ///    are to be correctly interpreted by an issuer.
45 ///
46 ///    Issuers MUST NOT issue certificates for a domain if the relevant
47 ///    CAA Resource Record set contains unknown property tags that have
48 ///    the Critical bit set.
49 ///
50 /// The following property tags are defined:
51 ///
52 /// issue <Issuer Domain Name> [; <name>=<value> ]* :  The issue property
53 ///    entry authorizes the holder of the domain name <Issuer Domain
54 ///    Name> or a party acting under the explicit authority of the holder
55 ///    of that domain name to issue certificates for the domain in which
56 ///    the property is published.
57 ///
58 /// issuewild <Issuer Domain Name> [; <name>=<value> ]* :  The issuewild
59 ///    property entry authorizes the holder of the domain name <Issuer
60 ///    Domain Name> or a party acting under the explicit authority of the
61 ///    holder of that domain name to issue wildcard certificates for the
62 ///    domain in which the property is published.
63 ///
64 /// iodef <URL> :  Specifies a URL to which an issuer MAY report
65 ///    certificate issue requests that are inconsistent with the issuer's
66 ///    Certification Practices or Certificate Policy, or that a
67 ///    Certificate Evaluator may use to report observation of a possible
68 ///    policy violation.  The Incident Object Description Exchange Format
69 ///    (IODEF) format is used [RFC5070].
70 ///
71 /// The following example is a DNS zone file (see [RFC1035]) that informs
72 /// CAs that certificates are not to be issued except by the holder of
73 /// the domain name 'ca.example.net' or an authorized agent thereof.
74 /// This policy applies to all subordinate domains under example.com.
75 ///
76 /// $ORIGIN example.com
77 /// .       CAA 0 issue "ca.example.net"
78 ///
79 /// If the domain name holder specifies one or more iodef properties, a
80 /// certificate issuer MAY report invalid certificate requests to that
81 /// address.  In the following example, the domain name holder specifies
82 /// that reports may be made by means of email with the IODEF data as an
83 /// attachment, a Web service [RFC6546], or both:
84 ///
85 /// $ORIGIN example.com
86 /// .       CAA 0 issue "ca.example.net"
87 /// .       CAA 0 iodef "mailto:security@example.com"
88 /// .       CAA 0 iodef "http://iodef.example.com/"
89 ///
90 /// A certificate issuer MAY specify additional parameters that allow
91 /// customers to specify additional parameters governing certificate
92 /// issuance.  This might be the Certificate Policy under which the
93 /// certificate is to be issued, the authentication process to be used
94 /// might be specified, or an account number specified by the CA to
95 /// enable these parameters to be retrieved.
96 ///
97 /// For example, the CA 'ca.example.net' has requested its customer
98 /// 'example.com' to specify the CA's account number '230123' in each of
99 /// the customer's CAA records.
100 ///
101 /// $ORIGIN example.com
102 /// .       CAA 0 issue "ca.example.net; account=230123"
103 ///
104 /// The syntax of additional parameters is a sequence of name-value pairs
105 /// as defined in Section 5.2.  The semantics of such parameters is left
106 /// to site policy and is outside the scope of this document.
107 ///
108 /// The critical flag is intended to permit future versions CAA to
109 /// introduce new semantics that MUST be understood for correct
110 /// processing of the record, preventing conforming CAs that do not
111 /// recognize the new semantics from issuing certificates for the
112 /// indicated domains.
113 ///
114 /// In the following example, the property 'tbs' is flagged as critical.
115 /// Neither the example.net CA nor any other issuer is authorized to
116 /// issue under either policy unless the processing rules for the 'tbs'
117 /// property tag are understood.
118 ///
119 /// $ORIGIN example.com
120 /// .       CAA 0 issue "ca.example.net; policy=ev"
121 /// .       CAA 128 tbs "Unknown"
122 ///
123 /// Note that the above restrictions only apply at certificate issue.
124 /// Since the validity of an end entity certificate is typically a year
125 /// or more, it is quite possible that the CAA records published at a
126 /// domain will change between the time a certificate was issued and
127 /// validation by a relying party.
128 /// ```
129 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
130 pub struct CAA {
131     #[doc(hidden)]
132     pub issuer_critical: bool,
133     #[doc(hidden)]
134     pub tag: Property,
135     #[doc(hidden)]
136     pub value: Value,
137 }
138 
139 impl CAA {
issue( issuer_critical: bool, tag: Property, name: Option<Name>, options: Vec<KeyValue>, ) -> Self140     fn issue(
141         issuer_critical: bool,
142         tag: Property,
143         name: Option<Name>,
144         options: Vec<KeyValue>,
145     ) -> Self {
146         assert!(tag.is_issue() || tag.is_issuewild());
147 
148         CAA {
149             issuer_critical,
150             tag,
151             value: Value::Issuer(name, options),
152         }
153     }
154 
155     /// Creates a new CAA issue record data, the tag is `issue`
156     ///
157     /// # Arguments
158     ///
159     /// * `issuer_critical` - indicates that the corresponding property tag MUST be understood if the semantics of the CAA record are to be correctly interpreted by an issuer
160     /// * `name` - authorized to issue certificates for the associated record label
161     /// * `options` - additional options for the issuer, e.g. 'account', etc.
new_issue(issuer_critical: bool, name: Option<Name>, options: Vec<KeyValue>) -> Self162     pub fn new_issue(issuer_critical: bool, name: Option<Name>, options: Vec<KeyValue>) -> Self {
163         Self::issue(issuer_critical, Property::Issue, name, options)
164     }
165 
166     /// Creates a new CAA issue record data, the tag is `issuewild`
167     ///
168     /// # Arguments
169     ///
170     /// * `issuer_critical` - indicates that the corresponding property tag MUST be understood if the semantics of the CAA record are to be correctly interpreted by an issuer
171     /// * `name` - authorized to issue certificates for the associated record label
172     /// * `options` - additional options for the issuer, e.g. 'account', etc.
new_issuewild( issuer_critical: bool, name: Option<Name>, options: Vec<KeyValue>, ) -> Self173     pub fn new_issuewild(
174         issuer_critical: bool,
175         name: Option<Name>,
176         options: Vec<KeyValue>,
177     ) -> Self {
178         Self::issue(issuer_critical, Property::IssueWild, name, options)
179     }
180 
181     /// Creates a new CAA issue record data, the tag is `iodef`
182     ///
183     /// # Arguments
184     ///
185     /// * `issuer_critical` - indicates that the corresponding property tag MUST be understood if the semantics of the CAA record are to be correctly interpreted by an issuer
186     /// * `url` - Url where issuer errors should be reported
187     ///
188     /// # Panics
189     ///
190     /// If `value` is not `Value::Issuer`
new_iodef(issuer_critical: bool, url: Url) -> Self191     pub fn new_iodef(issuer_critical: bool, url: Url) -> Self {
192         CAA {
193             issuer_critical,
194             tag: Property::Iodef,
195             value: Value::Url(url),
196         }
197     }
198 
199     /// Indicates that the corresponding property tag MUST be understood if the semantics of the CAA record are to be correctly interpreted by an issuer
issuer_critical(&self) -> bool200     pub fn issuer_critical(&self) -> bool {
201         self.issuer_critical
202     }
203 
204     /// The property tag, see struct documentation
tag(&self) -> &Property205     pub fn tag(&self) -> &Property {
206         &self.tag
207     }
208 
209     /// a potentially associated value with the property tag, see struct documentation
value(&self) -> &Value210     pub fn value(&self) -> &Value {
211         &self.value
212     }
213 }
214 
215 /// Specifies in what contexts this key may be trusted for use
216 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
217 pub enum Property {
218     /// The issue property
219     ///    entry authorizes the holder of the domain name <Issuer Domain
220     ///    Name> or a party acting under the explicit authority of the holder
221     ///    of that domain name to issue certificates for the domain in which
222     ///    the property is published.
223     Issue,
224     /// The issuewild
225     ///    property entry authorizes the holder of the domain name <Issuer
226     ///    Domain Name> or a party acting under the explicit authority of the
227     ///    holder of that domain name to issue wildcard certificates for the
228     ///    domain in which the property is published.
229     IssueWild,
230     /// Specifies a URL to which an issuer MAY report
231     ///    certificate issue requests that are inconsistent with the issuer's
232     ///    Certification Practices or Certificate Policy, or that a
233     ///    Certificate Evaluator may use to report observation of a possible
234     ///    policy violation. The Incident Object Description Exchange Format
235     ///    (IODEF) format is used [RFC5070].
236     Iodef,
237     /// Unknown format to Trust-DNS
238     Unknown(String),
239 }
240 
241 impl Property {
242     /// Convert to string form
as_str(&self) -> &str243     pub fn as_str(&self) -> &str {
244         match *self {
245             Property::Issue => "issue",
246             Property::IssueWild => "issuewild",
247             Property::Iodef => "iodef",
248             Property::Unknown(ref property) => property,
249         }
250     }
251 
252     /// true if the property is `issue`
is_issue(&self) -> bool253     pub fn is_issue(&self) -> bool {
254         if let Property::Issue = *self {
255             true
256         } else {
257             false
258         }
259     }
260 
261     /// true if the property is `issueworld`
is_issuewild(&self) -> bool262     pub fn is_issuewild(&self) -> bool {
263         if let Property::IssueWild = *self {
264             true
265         } else {
266             false
267         }
268     }
269 
270     /// true if the property is `iodef`
is_iodef(&self) -> bool271     pub fn is_iodef(&self) -> bool {
272         if let Property::Iodef = *self {
273             true
274         } else {
275             false
276         }
277     }
278 
279     /// true if the property is not known to Trust-DNS
is_unknown(&self) -> bool280     pub fn is_unknown(&self) -> bool {
281         if let Property::Unknown(_) = *self {
282             true
283         } else {
284             false
285         }
286     }
287 }
288 
289 impl From<String> for Property {
from(tag: String) -> Property290     fn from(tag: String) -> Property {
291         // RFC6488 section 5.1 states that "Matching of tag values is case
292         // insensitive."
293         let lower = tag.to_ascii_lowercase();
294         match &lower as &str {
295             "issue" => return Property::Issue,
296             "issuewild" => return Property::IssueWild,
297             "iodef" => return Property::Iodef,
298             &_ => (),
299         }
300 
301         Property::Unknown(tag)
302     }
303 }
304 
305 /// Potential values.
306 ///
307 /// These are based off the Tag field:
308 ///
309 /// `Issue` and `IssueWild` => `Issuer`,
310 /// `Iodef` => `Url`,
311 /// `Unknown` => `Unknown`,
312 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
313 pub enum Value {
314     /// Issuer authorized to issue certs for this zone, and any associated parameters
315     Issuer(Option<Name>, Vec<KeyValue>),
316     /// Url to which to send CA errors
317     Url(Url),
318     /// Unrecognized tag and value by Trust-DNS
319     Unknown(Vec<u8>),
320 }
321 
322 impl Value {
323     /// true if this is an `Issuer`
is_issuer(&self) -> bool324     pub fn is_issuer(&self) -> bool {
325         if let Value::Issuer(..) = *self {
326             true
327         } else {
328             false
329         }
330     }
331 
332     /// true if this is a `Url`
is_url(&self) -> bool333     pub fn is_url(&self) -> bool {
334         if let Value::Url(..) = *self {
335             true
336         } else {
337             false
338         }
339     }
340 
341     /// true if this is an `Unknown`
is_unknown(&self) -> bool342     pub fn is_unknown(&self) -> bool {
343         if let Value::Unknown(..) = *self {
344             true
345         } else {
346             false
347         }
348     }
349 }
350 
read_value( tag: &Property, decoder: &mut BinDecoder, value_len: Restrict<u16>, ) -> ProtoResult<Value>351 fn read_value(
352     tag: &Property,
353     decoder: &mut BinDecoder,
354     value_len: Restrict<u16>,
355 ) -> ProtoResult<Value> {
356     let value_len = value_len.map(|u| u as usize).unverified(/*used purely as length safely*/);
357     match *tag {
358         Property::Issue | Property::IssueWild => {
359             let slice = decoder.read_slice(value_len)?.unverified(/*read_issuer verified as safe*/);
360             let value = read_issuer(slice)?;
361             Ok(Value::Issuer(value.0, value.1))
362         }
363         Property::Iodef => {
364             let url = decoder.read_slice(value_len)?.unverified(/*read_iodef verified as safe*/);
365             let url = read_iodef(url)?;
366             Ok(Value::Url(url))
367         }
368         Property::Unknown(_) => Ok(Value::Unknown(
369             decoder.read_vec(value_len)?.unverified(/*unknown will fail in usage*/),
370         )),
371     }
372 }
373 
emit_value(encoder: &mut BinEncoder, value: &Value) -> ProtoResult<()>374 fn emit_value(encoder: &mut BinEncoder, value: &Value) -> ProtoResult<()> {
375     match *value {
376         Value::Issuer(ref name, ref key_values) => {
377             // output the name
378             if let Some(ref name) = *name {
379                 let name = name.to_string();
380                 encoder.emit_vec(name.as_bytes())?;
381             }
382 
383             // if there was no name, then we just output ';'
384             if name.is_none() && key_values.is_empty() {
385                 return encoder.emit(b';');
386             }
387 
388             for key_value in key_values {
389                 encoder.emit(b';')?;
390                 encoder.emit(b' ')?;
391                 encoder.emit_vec(key_value.key.as_bytes())?;
392                 encoder.emit(b'=')?;
393                 encoder.emit_vec(key_value.value.as_bytes())?;
394             }
395 
396             Ok(())
397         }
398         Value::Url(ref url) => {
399             let url = url.as_str();
400             let bytes = url.as_bytes();
401             encoder.emit_vec(bytes)
402         }
403         Value::Unknown(ref data) => encoder.emit_vec(data),
404     }
405 }
406 
407 enum ParseNameKeyPairState {
408     BeforeKey(Vec<KeyValue>),
409     Key {
410         first_char: bool,
411         key: String,
412         key_values: Vec<KeyValue>,
413     },
414     Value {
415         key: String,
416         value: String,
417         key_values: Vec<KeyValue>,
418     },
419 }
420 
421 /// Reads the issuer field according to the spec
422 ///
423 /// [RFC 6844, DNS Certification Authority Authorization, January 2013](https://tools.ietf.org/html/rfc6844#section-5.2)
424 ///
425 /// ```text
426 /// 5.2.  CAA issue Property
427 ///
428 ///    The issue property tag is used to request that certificate issuers
429 ///    perform CAA issue restriction processing for the domain and to grant
430 ///    authorization to specific certificate issuers.
431 ///
432 ///    The CAA issue property value has the following sub-syntax (specified
433 ///    in ABNF as per [RFC5234]).
434 ///
435 ///    issuevalue  = space [domain] space [";" *(space parameter) space]
436 ///
437 ///    domain = label *("." label)
438 ///    label = (ALPHA / DIGIT) *( *("-") (ALPHA / DIGIT))
439 ///
440 ///    space = *(SP / HTAB)
441 ///
442 ///    parameter =  tag "=" value
443 ///
444 ///    tag = 1*(ALPHA / DIGIT)
445 ///
446 ///    value = *VCHAR
447 ///
448 ///    For consistency with other aspects of DNS administration, domain name
449 ///    values are specified in letter-digit-hyphen Label (LDH-Label) form.
450 ///
451 ///    A CAA record with an issue parameter tag that does not specify a
452 ///    domain name is a request that certificate issuers perform CAA issue
453 ///    restriction processing for the corresponding domain without granting
454 ///    authorization to any certificate issuer.
455 ///
456 ///    This form of issue restriction would be appropriate to specify that
457 ///    no certificates are to be issued for the domain in question.
458 ///
459 ///    For example, the following CAA record set requests that no
460 ///    certificates be issued for the domain 'nocerts.example.com' by any
461 ///    certificate issuer.
462 ///
463 ///    nocerts.example.com       CAA 0 issue ";"
464 ///
465 ///    A CAA record with an issue parameter tag that specifies a domain name
466 ///    is a request that certificate issuers perform CAA issue restriction
467 ///    processing for the corresponding domain and grants authorization to
468 ///    the certificate issuer specified by the domain name.
469 ///
470 ///    For example, the following CAA record set requests that no
471 ///    certificates be issued for the domain 'certs.example.com' by any
472 ///    certificate issuer other than the example.net certificate issuer.
473 ///
474 ///    certs.example.com       CAA 0 issue "example.net"
475 ///
476 ///    CAA authorizations are additive; thus, the result of specifying both
477 ///    the empty issuer and a specified issuer is the same as specifying
478 ///    just the specified issuer alone.
479 ///
480 ///    An issuer MAY choose to specify issuer-parameters that further
481 ///    constrain the issue of certificates by that issuer, for example,
482 ///    specifying that certificates are to be subject to specific validation
483 ///    polices, billed to certain accounts, or issued under specific trust
484 ///    anchors.
485 ///
486 ///    The semantics of issuer-parameters are determined by the issuer
487 ///    alone.
488 /// ```
489 ///
490 /// Updated parsing rules:
491 ///
492 /// [RFC 6844bis, CAA Resource Record, May 2018](https://tools.ietf.org/html/draft-ietf-lamps-rfc6844bis-00)
493 /// [RFC 6844, CAA Record Extensions, May 2018](https://tools.ietf.org/html/draft-ietf-acme-caa-04)
494 ///
495 /// This explicitly allows `-` in key names, diverging from the original RFC. To support this, key names will
496 /// allow `-` as non-starting characters. Additionally, this significantly relaxes the characters allowed in the value
497 /// to allow URL like characters (it does not validate URL syntax).
read_issuer(bytes: &[u8]) -> ProtoResult<(Option<Name>, Vec<KeyValue>)>498 pub fn read_issuer(bytes: &[u8]) -> ProtoResult<(Option<Name>, Vec<KeyValue>)> {
499     let mut byte_iter = bytes.iter();
500 
501     // we want to reuse the name parsing rules
502     let name: Option<Name> = {
503         let take_name = byte_iter.by_ref().take_while(|ch| char::from(**ch) != ';');
504         let name_str = take_name.cloned().collect::<Vec<u8>>();
505 
506         if !name_str.is_empty() {
507             let name_str = str::from_utf8(&name_str)?;
508             Some(Name::parse(name_str, None)?)
509         } else {
510             None
511         }
512     };
513 
514     // initial state is looking for a key ';' is valid...
515     let mut state = ParseNameKeyPairState::BeforeKey(vec![]);
516 
517     // run the state machine through all remaining data, collecting all key/value pairs.
518     for ch in byte_iter {
519         match state {
520             // Name was already successfully parsed, otherwise we couldn't get here.
521             ParseNameKeyPairState::BeforeKey(key_values) => {
522                 match char::from(*ch) {
523                     // gobble ';', ' ', and tab
524                     ';' | ' ' | '\u{0009}' => state = ParseNameKeyPairState::BeforeKey(key_values),
525                     ch if ch.is_alphanumeric() && ch != '=' => {
526                         // We found the beginning of a new Key
527                         let mut key = String::new();
528                         key.push(ch);
529 
530                         state = ParseNameKeyPairState::Key {
531                             first_char: true,
532                             key,
533                             key_values,
534                         }
535                     }
536                     ch => return Err(format!("bad character in CAA issuer key: {}", ch).into()),
537                 }
538             }
539             ParseNameKeyPairState::Key {
540                 first_char,
541                 mut key,
542                 key_values,
543             } => {
544                 match char::from(*ch) {
545                     // transition to value
546                     '=' => {
547                         let value = String::new();
548                         state = ParseNameKeyPairState::Value {
549                             key,
550                             value,
551                             key_values,
552                         }
553                     }
554                     // push onto the existing key
555                     ch if (ch.is_alphanumeric() || (!first_char && ch == '-'))
556                         && ch != '='
557                         && ch != ';' =>
558                     {
559                         key.push(ch);
560                         state = ParseNameKeyPairState::Key {
561                             first_char: false,
562                             key,
563                             key_values,
564                         }
565                     }
566                     ch => return Err(format!("bad character in CAA issuer key: {}", ch).into()),
567                 }
568             }
569             ParseNameKeyPairState::Value {
570                 key,
571                 mut value,
572                 mut key_values,
573             } => {
574                 match char::from(*ch) {
575                     // transition back to find another pair
576                     ';' => {
577                         key_values.push(KeyValue { key, value });
578                         state = ParseNameKeyPairState::BeforeKey(key_values);
579                     }
580                     // push onto the existing key
581                     ch if !ch.is_control() && !ch.is_whitespace() => {
582                         value.push(ch);
583 
584                         state = ParseNameKeyPairState::Value {
585                             key,
586                             value,
587                             key_values,
588                         }
589                     }
590                     ch => return Err(format!("bad character in CAA issuer value: '{}'", ch).into()),
591                 }
592             }
593         }
594     }
595 
596     // valid final states are BeforeKey, where there was a final ';' but nothing followed it.
597     //                        Value, where we collected the final chars of the value, but no more data
598     let key_values = match state {
599         ParseNameKeyPairState::BeforeKey(key_values) => key_values,
600         ParseNameKeyPairState::Value {
601             key,
602             value,
603             mut key_values,
604         } => {
605             key_values.push(KeyValue { key, value });
606             key_values
607         }
608         ParseNameKeyPairState::Key { key, .. } => {
609             return Err(format!("key missing value: {}", key).into());
610         }
611     };
612 
613     Ok((name, key_values))
614 }
615 
616 /// Incident Object Description Exchange Format
617 ///
618 /// [RFC 6844, DNS Certification Authority Authorization, January 2013](https://tools.ietf.org/html/rfc6844#section-5.4)
619 ///
620 /// ```text
621 /// 5.4.  CAA iodef Property
622 ///
623 ///    The iodef property specifies a means of reporting certificate issue
624 ///    requests or cases of certificate issue for the corresponding domain
625 ///    that violate the security policy of the issuer or the domain name
626 ///    holder.
627 ///
628 ///    The Incident Object Description Exchange Format (IODEF) [RFC5070] is
629 ///    used to present the incident report in machine-readable form.
630 ///
631 ///    The iodef property takes a URL as its parameter.  The URL scheme type
632 ///    determines the method used for reporting:
633 ///
634 ///    mailto:  The IODEF incident report is reported as a MIME email
635 ///       attachment to an SMTP email that is submitted to the mail address
636 ///       specified.  The mail message sent SHOULD contain a brief text
637 ///       message to alert the recipient to the nature of the attachment.
638 ///
639 ///    http or https:  The IODEF report is submitted as a Web service
640 ///       request to the HTTP address specified using the protocol specified
641 ///       in [RFC6546].
642 /// ```
read_iodef(url: &[u8]) -> ProtoResult<Url>643 pub fn read_iodef(url: &[u8]) -> ProtoResult<Url> {
644     let url = str::from_utf8(url)?;
645     let url = Url::parse(url)?;
646     Ok(url)
647 }
648 
649 /// Issuer key and value pairs.
650 ///
651 /// See [RFC 6844, DNS Certification Authority Authorization, January 2013](https://tools.ietf.org/html/rfc6844#section-5.2)
652 /// for more explanation.
653 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
654 pub struct KeyValue {
655     key: String,
656     value: String,
657 }
658 
659 impl KeyValue {
660     /// Construct a new KeyValue pair
new<K: Into<String>, V: Into<String>>(key: K, value: V) -> Self661     pub fn new<K: Into<String>, V: Into<String>>(key: K, value: V) -> Self {
662         KeyValue {
663             key: key.into(),
664             value: value.into(),
665         }
666     }
667 
668     /// Gets a reference to the key of the pair.
key(&self) -> &str669     pub fn key(&self) -> &str {
670         &self.key
671     }
672 
673     /// Gets a reference to the value of the pair.
value(&self) -> &str674     pub fn value(&self) -> &str {
675         &self.value
676     }
677 }
678 
679 /// Read the binary CAA format
680 ///
681 /// [RFC 6844, DNS Certification Authority Authorization, January 2013](https://tools.ietf.org/html/rfc6844#section-5.1)
682 ///
683 /// ```text
684 /// 5.1.  Syntax
685 ///
686 ///   A CAA RR contains a single property entry consisting of a tag-value
687 ///   pair.  Each tag represents a property of the CAA record.  The value
688 ///   of a CAA property is that specified in the corresponding value field.
689 ///
690 ///   A domain name MAY have multiple CAA RRs associated with it and a
691 ///   given property MAY be specified more than once.
692 ///
693 ///   The CAA data field contains one property entry.  A property entry
694 ///   consists of the following data fields:
695 ///
696 ///   +0-1-2-3-4-5-6-7-|0-1-2-3-4-5-6-7-|
697 ///   | Flags          | Tag Length = n |
698 ///   +----------------+----------------+...+---------------+
699 ///   | Tag char 0     | Tag char 1     |...| Tag char n-1  |
700 ///   +----------------+----------------+...+---------------+
701 ///   +----------------+----------------+.....+----------------+
702 ///   | Value byte 0   | Value byte 1   |.....| Value byte m-1 |
703 ///   +----------------+----------------+.....+----------------+
704 ///
705 ///   Where n is the length specified in the Tag length field and m is the
706 ///   remaining octets in the Value field (m = d - n - 2) where d is the
707 ///   length of the RDATA section.
708 ///
709 ///   The data fields are defined as follows:
710 ///
711 ///   Flags:  One octet containing the following fields:
712 ///
713 ///      Bit 0, Issuer Critical Flag:  If the value is set to '1', the
714 ///         critical flag is asserted and the property MUST be understood
715 ///         if the CAA record is to be correctly processed by a certificate
716 ///         issuer.
717 ///
718 ///         A Certification Authority MUST NOT issue certificates for any
719 ///         Domain that contains a CAA critical property for an unknown or
720 ///         unsupported property tag that for which the issuer critical
721 ///         flag is set.
722 ///
723 ///      Note that according to the conventions set out in [RFC1035], bit 0
724 ///      is the Most Significant Bit and bit 7 is the Least Significant
725 ///      Bit. Thus, the Flags value 1 means that bit 7 is set while a value
726 ///      of 128 means that bit 0 is set according to this convention.
727 ///
728 ///      All other bit positions are reserved for future use.
729 ///
730 ///      To ensure compatibility with future extensions to CAA, DNS records
731 ///      compliant with this version of the CAA specification MUST clear
732 ///      (set to "0") all reserved flags bits.  Applications that interpret
733 ///      CAA records MUST ignore the value of all reserved flag bits.
734 ///
735 ///   Tag Length:  A single octet containing an unsigned integer specifying
736 ///      the tag length in octets.  The tag length MUST be at least 1 and
737 ///      SHOULD be no more than 15.
738 ///
739 ///   Tag:  The property identifier, a sequence of US-ASCII characters.
740 ///
741 ///      Tag values MAY contain US-ASCII characters 'a' through 'z', 'A'
742 ///      through 'Z', and the numbers 0 through 9.  Tag values SHOULD NOT
743 ///      contain any other characters.  Matching of tag values is case
744 ///      insensitive.
745 ///
746 ///      Tag values submitted for registration by IANA MUST NOT contain any
747 ///      characters other than the (lowercase) US-ASCII characters 'a'
748 ///      through 'z' and the numbers 0 through 9.
749 ///
750 ///   Value:  A sequence of octets representing the property value.
751 ///      Property values are encoded as binary values and MAY employ sub-
752 ///      formats.
753 ///
754 ///      The length of the value field is specified implicitly as the
755 ///      remaining length of the enclosing Resource Record data field.
756 /// ```
read(decoder: &mut BinDecoder, rdata_length: Restrict<u16>) -> ProtoResult<CAA>757 pub fn read(decoder: &mut BinDecoder, rdata_length: Restrict<u16>) -> ProtoResult<CAA> {
758     // the spec declares that other flags should be ignored for future compatibility...
759     let issuer_critical: bool =
760         decoder.read_u8()?.unverified(/*used as bitfield*/) & 0b1000_0000 != 0;
761 
762     let tag_len = decoder.read_u8()?;
763     let value_len: Restrict<u16> = rdata_length
764         .checked_sub(u16::from(tag_len.unverified(/*safe usage here*/)))
765         .checked_sub(2)
766         .map_err(|_| ProtoError::from("CAA tag character(s) out of bounds"))?;
767 
768     let tag = read_tag(decoder, tag_len)?;
769     let tag = Property::from(tag);
770     let value = read_value(&tag, decoder, value_len)?;
771 
772     Ok(CAA {
773         issuer_critical,
774         tag,
775         value,
776     })
777 }
778 
779 // TODO: change this to return &str
read_tag(decoder: &mut BinDecoder, len: Restrict<u8>) -> ProtoResult<String>780 fn read_tag(decoder: &mut BinDecoder, len: Restrict<u8>) -> ProtoResult<String> {
781     let len = len
782         .map(|len| len as usize)
783         .verify_unwrap(|len| *len > 0 && *len <= 15)
784         .map_err(|_| ProtoError::from("CAA tag length out of bounds, 1-15"))?;
785     let mut tag = String::with_capacity(len);
786 
787     for _ in 0..len {
788         let ch = decoder
789             .pop()?
790             .map(char::from)
791             .verify_unwrap(|ch| match ch {
792                 'a'..='z' | 'A'..='Z' | '0'..='9' => true,
793                 _ => false,
794             })
795             .map_err(|_| ProtoError::from("CAA tag character(s) out of bounds"))?;
796 
797         tag.push(ch);
798     }
799 
800     Ok(tag)
801 }
802 
803 /// writes out the tag in binary form to the buffer, returning the number of bytes written
emit_tag(buf: &mut [u8], tag: &Property) -> ProtoResult<u8>804 fn emit_tag(buf: &mut [u8], tag: &Property) -> ProtoResult<u8> {
805     let property = tag.as_str();
806     let property = property.as_bytes();
807 
808     let len = property.len();
809     if len > ::std::u8::MAX as usize {
810         return Err(format!("CAA property too long: {}", len).into());
811     }
812     if buf.len() < len {
813         return Err(format!(
814             "insufficient capacity in CAA buffer: {} for tag: {}",
815             buf.len(),
816             len
817         )
818         .into());
819     }
820 
821     // copy into the buffer
822     let buf = &mut buf[0..len];
823     buf.copy_from_slice(property);
824 
825     Ok(len as u8)
826 }
827 
828 /// Write the RData from the given Decoder
emit(encoder: &mut BinEncoder, caa: &CAA) -> ProtoResult<()>829 pub fn emit(encoder: &mut BinEncoder, caa: &CAA) -> ProtoResult<()> {
830     let mut flags = 0_u8;
831 
832     if caa.issuer_critical {
833         flags |= 0b1000_0000;
834     }
835 
836     encoder.emit(flags)?;
837     // TODO: it might be interesting to use the new place semantics here to output all the data, then place the length back to the beginning...
838     let mut tag_buf = [0_u8; ::std::u8::MAX as usize];
839     let len = emit_tag(&mut tag_buf, &caa.tag)?;
840 
841     // now write to the encoder
842     encoder.emit(len)?;
843     encoder.emit_vec(&tag_buf[0..len as usize])?;
844     emit_value(encoder, &caa.value)?;
845 
846     Ok(())
847 }
848 
849 #[cfg(test)]
850 mod tests {
851     #![allow(clippy::dbg_macro, clippy::print_stdout)]
852 
853     use super::*;
854     use std::str;
855 
856     #[test]
test_read_tag()857     fn test_read_tag() {
858         let ok_under15 = b"abcxyzABCXYZ019";
859         let mut decoder = BinDecoder::new(ok_under15);
860 
861         let read = read_tag(&mut decoder, Restrict::new(ok_under15.len() as u8))
862             .expect("failed to read tag");
863 
864         assert_eq!(str::from_utf8(ok_under15).unwrap(), read);
865     }
866 
867     #[test]
test_bad_tag()868     fn test_bad_tag() {
869         let bad_under15 = b"-";
870         let mut decoder = BinDecoder::new(bad_under15);
871 
872         assert!(read_tag(&mut decoder, Restrict::new(bad_under15.len() as u8)).is_err());
873     }
874 
875     #[test]
test_too_short_tag()876     fn test_too_short_tag() {
877         let too_short = b"";
878         let mut decoder = BinDecoder::new(too_short);
879 
880         assert!(read_tag(&mut decoder, Restrict::new(too_short.len() as u8)).is_err());
881     }
882 
883     #[test]
test_too_long_tag()884     fn test_too_long_tag() {
885         let too_long = b"0123456789abcdef";
886         let mut decoder = BinDecoder::new(too_long);
887 
888         assert!(read_tag(&mut decoder, Restrict::new(too_long.len() as u8)).is_err());
889     }
890 
891     #[test]
test_from_str_property()892     fn test_from_str_property() {
893         assert_eq!(Property::from("Issue".to_string()), Property::Issue);
894         assert_eq!(Property::from("issueWild".to_string()), Property::IssueWild);
895         assert_eq!(Property::from("iodef".to_string()), Property::Iodef);
896         assert_eq!(
897             Property::from("unknown".to_string()),
898             Property::Unknown("unknown".to_string())
899         );
900     }
901 
902     #[test]
test_read_issuer()903     fn test_read_issuer() {
904         // (Option<Name>, Vec<KeyValue>)
905         assert_eq!(
906             read_issuer(b"ca.example.net; account=230123").unwrap(),
907             (
908                 Some(Name::parse("ca.example.net", None).unwrap()),
909                 vec![KeyValue {
910                     key: "account".to_string(),
911                     value: "230123".to_string(),
912                 }],
913             )
914         );
915 
916         assert_eq!(
917             read_issuer(b"ca.example.net").unwrap(),
918             (Some(Name::parse("ca.example.net", None,).unwrap(),), vec![],)
919         );
920         assert_eq!(
921             read_issuer(b"ca.example.net; policy=ev").unwrap(),
922             (
923                 Some(Name::parse("ca.example.net", None).unwrap(),),
924                 vec![KeyValue {
925                     key: "policy".to_string(),
926                     value: "ev".to_string(),
927                 }],
928             )
929         );
930         assert_eq!(
931             read_issuer(b"ca.example.net; account=230123; policy=ev").unwrap(),
932             (
933                 Some(Name::parse("ca.example.net", None).unwrap(),),
934                 vec![
935                     KeyValue {
936                         key: "account".to_string(),
937                         value: "230123".to_string(),
938                     },
939                     KeyValue {
940                         key: "policy".to_string(),
941                         value: "ev".to_string(),
942                     },
943                 ],
944             )
945         );
946         assert_eq!(
947             read_issuer(b"example.net; account-uri=https://example.net/account/1234; validation-methods=dns-01").unwrap(),
948             (
949                 Some(Name::parse("example.net", None).unwrap(),),
950                 vec![
951                     KeyValue {
952                         key: "account-uri".to_string(),
953                         value: "https://example.net/account/1234".to_string(),
954                     },
955                     KeyValue {
956                         key: "validation-methods".to_string(),
957                         value: "dns-01".to_string(),
958                     },
959                 ],
960             )
961         );
962         assert_eq!(read_issuer(b";").unwrap(), (None, vec![]));
963     }
964 
965     #[test]
test_read_iodef()966     fn test_read_iodef() {
967         assert_eq!(
968             read_iodef(b"mailto:security@example.com").unwrap(),
969             Url::parse("mailto:security@example.com").unwrap()
970         );
971         assert_eq!(
972             read_iodef(b"http://iodef.example.com/").unwrap(),
973             Url::parse("http://iodef.example.com/").unwrap()
974         );
975     }
976 
test_encode_decode(rdata: CAA)977     fn test_encode_decode(rdata: CAA) {
978         let mut bytes = Vec::new();
979         let mut encoder: BinEncoder = BinEncoder::new(&mut bytes);
980         emit(&mut encoder, &rdata).expect("failed to emit caa");
981         let bytes = encoder.into_bytes();
982 
983         println!("bytes: {:?}", bytes);
984 
985         let mut decoder: BinDecoder = BinDecoder::new(bytes);
986         let read_rdata =
987             read(&mut decoder, Restrict::new(bytes.len() as u16)).expect("failed to read back");
988         assert_eq!(rdata, read_rdata);
989     }
990 
991     #[test]
test_encode_decode_issue()992     fn test_encode_decode_issue() {
993         test_encode_decode(CAA::new_issue(true, None, vec![]));
994         test_encode_decode(CAA::new_issue(
995             true,
996             Some(Name::parse("example.com", None).unwrap()),
997             vec![],
998         ));
999         test_encode_decode(CAA::new_issue(
1000             true,
1001             Some(Name::parse("example.com", None).unwrap()),
1002             vec![KeyValue::new("key", "value")],
1003         ));
1004         // technically the this parser supports this case, though it's not clear it's something the spec allows for
1005         test_encode_decode(CAA::new_issue(
1006             true,
1007             None,
1008             vec![KeyValue::new("key", "value")],
1009         ));
1010         // test fqdn
1011         test_encode_decode(CAA::new_issue(
1012             true,
1013             Some(Name::parse("example.com.", None).unwrap()),
1014             vec![],
1015         ));
1016     }
1017 
1018     #[test]
test_encode_decode_issuewild()1019     fn test_encode_decode_issuewild() {
1020         test_encode_decode(CAA::new_issuewild(false, None, vec![]));
1021         // other variants handled in test_encode_decode_issue
1022     }
1023 
1024     #[test]
test_encode_decode_iodef()1025     fn test_encode_decode_iodef() {
1026         test_encode_decode(CAA::new_iodef(
1027             true,
1028             Url::parse("http://www.example.com").unwrap(),
1029         ));
1030         test_encode_decode(CAA::new_iodef(
1031             false,
1032             Url::parse("mailto:root@example.com").unwrap(),
1033         ));
1034     }
1035 
test_encode(rdata: CAA, encoded: &[u8])1036     fn test_encode(rdata: CAA, encoded: &[u8]) {
1037         let mut bytes = Vec::new();
1038         let mut encoder: BinEncoder = BinEncoder::new(&mut bytes);
1039         emit(&mut encoder, &rdata).expect("failed to emit caa");
1040         let bytes = encoder.into_bytes();
1041         assert_eq!(&bytes as &[u8], encoded);
1042     }
1043 
1044     #[test]
test_encode_non_fqdn()1045     fn test_encode_non_fqdn() {
1046         let name_bytes: &[u8] = b"issueexample.com";
1047         let header: &[u8] = &[128, 5];
1048         let encoded: Vec<u8> = header.iter().chain(name_bytes.iter()).cloned().collect();
1049 
1050         test_encode(
1051             CAA::new_issue(
1052                 true,
1053                 Some(Name::parse("example.com", None).unwrap()),
1054                 vec![],
1055             ),
1056             &encoded,
1057         );
1058     }
1059 
1060     #[test]
test_encode_fqdn()1061     fn test_encode_fqdn() {
1062         let name_bytes: &[u8] = b"issueexample.com.";
1063         let header: [u8; 2] = [128, 5];
1064         let encoded: Vec<u8> = header.iter().chain(name_bytes.iter()).cloned().collect();
1065 
1066         test_encode(
1067             CAA::new_issue(
1068                 true,
1069                 Some(Name::parse("example.com.", None).unwrap()),
1070                 vec![],
1071             ),
1072             &encoded,
1073         );
1074     }
1075 }
1076