From 7fa3e39f856299c86cb1d6588f05cc092fba785f Mon Sep 17 00:00:00 2001 From: Andreas Auernhammer Date: Wed, 5 Feb 2025 01:29:41 +0100 Subject: [PATCH] sts: allow client-provided intermediate CAs (#20896) This commit allows clients to provide a set of intermediate CA certificates (up to `MaxIntermediateCAs`) that the server will use as intermediate CAs when verifying the trust chain from the client leaf certificate up to one trusted root CA. This is required if the client leaf certificate is not issued by a trusted CA directly but by an intermediate CA. Without this commit, MinIO rejects such certificates. Signed-off-by: Andreas Auernhammer --- cmd/sts-errors.go | 6 ++++++ cmd/sts-handlers.go | 23 +++++++++++++++++++---- cmd/stserrorcode_string.go | 13 +++++++------ 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/cmd/sts-errors.go b/cmd/sts-errors.go index 159331e8e..c68b68f1d 100644 --- a/cmd/sts-errors.go +++ b/cmd/sts-errors.go @@ -81,6 +81,7 @@ const ( ErrSTSMalformedPolicyDocument ErrSTSInsecureConnection ErrSTSInvalidClientCertificate + ErrSTSTooManyIntermediateCAs ErrSTSNotInitialized ErrSTSIAMNotInitialized ErrSTSUpstreamError @@ -145,6 +146,11 @@ var stsErrCodes = stsErrorCodeMap{ Description: "The provided client certificate is invalid. Retry with a different certificate.", HTTPStatusCode: http.StatusBadRequest, }, + ErrSTSTooManyIntermediateCAs: { + Code: "TooManyIntermediateCAs", + Description: "The provided client certificate contains too many intermediate CA certificates", + HTTPStatusCode: http.StatusBadRequest, + }, ErrSTSNotInitialized: { Code: "STSNotInitialized", Description: "STS API not initialized, please try again.", diff --git a/cmd/sts-handlers.go b/cmd/sts-handlers.go index 10621d064..c2b1fa172 100644 --- a/cmd/sts-handlers.go +++ b/cmd/sts-handlers.go @@ -780,12 +780,26 @@ func (sts *stsAPIHandlers) AssumeRoleWithCertificate(w http.ResponseWriter, r *h // policy mapping would be ambiguous. // However, we can filter all CA certificates and only check // whether they client has sent exactly one (non-CA) leaf certificate. - peerCertificates := make([]*x509.Certificate, 0, len(r.TLS.PeerCertificates)) + const MaxIntermediateCAs = 10 + var ( + peerCertificates = make([]*x509.Certificate, 0, len(r.TLS.PeerCertificates)) + intermediates *x509.CertPool + numIntermediates int + ) for _, cert := range r.TLS.PeerCertificates { if cert.IsCA { - continue + numIntermediates++ + if numIntermediates > MaxIntermediateCAs { + writeSTSErrorResponse(ctx, w, ErrSTSTooManyIntermediateCAs, fmt.Errorf("client certificate contains more than %d intermediate CAs", MaxIntermediateCAs)) + return + } + if intermediates == nil { + intermediates = x509.NewCertPool() + } + intermediates.AddCert(cert) + } else { + peerCertificates = append(peerCertificates, cert) } - peerCertificates = append(peerCertificates, cert) } r.TLS.PeerCertificates = peerCertificates @@ -806,7 +820,8 @@ func (sts *stsAPIHandlers) AssumeRoleWithCertificate(w http.ResponseWriter, r *h KeyUsages: []x509.ExtKeyUsage{ x509.ExtKeyUsageClientAuth, }, - Roots: globalRootCAs, + Intermediates: intermediates, + Roots: globalRootCAs, }) if err != nil { writeSTSErrorResponse(ctx, w, ErrSTSInvalidClientCertificate, err) diff --git a/cmd/stserrorcode_string.go b/cmd/stserrorcode_string.go index 1774e98cb..b11928350 100644 --- a/cmd/stserrorcode_string.go +++ b/cmd/stserrorcode_string.go @@ -18,15 +18,16 @@ func _() { _ = x[ErrSTSMalformedPolicyDocument-7] _ = x[ErrSTSInsecureConnection-8] _ = x[ErrSTSInvalidClientCertificate-9] - _ = x[ErrSTSNotInitialized-10] - _ = x[ErrSTSIAMNotInitialized-11] - _ = x[ErrSTSUpstreamError-12] - _ = x[ErrSTSInternalError-13] + _ = x[ErrSTSTooManyIntermediateCAs-10] + _ = x[ErrSTSNotInitialized-11] + _ = x[ErrSTSIAMNotInitialized-12] + _ = x[ErrSTSUpstreamError-13] + _ = x[ErrSTSInternalError-14] } -const _STSErrorCode_name = "STSNoneSTSAccessDeniedSTSMissingParameterSTSInvalidParameterValueSTSWebIdentityExpiredTokenSTSClientGrantsExpiredTokenSTSInvalidClientGrantsTokenSTSMalformedPolicyDocumentSTSInsecureConnectionSTSInvalidClientCertificateSTSNotInitializedSTSIAMNotInitializedSTSUpstreamErrorSTSInternalError" +const _STSErrorCode_name = "STSNoneSTSAccessDeniedSTSMissingParameterSTSInvalidParameterValueSTSWebIdentityExpiredTokenSTSClientGrantsExpiredTokenSTSInvalidClientGrantsTokenSTSMalformedPolicyDocumentSTSInsecureConnectionSTSInvalidClientCertificateSTSTooManyIntermediateCAsSTSNotInitializedSTSIAMNotInitializedSTSUpstreamErrorSTSInternalError" -var _STSErrorCode_index = [...]uint16{0, 7, 22, 41, 65, 91, 118, 145, 171, 192, 219, 236, 256, 272, 288} +var _STSErrorCode_index = [...]uint16{0, 7, 22, 41, 65, 91, 118, 145, 171, 192, 219, 244, 261, 281, 297, 313} func (i STSErrorCode) String() string { if i < 0 || i >= STSErrorCode(len(_STSErrorCode_index)-1) {