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  }