Skip to content

Commit 39283a6

Browse files
author
OpenShift Bot
authored
Merge pull request #13897 from smarterclayton/sanitize_pem
Merged by openshift-bot
2 parents 4f2b9d7 + 17abab0 commit 39283a6

File tree

2 files changed

+451
-2
lines changed

2 files changed

+451
-2
lines changed

pkg/route/api/validation/validation.go

Lines changed: 138 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package validation
22

33
import (
4+
"bytes"
5+
"crypto/ecdsa"
6+
"crypto/rsa"
47
"crypto/tls"
58
"crypto/x509"
9+
"encoding/pem"
610
"fmt"
711
"strings"
812

@@ -111,8 +115,116 @@ func ValidateRouteStatusUpdate(route *routeapi.Route, older *routeapi.Route) fie
111115
return allErrs
112116
}
113117

118+
type blockVerifierFunc func(block *pem.Block) (*pem.Block, error)
119+
120+
func publicKeyBlockVerifier(block *pem.Block) (*pem.Block, error) {
121+
key, err := x509.ParsePKIXPublicKey(block.Bytes)
122+
if err != nil {
123+
return nil, err
124+
}
125+
block = &pem.Block{
126+
Type: "PUBLIC KEY",
127+
}
128+
if block.Bytes, err = x509.MarshalPKIXPublicKey(key); err != nil {
129+
return nil, err
130+
}
131+
return block, nil
132+
}
133+
134+
func certificateBlockVerifier(block *pem.Block) (*pem.Block, error) {
135+
cert, err := x509.ParseCertificate(block.Bytes)
136+
if err != nil {
137+
return nil, err
138+
}
139+
block = &pem.Block{
140+
Type: "CERTIFICATE",
141+
Bytes: cert.Raw,
142+
}
143+
return block, nil
144+
}
145+
146+
func privateKeyBlockVerifier(block *pem.Block) (*pem.Block, error) {
147+
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
148+
if err != nil {
149+
key, err = x509.ParsePKCS1PrivateKey(block.Bytes)
150+
if err != nil {
151+
key, err = x509.ParseECPrivateKey(block.Bytes)
152+
if err != nil {
153+
return nil, fmt.Errorf("block %s is not valid", block.Type)
154+
}
155+
}
156+
}
157+
switch t := key.(type) {
158+
case *rsa.PrivateKey:
159+
block = &pem.Block{
160+
Type: "RSA PRIVATE KEY",
161+
Bytes: x509.MarshalPKCS1PrivateKey(t),
162+
}
163+
case *ecdsa.PrivateKey:
164+
block = &pem.Block{
165+
Type: "ECDSA PRIVATE KEY",
166+
}
167+
if block.Bytes, err = x509.MarshalECPrivateKey(t); err != nil {
168+
return nil, err
169+
}
170+
default:
171+
return nil, fmt.Errorf("block private key %T is not valid", key)
172+
}
173+
return block, nil
174+
}
175+
176+
func ignoreBlockVerifier(block *pem.Block) (*pem.Block, error) {
177+
return nil, nil
178+
}
179+
180+
var knownBlockDecoders = map[string]blockVerifierFunc{
181+
"RSA PRIVATE KEY": privateKeyBlockVerifier,
182+
"ECDSA PRIVATE KEY": privateKeyBlockVerifier,
183+
"PRIVATE KEY": privateKeyBlockVerifier,
184+
"PUBLIC KEY": publicKeyBlockVerifier,
185+
// Potential "in the wild" PEM encoded blocks that can be normalized
186+
"RSA PUBLIC KEY": publicKeyBlockVerifier,
187+
"DSA PUBLIC KEY": publicKeyBlockVerifier,
188+
"ECDSA PUBLIC KEY": publicKeyBlockVerifier,
189+
"CERTIFICATE": certificateBlockVerifier,
190+
// Blocks that should be dropped
191+
"EC PARAMETERS": ignoreBlockVerifier,
192+
}
193+
194+
// sanitizePEM takes a block of data that should be encoded in PEM and returns only
195+
// the parts of it that parse and serialize as valid recognized certs in valid PEM blocks.
196+
// We perform this transformation to eliminate potentially incorrect / invalid PEM contents
197+
// to prevent OpenSSL or other non Golang tools from receiving unsanitized input.
198+
func sanitizePEM(data []byte) ([]byte, error) {
199+
var block *pem.Block
200+
buf := &bytes.Buffer{}
201+
for len(data) > 0 {
202+
block, data = pem.Decode(data)
203+
if block == nil {
204+
return buf.Bytes(), nil
205+
}
206+
fn, ok := knownBlockDecoders[block.Type]
207+
if !ok {
208+
return nil, fmt.Errorf("unrecognized PEM block %s", block.Type)
209+
}
210+
newBlock, err := fn(block)
211+
if err != nil {
212+
return nil, err
213+
}
214+
if newBlock == nil {
215+
continue
216+
}
217+
if err := pem.Encode(buf, newBlock); err != nil {
218+
return nil, err
219+
}
220+
}
221+
return buf.Bytes(), nil
222+
}
223+
114224
// ExtendedValidateRoute performs an extended validation on the route
115-
// including checking that the TLS config is valid.
225+
// including checking that the TLS config is valid. It also sanitizes
226+
// the contents of valid certificates by removing any data that
227+
// is not recognizable PEM blocks on the incoming route.
116228
func ExtendedValidateRoute(route *routeapi.Route) field.ErrorList {
117229
tlsConfig := route.Spec.TLS
118230
result := field.ErrorList{}
@@ -142,6 +254,11 @@ func ExtendedValidateRoute(route *routeapi.Route) field.ErrorList {
142254
for _, cert := range certs {
143255
certPool.AddCert(cert)
144256
}
257+
if data, err := sanitizePEM([]byte(tlsConfig.CACertificate)); err != nil {
258+
result = append(result, field.Invalid(tlsFieldPath.Child("caCertificate"), "redacted ca certificate data", err.Error()))
259+
} else {
260+
tlsConfig.CACertificate = string(data)
261+
}
145262
}
146263

147264
verifyOptions = &x509.VerifyOptions{
@@ -154,6 +271,12 @@ func ExtendedValidateRoute(route *routeapi.Route) field.ErrorList {
154271
if len(tlsConfig.Certificate) > 0 {
155272
if _, err := validateCertificatePEM(tlsConfig.Certificate, verifyOptions); err != nil {
156273
result = append(result, field.Invalid(tlsFieldPath.Child("certificate"), "redacted certificate data", err.Error()))
274+
} else {
275+
if data, err := sanitizePEM([]byte(tlsConfig.Certificate)); err != nil {
276+
result = append(result, field.Invalid(tlsFieldPath.Child("certificate"), "redacted certificate data", err.Error()))
277+
} else {
278+
tlsConfig.Certificate = string(data)
279+
}
157280
}
158281

159282
certKeyBytes := []byte{}
@@ -168,10 +291,24 @@ func ExtendedValidateRoute(route *routeapi.Route) field.ErrorList {
168291
}
169292
}
170293

294+
if len(tlsConfig.Key) > 0 {
295+
if data, err := sanitizePEM([]byte(tlsConfig.Key)); err != nil {
296+
result = append(result, field.Invalid(tlsFieldPath.Child("key"), "redacted key data", err.Error()))
297+
} else {
298+
tlsConfig.Key = string(data)
299+
}
300+
}
301+
171302
if len(tlsConfig.DestinationCACertificate) > 0 {
172303
if _, err := cmdutil.CertificatesFromPEM([]byte(tlsConfig.DestinationCACertificate)); err != nil {
173304
errmsg := fmt.Sprintf("failed to parse destination CA certificate: %v", err)
174305
result = append(result, field.Invalid(tlsFieldPath.Child("destinationCACertificate"), "redacted destination ca certificate data", errmsg))
306+
} else {
307+
if data, err := sanitizePEM([]byte(tlsConfig.DestinationCACertificate)); err != nil {
308+
result = append(result, field.Invalid(tlsFieldPath.Child("destinationCACertificate"), "redacted destination ca certificate data", err.Error()))
309+
} else {
310+
tlsConfig.DestinationCACertificate = string(data)
311+
}
175312
}
176313
}
177314

0 commit comments

Comments
 (0)