|  |  | @ -5,7 +5,6 @@ import ( | 
			
		
	
		
			
				
					|  |  |  | 	"crypto/x509" | 
			
		
	
		
			
				
					|  |  |  | 	"encoding/json" | 
			
		
	
		
			
				
					|  |  |  | 	"fmt" | 
			
		
	
		
			
				
					|  |  |  | 	"math/big" | 
			
		
	
		
			
				
					|  |  |  | 	"strings" | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | 	"bitbucket.org/lightcodelabs/caddy2" | 
			
		
	
	
		
			
				
					|  |  | @ -22,16 +21,27 @@ type ConnectionPolicies []*ConnectionPolicy | 
			
		
	
		
			
				
					|  |  |  | // TLS configuration which selects the first matching policy based on
 | 
			
		
	
		
			
				
					|  |  |  | // the ClientHello.
 | 
			
		
	
		
			
				
					|  |  |  | func (cp ConnectionPolicies) TLSConfig(ctx caddy2.Context) (*tls.Config, error) { | 
			
		
	
		
			
				
					|  |  |  | 	// connection policy matchers
 | 
			
		
	
		
			
				
					|  |  |  | 	// set up each of the connection policies
 | 
			
		
	
		
			
				
					|  |  |  | 	for i, pol := range cp { | 
			
		
	
		
			
				
					|  |  |  | 		for modName, rawMsg := range pol.MatchersRaw { | 
			
		
	
		
			
				
					|  |  |  | 		// matchers
 | 
			
		
	
		
			
				
					|  |  |  | 		for modName, rawMsg := range pol.Matchers { | 
			
		
	
		
			
				
					|  |  |  | 			val, err := ctx.LoadModule("tls.handshake_match."+modName, rawMsg) | 
			
		
	
		
			
				
					|  |  |  | 			if err != nil { | 
			
		
	
		
			
				
					|  |  |  | 				return nil, fmt.Errorf("loading handshake matcher module '%s': %s", modName, err) | 
			
		
	
		
			
				
					|  |  |  | 			} | 
			
		
	
		
			
				
					|  |  |  | 			cp[i].matchers = append(cp[i].matchers, val.(ConnectionMatcher)) | 
			
		
	
		
			
				
					|  |  |  | 		} | 
			
		
	
		
			
				
					|  |  |  | 		cp[i].MatchersRaw = nil // allow GC to deallocate - TODO: Does this help?
 | 
			
		
	
		
			
				
					|  |  |  | 		cp[i].Matchers = nil // allow GC to deallocate - TODO: Does this help?
 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | 		// certificate selector
 | 
			
		
	
		
			
				
					|  |  |  | 		if pol.CertSelection != nil { | 
			
		
	
		
			
				
					|  |  |  | 			val, err := ctx.LoadModuleInline("policy", "tls.certificate_selection", pol.CertSelection) | 
			
		
	
		
			
				
					|  |  |  | 			if err != nil { | 
			
		
	
		
			
				
					|  |  |  | 				return nil, fmt.Errorf("loading certificate selection module: %s", err) | 
			
		
	
		
			
				
					|  |  |  | 			} | 
			
		
	
		
			
				
					|  |  |  | 			cp[i].certSelector = val.(certmagic.CertificateSelector) | 
			
		
	
		
			
				
					|  |  |  | 			cp[i].CertSelection = nil // allow GC to deallocate - TODO: Does this help?
 | 
			
		
	
		
			
				
					|  |  |  | 		} | 
			
		
	
		
			
				
					|  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | 	// pre-build standard TLS configs so we don't have to at handshake-time
 | 
			
		
	
	
		
			
				
					|  |  | @ -83,7 +93,8 @@ func (cp ConnectionPolicies) TLSConfig(ctx caddy2.Context) (*tls.Config, error) | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | // ConnectionPolicy specifies the logic for handling a TLS handshake.
 | 
			
		
	
		
			
				
					|  |  |  | type ConnectionPolicy struct { | 
			
		
	
		
			
				
					|  |  |  | 	MatchersRaw map[string]json.RawMessage `json:"match,omitempty"` | 
			
		
	
		
			
				
					|  |  |  | 	Matchers      map[string]json.RawMessage `json:"match,omitempty"` | 
			
		
	
		
			
				
					|  |  |  | 	CertSelection json.RawMessage            `json:"certificate_selection,omitempty"` | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | 	CipherSuites []string `json:"cipher_suites,omitempty"` | 
			
		
	
		
			
				
					|  |  |  | 	Curves       []string `json:"curves,omitempty"` | 
			
		
	
	
		
			
				
					|  |  | @ -91,14 +102,14 @@ type ConnectionPolicy struct { | 
			
		
	
		
			
				
					|  |  |  | 	ProtocolMin  string   `json:"protocol_min,omitempty"` | 
			
		
	
		
			
				
					|  |  |  | 	ProtocolMax  string   `json:"protocol_max,omitempty"` | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | 	CertSelection *CertSelectionPolicy `json:"certificate_selection,omitempty"` | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | 	// TODO: Client auth
 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | 	// TODO: see if starlark could be useful here - enterprise only
 | 
			
		
	
		
			
				
					|  |  |  | 	StarlarkHandshake string `json:"starlark_handshake,omitempty"` | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | 	matchers     []ConnectionMatcher | 
			
		
	
		
			
				
					|  |  |  | 	certSelector certmagic.CertificateSelector | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | 	stdTLSConfig *tls.Config | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  | @ -118,8 +129,8 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy2.Context) error { | 
			
		
	
		
			
				
					|  |  |  | 				return nil, fmt.Errorf("getting config for name %s: %v", hello.ServerName, err) | 
			
		
	
		
			
				
					|  |  |  | 			} | 
			
		
	
		
			
				
					|  |  |  | 			newCfg := certmagic.New(tlsApp.certCache, cfgTpl) | 
			
		
	
		
			
				
					|  |  |  | 			if p.CertSelection != nil { | 
			
		
	
		
			
				
					|  |  |  | 				newCfg.CertSelector = makeCertSelector(p) | 
			
		
	
		
			
				
					|  |  |  | 			if p.certSelector != nil { | 
			
		
	
		
			
				
					|  |  |  | 				newCfg.CertSelection = p.certSelector | 
			
		
	
		
			
				
					|  |  |  | 			} | 
			
		
	
		
			
				
					|  |  |  | 			return newCfg.GetCertificate(hello) | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
	
		
			
				
					|  |  | @ -178,56 +189,18 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy2.Context) error { | 
			
		
	
		
			
				
					|  |  |  | 	return nil | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | // CertSelectionPolicy represents a policy for selecting the certificate
 | 
			
		
	
		
			
				
					|  |  |  | // used to complete a handshake when there may be multiple options. All
 | 
			
		
	
		
			
				
					|  |  |  | // fields specified must match the candidate certificate for it to be chosen.
 | 
			
		
	
		
			
				
					|  |  |  | // This was needed to solve https://github.com/mholt/caddy/issues/2588.
 | 
			
		
	
		
			
				
					|  |  |  | type CertSelectionPolicy struct { | 
			
		
	
		
			
				
					|  |  |  | 	SerialNumber        *big.Int    `json:"serial_number,omitempty"` | 
			
		
	
		
			
				
					|  |  |  | 	SubjectOrganization string      `json:"subject.organization,omitempty"` | 
			
		
	
		
			
				
					|  |  |  | 	PublicKeyAlgorithm  pkAlgorithm `json:"public_key_algorithm,omitempty"` | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | func makeCertSelector(p *ConnectionPolicy) func(*tls.ClientHelloInfo, []certmagic.Certificate) (certmagic.Certificate, error) { | 
			
		
	
		
			
				
					|  |  |  | 	return func(hello *tls.ClientHelloInfo, choices []certmagic.Certificate) (certmagic.Certificate, error) { | 
			
		
	
		
			
				
					|  |  |  | 		for _, cert := range choices { | 
			
		
	
		
			
				
					|  |  |  | 			var matchOrg bool | 
			
		
	
		
			
				
					|  |  |  | 			if p.CertSelection.SubjectOrganization != "" { | 
			
		
	
		
			
				
					|  |  |  | 				for _, org := range cert.Subject.Organization { | 
			
		
	
		
			
				
					|  |  |  | 					if p.CertSelection.SubjectOrganization == org { | 
			
		
	
		
			
				
					|  |  |  | 						matchOrg = true | 
			
		
	
		
			
				
					|  |  |  | 						break | 
			
		
	
		
			
				
					|  |  |  | 					} | 
			
		
	
		
			
				
					|  |  |  | 				} | 
			
		
	
		
			
				
					|  |  |  | 			} | 
			
		
	
		
			
				
					|  |  |  | 			if !matchOrg { | 
			
		
	
		
			
				
					|  |  |  | 				continue | 
			
		
	
		
			
				
					|  |  |  | 			} | 
			
		
	
		
			
				
					|  |  |  | 			if p.CertSelection.PublicKeyAlgorithm != pkAlgorithm(x509.UnknownPublicKeyAlgorithm) && | 
			
		
	
		
			
				
					|  |  |  | 				pkAlgorithm(cert.PublicKeyAlgorithm) != p.CertSelection.PublicKeyAlgorithm { | 
			
		
	
		
			
				
					|  |  |  | 				continue | 
			
		
	
		
			
				
					|  |  |  | 			} | 
			
		
	
		
			
				
					|  |  |  | 			if p.CertSelection.SerialNumber != nil && | 
			
		
	
		
			
				
					|  |  |  | 				cert.SerialNumber.Cmp(p.CertSelection.SerialNumber) != 0 { | 
			
		
	
		
			
				
					|  |  |  | 				continue | 
			
		
	
		
			
				
					|  |  |  | 			} | 
			
		
	
		
			
				
					|  |  |  | 			return cert, nil | 
			
		
	
		
			
				
					|  |  |  | 		} | 
			
		
	
		
			
				
					|  |  |  | 		return certmagic.Certificate{}, fmt.Errorf("no certificates matched custom selection policy") | 
			
		
	
		
			
				
					|  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | type pkAlgorithm x509.PublicKeyAlgorithm | 
			
		
	
		
			
				
					|  |  |  | // PublicKeyAlgorithm is a JSON-unmarshalable wrapper type.
 | 
			
		
	
		
			
				
					|  |  |  | type PublicKeyAlgorithm x509.PublicKeyAlgorithm | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | // UnmarshalJSON satisfies json.Unmarshaler.
 | 
			
		
	
		
			
				
					|  |  |  | func (a *pkAlgorithm) UnmarshalJSON(b []byte) error { | 
			
		
	
		
			
				
					|  |  |  | func (a *PublicKeyAlgorithm) UnmarshalJSON(b []byte) error { | 
			
		
	
		
			
				
					|  |  |  | 	algoStr := strings.ToLower(strings.Trim(string(b), `"`)) | 
			
		
	
		
			
				
					|  |  |  | 	algo, ok := publicKeyAlgorithms[algoStr] | 
			
		
	
		
			
				
					|  |  |  | 	if !ok { | 
			
		
	
		
			
				
					|  |  |  | 		return fmt.Errorf("unrecognized public key algorithm: %s (expected one of %v)", | 
			
		
	
		
			
				
					|  |  |  | 			algoStr, publicKeyAlgorithms) | 
			
		
	
		
			
				
					|  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  | 	a = &algo | 
			
		
	
		
			
				
					|  |  |  | 	*a = PublicKeyAlgorithm(algo) | 
			
		
	
		
			
				
					|  |  |  | 	return nil | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  | 
 |