go_sysutils/cryptparse/funcs_tlsuri.go
brent saner 5dc944cf21
v1.5.1
FIXES:
* cryptparse.TlsUri.ToConn and cryptparse.TlsUri.ToTlsConn would
  previously use incorrect "host" parameter during dial for UDS/IPC
  sockets.
2024-08-12 15:59:38 -04:00

257 lines
5.4 KiB
Go

package cryptparse
import (
`crypto`
`crypto/tls`
`net`
`net/url`
`os`
`strings`
)
/*
WithConn returns a (crypto/)tls.Conn from an existing/already dialed net.Conn.
underlying should be a "bare" net.Conn; behavior is undefined/unknown if the underlying conn is already a (crypto/)tls.Conn.
*/
func (t *TlsUri) WithConn(underlying net.Conn) (conn *tls.Conn, err error) {
var cfg *tls.Config
if cfg, err = t.ToTlsConfig(); err != nil {
return
}
conn = tls.Client(underlying, cfg)
return
}
/*
ToConn returns a "bare" net.Conn (already dialed) from a TlsUri.
Note that this does NOT include the TLS configured or initialized; use TlsUri.ToTlsConn for that.
(A (crypto/)tls.Conn conforms to net.Conn.)
An error will be returned if no port is explicitly defined in the TlsUri.
*/
func (t *TlsUri) ToConn() (conn net.Conn, err error) {
var ok bool
var connHost string
var params map[string][]string
var netType string = DefaultNetType
params = t.Query()
if params != nil {
if _, ok = params[TlsUriParamNet]; ok {
netType = params[TlsUriParamNet][0]
}
}
netType = strings.ToLower(netType)
switch netType {
case "unix", "unixgram", "unixpacket":
connHost = t.Path
default:
connHost = t.Host
}
if conn, err = net.Dial(netType, connHost); err != nil {
return
}
return
}
/*
ToTlsConfig returns a *tls.Config from a TlsUri.
Unfortunately it's not possible for this library to do the reverse, as CA certificates are not able to be extracted from an x509.CertPool.
*/
func (t *TlsUri) ToTlsConfig() (cfg *tls.Config, err error) {
if cfg, err = ParseTlsUri(t.URL); err != nil {
return
}
return
}
/*
ToTlsConn returns a (crypto/)tls.Conn (already dialed) from a TlsUri.
An error will be returned if no port is explicitly defined in the TlsUri.
*/
func (t *TlsUri) ToTlsConn() (conn *tls.Conn, err error) {
var ok bool
var cfg *tls.Config
var connHost string
var params map[string][]string
var netType string = DefaultNetType
if cfg, err = t.ToTlsConfig(); err != nil {
return
}
params = t.Query()
if params != nil {
if _, ok = params[TlsUriParamNet]; ok {
netType = params[TlsUriParamNet][0]
}
}
netType = strings.ToLower(netType)
switch netType {
case "unix", "unixgram", "unixpacket":
connHost = t.Path
default:
connHost = t.Host
}
if conn, err = tls.Dial(netType, connHost, cfg); err != nil {
return
}
return
}
// ToTlsFlat returns a *TlsFlat from a TlsUri.
func (t *TlsUri) ToTlsFlat() (tlsFlat *TlsFlat, err error) {
var b []byte
var params url.Values
var paramMap map[string][]string
// These also have maps so they can backmap filenames.
var privKeys []crypto.PrivateKey
var privKeyMap map[string][]crypto.PrivateKey
var tlsCerts []tls.Certificate
var tlsCertMap map[string][]tls.Certificate
var isMatch bool
var fCert *TlsFlatCert
var val string
var f TlsFlat = TlsFlat{
SniName: t.Hostname(),
SkipVerify: false,
Certs: nil,
CaFiles: nil,
CipherSuites: nil,
MinTlsProtocol: nil,
MaxTlsProtocol: nil,
Curves: nil,
}
params = t.Query()
paramMap = params
if params == nil {
tlsFlat = &f
return
}
// CA cert(s).
if t.Query().Has(TlsUriParamCa) {
f.CaFiles = append(f.CaFiles, paramMap[TlsUriParamCa]...)
}
// Keys and Certs. These are done first so we can match to a client certificate.
if t.Query().Has(TlsUriParamKey) {
privKeyMap = make(map[string][]crypto.PrivateKey)
for _, kFile := range paramMap[TlsUriParamKey] {
if b, err = os.ReadFile(kFile); err != nil {
return
}
if privKeyMap[kFile], err = ParsePrivateKey(b); err != nil {
return
}
privKeys = append(privKeys, privKeyMap[kFile]...)
}
}
if t.Query().Has(TlsUriParamCert) {
tlsCertMap = make(map[string][]tls.Certificate)
for _, cFile := range paramMap[TlsUriParamCert] {
if b, err = os.ReadFile(cFile); err != nil {
return
}
if tlsCertMap[cFile], err = ParseLeafCert(b, privKeys); err != nil {
return
}
tlsCerts = append(tlsCerts, tlsCertMap[cFile]...)
}
}
// We then correlate. Whew, lads.
for cFile, c := range tlsCertMap {
for _, cert := range c {
for kFile, k := range privKeyMap {
if isMatch, err = IsMatchedPair(k, cert.Leaf); err != nil {
return
} else if isMatch {
fCert = &TlsFlatCert{
CertFile: cFile,
KeyFile: new(string),
}
*fCert.KeyFile = kFile
f.Certs = append(f.Certs, fCert)
}
}
}
}
// Hostname.
if t.Query().Has(TlsUriParamSni) {
f.SniName = t.Query().Get(TlsUriParamSni)
}
// Disable verification.
if t.Query().Has(TlsUriParamNoVerify) {
val = strings.ToLower(t.Query().Get(TlsUriParamNoVerify))
for _, i := range paramBoolValsTrue {
if val == i {
f.SkipVerify = true
break
}
}
}
// Ciphers.
if t.Query().Has(TlsUriParamCipher) {
f.CipherSuites = params[TlsUriParamCipher]
}
// Minimum TLS Protocol Version.
if t.Query().Has(TlsUriParamMinTls) {
f.MinTlsProtocol = new(string)
*f.MinTlsProtocol = t.Query().Get(TlsUriParamMinTls)
}
// Maximum TLS Protocol Version.
if t.Query().Has(TlsUriParamMaxTls) {
f.MaxTlsProtocol = new(string)
*f.MaxTlsProtocol = t.Query().Get(TlsUriParamMaxTls)
}
// Curves.
if t.Query().Has(TlsUriParamCurve) {
f.Curves = params[TlsUriParamCurve]
}
tlsFlat = &f
return
}
// ToURL returns the *url.URL representation of a TlsUri. Note that the params will remain, so remove them explicitly if needed.
func (t *TlsUri) ToURL() (u *url.URL) {
if t == nil {
return
}
u = t.URL
return
}