reflection work so far...
This commit is contained in:
234
funcs.go
234
funcs.go
@@ -11,11 +11,14 @@ import (
|
||||
`crypto/x509`
|
||||
`encoding/pem`
|
||||
`errors`
|
||||
`io`
|
||||
`net/url`
|
||||
`os`
|
||||
`path/filepath`
|
||||
`strconv`
|
||||
`strings`
|
||||
|
||||
`r00t2.io/sysutils/envs`
|
||||
`r00t2.io/sysutils/paths`
|
||||
)
|
||||
|
||||
@@ -85,6 +88,60 @@ func IsMatchedPair(privKey crypto.PrivateKey, cert *x509.Certificate) (isMatched
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
ParseMtlsMode parses string s and attempts to derive a TLS client certificate
|
||||
auth mode from it.
|
||||
|
||||
The string may either be the name (as per https://pkg.go.dev/crypto/tls#ClientAuthType)
|
||||
or an int (normal, hex, etc. string representation).
|
||||
*/
|
||||
func ParseMtlsMode(s string) (mode tls.ClientAuthType, err error) {
|
||||
|
||||
var nm string
|
||||
var n int64
|
||||
var m tls.ClientAuthType
|
||||
|
||||
if n, err = strconv.ParseInt(s, 10, 64); err != nil {
|
||||
if errors.Is(err, strconv.ErrSyntax) {
|
||||
// It's a name; parse below.
|
||||
err = nil
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// It's a number.
|
||||
m = tls.ClientAuthType(n)
|
||||
if !strings.HasPrefix(m.String(), "ClientAuthType(") {
|
||||
// It's valid; send it.
|
||||
mode = m
|
||||
return
|
||||
} else {
|
||||
// It's invalid.
|
||||
err = ErrBadMtlsMode
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// It's a name. First normalize the string so we don't need to do so many transforms.
|
||||
nm = strings.ToLower(strings.TrimSpace(s))
|
||||
// Then keep going until we either find it or we run out of valid auth types.
|
||||
for i := 0; ; i++ {
|
||||
m = tls.ClientAuthType(i)
|
||||
if strings.ToLower(m.String()) == nm {
|
||||
// Found; send it.
|
||||
mode = m
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(m.String(), "ClientAuthType(") {
|
||||
// We've reached the end of valid auth names and it still wasn't found.
|
||||
err = ErrBadMtlsMode
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
ParseTlsCipher parses string s and attempts to derive a TLS cipher suite (as a uint16) from it.
|
||||
Use ParseTlsCipherSuite if you wish for a tls.CipherSuite instead.
|
||||
@@ -403,34 +460,42 @@ func ParseTlsUri(tlsUri *url.URL) (tlsConf *tls.Config, err error) {
|
||||
|
||||
var b []byte
|
||||
var rootCAs *x509.CertPool
|
||||
var mtlsCAs *x509.CertPool
|
||||
var intermediateCAs []*x509.Certificate
|
||||
var concatCAs []*x509.Certificate
|
||||
var privKeys []crypto.PrivateKey
|
||||
var tlsCerts []tls.Certificate
|
||||
var allowInvalid bool
|
||||
var ciphers []uint16
|
||||
var curves []tls.CurveID
|
||||
var params map[string][]string
|
||||
var ok bool
|
||||
var val string
|
||||
var minVer uint16
|
||||
var maxVer uint16
|
||||
var ignoreMissing bool
|
||||
var keylog io.Writer
|
||||
var clientAuthType tls.ClientAuthType
|
||||
var params map[tlsUriParam][]string = make(map[tlsUriParam][]string)
|
||||
var buf *bytes.Buffer = new(bytes.Buffer)
|
||||
var srvNm string = tlsUri.Hostname()
|
||||
|
||||
params = tlsUri.Query()
|
||||
|
||||
if params == nil {
|
||||
if tlsUri.Query() == nil || len(tlsUri.Query()) == 0 {
|
||||
tlsConf = &tls.Config{
|
||||
ServerName: srvNm,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range tlsUri.Query() {
|
||||
params[tlsUriParam(k)] = v
|
||||
}
|
||||
|
||||
// These are all filepath(s).
|
||||
for _, k := range []string{
|
||||
TlsUriParamCa,
|
||||
TlsUriParamCert,
|
||||
TlsUriParamKey,
|
||||
for _, k := range []tlsUriParam{
|
||||
ParamCa,
|
||||
ParamCert,
|
||||
ParamKey,
|
||||
ParamMtlsCa,
|
||||
} {
|
||||
if _, ok = params[k]; ok {
|
||||
for idx, _ := range params[k] {
|
||||
@@ -441,15 +506,59 @@ func ParseTlsUri(tlsUri *url.URL) (tlsConf *tls.Config, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok = params[ParamIgnoreMissing]; ok {
|
||||
val = strings.ToLower(params[ParamIgnoreMissing][0])
|
||||
for _, i := range paramBoolValsTrue {
|
||||
if i == val {
|
||||
ignoreMissing = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This *might* be a filepath.
|
||||
if _, ok = params[ParamKeylog]; ok {
|
||||
switch params[ParamKeylog][0] {
|
||||
case KeyLogBufVal:
|
||||
keylog = new(bytes.Buffer)
|
||||
case KeyLogEnvVal:
|
||||
val = params[ParamKeylog][0]
|
||||
if envs.HasEnv(val) {
|
||||
val = os.Getenv(val)
|
||||
if err = paths.RealPath(&val); err != nil {
|
||||
return
|
||||
}
|
||||
if err = os.MkdirAll(filepath.Dir(val), 0700); err != nil {
|
||||
return
|
||||
}
|
||||
if keylog, err = os.OpenFile(val, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o0600); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
default:
|
||||
if err = paths.RealPath(¶ms[ParamKeylog][0]); err != nil {
|
||||
return
|
||||
}
|
||||
if err = os.MkdirAll(filepath.Dir(params[ParamKeylog][0]), 0700); err != nil {
|
||||
return
|
||||
}
|
||||
if keylog, err = os.OpenFile(params[ParamKeylog][0], os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o0600); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CA cert(s).
|
||||
buf.Reset()
|
||||
if _, ok = params[TlsUriParamCa]; ok {
|
||||
if _, ok = params[ParamCa]; ok {
|
||||
rootCAs = x509.NewCertPool()
|
||||
for _, c := range params[TlsUriParamCa] {
|
||||
for _, c := range params[ParamCa] {
|
||||
if b, err = os.ReadFile(c); err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
if errors.Is(err, os.ErrNotExist) && ignoreMissing {
|
||||
err = nil
|
||||
continue
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
buf.Write(b)
|
||||
@@ -463,12 +572,12 @@ func ParseTlsUri(tlsUri *url.URL) (tlsConf *tls.Config, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Keys. These are done first so we can match to a client certificate.
|
||||
// Keys. These are done first so we can match to a leaf certificate.
|
||||
buf.Reset()
|
||||
if _, ok = params[TlsUriParamKey]; ok {
|
||||
for _, k := range params[TlsUriParamKey] {
|
||||
if _, ok = params[ParamKey]; ok {
|
||||
for _, k := range params[ParamKey] {
|
||||
if b, err = os.ReadFile(k); err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
if errors.Is(err, os.ErrNotExist) && ignoreMissing {
|
||||
err = nil
|
||||
continue
|
||||
} else {
|
||||
@@ -482,12 +591,12 @@ func ParseTlsUri(tlsUri *url.URL) (tlsConf *tls.Config, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// (Client) Certificate(s).
|
||||
// (Leaf) Certificate(s).
|
||||
buf.Reset()
|
||||
if _, ok = params[TlsUriParamCert]; ok {
|
||||
for _, c := range params[TlsUriParamCert] {
|
||||
if _, ok = params[ParamCert]; ok {
|
||||
for _, c := range params[ParamCert] {
|
||||
if b, err = os.ReadFile(c); err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
if errors.Is(err, os.ErrNotExist) && ignoreMissing {
|
||||
err = nil
|
||||
continue
|
||||
} else {
|
||||
@@ -496,19 +605,24 @@ func ParseTlsUri(tlsUri *url.URL) (tlsConf *tls.Config, err error) {
|
||||
}
|
||||
buf.Write(b)
|
||||
}
|
||||
if tlsCerts, err = ParseLeafCert(buf.Bytes(), privKeys, intermediateCAs...); err != nil {
|
||||
if tlsCerts, concatCAs, err = ParseLeafCert(buf.Bytes(), privKeys, intermediateCAs...); err != nil {
|
||||
return
|
||||
}
|
||||
if concatCAs != nil {
|
||||
for _, ca := range concatCAs {
|
||||
rootCAs.AddCert(ca)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hostname (Override).
|
||||
if _, ok = params[TlsUriParamSni]; ok {
|
||||
srvNm = params[TlsUriParamSni][0]
|
||||
if _, ok = params[ParamSni]; ok {
|
||||
srvNm = params[ParamSni][0]
|
||||
}
|
||||
|
||||
// Disable Verification.
|
||||
if _, ok = params[TlsUriParamNoVerify]; ok {
|
||||
val = strings.ToLower(params[TlsUriParamNoVerify][0])
|
||||
if _, ok = params[ParamNoVerify]; ok {
|
||||
val = strings.ToLower(params[ParamNoVerify][0])
|
||||
for _, i := range paramBoolValsTrue {
|
||||
if i == val {
|
||||
allowInvalid = true
|
||||
@@ -517,39 +631,73 @@ func ParseTlsUri(tlsUri *url.URL) (tlsConf *tls.Config, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Client/mTLS cert auth mode.
|
||||
if _, ok = params[ParamMtlsMode]; ok {
|
||||
val = params[ParamMtlsMode][0]
|
||||
if clientAuthType, err = ParseMtlsMode(val); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Client/mTLS roots.
|
||||
buf.Reset()
|
||||
if clientAuthType != tls.NoClientCert {
|
||||
if _, ok = params[ParamMtlsCa]; ok {
|
||||
mtlsCAs = x509.NewCertPool()
|
||||
for _, c := range params[ParamMtlsCa] {
|
||||
if b, err = os.ReadFile(c); err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) && ignoreMissing {
|
||||
err = nil
|
||||
continue
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
buf.Write(b)
|
||||
}
|
||||
if mtlsCAs, _, _, err = ParseCA(buf.Bytes()); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
mtlsCAs = rootCAs.Clone()
|
||||
}
|
||||
}
|
||||
|
||||
// Ciphers.
|
||||
if _, ok = params[TlsUriParamCipher]; ok {
|
||||
ciphers = ParseTlsCiphers(strings.Join(params[TlsUriParamCipher], ","))
|
||||
if _, ok = params[ParamCipher]; ok {
|
||||
ciphers = ParseTlsCiphers(strings.Join(params[ParamCipher], ","))
|
||||
}
|
||||
|
||||
// Minimum TLS Protocol Version.
|
||||
if _, ok = params[TlsUriParamMinTls]; ok {
|
||||
if minVer, err = ParseTlsVersion(params[TlsUriParamMinTls][0]); err != nil {
|
||||
if _, ok = params[ParamMinTls]; ok {
|
||||
if minVer, err = ParseTlsVersion(params[ParamMinTls][0]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Maximum TLS Protocol Version.
|
||||
if _, ok = params[TlsUriParamMaxTls]; ok {
|
||||
if maxVer, err = ParseTlsVersion(params[TlsUriParamMaxTls][0]); err != nil {
|
||||
if _, ok = params[ParamMaxTls]; ok {
|
||||
if maxVer, err = ParseTlsVersion(params[ParamMaxTls][0]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Curves.
|
||||
if _, ok = params[TlsUriParamCurve]; ok {
|
||||
curves = ParseTlsCurves(strings.Join(params[TlsUriParamCurve], ","))
|
||||
if _, ok = params[ParamCurve]; ok {
|
||||
curves = ParseTlsCurves(strings.Join(params[ParamCurve], ","))
|
||||
}
|
||||
|
||||
tlsConf = &tls.Config{
|
||||
Certificates: tlsCerts,
|
||||
RootCAs: rootCAs,
|
||||
ServerName: srvNm,
|
||||
ClientAuth: clientAuthType,
|
||||
ClientCAs: mtlsCAs,
|
||||
InsecureSkipVerify: allowInvalid,
|
||||
CipherSuites: ciphers,
|
||||
MinVersion: minVer,
|
||||
MaxVersion: maxVer,
|
||||
CurvePreferences: curves,
|
||||
KeyLogWriter: keylog,
|
||||
}
|
||||
|
||||
return
|
||||
@@ -694,36 +842,36 @@ func ParseDhParams(dhRaw []byte) (params []*dhparam.DH, err error) {
|
||||
*/
|
||||
|
||||
/*
|
||||
ParseLeafCert parses PEM bytes from a (client) certificate file, iterates over a slice of
|
||||
ParseLeafCert parses PEM bytes from a certificate file, iterates over a slice of
|
||||
crypto.PrivateKey (finding one that matches), and returns one (or more) tls.Certificate.
|
||||
|
||||
The key may also be combined with the certificate in the same file.
|
||||
|
||||
If no private key matches or no client cert is found in the file, tlsCerts will be nil/missing
|
||||
If no private key matches or no leaf cert is found in the file, tlsCerts will be nil/missing
|
||||
that certificate but no error will be returned.
|
||||
This behavior can be avoided by passing a nil slice to keys.
|
||||
|
||||
Any leaf certificates ("server" certificate, as opposed to a signer/issuer) found in the file
|
||||
Any leaf certificates ("server"/"client" certificate, as opposed to a signer/issuer) found in the file
|
||||
will be assumed to be the desired one(s).
|
||||
|
||||
Any additional/supplementary intermediates may be provided. Any present in the PEM bytes (certRaw) will be included.
|
||||
Any additional/supplementary intermediates may be provided. Any present in the PEM bytes (certRaw) will be included
|
||||
in the tls.Certificate.Certificates.
|
||||
|
||||
Any *root* CAs found will be discarded. They should/can be extracted seperately via ParseCA.
|
||||
Any *root* CAs found will be split out separately into caCerts and NOT included.
|
||||
They should/can be extracted seperately via ParseCA.
|
||||
|
||||
The parsed and paired certificates and keys can be found in each respective tls.Certificate.Leaf and tls.Certificate.PrivateKey.
|
||||
The parsed and paired certificates and keys can be found in each respective tls.Certificate[n].Leaf and tls.Certificate[n].PrivateKey.
|
||||
Any certs without a corresponding key will be discarded.
|
||||
*/
|
||||
func ParseLeafCert(certRaw []byte, keys []crypto.PrivateKey, intermediates ...*x509.Certificate) (tlsCerts []tls.Certificate, err error) {
|
||||
func ParseLeafCert(certRaw []byte, keys []crypto.PrivateKey, intermediates ...*x509.Certificate) (tlsCerts []tls.Certificate, caCerts []*x509.Certificate, err error) {
|
||||
|
||||
var pemBlocks []*pem.Block
|
||||
var cert *x509.Certificate
|
||||
var certs []*x509.Certificate
|
||||
var caCerts []*x509.Certificate
|
||||
var parsedKeys []crypto.PrivateKey
|
||||
var isMatched bool
|
||||
var foundKey crypto.PrivateKey
|
||||
var interBytes [][]byte
|
||||
var skipKeyPair bool = keys == nil
|
||||
var parsedKeysBuf *bytes.Buffer = new(bytes.Buffer)
|
||||
|
||||
if pemBlocks, err = SplitPem(certRaw); err != nil {
|
||||
@@ -777,7 +925,7 @@ func ParseLeafCert(certRaw []byte, keys []crypto.PrivateKey, intermediates ...*x
|
||||
break
|
||||
}
|
||||
}
|
||||
if foundKey == nil && !skipKeyPair {
|
||||
if foundKey == nil {
|
||||
continue
|
||||
}
|
||||
tlsCerts = append(
|
||||
@@ -790,8 +938,6 @@ func ParseLeafCert(certRaw []byte, keys []crypto.PrivateKey, intermediates ...*x
|
||||
)
|
||||
}
|
||||
|
||||
_ = caCerts
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user