From 7a32e8ffb3f8b720394ef343082d9e5ca14cc99b Mon Sep 17 00:00:00 2001 From: Charlie Egan Date: Wed, 29 Nov 2023 20:24:16 +0000 Subject: [PATCH] topdown/crypto: Add URIStrings field to JSON certs This is being added to make it easier to write policy on the contents of certificate URI SANs. This is where information like SPIFFE IDs etc are contained and it's helpful to Rego authors to have access to these values without rebuilding the URI from the parsed data under URIs. Fixes: #6416 Signed-off-by: Charlie Egan --- ...-cryptox509parseandverifycertificates.yaml | 138 ++++++++++++++++++ ...-cryptox509parsecertificates-raw-uris.yaml | 26 ++++ topdown/crypto.go | 26 +++- 3 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 test/cases/testdata/cryptox509parseandverifycertificates/test-cryptox509parseandverifycertificates.yaml create mode 100644 test/cases/testdata/cryptox509parsecertificates/test-cryptox509parsecertificates-raw-uris.yaml diff --git a/test/cases/testdata/cryptox509parseandverifycertificates/test-cryptox509parseandverifycertificates.yaml b/test/cases/testdata/cryptox509parseandverifycertificates/test-cryptox509parseandverifycertificates.yaml new file mode 100644 index 0000000000..a01fce8061 --- /dev/null +++ b/test/cases/testdata/cryptox509parseandverifycertificates/test-cryptox509parseandverifycertificates.yaml @@ -0,0 +1,138 @@ +cases: +- data: + modules: + - | + package test + + import future.keywords + + certs := `-----BEGIN CERTIFICATE----- + MIIBoDCCAUagAwIBAgIRAJXcMYZALXooNq/VV/grXhMwCgYIKoZIzj0EAwIwLjER + MA8GA1UEChMIT1BBIFRlc3QxGTAXBgNVBAMTEE9QQSBUZXN0IFJvb3QgQ0EwHhcN + MjEwNzAxMTc0MTUzWhcNMzEwNjI5MTc0MTUzWjAuMREwDwYDVQQKEwhPUEEgVGVz + dDEZMBcGA1UEAxMQT1BBIFRlc3QgUm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49 + AwEHA0IABFqhdZA5LjsJgzsBvhgzfayZFOk+C7PmGCi7xz6zOC3xWORJZSNOyZeJ + YzSKFmoMZkcFMfslTW1jp9fwe1xl3HWjRTBDMA4GA1UdDwEB/wQEAwIBBjASBgNV + HRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTch60qxQvLl+AfDfcaXmjvT8GvpzAK + BggqhkjOPQQDAgNIADBFAiBqraIP0l2U0oNuH0+rf36hDks94wSB5EGlGH3lYNMR + ugIhANkbukX5hOP8pJDRWP/pYuv6MBnRY4BS8gpp9Vu31qOb + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIByDCCAW6gAwIBAgIQC0k4DPGrh9me73EJX5zntTAKBggqhkjOPQQDAjAuMREw + DwYDVQQKEwhPUEEgVGVzdDEZMBcGA1UEAxMQT1BBIFRlc3QgUm9vdCBDQTAeFw0y + MTA3MDExNzQxNTNaFw0zMTA2MjkxNzQxNTNaMDYxETAPBgNVBAoTCE9QQSBUZXN0 + MSEwHwYDVQQDExhPUEEgVGVzdCBJbnRlcm1lZGlhdGUgQ0EwWTATBgcqhkjOPQIB + BggqhkjOPQMBBwNCAARvXQa7fy476gDI81nqLYb2SnD459WxBmU0hk2bA3ZuNtI+ + H20KXz6ISmxH3MZ2WBm6rOy7y4Gn+WMCJuxzcl5jo2YwZDAOBgNVHQ8BAf8EBAMC + AQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuslZNjJl0V8I1Gj17IID + ALy/9WEwHwYDVR0jBBgwFoAU3IetKsULy5fgHw33Gl5o70/Br6cwCgYIKoZIzj0E + AwIDSAAwRQIgUwsYApW9Tsm6AstWswaKGie0srB4FUkUbfKwWmUI2JgCIQCBTySN + MF+EiQAMKyz/N9KUuXEckC356WvKcyJaYYcV0w== + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIB8zCCAZqgAwIBAgIRAID4gPKg7DDiuOfzUYFSXLAwCgYIKoZIzj0EAwIwNjER + MA8GA1UEChMIT1BBIFRlc3QxITAfBgNVBAMTGE9QQSBUZXN0IEludGVybWVkaWF0 + ZSBDQTAeFw0yMTA3MDUxNzQ5NTBaFw0zNjA3MDExNzQ5NDdaMCUxIzAhBgNVBAMT + Gm5vdGFyZWFsc2l0ZS5vcGEubG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0D + AQcDQgAE1YSXZXeaGGL+XeYyoPi/QdA39Ds4fgxSHJTMh+js393kByPm2PNtFkem + tUii3KCRJw3SEh3z0JWr/9y4+ua2L6OBmTCBljAOBgNVHQ8BAf8EBAMCB4AwHQYD + VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBRL0P0g17viZHo9 + CnXe3ZQJm48LXTAfBgNVHSMEGDAWgBS6yVk2MmXRXwjUaPXsggMAvL/1YTAlBgNV + HREEHjAcghpub3RhcmVhbHNpdGUub3BhLmxvY2FsaG9zdDAKBggqhkjOPQQDAgNH + ADBEAiAtmZewL94ijN0YwUGaJM9BXCaoTQPwkzugqjCj+K912QIgKKFvbPu4asrE + nwy7dzejHmQUcZ/aUNbc4VTbiv15ESk= + -----END CERTIFICATE----- + ` + + value := crypto.x509.parse_and_verify_certificates(certs) + + result := { + "valid": value[0], + "certs": [c| + some cert in value[1] + c := { + "CN": cert.Subject.CommonName, + "DNS": cert.DNSNames, + "URI": cert.URIStrings, + } + ], + } + + note: cryptox509parseandverifycertificates/base_case + query: data.test.result = x + want_result: + - x: + certs: + - CN: notarealsite.opa.localhost + DNS: + - notarealsite.opa.localhost + URI: + - CN: OPA Test Intermediate CA + DNS: + URI: + - CN: OPA Test Root CA + DNS: + URI: + valid: true +- data: + modules: + - | + package test + + import future.keywords + + certs := `-----BEGIN CERTIFICATE----- + MIIB1TCCAXugAwIBAgIIKIoxsnMwJJ4wCgYIKoZIzj0EAwIwPTELMAkGA1UEBhMC + R0IxEDAOBgNVBAoTB0V4YW1wbGUxHDAaBgNVBAUTEzI5MjEyMDE5NTA4MDk2NjI2 + MjIwIBcNMjMxMTI5MTc1NTQ2WhgPMjEyMzExMDUxNzU1NDZaMD0xCzAJBgNVBAYT + AkdCMRAwDgYDVQQKEwdFeGFtcGxlMRwwGgYDVQQFExMyOTIxMjAxOTUwODA5NjYy + NjIyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkvI9ddM0SuP9LvBWS1y64fuK + ELCjVF5W3FSKm3azKEkDi8Eq1I1UM80MgCjC5ChNNyM4+cmVUDrCkTl3SqRxa6Nj + MGEwDgYDVR0PAQH/BAQDAgIEMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFF7H + A8n3mXXnwUP0ypMJ9JwY5wasMB8GA1UdEQQYMBaGFHNwaWZmZTovL2V4YW1wbGUu + Y29tMAoGCCqGSM49BAMCA0gAMEUCIByB2l5RIWmaU8qcRv13qigbB9BV/F2raEk+ + pRQnsUcgAiEA9OvBpPKC/FBkI5vVvR7WgK5sGPna4+a0RkXxRQgN2jM= + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIB1jCCAXygAwIBAgIIV9914tIKKkMwCgYIKoZIzj0EAwIwPTELMAkGA1UEBhMC + R0IxEDAOBgNVBAoTB0V4YW1wbGUxHDAaBgNVBAUTEzI5MjEyMDE5NTA4MDk2NjI2 + MjIwIBcNMjMxMTI5MTc1NTQ2WhgPMjEyMjExMDUxNzU1NDZaMD0xCzAJBgNVBAYT + AkdCMRAwDgYDVQQKEwdFeGFtcGxlMRwwGgYDVQQFExM2MzMxOTA5MjE4MTUzMTQ2 + OTQ3MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMoy2UqvC8zL3sPfLNvG1nX5p + 6hhEyDjFtokORB4VkKiPXFryIFn8XHG0ipz6aKSwVMoDT2T/YXP/wWpVwPJCi6Nk + MGIwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD + ATAMBgNVHRMBAf8EAjAAMCMGA1UdEQQcMBqGGHNwaWZmZTovL2V4YW1wbGUuY29t + L29wYTAKBggqhkjOPQQDAgNIADBFAiBEmdSKGj2+9J5SQPIAmwdxpVTOxqmVQv2x + Vvita/AmowIhAOyX/alNJxL4iCfKUNwlC2lYxGhuWopWgB1Q32bQhTEh + -----END CERTIFICATE----- + ` + + value := crypto.x509.parse_and_verify_certificates(certs) + + result := { + "valid": value[0], + "certs": [c| + some cert in value[1] + c := { + "CN": cert.Subject.CommonName, + "DNS": cert.DNSNames, + "URI": cert.URIStrings, + } + ], + } + + note: cryptox509parseandverifycertificates/uri_strings + query: data.test.result = x + want_result: + - x: + certs: + - CN: '' + DNS: null + URI: + - spiffe://example.com/opa + - CN: '' + DNS: null + URI: + - spiffe://example.com + valid: true + diff --git a/test/cases/testdata/cryptox509parsecertificates/test-cryptox509parsecertificates-raw-uris.yaml b/test/cases/testdata/cryptox509parsecertificates/test-cryptox509parsecertificates-raw-uris.yaml new file mode 100644 index 0000000000..131c86da5f --- /dev/null +++ b/test/cases/testdata/cryptox509parsecertificates/test-cryptox509parsecertificates-raw-uris.yaml @@ -0,0 +1,26 @@ +cases: +- data: + modules: + - | + package generated + + certs = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVh5Z0F3SUJBZ0lJZGxpT1dVY1NXM3N3Q2dZSUtvWkl6ajBFQXdJd1BURUxNQWtHQTFVRUJoTUMKUjBJeEVEQU9CZ05WQkFvVEIwVjRZVzF3YkdVeEhEQWFCZ05WQkFVVEV6RTFPREV4TnpnNU56UTJPRFkxTmpneQpOalF3SUJjTk1qTXhNVEl3TVRZMU5USTRXaGdQTWpFeU1qRXdNamN4TmpVMU1qaGFNRDB4Q3pBSkJnTlZCQVlUCkFrZENNUkF3RGdZRFZRUUtFd2RGZUdGdGNHeGxNUnd3R2dZRFZRUUZFeE00TlRJM056SXlOREE0TlRJeE5qVXoKTVRFMU1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRXp0UDNrQnNpQXY4UUF5eWxUalJZSFlWegpjWTB5YmpBdC9VbWpZb3Fxb0o4SEtIdXF1ckRaUmVwa05qUXdwV3pmZndZZ0xaNk42SisyVUlPdlZ0TDZEcU5rCk1HSXdEZ1lEVlIwUEFRSC9CQVFEQWdlQU1CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUNCZ2dyQmdFRkJRY0QKQVRBTUJnTlZIUk1CQWY4RUFqQUFNQ01HQTFVZEVRUWNNQnFHR0hOd2FXWm1aVG92TDJWNFlXMXdiR1V1WTI5dApMMjl3WVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQXlRNDhPd25lTHkzMjZqYitEUjd5RjJhcS94Wnl1cW9qCitUU3ZLVVB5NEU0Q0lRQ0VMUlp3K0dWTjhJR0drVGV4MGxxTDNxY21mWldJbm15VitrbnQ0d3p3L3c9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==" + + uri_strings = crypto.x509.parse_certificates(certs)[0].URIStrings + note: cryptox509parsecertificates/uri_strings + query: data.generated.uri_strings = x + want_result: + - x: + - spiffe://example.com/opa +- data: + modules: + - | + package generated + + certs = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNTakNDQWJPZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRMEZBREJDTVFzd0NRWURWUVFHRXdKMWN6RUwKTUFrR0ExVUVDQXdDUTBFeEVEQU9CZ05WQkFvTUIwVjRZVzF3YkdVeEZEQVNCZ05WQkFNTUMyVjRZVzF3YkdVdQpZMjl0TUI0WERUSXpNVEV5T1RFMk5ESXdPRm9YRFRJME1URXlPREUyTkRJd09Gb3dRakVMTUFrR0ExVUVCaE1DCmRYTXhDekFKQmdOVkJBZ01Ba05CTVJBd0RnWURWUVFLREFkRmVHRnRjR3hsTVJRd0VnWURWUVFEREF0bGVHRnQKY0d4bExtTnZiVENCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBempOT1puY05DL25MdEZQYwpUNnNlSzZ0ditTbU9GaGk3NVBKRm9sQ0dFZUFiTHJHTzhaUmR2cXY2OStTTk41MUhrNFBJUDUrejk4aHZIdWJQClVtcGdOMVdVM3FKK2tOVTJXL3poR3pLMTdIL2c3YjVJTjRHZmx3bXJlRWZscnRicnZKSGRlRkVhVGtmYnVld0YKVmZvLzRiaG0yYUthczNHcUo3RnlBNDVxeVUwQ0F3RUFBYU5RTUU0d0hRWURWUjBPQkJZRUZOUStDMUVOK0dSYwpGS1dyTll6ZWdnbFc3TE9lTUI4R0ExVWRJd1FZTUJhQUZOUStDMUVOK0dSY0ZLV3JOWXplZ2dsVzdMT2VNQXdHCkExVWRFd1FGTUFNQkFmOHdEUVlKS29aSWh2Y05BUUVOQlFBRGdZRUF1cXdVVWVXbTFJaURRbmphK1hqd1VCaXUKQXBYWEx5NlFZRG9jUkhoRHlJS3BKSWRJOXltNi9RcVhkOFQ2WGlYUTJDbFNJbGxBSTByQWJYNUZvYzNxaWRkUAp0d2huT1pxd3NZdFhUNy9XOHFLSm5FVnhZckJuRmZSdk9SU3ZlOEtwSDErVHQ2elZEYlJ5bENacTR1TTNrZXN2CmJabXlVdlM1bldBVm44ZmhFdUU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=" + + uri_strings = crypto.x509.parse_certificates(certs)[0].URIStrings + note: cryptox509parsecertificates/uri_strings_no_uris + query: data.generated.uri_strings = x + want_result: + - x: null diff --git a/topdown/crypto.go b/topdown/crypto.go index c33abb2c93..520c051860 100644 --- a/topdown/crypto.go +++ b/topdown/crypto.go @@ -56,7 +56,7 @@ func builtinCryptoX509ParseCertificates(_ BuiltinContext, operands []*ast.Term, return err } - v, err := ast.InterfaceToValue(certs) + v, err := ast.InterfaceToValue(extendCertificates(certs)) if err != nil { return err } @@ -64,6 +64,28 @@ func builtinCryptoX509ParseCertificates(_ BuiltinContext, operands []*ast.Term, return iter(ast.NewTerm(v)) } +// extendedCert is a wrapper around x509.Certificate that adds additional fields for JSON serialization. +type extendedCert struct { + x509.Certificate + URIStrings []string +} + +func extendCertificates(certs []*x509.Certificate) []extendedCert { + // add a field to certs containing the URIs as strings + processedCerts := make([]extendedCert, len(certs)) + + for i, cert := range certs { + processedCerts[i].Certificate = *cert + if cert.URIs != nil { + processedCerts[i].URIStrings = make([]string, len(cert.URIs)) + for j, uri := range cert.URIs { + processedCerts[i].URIStrings[j] = uri.String() + } + } + } + return processedCerts +} + func builtinCryptoX509ParseAndVerifyCertificates(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { a := operands[0].Value @@ -87,7 +109,7 @@ func builtinCryptoX509ParseAndVerifyCertificates(_ BuiltinContext, operands []*a return iter(invalid) } - value, err := ast.InterfaceToValue(verified) + value, err := ast.InterfaceToValue(extendCertificates(verified)) if err != nil { return err }