github.com/zmap/zlint@v1.1.0/util/gtld.go (about) 1 /* 2 * ZLint Copyright 2018 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 util 16 17 import ( 18 "fmt" 19 "strings" 20 "time" 21 22 "github.com/zmap/zcrypto/x509" 23 ) 24 25 // This package uses the `zlint-gtld-update` command to generate a `tldMap` map. 26 //go:generate zlint-gtld-update ./gtld_map.go 27 28 const ( 29 GTLDPeriodDateFormat = "2006-01-02" 30 ) 31 32 // GTLDPeriod is a struct representing a gTLD's validity period. The field names 33 // are chosen to match the data returned by the ICANN gTLD v2 JSON registry[0]. 34 // See the `zlint-gtld-update` command for more information. 35 // [0] - https://www.icann.org/resources/registries/gtlds/v2/gtlds.json 36 type GTLDPeriod struct { 37 // GTLD is the GTLD the period corresponds to. It is used only for friendly 38 // error messages from `Valid` 39 GTLD string 40 // DelegationDate is the date at which ICANN delegated the gTLD into existence 41 // from the root DNS, or is empty if the gTLD was never delegated. 42 DelegationDate string 43 // RemovalDate is the date at which ICANN removed the gTLD delegation from the 44 // root DNS, or is empty if the gTLD is still delegated and has not been 45 // removed. 46 RemovalDate string 47 } 48 49 // Valid determines if the provided `when` time is within the GTLDPeriod for the 50 // gTLD. E.g. whether a certificate issued at `when` with a subject identifier 51 // using the specified gTLD can be considered a valid use of the gTLD. 52 func (p GTLDPeriod) Valid(when time.Time) error { 53 // NOTE: We can throw away the errors from time.Parse in this function because 54 // the zlint-gtld-update command only writes entries to the generated gTLD map 55 // after the dates have been verified as parseable 56 notBefore, _ := time.Parse(GTLDPeriodDateFormat, p.DelegationDate) 57 if when.Before(notBefore) { 58 return fmt.Errorf(`gTLD ".%s" is not valid until %s`, 59 p.GTLD, p.DelegationDate) 60 } 61 // The removal date may be empty. We only need to check `when` against the 62 // removal when it isn't empty 63 if p.RemovalDate != "" { 64 notAfter, _ := time.Parse(GTLDPeriodDateFormat, p.RemovalDate) 65 if when.After(notAfter) { 66 return fmt.Errorf(`gTLD ".%s" is not valid after %s`, 67 p.GTLD, p.RemovalDate) 68 } 69 } 70 return nil 71 } 72 73 // HasValidTLD checks that a domain ends in a valid TLD that was delegated in 74 // the root DNS at the time specified. 75 func HasValidTLD(domain string, when time.Time) bool { 76 labels := strings.Split(strings.ToLower(domain), ".") 77 rightLabel := labels[len(labels)-1] 78 // if the rightmost label is not present in the tldMap, it isn't valid and 79 // never was. 80 if tldPeriod, present := tldMap[rightLabel]; !present { 81 return false 82 } else if tldPeriod.Valid(when) != nil { 83 // If the TLD exists but the date is outside of the gTLD's validity period 84 // then it is not a valid TLD. 85 return false 86 } 87 // Otherwise the TLD exists, and was a valid TLD delegated in the root DNS 88 // at the time of the given date. 89 return true 90 } 91 92 // IsInTLDMap checks that a label is present in the TLD map. It does not 93 // consider the TLD's validity period and whether the TLD may have been removed, 94 // only whether it was ever a TLD that was delegated. 95 func IsInTLDMap(label string) bool { 96 label = strings.ToLower(label) 97 if _, ok := tldMap[label]; ok { 98 return true 99 } else { 100 return false 101 } 102 } 103 104 // CertificateSubjContainsTLD checks whether the provided Certificate has 105 // a Subject Common Name or DNS Subject Alternate Name that ends in the provided 106 // TLD label. If IsInTLDMap(label) returns false then CertificateSubjInTLD will 107 // return false. 108 func CertificateSubjInTLD(c *x509.Certificate, label string) bool { 109 label = strings.ToLower(label) 110 if strings.HasPrefix(label, ".") { 111 label = label[1:] 112 } 113 if !IsInTLDMap(label) { 114 return false 115 } 116 for _, name := range append(c.DNSNames, c.Subject.CommonName) { 117 if strings.HasSuffix(name, "."+label) { 118 return true 119 } 120 } 121 return false 122 }