...

Source file src/github.com/letsencrypt/boulder/va/dns.go

Documentation: github.com/letsencrypt/boulder/va

     1  package va
     2  
     3  import (
     4  	"context"
     5  	"crypto/sha256"
     6  	"crypto/subtle"
     7  	"encoding/base64"
     8  	"fmt"
     9  	"net"
    10  
    11  	"github.com/letsencrypt/boulder/core"
    12  	berrors "github.com/letsencrypt/boulder/errors"
    13  	"github.com/letsencrypt/boulder/identifier"
    14  	"github.com/letsencrypt/boulder/probs"
    15  )
    16  
    17  // getAddr will query for all A/AAAA records associated with hostname and return
    18  // the preferred address, the first net.IP in the addrs slice, and all addresses
    19  // resolved. This is the same choice made by the Go internal resolution library
    20  // used by net/http. If there is an error resolving the hostname, or if no
    21  // usable IP addresses are available then a berrors.DNSError instance is
    22  // returned with a nil net.IP slice.
    23  func (va ValidationAuthorityImpl) getAddrs(ctx context.Context, hostname string) ([]net.IP, error) {
    24  	addrs, err := va.dnsClient.LookupHost(ctx, hostname)
    25  	if err != nil {
    26  		return nil, berrors.DNSError("%v", err)
    27  	}
    28  
    29  	if len(addrs) == 0 {
    30  		// This should be unreachable, as no valid IP addresses being found results
    31  		// in an error being returned from LookupHost.
    32  		return nil, berrors.DNSError("No valid IP addresses found for %s", hostname)
    33  	}
    34  	va.log.Debugf("Resolved addresses for %s: %s", hostname, addrs)
    35  	return addrs, nil
    36  }
    37  
    38  // availableAddresses takes a ValidationRecord and splits the AddressesResolved
    39  // into a list of IPv4 and IPv6 addresses.
    40  func availableAddresses(allAddrs []net.IP) (v4 []net.IP, v6 []net.IP) {
    41  	for _, addr := range allAddrs {
    42  		if addr.To4() != nil {
    43  			v4 = append(v4, addr)
    44  		} else {
    45  			v6 = append(v6, addr)
    46  		}
    47  	}
    48  	return
    49  }
    50  
    51  func (va *ValidationAuthorityImpl) validateDNS01(ctx context.Context, ident identifier.ACMEIdentifier, challenge core.Challenge) ([]core.ValidationRecord, *probs.ProblemDetails) {
    52  	if ident.Type != identifier.DNS {
    53  		va.log.Infof("Identifier type for DNS challenge was not DNS: %s", ident)
    54  		return nil, probs.Malformed("Identifier type for DNS was not itself DNS")
    55  	}
    56  
    57  	// Compute the digest of the key authorization file
    58  	h := sha256.New()
    59  	h.Write([]byte(challenge.ProvidedKeyAuthorization))
    60  	authorizedKeysDigest := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
    61  
    62  	// Look for the required record in the DNS
    63  	challengeSubdomain := fmt.Sprintf("%s.%s", core.DNSPrefix, ident.Value)
    64  	txts, err := va.dnsClient.LookupTXT(ctx, challengeSubdomain)
    65  	if err != nil {
    66  		return nil, probs.DNS(err.Error())
    67  	}
    68  
    69  	// If there weren't any TXT records return a distinct error message to allow
    70  	// troubleshooters to differentiate between no TXT records and
    71  	// invalid/incorrect TXT records.
    72  	if len(txts) == 0 {
    73  		return nil, probs.Unauthorized(fmt.Sprintf("No TXT record found at %s", challengeSubdomain))
    74  	}
    75  
    76  	for _, element := range txts {
    77  		if subtle.ConstantTimeCompare([]byte(element), []byte(authorizedKeysDigest)) == 1 {
    78  			// Successful challenge validation
    79  			return []core.ValidationRecord{{Hostname: ident.Value}}, nil
    80  		}
    81  	}
    82  
    83  	invalidRecord := txts[0]
    84  	if len(invalidRecord) > 100 {
    85  		invalidRecord = invalidRecord[0:100] + "..."
    86  	}
    87  	var andMore string
    88  	if len(txts) > 1 {
    89  		andMore = fmt.Sprintf(" (and %d more)", len(txts)-1)
    90  	}
    91  	return nil, probs.Unauthorized(fmt.Sprintf("Incorrect TXT record %q%s found at %s",
    92  		invalidRecord, andMore, challengeSubdomain))
    93  }
    94  

View as plain text