github.com/zmap/zlint@v1.1.0/lints/lint_subject_contains_malformed_arpa_ip.go (about) 1 /* 2 * ZLint Copyright 2019 Regents of the University of Michigan 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy 6 * of the License at http://www.apache.org/licenses/LICENSE-2.0 7 * 8 * Unless required by applicable law or agreed to in writing, software 9 * distributed under the License is distributed on an "AS IS" BASIS, 10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 11 * implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package lints 16 17 import ( 18 "fmt" 19 "net" 20 "strings" 21 22 "github.com/zmap/zcrypto/x509" 23 "github.com/zmap/zlint/util" 24 ) 25 26 // arpaMalformedIP is a linter that warns for malformed names under the 27 // .in-addr.arpa or .ip6.arpa zones. 28 // See also: lint_subject_contains_reserved_arpa_ip.go for a lint that ensures 29 // well formed rDNS names in these zones do not specify an address in a IANA 30 // reserved network. 31 type arpaMalformedIP struct{} 32 33 // Initialize for an arpaMalformedIP linter is a NOP to statisfy linting 34 // interfaces. 35 func (l *arpaMalformedIP) Initialize() error { 36 return nil 37 } 38 39 // CheckApplies returns true if the certificate contains any names that end in 40 // one of the two designated zones for reverse DNS: in-addr.arpa or ip6.arpa. 41 func (l *arpaMalformedIP) CheckApplies(c *x509.Certificate) bool { 42 names := append([]string{c.Subject.CommonName}, c.DNSNames...) 43 for _, name := range names { 44 name = strings.ToLower(name) 45 if strings.HasSuffix(name, rdnsIPv4Suffix) || 46 strings.HasSuffix(name, rdnsIPv6Suffix) { 47 return true 48 } 49 } 50 return false 51 } 52 53 // Execute will check the given certificate to ensure that all of the DNS 54 // subject alternate names that specify a reverse DNS name under the respective 55 // IPv4 or IPv6 arpa zones are well formed. A Warn LintResult is returned if 56 // the name is in a reverse DNS zone but has the wrong number of labels. 57 func (l *arpaMalformedIP) Execute(c *x509.Certificate) *LintResult { 58 for _, name := range c.DNSNames { 59 name = strings.ToLower(name) 60 var err error 61 if strings.HasSuffix(name, rdnsIPv4Suffix) { 62 // If the name has the in-addr.arpa suffix then it should be an IPv4 reverse 63 // DNS name. 64 err = lintReversedIPAddressLabels(name, false) 65 } else if strings.HasSuffix(name, rdnsIPv6Suffix) { 66 // If the name has the ip6.arpa suffix then it should be an IPv6 reverse 67 // DNS name. 68 err = lintReversedIPAddressLabels(name, true) 69 } 70 // Return the first error as a negative lint result 71 if err != nil { 72 return &LintResult{ 73 Status: Warn, 74 Details: err.Error(), 75 } 76 } 77 } 78 79 return &LintResult{ 80 Status: Pass, 81 } 82 } 83 84 // lintReversedIPAddressLabels lints the given name as either a reversed IPv4 or 85 // IPv6 address under the respective ARPA zone based on the address class. An 86 // error is returned if there aren't enough labels in the name after removing 87 // the relevant arpa suffix. 88 func lintReversedIPAddressLabels(name string, ipv6 bool) error { 89 numRequiredLabels := rdnsIPv4Labels 90 zoneSuffix := rdnsIPv4Suffix 91 92 if ipv6 { 93 numRequiredLabels = rdnsIPv6Labels 94 zoneSuffix = rdnsIPv6Suffix 95 } 96 97 // Strip off the zone suffix to get only the reversed IP address 98 ipName := strings.TrimSuffix(name, zoneSuffix) 99 100 // A well encoded IPv4 or IPv6 reverse DNS name will have the correct number 101 // of labels to express the address 102 ipLabels := strings.Split(ipName, ".") 103 if len(ipLabels) != numRequiredLabels { 104 return fmt.Errorf( 105 "name %q has too few leading labels (%d vs %d) to be a reverse DNS entry "+ 106 "in the %q zone.", 107 name, len(ipLabels), numRequiredLabels, zoneSuffix) 108 } 109 110 // Reverse the IP labels and try to parse an IP address 111 var ip net.IP 112 if ipv6 { 113 ip = reversedLabelsToIPv6(ipLabels) 114 } else { 115 ip = reversedLabelsToIPv4(ipLabels) 116 } 117 118 // If the result isn't an IP then a warning should be generated 119 if ip == nil { 120 return fmt.Errorf( 121 "the first %d labels of name %q did not parse as a reversed IP address", 122 numRequiredLabels, name) 123 } 124 125 // Otherwise return no error - checking the actual value of the IP is left to 126 // `lint_subject_contains_reserved_arpa_ip.go`. 127 return nil 128 } 129 130 func init() { 131 RegisterLint(&Lint{ 132 Name: "w_subject_contains_malformed_arpa_ip", 133 Description: "Checks no subject domain name contains a rDNS entry in an .arpa zone with the wrong number of labels", 134 Citation: "BRs: 7.1.4.2.1", 135 Source: CABFBaselineRequirements, 136 EffectiveDate: util.CABEffectiveDate, 137 Lint: &arpaMalformedIP{}, 138 }) 139 }